From 2b77ab6437afa50f5d0a7417bc6a4917cc79f5d8 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 14 May 2013 09:44:50 +0800 Subject: [PATCH 001/201] FogBugz #111740: [PATCHv4 1/7] Integrates Nios II kernel (kernel) Integrates Nios II kernel (arch/nios2) from linux-nios2.git to linux-socfpga.git. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/Makefile | 16 + arch/nios2/kernel/asm-offsets.c | 95 +++++ arch/nios2/kernel/cpuinfo.c | 185 +++++++++ arch/nios2/kernel/early_printk.c | 115 ++++++ arch/nios2/kernel/entry-nommu.S | 318 +++++++++++++++ arch/nios2/kernel/entry.S | 584 +++++++++++++++++++++++++++ arch/nios2/kernel/head.S | 194 +++++++++ arch/nios2/kernel/insnemu.S | 593 +++++++++++++++++++++++++++ arch/nios2/kernel/irq.c | 89 +++++ arch/nios2/kernel/kgdb.c | 143 +++++++ arch/nios2/kernel/misaligned.c | 320 +++++++++++++++ arch/nios2/kernel/module.c | 170 ++++++++ arch/nios2/kernel/nios2_ksyms.c | 34 ++ arch/nios2/kernel/process.c | 395 ++++++++++++++++++ arch/nios2/kernel/prom.c | 164 ++++++++ arch/nios2/kernel/ptrace.c | 198 +++++++++ arch/nios2/kernel/setup.c | 233 +++++++++++ arch/nios2/kernel/signal.c | 661 +++++++++++++++++++++++++++++++ arch/nios2/kernel/sys_nios2.c | 166 ++++++++ arch/nios2/kernel/syscalltable.S | 379 ++++++++++++++++++ arch/nios2/kernel/time.c | 154 +++++++ arch/nios2/kernel/traps.c | 199 ++++++++++ arch/nios2/kernel/vmlinux.lds.S | 68 ++++ 23 files changed, 5473 insertions(+) create mode 100644 arch/nios2/kernel/Makefile create mode 100644 arch/nios2/kernel/asm-offsets.c create mode 100644 arch/nios2/kernel/cpuinfo.c create mode 100644 arch/nios2/kernel/early_printk.c create mode 100644 arch/nios2/kernel/entry-nommu.S create mode 100644 arch/nios2/kernel/entry.S create mode 100644 arch/nios2/kernel/head.S create mode 100644 arch/nios2/kernel/insnemu.S create mode 100644 arch/nios2/kernel/irq.c create mode 100644 arch/nios2/kernel/kgdb.c create mode 100644 arch/nios2/kernel/misaligned.c create mode 100644 arch/nios2/kernel/module.c create mode 100644 arch/nios2/kernel/nios2_ksyms.c create mode 100644 arch/nios2/kernel/process.c create mode 100644 arch/nios2/kernel/prom.c create mode 100644 arch/nios2/kernel/ptrace.c create mode 100644 arch/nios2/kernel/setup.c create mode 100644 arch/nios2/kernel/signal.c create mode 100644 arch/nios2/kernel/sys_nios2.c create mode 100644 arch/nios2/kernel/syscalltable.S create mode 100644 arch/nios2/kernel/time.c create mode 100644 arch/nios2/kernel/traps.c create mode 100644 arch/nios2/kernel/vmlinux.lds.S diff --git a/arch/nios2/kernel/Makefile b/arch/nios2/kernel/Makefile new file mode 100644 index 0000000000000..7ad727731a126 --- /dev/null +++ b/arch/nios2/kernel/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the nios2 linux kernel. +# + +extra-y := head.o vmlinux.lds + +obj-y += entry$(MMU).o + +obj-y += cpuinfo.o insnemu.o irq.o nios2_ksyms.o process.o prom.o ptrace.o \ + setup.o signal.o sys_nios2.o syscalltable.o time.o traps.o + +obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_CONSOLE) += console.o +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +obj-$(CONFIG_ALIGNMENT_TRAP) += misaligned.o +obj-$(CONFIG_KGDB) += kgdb.o diff --git a/arch/nios2/kernel/asm-offsets.c b/arch/nios2/kernel/asm-offsets.c new file mode 100644 index 0000000000000..7187fa62a8d2e --- /dev/null +++ b/arch/nios2/kernel/asm-offsets.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + /* struct task_struct */ + OFFSET(TASK_THREAD, task_struct, thread); + BLANK(); + + /* struct thread_struct */ + OFFSET(THREAD_KSP, thread_struct, ksp); + OFFSET(THREAD_KPSR, thread_struct, kpsr); +#ifndef CONFIG_MMU + OFFSET(THREAD_KESR, thread_struct, kesr); +#endif + BLANK(); + + /* struct pt_regs */ + OFFSET(PT_ORIG_R2, pt_regs, orig_r2); +#ifdef CONFIG_MMU + OFFSET(PT_ORIG_R7, pt_regs, orig_r7); +#endif + OFFSET(PT_R1, pt_regs, r1); + OFFSET(PT_R2, pt_regs, r2); + OFFSET(PT_R3, pt_regs, r3); + OFFSET(PT_R4, pt_regs, r4); + OFFSET(PT_R5, pt_regs, r5); + OFFSET(PT_R6, pt_regs, r6); + OFFSET(PT_R7, pt_regs, r7); + OFFSET(PT_R8, pt_regs, r8); + OFFSET(PT_R9, pt_regs, r9); + OFFSET(PT_R10, pt_regs, r10); + OFFSET(PT_R11, pt_regs, r11); + OFFSET(PT_R12, pt_regs, r12); + OFFSET(PT_R13, pt_regs, r13); + OFFSET(PT_R14, pt_regs, r14); + OFFSET(PT_R15, pt_regs, r15); + OFFSET(PT_EA, pt_regs, ea); + OFFSET(PT_RA, pt_regs, ra); + OFFSET(PT_FP, pt_regs, fp); + OFFSET(PT_SP, pt_regs, sp); + OFFSET(PT_GP, pt_regs, gp); + OFFSET(PT_ESTATUS, pt_regs, estatus); +#ifndef CONFIG_MMU + OFFSET(PT_STATUS_EXTENSION, pt_regs, status_extension); +#endif + DEFINE(PT_REGS_SIZE, sizeof(struct pt_regs)); + BLANK(); + + /* struct switch_stack */ + OFFSET(SW_R16, switch_stack, r16); + OFFSET(SW_R17, switch_stack, r17); + OFFSET(SW_R18, switch_stack, r18); + OFFSET(SW_R19, switch_stack, r19); + OFFSET(SW_R20, switch_stack, r20); + OFFSET(SW_R21, switch_stack, r21); + OFFSET(SW_R22, switch_stack, r22); + OFFSET(SW_R23, switch_stack, r23); + OFFSET(SW_FP, switch_stack, fp); + OFFSET(SW_GP, switch_stack, gp); + OFFSET(SW_RA, switch_stack, ra); + DEFINE(SWITCH_STACK_SIZE, sizeof(struct switch_stack)); + BLANK(); + + /* struct thread_info */ + OFFSET(TI_TASK, thread_info, task); + OFFSET(TI_FLAGS, thread_info, flags); + OFFSET(TI_PREEMPT_COUNT, thread_info, preempt_count); + BLANK(); + + return 0; +} diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c new file mode 100644 index 0000000000000..cb91e54645ef9 --- /dev/null +++ b/arch/nios2/kernel/cpuinfo.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2011 Tobias Klauser + * + * Based on cpuinfo.c from microblaze + * + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct cpuinfo cpuinfo; + +#define err_cpu(x) \ + pr_err("ERROR: Nios II " x " different for kernel and DTS\n"); + +static inline u32 fcpu(struct device_node *cpu, const char *n) +{ + u32 val = 0; + + of_property_read_u32(cpu, n, &val); + + return val; +} + +static inline u32 fcpu_has(struct device_node *cpu, const char *n) +{ + return of_get_property(cpu, n, NULL) ? 1 : 0; +} + +void __init setup_cpuinfo(void) +{ + struct device_node *cpu; + const char *str; + int len; + + cpu = of_find_node_by_type(NULL, "cpu"); + if (!cpu) + panic("%s: No CPU found in devicetree!\n", __func__); + + /* for now */ +#ifdef CONFIG_MMU + cpuinfo.mmu = 1; +#else + cpuinfo.mmu = 0; +#endif + + cpuinfo.cpu_clock_freq = fcpu(cpu, "clock-frequency"); + + str = of_get_property(cpu, "ALTR,implementation", &len); + if (str) + strlcpy(cpuinfo.cpu_impl, str, sizeof(cpuinfo.cpu_impl)); + else + strcpy(cpuinfo.cpu_impl, ""); + + cpuinfo.has_div = fcpu_has(cpu, "ALTR,has-div"); + cpuinfo.has_mul = fcpu_has(cpu, "ALTR,has-mul"); + cpuinfo.has_mulx = fcpu_has(cpu, "ALTR,has-mulx"); + +#ifdef CONFIG_NIOS2_HW_DIV_SUPPORT + if (!cpuinfo.has_div) + err_cpu("DIV"); +#endif +#ifdef CONFIG_NIOS2_HW_MUL_SUPPORT + if (!cpuinfo.has_mul) + err_cpu("MUL"); +#endif +#ifdef CONFIG_NIOS2_HW_MULX_SUPPORT + if (!cpuinfo.has_mulx) + err_cpu("MULX"); +#endif + + cpuinfo.icache_line_size = fcpu(cpu, "icache-line-size"); + cpuinfo.icache_size = fcpu(cpu, "icache-size"); + cpuinfo.dcache_line_size = fcpu(cpu, "dcache-line-size"); + cpuinfo.dcache_size = fcpu(cpu, "dcache-size"); + + cpuinfo.tlb_pid_num_bits = fcpu(cpu, "ALTR,pid-num-bits"); + cpuinfo.tlb_num_ways = fcpu(cpu, "ALTR,tlb-num-ways"); + cpuinfo.tlb_num_ways_log2 = ilog2(cpuinfo.tlb_num_ways); + cpuinfo.tlb_num_entries = fcpu(cpu, "ALTR,tlb-num-entries"); + cpuinfo.tlb_num_lines = cpuinfo.tlb_num_entries / cpuinfo.tlb_num_ways; + cpuinfo.tlb_ptr_sz = fcpu(cpu, "ALTR,tlb-ptr-sz"); + + cpuinfo.reset_addr = fcpu(cpu, "ALTR,reset-addr"); + cpuinfo.exception_addr = fcpu(cpu, "ALTR,exception-addr"); + cpuinfo.fast_tlb_miss_exc_addr = fcpu(cpu, "ALTR,fast-tlb-miss-addr"); +} + +#ifdef CONFIG_PROC_FS + +/* + * Get CPU information for use by the procfs. + */ +static int show_cpuinfo(struct seq_file *m, void *v) +{ + int count = 0; + const u32 clockfreq = cpuinfo.cpu_clock_freq; + + count = seq_printf(m, + "CPU:\t\tNios II/%s\n" + "MMU:\t\t%s\n" + "FPU:\t\tnone\n" + "Clocking:\t%u.%02u MHz\n" + "BogoMips:\t%lu.%02lu\n" + "Calibration:\t%lu loops\n", + cpuinfo.cpu_impl, + cpuinfo.mmu ? "present" : "none", + clockfreq / 1000000, (clockfreq / 100000) % 10, + (loops_per_jiffy * HZ) / 500000, + ((loops_per_jiffy * HZ) / 5000) % 100, + (loops_per_jiffy * HZ)); + + count += seq_printf(m, + "HW:\n" + " MUL:\t\t%s\n" + " MULX:\t\t%s\n" + " DIV:\t\t%s\n", + cpuinfo.has_mul ? "yes" : "no", + cpuinfo.has_mulx ? "yes" : "no", + cpuinfo.has_div ? "yes" : "no"); + + count += seq_printf(m, + "Icache:\t\t%ukB, line length: %u\n", + cpuinfo.icache_size >> 10, + cpuinfo.icache_line_size); + + count += seq_printf(m, + "Dcache:\t\t%ukB, line length: %u\n", + cpuinfo.dcache_size >> 10, + cpuinfo.dcache_line_size); + + if (cpuinfo.mmu) + count += seq_printf(m, + "TLB:\t\t%u ways, %u entries, %u PID bits\n", + cpuinfo.tlb_num_ways, + cpuinfo.tlb_num_entries, + cpuinfo.tlb_pid_num_bits); + + return 0; +} + +static void *cpuinfo_start(struct seq_file *m, loff_t *pos) +{ + unsigned long i = *pos; + + return i < num_possible_cpus() ? (void *) (i + 1) : NULL; +} + +static void *cpuinfo_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return cpuinfo_start(m, pos); +} + +static void cpuinfo_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations cpuinfo_op = { + .start = cpuinfo_start, + .next = cpuinfo_next, + .stop = cpuinfo_stop, + .show = show_cpuinfo +}; + +#endif /* CONFIG_PROC_FS */ diff --git a/arch/nios2/kernel/early_printk.c b/arch/nios2/kernel/early_printk.c new file mode 100644 index 0000000000000..af9eb6e0cf56f --- /dev/null +++ b/arch/nios2/kernel/early_printk.c @@ -0,0 +1,115 @@ +/* + * Early printk for Nios2. + * + * Copyright (C) 2010, Tobias Klauser + * Copyright (C) 2009, Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * 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. + */ + +#include +#include +#include +#include +#include + +static unsigned long base_addr; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) + +#define ALTERA_JTAGUART_DATA_REG 0 +#define ALTERA_JTAGUART_CONTROL_REG 4 +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 + +#define JUART_GET_CR() \ + __builtin_ldwio((void *)(base_addr + ALTERA_JTAGUART_CONTROL_REG)) +#define JUART_SET_CR(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_JTAGUART_CONTROL_REG), v) +#define JUART_SET_TX(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_JTAGUART_DATA_REG), v) + +static void early_console_write(struct console *con, const char *s, unsigned n) +{ + unsigned long status; + + while (n-- && *s) { + while (((status = JUART_GET_CR()) + & ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) + return; /* no connection activity */ +#endif + } + JUART_SET_TX(*s); + s++; + } +} + +#elif defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_STATUS_TRDY 0x0040 + +#define UART_GET_SR() \ + __builtin_ldwio((void *)(base_addr + ALTERA_UART_STATUS_REG)) +#define UART_SET_TX(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_UART_TXDATA_REG), v) + +static void early_console_putc(char c) +{ + while (!(UART_GET_SR() & ALTERA_UART_STATUS_TRDY)) + ; + + UART_SET_TX(c); +} + +static void early_console_write(struct console *con, const char *s, unsigned n) +{ + while (n-- && *s) { + early_console_putc(*s); + if (*s == '\n') + early_console_putc('\r'); + s++; + } +} + +#else +# error Neither SERIAL_ALTERA_JTAGUART_CONSOLE nor SERIAL_ALTERA_UART_CONSOLE \ +selected +#endif + +static struct console early_console = { + .name = "early", + .write = early_console_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1 +}; + +void __init setup_early_printk(void) +{ +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) || \ + defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + base_addr = early_altera_uart_or_juart_console(); +#else + base_addr = 0; +#endif + + if (!base_addr) + return; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) + /* Clear activity bit so BYPASS doesn't stall if we've used JTAG for + * downloading the kernel. This might cause early data to be lost even + * if the JTAG terminal is running. + */ + JUART_SET_CR(JUART_GET_CR() | ALTERA_JTAGUART_CONTROL_AC_MSK); +#endif + + register_console(&early_console); + pr_info("early_console initialized at 0x%08lx\n", base_addr); +} diff --git a/arch/nios2/kernel/entry-nommu.S b/arch/nios2/kernel/entry-nommu.S new file mode 100644 index 0000000000000..08096fae4d274 --- /dev/null +++ b/arch/nios2/kernel/entry-nommu.S @@ -0,0 +1,318 @@ +/* + * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) + * Copyright (C) 1998 D. Jeff Dionne , + * Kenneth Albanowski , + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * Copyright (C) 2004 Microtronix Datacom Ltd. + * Copyright (C) 2013 Altera Corporation + * + * Based on entry.S from m68knommu. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +.macro GET_THREAD_INFO reg +.if THREAD_SIZE & 0xffff0000 + andhi \reg, sp, %hi(~(THREAD_SIZE-1)) +.else + addi \reg, r0, %lo(~(THREAD_SIZE-1)) + and \reg, \reg, sp +.endif +.endm + +.text +.set noat +.set nobreak + +ENTRY(system_call) +/* SAVE_ALL */ + rdctl r10, status /* enable intrs again */ + ori r10, r10, STATUS_PIE + wrctl status, r10 + + movi r2, -ENOSYS + stw r2, PT_R2(sp) /* default return value in r2 */ + /* original r2 is in orig_r2 */ + + movui r1, NR_syscalls + bgeu r3, r1, ret_from_exception + slli r1, r3, 2 + movhi r11, %hiadj(sys_call_table) + add r1, r1, r11 + ldw r1, %lo(sys_call_table)(r1) + beq r1, r0, ret_from_exception + + movi r11, %lo(0xffffe000) /* Get thread info pointer */ + and r11, sp, r11 + ldw r11, TI_FLAGS(r11) + BTBNZ r11, r11, TIF_SYSCALL_TRACE, 1f + + callr r1 + stw r2, PT_R2(sp) /* save the return value */ + br ret_from_exception +1: + SAVE_SWITCH_STACK + call syscall_trace + RESTORE_SWITCH_STACK + /* wentao: restore r4-9, since they are trashed by syscall_trace */ + ldw r4, PT_R4(sp) + ldw r5, PT_R5(sp) + ldw r6, PT_R6(sp) + ldw r7, PT_R7(sp) + ldw r8, PT_R8(sp) + ldw r9, PT_R9(sp) + callr r1 + stw r2, PT_R2(sp) /* save the return value */ + SAVE_SWITCH_STACK + call syscall_trace + RESTORE_SWITCH_STACK + +ret_from_exception: + ldw r1, PT_STATUS_EXTENSION(sp) /* check if returning to kernel */ + TSTBZ r1, r1, PS_S, Luser_return /* if so, skip resched, signals */ + +restore_all: + rdctl r10, status /* disable intrs */ + andi r10, r10, %lo(~STATUS_PIE) + wrctl status, r10 + RESTORE_ALL + eret + +Luser_return: + GET_THREAD_INFO r24 /* get thread_info pointer */ + ldw r10, TI_FLAGS(r24) /* get thread_info->flags */ + ANDI32 r11, r10, _TIF_WORK_MASK + beq r11, r0, restore_all /* Nothing to do */ + BTBZ r1, r10, TIF_NEED_RESCHED, Lsignal_return + +Lwork_resched: + call schedule + br ret_from_exception + +Lsignal_return: + ANDI32 r1, r10, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME + beq r1, r0, restore_all + mov r4, sp /* pt_regs */ + SAVE_SWITCH_STACK + mov r5, r0 /* oldset = 0 */ + movi r6, 1 /* in_syscall = 1 */ + call do_notify_resume + RESTORE_SWITCH_STACK + br restore_all + +/* + * Handle software exceptions. Put here so external interrupts + * can fall throught to ret_from_interrupt. + */ + +software_exception: + ldw r24, -4(ea) /* instruction that caused the exception */ + xorhi r24, r24, 0x003b /* upper half of trap opcode */ + xori r24, r24, 0x683a /* lower half of trap opcode */ + cmpeqi r11, r24, 0x40 /* Check for imm=0x01 => breakpoint */ + bne r11, r0, breakpoint + bne r24, r0, instruction_trap /* N - check for instruction trap */ + cmpeqi r11, r2, TRAP_ID_SYSCALL /* ? Is this a syscall */ + bne r11, r0, system_call /* Y - handle syscall */ + cmpeqi r11, r2,63 /* ? Is this the old syscall number */ + bne r11, r0, system_call /* Y - handle syscall to catch older apps*/ + br restore_all /* N - everything else is ignored for now */ + +breakpoint: + mov r4, sp + call breakpoint_c + br restore_all + +/* + * This is the generic interrupt handler (for all hardware interrupt + * sources). It figures out the vector number and calls the appropriate + * interrupt service routine directly. + */ +ENTRY(inthandler) + SAVE_ALL + /* + * Test to see if the exception was a software exception or caused by an + * external interrupt, and vector accordingly. + */ + + rdctl r24, estatus + andi r24, r24, ESTATUS_EPIE + beq r24, r0, software_exception + rdctl r12, ipending + rdctl r9, ienable + and r12, r12, r9 + beq r12, r0, software_exception + + movi r24, -1 + stw r24, PT_ORIG_R2(sp) + + /* + * Process an external hardware interrupt. + */ + + addi ea, ea, -4 /* re-issue the interrupted instruction */ + stw ea, PT_EA(sp) +2: movi r4, %lo(-1) /* Start from bit position 0, highest priority */ + /* This is the IRQ # for handler call */ +1: andi r10, r12, 1 /* Isolate bit we are interested in */ + srli r12, r12, 1 /* shift count is costly without hardware + multiplier */ + addi r4, r4, 1 + beq r10, r0, 1b + mov r5, sp /* Setup pt_regs pointer for handler call */ + call do_IRQ + rdctl r12, ipending /* check again if irq still pending */ + rdctl r9, ienable /* Isolate possible interrupts */ + and r12, r12, r9 + bne r12, r0, 2b + /* br ret_from_interrupt */ /* fall throught to ret_from_interrupt */ + +ENTRY(ret_from_interrupt) + ldw r4, PT_STATUS_EXTENSION(sp) + TSTBZ r4, r4, PS_S, Luser_return /* Returning to user */ + +#ifdef CONFIG_PREEMPT + GET_THREAD_INFO r1 + ldw r4, TI_PREEMPT_COUNT(r1) + bne r4, r0, restore_all + +need_resched: + ldw r4, TI_FLAGS(r1) /* ? Need resched set */ + BTBZ r10, r4, TIF_NEED_RESCHED, restore_all + ldw r4, PT_ESTATUS(sp) /* ? Interrupts off */ + andi r10, r4, STATUS_PIE + beq r10, r0, restore_all + movia r4, PREEMPT_ACTIVE + stw r4, TI_PREEMPT_COUNT(r1) + rdctl r10, status /* enable intrs again */ + ori r10, r10, STATUS_PIE + wrctl status, r10 + PUSH r1 + call schedule + POP r1 + mov r4, r0 + stw r4, TI_PREEMPT_COUNT(r1) + rdctl r10, status /* disable intrs */ + andi r10, r10, %lo(~STATUS_PIE) + wrctl status, r10 + br need_resched +#else + br restore_all +#endif + + +/* + * Beware - when entering resume, prev (the current task) is + * in r4, next (the new task) is in r5, don't change these + * registers. + */ +ENTRY(resume) + + rdctl r7, status /* save thread status reg */ + stw r7, TASK_THREAD + THREAD_KPSR(r4) + + andi r7, r7, %lo(~STATUS_PIE) /* disable interrupts */ + wrctl status, r7 + + movia r8, status_extension /* save status extension */ + ldw r7, 0(r8) + stw r7, TASK_THREAD + THREAD_KESR(r4) + + SAVE_SWITCH_STACK + stw sp, TASK_THREAD + THREAD_KSP(r4)/* save kernel stack pointer */ + ldw sp, TASK_THREAD + THREAD_KSP(r5)/* restore new thread stack */ + movia r24, _current_thread /* save thread */ + GET_THREAD_INFO r1 + stw r1, 0(r24) + RESTORE_SWITCH_STACK + + ldw r7, TASK_THREAD + THREAD_KESR(r5) /* restore extended status + reg */ + stw r7, 0(r8) + + ldw r7, TASK_THREAD +TH READ_KPSR(r5)/* restore thread status reg */ + wrctl status, r7 + ret + +ENTRY(ret_from_fork) + call schedule_tail + br ret_from_exception + +ENTRY(sys_fork) + mov r4, sp + SAVE_SWITCH_STACK + call nios2_vfork + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_vfork) + mov r4, sp + SAVE_SWITCH_STACK + call nios2_vfork + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_execve) + mov r4, sp + SAVE_SWITCH_STACK + call nios2_execve + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_clone) + mov r4, sp + SAVE_SWITCH_STACK + call nios2_clone + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_sigsuspend) + mov r4, sp + SAVE_SWITCH_STACK + call do_sigsuspend + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_rt_sigsuspend) + mov r4, sp + SAVE_SWITCH_STACK + call do_rt_sigsuspend + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_sigreturn) + mov r4, sp + SAVE_SWITCH_STACK + call do_sigreturn + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_sigaltstack) + ldw r4, PT_R4(sp) + ldw r5, PT_R5(sp) + ldw r6, PT_SP(sp) + SAVE_SWITCH_STACK + call do_sigaltstack + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_rt_sigreturn) + SAVE_SWITCH_STACK + mov r4, sp + call do_rt_sigreturn + RESTORE_SWITCH_STACK + ret diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S new file mode 100644 index 0000000000000..ed049cc0bdbc0 --- /dev/null +++ b/arch/nios2/kernel/entry.S @@ -0,0 +1,584 @@ +/* + * linux/arch/nios2/kernel/entry.S + * + * Copyright (C) 2009, Wind River Systems Inc + * Copyright (C) 2013 Altera Corporation + * + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * Based on: + * + * linux/arch/nios2/kernel/entry.S + * + * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) + * Copyright (C) 1998 D. Jeff Dionne , + * Kenneth Albanowski , + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * Based on: + * + * linux/arch/m68knommu/kernel/entry.S + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * 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. + * + * Linux/m68k support by Hamish Macdonald + * + * 68060 fixes by Jesper Skov + * ColdFire support by Greg Ungerer (gerg@snapgear.com) + * 5307 fixes by David W. Miller + * linux 2.4 support David McCullough + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +.macro GET_THREAD_INFO reg +.if THREAD_SIZE & 0xffff0000 + andhi \reg, sp, %hi(~(THREAD_SIZE-1)) +.else + addi \reg, r0, %lo(~(THREAD_SIZE-1)) + and \reg, \reg, sp +.endif +.endm + +/* FIXME: Lots of these exceptions need to be handled */ +.section .rodata +.align 4 +exception_table: + .word unhandled_exception /* 0 - Reset */ + .word unhandled_exception /* 1 - Processor-only Reset */ + .word external_interrupt /* 2 - Interrupt */ + .word handle_trap /* 3 - Trap Instruction */ + + .word instruction_trap /* 4 - Unimplemented instruction */ + .word handle_illegal /* 5 - Illegal instruction */ + .word handle_unaligned /* 6 - Misaligned data access */ + .word handle_unaligned /* 7 - Misaligned destination address */ + + .word handle_diverror /* 8 - Division error */ + .word protection_exception_ba /* 9 - Supervisor-only instr. address */ + .word protection_exception_instr /* 10 - Supervisor only instruction */ + .word protection_exception_ba /* 11 - Supervisor only data address */ + + .word unhandled_exception /* 12 - Double TLB miss (data) */ + .word protection_exception_pte /* 13 - TLB permission violation (x) */ + .word protection_exception_pte /* 14 - TLB permission violation (r) */ + .word protection_exception_pte /* 15 - TLB permission violation (w) */ + + .word unhandled_exception /* 16 - MPU region violation */ + +trap_table: + .word handle_system_call /* 0 */ + .word instruction_trap /* 1 */ + .word instruction_trap /* 2 */ + .word instruction_trap /* 3 */ + .word instruction_trap /* 4 */ + .word instruction_trap /* 5 */ + .word instruction_trap /* 6 */ + .word instruction_trap /* 7 */ + .word instruction_trap /* 8 */ + .word instruction_trap /* 9 */ + .word instruction_trap /* 10 */ + .word instruction_trap /* 11 */ + .word instruction_trap /* 12 */ + .word instruction_trap /* 13 */ + .word instruction_trap /* 14 */ + .word instruction_trap /* 15 */ + .word instruction_trap /* 16 */ + .word instruction_trap /* 17 */ + .word instruction_trap /* 18 */ + .word instruction_trap /* 19 */ + .word instruction_trap /* 20 */ + .word instruction_trap /* 21 */ + .word instruction_trap /* 22 */ + .word instruction_trap /* 23 */ + .word instruction_trap /* 24 */ + .word instruction_trap /* 25 */ + .word instruction_trap /* 26 */ + .word instruction_trap /* 27 */ + .word instruction_trap /* 28 */ + .word instruction_trap /* 29 */ +#ifdef CONFIG_KGDB + .word handle_kgdb_breakpoint /* 30 KGDB breakpoint */ +#else + .word instruction_trap // 30 +#endif + .word handle_breakpoint // 31 + +.text +.set noat +.set nobreak + +ENTRY(inthandler) + SAVE_ALL + /* Clear EH bit before we get a new excpetion in the kernel + * and after we have saved it to the exception frame. This is done + * wheter it's trap, tlb-miss or interrupt. If we don't do this + * estatus is not updated the next exception. + */ + rdctl r24, status + movi r9, %lo(~STATUS_EH) + and r24, r24, r9 + wrctl status, r24 + + /* Read cause and vector and branch to the associated handler */ + mov r4, sp + rdctl r5, exception + movia r9, exception_table + add r24, r9, r5 + ldw r24, 0(r24) + jmp r24 + + +/*********************************************************************** + * Handle traps + *********************************************************************** + */ +ENTRY(handle_trap) + ldw r24, -4(ea) /* instruction that caused the exception */ + srli r24, r24, 4 + andi r24, r24, 0x7c + movia r9,trap_table + add r24, r24, r9 + ldw r24, 0(r24) + jmp r24 + + +/*********************************************************************** + * Handle system calls + *********************************************************************** + */ +ENTRY(handle_system_call) + /* Enable interrupts */ + rdctl r10, status + ori r10, r10, STATUS_PIE + wrctl status, r10 + + /* Reload registers destroyed by common code. */ + ldw r4, PT_R4(sp) + ldw r5, PT_R5(sp) + + /* Check that the requested system call is within limits */ + movui r1, NR_syscalls + bgeu r2, r1, ret_invsyscall + slli r1, r2, 2 + movhi r11, %hiadj(sys_call_table) + add r1, r1, r11 + ldw r1, %lo(sys_call_table)(r1) + beq r1, r0, ret_invsyscall + + /* Get thread info pointer */ + movi r11, %lo(0xfffff000) + and r11, sp, r11 + ldw r11, TI_FLAGS(r11) + + /* If someone is ptrace:ing us, take the long way. */ + BTBNZ r11, r11, TIF_SYSCALL_TRACE, traced_system_call + + /* Execute the system call */ + callr r1 + + /* If the syscall returns a negative result: + * Set r7 to 1 to indicate error, + * Negate r2 to get a positive error code + * If the syscall returns zero or a positive value: + * Set r7 to 0. + * The sigreturn system calls will skip the code below by + * adding to register ra. To avoid destroying registers + * + * FIXME: We probably need an orig_r7 + */ +translate_rc_and_ret: + movi r1, 0 + bge r2, zero, 3f + sub r2, zero, r2 + movi r1, 1 +3: + stw r2, PT_R2(sp) + stw r1, PT_R7(sp) +end_translate_rc_and_ret: + +ret_from_exception: + ldw r1, PT_ESTATUS(sp) + /* if so, skip resched, signals */ + TSTBNZ r1, r1, ESTATUS_EU, Luser_return + +restore_all: + rdctl r10, status /* disable intrs */ + andi r10, r10, %lo(~STATUS_PIE) + wrctl status, r10 + RESTORE_ALL + eret + + /* If the syscall number was invalid return ENOSYS */ +ret_invsyscall: + movi r2, -ENOSYS + br translate_rc_and_ret + + /* This implements the same as above, except it calls + * syscall_trace before and after the syscall in order + * for utilities like strace and gdb to work. + */ +traced_system_call: + SAVE_SWITCH_STACK + call syscall_trace + RESTORE_SWITCH_STACK + + /* r2 and r7-r9 might be destroyed by syscall_trace and we need to + * restore them before calling our syscall. + */ + ldw r2, PT_R2(sp) + ldw r4, PT_R4(sp) + ldw r5, PT_R5(sp) + ldw r6, PT_R6(sp) + ldw r7, PT_R7(sp) + ldw r8, PT_R8(sp) + ldw r9, PT_R9(sp) + + /* Fetch the syscall function, we don't need to check the boundaries + * since this is already done. + */ + slli r1, r2, 2 + movhi r11,%hiadj(sys_call_table) + add r1, r1, r11 + ldw r1, %lo(sys_call_table)(r1) + + callr r1 + + /* If the syscall returns a negative result: + * Set r7 to 1 to indicate error, + * Negate r2 to get a positive error code + * If the syscall returns zero or a positive value: + * Set r7 to 0. + * The sigreturn system calls will skip the code below by + * adding to register ra. To avoid destroying registers + * + * FIXME: We probably need an orig_r7 + */ +translate_rc_and_ret2: + movi r1, 0 + bge r2, zero, 4f + sub r2, zero, r2 + movi r1, 1 +4: + stw r2, PT_R2(sp) + stw r1, PT_R7(sp) +end_translate_rc_and_ret2: + SAVE_SWITCH_STACK + call syscall_trace + RESTORE_SWITCH_STACK + br ret_from_exception + +Luser_return: + GET_THREAD_INFO r11 /* get thread_info pointer */ + ldw r10, TI_FLAGS(r11) /* get thread_info->flags */ + ANDI32 r11, r10, _TIF_WORK_MASK + beq r11, r0, restore_all /* Nothing to do */ + BTBZ r1, r10, TIF_NEED_RESCHED, Lsignal_return + + /* Reschedule work */ + call schedule + br ret_from_exception + +Lsignal_return: + ANDI32 r1, r10, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME + beq r1, r0, restore_all + mov r4, sp /* pt_regs */ + SAVE_SWITCH_STACK + mov r5, r0 /* oldset = 0 */ + movi r6, 1 /* in_syscall = 1 */ + call do_notify_resume + RESTORE_SWITCH_STACK + br restore_all + + + +/*********************************************************************** + * Handle external interrupts. + *********************************************************************** + */ +/* + * This is the generic interrupt handler (for all hardware interrupt + * sources). It figures out the vector number and calls the appropriate + * interrupt service routine directly. + */ +external_interrupt: + rdctl r12, ipending + rdctl r9, ienable + and r12, r12, r9 + /* skip if no interrupt is pending */ + beq r12, r0, ret_from_interrupt + + movi r24, -1 + stw r24, PT_ORIG_R2(sp) + + /* + * Process an external hardware interrupt. + */ + + addi ea, ea, -4 /* re-issue the interrupted instruction */ + stw ea, PT_EA(sp) +2: movi r4, %lo(-1) /* Start from bit position 0, + highest priority */ + /* This is the IRQ # for handler call */ +1: andi r10, r12, 1 /* Isolate bit we are interested in */ + srli r12, r12, 1 /* shift count is costly without hardware + multiplier */ + addi r4, r4, 1 + beq r10, r0, 1b + mov r5, sp /* Setup pt_regs pointer for handler call */ + call do_IRQ + rdctl r12, ipending /* check again if irq still pending */ + rdctl r9, ienable /* Isolate possible interrupts */ + and r12, r12, r9 + bne r12, r0, 2b + /* br ret_from_interrupt */ /* fall throught to ret_from_interrupt */ + +ENTRY(ret_from_interrupt) + ldw r1, PT_ESTATUS(sp) /* check if returning to kernel */ + TSTBNZ r1, r1, ESTATUS_EU, Luser_return + +#ifdef CONFIG_PREEMPT + GET_THREAD_INFO r1 + ldw r4, TI_PREEMPT_COUNT(r1) + bne r4, r0, restore_all + +need_resched: + ldw r4, TI_FLAGS(r1) /* ? Need resched set */ + BTBZ r10, r4, TIF_NEED_RESCHED, restore_all + ldw r4, PT_ESTATUS(sp) /* ? Interrupts off */ + andi r10, r4, ESTATUS_EPIE + beq r10, r0, restore_all + movia r4, PREEMPT_ACTIVE + stw r4, TI_PREEMPT_COUNT(r1) + rdctl r10, status /* enable intrs again */ + ori r10, r10 ,STATUS_PIE + wrctl status, r10 + PUSH r1 + call schedule + POP r1 + mov r4, r0 + stw r4, TI_PREEMPT_COUNT(r1) + rdctl r10, status /* disable intrs */ + andi r10, r10, %lo(~STATUS_PIE) + wrctl status, r10 + br need_resched +#else + br restore_all +#endif + +/*********************************************************************** + * Syscalls implemented in assembly + *********************************************************************** + */ + +ENTRY(sys_nios2cmpxchg) + + /* r4 pointer to exchange variable */ + /* r5 old value */ + /* r6 new value */ + /* r8 - old interrupt status (assert this to enabled?) */ + /* r9 - temp */ + /* Disable interrupts (keep old status in r8) */ + rdctl r8, status + andi r9, r8, %lo(~STATUS_PIE) + wrctl status, r9 + + /* Make sure we skip the r2/r7 translation code when we return. */ + addi ra, ra, (end_translate_rc_and_ret - translate_rc_and_ret) + +ldw1: ldw r2, 0(r4) + bne r2, r5, .L6 + + /* We had a match, store the new value */ +stw1: stw r6, 0(r4) +.L6: + /* Reenable interrupts */ + wrctl status, r8 + + /* Store return value */ + stw r2, PT_R2(sp) + /* Indicate everything is ok */ + movi r2, 0 + stw r2, PT_R7(sp) + ret +fault: + movi r2, EFAULT + stw r2, PT_R2(sp) + movi r2, 1 + stw r2, PT_R7(sp) + wrctl status, r8 + ret + + /* setup the exception table */ + .section __ex_table, "a" + .word ldw1, fault + .word stw1, fault + .previous + + + +/*********************************************************************** + * A few syscall wrappers + *********************************************************************** + */ + +ENTRY(sys_fork) + mov r4, sp + SAVE_SWITCH_STACK + call nios2_fork + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_vfork) + mov r4, sp + SAVE_SWITCH_STACK + call nios2_vfork + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_execve) + mov r4, sp + SAVE_SWITCH_STACK + call nios2_execve + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_clone) + mov r4, sp + SAVE_SWITCH_STACK + call nios2_clone + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_sigsuspend) + mov r4, sp + SAVE_SWITCH_STACK + call do_sigsuspend + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_rt_sigsuspend) + mov r4, sp + SAVE_SWITCH_STACK + call do_rt_sigsuspend + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_sigreturn) + mov r4, sp + SAVE_SWITCH_STACK + call do_sigreturn + RESTORE_SWITCH_STACK + addi ra, ra, (end_translate_rc_and_ret - translate_rc_and_ret) + ret + +ENTRY(sys_sigaltstack) + ldw r4, PT_R4(sp) + ldw r5, PT_R5(sp) + ldw r6, PT_SP(sp) + SAVE_SWITCH_STACK + call do_sigaltstack + RESTORE_SWITCH_STACK + ret + +ENTRY(sys_rt_sigreturn) + SAVE_SWITCH_STACK + mov r4, sp + call do_rt_sigreturn + RESTORE_SWITCH_STACK + addi ra, ra, (end_translate_rc_and_ret - translate_rc_and_ret) + ret + +/*********************************************************************** + * A few other wrappers and stubs + *********************************************************************** + */ +protection_exception_pte: + rdctl r6, pteaddr + slli r6, r6, 10 + call do_page_fault + br ret_from_exception + +protection_exception_ba: + rdctl r6, badaddr + call do_page_fault + br ret_from_exception + +protection_exception_instr: + call handle_supervisor_instr + br ret_from_exception + +handle_breakpoint: + call breakpoint_c + br ret_from_exception + +#ifdef CONFIG_ALIGNMENT_TRAP +handle_unaligned: + SAVE_SWITCH_STACK + call handle_unaligned_c + RESTORE_SWITCH_STACK + br ret_from_exception +#else +handle_unaligned: + call handle_unaligned_c + br ret_from_exception +#endif + +handle_illegal: + call handle_illegal_c + br ret_from_exception + +handle_diverror: + call handle_diverror_c + br ret_from_exception + +#ifdef CONFIG_KGDB +handle_kgdb_breakpoint: + call kgdb_breakpoint_c + br ret_from_exception +#endif + +/* + * Beware - when entering resume, prev (the current task) is + * in r4, next (the new task) is in r5, don't change these + * registers. + */ +ENTRY(resume) + + rdctl r7, status /* save thread status reg */ + stw r7, TASK_THREAD + THREAD_KPSR(r4) + + andi r7, r7, %lo(~STATUS_PIE) /* disable interrupts */ + wrctl status, r7 + + SAVE_SWITCH_STACK + stw sp, TASK_THREAD + THREAD_KSP(r4)/* save kernel stack pointer */ + ldw sp, TASK_THREAD + THREAD_KSP(r5)/* restore new thread stack */ + movia r24, _current_thread /* save thread */ + GET_THREAD_INFO r1 + stw r1, 0(r24) + RESTORE_SWITCH_STACK + + ldw r7, TASK_THREAD + THREAD_KPSR(r5)/* restore thread status reg */ + wrctl status, r7 + ret + +ENTRY(ret_from_fork) + call schedule_tail + br ret_from_exception + + diff --git a/arch/nios2/kernel/head.S b/arch/nios2/kernel/head.S new file mode 100644 index 0000000000000..fe53e75528002 --- /dev/null +++ b/arch/nios2/kernel/head.S @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * Copyright (C) 2004 Microtronix Datacom Ltd + * Copyright (C) 2001 Vic Phillips, Microtronix Datacom Ltd. + * + * Based on head.S for Altera's Excalibur development board with nios processor + * + * Based on the following from the Excalibur sdk distribution: + * NA_MemoryMap.s, NR_JumpToStart.s, NR_Setup.s, NR_CWPManager.s + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MMU +/* + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +.data +.global empty_zero_page +.align 12 +empty_zero_page: + .space PAGE_SIZE +#endif /* CONFIG_MMU */ + +/* + * This global variable is used as an extension to the nios' + * STATUS register to emulate a user/supervisor mode. + */ + .data + .align 2 + .set noat +#ifndef CONFIG_MMU + .global status_extension +status_extension: + .long 0 +#endif /* CONFIG_MMU */ + + .global _current_thread +_current_thread: + .long 0 +/* + * Input(s): passed from u-boot + * r4 - Optional pointer to a board information structure. + * r5 - Optional pointer to the physical starting address of the init RAM + * disk. + * r6 - Optional pointer to the physical ending address of the init RAM + * disk. + * r7 - Optional pointer to the physical starting address of any kernel + * command-line parameters. + */ + +/* + * First executable code - detected and jumped to by the ROM bootstrap + * if the code resides in flash (looks for "Nios" at offset 0x0c from + * the potential executable image). + */ + __HEAD +ENTRY(_start) + wrctl status, r0 /* Disable interrupts */ + + /* Flush all cache lines within the instruction cache */ + movia r1, NIOS2_ICACHE_SIZE + movui r2, NIOS2_ICACHE_LINE_SIZE + +text_flush: + flushi r1 + sub r1, r1, r2 + bgt r1, r0, text_flush + br 1f + + /* + * This is the default location for the exception handler. Code in jump + * to our handler + */ +ENTRY(exception_handler_hook) + movia r24, inthandler + jmp r24 + +#ifdef CONFIG_MMU + +ENTRY(fast_handler) + nextpc et +helper: + stw r3, r3save - helper(et) + + rdctl r3 , pteaddr + srli r3, r3, 12 + slli r3, r3, 2 + movia et, pgd_current + + ldw et, 0(et) + add r3, et, r3 + ldw et, 0(r3) + + rdctl r3, pteaddr + andi r3, r3, 0xfff + add et, r3, et + ldw et, 0(et) + wrctl tlbacc, et + nextpc et +helper2: + ldw r3, r3save - helper2(et) + subi ea, ea, 4 + eret +r3save: + .word 0x0 +ENTRY(fast_handler_end) + +#endif /* CONFIG_MMU */ + +1: + /* + * After flushing the instruction cache, we must flush the data + * cache. + */ + movia r1, NIOS2_DCACHE_SIZE + movui r2, NIOS2_DCACHE_LINE_SIZE + +data_flush: + flushd 0(r1) + sub r1, r1, r2 + bgt r1, r0, data_flush + +#ifdef CONFIG_MMU + nextpc r1 /* Find out where we are */ +chkadr: + movia r2, chkadr + beq r1, r2,finish_move /* We are running in RAM done */ + addi r1, r1,(_start - chkadr) /* Source */ + movia r2, _start /* Destination */ + movia r3, __bss_start /* End of copy */ + +loop_move: /* r1: src, r2: dest, r3: last dest */ + ldw r8, 0(r1) /* load a word from [r1] */ + stw r8, 0(r2) /* store a word to dest [r2] */ + flushd 0(r2) /* Flush cache for safety */ + addi r1, r1, 4 /* inc the src addr */ + addi r2, r2, 4 /* inc the dest addr */ + blt r2, r3, loop_move + + movia r1, finish_move /* VMA(_start)->l1 */ + jmp r1 /* jmp to _start */ + +finish_move: +#endif /* CONFIG_MMU */ + + /* Mask off all possible interrupts */ + wrctl ienable, r0 + + /* Clear .bss */ + movia r2, __bss_start + movia r1, __bss_stop +1: + stb r0, 0(r2) + addi r2, r2, 1 + bne r1, r2, 1b + +#ifndef CONFIG_MMU + movia r1, status_extension /* get the STATUS extension address */ + movi r2, PS_S /* set initial mode = supervisor */ + stw r2, 0(r1) +#endif /* CONFIG_MMU */ + + movia r1, init_thread_union /* set stack at top of the task union */ + addi sp, r1, THREAD_SIZE + movia r2, _current_thread /* Remember current thread */ + stw r1, 0(r2) + + movia r1, nios2_boot_init /* save args r4-r7 passed from u-boot */ + callr r1 + + movia r1, start_kernel /* call start_kernel as a subroutine */ + callr r1 + + /* If we return from start_kernel, break to the oci debugger and + * buggered we are. + */ + break + + /* End of startup code */ +.set at diff --git a/arch/nios2/kernel/insnemu.S b/arch/nios2/kernel/insnemu.S new file mode 100644 index 0000000000000..9cfed86df8fa5 --- /dev/null +++ b/arch/nios2/kernel/insnemu.S @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2003-2013 Altera Corporation + * All rights reserved. + * + * 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 . + */ + + +#include +#include + +.set noat +.set nobreak + +/* +* Explicitly allow the use of r1 (the assembler temporary register) +* within this code. This register is normally reserved for the use of +* the compiler. +*/ + +ENTRY(instruction_trap) + ldw r1, PT_R1(sp) // Restore registers + ldw r2, PT_R2(sp) + ldw r3, PT_R3(sp) + ldw r4, PT_R4(sp) + ldw r5, PT_R5(sp) + ldw r6, PT_R6(sp) + ldw r7, PT_R7(sp) + ldw r8, PT_R8(sp) + ldw r9, PT_R9(sp) + ldw r10, PT_R10(sp) + ldw r11, PT_R11(sp) + ldw r12, PT_R12(sp) + ldw r13, PT_R13(sp) + ldw r14, PT_R14(sp) + ldw r15, PT_R15(sp) + ldw ra, PT_RA(sp) + ldw fp, PT_FP(sp) + ldw gp, PT_GP(sp) + ldw et, PT_ESTATUS(sp) + wrctl estatus, et + ldw ea, PT_EA(sp) + ldw et, PT_SP(sp) /* backup sp in et */ + + addi sp, sp, PT_REGS_SIZE + + /* INSTRUCTION EMULATION + * --------------------- + * + * Nios II processors generate exceptions for unimplemented instructions. + * The routines below emulate these instructions. Depending on the + * processor core, the only instructions that might need to be emulated + * are div, divu, mul, muli, mulxss, mulxsu, and mulxuu. + * + * The emulations match the instructions, except for the following + * limitations: + * + * 1) The emulation routines do not emulate the use of the exception + * temporary register (et) as a source operand because the exception + * handler already has modified it. + * + * 2) The routines do not emulate the use of the stack pointer (sp) or + * the exception return address register (ea) as a destination because + * modifying these registers crashes the exception handler or the + * interrupted routine. + * + * Detailed Design + * --------------- + * + * The emulation routines expect the contents of integer registers r0-r31 + * to be on the stack at addresses sp, 4(sp), 8(sp), ... 124(sp). The + * routines retrieve source operands from the stack and modify the + * destination register's value on the stack prior to the end of the + * exception handler. Then all registers except the destination register + * are restored to their previous values. + * + * The instruction that causes the exception is found at address -4(ea). + * The instruction's OP and OPX fields identify the operation to be + * performed. + * + * One instruction, muli, is an I-type instruction that is identified by + * an OP field of 0x24. + * + * muli AAAAA,BBBBB,IIIIIIIIIIIIIIII,-0x24- + * 27 22 6 0 <-- LSB of field + * + * The remaining emulated instructions are R-type and have an OP field + * of 0x3a. Their OPX fields identify them. + * + * R-type AAAAA,BBBBB,CCCCC,XXXXXX,NNNNN,-0x3a- + * 27 22 17 11 6 0 <-- LSB of field + * + * + * Opcode Encoding. muli is identified by its OP value. Then OPX & 0x02 + * is used to differentiate between the division opcodes and the + * remaining multiplication opcodes. + * + * Instruction OP OPX OPX & 0x02 + * ----------- ---- ---- ---------- + * muli 0x24 + * divu 0x3a 0x24 0 + * div 0x3a 0x25 0 + * mul 0x3a 0x27 != 0 + * mulxuu 0x3a 0x07 != 0 + * mulxsu 0x3a 0x17 != 0 + * mulxss 0x3a 0x1f != 0 + */ + + + /* + * Save everything on the stack to make it easy for the emulation + * routines to retrieve the source register operands. + */ + + addi sp, sp, -128 + stw zero, 0(sp) /* Save zero on stack to avoid special case for r0. */ + stw r1, 4(sp) + stw r2, 8(sp) + stw r3, 12(sp) + stw r4, 16(sp) + stw r5, 20(sp) + stw r6, 24(sp) + stw r7, 28(sp) + stw r8, 32(sp) + stw r9, 36(sp) + stw r10, 40(sp) + stw r11, 44(sp) + stw r12, 48(sp) + stw r13, 52(sp) + stw r14, 56(sp) + stw r15, 60(sp) + stw r16, 64(sp) + stw r17, 68(sp) + stw r18, 72(sp) + stw r19, 76(sp) + stw r20, 80(sp) + stw r21, 84(sp) + stw r22, 88(sp) + stw r23, 92(sp) + /* Don't bother to save et. It's already been changed. */ + rdctl r5, estatus + stw r5, 100(sp) + + stw gp, 104(sp) + stw et, 108(sp) /* et containts previous sp value. */ + stw fp, 112(sp) + stw ea, 116(sp) + stw ra, 120(sp) + + + /* + * Split the instruction into its fields. We need 4*A, 4*B, and 4*C as + * offsets to the stack pointer for access to the stored register values. + */ + ldw r2,-4(ea) /* r2 = AAAAA,BBBBB,IIIIIIIIIIIIIIII,PPPPPP */ + roli r3, r2, 7 /* r3 = BBB,IIIIIIIIIIIIIIII,PPPPPP,AAAAA,BB */ + roli r4, r3, 3 /* r4 = IIIIIIIIIIIIIIII,PPPPPP,AAAAA,BBBBB */ + roli r5, r4, 2 /* r5 = IIIIIIIIIIIIII,PPPPPP,AAAAA,BBBBB,II */ + srai r4, r4, 16 /* r4 = (sign-extended) IMM16 */ + roli r6, r5, 5 /* r6 = XXXX,NNNNN,PPPPPP,AAAAA,BBBBB,CCCCC,XX */ + andi r2, r2, 0x3f /* r2 = 00000000000000000000000000,PPPPPP */ + andi r3, r3, 0x7c /* r3 = 0000000000000000000000000,AAAAA,00 */ + andi r5, r5, 0x7c /* r5 = 0000000000000000000000000,BBBBB,00 */ + andi r6, r6, 0x7c /* r6 = 0000000000000000000000000,CCCCC,00 */ + + /* Now + * r2 = OP + * r3 = 4*A + * r4 = IMM16 (sign extended) + * r5 = 4*B + * r6 = 4*C + */ + + /* + * Get the operands. + * + * It is necessary to check for muli because it uses an I-type + * instruction format, while the other instructions are have an R-type + * format. + * + * Prepare for either multiplication or division loop. + * They both loop 32 times. + */ + movi r14, 32 + + add r3, r3, sp /* r3 = address of A-operand. */ + ldw r3, 0(r3) /* r3 = A-operand. */ + movi r7, 0x24 /* muli opcode (I-type instruction format) */ + beq r2, r7, mul_immed /* muli doesn't use the B register as a source */ + + add r5, r5, sp /* r5 = address of B-operand. */ + ldw r5, 0(r5) /* r5 = B-operand. */ + /* r4 = SSSSSSSSSSSSSSSS,-----IMM16------ */ + /* IMM16 not needed, align OPX portion */ + /* r4 = SSSSSSSSSSSSSSSS,CCCCC,-OPX--,00000 */ + srli r4, r4, 5 /* r4 = 00000,SSSSSSSSSSSSSSSS,CCCCC,-OPX-- */ + andi r4, r4, 0x3f /* r4 = 00000000000000000000000000,-OPX-- */ + + /* Now + * r2 = OP + * r3 = src1 + * r5 = src2 + * r4 = OPX (no longer can be muli) + * r6 = 4*C + */ + + + /* + * Multiply or Divide? + */ + andi r7, r4, 0x02 /* For R-type multiply instructions, + OPX & 0x02 != 0 */ + bne r7, zero, multiply + + + /* DIVISION + * + * Divide an unsigned dividend by an unsigned divisor using + * a shift-and-subtract algorithm. The example below shows + * 43 div 7 = 6 for 8-bit integers. This classic algorithm uses a + * single register to store both the dividend and the quotient, + * allowing both values to be shifted with a single instruction. + * + * remainder dividend:quotient + * --------- ----------------- + * initialize 00000000 00101011: + * shift 00000000 0101011:_ + * remainder >= divisor? no 00000000 0101011:0 + * shift 00000000 101011:0_ + * remainder >= divisor? no 00000000 101011:00 + * shift 00000001 01011:00_ + * remainder >= divisor? no 00000001 01011:000 + * shift 00000010 1011:000_ + * remainder >= divisor? no 00000010 1011:0000 + * shift 00000101 011:0000_ + * remainder >= divisor? no 00000101 011:00000 + * shift 00001010 11:00000_ + * remainder >= divisor? yes 00001010 11:000001 + * remainder -= divisor - 00000111 + * ---------- + * 00000011 11:000001 + * shift 00000111 1:000001_ + * remainder >= divisor? yes 00000111 1:0000011 + * remainder -= divisor - 00000111 + * ---------- + * 00000000 1:0000011 + * shift 00000001 :0000011_ + * remainder >= divisor? no 00000001 :00000110 + * + * The quotient is 00000110. + */ + +divide: + /* + * Prepare for division by assuming the result + * is unsigned, and storing its "sign" as 0. + */ + movi r17, 0 + + + /* Which division opcode? */ + xori r7, r4, 0x25 /* OPX of div */ + bne r7, zero, unsigned_division + + + /* + * OPX is div. Determine and store the sign of the quotient. + * Then take the absolute value of both operands. + */ + xor r17, r3, r5 /* MSB contains sign of quotient */ + bge r3,zero,dividend_is_nonnegative + sub r3, zero, r3 /* -r3 */ +dividend_is_nonnegative: + bge r5, zero, divisor_is_nonnegative + sub r5, zero, r5 /* -r5 */ +divisor_is_nonnegative: + + +unsigned_division: + /* Initialize the unsigned-division loop. */ + movi r13, 0 /* remainder = 0 */ + + /* Now + * r3 = dividend : quotient + * r4 = 0x25 for div, 0x24 for divu + * r5 = divisor + * r13 = remainder + * r14 = loop counter (already initialized to 32) + * r17 = MSB contains sign of quotient + */ + + + /* + * for (count = 32; count > 0; --count) + * { + */ +divide_loop: + + /* + * Division: + * + * (remainder:dividend:quotient) <<= 1; + */ + slli r13, r13, 1 + cmplt r7, r3, zero /* r7 = MSB of r3 */ + or r13, r13, r7 + slli r3, r3, 1 + + + /* + * if (remainder >= divisor) + * { + * set LSB of quotient + * remainder -= divisor; + * } + */ + bltu r13, r5, div_skip + ori r3, r3, 1 + sub r13, r13, r5 +div_skip: + + /* + * } + */ + subi r14, r14, 1 + bne r14, zero, divide_loop + + + /* Now + * r3 = quotient + * r4 = 0x25 for div, 0x24 for divu + * r6 = 4*C + * r17 = MSB contains sign of quotient + */ + + + /* + * Conditionally negate signed quotient. If quotient is unsigned, + * the sign already is initialized to 0. + */ + bge r17, zero, quotient_is_nonnegative + sub r3, zero, r3 /* -r3 */ + quotient_is_nonnegative: + + + /* + * Final quotient is in r3. + */ + add r6, r6, sp + stw r3, 0(r6) /* write quotient to stack */ + br restore_registers + + + + + /* MULTIPLICATION + * + * A "product" is the number that one gets by summing a "multiplicand" + * several times. The "multiplier" specifies the number of copies of the + * multiplicand that are summed. + * + * Actual multiplication algorithms don't use repeated addition, however. + * Shift-and-add algorithms get the same answer as repeated addition, and + * they are faster. To compute the lower half of a product (pppp below) + * one shifts the product left before adding in each of the partial + * products (a * mmmm) through (d * mmmm). + * + * To compute the upper half of a product (PPPP below), one adds in the + * partial products (d * mmmm) through (a * mmmm), each time following + * the add by a right shift of the product. + * + * mmmm + * * abcd + * ------ + * #### = d * mmmm + * #### = c * mmmm + * #### = b * mmmm + * #### = a * mmmm + * -------- + * PPPPpppp + * + * The example above shows 4 partial products. Computing actual Nios II + * products requires 32 partials. + * + * It is possible to compute the result of mulxsu from the result of + * mulxuu because the only difference between the results of these two + * opcodes is the value of the partial product associated with the sign + * bit of rA. + * + * mulxsu = mulxuu - (rA < 0) ? rB : 0; + * + * It is possible to compute the result of mulxss from the result of + * mulxsu because the only difference between the results of these two + * opcodes is the value of the partial product associated with the sign + * bit of rB. + * + * mulxss = mulxsu - (rB < 0) ? rA : 0; + * + */ + +mul_immed: + /* Opcode is muli. Change it into mul for remainder of algorithm. */ + mov r6, r5 /* Field B is dest register, not field C. */ + mov r5, r4 /* Field IMM16 is src2, not field B. */ + movi r4, 0x27 /* OPX of mul is 0x27 */ + +multiply: + /* Initialize the multiplication loop. */ + movi r9, 0 /* mul_product = 0 */ + movi r10, 0 /* mulxuu_product = 0 */ + mov r11, r5 /* save original multiplier for mulxsu and mulxss */ + mov r12, r5 /* mulxuu_multiplier (will be shifted) */ + movi r16, 1 /* used to create "rori B,A,1" from "ror B,A,r16" */ + + /* Now + * r3 = multiplicand + * r5 = mul_multiplier + * r6 = 4 * dest_register (used later as offset to sp) + * r7 = temp + * r9 = mul_product + * r10 = mulxuu_product + * r11 = original multiplier + * r12 = mulxuu_multiplier + * r14 = loop counter (already initialized) + * r16 = 1 + */ + + + /* + * for (count = 32; count > 0; --count) + * { + */ +multiply_loop: + + /* + * mul_product <<= 1; + * lsb = multiplier & 1; + */ + slli r9, r9, 1 + andi r7, r12, 1 + + /* + * if (lsb == 1) + * { + * mulxuu_product += multiplicand; + * } + */ + beq r7, zero, mulx_skip + add r10, r10, r3 + cmpltu r7, r10, r3 /* Save the carry from the MSB of mulxuu_product. */ + ror r7, r7, r16 /* r7 = 0x80000000 on carry, or else 0x00000000 */ +mulx_skip: + + /* + * if (MSB of mul_multiplier == 1) + * { + * mul_product += multiplicand; + * } + */ + bge r5, zero, mul_skip + add r9, r9, r3 +mul_skip: + + /* + * mulxuu_product >>= 1; logical shift + * mul_multiplier <<= 1; done with MSB + * mulx_multiplier >>= 1; done with LSB + */ + srli r10, r10, 1 + or r10, r10, r7 /* OR in the saved carry bit. */ + slli r5, r5, 1 + srli r12, r12, 1 + + + /* + * } + */ + subi r14, r14, 1 + bne r14, zero, multiply_loop + + + /* + * Multiply emulation loop done. + */ + + /* Now + * r3 = multiplicand + * r4 = OPX + * r6 = 4 * dest_register (used later as offset to sp) + * r7 = temp + * r9 = mul_product + * r10 = mulxuu_product + * r11 = original multiplier + */ + + + /* Calculate address for result from 4 * dest_register */ + add r6, r6, sp + + + /* + * Select/compute the result based on OPX. + */ + + + /* OPX == mul? Then store. */ + xori r7, r4, 0x27 + beq r7, zero, store_product + + /* It's one of the mulx.. opcodes. Move over the result. */ + mov r9, r10 + + /* OPX == mulxuu? Then store. */ + xori r7, r4, 0x07 + beq r7, zero, store_product + + /* Compute mulxsu + * + * mulxsu = mulxuu - (rA < 0) ? rB : 0; + */ + bge r3, zero, mulxsu_skip + sub r9, r9, r11 +mulxsu_skip: + + /* OPX == mulxsu? Then store. */ + xori r7, r4, 0x17 + beq r7, zero, store_product + + /* Compute mulxss + * + * mulxss = mulxsu - (rB < 0) ? rA : 0; + */ + bge r11,zero,mulxss_skip + sub r9, r9, r3 +mulxss_skip: + /* At this point, assume that OPX is mulxss, so store*/ + + +store_product: + stw r9, 0(r6) + + +restore_registers: + /* No need to restore r0. */ + ldw r5, 100(sp) + wrctl estatus, r5 + + ldw r1, 4(sp) + ldw r2, 8(sp) + ldw r3, 12(sp) + ldw r4, 16(sp) + ldw r5, 20(sp) + ldw r6, 24(sp) + ldw r7, 28(sp) + ldw r8, 32(sp) + ldw r9, 36(sp) + ldw r10, 40(sp) + ldw r11, 44(sp) + ldw r12, 48(sp) + ldw r13, 52(sp) + ldw r14, 56(sp) + ldw r15, 60(sp) + ldw r16, 64(sp) + ldw r17, 68(sp) + ldw r18, 72(sp) + ldw r19, 76(sp) + ldw r20, 80(sp) + ldw r21, 84(sp) + ldw r22, 88(sp) + ldw r23, 92(sp) + /* Does not need to restore et */ + ldw gp, 104(sp) + + ldw fp, 112(sp) + ldw ea, 116(sp) + ldw ra, 120(sp) + ldw sp, 108(sp) /* last restore sp */ + eret + +.set at +.set break + diff --git a/arch/nios2/kernel/irq.c b/arch/nios2/kernel/irq.c new file mode 100644 index 0000000000000..059dbe0b0d9c5 --- /dev/null +++ b/arch/nios2/kernel/irq.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2008 Thomas Chou + * + * based on irq.c from m68k which is: + * + * Copyright (C) 2007 Greg Ungerer + * + * 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 . + * + */ + +#include +#include +#include + +asmlinkage void do_IRQ(int irq, struct pt_regs *regs) +{ + struct pt_regs *oldregs = set_irq_regs(regs); + + irq_enter(); + generic_handle_irq(irq); + irq_exit(); + + set_irq_regs(oldregs); +} + +static void chip_unmask(struct irq_data *d) +{ + unsigned ien; + ien = RDCTL(CTL_IENABLE); + ien |= (1 << d->irq); + WRCTL(CTL_IENABLE, ien); +} + +static void chip_mask(struct irq_data *d) +{ + unsigned ien; + ien = RDCTL(CTL_IENABLE); + ien &= ~(1 << d->irq); + WRCTL(CTL_IENABLE, ien); +} + +static struct irq_chip m_irq_chip = { + .name = "NIOS2-INTC", + .irq_unmask = chip_unmask, + .irq_mask = chip_mask, +}; + +static int irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw_irq_num) +{ + irq_set_chip_and_handler(virq, &m_irq_chip, handle_level_irq); + + return 0; +} + +static struct irq_domain_ops irq_ops = { + .map = irq_map, + .xlate = irq_domain_xlate_onecell, +}; + + +void __init init_IRQ(void) +{ + struct irq_domain *domain; + struct device_node *node; + + node = of_find_compatible_node(NULL, NULL, "ALTR,nios2-1.0"); + BUG_ON(!node); + + domain = irq_domain_add_linear(node, NIOS2_CPU_NR_IRQS, &irq_ops, NULL); + BUG_ON(!domain); + + irq_set_default_host(domain); + of_node_put(node); +} diff --git a/arch/nios2/kernel/kgdb.c b/arch/nios2/kernel/kgdb.c new file mode 100644 index 0000000000000..d26573c5914b9 --- /dev/null +++ b/arch/nios2/kernel/kgdb.c @@ -0,0 +1,143 @@ +/* + * Nios2 KGDB support + * + * Copyright (C) 2011 Tobias Klauser + * + * Based on the code posted by Kazuyasu on the Altera Forum at: + * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 + * + * 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 . + * + */ + +#include +#include +#include + +static int wait_for_remote_debugger; + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[GDB_R0] = 0; + gdb_regs[GDB_AT] = regs->r1; + gdb_regs[GDB_R2] = regs->r2; + gdb_regs[GDB_R3] = regs->r3; + gdb_regs[GDB_R4] = regs->r4; + gdb_regs[GDB_R5] = regs->r5; + gdb_regs[GDB_R6] = regs->r6; + gdb_regs[GDB_R7] = regs->r7; + gdb_regs[GDB_R8] = regs->r8; + gdb_regs[GDB_R9] = regs->r9; + gdb_regs[GDB_R10] = regs->r10; + gdb_regs[GDB_R11] = regs->r11; + gdb_regs[GDB_R12] = regs->r12; + gdb_regs[GDB_R13] = regs->r13; + gdb_regs[GDB_R14] = regs->r14; + gdb_regs[GDB_R15] = regs->r15; + + gdb_regs[GDB_RA] = regs->ra; + gdb_regs[GDB_FP] = regs->fp; + gdb_regs[GDB_SP] = regs->sp; + gdb_regs[GDB_GP] = regs->gp; + gdb_regs[GDB_ESTATUS] = regs->estatus; + gdb_regs[GDB_PC] = regs->ea; +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + regs->r1 = gdb_regs[GDB_AT]; + regs->r2 = gdb_regs[GDB_R2]; + regs->r3 = gdb_regs[GDB_R3]; + regs->r4 = gdb_regs[GDB_R4]; + regs->r5 = gdb_regs[GDB_R5]; + regs->r6 = gdb_regs[GDB_R6]; + regs->r7 = gdb_regs[GDB_R7]; + regs->r8 = gdb_regs[GDB_R8]; + regs->r9 = gdb_regs[GDB_R9]; + regs->r10 = gdb_regs[GDB_R10]; + regs->r11 = gdb_regs[GDB_R11]; + regs->r12 = gdb_regs[GDB_R12]; + regs->r13 = gdb_regs[GDB_R13]; + regs->r14 = gdb_regs[GDB_R14]; + regs->r15 = gdb_regs[GDB_R15]; + + regs->ra = gdb_regs[GDB_RA]; + regs->fp = gdb_regs[GDB_FP]; + regs->sp = gdb_regs[GDB_SP]; + regs->gp = gdb_regs[GDB_GP]; + regs->estatus = gdb_regs[GDB_ESTATUS]; + regs->ea = gdb_regs[GDB_PC]; +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + gdb_regs[GDB_SP] = p->thread.kregs->sp; + gdb_regs[GDB_PC] = p->thread.kregs->ea; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->ea = pc; +} + +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long addr; + + switch (remcom_in_buffer[0]) { + case 's': + case 'c': + /* handle the optional parameters */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + regs->ea = addr; + + return 0; + } + + return -1; /* this means that we do not want to exit from the handler */ +} + +asmlinkage void kgdb_breakpoint_c(struct pt_regs *regs) +{ + /* + * The breakpoint entry code has moved the PC on by 4 bytes, so we must + * move it back. This could be done on the host but we do it here + */ + if (!wait_for_remote_debugger) + regs->ea -= 4; + else /* pass the first trap 30 code */ + wait_for_remote_debugger = 0; + + kgdb_handle_exception(30, SIGTRAP, 0, regs); +} + +int kgdb_arch_init(void) +{ + wait_for_remote_debugger = 1; + return 0; +} + +void kgdb_arch_exit(void) +{ + /* Nothing to do */ +} + +struct kgdb_arch arch_kgdb_ops = { + /* Breakpoint instruction: trap 30 */ + .gdb_bpt_instr = { 0xba, 0x6f, 0x3b, 0x00 }, +}; diff --git a/arch/nios2/kernel/misaligned.c b/arch/nios2/kernel/misaligned.c new file mode 100644 index 0000000000000..ade28d2da4138 --- /dev/null +++ b/arch/nios2/kernel/misaligned.c @@ -0,0 +1,320 @@ +/* + * linux/arch/nios2/kernel/misaligned.c + * + * basic emulation for mis-aligned accesses on the NIOS II cpu + * modeled after the version for arm in arm/alignment.c + * + * Brad Parker + * Copyright (C) 2010 Ambient Corporation + * Copyright (c) 2010 Altera Corporation, San Jose, California, USA. + * Copyright (c) 2010 Arrow Electronics, Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* instructions we emulate */ +#define INST_LDHU 0x0b +#define INST_STH 0x0d +#define INST_LDH 0x0f +#define INST_STW 0x15 +#define INST_LDW 0x17 + +static unsigned long ma_user, ma_kern, ma_skipped, ma_half, ma_word; + +static unsigned int ma_usermode; +#define UM_WARN 0x01 +#define UM_FIXUP 0x02 +#define UM_SIGNAL 0x04 +#define KM_WARN 0x08 + +/* see arch/nios2/include/asm/ptrace.h */ +static u8 sys_stack_frame_reg_offset[] = { + /* struct pt_regs */ + 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 0, + /* struct switch_stack */ + 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static int reg_offsets[32]; + +static inline u32 get_reg_val(struct pt_regs *fp, int reg) +{ + u8 *p = ((u8 *)fp) + reg_offsets[reg]; + return *(u32 *)p; +} + +static inline void put_reg_val(struct pt_regs *fp, int reg, u32 val) +{ + u8 *p = ((u8 *)fp) + reg_offsets[reg]; + *(u32 *)p = val; +} + +/* + * (mis)alignment handler + */ +asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause) +{ + u32 isn, addr, val; + int in_kernel; + u8 a, b, d0, d1, d2, d3; + u16 imm16; + unsigned int fault; + + /* back up one instruction */ + fp->ea -= 4; + + if (fixup_exception(fp)) { + ma_skipped++; + return; + } + + in_kernel = !user_mode(fp); + + isn = *(unsigned long *)(fp->ea); + + fault = 0; + + /* do fixup if in kernel or mode turned on */ + if (in_kernel || (ma_usermode & UM_FIXUP)) { + /* decompose instruction */ + a = (isn >> 27) & 0x1f; + b = (isn >> 22) & 0x1f; + imm16 = (isn >> 6) & 0xffff; + addr = get_reg_val(fp, a) + imm16; + + /* do fixup to saved registers */ + switch (isn & 0x3f) { + case INST_LDHU: + fault |= __get_user(d0, (u8 *)(addr+0)); + fault |= __get_user(d1, (u8 *)(addr+1)); + val = (d1 << 8) | d0; + put_reg_val(fp, b, val); + ma_half++; + break; + case INST_STH: + val = get_reg_val(fp, b); + d1 = val >> 8; + d0 = val >> 0; + + pr_debug("sth: ra=%d (%08x) rb=%d (%08x), imm16 %04x addr %08x val %08x\n", + a, get_reg_val(fp, a), + b, get_reg_val(fp, b), + imm16, addr, val); + + if (in_kernel) { + *(u8 *)(addr+0) = d0; + *(u8 *)(addr+1) = d1; + } else { + fault |= __put_user(d0, (u8 *)(addr+0)); + fault |= __put_user(d1, (u8 *)(addr+1)); + } + ma_half++; + break; + case INST_LDH: + fault |= __get_user(d0, (u8 *)(addr+0)); + fault |= __get_user(d1, (u8 *)(addr+1)); + val = (short)((d1 << 8) | d0); + put_reg_val(fp, b, val); + ma_half++; + break; + case INST_STW: + val = get_reg_val(fp, b); + d3 = val >> 24; + d2 = val >> 16; + d1 = val >> 8; + d0 = val >> 0; + if (in_kernel) { + *(u8 *)(addr+0) = d0; + *(u8 *)(addr+1) = d1; + *(u8 *)(addr+2) = d2; + *(u8 *)(addr+3) = d3; + } else { + fault |= __put_user(d0, (u8 *)(addr+0)); + fault |= __put_user(d1, (u8 *)(addr+1)); + fault |= __put_user(d2, (u8 *)(addr+2)); + fault |= __put_user(d3, (u8 *)(addr+3)); + } + ma_word++; + break; + case INST_LDW: + fault |= __get_user(d0, (u8 *)(addr+0)); + fault |= __get_user(d1, (u8 *)(addr+1)); + fault |= __get_user(d2, (u8 *)(addr+2)); + fault |= __get_user(d3, (u8 *)(addr+3)); + val = (d3 << 24) | (d2 << 16) | (d1 << 8) | d0; + put_reg_val(fp, b, val); + ma_word++; + break; + } + } + + addr = RDCTL(CTL_BADADDR); + cause >>= 2; + + if (fault) { + if (in_kernel) { + pr_err("fault during kernel misaligned fixup @ %#lx; addr 0x%08lx; isn=0x%08x\n", + fp->ea, (long unsigned int)addr, + (unsigned int)isn); + } else { + pr_err("fault during user misaligned fixup @ %#lx; isn=%08x addr=0x%08x sp=0x%08lx pid=%d\n", + fp->ea, + (unsigned int)isn, addr, fp->sp, + current->pid); + + _exception(SIGSEGV, fp, SEGV_MAPERR, fp->ea); + return; + } + } + + /* + * kernel mode - + * note exception and skip bad instruction (return) + */ + if (in_kernel) { + ma_kern++; + fp->ea += 4; + + if (ma_usermode & KM_WARN) { + pr_err("kernel unaligned access @ %#lx; BADADDR 0x%08lx; cause=%d, isn=0x%08lx\n", + fp->ea, + (long unsigned int)addr, cause, + (long unsigned int)isn); + /* show_regs(fp); */ + } + + return; + } + + ma_user++; + + /* + * user mode - + * possibly warn, + * possibly send SIGBUS signal to process + */ + if (ma_usermode & UM_WARN) { + pr_err("user unaligned access @ %#lx; isn=0x%08lx ea=0x%08lx ra=0x%08lx sp=0x%08lx\n", + (unsigned long)addr, (unsigned long)isn, + fp->ea, fp->ra, fp->sp); + } + + if (ma_usermode & UM_SIGNAL) + _exception(SIGBUS, fp, BUS_ADRALN, fp->ea); + else + fp->ea += 4; /* else advance */ +} + +#ifdef CONFIG_PROC_FS +static const char * const usermode_action[] = { + "ignored", /* 0 */ + "warn", /* 1 */ + "fixup", /* 2 */ + "fixup+warn", /* 3 */ + "signal", /* 4 */ + "signal+warn", /* 5 */ + "signal+fixup", + "signal+fixup+warn" +}; + +static int +proc_misaligned_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", ma_user); + p += sprintf(p, "Kernel:\t\t%lu\n", ma_kern); + p += sprintf(p, "Skipped:\t%lu\n", ma_skipped); + p += sprintf(p, "Half:\t\t%lu\n", ma_half); + p += sprintf(p, "Word:\t\t%lu\n", ma_word); + p += sprintf(p, "User faults:\t%i (%s)\n", + ma_usermode, + usermode_action[ma_usermode & 7]); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int +proc_misaligned_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') + ma_usermode = mode - '0'; + } + + return count; +} +#endif /* CONFIG_PROC_FS */ + +static void __init misaligned_calc_reg_offsets(void) +{ + int i, r, offset; + + /* pre-calc offsets of registers on sys call stack frame */ + offset = 0; + + /* struct pt_regs */ + for (i = 0; i < 16; i++) { + r = sys_stack_frame_reg_offset[i]; + reg_offsets[r] = offset; + offset += 4; + } + + /* struct switch_stack */ + offset = -sizeof(struct switch_stack); + for (i = 16; i < 32; i++) { + r = sys_stack_frame_reg_offset[i]; + reg_offsets[r] = offset; + offset += 4; + } +} + + +static int __init misaligned_init(void) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; + + res = create_proc_entry("misalign", S_IWUSR | S_IRUGO, NULL); + if (!res) + return -ENOMEM; + + res->read_proc = proc_misaligned_read; + res->write_proc = proc_misaligned_write; +#endif + + /* default mode - silent fix */ + ma_usermode = UM_FIXUP | KM_WARN; + + misaligned_calc_reg_offsets(); + + return 0; +} + +fs_initcall(misaligned_init); diff --git a/arch/nios2/kernel/module.c b/arch/nios2/kernel/module.c new file mode 100644 index 0000000000000..a82c6454f065c --- /dev/null +++ b/arch/nios2/kernel/module.c @@ -0,0 +1,170 @@ +/* + * Kernel module support for Nios II. + * + * Copyright (C) 2004 Microtronix Datacom Ltd. + * Written by Wentao Xu + * Copyright (C) 2001, 2003 Rusty Russell + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef CONFIG_MMU +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(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. + */ +} + +#else /* CONFIG_MMU */ + +/* + * FIXME: modules should NOT be allocated with kmalloc for (obvious) reasons. + * But we do it for now to avoid relocation issues. CALL26/PCREL26 cannot reach + * from 0x80000000 (vmalloc area) to 0xc00000000 (kernel) (kmalloc returns + * addresses in 0xc0000000) + * + * We should really have some trampolines for this instead. + */ + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return kmalloc(size, GFP_KERNEL); +} + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + kfree(module_region); + /* + * FIXME: If module_region == mod->init_region, trim exception + * table entries. + */ +} + +#endif /* CONFIG_MMU */ + +int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, + unsigned int symindex, unsigned int relsec, + struct module *mod) +{ + unsigned int i; + Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; + + pr_debug("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { + /* This is where to make the change */ + uint32_t word; + uint32_t *loc + = ((void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rela[i].r_offset); + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + Elf32_Sym *sym + = ((Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rela[i].r_info)); + uint32_t v = sym->st_value + rela[i].r_addend; + pr_debug("reltype %d 0x%x name:<%s>\n", + ELF32_R_TYPE(rela[i].r_info), + rela[i].r_offset, strtab + sym->st_name); + + switch (ELF32_R_TYPE(rela[i].r_info)) { + case R_NIOS2_NONE: + break; + case R_NIOS2_BFD_RELOC_32: + *loc += v; + break; + case R_NIOS2_PCREL16: + v -= (uint32_t)loc + 4; + if ((int32_t)v > 0x7fff || + (int32_t)v < -(int32_t)0x8000) { + pr_err("module %s: relocation overflow\n", + mod->name); + return -ENOEXEC; + } + word = *loc; + *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | + (word & 0x3f); + break; + case R_NIOS2_CALL26: + if (v & 3) { + pr_err("module %s: dangerous relocation\n", + mod->name); + return -ENOEXEC; + } + if ((v >> 28) != ((uint32_t)loc >> 28)) { + pr_err("module %s: relocation overflow\n", + mod->name); + return -ENOEXEC; + } + *loc = (*loc & 0x3f) | ((v >> 2) << 6); + break; + case R_NIOS2_HI16: + word = *loc; + *loc = ((((word >> 22) << 16) | + ((v >> 16) & 0xffff)) << 6) | (word & 0x3f); + break; + case R_NIOS2_LO16: + word = *loc; + *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | + (word & 0x3f); + break; + case R_NIOS2_HIADJ16: + { + Elf32_Addr word2; + + word = *loc; + word2 = ((v >> 16) + ((v >> 15) & 1)) & 0xffff; + *loc = ((((word >> 22) << 16) | word2) << 6) | + (word & 0x3f); + } + break; + + default: + pr_err("module %s: Unknown reloc: %u\n", + mod->name, ELF32_R_TYPE(rela[i].r_info)); + return -ENOEXEC; + } + } + + return 0; +} + +int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, + struct module *me) +{ + flush_cache_all(); + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/nios2/kernel/nios2_ksyms.c b/arch/nios2/kernel/nios2_ksyms.c new file mode 100644 index 0000000000000..467042f128ffe --- /dev/null +++ b/arch/nios2/kernel/nios2_ksyms.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + */ + +#include +#include + +/* string functions */ + +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memmove); + +/* + * libgcc functions - functions that are used internally by the + * compiler... (prototypes are not correct though, but that + * doesn't really matter since they're not versioned). + */ +#define DECLARE_EXPORT(name) extern void name(void); EXPORT_SYMBOL(name) + +DECLARE_EXPORT(__gcc_bcmp); +DECLARE_EXPORT(__divdi3); +DECLARE_EXPORT(__divsi3); +DECLARE_EXPORT(__moddi3); +DECLARE_EXPORT(__modsi3); +DECLARE_EXPORT(__udivdi3); +DECLARE_EXPORT(__udivmoddi4); +DECLARE_EXPORT(__udivsi3); +DECLARE_EXPORT(__umoddi3); +DECLARE_EXPORT(__umodsi3); diff --git a/arch/nios2/kernel/process.c b/arch/nios2/kernel/process.c new file mode 100644 index 0000000000000..3c0cd7c9de0ea --- /dev/null +++ b/arch/nios2/kernel/process.c @@ -0,0 +1,395 @@ +/* + * Architecture-dependent parts of process handling. + * + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * based on arch/m68knommu/kernel/process.c which is: + * + * Copyright (C) 2000-2002 David McCullough + * Copyright (C) 1995 Hamish Macdonald + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +asmlinkage void ret_from_fork(void); + +void (*pm_power_off)(void) = NULL; +EXPORT_SYMBOL(pm_power_off); + +void default_idle(void) +{ + local_irq_disable(); + if (!need_resched()) { + local_irq_enable(); + __asm__("nop"); + } else + local_irq_enable(); +} + +void (*idle)(void) = default_idle; + +/* + * 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) +{ + while (1) { + tick_nohz_idle_enter(); + rcu_idle_enter(); + while (!need_resched()) + idle(); + rcu_idle_exit(); + tick_nohz_idle_exit(); + + schedule_preempt_disabled(); + } +} + +/* + * The development boards have no way to pull a board reset. Just jump to the + * cpu reset address and let the boot loader or the code in head.S take care of + * resetting peripherals. + */ +void machine_restart(char *__unused) +{ + pr_notice("Machine restart (%08x)...\n", cpuinfo.reset_addr); + local_irq_disable(); + __asm__ __volatile__ ( + "jmp %0\n\t" + : + : "r" (cpuinfo.reset_addr) + : "r4"); +} + +void machine_halt(void) +{ + pr_notice("Machine halt...\n"); + local_irq_disable(); + for (;;) + ; +} + +/* + * There is no way to power off the development boards. So just spin for now. If + * we ever have a way of resetting a board using a GPIO we should add that here. + */ +void machine_power_off(void) +{ + pr_notice("Machine power off...\n"); + local_irq_disable(); + for (;;) + ; +} + +void show_regs(struct pt_regs *regs) +{ + pr_notice("\n"); + + pr_notice("r1: %08lx r2: %08lx r3: %08lx r4: %08lx\n", + regs->r1, regs->r2, regs->r3, regs->r4); + + pr_notice("r5: %08lx r6: %08lx r7: %08lx r8: %08lx\n", + regs->r5, regs->r6, regs->r7, regs->r8); + + pr_notice("r9: %08lx r10: %08lx r11: %08lx r12: %08lx\n", + regs->r9, regs->r10, regs->r11, regs->r12); + + pr_notice("r13: %08lx r14: %08lx r15: %08lx\n", + regs->r13, regs->r14, regs->r15); + + pr_notice("ra: %08lx fp: %08lx sp: %08lx gp: %08lx\n", + regs->ra, regs->fp, regs->sp, regs->gp); + + pr_notice("ea: %08lx estatus: %08lx\n", + regs->ea, regs->estatus); +#ifndef CONFIG_MMU + pr_notice("status_extension: %08lx\n", regs->status_extension); +#endif +} + +#ifdef CONFIG_MMU +static void kernel_thread_helper(void *arg, int (*fn)(void *)) +{ + do_exit(fn(arg)); +} +#endif + +/* + * Create a kernel thread + */ +int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ +#ifdef CONFIG_MMU + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + regs.r4 = (unsigned long) arg; + regs.r5 = (unsigned long) fn; + regs.ea = (unsigned long) kernel_thread_helper; + regs.estatus = STATUS_PIE; + + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, + NULL, NULL); +#else /* !CONFIG_MMU */ + long retval; + long clone_arg = flags | CLONE_VM; + mm_segment_t fs; + + fs = get_fs(); + set_fs(KERNEL_DS); + + __asm__ __volatile( + "movi r2,%6\n\t" /* TRAP_ID_SYSCALL */ + "movi r3,%1\n\t" /* __NR_clone */ + "mov r4,%5\n\t" /* (clone_arg */ + /* (flags | CLONE_VM)) */ + "movia r5,-1\n\t" /* usp: -1 */ + "trap\n\t" /* sys_clone */ + "\n\t" + "cmpeq r4,r3,zero\n\t" /* 2nd return value in r3 */ + "bne r4,zero,1f\n\t" /* 0: parent, just return. */ + /* See copy_thread, called */ + /* by do_fork, called by */ + /* nios2_clone, called by */ + /* sys_clone, called by */ + /* syscall trap handler. */ + + "mov r4,%4\n\t" /* fn's parameter (arg) */ + "\n\t" + "callr %3\n\t" /* Call function (fn) */ + "\n\t" + "mov r4,r2\n\t" /* fn's rtn code//;dgt2;tmp;*/ + "movi r2,%6\n\t" /* TRAP_ID_SYSCALL */ + "movi r3,%2\n\t" /* __NR_exit */ + "trap\n\t" /* sys_exit() */ + + /* Not reached by child */ + "1:\n\t" + "mov %0,r2\n\t" /* error rtn code (retval) */ + + : "=r" (retval) /* %0 */ + + : "i" (__NR_clone) /* %1 */ + , "i" (__NR_exit) /* %2 */ + , "r" (fn) /* %3 */ + , "r" (arg) /* %4 */ + , "r" (clone_arg) /* %5 (flags | CLONE_VM) */ + , "i" (TRAP_ID_SYSCALL) /* %6 */ + + : "r2", "r3", "r4", "r5", "ra"/* Clobbered */ + ); + + set_fs(fs); + return retval; +#endif /* CONFIG_MMU */ +} +EXPORT_SYMBOL(kernel_thread); + +void flush_thread(void) +{ + set_fs(USER_DS); +} + +int copy_thread(unsigned long clone_flags, + unsigned long usp, unsigned long topstk, + struct task_struct *p, struct pt_regs *regs) +{ + struct pt_regs *childregs; + struct switch_stack *childstack, *stack; + + childregs = task_pt_regs(p); + + /* Save pointer to registers in thread_struct */ + p->thread.kregs = childregs; + + /* Copy registers */ + *childregs = *regs; +#ifndef CONFIG_MMU + childregs->r2 = 0; /* Redundant? See return values below */ +#endif + + /* Copy stacktop and copy the top entrys from parent to child */ + stack = ((struct switch_stack *) regs) - 1; + childstack = ((struct switch_stack *) childregs) - 1; + *childstack = *stack; + childstack->ra = (unsigned long) ret_from_fork; + +#ifdef CONFIG_MMU + if (childregs->estatus & ESTATUS_EU) + childregs->sp = usp; + else + childregs->sp = (unsigned long) childstack; +#else + if (usp == -1) + p->thread.kregs->sp = (unsigned long) childstack; + else + p->thread.kregs->sp = usp; +#endif /* CONFIG_MMU */ + + /* Store the kernel stack in thread_struct */ + p->thread.ksp = (unsigned long) childstack; + +#ifdef CONFIG_MMU + /* Initialize tls register. */ + if (clone_flags & CLONE_SETTLS) + childstack->r23 = regs->r7; +#endif + + /* Set the return value for the child. */ + childregs->r2 = 0; +#ifdef CONFIG_MMU + childregs->r7 = 0; +#else + childregs->r3 = 1; /* kernel_thread parent test */ +#endif + + /* Set the return value for the parent. */ + regs->r2 = p->pid; +#ifdef CONFIG_MMU + regs->r7 = 0; /* No error */ +#else + regs->r3 = 0; /* kernel_thread parent test */ +#endif + + return 0; +} + +/* + * Generic dumping code. Used for panic and debug. + */ +void dump(struct pt_regs *fp) +{ + unsigned long *sp; + unsigned char *tp; + int i; + + pr_emerg("\nCURRENT PROCESS:\n\n"); + pr_emerg("COMM=%s PID=%d\n", current->comm, current->pid); + + if (current->mm) { + pr_emerg("TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n", + (int) current->mm->start_code, + (int) current->mm->end_code, + (int) current->mm->start_data, + (int) current->mm->end_data, + (int) current->mm->end_data, + (int) current->mm->brk); + pr_emerg("USER-STACK=%08x KERNEL-STACK=%08x\n\n", + (int) current->mm->start_stack, + (int)(((unsigned long) current) + THREAD_SIZE)); + } + + pr_emerg("PC: %08lx\n", fp->ea); + pr_emerg(KERN_EMERG "SR: %08lx SP: %08lx\n", + (long) fp->estatus, (long) fp); + + pr_emerg("r1: %08lx r2: %08lx r3: %08lx\n", + fp->r1, fp->r2, fp->r3); + + pr_emerg("r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + fp->r4, fp->r5, fp->r6, fp->r7); + pr_emerg("r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + fp->r8, fp->r9, fp->r10, fp->r11); + pr_emerg("r12: %08lx r13: %08lx r14: %08lx r15: %08lx\n", + fp->r12, fp->r13, fp->r14, fp->r15); + pr_emerg("or2: %08lx ra: %08lx fp: %08lx sp: %08lx\n", + fp->orig_r2, fp->ra, fp->fp, fp->sp); + pr_emerg("\nUSP: %08x TRAPFRAME: %08x\n", + (unsigned int) fp->sp, (unsigned int) fp); + + pr_emerg("\nCODE:"); + tp = ((unsigned char *) fp->ea) - 0x20; + for (sp = (unsigned long *) tp, i = 0; (i < 0x40); i += 4) { + if ((i % 0x10) == 0) + pr_emerg("\n%08x: ", (int) (tp + i)); + pr_emerg("%08x ", (int) *sp++); + } + pr_emerg("\n"); + + pr_emerg("\nKERNEL STACK:"); + tp = ((unsigned char *) fp) - 0x40; + for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { + if ((i % 0x10) == 0) + pr_emerg("\n%08x: ", (int) (tp + i)); + pr_emerg("%08x ", (int) *sp++); + } + pr_emerg("\n"); + pr_emerg("\n"); + + pr_emerg("\nUSER STACK:"); + tp = (unsigned char *) (fp->sp - 0x10); + for (sp = (unsigned long *) tp, i = 0; (i < 0x80); i += 4) { + if ((i % 0x10) == 0) + pr_emerg("\n%08x: ", (int) (tp + i)); + pr_emerg("%08x ", (int) *sp++); + } + pr_emerg("\n\n"); +} + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long fp, pc; + unsigned long stack_page; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + + stack_page = (unsigned long)p; + fp = ((struct switch_stack *)p->thread.ksp)->fp; /* ;dgt2 */ + do { + if (fp < stack_page+sizeof(struct task_struct) || + fp >= 8184+stack_page) /* ;dgt2;tmp */ + return 0; + pc = ((unsigned long *)fp)[1]; + if (!in_sched_functions(pc)) + return pc; + fp = *(unsigned long *) fp; + } while (count++ < 16); /* ;dgt2;tmp */ + return 0; +} + +/* + * Do necessary setup to start up a newly executed thread. + * Will startup in user mode (status_extension = 0). + */ +void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) +{ + memset((void *) regs, 0, sizeof(struct pt_regs)); +#ifdef CONFIG_MMU + regs->estatus = ESTATUS_EPIE | ESTATUS_EU; +#else + /* No user mode setting on NOMMU, at least for now */ + regs->estatus = ESTATUS_EPIE; +#endif /* CONFIG_MMU */ + regs->ea = pc; + regs->sp = sp; +} + +#ifdef CONFIG_MMU +#include + +/* Fill in the FPU structure for a core dump. */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) +{ + return 0; /* Nios2 has no FPU and thus no FPU registers */ +} +#endif /* CONFIG_MMU */ diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c new file mode 100644 index 0000000000000..893ed19d6505e --- /dev/null +++ b/arch/nios2/kernel/prom.c @@ -0,0 +1,164 @@ +/* + * Device tree support + * + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2010 Thomas Chou + * + * Based on MIPS support for CONFIG_OF device tree support + * + * Copyright (C) 2010 Cisco Systems 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. + * + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +void __init early_init_dt_add_memory_arch(u64 base, u64 size) +{ + u64 kernel_start = (u64)virt_to_phys(_text); + + if (!memory_size && + (kernel_start >= base) && (kernel_start < (base + size))) + memory_size = size; + + return; +} + +void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) +{ + return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS)); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void __init early_init_dt_setup_initrd_arch(unsigned long start, + unsigned long end) +{ + initrd_start = (unsigned long)__va(start); + initrd_end = (unsigned long)__va(end); + initrd_below_start_ok = 1; +} +#endif + +void __init early_init_devtree(void *params) +{ + if (params && be32_to_cpup((__be32 *)params) == OF_DT_HEADER) + initial_boot_params = params; +#if defined(CONFIG_NIOS2_DTB_AT_PHYS_ADDR) + else if (be32_to_cpup((__be32 *)CONFIG_NIOS2_DTB_PHYS_ADDR) == + OF_DT_HEADER) + initial_boot_params = (void *)CONFIG_NIOS2_DTB_PHYS_ADDR; +#endif + else if (be32_to_cpu((__be32)__dtb_start) == OF_DT_HEADER) + initial_boot_params = (void *)&__dtb_start; + else + return; + + /* Retrieve various informations from the /chosen node of the + * device-tree, including the platform type, initrd location and + * size, and more ... + */ + of_scan_flat_dt(early_init_dt_scan_chosen, cmd_line); + + /* Scan memory nodes */ + of_scan_flat_dt(early_init_dt_scan_root, NULL); + of_scan_flat_dt(early_init_dt_scan_memory, NULL); +} + +void __init device_tree_init(void) +{ + unsigned long base, size; + + if (!initial_boot_params) + return; + + base = virt_to_phys((void *)initial_boot_params); + size = be32_to_cpu(initial_boot_params->totalsize); + + /* + * If the chosen DTB is not the built-in one (passed via + * bootloader or found at a built-in physical address) and + * it is within main memory above the kernel binary itself + * (> memory_start), we need to reserve_bootmem(). + */ + if ((base >= memory_start) && (base < memory_end)) { + reserve_bootmem(base, size, BOOTMEM_DEFAULT); + unflatten_device_tree(); + free_bootmem(base, size); + } else + unflatten_device_tree(); +} + +#ifdef CONFIG_EARLY_PRINTK +static int __init early_init_dt_scan_serial(unsigned long node, + const char *uname, int depth, void *data) +{ + u64 *addr64 = (u64 *) data; + char *p; + + /* only consider serial nodes */ + if (strncmp(uname, "serial", 6) != 0) + return 0; + + p = of_get_flat_dt_prop(node, "compatible", NULL); + if (!p) + return 0; + + /* + * We found an altera_jtaguart but it wasn't configured for console, so + * skip it. + */ +#ifndef CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE + if (strncmp(p, "ALTR,juart", 10) == 0) + return 0; +#endif + + /* + * Same for altera_uart. + */ +#ifndef CONFIG_SERIAL_ALTERA_UART_CONSOLE + if (strncmp(p, "ALTR,uart", 9) == 0) + return 0; +#endif + + *addr64 = of_get_flat_dt_translate_address(node); + return *addr64 == OF_BAD_ADDR ? 0 : 1; +} + +unsigned long __init early_altera_uart_or_juart_console(void) +{ + u64 base = 0; + + if (of_scan_flat_dt(early_init_dt_scan_serial, &base)) + return (unsigned long)(base + CONFIG_IO_REGION_BASE); + else + return 0; +} +#endif /* CONFIG_EARLY_PRINTK */ diff --git a/arch/nios2/kernel/ptrace.c b/arch/nios2/kernel/ptrace.c new file mode 100644 index 0000000000000..5db61796b2c11 --- /dev/null +++ b/arch/nios2/kernel/ptrace.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * + * based on arch/m68knommu/kernel/ptrace.c + * + * Copyright (C) 1994 by Hamish Macdonald + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* determines which bits in the SR the user has access to. */ +/* 1 = access 0 = no access */ +#define SR_MASK 0x00000000 + +/* Find the stack offset for a register, relative to thread.ksp. */ +#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) +#define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ + - sizeof(struct switch_stack)) + +/* Mapping from PT_xxx to the stack offset at which the register is + * saved. + */ +static int regoff[] = { + -1, PT_REG(r1), PT_REG(r2), PT_REG(r3), + PT_REG(r4), PT_REG(r5), PT_REG(r6), PT_REG(r7), + PT_REG(r8), PT_REG(r9), PT_REG(r10), PT_REG(r11), + PT_REG(r12), PT_REG(r13), PT_REG(r14), PT_REG(r15), /* reg 15 */ + SW_REG(r16), SW_REG(r17), SW_REG(r18), SW_REG(r19), + SW_REG(r20), SW_REG(r21), SW_REG(r22), SW_REG(r23), + -1, -1, PT_REG(gp), PT_REG(sp), + PT_REG(fp), PT_REG(ea), -1, PT_REG(ra), /* reg 31 */ + PT_REG(ea), -1, -1, -1, /* use ea for pc */ + -1, -1, -1, -1, + -1, -1, -1, -1 /* reg 43 */ +}; + +/* + * Get contents of register REGNO in task TASK. + */ +static inline long get_reg(struct task_struct *task, int regno) +{ + unsigned long *addr; + + if (regno >= ARRAY_SIZE(regoff) || regoff[regno] == -1) + return 0; + + addr = (unsigned long *)((char *)task->thread.kregs + regoff[regno]); + return *addr; +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, int regno, + unsigned long data) +{ + unsigned long *addr; + + if (regno >= ARRAY_SIZE(regoff) || regoff[regno] == -1) + return -1; + + addr = (unsigned long *)((char *)task->thread.kregs + regoff[regno]); + *addr = data; + return 0; +} + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Nothing special to do here, no processor debug support. + */ +void ptrace_disable(struct task_struct *child) +{ +} + +long arch_ptrace(struct task_struct *child, long request, + unsigned long addr, unsigned long data) +{ + unsigned long tmp; + unsigned int i; + int ret; + + switch (request) { + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: + pr_debug("PEEKUSR: addr=0x%08lx\n", addr); + ret = -EIO; + if (addr & 3) + break; + + addr = addr >> 2; /* temporary hack. */ + ret = -EIO; + if (addr < ARRAY_SIZE(regoff)) + tmp = get_reg(child, addr); + else if (addr == PT_TEXT_ADDR / 4) + tmp = child->mm->start_code; + else if (addr == PT_DATA_ADDR / 4) + tmp = child->mm->start_data; + else if (addr == PT_TEXT_END_ADDR / 4) + tmp = child->mm->end_code; + else + break; + ret = put_user(tmp, (unsigned long *) data); + pr_debug("PEEKUSR: rdword=0x%08lx\n", tmp); + break; + /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: + pr_debug("POKEUSR: addr=0x%08lx, data=0x%08lx\n", addr, data); + ret = -EIO; + if (addr & 3) + break; + + addr = addr >> 2; /* temporary hack. */ + + if (addr == PTR_ESTATUS) { + data &= SR_MASK; + data |= get_reg(child, PTR_ESTATUS) & ~(SR_MASK); + } + if (addr < ARRAY_SIZE(regoff)) { + if (put_reg(child, addr, data)) + break; + ret = 0; + break; + } + break; + /* Get all gp regs from the child. */ + case PTRACE_GETREGS: + pr_debug("GETREGS\n"); + for (i = 0; i < ARRAY_SIZE(regoff); i++) { + tmp = get_reg(child, i); + if (put_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + data += sizeof(long); + } + ret = 0; + break; + /* Set all gp regs in the child. */ + case PTRACE_SETREGS: + pr_debug("SETREGS\n"); + for (i = 0; i < ARRAY_SIZE(regoff); i++) { + if (get_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + if (i == PTR_ESTATUS) { + tmp &= SR_MASK; + tmp |= get_reg(child, PTR_ESTATUS) & ~(SR_MASK); + } + put_reg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + default: + ret = ptrace_request(child, request, addr, data); + } + + return ret; +} + +asmlinkage void syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 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/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c new file mode 100644 index 0000000000000..fcfb82ee2d02a --- /dev/null +++ b/arch/nios2/kernel/setup.c @@ -0,0 +1,233 @@ +/* + * Nios2-specific parts of system setup + * + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * Copyright (C) 2001 Vic Phillips + * + * based on kernel/setup.c from m68knommu + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +unsigned long memory_start; +EXPORT_SYMBOL(memory_start); + +unsigned long memory_end; +EXPORT_SYMBOL(memory_end); + +unsigned long memory_size; + +char cmd_line[COMMAND_LINE_SIZE] = { 0, }; + +/* r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11*/ +/* r12 r13 r14 r15 or2 ra fp sp gp es ste ea*/ +static struct pt_regs fake_regs = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, (unsigned long)cpu_idle, 0, 0, 0, 0, + 0}; + +/* Copy a short hook instruction sequence to the exception address */ +static inline void copy_exception_handler(unsigned int addr) +{ + unsigned int start = (unsigned int) exception_handler_hook; + volatile unsigned int tmp = 0; + + /* FIXME: check overlap of source and destination address here? */ + + __asm__ __volatile__ ( + "ldw %2,0(%0)\n" + "stw %2,0(%1)\n" + "ldw %2,4(%0)\n" + "stw %2,4(%1)\n" + "ldw %2,8(%0)\n" + "stw %2,8(%1)\n" + "flushd 0(%1)\n" + "flushd 4(%1)\n" + "flushd 8(%1)\n" + "flushi %1\n" + "addi %1,%1,4\n" + "flushi %1\n" + "addi %1,%1,4\n" + "flushi %1\n" + : /* no output registers */ + : "r" (start), "r" (addr), "r" (tmp) + : "memory" + ); +} + +#ifdef CONFIG_MMU +/* Copy the fast TLB miss handler */ +static inline void copy_fast_tlb_miss_handler(unsigned int addr) +{ + unsigned int start = (unsigned int) fast_handler; + unsigned int end = (unsigned int) fast_handler_end; + volatile unsigned int tmp = 0; + + /* FIXME: check overlap of source and destination address here? */ + + __asm__ __volatile__ ( + "1:\n" + " ldw %3,0(%0)\n" + " stw %3,0(%1)\n" + " flushd 0(%1)\n" + " flushi %1\n" + " addi %0,%0,4\n" + " addi %1,%1,4\n" + " bne %0,%2,1b\n" + : /* no output registers */ + : "r" (start), "r" (addr), "r" (end), "r" (tmp) + : "memory" + ); +} +#endif /* CONFIG_MMU */ + +/* + * save args passed from u-boot, called from head.S + * + * @r4: NIOS magic + * @r5: initrd start + * @r6: initrd end or fdt + * @r7: kernel command line + */ +asmlinkage void __init nios2_boot_init(unsigned r4, unsigned r5, unsigned r6, + unsigned r7) +{ + unsigned dtb_passed = 0; + char cmdline_passed[COMMAND_LINE_SIZE] = { 0, }; + +#ifdef CONFIG_MMU + mmu_init(); +#endif + +#if defined(CONFIG_PASS_CMDLINE) + if (r4 == 0x534f494e) { /* r4 is magic NIOS */ +#if defined(CONFIG_BLK_DEV_INITRD) + if (r5) { /* initramfs */ + initrd_start = r5; + initrd_end = r6; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + dtb_passed = r6; + + if (r7) + strncpy(cmdline_passed, (char *)r7, COMMAND_LINE_SIZE); + } +#endif + + early_init_devtree((void *)dtb_passed); + +#ifndef CONFIG_CMDLINE_FORCE + if (cmdline_passed[0]) + strncpy(cmd_line, cmdline_passed, COMMAND_LINE_SIZE); +#ifdef CONFIG_CMDLINE_IGNORE_DTB + else + strncpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE); +#endif +#endif +} + +void __init setup_arch(char **cmdline_p) +{ + int bootmap_size; + + console_verbose(); + +#ifdef CONFIG_EARLY_PRINTK + setup_early_printk(); +#endif + + memory_start = PAGE_ALIGN((unsigned long)__pa(_end)); + memory_end = (unsigned long) CONFIG_MEM_BASE + memory_size; + + 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) _end; + init_task.thread.kregs = &fake_regs; + + /* Keep a copy of command line */ + *cmdline_p = &cmd_line[0]; + + memcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); + boot_command_line[COMMAND_LINE_SIZE-1] = 0; + + /* + * give all the memory to the bootmap allocator, tell it to put the + * boot mem_map at the start of memory + */ + pr_debug("init_bootmem_node(?,%#lx, %#x, %#lx)\n", + PFN_UP(memory_start), PFN_DOWN(PHYS_OFFSET), + PFN_DOWN(memory_end)); + bootmap_size = init_bootmem_node(NODE_DATA(0), + PFN_UP(memory_start), + PFN_DOWN(PHYS_OFFSET), + PFN_DOWN(memory_end)); + + /* + * free the usable memory, we have to make sure we do not free + * the bootmem bitmap so we then reserve it after freeing it :-) + */ + pr_debug("free_bootmem(%#lx, %#lx)\n", + memory_start, memory_end - memory_start); + free_bootmem(memory_start, memory_end - memory_start); + + /* + * 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. + * + * Arguments are start, size + */ + pr_debug("reserve_bootmem(%#lx, %#x)\n", memory_start, bootmap_size); + reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) { + reserve_bootmem(virt_to_phys((void *)initrd_start), + initrd_end - initrd_start, BOOTMEM_DEFAULT); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + device_tree_init(); + + setup_cpuinfo(); + + copy_exception_handler(cpuinfo.exception_addr); + +#ifdef CONFIG_MMU + copy_fast_tlb_miss_handler(cpuinfo.fast_tlb_miss_exc_addr); + + /* + * Initialize MMU context handling here because data from cpuinfo is + * needed for this. + */ + mmu_context_init(); +#endif + + /* + * get kmalloc into gear + */ + paging_init(); + +#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +} diff --git a/arch/nios2/kernel/signal.c b/arch/nios2/kernel/signal.c new file mode 100644 index 0000000000000..5ec1298fe296a --- /dev/null +++ b/arch/nios2/kernel/signal.c @@ -0,0 +1,661 @@ +/* + * Copyright (C) 2011-2012 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This file is based on kernel/signal.c from m68knommu. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall); + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int do_sigsuspend(struct pt_regs *regs) +{ + old_sigset_t mask = regs->r4; /* Verify correct syscall reg */ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + +#ifdef CONFIG_MMU + regs->r2 = EINTR; + regs->r7 = 1; +#else + regs->r2 = -EINTR; +#endif + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(regs, &saveset, 0)) + return -EINTR; + } +} + +asmlinkage int do_rt_sigsuspend(struct pt_regs *regs) +{ + sigset_t *unewset = (sigset_t *)regs->r4; + size_t sigsetsize = (size_t)regs->r5; + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + 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); + +#ifdef CONFIG_MMU + regs->r2 = EINTR; + regs->r7 = 1; +#else + regs->r2 = -EINTR; +#endif + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(regs, &saveset, 0)) + return -EINTR; + } +} + +asmlinkage int sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (!access_ok(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 (!access_ok(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; +} + +/* + * Do a signal return; undo the signal stack. + * + * Keep the return code on the stack quadword aligned! + * That makes the cache flush below easier. + */ + +struct sigframe { + char retcode[12]; + unsigned long extramask[_NSIG_WORDS-1]; + struct sigcontext sc; +}; + +struct rt_sigframe { + char retcode[12]; + struct siginfo info; + struct ucontext uc; +}; + +static inline int restore_sigcontext(struct pt_regs *regs, + struct sigcontext *usc, void *fp, int *pr2) +{ + int err = 0; + int estatus; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + estatus = regs->estatus; + + /* get previous pt_regs */ + if (copy_from_user(regs, &usc->regs, sizeof(*regs))) + goto badframe; + + /* Prevent user from being able to change + * certain processor status bits. Currently nothing. + */ + regs->estatus = (estatus & 0xffffffff) | (regs->estatus & 0); + regs->orig_r2 = -1; /* disable syscall checks */ + + *pr2 = regs->r2; + + return err; + +badframe: + return 1; +} + +static inline int rt_restore_ucontext(struct pt_regs *regs, + struct switch_stack *sw, + struct ucontext *uc, int *pr2) +{ + int temp; + greg_t *gregs = uc->uc_mcontext.gregs; + int err; + + err = __get_user(temp, &uc->uc_mcontext.version); + if (temp != MCONTEXT_VERSION) + goto badframe; + /* restore passed registers */ + /* FIXME: What registers should/shoudn't be saved ? + */ + err |= __get_user(regs->r1, &gregs[0]); + err |= __get_user(regs->r2, &gregs[1]); + err |= __get_user(regs->r3, &gregs[2]); + err |= __get_user(regs->r4, &gregs[3]); + err |= __get_user(regs->r5, &gregs[4]); + err |= __get_user(regs->r6, &gregs[5]); + err |= __get_user(regs->r7, &gregs[6]); + err |= __get_user(regs->r8, &gregs[7]); + err |= __get_user(regs->r9, &gregs[8]); + err |= __get_user(regs->r10, &gregs[9]); + err |= __get_user(regs->r11, &gregs[10]); + err |= __get_user(regs->r12, &gregs[11]); + err |= __get_user(regs->r13, &gregs[12]); + err |= __get_user(regs->r14, &gregs[13]); + err |= __get_user(regs->r15, &gregs[14]); + err |= __get_user(sw->r16, &gregs[15]); + err |= __get_user(sw->r17, &gregs[16]); + err |= __get_user(sw->r18, &gregs[17]); + err |= __get_user(sw->r19, &gregs[18]); + err |= __get_user(sw->r20, &gregs[19]); + err |= __get_user(sw->r21, &gregs[20]); + err |= __get_user(sw->r22, &gregs[21]); + err |= __get_user(sw->r23, &gregs[22]); + /* gregs[23] is handled below */ + err |= __get_user(sw->fp, &gregs[24]); /* Verify, should this be + settable */ + err |= __get_user(sw->gp, &gregs[25]); /* Verify, should this be + settable */ + + err |= __get_user(temp, &gregs[26]); /* Not really necessary no user + settable bits */ + err |= __get_user(regs->ea, &gregs[27]); + +#ifdef CONFIG_MMU + err |= __get_user(regs->ra, &gregs[23]); + err |= __get_user(regs->sp, &gregs[28]); +#else + + err |= __get_user(regs->ra, &gregs[23]); + err |= __get_user(regs->status_extension, + &uc->uc_mcontext.status_extension); +#endif + + regs->estatus = (regs->estatus & 0xffffffff); + regs->orig_r2 = -1; /* disable syscall checks */ + + if (do_sigaltstack(&uc->uc_stack, NULL, regs->sp) == -EFAULT) + goto badframe; + + *pr2 = regs->r2; + return err; + +badframe: + return 1; +} + +asmlinkage int do_sigreturn(struct pt_regs *regs) +{ + struct sigframe *frame = (struct sigframe *) regs->sp; + sigset_t set; + int rval; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + + if (__get_user(set.sig[0], &frame->sc.sc_mask) || + (_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(regs, &frame->sc, frame + 1, &rval)) + goto badframe; + return rval; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int do_rt_sigreturn(struct switch_stack *sw) +{ + struct pt_regs *regs = (struct pt_regs *)(sw + 1); + /* Verify, can we follow the stack back */ + struct rt_sigframe *frame = (struct rt_sigframe *) regs->sp; + sigset_t set; + int rval; + + if (!access_ok(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 (rt_restore_ucontext(regs, sw, &frame->uc, &rval)) + goto badframe; + + return rval; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +static int setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, + unsigned long mask) +{ + int err = 0; + + err |= __put_user(mask, &sc->sc_mask); + err |= copy_to_user(&sc->regs, regs, sizeof(*regs)); + return err; +} + +static inline int rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs) +{ + struct switch_stack *sw = (struct switch_stack *)regs - 1; + greg_t *gregs = uc->uc_mcontext.gregs; + int err = 0; + + err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); + err |= __put_user(regs->r1, &gregs[0]); + err |= __put_user(regs->r2, &gregs[1]); + err |= __put_user(regs->r3, &gregs[2]); + err |= __put_user(regs->r4, &gregs[3]); + err |= __put_user(regs->r5, &gregs[4]); + err |= __put_user(regs->r6, &gregs[5]); + err |= __put_user(regs->r7, &gregs[6]); + err |= __put_user(regs->r8, &gregs[7]); + err |= __put_user(regs->r9, &gregs[8]); + err |= __put_user(regs->r10, &gregs[9]); + err |= __put_user(regs->r11, &gregs[10]); + err |= __put_user(regs->r12, &gregs[11]); + err |= __put_user(regs->r13, &gregs[12]); + err |= __put_user(regs->r14, &gregs[13]); + err |= __put_user(regs->r15, &gregs[14]); + err |= __put_user(sw->r16, &gregs[15]); + err |= __put_user(sw->r17, &gregs[16]); + err |= __put_user(sw->r18, &gregs[17]); + err |= __put_user(sw->r19, &gregs[18]); + err |= __put_user(sw->r20, &gregs[19]); + err |= __put_user(sw->r21, &gregs[20]); + err |= __put_user(sw->r22, &gregs[21]); + err |= __put_user(sw->r23, &gregs[22]); +#ifdef CONFIG_MMU + err |= __put_user(regs->ra, &gregs[23]); +#else + err |= __put_user(regs->sp, &gregs[23]); +#endif + err |= __put_user(sw->fp, &gregs[24]); + err |= __put_user(sw->gp, &gregs[25]); + err |= __put_user(regs->ea, &gregs[27]); +#ifdef CONFIG_MMU + err |= __put_user(regs->sp, &gregs[28]); +#else + err |= __put_user(regs->status_extension, + &uc->uc_mcontext.status_extension); +#endif + return err; +} + +static inline void push_cache(unsigned long vaddr) +{ + flush_dcache_range(vaddr, vaddr + 12); + flush_icache_range(vaddr, vaddr + 12); +} + +static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size) +{ + unsigned long usp; + + /* Default to using normal stack. */ + usp = regs->sp; + + /* This is the X/Open sanctioned signal stack switching. */ +#ifdef CONFIG_MMU + if ((ka->sa.sa_flags & SA_ONSTACK) && (current->sas_ss_sp != 0)) { +#else + if (ka->sa.sa_flags & SA_ONSTACK) { +#endif + if (!on_sig_stack(usp)) + usp = current->sas_ss_sp + current->sas_ss_size; + } + + /* Verify, is it 32 or 64 bit aligned */ + return (void *)((usp - frame_size) & -8UL); +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs *regs) +{ + struct sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (_NSIG_WORDS > 1) + err |= copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + + err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); + + /* Set up to return from userspace. */ + regs->ra = (unsigned long) &frame->retcode[0]; + +#ifdef CONFIG_MMU + /* movi r2,__NR_sigreturn */ + err |= __put_user(0x00800004 + (__NR_sigreturn << 6), + (long *)(frame->retcode)); + /* trap */ + err |= __put_user(0x003b683a, (long *)(frame->retcode + 4)); +#else + /* movi r3,__NR_sigreturn */ + err |= __put_user(0x00c00004 + (__NR_sigreturn << 6), + (long *)(frame->retcode)); + /* mov r2,r0 */ + err |= __put_user(0x0005883a, (long *)(frame->retcode + 4)); + /* trap */ + err |= __put_user(0x003b683a, (long *)(frame->retcode + 8)); +#endif /* CONFIG_MMU */ + + if (err) + goto give_sigsegv; + + push_cache((unsigned long) &frame->retcode); + + /* Set up registers for signal handler */ + regs->sp = (unsigned long) frame; + regs->r4 = (unsigned long) (current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig); + regs->ea = (unsigned long) ka->sa.sa_handler; + return; + +give_sigsegv: + force_sigsegv(sig, current); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + err |= copy_siginfo_to_user(&frame->info, info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user((void *)current->sas_ss_sp, + &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->sp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= rt_setup_ucontext(&frame->uc, regs); + err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + /* Set up to return from userspace. */ + regs->ra = (unsigned long) &frame->retcode[0]; + +#ifdef CONFIG_MMU + /* movi r2,__NR_rt_sigreturn */ + err |= __put_user(0x00800004 + (__NR_rt_sigreturn << 6), + (long *)(frame->retcode)); + /* trap */ + err |= __put_user(0x003b683a, (long *)(frame->retcode + 4)); +#else + /* movi r3,__NR_rt_sigreturn */ + err |= __put_user(0x00c00004 + (__NR_rt_sigreturn << 6), + (long *)(frame->retcode)); + /* mov r2,r0 */ + err |= __put_user(0x0005883a, (long *)(frame->retcode + 4)); + /* trap */ + err |= __put_user(0x003b683a, (long *)(frame->retcode + 8)); +#endif /* CONFIG_MMU */ + + if (err) + goto give_sigsegv; + + push_cache((unsigned long) &frame->retcode); + + /* Set up registers for signal handler */ + regs->sp = (unsigned long) frame; + regs->r4 = (unsigned long) (current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig); + regs->r5 = (unsigned long) &frame->info; + regs->r6 = (unsigned long) &frame->uc; + regs->ea = (unsigned long) ka->sa.sa_handler; + return; + +give_sigsegv: + force_sigsegv(sig, current); +} + +static inline void handle_restart(struct pt_regs *regs, struct k_sigaction *ka, + int has_handler) +{ +#ifdef CONFIG_MMU + switch (regs->r2) { + case ERESTART_RESTARTBLOCK: + case ERESTARTNOHAND: + regs->r2 = EINTR; + regs->r7 = 1; + break; + case ERESTARTSYS: + if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { + regs->r2 = EINTR; + regs->r7 = 1; + break; + } + /* fallthrough */ + case ERESTARTNOINTR: + regs->r2 = regs->orig_r2; + regs->r7 = regs->orig_r7; + regs->ea -= 4; + break; + } +#else /* CONFIG_MMU */ + switch (regs->r2) { + case -ERESTARTNOHAND: + if (!has_handler) + goto do_restart; + regs->r2 = -EINTR; + break; + case -ERESTARTSYS: + if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { + regs->r2 = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: +do_restart: + regs->r2 = regs->orig_r2; + regs->ea -= 4; + break; + } +#endif /* CONFIG_MMU */ +} + +/* + * OK, we're invoking a handler + */ +static void handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *oldset, struct pt_regs *regs) +{ + /* set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka->sa.sa_mask); + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall) +{ + struct k_sigaction ka; + siginfo_t info; + int signr; + +#ifndef CONFIG_MMU + /* + * On NOMMU we always get in_syscall as 1 and instead need to look at + * orig_r2 whether we are in a syscall. + */ + if (regs->orig_r2 >= 0) + in_syscall = 1; + else + in_syscall = 0; +#endif + + /* FIXME - Do we still need to do this ? */ + current->thread.kregs = regs; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + /* + * Are we from a system call? If so, check system call + * restarting. + */ + if (in_syscall) + handle_restart(regs, &ka, 1); + /* Whee! Actually deliver the signal. */ + handle_signal(signr, &ka, &info, oldset, regs); + return 1; + } + +#ifdef CONFIG_MMU + /* + * No signal to deliver to the process - restart the syscall. + */ + if (in_syscall) { + /* Did the syscall return an error code */ + if (regs->r7 == 1) { + if (regs->r2 == ERESTARTNOHAND || + regs->r2 == ERESTARTSYS || + regs->r2 == ERESTARTNOINTR) { + regs->r2 = regs->orig_r2; + regs->r7 = regs->orig_r7; + regs->ea -= 4; + } else if (regs->r2 == ERESTART_RESTARTBLOCK) { + regs->r2 = __NR_restart_syscall; + regs->ea -= 4; + } + } + } +#else + /* Did we come from a system call? */ + if (in_syscall) { + /* Restart the system call - no handlers present */ + if (regs->r2 == -ERESTARTNOHAND || + regs->r2 == -ERESTARTSYS || + regs->r2 == -ERESTARTNOINTR) { + regs->r2 = regs->orig_r2; + regs->ea -= 4; + } else if (regs->r2 == -ERESTART_RESTARTBLOCK) { + regs->r2 = __NR_restart_syscall; + regs->ea -= 4; + } + } +#endif /* CONFIG_MMU */ + + return 0; +} + +asmlinkage void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, + int in_syscall) +{ + pr_debug("--> ENTERING %s\n", __func__); + /* + * We want the common case to go fast, which is why we may in certain + * cases get here from kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return; + + if (test_thread_flag(TIF_SIGPENDING)) + do_signal(regs, oldset, in_syscall); + + if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) + tracehook_notify_resume(regs); +} diff --git a/arch/nios2/kernel/sys_nios2.c b/arch/nios2/kernel/sys_nios2.c new file mode 100644 index 0000000000000..adf3af4d3a5a4 --- /dev/null +++ b/arch/nios2/kernel/sys_nios2.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2011-2012 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include + +asmlinkage int nios2_fork(struct pt_regs *regs) +{ +#ifdef CONFIG_MMU + return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL); +#else + return -EINVAL; +#endif +} + +asmlinkage int nios2_vfork(struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, + regs, 0, NULL, NULL); +} + +asmlinkage int nios2_clone(struct pt_regs *regs) +{ + unsigned long flags; + unsigned long newsp; + int __user *parent_tidptr, *child_tidptr; + + flags = regs->r4; + newsp = regs->r5; + if (newsp == 0) + newsp = regs->sp; +#ifdef CONFIG_MMU + parent_tidptr = (int __user *) regs->r6; + child_tidptr = (int __user *) regs->r8; +#else + parent_tidptr = NULL; + child_tidptr = NULL; +#endif + + return do_fork(flags, newsp, regs, 0, parent_tidptr, child_tidptr); +} + +asmlinkage int nios2_execve(struct pt_regs *regs) +{ + int error; + struct filename *filename; + + filename = getname((char *) regs->r4); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + return error; + error = do_execve(filename->name, + (const char __user *const __user *) regs->r5, + (const char __user *const __user *) regs->r6, + regs); + putname(filename); + return error; +} + +asmlinkage long sys_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_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); +} + +/* sys_cacheflush -- flush the processor cache. */ +asmlinkage int sys_cacheflush(unsigned long addr, int scope, int cache, + unsigned long len) +{ +#ifndef CONFIG_MMU + flush_cache_all(); +#else + struct vm_area_struct *vma; + + if (len == 0) + return 0; + + /* Check for overflow */ + if (addr + len < addr) + return -EFAULT; + + /* + * Verify that the specified address region actually belongs + * to this process. + */ + vma = find_vma(current->mm, addr); + if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end) + return -EFAULT; + + /* Ignore the scope and cache arguments. */ + flush_cache_range(vma, addr, addr + len); +#endif /* CONFIG_MMU */ + + return 0; +} + +asmlinkage int sys_getpagesize(void) +{ + return PAGE_SIZE; +} + +/* + * Do a system call from kernel instead of calling sys_execve so we + * end up with proper pt_regs. + */ +#ifdef CONFIG_MMU +int kernel_execve(const char *filename, + const char *const argv[], + const char *const envp[]) +{ + register long __res __asm__ ("r2"); + register long __sc __asm__ ("r2") = __NR_execve; + register long __a __asm__ ("r4") = (long) filename; + register long __b __asm__ ("r5") = (long) argv; + register long __c __asm__ ("r6") = (long) envp; + __asm__ __volatile__ ("trap" : "=r" (__res) + : "0" (__sc), "r" (__a), "r" (__b), "r" (__c) + : "memory"); + + return __res; +} +#else +int kernel_execve(const char *filename, + const char *const argv[], + const char *const envp[]) +{ + register long __res __asm__ ("r2") = TRAP_ID_SYSCALL; + register long __sc __asm__ ("r3") = __NR_execve; + register long __a __asm__ ("r4") = (long) filename; + register long __b __asm__ ("r5") = (long) argv; + register long __c __asm__ ("r6") = (long) envp; + __asm__ __volatile__ ("trap" : "=r" (__res) + : "0" (__res), "r" (__sc), "r" (__a), "r" (__b), + "r" (__c) : "memory"); + + return __res; +} + +#if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE) +#include +unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + + struct fb_info *info = filp->private_data; + return info->screen_base; +} +EXPORT_SYMBOL(get_fb_unmapped_area); +#endif /* CONFIG_FB */ +#endif /* CONFIG_MMU */ diff --git a/arch/nios2/kernel/syscalltable.S b/arch/nios2/kernel/syscalltable.S new file mode 100644 index 0000000000000..e2d29cc729854 --- /dev/null +++ b/arch/nios2/kernel/syscalltable.S @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * Derived from m68knommu + * + * 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. + */ + +#include +#include +#include +#include + +.text +ALIGN +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_chown + .long sys_ni_syscall /* old sys_break */ + .long sys_ni_syscall /* old sys_stat */ + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid + .long sys_getuid + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_ni_syscall /* old sys_fstat */ + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old sys_stty */ + .long sys_ni_syscall /* old sys_gtty */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35: old sys_ftime */ + .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 sys_prof */ + .long sys_brk /* 45 */ + .long sys_setgid + .long sys_getgid + .long sys_ni_syscall /* old sys_signal */ + .long sys_geteuid + .long sys_getegid /* 50 */ + .long sys_acct + .long sys_umount + .long sys_ni_syscall /* old sys_lock */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old sys_mpx */ + .long sys_setpgid + .long sys_ni_syscall /* old sys_ulimit */ + .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_ni_syscall /* old sys_sgetmask */ + .long sys_ni_syscall /* old sys_ssetmask */ + .long sys_setreuid /* 70 */ + .long sys_setregid + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_ni_syscall /* old sys_getrlimit */ + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups /* 80 */ + .long sys_setgroups + .long sys_old_select + .long sys_symlink + .long sys_ni_syscall /* old sys_lstat */ + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long sys_ni_syscall /* old sys_readdir */ + .long sys_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old sys_profil */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* old sys_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 /* old sys_iopl */ + .long sys_vhangup + .long sys_ni_syscall /* old sys_idle */ +#ifdef CONFIG_MMU + .long sys_nios2cmpxchg /* nios2-specific compare and exchange syscall + for atomic operations */ +#else + .long sys_ni_syscall +#endif + .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_cacheflush /* modify_ldt for i386 */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old sys_create_module */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130: old sys_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 /* old sys_afs_syscall */ + .long sys_setfsuid + .long sys_setfsgid + .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_setresuid + .long sys_getresuid /* 165 */ + .long sys_getpagesize + .long sys_ni_syscall /* old sys_query_module */ + .long sys_poll + .long sys_ni_syscall /* old sys_nfsservctl */ + .long sys_setresgid /* 170 */ + .long sys_getresgid + .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_lchown + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* old sys_getpmsg (streams1) */ + .long sys_ni_syscall /* old sys_putpmsg (streams2) */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap_pgoff + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_chown + .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_lchown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_ni_syscall /* unused */ + .long sys_ni_syscall /* unused */ + .long sys_getdents64 /* 220 */ + .long sys_gettid + .long sys_tkill + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr /* 225 */ + .long sys_getxattr + .long sys_lgetxattr + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr /* 230 */ + .long sys_flistxattr + .long sys_removexattr + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_futex /* 235 */ + .long sys_sendfile64 + .long sys_mincore + .long sys_madvise + .long sys_fcntl64 + .long sys_readahead /* 240 */ + .long sys_io_setup + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel /* 245 */ + .long sys_fadvise64 + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 250 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 255 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 260 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 265 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_mbind + .long sys_get_mempolicy + .long sys_set_mempolicy /* 270 */ + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive + .long sys_mq_notify /* 275 */ + .long sys_mq_getsetattr + .long sys_waitid + .long sys_ni_syscall /* old sys_setaltroot */ + .long sys_add_key + .long sys_request_key /* 280 */ + .long sys_keyctl + .long sys_ioprio_set + .long sys_ioprio_get + .long sys_inotify_init + .long sys_inotify_add_watch /* 285 */ + .long sys_inotify_rm_watch + .long sys_migrate_pages + .long sys_openat + .long sys_mkdirat + .long sys_mknodat /* 290 */ + .long sys_fchownat + .long sys_futimesat + .long sys_fstatat64 + .long sys_unlinkat + .long sys_renameat /* 295 */ + .long sys_linkat + .long sys_symlinkat + .long sys_readlinkat + .long sys_fchmodat + .long sys_faccessat /* 300 */ + .long sys_pselect6 + .long sys_ppoll + .long sys_unshare + .long sys_set_robust_list + .long sys_get_robust_list /* 305 */ + .long sys_splice + .long sys_sync_file_range + .long sys_tee + .long sys_vmsplice + .long sys_move_pages /* 310 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_kexec_load + .long sys_getcpu + .long sys_epoll_pwait /* 315 */ + .long sys_utimensat + .long sys_signalfd + .long sys_timerfd_create + .long sys_eventfd + .long sys_pread64 /* 320 */ + .long sys_pwrite64 + .long sys_fallocate + .long sys_timerfd_settime + .long sys_timerfd_gettime + .long sys_signalfd4 /* 325 */ + .long sys_eventfd2 + .long sys_epoll_create1 + .long sys_dup3 + .long sys_pipe2 + .long sys_inotify_init1 /* 330 */ + .long sys_preadv + .long sys_pwritev + .long sys_rt_sigqueueinfo + .long sys_perf_event_open + .long sys_recvmmsg /* 335 */ + .long sys_fanotify_init + .long sys_fanotify_mark + .long sys_prlimit64 + .long sys_name_to_handle_at + .long sys_open_by_handle_at /* 340 */ + .long sys_clock_adjtime + .long sys_syncfs + .long sys_sendmmsg + .long sys_setns + .long sys_process_vm_readv /* 345 */ + .long sys_process_vm_writev + .long sys_kcmp + + .rept NR_syscalls - 348 + .long sys_ni_syscall + .endr diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c new file mode 100644 index 0000000000000..482b2dff2f279 --- /dev/null +++ b/arch/nios2/kernel/time.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TICK_SIZE (tick_nsec / 1000) +#define NIOS2_TIMER_PERIOD (timer_freq / HZ) + +#define ALTERA_TIMER_STATUS_REG 0 +#define ALTERA_TIMER_CONTROL_REG 4 +#define ALTERA_TIMER_PERIODL_REG 8 +#define ALTERA_TIMER_PERIODH_REG 12 +#define ALTERA_TIMER_SNAPL_REG 16 +#define ALTERA_TIMER_SNAPH_REG 20 + +#define ALTERA_TIMER_CONTROL_ITO_MSK (0x1) +#define ALTERA_TIMER_CONTROL_CONT_MSK (0x2) +#define ALTERA_TIMER_CONTROL_START_MSK (0x4) +#define ALTERA_TIMER_CONTROL_STOP_MSK (0x8) + +static u32 nios2_timer_count; +static u32 timer_membase; +static u32 timer_freq; + +static inline unsigned long read_timersnapshot(void) +{ + unsigned long count; + + outw(0, timer_membase + ALTERA_TIMER_SNAPL_REG); + count = + inw(timer_membase + ALTERA_TIMER_SNAPH_REG) << 16 | + inw(timer_membase + ALTERA_TIMER_SNAPL_REG); + + return count; +} + +static inline void write_timerperiod(unsigned long period) +{ + outw(period, timer_membase + ALTERA_TIMER_PERIODL_REG); + outw(period >> 16, timer_membase + ALTERA_TIMER_PERIODH_REG); +} + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "xtime_update()" routine every clocktick + */ +irqreturn_t timer_interrupt(int irq, void *dummy) +{ + /* Clear the interrupt condition */ + outw(0, timer_membase + ALTERA_TIMER_STATUS_REG); + nios2_timer_count += NIOS2_TIMER_PERIOD; + + profile_tick(CPU_PROFILING); + + xtime_update(1); + + update_process_times(user_mode(get_irq_regs())); + + return IRQ_HANDLED; +} + +static cycle_t nios2_timer_read(struct clocksource *cs) +{ + unsigned long flags; + u32 cycles; + u32 tcn; + + local_irq_save(flags); + tcn = NIOS2_TIMER_PERIOD - 1 - read_timersnapshot(); + cycles = nios2_timer_count; + local_irq_restore(flags); + + return cycles + tcn; +} + +static struct clocksource nios2_timer = { + .name = "timer", + .rating = 250, + .read = nios2_timer_read, + .shift = 20, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static struct irqaction nios2_timer_irq = { + .name = "timer", + .flags = IRQF_TIMER, + .handler = timer_interrupt, +}; + +void __init nios2_late_time_init(void) +{ + u32 irq; + unsigned int ctrl; + struct device_node *timer = + of_find_compatible_node(NULL, NULL, "ALTR,timer-1.0"); + + BUG_ON(!timer); + + timer_membase = of_translate_address(timer, of_get_address(timer, 0, + NULL, NULL)); + timer_membase = (u32) ioremap(timer_membase, PAGE_SIZE); + + if (of_property_read_u32(timer, "clock-frequency", &timer_freq)) { + pr_err("Can't get timer clock-frequency from device tree\n"); + return; + } + + irq = irq_of_parse_and_map(timer, 0); + if (irq < 0) { + pr_err("Can't get timer interrupt\n"); + return; + } + setup_irq(irq, &nios2_timer_irq); + + write_timerperiod(NIOS2_TIMER_PERIOD - 1); + + /* clocksource initialize */ + nios2_timer.mult = clocksource_hz2mult(timer_freq, nios2_timer.shift); + clocksource_register(&nios2_timer); + + /* interrupt enable + continuous + start */ + ctrl = ALTERA_TIMER_CONTROL_ITO_MSK | ALTERA_TIMER_CONTROL_CONT_MSK | + ALTERA_TIMER_CONTROL_START_MSK; + outw(ctrl, timer_membase + ALTERA_TIMER_CONTROL_REG); +} + +void read_persistent_clock(struct timespec *ts) +{ + ts->tv_sec = mktime(2007, 1, 1, 0, 0, 0); + ts->tv_nsec = 0; +} + +void __init time_init(void) +{ + late_time_init = nios2_late_time_init; +} diff --git a/arch/nios2/kernel/traps.c b/arch/nios2/kernel/traps.c new file mode 100644 index 0000000000000..872354a48435b --- /dev/null +++ b/arch/nios2/kernel/traps.c @@ -0,0 +1,199 @@ +/* + * Hardware exception handling + * + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * Copyright (C) 2001 Vic Phillips + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + + +static DEFINE_SPINLOCK(die_lock); + +void die(const char *str, struct pt_regs *regs, long err) +{ + console_verbose(); + spin_lock_irq(&die_lock); + pr_warn("Oops: %s, sig: %ld\n", str, err); + show_regs(regs); + spin_unlock_irq(&die_lock); + /* + * do_exit() should take care of panic'ing from an interrupt + * context so we don't handle it here + */ + do_exit(err); +} + +void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr) +{ + siginfo_t info; + + if (!user_mode(regs)) + die("Exception in kernel mode", regs, signo); + + info.si_signo = signo; + info.si_errno = 0; + info.si_code = code; + info.si_addr = (void __user *) addr; + force_sig_info(signo, &info, current); +} + +/* + * The architecture-independent backtrace generator + */ +void dump_stack(void) +{ + unsigned long stack; + + show_stack(current, &stack); +} +EXPORT_SYMBOL(dump_stack); + +/* + * The show_stack is an external API which we do not use ourselves. + */ + +int kstack_depth_to_print = 48; + +void show_stack(struct task_struct *task, unsigned long *stack) +{ + unsigned long *endstack, addr; + int i; + + if (!stack) { + if (task) + stack = (unsigned long *)task->thread.ksp; + else + stack = (unsigned long *)&stack; + } + + addr = (unsigned long) stack; + endstack = (unsigned long *) PAGE_ALIGN(addr); + + pr_emerg("Stack from %08lx:", (unsigned long)stack); + for (i = 0; i < kstack_depth_to_print; i++) { + if (stack + 1 > endstack) + break; + if (i % 8 == 0) + pr_emerg("\n "); + pr_emerg(" %08lx", *stack++); + } + + pr_emerg("\nCall Trace:"); + i = 0; + while (stack + 1 <= endstack) { + addr = *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))) { + if (i % 4 == 0) + pr_emerg("\n "); + pr_emerg(" [<%08lx>]", addr); + i++; + } + } + pr_emerg("\n"); +} + +void __init trap_init(void) +{ + /* Nothing to do here */ +} + +/* Breakpoint handler */ +asmlinkage void breakpoint_c(struct pt_regs *fp) +{ + /* + * The breakpoint entry code has moved the PC on by 4 bytes, so we must + * move it back. This could be done on the host but we do it here + * because monitor.S of JTAG gdbserver does it too. + */ + fp->ea -= 4; + _exception(SIGTRAP, fp, TRAP_BRKPT, fp->ea); +} + +#if defined(CONFIG_MMU) && !defined(CONFIG_ALIGNMENT_TRAP) +/* Alignment exception handler */ +asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause) +{ + unsigned long addr = RDCTL(CTL_BADADDR); + + cause >>= 2; + fp->ea -= 4; + + if (fixup_exception(fp)) + return; + + if (!user_mode(fp)) { + pr_alert("Unaligned access from kernel mode, this might be a hardware\n"); + pr_alert("problem, dump registers and restart the instruction\n"); + pr_alert(" BADADDR 0x%08lx\n", addr); + pr_alert(" cause %d\n", cause); + pr_alert(" op-code 0x%08lx\n", *(unsigned long *)(fp->ea)); + show_regs(fp); + return; + } + + _exception(SIGBUS, fp, BUS_ADRALN, addr); +} +#endif /* CONFIG_MMU && !CONFIG_ALIGNMENT_TRAP */ + +/* Illegal instruction handler */ +asmlinkage void handle_illegal_c(struct pt_regs *fp) +{ + fp->ea -= 4; + _exception(SIGILL, fp, ILL_ILLOPC, fp->ea); +} + +/* Supervisor instruction handler */ +asmlinkage void handle_supervisor_instr(struct pt_regs *fp) +{ + fp->ea -= 4; + _exception(SIGILL, fp, ILL_PRVOPC, fp->ea); +} + +/* Division error handler */ +asmlinkage void handle_diverror_c(struct pt_regs *fp) +{ + fp->ea -= 4; + _exception(SIGFPE, fp, FPE_INTDIV, fp->ea); +} + +/* Unhandled exception handler */ +asmlinkage void unhandled_exception(struct pt_regs *regs, int cause) +{ + unsigned long addr = RDCTL(CTL_BADADDR); + + cause /= 4; + + pr_emerg("Unhandled exception #%d in %s mode (badaddr=0x%08lx)\n", + cause, user_mode(regs) ? "user" : "kernel", addr); + + regs->ea -= 4; + show_regs(regs); + + pr_emerg("opcode: 0x%08lx\n", *(unsigned long *)(regs->ea)); + + /* TODO: What should we do here? WRS code was halting the ISS with + * WRCTL(6,1) and spinning forever afterwards. */ +} diff --git a/arch/nios2/kernel/vmlinux.lds.S b/arch/nios2/kernel/vmlinux.lds.S new file mode 100644 index 0000000000000..db9078f9b95ea --- /dev/null +++ b/arch/nios2/kernel/vmlinux.lds.S @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 Thomas Chou + * + * 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 . + * + */ +#include +#include +#include +#include + +OUTPUT_FORMAT("elf32-littlenios2", "elf32-littlenios2", "elf32-littlenios2") + +OUTPUT_ARCH(nios) +ENTRY(_start) /* Defined in head.S */ + +jiffies = jiffies_64; + +SECTIONS +{ + . = CONFIG_MEM_BASE | CONFIG_KERNEL_REGION_BASE; + + _text = .; + _stext = .; + HEAD_TEXT_SECTION + .text : { + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT + IRQENTRY_TEXT + KPROBES_TEXT + } =0 + _etext = .; + + EXCEPTION_TABLE(L1_CACHE_BYTES) + + . = ALIGN(PAGE_SIZE); + __init_begin = .; + INIT_TEXT_SECTION(PAGE_SIZE) + INIT_DATA_SECTION(PAGE_SIZE) + PERCPU_SECTION(L1_CACHE_BYTES) + __init_end = .; + + _sdata = .; + RO_DATA_SECTION(PAGE_SIZE) + RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE) + _edata = .; + + BSS_SECTION(0, 0, 0) + _end = .; + + STABS_DEBUG + DWARF_DEBUG + NOTES + + DISCARDS +} From 43792e7e01b4befb5fba8989378e0b995337fc0b Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 14 May 2013 09:47:51 +0800 Subject: [PATCH 002/201] FogBugz #111740: [PATCHv3 2/7] Integrates Nios II kernel (mm) Integrates Nios II kernel (arch/nios2) from linux-nios2.git to linux-socfpga.git. Signed-off-by: Ley Foon Tan --- arch/nios2/mm/Makefile | 12 ++ arch/nios2/mm/cacheflush-nommu.c | 258 +++++++++++++++++++++++++++ arch/nios2/mm/cacheflush.c | 221 ++++++++++++++++++++++++ arch/nios2/mm/dma-mapping-nommu.c | 140 +++++++++++++++ arch/nios2/mm/dma-mapping.c | 204 ++++++++++++++++++++++ arch/nios2/mm/extable.c | 25 +++ arch/nios2/mm/fault.c | 244 ++++++++++++++++++++++++++ arch/nios2/mm/init.c | 162 +++++++++++++++++ arch/nios2/mm/ioremap.c | 188 ++++++++++++++++++++ arch/nios2/mm/mmu_context.c | 116 +++++++++++++ arch/nios2/mm/pgtable.c | 73 ++++++++ arch/nios2/mm/tlb.c | 278 ++++++++++++++++++++++++++++++ arch/nios2/mm/uaccess.c | 162 +++++++++++++++++ 13 files changed, 2083 insertions(+) create mode 100644 arch/nios2/mm/Makefile create mode 100644 arch/nios2/mm/cacheflush-nommu.c create mode 100644 arch/nios2/mm/cacheflush.c create mode 100644 arch/nios2/mm/dma-mapping-nommu.c create mode 100644 arch/nios2/mm/dma-mapping.c create mode 100644 arch/nios2/mm/extable.c create mode 100644 arch/nios2/mm/fault.c create mode 100644 arch/nios2/mm/init.c create mode 100644 arch/nios2/mm/ioremap.c create mode 100644 arch/nios2/mm/mmu_context.c create mode 100644 arch/nios2/mm/pgtable.c create mode 100644 arch/nios2/mm/tlb.c create mode 100644 arch/nios2/mm/uaccess.c diff --git a/arch/nios2/mm/Makefile b/arch/nios2/mm/Makefile new file mode 100644 index 0000000000000..39a4a4c2b2eff --- /dev/null +++ b/arch/nios2/mm/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the Nios2-specific parts of the memory manager. +# + +obj-y := init.o + +obj-y += cacheflush$(MMU).o + +obj-$(CONFIG_MMU) += pgtable.o tlb.o uaccess.o fault.o +obj-$(CONFIG_MMU) += ioremap.o extable.o mmu_context.o + +obj-y += dma-mapping$(MMU).o \ No newline at end of file diff --git a/arch/nios2/mm/cacheflush-nommu.c b/arch/nios2/mm/cacheflush-nommu.c new file mode 100644 index 0000000000000..ae4be32db90d4 --- /dev/null +++ b/arch/nios2/mm/cacheflush-nommu.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * based on arch/m68k/mm/memory.c which is: + * + * Copyright (C) 1999-2002 Greg Ungerer + * Copyright (C) 1998 Kenneth Albanowski + * Copyright (C) 1995 Hamish Macdonald + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Define cache invalidate functions. The instruction and data cache + * will need to be flushed. Write back the dirty data cache and invalidate + * the instruction cache for the range. + * + */ +static inline void cache_invalidate_inst(unsigned long paddr, int len) +{ + if (cpuinfo.icache_size != 0 && likely(len > 0)) { + if (len >= cpuinfo.icache_size) { + __asm__ __volatile__("1:\n\t" + "flushi %0\n\t" + "sub %0,%0,%1\n\t" + "bgt %0,r0,1b\n\t" + : + : "r" (cpuinfo.icache_size), + "r" (cpuinfo.icache_line_size)); + } else { + unsigned long sset, eset; + + sset = paddr & (~(cpuinfo.icache_line_size - 1)); + eset = + (paddr + len + cpuinfo.icache_line_size - 1) & + (~(cpuinfo.icache_line_size - 1)); + + __asm__ __volatile__("1:\n\t" + "flushi %0\n\t" + "add %0,%0,%2\n\t" + "blt %0,%1,1b\n\t" + : + : "r" (sset), + "r" (eset), + "r" (cpuinfo.icache_line_size)); + } + } + __asm__ __volatile__("\tflushp\n"); +} + +static inline void cache_invalidate_data(unsigned long paddr, + unsigned long len) +{ + unsigned long cache_size, line_size; + + line_size = cpuinfo.dcache_line_size; + cache_size = cpuinfo.dcache_size; + if (cache_size == 0 || unlikely(len == 0)) + return; + + if (len >= cache_size * 2) { + /* + * Invalidating an area at least twice the data cache size. + * Write back and invalidate all dirty cache lines. + */ + __asm__ __volatile__("1:\n\t" + "flushd 0(%0)\n\t" + "sub %0,%0,%1\n\t" + "bgt %0,r0,1b\n\t" + : + : "r" (cache_size), + "r" (line_size)); + + } else { + /* + * Invalidating an area less than twice the data cache size. + * Only invalidate the desired area. + */ + unsigned long sset, eset, pend; + + pend = paddr + len; + sset = paddr & (~(line_size - 1)); + eset = pend & (~(line_size - 1)); + if (sset != paddr && sset < eset) { + /* + * The start of the area is unaligned. Write back + * and invalidate a dirty cache line that overlaps the + * start of the area. (This is done at the end if the + * same cache line extends beyond the end of the area.) + */ + __asm__ __volatile__("\tflushda 0(%0)\n" + : + : "r" (sset)); + sset += line_size; + } + if (sset < eset) { + if (line_size > 4) { + /* + * Invalidate all the cache lines that fall + * completely within the area. + */ + __asm__ __volatile__("1:\n\t" + "initda 0(%0)\n\t" + "add %0,%0,%2\n\t" + "blt %0,%1,1b\n\t" + : + : "r" (sset), + "r" (eset), + "r"(line_size)); + } else { + /* + * CPU doesn't implement "initda" when cache + * line size is 4, so use "flushda" instead, + * which will also write back dirty cache + * lines before invalidating them. + */ + __asm__ __volatile__("1:\n\t" + "flushda 0(%0)\n\t" + "add %0,%0,%2\n\t" + "blt %0,%1,1b\n\t" + : + : "r" (sset), + "r" (eset), + "r"(line_size)); + } + } + if (eset != pend) { + /* + * The end of the area is unaligned. Write back and + * invalidate a dirty cache line that overlaps the + * end of the area. + */ + __asm__ __volatile__("\tflushda 0(%0)\n" + : + : "r" (eset)); + } + } +} + +static inline void cache_push_invalidate_data(unsigned long paddr, int len) +{ + if (cpuinfo.dcache_size == 0 || unlikely(len <= 0)) + return; + + if (len >= cpuinfo.dcache_size * 2) { + __asm__ __volatile__("1:\n\t" + "flushd 0(%0)\n\t" + "sub %0,%0,%1\n\t" + "bgt %0,r0,1b\n\t" + : + : "r" (cpuinfo.dcache_size), + "r" (cpuinfo.dcache_line_size)); + + } else { + unsigned long sset, eset; + + sset = paddr & (~(cpuinfo.dcache_line_size - 1)); + eset = + (paddr + len + cpuinfo.dcache_line_size - 1) & + (~(cpuinfo.dcache_line_size - 1)); + + __asm__ __volatile__("1:\n\t" + "flushda 0(%0)\n\t" + "add %0,%0,%2\n\t" + "blt %0,%1,1b\n\t" + : + : "r" (sset), + "r" (eset), + "r"(cpuinfo.dcache_line_size)); + } +} + +/* + * cache_push() semantics: Write back any dirty cache data in the given area, + * and invalidate the range in the instruction cache. It needs not (but may) + * invalidate those entries also in the data cache. The range is defined by a + * _physical_ address. + */ + +void cache_push(unsigned long paddr, int len) +{ + cache_push_invalidate_data(paddr, len); + cache_invalidate_inst(paddr, len); +} + +void dcache_push_all(void) +{ + if (cpuinfo.dcache_size != 0) { + __asm__ __volatile__("1:\n\t" + "flushd 0(%0)\n\t" + "sub %0,%0,%1\n\t" + "bgt %0,r0,1b\n\t" + : + : "r" (cpuinfo.dcache_size), + "r" (cpuinfo.dcache_line_size)); + } +} + +void icache_push_all(void) +{ + if (cpuinfo.icache_size != 0) { + __asm__ __volatile__("1:\n\t" + "flushi %0\n\t" + "sub %0,%0,%1\n\t" + "bgt %0,r0,1b\n\t" + : + : "r" (cpuinfo.icache_size), + "r" (cpuinfo.icache_line_size)); + } + __asm__ __volatile__("\tflushp\n"); +} + +/* + * dcache_push() semantics: Write back and dirty data cache and invalidate + * the range. + */ +void dcache_push(unsigned long vaddr, int len) +{ + cache_push_invalidate_data(vaddr, len); +} +EXPORT_SYMBOL(dcache_push); + +/* + * icache_push() semantics: Invalidate instruction cache in the range. + * Need to write back dirty data cache lines first. As a side-effect, + * this also invalidates the affected data lines. + */ +void icache_push(unsigned long vaddr, int len) +{ + cache_push_invalidate_data(vaddr, len); + cache_invalidate_inst(vaddr, len); +} + +/* + * nios2_clear_dcache_range() semantics: Invalidate a range of virtual + * addresses in the data cache, writing back as little as possible because + * the area is about to be overwritten, e.g. by a DMA transfer. + */ +void nios2_clear_dcache_range(unsigned long vstart, unsigned long vend) +{ + cache_invalidate_data(vstart, vend - vstart); +} diff --git a/arch/nios2/mm/cacheflush.c b/arch/nios2/mm/cacheflush.c new file mode 100644 index 0000000000000..36d027f41da32 --- /dev/null +++ b/arch/nios2/mm/cacheflush.c @@ -0,0 +1,221 @@ +/* + * 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. + * + * Copyright (C) 2009, Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + */ + +#include +#include +#include +#include + +#include +#include + +static void __flush_dcache(unsigned long start, unsigned long end) +{ + unsigned long addr; + + start &= ~(cpuinfo.dcache_line_size - 1); + end += (cpuinfo.dcache_line_size - 1); + end &= ~(cpuinfo.dcache_line_size - 1); + + if (end > start + cpuinfo.dcache_size) + end = start + cpuinfo.dcache_size; + + for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) { + __asm__ __volatile__ (" flushd 0(%0)\n" + : /* Outputs */ + : /* Inputs */ "r"(addr) + /* : No clobber */); + } +} + +static void __flush_icache(unsigned long start, unsigned long end) +{ + unsigned long addr; + + start &= ~(cpuinfo.icache_line_size - 1); + end += (cpuinfo.icache_line_size - 1); + end &= ~(cpuinfo.icache_line_size - 1); + + if (end > start + cpuinfo.icache_size) + end = start + cpuinfo.icache_size; + + for (addr = start; addr < end; addr += cpuinfo.icache_line_size) { + __asm__ __volatile__ (" flushi %0\n" + : /* Outputs */ + : /* Inputs */ "r"(addr) + /* : No clobber */); + } + __asm__ __volatile(" flushp\n"); +} + +static void flush_aliases(struct address_space *mapping, struct page *page) +{ + struct mm_struct *mm = current->active_mm; + struct vm_area_struct *mpnt; + pgoff_t pgoff; + + pgoff = page->index; + + flush_dcache_mmap_lock(mapping); + vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) { + unsigned long offset; + + if (mpnt->vm_mm != mm) + continue; + if (!(mpnt->vm_flags & VM_MAYSHARE)) + continue; + + offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; + flush_cache_page(mpnt, mpnt->vm_start + offset, + page_to_pfn(page)); + } + flush_dcache_mmap_unlock(mapping); +} + +void flush_cache_all(void) +{ + __flush_dcache(0, cpuinfo.dcache_size); + __flush_icache(0, cpuinfo.icache_size); +} + +void flush_cache_mm(struct mm_struct *mm) +{ + flush_cache_all(); +} + +void flush_cache_dup_mm(struct mm_struct *mm) +{ + flush_cache_all(); +} + +void flush_icache_range(unsigned long start, unsigned long end) +{ + __flush_dcache(start, end); + __flush_icache(start, end); +} + +void flush_dcache_range(unsigned long start, unsigned long end) +{ + __flush_dcache(start, end); + /* FIXME: Maybe we should remove __flush_icache ? */ + __flush_icache(start, end); +} +EXPORT_SYMBOL(flush_dcache_range); + +void flush_cache_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + __flush_dcache(start, end); + if (vma == NULL || (vma->vm_flags & VM_EXEC)) + __flush_icache(start, end); +} + +void flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + unsigned long start = (unsigned long) page_address(page); + unsigned long end = start + PAGE_SIZE; + + __flush_dcache(start, end); + __flush_icache(start, end); +} + +void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, + unsigned long pfn) +{ + unsigned long start = vmaddr; + unsigned long end = start + PAGE_SIZE; + + __flush_dcache(start, end); + if (vma->vm_flags & VM_EXEC) + __flush_icache(start, end); +} + +void flush_dcache_page(struct page *page) +{ + struct address_space *mapping = page_mapping(page); + + /* Flush this page if there are aliases. */ + if (mapping) { + if (!mapping_mapped(mapping)) { + clear_bit(PG_arch_1, &page->flags); + } else if (mapping) { + unsigned long start = (unsigned long)page_address(page); + __flush_dcache(start, start + PAGE_SIZE); + flush_aliases(mapping, page); + } + } +} +EXPORT_SYMBOL(flush_dcache_page); + +void update_mmu_cache(struct vm_area_struct *vma, + unsigned long address, pte_t *pte) +{ + unsigned long pfn = pte_pfn(*pte); + struct page *page; + + if (!pfn_valid(pfn)) + return; + + page = pfn_to_page(pfn); + + if (!PageReserved(page) && !test_bit(PG_arch_1, &page->flags)) { + unsigned long start = page_to_virt(page); + struct address_space *mapping; + + __flush_dcache(start, start + PAGE_SIZE); + + mapping = page_mapping(page); + if (mapping) + flush_aliases(mapping, page); + set_bit(PG_arch_1, &page->flags); + } +} + +void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, + struct page *to) +{ + __flush_dcache(vaddr, vaddr + PAGE_SIZE); + __flush_icache(vaddr, vaddr + PAGE_SIZE); + copy_page(vto, vfrom); + __flush_dcache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE); + /* FIXME: really necessary? */ + __flush_icache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE); +} + +void clear_user_page(void *addr, unsigned long vaddr, struct page *page) +{ + __flush_dcache(vaddr, vaddr + PAGE_SIZE); + __flush_icache(vaddr, vaddr + PAGE_SIZE); + clear_page(addr); + __flush_dcache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE); + /* FIXME: really necessary? */ + __flush_icache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE); +} + +void copy_from_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long user_vaddr, + void *dst, void *src, int len) +{ + flush_cache_page(vma, user_vaddr, page_to_pfn(page)); + memcpy(dst, src, len); + __flush_dcache((unsigned long)src, (unsigned long)src + len); + if (vma->vm_flags & VM_EXEC) + __flush_icache((unsigned long)src, (unsigned long)src + len); +} + +void copy_to_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long user_vaddr, + void *dst, void *src, int len) +{ + flush_cache_page(vma, user_vaddr, page_to_pfn(page)); + memcpy(dst, src, len); + __flush_dcache((unsigned long)dst, (unsigned long)dst + len); + if (vma->vm_flags & VM_EXEC) + __flush_icache((unsigned long)dst, (unsigned long)dst + len); +} diff --git a/arch/nios2/mm/dma-mapping-nommu.c b/arch/nios2/mm/dma-mapping-nommu.c new file mode 100644 index 0000000000000..06ae5f1e7c7b2 --- /dev/null +++ b/arch/nios2/mm/dma-mapping-nommu.c @@ -0,0 +1,140 @@ +/* + * Dynamic DMA mapping support. + * + * We never have any address translations to worry about, so this + * is just alloc/free. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp) +{ + void *ret; + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (dev == NULL || (*dev->dma_mask < 0xffffffff)) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = (dma_addr_t)ret; + ret = ioremap((unsigned long)ret, size); + } + return ret; +} +EXPORT_SYMBOL(dma_alloc_coherent); + +void dma_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)dma_handle, get_order(size)); +} +EXPORT_SYMBOL(dma_free_coherent); + +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir) +{ +} +EXPORT_SYMBOL(dma_sync_single_for_cpu); + +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir) +{ + unsigned long addr; + + BUG_ON(dir == DMA_NONE); + + addr = dma_handle + PAGE_OFFSET; + __dma_sync(addr, size, dir); +} +EXPORT_SYMBOL(dma_sync_single_for_device); + +void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction dir) +{ +} +EXPORT_SYMBOL(dma_sync_single_range_for_cpu); + +void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction dir) +{ + unsigned long addr; + + BUG_ON(dir == DMA_NONE); + + addr = dma_handle + offset + PAGE_OFFSET; + __dma_sync(addr, size, dir); +} +EXPORT_SYMBOL(dma_sync_single_range_for_device); + +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ +} +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction dir) +{ + int i; + + BUG_ON(dir == DMA_NONE); + + /* Make sure that gcc doesn't leave the empty loop body. */ + for_each_sg(sg, sg, nelems, i) { + __dma_sync((unsigned long)sg_virt(sg), sg->length, dir); + } +} +EXPORT_SYMBOL(dma_sync_sg_for_device); + +dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir) +{ + return dma_map_single(dev, page_address(page) + offset, size, dir); +} +EXPORT_SYMBOL(dma_map_page); + +void dma_unmap_page(struct device *dev, dma_addr_t address, + size_t size, enum dma_data_direction dir) +{ +} +EXPORT_SYMBOL(dma_unmap_page); + +int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir) +{ + int i; + + BUG_ON(dir == DMA_NONE); + + for (i = 0; i < nents; i++, sg++) { + sg->dma_address = dma_map_single(dev, sg_virt(sg), + sg->length, dir); + } + + return nents; +} +EXPORT_SYMBOL(dma_map_sg); + +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ + BUG_ON(dir == DMA_NONE); +} +EXPORT_SYMBOL(dma_unmap_sg); diff --git a/arch/nios2/mm/dma-mapping.c b/arch/nios2/mm/dma-mapping.c new file mode 100644 index 0000000000000..76d23283cc240 --- /dev/null +++ b/arch/nios2/mm/dma-mapping.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * Based on DMA code from MIPS. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp) +{ + void *ret; + + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + /* optimized page clearing */ + gfp |= __GFP_ZERO; + + if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) + gfp |= GFP_DMA; + + ret = (void *) __get_free_pages(gfp, get_order(size)); + if (ret != NULL) { + *dma_handle = virt_to_phys(ret); + flush_dcache_range((unsigned long) ret, + (unsigned long) ret + size); + ret = UNCAC_ADDR(ret); + } + + 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) CAC_ADDR((unsigned long) vaddr); + free_pages(addr, get_order(size)); +} +EXPORT_SYMBOL(dma_free_coherent); + +int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ + int i; + + BUG_ON(direction == DMA_NONE); + + for_each_sg(sg, sg, nents, i) { + unsigned long addr; + + addr = (unsigned long) sg_virt(sg); + if (addr) { + __dma_sync(addr, sg->length, direction); + sg->dma_address = sg_phys(sg); + } + } + + return nents; +} +EXPORT_SYMBOL(dma_map_sg); + +dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + unsigned long addr; + + BUG_ON(direction == DMA_NONE); + + addr = (unsigned long) page_address(page) + offset; + __dma_sync(addr, size, direction); + + return page_to_phys(page) + offset; +} +EXPORT_SYMBOL(dma_map_page); + +void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, + enum dma_data_direction direction) +{ + BUG_ON(direction == DMA_NONE); + + if (direction != DMA_TO_DEVICE) { + unsigned long addr; + + addr = dma_address + PAGE_OFFSET; + __dma_sync(addr, size, direction); + } +} +EXPORT_SYMBOL(dma_unmap_page); + +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, + enum dma_data_direction direction) +{ + unsigned long addr; + int i; + + BUG_ON(direction == DMA_NONE); + + if (direction == DMA_TO_DEVICE) + return; + + for_each_sg(sg, sg, nhwentries, i) { + addr = (unsigned long) sg_virt(sg); + if (addr) + __dma_sync(addr, sg->length, direction); + } +} +EXPORT_SYMBOL(dma_unmap_sg); + +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) +{ + unsigned long addr; + + BUG_ON(direction == DMA_NONE); + + addr = dma_handle + PAGE_OFFSET; + __dma_sync(addr, size, direction); +} +EXPORT_SYMBOL(dma_sync_single_for_cpu); + +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) +{ + unsigned long addr; + + BUG_ON(direction == DMA_NONE); + + addr = dma_handle + PAGE_OFFSET; + __dma_sync(addr, size, direction); +} +EXPORT_SYMBOL(dma_sync_single_for_device); + +void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + unsigned long addr; + + BUG_ON(direction == DMA_NONE); + + addr = dma_handle + offset + PAGE_OFFSET; + __dma_sync(addr, size, direction); +} +EXPORT_SYMBOL(dma_sync_single_range_for_cpu); + +void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + unsigned long addr; + + BUG_ON(direction == DMA_NONE); + + addr = dma_handle + offset + PAGE_OFFSET; + __dma_sync(addr, size, direction); +} +EXPORT_SYMBOL(dma_sync_single_range_for_device); + +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + int i; + + BUG_ON(direction == DMA_NONE); + + /* Make sure that gcc doesn't leave the empty loop body. */ + for_each_sg(sg, sg, nelems, i) { + __dma_sync((unsigned long)sg_virt(sg), + sg->length, direction); + } +} +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction) +{ + int i; + + BUG_ON(direction == DMA_NONE); + + /* Make sure that gcc doesn't leave the empty loop body. */ + for_each_sg(sg, sg, nelems, i) + __dma_sync((unsigned long)sg_virt(sg), sg->length, direction); + +} +EXPORT_SYMBOL(dma_sync_sg_for_device); diff --git a/arch/nios2/mm/extable.c b/arch/nios2/mm/extable.c new file mode 100644 index 0000000000000..4d2fc5a589d09 --- /dev/null +++ b/arch/nios2/mm/extable.c @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010, Tobias Klauser + * Copyright (C) 2009, Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * 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. + */ + +#include +#include + +int fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + fixup = search_exception_tables(regs->ea); + if (fixup) { + regs->ea = fixup->fixup; + return 1; + } + + return 0; +} diff --git a/arch/nios2/mm/fault.c b/arch/nios2/mm/fault.c new file mode 100644 index 0000000000000..75db12ee943c0 --- /dev/null +++ b/arch/nios2/mm/fault.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * based on arch/mips/mm/fault.c which is: + * + * Copyright (C) 1995-2000 Ralf Baechle + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define EXC_SUPERV_INSN_ACCESS 9 /* Supervisor only instruction address */ +#define EXC_SUPERV_DATA_ACCESS 11 /* Supervisor only data address */ +#define EXC_X_PROTECTION_FAULT 13 /* TLB permission violation (x) */ +#define EXC_R_PROTECTION_FAULT 14 /* TLB permission violation (r) */ +#define EXC_W_PROTECTION_FAULT 15 /* TLB permission violation (w) */ + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause, + unsigned long address) +{ + struct vm_area_struct *vma = NULL; + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; + int code = SEGV_MAPERR; + int fault; + unsigned int flags = 0; + + cause >>= 2; + + /* Restart the instruction */ + regs->ea -= 4; + + /* + * 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. + */ + if (unlikely(address >= VMALLOC_START && address <= VMALLOC_END)) { + if (user_mode(regs)) + goto bad_area_nosemaphore; + else + goto vmalloc_fault; + } + + if (unlikely(address >= TASK_SIZE)) + goto bad_area_nosemaphore; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_atomic() || !mm) + goto bad_area_nosemaphore; + + 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 (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: + code = SEGV_ACCERR; + + switch (cause) { + case EXC_SUPERV_INSN_ACCESS: + goto bad_area; + case EXC_SUPERV_DATA_ACCESS: + goto bad_area; + case EXC_X_PROTECTION_FAULT: + if (!(vma->vm_flags & VM_EXEC)) + goto bad_area; + break; + case EXC_R_PROTECTION_FAULT: + if (!(vma->vm_flags & VM_READ)) + goto bad_area; + break; + case EXC_W_PROTECTION_FAULT: + flags = FAULT_FLAG_WRITE; + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + break; + } + +survive: + /* + * 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, flags); + 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) + tsk->maj_flt++; + else + tsk->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); + +bad_area_nosemaphore: + /* User mode accesses just cause a SIGSEGV */ + if (user_mode(regs)) { + _exception(SIGSEGV, regs, code, address); + return; + } + +no_context: + /* 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); + + pr_alert("Unable to handle kernel %s at virtual address %08lx", + address < PAGE_SIZE ? "NULL pointer dereference" : + "paging request", address); + pr_alert("ea = %08lx, ra = %08lx, cause = %ld\n", regs->ea, regs->ra, + cause); + panic("Oops"); + return; + +/* + * 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); + if (is_global_init(tsk)) { + yield(); + down_read(&mm->mmap_sem); + goto survive; + } + pr_info("VM: killing process %s\n", tsk->comm); + if (user_mode(regs)) + do_group_exit(SIGKILL); + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + + /* Kernel mode? Handle exceptions or die */ + if (!user_mode(regs)) + goto no_context; + + _exception(SIGBUS, regs, BUS_ADRERR, address); + 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 offset = pgd_index(address); + pgd_t *pgd, *pgd_k; + pud_t *pud, *pud_k; + pmd_t *pmd, *pmd_k; + pte_t *pte_k; + +#if 1 + /* FIXME: Is this entirely correct ? */ + pgd = pgd_current + offset; +#else + pgd = ¤t->mm->pgd[offset]; +#endif + pgd_k = init_mm.pgd + offset; + + if (!pgd_present(*pgd_k)) + goto no_context; + set_pgd(pgd, *pgd_k); + + pud = pud_offset(pgd, address); + pud_k = pud_offset(pgd_k, address); + if (!pud_present(*pud_k)) + goto no_context; + pmd = pmd_offset(pud, address); + pmd_k = pmd_offset(pud_k, address); + if (!pmd_present(*pmd_k)) + goto no_context; + set_pmd(pmd, *pmd_k); + + pte_k = pte_offset_kernel(pmd_k, address); + if (!pte_present(*pte_k)) + goto no_context; + + flush_tlb_one(address); + return; + } +} diff --git a/arch/nios2/mm/init.c b/arch/nios2/mm/init.c new file mode 100644 index 0000000000000..4e3ca895af7c1 --- /dev/null +++ b/arch/nios2/mm/init.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * based on arch/m68k/mm/init.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MMU +pgd_t *pgd_current; +#endif + +/* + * paging_init() continues the virtual memory environment setup which + * was begun by the code in arch/head.S. + * The parameters are pointers to where to stick the starting and ending + * addresses of available kernel virtual memory. + */ +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES]; + unsigned long start_mem, end_mem; + + memset(zones_size, 0, sizeof(zones_size)); + + /* + * Make sure start_mem is page aligned, otherwise bootmem and + * page_alloc get different views of the world. + */ +#ifdef CONFIG_MMU + start_mem = PHYS_OFFSET; + end_mem = memory_end; +#else + start_mem = PAGE_ALIGN(memory_start); + end_mem = memory_end & PAGE_MASK; +#endif /* CONFIG_MMU */ + +#ifdef CONFIG_MMU + pagetable_init(); + pgd_current = swapper_pg_dir; +#endif + + /* + * Set up SFC/DFC registers (user data space). + */ +#ifndef CONFIG_MMU + set_fs(KERNEL_DS); +#endif + +#ifdef CONFIG_MMU + zones_size[ZONE_DMA] = ((end_mem - start_mem) >> PAGE_SHIFT); +#else + zones_size[ZONE_DMA] = (end_mem - PAGE_OFFSET) >> PAGE_SHIFT; +#endif /* CONFIG_MMU */ + + /* pass the memory from the bootmem allocator to the main allocator */ + free_area_init(zones_size); +} + +void __init mem_init(void) +{ + unsigned int codek = 0, datak = 0; + unsigned long end_mem = memory_end; /* this must not include + kernel stack at top */ + + pr_debug("mem_init: start=%lx, end=%lx\n", memory_start, memory_end); + + end_mem &= PAGE_MASK; + high_memory = __va(end_mem); + +#ifdef CONFIG_MMU + max_mapnr = ((unsigned long)end_mem) >> PAGE_SHIFT; +#else + max_mapnr = (((unsigned long)high_memory) - PAGE_OFFSET) >> PAGE_SHIFT; +#endif /* CONFIG_MMU */ + num_physpages = max_mapnr; + pr_debug("We have %ld pages of RAM\n", num_physpages); + + /* this will put all memory onto the freelists */ + totalram_pages = free_all_bootmem(); + + codek = (_etext - _stext) >> 10; + datak = (_end - _etext) >> 10; + + pr_info("Memory available: %luk/%luk RAM (%dk kernel code, %dk data)\n", + nr_free_pages() << (PAGE_SHIFT - 10), + (unsigned long)((_end - _stext) >> 10), + codek, datak); +} + +#ifdef CONFIG_MMU +void __init mmu_init(void) +{ + flush_tlb_all(); +} +#endif + +static void __init free_init_pages(const char *what, unsigned long start, + unsigned long end) +{ + unsigned long addr; + + /* next to check that the page we free is not a partial page */ + for (addr = start; addr + PAGE_SIZE <= end; addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + init_page_count(virt_to_page(addr)); + free_page(addr); + totalram_pages++; + } + + pr_notice("Freeing %s: %ldk freed (0x%lx - 0x%lx)\n", + what, (end - start) >> 10, start, end); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void __init free_initrd_mem(unsigned long start, unsigned long end) +{ + free_init_pages("initrd memory", start, end); +} +#endif + +void __init_refok free_initmem(void) +{ + free_init_pages("unused kernel memory", + (unsigned long)(&__init_begin), + (unsigned long)(&__init_end)); +} + +#ifdef CONFIG_MMU + +#define __page_aligned(order) __aligned(PAGE_SIZE << (order)) +pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned(PGD_ORDER); +pte_t invalid_pte_table[PTRS_PER_PTE] __page_aligned(PTE_ORDER); + +#endif /* CONFIG_MMU */ diff --git a/arch/nios2/mm/ioremap.c b/arch/nios2/mm/ioremap.c new file mode 100644 index 0000000000000..72bec05f6a1d0 --- /dev/null +++ b/arch/nios2/mm/ioremap.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * based on arch/m68knommu/mm/kmap.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +static inline void remap_area_pte(pte_t *pte, unsigned long address, + unsigned long size, unsigned long phys_addr, + unsigned long flags) +{ + unsigned long end; + unsigned long pfn; + pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | _PAGE_READ + | _PAGE_WRITE | flags); + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + if (address >= end) + BUG(); + pfn = PFN_DOWN(phys_addr); + do { + if (!pte_none(*pte)) { + pr_err("remap_area_pte: page already exists\n"); + BUG(); + } + set_pte(pte, pfn_pte(pfn, pgprot)); + address += PAGE_SIZE; + pfn++; + pte++; + } while (address && (address < end)); +} + +static inline int remap_area_pmd(pmd_t *pmd, unsigned long address, + unsigned long size, unsigned long phys_addr, + unsigned long flags) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + if (address >= end) + BUG(); + do { + pte_t *pte = pte_alloc_kernel(pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, + flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address && (address < end)); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + int error; + pgd_t *dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + if (address >= end) + BUG(); + do { + pud_t *pud; + pmd_t *pmd; + + error = -ENOMEM; + pud = pud_alloc(&init_mm, dir, address); + if (!pud) + break; + pmd = pmd_alloc(&init_mm, pud, address); + if (!pmd) + break; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + break; + error = 0; + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + flush_tlb_all(); + return error; +} + +#define IS_MAPPABLE_UNCACHEABLE(addr) (addr < 0x20000000UL) + +/* + * Map some physical address range into the kernel address space. + */ +void __iomem *__ioremap(unsigned long phys_addr, unsigned long size, + unsigned long cacheflag) +{ + struct vm_struct *area; + unsigned long offset; + unsigned long last_addr; + void *addr; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + + if (!size || last_addr < phys_addr) + return NULL; + + /* Don't allow anybody to remap normal RAM that we're using */ + if (phys_addr > PHYS_OFFSET && phys_addr < virt_to_phys(high_memory)) { + char *t_addr, *t_end; + struct page *page; + + t_addr = __va(phys_addr); + t_end = t_addr + (size - 1); + for (page = virt_to_page(t_addr); + page <= virt_to_page(t_end); page++) + if (!PageReserved(page)) + return NULL; + } + + /* + * Map uncached objects in the low part of address space to + * CONFIG_IO_REGION_BASE + */ + if (IS_MAPPABLE_UNCACHEABLE(phys_addr) && + IS_MAPPABLE_UNCACHEABLE(last_addr) && + !(cacheflag & _PAGE_CACHED)) + return (void __iomem *)(CONFIG_IO_REGION_BASE + phys_addr); + + /* Mappings have to be page-aligned */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr + 1) - phys_addr; + + /* Ok, go for it */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + addr = area->addr; + if (remap_area_pages((unsigned long) addr, phys_addr, size, + cacheflag)) { + vunmap(addr); + return NULL; + } + return (void __iomem *) (offset + (char *)addr); +} +EXPORT_SYMBOL(__ioremap); + +/* + * __iounmap unmaps nearly everything, so be careful + * it doesn't free currently pointer/page tables anymore but it + * wasn't used anyway and might be added later. + */ +void __iounmap(void __iomem *addr) +{ + struct vm_struct *p; + + if ((unsigned long) addr > CONFIG_IO_REGION_BASE) /* FIXME */ + return; + + p = remove_vm_area((void *) (PAGE_MASK & (unsigned long __force) addr)); + if (!p) + pr_err("iounmap: bad address %p\n", addr); + kfree(p); +} +EXPORT_SYMBOL(__iounmap); diff --git a/arch/nios2/mm/mmu_context.c b/arch/nios2/mm/mmu_context.c new file mode 100644 index 0000000000000..45d6b9c58d677 --- /dev/null +++ b/arch/nios2/mm/mmu_context.c @@ -0,0 +1,116 @@ +/* + * MMU context handling. + * + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * 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. + */ + +#include + +#include +#include +#include + +/* The pids position and mask in context */ +#define PID_SHIFT 0 +#define PID_BITS (cpuinfo.tlb_pid_num_bits) +#define PID_MASK ((1UL << PID_BITS) - 1) + +/* The versions position and mask in context */ +#define VERSION_BITS (32 - PID_BITS) +#define VERSION_SHIFT (PID_SHIFT + PID_BITS) +#define VERSION_MASK ((1UL << VERSION_BITS) - 1) + +/* Return the version part of a context */ +#define CTX_VERSION(c) (((c) >> VERSION_SHIFT) & VERSION_MASK) + +/* Return the pid part of a context */ +#define CTX_PID(c) (((c) >> PID_SHIFT) & PID_MASK) + +/* Value of the first context (version 1, pid 0) */ +#define FIRST_CTX ((1UL << VERSION_SHIFT) | (0 << PID_SHIFT)) + +static mm_context_t next_mmu_context; + +/* + * Initialize MMU context management stuff. + */ +void __init mmu_context_init(void) +{ + /* We need to set this here because the value depends on runtime data + * from cpuinfo */ + next_mmu_context = FIRST_CTX; +} + +/* + * Set new context (pid), keep way + */ +static void set_context(mm_context_t context) +{ + set_mmu_pid(CTX_PID(context)); +} + +static mm_context_t get_new_context(void) +{ + /* Return the next pid */ + next_mmu_context += (1UL << PID_SHIFT); + + /* If the pid field wraps around we increase the version and + * flush the tlb */ + if (unlikely(CTX_PID(next_mmu_context) == 0)) { + /* Version is incremented since the pid increment above + * overflows info version */ + flush_cache_all(); + flush_tlb_all(); + } + + /* If the version wraps we start over with the first generation, we do + * not need to flush the tlb here since it's always done above */ + if (unlikely(CTX_VERSION(next_mmu_context) == 0)) + next_mmu_context = FIRST_CTX; + + return next_mmu_context; +} + +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) +{ + unsigned long flags; + + local_irq_save(flags); + + /* If the process context we are swapping in has a different context + * generation then we have it should get a new generation/pid */ + if (unlikely(CTX_VERSION(next->context) != + CTX_VERSION(next_mmu_context))) + next->context = get_new_context(); + + /* Save the current pgd so the fast tlb handler can find it */ + pgd_current = next->pgd; + + /* Set the current context */ + set_context(next->context); + + local_irq_restore(flags); +} + +/* + * After we have set current->mm to a new value, this activates + * the context for the new mm so we see the new mappings. + */ +void activate_mm(struct mm_struct *prev, struct mm_struct *next) +{ + next->context = get_new_context(); + set_context(next->context); + pgd_current = next->pgd; +} + +unsigned long get_pid_from_context(mm_context_t *context) +{ + return CTX_PID((*context)); +} diff --git a/arch/nios2/mm/pgtable.c b/arch/nios2/mm/pgtable.c new file mode 100644 index 0000000000000..21e865f1079ab --- /dev/null +++ b/arch/nios2/mm/pgtable.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2009 Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * 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. + */ + +#include +#include + +#include +#include + +/* pteaddr: + * ptbase | vpn* | zero + * 31-22 | 21-2 | 1-0 + * + * *vpn is preserved on double fault + * + * tlbacc: + * IG |*flags| pfn + * 31-25|24-20 | 19-0 + * + * *crwxg + * + * tlbmisc: + * resv |way |rd | we|pid |dbl|bad|perm|d + * 31-24 |23-20 |19 | 20|17-4|3 |2 |1 |0 + * + */ + +/* + * Initialize a new pgd / pmd table with invalid pointers. + */ +static void pgd_init(pgd_t *pgd) +{ + unsigned long *p = (unsigned long *) pgd; + int i; + + for (i = 0; i < USER_PTRS_PER_PGD; i += 8) { + p[i + 0] = (unsigned long) invalid_pte_table; + p[i + 1] = (unsigned long) invalid_pte_table; + p[i + 2] = (unsigned long) invalid_pte_table; + p[i + 3] = (unsigned long) invalid_pte_table; + p[i + 4] = (unsigned long) invalid_pte_table; + p[i + 5] = (unsigned long) invalid_pte_table; + p[i + 6] = (unsigned long) invalid_pte_table; + p[i + 7] = (unsigned long) invalid_pte_table; + } +} + +pgd_t *pgd_alloc(struct mm_struct *mm) +{ + pgd_t *ret, *init; + ret = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_ORDER); + if (ret) { + init = pgd_offset(&init_mm, 0UL); + pgd_init(ret); + memcpy(ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + } + + return ret; +} + +void __init pagetable_init(void) +{ + /* Initialize the entire pgd. */ + pgd_init(swapper_pg_dir); + pgd_init(swapper_pg_dir + USER_PTRS_PER_PGD); +} diff --git a/arch/nios2/mm/tlb.c b/arch/nios2/mm/tlb.c new file mode 100644 index 0000000000000..fc6fabbf6d56b --- /dev/null +++ b/arch/nios2/mm/tlb.c @@ -0,0 +1,278 @@ +/* + * Nios2 TLB handling + * + * Copyright (C) 2009, Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TLB_INDEX_MASK \ + ((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \ + << PAGE_SHIFT) + +/* Used as illegal PHYS_ADDR for TLB mappings + */ +/* FIXME: ((1UL << DATA_ADDR_WIDTH) - 1) + */ +#define MAX_PHYS_ADDR 0 + +/* bit definitions for TLBMISC register */ +#define PID_SHIFT 4 +#define PID_MASK ((1UL << cpuinfo.tlb_pid_num_bits) - 1) + +#define WAY_SHIFT 20 +#define WAY_MASK 0xf + +/* + * All entries common to a mm share an asid. To effectively flush these + * entries, we just bump the asid. + */ +void flush_tlb_mm(struct mm_struct *mm) +{ + if (current->mm == mm) + flush_tlb_all(); + else + memset(&mm->context, 0, sizeof(mm_context_t)); +} + +/* + * This one is only used for pages with the global bit set so we don't care + * much about the ASID. + * + * FIXME: This is proken, to prove it mmap a read/write-page, mprotect it to + * read only then write to it, the tlb-entry will not be flushed. + * + */ +void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid) +{ + unsigned int way; + unsigned long org_misc; + + pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); + + /* remember pid/way until we return. + * CHECKME: is there a race here when writing org_misc back? */ + org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | + (WAY_MASK << WAY_SHIFT))); + + WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2); + + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { + unsigned long pteaddr; + unsigned long tlbmisc; + unsigned long pid; + + WRCTL(CTL_TLBMISC, (1UL << 19) | (way << 20)); + pteaddr = RDCTL(CTL_PTEADDR); + tlbmisc = RDCTL(CTL_TLBMISC); + pid = (tlbmisc >> PID_SHIFT) & PID_MASK; + if (((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) && + pid == mmu_pid) { + unsigned long vaddr = CONFIG_IO_REGION_BASE + + ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) + + (addr & TLB_INDEX_MASK); + pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n", + vaddr, way, pid); + + WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2); + WRCTL(CTL_TLBMISC, (1UL << 18) | (way << 20) | + (pid << PID_SHIFT)); + WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); + } + } + + WRCTL(CTL_TLBMISC, org_misc); +} + +void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context); + + while (start < end) { + flush_tlb_one_pid(start, mmu_pid); + start += PAGE_SIZE; + } +} + +void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + while (start < end) { + flush_tlb_one(start); + start += PAGE_SIZE; + } +} + +/* + * This one is only used for pages with the global bit set so we don't care + * much about the ASID. + */ +void flush_tlb_one(unsigned long addr) +{ + unsigned int way; + unsigned long pid, org_misc; + + pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); + + /* remember pid/way until we return. + * CHECKME: is there a race here when writing org_misc back? */ + org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | + (WAY_MASK << WAY_SHIFT))); + + WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2); + + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { + unsigned long pteaddr; + unsigned long tlbmisc; + + WRCTL(CTL_TLBMISC, (1UL << 19) | (way << 20)); + pteaddr = RDCTL(CTL_PTEADDR); + tlbmisc = RDCTL(CTL_TLBMISC); + + if ((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) { + unsigned long vaddr = CONFIG_IO_REGION_BASE + + ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) + + (addr & TLB_INDEX_MASK); + + pid = (tlbmisc >> PID_SHIFT) & PID_MASK; + pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n", + vaddr, way, pid); + + WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2); + WRCTL(CTL_TLBMISC, (1UL << 18) | (way << 20) | + (pid << PID_SHIFT)); + WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); + } + } + + WRCTL(CTL_TLBMISC, org_misc); +} + +void dump_tlb_line(unsigned long line) +{ + unsigned int way; + unsigned long org_misc; + + pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line, + line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2)); + + /* remember pid/way until we return */ + org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | + (WAY_MASK << WAY_SHIFT))); + + WRCTL(CTL_PTEADDR, line << 2); + + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { + unsigned long pteaddr; + unsigned long tlbmisc; + unsigned long tlbacc; + + WRCTL(CTL_TLBMISC, (1UL << 19) | (way << 20)); + pteaddr = RDCTL(CTL_PTEADDR); + tlbmisc = RDCTL(CTL_TLBMISC); + tlbacc = RDCTL(CTL_TLBACC); + + if ((tlbacc << PAGE_SHIFT) != (MAX_PHYS_ADDR & PAGE_MASK)) { + pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n", + way, + (pteaddr << (PAGE_SHIFT-2)), + (tlbacc << PAGE_SHIFT), + (tlbmisc >> PID_SHIFT) & PID_MASK, + (tlbacc & _PAGE_READ ? 'r' : '-'), + (tlbacc & _PAGE_WRITE ? 'w' : '-'), + (tlbacc & _PAGE_EXEC ? 'x' : '-'), + (tlbacc & _PAGE_GLOBAL ? 'g' : '-'), + (tlbacc & _PAGE_CACHED ? 'c' : '-')); + } + } + + WRCTL(CTL_TLBMISC, org_misc); +} + +void dump_tlb(void) +{ + unsigned int i; + for (i = 0; i < cpuinfo.tlb_num_lines; i++) + dump_tlb_line(i); +} + +void flush_tlb_pid(unsigned long pid) +{ + unsigned int line; + unsigned int way; + unsigned long org_misc; + + /* remember pid/way until we return */ + org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | + (WAY_MASK << WAY_SHIFT))); + + for (line = 0; line < cpuinfo.tlb_num_lines; line++) { + /* FIXME: << TLB_WAY_BITS should probably not be here */ + WRCTL(CTL_PTEADDR, line << 2); + + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { + unsigned long pteaddr; + unsigned long tlbmisc; + unsigned long tlbacc; + + WRCTL(CTL_TLBMISC, (1UL << 19) | (way << 20)); + pteaddr = RDCTL(CTL_PTEADDR); + tlbmisc = RDCTL(CTL_TLBMISC); + tlbacc = RDCTL(CTL_TLBACC); + + if (((tlbmisc>>PID_SHIFT)&PID_MASK) == pid) { + WRCTL(CTL_TLBMISC, (1UL << 18) | (way << 20) | + (pid << PID_SHIFT)); + WRCTL(CTL_TLBACC, + (MAX_PHYS_ADDR >> PAGE_SHIFT)); + } + } + + WRCTL(CTL_TLBMISC, org_misc); + } +} + +void flush_tlb_all(void) +{ + int i; + unsigned long vaddr = CONFIG_IO_REGION_BASE; + unsigned int way; + unsigned long org_misc; + + /* remember pid/way */ + org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | + (WAY_MASK << WAY_SHIFT))); + + /* Map each TLB entry to physcal address 0 with no-access and a + bad ptbase */ + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { + for (i = 0; i < cpuinfo.tlb_num_lines; i++) { + WRCTL(CTL_PTEADDR, ((vaddr) >> PAGE_SHIFT) << 2); + WRCTL(CTL_TLBMISC, (1<<18) | (way << 20)); + WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); + vaddr += 1UL << 12; + } + } + + /* restore pid/way */ + WRCTL(CTL_TLBMISC, org_misc); +} + +void set_mmu_pid(unsigned long pid) +{ + WRCTL(CTL_TLBMISC, (RDCTL(CTL_TLBMISC) & (WAY_MASK << WAY_SHIFT)) | + ((pid & PID_MASK) << PID_SHIFT)); +} diff --git a/arch/nios2/mm/uaccess.c b/arch/nios2/mm/uaccess.c new file mode 100644 index 0000000000000..4a38ad37c18b4 --- /dev/null +++ b/arch/nios2/mm/uaccess.c @@ -0,0 +1,162 @@ +/* + * 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. + * + * Copyright (C) 2009, Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + */ + +#include +#include + +asm(".global __copy_from_user\n" + " .type __copy_from_user, @function\n" + "__copy_from_user:\n" + " movi r2,7\n" + " mov r3,r4\n" + " bge r2,r6,1f\n" + " xor r2,r4,r5\n" + " andi r2,r2,3\n" + " movi r7,3\n" + " beq r2,zero,4f\n" + "1: addi r6,r6,-1\n" + " movi r2,-1\n" + " beq r6,r2,3f\n" + " mov r7,r2\n" + "2: ldbu r2,0(r5)\n" + " addi r6,r6,-1\n" + " addi r5,r5,1\n" + " stb r2,0(r3)\n" + " addi r3,r3,1\n" + " bne r6,r7,2b\n" + "3:\n" + " addi r2,r6,1\n" + " ret\n" + "13:mov r2,r6\n" + " ret\n" + "4: andi r2,r4,1\n" + " cmpeq r2,r2,zero\n" + " beq r2,zero,7f\n" + "5: andi r2,r3,2\n" + " beq r2,zero,6f\n" + "9: ldhu r2,0(r5)\n" + " addi r6,r6,-2\n" + " addi r5,r5,2\n" + " sth r2,0(r3)\n" + " addi r3,r3,2\n" + "6: bge r7,r6,1b\n" + "10:ldw r2,0(r5)\n" + " addi r6,r6,-4\n" + " addi r5,r5,4\n" + " stw r2,0(r3)\n" + " addi r3,r3,4\n" + " br 6b\n" + "7: ldbu r2,0(r5)\n" + " addi r6,r6,-1\n" + " addi r5,r5,1\n" + " addi r3,r4,1\n" + " stb r2,0(r4)\n" + " br 5b\n" + ".section __ex_table,\"a\"\n" + ".word 2b,3b\n" + ".word 9b,13b\n" + ".word 10b,13b\n" + ".word 7b,13b\n" + ".previous\n" + ); +EXPORT_SYMBOL(__copy_from_user); + +asm( + " .global __copy_to_user\n" + " .type __copy_to_user, @function\n" + "__copy_to_user:\n" + " movi r2,7\n" + " mov r3,r4\n" + " bge r2,r6,1f\n" + " xor r2,r4,r5\n" + " andi r2,r2,3\n" + " movi r7,3\n" + " beq r2,zero,4f\n" + /* Bail if we try to copy zero bytes */ + "1: addi r6,r6,-1\n" + " movi r2,-1\n" + " beq r6,r2,3f\n" + /* Copy byte by byte for small copies and if src^dst != 0 */ + " mov r7,r2\n" + "2: ldbu r2,0(r5)\n" + " addi r5,r5,1\n" + "9: stb r2,0(r3)\n" + " addi r6,r6,-1\n" + " addi r3,r3,1\n" + " bne r6,r7,2b\n" + "3: addi r2,r6,1\n" + " ret\n" + "13:mov r2,r6\n" + " ret\n" + /* If 'to' is an odd address byte copy */ + "4: andi r2,r4,1\n" + " cmpeq r2,r2,zero\n" + " beq r2,zero,7f\n" + /* If 'to' is not divideable by four copy halfwords */ + "5: andi r2,r3,2\n" + " beq r2,zero,6f\n" + " ldhu r2,0(r5)\n" + " addi r5,r5,2\n" + "10:sth r2,0(r3)\n" + " addi r6,r6,-2\n" + " addi r3,r3,2\n" + /* Copy words */ + "6: bge r7,r6,1b\n" + " ldw r2,0(r5)\n" + " addi r5,r5,4\n" + "11:stw r2,0(r3)\n" + " addi r6,r6,-4\n" + " addi r3,r3,4\n" + " br 6b\n" + /* Copy remaining bytes */ + "7: ldbu r2,0(r5)\n" + " addi r5,r5,1\n" + " addi r3,r4,1\n" + "12: stb r2,0(r4)\n" + " addi r6,r6,-1\n" + " br 5b\n" + ".section __ex_table,\"a\"\n" + ".word 9b,3b\n" + ".word 10b,13b\n" + ".word 11b,13b\n" + ".word 12b,13b\n" + ".previous\n"); +EXPORT_SYMBOL(__copy_to_user); + +long strncpy_from_user(char *__to, const char __user *__from, long __len) +{ + int l = strnlen_user(__from, __len); + int is_zt = 1; + + if (l > __len) { + is_zt = 0; + l = __len; + } + + if (l == 0 || copy_from_user(__to, __from, l)) + return -EFAULT; + + if (is_zt) + l--; + return l; +} + +long strnlen_user(const char __user *s, long n) +{ + long i; + + for (i = 0; i < n; i++) { + char c; + if (get_user(c, s + i) == -EFAULT) + return 0; + if (c == 0) + return i + 1; + } + return n + 1; +} From e45e8def9045bd1240b0a641bc6b98ddb7775a9d Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 14 May 2013 09:53:58 +0800 Subject: [PATCH 003/201] FogBugz #111740: [PATCHv3 3/7] Integrates Nios II kernel (configs) Integrates Nios II kernel (arch/nios2) from linux-nios2.git to linux-socfpga.git. Signed-off-by: Ley Foon Tan --- arch/nios2/Kconfig | 227 +++++++++++++++++++++++++ arch/nios2/Kconfig.debug | 28 +++ arch/nios2/Makefile | 89 ++++++++++ arch/nios2/boot/dts/3c120_devboard.dts | 187 ++++++++++++++++++++ arch/nios2/configs/3c120_defconfig | 79 +++++++++ include/uapi/linux/elf-em.h | 1 + 6 files changed, 611 insertions(+) create mode 100644 arch/nios2/Kconfig create mode 100644 arch/nios2/Kconfig.debug create mode 100644 arch/nios2/Makefile create mode 100644 arch/nios2/boot/dts/3c120_devboard.dts create mode 100644 arch/nios2/configs/3c120_defconfig diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig new file mode 100644 index 0000000000000..3521a679dd9b9 --- /dev/null +++ b/arch/nios2/Kconfig @@ -0,0 +1,227 @@ +config NIOS2 + def_bool y + select OF + select OF_EARLY_FLATTREE + select HAVE_OPROFILE + select HAVE_ARCH_KGDB + select USB_ARCH_HAS_HCD if USB_SUPPORT + select ARCH_WANT_OPTIONAL_GPIOLIB + select HAVE_GENERIC_HARDIRQS + select GENERIC_IRQ_PROBE + select GENERIC_IRQ_SHOW + select GENERIC_CPU_DEVICES + select GENERIC_ATOMIC64 + select MODULES_USE_ELF_RELA + select IRQ_DOMAIN + select SOC_BUS + +config GENERIC_CSUM + def_bool y + +config GENERIC_FIND_NEXT_BIT + def_bool y + +config GENERIC_HWEIGHT + def_bool y + +config GENERIC_CALIBRATE_DELAY + def_bool y + +config GENERIC_GPIO + bool + +config NO_IOPORT + def_bool y + +config HAS_DMA + def_bool y + +config ZONE_DMA + def_bool y + +config FPU + def_bool n + +config SWAP + def_bool n + +config RWSEM_GENERIC_SPINLOCK + def_bool y + +config TRACE_IRQFLAGS_SUPPORT + def_bool n + +source "init/Kconfig" + +menu "Kernel features" + +source "kernel/Kconfig.preempt" + +source "kernel/Kconfig.freezer" + +source "kernel/Kconfig.hz" + +source "kernel/time/Kconfig" + +source "mm/Kconfig" + +config FORCE_MAX_ZONEORDER + int "Maximum zone order" + range 9 20 + default "11" + help + The kernel memory allocator divides physically contiguous memory + blocks into "zones", where each zone is a power of two number of + pages. This option selects the largest power of two that the kernel + keeps in the memory allocator. If you need to allocate very large + blocks of physically contiguous memory, then you may need to + increase this value. + + This config option is actually maximum order plus one. For example, + a value of 11 means that the largest free memory block is 2^10 pages. + +endmenu + +source "arch/nios2/platform/Kconfig.platform" + +menu "Processor type and features" + +config MMU + bool "MMU support" + default y + help + This option enables support for the Nios II MMU. Only enable this + option if your design contains a Nios II/f core with MMU enabled. + +config ALIGNMENT_TRAP + bool "Catch alignment trap" + default y + depends on MMU + help + Nios II CPUs cannot fetch/store data which is not bus aligned, + i.e., a 2 or 4 byte fetch must start at an address divisible by + 2 or 4. Any non-aligned load/store instructions will be trapped and + emulated in software if you say Y here, which has a performance + impact. + +comment "Boot options" + +config CMDLINE_BOOL + bool "Default bootloader kernel arguments" + default y + +config CMDLINE + string "Default kernel command string" + default "" + depends on CMDLINE_BOOL + help + On some platforms, there is currently no way for the boot loader to + pass arguments to the kernel. For these platforms, you can supply + some command-line options at build time by entering them here. In + other cases you can specify kernel args so that you don't have + to set them up in board prom initialization routines. + +config CMDLINE_FORCE + bool "Force default kernel command string" + depends on CMDLINE_BOOL + help + Set this to have arguments from the default kernel command string + override those passed by the boot loader. + +config CMDLINE_IGNORE_DTB + bool "Ignore kernel command string from DTB" + depends on !CMDLINE_FORCE + default y + help + Set this to ignore the bootargs property from the devicetree's + chosen node and fall back to CMDLINE if nothing is passed. + +config PASS_CMDLINE + bool "Passed kernel command line from u-boot" + default n + help + Use bootargs env variable from u-boot for kernel command line. + will override "Default kernel command string". + Say N if you are unsure. + +config BOOT_LINK_OFFSET + hex "Link address offset for booting" + default "0x00500000" + help + This option allows you to set the link address offset of the zImage. + This can be useful if you are on a board which has a small amount of + memory. + +endmenu + +menu "Advanced setup" + +config ADVANCED_OPTIONS + bool "Prompt for advanced kernel configuration options" + help + +comment "Default settings for advanced configuration options are used" + depends on !ADVANCED_OPTIONS + +config KERNEL_MMU_REGION_BASE_BOOL + bool "Set custom kernel MMU region base address" + depends on ADVANCED_OPTIONS + help + This option allows you to set the virtual address of the kernel MMU region. + + Say N here unless you know what you are doing. + +config KERNEL_MMU_REGION_BASE + hex "Virtual base address of the kernel MMU region " if KERNEL_MMU_REGION_BASE_BOOL + default "0x80000000" if MMU + default "0x00000000" if !MMU + help + This option allows you to set the virtual base address of the kernel MMU region. + +config KERNEL_REGION_BASE_BOOL + bool "Set custom kernel region base address" + depends on ADVANCED_OPTIONS + help + This option allows you to set the virtual address of the kernel region. + + Say N here unless you know what you are doing. + +config KERNEL_REGION_BASE + hex "Virtual base address of the kernel region " if KERNEL_REGION_BASE_BOOL + default "0xc0000000" if MMU + default "0x00000000" if !MMU + +config IO_REGION_BASE_BOOL + bool "Set custom I/O region base address" + depends on ADVANCED_OPTIONS + help + This option allows you to set the virtual address of the I/O region. + + Say N here unless you know what you are doing. + +config IO_REGION_BASE + hex "Virtual base address of the I/O region " if IO_REGION_BASE_BOOL + default "0xe0000000" if MMU + default "0x80000000" if !MMU + +endmenu + +menu "Executable file formats" + +source "fs/Kconfig.binfmt" + +endmenu + +source "net/Kconfig" + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/nios2/Kconfig.debug" + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" diff --git a/arch/nios2/Kconfig.debug b/arch/nios2/Kconfig.debug new file mode 100644 index 0000000000000..de063bd1e7326 --- /dev/null +++ b/arch/nios2/Kconfig.debug @@ -0,0 +1,28 @@ +menu "Kernel hacking" + +config TRACE_IRQFLAGS_SUPPORT + def_bool y + +source "lib/Kconfig.debug" + +config EARLY_PRINTK + bool "Activate early kernel debugging" + default y + select SERIAL_CORE_CONSOLE + depends on SERIAL_ALTERA_JTAGUART_CONSOLE || SERIAL_ALTERA_UART_CONSOLE + help + Enable early printk on console + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. + You should normally say N here, unless you want to debug such a crash. + +config DEBUG_STACK_USAGE + bool "Enable stack utilization instrumentation" + depends on DEBUG_KERNEL + help + Enables the display of the minimum amount of free stack which each + task has ever had available in the sysrq-T and sysrq-P debug output. + + This option will slow down process creation somewhat. + +endmenu diff --git a/arch/nios2/Makefile b/arch/nios2/Makefile new file mode 100644 index 0000000000000..23f4b13e3325b --- /dev/null +++ b/arch/nios2/Makefile @@ -0,0 +1,89 @@ +# +# 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. +# +# Copyright (C) 2013 Altera Corporation +# Copyright (C) 1994, 95, 96, 2003 by Wind River Systems +# Written by Fredrik Markstrom +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" cleaning up for this architecture. +# +# Nios2 port by Wind River Systems Inc trough: +# fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + +ifeq ($(CONFIG_MMU),y) +UTS_SYSNAME = Linux +else +UTS_SYSNAME = uClinux +MMU := -nommu +KBUILD_CFLAGS += -D__uClinux__ +KBUILD_AFLAGS += -D__uClinux__ +endif + +export MMU + +cflags-y := +LDFLAGS := +LDFLAGS_vmlinux := + +LIBGCC := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name) + +KBUILD_AFLAGS += $(cflags-y) +KBUILD_CFLAGS += -pipe -D__linux__ -D__ELF__ $(cflags-y) +KBUILD_CFLAGS += $(if $(CONFIG_NIOS2_HW_MUL_SUPPORT),-mhw-mul,-mno-hw-mul) +KBUILD_CFLAGS += $(if $(CONFIG_NIOS2_HW_MULX_SUPPORT),-mhw-mulx,-mno-hw-mulx) +KBUILD_CFLAGS += $(if $(CONFIG_NIOS2_HW_DIV_SUPPORT),-mhw-div,-mno-hw-div) +KBUILD_CFLAGS += $(if $(CONFIG_NIOS2_FPU_SUPPORT),-mcustom-fpu-cfg=60-1,) + +KBUILD_CFLAGS += -fno-optimize-sibling-calls +KBUILD_CFLAGS += -DUTS_SYSNAME=\"$(UTS_SYSNAME)\" +KBUILD_CFLAGS += -fno-builtin +KBUILD_CFLAGS += -G 0 + +LDFLAGS += -mnios2elf + +head-y := arch/nios2/kernel/head.o +libs-y += arch/nios2/lib/ $(LIBGCC) +core-y += arch/nios2/kernel/ arch/nios2/mm/ +core-y += arch/nios2/platform/ + +drivers-$(CONFIG_OPROFILE) += arch/$(ARCH)/oprofile/ + +INSTALL_PATH ?= /tftpboot +boot := arch/$(ARCH)/boot +BOOT_TARGETS = vmImage zImage +PHONY += $(BOOT_TARGETS) install +KBUILD_IMAGE := $(boot)/vmImage + +ifneq ($(CONFIG_DTB_SOURCE),"") + core-y += $(boot)/ +endif + +all: vmImage + +archclean: + $(Q)$(MAKE) $(clean)=$(boot) + +%.dtb: + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +dtbs: + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +$(BOOT_TARGETS): vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +install: + $(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) install + +define archhelp + echo '* vmImage - Kernel-only image for U-Boot (arch/$(ARCH)/boot/vmImage)' + echo ' install - Install kernel using' + echo ' (your) ~/bin/$(CROSS_COMPILE)installkernel or' + echo ' (distribution) PATH: $(CROSS_COMPILE)installkernel or' + echo ' install to $$(INSTALL_PATH)' + echo ' dtbs - Build device tree blobs for enabled boards' +endef diff --git a/arch/nios2/boot/dts/3c120_devboard.dts b/arch/nios2/boot/dts/3c120_devboard.dts new file mode 100644 index 0000000000000..5e29c3a086f00 --- /dev/null +++ b/arch/nios2/boot/dts/3c120_devboard.dts @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2013 Altera Corporation + * + * 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 . + * + * This file is generated by sopc2dts. + */ + +/dts-v1/; + +/ { + model = "ALTR,qsys_ghrd_3c120"; + compatible = "ALTR,qsys_ghrd_3c120"; + #address-cells = < 1 >; + #size-cells = < 1 >; + + cpus { + #address-cells = < 1 >; + #size-cells = < 0 >; + + cpu: cpu@0x0 { + device_type = "cpu"; + compatible = "ALTR,nios2-13.0", "ALTR,nios2-1.0"; + reg = < 0x00000000 >; + interrupt-controller; + #interrupt-cells = < 1 >; + clock-frequency = < 125000000 >; + dcache-line-size = < 32 >; + icache-line-size = < 32 >; + dcache-size = < 32768 >; + icache-size = < 32768 >; + ALTR,implementation = "fast"; + ALTR,pid-num-bits = < 8 >; + ALTR,tlb-num-ways = < 16 >; + ALTR,tlb-num-entries = < 128 >; + ALTR,tlb-ptr-sz = < 7 >; + ALTR,has-div; + ALTR,has-mul; + ALTR,reset-addr = < 0xc0000000 >; + ALTR,fast-tlb-miss-addr = < 0xc7fff400 >; + ALTR,exception-addr = < 0xd0000020 >; + }; //end cpu@0x0 (cpu) + }; //end cpus + + memory@0 { + device_type = "memory"; + reg = < 0x10000000 0x08000000 + 0x07FFF400 0x00000400 >; + }; //end memory@0 + + sopc@0 { + device_type = "soc"; + ranges; + #address-cells = < 1 >; + #size-cells = < 1 >; + compatible = "ALTR,avalon", "simple-bus"; + bus-frequency = < 125000000 >; + + pb_cpu_to_io: bridge@0x8000000 { + compatible = "ALTR,avalon-13.0", "simple-bus"; + reg = < 0x08000000 0x00800000 >; + #address-cells = < 1 >; + #size-cells = < 1 >; + ranges = < 0x00400000 0x08400000 0x00000020 + 0x00004D40 0x08004D40 0x00000008 + 0x00004D50 0x08004D50 0x00000008 + 0x00004000 0x08004000 0x00000400 + 0x00004400 0x08004400 0x00000040 + 0x00004800 0x08004800 0x00000040 + 0x00002000 0x08002000 0x00002000 + 0x00004C80 0x08004C80 0x00000020 + 0x00004CC0 0x08004CC0 0x00000010 + 0x00004CE0 0x08004CE0 0x00000010 + 0x00004D00 0x08004D00 0x00000010 >; + + timer_1ms: timer@0x400000 { + compatible = "ALTR,timer-13.0.1", "ALTR,timer-1.0"; + reg = < 0x00400000 0x00000020 >; + interrupt-parent = < &cpu >; + interrupts = < 11 >; + clock-frequency = < 125000000 >; + }; //end timer@0x400000 (timer_1ms) + + sysid: sysid@0x4d40 { + compatible = "ALTR,sysid-13.0", "ALTR,sysid-1.0"; + reg = < 0x00004D40 0x00000008 >; + id = < 0 >; + timestamp = < 1364882880 >; + }; //end sysid@0x4d40 (sysid) + + jtag_uart: serial@0x4d50 { + compatible = "ALTR,juart-13.0.1", "ALTR,juart-1.0"; + reg = < 0x00004D50 0x00000008 >; + interrupt-parent = < &cpu >; + interrupts = < 1 >; + }; //end serial@0x4d50 (jtag_uart) + + tse_mac: ethernet@0x4000 { + compatible = "ALTR,tse-13.0", "ALTR,tse-1.0"; + reg = < 0x00004000 0x00000400 + 0x00004400 0x00000040 + 0x00004800 0x00000040 + 0x00002000 0x00002000 >; + interrupt-parent = < &cpu >; + interrupts = < 2 3 >; + ALTR,rx-fifo-depth = < 2048 >; + ALTR,tx-fifo-depth = < 2048 >; + address-bits = < 48 >; + max-frame-size = < 1518 >; + local-mac-address = [ 02 00 00 00 00 00 ]; + phy-mode = "rgmii-id"; + ALTR,mii-id = < 0 >; + }; //end ethernet@0x4000 (tse_mac) + + uart: serial@0x4c80 { + compatible = "ALTR,uart-13.0.1", "ALTR,uart-1.0"; + reg = < 0x00004C80 0x00000020 >; + interrupt-parent = < &cpu >; + interrupts = < 10 >; + current-speed = < 115200 >; + clock-frequency = < 62500000 >; + }; //end serial@0x4c80 (uart) + + user_led_pio_8out: gpio@0x4cc0 { + compatible = "ALTR,pio-13.0.1", "ALTR,pio-1.0"; + reg = < 0x00004CC0 0x00000010 >; + width = < 8 >; + resetvalue = < 255 >; + #gpio-cells = < 2 >; + gpio-controller; + }; //end gpio@0x4cc0 (user_led_pio_8out) + + user_dipsw_pio_8in: gpio@0x4ce0 { + compatible = "ALTR,pio-13.0.1", "ALTR,pio-1.0"; + reg = < 0x00004CE0 0x00000010 >; + interrupt-parent = < &cpu >; + interrupts = < 8 >; + width = < 8 >; + resetvalue = < 0 >; + level_trigger = < 0 >; + #gpio-cells = < 2 >; + gpio-controller; + }; //end gpio@0x4ce0 (user_dipsw_pio_8in) + + user_pb_pio_4in: gpio@0x4d00 { + compatible = "ALTR,pio-13.0.1", "ALTR,pio-1.0"; + reg = < 0x00004D00 0x00000010 >; + interrupt-parent = < &cpu >; + interrupts = < 9 >; + width = < 4 >; + resetvalue = < 0 >; + level_trigger = < 0 >; + #gpio-cells = < 2 >; + gpio-controller; + }; //end gpio@0x4d00 (user_pb_pio_4in) + }; //end bridge@0x8000000 (pb_cpu_to_io) + + cfi_flash_64m: flash@0x0 { + compatible = "ALTR,cfi_flash-13.0", "cfi-flash"; + reg = < 0x00000000 0x04000000 >; + bank-width = < 2 >; + device-width = < 1 >; + #address-cells = < 1 >; + #size-cells = < 1 >; + + partition@800000 { + reg = < 0x00800000 0x01E00000 >; + label = "JFFS2 Filesystem"; + }; //end partition@800000 + }; //end flash@0x0 (cfi_flash_64m) + }; //end sopc@0 + + chosen { + bootargs = "debug console=ttyJ0,115200"; + }; //end chosen +}; //end / diff --git a/arch/nios2/configs/3c120_defconfig b/arch/nios2/configs/3c120_defconfig new file mode 100644 index 0000000000000..3f7aba567a135 --- /dev/null +++ b/arch/nios2/configs/3c120_defconfig @@ -0,0 +1,79 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_ELF_CORE is not set +# CONFIG_EPOLL is not set +# CONFIG_SIGNALFD is not set +# CONFIG_TIMERFD is not set +# CONFIG_EVENTFD is not set +# CONFIG_SHMEM is not set +CONFIG_EMBEDDED=y +CONFIG_SLAB=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MEM_BASE=0x10000000 +CONFIG_NIOS2_HW_MUL_SUPPORT=y +CONFIG_NIOS2_HW_DIV_SUPPORT=y +# CONFIG_CMDLINE_IGNORE_DTB is not set +CONFIG_PASS_CMDLINE=y +CONFIG_BOOT_LINK_OFFSET=0x00800000 +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# 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_IPV6 is not set +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_FW_LOADER is not set +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_NETDEVICES=y +CONFIG_MII=y +CONFIG_NET_VENDOR_ALTERA=y +CONFIG_ALT_TSE=y +CONFIG_MARVELL_PHY=y +# CONFIG_WLAN is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +CONFIG_SERIAL_ALTERA_JTAGUART=y +CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE=y +CONFIG_SERIAL_ALTERA_UART=y +# CONFIG_HW_RANDOM is not set +CONFIG_GPIOLIB=y +CONFIG_GPIO_ALTERA=y +# CONFIG_HWMON is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_SUNRPC_DEBUG=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +CONFIG_DEBUG_INFO=y +CONFIG_KGDB=y diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h index 01529bd964387..326972a094eb8 100644 --- a/include/uapi/linux/elf-em.h +++ b/include/uapi/linux/elf-em.h @@ -33,6 +33,7 @@ #define EM_M32R 88 /* Renesas M32R */ #define EM_MN10300 89 /* Panasonic/MEI MN10300, AM33 */ #define EM_BLACKFIN 106 /* ADI Blackfin Processor */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ #define EM_TI_C6000 140 /* TI C6X DSPs */ #define EM_AARCH64 183 /* ARM 64 bit */ #define EM_FRV 0x5441 /* Fujitsu FR-V */ From 38ea22fce037b154c71c51bc24d2cb3fa9084574 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 14 May 2013 09:55:26 +0800 Subject: [PATCH 004/201] FogBugz #111740: [PATCHv3 4/7] Integrates Nios II kernel (oprofile,platform) Integrates Nios II kernel (arch/nios2) from linux-nios2.git to linux-socfpga.git. Signed-off-by: Ley Foon Tan --- arch/nios2/oprofile/Makefile | 13 +++ arch/nios2/oprofile/nios2_oprofile.c | 30 ++++++ arch/nios2/platform/Kconfig.platform | 139 +++++++++++++++++++++++++++ arch/nios2/platform/Makefile | 1 + arch/nios2/platform/platform.c | 93 ++++++++++++++++++ 5 files changed, 276 insertions(+) create mode 100644 arch/nios2/oprofile/Makefile create mode 100644 arch/nios2/oprofile/nios2_oprofile.c create mode 100644 arch/nios2/platform/Kconfig.platform create mode 100644 arch/nios2/platform/Makefile create mode 100644 arch/nios2/platform/platform.c diff --git a/arch/nios2/oprofile/Makefile b/arch/nios2/oprofile/Makefile new file mode 100644 index 0000000000000..27b07c19e0000 --- /dev/null +++ b/arch/nios2/oprofile/Makefile @@ -0,0 +1,13 @@ +# +# arch/nios2/oprofile/Makefile +# + +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) nios2_oprofile.o diff --git a/arch/nios2/oprofile/nios2_oprofile.c b/arch/nios2/oprofile/nios2_oprofile.c new file mode 100644 index 0000000000000..29e9ab6fbaa52 --- /dev/null +++ b/arch/nios2/oprofile/nios2_oprofile.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2010 Thomas Chou + * + * 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 . + * + */ + + +#include +#include + +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + return -1; +} + +void oprofile_arch_exit(void) +{ +} diff --git a/arch/nios2/platform/Kconfig.platform b/arch/nios2/platform/Kconfig.platform new file mode 100644 index 0000000000000..dd67f9826ca03 --- /dev/null +++ b/arch/nios2/platform/Kconfig.platform @@ -0,0 +1,139 @@ +menu "Platform options" + +comment "Memory settings" + +config MEM_BASE + hex "Memory base address" + default "0x00000000" + help + This is the physical address of the memory that the kernel will run + from. This address is used to link the kernel and setup initial memory + management. You should take the raw memory address without any MMU + or cache bits set. + Please not that this address is used directly so you have to manually + do address translation if it's connected to a bridge. + +comment "Device tree" + +config NIOS2_DTB_AT_PHYS_ADDR + bool "DTB at physical address" + default n + help + When enabled you can select a physical address to load the dtb from. + Normally this address is passed by a bootloader such as u-boot but + using this you can use a devicetree without a bootloader. + This way you can store a devicetree in NOR flash or an onchip rom. + Please note that this addres is used directly so you have to manually + do address translation if it's connected to a bridge. Also take into + account that when using an MMU you'd have to ad 0xC0000000 to your + address + +config NIOS2_DTB_PHYS_ADDR + hex "DTB Address" + depends on NIOS2_DTB_AT_PHYS_ADDR + default "0xC0000000" + help + Physical address of a dtb blob. + +config DTB_SOURCE_BOOL + bool "Compile and link device tree into kernel image" + default n + help + This allows you to specify a dts (device tree source) file + which will be compiled and linked into the kernel image. + +config DTB_SOURCE + string "Device tree source file" + depends on DTB_SOURCE_BOOL + default "" + help + Absolute path to the device tree source (dts) file describing your + system. + +comment "Nios II instructions" + +config NIOS2_HW_MUL_SUPPORT + bool "Enable MUL instruction" + default n + help + Set to true if you configured the Nios II to include the MUL + instruction. This will enable the -mhw-mul compiler flag. + +config NIOS2_HW_MULX_SUPPORT + bool "Enable MULX instruction" + default n + help + Set to true if you configured the Nios II to include the MULX + instruction. Enables the -mhw-mulx compiler flag. + +config NIOS2_HW_DIV_SUPPORT + bool "Enable DIV instruction" + default n + help + Set to true if you configured the Nios II to include the DIV + instruction. Enables the -mhw-div compiler flag. + +config NIOS2_FPU_SUPPORT + bool "Custom floating point instr support" + default n + help + Enables the -mcustom-fpu-cfg=60-1 compiler flag. + +config NIOS2_CI_SWAB_SUPPORT + bool "Byteswap custom instruction" + default n + help + Use the byteswap (endian convertor) Nios II custom instruction provided + by Altera and which can be enabled in SOPC builder. This accelerates + endian conversions in the kernel (e.g. ntohs). + +config NIOS2_CI_SWAB_NO + int "Byteswap custom instruction number" if NIOS2_CI_SWAB_SUPPORT + default 0 + help + Number of the instruction as configured in SOPC Builder. + +comment "Cache settings" + +config CUSTOM_CACHE_SETTINGS + bool "Custom cache settings" + help + This option allows you to tweak the cache settings used during early + boot (where the information from device tree is not yet available). + There should be no reason to change these values. Linux will work + perfectly fine, even if the Nios II is configured with smaller caches. + + Say N here unless you know what you are doing. + +config NIOS2_DCACHE_SIZE + hex "D-Cache size" if CUSTOM_CACHE_SETTINGS + default "0x10000" + help + Maximum possible data cache size. + +config NIOS2_DCACHE_LINE_SIZE + hex "D-Cache line size" if CUSTOM_CACHE_SETTINGS + default "0x4" + help + Minimum possible data cache line size. + +config NIOS2_ICACHE_SIZE + hex "I-Cache size" if CUSTOM_CACHE_SETTINGS + default "0x10000" + help + Maximum possible instruction cache size. + +config NIOS2_ICACHE_LINE_SIZE + hex "I-Cache line size" if CUSTOM_CACHE_SETTINGS + default "0x20" + help + Maximum possible instruction cache line size. + +config NIOS2_ICACHE_LINE_SHIFT + hex "I-Cache line shift" if CUSTOM_CACHE_SETTINGS + default "0x5" + help + log2 of the instruction cache line size. This must match + CONFIG_NIOS2_ICACHE_LINE_SIZE. + +endmenu diff --git a/arch/nios2/platform/Makefile b/arch/nios2/platform/Makefile new file mode 100644 index 0000000000000..46364f1d9352d --- /dev/null +++ b/arch/nios2/platform/Makefile @@ -0,0 +1 @@ +obj-y += platform.o diff --git a/arch/nios2/platform/platform.c b/arch/nios2/platform/platform.c new file mode 100644 index 0000000000000..3cb4b7c20c0d8 --- /dev/null +++ b/arch/nios2/platform/platform.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2011 Thomas Chou + * Copyright (C) 2011 Walter Goossens + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NIOS2_SYSID_DEFAULT (0x1) +#define NIOS2_REVISION_DEFAULT (0x1) + +/* Sysid register map */ +#define SYSID_ID_REG (0x0) + +static struct of_device_id altera_of_bus_ids[] __initdata = { + { .compatible = "simple-bus", }, + { .compatible = "altr,avalon", }, + {} +}; + +static void __init nios2_soc_device_init(void) +{ + struct device_node *root; + struct device_node *sysid_node; + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + void __iomem *sysid_base; + const char *machine; + u32 id = NIOS2_SYSID_DEFAULT; + int err; + + root = of_find_node_by_path("/"); + if (!root) + return; + + err = of_property_read_string(root, "model", &machine); + if (err) + return; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return; + + sysid_node = of_find_compatible_node(root, NULL, "ALTR,sysid-1.0"); + if (sysid_node) { + sysid_base = of_iomap(sysid_node, 0); + if (sysid_base) { + /* Use id from Sysid hardware. */ + id = readl(sysid_base + SYSID_ID_REG); + iounmap(sysid_base); + } + of_node_put(sysid_node); + } + + of_node_put(root); + + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", id); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", + NIOS2_REVISION_DEFAULT); + soc_dev_attr->machine = kasprintf(GFP_KERNEL, "%s", machine); + soc_dev_attr->family = "Nios II"; + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR_OR_NULL(soc_dev)) { + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr->machine); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); + return; + } + + return; +} + +static int __init nios2_device_probe(void) +{ + nios2_soc_device_init(); + + of_platform_bus_probe(NULL, altera_of_bus_ids, NULL); + return 0; +} + +device_initcall(nios2_device_probe); From 4fa39f838e9d4334bffeac4150ca4045aaaaa9c4 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 14 May 2013 10:02:41 +0800 Subject: [PATCH 005/201] FogBugz #111740: [PATCHv4 5/7] Integrates Nios II kernel (boot) Integrates Nios II kernel (arch/nios2) from linux-nios2.git to linux-socfpga.git. Signed-off-by: Ley Foon Tan --- arch/nios2/boot/Makefile | 58 +++++++ arch/nios2/boot/compressed/Makefile | 21 +++ arch/nios2/boot/compressed/console.c | 125 +++++++++++++++ arch/nios2/boot/compressed/head.S | 117 ++++++++++++++ arch/nios2/boot/compressed/misc.c | 185 +++++++++++++++++++++++ arch/nios2/boot/compressed/vmlinux.lds.S | 52 +++++++ arch/nios2/boot/compressed/vmlinux.scr | 28 ++++ arch/nios2/boot/linked_dtb.S | 19 +++ 8 files changed, 605 insertions(+) create mode 100644 arch/nios2/boot/Makefile create mode 100644 arch/nios2/boot/compressed/Makefile create mode 100644 arch/nios2/boot/compressed/console.c create mode 100644 arch/nios2/boot/compressed/head.S create mode 100644 arch/nios2/boot/compressed/misc.c create mode 100644 arch/nios2/boot/compressed/vmlinux.lds.S create mode 100644 arch/nios2/boot/compressed/vmlinux.scr create mode 100644 arch/nios2/boot/linked_dtb.S diff --git a/arch/nios2/boot/Makefile b/arch/nios2/boot/Makefile new file mode 100644 index 0000000000000..cf1ee5008305d --- /dev/null +++ b/arch/nios2/boot/Makefile @@ -0,0 +1,58 @@ +# +# arch/nios2/boot/Makefile +# +# 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. +# + +UIMAGE_LOADADDR = $(shell $(NM) vmlinux | awk '$$NF == "_stext" {print $$1}') +UIMAGE_ENTRYADDR = $(shell $(NM) vmlinux | awk '$$NF == "_start" {print $$1}') +UIMAGE_COMPRESSION = gzip + +OBJCOPYFLAGS_vmlinux.bin := -O binary +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) + +$(obj)/vmImage: $(obj)/vmlinux.gz + $(call if_changed,uimage) + @$(kecho) 'Kernel: $@ is ready' + +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + @$(kecho) 'Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: $(obj)/vmlinux.gz FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + +# Rule to build device tree blobs +DTB_SRC := $(subst ",,$(CONFIG_DTB_SOURCE)) + +# Make sure the generated dtb gets removed during clean +extra-$(CONFIG_DTB_SOURCE_BOOL) += system.dtb + +$(obj)/system.dtb: $(DTB_SRC) FORCE + $(call cmd,dtc) + +# Ensure system.dtb exists +$(obj)/linked_dtb.o: $(obj)/system.dtb + +#ifneq ($(CONFIG_DTB_SOURCE),"") +obj-$(CONFIG_DTB_SOURCE_BOOL) += linked_dtb.o +#endif + +targets += $(dtb-y) + +# Rule to build device tree blobs with make command +$(obj)/%.dtb: $(src)/dts/%.dts FORCE + $(call if_changed_dep,dtc) + +$(obj)/dtbs: $(addprefix $(obj)/, $(dtb-y)) + +clean-files := *.dtb + +install: + sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)" diff --git a/arch/nios2/boot/compressed/Makefile b/arch/nios2/boot/compressed/Makefile new file mode 100644 index 0000000000000..665d53599fd6b --- /dev/null +++ b/arch/nios2/boot/compressed/Makefile @@ -0,0 +1,21 @@ +# +# linux/arch/nios2/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +targets := vmlinux head.o misc.o piggy.o vmlinux.lds +asflags-y := + +OBJECTS = $(obj)/head.o $(obj)/misc.o + +LDFLAGS_vmlinux := -T + +$(obj)/vmlinux: $(obj)/vmlinux.lds $(OBJECTS) $(obj)/piggy.o FORCE + $(call if_changed,ld) + @: + +LDFLAGS_piggy.o := -r --format binary --oformat elf32-littlenios2 -T + +$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/../vmlinux.gz FORCE + $(call if_changed,ld) diff --git a/arch/nios2/boot/compressed/console.c b/arch/nios2/boot/compressed/console.c new file mode 100644 index 0000000000000..aa05724a0b238 --- /dev/null +++ b/arch/nios2/boot/compressed/console.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2008-2010 Thomas Chou + * + * 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 . + * + */ + +#include + +#if (defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) && defined(JTAG_UART_BASE))\ + || (defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) && defined(UART0_BASE)) +static void *my_ioremap(unsigned long physaddr) +{ + return (void *)(physaddr | CONFIG_IO_REGION_BASE); +} +#endif + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) && defined(JTAG_UART_BASE) + +#define ALTERA_JTAGUART_SIZE 8 +#define ALTERA_JTAGUART_DATA_REG 0 +#define ALTERA_JTAGUART_CONTROL_REG 4 +#define ALTERA_JTAGUART_CONTROL_AC_MSK (0x00000400) +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK (0xFFFF0000) +static unsigned uartbase; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) +static void jtag_putc(int ch) +{ + if (readl(uartbase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) + writeb(ch, uartbase + ALTERA_JTAGUART_DATA_REG); +} +#else +static void jtag_putc(int ch) +{ + while ((readl(uartbase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) + ; + writeb(ch, uartbase + ALTERA_JTAGUART_DATA_REG); +} +#endif + +static int putchar(int ch) +{ + jtag_putc(ch); + return ch; +} + +static void console_init(void) +{ + uartbase = (unsigned long) my_ioremap((unsigned long) JTAG_UART_BASE); + writel(ALTERA_JTAGUART_CONTROL_AC_MSK, + uartbase + ALTERA_JTAGUART_CONTROL_REG); +} + +#elif defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) && defined(UART0_BASE) + +#define ALTERA_UART_SIZE 32 +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_DIVISOR_REG 16 +#define ALTERA_UART_STATUS_TRDY_MSK (0x40) +static unsigned uartbase; + +static void uart_putc(int ch) +{ + int i; + + for (i = 0; (i < 0x10000); i++) { + if (readw(uartbase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) + break; + } + writeb(ch, uartbase + ALTERA_UART_TXDATA_REG); +} + +static int putchar(int ch) +{ + uart_putc(ch); + if (ch == '\n') + uart_putc('\r'); + return ch; +} + +static void console_init(void) +{ + unsigned int baud, baudclk; + + uartbase = (unsigned long) my_ioremap((unsigned long) UART0_BASE); + baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE; + baudclk = UART0_FREQ / baud; + writew(baudclk, uartbase + ALTERA_UART_DIVISOR_REG); +} + +#else + +static int putchar(int ch) +{ + return ch; +} + +static void console_init(void) +{ +} + +#endif + +static int puts(const char *s) +{ + while (*s) + putchar(*s++); + return 0; +} diff --git a/arch/nios2/boot/compressed/head.S b/arch/nios2/boot/compressed/head.S new file mode 100644 index 0000000000000..0658081d15fc3 --- /dev/null +++ b/arch/nios2/boot/compressed/head.S @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009 Thomas Chou + * + * Based on arch/nios2/kernel/head.S + * + * 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. + * + */ + +/* + * This code can be loaded anywhere, eg FLASH ROM as reset vector, + * as long as output does not overlap it. + */ + +#include +#include + + .text + .set noat +ENTRY(_start) + wrctl status, r0 /* disable interrupt */ + /* invalidate all instruction cache */ + movia r1, NIOS2_ICACHE_SIZE + movui r2, NIOS2_ICACHE_LINE_SIZE +1: initi r1 + sub r1, r1, r2 + bgt r1, r0, 1b + /* invalidate all data cache */ + movia r1, NIOS2_DCACHE_SIZE + movui r2, NIOS2_DCACHE_LINE_SIZE +1: initd 0(r1) + sub r1, r1, r2 + bgt r1, r0, 1b + + nextpc r1 /* Find out where we are */ +chkadr: + movia r2, chkadr + beq r1, r2, finish_move /* We are running in correct address, + done */ + /* move code, r1: src, r2: dest, r3: last dest */ + addi r1, r1, (_start - chkadr) /* Source */ + movia r2, _start /* Destination */ + movia r3, __bss_start /* End of copy */ +1: ldw r8, 0(r1) /* load a word from [r1] */ + stw r8, 0(r2) /* stort a word to dest [r2] */ + addi r1, r1, 4 /* inc the src addr */ + addi r2, r2, 4 /* inc the dest addr */ + blt r2, r3, 1b + /* flush the data cache after moving */ + movia r1, NIOS2_DCACHE_SIZE + movui r2, NIOS2_DCACHE_LINE_SIZE +1: flushd 0(r1) + sub r1, r1, r2 + bgt r1, r0, 1b + movia r1, finish_move + jmp r1 /* jmp to linked address */ + +finish_move: + /* zero out the .bss segment (uninitialized common data) */ + movia r2, __bss_start /* presume nothing is between */ + movia r1, _end /* the .bss and _end. */ +1: stb r0, 0(r2) + addi r2, r2, 1 + bne r1, r2, 1b + /* + * set up the stack pointer, some where higher than _end. + * The stack space must be greater than 32K for decompress. + */ + movia sp, 0x10000 + add sp, sp, r1 + /* save args passed from u-boot, maybe */ + addi sp, sp, -16 + stw r4, 0(sp) + stw r5, 4(sp) + stw r6, 8(sp) + stw r7, 12(sp) + /* decompress the kernel */ + call decompress_kernel + /* pass saved args to kernel */ + ldw r4, 0(sp) + ldw r5, 4(sp) + ldw r6, 8(sp) + ldw r7, 12(sp) + + /* flush all data cache after decompressing */ + movia r1, NIOS2_DCACHE_SIZE + movui r2, NIOS2_DCACHE_LINE_SIZE +1: flushd 0(r1) + sub r1, r1, r2 + bgt r1, r0, 1b + /* flush all instruction cache */ + movia r1, NIOS2_ICACHE_SIZE + movui r2, NIOS2_ICACHE_LINE_SIZE +1: flushi r1 + sub r1, r1, r2 + bgt r1, r0, 1b + flushp + /* jump to start real kernel */ + movia r1, (CONFIG_MEM_BASE | CONFIG_KERNEL_REGION_BASE) + jmp r1 + + .balign 512 +fake_headers_as_bzImage: + .short 0 + .ascii "HdrS" + .short 0x0202 + .short 0 + .short 0 + .byte 0x00, 0x10 + .short 0 + .byte 0 + .byte 1 + .byte 0x00, 0x80 + .long 0 + .long 0 diff --git a/arch/nios2/boot/compressed/misc.c b/arch/nios2/boot/compressed/misc.c new file mode 100644 index 0000000000000..8de1fb80663f6 --- /dev/null +++ b/arch/nios2/boot/compressed/misc.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2009 Thomas Chou + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * + * Adapted for SH by Stuart Menefy, Aug 1999 + * + * Modified to use standard LinuxSH BIOS by Greg Banks 7Jul2000 + * + * Based on arch/sh/boot/compressed/misc.c + * + * 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 . + * + */ + +#include + +/* + * gzip declarations + */ +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy +#define memzero(s, n) memset((s), 0, (n)) + +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 */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +#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(char *m); + +extern char input_data[]; +extern int input_len; + +static long bytes_out; +static uch *output_data; +static unsigned long output_ptr; + +#include "console.c" + +static void error(char *m); + +int puts(const char *); + +extern int _end; +static unsigned long free_mem_ptr; +static unsigned long free_mem_end_ptr; + +#define HEAP_SIZE 0x10000 + +#include "../../../../lib/inflate.c" + +void *memset(void *s, int c, size_t n) +{ + int i; + char *ss = (char *)s; + + for (i = 0; i < n; i++) + ss[i] = c; + return s; +} + +void *memcpy(void *__dest, __const void *__src, size_t __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i = 0; i < __n; i++) + d[i] = s[i]; + return __dest; +} + +/* + * 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(void) +{ + if (insize != 0) + error("ran out of input data"); + + 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(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 error(char *x) +{ + puts("\nERROR\n"); + puts(x); + puts("\n\n -- System halted"); + + while (1) /* Halt */ + ; +} + +void decompress_kernel(void) +{ + output_data = (void *) (CONFIG_MEM_BASE | CONFIG_KERNEL_REGION_BASE); + output_ptr = 0; + free_mem_ptr = (unsigned long)&_end; + free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; + + console_init(); + makecrc(); + puts("Uncompressing Linux... "); + gunzip(); + puts("Ok, booting the kernel.\n"); +} diff --git a/arch/nios2/boot/compressed/vmlinux.lds.S b/arch/nios2/boot/compressed/vmlinux.lds.S new file mode 100644 index 0000000000000..7a76cda990896 --- /dev/null +++ b/arch/nios2/boot/compressed/vmlinux.lds.S @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 Thomas Chou + * + * 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 . + * + */ + +#include + +OUTPUT_FORMAT("elf32-littlenios2", "elf32-littlenios2", "elf32-littlenios2") + +OUTPUT_ARCH(nios) +ENTRY(_start) /* Defined in head.S */ + +SECTIONS +{ + . = (CONFIG_MEM_BASE + CONFIG_BOOT_LINK_OFFSET) | \ + CONFIG_KERNEL_REGION_BASE; + + _text = .; + .text : { *(.text) } = 0 + .rodata : { *(.rodata) *(.rodata.*) } + _etext = .; + + . = ALIGN(32 / 8); + .data : { *(.data) } + . = ALIGN(32 / 8); + _got = .; + .got : { *(.got) _egot = .; *(.got.*) } + _edata = .; + + . = ALIGN(32 / 8); + __bss_start = .; + .bss : { *(.bss) *(.sbss) } + . = ALIGN(32 / 8); + _ebss = .; + end = . ; + _end = . ; + + got_len = (_egot - _got); +} diff --git a/arch/nios2/boot/compressed/vmlinux.scr b/arch/nios2/boot/compressed/vmlinux.scr new file mode 100644 index 0000000000000..28c42f1d127ee --- /dev/null +++ b/arch/nios2/boot/compressed/vmlinux.scr @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009 Thomas Chou + * + * 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 . + * + */ + +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + . = ALIGN(4); + input_data_end = .; + } +} diff --git a/arch/nios2/boot/linked_dtb.S b/arch/nios2/boot/linked_dtb.S new file mode 100644 index 0000000000000..071f922db338e --- /dev/null +++ b/arch/nios2/boot/linked_dtb.S @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 Thomas Chou + * + * 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 . + * + */ +.section .dtb.init.rodata,"a" +.incbin "arch/nios2/boot/system.dtb" From 623348a4d141bdecaaf96a7be9ef4b0a1fa8f7f9 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 14 May 2013 12:31:51 +0800 Subject: [PATCH 006/201] FogBugz #111740: [PATCHv4 6/7] Integrates Nios II kernel (include) Integrates Nios II kernel (arch/nios2) from linux-nios2.git to linux-socfpga.git. Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/Kbuild | 55 ++++ arch/nios2/include/asm/asm-macros.h | 313 ++++++++++++++++++ arch/nios2/include/asm/asm-offsets.h | 20 ++ arch/nios2/include/asm/barrier.h | 29 ++ arch/nios2/include/asm/cache.h | 41 +++ arch/nios2/include/asm/cacheflush.h | 92 ++++++ arch/nios2/include/asm/checksum.h | 78 +++++ arch/nios2/include/asm/cmpxchg.h | 61 ++++ arch/nios2/include/asm/cpuinfo.h | 58 ++++ arch/nios2/include/asm/delay.h | 92 ++++++ arch/nios2/include/asm/dma-mapping.h | 113 +++++++ arch/nios2/include/asm/elf.h | 138 ++++++++ arch/nios2/include/asm/entry.h | 174 ++++++++++ arch/nios2/include/asm/flat.h | 136 ++++++++ arch/nios2/include/asm/futex-irq.h | 130 ++++++++ arch/nios2/include/asm/futex.h | 110 +++++++ arch/nios2/include/asm/gpio.h | 39 +++ arch/nios2/include/asm/io.h | 310 ++++++++++++++++++ arch/nios2/include/asm/irq.h | 34 ++ arch/nios2/include/asm/irqflags.h | 69 ++++ arch/nios2/include/asm/kgdb.h | 92 ++++++ arch/nios2/include/asm/linkage.h | 30 ++ arch/nios2/include/asm/mmu.h | 23 ++ arch/nios2/include/asm/mmu_context.h | 23 ++ arch/nios2/include/asm/mmu_context_mm.h | 66 ++++ arch/nios2/include/asm/mutex.h | 1 + arch/nios2/include/asm/page.h | 153 +++++++++ arch/nios2/include/asm/pci.h | 25 ++ arch/nios2/include/asm/pgalloc.h | 89 ++++++ arch/nios2/include/asm/pgtable-bits.h | 35 ++ arch/nios2/include/asm/pgtable.h | 23 ++ arch/nios2/include/asm/pgtable_mm.h | 312 ++++++++++++++++++ arch/nios2/include/asm/pgtable_no.h | 83 +++++ arch/nios2/include/asm/processor.h | 127 ++++++++ arch/nios2/include/asm/prom.h | 28 ++ arch/nios2/include/asm/ptrace.h | 42 +++ arch/nios2/include/asm/registers.h | 62 ++++ arch/nios2/include/asm/setup.h | 40 +++ arch/nios2/include/asm/signal.h | 66 ++++ arch/nios2/include/asm/string.h | 24 ++ arch/nios2/include/asm/switch_to.h | 31 ++ arch/nios2/include/asm/thread_info.h | 129 ++++++++ arch/nios2/include/asm/timex.h | 27 ++ arch/nios2/include/asm/tlb.h | 42 +++ arch/nios2/include/asm/tlbflush.h | 52 +++ arch/nios2/include/asm/traps.h | 19 ++ arch/nios2/include/asm/uaccess.h | 336 ++++++++++++++++++++ arch/nios2/include/asm/ucontext.h | 37 +++ arch/nios2/include/asm/unistd.h | 47 +++ arch/nios2/include/uapi/asm/Kbuild | 16 + arch/nios2/include/uapi/asm/byteorder.h | 22 ++ arch/nios2/include/uapi/asm/elf.h | 67 ++++ arch/nios2/include/uapi/asm/fcntl.h | 36 +++ arch/nios2/include/uapi/asm/ioctls.h | 25 ++ arch/nios2/include/uapi/asm/poll.h | 25 ++ arch/nios2/include/uapi/asm/posix_types.h | 58 ++++ arch/nios2/include/uapi/asm/ptrace.h | 129 ++++++++ arch/nios2/include/uapi/asm/setup.h | 41 +++ arch/nios2/include/uapi/asm/sigcontext.h | 35 ++ arch/nios2/include/uapi/asm/signal.h | 140 ++++++++ arch/nios2/include/uapi/asm/stat.h | 90 ++++++ arch/nios2/include/uapi/asm/swab.h | 37 +++ arch/nios2/include/uapi/asm/unistd.h | 370 ++++++++++++++++++++++ 63 files changed, 5247 insertions(+) create mode 100644 arch/nios2/include/asm/Kbuild create mode 100644 arch/nios2/include/asm/asm-macros.h create mode 100644 arch/nios2/include/asm/asm-offsets.h create mode 100644 arch/nios2/include/asm/barrier.h create mode 100644 arch/nios2/include/asm/cache.h create mode 100644 arch/nios2/include/asm/cacheflush.h create mode 100644 arch/nios2/include/asm/checksum.h create mode 100644 arch/nios2/include/asm/cmpxchg.h create mode 100644 arch/nios2/include/asm/cpuinfo.h create mode 100644 arch/nios2/include/asm/delay.h create mode 100644 arch/nios2/include/asm/dma-mapping.h create mode 100644 arch/nios2/include/asm/elf.h create mode 100644 arch/nios2/include/asm/entry.h create mode 100644 arch/nios2/include/asm/flat.h create mode 100644 arch/nios2/include/asm/futex-irq.h create mode 100644 arch/nios2/include/asm/futex.h create mode 100644 arch/nios2/include/asm/gpio.h create mode 100644 arch/nios2/include/asm/io.h create mode 100644 arch/nios2/include/asm/irq.h create mode 100644 arch/nios2/include/asm/irqflags.h create mode 100644 arch/nios2/include/asm/kgdb.h create mode 100644 arch/nios2/include/asm/linkage.h create mode 100644 arch/nios2/include/asm/mmu.h create mode 100644 arch/nios2/include/asm/mmu_context.h create mode 100644 arch/nios2/include/asm/mmu_context_mm.h create mode 100644 arch/nios2/include/asm/mutex.h create mode 100644 arch/nios2/include/asm/page.h create mode 100644 arch/nios2/include/asm/pci.h create mode 100644 arch/nios2/include/asm/pgalloc.h create mode 100644 arch/nios2/include/asm/pgtable-bits.h create mode 100644 arch/nios2/include/asm/pgtable.h create mode 100644 arch/nios2/include/asm/pgtable_mm.h create mode 100644 arch/nios2/include/asm/pgtable_no.h create mode 100644 arch/nios2/include/asm/processor.h create mode 100644 arch/nios2/include/asm/prom.h create mode 100644 arch/nios2/include/asm/ptrace.h create mode 100644 arch/nios2/include/asm/registers.h create mode 100644 arch/nios2/include/asm/setup.h create mode 100644 arch/nios2/include/asm/signal.h create mode 100644 arch/nios2/include/asm/string.h create mode 100644 arch/nios2/include/asm/switch_to.h create mode 100644 arch/nios2/include/asm/thread_info.h create mode 100644 arch/nios2/include/asm/timex.h create mode 100644 arch/nios2/include/asm/tlb.h create mode 100644 arch/nios2/include/asm/tlbflush.h create mode 100644 arch/nios2/include/asm/traps.h create mode 100644 arch/nios2/include/asm/uaccess.h create mode 100644 arch/nios2/include/asm/ucontext.h create mode 100644 arch/nios2/include/asm/unistd.h create mode 100644 arch/nios2/include/uapi/asm/Kbuild create mode 100644 arch/nios2/include/uapi/asm/byteorder.h create mode 100644 arch/nios2/include/uapi/asm/elf.h create mode 100644 arch/nios2/include/uapi/asm/fcntl.h create mode 100644 arch/nios2/include/uapi/asm/ioctls.h create mode 100644 arch/nios2/include/uapi/asm/poll.h create mode 100644 arch/nios2/include/uapi/asm/posix_types.h create mode 100644 arch/nios2/include/uapi/asm/ptrace.h create mode 100644 arch/nios2/include/uapi/asm/setup.h create mode 100644 arch/nios2/include/uapi/asm/sigcontext.h create mode 100644 arch/nios2/include/uapi/asm/signal.h create mode 100644 arch/nios2/include/uapi/asm/stat.h create mode 100644 arch/nios2/include/uapi/asm/swab.h create mode 100644 arch/nios2/include/uapi/asm/unistd.h diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild new file mode 100644 index 0000000000000..30f8ed0931a29 --- /dev/null +++ b/arch/nios2/include/asm/Kbuild @@ -0,0 +1,55 @@ +include include/asm-generic/Kbuild.asm + +header-y += ucontext.h +header-y += traps.h + +generic-y += atomic.h +generic-y += auxvec.h +generic-y += bitops.h +generic-y += bitsperlong.h +generic-y += bug.h +generic-y += bugs.h +generic-y += cputime.h +generic-y += current.h +generic-y += device.h +generic-y += div64.h +generic-y += dma.h +generic-y += emergency-restart.h +generic-y += errno.h +generic-y += exec.h +generic-y += fb.h +generic-y += ftrace.h +generic-y += futex.h +generic-y += hardirq.h +generic-y += hw_irq.h +generic-y += ioctl.h +generic-y += ipcbuf.h +generic-y += irq_regs.h +generic-y += kdebug.h +generic-y += kmap_types.h +generic-y += local.h +generic-y += mman.h +generic-y += module.h +generic-y += msgbuf.h +generic-y += param.h +generic-y += percpu.h +generic-y += resource.h +generic-y += scatterlist.h +generic-y += sections.h +generic-y += segment.h +generic-y += sembuf.h +generic-y += serial.h +generic-y += shmbuf.h +generic-y += shmparam.h +generic-y += siginfo.h +generic-y += socket.h +generic-y += sockios.h +generic-y += spinlock.h +generic-y += statfs.h +generic-y += termbits.h +generic-y += termios.h +generic-y += topology.h +generic-y += types.h +generic-y += unaligned.h +generic-y += user.h +generic-y += xor.h diff --git a/arch/nios2/include/asm/asm-macros.h b/arch/nios2/include/asm/asm-macros.h new file mode 100644 index 0000000000000..5b6f166f88076 --- /dev/null +++ b/arch/nios2/include/asm/asm-macros.h @@ -0,0 +1,313 @@ +/* + * Macro used to simplify coding multi-line assembler. + * Some of the bit test macro can simplify down to one line + * depending on the mask value. + * + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef _ASM_NIOS2_ASMMACROS_H +#define _ASM_NIOS2_ASMMACROS_H +/* + * ANDs reg2 with mask and places the result in reg1. + * + * You cannnot use the same register for reg1 & reg2. + */ + +.macro ANDI32 reg1, reg2, mask +.if \mask & 0xffff + .if \mask & 0xffff0000 + movhi \reg1, %hi(\mask) + movui \reg1, %lo(\mask) + and \reg1, \reg1, \reg2 + .else + andi \reg1, \reg2, %lo(\mask) + .endif +.else + andhi \reg1, \reg2, %hi(\mask) +.endif +.endm + +/* + * ORs reg2 with mask and places the result in reg1. + * + * It is safe to use the same register for reg1 & reg2. + */ + +.macro ORI32 reg1, reg2, mask +.if \mask & 0xffff + .if \mask & 0xffff0000 + orhi \reg1, \reg2, %hi(\mask) + ori \reg1, \reg2, %lo(\mask) + .else + ori \reg1, \reg2, %lo(\mask) + .endif +.else + orhi \reg1, \reg2, %hi(\mask) +.endif +.endm + +/* + * XORs reg2 with mask and places the result in reg1. + * + * It is safe to use the same register for reg1 & reg2. + */ + +.macro XORI32 reg1, reg2, mask +.if \mask & 0xffff + .if \mask & 0xffff0000 + xorhi \reg1, \reg2, %hi(\mask) + xori \reg1, \reg1, %lo(\mask) + .else + xori \reg1, \reg2, %lo(\mask) + .endif +.else + xorhi \reg1, \reg2, %hi(\mask) +.endif +.endm + +/* + * This is a support macro for BTBZ & BTBNZ. It checks + * the bit to make sure it is valid 32 value. + * + * It is safe to use the same register for reg1 & reg2. + */ + +.macro BT reg1, reg2, bit +.if \bit > 31 + .err +.else + .if \bit < 16 + andi \reg1, \reg2, (1 << \bit) + .else + andhi \reg1, \reg2, (1 << (\bit - 16)) + .endif +.endif +.endm + +/* + * Tests the bit in reg2 and branches to label if the + * bit is zero. The result of the bit test is stored in reg1. + * + * It is safe to use the same register for reg1 & reg2. + */ + +.macro BTBZ reg1, reg2, bit, label + BT \reg1, \reg2, \bit + beq \reg1, r0, \label +.endm + +/* + * Tests the bit in reg2 and branches to label if the + * bit is non-zero. The result of the bit test is stored in reg1. + * + * It is safe to use the same register for reg1 & reg2. + */ + +.macro BTBNZ reg1, reg2, bit, label + BT \reg1, \reg2, \bit + bne \reg1, r0, \label +.endm + +/* + * Tests the bit in reg2 and then compliments the bit in reg2. + * The result of the bit test is stored in reg1. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTC reg1, reg2, bit +.if \bit > 31 + .err +.else + .if \bit < 16 + andi \reg1, \reg2, (1 << \bit) + xori \reg2, \reg2, (1 << \bit) + .else + andhi \reg1, \reg2, (1 << (\bit - 16)) + xorhi \reg2, \reg2, (1 << (\bit - 16)) + .endif +.endif +.endm + +/* + * Tests the bit in reg2 and then sets the bit in reg2. + * The result of the bit test is stored in reg1. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTS reg1, reg2, bit +.if \bit > 31 + .err +.else + .if \bit < 16 + andi \reg1, \reg2, (1 << \bit) + ori \reg2, \reg2, (1 << \bit) + .else + andhi \reg1, \reg2, (1 << (\bit - 16)) + orhi \reg2, \reg2, (1 << (\bit - 16)) + .endif +.endif +.endm + +/* + * Tests the bit in reg2 and then resets the bit in reg2. + * The result of the bit test is stored in reg1. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTR reg1, reg2, bit +.if \bit > 31 + .err +.else + .if \bit < 16 + andi \reg1, \reg2, (1 << \bit) + andi \reg2, \reg2, %lo(~(1 << \bit)) + .else + andhi \reg1, \reg2, (1 << (\bit - 16)) + andhi \reg2, \reg2, %lo(~(1 << (\bit - 16))) + .endif +.endif +.endm + +/* + * Tests the bit in reg2 and then compliments the bit in reg2. + * The result of the bit test is stored in reg1. If the + * original bit was zero it branches to label. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTCBZ reg1, reg2, bit, label + BTC \reg1, \reg2, \bit + beq \reg1, r0, \label +.endm + +/* + * Tests the bit in reg2 and then compliments the bit in reg2. + * The result of the bit test is stored in reg1. If the + * original bit was non-zero it branches to label. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTCBNZ reg1, reg2, bit, label + BTC \reg1, \reg2, \bit + bne \reg1, r0, \label +.endm + +/* + * Tests the bit in reg2 and then sets the bit in reg2. + * The result of the bit test is stored in reg1. If the + * original bit was zero it branches to label. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTSBZ reg1, reg2, bit, label + BTS \reg1, \reg2, \bit + beq \reg1, r0, \label +.endm + +/* + * Tests the bit in reg2 and then sets the bit in reg2. + * The result of the bit test is stored in reg1. If the + * original bit was non-zero it branches to label. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTSBNZ reg1, reg2, bit, label + BTS \reg1, \reg2, \bit + bne \reg1, r0, \label +.endm + +/* + * Tests the bit in reg2 and then resets the bit in reg2. + * The result of the bit test is stored in reg1. If the + * original bit was zero it branches to label. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTRBZ reg1, reg2, bit, label + BTR \reg1, \reg2, \bit + bne \reg1, r0, \label +.endm + +/* + * Tests the bit in reg2 and then resets the bit in reg2. + * The result of the bit test is stored in reg1. If the + * original bit was non-zero it branches to label. + * + * It is NOT safe to use the same register for reg1 & reg2. + */ + +.macro BTRBNZ reg1, reg2, bit, label + BTR \reg1, \reg2, \bit + bne \reg1, r0, \label +.endm + +/* + * Tests the bits in mask against reg2 stores the result in reg1. + * If the all the bits in the mask are zero it branches to label. + * + * It is safe to use the same register for reg1 & reg2. + */ + +.macro TSTBZ reg1, reg2, mask, label + ANDI32 \reg1, \reg2, \mask + beq \reg1, r0, \label +.endm + +/* + * Tests the bits in mask against reg2 stores the result in reg1. + * If the any of the bits in the mask are 1 it branches to label. + * + * It is safe to use the same register for reg1 & reg2. + */ + +.macro TSTBNZ reg1, reg2, mask, label + ANDI32 \reg1, \reg2, \mask + bne \reg1, r0, \label +.endm + +/* + * Pushes reg onto the stack. + */ + +.macro PUSH reg + addi sp, sp, -4 + stw \reg, 0(sp) +.endm + +/* + * Pops the top of the stack into reg. + */ + +.macro POP reg + ldw \reg, 0(sp) + addi sp, sp, 4 +.endm + + +#endif /* _ASM_NIOS2_ASMMACROS_H */ diff --git a/arch/nios2/include/asm/asm-offsets.h b/arch/nios2/include/asm/asm-offsets.h new file mode 100644 index 0000000000000..5b9f5e04a0580 --- /dev/null +++ b/arch/nios2/include/asm/asm-offsets.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2009 Thomas Chou + * + * 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 . + * + */ + +#include diff --git a/arch/nios2/include/asm/barrier.h b/arch/nios2/include/asm/barrier.h new file mode 100644 index 0000000000000..899ae3ec283a3 --- /dev/null +++ b/arch/nios2/include/asm/barrier.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ + +#ifndef _ASM_NIOS2_BARRIER_H +#define _ASM_NIOS2_BARRIER_H + +#include + +#define nop() (asm volatile ("nop" : : )) + +#define mb() barrier() +#define rmb() mb() +#define wmb() mb() + +#define set_mb(var, value) ((void) xchg(&var, value)) + +#define smp_mb() mb() +#define smp_rmb() rmb() +#define smp_wmb() wmb() + +#define read_barrier_depends() do { } while (0) +#define smp_read_barrier_depends() do { } while (0) + +#endif /* _ASM_NIOS2_BARRIER_H */ diff --git a/arch/nios2/include/asm/cache.h b/arch/nios2/include/asm/cache.h new file mode 100644 index 0000000000000..84ece847b7e02 --- /dev/null +++ b/arch/nios2/include/asm/cache.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _ASM_NIOS2_CACHE_H +#define _ASM_NIOS2_CACHE_H + +#define NIOS2_DCACHE_SIZE CONFIG_NIOS2_DCACHE_SIZE +#define NIOS2_ICACHE_SIZE CONFIG_NIOS2_ICACHE_SIZE +#define NIOS2_DCACHE_LINE_SIZE CONFIG_NIOS2_DCACHE_LINE_SIZE +#define NIOS2_ICACHE_LINE_SIZE CONFIG_NIOS2_ICACHE_LINE_SIZE +#define NIOS2_ICACHE_LINE_SHIFT CONFIG_NIOS2_ICACHE_LINE_SHIFT + +/* bytes per L1 cache line */ +#define L1_CACHE_SHIFT NIOS2_ICACHE_LINE_SHIFT +#define L1_CACHE_BYTES NIOS2_ICACHE_LINE_SIZE + +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES + +#define __cacheline_aligned +#define ____cacheline_aligned + +#endif diff --git a/arch/nios2/include/asm/cacheflush.h b/arch/nios2/include/asm/cacheflush.h new file mode 100644 index 0000000000000..61d27104de2e6 --- /dev/null +++ b/arch/nios2/include/asm/cacheflush.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2003 Microtronix Datacom Ltd. + * Copyright (C) 2000-2002 Greg Ungerer + * + * Ported from m68knommu. + * + * 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. + */ + +#ifndef _ASM_NIOS2_CACHEFLUSH_H +#define _ASM_NIOS2_CACHEFLUSH_H + +#ifdef CONFIG_MMU + +struct mm_struct; + +extern void flush_cache_all(void); +extern void flush_cache_mm(struct mm_struct *mm); +extern void flush_cache_dup_mm(struct mm_struct *mm); +extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end); +extern void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, + unsigned long pfn); +#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 +extern void flush_dcache_page(struct page *page); + +extern void flush_icache_range(unsigned long start, unsigned long end); +extern void flush_icache_page(struct vm_area_struct *vma, struct page *page); + +#define flush_cache_vmap(start, end) flush_dcache_range(start, end) +#define flush_cache_vunmap(start, end) flush_dcache_range(start, end) + +extern void copy_to_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long user_vaddr, + void *dst, void *src, int len); +extern void copy_from_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long user_vaddr, + void *dst, void *src, int len); + +/* FIXME: Remove: Linux does not define this interfaces, it's arch specific. */ +extern void flush_dcache_range(unsigned long start, unsigned long end); + +#else /* CONFIG_MMU */ + +extern void cache_push(unsigned long vaddr, int len); +extern void dcache_push(unsigned long vaddr, int len); +extern void icache_push(unsigned long vaddr, int len); +extern void dcache_push_all(void); +extern void icache_push_all(void); + +static inline void __flush_cache_all(void) +{ + dcache_push_all(); + icache_push_all(); +} + +#define flush_cache_all() __flush_cache_all() +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_dup_mm(mm) do { } while (0) +#define flush_cache_range(vma, start, end) \ + cache_push((start), (end) - (start)) +#define flush_cache_page(vma, vmaddr) do { } while (0) +#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0 +#define flush_dcache_page(page) do { } while (0) + +#define flush_icache_range(start, end) \ + icache_push((start), (end) - (start)) +#define flush_icache_page(vma, pg) do { } while (0) +#define flush_icache_user_range(vma, pg, adr, len) do { } while (0) +#define flush_cache_vmap(start, end) do { } while (0) +#define flush_cache_vunmap(start, end) do { } while (0) + +#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ + memcpy(dst, src, len) +#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ + memcpy(dst, src, len) + +/* FIXME: Remove: Linux does not define this interfaces, it's arch specific. */ +#define flush_dcache_range(start, end) \ + dcache_push((start), (end) - (start)) + +/* Arch-specific: invalidate virtual memory range. */ +extern void nios2_clear_dcache_range(unsigned long vstart, unsigned long vend); + +#endif /* CONFIG_MMU */ + +#define flush_dcache_mmap_lock(mapping) do { } while (0) +#define flush_dcache_mmap_unlock(mapping) do { } while (0) + +#endif /* _ASM_NIOS2_CACHEFLUSH_H */ diff --git a/arch/nios2/include/asm/checksum.h b/arch/nios2/include/asm/checksum.h new file mode 100644 index 0000000000000..6bc1f0d5df7be --- /dev/null +++ b/arch/nios2/include/asm/checksum.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ + +#ifndef _ASM_NIOS_CHECKSUM_H +#define _ASM_NIOS_CHECKSUM_H + +/* Take these from lib/checksum.c */ +extern __wsum csum_partial(const void *buff, int len, __wsum sum); +extern __wsum csum_partial_copy(const void *src, void *dst, int len, + __wsum sum); +extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst, + int len, __wsum sum, int *csum_err); +#define csum_partial_copy_nocheck(src, dst, len, sum) \ + csum_partial_copy((src), (dst), (len), (sum)) + +extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl); +extern __sum16 ip_compute_csum(const void *buff, int len); + +/* + * Fold a partial checksum + */ +static inline __sum16 csum_fold(__wsum sum) +{ + __asm__ __volatile__( + "add %0, %1, %0\n" + "cmpltu r8, %0, %1\n" + "srli %0, %0, 16\n" + "add %0, %0, r8\n" + "nor %0, %0, %0\n" + : "=r" (sum) + : "r" (sum << 16), "0" (sum) + : "r8"); + return (__force __sum16) sum; +} + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ +#define csum_tcpudp_nofold csum_tcpudp_nofold +static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr, + unsigned short len, + unsigned short proto, + __wsum sum) +{ + __asm__ __volatile__( + "add %0, %1, %0\n" + "cmpltu r8, %0, %1\n" + "add %0, %0, r8\n" /* add carry */ + "add %0, %2, %0\n" + "cmpltu r8, %0, %2\n" + "add %0, %0, r8\n" /* add carry */ + "add %0, %3, %0\n" + "cmpltu r8, %0, %3\n" + "add %0, %0, r8\n" /* add carry */ + : "=r" (sum), "=r" (saddr) + : "r" (daddr), "r" ((ntohs(len) << 16) + (proto * 256)), + "0" (sum), + "1" (saddr) + : "r8"); + + return sum; +} + +static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr, + unsigned short len, + unsigned short proto, __wsum sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum)); +} + +#endif /* _ASM_NIOS_CHECKSUM_H */ diff --git a/arch/nios2/include/asm/cmpxchg.h b/arch/nios2/include/asm/cmpxchg.h new file mode 100644 index 0000000000000..85938711542d8 --- /dev/null +++ b/arch/nios2/include/asm/cmpxchg.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ + +#ifndef _ASM_NIOS2_CMPXCHG_H +#define _ASM_NIOS2_CMPXCHG_H + +#include + +#define xchg(ptr, x) \ + ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))) + +struct __xchg_dummy { unsigned long a[100]; }; +#define __xg(x) ((volatile struct __xchg_dummy *)(x)) + +static inline unsigned long __xchg(unsigned long x, volatile void *ptr, + int size) +{ + unsigned long tmp, flags; + + local_irq_save(flags); + + switch (size) { + case 1: + __asm__ __volatile__( + "ldb %0, %2\n" + "stb %1, %2\n" + : "=&r" (tmp) + : "r" (x), "m" (*__xg(ptr)) + : "memory"); + break; + case 2: + __asm__ __volatile__( + "ldh %0, %2\n" + "sth %1, %2\n" + : "=&r" (tmp) + : "r" (x), "m" (*__xg(ptr)) + : "memory"); + break; + case 4: + __asm__ __volatile__( + "ldw %0, %2\n" + "stw %1, %2\n" + : "=&r" (tmp) + : "r" (x), "m" (*__xg(ptr)) + : "memory"); + break; + } + + local_irq_restore(flags); + return tmp; +} + +#include +#include + +#endif /* _ASM_NIOS2_CMPXCHG_H */ diff --git a/arch/nios2/include/asm/cpuinfo.h b/arch/nios2/include/asm/cpuinfo.h new file mode 100644 index 0000000000000..eafd7267e46b4 --- /dev/null +++ b/arch/nios2/include/asm/cpuinfo.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_CPUINFO_H +#define _ASM_NIOS2_CPUINFO_H + +#include +#include + +struct cpuinfo { + /* Core CPU configuration */ + char cpu_impl[12]; + u32 cpu_clock_freq; + u32 mmu; + u32 has_div; + u32 has_mul; + u32 has_mulx; + + /* CPU caches */ + u32 icache_line_size; + u32 icache_size; + u32 dcache_line_size; + u32 dcache_size; + + /* TLB */ + u32 tlb_pid_num_bits; /* number of bits used for the PID in TLBMISC */ + u32 tlb_num_ways; + u32 tlb_num_ways_log2; + u32 tlb_num_entries; + u32 tlb_num_lines; + u32 tlb_ptr_sz; + + /* Addresses */ + u32 reset_addr; + u32 exception_addr; + u32 fast_tlb_miss_exc_addr; +}; + +extern struct cpuinfo cpuinfo; + +extern void setup_cpuinfo(void); + +#endif /* _ASM_NIOS2_CPUINFO_H */ diff --git a/arch/nios2/include/asm/delay.h b/arch/nios2/include/asm/delay.h new file mode 100644 index 0000000000000..8070a090c09ec --- /dev/null +++ b/arch/nios2/include/asm/delay.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + */ + +#ifndef _ASM_NIOS2_DELAY_H +#define _ASM_NIOS2_DELAY_H + +#include + +static inline void __delay(unsigned long loops) +{ + int dummy; + + __asm__ __volatile__( + "1:\n\t" + " beq %0,zero,2f\n\t" + " addi %0, %0, -1\n\t" + " br 1b\n\t" + "2:\n\t" + : "=r" (dummy) /* Need output for optimizer */ + : "0" (loops)); /* %0 Input */ +} + +/* + * Note that 19 * 226 == 4294 ==~ 2^32 / 10^6, so + * loops = (4294 * usecs * loops_per_jiffy * HZ) / 2^32. + * + * The mul instruction gives us loops = (a * b) / 2^32. + * We choose a = usecs * 19 * HZ and b = loops_per_jiffy * 226 + * because this lets us support a wide range of HZ and + * loops_per_jiffy values without either a or b overflowing 2^32. + * Thus we need usecs * HZ <= (2^32 - 1) / 19 = 226050910 and + * loops_per_jiffy <= (2^32 - 1) / 226 = 19004280 + * (which corresponds to ~3800 bogomips at HZ = 100). + * -- paulus + */ +#define __MAX_UDELAY (226050910UL/HZ) /* maximum udelay argument */ +#define __MAX_NDELAY (4294967295UL/HZ) /* maximum ndelay argument */ + +extern unsigned long loops_per_jiffy; + +static inline void __udelay(unsigned int x) +{ + unsigned int loops; + + /* + * Note, if this is compiled with -mhw-mulx it will produce a "mulxuu" + * (at least in toolchain 145) so there is no need for inline + * assembly here anymore, which might in turn be emulated if unsupported + * by the design. + */ + loops = (unsigned int)((((unsigned long long)(x) * + (unsigned long long)(loops_per_jiffy * 226))) >> 32); + +/* + __asm__("mulxuu %0,%1,%2" : "=r" (loops) : + "r" (x), "r" (loops_per_jiffy * 226)); +*/ + __delay(loops); +} + +static inline void __ndelay(unsigned int x) +{ + unsigned int loops; + + /* see comment in __udelay */ + loops = (unsigned int)((((unsigned long long)(x) * + (unsigned long long)(loops_per_jiffy * 5))) >> 32); + +/* + __asm__("mulxuu %0,%1,%2" : "=r" (loops) : + "r" (x), "r" (loops_per_jiffy * 5)); +*/ + __delay(loops); +} + +extern void __bad_udelay(void); /* deliberately undefined */ +extern void __bad_ndelay(void); /* deliberately undefined */ + +#define udelay(n) (__builtin_constant_p(n) ? \ + ((n) > __MAX_UDELAY ? __bad_udelay() : __udelay((n) * (19 * HZ))) : \ + __udelay((n) * (19 * HZ))) + +#define ndelay(n) (__builtin_constant_p(n) ? \ + ((n) > __MAX_NDELAY ? __bad_ndelay() : __ndelay((n) * HZ)) : \ + __ndelay((n) * HZ)) + +#endif /* _ASM_NIOS2_DELAY_H */ diff --git a/arch/nios2/include/asm/dma-mapping.h b/arch/nios2/include/asm/dma-mapping.h new file mode 100644 index 0000000000000..73c25920cf9c1 --- /dev/null +++ b/arch/nios2/include/asm/dma-mapping.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * + * 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. + */ + +#ifndef _ASM_NIOS2_DMA_MAPPING_H +#define _ASM_NIOS2_DMA_MAPPING_H + +#include +#include +#include + +static inline void __dma_sync(unsigned long addr, size_t size, + enum dma_data_direction direction) +{ + switch (direction) { + case DMA_FROM_DEVICE: +#ifndef CONFIG_MMU + nios2_clear_dcache_range(addr, (unsigned long)(addr + size)); + break; +#endif + case DMA_TO_DEVICE: + case DMA_BIDIRECTIONAL: + flush_dcache_range(addr, (unsigned long)(addr + size)); + break; + default: + BUG(); + } +} + +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) +#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag); + +void dma_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle); + +static inline dma_addr_t dma_map_single(struct device *dev, void *ptr, + size_t size, + enum dma_data_direction direction) +{ + __dma_sync((unsigned long)ptr, size, direction); + return virt_to_phys(ptr); +} + +static inline void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction direction) +{ +} + +extern int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction); +extern dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, enum dma_data_direction direction); +extern void dma_unmap_page(struct device *dev, dma_addr_t dma_address, + size_t size, enum dma_data_direction direction); +extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nhwentries, enum dma_data_direction direction); +extern void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction); +extern void dma_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, size_t size, enum dma_data_direction direction); +extern void dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t dma_handle, unsigned long offset, size_t size, + enum dma_data_direction direction); +extern void dma_sync_single_range_for_device(struct device *dev, + dma_addr_t dma_handle, unsigned long offset, size_t size, + enum dma_data_direction direction); +extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction); +extern void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction); + +static inline int dma_supported(struct device *dev, u64 mask) +{ + /* we fall back to GFP_DMA when the mask isn't all 1s, + * so we can't guarantee allocations that must be + * within a tighter range than GFP_DMA. + */ + if (mask < 0x00ffffff) + return 0; + + return 1; +} + +static inline int dma_set_mask(struct device *dev, u64 mask) +{ + if (!dev->dma_mask || !dma_supported(dev, mask)) + return -EIO; + + *dev->dma_mask = mask; + + return 0; +} + +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + return 0; +} + +static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, + enum dma_data_direction direction) +{ + __dma_sync((unsigned long)vaddr, size, direction); +} + +#endif /* _ASM_NIOS2_DMA_MAPPING_H */ diff --git a/arch/nios2/include/asm/elf.h b/arch/nios2/include/asm/elf.h new file mode 100644 index 0000000000000..e3bf48a10935f --- /dev/null +++ b/arch/nios2/include/asm/elf.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_ELF_H +#define _ASM_NIOS2_ELF_H + +#include + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) ((x)->e_machine == EM_ALTERA_NIOS2) + +#define ELF_PLAT_INIT(_r, load_addr) + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +/* This is the location that an ET_DYN program is loaded if exec'ed. Typical + use of this is to invoke "./ld.so someprog" to test out a new version of + the loader. We need to make sure that it is out of the way of the program + that it will "exec", and that there is sufficient room for the brk. */ + +#define ELF_ET_DYN_BASE 0xD0000000UL + +/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is + now struct_user_regs, they are different) */ + +#ifdef CONFIG_MMU +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ +{ do { \ + /* Bleech. */ \ + pr_reg[0] = regs->r8; \ + pr_reg[1] = regs->r9; \ + pr_reg[2] = regs->r10; \ + pr_reg[3] = regs->r11; \ + pr_reg[4] = regs->r12; \ + pr_reg[5] = regs->r13; \ + pr_reg[6] = regs->r14; \ + pr_reg[7] = regs->r15; \ + pr_reg[8] = regs->r1; \ + pr_reg[9] = regs->r2; \ + pr_reg[10] = regs->r3; \ + pr_reg[11] = regs->r4; \ + pr_reg[12] = regs->r5; \ + pr_reg[13] = regs->r6; \ + pr_reg[14] = regs->r7; \ + pr_reg[15] = regs->orig_r2; \ + pr_reg[16] = regs->ra; \ + pr_reg[17] = regs->fp; \ + pr_reg[18] = regs->sp; \ + pr_reg[19] = regs->gp; \ + pr_reg[20] = regs->estatus; \ + pr_reg[21] = regs->ea; \ + pr_reg[22] = regs->orig_r7; \ + { \ + struct switch_stack *sw = ((struct switch_stack *)regs) - 1; \ + pr_reg[23] = sw->r16; \ + pr_reg[24] = sw->r17; \ + pr_reg[25] = sw->r18; \ + pr_reg[26] = sw->r19; \ + pr_reg[27] = sw->r20; \ + pr_reg[28] = sw->r21; \ + pr_reg[29] = sw->r22; \ + pr_reg[30] = sw->r23; \ + pr_reg[31] = sw->fp; \ + pr_reg[32] = sw->gp; \ + pr_reg[33] = sw->ra; \ + } \ +} while (0); } +#else +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ +{ do { \ + /* Bleech. */ \ + pr_reg[0] = regs->r1; \ + pr_reg[1] = regs->r2; \ + pr_reg[2] = regs->r3; \ + pr_reg[3] = regs->r4; \ + pr_reg[4] = regs->r5; \ + pr_reg[5] = regs->r6; \ + pr_reg[6] = regs->r7; \ + pr_reg[7] = regs->r8; \ + pr_reg[8] = regs->r9; \ + pr_reg[9] = regs->r10; \ + pr_reg[10] = regs->r11; \ + pr_reg[11] = regs->r12; \ + pr_reg[12] = regs->r13; \ + pr_reg[13] = regs->r14; \ + pr_reg[14] = regs->r15; \ + pr_reg[23] = regs->sp; \ + pr_reg[26] = regs->estatus; \ + { \ + struct switch_stack *sw = ((struct switch_stack *)regs) - 1; \ + pr_reg[15] = sw->r16; \ + pr_reg[16] = sw->r17; \ + pr_reg[17] = sw->r18; \ + pr_reg[18] = sw->r19; \ + pr_reg[19] = sw->r20; \ + pr_reg[20] = sw->r21; \ + pr_reg[21] = sw->r22; \ + pr_reg[22] = sw->r23; \ + pr_reg[24] = sw->fp; \ + pr_reg[25] = sw->gp; \ + } \ +} while (0); } + +#endif /* CONFIG_MMU */ + +/* This yields a mask that user programs can use to figure out what + instruction set this cpu supports. */ + +#define ELF_HWCAP (0) + +/* This yields a string that ld.so will use to load implementation + specific libraries for optimization. This is more specific in + intent than poking at uname or /proc/cpuinfo. */ + +#define ELF_PLATFORM (NULL) + +#define SET_PERSONALITY(ex) \ + set_personality(PER_LINUX_32BIT | (current->personality & (~PER_MASK))) + +#endif /* _ASM_NIOS2_ELF_H */ diff --git a/arch/nios2/include/asm/entry.h b/arch/nios2/include/asm/entry.h new file mode 100644 index 0000000000000..150eea6dd9e92 --- /dev/null +++ b/arch/nios2/include/asm/entry.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * Based on m68knommu asm/entry.h + * + * 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. + */ + +#ifndef _ASM_NIOS2_ENTRY_H +#define _ASM_NIOS2_ENTRY_H + +#ifdef __ASSEMBLY__ + +#include +#include +#include + +/* + * Stack layout in 'ret_from_exception': + * + * This allows access to the syscall arguments in registers r4-r8 + * + * 0(sp) - r8 + * 4(sp) - r9 + * 8(sp) - r10 + * C(sp) - r11 + * 10(sp) - r12 + * 14(sp) - r13 + * 18(sp) - r14 + * 1C(sp) - r15 + * 20(sp) - r1 + * 24(sp) - r2 + * 28(sp) - r3 + * 2C(sp) - r4 + * 30(sp) - r5 + * 34(sp) - r6 + * 38(sp) - r7 + * 3C(sp) - orig_r2 + * 40(sp) - ra + * 44(sp) - fp + * 48(sp) - sp + * 4C(sp) - gp + * 50(sp) - estatus + * 54(sp) - status_extension (NOMMU only) + * 58(sp) - ea + */ + +/* + * Standard Nios2 interrupt entry and exit macros. + * Must be called with interrupts disabled. + */ +.macro SAVE_ALL +#ifdef CONFIG_MMU + rdctl r24, estatus + andi r24, r24, ESTATUS_EU + beq r24, r0, 1f /* In supervisor mode, already on kernel stack */ +#else + movia r24, status_extension /* Read status extension */ + ldw r24, 0(r24) + andi r24, r24, PS_S + bne r24, r0, 1f /* In supervisor mode, already on kernel stack */ +#endif /* CONFIG_MMU */ + + movia r24, _current_thread /* Switch to current kernel stack */ + ldw r24, 0(r24) /* using the thread_info */ + addi r24, r24, THREAD_SIZE-PT_REGS_SIZE + stw sp, PT_SP(r24) /* Save user stack before changing */ + mov sp, r24 + br 2f + +1 : mov r24, sp + addi sp, sp, -PT_REGS_SIZE /* Backup the kernel stack pointer */ + stw r24, PT_SP(sp) +2 : stw r1, PT_R1(sp) + stw r2, PT_R2(sp) + stw r3, PT_R3(sp) + stw r4, PT_R4(sp) + stw r5, PT_R5(sp) + stw r6, PT_R6(sp) + stw r7, PT_R7(sp) + stw r8, PT_R8(sp) + stw r9, PT_R9(sp) + stw r10, PT_R10(sp) + stw r11, PT_R11(sp) + stw r12, PT_R12(sp) + stw r13, PT_R13(sp) + stw r14, PT_R14(sp) + stw r15, PT_R15(sp) + stw r2, PT_ORIG_R2(sp) +#ifdef CONFIG_MMU + stw r7, PT_ORIG_R7(sp) +#endif + stw ra, PT_RA(sp) + stw fp, PT_FP(sp) + stw gp, PT_GP(sp) + rdctl r24, estatus + stw r24, PT_ESTATUS(sp) +#ifndef CONFIG_MMU + movia r24, status_extension /* Read status extension */ + ldw r1, 0(r24) + stw r1, PT_STATUS_EXTENSION(sp) /* Store user/supervisor status */ + ORI32 r1, r1, PS_S /* Set supervisor mode */ + stw r1, 0(r24) +#endif /* CONFIG_MMU */ + stw ea, PT_EA(sp) +.endm + +.macro RESTORE_ALL +#ifdef CONFIG_MMU + ldw r1, PT_R1(sp) /* Restore registers */ +#else + ldw r1, PT_STATUS_EXTENSION(sp) /* Restore user/supervisor status */ + movia r24, status_extension + stw r1, 0(r24) + ldw r1, PT_R1(sp) /* Restore registers */ +#endif /* CONFIG_MMU */ + ldw r2, PT_R2(sp) + ldw r3, PT_R3(sp) + ldw r4, PT_R4(sp) + ldw r5, PT_R5(sp) + ldw r6, PT_R6(sp) + ldw r7, PT_R7(sp) + ldw r8, PT_R8(sp) + ldw r9, PT_R9(sp) + ldw r10, PT_R10(sp) + ldw r11, PT_R11(sp) + ldw r12, PT_R12(sp) + ldw r13, PT_R13(sp) + ldw r14, PT_R14(sp) + ldw r15, PT_R15(sp) + ldw ra, PT_RA(sp) + ldw fp, PT_FP(sp) + ldw gp, PT_GP(sp) + ldw r24, PT_ESTATUS(sp) + wrctl estatus, r24 + ldw ea, PT_EA(sp) + ldw sp, PT_SP(sp) /* Restore sp last */ +.endm + +.macro SAVE_SWITCH_STACK + addi sp, sp, -SWITCH_STACK_SIZE + stw r16, SW_R16(sp) + stw r17, SW_R17(sp) + stw r18, SW_R18(sp) + stw r19, SW_R19(sp) + stw r20, SW_R20(sp) + stw r21, SW_R21(sp) + stw r22, SW_R22(sp) + stw r23, SW_R23(sp) + stw fp, SW_FP(sp) + stw gp, SW_GP(sp) + stw ra, SW_RA(sp) +.endm + +.macro RESTORE_SWITCH_STACK + ldw r16, SW_R16(sp) + ldw r17, SW_R17(sp) + ldw r18, SW_R18(sp) + ldw r19, SW_R19(sp) + ldw r20, SW_R20(sp) + ldw r21, SW_R21(sp) + ldw r22, SW_R22(sp) + ldw r23, SW_R23(sp) + ldw fp, SW_FP(sp) + ldw gp, SW_GP(sp) + ldw ra, SW_RA(sp) + addi sp, sp, SWITCH_STACK_SIZE +.endm + +#endif /* __ASSEMBLY__ */ +#endif /* _ASM_NIOS2_ENTRY_H */ diff --git a/arch/nios2/include/asm/flat.h b/arch/nios2/include/asm/flat.h new file mode 100644 index 0000000000000..933b7a1210dd2 --- /dev/null +++ b/arch/nios2/include/asm/flat.h @@ -0,0 +1,136 @@ +/* + * include/asm-nios2/flat.h -- uClinux bFLT relocations + * + * Copyright (C) 2004,05 Microtronix Datacom Ltd + * + * 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. + * + * Written by Wentao Xu + */ + +#ifndef _ASM_NIOS2_FLAT_H__ +#define _ASM_NIOS2_FLAT_H__ + +#define flat_reloc_valid(reloc, size) ((reloc) <= (size + 0x8000)) + +/* The stack is 64-bit aligned for Nios II, so (sp - 1) shall + * be 64-bit aligned, where -1 is for argc + */ +#define flat_stack_align(sp) \ + (sp = (unsigned long *)(((unsigned long)sp - 1) & (-8))) + +/* The uClibc port for Nios II expects the argc is followed by argv and envp */ +#define flat_argvp_envp_on_stack() 1 + +#define flat_old_ram_flag(flags) (flags) + +/* We store the type of relocation in the top 4 bits of the `relval.' */ + +/* Convert a relocation entry into an address. */ +static inline unsigned long flat_get_relocate_addr(unsigned long relval) +{ + return relval & 0x0fffffff; /* Mask out top 4-bits */ +} + +#define FLAT_NIOS2_RELOC_TYPE(relval) ((relval) >> 28) + +#define FLAT_NIOS2_R_32 0 /* Normal 32-bit reloc */ +#define FLAT_NIOS2_R_HI_LO 1 /* High 16-bits + low 16-bits field */ +#define FLAT_NIOS2_R_HIADJ_LO 2 /* High 16-bits adjust + low 16-bits field */ +#define FLAT_NIOS2_R_CALL26 4 /* Call imm26 */ + +#define flat_set_persistent(relval, p) 0 + +/* Extract the address to be relocated from the symbol reference at rp; + * relval is the raw relocation-table entry from which RP is derived. + * rp shall always be 32-bit aligned + */ +static inline unsigned long flat_get_addr_from_rp(unsigned long *rp, + unsigned long relval, + unsigned long flags, + unsigned long *persistent) +{ + switch (FLAT_NIOS2_RELOC_TYPE(relval)) { + case FLAT_NIOS2_R_32: + /* Simple 32-bit address. The loader expect it in bigger + * endian */ + return htonl(*rp); + + case FLAT_NIOS2_R_HI_LO: + /* get the two 16-bit immediate value from instructions, then + * construct a 32-bit value. Again the loader expect bigger + * endian + */ + return htonl((((rp[0] >> 6) & 0xFFFF) << 16) | + ((rp[1] >> 6) & 0xFFFF)); + + case FLAT_NIOS2_R_HIADJ_LO: + { + /* get the two 16-bit immediate value from instructions, then + * construct a 32-bit value. Again the loader expect bigger + * endian + */ + unsigned int low, high; + high = (rp[0] >> 6) & 0xFFFF; + low = (rp[1] >> 6) & 0xFFFF; + + if ((low >> 15) & 1) + high--; + + return htonl((high << 16) | low); + } + case FLAT_NIOS2_R_CALL26: + /* the 26-bit immediate value is actually 28-bit */ + return htonl(((*rp) >> 6) << 2); + + default: + return ~0; /* bogus value */ + } +} + +/* Insert the address addr into the symbol reference at rp; + * relval is the raw relocation-table entry from which rp is derived. + * rp shall always be 32-bit aligned + */ +static inline void flat_put_addr_at_rp(unsigned long *rp, unsigned long addr, + unsigned long relval) +{ + unsigned long exist_val; + switch (FLAT_NIOS2_RELOC_TYPE(relval)) { + case FLAT_NIOS2_R_32: + /* Simple 32-bit address. */ + *rp = addr; + break; + + case FLAT_NIOS2_R_HI_LO: + exist_val = rp[0]; + rp[0] = ((((exist_val >> 22) << 16) | (addr >> 16)) << 6) | + (exist_val & 0x3F); + exist_val = rp[1]; + rp[1] = ((((exist_val >> 22) << 16) | (addr & 0xFFFF)) << 6) | + (exist_val & 0x3F); + break; + + case FLAT_NIOS2_R_HIADJ_LO: + { + unsigned int high = (addr >> 16); + if ((addr >> 15) & 1) + high = (high + 1) & 0xFFFF; + exist_val = rp[0]; + rp[0] = ((((exist_val >> 22) << 16) | high) << 6) | + (exist_val & 0x3F); + exist_val = rp[1]; + rp[1] = ((((exist_val >> 22) << 16) | (addr & 0xFFFF)) << 6) | + (exist_val & 0x3F); + break; + } + case FLAT_NIOS2_R_CALL26: + /* the opcode of CALL is 0, so just store the value */ + *rp = ((addr >> 2) << 6); + break; + } +} + +#endif /* _ASM_NIOS2_FLAT_H__ */ diff --git a/arch/nios2/include/asm/futex-irq.h b/arch/nios2/include/asm/futex-irq.h new file mode 100644 index 0000000000000..ade929663dc64 --- /dev/null +++ b/arch/nios2/include/asm/futex-irq.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2013 Altera Corporation + * + * 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 . + * + * Based on asm/futex.h from sh platform. + * + */ + +#ifndef __ASM_NIOS2_FUTEX_IRQ_H +#define __ASM_NIOS2_FUTEX_IRQ_H + + +static inline int atomic_futex_op_xchg_set(int oparg, u32 __user *uaddr, + int *oldval) +{ + unsigned long flags; + int ret; + + local_irq_save(flags); + + ret = get_user(*oldval, uaddr); + if (!ret) + ret = put_user(oparg, uaddr); + + local_irq_restore(flags); + + return ret; +} + +static inline int atomic_futex_op_xchg_add(int oparg, u32 __user *uaddr, + int *oldval) +{ + unsigned long flags; + int ret; + + local_irq_save(flags); + + ret = get_user(*oldval, uaddr); + if (!ret) + ret = put_user(*oldval + oparg, uaddr); + + local_irq_restore(flags); + + return ret; +} + +static inline int atomic_futex_op_xchg_or(int oparg, u32 __user *uaddr, + int *oldval) +{ + unsigned long flags; + int ret; + + local_irq_save(flags); + + ret = get_user(*oldval, uaddr); + if (!ret) + ret = put_user(*oldval | oparg, uaddr); + + local_irq_restore(flags); + + return ret; +} + +static inline int atomic_futex_op_xchg_and(int oparg, u32 __user *uaddr, + int *oldval) +{ + unsigned long flags; + int ret; + + local_irq_save(flags); + + ret = get_user(*oldval, uaddr); + if (!ret) + ret = put_user(*oldval & oparg, uaddr); + + local_irq_restore(flags); + + return ret; +} + +static inline int atomic_futex_op_xchg_xor(int oparg, u32 __user *uaddr, + int *oldval) +{ + unsigned long flags; + int ret; + + local_irq_save(flags); + + ret = get_user(*oldval, uaddr); + if (!ret) + ret = put_user(*oldval ^ oparg, uaddr); + + local_irq_restore(flags); + + return ret; +} + +static inline int atomic_futex_op_cmpxchg_inatomic(u32 *uval, + u32 __user *uaddr, + u32 oldval, u32 newval) +{ + unsigned long flags; + int ret; + u32 prev = 0; + + local_irq_save(flags); + + ret = get_user(prev, uaddr); + if (!ret && oldval == prev) + ret = put_user(newval, uaddr); + + local_irq_restore(flags); + + *uval = prev; + return ret; +} + +#endif /* __ASM_NIOS2_FUTEX_IRQ_H */ diff --git a/arch/nios2/include/asm/futex.h b/arch/nios2/include/asm/futex.h new file mode 100644 index 0000000000000..52b2fa9c3aeb9 --- /dev/null +++ b/arch/nios2/include/asm/futex.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 Altera Corporation + * + * 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 . + * + * Based on asm/futex.h from sh platform. + * + */ + +#ifndef __ASM_NIOS2_FUTEX_H +#define __ASM_NIOS2_FUTEX_H + +#ifdef __KERNEL__ + +#include +#include +#include + +#include + +static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) +{ + int op = (encoded_op >> 28) & 7; + int cmp = (encoded_op >> 24) & 15; + int oparg = (encoded_op << 8) >> 20; + int cmparg = (encoded_op << 20) >> 20; + int oldval = 0, ret; + + if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) + oparg = 1 << oparg; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + + pagefault_disable(); + + switch (op) { + case FUTEX_OP_SET: + ret = atomic_futex_op_xchg_set(oparg, uaddr, &oldval); + break; + case FUTEX_OP_ADD: + ret = atomic_futex_op_xchg_add(oparg, uaddr, &oldval); + break; + case FUTEX_OP_OR: + ret = atomic_futex_op_xchg_or(oparg, uaddr, &oldval); + break; + case FUTEX_OP_ANDN: + ret = atomic_futex_op_xchg_and(~oparg, uaddr, &oldval); + break; + case FUTEX_OP_XOR: + ret = atomic_futex_op_xchg_xor(oparg, uaddr, &oldval); + break; + default: + ret = -ENOSYS; + break; + } + + pagefault_enable(); + + if (!ret) { + switch (cmp) { + case FUTEX_OP_CMP_EQ: + ret = (oldval == cmparg); + break; + case FUTEX_OP_CMP_NE: + ret = (oldval != cmparg); + break; + case FUTEX_OP_CMP_LT: + ret = (oldval < cmparg); + break; + case FUTEX_OP_CMP_GE: + ret = (oldval >= cmparg); + break; + case FUTEX_OP_CMP_LE: + ret = (oldval <= cmparg); + break; + case FUTEX_OP_CMP_GT: + ret = (oldval > cmparg); + break; + default: + ret = -ENOSYS; + } + } + + return ret; +} + +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) +{ + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + + return atomic_futex_op_cmpxchg_inatomic(uval, uaddr, oldval, newval); +} + +#endif /* __KERNEL__ */ +#endif /* __ASM_NIOS2_FUTEX_H */ diff --git a/arch/nios2/include/asm/gpio.h b/arch/nios2/include/asm/gpio.h new file mode 100644 index 0000000000000..1bf4061e3536c --- /dev/null +++ b/arch/nios2/include/asm/gpio.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 Tobias Klauser + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_GPIO_H +#define _ASM_NIOS2_GPIO_H + +#include +#include + +#ifdef CONFIG_GPIOLIB + +#define gpio_get_value __gpio_get_value +#define gpio_set_value __gpio_set_value +#define gpio_cansleep __gpio_cansleep +#define gpio_to_irq __gpio_to_irq + +static inline int irq_to_gpio(unsigned int irq) +{ + return -EINVAL; +} + +#endif /* CONFIG_GPIOLIB */ + +#endif /* _ASM_NIOS2_GPIO_H */ diff --git a/arch/nios2/include/asm/io.h b/arch/nios2/include/asm/io.h new file mode 100644 index 0000000000000..2e01ee6572a0f --- /dev/null +++ b/arch/nios2/include/asm/io.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ + +#ifndef _ASM_NIOS2_IO_H +#define _ASM_NIOS2_IO_H + +#include + +#define readb(addr) \ + ({ unsigned char __v = (*(volatile unsigned char *)(addr)); __v; }) +#define readw(addr) \ + ({ unsigned short __v = (*(volatile unsigned short *)(addr)); __v; }) +#define readl(addr) \ + ({ unsigned int __v = (*(volatile unsigned int *)(addr)); __v; }) + +#define readb_relaxed(addr) readb(addr) +#define readw_relaxed(addr) readw(addr) +#define readl_relaxed(addr) readl(addr) + +#define writeb(b, addr) \ + (void)((*(volatile unsigned char *)(addr)) = (b)) +#define writew(b, addr) \ + (void)((*(volatile unsigned short *)(addr)) = (b)) +#define writel(b, addr) \ + (void)((*(volatile unsigned int *)(addr)) = (b)) + +#define __raw_readb readb +#define __raw_readw readw +#define __raw_readl readl +#define __raw_writeb writeb +#define __raw_writew writew +#define __raw_writel writel + +#ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE +# define __IO_USE_DUFFS +#endif + +#ifdef __IO_USE_DUFFS + +/* Use "Duff's Device" to unroll the loops. */ +#define __IO_OUT_LOOP(a, b, l) \ + do { \ + if (l > 0) { \ + int _n = (l + 7) / 8; \ + switch (l % 8) { \ + case 0: \ + do { \ + *a = *b++; \ + case 7: \ + *a = *b++; \ + case 6: \ + *a = *b++; \ + case 5: \ + *a = *b++; \ + case 4: \ + *a = *b++; \ + case 3: \ + *a = *b++; \ + case 2: \ + *a = *b++; \ + case 1: \ + *a = *b++; \ + } while (--_n > 0); \ + } \ + } \ + } while (0) + +#define __IO_IN_LOOP(a, b, l) \ + do { \ + if (l > 0) { \ + int _n = (l + 7) / 8; \ + switch (l % 8) { \ + case 0: \ + do { \ + *b++ = *a; \ + case 7: \ + *b++ = *a; \ + case 6: \ + *b++ = *a; \ + case 5: \ + *b++ = *a; \ + case 4: \ + *b++ = *a; \ + case 3: \ + *b++ = *a; \ + case 2: \ + *b++ = *a; \ + case 1: \ + *b++ = *a; \ + } while (--_n > 0); \ + } \ + } \ + } while (0) + +#else /* __IO_USE_DUFFS */ + +/* Use simple loops. */ +#define __IO_OUT_LOOP(a, b, l) \ + do { \ + while (l--) \ + *a = *b++; \ + } while (0) + +#define __IO_IN_LOOP(a, b, l) \ + do { \ + while (l--) \ + *b++ = *a; \ + } while (0) + +#endif /* __IO_USE_DUFFS */ + +static inline void io_outsb(unsigned int addr, void *buf, int len) +{ + volatile unsigned char *ap = (volatile unsigned char *)addr; + unsigned char *bp = (unsigned char *)buf; + __IO_OUT_LOOP(ap, bp, len); +} + +static inline void io_outsw(unsigned int addr, void *buf, int len) +{ + volatile unsigned short *ap = (volatile unsigned short *)addr; + unsigned short *bp = (unsigned short *)buf; + __IO_OUT_LOOP(ap, bp, len); +} + +static inline void io_outsl(unsigned int addr, void *buf, int len) +{ + volatile unsigned int *ap = (volatile unsigned int *)addr; + unsigned int *bp = (unsigned int *)buf; + __IO_OUT_LOOP(ap, bp, len); +} + +static inline void io_insb(unsigned int addr, void *buf, int len) +{ + volatile unsigned char *ap = (volatile unsigned char *)addr; + unsigned char *bp = (unsigned char *)buf; + __IO_IN_LOOP(ap, bp, len); +} + +static inline void io_insw(unsigned int addr, void *buf, int len) +{ + volatile unsigned short *ap = (volatile unsigned short *)addr; + unsigned short *bp = (unsigned short *)buf; + __IO_IN_LOOP(ap, bp, len); +} + +static inline void io_insl(unsigned int addr, void *buf, int len) +{ + volatile unsigned int *ap = (volatile unsigned int *)addr; + unsigned int *bp = (unsigned int *)buf; + __IO_IN_LOOP(ap, bp, len); +} + +#undef __IO_OUT_LOOP +#undef __IO_IN_LOOP +#undef __IO_USE_DUFFS + +#define mmiowb() + +/* + * make the short names macros so specific devices + * can override them as required + */ + +#define memset_io(a, b, c) memset((void *)(a), (b), (c)) +#define memcpy_fromio(a, b, c) memcpy((a), (void *)(b), (c)) +#define memcpy_toio(a, b, c) memcpy((void *)(a), (b), (c)) + +#define inb(addr) readb(addr) +#define inw(addr) readw(addr) +#define inl(addr) readl(addr) +#define outb(x, addr) ((void) writeb(x, addr)) +#define outw(x, addr) ((void) writew(x, addr)) +#define outl(x, addr) ((void) writel(x, addr)) + +#define inb_p(addr) inb(addr) +#define inw_p(addr) inw(addr) +#define inl_p(addr) inl(addr) +#define outb_p(x, addr) outb(x, addr) +#define outw_p(x, addr) outw(x, addr) +#define outl_p(x, addr) outl(x, addr) + +#define outsb(a, b, l) io_outsb(a, b, l) +#define outsw(a, b, l) io_outsw(a, b, l) +#define outsl(a, b, l) io_outsl(a, b, l) + +#define insb(a, b, l) io_insb(a, b, l) +#define insw(a, b, l) io_insw(a, b, l) +#define insl(a, b, l) io_insl(a, b, l) + +#define ioread8_rep(a, d, c) insb(a, d, c) +#define ioread16_rep(a, d, c) insw(a, d, c) +#define ioread32_rep(a, d, c) insl(a, d, c) +#define iowrite8_rep(a, s, c) outsb(a, s, c) +#define iowrite16_rep(a, s, c) outsw(a, s, c) +#define iowrite32_rep(a , s, c) outsl(a, s, c) + +#define ioread8(X) readb(X) +#define ioread16(X) readw(X) +#define ioread32(X) readl(X) +#define iowrite8(val, X) writeb(val, X) +#define iowrite16(val, X) writew(val, X) +#define iowrite32(val, X) writel(val, X) + +#ifdef CONFIG_MMU + +extern void __iomem *__ioremap(unsigned long physaddr, unsigned long size, + unsigned long cacheflag); +extern void __iounmap(void __iomem *addr); + +#else + +static inline void __iomem *__ioremap(unsigned long physaddr, + unsigned long size, + unsigned long cacheflag) +{ + if (cacheflag & _PAGE_CACHED) { + return (void __iomem *)(physaddr & ~CONFIG_IO_REGION_BASE); + } else { +/* flush_dcache_range(physaddr, physaddr + size); */ + return (void __iomem *)(physaddr | CONFIG_IO_REGION_BASE); + } +} + +#define __iounmap(addr) do {} while (0) + +#endif /* CONFIG_MMU */ + +static inline void __iomem *ioremap(unsigned long physaddr, unsigned long size) +{ + return __ioremap(physaddr, size, 0); +} + +static inline void __iomem *ioremap_nocache(unsigned long physaddr, + unsigned long size) +{ + return __ioremap(physaddr, size, 0); +} + +static inline void __iomem *ioremap_writethrough(unsigned long physaddr, + unsigned long size) +{ + return __ioremap(physaddr, size, 0); +} + +static inline void __iomem *ioremap_fullcache(unsigned long physaddr, + unsigned long size) +{ + return __ioremap(physaddr, size, _PAGE_CACHED); +} + +static inline void iounmap(void __iomem *addr) +{ + __iounmap(addr); +} + +#define IO_SPACE_LIMIT 0xffffffff + +/* Pages to physical address... */ +#ifdef CONFIG_MMU +# define page_to_phys(page) virt_to_phys(page_to_virt(page)) +# define page_to_bus(page) page_to_virt(page) +#else +# define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) +# define page_to_bus(page) ((page - mem_map) << PAGE_SHIFT) +#endif /* CONFIG_MMU */ + +/* Macros used for converting between virtual and physical mappings. */ +#ifdef CONFIG_MMU +# define phys_to_virt(vaddr) \ + ((void *)((unsigned long)vaddr + PAGE_OFFSET - PHYS_OFFSET)) +# define virt_to_phys(vaddr) \ + ((unsigned long)((unsigned long)vaddr - PAGE_OFFSET + PHYS_OFFSET)) +#else +# define phys_to_virt(vaddr) ((void *)(vaddr)) +# define virt_to_phys(vaddr) ((unsigned long)(vaddr)) +#endif /* CONFIG_MMU */ + +#define virt_to_bus virt_to_phys +#define bus_to_virt phys_to_virt + +#define ioport_map(port, nr) ioremap(port, nr) +#define ioport_unmap(port) iounmap(port) + +/* + * Convert a physical pointer to a virtual kernel pointer for /dev/mem + * access + */ +#define xlate_dev_mem_ptr(p) __va(p) + +/* + * Convert a virtual cached pointer to an uncached pointer + */ +#define xlate_dev_kmem_ptr(p) p + +/* Macros used for smc91x.c driver */ +#define readsb(p, d, l) insb(p, d, l) +#define readsw(p, d, l) insw(p, d, l) +#define readsl(p, d, l) insl(p, d, l) +#define writesb(p, d, l) outsb(p, d, l) +#define writesw(p, d, l) outsw(p, d, l) +#define writesl(p, d, l) outsl(p, d, l) + +#endif /* _ASM_NIOS2_IO_H */ diff --git a/arch/nios2/include/asm/irq.h b/arch/nios2/include/asm/irq.h new file mode 100644 index 0000000000000..f7e94b9210d37 --- /dev/null +++ b/arch/nios2/include/asm/irq.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2011 Tobias Klauser + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_IRQ_H +#define _ASM_NIOS2_IRQ_H + +#define NIOS2_CPU_NR_IRQS 32 +/* Reserve 32 additional interrupts for GPIO IRQs */ +#define NR_IRQS (NIOS2_CPU_NR_IRQS + 32) + +#ifndef NO_IRQ +#define NO_IRQ (-1) +#endif + +#include +#include + +#endif diff --git a/arch/nios2/include/asm/irqflags.h b/arch/nios2/include/asm/irqflags.h new file mode 100644 index 0000000000000..e91f4f0b7c694 --- /dev/null +++ b/arch/nios2/include/asm/irqflags.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010 Thomas Chou + * + * 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 . + * + */ +#ifndef _ASM_IRQFLAGS_H +#define _ASM_IRQFLAGS_H + +#include + +static inline unsigned long arch_local_save_flags(void) +{ + return RDCTL(CTL_STATUS); +} + +/* + * This will restore ALL status register flags, not only the interrupt + * mask flag. + */ +static inline void arch_local_irq_restore(unsigned long flags) +{ + WRCTL(CTL_STATUS, flags); +} + +static inline void arch_local_irq_disable(void) +{ + unsigned long flags; + flags = arch_local_save_flags(); + arch_local_irq_restore(flags & ~STATUS_PIE); +} + +static inline void arch_local_irq_enable(void) +{ + unsigned long flags; + flags = arch_local_save_flags(); + arch_local_irq_restore(flags | STATUS_PIE); +} + +static inline int arch_irqs_disabled_flags(unsigned long flags) +{ + return (flags & STATUS_PIE) == 0; +} + +static inline int arch_irqs_disabled(void) +{ + return arch_irqs_disabled_flags(arch_local_save_flags()); +} + +static inline unsigned long arch_local_irq_save(void) +{ + unsigned long flags; + flags = arch_local_save_flags(); + arch_local_irq_restore(flags & ~STATUS_PIE); + return flags; +} + +#endif /* _ASM_IRQFLAGS_H */ diff --git a/arch/nios2/include/asm/kgdb.h b/arch/nios2/include/asm/kgdb.h new file mode 100644 index 0000000000000..ec454c0885803 --- /dev/null +++ b/arch/nios2/include/asm/kgdb.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * + * Based on the code posted by Kazuyasu on the Altera Forum at: + * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_KGDB_H +#define _ASM_NIOS2_KGDB_H + +#define CACHE_FLUSH_IS_SAFE 1 +#define BUFMAX 2048 + +enum regnames { + GDB_R0 = 0, + GDB_AT, + GDB_R2, + GDB_R3, + GDB_R4, + GDB_R5, + GDB_R6, + GDB_R7, + GDB_R8, + GDB_R9, + GDB_R10, + GDB_R11, + GDB_R12, + GDB_R13, + GDB_R14, + GDB_R15, + GDB_R16, + GDB_R17, + GDB_R18, + GDB_R19, + GDB_R20, + GDB_R21, + GDB_R22, + GDB_R23, + GDB_ET, + GDB_BT, + GDB_GP, + GDB_SP, + GDB_FP, + GDB_EA, + GDB_BA, + GDB_RA, + GDB_PC, + GDB_STATUS, + GDB_ESTATUS, + GDB_BSTATUS, + GDB_IENABLE, + GDB_IPENDING, + GDB_CPUID, + GDB_CTL6, + GDB_EXCEPTION, + GDB_PTEADDR, + GDB_TLBACC, + GDB_TLBMISC, + GDB_CTL11, + GDB_BADADDR, + GDB_CONFIG, + /* do not change the last entry or anything below! */ + GDB_NUMREGBYTES /* number of registers */ +}; + +#define NUMREGBYTES (GDB_NUMREGBYTES * 4) + +#define BREAK_INSTR_SIZE 4 +static inline void arch_kgdb_breakpoint(void) +{ + /* + * we cannot use 'trap 30' here as the nios2 assembler is bugged and + * does consider any argument an error, so we just encode it directly. + */ + __asm__ __volatile__ (".word 0x003b6fba"); +} + +#endif /* _ASM_NIOS2_KGDB_H */ diff --git a/arch/nios2/include/asm/linkage.h b/arch/nios2/include/asm/linkage.h new file mode 100644 index 0000000000000..1519d60c036d2 --- /dev/null +++ b/arch/nios2/include/asm/linkage.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2009 Thomas Chou + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _ASM_NIOS2_LINKAGE_H +#define _ASM_NIOS2_LINKAGE_H + +/* This file is required by include/linux/linkage.h */ +#define __ALIGN .align 4 +#define __ALIGN_STR ".align 4" + +#endif diff --git a/arch/nios2/include/asm/mmu.h b/arch/nios2/include/asm/mmu.h new file mode 100644 index 0000000000000..d4b65173f2a98 --- /dev/null +++ b/arch/nios2/include/asm/mmu.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * Taken from m68knommu. + * + * 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. + */ + +#ifndef _ASM_NIOS2_MMU_H +#define _ASM_NIOS2_MMU_H + +#ifndef CONFIG_MMU +# include +#else + +/* Default "unsigned long" context */ +typedef unsigned long mm_context_t; + +#endif /* CONFIG_MMU */ +#endif /* _ASM_NIOS2_MMU_H */ diff --git a/arch/nios2/include/asm/mmu_context.h b/arch/nios2/include/asm/mmu_context.h new file mode 100644 index 0000000000000..3d239916612b6 --- /dev/null +++ b/arch/nios2/include/asm/mmu_context.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * + * 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 . + * + */ + +#ifdef CONFIG_MMU +# include "mmu_context_mm.h" +#else +# include +#endif diff --git a/arch/nios2/include/asm/mmu_context_mm.h b/arch/nios2/include/asm/mmu_context_mm.h new file mode 100644 index 0000000000000..294b4b1f81d4e --- /dev/null +++ b/arch/nios2/include/asm/mmu_context_mm.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 1996, 1997, 1998, 1999 by Ralf Baechle + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * based on MIPS asm/mmu_context.h + * + * 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. + */ + +#ifndef _ASM_NIOS2_MMU_CONTEXT_H +#define _ASM_NIOS2_MMU_CONTEXT_H + +#include + +extern void mmu_context_init(void); +extern unsigned long get_pid_from_context(mm_context_t *ctx); + +/* + * For the fast tlb miss handlers, we keep a pointer to the current pgd. + * processor. + */ +extern pgd_t *pgd_current; + +static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) +{ +} + +/* + * Initialize the context related info for a new mm_struct instance. + * + * Set all new contexts to 0, that way the generation will never match + * the currently running generation when this context is switched in. + */ +static inline int init_new_context(struct task_struct *tsk, + struct mm_struct *mm) +{ + mm->context = 0; + return 0; +} + +/* + * Destroy context related info for an mm_struct that is about + * to be put to rest. + */ +static inline void destroy_context(struct mm_struct *mm) +{ +} + +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk); + +static inline void deactivate_mm(struct task_struct *tsk, + struct mm_struct *mm) +{ +} + +/* + * After we have set current->mm to a new value, this activates + * the context for the new mm so we see the new mappings. + */ +void activate_mm(struct mm_struct *prev, struct mm_struct *next); + +#endif /* _ASM_NIOS2_MMU_CONTEXT_H */ diff --git a/arch/nios2/include/asm/mutex.h b/arch/nios2/include/asm/mutex.h new file mode 100644 index 0000000000000..ff6101aa2c715 --- /dev/null +++ b/arch/nios2/include/asm/mutex.h @@ -0,0 +1 @@ +#include diff --git a/arch/nios2/include/asm/page.h b/arch/nios2/include/asm/page.h new file mode 100644 index 0000000000000..4339537e8db5a --- /dev/null +++ b/arch/nios2/include/asm/page.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * MMU support based on asm/page.h from mips which is: + * + * Copyright (C) 1994 - 1999, 2000, 03 Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * + * NOMMU support based on asm/page.h from m68knommu. + * + * 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. + */ + +#ifndef _ASM_NIOS2_PAGE_H +#define _ASM_NIOS2_PAGE_H + +#include + +/* + * PAGE_SHIFT determines the page size + */ +#define PAGE_SHIFT 12 +#define PAGE_SIZE 4096 +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +/* + * PAGE_OFFSET -- the first address of the first page of memory. + */ +#define PAGE_OFFSET (CONFIG_MEM_BASE + CONFIG_KERNEL_REGION_BASE) + +#ifndef __ASSEMBLY__ + +/* + * This gives the physical RAM offset. + */ +#define PHYS_OFFSET CONFIG_MEM_BASE + +/* + * It's normally defined only for FLATMEM config but it's + * used in our early mem init code for all memory models. + * So always define it. + */ +#define ARCH_PFN_OFFSET PFN_UP(PHYS_OFFSET) + +#ifndef CONFIG_MMU +# define get_user_page(vaddr) __get_free_page(GFP_KERNEL) +# define free_user_page(page, addr) free_page(addr) +#endif /* CONFIG_MMU */ + +#define clear_page(page) memset((page), 0, PAGE_SIZE) +#define copy_page(to, from) memcpy((to), (from), PAGE_SIZE) + +#ifdef CONFIG_MMU +struct page; + +extern void clear_user_page(void *addr, unsigned long vaddr, struct page *page); +extern void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, + struct page *to); +#else +# define clear_user_page(page, vaddr, pg) clear_page(page) +# define copy_user_page(to, from, vaddr, pg) copy_page(to, from) +#endif /* CONFIG_MMU */ + +extern unsigned long shm_align_mask; + +/* + * These are used to make use of C type-checking. + */ +typedef struct page *pgtable_t; +typedef struct { unsigned long pte; } pte_t; +typedef struct { unsigned long pgd; } pgd_t; +typedef struct { unsigned long pgprot; } pgprot_t; + +#define pte_val(x) ((x).pte) +#define pgd_val(x) ((x).pgd) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) }) +#define __pgd(x) ((pgd_t) { (x) }) +#define __pgprot(x) ((pgprot_t) { (x) }) + +#ifndef CONFIG_MMU +typedef struct { unsigned long pmd[16]; } pmd_t; + +# define pmd_val(x) ((&x)->pmd[0]) +# define __pmd(x) ((pmd_t) { (x) }) +#endif /* CONFIG_MMU */ + +extern unsigned long memory_start; +extern unsigned long memory_end; +extern unsigned long memory_size; + +extern struct page *mem_map; + +#endif /* !__ASSEMBLY__ */ + +#ifdef CONFIG_MMU +# define __pa(x) \ + ((unsigned long)(x) - PAGE_OFFSET + PHYS_OFFSET) +# define __va(x) \ + ((void *)((unsigned long)(x) + PAGE_OFFSET - PHYS_OFFSET)) +#else +# define __pa(x) ((unsigned long)(x)) +# define __va(x) ((void *)(x)) +#endif /* CONFIG_MMU */ + +#define page_to_virt(page) \ + ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) + +#ifdef CONFIG_MMU +# define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) +# define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && \ + (pfn) < (max_mapnr + ARCH_PFN_OFFSET)) + +# define virt_to_page(vaddr) pfn_to_page(PFN_DOWN(virt_to_phys(vaddr))) +# define virt_addr_valid(vaddr) pfn_valid(PFN_DOWN(virt_to_phys(vaddr))) +#else /* CONFIG_MMU */ +# define pfn_valid(pfn) ((pfn) < max_mapnr) + +# define virt_to_page(vaddr) \ + ((void *) vaddr < (void *) memory_end ? mem_map + \ + (((unsigned long)(vaddr) - PAGE_OFFSET) >> PAGE_SHIFT) : 0UL) + +# define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) +# define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT) + +# define pfn_to_page(pfn) virt_to_page(pfn_to_virt(pfn)) +# define page_to_pfn(page) virt_to_pfn(page_to_virt(page)) + +# define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ + ((void *)(kaddr) < (void *)memory_end)) +#endif /* CONFIG_MMU */ + +#ifdef CONFIG_MMU +# define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + +# define UNCAC_ADDR(addr) \ + ((void *)((unsigned)(addr) | CONFIG_IO_REGION_BASE)) +# define CAC_ADDR(addr) \ + ((void *)(((unsigned)(addr) & ~CONFIG_IO_REGION_BASE) | \ + CONFIG_KERNEL_REGION_BASE)) + +#include + +#endif /* CONFIG_MMU */ + +#include + +#endif /* _ASM_NIOS2_PAGE_H */ diff --git a/arch/nios2/include/asm/pci.h b/arch/nios2/include/asm/pci.h new file mode 100644 index 0000000000000..69c86faffe17d --- /dev/null +++ b/arch/nios2/include/asm/pci.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013 Altera Corporation + * + * 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 . + * + */ + + +#ifndef __ASM_NIOS2_PCI_H__ +#define __ASM_NIOS2_PCI_H__ + +/* We don't support PCI yet, but some drivers require this file anyway */ + +#endif /* __ASM_NIOS2_PCI_H__ */ diff --git a/arch/nios2/include/asm/pgalloc.h b/arch/nios2/include/asm/pgalloc.h new file mode 100644 index 0000000000000..9f912bfb5d5eb --- /dev/null +++ b/arch/nios2/include/asm/pgalloc.h @@ -0,0 +1,89 @@ +/* + * 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. + * + * Copyright (C) 1994 - 2001, 2003 by Ralf Baechle + * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc. + */ + +#ifndef _ASM_NIOS2_PGALLOC_H +#define _ASM_NIOS2_PGALLOC_H + +#ifndef CONFIG_MMU +# include +#else + +#include + +static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, + pte_t *pte) +{ + set_pmd(pmd, __pmd((unsigned long)pte)); +} + +static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, + pgtable_t pte) +{ + set_pmd(pmd, __pmd((unsigned long)page_address(pte))); +} +#define pmd_pgtable(pmd) pmd_page(pmd) + +/* + * Initialize a new pmd table with invalid pointers. + */ +extern void pmd_init(unsigned long page, unsigned long pagetable); + +extern pgd_t *pgd_alloc(struct mm_struct *mm); + +static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) +{ + free_pages((unsigned long)pgd, PGD_ORDER); +} + +static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, + unsigned long address) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, + PTE_ORDER); + + return pte; +} + +static inline pgtable_t pte_alloc_one(struct mm_struct *mm, + unsigned long address) +{ + struct page *pte; + + pte = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER); + if (pte) { + clear_highpage(pte); + pgtable_page_ctor(pte); + } + return pte; +} + +static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) +{ + free_pages((unsigned long)pte, PTE_ORDER); +} + +static inline void pte_free(struct mm_struct *mm, struct page *pte) +{ + pgtable_page_dtor(pte); + __free_pages(pte, PTE_ORDER); +} + +#define __pte_free_tlb(tlb, pte, addr) \ + do { \ + pgtable_page_dtor(pte); \ + tlb_remove_page((tlb), (pte)); \ + } while (0) + +#endif /* CONFIG_MMU */ + +#define check_pgt_cache() do { } while (0) + +#endif /* _ASM_NIOS2_PGALLOC_H */ diff --git a/arch/nios2/include/asm/pgtable-bits.h b/arch/nios2/include/asm/pgtable-bits.h new file mode 100644 index 0000000000000..ce9e7069aa969 --- /dev/null +++ b/arch/nios2/include/asm/pgtable-bits.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * + * 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. + */ + +#ifndef _ASM_NIOS2_PGTABLE_BITS_H +#define _ASM_NIOS2_PGTABLE_BITS_H + +/* + * These are actual hardware defined protection bits in the tlbacc register + * which looks like this: + * + * 31 30 ... 26 25 24 23 22 21 20 19 18 ... 1 0 + * ignored........ C R W X G PFN............ + */ +#define _PAGE_GLOBAL (1<<20) +#define _PAGE_EXEC (1<<21) +#define _PAGE_WRITE (1<<22) +#define _PAGE_READ (1<<23) +#define _PAGE_CACHED (1<<24) /* C: data access cacheable */ + +/* + * Software defined bits. They are ignored by the hardware and always read back + * as zero, but can be written as non-zero. + */ +#define _PAGE_PRESENT (1<<25) /* PTE contains a translation */ +#define _PAGE_ACCESSED (1<<26) /* page referenced */ +#define _PAGE_DIRTY (1<<27) /* dirty page */ +#define _PAGE_FILE (1<<28) /* PTE used for file mapping or swap */ + +#endif /* _ASM_NIOS2_PGTABLE_BITS_H */ diff --git a/arch/nios2/include/asm/pgtable.h b/arch/nios2/include/asm/pgtable.h new file mode 100644 index 0000000000000..9a0ad9ff771d3 --- /dev/null +++ b/arch/nios2/include/asm/pgtable.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * + * 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 . + * + */ + +#ifdef CONFIG_MMU +# include +#else +# include +#endif diff --git a/arch/nios2/include/asm/pgtable_mm.h b/arch/nios2/include/asm/pgtable_mm.h new file mode 100644 index 0000000000000..9d68c46881a12 --- /dev/null +++ b/arch/nios2/include/asm/pgtable_mm.h @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * + * Based on asm/pgtable-32.h from mips which is: + * + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 2003 Ralf Baechle + * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc. + * + * 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. + */ + +#ifndef _ASM_NIOS2_PGTABLE_H +#define _ASM_NIOS2_PGTABLE_H + +#include +#include +#include +#include +#include + +#include +#include + +#define FIRST_USER_ADDRESS 0 + +#define VMALLOC_START CONFIG_KERNEL_MMU_REGION_BASE +#define VMALLOC_END (CONFIG_KERNEL_REGION_BASE - 1) + +struct mm_struct; + +/* Helper macro */ +#define MKP(x, w, r) __pgprot(_PAGE_PRESENT | _PAGE_CACHED | \ + ((x) ? _PAGE_EXEC : 0) | \ + ((r) ? _PAGE_READ : 0) | \ + ((w) ? _PAGE_WRITE : 0)) +/* + * These are the macros that generic kernel code needs + * (to populate protection_map[]) + */ + +/* Remove W bit on private pages for COW support */ +#define __P000 MKP(0, 0, 0) +#define __P001 MKP(0, 0, 1) +#define __P010 MKP(0, 0, 0) /* COW */ +#define __P011 MKP(0, 0, 1) /* COW */ +#define __P100 MKP(1, 0, 0) +#define __P101 MKP(1, 0, 1) +#define __P110 MKP(1, 0, 0) /* COW */ +#define __P111 MKP(1, 0, 1) /* COW */ + +/* Shared pages can have exact HW mapping */ +#define __S000 MKP(0, 0, 0) +#define __S001 MKP(0, 0, 1) +#define __S010 MKP(0, 1, 0) +#define __S011 MKP(0, 1, 1) +#define __S100 MKP(1, 0, 0) +#define __S101 MKP(1, 0, 1) +#define __S110 MKP(1, 1, 0) +#define __S111 MKP(1, 1, 1) + +/* Used all over the kernel */ +#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_CACHED | _PAGE_READ | \ + _PAGE_WRITE | _PAGE_EXEC | _PAGE_GLOBAL) + +#define PAGE_COPY MKP(0, 0, 1) + +#define PGD_ORDER 0 +#define PTE_ORDER 0 + +#define PTRS_PER_PGD ((PAGE_SIZE << PGD_ORDER) / sizeof(pgd_t)) +#define PTRS_PER_PTE ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t)) + +#define USER_PTRS_PER_PGD (CONFIG_KERNEL_MMU_REGION_BASE / PGDIR_SIZE) + +#define PGDIR_SHIFT 22 +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; +extern pte_t invalid_pte_table[PAGE_SIZE/sizeof(pte_t)]; + +/* + * (pmds are folded into puds so this doesn't get actually called, + * but the define is needed for a generic inline function.) + */ +static inline void set_pmd(pmd_t *pmdptr, pmd_t pmdval) +{ + pmdptr->pud.pgd.pgd = pmdval.pud.pgd.pgd; +} + +/* to find an entry in a page-table-directory */ +#define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) +#define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr)) + +static inline int pte_write(pte_t pte) \ + { return pte_val(pte) & _PAGE_WRITE; } +static inline int pte_dirty(pte_t pte) \ + { return pte_val(pte) & _PAGE_DIRTY; } +static inline int pte_young(pte_t pte) \ + { return pte_val(pte) & _PAGE_ACCESSED; } +static inline int pte_file(pte_t pte) \ + { return pte_val(pte) & _PAGE_FILE; } +static inline int pte_special(pte_t pte) { return 0; } + +#define pgprot_noncached pgprot_noncached + +static inline pgprot_t pgprot_noncached(pgprot_t _prot) +{ + unsigned long prot = pgprot_val(_prot); + + prot &= ~_PAGE_CACHED; + + return __pgprot(prot); +} + +/* + * FIXME: Today unmapped pages are mapped to the low physical addresses + * and not 0 (to avoid to trigger the false alias detection in the iss) + * Also check pte_clear. + */ +static inline int pte_none(pte_t pte) +{ + return !(pte_val(pte) & ~(_PAGE_GLOBAL|0xf)); +} + +static inline int pte_present(pte_t pte) \ + { return pte_val(pte) & _PAGE_PRESENT; } + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +static inline pte_t pte_wrprotect(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_WRITE; + return pte; +} + +static inline pte_t pte_mkclean(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_DIRTY; + return pte; +} + +static inline pte_t pte_mkold(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_ACCESSED; + return pte; +} + +static inline pte_t pte_mkwrite(pte_t pte) +{ + pte_val(pte) |= _PAGE_WRITE; + return pte; +} + +static inline pte_t pte_mkdirty(pte_t pte) +{ + pte_val(pte) |= _PAGE_DIRTY; + return pte; +} + +static inline pte_t pte_mkspecial(pte_t pte) { return pte; } + +static inline pte_t pte_mkyoung(pte_t pte) +{ + pte_val(pte) |= _PAGE_ACCESSED; + return pte; +} + +static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ + const unsigned long mask = _PAGE_READ | _PAGE_WRITE | _PAGE_EXEC; + pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask); + return pte; +} + +static inline int pmd_present(pmd_t pmd) +{ + return (pmd_val(pmd) != (unsigned long) invalid_pte_table) + && (pmd_val(pmd) != 0UL); +} + +static inline void pmd_clear(pmd_t *pmdp) +{ + pmd_val(*pmdp) = (unsigned long) invalid_pte_table; +} + +#define pte_pfn(pte) (pte_val(pte) & 0xfffff) +#define pfn_pte(pfn, prot) (__pte(pfn | pgprot_val(prot))) +#define pte_page(pte) (pfn_to_page(pte_pfn(pte))) + +/* + * Store a linux PTE into the linux page table. + */ +static inline void set_pte(pte_t *ptep, pte_t pteval) +{ + *ptep = pteval; +} + +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval) +{ + unsigned long paddr = page_to_virt(pte_page(pteval)); + flush_dcache_range(paddr, paddr + PAGE_SIZE); + set_pte(ptep, pteval); +} + +static inline int pmd_none(pmd_t pmd) +{ + return (pmd_val(pmd) == + (unsigned long) invalid_pte_table) || (pmd_val(pmd) == 0UL); +} + +#define pmd_bad(pmd) (pmd_val(pmd) & ~PAGE_MASK) + +static inline void pte_clear(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + pte_t null; + + pte_val(null) = (addr >> PAGE_SHIFT) & 0xf; + + set_pte_at(mm, addr, ptep, null); + flush_tlb_one(addr); +} + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ +#define mk_pte(page, prot) (pfn_pte(page_to_pfn(page), prot)) + +#define pte_unmap(pte) do { } while (0) + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ +#define pmd_phys(pmd) virt_to_phys((void *)pmd_val(pmd)) +#define pmd_page(pmd) (pfn_to_page(pmd_phys(pmd) >> PAGE_SHIFT)) +#define pmd_page_vaddr(pmd) pmd_val(pmd) + +#define pte_offset_map(dir, addr) \ + ((pte_t *) page_address(pmd_page(*dir)) + \ + (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) + +/* to find an entry in a kernel page-table-directory */ +#define pgd_offset_k(addr) pgd_offset(&init_mm, addr) + +/* Get the address to the PTE for a vaddr in specfic directory */ +#define pte_offset_kernel(dir, addr) \ + ((pte_t *) pmd_page_vaddr(*(dir)) + \ + (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) + +#define pte_ERROR(e) \ + pr_err("%s:%d: bad pte %08lx.\n", \ + __FILE__, __LINE__, pte_val(e)) +#define pgd_ERROR(e) \ + pr_err("%s:%d: bad pgd %08lx.\n", \ + __FILE__, __LINE__, pgd_val(e)) + +/* + * Encode and decode a swap entry (must be !pte_none(pte) && !pte_present(pte) + * && !pte_file(pte)): + * + * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 ... 1 0 + * 0 0 0 0 type. 0 0 0 0 0 0 offset......... + * + * This gives us up to 2**2 = 4 swap files and 2**20 * 4K = 4G per swap file. + * + * Note that the offset field is always non-zero, thus !pte_none(pte) is always + * true. + */ +#define __swp_type(swp) (((swp).val >> 26) & 0x3) +#define __swp_offset(swp) ((swp).val & 0xfffff) +#define __swp_entry(type, off) ((swp_entry_t) { (((type) & 0x3) << 26) \ + | ((off) & 0xfffff) }) +#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) + +/* Encode and decode a nonlinear file mapping entry */ +#define PTE_FILE_MAX_BITS 25 +#define pte_to_pgoff(pte) (pte_val(pte) & 0x1ffffff) +#define pgoff_to_pte(off) __pte(((off) & 0x1ffffff) | _PAGE_FILE) + +#define kern_addr_valid(addr) (1) + +#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ + remap_pfn_range(vma, vaddr, pfn, size, prot) + +#include + +#define pgtable_cache_init() do { } while (0) + +extern void __init paging_init(void); +extern void __init mmu_init(void); + +extern void update_mmu_cache(struct vm_area_struct *vma, + unsigned long address, pte_t *pte); + +#endif /* _ASM_NIOS2_PGTABLE_H */ diff --git a/arch/nios2/include/asm/pgtable_no.h b/arch/nios2/include/asm/pgtable_no.h new file mode 100644 index 0000000000000..ec624cc799088 --- /dev/null +++ b/arch/nios2/include/asm/pgtable_no.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * Based on asm/pgtable_no.h from m68k which is: + * + * Copyright (C) 2000-2002 Greg Ungerer + * + * 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. + */ + +#ifndef _ASM_NIOS2_PGTABLE_NO_H +#define _ASM_NIOS2_PGTABLE_NO_H + +#include + +#include + +typedef pte_t *pte_addr_t; + +#define pgd_present(pgd) (1) /* pages are always present on NO_MM */ +#define pgd_none(pgd) (0) +#define pgd_bad(pgd) (0) +#define pgd_clear(pgdp) +#define kern_addr_valid(addr) (1) +#define pmd_offset(a, b) ((void *)0) + +#define PAGE_NONE __pgprot(0) /* these mean nothing to NO_MM */ +#define PAGE_SHARED __pgprot(0) /* these mean nothing to NO_MM */ +#define PAGE_COPY __pgprot(0) /* these mean nothing to NO_MM */ +#define PAGE_READONLY __pgprot(0) /* these mean nothing to NO_MM */ +#define PAGE_KERNEL __pgprot(0) /* these mean nothing to NO_MM */ + +extern void paging_init(void); +#define swapper_pg_dir ((pgd_t *) 0) + +#define __swp_type(x) (0) +#define __swp_offset(x) (0) +#define __swp_entry(typ, off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) + +static inline int pte_file(pte_t pte) { return 0; } + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +#define ZERO_PAGE(vaddr) (virt_to_page(0)) + +/* + * No page table caches to initialise + */ +#define pgtable_cache_init() do { } while (0) + +#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ + remap_pfn_range(vma, vaddr, pfn, size, prot) + +/* + * All 32bit addresses are effectively valid for vmalloc... + * Sort of meaningless for non-VM targets. + */ +#define VMALLOC_START 0 +#define VMALLOC_END 0xffffffff + +#define arch_enter_lazy_mmu_mode() do {} while (0) +#define arch_leave_lazy_mmu_mode() do {} while (0) +#define arch_flush_lazy_mmu_mode() do {} while (0) +#define arch_enter_lazy_cpu_mode() do {} while (0) +#define arch_leave_lazy_cpu_mode() do {} while (0) +#define arch_flush_lazy_cpu_mode() do {} while (0) + +#include + +/* We provide a special get_unmapped_area for framebuffer mmaps of nommu */ +extern unsigned long get_fb_unmapped_area(struct file *filp, unsigned long, + unsigned long, unsigned long, + unsigned long); +#define HAVE_ARCH_FB_UNMAPPED_AREA + +#endif /* _ASM_NIOS2_PGTABLE_NO_H */ diff --git a/arch/nios2/include/asm/processor.h b/arch/nios2/include/asm/processor.h new file mode 100644 index 0000000000000..ecc7dc9a5a421 --- /dev/null +++ b/arch/nios2/include/asm/processor.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * Copyright (C) 2001 Ken Hill (khill@microtronix.com) + * Vic Phillips (vic@microtronix.com) + * + * based on SPARC asm/processor_32.h which is: + * + * Copyright (C) 1994 David S. Miller + * + * 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. + */ + +#ifndef _ASM_NIOS2_PROCESSOR_H +#define _ASM_NIOS2_PROCESSOR_H + +#include +#include + +#define NIOS2_FLAG_KTHREAD 0x00000001 /* task is a kernel thread */ + +#define NIOS2_OP_NOP 0x1883a +#define NIOS2_OP_BREAK 0x3da03a + +#ifdef CONFIG_MMU +#ifdef __KERNEL__ + +#define STACK_TOP TASK_SIZE +#define STACK_TOP_MAX STACK_TOP + +#endif /* __KERNEL__ */ +#endif /* CONFIG_MMU */ + +#ifndef __ASSEMBLY__ + +/* + * Default implementation of macro that returns current + * instruction pointer ("program counter"). + */ +#define current_text_addr() ({ __label__ _l; _l: &&_l; }) + +#ifdef CONFIG_MMU +# define TASK_SIZE 0x7FFF0000UL +# define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) +#else +/* + * User space process size: 1st byte beyond user address space. + * Fairly meaningless on nommu. Parts of user programs can be scattered + * in a lot of places, so just disable this by setting it to 0xFFFFFFFF. + */ +# define TASK_SIZE 0xFFFFFFFFUL +/* + * This decides where the kernel will search for a free chunk of vm + * space during mmap's. We won't be using it. + */ +# define TASK_UNMAPPED_BASE 0 +#endif /* CONFIG_MMU */ + +/* The Nios processor specific thread struct. */ +struct thread_struct { + struct pt_regs *kregs; + + /* Context switch saved kernel state. */ + unsigned long ksp; + unsigned long kpsr; +#ifndef CONFIG_MMU + unsigned long kesr; +#endif +}; + +#define INIT_MMAP \ + { &init_mm, (0), (0), __pgprot(0x0), VM_READ | VM_WRITE | VM_EXEC } + +#ifdef CONFIG_MMU +# define INIT_THREAD { \ + .kregs = NULL, \ + .ksp = 0, \ + .kpsr = 0, \ +} +#else +# define INIT_THREAD { \ + .kregs = NULL, \ + .ksp = sizeof(init_stack) + (unsigned long) init_stack, \ + .kpsr = 0, \ +} +#endif /* CONFIG_MMU */ + +extern void start_thread(struct pt_regs *regs, unsigned long pc, + unsigned long sp); + +struct task_struct; + +/* Free all resources held by a thread. */ +static inline void release_thread(struct task_struct *dead_task) +{ +} + +/* Free current thread data structures etc.. */ +static inline void exit_thread(void) +{ +} + +/* Return saved PC of a blocked thread. */ +#define thread_saved_pc(tsk) ((tsk)->thread.kregs->ea) + +extern unsigned long get_wchan(struct task_struct *p); + +/* Prepare to copy thread state - unlazy all lazy status */ +#define prepare_to_copy(tsk) do { } while (0) + +extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); + +#define task_pt_regs(p) \ + ((struct pt_regs *)(THREAD_SIZE + task_stack_page(p)) - 1) + +/* Used by procfs */ +#define KSTK_EIP(tsk) ((tsk)->thread.kregs->ea) +#define KSTK_ESP(tsk) ((tsk)->thread.kregs->sp) + +#define cpu_relax() barrier() + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_NIOS2_PROCESSOR_H */ diff --git a/arch/nios2/include/asm/prom.h b/arch/nios2/include/asm/prom.h new file mode 100644 index 0000000000000..e1451bccb3ee0 --- /dev/null +++ b/arch/nios2/include/asm/prom.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 Thomas Chou + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_PROM_H +#define _ASM_NIOS2_PROM_H + +extern int __dtb_start; + +extern unsigned long early_altera_uart_or_juart_console(void); + +extern void device_tree_init(void); + +#endif /* _ASM_NIOS2_PROM_H */ diff --git a/arch/nios2/include/asm/ptrace.h b/arch/nios2/include/asm/ptrace.h new file mode 100644 index 0000000000000..50498e9a816d5 --- /dev/null +++ b/arch/nios2/include/asm/ptrace.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * based on m68k asm/processor.h + * + * 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. + */ + +#ifndef _ASM_NIOS2_PTRACE_H +#define _ASM_NIOS2_PTRACE_H + +#include + +#if defined __KERNEL__ && !defined CONFIG_MMU +# define PS_S 0x00000001 +#endif + +#ifndef __ASSEMBLY__ + +/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 + +/* + * Supervisor mode + */ + +#ifdef CONFIG_MMU +# define user_mode(regs) (((regs)->estatus & ESTATUS_EU)) +#else +# define user_mode(regs) (!((regs)->status_extension & PS_S)) +#endif /* CONFIG_MMU */ + +#define instruction_pointer(regs) ((regs)->ra) +#define profile_pc(regs) instruction_pointer(regs) +extern void show_regs(struct pt_regs *); + +#endif /* __ASSEMBLY__ */ +#endif /* _ASM_NIOS2_PTRACE_H */ diff --git a/arch/nios2/include/asm/registers.h b/arch/nios2/include/asm/registers.h new file mode 100644 index 0000000000000..99d73bdc966f8 --- /dev/null +++ b/arch/nios2/include/asm/registers.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_REGISTERS_H +#define _ASM_NIOS2_REGISTERS_H + +/* control register numbers */ +#define CTL_STATUS 0 +#define CTL_ESTATUS 1 +#define CTL_BSTATUS 2 +#define CTL_IENABLE 3 +#define CTL_IPENDING 4 +#define CTL_CPUID 5 +#define CTL_RSV1 6 +#define CTL_EXCEPTION 7 +#define CTL_PTEADDR 8 +#define CTL_TLBACC 9 +#define CTL_TLBMISC 10 +#define CTL_RSV2 11 +#define CTL_BADADDR 12 +#define CTL_CONFIG 13 +#define CTL_MPUBASE 14 +#define CTL_MPUACC 15 + +/* access control registers using GCC builtins */ +#define RDCTL(r) __builtin_rdctl(r) +#define WRCTL(r, v) __builtin_wrctl(r, v) + +/* status register bits */ +#define STATUS_PIE (1 << 0) /* processor interrupt enable */ +#define STATUS_U (1 << 1) /* user mode */ +#define STATUS_EH (1 << 2) /* Exception mode */ + +/* estatus register bits */ +#define ESTATUS_EPIE (1 << 0) /* processor interrupt enable */ +#define ESTATUS_EU (1 << 1) /* user mode */ +#define ESTATUS_EH (1 << 2) /* Exception mode */ + +#ifdef CONFIG_MMU + +/* tlbmisc register bits */ +#define TLBMISC_WE (1 << 18) /* TLB write enable */ +#define TLBMISC_RD (1 << 19) /* TLB read */ + +#endif /* CONFIG_MMU */ + +#endif /* _ASM_NIOS2_REGISTERS_H */ diff --git a/arch/nios2/include/asm/setup.h b/arch/nios2/include/asm/setup.h new file mode 100644 index 0000000000000..2c571d998118f --- /dev/null +++ b/arch/nios2/include/asm/setup.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_SETUP_H +#define _ASM_NIOS2_SETUP_H + +#include + +#ifndef __ASSEMBLY__ +# ifdef __KERNEL__ + +extern char cmd_line[COMMAND_LINE_SIZE]; + +extern char exception_handler_hook[]; +extern char fast_handler[]; +extern char fast_handler_end[]; + +extern void pagetable_init(void); + +extern void setup_early_printk(void); + +# endif/* __KERNEL__ */ +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_NIOS2_SETUP_H */ diff --git a/arch/nios2/include/asm/signal.h b/arch/nios2/include/asm/signal.h new file mode 100644 index 0000000000000..e0c9af0d286a7 --- /dev/null +++ b/arch/nios2/include/asm/signal.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004, Microtronix Datacom Ltd. + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _ASM_NIOS2_SIGNAL_H +#define _ASM_NIOS2_SIGNAL_H + +#include + +/* Most things should be clean enough to redefine this at will, if care + is taken to make libc match. */ + +#define _NSIG 64 +#define _NSIG_BPW 32 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef unsigned long old_sigset_t; /* at least 32 bits */ + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#include + +struct old_sigaction { + __sighandler_t sa_handler; + old_sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; /* mask last for extensibility */ +}; + +struct k_sigaction { + struct sigaction sa; +}; + +#include +#undef __HAVE_ARCH_SIG_BITOPS + +#define ptrace_signal_deliver(regs, cookie) do { } while (0) + +#endif /* _ASM_NIOS2_SIGNAL_H */ diff --git a/arch/nios2/include/asm/string.h b/arch/nios2/include/asm/string.h new file mode 100644 index 0000000000000..14dd570d64f70 --- /dev/null +++ b/arch/nios2/include/asm/string.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + */ + +#ifndef _ASM_NIOS2_STRING_H +#define _ASM_NIOS2_STRING_H + +#ifdef __KERNEL__ + +#define __HAVE_ARCH_MEMSET +#define __HAVE_ARCH_MEMCPY +#define __HAVE_ARCH_MEMMOVE + +extern void *memset(void *s, int c, size_t count); +extern void *memcpy(void *d, const void *s, size_t count); +extern void *memmove(void *d, const void *s, size_t count); + +#endif /* __KERNEL__ */ + +#endif /* _ASM_NIOS2_STRING_H */ diff --git a/arch/nios2/include/asm/switch_to.h b/arch/nios2/include/asm/switch_to.h new file mode 100644 index 0000000000000..c47b3f4afbcdd --- /dev/null +++ b/arch/nios2/include/asm/switch_to.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ +#ifndef _ASM_NIOS2_SWITCH_TO_H +#define _ASM_NIOS2_SWITCH_TO_H + +/* + * switch_to(n) should switch tasks to task ptr, first checking that + * ptr isn't the current task, in which case it does nothing. This + * also clears the TS-flag if the task we switched to has used the + * math co-processor latest. + */ +#define switch_to(prev, next, last) \ +{ \ + void *_last; \ + __asm__ __volatile__ ( \ + "mov r4, %1\n" \ + "mov r5, %2\n" \ + "call resume\n" \ + "mov %0,r4\n" \ + : "=r" (_last) \ + : "r" (prev), "r" (next) \ + : "r4", "r5", "r7", "r8", "ra"); \ + (last) = _last; \ +} + +#endif /* _ASM_NIOS2_SWITCH_TO_H */ diff --git a/arch/nios2/include/asm/thread_info.h b/arch/nios2/include/asm/thread_info.h new file mode 100644 index 0000000000000..da57228e0f781 --- /dev/null +++ b/arch/nios2/include/asm/thread_info.h @@ -0,0 +1,129 @@ +/* + * NiosII low-level thread information + * + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * Based on asm/thread_info_no.h from m68k which is: + * + * Copyright (C) 2002 David Howells + * + * 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. + */ + +#ifndef _ASM_NIOS2_THREAD_INFO_H +#define _ASM_NIOS2_THREAD_INFO_H + +#ifdef __KERNEL__ + +/* + * Size of the kernel stack for each process. + */ +#define THREAD_SIZE_ORDER 1 +#define THREAD_SIZE 8192 /* 2 * PAGE_SIZE */ + +#ifndef __ASSEMBLY__ + +typedef struct { + unsigned long seg; +} mm_segment_t; + +/* + * low level task data that entry.S needs immediate access to + * - this struct should fit entirely inside of one cache line + * - this struct shares the supervisor stack pages + * - if the contents of this structure are changed, the assembly constants + * must also be changed + */ +struct thread_info { + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + unsigned long flags; /* low level flags */ + __u32 cpu; /* current CPU */ + int preempt_count; /* 0 => preemptable,<0 => BUG */ + mm_segment_t addr_limit; /* thread address space: + 0-0x7FFFFFFF for user-thead + 0-0xFFFFFFFF for kernel-thread + */ + struct restart_block restart_block; +#ifdef CONFIG_MMU + struct pt_regs *regs; +#endif +}; + +/* + * macros/functions for gaining access to the thread information structure + * + * preempt_count needs to be 1 initially, until the scheduler is functional. + */ +#define INIT_THREAD_INFO(tsk) \ +{ \ + .task = &tsk, \ + .exec_domain = &default_exec_domain, \ + .flags = 0, \ + .cpu = 0, \ + .preempt_count = INIT_PREEMPT_COUNT, \ + .addr_limit = KERNEL_DS, \ + .restart_block = { \ + .fn = do_no_restart_syscall, \ + }, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* how to get the thread information struct from C */ +static inline struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + __asm__ __volatile__( + "mov %0, sp\n" + "and %0, %0, %1\n" + : "=&r" (ti) + : "r" (~(THREAD_SIZE-1)) + ); + return ti; +} +#endif /* !__ASSEMBLY__ */ + +#define PREEMPT_ACTIVE 0x10000000 + +/* + * thread information flags + * - these are process state flags that various assembly files may need to + * access + * - pending work-to-be-done flags are in LSW + * - other flags in MSW + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_MEMDIE 4 /* is terminating due to OOM killer */ +#define TIF_SECCOMP 5 /* secure computing */ +#define TIF_SYSCALL_AUDIT 6 /* syscall auditing active */ +#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */ + +#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling + TIF_NEED_RESCHED */ + +#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) +#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) +#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) +#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) +#define _TIF_SECCOMP (1 << TIF_SECCOMP) +#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) +#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) +#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) + +/* work to do on interrupt/exception return */ +#define _TIF_WORK_MASK 0x0000FFFE + +/* work to do on any return to u-space */ +# define _TIF_ALLWORK_MASK 0x0000FFFF + +#endif /* __KERNEL__ */ + +#endif /* _ASM_NIOS2_THREAD_INFO_H */ diff --git a/arch/nios2/include/asm/timex.h b/arch/nios2/include/asm/timex.h new file mode 100644 index 0000000000000..524b47d79d3da --- /dev/null +++ b/arch/nios2/include/asm/timex.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 Thomas Chou + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_TIMEX_H +#define _ASM_NIOS2_TIMEX_H + +/* Supply dummy tick-rate. Real value will be read from devicetree */ +#define CLOCK_TICK_RATE (HZ * 100000UL) + +#include + +#endif diff --git a/arch/nios2/include/asm/tlb.h b/arch/nios2/include/asm/tlb.h new file mode 100644 index 0000000000000..6c3689af046dd --- /dev/null +++ b/arch/nios2/include/asm/tlb.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2009 Wind River Systems Inc + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ + +#ifndef _ASM_NIOS2_TLB_H +#define _ASM_NIOS2_TLB_H + +#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) + +#ifdef CONFIG_MMU + +extern void set_mmu_pid(unsigned long pid); + +/* + * NiosII doesn't need any special per-pte or per-vma handling, except + * we need to flush cache for the area to be unmapped. + */ +#define tlb_start_vma(tlb, vma) \ + do { \ + if (!tlb->fullmm) \ + flush_cache_range(vma, vma->vm_start, vma->vm_end); \ + } while (0) + +#else + +#define tlb_start_vma(tlb, vma) do { } while (0) + +#endif /* CONFIG_MMU */ + +#define tlb_end_vma(tlb, vma) do { } while (0) +#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0) + +#include +#include + +#endif /* _ASM_NIOS2_TLB_H */ diff --git a/arch/nios2/include/asm/tlbflush.h b/arch/nios2/include/asm/tlbflush.h new file mode 100644 index 0000000000000..68d778ce55dcd --- /dev/null +++ b/arch/nios2/include/asm/tlbflush.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_TLBFLUSH_H +#define _ASM_NIOS2_TLBFLUSH_H + +#ifndef CONFIG_MMU +# include +#else + +struct mm_struct; + +/* + * TLB flushing: + * + * - flush_tlb_all() flushes all processes TLB entries + * - flush_tlb_mm(mm) flushes the specified mm context TLB entries + * - flush_tlb_page(vma, vmaddr) flushes one page + * - flush_tlb_range(vma, start, end) flushes a range of pages + * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages + */ +extern void flush_tlb_all(void); +extern void flush_tlb_mm(struct mm_struct *mm); +extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end); +extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); +extern void flush_tlb_one(unsigned long vaddr); + +static inline void flush_tlb_page(struct vm_area_struct *vma, + unsigned long addr) +{ + flush_tlb_one(addr); +} + +#endif /* CONFIG_MMU */ + +#endif /* _ASM_NIOS2_TLBFLUSH_H */ diff --git a/arch/nios2/include/asm/traps.h b/arch/nios2/include/asm/traps.h new file mode 100644 index 0000000000000..82a48473280d8 --- /dev/null +++ b/arch/nios2/include/asm/traps.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * 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. + */ + +#ifndef _ASM_NIOS2_TRAPS_H +#define _ASM_NIOS2_TRAPS_H + +#define TRAP_ID_SYSCALL 0 + +#ifndef __ASSEMBLY__ +void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr); +#endif + +#endif /* _ASM_NIOS2_TRAPS_H */ diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h new file mode 100644 index 0000000000000..23d7b01712dad --- /dev/null +++ b/arch/nios2/include/asm/uaccess.h @@ -0,0 +1,336 @@ +/* + * User space memory access functions for Nios II + * + * Copyright (C) 2010-2011, Tobias Klauser + * Copyright (C) 2009, Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * Based on asm/uaccess.h from m68knommu + * + * 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. + */ + +#ifndef _ASM_NIOS2_UACCESS_H +#define _ASM_NIOS2_UACCESS_H + +#include +#include +#include + +#include + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 + +/* + * The exception table consists of pairs of addresses: the first is the + * address of an instruction that is allowed to fault, and the second is + * the address at which the program should continue. No registers are + * modified, so it is entirely up to the continuation code to figure out + * what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ +struct exception_table_entry { + unsigned long insn; + unsigned long fixup; +}; + +#ifdef CONFIG_MMU +extern int fixup_exception(struct pt_regs *regs); +#endif + +/* + * Segment stuff + */ +#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) +#define USER_DS MAKE_MM_SEG(0x80000000UL) +#define KERNEL_DS MAKE_MM_SEG(0) + +#define get_ds() (KERNEL_DS) + +#define get_fs() (current_thread_info()->addr_limit) +#define set_fs(seg) (current_thread_info()->addr_limit = (seg)) + +#define segment_eq(a, b) ((a).seg == (b).seg) + +#ifdef CONFIG_MMU +#define __access_ok(addr, len) \ + (((signed long)(((long)get_fs().seg) & \ + ((long)(addr) | (((long)(addr)) + (len)) | (len)))) == 0) +#else +static inline int __access_ok(unsigned long addr, unsigned long size) +{ + addr &= ~CONFIG_IO_REGION_BASE; /* ignore 'uncached' bit */ + return ((addr >= CONFIG_MEM_BASE) && ((addr + size) <= memory_end)); +} +#endif /* CONFIG_MMU */ + +#define access_ok(type, addr, len) \ + likely(__access_ok((unsigned long)(addr), (unsigned long)(len))) + +#ifdef CONFIG_MMU +# define __EX_TABLE_SECTION ".section __ex_table,\"a\"\n" +#else +# define __EX_TABLE_SECTION ".section .discard,\"a\"\n" +#endif + +/* + * Zero Userspace + */ + +static inline unsigned long __must_check __clear_user(void __user *to, + unsigned long n) +{ + __asm__ __volatile__ ( + "1: stb zero, 0(%1)\n" + " addi %0, %0, -1\n" + " addi %1, %1, 1\n" + " bne %0, zero, 1b\n" + "2:\n" + __EX_TABLE_SECTION + ".word 1b, 2b\n" + ".previous\n" + : "=r" (n), "=r" (to) + : "0" (n), "1" (to) + ); + + return n; +} + +static inline unsigned long __must_check clear_user(void __user *to, + unsigned long n) +{ + if (!access_ok(VERIFY_WRITE, to, n)) + return n; + return __clear_user(to, n); +} + +#ifdef CONFIG_MMU +extern long __copy_from_user(void *to, const void __user *from, + unsigned long n); +extern long __copy_to_user(void __user *to, const void *from, unsigned long n); + +static inline long copy_from_user(void *to, const void __user *from, + unsigned long n) +{ + if (!access_ok(VERIFY_READ, from, n)) + return n; + return __copy_from_user(to, from, n); +} + +static inline long copy_to_user(void __user *to, const void *from, + unsigned long n) +{ + if (!access_ok(VERIFY_WRITE, to, n)) + return n; + return __copy_to_user(to, from, n); +} + +extern long strncpy_from_user(char *__to, const char __user *__from, + long __len); +extern long strnlen_user(const char __user *s, long n); + +#else /* CONFIG_MMU */ +# define copy_from_user(to, from, n) (memcpy(to, from, n), 0) +# define copy_to_user(to, from, n) (memcpy(to, from, n), 0) + +# define __copy_from_user(to, from, n) copy_from_user(to, from, n) +# define __copy_to_user(to, from, n) copy_to_user(to, from, n) + +static inline long strncpy_from_user(char *dst, const char *src, long count) +{ + char *tmp; + strncpy(dst, src, count); + for (tmp = dst; *tmp && count > 0; tmp++, count--) + ; + return tmp - dst; /* DAVIDM should we count a NUL ? check getname */ +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ +static inline long strnlen_user(const char *src, long n) +{ + return strlen(src) + 1; /* DAVIDM make safer */ +} + +#endif /* CONFIG_MMU */ + +#define __copy_from_user_inatomic __copy_from_user +#define __copy_to_user_inatomic __copy_to_user + +/* + * TODO: get_user/put_user stuff below can probably be the same for MMU and + * NOMMU. + */ + +#ifdef CONFIG_MMU + +/* Optimized macros */ +#define __get_user_asm(val, insn, addr, err) \ +{ \ + __asm__ __volatile__( \ + " movi %0, %3\n" \ + "1: " insn " %1, 0(%2)\n" \ + " movi %0, 0\n" \ + "2:\n" \ + " .section __ex_table,\"a\"\n" \ + " .word 1b, 2b\n" \ + " .previous" \ + : "=&r" (err), "=r" (val) \ + : "r" (addr), "i" (-EFAULT)); \ +} + +#define __get_user_unknown(val, size, ptr, err) do { \ + err = 0; \ + if (copy_from_user(&(val), ptr, size)) { \ + err = -EFAULT; \ + } \ + } while (0) + +#define __get_user_common(val, size, ptr, err) \ +do { \ + switch (size) { \ + case 1: \ + __get_user_asm(val, "ldbu", ptr, err); \ + break; \ + case 2: \ + __get_user_asm(val, "ldhu", ptr, err); \ + break; \ + case 4: \ + __get_user_asm(val, "ldw", ptr, err); \ + break; \ + default: \ + __get_user_unknown(val, size, ptr, err); \ + break; \ + } \ +} while (0) + +#define __get_user(x, ptr) \ + ({ \ + long __gu_err = -EFAULT; \ + const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \ + unsigned long __gu_val; /* FIXME: should be __typeof__ */ \ + __get_user_common(__gu_val, sizeof(*(ptr)), __gu_ptr, __gu_err);\ + (x) = (__typeof__(x))__gu_val; \ + __gu_err; \ + }) + +#define get_user(x, ptr) \ +({ \ + long __gu_err = -EFAULT; \ + const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \ + unsigned long __gu_val = 0; \ + if (access_ok(VERIFY_READ, __gu_ptr, sizeof(*__gu_ptr))) \ + __get_user_common(__gu_val, sizeof(*__gu_ptr), \ + __gu_ptr, __gu_err); \ + (x) = (__typeof__(x))__gu_val; \ + __gu_err; \ +}) + +#define __put_user_asm(val, insn, ptr, err) \ +{ \ + __asm__ __volatile__( \ + " movi %0, %3\n" \ + "1: " insn " %1, 0(%2)\n" \ + " movi %0, 0\n" \ + "2:\n" \ + " .section __ex_table,\"a\"\n" \ + " .word 1b, 2b\n" \ + " .previous\n" \ + : "=&r" (err) \ + : "r" (val), "r" (ptr), "i" (-EFAULT)); \ +} + +#define put_user(x, ptr) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) __user *__pu_ptr = (ptr); \ + __typeof__(*(ptr)) __pu_val = (__typeof(*ptr))(x); \ + if (access_ok(VERIFY_WRITE, __pu_ptr, sizeof(*__pu_ptr))) { \ + switch (sizeof(*__pu_ptr)) { \ + case 1: \ + __put_user_asm(__pu_val, "stb", __pu_ptr, __pu_err); \ + break; \ + case 2: \ + __put_user_asm(__pu_val, "sth", __pu_ptr, __pu_err); \ + break; \ + case 4: \ + __put_user_asm(__pu_val, "stw", __pu_ptr, __pu_err); \ + break; \ + default: \ + /* XXX: This looks wrong... */ \ + __pu_err = 0; \ + if (copy_to_user(__pu_ptr, &(__pu_val), \ + sizeof(*__pu_ptr))) \ + __pu_err = -EFAULT; \ + break; \ + } \ + } \ + __pu_err; \ +}) + +#define __put_user(x, ptr) put_user(x, ptr) + +#else /* CONFIG_MMU */ + +/* + * These are the main single-value transfer routines. They automatically + * use the right size if we just have the right pointer type. + */ + +#define put_user(x, ptr) \ +({ \ + int __pu_err = 0; \ + __typeof__(*(ptr)) __pu_val = (x); \ + switch (sizeof(*(ptr))) { \ + case 1: \ + case 2: \ + case 4: \ + case 8: \ + memcpy(ptr, &__pu_val, sizeof(*(ptr))); \ + break; \ + default: \ + __pu_err = __put_user_bad(); \ + break; \ + } \ + __pu_err; \ +}) +#define __put_user(x, ptr) put_user(x, ptr) + +extern int __put_user_bad(void); + +#define get_user(x, ptr) \ +({ \ + int __gu_err = 0; \ + typeof(*(ptr)) __gu_val = 0; \ + switch (sizeof(*(ptr))) { \ + case 1: \ + case 2: \ + case 4: \ + case 8: \ + memcpy(&__gu_val, ptr, sizeof(*(ptr))); \ + break; \ + default: \ + __gu_val = 0; \ + __gu_err = __get_user_bad(); \ + break; \ + } \ + (x) = __gu_val; \ + __gu_err; \ +}) +#define __get_user(x, ptr) get_user(x, ptr) + +extern int __get_user_bad(void); + +#endif /* CONFIG_MMU */ + +#endif /* _ASM_NIOS2_UACCESS_H */ diff --git a/arch/nios2/include/asm/ucontext.h b/arch/nios2/include/asm/ucontext.h new file mode 100644 index 0000000000000..a1aeef9ba8547 --- /dev/null +++ b/arch/nios2/include/asm/ucontext.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * derived from m68knommu + * + * 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. + */ + +#ifndef _ASM_NIOS2_UCONTEXT_H +#define _ASM_NIOS2_UCONTEXT_H + +typedef int greg_t; +#define NGREG 32 +typedef greg_t gregset_t[NGREG]; + +struct mcontext { + int version; +#ifdef __uClinux__ + int status_extension; +#endif + gregset_t gregs; +}; + +#define MCONTEXT_VERSION 2 + +struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct mcontext uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#endif diff --git a/arch/nios2/include/asm/unistd.h b/arch/nios2/include/asm/unistd.h new file mode 100644 index 0000000000000..921a3f09cd7e3 --- /dev/null +++ b/arch/nios2/include/asm/unistd.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2010 Frans Meulenbroeks + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + */ + +#ifndef _ASM_NIOS2_UNISTD_H +#define _ASM_NIOS2_UNISTD_H + +#include + +#define __ARCH_WANT_IPC_PARSE_VERSION +#define __ARCH_WANT_STAT64 +#define __ARCH_WANT_SYS_ALARM +#define __ARCH_WANT_SYS_GETHOSTNAME +#define __ARCH_WANT_SYS_IPC +#define __ARCH_WANT_SYS_PAUSE +#define __ARCH_WANT_SYS_SGETMASK +#define __ARCH_WANT_SYS_SIGNAL +#define __ARCH_WANT_SYS_TIME +#define __ARCH_WANT_SYS_UTIME +#define __ARCH_WANT_SYS_WAITPID +#define __ARCH_WANT_SYS_SOCKETCALL +#define __ARCH_WANT_SYS_FADVISE64 +#define __ARCH_WANT_SYS_GETPGRP +#define __ARCH_WANT_SYS_LLSEEK +#define __ARCH_WANT_SYS_NICE +#define __ARCH_WANT_SYS_OLDUMOUNT +#define __ARCH_WANT_SYS_OLD_SELECT +#define __ARCH_WANT_SYS_OLD_UNAME +#define __ARCH_WANT_SYS_SIGPENDING +#define __ARCH_WANT_SYS_SIGPROCMASK +#define __ARCH_WANT_SYS_RT_SIGACTION + +/* + * "Conditional" syscalls + * + * What we want is __attribute__((weak,alias("sys_ni_syscall"))), + * but it doesn't work on all toolchains, so we just do it by hand + */ +#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); + +#endif /* _ASM_NIOS2_UNISTD_H */ diff --git a/arch/nios2/include/uapi/asm/Kbuild b/arch/nios2/include/uapi/asm/Kbuild new file mode 100644 index 0000000000000..e678394e227f8 --- /dev/null +++ b/arch/nios2/include/uapi/asm/Kbuild @@ -0,0 +1,16 @@ +include include/uapi/asm-generic/Kbuild.asm + +header-y += byteorder.h +header-y += elf.h +header-y += fcntl.h +header-y += ioctls.h +header-y += poll.h +header-y += posix_types.h +header-y += processor.h +header-y += ptrace.h +header-y += setup.h +header-y += sigcontext.h +header-y += signal.h +header-y += stat.h +header-y += swab.h +header-y += unistd.h diff --git a/arch/nios2/include/uapi/asm/byteorder.h b/arch/nios2/include/uapi/asm/byteorder.h new file mode 100644 index 0000000000000..3ab5dc20d757c --- /dev/null +++ b/arch/nios2/include/uapi/asm/byteorder.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 Thomas Chou + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + * + */ + +#ifndef _ASM_NIOS2_BYTEORDER_H +#define _ASM_NIOS2_BYTEORDER_H + +#include + +#endif diff --git a/arch/nios2/include/uapi/asm/elf.h b/arch/nios2/include/uapi/asm/elf.h new file mode 100644 index 0000000000000..a5b91ae5cf56f --- /dev/null +++ b/arch/nios2/include/uapi/asm/elf.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * + * 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 . + * + */ + + +#ifndef _UAPI_ASM_NIOS2_ELF_H +#define _UAPI_ASM_NIOS2_ELF_H + +#include + +/* Relocation types */ +#define R_NIOS2_NONE 0 +#define R_NIOS2_S16 1 +#define R_NIOS2_U16 2 +#define R_NIOS2_PCREL16 3 +#define R_NIOS2_CALL26 4 +#define R_NIOS2_IMM5 5 +#define R_NIOS2_CACHE_OPX 6 +#define R_NIOS2_IMM6 7 +#define R_NIOS2_IMM8 8 +#define R_NIOS2_HI16 9 +#define R_NIOS2_LO16 10 +#define R_NIOS2_HIADJ16 11 +#define R_NIOS2_BFD_RELOC_32 12 +#define R_NIOS2_BFD_RELOC_16 13 +#define R_NIOS2_BFD_RELOC_8 14 +#define R_NIOS2_GPREL 15 +#define R_NIOS2_GNU_VTINHERIT 16 +#define R_NIOS2_GNU_VTENTRY 17 +#define R_NIOS2_UJMP 18 +#define R_NIOS2_CJMP 19 +#define R_NIOS2_CALLR 20 +#define R_NIOS2_ALIGN 21 +/* Keep this the last entry. */ +#define R_NIOS2_NUM 22 + +typedef unsigned long elf_greg_t; + +#define ELF_NGREG \ + ((sizeof(struct pt_regs) + sizeof(struct switch_stack)) / \ + sizeof(elf_greg_t)) +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +typedef unsigned long elf_fpregset_t; + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_ALTERA_NIOS2 + +#endif /* _UAPI_ASM_NIOS2_ELF_H */ diff --git a/arch/nios2/include/uapi/asm/fcntl.h b/arch/nios2/include/uapi/asm/fcntl.h new file mode 100644 index 0000000000000..76d4a69f4fbdd --- /dev/null +++ b/arch/nios2/include/uapi/asm/fcntl.h @@ -0,0 +1,36 @@ +/* + * This file came from the m68k port. + * + * Copyright (C) 2004 Microtronix Datacom Ltd. + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _ASM_NIOS2_FCNTL_H +#define _ASM_NIOS2_FCNTL_H + +#define O_DIRECTORY 040000 /* must be a directory */ +#define O_NOFOLLOW 0100000 /* don't follow links */ +#define O_DIRECT 0200000 /* direct disk access hint - currently + ignored */ +#define O_LARGEFILE 0400000 + +#include + +#endif /* _ASM_NIOS2_FCNTL_H */ diff --git a/arch/nios2/include/uapi/asm/ioctls.h b/arch/nios2/include/uapi/asm/ioctls.h new file mode 100644 index 0000000000000..ce0b09c3eaa26 --- /dev/null +++ b/arch/nios2/include/uapi/asm/ioctls.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2009 Thomas Chou + * + * 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 . + * + */ + +#ifndef _ASM_NIOS2_IOCTLS_H +#define _ASM_NIOS2_IOCTLS_H + +#define FIOQSIZE 0x545E +#include + +#endif diff --git a/arch/nios2/include/uapi/asm/poll.h b/arch/nios2/include/uapi/asm/poll.h new file mode 100644 index 0000000000000..5bbf747031ccc --- /dev/null +++ b/arch/nios2/include/uapi/asm/poll.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2009 Thomas Chou + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + * + */ + +#ifndef _ASM_NIOS2_POLL_H +#define _ASM_NIOS2_POLL_H + +#define POLLWRNORM POLLOUT +#define POLLWRBAND 256 + +#include + +#endif diff --git a/arch/nios2/include/uapi/asm/posix_types.h b/arch/nios2/include/uapi/asm/posix_types.h new file mode 100644 index 0000000000000..e326dafbc7311 --- /dev/null +++ b/arch/nios2/include/uapi/asm/posix_types.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 Thomas Chou + * + * 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 . + * + */ +#ifndef _ASM_NIOS2_POSIX_TYPES_H +#define _ASM_NIOS2_POSIX_TYPES_H + +typedef unsigned short __kernel_mode_t; +#define __kernel_mode_t __kernel_mode_t + +typedef unsigned short __kernel_nlink_t; +#define __kernel_nlink_t __kernel_nlink_t + +#ifdef CONFIG_MMU +typedef unsigned int __kernel_ipc_pid_t; +#define __kernel_ipc_pid_t __kernel_ipc_pid_t + +typedef unsigned long __kernel_size_t; +typedef long __kernel_ssize_t; +typedef int __kernel_ptrdiff_t; +#define __kernel_size_t __kernel_size_t + +#else +typedef unsigned short __kernel_ipc_pid_t; +#define __kernel_ipc_pid_t __kernel_ipc_pid_t + +typedef unsigned short __kernel_uid_t; +typedef unsigned short __kernel_gid_t; +#define __kernel_uid_t __kernel_uid_t + +typedef unsigned int __kernel_uid32_t; +typedef unsigned int __kernel_gid32_t; +#define __kernel_uid32_t __kernel_uid32_t +#endif /* CONFIG_MMU */ + +typedef unsigned short __kernel_old_uid_t; +typedef unsigned short __kernel_old_gid_t; +#define __kernel_old_uid_t __kernel_old_uid_t + +typedef unsigned short __kernel_old_dev_t; +#define __kernel_old_dev_t __kernel_old_dev_t + +#include + +#endif /* _ASM_NIOS2_POSIX_TYPES_H */ diff --git a/arch/nios2/include/uapi/asm/ptrace.h b/arch/nios2/include/uapi/asm/ptrace.h new file mode 100644 index 0000000000000..130eb4c28bc88 --- /dev/null +++ b/arch/nios2/include/uapi/asm/ptrace.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * based on m68k asm/processor.h + * + * 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. + */ + +#ifndef _UAPI_ASM_NIOS2_PTRACE_H +#define _UAPI_ASM_NIOS2_PTRACE_H + +#ifndef __ASSEMBLY__ + +/* + * Register numbers used by 'ptrace' system call interface. + */ + +/* GP registers */ +#define PTR_R0 0 +#define PTR_R1 1 +#define PTR_R2 2 +#define PTR_R3 3 +#define PTR_R4 4 +#define PTR_R5 5 +#define PTR_R6 6 +#define PTR_R7 7 +#define PTR_R8 8 +#define PTR_R9 9 +#define PTR_R10 10 +#define PTR_R11 11 +#define PTR_R12 12 +#define PTR_R13 13 +#define PTR_R14 14 +#define PTR_R15 15 +#define PTR_R16 16 +#define PTR_R17 17 +#define PTR_R18 18 +#define PTR_R19 19 +#define PTR_R20 20 +#define PTR_R21 21 +#define PTR_R22 22 +#define PTR_R23 23 +#define PTR_R24 24 +#define PTR_R25 25 +#define PTR_GP 26 +#define PTR_SP 27 +#define PTR_FP 28 +#define PTR_EA 29 +#define PTR_BA 30 +#define PTR_RA 31 +/* Control registers */ +#define PTR_PC 32 +#define PTR_STATUS 33 +#define PTR_ESTATUS 34 +#define PTR_BSTATUS 35 +#define PTR_IENABLE 36 +#define PTR_IPENDING 37 +#define PTR_CPUID 38 +#define PTR_CTL6 39 +#define PTR_CTL7 40 +#define PTR_PTEADDR 41 +#define PTR_TLBACC 42 +#define PTR_TLBMISC 43 + +/* Text/data offsets, needed by gdbserver */ +#define PT_TEXT_ADDR (44*4) +#define PT_TEXT_END_ADDR (45*4) +#define PT_DATA_ADDR (46*4) + +/* this struct defines the way the registers are stored on the + stack during a system call. + + There is a fake_regs in setup.c that has to match pt_regs.*/ + +struct pt_regs { + unsigned long r8; /* r8-r15 Caller-saved GP registers */ + unsigned long r9; + unsigned long r10; + unsigned long r11; + unsigned long r12; + unsigned long r13; + unsigned long r14; + unsigned long r15; + unsigned long r1; /* Assembler temporary */ + unsigned long r2; /* Retval LS 32bits */ + unsigned long r3; /* Retval MS 32bits */ + unsigned long r4; /* r4-r7 Register arguments */ + unsigned long r5; + unsigned long r6; + unsigned long r7; + unsigned long orig_r2; /* Copy of r2 ?? */ + unsigned long ra; /* Return address */ + unsigned long fp; /* Frame pointer */ + unsigned long sp; /* Stack pointer */ + unsigned long gp; /* Global pointer */ + unsigned long estatus; +#ifdef __uClinux__ + unsigned long status_extension; /* Status extension. Used to + fake user mode */ +#endif + unsigned long ea; /* Exception return address (pc) */ +#ifndef __uClinux__ + unsigned long orig_r7; +#endif +}; + +/* + * This is the extended stack used by signal handlers and the context + * switcher: it's pushed after the normal "struct pt_regs". + */ +struct switch_stack { + unsigned long r16; /* r16-r23 Callee-saved GP registers */ + unsigned long r17; + unsigned long r18; + unsigned long r19; + unsigned long r20; + unsigned long r21; + unsigned long r22; + unsigned long r23; + unsigned long fp; + unsigned long gp; + unsigned long ra; +}; + +#endif /* __ASSEMBLY__ */ +#endif /* _UAPI_ASM_NIOS2_PTRACE_H */ diff --git a/arch/nios2/include/uapi/asm/setup.h b/arch/nios2/include/uapi/asm/setup.h new file mode 100644 index 0000000000000..f3fc3e7f34f29 --- /dev/null +++ b/arch/nios2/include/uapi/asm/setup.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * + * 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 . + * + */ + + +#ifndef _ASM_NIOS2_SETUP_H +#define _ASM_NIOS2_SETUP_H + +#include + +#ifndef __ASSEMBLY__ +# ifdef __KERNEL__ + +extern char cmd_line[COMMAND_LINE_SIZE]; + +extern char exception_handler_hook[]; +extern char fast_handler[]; +extern char fast_handler_end[]; + +extern void pagetable_init(void); + +extern void setup_early_printk(void); + +# endif/* __KERNEL__ */ +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_NIOS2_SETUP_H */ diff --git a/arch/nios2/include/uapi/asm/sigcontext.h b/arch/nios2/include/uapi/asm/sigcontext.h new file mode 100644 index 0000000000000..00223471ee8a8 --- /dev/null +++ b/arch/nios2/include/uapi/asm/sigcontext.h @@ -0,0 +1,35 @@ +/* + * Taken from the m68knommu. + * + * Copyright (C) 2004, Microtronix Datacom Ltd. + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _ASM_NIOS2_SIGCONTEXT_H +#define _ASM_NIOS2_SIGCONTEXT_H + +#include + +struct sigcontext { + struct pt_regs regs; + unsigned long sc_mask; /* old sigmask */ +}; + +#endif diff --git a/arch/nios2/include/uapi/asm/signal.h b/arch/nios2/include/uapi/asm/signal.h new file mode 100644 index 0000000000000..b89779e059cec --- /dev/null +++ b/arch/nios2/include/uapi/asm/signal.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2004, Microtronix Datacom Ltd. + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _UAPI_ASM_NIOS2_SIGNAL_H +#define _UAPI_ASM_NIOS2_SIGNAL_H + +#include + +/* Avoid too many header ordering problems. */ +struct siginfo; + +#ifndef __KERNEL__ +/* Here we must cater to libcs that poke about in kernel headers. */ + +#define NSIG 32 +typedef unsigned long sigset_t; + +#endif /* __KERNEL__ */ + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +/* +#define SIGLOST 29 +*/ +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +/* + * SA_FLAGS values: + * + * SA_ONSTACK indicates that a registered stack_t will be used. + * SA_RESTART flag to get restarting signals (which were the default long ago) + * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. + * SA_RESETHAND clears the handler when the signal is delivered. + * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. + * SA_NODEFER prevents the current signal from being masked in the handler. + * + * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single + * Unix names RESETHAND and NODEFER respectively. + */ +#define SA_NOCLDSTOP 0x00000001 +#define SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define SA_SIGINFO 0x00000004 +#define SA_ONSTACK 0x08000000 +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND + +/* + * sigaltstack controls + */ +#define SS_ONSTACK 1 +#define SS_DISABLE 2 + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 + +#include + +#ifndef __KERNEL__ +/* Here we must cater to libcs that poke about in kernel headers. */ + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction (_u._sa_sigaction) + +#endif /* __KERNEL__ */ + +typedef struct sigaltstack { + void *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; + +#endif /* _UAPI_ASM_NIOS2_SIGNAL_H */ diff --git a/arch/nios2/include/uapi/asm/stat.h b/arch/nios2/include/uapi/asm/stat.h new file mode 100644 index 0000000000000..109ec1889e4fa --- /dev/null +++ b/arch/nios2/include/uapi/asm/stat.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + */ + +#ifndef _ASM_NIOS2_STAT_H +#define _ASM_NIOS2_STAT_H + +struct __old_kernel_stat { + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned long st_size; + unsigned long st_atime; + unsigned long st_mtime; + unsigned long st_ctime; +}; + +struct stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long __unused1; + unsigned long st_mtime; + unsigned long __unused2; + unsigned long st_ctime; + unsigned long __unused3; + unsigned long __unused4; + unsigned long __unused5; +}; + +/* This matches struct stat64 in glibc2.1, hence the absolutely + * insane amounts of padding around dev_t's. + */ +struct stat64 { + unsigned long long st_dev; + unsigned char __pad1[4]; + +#define STAT64_HAS_BROKEN_ST_INO 1 + unsigned long __st_ino; + + unsigned int st_mode; + unsigned int st_nlink; + + unsigned long st_uid; + unsigned long st_gid; + + unsigned long long st_rdev; + unsigned char __pad3[4]; + + long long st_size; + unsigned long st_blksize; +#ifdef CONFIG_MMU + unsigned long long st_blocks; /* Number 512-byte blocks allocated. */ +#else + unsigned long __pad4; /* future possible st_blocks high bits */ + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ +#endif + + unsigned long st_atime; + unsigned long st_atime_nsec; + + unsigned long st_mtime; + unsigned long st_mtime_nsec; + + unsigned long st_ctime; + unsigned long st_ctime_nsec; + + unsigned long long st_ino; +}; + +#endif diff --git a/arch/nios2/include/uapi/asm/swab.h b/arch/nios2/include/uapi/asm/swab.h new file mode 100644 index 0000000000000..b4e22ebaeb179 --- /dev/null +++ b/arch/nios2/include/uapi/asm/swab.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 Tobias Klauser + * Copyright (C) 2011 Pyramid Technical Consultants, Inc. + * + * 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. + */ + +#ifndef _ASM_NIOS2_SWAB_H +#define _ASM_NIOS2_SWAB_H + +#include +#include + +#ifdef CONFIG_NIOS2_CI_SWAB_SUPPORT +#ifdef __GNUC__ + +#define __nios2_swab(x) \ + __builtin_custom_ini(CONFIG_NIOS2_CI_SWAB_NO, (x)) + +static inline __attribute__((const)) __u16 __arch_swab16(__u16 x) +{ + return (__u16) __nios2_swab(((__u32) x) << 16); +} +#define __arch_swab16 __arch_swab16 + +static inline __attribute__((const)) __u32 __arch_swab32(__u32 x) +{ + return (__u32) __nios2_swab(x); +} +#define __arch_swab32 __arch_swab32 + +#endif /* __GNUC__ */ +#endif /* CONFIG_NIOS2_CI_SWAB_SUPPORT */ + +#endif /* _ASM_NIOS2_SWAB_H */ diff --git a/arch/nios2/include/uapi/asm/unistd.h b/arch/nios2/include/uapi/asm/unistd.h new file mode 100644 index 0000000000000..4837efafe44ba --- /dev/null +++ b/arch/nios2/include/uapi/asm/unistd.h @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2010 Frans Meulenbroeks + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + */ + +#ifndef _UAPI_ASM_NIOS2_UNISTD_H +#define _UAPI_ASM_NIOS2_UNISTD_H + +/* + * TRAP 0 isr expects the the syscall # in r3, and arguments in r4, r5, ... + * Return value in r2 and error/ok flag in r7. + */ + +#define __NR_restart_syscall 0 +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_chown 16 + /* 17 __NR_break obsolete */ + /* 18 __NR_oldstat obsolete */ +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 + /* 28 __NR_oldfstat obsolete */ +#define __NR_pause 29 +#define __NR_utime 30 + /* 31 __NR_stty obsolete */ + /* 32 __NR_gtty obsolete */ +#define __NR_access 33 +#define __NR_nice 34 + /* 35 __NR_ftime obsolete */ +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 + /* 44 __NR_prof obsolete */ +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 + /* 48 __NR_signal obsolete */ +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount2 52 + /* 53 __NR_lock obsolete */ +#define __NR_ioctl 54 +#define __NR_fcntl 55 + /* 56 __NR_mpx obsolete */ +#define __NR_setpgid 57 + /* 58 __NR_ulimit obsolete */ + /* 59 __NR_oldolduname obsolete */ +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 + /* 68 __NR_sgetmask obsolete */ + /* 69 __NR_ssetmask obsolete */ +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 + /* 84 __NR_oldlstat obsolete */ +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 + /* 89 __NR_readdir obsolete */ +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 + /* 98 __NR_profil obsolete */ +#define __NR_statfs 99 +#define __NR_fstatfs 100 + /* 101 __NR_ioperm obsolete */ +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 + /* 109 __NR_olduname obsolete */ + /* 110 __NR_iopl obsolete */ +#define __NR_vhangup 111 + /* 112 __NR_idle obsolete */ +#define __NR_nios2cmpxchg 113 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_cacheflush 123 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 + /* 127 __NR_create_module obsolete */ +#define __NR_init_module 128 +#define __NR_delete_module 129 + /* 130 __NR_get_kernel_syms obsolete */ +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 + /* 137 __NR_afs_syscall obsolete */ +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 /* obsolete */ +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_getpagesize 166 + /* 167 __NR_query_module obsolete */ +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 + /* 180 we cannot have both NR_pread and NR_pread64 */ + /* 181 we cannot have both NR_pwrite and NR_pwrite64 */ +#define __NR_lchown 182 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 + /* 188 __NR_getpmsg obsolete */ + /* 189 __NR_putpmsg obsolete */ +#define __NR_vfork 190 +#define __NR_ugetrlimit 191 +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#define __NR_chown32 198 +#define __NR_getuid32 199 +#define __NR_getgid32 200 +#define __NR_geteuid32 201 +#define __NR_getegid32 202 +#define __NR_setreuid32 203 +#define __NR_setregid32 204 +#define __NR_getgroups32 205 +#define __NR_setgroups32 206 +#define __NR_fchown32 207 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#define __NR_lchown32 212 +#define __NR_setuid32 213 +#define __NR_setgid32 214 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#define __NR_pivot_root 217 + /* 218 unused */ + /* 219 unused */ +#define __NR_getdents64 220 +#define __NR_gettid 221 +#define __NR_tkill 222 +#define __NR_setxattr 223 +#define __NR_lsetxattr 224 +#define __NR_fsetxattr 225 +#define __NR_getxattr 226 +#define __NR_lgetxattr 227 +#define __NR_fgetxattr 228 +#define __NR_listxattr 229 +#define __NR_llistxattr 230 +#define __NR_flistxattr 231 +#define __NR_removexattr 232 +#define __NR_lremovexattr 233 +#define __NR_fremovexattr 234 +#define __NR_futex 235 +#define __NR_sendfile64 236 +#define __NR_mincore 237 +#define __NR_madvise 238 +#define __NR_fcntl64 239 +#define __NR_readahead 240 +#define __NR_io_setup 241 +#define __NR_io_destroy 242 +#define __NR_io_getevents 243 +#define __NR_io_submit 244 +#define __NR_io_cancel 245 +#define __NR_fadvise64 246 +#define __NR_exit_group 247 +#define __NR_lookup_dcookie 248 +#define __NR_epoll_create 249 +#define __NR_epoll_ctl 250 +#define __NR_epoll_wait 251 +#define __NR_remap_file_pages 252 +#define __NR_set_tid_address 253 +#define __NR_timer_create 254 +#define __NR_timer_settime 255 +#define __NR_timer_gettime 256 +#define __NR_timer_getoverrun 257 +#define __NR_timer_delete 258 +#define __NR_clock_settime 259 +#define __NR_clock_gettime 260 +#define __NR_clock_getres 261 +#define __NR_clock_nanosleep 262 +#define __NR_statfs64 263 +#define __NR_fstatfs64 264 +#define __NR_tgkill 265 +#define __NR_utimes 266 +#define __NR_fadvise64_64 267 +#define __NR_mbind 268 +#define __NR_get_mempolicy 269 +#define __NR_set_mempolicy 270 +#define __NR_mq_open 271 +#define __NR_mq_unlink 272 +#define __NR_mq_timedsend 273 +#define __NR_mq_timedreceive 274 +#define __NR_mq_notify 275 +#define __NR_mq_getsetattr 276 +#define __NR_waitid 277 + /* 278 __NR_sys_setaltroot obsolete */ +#define __NR_add_key 279 +#define __NR_request_key 280 +#define __NR_keyctl 281 +#define __NR_ioprio_set 282 +#define __NR_ioprio_get 283 +#define __NR_inotify_init 284 +#define __NR_inotify_add_watch 285 +#define __NR_inotify_rm_watch 286 +#define __NR_migrate_pages 287 +#define __NR_openat 288 +#define __NR_mkdirat 289 +#define __NR_mknodat 290 +#define __NR_fchownat 291 +#define __NR_futimesat 292 +#define __NR_fstatat64 293 +#define __NR_unlinkat 294 +#define __NR_renameat 295 +#define __NR_linkat 296 +#define __NR_symlinkat 297 +#define __NR_readlinkat 298 +#define __NR_fchmodat 299 +#define __NR_faccessat 300 +#define __NR_pselect6 301 +#define __NR_ppoll 302 +#define __NR_unshare 303 +#define __NR_set_robust_list 304 +#define __NR_get_robust_list 305 +#define __NR_splice 306 +#define __NR_sync_file_range 307 +#define __NR_tee 308 +#define __NR_vmsplice 309 +#define __NR_move_pages 310 +#define __NR_sched_setaffinity 311 +#define __NR_sched_getaffinity 312 +#define __NR_kexec_load 313 +#define __NR_getcpu 314 +#define __NR_epoll_pwait 315 +#define __NR_utimensat 316 +#define __NR_signalfd 317 +#define __NR_timerfd_create 318 +#define __NR_eventfd 319 +#define __NR_pread64 320 +#define __NR_pwrite64 321 +#define __NR_fallocate 322 +#define __NR_timerfd_settime 323 +#define __NR_timerfd_gettime 324 +#define __NR_signalfd4 325 +#define __NR_eventfd2 326 +#define __NR_epoll_create1 327 +#define __NR_dup3 328 +#define __NR_pipe2 329 +#define __NR_inotify_init1 330 +#define __NR_preadv 331 +#define __NR_pwritev 332 +#define __NR_rt_tgsigqueueinfo 333 +#define __NR_perf_event_open 334 +#define __NR_recvmmsg 335 +#define __NR_fanotify_init 336 +#define __NR_fanotify_mark 337 +#define __NR_prlimit64 338 +#define __NR_name_to_handle_at 339 +#define __NR_open_by_handle_at 340 +#define __NR_clock_adjtime 341 +#define __NR_syncfs 342 +#define __NR_sendmmsg 343 +#define __NR_setns 344 +#define __NR_process_vm_readv 345 +#define __NR_process_vm_writev 346 +#define __NR_kcmp 347 + +#define NR_syscalls 348 + +#endif /* _UAPI_ASM_NIOS2_UNISTD_H */ From aec56768b501f385a1869aa6ddce8d63e55218c6 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 14 May 2013 12:34:15 +0800 Subject: [PATCH 007/201] FogBugz #111740: [PATCHv3 7/7] Integrates Nios II kernel (lib) Integrates Nios II kernel (arch/nios2) from linux-nios2.git to linux-socfpga.git. Signed-off-by: Ley Foon Tan --- arch/nios2/lib/Makefile | 1 + arch/nios2/lib/memcpy.c | 199 +++++++++++++++++++++++++++++++++++++++ arch/nios2/lib/memmove.c | 82 ++++++++++++++++ arch/nios2/lib/memset.c | 81 ++++++++++++++++ 4 files changed, 363 insertions(+) create mode 100644 arch/nios2/lib/Makefile create mode 100644 arch/nios2/lib/memcpy.c create mode 100644 arch/nios2/lib/memmove.c create mode 100644 arch/nios2/lib/memset.c diff --git a/arch/nios2/lib/Makefile b/arch/nios2/lib/Makefile new file mode 100644 index 0000000000000..f9c5bc02ba357 --- /dev/null +++ b/arch/nios2/lib/Makefile @@ -0,0 +1 @@ +lib-y = memset.o memcpy.o memmove.o \ No newline at end of file diff --git a/arch/nios2/lib/memcpy.c b/arch/nios2/lib/memcpy.c new file mode 100644 index 0000000000000..ac7eec4c403ae --- /dev/null +++ b/arch/nios2/lib/memcpy.c @@ -0,0 +1,199 @@ +/* Extracted from GLIBC memcpy.c and memcopy.h, which is: + Copyright (C) 1991, 1992, 1993, 1997, 2004 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Torbjorn Granlund (tege@sics.se). + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +/* Type to use for aligned memory operations. + This should normally be the biggest type supported by a single load + and store. */ +#define op_t unsigned long int +#define OPSIZ (sizeof(op_t)) + +/* Optimal type for storing bytes in registers. */ +#define reg_char char + +#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2))) + +/* Copy exactly NBYTES bytes from SRC_BP to DST_BP, + without any assumptions about alignment of the pointers. */ +#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes) \ +do { \ + size_t __nbytes = (nbytes); \ + while (__nbytes > 0) { \ + unsigned char __x = ((unsigned char *) src_bp)[0]; \ + src_bp += 1; \ + __nbytes -= 1; \ + ((unsigned char *) dst_bp)[0] = __x; \ + dst_bp += 1; \ + } \ +} while (0) + +/* Copy *up to* NBYTES bytes from SRC_BP to DST_BP, with + the assumption that DST_BP is aligned on an OPSIZ multiple. If + not all bytes could be easily copied, store remaining number of bytes + in NBYTES_LEFT, otherwise store 0. */ +/* extern void _wordcopy_fwd_aligned __P ((long int, long int, size_t)); */ +/* extern void _wordcopy_fwd_dest_aligned __P ((long int, long int, size_t)); */ +#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes) \ +do { \ + if (src_bp % OPSIZ == 0) \ + _wordcopy_fwd_aligned(dst_bp, src_bp, (nbytes) / OPSIZ);\ + else \ + _wordcopy_fwd_dest_aligned(dst_bp, src_bp, (nbytes) / OPSIZ);\ + src_bp += (nbytes) & -OPSIZ; \ + dst_bp += (nbytes) & -OPSIZ; \ + (nbytes_left) = (nbytes) % OPSIZ; \ +} while (0) + + +/* Threshold value for when to enter the unrolled loops. */ +#define OP_T_THRES 16 + +/* _wordcopy_fwd_aligned -- Copy block beginning at SRCP to + block beginning at DSTP with LEN `op_t' words (not LEN bytes!). + Both SRCP and DSTP should be aligned for memory operations on `op_t's. */ +/* stream-lined (read x8 + write x8) */ +static void _wordcopy_fwd_aligned(long int dstp, long int srcp, size_t len) +{ + while (len > 7) { + register op_t a0, a1, a2, a3, a4, a5, a6, a7; + a0 = ((op_t *) srcp)[0]; + a1 = ((op_t *) srcp)[1]; + a2 = ((op_t *) srcp)[2]; + a3 = ((op_t *) srcp)[3]; + a4 = ((op_t *) srcp)[4]; + a5 = ((op_t *) srcp)[5]; + a6 = ((op_t *) srcp)[6]; + a7 = ((op_t *) srcp)[7]; + ((op_t *) dstp)[0] = a0; + ((op_t *) dstp)[1] = a1; + ((op_t *) dstp)[2] = a2; + ((op_t *) dstp)[3] = a3; + ((op_t *) dstp)[4] = a4; + ((op_t *) dstp)[5] = a5; + ((op_t *) dstp)[6] = a6; + ((op_t *) dstp)[7] = a7; + + srcp += 8 * OPSIZ; + dstp += 8 * OPSIZ; + len -= 8; + } + while (len > 0) { + *(op_t *)dstp = *(op_t *)srcp; + + srcp += OPSIZ; + dstp += OPSIZ; + len -= 1; + } +} + +/* _wordcopy_fwd_dest_aligned -- Copy block beginning at SRCP to + block beginning at DSTP with LEN `op_t' words (not LEN bytes!). + DSTP should be aligned for memory operations on `op_t's, but SRCP must + *not* be aligned. */ +/* stream-lined (read x4 + write x4) */ +static void _wordcopy_fwd_dest_aligned(long int dstp, long int srcp, + size_t len) +{ + op_t ap; + int sh_1, sh_2; + + /* Calculate how to shift a word read at the memory operation + aligned srcp to make it aligned for copy. */ + + sh_1 = 8 * (srcp % OPSIZ); + sh_2 = 8 * OPSIZ - sh_1; + + /* Make SRCP aligned by rounding it down to the beginning of the `op_t' + it points in the middle of. */ + srcp &= -OPSIZ; + ap = ((op_t *) srcp)[0]; + srcp += OPSIZ; + + while (len > 3) { + op_t a0, a1, a2, a3; + a0 = ((op_t *) srcp)[0]; + a1 = ((op_t *) srcp)[1]; + a2 = ((op_t *) srcp)[2]; + a3 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[0] = MERGE(ap, sh_1, a0, sh_2); + ((op_t *) dstp)[1] = MERGE(a0, sh_1, a1, sh_2); + ((op_t *) dstp)[2] = MERGE(a1, sh_1, a2, sh_2); + ((op_t *) dstp)[3] = MERGE(a2, sh_1, a3, sh_2); + + ap = a3; + srcp += 4 * OPSIZ; + dstp += 4 * OPSIZ; + len -= 4; + } + while (len > 0) { + register op_t a0; + a0 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = MERGE(ap, sh_1, a0, sh_2); + + ap = a0; + srcp += OPSIZ; + dstp += OPSIZ; + len -= 1; + } +} + +void *memcpy(void *dstpp, const void *srcpp, size_t len) +{ + unsigned long int dstp = (long int) dstpp; + unsigned long int srcp = (long int) srcpp; + + /* Copy from the beginning to the end. */ + + /* If there not too few bytes to copy, use word copy. */ + if (len >= OP_T_THRES) { + /* Copy just a few bytes to make DSTP aligned. */ + len -= (-dstp) % OPSIZ; + BYTE_COPY_FWD(dstp, srcp, (-dstp) % OPSIZ); + + /* Copy whole pages from SRCP to DSTP by virtual address + manipulation, as much as possible. */ + + /* PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len); */ + + /* Copy from SRCP to DSTP taking advantage of the known + alignment of DSTP. Number of bytes remaining is put in the + third argument, i.e. in LEN. This number may vary from + machine to machine. */ + + WORD_COPY_FWD(dstp, srcp, len, len); + + /* Fall out and copy the tail. */ + } + + /* There are just a few bytes to copy. Use byte memory operations. */ + BYTE_COPY_FWD(dstp, srcp, len); + + return dstpp; +} + +void *memcpyb(void *dstpp, const void *srcpp, unsigned len) +{ + unsigned long int dstp = (long int) dstpp; + unsigned long int srcp = (long int) srcpp; + + BYTE_COPY_FWD(dstp, srcp, len); + + return dstpp; +} diff --git a/arch/nios2/lib/memmove.c b/arch/nios2/lib/memmove.c new file mode 100644 index 0000000000000..c65ef517eb80c --- /dev/null +++ b/arch/nios2/lib/memmove.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + */ + +#include +#include + +#ifdef __HAVE_ARCH_MEMMOVE +void *memmove(void *d, const void *s, size_t count) +{ + unsigned long dst, src; + + if (!count) + return d; + + if (d < s) { + dst = (unsigned long) d; + src = (unsigned long) s; + + if ((count < 8) || ((dst ^ src) & 3)) + goto restup; + + if (dst & 1) { + *(char *)dst++ = *(char *)src++; + count--; + } + if (dst & 2) { + *(short *)dst = *(short *)src; + src += 2; + dst += 2; + count -= 2; + } + while (count > 3) { + *(long *)dst = *(long *)src; + src += 4; + dst += 4; + count -= 4; + } +restup: + while (count--) + *(char *)dst++ = *(char *)src++; + } else { + dst = (unsigned long) d + count; + src = (unsigned long) s + count; + + if ((count < 8) || ((dst ^ src) & 3)) + goto restdown; + + if (dst & 1) { + src--; + dst--; + count--; + *(char *)dst = *(char *)src; + } + if (dst & 2) { + src -= 2; + dst -= 2; + count -= 2; + *(short *)dst = *(short *)src; + } + while (count > 3) { + src -= 4; + dst -= 4; + count -= 4; + *(long *)dst = *(long *)src; + } +restdown: + while (count--) { + src--; + dst--; + *(char *)dst = *(char *)src; + } + } + + return d; +} +#endif /* __HAVE_ARCH_MEMMOVE */ diff --git a/arch/nios2/lib/memset.c b/arch/nios2/lib/memset.c new file mode 100644 index 0000000000000..65e97802f5cc8 --- /dev/null +++ b/arch/nios2/lib/memset.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2004 Microtronix Datacom Ltd + * + * 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. + */ + +#include +#include + +#ifdef __HAVE_ARCH_MEMSET +void *memset(void *s, int c, size_t count) +{ + int destptr, charcnt, dwordcnt, fill8reg, wrkrega; + + if (!count) + return s; + + c &= 0xFF; + + if (count <= 8) { + char *xs = (char *) s; + + while (count--) + *xs++ = c; + return s; + } + + __asm__ __volatile__ ( + /* fill8 %3, %5 (c & 0xff) */ + " slli %4, %5, 8\n" + " or %4, %4, %5\n" + " slli %3, %4, 16\n" + " or %3, %3, %4\n" + /* Word-align %0 (s) if necessary */ + " andi %4, %0, 0x01\n" + " beq %4, zero, 1f\n" + " addi %1, %1, -1\n" + " stb %3, 0(%0)\n" + " addi %0, %0, 1\n" + "1: mov %2, %1\n" + /* Dword-align %0 (s) if necessary */ + " andi %4, %0, 0x02\n" + " beq %4, zero, 2f\n" + " addi %1, %1, -2\n" + " sth %3, 0(%0)\n" + " addi %0, %0, 2\n" + " mov %2, %1\n" + /* %1 and %2 are how many more bytes to set */ + "2: srli %2, %2, 2\n" + /* %2 is how many dwords to set */ + "3: stw %3, 0(%0)\n" + " addi %0, %0, 4\n" + " addi %2, %2, -1\n" + " bne %2, zero, 3b\n" + /* store residual word and/or byte if necessary */ + " andi %4, %1, 0x02\n" + " beq %4, zero, 4f\n" + " sth %3, 0(%0)\n" + " addi %0, %0, 2\n" + /* store residual byte if necessary */ + "4: andi %4, %1, 0x01\n" + " beq %4, zero, 5f\n" + " stb %3, 0(%0)\n" + "5:\n" + : "=r" (destptr), /* %0 Output */ + "=r" (charcnt), /* %1 Output */ + "=r" (dwordcnt), /* %2 Output */ + "=r" (fill8reg), /* %3 Output */ + "=r" (wrkrega) /* %4 Output */ + : "r" (c), /* %5 Input */ + "0" (s), /* %0 Input/Output */ + "1" (count) /* %1 Input/Output */ + : "memory" /* clobbered */ + ); + + return s; +} +#endif /* __HAVE_ARCH_MEMSET */ From 6aa264d9878956c3e0bd624a92593a288611660a Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 16 May 2013 17:52:29 +0800 Subject: [PATCH 008/201] FogBugz #124090: arch/nios2: Port to support kernel v3.8 - Use generic fork and vfork - Remove execve - New copy_thread() implementation Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/Kbuild | 1 + arch/nios2/include/asm/processor.h | 2 - arch/nios2/include/asm/ptrace.h | 5 + arch/nios2/include/asm/signal.h | 2 +- arch/nios2/include/asm/unistd.h | 4 +- arch/nios2/include/uapi/asm/unistd.h | 4 +- arch/nios2/kernel/entry-nommu.S | 25 ++--- arch/nios2/kernel/entry.S | 29 ++--- arch/nios2/kernel/process.c | 157 ++++++--------------------- arch/nios2/kernel/sys_nios2.c | 39 ++----- arch/nios2/kernel/syscalltable.S | 9 +- 11 files changed, 82 insertions(+), 195 deletions(-) diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild index 30f8ed0931a29..0f6bb34077e2d 100644 --- a/arch/nios2/include/asm/Kbuild +++ b/arch/nios2/include/asm/Kbuild @@ -48,6 +48,7 @@ generic-y += spinlock.h generic-y += statfs.h generic-y += termbits.h generic-y += termios.h +generic-y += trace_clock.h generic-y += topology.h generic-y += types.h generic-y += unaligned.h diff --git a/arch/nios2/include/asm/processor.h b/arch/nios2/include/asm/processor.h index ecc7dc9a5a421..b7ab1a046a6d0 100644 --- a/arch/nios2/include/asm/processor.h +++ b/arch/nios2/include/asm/processor.h @@ -111,8 +111,6 @@ extern unsigned long get_wchan(struct task_struct *p); /* Prepare to copy thread state - unlazy all lazy status */ #define prepare_to_copy(tsk) do { } while (0) -extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); - #define task_pt_regs(p) \ ((struct pt_regs *)(THREAD_SIZE + task_stack_page(p)) - 1) diff --git a/arch/nios2/include/asm/ptrace.h b/arch/nios2/include/asm/ptrace.h index 50498e9a816d5..92969bf7902d1 100644 --- a/arch/nios2/include/asm/ptrace.h +++ b/arch/nios2/include/asm/ptrace.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Altera Corporation * Copyright (C) 2010 Tobias Klauser * Copyright (C) 2004 Microtronix Datacom Ltd * @@ -36,7 +37,11 @@ #define instruction_pointer(regs) ((regs)->ra) #define profile_pc(regs) instruction_pointer(regs) +#define user_stack_pointer(regs) ((regs)->sp) extern void show_regs(struct pt_regs *); +#define current_pt_regs() \ + ((struct pt_regs *)((unsigned long)current_thread_info() + THREAD_SIZE)\ + - 1) #endif /* __ASSEMBLY__ */ #endif /* _ASM_NIOS2_PTRACE_H */ diff --git a/arch/nios2/include/asm/signal.h b/arch/nios2/include/asm/signal.h index e0c9af0d286a7..953fdebd8f87f 100644 --- a/arch/nios2/include/asm/signal.h +++ b/arch/nios2/include/asm/signal.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Altera Corporation * Copyright (C) 2004, Microtronix Datacom Ltd. * * All rights reserved. @@ -61,6 +62,5 @@ struct k_sigaction { #include #undef __HAVE_ARCH_SIG_BITOPS -#define ptrace_signal_deliver(regs, cookie) do { } while (0) #endif /* _ASM_NIOS2_SIGNAL_H */ diff --git a/arch/nios2/include/asm/unistd.h b/arch/nios2/include/asm/unistd.h index 921a3f09cd7e3..cfff20f375721 100644 --- a/arch/nios2/include/asm/unistd.h +++ b/arch/nios2/include/asm/unistd.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Altera Corporation * Copyright (C) 2010 Tobias Klauser * Copyright (C) 2010 Frans Meulenbroeks * Copyright (C) 2004 Microtronix Datacom Ltd @@ -35,7 +36,8 @@ #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION - +#define __ARCH_WANT_SYS_VFORK +#define __ARCH_WANT_SYS_FORK /* * "Conditional" syscalls * diff --git a/arch/nios2/include/uapi/asm/unistd.h b/arch/nios2/include/uapi/asm/unistd.h index 4837efafe44ba..4c2ec55ab0203 100644 --- a/arch/nios2/include/uapi/asm/unistd.h +++ b/arch/nios2/include/uapi/asm/unistd.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Altera Corporation * Copyright (C) 2010 Tobias Klauser * Copyright (C) 2010 Frans Meulenbroeks * Copyright (C) 2004 Microtronix Datacom Ltd @@ -364,7 +365,8 @@ #define __NR_process_vm_readv 345 #define __NR_process_vm_writev 346 #define __NR_kcmp 347 +#define __NR_finit_module 348 -#define NR_syscalls 348 +#define NR_syscalls 349 #endif /* _UAPI_ASM_NIOS2_UNISTD_H */ diff --git a/arch/nios2/kernel/entry-nommu.S b/arch/nios2/kernel/entry-nommu.S index 08096fae4d274..33ee390e1af9e 100644 --- a/arch/nios2/kernel/entry-nommu.S +++ b/arch/nios2/kernel/entry-nommu.S @@ -252,29 +252,26 @@ ENTRY(ret_from_fork) call schedule_tail br ret_from_exception -ENTRY(sys_fork) - mov r4, sp - SAVE_SWITCH_STACK - call nios2_vfork - RESTORE_SWITCH_STACK - ret +ENTRY(ret_from_kernel_thread) + call schedule_tail + mov r4,r17 /* arg */ + callr r16 /* function */ + br ret_from_exception -ENTRY(sys_vfork) - mov r4, sp +ENTRY(__sys_fork) SAVE_SWITCH_STACK - call nios2_vfork + call sys_fork RESTORE_SWITCH_STACK ret -ENTRY(sys_execve) - mov r4, sp +ENTRY(__sys_vfork) SAVE_SWITCH_STACK - call nios2_execve + call sys_vfork RESTORE_SWITCH_STACK ret -ENTRY(sys_clone) - mov r4, sp +ENTRY(__sys_clone) + mov r4,sp SAVE_SWITCH_STACK call nios2_clone RESTORE_SWITCH_STACK diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S index ed049cc0bdbc0..a769cafd2b082 100644 --- a/arch/nios2/kernel/entry.S +++ b/arch/nios2/kernel/entry.S @@ -1,8 +1,8 @@ /* * linux/arch/nios2/kernel/entry.S * - * Copyright (C) 2009, Wind River Systems Inc * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2009, Wind River Systems Inc * * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com * @@ -436,29 +436,20 @@ fault: *********************************************************************** */ -ENTRY(sys_fork) - mov r4, sp +ENTRY(__sys_fork) SAVE_SWITCH_STACK - call nios2_fork + call sys_fork RESTORE_SWITCH_STACK ret -ENTRY(sys_vfork) - mov r4, sp +ENTRY(__sys_vfork) SAVE_SWITCH_STACK - call nios2_vfork + call sys_vfork RESTORE_SWITCH_STACK ret -ENTRY(sys_execve) - mov r4, sp - SAVE_SWITCH_STACK - call nios2_execve - RESTORE_SWITCH_STACK - ret - -ENTRY(sys_clone) - mov r4, sp +ENTRY(__sys_clone) + mov r4,sp SAVE_SWITCH_STACK call nios2_clone RESTORE_SWITCH_STACK @@ -581,4 +572,8 @@ ENTRY(ret_from_fork) call schedule_tail br ret_from_exception - +ENTRY(ret_from_kernel_thread) + call schedule_tail + mov r4,r17 /* arg */ + callr r16 /* function */ + br ret_from_exception diff --git a/arch/nios2/kernel/process.c b/arch/nios2/kernel/process.c index 3c0cd7c9de0ea..93489b16258ba 100644 --- a/arch/nios2/kernel/process.c +++ b/arch/nios2/kernel/process.c @@ -28,6 +28,7 @@ #include asmlinkage void ret_from_fork(void); +asmlinkage void ret_from_kernel_thread(void); void (*pm_power_off)(void) = NULL; EXPORT_SYMBOL(pm_power_off); @@ -126,150 +127,60 @@ void show_regs(struct pt_regs *regs) #endif } -#ifdef CONFIG_MMU -static void kernel_thread_helper(void *arg, int (*fn)(void *)) -{ - do_exit(fn(arg)); -} -#endif - -/* - * Create a kernel thread - */ -int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) -{ -#ifdef CONFIG_MMU - struct pt_regs regs; - - memset(®s, 0, sizeof(regs)); - regs.r4 = (unsigned long) arg; - regs.r5 = (unsigned long) fn; - regs.ea = (unsigned long) kernel_thread_helper; - regs.estatus = STATUS_PIE; - - return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, - NULL, NULL); -#else /* !CONFIG_MMU */ - long retval; - long clone_arg = flags | CLONE_VM; - mm_segment_t fs; - - fs = get_fs(); - set_fs(KERNEL_DS); - - __asm__ __volatile( - "movi r2,%6\n\t" /* TRAP_ID_SYSCALL */ - "movi r3,%1\n\t" /* __NR_clone */ - "mov r4,%5\n\t" /* (clone_arg */ - /* (flags | CLONE_VM)) */ - "movia r5,-1\n\t" /* usp: -1 */ - "trap\n\t" /* sys_clone */ - "\n\t" - "cmpeq r4,r3,zero\n\t" /* 2nd return value in r3 */ - "bne r4,zero,1f\n\t" /* 0: parent, just return. */ - /* See copy_thread, called */ - /* by do_fork, called by */ - /* nios2_clone, called by */ - /* sys_clone, called by */ - /* syscall trap handler. */ - - "mov r4,%4\n\t" /* fn's parameter (arg) */ - "\n\t" - "callr %3\n\t" /* Call function (fn) */ - "\n\t" - "mov r4,r2\n\t" /* fn's rtn code//;dgt2;tmp;*/ - "movi r2,%6\n\t" /* TRAP_ID_SYSCALL */ - "movi r3,%2\n\t" /* __NR_exit */ - "trap\n\t" /* sys_exit() */ - - /* Not reached by child */ - "1:\n\t" - "mov %0,r2\n\t" /* error rtn code (retval) */ - - : "=r" (retval) /* %0 */ - - : "i" (__NR_clone) /* %1 */ - , "i" (__NR_exit) /* %2 */ - , "r" (fn) /* %3 */ - , "r" (arg) /* %4 */ - , "r" (clone_arg) /* %5 (flags | CLONE_VM) */ - , "i" (TRAP_ID_SYSCALL) /* %6 */ - - : "r2", "r3", "r4", "r5", "ra"/* Clobbered */ - ); - - set_fs(fs); - return retval; -#endif /* CONFIG_MMU */ -} -EXPORT_SYMBOL(kernel_thread); - void flush_thread(void) { set_fs(USER_DS); } int copy_thread(unsigned long clone_flags, - unsigned long usp, unsigned long topstk, - struct task_struct *p, struct pt_regs *regs) + unsigned long usp, unsigned long arg, struct task_struct *p) { - struct pt_regs *childregs; - struct switch_stack *childstack, *stack; - - childregs = task_pt_regs(p); - - /* Save pointer to registers in thread_struct */ - p->thread.kregs = childregs; + struct pt_regs *childregs = task_pt_regs(p); + struct pt_regs *regs; + struct switch_stack *stack; + struct switch_stack *childstack = + ((struct switch_stack *)childregs) - 1; + + if (unlikely(p->flags & PF_KTHREAD)) { + memset(childstack, 0, + sizeof(struct switch_stack) + sizeof(struct pt_regs)); + + childstack->r16 = usp; /* fn */ + childstack->r17 = arg; + childstack->ra = (unsigned long) ret_from_kernel_thread; + childregs->estatus = STATUS_PIE; +#ifdef CONFIG_MMU + childregs->sp = (unsigned long) childstack; +#else + p->thread.kregs->sp = (unsigned long) childstack; +#endif + p->thread.ksp = (unsigned long) childstack; + p->thread.kregs = childregs; + return 0; + } - /* Copy registers */ + regs = current_pt_regs(); *childregs = *regs; -#ifndef CONFIG_MMU - childregs->r2 = 0; /* Redundant? See return values below */ -#endif + childregs->r2 = 0; /* Set the return value for the child. */ + childregs->r7 = 0; - /* Copy stacktop and copy the top entrys from parent to child */ stack = ((struct switch_stack *) regs) - 1; - childstack = ((struct switch_stack *) childregs) - 1; *childstack = *stack; - childstack->ra = (unsigned long) ret_from_fork; + childstack->ra = (unsigned long)ret_from_fork; + p->thread.kregs = childregs; + p->thread.ksp = (unsigned long) childstack; #ifdef CONFIG_MMU - if (childregs->estatus & ESTATUS_EU) + if (usp) childregs->sp = usp; - else - childregs->sp = (unsigned long) childstack; -#else - if (usp == -1) - p->thread.kregs->sp = (unsigned long) childstack; - else - p->thread.kregs->sp = usp; -#endif /* CONFIG_MMU */ - - /* Store the kernel stack in thread_struct */ - p->thread.ksp = (unsigned long) childstack; -#ifdef CONFIG_MMU /* Initialize tls register. */ if (clone_flags & CLONE_SETTLS) childstack->r23 = regs->r7; -#endif - - /* Set the return value for the child. */ - childregs->r2 = 0; -#ifdef CONFIG_MMU - childregs->r7 = 0; #else - childregs->r3 = 1; /* kernel_thread parent test */ -#endif - - /* Set the return value for the parent. */ - regs->r2 = p->pid; -#ifdef CONFIG_MMU - regs->r7 = 0; /* No error */ -#else - regs->r3 = 0; /* kernel_thread parent test */ + if (usp) + p->thread.kregs->sp = usp; #endif - return 0; } diff --git a/arch/nios2/kernel/sys_nios2.c b/arch/nios2/kernel/sys_nios2.c index adf3af4d3a5a4..364c6e3e901ea 100644 --- a/arch/nios2/kernel/sys_nios2.c +++ b/arch/nios2/kernel/sys_nios2.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Altera Corporation * Copyright (C) 2011-2012 Tobias Klauser * Copyright (C) 2004 Microtronix Datacom Ltd. * @@ -16,21 +17,12 @@ #include #include -asmlinkage int nios2_fork(struct pt_regs *regs) -{ -#ifdef CONFIG_MMU - return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL); -#else - return -EINVAL; -#endif -} - -asmlinkage int nios2_vfork(struct pt_regs *regs) -{ - return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, - regs, 0, NULL, NULL); -} +/* We don't want to use generic sys_clone because Nios II passes all arguments + * on stack. And we need to save all these registers, which means we need + * push all these registers on top of pt_regs. So, it is better to pass in + * pt_regs* and extract the arguments for do_fork() from here. + */ asmlinkage int nios2_clone(struct pt_regs *regs) { unsigned long flags; @@ -49,24 +41,7 @@ asmlinkage int nios2_clone(struct pt_regs *regs) child_tidptr = NULL; #endif - return do_fork(flags, newsp, regs, 0, parent_tidptr, child_tidptr); -} - -asmlinkage int nios2_execve(struct pt_regs *regs) -{ - int error; - struct filename *filename; - - filename = getname((char *) regs->r4); - error = PTR_ERR(filename); - if (IS_ERR(filename)) - return error; - error = do_execve(filename->name, - (const char __user *const __user *) regs->r5, - (const char __user *const __user *) regs->r6, - regs); - putname(filename); - return error; + return do_fork(flags, newsp, 0, parent_tidptr, child_tidptr); } asmlinkage long sys_mmap(unsigned long addr, unsigned long len, diff --git a/arch/nios2/kernel/syscalltable.S b/arch/nios2/kernel/syscalltable.S index e2d29cc729854..695944a3c5f26 100644 --- a/arch/nios2/kernel/syscalltable.S +++ b/arch/nios2/kernel/syscalltable.S @@ -22,7 +22,7 @@ ALIGN ENTRY(sys_call_table) .long sys_restart_syscall /* 0 */ .long sys_exit - .long sys_fork + .long __sys_fork .long sys_read .long sys_write .long sys_open /* 5 */ @@ -145,7 +145,7 @@ ENTRY(sys_call_table) .long sys_ipc .long sys_fsync .long sys_sigreturn - .long sys_clone /* 120 */ + .long __sys_clone /* 120 */ .long sys_setdomainname .long sys_newuname .long sys_cacheflush /* modify_ldt for i386 */ @@ -215,7 +215,7 @@ ENTRY(sys_call_table) .long sys_sendfile .long sys_ni_syscall /* old sys_getpmsg (streams1) */ .long sys_ni_syscall /* old sys_putpmsg (streams2) */ - .long sys_vfork /* 190 */ + .long __sys_vfork /* 190 */ .long sys_getrlimit .long sys_mmap_pgoff .long sys_truncate64 @@ -373,7 +373,8 @@ ENTRY(sys_call_table) .long sys_process_vm_readv /* 345 */ .long sys_process_vm_writev .long sys_kcmp + .long sys_finit_module - .rept NR_syscalls - 348 + .rept NR_syscalls - 349 .long sys_ni_syscall .endr From 89aa0d271e5c161a34ea9193e8931f5369ca957f Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 28 May 2013 11:02:48 +0800 Subject: [PATCH 009/201] FogBugz #125829: arch/nios2: Upgrade to v3.9 -Switch to generic sigalstack -Switch to generic old sigsuspend -Switch to generic old sigaction Signed-off-by: Ley Foon Tan --- arch/nios2/Kconfig | 2 + arch/nios2/include/asm/elf.h | 3 - arch/nios2/include/asm/signal.h | 18 +----- arch/nios2/include/asm/unistd.h | 1 - arch/nios2/kernel/entry-nommu.S | 23 ------- arch/nios2/kernel/entry.S | 23 ------- arch/nios2/kernel/kgdb.c | 2 +- arch/nios2/kernel/signal.c | 104 ++------------------------------ 8 files changed, 8 insertions(+), 168 deletions(-) diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index 3521a679dd9b9..b7454151c244b 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -14,6 +14,8 @@ config NIOS2 select MODULES_USE_ELF_RELA select IRQ_DOMAIN select SOC_BUS + select OLD_SIGACTION + select OLD_SIGSUSPEND config GENERIC_CSUM def_bool y diff --git a/arch/nios2/include/asm/elf.h b/arch/nios2/include/asm/elf.h index e3bf48a10935f..abc7ca1724f5a 100644 --- a/arch/nios2/include/asm/elf.h +++ b/arch/nios2/include/asm/elf.h @@ -132,7 +132,4 @@ #define ELF_PLATFORM (NULL) -#define SET_PERSONALITY(ex) \ - set_personality(PER_LINUX_32BIT | (current->personality & (~PER_MASK))) - #endif /* _ASM_NIOS2_ELF_H */ diff --git a/arch/nios2/include/asm/signal.h b/arch/nios2/include/asm/signal.h index 953fdebd8f87f..e2f8deedda2b1 100644 --- a/arch/nios2/include/asm/signal.h +++ b/arch/nios2/include/asm/signal.h @@ -41,23 +41,7 @@ typedef struct { #include -struct old_sigaction { - __sighandler_t sa_handler; - old_sigset_t sa_mask; - unsigned long sa_flags; - void (*sa_restorer)(void); -}; - -struct sigaction { - __sighandler_t sa_handler; - unsigned long sa_flags; - void (*sa_restorer)(void); - sigset_t sa_mask; /* mask last for extensibility */ -}; - -struct k_sigaction { - struct sigaction sa; -}; +#define __ARCH_HAS_SA_RESTORER #include #undef __HAVE_ARCH_SIG_BITOPS diff --git a/arch/nios2/include/asm/unistd.h b/arch/nios2/include/asm/unistd.h index cfff20f375721..e9e8d9b2692bc 100644 --- a/arch/nios2/include/asm/unistd.h +++ b/arch/nios2/include/asm/unistd.h @@ -35,7 +35,6 @@ #define __ARCH_WANT_SYS_OLD_UNAME #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_FORK /* diff --git a/arch/nios2/kernel/entry-nommu.S b/arch/nios2/kernel/entry-nommu.S index 33ee390e1af9e..60c8cb1623d39 100644 --- a/arch/nios2/kernel/entry-nommu.S +++ b/arch/nios2/kernel/entry-nommu.S @@ -277,20 +277,6 @@ ENTRY(__sys_clone) RESTORE_SWITCH_STACK ret -ENTRY(sys_sigsuspend) - mov r4, sp - SAVE_SWITCH_STACK - call do_sigsuspend - RESTORE_SWITCH_STACK - ret - -ENTRY(sys_rt_sigsuspend) - mov r4, sp - SAVE_SWITCH_STACK - call do_rt_sigsuspend - RESTORE_SWITCH_STACK - ret - ENTRY(sys_sigreturn) mov r4, sp SAVE_SWITCH_STACK @@ -298,15 +284,6 @@ ENTRY(sys_sigreturn) RESTORE_SWITCH_STACK ret -ENTRY(sys_sigaltstack) - ldw r4, PT_R4(sp) - ldw r5, PT_R5(sp) - ldw r6, PT_SP(sp) - SAVE_SWITCH_STACK - call do_sigaltstack - RESTORE_SWITCH_STACK - ret - ENTRY(sys_rt_sigreturn) SAVE_SWITCH_STACK mov r4, sp diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S index a769cafd2b082..d3ebcda4fd0a3 100644 --- a/arch/nios2/kernel/entry.S +++ b/arch/nios2/kernel/entry.S @@ -455,20 +455,6 @@ ENTRY(__sys_clone) RESTORE_SWITCH_STACK ret -ENTRY(sys_sigsuspend) - mov r4, sp - SAVE_SWITCH_STACK - call do_sigsuspend - RESTORE_SWITCH_STACK - ret - -ENTRY(sys_rt_sigsuspend) - mov r4, sp - SAVE_SWITCH_STACK - call do_rt_sigsuspend - RESTORE_SWITCH_STACK - ret - ENTRY(sys_sigreturn) mov r4, sp SAVE_SWITCH_STACK @@ -477,15 +463,6 @@ ENTRY(sys_sigreturn) addi ra, ra, (end_translate_rc_and_ret - translate_rc_and_ret) ret -ENTRY(sys_sigaltstack) - ldw r4, PT_R4(sp) - ldw r5, PT_R5(sp) - ldw r6, PT_SP(sp) - SAVE_SWITCH_STACK - call do_sigaltstack - RESTORE_SWITCH_STACK - ret - ENTRY(sys_rt_sigreturn) SAVE_SWITCH_STACK mov r4, sp diff --git a/arch/nios2/kernel/kgdb.c b/arch/nios2/kernel/kgdb.c index d26573c5914b9..b506d45f55a2a 100644 --- a/arch/nios2/kernel/kgdb.c +++ b/arch/nios2/kernel/kgdb.c @@ -20,7 +20,7 @@ * along with this program. If not, see . * */ - +#include #include #include #include diff --git a/arch/nios2/kernel/signal.c b/arch/nios2/kernel/signal.c index 5ec1298fe296a..f66240637dac5 100644 --- a/arch/nios2/kernel/signal.c +++ b/arch/nios2/kernel/signal.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Altera Corporation * Copyright (C) 2011-2012 Tobias Klauser * Copyright (C) 2004 Microtronix Datacom Ltd * Copyright (C) 1991, 1992 Linus Torvalds @@ -25,100 +26,6 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall); -/* - * Atomically swap in the new signal mask, and wait for a signal. - */ -asmlinkage int do_sigsuspend(struct pt_regs *regs) -{ - old_sigset_t mask = regs->r4; /* Verify correct syscall reg */ - sigset_t saveset; - - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - -#ifdef CONFIG_MMU - regs->r2 = EINTR; - regs->r7 = 1; -#else - regs->r2 = -EINTR; -#endif - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(regs, &saveset, 0)) - return -EINTR; - } -} - -asmlinkage int do_rt_sigsuspend(struct pt_regs *regs) -{ - sigset_t *unewset = (sigset_t *)regs->r4; - size_t sigsetsize = (size_t)regs->r5; - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - 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); - -#ifdef CONFIG_MMU - regs->r2 = EINTR; - regs->r7 = 1; -#else - regs->r2 = -EINTR; -#endif - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - if (do_signal(regs, &saveset, 0)) - return -EINTR; - } -} - -asmlinkage int sys_sigaction(int sig, const struct old_sigaction *act, - struct old_sigaction *oact) -{ - struct k_sigaction new_ka, old_ka; - int ret; - - if (act) { - old_sigset_t mask; - if (!access_ok(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 (!access_ok(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; -} - /* * Do a signal return; undo the signal stack. * @@ -227,7 +134,8 @@ static inline int rt_restore_ucontext(struct pt_regs *regs, regs->estatus = (regs->estatus & 0xffffffff); regs->orig_r2 = -1; /* disable syscall checks */ - if (do_sigaltstack(&uc->uc_stack, NULL, regs->sp) == -EFAULT) + err |= restore_altstack(&uc->uc_stack); + if (err) goto badframe; *pr2 = regs->r2; @@ -447,11 +355,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); - err |= __put_user((void *)current->sas_ss_sp, - &frame->uc.uc_stack.ss_sp); - err |= __put_user(sas_ss_flags(regs->sp), - &frame->uc.uc_stack.ss_flags); - err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= __save_altstack(&frame->uc.uc_stack, regs->sp); err |= rt_setup_ucontext(&frame->uc, regs); err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); From c0cb72b9636b20a60bb920781f758a379cdd4abe Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 29 May 2013 13:23:18 +0800 Subject: [PATCH 010/201] FogBugz #126924: arch/nios2: Add generic kvm_para.h This header file is required when enable CONFIG_LOCKUP_DETECTOR. Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/Kbuild | 1 + arch/nios2/include/uapi/asm/Kbuild | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild index 0f6bb34077e2d..f9aff08bcb223 100644 --- a/arch/nios2/include/asm/Kbuild +++ b/arch/nios2/include/asm/Kbuild @@ -27,6 +27,7 @@ generic-y += ipcbuf.h generic-y += irq_regs.h generic-y += kdebug.h generic-y += kmap_types.h +generic-y += kvm_para.h generic-y += local.h generic-y += mman.h generic-y += module.h diff --git a/arch/nios2/include/uapi/asm/Kbuild b/arch/nios2/include/uapi/asm/Kbuild index e678394e227f8..f7cb6faea19ae 100644 --- a/arch/nios2/include/uapi/asm/Kbuild +++ b/arch/nios2/include/uapi/asm/Kbuild @@ -4,6 +4,7 @@ header-y += byteorder.h header-y += elf.h header-y += fcntl.h header-y += ioctls.h +header-y += kvm_para.h header-y += poll.h header-y += posix_types.h header-y += processor.h From 838d8d794b8d134440e63a4d09f345bc97655d2c Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 6 Jun 2013 13:29:06 +0800 Subject: [PATCH 011/201] FogBugz #128540: arch/nios2: Change timer driver to use of_iomap() Change from ioremap() to device-tree-compatible function of_iomap(). Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/time.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c index 482b2dff2f279..5ad6255039804 100644 --- a/arch/nios2/kernel/time.c +++ b/arch/nios2/kernel/time.c @@ -36,7 +36,7 @@ #define ALTERA_TIMER_CONTROL_STOP_MSK (0x8) static u32 nios2_timer_count; -static u32 timer_membase; +static void __iomem *timer_membase; static u32 timer_freq; static inline unsigned long read_timersnapshot(void) @@ -114,9 +114,9 @@ void __init nios2_late_time_init(void) BUG_ON(!timer); - timer_membase = of_translate_address(timer, of_get_address(timer, 0, - NULL, NULL)); - timer_membase = (u32) ioremap(timer_membase, PAGE_SIZE); + timer_membase = of_iomap(timer, 0); + if (WARN_ON(!timer_membase)) + return; if (of_property_read_u32(timer, "clock-frequency", &timer_freq)) { pr_err("Can't get timer clock-frequency from device tree\n"); From 6654c8d965b12c2e1c26c7d79f8f9153abddbaa4 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 6 Jun 2013 13:29:35 +0800 Subject: [PATCH 012/201] FogBugz #128549: arch/nios2: Include asm-generic/io.h Include asm-generic/io.h in arch/nios2/include/asm/io.h and remove duplicated macros from io.h. Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/io.h | 98 +++++++------------------------------ 1 file changed, 19 insertions(+), 79 deletions(-) diff --git a/arch/nios2/include/asm/io.h b/arch/nios2/include/asm/io.h index 2e01ee6572a0f..b46cde2917179 100644 --- a/arch/nios2/include/asm/io.h +++ b/arch/nios2/include/asm/io.h @@ -12,30 +12,7 @@ #include -#define readb(addr) \ - ({ unsigned char __v = (*(volatile unsigned char *)(addr)); __v; }) -#define readw(addr) \ - ({ unsigned short __v = (*(volatile unsigned short *)(addr)); __v; }) -#define readl(addr) \ - ({ unsigned int __v = (*(volatile unsigned int *)(addr)); __v; }) - -#define readb_relaxed(addr) readb(addr) -#define readw_relaxed(addr) readw(addr) -#define readl_relaxed(addr) readl(addr) - -#define writeb(b, addr) \ - (void)((*(volatile unsigned char *)(addr)) = (b)) -#define writew(b, addr) \ - (void)((*(volatile unsigned short *)(addr)) = (b)) -#define writel(b, addr) \ - (void)((*(volatile unsigned int *)(addr)) = (b)) - -#define __raw_readb readb -#define __raw_readw readw -#define __raw_readl readl -#define __raw_writeb writeb -#define __raw_writew writew -#define __raw_writel writel +#define IO_SPACE_LIMIT 0xffffffff #ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE # define __IO_USE_DUFFS @@ -115,21 +92,21 @@ #endif /* __IO_USE_DUFFS */ -static inline void io_outsb(unsigned int addr, void *buf, int len) +static inline void io_outsb(unsigned int addr, const void *buf, int len) { volatile unsigned char *ap = (volatile unsigned char *)addr; unsigned char *bp = (unsigned char *)buf; __IO_OUT_LOOP(ap, bp, len); } -static inline void io_outsw(unsigned int addr, void *buf, int len) +static inline void io_outsw(unsigned int addr, const void *buf, int len) { volatile unsigned short *ap = (volatile unsigned short *)addr; unsigned short *bp = (unsigned short *)buf; __IO_OUT_LOOP(ap, bp, len); } -static inline void io_outsl(unsigned int addr, void *buf, int len) +static inline void io_outsl(unsigned int addr, const void *buf, int len) { volatile unsigned int *ap = (volatile unsigned int *)addr; unsigned int *bp = (unsigned int *)buf; @@ -161,17 +138,24 @@ static inline void io_insl(unsigned int addr, void *buf, int len) #undef __IO_IN_LOOP #undef __IO_USE_DUFFS -#define mmiowb() +#define readb_relaxed(addr) readb(addr) +#define readw_relaxed(addr) readw(addr) +#define readl_relaxed(addr) readl(addr) + +#define outsb(a, b, l) io_outsb(a, b, l) +#define outsw(a, b, l) io_outsw(a, b, l) +#define outsl(a, b, l) io_outsl(a, b, l) + +#define insb(a, b, l) io_insb(a, b, l) +#define insw(a, b, l) io_insw(a, b, l) +#define insl(a, b, l) io_insl(a, b, l) + +#include /* * make the short names macros so specific devices * can override them as required */ - -#define memset_io(a, b, c) memset((void *)(a), (b), (c)) -#define memcpy_fromio(a, b, c) memcpy((a), (void *)(b), (c)) -#define memcpy_toio(a, b, c) memcpy((void *)(a), (b), (c)) - #define inb(addr) readb(addr) #define inw(addr) readw(addr) #define inl(addr) readl(addr) @@ -179,35 +163,6 @@ static inline void io_insl(unsigned int addr, void *buf, int len) #define outw(x, addr) ((void) writew(x, addr)) #define outl(x, addr) ((void) writel(x, addr)) -#define inb_p(addr) inb(addr) -#define inw_p(addr) inw(addr) -#define inl_p(addr) inl(addr) -#define outb_p(x, addr) outb(x, addr) -#define outw_p(x, addr) outw(x, addr) -#define outl_p(x, addr) outl(x, addr) - -#define outsb(a, b, l) io_outsb(a, b, l) -#define outsw(a, b, l) io_outsw(a, b, l) -#define outsl(a, b, l) io_outsl(a, b, l) - -#define insb(a, b, l) io_insb(a, b, l) -#define insw(a, b, l) io_insw(a, b, l) -#define insl(a, b, l) io_insl(a, b, l) - -#define ioread8_rep(a, d, c) insb(a, d, c) -#define ioread16_rep(a, d, c) insw(a, d, c) -#define ioread32_rep(a, d, c) insl(a, d, c) -#define iowrite8_rep(a, s, c) outsb(a, s, c) -#define iowrite16_rep(a, s, c) outsw(a, s, c) -#define iowrite32_rep(a , s, c) outsl(a, s, c) - -#define ioread8(X) readb(X) -#define ioread16(X) readw(X) -#define ioread32(X) readl(X) -#define iowrite8(val, X) writeb(val, X) -#define iowrite16(val, X) writew(val, X) -#define iowrite32(val, X) writel(val, X) - #ifdef CONFIG_MMU extern void __iomem *__ioremap(unsigned long physaddr, unsigned long size, @@ -220,12 +175,10 @@ static inline void __iomem *__ioremap(unsigned long physaddr, unsigned long size, unsigned long cacheflag) { - if (cacheflag & _PAGE_CACHED) { + if (cacheflag & _PAGE_CACHED) return (void __iomem *)(physaddr & ~CONFIG_IO_REGION_BASE); - } else { -/* flush_dcache_range(physaddr, physaddr + size); */ + else return (void __iomem *)(physaddr | CONFIG_IO_REGION_BASE); - } } #define __iounmap(addr) do {} while (0) @@ -260,8 +213,6 @@ static inline void iounmap(void __iomem *addr) __iounmap(addr); } -#define IO_SPACE_LIMIT 0xffffffff - /* Pages to physical address... */ #ifdef CONFIG_MMU # define page_to_phys(page) virt_to_phys(page_to_virt(page)) @@ -288,17 +239,6 @@ static inline void iounmap(void __iomem *addr) #define ioport_map(port, nr) ioremap(port, nr) #define ioport_unmap(port) iounmap(port) -/* - * Convert a physical pointer to a virtual kernel pointer for /dev/mem - * access - */ -#define xlate_dev_mem_ptr(p) __va(p) - -/* - * Convert a virtual cached pointer to an uncached pointer - */ -#define xlate_dev_kmem_ptr(p) p - /* Macros used for smc91x.c driver */ #define readsb(p, d, l) insb(p, d, l) #define readsw(p, d, l) insw(p, d, l) From 26ee874ad18229d24965975af4056b33cb897906 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 6 Aug 2013 17:30:11 +0800 Subject: [PATCH 013/201] FogBugz #143817: arch/nios2: Update 3c120 default dts and config config: CONFIG_PROC_DEVICETREE=y CONFIG_GPIO_SYSFS=y DTS from latest DTG. Updated PIO, reset-addr and removed unused compatible string. Signed-off-by: Ley Foon Tan --- arch/nios2/boot/dts/3c120_devboard.dts | 32 +++++++++++++++----------- arch/nios2/configs/3c120_defconfig | 3 ++- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/arch/nios2/boot/dts/3c120_devboard.dts b/arch/nios2/boot/dts/3c120_devboard.dts index 5e29c3a086f00..53d17a2d12d68 100644 --- a/arch/nios2/boot/dts/3c120_devboard.dts +++ b/arch/nios2/boot/dts/3c120_devboard.dts @@ -31,7 +31,7 @@ cpu: cpu@0x0 { device_type = "cpu"; - compatible = "ALTR,nios2-13.0", "ALTR,nios2-1.0"; + compatible = "ALTR,nios2-1.0"; reg = < 0x00000000 >; interrupt-controller; #interrupt-cells = < 1 >; @@ -45,11 +45,12 @@ ALTR,tlb-num-ways = < 16 >; ALTR,tlb-num-entries = < 128 >; ALTR,tlb-ptr-sz = < 7 >; - ALTR,has-div; - ALTR,has-mul; - ALTR,reset-addr = < 0xc0000000 >; + ALTR,has-div = < 1 >; + ALTR,has-mul = < 1 >; + ALTR,reset-addr = < 0xc2800000 >; ALTR,fast-tlb-miss-addr = < 0xc7fff400 >; ALTR,exception-addr = < 0xd0000020 >; + ALTR,has-initda = < 1 >; }; //end cpu@0x0 (cpu) }; //end cpus @@ -68,7 +69,7 @@ bus-frequency = < 125000000 >; pb_cpu_to_io: bridge@0x8000000 { - compatible = "ALTR,avalon-13.0", "simple-bus"; + compatible = "simple-bus"; reg = < 0x08000000 0x00800000 >; #address-cells = < 1 >; #size-cells = < 1 >; @@ -85,7 +86,7 @@ 0x00004D00 0x08004D00 0x00000010 >; timer_1ms: timer@0x400000 { - compatible = "ALTR,timer-13.0.1", "ALTR,timer-1.0"; + compatible = "ALTR,timer-1.0"; reg = < 0x00400000 0x00000020 >; interrupt-parent = < &cpu >; interrupts = < 11 >; @@ -93,25 +94,26 @@ }; //end timer@0x400000 (timer_1ms) sysid: sysid@0x4d40 { - compatible = "ALTR,sysid-13.0", "ALTR,sysid-1.0"; + compatible = "ALTR,sysid-1.0"; reg = < 0x00004D40 0x00000008 >; id = < 0 >; timestamp = < 1364882880 >; }; //end sysid@0x4d40 (sysid) jtag_uart: serial@0x4d50 { - compatible = "ALTR,juart-13.0.1", "ALTR,juart-1.0"; + compatible = "ALTR,juart-1.0"; reg = < 0x00004D50 0x00000008 >; interrupt-parent = < &cpu >; interrupts = < 1 >; }; //end serial@0x4d50 (jtag_uart) tse_mac: ethernet@0x4000 { - compatible = "ALTR,tse-13.0", "ALTR,tse-1.0"; + compatible = "ALTR,tse-1.0"; reg = < 0x00004000 0x00000400 0x00004400 0x00000040 0x00004800 0x00000040 0x00002000 0x00002000 >; + reg-names = "control_port", "rx_csr", "tx_csr", "s1"; interrupt-parent = < &cpu >; interrupts = < 2 3 >; ALTR,rx-fifo-depth = < 2048 >; @@ -124,7 +126,7 @@ }; //end ethernet@0x4000 (tse_mac) uart: serial@0x4c80 { - compatible = "ALTR,uart-13.0.1", "ALTR,uart-1.0"; + compatible = "ALTR,uart-1.0"; reg = < 0x00004C80 0x00000020 >; interrupt-parent = < &cpu >; interrupts = < 10 >; @@ -133,7 +135,7 @@ }; //end serial@0x4c80 (uart) user_led_pio_8out: gpio@0x4cc0 { - compatible = "ALTR,pio-13.0.1", "ALTR,pio-1.0"; + compatible = "ALTR,pio-1.0"; reg = < 0x00004CC0 0x00000010 >; width = < 8 >; resetvalue = < 255 >; @@ -142,24 +144,26 @@ }; //end gpio@0x4cc0 (user_led_pio_8out) user_dipsw_pio_8in: gpio@0x4ce0 { - compatible = "ALTR,pio-13.0.1", "ALTR,pio-1.0"; + compatible = "ALTR,pio-1.0"; reg = < 0x00004CE0 0x00000010 >; interrupt-parent = < &cpu >; interrupts = < 8 >; width = < 8 >; resetvalue = < 0 >; + edge_type = < 2 >; level_trigger = < 0 >; #gpio-cells = < 2 >; gpio-controller; }; //end gpio@0x4ce0 (user_dipsw_pio_8in) user_pb_pio_4in: gpio@0x4d00 { - compatible = "ALTR,pio-13.0.1", "ALTR,pio-1.0"; + compatible = "ALTR,pio-1.0"; reg = < 0x00004D00 0x00000010 >; interrupt-parent = < &cpu >; interrupts = < 9 >; width = < 4 >; resetvalue = < 0 >; + edge_type = < 2 >; level_trigger = < 0 >; #gpio-cells = < 2 >; gpio-controller; @@ -167,7 +171,7 @@ }; //end bridge@0x8000000 (pb_cpu_to_io) cfi_flash_64m: flash@0x0 { - compatible = "ALTR,cfi_flash-13.0", "cfi-flash"; + compatible = "cfi-flash"; reg = < 0x00000000 0x04000000 >; bank-width = < 2 >; device-width = < 1 >; diff --git a/arch/nios2/configs/3c120_defconfig b/arch/nios2/configs/3c120_defconfig index 3f7aba567a135..52b514ff25bf5 100644 --- a/arch/nios2/configs/3c120_defconfig +++ b/arch/nios2/configs/3c120_defconfig @@ -1,4 +1,3 @@ -CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 @@ -44,6 +43,7 @@ CONFIG_MTD_CFI=y CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_PHYSMAP_OF=y +CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_LOOP=y CONFIG_NETDEVICES=y CONFIG_MII=y @@ -60,6 +60,7 @@ CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE=y CONFIG_SERIAL_ALTERA_UART=y # CONFIG_HW_RANDOM is not set CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y CONFIG_GPIO_ALTERA=y # CONFIG_HWMON is not set # CONFIG_USB_SUPPORT is not set From 3f44f2314b4bcc28e860122704fdee0959cead33 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 12 Aug 2013 11:10:00 +0800 Subject: [PATCH 014/201] FogBugz #143478: arch/nios2: Move sysid from arch to drivers Removed sysid from arch/arm and use default soc_id/revision. Signed-off-by: Ley Foon Tan --- arch/nios2/platform/platform.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/arch/nios2/platform/platform.c b/arch/nios2/platform/platform.c index 3cb4b7c20c0d8..5e663aa68019e 100644 --- a/arch/nios2/platform/platform.c +++ b/arch/nios2/platform/platform.c @@ -16,12 +16,9 @@ #include #include -#define NIOS2_SYSID_DEFAULT (0x1) +#define NIOS2_ID_DEFAULT (0x1) #define NIOS2_REVISION_DEFAULT (0x1) -/* Sysid register map */ -#define SYSID_ID_REG (0x0) - static struct of_device_id altera_of_bus_ids[] __initdata = { { .compatible = "simple-bus", }, { .compatible = "altr,avalon", }, @@ -31,12 +28,9 @@ static struct of_device_id altera_of_bus_ids[] __initdata = { static void __init nios2_soc_device_init(void) { struct device_node *root; - struct device_node *sysid_node; struct soc_device *soc_dev; struct soc_device_attribute *soc_dev_attr; - void __iomem *sysid_base; const char *machine; - u32 id = NIOS2_SYSID_DEFAULT; int err; root = of_find_node_by_path("/"); @@ -47,24 +41,13 @@ static void __init nios2_soc_device_init(void) if (err) return; + of_node_put(root); + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return; - sysid_node = of_find_compatible_node(root, NULL, "ALTR,sysid-1.0"); - if (sysid_node) { - sysid_base = of_iomap(sysid_node, 0); - if (sysid_base) { - /* Use id from Sysid hardware. */ - id = readl(sysid_base + SYSID_ID_REG); - iounmap(sysid_base); - } - of_node_put(sysid_node); - } - - of_node_put(root); - - soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", id); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", NIOS2_ID_DEFAULT); soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", NIOS2_REVISION_DEFAULT); soc_dev_attr->machine = kasprintf(GFP_KERNEL, "%s", machine); From 938ff8d5c5d0f25667c103302bebac7212c39df4 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 13 Aug 2013 15:39:05 +0800 Subject: [PATCH 015/201] FogBugz #145411: Upgrade Nios II kernel to v3.10 - Changed create_proc_entry to proc_create - Align arch_cpu_idle with kernel.org - Align free_init_pages from kernel.org - Removed dump_stack() - Removed cond_syscall() Signed-off-by: Ley Foon Tan --- arch/nios2/Kconfig | 2 - arch/nios2/include/asm/unistd.h | 7 ---- arch/nios2/kernel/early_printk.c | 5 ++- arch/nios2/kernel/misaligned.c | 63 ++++++++++++++++---------------- arch/nios2/kernel/process.c | 32 ++-------------- arch/nios2/kernel/setup.c | 2 +- arch/nios2/kernel/traps.c | 11 ------ arch/nios2/mm/init.c | 23 +----------- 8 files changed, 40 insertions(+), 105 deletions(-) diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index b7454151c244b..b119e38d1e594 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -29,8 +29,6 @@ config GENERIC_HWEIGHT config GENERIC_CALIBRATE_DELAY def_bool y -config GENERIC_GPIO - bool config NO_IOPORT def_bool y diff --git a/arch/nios2/include/asm/unistd.h b/arch/nios2/include/asm/unistd.h index e9e8d9b2692bc..7920cf3b08e58 100644 --- a/arch/nios2/include/asm/unistd.h +++ b/arch/nios2/include/asm/unistd.h @@ -37,12 +37,5 @@ #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_FORK -/* - * "Conditional" syscalls - * - * What we want is __attribute__((weak,alias("sys_ni_syscall"))), - * but it doesn't work on all toolchains, so we just do it by hand - */ -#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); #endif /* _ASM_NIOS2_UNISTD_H */ diff --git a/arch/nios2/kernel/early_printk.c b/arch/nios2/kernel/early_printk.c index af9eb6e0cf56f..1e06fb4e944e1 100644 --- a/arch/nios2/kernel/early_printk.c +++ b/arch/nios2/kernel/early_printk.c @@ -83,7 +83,7 @@ static void early_console_write(struct console *con, const char *s, unsigned n) selected #endif -static struct console early_console = { +static struct console early_console_prom = { .name = "early", .write = early_console_write, .flags = CON_PRINTBUFFER | CON_BOOT, @@ -110,6 +110,7 @@ void __init setup_early_printk(void) JUART_SET_CR(JUART_GET_CR() | ALTERA_JTAGUART_CONTROL_AC_MSK); #endif - register_console(&early_console); + early_console = &early_console_prom; + register_console(early_console); pr_info("early_console initialized at 0x%08lx\n", base_addr); } diff --git a/arch/nios2/kernel/misaligned.c b/arch/nios2/kernel/misaligned.c index ade28d2da4138..f8845ab72af03 100644 --- a/arch/nios2/kernel/misaligned.c +++ b/arch/nios2/kernel/misaligned.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -222,42 +223,34 @@ static const char * const usermode_action[] = { "ignored", /* 0 */ "warn", /* 1 */ "fixup", /* 2 */ - "fixup+warn", /* 3 */ + "fixup+warn", /* 3 */ "signal", /* 4 */ - "signal+warn", /* 5 */ + "signal+warn", /* 5 */ "signal+fixup", "signal+fixup+warn" }; -static int -proc_misaligned_read(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int misaligned_proc_show(struct seq_file *m, void *v) { - char *p = page; - int len; - - p += sprintf(p, "User:\t\t%lu\n", ma_user); - p += sprintf(p, "Kernel:\t\t%lu\n", ma_kern); - p += sprintf(p, "Skipped:\t%lu\n", ma_skipped); - p += sprintf(p, "Half:\t\t%lu\n", ma_half); - p += sprintf(p, "Word:\t\t%lu\n", ma_word); - p += sprintf(p, "User faults:\t%i (%s)\n", - ma_usermode, - usermode_action[ma_usermode & 7]); - - len = (p - page) - off; - if (len < 0) - len = 0; - - *eof = (len <= count) ? 1 : 0; - *start = page + off; - - return len; + seq_printf(m, "User:\t\t%lu\n", ma_user); + seq_printf(m, "Kernel:\t\t%lu\n", ma_kern); + seq_printf(m, "Skipped:\t%lu\n", ma_skipped); + seq_printf(m, "Half:\t\t%lu\n", ma_half); + seq_printf(m, "Word:\t\t%lu\n", ma_word); + seq_printf(m, "User faults:\t%i (%s)\n", ma_usermode, + usermode_action[ma_usermode & 7]); + + return 0; +} + +static int proc_misaligned_open(struct inode *inode, struct file *file) +{ + return single_open(file, misaligned_proc_show, NULL); } -static int -proc_misaligned_write(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t proc_misaligned_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos) { char mode; @@ -270,6 +263,14 @@ proc_misaligned_write(struct file *file, const char __user *buffer, return count; } + +static const struct file_operations misalign_fops = { + .open = proc_misaligned_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = proc_misaligned_write, +}; #endif /* CONFIG_PROC_FS */ static void __init misaligned_calc_reg_offsets(void) @@ -299,14 +300,12 @@ static void __init misaligned_calc_reg_offsets(void) static int __init misaligned_init(void) { #ifdef CONFIG_PROC_FS + struct proc_dir_entry *res; - res = create_proc_entry("misalign", S_IWUSR | S_IRUGO, NULL); + res = proc_create("misalign", S_IWUSR | S_IRUGO, NULL, &misalign_fops); if (!res) return -ENOMEM; - - res->read_proc = proc_misaligned_read; - res->write_proc = proc_misaligned_write; #endif /* default mode - silent fix */ diff --git a/arch/nios2/kernel/process.c b/arch/nios2/kernel/process.c index 93489b16258ba..f71d786c3c1d8 100644 --- a/arch/nios2/kernel/process.c +++ b/arch/nios2/kernel/process.c @@ -33,36 +33,9 @@ asmlinkage void ret_from_kernel_thread(void); void (*pm_power_off)(void) = NULL; EXPORT_SYMBOL(pm_power_off); -void default_idle(void) +void arch_cpu_idle(void) { - local_irq_disable(); - if (!need_resched()) { - local_irq_enable(); - __asm__("nop"); - } else - local_irq_enable(); -} - -void (*idle)(void) = default_idle; - -/* - * 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) -{ - while (1) { - tick_nohz_idle_enter(); - rcu_idle_enter(); - while (!need_resched()) - idle(); - rcu_idle_exit(); - tick_nohz_idle_exit(); - - schedule_preempt_disabled(); - } + local_irq_enable(); } /* @@ -104,6 +77,7 @@ void machine_power_off(void) void show_regs(struct pt_regs *regs) { pr_notice("\n"); + show_regs_print_info(KERN_DEFAULT); pr_notice("r1: %08lx r2: %08lx r3: %08lx r4: %08lx\n", regs->r1, regs->r2, regs->r3, regs->r4); diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index fcfb82ee2d02a..ad2717cedf826 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -40,7 +40,7 @@ char cmd_line[COMMAND_LINE_SIZE] = { 0, }; /* r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11*/ /* r12 r13 r14 r15 or2 ra fp sp gp es ste ea*/ static struct pt_regs fake_regs = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, (unsigned long)cpu_idle, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0}; /* Copy a short hook instruction sequence to the exception address */ diff --git a/arch/nios2/kernel/traps.c b/arch/nios2/kernel/traps.c index 872354a48435b..53fd60f09c6ed 100644 --- a/arch/nios2/kernel/traps.c +++ b/arch/nios2/kernel/traps.c @@ -51,17 +51,6 @@ void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr) force_sig_info(signo, &info, current); } -/* - * The architecture-independent backtrace generator - */ -void dump_stack(void) -{ - unsigned long stack; - - show_stack(current, &stack); -} -EXPORT_SYMBOL(dump_stack); - /* * The show_stack is an external API which we do not use ourselves. */ diff --git a/arch/nios2/mm/init.c b/arch/nios2/mm/init.c index 4e3ca895af7c1..644dbd40a8f30 100644 --- a/arch/nios2/mm/init.c +++ b/arch/nios2/mm/init.c @@ -122,35 +122,16 @@ void __init mmu_init(void) } #endif -static void __init free_init_pages(const char *what, unsigned long start, - unsigned long end) -{ - unsigned long addr; - - /* next to check that the page we free is not a partial page */ - for (addr = start; addr + PAGE_SIZE <= end; addr += PAGE_SIZE) { - ClearPageReserved(virt_to_page(addr)); - init_page_count(virt_to_page(addr)); - free_page(addr); - totalram_pages++; - } - - pr_notice("Freeing %s: %ldk freed (0x%lx - 0x%lx)\n", - what, (end - start) >> 10, start, end); -} - #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_init_pages("initrd memory", start, end); + free_reserved_area(start, end, 0, "initrd"); } #endif void __init_refok free_initmem(void) { - free_init_pages("unused kernel memory", - (unsigned long)(&__init_begin), - (unsigned long)(&__init_end)); + free_initmem_default(0); } #ifdef CONFIG_MMU From 79279af248862e7ce72293714fcd764623447238 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 30 Aug 2013 11:16:57 +0800 Subject: [PATCH 016/201] FogBugz #151087: arch/nios2: Removed unused function kernel_execve() Main kernel have removed the use of kernel_execve() and use the generic do_execve(). See commit ae903caae26 for detail. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/sys_nios2.c | 36 ----------------------------------- 1 file changed, 36 deletions(-) diff --git a/arch/nios2/kernel/sys_nios2.c b/arch/nios2/kernel/sys_nios2.c index 364c6e3e901ea..a8aa1483bba85 100644 --- a/arch/nios2/kernel/sys_nios2.c +++ b/arch/nios2/kernel/sys_nios2.c @@ -90,43 +90,7 @@ asmlinkage int sys_getpagesize(void) return PAGE_SIZE; } -/* - * Do a system call from kernel instead of calling sys_execve so we - * end up with proper pt_regs. - */ #ifdef CONFIG_MMU -int kernel_execve(const char *filename, - const char *const argv[], - const char *const envp[]) -{ - register long __res __asm__ ("r2"); - register long __sc __asm__ ("r2") = __NR_execve; - register long __a __asm__ ("r4") = (long) filename; - register long __b __asm__ ("r5") = (long) argv; - register long __c __asm__ ("r6") = (long) envp; - __asm__ __volatile__ ("trap" : "=r" (__res) - : "0" (__sc), "r" (__a), "r" (__b), "r" (__c) - : "memory"); - - return __res; -} -#else -int kernel_execve(const char *filename, - const char *const argv[], - const char *const envp[]) -{ - register long __res __asm__ ("r2") = TRAP_ID_SYSCALL; - register long __sc __asm__ ("r3") = __NR_execve; - register long __a __asm__ ("r4") = (long) filename; - register long __b __asm__ ("r5") = (long) argv; - register long __c __asm__ ("r6") = (long) envp; - __asm__ __volatile__ ("trap" : "=r" (__res) - : "0" (__res), "r" (__sc), "r" (__a), "r" (__b), - "r" (__c) : "memory"); - - return __res; -} - #if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE) #include unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, From dd1d6f0ca5a80fe93b8bd1db7c063e954c4040ac Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 18 Sep 2013 16:54:29 +0800 Subject: [PATCH 017/201] Ported Nios II to support v3.11 - Consolidate io_remap_pfn_range definitions (see commit 40d158e61) - Enhance free_reserved_area() to support poisoning memory with zero (see commit dbe67df4) - Concentrate modification of totalram_pages into the mm core (see commit 0c9885347) - Remove num_physpages and simplify mem_init() (see commit 189541818) - Invoke oom-killer from remaining unconverted page fault handlers (see commit 609838cf) Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/pgtable_mm.h | 3 --- arch/nios2/include/asm/pgtable_no.h | 3 --- arch/nios2/mm/fault.c | 8 ++++---- arch/nios2/mm/init.c | 18 ++++-------------- 4 files changed, 8 insertions(+), 24 deletions(-) diff --git a/arch/nios2/include/asm/pgtable_mm.h b/arch/nios2/include/asm/pgtable_mm.h index 9d68c46881a12..c4cf0b5906dfa 100644 --- a/arch/nios2/include/asm/pgtable_mm.h +++ b/arch/nios2/include/asm/pgtable_mm.h @@ -296,9 +296,6 @@ static inline void pte_clear(struct mm_struct *mm, #define kern_addr_valid(addr) (1) -#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ - remap_pfn_range(vma, vaddr, pfn, size, prot) - #include #define pgtable_cache_init() do { } while (0) diff --git a/arch/nios2/include/asm/pgtable_no.h b/arch/nios2/include/asm/pgtable_no.h index ec624cc799088..56bec03394b73 100644 --- a/arch/nios2/include/asm/pgtable_no.h +++ b/arch/nios2/include/asm/pgtable_no.h @@ -55,9 +55,6 @@ static inline int pte_file(pte_t pte) { return 0; } */ #define pgtable_cache_init() do { } while (0) -#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ - remap_pfn_range(vma, vaddr, pfn, size, prot) - /* * All 32bit addresses are effectively valid for vmalloc... * Sort of meaningless for non-VM targets. diff --git a/arch/nios2/mm/fault.c b/arch/nios2/mm/fault.c index 75db12ee943c0..f5352e979859a 100644 --- a/arch/nios2/mm/fault.c +++ b/arch/nios2/mm/fault.c @@ -182,10 +182,10 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause, down_read(&mm->mmap_sem); goto survive; } - pr_info("VM: killing process %s\n", tsk->comm); - if (user_mode(regs)) - do_group_exit(SIGKILL); - goto no_context; + if (!user_mode(regs)) + goto no_context; + pagefault_out_of_memory(); + return; do_sigbus: up_read(&mm->mmap_sem); diff --git a/arch/nios2/mm/init.c b/arch/nios2/mm/init.c index 644dbd40a8f30..1a28b0336de10 100644 --- a/arch/nios2/mm/init.c +++ b/arch/nios2/mm/init.c @@ -86,7 +86,6 @@ void __init paging_init(void) void __init mem_init(void) { - unsigned int codek = 0, datak = 0; unsigned long end_mem = memory_end; /* this must not include kernel stack at top */ @@ -100,19 +99,10 @@ void __init mem_init(void) #else max_mapnr = (((unsigned long)high_memory) - PAGE_OFFSET) >> PAGE_SHIFT; #endif /* CONFIG_MMU */ - num_physpages = max_mapnr; - pr_debug("We have %ld pages of RAM\n", num_physpages); /* this will put all memory onto the freelists */ - totalram_pages = free_all_bootmem(); - - codek = (_etext - _stext) >> 10; - datak = (_end - _etext) >> 10; - - pr_info("Memory available: %luk/%luk RAM (%dk kernel code, %dk data)\n", - nr_free_pages() << (PAGE_SHIFT - 10), - (unsigned long)((_end - _stext) >> 10), - codek, datak); + free_all_bootmem(); + mem_init_print_info(NULL); } #ifdef CONFIG_MMU @@ -125,13 +115,13 @@ void __init mmu_init(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void*)start, (void*)end, -1, "initrd"); } #endif void __init_refok free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_MMU From 7ffe895f7b9ff691fea17b525c74126857fee5a3 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 17 Oct 2013 18:03:33 +0800 Subject: [PATCH 018/201] FogBugz #162062: arch/nios2: Add .got section to linker script This fix the Nios II kernel links to LIBGCC library functions issue. The GOT PLT needs to be on the beginning of the GOT. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/vmlinux.lds.S | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/nios2/kernel/vmlinux.lds.S b/arch/nios2/kernel/vmlinux.lds.S index db9078f9b95ea..0f220b978228c 100644 --- a/arch/nios2/kernel/vmlinux.lds.S +++ b/arch/nios2/kernel/vmlinux.lds.S @@ -31,6 +31,13 @@ SECTIONS { . = CONFIG_MEM_BASE | CONFIG_KERNEL_REGION_BASE; + .got : { + *(.got.plt) + *(.igot.plt) + *(.got) + *(.igot) + } + _text = .; _stext = .; HEAD_TEXT_SECTION From 6e5ab1c389fa3c1d0cf6b015a039fdfcc43e7de8 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 25 Oct 2013 17:05:24 +0800 Subject: [PATCH 019/201] FogBugz #163802: arch/nios2: Copy FDT from init memory to regular memory If the supplied DTB is builtin into kernel, it is in __init memory and will eventually be destroyed after bootup. But if drivers are loaded as modules, they still need to access the DTB entries after the kernel has started. Therefore, the DTB is moved to a new allocated area. Thanks Jeroen Van den Keybus for the fix. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/prom.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c index 893ed19d6505e..21486726450b6 100644 --- a/arch/nios2/kernel/prom.c +++ b/arch/nios2/kernel/prom.c @@ -95,6 +95,7 @@ void __init early_init_devtree(void *params) void __init device_tree_init(void) { unsigned long base, size; + void *fdt_copy; if (!initial_boot_params) return; @@ -103,17 +104,36 @@ void __init device_tree_init(void) size = be32_to_cpu(initial_boot_params->totalsize); /* - * If the chosen DTB is not the built-in one (passed via - * bootloader or found at a built-in physical address) and - * it is within main memory above the kernel binary itself - * (> memory_start), we need to reserve_bootmem(). + * If the supplied DTB is located in the kernel, it is + * in __init memory and will eventually be destroyed + * ('Freeing unused kernel memory: ...'). + * + * If the DTB is located in the available system RAM + * (between memory_start and memory_end), it will also + * eventually be destroyed if that memory is allocated by + * applications. The DTB might get placed there by a boot- + * loader from not directly addressable memory such as a + * SPI EEPROM sector. + * + * Usually, the DT will already have served its + * purpose when the kernel starts. However, if drivers are + * loaded as modules, they must still be able to access the + * DTB entries after the kernel has started. + * + * Therefore, in these 2 cases, the DTB is moved to a new + * bootmem-allocator allocated area. It has the added + * benefit of reducing memory fragmentation. */ - if ((base >= memory_start) && (base < memory_end)) { + + if ((base >= __pa(_text)) && (base < memory_end)) { reserve_bootmem(base, size, BOOTMEM_DEFAULT); - unflatten_device_tree(); + fdt_copy = alloc_bootmem(size); + memcpy(fdt_copy, initial_boot_params, size); + initial_boot_params = fdt_copy; free_bootmem(base, size); - } else - unflatten_device_tree(); + } + + unflatten_device_tree(); } #ifdef CONFIG_EARLY_PRINTK From edb47be23d91f8bbfe87347edc9d441ebb65f318 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 13 Nov 2013 15:10:26 +0800 Subject: [PATCH 020/201] arch/nios2: Port Nios II to kernel v3.12 - arch: mm: pass userspace fault flag to generic fault handler - of: Specify initrd location using 64-bit - updated defconfig - remove HAVE_GENERIC_HARDIRQS from Kconfig Signed-off-by: Ley Foon Tan --- arch/nios2/Kconfig | 1 - arch/nios2/configs/3c120_defconfig | 1 + arch/nios2/kernel/prom.c | 3 +-- arch/nios2/mm/fault.c | 5 ++++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index b119e38d1e594..b684bf6a56bfd 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -6,7 +6,6 @@ config NIOS2 select HAVE_ARCH_KGDB select USB_ARCH_HAS_HCD if USB_SUPPORT select ARCH_WANT_OPTIONAL_GPIOLIB - select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES diff --git a/arch/nios2/configs/3c120_defconfig b/arch/nios2/configs/3c120_defconfig index 52b514ff25bf5..5d98ec5ade585 100644 --- a/arch/nios2/configs/3c120_defconfig +++ b/arch/nios2/configs/3c120_defconfig @@ -8,6 +8,7 @@ CONFIG_SYSCTL_SYSCALL=y # CONFIG_TIMERFD is not set # CONFIG_EVENTFD is not set # CONFIG_SHMEM is not set +# CONFIG_AIO is not set CONFIG_EMBEDDED=y CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c index 21486726450b6..bfd2b13da7dfb 100644 --- a/arch/nios2/kernel/prom.c +++ b/arch/nios2/kernel/prom.c @@ -58,8 +58,7 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) } #ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { initrd_start = (unsigned long)__va(start); initrd_end = (unsigned long)__va(end); diff --git a/arch/nios2/mm/fault.c b/arch/nios2/mm/fault.c index f5352e979859a..bddbb808fbcd1 100644 --- a/arch/nios2/mm/fault.c +++ b/arch/nios2/mm/fault.c @@ -79,6 +79,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause, if (in_atomic() || !mm) goto bad_area_nosemaphore; + if (user_mode(regs)) + flags |= FAULT_FLAG_USER; + down_read(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) @@ -110,9 +113,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause, goto bad_area; break; case EXC_W_PROTECTION_FAULT: - flags = FAULT_FLAG_WRITE; if (!(vma->vm_flags & VM_WRITE)) goto bad_area; + flags = FAULT_FLAG_WRITE; break; } From 800051a662efcf54be5c2e346ad67252ca6d2319 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 25 Oct 2013 18:39:06 +0800 Subject: [PATCH 021/201] FogBugz #163821: arch/nios2: Add devtmpfs to defconfig Enable DEVTMFS as default and removed non-exist configs. CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y Note, need to compile rootfs with BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_DEVTMPFS enabled. Signed-off-by: Ley Foon Tan --- arch/nios2/configs/3c120_defconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/nios2/configs/3c120_defconfig b/arch/nios2/configs/3c120_defconfig index 5d98ec5ade585..d4c617296d042 100644 --- a/arch/nios2/configs/3c120_defconfig +++ b/arch/nios2/configs/3c120_defconfig @@ -35,10 +35,11 @@ CONFIG_IP_PNP_RARP=y # CONFIG_IPV6 is not set # CONFIG_WIRELESS is not set CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y # CONFIG_FW_LOADER is not set CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_CFI_INTELEXT=y @@ -47,7 +48,6 @@ CONFIG_MTD_PHYSMAP_OF=y CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_LOOP=y CONFIG_NETDEVICES=y -CONFIG_MII=y CONFIG_NET_VENDOR_ALTERA=y CONFIG_ALT_TSE=y CONFIG_MARVELL_PHY=y From 2237e30b6ced938dcf3377e06c52a5b9fbfb24b3 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 7 Nov 2013 18:01:18 +0800 Subject: [PATCH 022/201] FogBugz #166446: nios2: move .GOT section after .text section Fix boot from flash issue. .GOT section needs to be after .text section because arch/nios2/boot/compressed/misc.c:decompress_kernel() assumes kernel is at top of the memory region. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/vmlinux.lds.S | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/nios2/kernel/vmlinux.lds.S b/arch/nios2/kernel/vmlinux.lds.S index 0f220b978228c..ec43c9f72b18a 100644 --- a/arch/nios2/kernel/vmlinux.lds.S +++ b/arch/nios2/kernel/vmlinux.lds.S @@ -31,13 +31,6 @@ SECTIONS { . = CONFIG_MEM_BASE | CONFIG_KERNEL_REGION_BASE; - .got : { - *(.got.plt) - *(.igot.plt) - *(.got) - *(.igot) - } - _text = .; _stext = .; HEAD_TEXT_SECTION @@ -50,6 +43,13 @@ SECTIONS } =0 _etext = .; + .got : { + *(.got.plt) + *(.igot.plt) + *(.got) + *(.igot) + } + EXCEPTION_TABLE(L1_CACHE_BYTES) . = ALIGN(PAGE_SIZE); From e5a41da53b648d74d63c8e8eec4640ed79a8974f Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 11 Dec 2013 14:50:06 +0800 Subject: [PATCH 023/201] FogBugz #172871: Use hardware interrupt instead of virtual interrupt Fixed incorrect interrupt number when setting IENABLE register. We should use hardware interrupt number instead of virtual interrupt number. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/nios2/kernel/irq.c b/arch/nios2/kernel/irq.c index 059dbe0b0d9c5..7db8e054aaaf4 100644 --- a/arch/nios2/kernel/irq.c +++ b/arch/nios2/kernel/irq.c @@ -41,7 +41,7 @@ static void chip_unmask(struct irq_data *d) { unsigned ien; ien = RDCTL(CTL_IENABLE); - ien |= (1 << d->irq); + ien |= (1 << d->hwirq); WRCTL(CTL_IENABLE, ien); } @@ -49,7 +49,7 @@ static void chip_mask(struct irq_data *d) { unsigned ien; ien = RDCTL(CTL_IENABLE); - ien &= ~(1 << d->irq); + ien &= ~(1 << d->hwirq); WRCTL(CTL_IENABLE, ien); } From b3326f28e2119df8d14e8b1e8683966fccbe2cca Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 2 Jan 2014 08:57:46 +0800 Subject: [PATCH 024/201] FogBugz #175708: nios2: irq: Get mapped IRQ before calling the handler The IRQ handler needs the Linux IRQ, not the hardware-specific IRQ. Therefore, let's distinguish that clearly in the parameter name and also call irq_find_mapping() to get the mapped IRQ before calling the handler. This fixes a bootup issue, which caused handle_bad_irq() to be continously called with IRQ = 0, which is the hwirq for the timer tick. Tested on Altera's NEEK board with a custom FPGA design. Signed-off-by: Ezequiel Garcia Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/irq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/nios2/kernel/irq.c b/arch/nios2/kernel/irq.c index 7db8e054aaaf4..097a760ba3fcc 100644 --- a/arch/nios2/kernel/irq.c +++ b/arch/nios2/kernel/irq.c @@ -26,11 +26,13 @@ #include #include -asmlinkage void do_IRQ(int irq, struct pt_regs *regs) +asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) { struct pt_regs *oldregs = set_irq_regs(regs); + int irq; irq_enter(); + irq = irq_find_mapping(NULL, hwirq); generic_handle_irq(irq); irq_exit(); @@ -72,7 +74,6 @@ static struct irq_domain_ops irq_ops = { .xlate = irq_domain_xlate_onecell, }; - void __init init_IRQ(void) { struct irq_domain *domain; From 169d069e94e05768ac43c11d1fbad5d8200c5b4c Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 3 Jan 2014 17:04:54 +0800 Subject: [PATCH 025/201] FogBugz #175952: arch/nios2: Cleanup comments Cleanup unused comments. Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/cacheflush.h | 2 -- arch/nios2/include/asm/uaccess.h | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/nios2/include/asm/cacheflush.h b/arch/nios2/include/asm/cacheflush.h index 61d27104de2e6..b5b5deb44e98d 100644 --- a/arch/nios2/include/asm/cacheflush.h +++ b/arch/nios2/include/asm/cacheflush.h @@ -39,7 +39,6 @@ extern void copy_from_user_page(struct vm_area_struct *vma, struct page *page, unsigned long user_vaddr, void *dst, void *src, int len); -/* FIXME: Remove: Linux does not define this interfaces, it's arch specific. */ extern void flush_dcache_range(unsigned long start, unsigned long end); #else /* CONFIG_MMU */ @@ -77,7 +76,6 @@ static inline void __flush_cache_all(void) #define copy_from_user_page(vma, page, vaddr, dst, src, len) \ memcpy(dst, src, len) -/* FIXME: Remove: Linux does not define this interfaces, it's arch specific. */ #define flush_dcache_range(start, end) \ dcache_push((start), (end) - (start)) diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h index 23d7b01712dad..8c6d541b03f02 100644 --- a/arch/nios2/include/asm/uaccess.h +++ b/arch/nios2/include/asm/uaccess.h @@ -218,7 +218,7 @@ do { \ ({ \ long __gu_err = -EFAULT; \ const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \ - unsigned long __gu_val; /* FIXME: should be __typeof__ */ \ + unsigned long __gu_val; \ __get_user_common(__gu_val, sizeof(*(ptr)), __gu_ptr, __gu_err);\ (x) = (__typeof__(x))__gu_val; \ __gu_err; \ From 1dcf66f8927deb8d4dfe7d5cb03aaf7306101904 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 6 Jan 2014 10:43:55 +0800 Subject: [PATCH 026/201] FogBugz #175951: nios2: Move the MMU initialization The current MMU initialization consists only in the flush of all the TLB entries. This is done based in the cpuinfo values that specify the TLB geometry. Hence, the current calling of mmu_init() *before* the cpuinfo is setup is wrong and doesn't perform any TLB flushing. Fix it by moving the call to mmu_init() to be done *after* the cpuinfo has been correctly filled. This commit fixes soft-reboots, which are currently broken. Upon soft-reboot (from a system that has entered userspace) the TLB would be filled and were previously never flushed. Such systems booting from a soft-reboot would fail to enter userpace, panic'ing like this: Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b Signed-off-by: Ezequiel Garcia Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/setup.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index ad2717cedf826..36f4a8a77dcc6 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -112,10 +112,6 @@ asmlinkage void __init nios2_boot_init(unsigned r4, unsigned r5, unsigned r6, unsigned dtb_passed = 0; char cmdline_passed[COMMAND_LINE_SIZE] = { 0, }; -#ifdef CONFIG_MMU - mmu_init(); -#endif - #if defined(CONFIG_PASS_CMDLINE) if (r4 == 0x534f494e) { /* r4 is magic NIOS */ #if defined(CONFIG_BLK_DEV_INITRD) @@ -213,6 +209,8 @@ void __init setup_arch(char **cmdline_p) copy_exception_handler(cpuinfo.exception_addr); #ifdef CONFIG_MMU + mmu_init(); + copy_fast_tlb_miss_handler(cpuinfo.fast_tlb_miss_exc_addr); /* From c1dd18fd89254e30d0fd683e8f8cf18a3277e70c Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 24 Jan 2014 18:19:47 +0800 Subject: [PATCH 027/201] arch/nios2: Port nios2 kernel to v3.13 - add asm/preempt.h and posix_types.h to Kbuild - remove kernel/time/Kconfig from Kconfig - handle pgtable_page_ctor() fail - remove PREEMPT_ACTIVE and use generic - remove duplicated arch/nios2/include/uapi/asm/setup.h - convert to use new early_init_dt_scan function - convert to use new of_flat_dt_get_machine function - use boot_command_line instead of private cmd_line - use unflatten_and_copy_device_tree - use early_init_dt_scan - remove unnecessary prom.c includes Signed-off-by: Ley Foon Tan --- arch/nios2/Kconfig | 2 - arch/nios2/include/asm/Kbuild | 2 + arch/nios2/include/asm/pgalloc.h | 5 +- arch/nios2/include/asm/prom.h | 2 - arch/nios2/include/asm/setup.h | 2 - arch/nios2/include/asm/thread_info.h | 2 - arch/nios2/include/uapi/asm/posix_types.h | 58 ------------------ arch/nios2/include/uapi/asm/setup.h | 41 ------------- arch/nios2/kernel/prom.c | 71 +---------------------- arch/nios2/kernel/setup.c | 13 ++--- arch/nios2/platform/platform.c | 13 +---- 11 files changed, 14 insertions(+), 197 deletions(-) delete mode 100644 arch/nios2/include/uapi/asm/posix_types.h delete mode 100644 arch/nios2/include/uapi/asm/setup.h diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index b684bf6a56bfd..7d958e78660dd 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -60,8 +60,6 @@ source "kernel/Kconfig.freezer" source "kernel/Kconfig.hz" -source "kernel/time/Kconfig" - source "mm/Kconfig" config FORCE_MAX_ZONEORDER diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild index f9aff08bcb223..c5ed4a4ca2c16 100644 --- a/arch/nios2/include/asm/Kbuild +++ b/arch/nios2/include/asm/Kbuild @@ -34,6 +34,8 @@ generic-y += module.h generic-y += msgbuf.h generic-y += param.h generic-y += percpu.h +generic-y += posix_types.h +generic-y += preempt.h generic-y += resource.h generic-y += scatterlist.h generic-y += sections.h diff --git a/arch/nios2/include/asm/pgalloc.h b/arch/nios2/include/asm/pgalloc.h index 9f912bfb5d5eb..eed8a67ff4eb0 100644 --- a/arch/nios2/include/asm/pgalloc.h +++ b/arch/nios2/include/asm/pgalloc.h @@ -59,8 +59,11 @@ static inline pgtable_t pte_alloc_one(struct mm_struct *mm, pte = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER); if (pte) { + if (!pgtable_page_ctor(pte)) { + __free_page(pte); + return NULL; + } clear_highpage(pte); - pgtable_page_ctor(pte); } return pte; } diff --git a/arch/nios2/include/asm/prom.h b/arch/nios2/include/asm/prom.h index e1451bccb3ee0..7a71e4c50a421 100644 --- a/arch/nios2/include/asm/prom.h +++ b/arch/nios2/include/asm/prom.h @@ -23,6 +23,4 @@ extern int __dtb_start; extern unsigned long early_altera_uart_or_juart_console(void); -extern void device_tree_init(void); - #endif /* _ASM_NIOS2_PROM_H */ diff --git a/arch/nios2/include/asm/setup.h b/arch/nios2/include/asm/setup.h index 2c571d998118f..a3e05a7f977a5 100644 --- a/arch/nios2/include/asm/setup.h +++ b/arch/nios2/include/asm/setup.h @@ -24,8 +24,6 @@ #ifndef __ASSEMBLY__ # ifdef __KERNEL__ -extern char cmd_line[COMMAND_LINE_SIZE]; - extern char exception_handler_hook[]; extern char fast_handler[]; extern char fast_handler_end[]; diff --git a/arch/nios2/include/asm/thread_info.h b/arch/nios2/include/asm/thread_info.h index da57228e0f781..8b516a9e62b1c 100644 --- a/arch/nios2/include/asm/thread_info.h +++ b/arch/nios2/include/asm/thread_info.h @@ -88,8 +88,6 @@ static inline struct thread_info *current_thread_info(void) } #endif /* !__ASSEMBLY__ */ -#define PREEMPT_ACTIVE 0x10000000 - /* * thread information flags * - these are process state flags that various assembly files may need to diff --git a/arch/nios2/include/uapi/asm/posix_types.h b/arch/nios2/include/uapi/asm/posix_types.h deleted file mode 100644 index e326dafbc7311..0000000000000 --- a/arch/nios2/include/uapi/asm/posix_types.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2009 Thomas Chou - * - * 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 . - * - */ -#ifndef _ASM_NIOS2_POSIX_TYPES_H -#define _ASM_NIOS2_POSIX_TYPES_H - -typedef unsigned short __kernel_mode_t; -#define __kernel_mode_t __kernel_mode_t - -typedef unsigned short __kernel_nlink_t; -#define __kernel_nlink_t __kernel_nlink_t - -#ifdef CONFIG_MMU -typedef unsigned int __kernel_ipc_pid_t; -#define __kernel_ipc_pid_t __kernel_ipc_pid_t - -typedef unsigned long __kernel_size_t; -typedef long __kernel_ssize_t; -typedef int __kernel_ptrdiff_t; -#define __kernel_size_t __kernel_size_t - -#else -typedef unsigned short __kernel_ipc_pid_t; -#define __kernel_ipc_pid_t __kernel_ipc_pid_t - -typedef unsigned short __kernel_uid_t; -typedef unsigned short __kernel_gid_t; -#define __kernel_uid_t __kernel_uid_t - -typedef unsigned int __kernel_uid32_t; -typedef unsigned int __kernel_gid32_t; -#define __kernel_uid32_t __kernel_uid32_t -#endif /* CONFIG_MMU */ - -typedef unsigned short __kernel_old_uid_t; -typedef unsigned short __kernel_old_gid_t; -#define __kernel_old_uid_t __kernel_old_uid_t - -typedef unsigned short __kernel_old_dev_t; -#define __kernel_old_dev_t __kernel_old_dev_t - -#include - -#endif /* _ASM_NIOS2_POSIX_TYPES_H */ diff --git a/arch/nios2/include/uapi/asm/setup.h b/arch/nios2/include/uapi/asm/setup.h deleted file mode 100644 index f3fc3e7f34f29..0000000000000 --- a/arch/nios2/include/uapi/asm/setup.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2011 Tobias Klauser - * - * 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 . - * - */ - - -#ifndef _ASM_NIOS2_SETUP_H -#define _ASM_NIOS2_SETUP_H - -#include - -#ifndef __ASSEMBLY__ -# ifdef __KERNEL__ - -extern char cmd_line[COMMAND_LINE_SIZE]; - -extern char exception_handler_hook[]; -extern char fast_handler[]; -extern char fast_handler_end[]; - -extern void pagetable_init(void); - -extern void setup_early_printk(void); - -# endif/* __KERNEL__ */ -#endif /* __ASSEMBLY__ */ - -#endif /* _ASM_NIOS2_SETUP_H */ diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c index bfd2b13da7dfb..0ca89fd7b682b 100644 --- a/arch/nios2/kernel/prom.c +++ b/arch/nios2/kernel/prom.c @@ -24,22 +24,14 @@ */ #include -#include -#include #include #include -#include -#include #include #include -#include -#include #include -#include #include #include -#include void __init early_init_dt_add_memory_arch(u64 base, u64 size) { @@ -57,15 +49,6 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) return __alloc_bootmem(size, align, __pa(MAX_DMA_ADDRESS)); } -#ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ - initrd_start = (unsigned long)__va(start); - initrd_end = (unsigned long)__va(end); - initrd_below_start_ok = 1; -} -#endif - void __init early_init_devtree(void *params) { if (params && be32_to_cpup((__be32 *)params) == OF_DT_HEADER) @@ -80,59 +63,7 @@ void __init early_init_devtree(void *params) else return; - /* Retrieve various informations from the /chosen node of the - * device-tree, including the platform type, initrd location and - * size, and more ... - */ - of_scan_flat_dt(early_init_dt_scan_chosen, cmd_line); - - /* Scan memory nodes */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - of_scan_flat_dt(early_init_dt_scan_memory, NULL); -} - -void __init device_tree_init(void) -{ - unsigned long base, size; - void *fdt_copy; - - if (!initial_boot_params) - return; - - base = virt_to_phys((void *)initial_boot_params); - size = be32_to_cpu(initial_boot_params->totalsize); - - /* - * If the supplied DTB is located in the kernel, it is - * in __init memory and will eventually be destroyed - * ('Freeing unused kernel memory: ...'). - * - * If the DTB is located in the available system RAM - * (between memory_start and memory_end), it will also - * eventually be destroyed if that memory is allocated by - * applications. The DTB might get placed there by a boot- - * loader from not directly addressable memory such as a - * SPI EEPROM sector. - * - * Usually, the DT will already have served its - * purpose when the kernel starts. However, if drivers are - * loaded as modules, they must still be able to access the - * DTB entries after the kernel has started. - * - * Therefore, in these 2 cases, the DTB is moved to a new - * bootmem-allocator allocated area. It has the added - * benefit of reducing memory fragmentation. - */ - - if ((base >= __pa(_text)) && (base < memory_end)) { - reserve_bootmem(base, size, BOOTMEM_DEFAULT); - fdt_copy = alloc_bootmem(size); - memcpy(fdt_copy, initial_boot_params, size); - initial_boot_params = fdt_copy; - free_bootmem(base, size); - } - - unflatten_device_tree(); + early_init_dt_scan(initial_boot_params); } #ifdef CONFIG_EARLY_PRINTK diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index 36f4a8a77dcc6..18a2cfa9a2e6d 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -35,8 +35,6 @@ EXPORT_SYMBOL(memory_end); unsigned long memory_size; -char cmd_line[COMMAND_LINE_SIZE] = { 0, }; - /* r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11*/ /* r12 r13 r14 r15 or2 ra fp sp gp es ste ea*/ static struct pt_regs fake_regs = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -131,10 +129,10 @@ asmlinkage void __init nios2_boot_init(unsigned r4, unsigned r5, unsigned r6, #ifndef CONFIG_CMDLINE_FORCE if (cmdline_passed[0]) - strncpy(cmd_line, cmdline_passed, COMMAND_LINE_SIZE); + strncpy(boot_command_line, cmdline_passed, COMMAND_LINE_SIZE); #ifdef CONFIG_CMDLINE_IGNORE_DTB else - strncpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE); + strncpy(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif #endif } @@ -159,10 +157,7 @@ void __init setup_arch(char **cmdline_p) init_task.thread.kregs = &fake_regs; /* Keep a copy of command line */ - *cmdline_p = &cmd_line[0]; - - memcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); - boot_command_line[COMMAND_LINE_SIZE-1] = 0; + *cmdline_p = boot_command_line; /* * give all the memory to the bootmap allocator, tell it to put the @@ -202,7 +197,7 @@ void __init setup_arch(char **cmdline_p) } #endif /* CONFIG_BLK_DEV_INITRD */ - device_tree_init(); + unflatten_and_copy_device_tree(); setup_cpuinfo(); diff --git a/arch/nios2/platform/platform.c b/arch/nios2/platform/platform.c index 5e663aa68019e..2e5b8f624f4d5 100644 --- a/arch/nios2/platform/platform.c +++ b/arch/nios2/platform/platform.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -27,22 +28,14 @@ static struct of_device_id altera_of_bus_ids[] __initdata = { static void __init nios2_soc_device_init(void) { - struct device_node *root; struct soc_device *soc_dev; struct soc_device_attribute *soc_dev_attr; const char *machine; - int err; - root = of_find_node_by_path("/"); - if (!root) + machine = of_flat_dt_get_machine_name(); + if (!machine) return; - err = of_property_read_string(root, "model", &machine); - if (err) - return; - - of_node_put(root); - soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return; From f745d4b21bb2535d5374542af299fd588434ec87 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 28 Jan 2014 11:29:25 +0800 Subject: [PATCH 028/201] FogBugz #180286: Remove NOMMU support from Nios II Remove NOMMU support from Nios II from v3.13. Please use older version kernel if you need NOMMU. Reason to remove NOMMU support are: The latest Nios II Linux toolchain (v4.7.3) only support MMU version. The toolchain and nios2 kernel migration to generic syscall ABI also will break NOMMU support. This has been discussed and communicated in nios2-dev mailing list, see detail in: http://lists.rocketboards.org/pipermail/nios2-dev/2013-September/006874.html Signed-off-by: Ley Foon Tan --- arch/nios2/Kconfig | 16 +- arch/nios2/Makefile | 7 - arch/nios2/boot/dts/3c120_devboard.dts | 1 + arch/nios2/include/asm/cacheflush.h | 45 ---- arch/nios2/include/asm/dma-mapping.h | 4 - arch/nios2/include/asm/elf.h | 38 --- arch/nios2/include/asm/entry.h | 24 +- arch/nios2/include/asm/io.h | 28 --- arch/nios2/include/asm/mmu.h | 5 - arch/nios2/include/asm/mmu_context.h | 75 ++++-- arch/nios2/include/asm/mmu_context_mm.h | 66 ----- arch/nios2/include/asm/page.h | 42 ---- arch/nios2/include/asm/pgalloc.h | 6 - arch/nios2/include/asm/pgtable.h | 318 ++++++++++++++++++++++-- arch/nios2/include/asm/pgtable_mm.h | 309 ----------------------- arch/nios2/include/asm/pgtable_no.h | 80 ------ arch/nios2/include/asm/processor.h | 27 -- arch/nios2/include/asm/ptrace.h | 9 +- arch/nios2/include/asm/registers.h | 4 - arch/nios2/include/asm/thread_info.h | 2 - arch/nios2/include/asm/tlb.h | 8 - arch/nios2/include/asm/tlbflush.h | 6 - arch/nios2/include/asm/uaccess.h | 103 -------- arch/nios2/include/uapi/asm/stat.h | 5 - arch/nios2/kernel/Makefile | 2 +- arch/nios2/kernel/asm-offsets.c | 9 +- arch/nios2/kernel/cpuinfo.c | 23 +- arch/nios2/kernel/entry-nommu.S | 292 ---------------------- arch/nios2/kernel/head.S | 19 -- arch/nios2/kernel/module.c | 22 -- arch/nios2/kernel/process.c | 21 +- arch/nios2/kernel/setup.c | 4 - arch/nios2/kernel/signal.c | 88 ------- arch/nios2/kernel/sys_nios2.c | 12 +- arch/nios2/kernel/syscalltable.S | 4 - arch/nios2/kernel/traps.c | 4 +- arch/nios2/mm/Makefile | 8 +- arch/nios2/mm/cacheflush-nommu.c | 258 ------------------- arch/nios2/mm/dma-mapping-nommu.c | 140 ----------- arch/nios2/mm/init.c | 27 -- 40 files changed, 390 insertions(+), 1771 deletions(-) delete mode 100644 arch/nios2/include/asm/mmu_context_mm.h delete mode 100644 arch/nios2/include/asm/pgtable_mm.h delete mode 100644 arch/nios2/include/asm/pgtable_no.h delete mode 100644 arch/nios2/kernel/entry-nommu.S delete mode 100644 arch/nios2/mm/cacheflush-nommu.c delete mode 100644 arch/nios2/mm/dma-mapping-nommu.c diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index 7d958e78660dd..e92deed63b9fc 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -84,16 +84,11 @@ source "arch/nios2/platform/Kconfig.platform" menu "Processor type and features" config MMU - bool "MMU support" - default y - help - This option enables support for the Nios II MMU. Only enable this - option if your design contains a Nios II/f core with MMU enabled. + def_bool y config ALIGNMENT_TRAP bool "Catch alignment trap" default y - depends on MMU help Nios II CPUs cannot fetch/store data which is not bus aligned, i.e., a 2 or 4 byte fetch must start at an address divisible by @@ -170,8 +165,7 @@ config KERNEL_MMU_REGION_BASE_BOOL config KERNEL_MMU_REGION_BASE hex "Virtual base address of the kernel MMU region " if KERNEL_MMU_REGION_BASE_BOOL - default "0x80000000" if MMU - default "0x00000000" if !MMU + default "0x80000000" help This option allows you to set the virtual base address of the kernel MMU region. @@ -185,8 +179,7 @@ config KERNEL_REGION_BASE_BOOL config KERNEL_REGION_BASE hex "Virtual base address of the kernel region " if KERNEL_REGION_BASE_BOOL - default "0xc0000000" if MMU - default "0x00000000" if !MMU + default "0xc0000000" config IO_REGION_BASE_BOOL bool "Set custom I/O region base address" @@ -198,8 +191,7 @@ config IO_REGION_BASE_BOOL config IO_REGION_BASE hex "Virtual base address of the I/O region " if IO_REGION_BASE_BOOL - default "0xe0000000" if MMU - default "0x80000000" if !MMU + default "0xe0000000" endmenu diff --git a/arch/nios2/Makefile b/arch/nios2/Makefile index 23f4b13e3325b..7a74aedeeb23e 100644 --- a/arch/nios2/Makefile +++ b/arch/nios2/Makefile @@ -14,14 +14,7 @@ # Nios2 port by Wind River Systems Inc trough: # fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com -ifeq ($(CONFIG_MMU),y) UTS_SYSNAME = Linux -else -UTS_SYSNAME = uClinux -MMU := -nommu -KBUILD_CFLAGS += -D__uClinux__ -KBUILD_AFLAGS += -D__uClinux__ -endif export MMU diff --git a/arch/nios2/boot/dts/3c120_devboard.dts b/arch/nios2/boot/dts/3c120_devboard.dts index 53d17a2d12d68..da7c73df74159 100644 --- a/arch/nios2/boot/dts/3c120_devboard.dts +++ b/arch/nios2/boot/dts/3c120_devboard.dts @@ -51,6 +51,7 @@ ALTR,fast-tlb-miss-addr = < 0xc7fff400 >; ALTR,exception-addr = < 0xd0000020 >; ALTR,has-initda = < 1 >; + ALTR,has-mmu = < 1 >; }; //end cpu@0x0 (cpu) }; //end cpus diff --git a/arch/nios2/include/asm/cacheflush.h b/arch/nios2/include/asm/cacheflush.h index b5b5deb44e98d..98fc3bb12d7c0 100644 --- a/arch/nios2/include/asm/cacheflush.h +++ b/arch/nios2/include/asm/cacheflush.h @@ -12,8 +12,6 @@ #ifndef _ASM_NIOS2_CACHEFLUSH_H #define _ASM_NIOS2_CACHEFLUSH_H -#ifdef CONFIG_MMU - struct mm_struct; extern void flush_cache_all(void); @@ -41,49 +39,6 @@ extern void copy_from_user_page(struct vm_area_struct *vma, struct page *page, extern void flush_dcache_range(unsigned long start, unsigned long end); -#else /* CONFIG_MMU */ - -extern void cache_push(unsigned long vaddr, int len); -extern void dcache_push(unsigned long vaddr, int len); -extern void icache_push(unsigned long vaddr, int len); -extern void dcache_push_all(void); -extern void icache_push_all(void); - -static inline void __flush_cache_all(void) -{ - dcache_push_all(); - icache_push_all(); -} - -#define flush_cache_all() __flush_cache_all() -#define flush_cache_mm(mm) do { } while (0) -#define flush_cache_dup_mm(mm) do { } while (0) -#define flush_cache_range(vma, start, end) \ - cache_push((start), (end) - (start)) -#define flush_cache_page(vma, vmaddr) do { } while (0) -#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0 -#define flush_dcache_page(page) do { } while (0) - -#define flush_icache_range(start, end) \ - icache_push((start), (end) - (start)) -#define flush_icache_page(vma, pg) do { } while (0) -#define flush_icache_user_range(vma, pg, adr, len) do { } while (0) -#define flush_cache_vmap(start, end) do { } while (0) -#define flush_cache_vunmap(start, end) do { } while (0) - -#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ - memcpy(dst, src, len) -#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ - memcpy(dst, src, len) - -#define flush_dcache_range(start, end) \ - dcache_push((start), (end) - (start)) - -/* Arch-specific: invalidate virtual memory range. */ -extern void nios2_clear_dcache_range(unsigned long vstart, unsigned long vend); - -#endif /* CONFIG_MMU */ - #define flush_dcache_mmap_lock(mapping) do { } while (0) #define flush_dcache_mmap_unlock(mapping) do { } while (0) diff --git a/arch/nios2/include/asm/dma-mapping.h b/arch/nios2/include/asm/dma-mapping.h index 73c25920cf9c1..cb2283b843dda 100644 --- a/arch/nios2/include/asm/dma-mapping.h +++ b/arch/nios2/include/asm/dma-mapping.h @@ -19,10 +19,6 @@ static inline void __dma_sync(unsigned long addr, size_t size, { switch (direction) { case DMA_FROM_DEVICE: -#ifndef CONFIG_MMU - nios2_clear_dcache_range(addr, (unsigned long)(addr + size)); - break; -#endif case DMA_TO_DEVICE: case DMA_BIDIRECTIONAL: flush_dcache_range(addr, (unsigned long)(addr + size)); diff --git a/arch/nios2/include/asm/elf.h b/arch/nios2/include/asm/elf.h index abc7ca1724f5a..3b169442763f8 100644 --- a/arch/nios2/include/asm/elf.h +++ b/arch/nios2/include/asm/elf.h @@ -41,7 +41,6 @@ /* regs is struct pt_regs, pr_reg is elf_gregset_t (which is now struct_user_regs, they are different) */ -#ifdef CONFIG_MMU #define ELF_CORE_COPY_REGS(pr_reg, regs) \ { do { \ /* Bleech. */ \ @@ -83,43 +82,6 @@ pr_reg[33] = sw->ra; \ } \ } while (0); } -#else -#define ELF_CORE_COPY_REGS(pr_reg, regs) \ -{ do { \ - /* Bleech. */ \ - pr_reg[0] = regs->r1; \ - pr_reg[1] = regs->r2; \ - pr_reg[2] = regs->r3; \ - pr_reg[3] = regs->r4; \ - pr_reg[4] = regs->r5; \ - pr_reg[5] = regs->r6; \ - pr_reg[6] = regs->r7; \ - pr_reg[7] = regs->r8; \ - pr_reg[8] = regs->r9; \ - pr_reg[9] = regs->r10; \ - pr_reg[10] = regs->r11; \ - pr_reg[11] = regs->r12; \ - pr_reg[12] = regs->r13; \ - pr_reg[13] = regs->r14; \ - pr_reg[14] = regs->r15; \ - pr_reg[23] = regs->sp; \ - pr_reg[26] = regs->estatus; \ - { \ - struct switch_stack *sw = ((struct switch_stack *)regs) - 1; \ - pr_reg[15] = sw->r16; \ - pr_reg[16] = sw->r17; \ - pr_reg[17] = sw->r18; \ - pr_reg[18] = sw->r19; \ - pr_reg[19] = sw->r20; \ - pr_reg[20] = sw->r21; \ - pr_reg[21] = sw->r22; \ - pr_reg[22] = sw->r23; \ - pr_reg[24] = sw->fp; \ - pr_reg[25] = sw->gp; \ - } \ -} while (0); } - -#endif /* CONFIG_MMU */ /* This yields a mask that user programs can use to figure out what instruction set this cpu supports. */ diff --git a/arch/nios2/include/asm/entry.h b/arch/nios2/include/asm/entry.h index 150eea6dd9e92..8b7cddd086b02 100644 --- a/arch/nios2/include/asm/entry.h +++ b/arch/nios2/include/asm/entry.h @@ -53,16 +53,9 @@ * Must be called with interrupts disabled. */ .macro SAVE_ALL -#ifdef CONFIG_MMU rdctl r24, estatus andi r24, r24, ESTATUS_EU beq r24, r0, 1f /* In supervisor mode, already on kernel stack */ -#else - movia r24, status_extension /* Read status extension */ - ldw r24, 0(r24) - andi r24, r24, PS_S - bne r24, r0, 1f /* In supervisor mode, already on kernel stack */ -#endif /* CONFIG_MMU */ movia r24, _current_thread /* Switch to current kernel stack */ ldw r24, 0(r24) /* using the thread_info */ @@ -90,33 +83,18 @@ stw r14, PT_R14(sp) stw r15, PT_R15(sp) stw r2, PT_ORIG_R2(sp) -#ifdef CONFIG_MMU stw r7, PT_ORIG_R7(sp) -#endif + stw ra, PT_RA(sp) stw fp, PT_FP(sp) stw gp, PT_GP(sp) rdctl r24, estatus stw r24, PT_ESTATUS(sp) -#ifndef CONFIG_MMU - movia r24, status_extension /* Read status extension */ - ldw r1, 0(r24) - stw r1, PT_STATUS_EXTENSION(sp) /* Store user/supervisor status */ - ORI32 r1, r1, PS_S /* Set supervisor mode */ - stw r1, 0(r24) -#endif /* CONFIG_MMU */ stw ea, PT_EA(sp) .endm .macro RESTORE_ALL -#ifdef CONFIG_MMU - ldw r1, PT_R1(sp) /* Restore registers */ -#else - ldw r1, PT_STATUS_EXTENSION(sp) /* Restore user/supervisor status */ - movia r24, status_extension - stw r1, 0(r24) ldw r1, PT_R1(sp) /* Restore registers */ -#endif /* CONFIG_MMU */ ldw r2, PT_R2(sp) ldw r3, PT_R3(sp) ldw r4, PT_R4(sp) diff --git a/arch/nios2/include/asm/io.h b/arch/nios2/include/asm/io.h index b46cde2917179..b4dc7e65180cf 100644 --- a/arch/nios2/include/asm/io.h +++ b/arch/nios2/include/asm/io.h @@ -163,28 +163,10 @@ static inline void io_insl(unsigned int addr, void *buf, int len) #define outw(x, addr) ((void) writew(x, addr)) #define outl(x, addr) ((void) writel(x, addr)) -#ifdef CONFIG_MMU - extern void __iomem *__ioremap(unsigned long physaddr, unsigned long size, unsigned long cacheflag); extern void __iounmap(void __iomem *addr); -#else - -static inline void __iomem *__ioremap(unsigned long physaddr, - unsigned long size, - unsigned long cacheflag) -{ - if (cacheflag & _PAGE_CACHED) - return (void __iomem *)(physaddr & ~CONFIG_IO_REGION_BASE); - else - return (void __iomem *)(physaddr | CONFIG_IO_REGION_BASE); -} - -#define __iounmap(addr) do {} while (0) - -#endif /* CONFIG_MMU */ - static inline void __iomem *ioremap(unsigned long physaddr, unsigned long size) { return __ioremap(physaddr, size, 0); @@ -214,24 +196,14 @@ static inline void iounmap(void __iomem *addr) } /* Pages to physical address... */ -#ifdef CONFIG_MMU # define page_to_phys(page) virt_to_phys(page_to_virt(page)) # define page_to_bus(page) page_to_virt(page) -#else -# define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT) -# define page_to_bus(page) ((page - mem_map) << PAGE_SHIFT) -#endif /* CONFIG_MMU */ /* Macros used for converting between virtual and physical mappings. */ -#ifdef CONFIG_MMU # define phys_to_virt(vaddr) \ ((void *)((unsigned long)vaddr + PAGE_OFFSET - PHYS_OFFSET)) # define virt_to_phys(vaddr) \ ((unsigned long)((unsigned long)vaddr - PAGE_OFFSET + PHYS_OFFSET)) -#else -# define phys_to_virt(vaddr) ((void *)(vaddr)) -# define virt_to_phys(vaddr) ((unsigned long)(vaddr)) -#endif /* CONFIG_MMU */ #define virt_to_bus virt_to_phys #define bus_to_virt phys_to_virt diff --git a/arch/nios2/include/asm/mmu.h b/arch/nios2/include/asm/mmu.h index d4b65173f2a98..96ab052830ce4 100644 --- a/arch/nios2/include/asm/mmu.h +++ b/arch/nios2/include/asm/mmu.h @@ -12,12 +12,7 @@ #ifndef _ASM_NIOS2_MMU_H #define _ASM_NIOS2_MMU_H -#ifndef CONFIG_MMU -# include -#else - /* Default "unsigned long" context */ typedef unsigned long mm_context_t; -#endif /* CONFIG_MMU */ #endif /* _ASM_NIOS2_MMU_H */ diff --git a/arch/nios2/include/asm/mmu_context.h b/arch/nios2/include/asm/mmu_context.h index 3d239916612b6..294b4b1f81d4e 100644 --- a/arch/nios2/include/asm/mmu_context.h +++ b/arch/nios2/include/asm/mmu_context.h @@ -1,23 +1,66 @@ /* * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 1996, 1997, 1998, 1999 by Ralf Baechle + * Copyright (C) 1999 Silicon Graphics, 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. + * based on MIPS asm/mmu_context.h * - * 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 . + * 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. + */ + +#ifndef _ASM_NIOS2_MMU_CONTEXT_H +#define _ASM_NIOS2_MMU_CONTEXT_H + +#include + +extern void mmu_context_init(void); +extern unsigned long get_pid_from_context(mm_context_t *ctx); + +/* + * For the fast tlb miss handlers, we keep a pointer to the current pgd. + * processor. + */ +extern pgd_t *pgd_current; + +static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) +{ +} + +/* + * Initialize the context related info for a new mm_struct instance. * + * Set all new contexts to 0, that way the generation will never match + * the currently running generation when this context is switched in. + */ +static inline int init_new_context(struct task_struct *tsk, + struct mm_struct *mm) +{ + mm->context = 0; + return 0; +} + +/* + * Destroy context related info for an mm_struct that is about + * to be put to rest. + */ +static inline void destroy_context(struct mm_struct *mm) +{ +} + +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk); + +static inline void deactivate_mm(struct task_struct *tsk, + struct mm_struct *mm) +{ +} + +/* + * After we have set current->mm to a new value, this activates + * the context for the new mm so we see the new mappings. */ +void activate_mm(struct mm_struct *prev, struct mm_struct *next); -#ifdef CONFIG_MMU -# include "mmu_context_mm.h" -#else -# include -#endif +#endif /* _ASM_NIOS2_MMU_CONTEXT_H */ diff --git a/arch/nios2/include/asm/mmu_context_mm.h b/arch/nios2/include/asm/mmu_context_mm.h deleted file mode 100644 index 294b4b1f81d4e..0000000000000 --- a/arch/nios2/include/asm/mmu_context_mm.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2010 Tobias Klauser - * Copyright (C) 1996, 1997, 1998, 1999 by Ralf Baechle - * Copyright (C) 1999 Silicon Graphics, Inc. - * - * based on MIPS asm/mmu_context.h - * - * 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. - */ - -#ifndef _ASM_NIOS2_MMU_CONTEXT_H -#define _ASM_NIOS2_MMU_CONTEXT_H - -#include - -extern void mmu_context_init(void); -extern unsigned long get_pid_from_context(mm_context_t *ctx); - -/* - * For the fast tlb miss handlers, we keep a pointer to the current pgd. - * processor. - */ -extern pgd_t *pgd_current; - -static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) -{ -} - -/* - * Initialize the context related info for a new mm_struct instance. - * - * Set all new contexts to 0, that way the generation will never match - * the currently running generation when this context is switched in. - */ -static inline int init_new_context(struct task_struct *tsk, - struct mm_struct *mm) -{ - mm->context = 0; - return 0; -} - -/* - * Destroy context related info for an mm_struct that is about - * to be put to rest. - */ -static inline void destroy_context(struct mm_struct *mm) -{ -} - -void switch_mm(struct mm_struct *prev, struct mm_struct *next, - struct task_struct *tsk); - -static inline void deactivate_mm(struct task_struct *tsk, - struct mm_struct *mm) -{ -} - -/* - * After we have set current->mm to a new value, this activates - * the context for the new mm so we see the new mappings. - */ -void activate_mm(struct mm_struct *prev, struct mm_struct *next); - -#endif /* _ASM_NIOS2_MMU_CONTEXT_H */ diff --git a/arch/nios2/include/asm/page.h b/arch/nios2/include/asm/page.h index 4339537e8db5a..4f25e0fe6cb96 100644 --- a/arch/nios2/include/asm/page.h +++ b/arch/nios2/include/asm/page.h @@ -45,24 +45,14 @@ */ #define ARCH_PFN_OFFSET PFN_UP(PHYS_OFFSET) -#ifndef CONFIG_MMU -# define get_user_page(vaddr) __get_free_page(GFP_KERNEL) -# define free_user_page(page, addr) free_page(addr) -#endif /* CONFIG_MMU */ - #define clear_page(page) memset((page), 0, PAGE_SIZE) #define copy_page(to, from) memcpy((to), (from), PAGE_SIZE) -#ifdef CONFIG_MMU struct page; extern void clear_user_page(void *addr, unsigned long vaddr, struct page *page); extern void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, struct page *to); -#else -# define clear_user_page(page, vaddr, pg) clear_page(page) -# define copy_user_page(to, from, vaddr, pg) copy_page(to, from) -#endif /* CONFIG_MMU */ extern unsigned long shm_align_mask; @@ -82,13 +72,6 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define __pgd(x) ((pgd_t) { (x) }) #define __pgprot(x) ((pgprot_t) { (x) }) -#ifndef CONFIG_MMU -typedef struct { unsigned long pmd[16]; } pmd_t; - -# define pmd_val(x) ((&x)->pmd[0]) -# define __pmd(x) ((pmd_t) { (x) }) -#endif /* CONFIG_MMU */ - extern unsigned long memory_start; extern unsigned long memory_end; extern unsigned long memory_size; @@ -97,44 +80,21 @@ extern struct page *mem_map; #endif /* !__ASSEMBLY__ */ -#ifdef CONFIG_MMU # define __pa(x) \ ((unsigned long)(x) - PAGE_OFFSET + PHYS_OFFSET) # define __va(x) \ ((void *)((unsigned long)(x) + PAGE_OFFSET - PHYS_OFFSET)) -#else -# define __pa(x) ((unsigned long)(x)) -# define __va(x) ((void *)(x)) -#endif /* CONFIG_MMU */ #define page_to_virt(page) \ ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET) -#ifdef CONFIG_MMU # define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) # define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && \ (pfn) < (max_mapnr + ARCH_PFN_OFFSET)) # define virt_to_page(vaddr) pfn_to_page(PFN_DOWN(virt_to_phys(vaddr))) # define virt_addr_valid(vaddr) pfn_valid(PFN_DOWN(virt_to_phys(vaddr))) -#else /* CONFIG_MMU */ -# define pfn_valid(pfn) ((pfn) < max_mapnr) - -# define virt_to_page(vaddr) \ - ((void *) vaddr < (void *) memory_end ? mem_map + \ - (((unsigned long)(vaddr) - PAGE_OFFSET) >> PAGE_SHIFT) : 0UL) -# define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) -# define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT) - -# define pfn_to_page(pfn) virt_to_page(pfn_to_virt(pfn)) -# define page_to_pfn(page) virt_to_pfn(page_to_virt(page)) - -# define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ - ((void *)(kaddr) < (void *)memory_end)) -#endif /* CONFIG_MMU */ - -#ifdef CONFIG_MMU # define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) @@ -146,8 +106,6 @@ extern struct page *mem_map; #include -#endif /* CONFIG_MMU */ - #include #endif /* _ASM_NIOS2_PAGE_H */ diff --git a/arch/nios2/include/asm/pgalloc.h b/arch/nios2/include/asm/pgalloc.h index eed8a67ff4eb0..6e2985e0a7b90 100644 --- a/arch/nios2/include/asm/pgalloc.h +++ b/arch/nios2/include/asm/pgalloc.h @@ -10,10 +10,6 @@ #ifndef _ASM_NIOS2_PGALLOC_H #define _ASM_NIOS2_PGALLOC_H -#ifndef CONFIG_MMU -# include -#else - #include static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, @@ -85,8 +81,6 @@ static inline void pte_free(struct mm_struct *mm, struct page *pte) tlb_remove_page((tlb), (pte)); \ } while (0) -#endif /* CONFIG_MMU */ - #define check_pgt_cache() do { } while (0) #endif /* _ASM_NIOS2_PGALLOC_H */ diff --git a/arch/nios2/include/asm/pgtable.h b/arch/nios2/include/asm/pgtable.h index 9a0ad9ff771d3..c4cf0b5906dfa 100644 --- a/arch/nios2/include/asm/pgtable.h +++ b/arch/nios2/include/asm/pgtable.h @@ -1,23 +1,309 @@ /* - * Copyright (C) 2010 Tobias Klauser + * Copyright (C) 2011 Tobias Klauser + * Copyright (C) 2009 Wind River Systems 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. + * Based on asm/pgtable-32.h from mips which is: * - * 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. + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 2003 Ralf Baechle + * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * 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. + */ + +#ifndef _ASM_NIOS2_PGTABLE_H +#define _ASM_NIOS2_PGTABLE_H + +#include +#include +#include +#include +#include + +#include +#include + +#define FIRST_USER_ADDRESS 0 + +#define VMALLOC_START CONFIG_KERNEL_MMU_REGION_BASE +#define VMALLOC_END (CONFIG_KERNEL_REGION_BASE - 1) + +struct mm_struct; + +/* Helper macro */ +#define MKP(x, w, r) __pgprot(_PAGE_PRESENT | _PAGE_CACHED | \ + ((x) ? _PAGE_EXEC : 0) | \ + ((r) ? _PAGE_READ : 0) | \ + ((w) ? _PAGE_WRITE : 0)) +/* + * These are the macros that generic kernel code needs + * (to populate protection_map[]) + */ + +/* Remove W bit on private pages for COW support */ +#define __P000 MKP(0, 0, 0) +#define __P001 MKP(0, 0, 1) +#define __P010 MKP(0, 0, 0) /* COW */ +#define __P011 MKP(0, 0, 1) /* COW */ +#define __P100 MKP(1, 0, 0) +#define __P101 MKP(1, 0, 1) +#define __P110 MKP(1, 0, 0) /* COW */ +#define __P111 MKP(1, 0, 1) /* COW */ + +/* Shared pages can have exact HW mapping */ +#define __S000 MKP(0, 0, 0) +#define __S001 MKP(0, 0, 1) +#define __S010 MKP(0, 1, 0) +#define __S011 MKP(0, 1, 1) +#define __S100 MKP(1, 0, 0) +#define __S101 MKP(1, 0, 1) +#define __S110 MKP(1, 1, 0) +#define __S111 MKP(1, 1, 1) + +/* Used all over the kernel */ +#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_CACHED | _PAGE_READ | \ + _PAGE_WRITE | _PAGE_EXEC | _PAGE_GLOBAL) + +#define PAGE_COPY MKP(0, 0, 1) + +#define PGD_ORDER 0 +#define PTE_ORDER 0 + +#define PTRS_PER_PGD ((PAGE_SIZE << PGD_ORDER) / sizeof(pgd_t)) +#define PTRS_PER_PTE ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t)) + +#define USER_PTRS_PER_PGD (CONFIG_KERNEL_MMU_REGION_BASE / PGDIR_SIZE) + +#define PGDIR_SHIFT 22 +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; +extern pte_t invalid_pte_table[PAGE_SIZE/sizeof(pte_t)]; + +/* + * (pmds are folded into puds so this doesn't get actually called, + * but the define is needed for a generic inline function.) + */ +static inline void set_pmd(pmd_t *pmdptr, pmd_t pmdval) +{ + pmdptr->pud.pgd.pgd = pmdval.pud.pgd.pgd; +} + +/* to find an entry in a page-table-directory */ +#define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) +#define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr)) + +static inline int pte_write(pte_t pte) \ + { return pte_val(pte) & _PAGE_WRITE; } +static inline int pte_dirty(pte_t pte) \ + { return pte_val(pte) & _PAGE_DIRTY; } +static inline int pte_young(pte_t pte) \ + { return pte_val(pte) & _PAGE_ACCESSED; } +static inline int pte_file(pte_t pte) \ + { return pte_val(pte) & _PAGE_FILE; } +static inline int pte_special(pte_t pte) { return 0; } + +#define pgprot_noncached pgprot_noncached + +static inline pgprot_t pgprot_noncached(pgprot_t _prot) +{ + unsigned long prot = pgprot_val(_prot); + + prot &= ~_PAGE_CACHED; + + return __pgprot(prot); +} + +/* + * FIXME: Today unmapped pages are mapped to the low physical addresses + * and not 0 (to avoid to trigger the false alias detection in the iss) + * Also check pte_clear. + */ +static inline int pte_none(pte_t pte) +{ + return !(pte_val(pte) & ~(_PAGE_GLOBAL|0xf)); +} + +static inline int pte_present(pte_t pte) \ + { return pte_val(pte) & _PAGE_PRESENT; } + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +static inline pte_t pte_wrprotect(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_WRITE; + return pte; +} + +static inline pte_t pte_mkclean(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_DIRTY; + return pte; +} + +static inline pte_t pte_mkold(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_ACCESSED; + return pte; +} + +static inline pte_t pte_mkwrite(pte_t pte) +{ + pte_val(pte) |= _PAGE_WRITE; + return pte; +} + +static inline pte_t pte_mkdirty(pte_t pte) +{ + pte_val(pte) |= _PAGE_DIRTY; + return pte; +} + +static inline pte_t pte_mkspecial(pte_t pte) { return pte; } + +static inline pte_t pte_mkyoung(pte_t pte) +{ + pte_val(pte) |= _PAGE_ACCESSED; + return pte; +} + +static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ + const unsigned long mask = _PAGE_READ | _PAGE_WRITE | _PAGE_EXEC; + pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask); + return pte; +} + +static inline int pmd_present(pmd_t pmd) +{ + return (pmd_val(pmd) != (unsigned long) invalid_pte_table) + && (pmd_val(pmd) != 0UL); +} + +static inline void pmd_clear(pmd_t *pmdp) +{ + pmd_val(*pmdp) = (unsigned long) invalid_pte_table; +} + +#define pte_pfn(pte) (pte_val(pte) & 0xfffff) +#define pfn_pte(pfn, prot) (__pte(pfn | pgprot_val(prot))) +#define pte_page(pte) (pfn_to_page(pte_pfn(pte))) + +/* + * Store a linux PTE into the linux page table. + */ +static inline void set_pte(pte_t *ptep, pte_t pteval) +{ + *ptep = pteval; +} + +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval) +{ + unsigned long paddr = page_to_virt(pte_page(pteval)); + flush_dcache_range(paddr, paddr + PAGE_SIZE); + set_pte(ptep, pteval); +} + +static inline int pmd_none(pmd_t pmd) +{ + return (pmd_val(pmd) == + (unsigned long) invalid_pte_table) || (pmd_val(pmd) == 0UL); +} + +#define pmd_bad(pmd) (pmd_val(pmd) & ~PAGE_MASK) + +static inline void pte_clear(struct mm_struct *mm, + unsigned long addr, pte_t *ptep) +{ + pte_t null; + + pte_val(null) = (addr >> PAGE_SHIFT) & 0xf; + + set_pte_at(mm, addr, ptep, null); + flush_tlb_one(addr); +} + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ +#define mk_pte(page, prot) (pfn_pte(page_to_pfn(page), prot)) + +#define pte_unmap(pte) do { } while (0) + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ +#define pmd_phys(pmd) virt_to_phys((void *)pmd_val(pmd)) +#define pmd_page(pmd) (pfn_to_page(pmd_phys(pmd) >> PAGE_SHIFT)) +#define pmd_page_vaddr(pmd) pmd_val(pmd) + +#define pte_offset_map(dir, addr) \ + ((pte_t *) page_address(pmd_page(*dir)) + \ + (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) + +/* to find an entry in a kernel page-table-directory */ +#define pgd_offset_k(addr) pgd_offset(&init_mm, addr) + +/* Get the address to the PTE for a vaddr in specfic directory */ +#define pte_offset_kernel(dir, addr) \ + ((pte_t *) pmd_page_vaddr(*(dir)) + \ + (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) + +#define pte_ERROR(e) \ + pr_err("%s:%d: bad pte %08lx.\n", \ + __FILE__, __LINE__, pte_val(e)) +#define pgd_ERROR(e) \ + pr_err("%s:%d: bad pgd %08lx.\n", \ + __FILE__, __LINE__, pgd_val(e)) + +/* + * Encode and decode a swap entry (must be !pte_none(pte) && !pte_present(pte) + * && !pte_file(pte)): + * + * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 ... 1 0 + * 0 0 0 0 type. 0 0 0 0 0 0 offset......... + * + * This gives us up to 2**2 = 4 swap files and 2**20 * 4K = 4G per swap file. * + * Note that the offset field is always non-zero, thus !pte_none(pte) is always + * true. */ +#define __swp_type(swp) (((swp).val >> 26) & 0x3) +#define __swp_offset(swp) ((swp).val & 0xfffff) +#define __swp_entry(type, off) ((swp_entry_t) { (((type) & 0x3) << 26) \ + | ((off) & 0xfffff) }) +#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) + +/* Encode and decode a nonlinear file mapping entry */ +#define PTE_FILE_MAX_BITS 25 +#define pte_to_pgoff(pte) (pte_val(pte) & 0x1ffffff) +#define pgoff_to_pte(off) __pte(((off) & 0x1ffffff) | _PAGE_FILE) + +#define kern_addr_valid(addr) (1) + +#include + +#define pgtable_cache_init() do { } while (0) + +extern void __init paging_init(void); +extern void __init mmu_init(void); + +extern void update_mmu_cache(struct vm_area_struct *vma, + unsigned long address, pte_t *pte); -#ifdef CONFIG_MMU -# include -#else -# include -#endif +#endif /* _ASM_NIOS2_PGTABLE_H */ diff --git a/arch/nios2/include/asm/pgtable_mm.h b/arch/nios2/include/asm/pgtable_mm.h deleted file mode 100644 index c4cf0b5906dfa..0000000000000 --- a/arch/nios2/include/asm/pgtable_mm.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) 2011 Tobias Klauser - * Copyright (C) 2009 Wind River Systems Inc - * - * Based on asm/pgtable-32.h from mips which is: - * - * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 2003 Ralf Baechle - * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc. - * - * 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. - */ - -#ifndef _ASM_NIOS2_PGTABLE_H -#define _ASM_NIOS2_PGTABLE_H - -#include -#include -#include -#include -#include - -#include -#include - -#define FIRST_USER_ADDRESS 0 - -#define VMALLOC_START CONFIG_KERNEL_MMU_REGION_BASE -#define VMALLOC_END (CONFIG_KERNEL_REGION_BASE - 1) - -struct mm_struct; - -/* Helper macro */ -#define MKP(x, w, r) __pgprot(_PAGE_PRESENT | _PAGE_CACHED | \ - ((x) ? _PAGE_EXEC : 0) | \ - ((r) ? _PAGE_READ : 0) | \ - ((w) ? _PAGE_WRITE : 0)) -/* - * These are the macros that generic kernel code needs - * (to populate protection_map[]) - */ - -/* Remove W bit on private pages for COW support */ -#define __P000 MKP(0, 0, 0) -#define __P001 MKP(0, 0, 1) -#define __P010 MKP(0, 0, 0) /* COW */ -#define __P011 MKP(0, 0, 1) /* COW */ -#define __P100 MKP(1, 0, 0) -#define __P101 MKP(1, 0, 1) -#define __P110 MKP(1, 0, 0) /* COW */ -#define __P111 MKP(1, 0, 1) /* COW */ - -/* Shared pages can have exact HW mapping */ -#define __S000 MKP(0, 0, 0) -#define __S001 MKP(0, 0, 1) -#define __S010 MKP(0, 1, 0) -#define __S011 MKP(0, 1, 1) -#define __S100 MKP(1, 0, 0) -#define __S101 MKP(1, 0, 1) -#define __S110 MKP(1, 1, 0) -#define __S111 MKP(1, 1, 1) - -/* Used all over the kernel */ -#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_CACHED | _PAGE_READ | \ - _PAGE_WRITE | _PAGE_EXEC | _PAGE_GLOBAL) - -#define PAGE_COPY MKP(0, 0, 1) - -#define PGD_ORDER 0 -#define PTE_ORDER 0 - -#define PTRS_PER_PGD ((PAGE_SIZE << PGD_ORDER) / sizeof(pgd_t)) -#define PTRS_PER_PTE ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t)) - -#define USER_PTRS_PER_PGD (CONFIG_KERNEL_MMU_REGION_BASE / PGDIR_SIZE) - -#define PGDIR_SHIFT 22 -#define PGDIR_SIZE (1UL << PGDIR_SHIFT) -#define PGDIR_MASK (~(PGDIR_SIZE-1)) - -/* - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc.. - */ -extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; -#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) - -extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; -extern pte_t invalid_pte_table[PAGE_SIZE/sizeof(pte_t)]; - -/* - * (pmds are folded into puds so this doesn't get actually called, - * but the define is needed for a generic inline function.) - */ -static inline void set_pmd(pmd_t *pmdptr, pmd_t pmdval) -{ - pmdptr->pud.pgd.pgd = pmdval.pud.pgd.pgd; -} - -/* to find an entry in a page-table-directory */ -#define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) -#define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr)) - -static inline int pte_write(pte_t pte) \ - { return pte_val(pte) & _PAGE_WRITE; } -static inline int pte_dirty(pte_t pte) \ - { return pte_val(pte) & _PAGE_DIRTY; } -static inline int pte_young(pte_t pte) \ - { return pte_val(pte) & _PAGE_ACCESSED; } -static inline int pte_file(pte_t pte) \ - { return pte_val(pte) & _PAGE_FILE; } -static inline int pte_special(pte_t pte) { return 0; } - -#define pgprot_noncached pgprot_noncached - -static inline pgprot_t pgprot_noncached(pgprot_t _prot) -{ - unsigned long prot = pgprot_val(_prot); - - prot &= ~_PAGE_CACHED; - - return __pgprot(prot); -} - -/* - * FIXME: Today unmapped pages are mapped to the low physical addresses - * and not 0 (to avoid to trigger the false alias detection in the iss) - * Also check pte_clear. - */ -static inline int pte_none(pte_t pte) -{ - return !(pte_val(pte) & ~(_PAGE_GLOBAL|0xf)); -} - -static inline int pte_present(pte_t pte) \ - { return pte_val(pte) & _PAGE_PRESENT; } - -/* - * The following only work if pte_present() is true. - * Undefined behaviour if not.. - */ -static inline pte_t pte_wrprotect(pte_t pte) -{ - pte_val(pte) &= ~_PAGE_WRITE; - return pte; -} - -static inline pte_t pte_mkclean(pte_t pte) -{ - pte_val(pte) &= ~_PAGE_DIRTY; - return pte; -} - -static inline pte_t pte_mkold(pte_t pte) -{ - pte_val(pte) &= ~_PAGE_ACCESSED; - return pte; -} - -static inline pte_t pte_mkwrite(pte_t pte) -{ - pte_val(pte) |= _PAGE_WRITE; - return pte; -} - -static inline pte_t pte_mkdirty(pte_t pte) -{ - pte_val(pte) |= _PAGE_DIRTY; - return pte; -} - -static inline pte_t pte_mkspecial(pte_t pte) { return pte; } - -static inline pte_t pte_mkyoung(pte_t pte) -{ - pte_val(pte) |= _PAGE_ACCESSED; - return pte; -} - -static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) -{ - const unsigned long mask = _PAGE_READ | _PAGE_WRITE | _PAGE_EXEC; - pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask); - return pte; -} - -static inline int pmd_present(pmd_t pmd) -{ - return (pmd_val(pmd) != (unsigned long) invalid_pte_table) - && (pmd_val(pmd) != 0UL); -} - -static inline void pmd_clear(pmd_t *pmdp) -{ - pmd_val(*pmdp) = (unsigned long) invalid_pte_table; -} - -#define pte_pfn(pte) (pte_val(pte) & 0xfffff) -#define pfn_pte(pfn, prot) (__pte(pfn | pgprot_val(prot))) -#define pte_page(pte) (pfn_to_page(pte_pfn(pte))) - -/* - * Store a linux PTE into the linux page table. - */ -static inline void set_pte(pte_t *ptep, pte_t pteval) -{ - *ptep = pteval; -} - -static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t pteval) -{ - unsigned long paddr = page_to_virt(pte_page(pteval)); - flush_dcache_range(paddr, paddr + PAGE_SIZE); - set_pte(ptep, pteval); -} - -static inline int pmd_none(pmd_t pmd) -{ - return (pmd_val(pmd) == - (unsigned long) invalid_pte_table) || (pmd_val(pmd) == 0UL); -} - -#define pmd_bad(pmd) (pmd_val(pmd) & ~PAGE_MASK) - -static inline void pte_clear(struct mm_struct *mm, - unsigned long addr, pte_t *ptep) -{ - pte_t null; - - pte_val(null) = (addr >> PAGE_SHIFT) & 0xf; - - set_pte_at(mm, addr, ptep, null); - flush_tlb_one(addr); -} - -/* - * Conversion functions: convert a page and protection to a page entry, - * and a page entry and page directory to the page they refer to. - */ -#define mk_pte(page, prot) (pfn_pte(page_to_pfn(page), prot)) - -#define pte_unmap(pte) do { } while (0) - -/* - * Conversion functions: convert a page and protection to a page entry, - * and a page entry and page directory to the page they refer to. - */ -#define pmd_phys(pmd) virt_to_phys((void *)pmd_val(pmd)) -#define pmd_page(pmd) (pfn_to_page(pmd_phys(pmd) >> PAGE_SHIFT)) -#define pmd_page_vaddr(pmd) pmd_val(pmd) - -#define pte_offset_map(dir, addr) \ - ((pte_t *) page_address(pmd_page(*dir)) + \ - (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) - -/* to find an entry in a kernel page-table-directory */ -#define pgd_offset_k(addr) pgd_offset(&init_mm, addr) - -/* Get the address to the PTE for a vaddr in specfic directory */ -#define pte_offset_kernel(dir, addr) \ - ((pte_t *) pmd_page_vaddr(*(dir)) + \ - (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))) - -#define pte_ERROR(e) \ - pr_err("%s:%d: bad pte %08lx.\n", \ - __FILE__, __LINE__, pte_val(e)) -#define pgd_ERROR(e) \ - pr_err("%s:%d: bad pgd %08lx.\n", \ - __FILE__, __LINE__, pgd_val(e)) - -/* - * Encode and decode a swap entry (must be !pte_none(pte) && !pte_present(pte) - * && !pte_file(pte)): - * - * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 ... 1 0 - * 0 0 0 0 type. 0 0 0 0 0 0 offset......... - * - * This gives us up to 2**2 = 4 swap files and 2**20 * 4K = 4G per swap file. - * - * Note that the offset field is always non-zero, thus !pte_none(pte) is always - * true. - */ -#define __swp_type(swp) (((swp).val >> 26) & 0x3) -#define __swp_offset(swp) ((swp).val & 0xfffff) -#define __swp_entry(type, off) ((swp_entry_t) { (((type) & 0x3) << 26) \ - | ((off) & 0xfffff) }) -#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val }) -#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) - -/* Encode and decode a nonlinear file mapping entry */ -#define PTE_FILE_MAX_BITS 25 -#define pte_to_pgoff(pte) (pte_val(pte) & 0x1ffffff) -#define pgoff_to_pte(off) __pte(((off) & 0x1ffffff) | _PAGE_FILE) - -#define kern_addr_valid(addr) (1) - -#include - -#define pgtable_cache_init() do { } while (0) - -extern void __init paging_init(void); -extern void __init mmu_init(void); - -extern void update_mmu_cache(struct vm_area_struct *vma, - unsigned long address, pte_t *pte); - -#endif /* _ASM_NIOS2_PGTABLE_H */ diff --git a/arch/nios2/include/asm/pgtable_no.h b/arch/nios2/include/asm/pgtable_no.h deleted file mode 100644 index 56bec03394b73..0000000000000 --- a/arch/nios2/include/asm/pgtable_no.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2011 Tobias Klauser - * Copyright (C) 2004 Microtronix Datacom Ltd - * - * Based on asm/pgtable_no.h from m68k which is: - * - * Copyright (C) 2000-2002 Greg Ungerer - * - * 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. - */ - -#ifndef _ASM_NIOS2_PGTABLE_NO_H -#define _ASM_NIOS2_PGTABLE_NO_H - -#include - -#include - -typedef pte_t *pte_addr_t; - -#define pgd_present(pgd) (1) /* pages are always present on NO_MM */ -#define pgd_none(pgd) (0) -#define pgd_bad(pgd) (0) -#define pgd_clear(pgdp) -#define kern_addr_valid(addr) (1) -#define pmd_offset(a, b) ((void *)0) - -#define PAGE_NONE __pgprot(0) /* these mean nothing to NO_MM */ -#define PAGE_SHARED __pgprot(0) /* these mean nothing to NO_MM */ -#define PAGE_COPY __pgprot(0) /* these mean nothing to NO_MM */ -#define PAGE_READONLY __pgprot(0) /* these mean nothing to NO_MM */ -#define PAGE_KERNEL __pgprot(0) /* these mean nothing to NO_MM */ - -extern void paging_init(void); -#define swapper_pg_dir ((pgd_t *) 0) - -#define __swp_type(x) (0) -#define __swp_offset(x) (0) -#define __swp_entry(typ, off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) -#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) -#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) - -static inline int pte_file(pte_t pte) { return 0; } - -/* - * ZERO_PAGE is a global shared page that is always zero: used - * for zero-mapped memory areas etc.. - */ -#define ZERO_PAGE(vaddr) (virt_to_page(0)) - -/* - * No page table caches to initialise - */ -#define pgtable_cache_init() do { } while (0) - -/* - * All 32bit addresses are effectively valid for vmalloc... - * Sort of meaningless for non-VM targets. - */ -#define VMALLOC_START 0 -#define VMALLOC_END 0xffffffff - -#define arch_enter_lazy_mmu_mode() do {} while (0) -#define arch_leave_lazy_mmu_mode() do {} while (0) -#define arch_flush_lazy_mmu_mode() do {} while (0) -#define arch_enter_lazy_cpu_mode() do {} while (0) -#define arch_leave_lazy_cpu_mode() do {} while (0) -#define arch_flush_lazy_cpu_mode() do {} while (0) - -#include - -/* We provide a special get_unmapped_area for framebuffer mmaps of nommu */ -extern unsigned long get_fb_unmapped_area(struct file *filp, unsigned long, - unsigned long, unsigned long, - unsigned long); -#define HAVE_ARCH_FB_UNMAPPED_AREA - -#endif /* _ASM_NIOS2_PGTABLE_NO_H */ diff --git a/arch/nios2/include/asm/processor.h b/arch/nios2/include/asm/processor.h index b7ab1a046a6d0..aeff62f08fa22 100644 --- a/arch/nios2/include/asm/processor.h +++ b/arch/nios2/include/asm/processor.h @@ -25,14 +25,12 @@ #define NIOS2_OP_NOP 0x1883a #define NIOS2_OP_BREAK 0x3da03a -#ifdef CONFIG_MMU #ifdef __KERNEL__ #define STACK_TOP TASK_SIZE #define STACK_TOP_MAX STACK_TOP #endif /* __KERNEL__ */ -#endif /* CONFIG_MMU */ #ifndef __ASSEMBLY__ @@ -42,22 +40,8 @@ */ #define current_text_addr() ({ __label__ _l; _l: &&_l; }) -#ifdef CONFIG_MMU # define TASK_SIZE 0x7FFF0000UL # define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) -#else -/* - * User space process size: 1st byte beyond user address space. - * Fairly meaningless on nommu. Parts of user programs can be scattered - * in a lot of places, so just disable this by setting it to 0xFFFFFFFF. - */ -# define TASK_SIZE 0xFFFFFFFFUL -/* - * This decides where the kernel will search for a free chunk of vm - * space during mmap's. We won't be using it. - */ -# define TASK_UNMAPPED_BASE 0 -#endif /* CONFIG_MMU */ /* The Nios processor specific thread struct. */ struct thread_struct { @@ -66,27 +50,16 @@ struct thread_struct { /* Context switch saved kernel state. */ unsigned long ksp; unsigned long kpsr; -#ifndef CONFIG_MMU - unsigned long kesr; -#endif }; #define INIT_MMAP \ { &init_mm, (0), (0), __pgprot(0x0), VM_READ | VM_WRITE | VM_EXEC } -#ifdef CONFIG_MMU # define INIT_THREAD { \ .kregs = NULL, \ .ksp = 0, \ .kpsr = 0, \ } -#else -# define INIT_THREAD { \ - .kregs = NULL, \ - .ksp = sizeof(init_stack) + (unsigned long) init_stack, \ - .kpsr = 0, \ -} -#endif /* CONFIG_MMU */ extern void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp); diff --git a/arch/nios2/include/asm/ptrace.h b/arch/nios2/include/asm/ptrace.h index 92969bf7902d1..ca8588ed8c32e 100644 --- a/arch/nios2/include/asm/ptrace.h +++ b/arch/nios2/include/asm/ptrace.h @@ -15,10 +15,6 @@ #include -#if defined __KERNEL__ && !defined CONFIG_MMU -# define PS_S 0x00000001 -#endif - #ifndef __ASSEMBLY__ /* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ @@ -29,11 +25,8 @@ * Supervisor mode */ -#ifdef CONFIG_MMU # define user_mode(regs) (((regs)->estatus & ESTATUS_EU)) -#else -# define user_mode(regs) (!((regs)->status_extension & PS_S)) -#endif /* CONFIG_MMU */ + #define instruction_pointer(regs) ((regs)->ra) #define profile_pc(regs) instruction_pointer(regs) diff --git a/arch/nios2/include/asm/registers.h b/arch/nios2/include/asm/registers.h index 99d73bdc966f8..d37a717011e87 100644 --- a/arch/nios2/include/asm/registers.h +++ b/arch/nios2/include/asm/registers.h @@ -51,12 +51,8 @@ #define ESTATUS_EU (1 << 1) /* user mode */ #define ESTATUS_EH (1 << 2) /* Exception mode */ -#ifdef CONFIG_MMU - /* tlbmisc register bits */ #define TLBMISC_WE (1 << 18) /* TLB write enable */ #define TLBMISC_RD (1 << 19) /* TLB read */ -#endif /* CONFIG_MMU */ - #endif /* _ASM_NIOS2_REGISTERS_H */ diff --git a/arch/nios2/include/asm/thread_info.h b/arch/nios2/include/asm/thread_info.h index 8b516a9e62b1c..d88cbcaffa7a2 100644 --- a/arch/nios2/include/asm/thread_info.h +++ b/arch/nios2/include/asm/thread_info.h @@ -48,9 +48,7 @@ struct thread_info { 0-0xFFFFFFFF for kernel-thread */ struct restart_block restart_block; -#ifdef CONFIG_MMU struct pt_regs *regs; -#endif }; /* diff --git a/arch/nios2/include/asm/tlb.h b/arch/nios2/include/asm/tlb.h index 6c3689af046dd..d3bc648e08b5d 100644 --- a/arch/nios2/include/asm/tlb.h +++ b/arch/nios2/include/asm/tlb.h @@ -13,8 +13,6 @@ #define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) -#ifdef CONFIG_MMU - extern void set_mmu_pid(unsigned long pid); /* @@ -27,12 +25,6 @@ extern void set_mmu_pid(unsigned long pid); flush_cache_range(vma, vma->vm_start, vma->vm_end); \ } while (0) -#else - -#define tlb_start_vma(tlb, vma) do { } while (0) - -#endif /* CONFIG_MMU */ - #define tlb_end_vma(tlb, vma) do { } while (0) #define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0) diff --git a/arch/nios2/include/asm/tlbflush.h b/arch/nios2/include/asm/tlbflush.h index 68d778ce55dcd..e19652fca1c68 100644 --- a/arch/nios2/include/asm/tlbflush.h +++ b/arch/nios2/include/asm/tlbflush.h @@ -19,10 +19,6 @@ #ifndef _ASM_NIOS2_TLBFLUSH_H #define _ASM_NIOS2_TLBFLUSH_H -#ifndef CONFIG_MMU -# include -#else - struct mm_struct; /* @@ -47,6 +43,4 @@ static inline void flush_tlb_page(struct vm_area_struct *vma, flush_tlb_one(addr); } -#endif /* CONFIG_MMU */ - #endif /* _ASM_NIOS2_TLBFLUSH_H */ diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h index 8c6d541b03f02..3e36f536afaba 100644 --- a/arch/nios2/include/asm/uaccess.h +++ b/arch/nios2/include/asm/uaccess.h @@ -41,9 +41,7 @@ struct exception_table_entry { unsigned long fixup; }; -#ifdef CONFIG_MMU extern int fixup_exception(struct pt_regs *regs); -#endif /* * Segment stuff @@ -59,26 +57,14 @@ extern int fixup_exception(struct pt_regs *regs); #define segment_eq(a, b) ((a).seg == (b).seg) -#ifdef CONFIG_MMU #define __access_ok(addr, len) \ (((signed long)(((long)get_fs().seg) & \ ((long)(addr) | (((long)(addr)) + (len)) | (len)))) == 0) -#else -static inline int __access_ok(unsigned long addr, unsigned long size) -{ - addr &= ~CONFIG_IO_REGION_BASE; /* ignore 'uncached' bit */ - return ((addr >= CONFIG_MEM_BASE) && ((addr + size) <= memory_end)); -} -#endif /* CONFIG_MMU */ #define access_ok(type, addr, len) \ likely(__access_ok((unsigned long)(addr), (unsigned long)(len))) -#ifdef CONFIG_MMU # define __EX_TABLE_SECTION ".section __ex_table,\"a\"\n" -#else -# define __EX_TABLE_SECTION ".section .discard,\"a\"\n" -#endif /* * Zero Userspace @@ -111,7 +97,6 @@ static inline unsigned long __must_check clear_user(void __user *to, return __clear_user(to, n); } -#ifdef CONFIG_MMU extern long __copy_from_user(void *to, const void __user *from, unsigned long n); extern long __copy_to_user(void __user *to, const void *from, unsigned long n); @@ -136,44 +121,9 @@ extern long strncpy_from_user(char *__to, const char __user *__from, long __len); extern long strnlen_user(const char __user *s, long n); -#else /* CONFIG_MMU */ -# define copy_from_user(to, from, n) (memcpy(to, from, n), 0) -# define copy_to_user(to, from, n) (memcpy(to, from, n), 0) - -# define __copy_from_user(to, from, n) copy_from_user(to, from, n) -# define __copy_to_user(to, from, n) copy_to_user(to, from, n) - -static inline long strncpy_from_user(char *dst, const char *src, long count) -{ - char *tmp; - strncpy(dst, src, count); - for (tmp = dst; *tmp && count > 0; tmp++, count--) - ; - return tmp - dst; /* DAVIDM should we count a NUL ? check getname */ -} - -/* - * Return the size of a string (including the ending 0) - * - * Return 0 on exception, a value greater than N if too long - */ -static inline long strnlen_user(const char *src, long n) -{ - return strlen(src) + 1; /* DAVIDM make safer */ -} - -#endif /* CONFIG_MMU */ - #define __copy_from_user_inatomic __copy_from_user #define __copy_to_user_inatomic __copy_to_user -/* - * TODO: get_user/put_user stuff below can probably be the same for MMU and - * NOMMU. - */ - -#ifdef CONFIG_MMU - /* Optimized macros */ #define __get_user_asm(val, insn, addr, err) \ { \ @@ -280,57 +230,4 @@ do { \ #define __put_user(x, ptr) put_user(x, ptr) -#else /* CONFIG_MMU */ - -/* - * These are the main single-value transfer routines. They automatically - * use the right size if we just have the right pointer type. - */ - -#define put_user(x, ptr) \ -({ \ - int __pu_err = 0; \ - __typeof__(*(ptr)) __pu_val = (x); \ - switch (sizeof(*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - case 8: \ - memcpy(ptr, &__pu_val, sizeof(*(ptr))); \ - break; \ - default: \ - __pu_err = __put_user_bad(); \ - break; \ - } \ - __pu_err; \ -}) -#define __put_user(x, ptr) put_user(x, ptr) - -extern int __put_user_bad(void); - -#define get_user(x, ptr) \ -({ \ - int __gu_err = 0; \ - typeof(*(ptr)) __gu_val = 0; \ - switch (sizeof(*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - case 8: \ - memcpy(&__gu_val, ptr, sizeof(*(ptr))); \ - break; \ - default: \ - __gu_val = 0; \ - __gu_err = __get_user_bad(); \ - break; \ - } \ - (x) = __gu_val; \ - __gu_err; \ -}) -#define __get_user(x, ptr) get_user(x, ptr) - -extern int __get_user_bad(void); - -#endif /* CONFIG_MMU */ - #endif /* _ASM_NIOS2_UACCESS_H */ diff --git a/arch/nios2/include/uapi/asm/stat.h b/arch/nios2/include/uapi/asm/stat.h index 109ec1889e4fa..7909a7eef7086 100644 --- a/arch/nios2/include/uapi/asm/stat.h +++ b/arch/nios2/include/uapi/asm/stat.h @@ -68,12 +68,7 @@ struct stat64 { long long st_size; unsigned long st_blksize; -#ifdef CONFIG_MMU unsigned long long st_blocks; /* Number 512-byte blocks allocated. */ -#else - unsigned long __pad4; /* future possible st_blocks high bits */ - unsigned long st_blocks; /* Number 512-byte blocks allocated. */ -#endif unsigned long st_atime; unsigned long st_atime_nsec; diff --git a/arch/nios2/kernel/Makefile b/arch/nios2/kernel/Makefile index 7ad727731a126..6ae94056cff59 100644 --- a/arch/nios2/kernel/Makefile +++ b/arch/nios2/kernel/Makefile @@ -4,7 +4,7 @@ extra-y := head.o vmlinux.lds -obj-y += entry$(MMU).o +obj-y += entry.o obj-y += cpuinfo.o insnemu.o irq.o nios2_ksyms.o process.o prom.o ptrace.o \ setup.o signal.o sys_nios2.o syscalltable.o time.o traps.o diff --git a/arch/nios2/kernel/asm-offsets.c b/arch/nios2/kernel/asm-offsets.c index 7187fa62a8d2e..3852f5c970aa9 100644 --- a/arch/nios2/kernel/asm-offsets.c +++ b/arch/nios2/kernel/asm-offsets.c @@ -33,16 +33,12 @@ int main(void) /* struct thread_struct */ OFFSET(THREAD_KSP, thread_struct, ksp); OFFSET(THREAD_KPSR, thread_struct, kpsr); -#ifndef CONFIG_MMU - OFFSET(THREAD_KESR, thread_struct, kesr); -#endif BLANK(); /* struct pt_regs */ OFFSET(PT_ORIG_R2, pt_regs, orig_r2); -#ifdef CONFIG_MMU OFFSET(PT_ORIG_R7, pt_regs, orig_r7); -#endif + OFFSET(PT_R1, pt_regs, r1); OFFSET(PT_R2, pt_regs, r2); OFFSET(PT_R3, pt_regs, r3); @@ -64,9 +60,6 @@ int main(void) OFFSET(PT_SP, pt_regs, sp); OFFSET(PT_GP, pt_regs, gp); OFFSET(PT_ESTATUS, pt_regs, estatus); -#ifndef CONFIG_MMU - OFFSET(PT_STATUS_EXTENSION, pt_regs, status_extension); -#endif DEFINE(PT_REGS_SIZE, sizeof(struct pt_regs)); BLANK(); diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index cb91e54645ef9..02bdafaf5bbce 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -56,12 +56,12 @@ void __init setup_cpuinfo(void) if (!cpu) panic("%s: No CPU found in devicetree!\n", __func__); - /* for now */ -#ifdef CONFIG_MMU - cpuinfo.mmu = 1; -#else - cpuinfo.mmu = 0; -#endif + cpuinfo.mmu = fcpu_has(cpu, "ALTR,has-mmu"); + if (!cpuinfo.mmu) { + panic("ERROR: Can't get 'ALTR,has-mmu' from device tree. Only support" + " Nios II with MMU enabled. Please enable MMU in Nios II " + "hardware."); + } cpuinfo.cpu_clock_freq = fcpu(cpu, "clock-frequency"); @@ -148,12 +148,11 @@ static int show_cpuinfo(struct seq_file *m, void *v) cpuinfo.dcache_size >> 10, cpuinfo.dcache_line_size); - if (cpuinfo.mmu) - count += seq_printf(m, - "TLB:\t\t%u ways, %u entries, %u PID bits\n", - cpuinfo.tlb_num_ways, - cpuinfo.tlb_num_entries, - cpuinfo.tlb_pid_num_bits); + count += seq_printf(m, + "TLB:\t\t%u ways, %u entries, %u PID bits\n", + cpuinfo.tlb_num_ways, + cpuinfo.tlb_num_entries, + cpuinfo.tlb_pid_num_bits); return 0; } diff --git a/arch/nios2/kernel/entry-nommu.S b/arch/nios2/kernel/entry-nommu.S deleted file mode 100644 index 60c8cb1623d39..0000000000000 --- a/arch/nios2/kernel/entry-nommu.S +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) - * Copyright (C) 1998 D. Jeff Dionne , - * Kenneth Albanowski , - * Copyright (C) 2000 Lineo Inc. (www.lineo.com) - * Copyright (C) 2004 Microtronix Datacom Ltd. - * Copyright (C) 2013 Altera Corporation - * - * Based on entry.S from m68knommu. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -.macro GET_THREAD_INFO reg -.if THREAD_SIZE & 0xffff0000 - andhi \reg, sp, %hi(~(THREAD_SIZE-1)) -.else - addi \reg, r0, %lo(~(THREAD_SIZE-1)) - and \reg, \reg, sp -.endif -.endm - -.text -.set noat -.set nobreak - -ENTRY(system_call) -/* SAVE_ALL */ - rdctl r10, status /* enable intrs again */ - ori r10, r10, STATUS_PIE - wrctl status, r10 - - movi r2, -ENOSYS - stw r2, PT_R2(sp) /* default return value in r2 */ - /* original r2 is in orig_r2 */ - - movui r1, NR_syscalls - bgeu r3, r1, ret_from_exception - slli r1, r3, 2 - movhi r11, %hiadj(sys_call_table) - add r1, r1, r11 - ldw r1, %lo(sys_call_table)(r1) - beq r1, r0, ret_from_exception - - movi r11, %lo(0xffffe000) /* Get thread info pointer */ - and r11, sp, r11 - ldw r11, TI_FLAGS(r11) - BTBNZ r11, r11, TIF_SYSCALL_TRACE, 1f - - callr r1 - stw r2, PT_R2(sp) /* save the return value */ - br ret_from_exception -1: - SAVE_SWITCH_STACK - call syscall_trace - RESTORE_SWITCH_STACK - /* wentao: restore r4-9, since they are trashed by syscall_trace */ - ldw r4, PT_R4(sp) - ldw r5, PT_R5(sp) - ldw r6, PT_R6(sp) - ldw r7, PT_R7(sp) - ldw r8, PT_R8(sp) - ldw r9, PT_R9(sp) - callr r1 - stw r2, PT_R2(sp) /* save the return value */ - SAVE_SWITCH_STACK - call syscall_trace - RESTORE_SWITCH_STACK - -ret_from_exception: - ldw r1, PT_STATUS_EXTENSION(sp) /* check if returning to kernel */ - TSTBZ r1, r1, PS_S, Luser_return /* if so, skip resched, signals */ - -restore_all: - rdctl r10, status /* disable intrs */ - andi r10, r10, %lo(~STATUS_PIE) - wrctl status, r10 - RESTORE_ALL - eret - -Luser_return: - GET_THREAD_INFO r24 /* get thread_info pointer */ - ldw r10, TI_FLAGS(r24) /* get thread_info->flags */ - ANDI32 r11, r10, _TIF_WORK_MASK - beq r11, r0, restore_all /* Nothing to do */ - BTBZ r1, r10, TIF_NEED_RESCHED, Lsignal_return - -Lwork_resched: - call schedule - br ret_from_exception - -Lsignal_return: - ANDI32 r1, r10, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME - beq r1, r0, restore_all - mov r4, sp /* pt_regs */ - SAVE_SWITCH_STACK - mov r5, r0 /* oldset = 0 */ - movi r6, 1 /* in_syscall = 1 */ - call do_notify_resume - RESTORE_SWITCH_STACK - br restore_all - -/* - * Handle software exceptions. Put here so external interrupts - * can fall throught to ret_from_interrupt. - */ - -software_exception: - ldw r24, -4(ea) /* instruction that caused the exception */ - xorhi r24, r24, 0x003b /* upper half of trap opcode */ - xori r24, r24, 0x683a /* lower half of trap opcode */ - cmpeqi r11, r24, 0x40 /* Check for imm=0x01 => breakpoint */ - bne r11, r0, breakpoint - bne r24, r0, instruction_trap /* N - check for instruction trap */ - cmpeqi r11, r2, TRAP_ID_SYSCALL /* ? Is this a syscall */ - bne r11, r0, system_call /* Y - handle syscall */ - cmpeqi r11, r2,63 /* ? Is this the old syscall number */ - bne r11, r0, system_call /* Y - handle syscall to catch older apps*/ - br restore_all /* N - everything else is ignored for now */ - -breakpoint: - mov r4, sp - call breakpoint_c - br restore_all - -/* - * This is the generic interrupt handler (for all hardware interrupt - * sources). It figures out the vector number and calls the appropriate - * interrupt service routine directly. - */ -ENTRY(inthandler) - SAVE_ALL - /* - * Test to see if the exception was a software exception or caused by an - * external interrupt, and vector accordingly. - */ - - rdctl r24, estatus - andi r24, r24, ESTATUS_EPIE - beq r24, r0, software_exception - rdctl r12, ipending - rdctl r9, ienable - and r12, r12, r9 - beq r12, r0, software_exception - - movi r24, -1 - stw r24, PT_ORIG_R2(sp) - - /* - * Process an external hardware interrupt. - */ - - addi ea, ea, -4 /* re-issue the interrupted instruction */ - stw ea, PT_EA(sp) -2: movi r4, %lo(-1) /* Start from bit position 0, highest priority */ - /* This is the IRQ # for handler call */ -1: andi r10, r12, 1 /* Isolate bit we are interested in */ - srli r12, r12, 1 /* shift count is costly without hardware - multiplier */ - addi r4, r4, 1 - beq r10, r0, 1b - mov r5, sp /* Setup pt_regs pointer for handler call */ - call do_IRQ - rdctl r12, ipending /* check again if irq still pending */ - rdctl r9, ienable /* Isolate possible interrupts */ - and r12, r12, r9 - bne r12, r0, 2b - /* br ret_from_interrupt */ /* fall throught to ret_from_interrupt */ - -ENTRY(ret_from_interrupt) - ldw r4, PT_STATUS_EXTENSION(sp) - TSTBZ r4, r4, PS_S, Luser_return /* Returning to user */ - -#ifdef CONFIG_PREEMPT - GET_THREAD_INFO r1 - ldw r4, TI_PREEMPT_COUNT(r1) - bne r4, r0, restore_all - -need_resched: - ldw r4, TI_FLAGS(r1) /* ? Need resched set */ - BTBZ r10, r4, TIF_NEED_RESCHED, restore_all - ldw r4, PT_ESTATUS(sp) /* ? Interrupts off */ - andi r10, r4, STATUS_PIE - beq r10, r0, restore_all - movia r4, PREEMPT_ACTIVE - stw r4, TI_PREEMPT_COUNT(r1) - rdctl r10, status /* enable intrs again */ - ori r10, r10, STATUS_PIE - wrctl status, r10 - PUSH r1 - call schedule - POP r1 - mov r4, r0 - stw r4, TI_PREEMPT_COUNT(r1) - rdctl r10, status /* disable intrs */ - andi r10, r10, %lo(~STATUS_PIE) - wrctl status, r10 - br need_resched -#else - br restore_all -#endif - - -/* - * Beware - when entering resume, prev (the current task) is - * in r4, next (the new task) is in r5, don't change these - * registers. - */ -ENTRY(resume) - - rdctl r7, status /* save thread status reg */ - stw r7, TASK_THREAD + THREAD_KPSR(r4) - - andi r7, r7, %lo(~STATUS_PIE) /* disable interrupts */ - wrctl status, r7 - - movia r8, status_extension /* save status extension */ - ldw r7, 0(r8) - stw r7, TASK_THREAD + THREAD_KESR(r4) - - SAVE_SWITCH_STACK - stw sp, TASK_THREAD + THREAD_KSP(r4)/* save kernel stack pointer */ - ldw sp, TASK_THREAD + THREAD_KSP(r5)/* restore new thread stack */ - movia r24, _current_thread /* save thread */ - GET_THREAD_INFO r1 - stw r1, 0(r24) - RESTORE_SWITCH_STACK - - ldw r7, TASK_THREAD + THREAD_KESR(r5) /* restore extended status - reg */ - stw r7, 0(r8) - - ldw r7, TASK_THREAD +TH READ_KPSR(r5)/* restore thread status reg */ - wrctl status, r7 - ret - -ENTRY(ret_from_fork) - call schedule_tail - br ret_from_exception - -ENTRY(ret_from_kernel_thread) - call schedule_tail - mov r4,r17 /* arg */ - callr r16 /* function */ - br ret_from_exception - -ENTRY(__sys_fork) - SAVE_SWITCH_STACK - call sys_fork - RESTORE_SWITCH_STACK - ret - -ENTRY(__sys_vfork) - SAVE_SWITCH_STACK - call sys_vfork - RESTORE_SWITCH_STACK - ret - -ENTRY(__sys_clone) - mov r4,sp - SAVE_SWITCH_STACK - call nios2_clone - RESTORE_SWITCH_STACK - ret - -ENTRY(sys_sigreturn) - mov r4, sp - SAVE_SWITCH_STACK - call do_sigreturn - RESTORE_SWITCH_STACK - ret - -ENTRY(sys_rt_sigreturn) - SAVE_SWITCH_STACK - mov r4, sp - call do_rt_sigreturn - RESTORE_SWITCH_STACK - ret diff --git a/arch/nios2/kernel/head.S b/arch/nios2/kernel/head.S index fe53e75528002..cff8fa4f610af 100644 --- a/arch/nios2/kernel/head.S +++ b/arch/nios2/kernel/head.S @@ -23,7 +23,6 @@ #include #include -#ifdef CONFIG_MMU /* * ZERO_PAGE is a special page that is used for zero-initialized * data and COW. @@ -33,7 +32,6 @@ .align 12 empty_zero_page: .space PAGE_SIZE -#endif /* CONFIG_MMU */ /* * This global variable is used as an extension to the nios' @@ -42,11 +40,6 @@ empty_zero_page: .data .align 2 .set noat -#ifndef CONFIG_MMU - .global status_extension -status_extension: - .long 0 -#endif /* CONFIG_MMU */ .global _current_thread _current_thread: @@ -89,8 +82,6 @@ ENTRY(exception_handler_hook) movia r24, inthandler jmp r24 -#ifdef CONFIG_MMU - ENTRY(fast_handler) nextpc et helper: @@ -119,8 +110,6 @@ r3save: .word 0x0 ENTRY(fast_handler_end) -#endif /* CONFIG_MMU */ - 1: /* * After flushing the instruction cache, we must flush the data @@ -134,7 +123,6 @@ data_flush: sub r1, r1, r2 bgt r1, r0, data_flush -#ifdef CONFIG_MMU nextpc r1 /* Find out where we are */ chkadr: movia r2, chkadr @@ -155,7 +143,6 @@ loop_move: /* r1: src, r2: dest, r3: last dest */ jmp r1 /* jmp to _start */ finish_move: -#endif /* CONFIG_MMU */ /* Mask off all possible interrupts */ wrctl ienable, r0 @@ -168,12 +155,6 @@ finish_move: addi r2, r2, 1 bne r1, r2, 1b -#ifndef CONFIG_MMU - movia r1, status_extension /* get the STATUS extension address */ - movi r2, PS_S /* set initial mode = supervisor */ - stw r2, 0(r1) -#endif /* CONFIG_MMU */ - movia r1, init_thread_union /* set stack at top of the task union */ addi sp, r1, THREAD_SIZE movia r2, _current_thread /* Remember current thread */ diff --git a/arch/nios2/kernel/module.c b/arch/nios2/kernel/module.c index a82c6454f065c..4c014543c3c7d 100644 --- a/arch/nios2/kernel/module.c +++ b/arch/nios2/kernel/module.c @@ -22,26 +22,6 @@ #include #include -#ifndef CONFIG_MMU -void *module_alloc(unsigned long size) -{ - if (size == 0) - return NULL; - return vmalloc(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. - */ -} - -#else /* CONFIG_MMU */ - /* * FIXME: modules should NOT be allocated with kmalloc for (obvious) reasons. * But we do it for now to avoid relocation issues. CALL26/PCREL26 cannot reach @@ -68,8 +48,6 @@ void module_free(struct module *mod, void *module_region) */ } -#endif /* CONFIG_MMU */ - int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *mod) diff --git a/arch/nios2/kernel/process.c b/arch/nios2/kernel/process.c index f71d786c3c1d8..84eb8e03f21c9 100644 --- a/arch/nios2/kernel/process.c +++ b/arch/nios2/kernel/process.c @@ -96,9 +96,6 @@ void show_regs(struct pt_regs *regs) pr_notice("ea: %08lx estatus: %08lx\n", regs->ea, regs->estatus); -#ifndef CONFIG_MMU - pr_notice("status_extension: %08lx\n", regs->status_extension); -#endif } void flush_thread(void) @@ -123,11 +120,8 @@ int copy_thread(unsigned long clone_flags, childstack->r17 = arg; childstack->ra = (unsigned long) ret_from_kernel_thread; childregs->estatus = STATUS_PIE; -#ifdef CONFIG_MMU childregs->sp = (unsigned long) childstack; -#else - p->thread.kregs->sp = (unsigned long) childstack; -#endif + p->thread.ksp = (unsigned long) childstack; p->thread.kregs = childregs; return 0; @@ -144,17 +138,13 @@ int copy_thread(unsigned long clone_flags, p->thread.kregs = childregs; p->thread.ksp = (unsigned long) childstack; -#ifdef CONFIG_MMU if (usp) childregs->sp = usp; /* Initialize tls register. */ if (clone_flags & CLONE_SETTLS) childstack->r23 = regs->r7; -#else - if (usp) - p->thread.kregs->sp = usp; -#endif + return 0; } @@ -259,17 +249,11 @@ unsigned long get_wchan(struct task_struct *p) void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { memset((void *) regs, 0, sizeof(struct pt_regs)); -#ifdef CONFIG_MMU regs->estatus = ESTATUS_EPIE | ESTATUS_EU; -#else - /* No user mode setting on NOMMU, at least for now */ - regs->estatus = ESTATUS_EPIE; -#endif /* CONFIG_MMU */ regs->ea = pc; regs->sp = sp; } -#ifdef CONFIG_MMU #include /* Fill in the FPU structure for a core dump. */ @@ -277,4 +261,3 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r) { return 0; /* Nios2 has no FPU and thus no FPU registers */ } -#endif /* CONFIG_MMU */ diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index 18a2cfa9a2e6d..84364ac462dba 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -70,7 +70,6 @@ static inline void copy_exception_handler(unsigned int addr) ); } -#ifdef CONFIG_MMU /* Copy the fast TLB miss handler */ static inline void copy_fast_tlb_miss_handler(unsigned int addr) { @@ -94,7 +93,6 @@ static inline void copy_fast_tlb_miss_handler(unsigned int addr) : "memory" ); } -#endif /* CONFIG_MMU */ /* * save args passed from u-boot, called from head.S @@ -203,7 +201,6 @@ void __init setup_arch(char **cmdline_p) copy_exception_handler(cpuinfo.exception_addr); -#ifdef CONFIG_MMU mmu_init(); copy_fast_tlb_miss_handler(cpuinfo.fast_tlb_miss_exc_addr); @@ -213,7 +210,6 @@ void __init setup_arch(char **cmdline_p) * needed for this. */ mmu_context_init(); -#endif /* * get kmalloc into gear diff --git a/arch/nios2/kernel/signal.c b/arch/nios2/kernel/signal.c index f66240637dac5..f87bb80fdb7b9 100644 --- a/arch/nios2/kernel/signal.c +++ b/arch/nios2/kernel/signal.c @@ -121,15 +121,8 @@ static inline int rt_restore_ucontext(struct pt_regs *regs, settable bits */ err |= __get_user(regs->ea, &gregs[27]); -#ifdef CONFIG_MMU err |= __get_user(regs->ra, &gregs[23]); err |= __get_user(regs->sp, &gregs[28]); -#else - - err |= __get_user(regs->ra, &gregs[23]); - err |= __get_user(regs->status_extension, - &uc->uc_mcontext.status_extension); -#endif regs->estatus = (regs->estatus & 0xffffffff); regs->orig_r2 = -1; /* disable syscall checks */ @@ -245,20 +238,11 @@ static inline int rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs) err |= __put_user(sw->r21, &gregs[20]); err |= __put_user(sw->r22, &gregs[21]); err |= __put_user(sw->r23, &gregs[22]); -#ifdef CONFIG_MMU err |= __put_user(regs->ra, &gregs[23]); -#else - err |= __put_user(regs->sp, &gregs[23]); -#endif err |= __put_user(sw->fp, &gregs[24]); err |= __put_user(sw->gp, &gregs[25]); err |= __put_user(regs->ea, &gregs[27]); -#ifdef CONFIG_MMU err |= __put_user(regs->sp, &gregs[28]); -#else - err |= __put_user(regs->status_extension, - &uc->uc_mcontext.status_extension); -#endif return err; } @@ -277,11 +261,7 @@ static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, usp = regs->sp; /* This is the X/Open sanctioned signal stack switching. */ -#ifdef CONFIG_MMU if ((ka->sa.sa_flags & SA_ONSTACK) && (current->sas_ss_sp != 0)) { -#else - if (ka->sa.sa_flags & SA_ONSTACK) { -#endif if (!on_sig_stack(usp)) usp = current->sas_ss_sp + current->sas_ss_size; } @@ -307,21 +287,11 @@ static void setup_frame(int sig, struct k_sigaction *ka, /* Set up to return from userspace. */ regs->ra = (unsigned long) &frame->retcode[0]; -#ifdef CONFIG_MMU /* movi r2,__NR_sigreturn */ err |= __put_user(0x00800004 + (__NR_sigreturn << 6), (long *)(frame->retcode)); /* trap */ err |= __put_user(0x003b683a, (long *)(frame->retcode + 4)); -#else - /* movi r3,__NR_sigreturn */ - err |= __put_user(0x00c00004 + (__NR_sigreturn << 6), - (long *)(frame->retcode)); - /* mov r2,r0 */ - err |= __put_user(0x0005883a, (long *)(frame->retcode + 4)); - /* trap */ - err |= __put_user(0x003b683a, (long *)(frame->retcode + 8)); -#endif /* CONFIG_MMU */ if (err) goto give_sigsegv; @@ -362,21 +332,11 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. */ regs->ra = (unsigned long) &frame->retcode[0]; -#ifdef CONFIG_MMU /* movi r2,__NR_rt_sigreturn */ err |= __put_user(0x00800004 + (__NR_rt_sigreturn << 6), (long *)(frame->retcode)); /* trap */ err |= __put_user(0x003b683a, (long *)(frame->retcode + 4)); -#else - /* movi r3,__NR_rt_sigreturn */ - err |= __put_user(0x00c00004 + (__NR_rt_sigreturn << 6), - (long *)(frame->retcode)); - /* mov r2,r0 */ - err |= __put_user(0x0005883a, (long *)(frame->retcode + 4)); - /* trap */ - err |= __put_user(0x003b683a, (long *)(frame->retcode + 8)); -#endif /* CONFIG_MMU */ if (err) goto give_sigsegv; @@ -402,7 +362,6 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, static inline void handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) { -#ifdef CONFIG_MMU switch (regs->r2) { case ERESTART_RESTARTBLOCK: case ERESTARTNOHAND: @@ -422,26 +381,6 @@ static inline void handle_restart(struct pt_regs *regs, struct k_sigaction *ka, regs->ea -= 4; break; } -#else /* CONFIG_MMU */ - switch (regs->r2) { - case -ERESTARTNOHAND: - if (!has_handler) - goto do_restart; - regs->r2 = -EINTR; - break; - case -ERESTARTSYS: - if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { - regs->r2 = -EINTR; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: -do_restart: - regs->r2 = regs->orig_r2; - regs->ea -= 4; - break; - } -#endif /* CONFIG_MMU */ } /* @@ -477,17 +416,6 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall) siginfo_t info; int signr; -#ifndef CONFIG_MMU - /* - * On NOMMU we always get in_syscall as 1 and instead need to look at - * orig_r2 whether we are in a syscall. - */ - if (regs->orig_r2 >= 0) - in_syscall = 1; - else - in_syscall = 0; -#endif - /* FIXME - Do we still need to do this ? */ current->thread.kregs = regs; @@ -507,7 +435,6 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall) return 1; } -#ifdef CONFIG_MMU /* * No signal to deliver to the process - restart the syscall. */ @@ -526,21 +453,6 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall) } } } -#else - /* Did we come from a system call? */ - if (in_syscall) { - /* Restart the system call - no handlers present */ - if (regs->r2 == -ERESTARTNOHAND || - regs->r2 == -ERESTARTSYS || - regs->r2 == -ERESTARTNOINTR) { - regs->r2 = regs->orig_r2; - regs->ea -= 4; - } else if (regs->r2 == -ERESTART_RESTARTBLOCK) { - regs->r2 = __NR_restart_syscall; - regs->ea -= 4; - } - } -#endif /* CONFIG_MMU */ return 0; } diff --git a/arch/nios2/kernel/sys_nios2.c b/arch/nios2/kernel/sys_nios2.c index a8aa1483bba85..955ad47d2d3f7 100644 --- a/arch/nios2/kernel/sys_nios2.c +++ b/arch/nios2/kernel/sys_nios2.c @@ -33,13 +33,9 @@ asmlinkage int nios2_clone(struct pt_regs *regs) newsp = regs->r5; if (newsp == 0) newsp = regs->sp; -#ifdef CONFIG_MMU + parent_tidptr = (int __user *) regs->r6; child_tidptr = (int __user *) regs->r8; -#else - parent_tidptr = NULL; - child_tidptr = NULL; -#endif return do_fork(flags, newsp, 0, parent_tidptr, child_tidptr); } @@ -58,9 +54,6 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, asmlinkage int sys_cacheflush(unsigned long addr, int scope, int cache, unsigned long len) { -#ifndef CONFIG_MMU - flush_cache_all(); -#else struct vm_area_struct *vma; if (len == 0) @@ -80,7 +73,6 @@ asmlinkage int sys_cacheflush(unsigned long addr, int scope, int cache, /* Ignore the scope and cache arguments. */ flush_cache_range(vma, addr, addr + len); -#endif /* CONFIG_MMU */ return 0; } @@ -90,7 +82,6 @@ asmlinkage int sys_getpagesize(void) return PAGE_SIZE; } -#ifdef CONFIG_MMU #if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE) #include unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, @@ -102,4 +93,3 @@ unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, } EXPORT_SYMBOL(get_fb_unmapped_area); #endif /* CONFIG_FB */ -#endif /* CONFIG_MMU */ diff --git a/arch/nios2/kernel/syscalltable.S b/arch/nios2/kernel/syscalltable.S index 695944a3c5f26..e78fcadc37ae6 100644 --- a/arch/nios2/kernel/syscalltable.S +++ b/arch/nios2/kernel/syscalltable.S @@ -133,12 +133,8 @@ ENTRY(sys_call_table) .long sys_ni_syscall /* old sys_iopl */ .long sys_vhangup .long sys_ni_syscall /* old sys_idle */ -#ifdef CONFIG_MMU .long sys_nios2cmpxchg /* nios2-specific compare and exchange syscall for atomic operations */ -#else - .long sys_ni_syscall -#endif .long sys_wait4 .long sys_swapoff /* 115 */ .long sys_sysinfo diff --git a/arch/nios2/kernel/traps.c b/arch/nios2/kernel/traps.c index 53fd60f09c6ed..af4c93a40fc83 100644 --- a/arch/nios2/kernel/traps.c +++ b/arch/nios2/kernel/traps.c @@ -121,7 +121,7 @@ asmlinkage void breakpoint_c(struct pt_regs *fp) _exception(SIGTRAP, fp, TRAP_BRKPT, fp->ea); } -#if defined(CONFIG_MMU) && !defined(CONFIG_ALIGNMENT_TRAP) +#ifndef CONFIG_ALIGNMENT_TRAP /* Alignment exception handler */ asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause) { @@ -145,7 +145,7 @@ asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause) _exception(SIGBUS, fp, BUS_ADRALN, addr); } -#endif /* CONFIG_MMU && !CONFIG_ALIGNMENT_TRAP */ +#endif /* CONFIG_ALIGNMENT_TRAP */ /* Illegal instruction handler */ asmlinkage void handle_illegal_c(struct pt_regs *fp) diff --git a/arch/nios2/mm/Makefile b/arch/nios2/mm/Makefile index 39a4a4c2b2eff..6ef15aae70ef0 100644 --- a/arch/nios2/mm/Makefile +++ b/arch/nios2/mm/Makefile @@ -4,9 +4,9 @@ obj-y := init.o -obj-y += cacheflush$(MMU).o +obj-y += cacheflush.o -obj-$(CONFIG_MMU) += pgtable.o tlb.o uaccess.o fault.o -obj-$(CONFIG_MMU) += ioremap.o extable.o mmu_context.o +obj-y += pgtable.o tlb.o uaccess.o fault.o +obj-y += ioremap.o extable.o mmu_context.o -obj-y += dma-mapping$(MMU).o \ No newline at end of file +obj-y += dma-mapping.o \ No newline at end of file diff --git a/arch/nios2/mm/cacheflush-nommu.c b/arch/nios2/mm/cacheflush-nommu.c deleted file mode 100644 index ae4be32db90d4..0000000000000 --- a/arch/nios2/mm/cacheflush-nommu.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2004 Microtronix Datacom Ltd. - * - * based on arch/m68k/mm/memory.c which is: - * - * Copyright (C) 1999-2002 Greg Ungerer - * Copyright (C) 1998 Kenneth Albanowski - * Copyright (C) 1995 Hamish Macdonald - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Define cache invalidate functions. The instruction and data cache - * will need to be flushed. Write back the dirty data cache and invalidate - * the instruction cache for the range. - * - */ -static inline void cache_invalidate_inst(unsigned long paddr, int len) -{ - if (cpuinfo.icache_size != 0 && likely(len > 0)) { - if (len >= cpuinfo.icache_size) { - __asm__ __volatile__("1:\n\t" - "flushi %0\n\t" - "sub %0,%0,%1\n\t" - "bgt %0,r0,1b\n\t" - : - : "r" (cpuinfo.icache_size), - "r" (cpuinfo.icache_line_size)); - } else { - unsigned long sset, eset; - - sset = paddr & (~(cpuinfo.icache_line_size - 1)); - eset = - (paddr + len + cpuinfo.icache_line_size - 1) & - (~(cpuinfo.icache_line_size - 1)); - - __asm__ __volatile__("1:\n\t" - "flushi %0\n\t" - "add %0,%0,%2\n\t" - "blt %0,%1,1b\n\t" - : - : "r" (sset), - "r" (eset), - "r" (cpuinfo.icache_line_size)); - } - } - __asm__ __volatile__("\tflushp\n"); -} - -static inline void cache_invalidate_data(unsigned long paddr, - unsigned long len) -{ - unsigned long cache_size, line_size; - - line_size = cpuinfo.dcache_line_size; - cache_size = cpuinfo.dcache_size; - if (cache_size == 0 || unlikely(len == 0)) - return; - - if (len >= cache_size * 2) { - /* - * Invalidating an area at least twice the data cache size. - * Write back and invalidate all dirty cache lines. - */ - __asm__ __volatile__("1:\n\t" - "flushd 0(%0)\n\t" - "sub %0,%0,%1\n\t" - "bgt %0,r0,1b\n\t" - : - : "r" (cache_size), - "r" (line_size)); - - } else { - /* - * Invalidating an area less than twice the data cache size. - * Only invalidate the desired area. - */ - unsigned long sset, eset, pend; - - pend = paddr + len; - sset = paddr & (~(line_size - 1)); - eset = pend & (~(line_size - 1)); - if (sset != paddr && sset < eset) { - /* - * The start of the area is unaligned. Write back - * and invalidate a dirty cache line that overlaps the - * start of the area. (This is done at the end if the - * same cache line extends beyond the end of the area.) - */ - __asm__ __volatile__("\tflushda 0(%0)\n" - : - : "r" (sset)); - sset += line_size; - } - if (sset < eset) { - if (line_size > 4) { - /* - * Invalidate all the cache lines that fall - * completely within the area. - */ - __asm__ __volatile__("1:\n\t" - "initda 0(%0)\n\t" - "add %0,%0,%2\n\t" - "blt %0,%1,1b\n\t" - : - : "r" (sset), - "r" (eset), - "r"(line_size)); - } else { - /* - * CPU doesn't implement "initda" when cache - * line size is 4, so use "flushda" instead, - * which will also write back dirty cache - * lines before invalidating them. - */ - __asm__ __volatile__("1:\n\t" - "flushda 0(%0)\n\t" - "add %0,%0,%2\n\t" - "blt %0,%1,1b\n\t" - : - : "r" (sset), - "r" (eset), - "r"(line_size)); - } - } - if (eset != pend) { - /* - * The end of the area is unaligned. Write back and - * invalidate a dirty cache line that overlaps the - * end of the area. - */ - __asm__ __volatile__("\tflushda 0(%0)\n" - : - : "r" (eset)); - } - } -} - -static inline void cache_push_invalidate_data(unsigned long paddr, int len) -{ - if (cpuinfo.dcache_size == 0 || unlikely(len <= 0)) - return; - - if (len >= cpuinfo.dcache_size * 2) { - __asm__ __volatile__("1:\n\t" - "flushd 0(%0)\n\t" - "sub %0,%0,%1\n\t" - "bgt %0,r0,1b\n\t" - : - : "r" (cpuinfo.dcache_size), - "r" (cpuinfo.dcache_line_size)); - - } else { - unsigned long sset, eset; - - sset = paddr & (~(cpuinfo.dcache_line_size - 1)); - eset = - (paddr + len + cpuinfo.dcache_line_size - 1) & - (~(cpuinfo.dcache_line_size - 1)); - - __asm__ __volatile__("1:\n\t" - "flushda 0(%0)\n\t" - "add %0,%0,%2\n\t" - "blt %0,%1,1b\n\t" - : - : "r" (sset), - "r" (eset), - "r"(cpuinfo.dcache_line_size)); - } -} - -/* - * cache_push() semantics: Write back any dirty cache data in the given area, - * and invalidate the range in the instruction cache. It needs not (but may) - * invalidate those entries also in the data cache. The range is defined by a - * _physical_ address. - */ - -void cache_push(unsigned long paddr, int len) -{ - cache_push_invalidate_data(paddr, len); - cache_invalidate_inst(paddr, len); -} - -void dcache_push_all(void) -{ - if (cpuinfo.dcache_size != 0) { - __asm__ __volatile__("1:\n\t" - "flushd 0(%0)\n\t" - "sub %0,%0,%1\n\t" - "bgt %0,r0,1b\n\t" - : - : "r" (cpuinfo.dcache_size), - "r" (cpuinfo.dcache_line_size)); - } -} - -void icache_push_all(void) -{ - if (cpuinfo.icache_size != 0) { - __asm__ __volatile__("1:\n\t" - "flushi %0\n\t" - "sub %0,%0,%1\n\t" - "bgt %0,r0,1b\n\t" - : - : "r" (cpuinfo.icache_size), - "r" (cpuinfo.icache_line_size)); - } - __asm__ __volatile__("\tflushp\n"); -} - -/* - * dcache_push() semantics: Write back and dirty data cache and invalidate - * the range. - */ -void dcache_push(unsigned long vaddr, int len) -{ - cache_push_invalidate_data(vaddr, len); -} -EXPORT_SYMBOL(dcache_push); - -/* - * icache_push() semantics: Invalidate instruction cache in the range. - * Need to write back dirty data cache lines first. As a side-effect, - * this also invalidates the affected data lines. - */ -void icache_push(unsigned long vaddr, int len) -{ - cache_push_invalidate_data(vaddr, len); - cache_invalidate_inst(vaddr, len); -} - -/* - * nios2_clear_dcache_range() semantics: Invalidate a range of virtual - * addresses in the data cache, writing back as little as possible because - * the area is about to be overwritten, e.g. by a DMA transfer. - */ -void nios2_clear_dcache_range(unsigned long vstart, unsigned long vend) -{ - cache_invalidate_data(vstart, vend - vstart); -} diff --git a/arch/nios2/mm/dma-mapping-nommu.c b/arch/nios2/mm/dma-mapping-nommu.c deleted file mode 100644 index 06ae5f1e7c7b2..0000000000000 --- a/arch/nios2/mm/dma-mapping-nommu.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Dynamic DMA mapping support. - * - * We never have any address translations to worry about, so this - * is just alloc/free. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include - -void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp) -{ - void *ret; - /* ignore region specifiers */ - gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); - - if (dev == NULL || (*dev->dma_mask < 0xffffffff)) - gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); - - if (ret != NULL) { - memset(ret, 0, size); - *dma_handle = (dma_addr_t)ret; - ret = ioremap((unsigned long)ret, size); - } - return ret; -} -EXPORT_SYMBOL(dma_alloc_coherent); - -void dma_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dma_handle) -{ - free_pages((unsigned long)dma_handle, get_order(size)); -} -EXPORT_SYMBOL(dma_free_coherent); - -void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, - size_t size, enum dma_data_direction dir) -{ -} -EXPORT_SYMBOL(dma_sync_single_for_cpu); - -void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, - size_t size, enum dma_data_direction dir) -{ - unsigned long addr; - - BUG_ON(dir == DMA_NONE); - - addr = dma_handle + PAGE_OFFSET; - __dma_sync(addr, size, dir); -} -EXPORT_SYMBOL(dma_sync_single_for_device); - -void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, - unsigned long offset, size_t size, - enum dma_data_direction dir) -{ -} -EXPORT_SYMBOL(dma_sync_single_range_for_cpu); - -void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, - unsigned long offset, size_t size, - enum dma_data_direction dir) -{ - unsigned long addr; - - BUG_ON(dir == DMA_NONE); - - addr = dma_handle + offset + PAGE_OFFSET; - __dma_sync(addr, size, dir); -} -EXPORT_SYMBOL(dma_sync_single_range_for_device); - -void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ -} -EXPORT_SYMBOL(dma_sync_sg_for_cpu); - -void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, - int nelems, enum dma_data_direction dir) -{ - int i; - - BUG_ON(dir == DMA_NONE); - - /* Make sure that gcc doesn't leave the empty loop body. */ - for_each_sg(sg, sg, nelems, i) { - __dma_sync((unsigned long)sg_virt(sg), sg->length, dir); - } -} -EXPORT_SYMBOL(dma_sync_sg_for_device); - -dma_addr_t dma_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir) -{ - return dma_map_single(dev, page_address(page) + offset, size, dir); -} -EXPORT_SYMBOL(dma_map_page); - -void dma_unmap_page(struct device *dev, dma_addr_t address, - size_t size, enum dma_data_direction dir) -{ -} -EXPORT_SYMBOL(dma_unmap_page); - -int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction dir) -{ - int i; - - BUG_ON(dir == DMA_NONE); - - for (i = 0; i < nents; i++, sg++) { - sg->dma_address = dma_map_single(dev, sg_virt(sg), - sg->length, dir); - } - - return nents; -} -EXPORT_SYMBOL(dma_map_sg); - -void dma_unmap_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir) -{ - BUG_ON(dir == DMA_NONE); -} -EXPORT_SYMBOL(dma_unmap_sg); diff --git a/arch/nios2/mm/init.c b/arch/nios2/mm/init.c index 1a28b0336de10..8afd9ded5b9e3 100644 --- a/arch/nios2/mm/init.c +++ b/arch/nios2/mm/init.c @@ -33,9 +33,7 @@ #include #include -#ifdef CONFIG_MMU pgd_t *pgd_current; -#endif /* * paging_init() continues the virtual memory environment setup which @@ -54,31 +52,16 @@ void __init paging_init(void) * Make sure start_mem is page aligned, otherwise bootmem and * page_alloc get different views of the world. */ -#ifdef CONFIG_MMU start_mem = PHYS_OFFSET; end_mem = memory_end; -#else - start_mem = PAGE_ALIGN(memory_start); - end_mem = memory_end & PAGE_MASK; -#endif /* CONFIG_MMU */ -#ifdef CONFIG_MMU pagetable_init(); pgd_current = swapper_pg_dir; -#endif /* * Set up SFC/DFC registers (user data space). */ -#ifndef CONFIG_MMU - set_fs(KERNEL_DS); -#endif - -#ifdef CONFIG_MMU zones_size[ZONE_DMA] = ((end_mem - start_mem) >> PAGE_SHIFT); -#else - zones_size[ZONE_DMA] = (end_mem - PAGE_OFFSET) >> PAGE_SHIFT; -#endif /* CONFIG_MMU */ /* pass the memory from the bootmem allocator to the main allocator */ free_area_init(zones_size); @@ -94,23 +77,17 @@ void __init mem_init(void) end_mem &= PAGE_MASK; high_memory = __va(end_mem); -#ifdef CONFIG_MMU max_mapnr = ((unsigned long)end_mem) >> PAGE_SHIFT; -#else - max_mapnr = (((unsigned long)high_memory) - PAGE_OFFSET) >> PAGE_SHIFT; -#endif /* CONFIG_MMU */ /* this will put all memory onto the freelists */ free_all_bootmem(); mem_init_print_info(NULL); } -#ifdef CONFIG_MMU void __init mmu_init(void) { flush_tlb_all(); } -#endif #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) @@ -124,10 +101,6 @@ void __init_refok free_initmem(void) free_initmem_default(-1); } -#ifdef CONFIG_MMU - #define __page_aligned(order) __aligned(PAGE_SIZE << (order)) pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned(PGD_ORDER); pte_t invalid_pte_table[PTRS_PER_PTE] __page_aligned(PTE_ORDER); - -#endif /* CONFIG_MMU */ From 77ae4d02ac41fa5aa44d840fc051b4f78b6754c8 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 28 Jan 2014 15:14:04 +0800 Subject: [PATCH 029/201] FogBugz #180349: nios2: timer: Use panic() as error condition There's not much to do if we don't have timer as clocksource. Use panic() and show a meaningful error. Signed-off-by: Ezequiel Garcia Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/time.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c index 5ad6255039804..9dc1752b2457d 100644 --- a/arch/nios2/kernel/time.c +++ b/arch/nios2/kernel/time.c @@ -115,20 +115,18 @@ void __init nios2_late_time_init(void) BUG_ON(!timer); timer_membase = of_iomap(timer, 0); - if (WARN_ON(!timer_membase)) - return; + if (!timer_membase) + panic("Unable to map timer resource\n"); - if (of_property_read_u32(timer, "clock-frequency", &timer_freq)) { - pr_err("Can't get timer clock-frequency from device tree\n"); - return; - } + if (of_property_read_u32(timer, "clock-frequency", &timer_freq)) + panic("Unable to get timer clock frequency\n"); irq = irq_of_parse_and_map(timer, 0); - if (irq < 0) { - pr_err("Can't get timer interrupt\n"); - return; - } - setup_irq(irq, &nios2_timer_irq); + if (irq < 0) + panic("Unable to parse timer irq\n"); + + if (setup_irq(irq, &nios2_timer_irq)) + panic("Unable to setup timer irq\n"); write_timerperiod(NIOS2_TIMER_PERIOD - 1); From d60f5b01f7205bc9213bc937de17b47ca133e791 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 28 Jan 2014 16:25:28 +0800 Subject: [PATCH 030/201] FogBugz #180363: nios2: timer: Fix interrupt mapping error check The 'irq' type must be signed in order to check for negative values. Signed-off-by: Ezequiel Garcia Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c index 9dc1752b2457d..8599fa27fccef 100644 --- a/arch/nios2/kernel/time.c +++ b/arch/nios2/kernel/time.c @@ -107,7 +107,7 @@ static struct irqaction nios2_timer_irq = { void __init nios2_late_time_init(void) { - u32 irq; + int irq; unsigned int ctrl; struct device_node *timer = of_find_compatible_node(NULL, NULL, "ALTR,timer-1.0"); From 8c8890b61df9486745e357e2ee218f3d02c4eeb3 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 28 Jan 2014 18:54:47 +0800 Subject: [PATCH 031/201] FogBugz #180381: nios2: timer: Use CLOCKSOURCE_OF to setup the timer This allows to clean-up the code a bit, making it more readable and consistent architecture-wide. Signed-off-by: Ezequiel Garcia Signed-off-by: Ley Foon Tan --- arch/nios2/Kconfig | 1 + arch/nios2/kernel/time.c | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index e92deed63b9fc..74147fb958c94 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -15,6 +15,7 @@ config NIOS2 select SOC_BUS select OLD_SIGACTION select OLD_SIGSUSPEND + select CLKSRC_OF config GENERIC_CSUM def_bool y diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c index 8599fa27fccef..0677ebaa1dffd 100644 --- a/arch/nios2/kernel/time.c +++ b/arch/nios2/kernel/time.c @@ -105,14 +105,10 @@ static struct irqaction nios2_timer_irq = { .handler = timer_interrupt, }; -void __init nios2_late_time_init(void) +static void __init nios2_time_init(struct device_node *timer) { int irq; unsigned int ctrl; - struct device_node *timer = - of_find_compatible_node(NULL, NULL, "ALTR,timer-1.0"); - - BUG_ON(!timer); timer_membase = of_iomap(timer, 0); if (!timer_membase) @@ -148,5 +144,7 @@ void read_persistent_clock(struct timespec *ts) void __init time_init(void) { - late_time_init = nios2_late_time_init; + clocksource_of_init(); } + +CLOCKSOURCE_OF_DECLARE(nios2_timer, "ALTR,timer-1.0", nios2_time_init); From 938785cb12974f8be1a6af40bc4b55d2423a0085 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 13 Feb 2014 11:23:03 +0800 Subject: [PATCH 032/201] FogBugz #183586: nios2: Use checking for ALTR,pid-num-ways Remove checking for ALTR,has-mmu and use ALTR,pid-num-ways instead for backward compatibility. pid-num-ways shouldn't be 0 if MMU is enabled. This also can prevent division by zero exception when divide with cpuinfo.tlb_num_ways later. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/cpuinfo.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index 02bdafaf5bbce..0b6c8c597674e 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -56,13 +56,6 @@ void __init setup_cpuinfo(void) if (!cpu) panic("%s: No CPU found in devicetree!\n", __func__); - cpuinfo.mmu = fcpu_has(cpu, "ALTR,has-mmu"); - if (!cpuinfo.mmu) { - panic("ERROR: Can't get 'ALTR,has-mmu' from device tree. Only support" - " Nios II with MMU enabled. Please enable MMU in Nios II " - "hardware."); - } - cpuinfo.cpu_clock_freq = fcpu(cpu, "clock-frequency"); str = of_get_property(cpu, "ALTR,implementation", &len); @@ -88,13 +81,16 @@ void __init setup_cpuinfo(void) err_cpu("MULX"); #endif + cpuinfo.tlb_num_ways = fcpu(cpu, "ALTR,tlb-num-ways"); + if (!cpuinfo.tlb_num_ways) + panic("ALTR,tlb-num-ways can't be 0. Please check your hardware " + "system\n"); cpuinfo.icache_line_size = fcpu(cpu, "icache-line-size"); cpuinfo.icache_size = fcpu(cpu, "icache-size"); cpuinfo.dcache_line_size = fcpu(cpu, "dcache-line-size"); cpuinfo.dcache_size = fcpu(cpu, "dcache-size"); cpuinfo.tlb_pid_num_bits = fcpu(cpu, "ALTR,pid-num-bits"); - cpuinfo.tlb_num_ways = fcpu(cpu, "ALTR,tlb-num-ways"); cpuinfo.tlb_num_ways_log2 = ilog2(cpuinfo.tlb_num_ways); cpuinfo.tlb_num_entries = fcpu(cpu, "ALTR,tlb-num-entries"); cpuinfo.tlb_num_lines = cpuinfo.tlb_num_entries / cpuinfo.tlb_num_ways; From ef1738b120b5803f375b77e1d30373f63462aab0 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 19 Feb 2014 11:04:30 +0800 Subject: [PATCH 033/201] FogBugz #184715: nios2: irq: s/unsigned/u32 Usage of 'unsigned' with implicit type is frowned upon so let's replace it with 'u32'. Signed-off-by: Ezequiel Garcia Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/nios2/kernel/irq.c b/arch/nios2/kernel/irq.c index 097a760ba3fcc..e9265bd9fa450 100644 --- a/arch/nios2/kernel/irq.c +++ b/arch/nios2/kernel/irq.c @@ -41,7 +41,7 @@ asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) static void chip_unmask(struct irq_data *d) { - unsigned ien; + u32 ien; ien = RDCTL(CTL_IENABLE); ien |= (1 << d->hwirq); WRCTL(CTL_IENABLE, ien); @@ -49,7 +49,7 @@ static void chip_unmask(struct irq_data *d) static void chip_mask(struct irq_data *d) { - unsigned ien; + u32 ien; ien = RDCTL(CTL_IENABLE); ien &= ~(1 << d->hwirq); WRCTL(CTL_IENABLE, ien); From 0cd41bd1735ef512c708e0ed7195c66d4c1a677f Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 20 Feb 2014 17:09:02 +0800 Subject: [PATCH 034/201] FogBugz #185226: nios2: Simplify current_thread_info() implementation This commit replaces the assembler implementation by a more readable and equivalent pure-C implementation. Signed-off-by: Ezequiel Garcia Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/thread_info.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/nios2/include/asm/thread_info.h b/arch/nios2/include/asm/thread_info.h index d88cbcaffa7a2..1f266575beb51 100644 --- a/arch/nios2/include/asm/thread_info.h +++ b/arch/nios2/include/asm/thread_info.h @@ -75,14 +75,9 @@ struct thread_info { /* how to get the thread information struct from C */ static inline struct thread_info *current_thread_info(void) { - struct thread_info *ti; - __asm__ __volatile__( - "mov %0, sp\n" - "and %0, %0, %1\n" - : "=&r" (ti) - : "r" (~(THREAD_SIZE-1)) - ); - return ti; + register unsigned long sp asm("sp"); + + return (struct thread_info *)(sp & ~(THREAD_SIZE - 1)); } #endif /* !__ASSEMBLY__ */ From 4576f9e2619dbd6cbfa9ff53aba16008f23fc1c3 Mon Sep 17 00:00:00 2001 From: Tien Hock Loh Date: Fri, 28 Feb 2014 16:58:12 +0800 Subject: [PATCH 035/201] FogBugz #183948: Fix Nios II build failure GPIO Altera driver uses writel_relaxed, which doesn't exist in nios2 io.h. This is to add the _relaxed function to io.h. Signed-off-by: Tien Hock Loh --- arch/nios2/include/asm/io.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/nios2/include/asm/io.h b/arch/nios2/include/asm/io.h index b4dc7e65180cf..8e58e3f3ce1a2 100644 --- a/arch/nios2/include/asm/io.h +++ b/arch/nios2/include/asm/io.h @@ -142,6 +142,10 @@ static inline void io_insl(unsigned int addr, void *buf, int len) #define readw_relaxed(addr) readw(addr) #define readl_relaxed(addr) readl(addr) +#define writeb_relaxed(x, addr) writeb(x, addr) +#define writew_relaxed(x, addr) writew(x, addr) +#define writel_relaxed(x, addr) writel(x, addr) + #define outsb(a, b, l) io_outsb(a, b, l) #define outsw(a, b, l) io_outsw(a, b, l) #define outsl(a, b, l) io_outsl(a, b, l) From 47d940e586a7d538924b87ab10493f90e28d9c64 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 27 Feb 2014 16:00:53 +0800 Subject: [PATCH 036/201] FogBugz #186973: nios2: Fix warning from cacheflush.h Fixed this warning by adding and remove unused cacheflush.h in process.c. Example warning message: arch/nios2/include/asm/cacheflush.h:21:2: warning: 'struct vm_area_struct' declared inside parameter list Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/cacheflush.h | 2 ++ arch/nios2/kernel/process.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/nios2/include/asm/cacheflush.h b/arch/nios2/include/asm/cacheflush.h index 98fc3bb12d7c0..ea87a688b1841 100644 --- a/arch/nios2/include/asm/cacheflush.h +++ b/arch/nios2/include/asm/cacheflush.h @@ -12,6 +12,8 @@ #ifndef _ASM_NIOS2_CACHEFLUSH_H #define _ASM_NIOS2_CACHEFLUSH_H +#include + struct mm_struct; extern void flush_cache_all(void); diff --git a/arch/nios2/kernel/process.c b/arch/nios2/kernel/process.c index 84eb8e03f21c9..4467dbbea6a26 100644 --- a/arch/nios2/kernel/process.c +++ b/arch/nios2/kernel/process.c @@ -24,7 +24,6 @@ #include #include -#include #include asmlinkage void ret_from_fork(void); From 39faa0e6e0a6584d3f93ee174b4fc688030587fc Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 2 Apr 2014 17:26:27 +0800 Subject: [PATCH 037/201] FogBugz #194889: nios2: Export symbol __muldi3 make modules TSE drivers have this error: ERROR: "__muldi3" [drivers/net/ethernet/altera/altera_tse.ko] undefined! __muldi3 is a function in libgcc.a. So, we need to export this symbol for loadable module to use. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/nios2_ksyms.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/nios2/kernel/nios2_ksyms.c b/arch/nios2/kernel/nios2_ksyms.c index 467042f128ffe..eaf7f0cee5fac 100644 --- a/arch/nios2/kernel/nios2_ksyms.c +++ b/arch/nios2/kernel/nios2_ksyms.c @@ -32,3 +32,4 @@ DECLARE_EXPORT(__udivmoddi4); DECLARE_EXPORT(__udivsi3); DECLARE_EXPORT(__umoddi3); DECLARE_EXPORT(__umodsi3); +DECLARE_EXPORT(__muldi3); From 1760f0185c432fef4fdb543331ba945df31f4515 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 7 Apr 2014 15:10:33 +0800 Subject: [PATCH 038/201] nios2: Port to v3.14 - Use generic barrier.h - Add syscall sched_setattr and sched_getattr - Add hash.h and mcs_spinlock.h to kbuild Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/Kbuild | 5 ++++- arch/nios2/include/asm/barrier.h | 29 ---------------------------- arch/nios2/include/uapi/asm/unistd.h | 4 +++- arch/nios2/kernel/syscalltable.S | 4 +++- 4 files changed, 10 insertions(+), 32 deletions(-) delete mode 100644 arch/nios2/include/asm/barrier.h diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild index c5ed4a4ca2c16..3bbfb6d3b3b65 100644 --- a/arch/nios2/include/asm/Kbuild +++ b/arch/nios2/include/asm/Kbuild @@ -5,6 +5,7 @@ header-y += traps.h generic-y += atomic.h generic-y += auxvec.h +generic-y += barrier.h generic-y += bitops.h generic-y += bitsperlong.h generic-y += bug.h @@ -21,6 +22,7 @@ generic-y += fb.h generic-y += ftrace.h generic-y += futex.h generic-y += hardirq.h +generic-y += hash.h generic-y += hw_irq.h generic-y += ioctl.h generic-y += ipcbuf.h @@ -29,6 +31,7 @@ generic-y += kdebug.h generic-y += kmap_types.h generic-y += kvm_para.h generic-y += local.h +generic-y += mcs_spinlock.h generic-y += mman.h generic-y += module.h generic-y += msgbuf.h @@ -51,8 +54,8 @@ generic-y += spinlock.h generic-y += statfs.h generic-y += termbits.h generic-y += termios.h -generic-y += trace_clock.h generic-y += topology.h +generic-y += trace_clock.h generic-y += types.h generic-y += unaligned.h generic-y += user.h diff --git a/arch/nios2/include/asm/barrier.h b/arch/nios2/include/asm/barrier.h deleted file mode 100644 index 899ae3ec283a3..0000000000000 --- a/arch/nios2/include/asm/barrier.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2004 Microtronix Datacom Ltd. - * - * 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. - */ - -#ifndef _ASM_NIOS2_BARRIER_H -#define _ASM_NIOS2_BARRIER_H - -#include - -#define nop() (asm volatile ("nop" : : )) - -#define mb() barrier() -#define rmb() mb() -#define wmb() mb() - -#define set_mb(var, value) ((void) xchg(&var, value)) - -#define smp_mb() mb() -#define smp_rmb() rmb() -#define smp_wmb() wmb() - -#define read_barrier_depends() do { } while (0) -#define smp_read_barrier_depends() do { } while (0) - -#endif /* _ASM_NIOS2_BARRIER_H */ diff --git a/arch/nios2/include/uapi/asm/unistd.h b/arch/nios2/include/uapi/asm/unistd.h index 4c2ec55ab0203..a0bf289c65ab0 100644 --- a/arch/nios2/include/uapi/asm/unistd.h +++ b/arch/nios2/include/uapi/asm/unistd.h @@ -366,7 +366,9 @@ #define __NR_process_vm_writev 346 #define __NR_kcmp 347 #define __NR_finit_module 348 +#define __NR_sched_setattr 349 +#define __NR_sched_getattr 350 -#define NR_syscalls 349 +#define NR_syscalls 351 #endif /* _UAPI_ASM_NIOS2_UNISTD_H */ diff --git a/arch/nios2/kernel/syscalltable.S b/arch/nios2/kernel/syscalltable.S index e78fcadc37ae6..e64ecfa14d5b8 100644 --- a/arch/nios2/kernel/syscalltable.S +++ b/arch/nios2/kernel/syscalltable.S @@ -370,7 +370,9 @@ ENTRY(sys_call_table) .long sys_process_vm_writev .long sys_kcmp .long sys_finit_module + .long sys_sched_setattr + .long sys_sched_getattr /* 350 */ - .rept NR_syscalls - 349 + .rept NR_syscalls - 351 .long sys_ni_syscall .endr From dd8309751e8c74ba5c3f37936c11f30250883a3f Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 10 Apr 2014 11:04:40 +0800 Subject: [PATCH 039/201] FogBugz #196142: nios2: Update TSE in defconfig and dts for 3c120 - update TSE binding in dts - update to use CONFIG_ALTERA_TSE v2: - Remove gpio change in dts. Signed-off-by: Ley Foon Tan --- arch/nios2/boot/dts/3c120_devboard.dts | 15 +++++++++++++-- arch/nios2/configs/3c120_defconfig | 5 ++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/nios2/boot/dts/3c120_devboard.dts b/arch/nios2/boot/dts/3c120_devboard.dts index da7c73df74159..c26641e091ccb 100644 --- a/arch/nios2/boot/dts/3c120_devboard.dts +++ b/arch/nios2/boot/dts/3c120_devboard.dts @@ -117,13 +117,24 @@ reg-names = "control_port", "rx_csr", "tx_csr", "s1"; interrupt-parent = < &cpu >; interrupts = < 2 3 >; - ALTR,rx-fifo-depth = < 2048 >; - ALTR,tx-fifo-depth = < 2048 >; + interrupt-names = "rx_irq", "tx_irq"; + rx-fifo-depth = < 8192 >; + tx-fifo-depth = < 8192 >; address-bits = < 48 >; max-frame-size = < 1518 >; local-mac-address = [ 02 00 00 00 00 00 ]; phy-mode = "rgmii-id"; ALTR,mii-id = < 0 >; + phy-handle = < &phy0 >; + tse_mac_mdio: mdio { + compatible = "altr,tse-mdio"; + #address-cells = < 1 >; + #size-cells = < 0 >; + phy0: ethernet-phy@18 { + reg = < 18 >; + device_type = "ethernet-phy"; + }; + }; }; //end ethernet@0x4000 (tse_mac) uart: serial@0x4c80 { diff --git a/arch/nios2/configs/3c120_defconfig b/arch/nios2/configs/3c120_defconfig index d4c617296d042..65804fc9cad23 100644 --- a/arch/nios2/configs/3c120_defconfig +++ b/arch/nios2/configs/3c120_defconfig @@ -48,8 +48,7 @@ CONFIG_MTD_PHYSMAP_OF=y CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_LOOP=y CONFIG_NETDEVICES=y -CONFIG_NET_VENDOR_ALTERA=y -CONFIG_ALT_TSE=y +CONFIG_ALTERA_TSE=y CONFIG_MARVELL_PHY=y # CONFIG_WLAN is not set # CONFIG_INPUT_MOUSE is not set @@ -76,6 +75,6 @@ CONFIG_JFFS2_FS=y CONFIG_NFS_FS=y CONFIG_NFS_V3_ACL=y CONFIG_SUNRPC_DEBUG=y -# CONFIG_ENABLE_WARN_DEPRECATED is not set CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set CONFIG_KGDB=y From 70af581ed44939f1ce5e60ead56bd889664ce5a9 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 10 Apr 2014 11:24:43 +0800 Subject: [PATCH 040/201] FogBugz #196768: nios2: Update gpio node in 3c120_devboard.dts New dts parameter (altr,interrupt_type) required by Altera GPIO driver. Signed-off-by: Ley Foon Tan --- arch/nios2/boot/dts/3c120_devboard.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/nios2/boot/dts/3c120_devboard.dts b/arch/nios2/boot/dts/3c120_devboard.dts index c26641e091ccb..cad29a9d409c4 100644 --- a/arch/nios2/boot/dts/3c120_devboard.dts +++ b/arch/nios2/boot/dts/3c120_devboard.dts @@ -164,6 +164,7 @@ resetvalue = < 0 >; edge_type = < 2 >; level_trigger = < 0 >; + altr,interrupt_type = < 3 >; #gpio-cells = < 2 >; gpio-controller; }; //end gpio@0x4ce0 (user_dipsw_pio_8in) @@ -177,6 +178,7 @@ resetvalue = < 0 >; edge_type = < 2 >; level_trigger = < 0 >; + altr,interrupt_type = < 3 >; #gpio-cells = < 2 >; gpio-controller; }; //end gpio@0x4d00 (user_pb_pio_4in) From 8941ef12991dfc07dfc2df96a5b6546da9b0d0d8 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 10 Apr 2014 16:14:58 +0800 Subject: [PATCH 041/201] FogBugz #196811: nios2: Remove unused flat.h Remove unused flat.h. It is for uClinux FLAT format binaries. Nios II no longer support uClinux (NOMMU). Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/flat.h | 136 ---------------------------------- 1 file changed, 136 deletions(-) delete mode 100644 arch/nios2/include/asm/flat.h diff --git a/arch/nios2/include/asm/flat.h b/arch/nios2/include/asm/flat.h deleted file mode 100644 index 933b7a1210dd2..0000000000000 --- a/arch/nios2/include/asm/flat.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * include/asm-nios2/flat.h -- uClinux bFLT relocations - * - * Copyright (C) 2004,05 Microtronix Datacom Ltd - * - * 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. - * - * Written by Wentao Xu - */ - -#ifndef _ASM_NIOS2_FLAT_H__ -#define _ASM_NIOS2_FLAT_H__ - -#define flat_reloc_valid(reloc, size) ((reloc) <= (size + 0x8000)) - -/* The stack is 64-bit aligned for Nios II, so (sp - 1) shall - * be 64-bit aligned, where -1 is for argc - */ -#define flat_stack_align(sp) \ - (sp = (unsigned long *)(((unsigned long)sp - 1) & (-8))) - -/* The uClibc port for Nios II expects the argc is followed by argv and envp */ -#define flat_argvp_envp_on_stack() 1 - -#define flat_old_ram_flag(flags) (flags) - -/* We store the type of relocation in the top 4 bits of the `relval.' */ - -/* Convert a relocation entry into an address. */ -static inline unsigned long flat_get_relocate_addr(unsigned long relval) -{ - return relval & 0x0fffffff; /* Mask out top 4-bits */ -} - -#define FLAT_NIOS2_RELOC_TYPE(relval) ((relval) >> 28) - -#define FLAT_NIOS2_R_32 0 /* Normal 32-bit reloc */ -#define FLAT_NIOS2_R_HI_LO 1 /* High 16-bits + low 16-bits field */ -#define FLAT_NIOS2_R_HIADJ_LO 2 /* High 16-bits adjust + low 16-bits field */ -#define FLAT_NIOS2_R_CALL26 4 /* Call imm26 */ - -#define flat_set_persistent(relval, p) 0 - -/* Extract the address to be relocated from the symbol reference at rp; - * relval is the raw relocation-table entry from which RP is derived. - * rp shall always be 32-bit aligned - */ -static inline unsigned long flat_get_addr_from_rp(unsigned long *rp, - unsigned long relval, - unsigned long flags, - unsigned long *persistent) -{ - switch (FLAT_NIOS2_RELOC_TYPE(relval)) { - case FLAT_NIOS2_R_32: - /* Simple 32-bit address. The loader expect it in bigger - * endian */ - return htonl(*rp); - - case FLAT_NIOS2_R_HI_LO: - /* get the two 16-bit immediate value from instructions, then - * construct a 32-bit value. Again the loader expect bigger - * endian - */ - return htonl((((rp[0] >> 6) & 0xFFFF) << 16) | - ((rp[1] >> 6) & 0xFFFF)); - - case FLAT_NIOS2_R_HIADJ_LO: - { - /* get the two 16-bit immediate value from instructions, then - * construct a 32-bit value. Again the loader expect bigger - * endian - */ - unsigned int low, high; - high = (rp[0] >> 6) & 0xFFFF; - low = (rp[1] >> 6) & 0xFFFF; - - if ((low >> 15) & 1) - high--; - - return htonl((high << 16) | low); - } - case FLAT_NIOS2_R_CALL26: - /* the 26-bit immediate value is actually 28-bit */ - return htonl(((*rp) >> 6) << 2); - - default: - return ~0; /* bogus value */ - } -} - -/* Insert the address addr into the symbol reference at rp; - * relval is the raw relocation-table entry from which rp is derived. - * rp shall always be 32-bit aligned - */ -static inline void flat_put_addr_at_rp(unsigned long *rp, unsigned long addr, - unsigned long relval) -{ - unsigned long exist_val; - switch (FLAT_NIOS2_RELOC_TYPE(relval)) { - case FLAT_NIOS2_R_32: - /* Simple 32-bit address. */ - *rp = addr; - break; - - case FLAT_NIOS2_R_HI_LO: - exist_val = rp[0]; - rp[0] = ((((exist_val >> 22) << 16) | (addr >> 16)) << 6) | - (exist_val & 0x3F); - exist_val = rp[1]; - rp[1] = ((((exist_val >> 22) << 16) | (addr & 0xFFFF)) << 6) | - (exist_val & 0x3F); - break; - - case FLAT_NIOS2_R_HIADJ_LO: - { - unsigned int high = (addr >> 16); - if ((addr >> 15) & 1) - high = (high + 1) & 0xFFFF; - exist_val = rp[0]; - rp[0] = ((((exist_val >> 22) << 16) | high) << 6) | - (exist_val & 0x3F); - exist_val = rp[1]; - rp[1] = ((((exist_val >> 22) << 16) | (addr & 0xFFFF)) << 6) | - (exist_val & 0x3F); - break; - } - case FLAT_NIOS2_R_CALL26: - /* the opcode of CALL is 0, so just store the value */ - *rp = ((addr >> 2) << 6); - break; - } -} - -#endif /* _ASM_NIOS2_FLAT_H__ */ From 2ba2d36f257c002af14df7075fb816decd4d82e9 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 9 Apr 2014 18:27:05 +0800 Subject: [PATCH 042/201] FogBugz #195229: nios2: Do not modify tlbmisc.PID while modifying TLB Currently tlbmisc.PID register changes value, while TLB entries are flushed. For a tracing the CPU this is not nice, because it will indicate a context switch to the trace tool. This fix to keep the tlbmisc.PID value unmodified, while flushing TLB entries, since this means you always have got the correct PID value in tlbmisc (the PID value of the current mmu context). v2: - Move tlbmisc register defines to registers.h, we already have tlbmisc defines in registers.h originally. - Change to use tlbmisc register defines. - New helper function to get misc and pid. Signed-off-by: Ingo Rohloff Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/registers.h | 7 +++ arch/nios2/mm/tlb.c | 92 +++++++++++++++--------------- 2 files changed, 54 insertions(+), 45 deletions(-) diff --git a/arch/nios2/include/asm/registers.h b/arch/nios2/include/asm/registers.h index d37a717011e87..0e6ffef08b20e 100644 --- a/arch/nios2/include/asm/registers.h +++ b/arch/nios2/include/asm/registers.h @@ -52,7 +52,14 @@ #define ESTATUS_EH (1 << 2) /* Exception mode */ /* tlbmisc register bits */ +#define TLBMISC_PID_SHIFT 4 +#define TLBMISC_PID_MASK ((1UL << cpuinfo.tlb_pid_num_bits) - 1) +#define TLBMISC_WAY_MASK 0xf +#define TLBMISC_WAY_SHIFT 20 + +#define TLBMISC_PID (TLBMISC_PID_MASK << TLBMISC_PID_SHIFT) /* TLB PID */ #define TLBMISC_WE (1 << 18) /* TLB write enable */ #define TLBMISC_RD (1 << 19) /* TLB read */ +#define TLBMISC_WAY (TLBMISC_WAY_MASK << TLBMISC_WAY_SHIFT) /* TLB way */ #endif /* _ASM_NIOS2_REGISTERS_H */ diff --git a/arch/nios2/mm/tlb.c b/arch/nios2/mm/tlb.c index fc6fabbf6d56b..51201512190b7 100644 --- a/arch/nios2/mm/tlb.c +++ b/arch/nios2/mm/tlb.c @@ -29,12 +29,12 @@ */ #define MAX_PHYS_ADDR 0 -/* bit definitions for TLBMISC register */ -#define PID_SHIFT 4 -#define PID_MASK ((1UL << cpuinfo.tlb_pid_num_bits) - 1) - -#define WAY_SHIFT 20 -#define WAY_MASK 0xf +static void get_misc_and_pid(unsigned long *misc, unsigned long *pid) +{ + *misc = RDCTL(CTL_TLBMISC); + *misc &= (TLBMISC_PID | TLBMISC_WAY); + *pid = *misc & TLBMISC_PID; +} /* * All entries common to a mm share an asid. To effectively flush these @@ -59,14 +59,12 @@ void flush_tlb_mm(struct mm_struct *mm) void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid) { unsigned int way; - unsigned long org_misc; + unsigned long org_misc, pid_misc; pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); - /* remember pid/way until we return. - * CHECKME: is there a race here when writing org_misc back? */ - org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | - (WAY_MASK << WAY_SHIFT))); + /* remember pid/way until we return. */ + get_misc_and_pid(&org_misc, &pid_misc); WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2); @@ -75,21 +73,23 @@ void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid) unsigned long tlbmisc; unsigned long pid; - WRCTL(CTL_TLBMISC, (1UL << 19) | (way << 20)); + tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); + WRCTL(CTL_TLBMISC, tlbmisc); pteaddr = RDCTL(CTL_PTEADDR); tlbmisc = RDCTL(CTL_TLBMISC); - pid = (tlbmisc >> PID_SHIFT) & PID_MASK; + pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK; if (((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) && pid == mmu_pid) { unsigned long vaddr = CONFIG_IO_REGION_BASE + ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) + (addr & TLB_INDEX_MASK); pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n", - vaddr, way, pid); + vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT)); WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2); - WRCTL(CTL_TLBMISC, (1UL << 18) | (way << 20) | - (pid << PID_SHIFT)); + tlbmisc = pid_misc | TLBMISC_WE | + (way << TLBMISC_WAY_SHIFT); + WRCTL(CTL_TLBMISC, tlbmisc); WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); } } @@ -123,14 +123,12 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end) void flush_tlb_one(unsigned long addr) { unsigned int way; - unsigned long pid, org_misc; + unsigned long org_misc, pid_misc; pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); - /* remember pid/way until we return. - * CHECKME: is there a race here when writing org_misc back? */ - org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | - (WAY_MASK << WAY_SHIFT))); + /* remember pid/way until we return. */ + get_misc_and_pid(&org_misc, &pid_misc); WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2); @@ -138,7 +136,8 @@ void flush_tlb_one(unsigned long addr) unsigned long pteaddr; unsigned long tlbmisc; - WRCTL(CTL_TLBMISC, (1UL << 19) | (way << 20)); + tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); + WRCTL(CTL_TLBMISC, tlbmisc); pteaddr = RDCTL(CTL_PTEADDR); tlbmisc = RDCTL(CTL_TLBMISC); @@ -147,13 +146,13 @@ void flush_tlb_one(unsigned long addr) ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) + (addr & TLB_INDEX_MASK); - pid = (tlbmisc >> PID_SHIFT) & PID_MASK; pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n", - vaddr, way, pid); + vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT)); + tlbmisc = pid_misc | TLBMISC_WE | + (way << TLBMISC_WAY_SHIFT); WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2); - WRCTL(CTL_TLBMISC, (1UL << 18) | (way << 20) | - (pid << PID_SHIFT)); + WRCTL(CTL_TLBMISC, tlbmisc); WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); } } @@ -170,8 +169,7 @@ void dump_tlb_line(unsigned long line) line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2)); /* remember pid/way until we return */ - org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | - (WAY_MASK << WAY_SHIFT))); + org_misc = (RDCTL(CTL_TLBMISC) & (TLBMISC_PID | TLBMISC_WAY)); WRCTL(CTL_PTEADDR, line << 2); @@ -180,7 +178,7 @@ void dump_tlb_line(unsigned long line) unsigned long tlbmisc; unsigned long tlbacc; - WRCTL(CTL_TLBMISC, (1UL << 19) | (way << 20)); + WRCTL(CTL_TLBMISC, TLBMISC_RD | (way << TLBMISC_WAY_SHIFT)); pteaddr = RDCTL(CTL_PTEADDR); tlbmisc = RDCTL(CTL_TLBMISC); tlbacc = RDCTL(CTL_TLBACC); @@ -190,7 +188,8 @@ void dump_tlb_line(unsigned long line) way, (pteaddr << (PAGE_SHIFT-2)), (tlbacc << PAGE_SHIFT), - (tlbmisc >> PID_SHIFT) & PID_MASK, + ((tlbmisc >> TLBMISC_PID_SHIFT) & + TLBMISC_PID_MASK), (tlbacc & _PAGE_READ ? 'r' : '-'), (tlbacc & _PAGE_WRITE ? 'w' : '-'), (tlbacc & _PAGE_EXEC ? 'x' : '-'), @@ -213,14 +212,12 @@ void flush_tlb_pid(unsigned long pid) { unsigned int line; unsigned int way; - unsigned long org_misc; + unsigned long org_misc, pid_misc; /* remember pid/way until we return */ - org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | - (WAY_MASK << WAY_SHIFT))); + get_misc_and_pid(&org_misc, &pid_misc); for (line = 0; line < cpuinfo.tlb_num_lines; line++) { - /* FIXME: << TLB_WAY_BITS should probably not be here */ WRCTL(CTL_PTEADDR, line << 2); for (way = 0; way < cpuinfo.tlb_num_ways; way++) { @@ -228,14 +225,18 @@ void flush_tlb_pid(unsigned long pid) unsigned long tlbmisc; unsigned long tlbacc; - WRCTL(CTL_TLBMISC, (1UL << 19) | (way << 20)); + tlbmisc = pid_misc | TLBMISC_RD | + (way << TLBMISC_WAY_SHIFT); + WRCTL(CTL_TLBMISC, tlbmisc); pteaddr = RDCTL(CTL_PTEADDR); tlbmisc = RDCTL(CTL_TLBMISC); tlbacc = RDCTL(CTL_TLBACC); - if (((tlbmisc>>PID_SHIFT)&PID_MASK) == pid) { - WRCTL(CTL_TLBMISC, (1UL << 18) | (way << 20) | - (pid << PID_SHIFT)); + if (((tlbmisc>>TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK) + == pid) { + tlbmisc = pid_misc | TLBMISC_WE | + (way << TLBMISC_WAY_SHIFT); + WRCTL(CTL_TLBMISC, tlbmisc); WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); } @@ -250,18 +251,19 @@ void flush_tlb_all(void) int i; unsigned long vaddr = CONFIG_IO_REGION_BASE; unsigned int way; - unsigned long org_misc; + unsigned long org_misc, pid_misc, tlbmisc; - /* remember pid/way */ - org_misc = (RDCTL(CTL_TLBMISC) & ((PID_MASK << PID_SHIFT) | - (WAY_MASK << WAY_SHIFT))); + /* remember pid/way until we return */ + get_misc_and_pid(&org_misc, &pid_misc); + pid_misc |= TLBMISC_WE; /* Map each TLB entry to physcal address 0 with no-access and a bad ptbase */ for (way = 0; way < cpuinfo.tlb_num_ways; way++) { + tlbmisc = pid_misc | (way << TLBMISC_WAY_SHIFT); for (i = 0; i < cpuinfo.tlb_num_lines; i++) { WRCTL(CTL_PTEADDR, ((vaddr) >> PAGE_SHIFT) << 2); - WRCTL(CTL_TLBMISC, (1<<18) | (way << 20)); + WRCTL(CTL_TLBMISC, tlbmisc); WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); vaddr += 1UL << 12; } @@ -273,6 +275,6 @@ void flush_tlb_all(void) void set_mmu_pid(unsigned long pid) { - WRCTL(CTL_TLBMISC, (RDCTL(CTL_TLBMISC) & (WAY_MASK << WAY_SHIFT)) | - ((pid & PID_MASK) << PID_SHIFT)); + WRCTL(CTL_TLBMISC, (RDCTL(CTL_TLBMISC) & TLBMISC_WAY) | + ((pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT)); } From 1ff9c59dab8d10cabf89647651ba5866595c89bb Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 8 Apr 2014 18:49:39 +0800 Subject: [PATCH 043/201] FogBugz #196210: nios2: remove unused __uClinux__ Define __uClinux__ is unused after NOMMU removal. Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/ucontext.h | 3 --- arch/nios2/include/uapi/asm/ptrace.h | 6 ------ 2 files changed, 9 deletions(-) diff --git a/arch/nios2/include/asm/ucontext.h b/arch/nios2/include/asm/ucontext.h index a1aeef9ba8547..5870ef4e92fba 100644 --- a/arch/nios2/include/asm/ucontext.h +++ b/arch/nios2/include/asm/ucontext.h @@ -18,9 +18,6 @@ typedef greg_t gregset_t[NGREG]; struct mcontext { int version; -#ifdef __uClinux__ - int status_extension; -#endif gregset_t gregs; }; diff --git a/arch/nios2/include/uapi/asm/ptrace.h b/arch/nios2/include/uapi/asm/ptrace.h index 130eb4c28bc88..e37ca15350ea6 100644 --- a/arch/nios2/include/uapi/asm/ptrace.h +++ b/arch/nios2/include/uapi/asm/ptrace.h @@ -97,14 +97,8 @@ struct pt_regs { unsigned long sp; /* Stack pointer */ unsigned long gp; /* Global pointer */ unsigned long estatus; -#ifdef __uClinux__ - unsigned long status_extension; /* Status extension. Used to - fake user mode */ -#endif unsigned long ea; /* Exception return address (pc) */ -#ifndef __uClinux__ unsigned long orig_r7; -#endif }; /* From cfa81fff186bcdaa253f3a9ba7a88db73328e775 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 15 Apr 2014 16:02:23 +0800 Subject: [PATCH 044/201] FogBugz #197544: nios2: update 3c120_defconfig to match hardware Hardware have 0x8000 bytes for dcache and icache sizes. Signed-off-by: Ley Foon Tan v2: - fix fogbugz number. --- arch/nios2/configs/3c120_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/nios2/configs/3c120_defconfig b/arch/nios2/configs/3c120_defconfig index 65804fc9cad23..6aa13239e338f 100644 --- a/arch/nios2/configs/3c120_defconfig +++ b/arch/nios2/configs/3c120_defconfig @@ -16,6 +16,9 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MEM_BASE=0x10000000 CONFIG_NIOS2_HW_MUL_SUPPORT=y CONFIG_NIOS2_HW_DIV_SUPPORT=y +CONFIG_CUSTOM_CACHE_SETTINGS=y +CONFIG_NIOS2_DCACHE_SIZE=0x8000 +CONFIG_NIOS2_ICACHE_SIZE=0x8000 # CONFIG_CMDLINE_IGNORE_DTB is not set CONFIG_PASS_CMDLINE=y CONFIG_BOOT_LINK_OFFSET=0x00800000 From 411f321bb9cfe80bb256e0521763639341572d21 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 18 Apr 2014 10:27:46 +0800 Subject: [PATCH 045/201] FogBugz #190747: nios2 dma mapping review and cleanup Review and cleanup code in dma mapping code: - Add invalidate_dcache_range for DMA_FROM_DEVICE dma sync - Change to use valid_dma_direction for checking - Change to use phys_to_virt() to convert physical to virtual address - Remove unnecessary FIXME - Add __flush_dcache_all() function to flush dcache by using flushd instruction Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/cacheflush.h | 1 + arch/nios2/include/asm/dma-mapping.h | 16 ++++--- arch/nios2/kernel/cpuinfo.c | 5 ++ arch/nios2/mm/cacheflush.c | 56 ++++++++++++++++++----- arch/nios2/mm/dma-mapping.c | 68 ++++++++++------------------ 5 files changed, 86 insertions(+), 60 deletions(-) diff --git a/arch/nios2/include/asm/cacheflush.h b/arch/nios2/include/asm/cacheflush.h index ea87a688b1841..ce2605a8c44de 100644 --- a/arch/nios2/include/asm/cacheflush.h +++ b/arch/nios2/include/asm/cacheflush.h @@ -40,6 +40,7 @@ extern void copy_from_user_page(struct vm_area_struct *vma, struct page *page, void *dst, void *src, int len); extern void flush_dcache_range(unsigned long start, unsigned long end); +extern void invalidate_dcache_range(unsigned long start, unsigned long end); #define flush_dcache_mmap_lock(mapping) do { } while (0) #define flush_dcache_mmap_unlock(mapping) do { } while (0) diff --git a/arch/nios2/include/asm/dma-mapping.h b/arch/nios2/include/asm/dma-mapping.h index cb2283b843dda..28db117124745 100644 --- a/arch/nios2/include/asm/dma-mapping.h +++ b/arch/nios2/include/asm/dma-mapping.h @@ -14,14 +14,18 @@ #include #include -static inline void __dma_sync(unsigned long addr, size_t size, +static inline void __dma_sync(void *vaddr, size_t size, enum dma_data_direction direction) { switch (direction) { - case DMA_FROM_DEVICE: - case DMA_TO_DEVICE: + case DMA_FROM_DEVICE: /* invalidate cache */ + invalidate_dcache_range((unsigned long)vaddr, + (unsigned long)(vaddr + size)); + break; + case DMA_TO_DEVICE: /* flush and invalidate cache */ case DMA_BIDIRECTIONAL: - flush_dcache_range(addr, (unsigned long)(addr + size)); + flush_dcache_range((unsigned long)vaddr, + (unsigned long)(vaddr + size)); break; default: BUG(); @@ -41,7 +45,7 @@ static inline dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size, enum dma_data_direction direction) { - __dma_sync((unsigned long)ptr, size, direction); + __dma_sync(ptr, size, direction); return virt_to_phys(ptr); } @@ -103,7 +107,7 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, enum dma_data_direction direction) { - __dma_sync((unsigned long)vaddr, size, direction); + __dma_sync(vaddr, size, direction); } #endif /* _ASM_NIOS2_DMA_MAPPING_H */ diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index 0b6c8c597674e..0a7fb2c4ef760 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -56,6 +56,11 @@ void __init setup_cpuinfo(void) if (!cpu) panic("%s: No CPU found in devicetree!\n", __func__); + if (!fcpu_has(cpu, "ALTR,has-initda")) + panic("initda instruction is unimplemented. Please update your " + "hardware system to have more than 4-byte line data " + "cache\n"); + cpuinfo.cpu_clock_freq = fcpu(cpu, "clock-frequency"); str = of_get_property(cpu, "ALTR,implementation", &len); diff --git a/arch/nios2/mm/cacheflush.c b/arch/nios2/mm/cacheflush.c index 36d027f41da32..0c4d0d416f8f6 100644 --- a/arch/nios2/mm/cacheflush.c +++ b/arch/nios2/mm/cacheflush.c @@ -23,6 +23,25 @@ static void __flush_dcache(unsigned long start, unsigned long end) end += (cpuinfo.dcache_line_size - 1); end &= ~(cpuinfo.dcache_line_size - 1); + if (end > start + cpuinfo.dcache_size) + end = start + cpuinfo.dcache_size; + + for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) { + __asm__ __volatile__ (" flushda 0(%0)\n" + : /* Outputs */ + : /* Inputs */ "r"(addr) + /* : No clobber */); + } +} + +static void __flush_dcache_all(unsigned long start, unsigned long end) +{ + unsigned long addr; + + start &= ~(cpuinfo.dcache_line_size - 1); + end += (cpuinfo.dcache_line_size - 1); + end &= ~(cpuinfo.dcache_line_size - 1); + if (end > start + cpuinfo.dcache_size) end = start + cpuinfo.dcache_size; @@ -34,6 +53,25 @@ static void __flush_dcache(unsigned long start, unsigned long end) } } +static void __invalidate_dcache(unsigned long start, unsigned long end) +{ + unsigned long addr; + + start &= ~(cpuinfo.dcache_line_size - 1); + end += (cpuinfo.dcache_line_size - 1); + end &= ~(cpuinfo.dcache_line_size - 1); + + if (end > start + cpuinfo.dcache_size) + end = start + cpuinfo.dcache_size; + + for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) { + __asm__ __volatile__ (" initda 0(%0)\n" + : /* Outputs */ + : /* Inputs */ "r"(addr) + /* : No clobber */); + } +} + static void __flush_icache(unsigned long start, unsigned long end) { unsigned long addr; @@ -80,7 +118,7 @@ static void flush_aliases(struct address_space *mapping, struct page *page) void flush_cache_all(void) { - __flush_dcache(0, cpuinfo.dcache_size); + __flush_dcache_all(0, cpuinfo.dcache_size); __flush_icache(0, cpuinfo.icache_size); } @@ -96,18 +134,21 @@ void flush_cache_dup_mm(struct mm_struct *mm) void flush_icache_range(unsigned long start, unsigned long end) { - __flush_dcache(start, end); __flush_icache(start, end); } void flush_dcache_range(unsigned long start, unsigned long end) { __flush_dcache(start, end); - /* FIXME: Maybe we should remove __flush_icache ? */ - __flush_icache(start, end); } EXPORT_SYMBOL(flush_dcache_range); +void invalidate_dcache_range(unsigned long start, unsigned long end) +{ + __invalidate_dcache(start, end); +} +EXPORT_SYMBOL(invalidate_dcache_range); + void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { @@ -121,7 +162,6 @@ void flush_icache_page(struct vm_area_struct *vma, struct page *page) unsigned long start = (unsigned long) page_address(page); unsigned long end = start + PAGE_SIZE; - __flush_dcache(start, end); __flush_icache(start, end); } @@ -181,21 +221,15 @@ void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, struct page *to) { __flush_dcache(vaddr, vaddr + PAGE_SIZE); - __flush_icache(vaddr, vaddr + PAGE_SIZE); copy_page(vto, vfrom); __flush_dcache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE); - /* FIXME: really necessary? */ - __flush_icache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE); } void clear_user_page(void *addr, unsigned long vaddr, struct page *page) { __flush_dcache(vaddr, vaddr + PAGE_SIZE); - __flush_icache(vaddr, vaddr + PAGE_SIZE); clear_page(addr); __flush_dcache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE); - /* FIXME: really necessary? */ - __flush_icache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE); } void copy_from_user_page(struct vm_area_struct *vma, struct page *page, diff --git a/arch/nios2/mm/dma-mapping.c b/arch/nios2/mm/dma-mapping.c index 76d23283cc240..a9bafe9f5b5c6 100644 --- a/arch/nios2/mm/dma-mapping.c +++ b/arch/nios2/mm/dma-mapping.c @@ -60,12 +60,12 @@ int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, { int i; - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); for_each_sg(sg, sg, nents, i) { - unsigned long addr; + void *addr; - addr = (unsigned long) sg_virt(sg); + addr = sg_virt(sg); if (addr) { __dma_sync(addr, sg->length, direction); sg->dma_address = sg_phys(sg); @@ -80,11 +80,11 @@ dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction direction) { - unsigned long addr; + void *addr; - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); - addr = (unsigned long) page_address(page) + offset; + addr = page_address(page) + offset; __dma_sync(addr, size, direction); return page_to_phys(page) + offset; @@ -94,30 +94,26 @@ EXPORT_SYMBOL(dma_map_page); void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, enum dma_data_direction direction) { - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); - if (direction != DMA_TO_DEVICE) { - unsigned long addr; - - addr = dma_address + PAGE_OFFSET; - __dma_sync(addr, size, direction); - } + if (direction != DMA_TO_DEVICE) + __dma_sync(phys_to_virt(dma_address), size, direction); } EXPORT_SYMBOL(dma_unmap_page); void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, enum dma_data_direction direction) { - unsigned long addr; + void *addr; int i; - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); if (direction == DMA_TO_DEVICE) return; for_each_sg(sg, sg, nhwentries, i) { - addr = (unsigned long) sg_virt(sg); + addr = sg_virt(sg); if (addr) __dma_sync(addr, sg->length, direction); } @@ -127,24 +123,18 @@ EXPORT_SYMBOL(dma_unmap_sg); void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { - unsigned long addr; - - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); - addr = dma_handle + PAGE_OFFSET; - __dma_sync(addr, size, direction); + __dma_sync(phys_to_virt(dma_handle), size, direction); } EXPORT_SYMBOL(dma_sync_single_for_cpu); void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, enum dma_data_direction direction) { - unsigned long addr; - - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); - addr = dma_handle + PAGE_OFFSET; - __dma_sync(addr, size, direction); + __dma_sync(phys_to_virt(dma_handle), size, direction); } EXPORT_SYMBOL(dma_sync_single_for_device); @@ -152,12 +142,9 @@ void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) { - unsigned long addr; + BUG_ON(!valid_dma_direction(direction)); - BUG_ON(direction == DMA_NONE); - - addr = dma_handle + offset + PAGE_OFFSET; - __dma_sync(addr, size, direction); + __dma_sync(phys_to_virt(dma_handle), size, direction); } EXPORT_SYMBOL(dma_sync_single_range_for_cpu); @@ -165,12 +152,9 @@ void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, unsigned long offset, size_t size, enum dma_data_direction direction) { - unsigned long addr; - - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); - addr = dma_handle + offset + PAGE_OFFSET; - __dma_sync(addr, size, direction); + __dma_sync(phys_to_virt(dma_handle), size, direction); } EXPORT_SYMBOL(dma_sync_single_range_for_device); @@ -179,13 +163,11 @@ void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, { int i; - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); /* Make sure that gcc doesn't leave the empty loop body. */ - for_each_sg(sg, sg, nelems, i) { - __dma_sync((unsigned long)sg_virt(sg), - sg->length, direction); - } + for_each_sg(sg, sg, nelems, i) + __dma_sync(sg_virt(sg), sg->length, direction); } EXPORT_SYMBOL(dma_sync_sg_for_cpu); @@ -194,11 +176,11 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, { int i; - BUG_ON(direction == DMA_NONE); + BUG_ON(!valid_dma_direction(direction)); /* Make sure that gcc doesn't leave the empty loop body. */ for_each_sg(sg, sg, nelems, i) - __dma_sync((unsigned long)sg_virt(sg), sg->length, direction); + __dma_sync(sg_virt(sg), sg->length, direction); } EXPORT_SYMBOL(dma_sync_sg_for_device); From 9fe4a89325de1a947ed9cc746f3dd21a6ef6b406 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 16 Apr 2014 19:44:34 +0800 Subject: [PATCH 046/201] FogBugz #198073: nios2: Fix virt_to_phys and phy_to_virt macros The original code support only the address within system memory region. It is wrong when we try to convert an address from I/O region. Change to use bitwise operation instead of arithmetic operation to prevent overflow. We need to OR with 0xC0000000 (CONFIG_KERNEL_REGION_BASE) if convert phys_to_virt and clear top 3 bit to convert virt_to_phys. For example: Original code: virt_to_phy(0xE0000000) = 0xE0000000 - 0xD0000000 + 0x10000000 = 0x20000000 (this is wrong) New code: virt_to_phy(0xE0000000) = 0xE0000000 & ~0xE0000000 = 0x0 (correct) Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/io.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/nios2/include/asm/io.h b/arch/nios2/include/asm/io.h index 8e58e3f3ce1a2..d78d38094a745 100644 --- a/arch/nios2/include/asm/io.h +++ b/arch/nios2/include/asm/io.h @@ -205,9 +205,10 @@ static inline void iounmap(void __iomem *addr) /* Macros used for converting between virtual and physical mappings. */ # define phys_to_virt(vaddr) \ - ((void *)((unsigned long)vaddr + PAGE_OFFSET - PHYS_OFFSET)) + ((void *)((unsigned long)vaddr | CONFIG_KERNEL_REGION_BASE)) +/* Clear top 3 bits */ # define virt_to_phys(vaddr) \ - ((unsigned long)((unsigned long)vaddr - PAGE_OFFSET + PHYS_OFFSET)) + ((unsigned long)((unsigned long)vaddr & ~0xE0000000)) #define virt_to_bus virt_to_phys #define bus_to_virt phys_to_virt From 8a8edf4392e54cd63c3fa8feaaf7f26e786cde60 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 16 Apr 2014 18:55:37 +0800 Subject: [PATCH 047/201] FogBugz #196791: nios2: update icache size settings Remove icache line size settings from Kconfig because Nios2 hardware supports only 32 bytes always. The valid icache size is between 512 byte to 64Kbytes. Signed-off-by: Ley Foon Tan v3: - Change default to 0x1000 to match default value in hardware. v2: - Add checking for CONFIG_NIOS2_ICACHE_SIZE vs device tree icache-size --- arch/nios2/include/asm/cache.h | 4 ++-- arch/nios2/kernel/cpuinfo.c | 6 ++++++ arch/nios2/platform/Kconfig.platform | 16 ++-------------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/arch/nios2/include/asm/cache.h b/arch/nios2/include/asm/cache.h index 84ece847b7e02..f7fd2c6e6b309 100644 --- a/arch/nios2/include/asm/cache.h +++ b/arch/nios2/include/asm/cache.h @@ -26,8 +26,8 @@ #define NIOS2_DCACHE_SIZE CONFIG_NIOS2_DCACHE_SIZE #define NIOS2_ICACHE_SIZE CONFIG_NIOS2_ICACHE_SIZE #define NIOS2_DCACHE_LINE_SIZE CONFIG_NIOS2_DCACHE_LINE_SIZE -#define NIOS2_ICACHE_LINE_SIZE CONFIG_NIOS2_ICACHE_LINE_SIZE -#define NIOS2_ICACHE_LINE_SHIFT CONFIG_NIOS2_ICACHE_LINE_SHIFT +#define NIOS2_ICACHE_LINE_SHIFT 5 +#define NIOS2_ICACHE_LINE_SIZE (1 << NIOS2_ICACHE_LINE_SHIFT) /* bytes per L1 cache line */ #define L1_CACHE_SHIFT NIOS2_ICACHE_LINE_SHIFT diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index 0a7fb2c4ef760..66d9cd0f72499 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -92,6 +92,12 @@ void __init setup_cpuinfo(void) "system\n"); cpuinfo.icache_line_size = fcpu(cpu, "icache-line-size"); cpuinfo.icache_size = fcpu(cpu, "icache-size"); + if (CONFIG_NIOS2_ICACHE_SIZE != cpuinfo.icache_size) + pr_warn("Warning: icache size configuration mismatch " + "(0x%x vs 0x%x) of CONFIG_NIOS2_ICACHE_SIZE vs " + "device tree icache-size\n", + CONFIG_NIOS2_ICACHE_SIZE, cpuinfo.icache_size); + cpuinfo.dcache_line_size = fcpu(cpu, "dcache-line-size"); cpuinfo.dcache_size = fcpu(cpu, "dcache-size"); diff --git a/arch/nios2/platform/Kconfig.platform b/arch/nios2/platform/Kconfig.platform index dd67f9826ca03..8ff8dc0f92058 100644 --- a/arch/nios2/platform/Kconfig.platform +++ b/arch/nios2/platform/Kconfig.platform @@ -119,21 +119,9 @@ config NIOS2_DCACHE_LINE_SIZE config NIOS2_ICACHE_SIZE hex "I-Cache size" if CUSTOM_CACHE_SETTINGS - default "0x10000" + range 0x200 0x10000 + default "0x1000" help Maximum possible instruction cache size. -config NIOS2_ICACHE_LINE_SIZE - hex "I-Cache line size" if CUSTOM_CACHE_SETTINGS - default "0x20" - help - Maximum possible instruction cache line size. - -config NIOS2_ICACHE_LINE_SHIFT - hex "I-Cache line shift" if CUSTOM_CACHE_SETTINGS - default "0x5" - help - log2 of the instruction cache line size. This must match - CONFIG_NIOS2_ICACHE_LINE_SIZE. - endmenu From fb772460500351173cd3dbdcca7efbd3e96dee06 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 16 Apr 2014 19:03:51 +0800 Subject: [PATCH 048/201] FogBugz #196797: nios2: Add valid range for dcache size in Kconfig Hardware supported range is between 512 byte to 64Kbytes. Signed-off-by: Ley Foon Tan v3: - Change default value to 0x800 to match default value in hardware. v2: - Add checking for CONFIG_NIOS2_DCACHE_SIZE vs device tree dcache-size --- arch/nios2/kernel/cpuinfo.c | 5 +++++ arch/nios2/platform/Kconfig.platform | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index 66d9cd0f72499..05d04c23cec06 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -100,6 +100,11 @@ void __init setup_cpuinfo(void) cpuinfo.dcache_line_size = fcpu(cpu, "dcache-line-size"); cpuinfo.dcache_size = fcpu(cpu, "dcache-size"); + if (CONFIG_NIOS2_DCACHE_SIZE != cpuinfo.dcache_size) + pr_warn("Warning: dcache size configuration mismatch " + "(0x%x vs 0x%x) of CONFIG_NIOS2_DCACHE_SIZE vs " + "device tree dcache-size\n", + CONFIG_NIOS2_DCACHE_SIZE, cpuinfo.dcache_size); cpuinfo.tlb_pid_num_bits = fcpu(cpu, "ALTR,pid-num-bits"); cpuinfo.tlb_num_ways_log2 = ilog2(cpuinfo.tlb_num_ways); diff --git a/arch/nios2/platform/Kconfig.platform b/arch/nios2/platform/Kconfig.platform index 8ff8dc0f92058..0f646b2732eaf 100644 --- a/arch/nios2/platform/Kconfig.platform +++ b/arch/nios2/platform/Kconfig.platform @@ -107,7 +107,8 @@ config CUSTOM_CACHE_SETTINGS config NIOS2_DCACHE_SIZE hex "D-Cache size" if CUSTOM_CACHE_SETTINGS - default "0x10000" + range 0x200 0x10000 + default "0x800" help Maximum possible data cache size. From c6f981932dfab23cb3bb51c7663c97244b69dd1f Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 25 Apr 2014 11:45:29 +0800 Subject: [PATCH 049/201] FogBugz #195995: nios2: Change default NIOS2_DCACHE_LINE_SIZE FogBugz 190747 added initda instruction support and this instruction requires more than 4-byte data cache line. So, default NIOS2_DCACHE_LINE_SIZE needs change to more than 4 and in range 0x10 and 0x20. Also add checking for CONFIG_NIOS2_DCACHE_LINE_SIZE vs device tree dcache-line-size to make sure they are matched. Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/cpuinfo.c | 6 ++++++ arch/nios2/platform/Kconfig.platform | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index 05d04c23cec06..e833cff6e3648 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -99,6 +99,12 @@ void __init setup_cpuinfo(void) CONFIG_NIOS2_ICACHE_SIZE, cpuinfo.icache_size); cpuinfo.dcache_line_size = fcpu(cpu, "dcache-line-size"); + if (CONFIG_NIOS2_DCACHE_LINE_SIZE != cpuinfo.dcache_line_size) + pr_warn("Warning: dcache line size configuration mismatch " + "(0x%x vs 0x%x) of CONFIG_NIOS2_DCACHE_LINE_SIZE vs " + "device tree dcache-line-size\n", + CONFIG_NIOS2_DCACHE_LINE_SIZE, cpuinfo.dcache_line_size); + cpuinfo.dcache_size = fcpu(cpu, "dcache-size"); if (CONFIG_NIOS2_DCACHE_SIZE != cpuinfo.dcache_size) pr_warn("Warning: dcache size configuration mismatch " diff --git a/arch/nios2/platform/Kconfig.platform b/arch/nios2/platform/Kconfig.platform index 0f646b2732eaf..e767c51afd9f4 100644 --- a/arch/nios2/platform/Kconfig.platform +++ b/arch/nios2/platform/Kconfig.platform @@ -114,7 +114,8 @@ config NIOS2_DCACHE_SIZE config NIOS2_DCACHE_LINE_SIZE hex "D-Cache line size" if CUSTOM_CACHE_SETTINGS - default "0x4" + range 0x10 0x20 + default "0x20" help Minimum possible data cache line size. From 9c1212b9be5f7d73a141f88887f8faa333f47ceb Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 28 Apr 2014 17:59:25 +0800 Subject: [PATCH 050/201] FogBugz #199159: nios2: fix parentheses warning Add parentheses for 'vaddr' variable. Example warning: mm/bootmem.c:585:3: warning: suggest parentheses around arithmetic in operand of '|' [-Wparentheses] mm/bootmem.c:585:3: region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) + start_off); Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/io.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/nios2/include/asm/io.h b/arch/nios2/include/asm/io.h index d78d38094a745..46f4a04388700 100644 --- a/arch/nios2/include/asm/io.h +++ b/arch/nios2/include/asm/io.h @@ -205,10 +205,10 @@ static inline void iounmap(void __iomem *addr) /* Macros used for converting between virtual and physical mappings. */ # define phys_to_virt(vaddr) \ - ((void *)((unsigned long)vaddr | CONFIG_KERNEL_REGION_BASE)) + ((void *)((unsigned long)(vaddr) | CONFIG_KERNEL_REGION_BASE)) /* Clear top 3 bits */ # define virt_to_phys(vaddr) \ - ((unsigned long)((unsigned long)vaddr & ~0xE0000000)) + ((unsigned long)((unsigned long)(vaddr) & ~0xE0000000)) #define virt_to_bus virt_to_phys #define bus_to_virt phys_to_virt From 53edcd0cfa64d29d19cb84a315188278e4d18eb0 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 25 Apr 2014 11:13:00 +0800 Subject: [PATCH 051/201] FogBugz #193936: nios2: Fix nios2 compilation warnings The warnings is caused by PAGE_SIZE macro. This is one of the change request by kernel community when nios2 usptreaming. Recommended way is using AC() macro for PAGE_SIZE calculation. Discussion: https://lkml.org/lkml/2014/4/22/577 Example warning: fs/select.c:901:9: warning: comparison of distinct pointer types lacks a cast [enabled by default] Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/page.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/nios2/include/asm/page.h b/arch/nios2/include/asm/page.h index 4f25e0fe6cb96..6f455f4f6fc36 100644 --- a/arch/nios2/include/asm/page.h +++ b/arch/nios2/include/asm/page.h @@ -18,12 +18,13 @@ #define _ASM_NIOS2_PAGE_H #include +#include /* * PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 -#define PAGE_SIZE 4096 +#define PAGE_SIZE (_AC(1, UL) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE - 1)) /* From be21f3734ed8c4842534792b124669e80a43d215 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 16 May 2014 15:29:01 +0800 Subject: [PATCH 052/201] FogBugz #205359: nios2: flushp after write instruction to memory According to nios2 handbook [1], software needs to execute flushd, flushi and flushp instructions after write instruction to memory. This patch add the flushp instruction after flushd and flushi. [1]http://www.altera.com/literature/hb/nios2/n2sw_nii52007.pdf (page 9-5) Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/setup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index 84364ac462dba..060c63e027ded 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -64,6 +64,7 @@ static inline void copy_exception_handler(unsigned int addr) "flushi %1\n" "addi %1,%1,4\n" "flushi %1\n" + "flushp\n" : /* no output registers */ : "r" (start), "r" (addr), "r" (tmp) : "memory" @@ -85,6 +86,7 @@ static inline void copy_fast_tlb_miss_handler(unsigned int addr) " stw %3,0(%1)\n" " flushd 0(%1)\n" " flushi %1\n" + " flushp\n" " addi %0,%0,4\n" " addi %1,%1,4\n" " bne %0,%2,1b\n" From d42e23da59063cc5ee7974009063cfdbb69d4728 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 16 May 2014 15:30:57 +0800 Subject: [PATCH 053/201] FogBugz #205319: nios2: Use initi and initd to initialize caches Change flushi to initi and flushd to initd instructions. According to nios2 handbook: - Use initi instruction initializes a single instruction cache line. Do not use the flushi instruction because it might cause undesired effects when used to initialize the instruction cache in future Nios II implementations. - Use initd instruction initializes a single data cache line. Do not use the flushd instruction for this purpose, because it writes dirty lines back to memory. [1]http://www.altera.com/literature/hb/nios2/n2sw_nii52007.pdf Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/head.S | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/nios2/kernel/head.S b/arch/nios2/kernel/head.S index cff8fa4f610af..372ce4a33018b 100644 --- a/arch/nios2/kernel/head.S +++ b/arch/nios2/kernel/head.S @@ -64,14 +64,14 @@ _current_thread: ENTRY(_start) wrctl status, r0 /* Disable interrupts */ - /* Flush all cache lines within the instruction cache */ + /* Initialize all cache lines within the instruction cache */ movia r1, NIOS2_ICACHE_SIZE movui r2, NIOS2_ICACHE_LINE_SIZE -text_flush: - flushi r1 +icache_init: + initi r1 sub r1, r1, r2 - bgt r1, r0, text_flush + bgt r1, r0, icache_init br 1f /* @@ -112,16 +112,16 @@ ENTRY(fast_handler_end) 1: /* - * After flushing the instruction cache, we must flush the data - * cache. + * After the instruction cache is initialized, the data cache must + * also be initialized. */ movia r1, NIOS2_DCACHE_SIZE movui r2, NIOS2_DCACHE_LINE_SIZE -data_flush: - flushd 0(r1) +dcache_init: + initd 0(r1) sub r1, r1, r2 - bgt r1, r0, data_flush + bgt r1, r0, dcache_init nextpc r1 /* Find out where we are */ chkadr: From 478790f36f2b709e7972aa8f7032c71dccce56fc Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 16 May 2014 16:26:18 +0800 Subject: [PATCH 054/201] FogBugz #205636: nios2: check exception handler address Don't copy exception handler if it is already at the exception address. The CPU may be configured with the exception address already pointing to the kernel's exception handler. Don't unnecessarily copy the handler in this case, although the code appears harmless. But, this can optimize boot up time. Signed-off-by: Yuriy Kozlov Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/setup.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index 060c63e027ded..f591c803a4a37 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -47,6 +47,11 @@ static inline void copy_exception_handler(unsigned int addr) unsigned int start = (unsigned int) exception_handler_hook; volatile unsigned int tmp = 0; + if (start == addr) { + /* The CPU exception address already points to the handler. */ + return; + } + /* FIXME: check overlap of source and destination address here? */ __asm__ __volatile__ ( From ba3850fd3ccac2f4014a66db187aa85dfcffa03d Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 28 May 2014 09:09:34 -0500 Subject: [PATCH 055/201] clk: socfpga: Add a second parent option for the dbg_base_clk The debug base clock can be bypassed from the main PLL to the OSC1 clock. The bypass register is the staysoc1(0x10) register that is in the clock manager. This patch adds the option to get the correct parent for the debug base clock. Signed-off-by: Dinh Nguyen --- v2: Update socfpga_periph_init to support multiple parents --- arch/arm/boot/dts/socfpga.dtsi | 2 +- drivers/clk/socfpga/clk-periph.c | 21 +++++++++++++++++---- drivers/clk/socfpga/clk.h | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 4676f25e87a7e..d9fdcf86078c7 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -163,7 +163,7 @@ dbg_base_clk: dbg_base_clk { #clock-cells = <0>; compatible = "altr,socfpga-perip-clk"; - clocks = <&main_pll>; + clocks = <&main_pll>, <&osc1>; div-reg = <0xe8 0 9>; reg = <0x50>; }; diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c index 46531c34ec9b5..9b64847bfdad9 100644 --- a/drivers/clk/socfpga/clk-periph.c +++ b/drivers/clk/socfpga/clk-periph.c @@ -45,8 +45,17 @@ static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, return parent_rate / div; } +static u8 clk_periclk_get_parent(struct clk_hw *hwclk) +{ + u32 clk_src; + + clk_src = readl(clk_mgr_base_addr + CLKMGR_DBCTRL); + return clk_src & 0x1; +} + static const struct clk_ops periclk_ops = { .recalc_rate = clk_periclk_recalc_rate, + .get_parent = clk_periclk_get_parent, }; static __init void __socfpga_periph_init(struct device_node *node, @@ -56,11 +65,12 @@ static __init void __socfpga_periph_init(struct device_node *node, struct clk *clk; struct socfpga_periph_clk *periph_clk; const char *clk_name = node->name; - const char *parent_name; + const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; int rc; u32 fixed_div; u32 div_reg[3]; + int i = 0; of_property_read_u32(node, "reg", ®); @@ -90,9 +100,12 @@ static __init void __socfpga_periph_init(struct device_node *node, init.name = clk_name; init.ops = ops; init.flags = 0; - parent_name = of_clk_get_parent_name(node, 0); - init.parent_names = &parent_name; - init.num_parents = 1; + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.parent_names = parent_name; + init.num_parents = i; periph_clk->hw.hw.init = &init; diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h index d291f60c46e1a..d036de2ae58f1 100644 --- a/drivers/clk/socfpga/clk.h +++ b/drivers/clk/socfpga/clk.h @@ -23,6 +23,7 @@ /* Clock Manager offsets */ #define CLKMGR_CTRL 0x0 #define CLKMGR_BYPASS 0x4 +#define CLKMGR_DBCTRL 0x10 #define CLKMGR_L4SRC 0x70 #define CLKMGR_PERPLL_SRC 0xAC From 4eac7a9886b762c19936403c54a4b1e96feba552 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 22 May 2014 09:55:13 -0500 Subject: [PATCH 056/201] FogBugz #198256: Fix unnecessary USB overcurrent condition The CycloneV and ArriaV devkit has a small C_VBUS capacitor on the devkit. Because of this, the USB driver sometimes detects an unnecessary overcurrent error condition. This patch uses the external VbusValid signal instead. Signed-off-by: Dinh Nguyen --- drivers/usb/dwc2/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 27d2c9b8a0344..33dbdd475e400 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -404,6 +404,11 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq) if (hsotg->core_params->ts_dline > 0) usbcfg |= GUSBCFG_TERMSELDLPULSE; + /* Set external VBUS indicator as needed. */ + if (hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_ULPI) + usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND | + GUSBCFG_INDICATORPASSTHROUGH); + writel(usbcfg, hsotg->regs + GUSBCFG); /* Reset the Controller */ From bf5ab0ae8246e453d1aa40e262b4af3dff35712a Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 12 Feb 2013 15:20:24 -0600 Subject: [PATCH 057/201] FogBugz #91445: Fix intermittent data starvation for DMA on SD/MMC This issue is related to FB #13909. We can also safely ignore the FIFO Under/Overrun error condition when the driver is using IDMAC. Signed-off-by: Dinh Nguyen Conflicts: arch/arm/configs/socfpga_defconfig drivers/mmc/host/dw_mmc.c --- drivers/mmc/host/dw_mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1ac227c603b7e..710714c53e893 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -42,8 +42,8 @@ /* Common flag combinations */ #define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \ - SDMMC_INT_HTO | SDMMC_INT_SBE | \ - SDMMC_INT_EBE) + SDMMC_INT_HTO | SDMMC_INT_FRUN | \ + SDMMC_INT_SBE | SDMMC_INT_EBE) #define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \ SDMMC_INT_RESP_ERR) #define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \ From 9963af108ae5ac420d3e1ffde6b46dddd9ac9321 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 1 Aug 2013 22:21:55 -0500 Subject: [PATCH 058/201] FogBugz #142858: Fix intermittent SD/MMC RFS from not mounting This bug was uncovered during a kernel upgrade to v3.10. The SD/MMC Hardware Locked Write Error(HLE) was intermittently getting triggered. The driver does not have any mechanism for handling this interrupt. Clear the HLE interrupt when it gets triggered. Signed-off-by: Dinh Nguyen --- drivers/mmc/host/dw_mmc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 710714c53e893..74a393f0b8b42 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1872,6 +1872,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) queue_work(host->card_workqueue, &host->card_work); } + if (pending & SDMMC_INT_HLE) + mci_writel(host, RINTSTS, SDMMC_INT_HLE); + /* Handle SDIO Interrupts */ for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; From abd93f14bb3e1e61368fbe38acb1480c14fd4692 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 26 Mar 2014 09:03:10 -0500 Subject: [PATCH 059/201] FogBugz #184030: mmc: dw_mmc: fix status error for Kingston brand SD cards commits [90c2143a8f mmc: dw_mmc: guarantee stop-abort cmd in data errors] and [e352c813110 mmc: dw_mmc: rework the code related to cmd/data completion] caused the SD/MMC DMA interface to stop working on Kingston brand SD cards. This patch fixes the issue where the SD/MMC driver would fail initializing the Kingston branded SD card. Signed-off-by: Dinh Nguyen --- drivers/mmc/host/dw_mmc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 74a393f0b8b42..3bb2decf7eaef 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1302,7 +1302,8 @@ static void dw_mci_tasklet_func(unsigned long priv) if (test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events)) { dw_mci_stop_dma(host); - send_stop_abort(host, data); + if (data->stop) + send_stop_abort(host, data); state = STATE_DATA_ERROR; break; } @@ -1324,6 +1325,11 @@ static void dw_mci_tasklet_func(unsigned long priv) set_bit(EVENT_DATA_COMPLETE, &host->completed_events); err = dw_mci_data_complete(host, data); + if (!data->stop) { + dw_mci_request_end(host, host->mrq); + goto unlock; + } + if (!err) { if (!data->stop || mrq->sbc) { if (mrq->sbc && data->stop) From 67288a40773762067501e557ee096babd5de366c Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 16 Oct 2012 11:02:35 -0600 Subject: [PATCH 060/201] arm: socfpga: Enable spi and qspi driver for socfpga -Add spi and qspi driver device tree entries -Add spi and qspi into socfpga_defconfig Signed-off-by: Dinh Nguyen --- arch/arm/boot/dts/socfpga.dtsi | 42 +++++++++++++++++ arch/arm/boot/dts/socfpga_cyclone5.dtsi | 63 +++++++++++++++++++++++++ arch/arm/configs/socfpga_defconfig | 6 +++ 3 files changed, 111 insertions(+) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index d9fdcf86078c7..3438b929c7aef 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -624,6 +624,48 @@ clock-names = "biu", "ciu"; }; + spi0: spi@fff00000 { + compatible = "snps,dw-spi-mmio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xfff00000 0x1000>; + interrupts = <0 154 4>; + num-chipselect = <4>; + bus-num = <0>; + tx-dma-channel = <&pdma 16>; + rx-dma-channel = <&pdma 17>; + clocks = <&per_base_clk>; + status = "disabled"; + + spidev@0 { + compatible = "spidev"; + reg = <0>; /* chip select */ + spi-max-frequency = <100000000>; + enable-dma = <1>; + }; + }; + + spi1: spi@fff01000 { + compatible = "snps,dw-spi-mmio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xfff01000 0x1000>; + interrupts = <0 156 4>; + num-chipselect = <4>; + bus-num = <1>; + tx-dma-channel = <&pdma 20>; + rx-dma-channel = <&pdma 21>; + clocks = <&per_base_clk>; + status = "disabled"; + + spidev@0 { + compatible = "spidev"; + reg = <0>; + spi-max-frequency = <100000000>; + enable-dma = <1>; + }; + }; + /* Local timer */ timer@fffec600 { compatible = "arm,cortex-a9-twd-timer"; diff --git a/arch/arm/boot/dts/socfpga_cyclone5.dtsi b/arch/arm/boot/dts/socfpga_cyclone5.dtsi index bf511828729f9..b972951fcdff3 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5.dtsi +++ b/arch/arm/boot/dts/socfpga_cyclone5.dtsi @@ -49,4 +49,67 @@ cpu1-start-addr = <0xffd080c4>; }; }; + + soc { + qspi: spi@ff705000 { + compatible = "cadence,qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xff705000 0x1000>, + <0xffa00000 0x1000>; + interrupts = <0 151 4>; + master-ref-clk = <400000000>; + ext-decoder = <0>; /* external decoder */ + num-chipselect = <4>; + fifo-depth = <128>; + bus-num = <2>; + + flash0: n25q128@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "n25q128"; + reg = <0>; /* chip select */ + spi-max-frequency = <100000000>; + page-size = <256>; + block-size = <16>; /* 2^16, 64KB */ + quad = <1>; /* 1-support quad */ + tshsl-ns = <200>; + tsd2d-ns = <255>; + tchsh-ns = <20>; + tslch-ns = <20>; + + partition@0 { + /* 8MB for raw data. */ + label = "Flash 0 Raw Data"; + reg = <0x0 0x800000>; + }; + partition@800000 { + /* 8MB for jffs2 data. */ + label = "Flash 0 jffs2 Filesystem"; + reg = <0x800000 0x800000>; + }; + }; + + flash1: n25q128@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "n25q128"; + reg = <1>; /* chip select */ + spi-max-frequency = <100000000>; + page-size = <256>; + block-size = <16>; /* 2^16, 64KB */ + quad = <1>; + tshsl-ns = <200>; + tsd2d-ns = <255>; + tchsh-ns = <20>; + tslch-ns = <20>; + + partition@0 { + /* 16MB for user data. */ + label = "Flash 1 User Data"; + reg = <0x0 0x1000000>; + }; + }; + }; + }; }; diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index e3a05e8801d8f..5559faaab4845 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -63,6 +63,12 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_NR_UARTS=2 CONFIG_SERIAL_8250_RUNTIME_UARTS=2 CONFIG_SERIAL_8250_DW=y +CONFIG_SPI=y +CONFIG_SPI_CADENCE_QSPI=y +CONFIG_SPI_DESIGNWARE=y +CONFIG_SPI_DW_PL330_DMA=y +CONFIG_SPI_DW_MMIO=y +CONFIG_SPI_SPIDEV=y # CONFIG_RTC_HCTOSYS is not set CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y From 1ebb1c21a19541f6e1fca8e89bf6df1afe69ae1f Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 22 Oct 2012 15:48:35 -0600 Subject: [PATCH 061/201] FogBugz #79418: Add support for Denali NAND controller Enable support for SOCFPGA to use the Denali NAND controller. Add a node for the NAND clock. Signed-off-by: Dinh Nguyen --- arch/arm/boot/dts/socfpga.dtsi | 40 ++++++++++++++++++++++++++++++ arch/arm/configs/socfpga_defconfig | 9 +++++++ 2 files changed, 49 insertions(+) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 3438b929c7aef..db50a9ad69d5d 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -624,6 +624,46 @@ clock-names = "biu", "ciu"; }; + nand: nand@ff900000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "denali,denali-nand-dt"; + reg = <0xff900000 0x100000>, <0xffb80000 0x10000>; + reg-names = "nand_data", "denali_reg"; + interrupts = <0 144 4>; + dma-mask = <0xffffffff>; + clocks = <&nand_clk>; + have-hw-ecc-fixup; + status = "disabled"; + + partition@nand-boot { + /* 8MB for raw data. */ + label = "NAND Flash Boot Area 8MB"; + reg = <0x0 0x800000>; + }; + partition@nand-rootfs { + /* 128MB jffs2 root filesystem. */ + label = "NAND Flash jffs2 Root Filesystem 128MB"; + reg = <0x800000 0x8000000>; + }; + partition@nand-128 { + label = "NAND Flash 128 MB"; + reg = <0x8800000 0x8000000>; + }; + partition@nand-64 { + label = "NAND Flash 64 MB"; + reg = <0x10800000 0x4000000>; + }; + partition@nand-32 { + label = "NAND Flash 32 MB"; + reg = <0x14800000 0x2000000>; + }; + partition@nand-16 { + label = "NAND Flash 16 MB"; + reg = <0x16800000 0x1000000>; + }; + }; + spi0: spi@fff00000 { compatible = "snps,dw-spi-mmio"; #address-cells = <1>; diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 5559faaab4845..bf2771e4d4da8 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -42,6 +42,15 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_IP_PNP_RARP=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y +CONFIG_MTD=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_NAND_ECC=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_DENALI=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_NAND_IDS=y CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=2 From cba961f194b97cfef9821c1ee3eea5fb35c31cde Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 21 Dec 2012 14:07:14 -0600 Subject: [PATCH 062/201] FogBugz #91408: Add GPIO driver to support FPGA PIO Clean up gpio-dw and gpio-altera drivers. Release the FPGA bridges out of reset. Signed-off-by: Dinh Nguyen Conflicts: arch/arm/boot/dts/socfpga.dtsi arch/arm/configs/socfpga_defconfig arch/arm/mach-socfpga/socfpga.c drivers/gpio/Kconfig drivers/gpio/gpio-dw.c --- arch/arm/configs/socfpga_defconfig | 3 + arch/arm/mach-socfpga/socfpga.c | 15 ++ drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-altera.c | 279 +++++++++++++++++++++++++++++ drivers/gpio/gpio-dw.c | 217 ++++++++++++++++++++++ 6 files changed, 521 insertions(+) create mode 100644 drivers/gpio/gpio-altera.c create mode 100644 drivers/gpio/gpio-dw.c diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index bf2771e4d4da8..fa40056d79673 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -78,6 +78,9 @@ CONFIG_SPI_DESIGNWARE=y CONFIG_SPI_DW_PL330_DMA=y CONFIG_SPI_DW_MMIO=y CONFIG_SPI_SPIDEV=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_ALTERA=m # CONFIG_RTC_HCTOSYS is not set CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index adbf38314ca8c..1e8f2e0d71632 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -56,6 +56,15 @@ static void __init socfpga_scu_map_io(void) iotable_init(&scu_io_desc, 1); } +static void __init enable_periphs(void) +{ + /* Release all peripherals from reset.*/ + __raw_writel(0, rst_manager_base_addr + SOCFPGA_RSTMGR_MODPERRST); + + /* Release all FPGA bridges from reset.*/ + __raw_writel(0, rst_manager_base_addr + SOCFPGA_RSTMGR_BRGMODRST); +} + static void __init socfpga_map_io(void) { socfpga_scu_map_io(); @@ -98,6 +107,11 @@ static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd) writel(temp, rst_manager_base_addr + SOCFPGA_RSTMGR_CTRL); } +static void __init socfpga_cyclone5_init(void) +{ + enable_periphs(); +} + static const char *altera_dt_match[] = { "altr,socfpga", NULL @@ -109,6 +123,7 @@ DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA") .smp = smp_ops(socfpga_smp_ops), .map_io = socfpga_map_io, .init_irq = socfpga_init_irq, + .init_machine = socfpga_cyclone5_init, .restart = socfpga_cyclone5_restart, .dt_compat = altera_dt_match, MACHINE_END diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4a1b5113e5277..8bd7acfab2737 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -141,6 +141,12 @@ config GPIO_DWAPB Say Y or M here to build support for the Synopsys DesignWare APB GPIO block. +config GPIO_ALTERA + tristate "Altera GPIO" + depends on OF_GPIO + help + Say yes here to support the Altera PIO device. + config GPIO_IT8761E tristate "IT8761E GPIO support" depends on X86 # unconditional access to IO space. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d10f6a9d875a2..c0ce6c3b991dd 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o +obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c new file mode 100644 index 0000000000000..42842d96b4d6d --- /dev/null +++ b/drivers/gpio/gpio-altera.c @@ -0,0 +1,279 @@ +/* + * Altera GPIO driver + * + * Copyright (C) 2012 Tobias Klauser + * Copyright (C) 2011 Thomas Chou + * + * Based on Xilinx gpio driver, which is + * Copyright 2008 Xilinx, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_gpio" + +/* Register Offset Definitions */ +#define ALTERA_GPIO_DATA_OFFSET 0x0 /* Data register */ +#define ALTERA_GPIO_DIR_OFFSET 0x4 /* I/O direction register */ +#define ALTERA_GPIO_IRQ_MASK 0x8 +#define ALTERA_GPIO_EDGE_CAP 0xc + +struct altera_gpio_instance { + struct of_mm_gpio_chip mmchip; + u32 gpio_state; /* GPIO state shadow register */ + u32 gpio_dir; /* GPIO direction shadow register */ + int irq; /* GPIO controller IRQ number */ + int irq_base; /* base number for the "virtual" GPIO IRQs */ + u32 irq_mask; /* IRQ mask */ + spinlock_t gpio_lock; /* Lock used for synchronization */ +}; + +static inline struct altera_gpio_instance *to_altera_gpio( + struct of_mm_gpio_chip *mm_gc) +{ + return container_of(mm_gc, struct altera_gpio_instance, mmchip); +} + +/* + * altera_gpio_get - Read the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function reads the specified signal of the GPIO device. It returns 0 if + * the signal clear, 1 if signal is set or negative value on error. + */ +static int altera_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); + void __iomem *edge_cap = mm_gc->regs + ALTERA_GPIO_EDGE_CAP; + unsigned long value; + + if (chip->irq >= 0) { + if (__raw_readl(edge_cap) & (1 << gpio)) + __raw_writel(1 << gpio, edge_cap); + } + + value = __raw_readl(mm_gc->regs + ALTERA_GPIO_DATA_OFFSET); + return (value >> gpio) & 1; +} + +/* + * altera_gpio_set - Write the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function writes the specified value in to the specified signal of the + * GPIO device. + */ +static void altera_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write to shadow register and output */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + __raw_writel(chip->gpio_state, mm_gc->regs + ALTERA_GPIO_DATA_OFFSET); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +/* + * altera_gpio_dir_in - Set the direction of the specified GPIO signal as input. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function sets the direction of specified GPIO signal as input. + * It returns 0 if direction of GPIO signals is set as input otherwise it + * returns negative error value. + */ +static int altera_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Clear the GPIO bit in shadow register and set direction as input */ + chip->gpio_dir &= ~(1 << gpio); + __raw_writel(chip->gpio_dir, mm_gc->regs + ALTERA_GPIO_DIR_OFFSET); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/* + * altera_gpio_dir_out - Set the direction of the specified GPIO as output. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function sets the direction of specified GPIO signal as output. If all + * GPIO signals of GPIO chip is configured as input then it returns + * error otherwise it returns 0. + */ +static int altera_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write state of GPIO signal */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + __raw_writel(chip->gpio_state, mm_gc->regs + ALTERA_GPIO_DATA_OFFSET); + + /* Set the GPIO bit in shadow register and set direction as output */ + chip->gpio_dir |= (1 << gpio); + __raw_writel(chip->gpio_dir, mm_gc->regs + ALTERA_GPIO_DIR_OFFSET); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/* + * altera_gpio_save_regs - Set initial values of GPIO pins + * @mm_gc: pointer to memory mapped GPIO chip structure + */ +static void altera_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); + + __raw_writel(chip->gpio_state, mm_gc->regs + ALTERA_GPIO_DATA_OFFSET); + __raw_writel(chip->gpio_dir, mm_gc->regs + ALTERA_GPIO_DIR_OFFSET); +} + + + +/* + * altera_gpio_of_probe - Probe method for the GPIO device. + * @np: pointer to device tree node + * + * This function probes the GPIO device in the device tree. It initializes the + * driver data structure. It returns 0, if the driver is bound to the GPIO + * device, or a negative value if there is an error. + */ +static int __devinit altera_gpio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct altera_gpio_instance *chip; + int status = 0; + u32 reg; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + /* Update GPIO state shadow register with default value */ + if (of_property_read_u32(np, "resetvalue", ®) == 0) + chip->gpio_state = reg; + + /* Update GPIO direction shadow register with default value */ + chip->gpio_dir = 0; /* By default, all pins are inputs */ + + /* Check device node for device width. Default to full width. */ + if (of_property_read_u32(np, "width", ®) == 0) + chip->mmchip.gc.ngpio = reg; + else + chip->mmchip.gc.ngpio = 32; + + spin_lock_init(&chip->gpio_lock); + + chip->mmchip.gc.direction_input = altera_gpio_dir_in; + chip->mmchip.gc.direction_output = altera_gpio_dir_out; + chip->mmchip.gc.get = altera_gpio_get; + chip->mmchip.gc.set = altera_gpio_set; + + chip->mmchip.save_regs = altera_gpio_save_regs; + + /* Call the OF gpio helper to setup and register the GPIO device */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { + kfree(chip); + pr_err("%s: error in probe function with status %d\n", + np->full_name, status); + return status; + } + + platform_set_drvdata(pdev, chip); + + return 0; +} + +static int altera_gpio_remove(struct platform_device *pdev) +{ + int status; + + struct altera_gpio_instance *chip = platform_get_drvdata(pdev); + status = gpiochip_remove(&chip->mmchip.gc); + if (status < 0) + return status; + + kfree(chip); + return -EIO; +} + +#ifdef CONFIG_OF +static struct of_device_id altera_gpio_of_match[] __devinitdata = { + { .compatible = "altr,pio-1.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, altera_gpio_of_match); +#else +#define altera_gpio_of_match NULL +#endif + +static struct platform_driver altera_gpio_driver = { + .driver = { + .name = "altera_gpio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_gpio_of_match), + }, + .probe = altera_gpio_probe, + .remove = altera_gpio_remove, +}; + +static int __init altera_gpio_init(void) +{ + return platform_driver_register(&altera_gpio_driver); +} +subsys_initcall(altera_gpio_init); + +static void __exit altera_gpio_exit(void) +{ + platform_driver_unregister(&altera_gpio_driver); +} +module_exit(altera_gpio_exit); + +MODULE_DESCRIPTION("Altera GPIO driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/gpio/gpio-dw.c b/drivers/gpio/gpio-dw.c new file mode 100644 index 0000000000000..802bd1491973f --- /dev/null +++ b/drivers/gpio/gpio-dw.c @@ -0,0 +1,217 @@ +/* + * Designware GPIO support functions + * + * Copyright (C) 2012 Altera + * + * 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 3 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIO_INT_EN_REG_OFFSET (0x30) +#define GPIO_INT_MASK_REG_OFFSET (0x34) +#define GPIO_INT_TYPE_LEVEL_REG_OFFSET (0x38) +#define GPIO_INT_POLARITY_REG_OFFSET (0x3c) +#define GPIO_INT_STATUS_REG_OFFSET (0x40) +#define GPIO_PORT_A_EOI_REG_OFFSET (0x4c) + +#define GPIO_DDR_OFFSET_PORT (0x4) +#define DW_GPIO_EXT (0x50) +#define DW_GPIO_DR (0x0) +#define DRV_NAME "dw gpio" + +struct dw_gpio_instance { + struct of_mm_gpio_chip mmchip; + u32 gpio_state; /* GPIO state shadow register */ + u32 gpio_dir; /* GPIO direction shadow register */ + int irq; /* GPIO controller IRQ number */ + int irq_base; /* base number for the "virtual" GPIO IRQs */ + u32 irq_mask; /* IRQ mask */ + spinlock_t gpio_lock; /* Lock used for synchronization */ +}; + +static int dw_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + + return (__raw_readl(mm_gc->regs + DW_GPIO_EXT) >> offset) & 1; +} + +static void dw_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct dw_gpio_instance *chip = container_of(mm_gc, struct dw_gpio_instance, mmchip); + unsigned long flags; + u32 data_reg; + + spin_lock_irqsave(&chip->gpio_lock, flags); + data_reg = __raw_readl(mm_gc->regs + DW_GPIO_DR); + data_reg = (data_reg & ~(1<regs + DW_GPIO_DR); + spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +static int dw_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct dw_gpio_instance *chip = container_of(mm_gc, struct dw_gpio_instance, mmchip); + unsigned long flags; + u32 gpio_ddr; + + spin_lock_irqsave(&chip->gpio_lock, flags); + /* Set pin as input, assumes software controlled IP */ + gpio_ddr = __raw_readl(mm_gc->regs + GPIO_DDR_OFFSET_PORT); + gpio_ddr &= ~(1 << offset); + __raw_writel(gpio_ddr, mm_gc->regs + GPIO_DDR_OFFSET_PORT); + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +static int dw_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct dw_gpio_instance *chip = container_of(mm_gc, struct dw_gpio_instance, mmchip); + unsigned long flags; + u32 gpio_ddr; + + dw_gpio_set(gc, offset, value); + + spin_lock_irqsave(&chip->gpio_lock, flags); + /* Set pin as output, assumes software controlled IP */ + gpio_ddr = __raw_readl(mm_gc->regs + GPIO_DDR_OFFSET_PORT); + gpio_ddr |= (1 << offset); + __raw_writel(gpio_ddr, mm_gc->regs + GPIO_DDR_OFFSET_PORT); + spin_unlock_irqrestore(&chip->gpio_lock, flags); + return 0; +} + +/* + * dw_gpio_probe - Probe method for the GPIO device. + * @np: pointer to device tree node + * + * This function probes the GPIO device in the device tree. It initializes the + * driver data structure. It returns 0, if the driver is bound to the GPIO + * device, or a negative value if there is an error. + */ +static int __devinit dw_gpio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct dw_gpio_instance *chip; + int status = 0; + u32 reg; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + { + printk(KERN_ERR "%s 2 ERROR allocating memory", __func__); + return -ENOMEM; + } + + /* Update GPIO state shadow register with default value */ + if (of_property_read_u32(np, "resetvalue", ®) == 0) + chip->gpio_state = reg; + + /* Update GPIO direction shadow register with default value */ + chip->gpio_dir = 0; /* By default, all pins are inputs */ + + /* Check device node for device width */ + if (of_property_read_u32(np, "width", ®) == 0) + chip->mmchip.gc.ngpio = reg; + else + chip->mmchip.gc.ngpio = 32; /* By default assume full GPIO controller */ + + spin_lock_init(&chip->gpio_lock); + + chip->mmchip.gc.direction_input = dw_gpio_direction_input; + chip->mmchip.gc.direction_output = dw_gpio_direction_output; + chip->mmchip.gc.get = dw_gpio_get; + chip->mmchip.gc.set = dw_gpio_set; + + /* Call the OF gpio helper to setup and register the GPIO device */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { + kfree(chip); + pr_err("%s: error in probe function with status %d\n", + np->full_name, status); + return status; + } + + platform_set_drvdata(pdev, chip); + return 0; +} + +static int dw_gpio_remove(struct platform_device *pdev) +{ + /* todo check this and see that we don't have a memory leak */ + int status; + + struct dw_gpio_instance *chip = platform_get_drvdata(pdev); + status = gpiochip_remove(&chip->mmchip.gc); + if (status < 0) + return status; + + kfree(chip); + return -EIO; +} + +#ifdef CONFIG_OF +static const struct of_device_id dwgpio_match[] = { + {.compatible = DW_GPIO_COMPATIBLE,}, + {} +}; +MODULE_DEVICE_TABLE(of, dwgpio_match); +#else +#define dwgpio_match NULL +#endif + +static struct platform_driver dwgpio_driver = { + .driver = { + .name = "dw_gpio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(dwgpio_match), + }, + .probe = dw_gpio_probe, + .remove = dw_gpio_remove, +}; + +static int __init dwgpio_init(void) +{ + return platform_driver_register(&dwgpio_driver); +} +subsys_initcall(dwgpio_init); + +static void __exit dwgpio_exit(void) +{ + platform_driver_unregister(&dwgpio_driver); +} +module_exit(dwgpio_exit); + + +MODULE_DESCRIPTION("Altera GPIO driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); From bfceb185282307057280f8c60f1d408a2e3d6596 Mon Sep 17 00:00:00 2001 From: Tien Hock Loh Date: Fri, 1 Feb 2013 19:35:01 +0800 Subject: [PATCH 063/201] FogBugz #95976: Altera GPIO soft IP driver Port Altera GPIO soft IP driver from mpc8xxx so that IRQ is supported for Altera GPIO driver Signed-off-by: Tien Hock Loh --- .../devicetree/bindings/gpio/gpio-altera.txt | 33 ++ drivers/gpio/Makefile | 2 +- drivers/gpio/gpio-altera.c | 404 +++++++++++------- 3 files changed, 278 insertions(+), 161 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-altera.txt diff --git a/Documentation/devicetree/bindings/gpio/gpio-altera.txt b/Documentation/devicetree/bindings/gpio/gpio-altera.txt new file mode 100644 index 0000000000000..39cead59d177a --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-altera.txt @@ -0,0 +1,33 @@ +Altera GPIO controller bindings + +Required properties: +- compatible: + - "altr,pio-1.0" +- #gpio-cells : Should be two. + - first cell is the pin number + - second cell is used to specify optional parameters (unused) +- gpio-controller : Marks the device node as a GPIO controller. +- #interrupt-cells : Should be 1. +- interrupt-controller: Mark the device node as an interrupt controller + The first cell is the GPIO number. + +Altera GPIO specific properties: +- width: Width of the GPIO bank, range from 1-32 +- level_trigger: Specifies whether the GPIO interrupt is level trigger. + This field is required if the Altera GPIO controller used has IRQ enabled. + +Note: If the Altera GPIO is set to be built as a module, peripherals that uses +Altera GPIO as interrupt-parent should be a module so that the peripheral +doesn't get initialized before Altera GPIO is initialized. + +Example: + +gpio_altr: gpio_altr { + compatible = "altr,pio-1.0"; + width = <32>; + level_trigger = <0>; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <1>; + interrupt-controller; +}; diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c0ce6c3b991dd..77fb72fa028fb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -14,7 +14,7 @@ obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o -obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o +obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 42842d96b4d6d..dcb8fb90a14a8 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -1,248 +1,334 @@ /* - * Altera GPIO driver + * Copyright (C) 2013 Altera Corporation + * Based on gpio-mpc8xxx.c * - * Copyright (C) 2012 Tobias Klauser - * Copyright (C) 2011 Thomas Chou + * 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 3 of the License, or + * (at your option) any later version. * - * Based on Xilinx gpio driver, which is - * Copyright 2008 Xilinx, Inc. * - * 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. + * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see */ -#include #include +#include #include +#include #include -#include +#include #include +#include +#include +#include #include -#define DRV_NAME "altera_gpio" - -/* Register Offset Definitions */ -#define ALTERA_GPIO_DATA_OFFSET 0x0 /* Data register */ -#define ALTERA_GPIO_DIR_OFFSET 0x4 /* I/O direction register */ +#define ALTERA_GPIO_DATA 0x0 +#define ALTERA_GPIO_DIR 0x4 #define ALTERA_GPIO_IRQ_MASK 0x8 #define ALTERA_GPIO_EDGE_CAP 0xc +#define ALTERA_GPIO_OUTSET 0x10 +#define ALTERA_GPIO_OUTCLEAR 0x14 -struct altera_gpio_instance { +struct altera_gpio_chip { struct of_mm_gpio_chip mmchip; - u32 gpio_state; /* GPIO state shadow register */ - u32 gpio_dir; /* GPIO direction shadow register */ - int irq; /* GPIO controller IRQ number */ - int irq_base; /* base number for the "virtual" GPIO IRQs */ - u32 irq_mask; /* IRQ mask */ + struct irq_domain *irq; /* GPIO controller IRQ number */ spinlock_t gpio_lock; /* Lock used for synchronization */ + int level_trigger; + int hwirq; }; -static inline struct altera_gpio_instance *to_altera_gpio( - struct of_mm_gpio_chip *mm_gc) +static void altera_gpio_irq_unmask(struct irq_data *d) { - return container_of(mm_gc, struct altera_gpio_instance, mmchip); + struct altera_gpio_chip *altera_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm_gc = &altera_gc->mmchip; + unsigned long flags; + unsigned int intmask; + + spin_lock_irqsave(&altera_gc->gpio_lock, flags); + intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */ + intmask |= (1 << irqd_to_hwirq(d)); + writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); } -/* - * altera_gpio_get - Read the specified signal of the GPIO device. - * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * - * This function reads the specified signal of the GPIO device. It returns 0 if - * the signal clear, 1 if signal is set or negative value on error. - */ -static int altera_gpio_get(struct gpio_chip *gc, unsigned int gpio) +static void altera_gpio_irq_mask(struct irq_data *d) { - struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); - void __iomem *edge_cap = mm_gc->regs + ALTERA_GPIO_EDGE_CAP; - unsigned long value; - - if (chip->irq >= 0) { - if (__raw_readl(edge_cap) & (1 << gpio)) - __raw_writel(1 << gpio, edge_cap); - } + struct altera_gpio_chip *altera_gc = irq_data_get_irq_chip_data(d); + struct of_mm_gpio_chip *mm_gc = &altera_gc->mmchip; + unsigned long flags; + unsigned int intmask; + + spin_lock_irqsave(&altera_gc->gpio_lock, flags); + intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */ + intmask &= ~(1 << irqd_to_hwirq(d)); + writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); +} - value = __raw_readl(mm_gc->regs + ALTERA_GPIO_DATA_OFFSET); - return (value >> gpio) & 1; +static int altera_gpio_irq_set_type(struct irq_data *d, + unsigned int type) +{ + if (type == IRQ_TYPE_NONE) + return 0; + return -EINVAL; } -/* - * altera_gpio_set - Write the specified signal of the GPIO device. - * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * @val: Value to be written to specified signal. - * - * This function writes the specified value in to the specified signal of the - * GPIO device. - */ -static void altera_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +static struct irq_chip altera_irq_chip = { + .name = "altera-gpio", + .irq_mask = altera_gpio_irq_mask, + .irq_unmask = altera_gpio_irq_unmask, + .irq_set_type = altera_gpio_irq_set_type, +}; + +static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) { - unsigned long flags; struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); - spin_lock_irqsave(&chip->gpio_lock, flags); + return (readl(mm_gc->regs + ALTERA_GPIO_DATA) >> offset) & 1; +} - /* Write to shadow register and output */ - if (val) - chip->gpio_state |= 1 << gpio; - else - chip->gpio_state &= ~(1 << gpio); - __raw_writel(chip->gpio_state, mm_gc->regs + ALTERA_GPIO_DATA_OFFSET); +static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct altera_gpio_chip *chip = container_of(mm_gc, + struct altera_gpio_chip, mmchip); + unsigned long flags; + unsigned int data_reg; + spin_lock_irqsave(&chip->gpio_lock, flags); + data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); + data_reg = (data_reg & ~(1 << offset)) | (value << offset); + writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); spin_unlock_irqrestore(&chip->gpio_lock, flags); } -/* - * altera_gpio_dir_in - Set the direction of the specified GPIO signal as input. - * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * - * This function sets the direction of specified GPIO signal as input. - * It returns 0 if direction of GPIO signals is set as input otherwise it - * returns negative error value. - */ -static int altera_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) { - unsigned long flags; struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); + struct altera_gpio_chip *chip = container_of(mm_gc, + struct altera_gpio_chip, mmchip); + unsigned long flags; + unsigned int gpio_ddr; spin_lock_irqsave(&chip->gpio_lock, flags); - - /* Clear the GPIO bit in shadow register and set direction as input */ - chip->gpio_dir &= ~(1 << gpio); - __raw_writel(chip->gpio_dir, mm_gc->regs + ALTERA_GPIO_DIR_OFFSET); - + /* Set pin as input, assumes software controlled IP */ + gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); + gpio_ddr &= ~(1 << offset); + writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); spin_unlock_irqrestore(&chip->gpio_lock, flags); return 0; } -/* - * altera_gpio_dir_out - Set the direction of the specified GPIO as output. - * @gc: Pointer to gpio_chip device structure. - * @gpio: GPIO signal number. - * @val: Value to be written to specified signal. - * - * This function sets the direction of specified GPIO signal as output. If all - * GPIO signals of GPIO chip is configured as input then it returns - * error otherwise it returns 0. - */ -static int altera_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +static int altera_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) { - unsigned long flags; struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); + struct altera_gpio_chip *chip = container_of(mm_gc, + struct altera_gpio_chip, mmchip); + unsigned long flags; + unsigned int data_reg, gpio_ddr; spin_lock_irqsave(&chip->gpio_lock, flags); - - /* Write state of GPIO signal */ - if (val) - chip->gpio_state |= 1 << gpio; - else - chip->gpio_state &= ~(1 << gpio); - __raw_writel(chip->gpio_state, mm_gc->regs + ALTERA_GPIO_DATA_OFFSET); - - /* Set the GPIO bit in shadow register and set direction as output */ - chip->gpio_dir |= (1 << gpio); - __raw_writel(chip->gpio_dir, mm_gc->regs + ALTERA_GPIO_DIR_OFFSET); - + /* Sets the GPIO value */ + data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); + data_reg = (data_reg & ~(1 << offset)) | (value << offset); + writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); + + /* Set pin as output, assumes software controlled IP */ + gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); + gpio_ddr |= (1 << offset); + writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); spin_unlock_irqrestore(&chip->gpio_lock, flags); return 0; } -/* - * altera_gpio_save_regs - Set initial values of GPIO pins - * @mm_gc: pointer to memory mapped GPIO chip structure - */ -static void altera_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) +static int altera_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { - struct altera_gpio_instance *chip = to_altera_gpio(mm_gc); + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct altera_gpio_chip *altera_gc = container_of(mm_gc, + struct altera_gpio_chip, mmchip); - __raw_writel(chip->gpio_state, mm_gc->regs + ALTERA_GPIO_DATA_OFFSET); - __raw_writel(chip->gpio_dir, mm_gc->regs + ALTERA_GPIO_DIR_OFFSET); + if (altera_gc->irq == 0) + return -ENXIO; + if ((altera_gc->irq && offset) < altera_gc->mmchip.gc.ngpio) + return irq_create_mapping(altera_gc->irq, offset); + else + return -ENXIO; } +static void altera_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct altera_gpio_chip *altera_gc = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct of_mm_gpio_chip *mm_gc = &altera_gc->mmchip; + unsigned long status; + + int base; + chip->irq_mask(&desc->irq_data); + + if (altera_gc->level_trigger) + status = readl(mm_gc->regs + ALTERA_GPIO_DATA); + else { + status = readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP); + writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); + } + + status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + for (base = 0; base < mm_gc->gc.ngpio; base++) { + if ((1 << base) & status) { + generic_handle_irq( + irq_linear_revmap(altera_gc->irq, base)); + } + } + chip->irq_eoi(irq_desc_get_irq_data(desc)); + chip->irq_unmask(&desc->irq_data); +} -/* - * altera_gpio_of_probe - Probe method for the GPIO device. - * @np: pointer to device tree node - * - * This function probes the GPIO device in the device tree. It initializes the - * driver data structure. It returns 0, if the driver is bound to the GPIO - * device, or a negative value if there is an error. - */ -static int __devinit altera_gpio_probe(struct platform_device *pdev) +static int altera_gpio_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw_irq_num) { - struct device_node *np = pdev->dev.of_node; - struct altera_gpio_instance *chip; - int status = 0; - u32 reg; + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &altera_irq_chip, handle_level_irq); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static struct irq_domain_ops altera_gpio_irq_ops = { + .map = altera_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return -ENOMEM; +int altera_gpio_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + int id, reg, ret; + struct altera_gpio_chip *altera_gc = devm_kzalloc(&pdev->dev, + sizeof(*altera_gc), GFP_KERNEL); + if (altera_gc == NULL) { + ret = -ENOMEM; + pr_err("%s: registration failed with status %d\n", + node->full_name, ret); + return ret; + } + altera_gc->irq = 0; - /* Update GPIO state shadow register with default value */ - if (of_property_read_u32(np, "resetvalue", ®) == 0) - chip->gpio_state = reg; + spin_lock_init(&altera_gc->gpio_lock); - /* Update GPIO direction shadow register with default value */ - chip->gpio_dir = 0; /* By default, all pins are inputs */ + id = pdev->id; - /* Check device node for device width. Default to full width. */ - if (of_property_read_u32(np, "width", ®) == 0) - chip->mmchip.gc.ngpio = reg; + if (of_property_read_u32(node, "width", ®)) + /*By default assume full GPIO controller*/ + altera_gc->mmchip.gc.ngpio = 32; else - chip->mmchip.gc.ngpio = 32; + altera_gc->mmchip.gc.ngpio = reg; - spin_lock_init(&chip->gpio_lock); + if (altera_gc->mmchip.gc.ngpio > 32) { + pr_warn("%s: ngpio is greater than 32, defaulting to 32\n", + node->full_name); + altera_gc->mmchip.gc.ngpio = 32; + } - chip->mmchip.gc.direction_input = altera_gpio_dir_in; - chip->mmchip.gc.direction_output = altera_gpio_dir_out; - chip->mmchip.gc.get = altera_gpio_get; - chip->mmchip.gc.set = altera_gpio_set; + altera_gc->mmchip.gc.direction_input = altera_gpio_direction_input; + altera_gc->mmchip.gc.direction_output = altera_gpio_direction_output; + altera_gc->mmchip.gc.get = altera_gpio_get; + altera_gc->mmchip.gc.set = altera_gpio_set; + altera_gc->mmchip.gc.to_irq = altera_gpio_to_irq; + altera_gc->mmchip.gc.owner = THIS_MODULE; - chip->mmchip.save_regs = altera_gpio_save_regs; + ret = of_mm_gpiochip_add(node, &altera_gc->mmchip); + if (ret) + goto err; - /* Call the OF gpio helper to setup and register the GPIO device */ - status = of_mm_gpiochip_add(np, &chip->mmchip); - if (status) { - kfree(chip); - pr_err("%s: error in probe function with status %d\n", - np->full_name, status); - return status; + platform_set_drvdata(pdev, altera_gc); + + if (of_get_property(node, "interrupts", ®) == NULL) + goto skip_irq; + altera_gc->hwirq = irq_of_parse_and_map(node, 0); + + if (altera_gc->hwirq == NO_IRQ) + goto skip_irq; + + altera_gc->irq = irq_domain_add_linear(node, altera_gc->mmchip.gc.ngpio, + &altera_gpio_irq_ops, altera_gc); + + if (!altera_gc->irq) { + ret = -ENODEV; + goto dispose_irq; + } + + if (of_property_read_u32(node, "level_trigger", ®)) { + ret = -EINVAL; + pr_err("%s: level_trigger value not set in device tree\n", + node->full_name); + goto teardown; } + altera_gc->level_trigger = reg; + + irq_set_handler_data(altera_gc->hwirq, altera_gc); + irq_set_chained_handler(altera_gc->hwirq, altera_gpio_irq_handler); + + return 0; + +teardown: + irq_domain_remove(altera_gc->irq); +dispose_irq: + irq_dispose_mapping(altera_gc->hwirq); + WARN_ON(gpiochip_remove(&altera_gc->mmchip.gc) < 0); - platform_set_drvdata(pdev, chip); +err: + pr_err("%s: registration failed with status %d\n", + node->full_name, ret); + devm_kfree(&pdev->dev, altera_gc); + return ret; +skip_irq: return 0; } static int altera_gpio_remove(struct platform_device *pdev) { + unsigned int irq, i; int status; + struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev); + + status = gpiochip_remove(&altera_gc->mmchip.gc); - struct altera_gpio_instance *chip = platform_get_drvdata(pdev); - status = gpiochip_remove(&chip->mmchip.gc); if (status < 0) return status; + + if (altera_gc->irq) { + irq_dispose_mapping(altera_gc->hwirq); - kfree(chip); + for (i = 0; i < altera_gc->mmchip.gc.ngpio; i++) { + irq = irq_find_mapping(altera_gc->irq, i); + if (irq > 0) + irq_dispose_mapping(irq); + } + + irq_domain_remove(altera_gc->irq); + } + + irq_set_handler_data(altera_gc->hwirq, NULL); + irq_set_chained_handler(altera_gc->hwirq, NULL); + devm_kfree(&pdev->dev, altera_gc); return -EIO; } #ifdef CONFIG_OF -static struct of_device_id altera_gpio_of_match[] __devinitdata = { +static struct of_device_id altera_gpio_of_match[] = { { .compatible = "altr,pio-1.0", }, {}, }; @@ -274,6 +360,4 @@ static void __exit altera_gpio_exit(void) module_exit(altera_gpio_exit); MODULE_DESCRIPTION("Altera GPIO driver"); -MODULE_AUTHOR("Thomas Chou "); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); From 484820f5e28c3eeee223fc79686762fcbb67b9ee Mon Sep 17 00:00:00 2001 From: Tien Hock Loh Date: Wed, 27 Mar 2013 19:18:19 +0800 Subject: [PATCH 064/201] FogBugz #110526: Change Linux driver to GPLv2 instead of GPLv3 The current Altera GPIO Linux driver use GPLv3 header. The Linux kernel uses GPLv2. This change fixes the license header to GPLv2 Signed-off-by: Tien Hock Loh --- drivers/gpio/gpio-altera.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index dcb8fb90a14a8..edaed070322ca 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -2,19 +2,18 @@ * Copyright (C) 2013 Altera Corporation * Based on gpio-mpc8xxx.c * - * This program is free software: you can redistribute it and/or modify + * 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 3 of the License, or + * 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 + * along with this program. If not, see . */ #include From d8cf2137d30ccd7e9bd46c93cfd1f0d475e2b134 Mon Sep 17 00:00:00 2001 From: Tien Hock Loh Date: Mon, 20 May 2013 15:50:14 +0800 Subject: [PATCH 065/201] FogBugz #123541: GPIO altera gives warning message when used as interrupt-controller Fix an issue where GPIO altera gives warning message when it is used as an interrupt controller with device tree. The irq translation of the gpio should be one cell instead of two cells. Signed-off-by: Tien Hock Loh --- drivers/gpio/gpio-altera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index edaed070322ca..75bde7c632f60 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -207,7 +207,7 @@ static int altera_gpio_irq_map(struct irq_domain *h, unsigned int virq, static struct irq_domain_ops altera_gpio_irq_ops = { .map = altera_gpio_irq_map, - .xlate = irq_domain_xlate_twocell, + .xlate = irq_domain_xlate_onecell, }; int altera_gpio_probe(struct platform_device *pdev) From 5446400a8d4b4d51ed82cc8dc174c08e6af2b11e Mon Sep 17 00:00:00 2001 From: Tien Hock Loh Date: Mon, 27 May 2013 14:36:57 +0800 Subject: [PATCH 066/201] FogBugz #121458: Fix irq_set_type to allow edge and level correctly This fixes irq_set_type to GPIO Altera to allow edge and level trigger correctly. Previously irq_set_type will fail when user request anything other than IRQ_TYPE_NONE. Signed-off-by: Tien Hock Loh --- .../devicetree/bindings/gpio/gpio-altera.txt | 2 ++ drivers/gpio/gpio-altera.c | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-altera.txt b/Documentation/devicetree/bindings/gpio/gpio-altera.txt index 39cead59d177a..bfbcfdf010c7e 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-altera.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-altera.txt @@ -15,6 +15,8 @@ Altera GPIO specific properties: - width: Width of the GPIO bank, range from 1-32 - level_trigger: Specifies whether the GPIO interrupt is level trigger. This field is required if the Altera GPIO controller used has IRQ enabled. +- edge_type: Specifies the GPIO edge trigger type. This field will only exist + if IRQ is enabled in the core and level_trigger is 0 Note: If the Altera GPIO is set to be built as a module, peripherals that uses Altera GPIO as interrupt-parent should be a module so that the peripheral diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 75bde7c632f60..70c1d77bb46d1 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -34,12 +34,16 @@ #define ALTERA_GPIO_EDGE_CAP 0xc #define ALTERA_GPIO_OUTSET 0x10 #define ALTERA_GPIO_OUTCLEAR 0x14 +#define ALTERA_IRQ_RISING 0 +#define ALTERA_IRQ_FALLING 1 +#define ALTERA_IRQ_BOTH 2 struct altera_gpio_chip { struct of_mm_gpio_chip mmchip; struct irq_domain *irq; /* GPIO controller IRQ number */ spinlock_t gpio_lock; /* Lock used for synchronization */ int level_trigger; + int edge_type; int hwirq; }; @@ -76,8 +80,26 @@ static void altera_gpio_irq_mask(struct irq_data *d) static int altera_gpio_irq_set_type(struct irq_data *d, unsigned int type) { + struct altera_gpio_chip *altera_gc = irq_data_get_irq_chip_data(d); + if (type == IRQ_TYPE_NONE) return 0; + + if (altera_gc->level_trigger) { + if (type == IRQ_TYPE_LEVEL_HIGH) + return 0; + } else { + if (type == IRQ_TYPE_EDGE_RISING && + altera_gc->edge_type == ALTERA_IRQ_RISING) + return 0; + else if (type == IRQ_TYPE_EDGE_FALLING && + altera_gc->edge_type == ALTERA_IRQ_FALLING) + return 0; + else if (type == IRQ_TYPE_EDGE_BOTH && + altera_gc->edge_type == ALTERA_IRQ_BOTH) + return 0; + } + return -EINVAL; } @@ -276,6 +298,18 @@ int altera_gpio_probe(struct platform_device *pdev) } altera_gc->level_trigger = reg; + /* If it is not level triggered PIO + Check what edge type it is */ + if (!altera_gc->level_trigger) { + if (of_property_read_u32(node, "edge_type", ®)) { + ret = -EINVAL; + pr_err("%s: edge_type value not set in device tree\n" + , node->full_name); + goto teardown; + } + } + altera_gc->edge_type = reg; + irq_set_handler_data(altera_gc->hwirq, altera_gc); irq_set_chained_handler(altera_gc->hwirq, altera_gpio_irq_handler); From 5a5a9b63250c04dd45593e35b7e5aa97bf70d469 Mon Sep 17 00:00:00 2001 From: Tien Hock Loh Date: Thu, 30 Jan 2014 12:18:13 +0800 Subject: [PATCH 067/201] FogBugz #128579: Altera GPIO upstream updates This change is to address feedbacks from the Linux community on upstreaming the Altera GPIO driver. This includes updating the device tree binding naming convensions, coding style, etc. Signed-off-by: Tien Hock Loh Conflicts: Documentation/devicetree/bindings/gpio/gpio-altera.txt --- .../devicetree/bindings/gpio/gpio-altera.txt | 43 ++-- drivers/gpio/gpio-altera.c | 227 ++++++++++-------- 2 files changed, 151 insertions(+), 119 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio-altera.txt b/Documentation/devicetree/bindings/gpio/gpio-altera.txt index bfbcfdf010c7e..1de1f9bc24dee 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-altera.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-altera.txt @@ -3,32 +3,41 @@ Altera GPIO controller bindings Required properties: - compatible: - "altr,pio-1.0" -- #gpio-cells : Should be two. - - first cell is the pin number - - second cell is used to specify optional parameters (unused) +- reg: Physical base address and length of the controller's registers. +- #gpio-cells : Should be 1 + - The first cell is the gpio offset number - gpio-controller : Marks the device node as a GPIO controller. - #interrupt-cells : Should be 1. + - The first cell is the GPIO offset number within the GPIO controller. +- interrupts: Specify the interrupt. - interrupt-controller: Mark the device node as an interrupt controller - The first cell is the GPIO number. -Altera GPIO specific properties: -- width: Width of the GPIO bank, range from 1-32 -- level_trigger: Specifies whether the GPIO interrupt is level trigger. - This field is required if the Altera GPIO controller used has IRQ enabled. -- edge_type: Specifies the GPIO edge trigger type. This field will only exist - if IRQ is enabled in the core and level_trigger is 0 +Altera GPIO specific required properties: +- altr,interrupt_trigger: Specifies the interrupt trigger type the GPIO + hardware is synthesized. This field is required if the Altera GPIO controller + used has IRQ enabled as the interrupt type is not software controlled, + but hardware synthesized. Required if GPIO is used as an interrupt + controller. The value is defined in + Only the following flags are supported: + IRQ_TYPE_EDGE_RISING + IRQ_TYPE_EDGE_FALLING + IRQ_TYPE_EDGE_BOTH + IRQ_TYPE_LEVEL_HIGH -Note: If the Altera GPIO is set to be built as a module, peripherals that uses -Altera GPIO as interrupt-parent should be a module so that the peripheral -doesn't get initialized before Altera GPIO is initialized. +Altera GPIO specific optional properties: +- altr,gpio-bank-width: Width of the GPIO bank. This defines how many pins the + GPIO device has. Ranges between 1-32. Optional and defaults to 32 is not + specified. Example: -gpio_altr: gpio_altr { +gpio_altr: gpio@40000 { compatible = "altr,pio-1.0"; - width = <32>; - level_trigger = <0>; - #gpio-cells = <2>; + reg = <0xff200000 0x10>; + interrupts = <0 45 4>; + altr,gpio-bank-width = <32>; + altr,interrupt_trigger = ; + #gpio-cells = <1>; gpio-controller; #interrupt-cells = <1>; interrupt-controller; diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 70c1d77bb46d1..ab0738f491e74 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -17,16 +17,19 @@ */ #include -#include -#include -#include +#include +#include +#include #include #include +#include +#include #include -#include -#include #include #include +#include + +#include #define ALTERA_GPIO_DATA 0x0 #define ALTERA_GPIO_DIR 0x4 @@ -34,17 +37,24 @@ #define ALTERA_GPIO_EDGE_CAP 0xc #define ALTERA_GPIO_OUTSET 0x10 #define ALTERA_GPIO_OUTCLEAR 0x14 -#define ALTERA_IRQ_RISING 0 -#define ALTERA_IRQ_FALLING 1 -#define ALTERA_IRQ_BOTH 2 +/** +* struct altera_gpio_chip +* @mmchip : memory mapped chip structure. +* @irq : irq domain that this driver is registered to. +* @gpio_lock : synchronization lock so that new irq/set/get requests + will be blocked until the current one completes. +* @interrupt_trigger : specifies the hardware configured IRQ trigger type + (rising, falling, both, high) +* @mapped_irq : kernel mapped irq number. +*/ struct altera_gpio_chip { struct of_mm_gpio_chip mmchip; - struct irq_domain *irq; /* GPIO controller IRQ number */ - spinlock_t gpio_lock; /* Lock used for synchronization */ - int level_trigger; + struct irq_domain *domain; + spinlock_t gpio_lock; + int interrupt_trigger; int edge_type; - int hwirq; + int mapped_irq; }; static void altera_gpio_irq_unmask(struct irq_data *d) @@ -57,7 +67,7 @@ static void altera_gpio_irq_unmask(struct irq_data *d) spin_lock_irqsave(&altera_gc->gpio_lock, flags); intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */ - intmask |= (1 << irqd_to_hwirq(d)); + intmask |= BIT(irqd_to_hwirq(d)); writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); } @@ -72,7 +82,7 @@ static void altera_gpio_irq_mask(struct irq_data *d) spin_lock_irqsave(&altera_gc->gpio_lock, flags); intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */ - intmask &= ~(1 << irqd_to_hwirq(d)); + intmask &= ~BIT(irqd_to_hwirq(d)); writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); } @@ -85,36 +95,50 @@ static int altera_gpio_irq_set_type(struct irq_data *d, if (type == IRQ_TYPE_NONE) return 0; - if (altera_gc->level_trigger) { - if (type == IRQ_TYPE_LEVEL_HIGH) - return 0; + if (type == IRQ_TYPE_LEVEL_HIGH && + altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) { + return 0; } else { if (type == IRQ_TYPE_EDGE_RISING && - altera_gc->edge_type == ALTERA_IRQ_RISING) + altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_RISING) return 0; else if (type == IRQ_TYPE_EDGE_FALLING && - altera_gc->edge_type == ALTERA_IRQ_FALLING) + altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_FALLING) return 0; else if (type == IRQ_TYPE_EDGE_BOTH && - altera_gc->edge_type == ALTERA_IRQ_BOTH) + altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_BOTH) return 0; } return -EINVAL; } +static unsigned int altera_gpio_irq_startup(struct irq_data *d) +{ + altera_gpio_irq_unmask(d); + + return 0; +} + +static void altera_gpio_irq_shutdown(struct irq_data *d) +{ + altera_gpio_irq_unmask(d); +} + static struct irq_chip altera_irq_chip = { .name = "altera-gpio", .irq_mask = altera_gpio_irq_mask, .irq_unmask = altera_gpio_irq_unmask, .irq_set_type = altera_gpio_irq_set_type, + .irq_startup = altera_gpio_irq_startup, + .irq_shutdown = altera_gpio_irq_shutdown, }; static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) { struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); - return (readl(mm_gc->regs + ALTERA_GPIO_DATA) >> offset) & 1; + return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) >> offset); } static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) @@ -127,7 +151,10 @@ static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) spin_lock_irqsave(&chip->gpio_lock, flags); data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); - data_reg = (data_reg & ~(1 << offset)) | (value << offset); + if (value) + data_reg |= BIT(offset); + else + data_reg &= ~BIT(offset); writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); spin_unlock_irqrestore(&chip->gpio_lock, flags); } @@ -143,7 +170,7 @@ static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) spin_lock_irqsave(&chip->gpio_lock, flags); /* Set pin as input, assumes software controlled IP */ gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); - gpio_ddr &= ~(1 << offset); + gpio_ddr &= ~BIT(offset); writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -162,12 +189,15 @@ static int altera_gpio_direction_output(struct gpio_chip *gc, spin_lock_irqsave(&chip->gpio_lock, flags); /* Sets the GPIO value */ data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); - data_reg = (data_reg & ~(1 << offset)) | (value << offset); - writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); + if (value) + data_reg |= BIT(offset); + else + data_reg &= ~BIT(offset); + writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); /* Set pin as output, assumes software controlled IP */ gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); - gpio_ddr |= (1 << offset); + gpio_ddr |= BIT(offset); writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -180,10 +210,10 @@ static int altera_gpio_to_irq(struct gpio_chip *gc, unsigned offset) struct altera_gpio_chip *altera_gc = container_of(mm_gc, struct altera_gpio_chip, mmchip); - if (altera_gc->irq == 0) + if (!altera_gc->domain) return -ENXIO; - if ((altera_gc->irq && offset) < altera_gc->mmchip.gc.ngpio) - return irq_create_mapping(altera_gc->irq, offset); + if (offset < altera_gc->mmchip.gc.ngpio) + return irq_find_mapping(altera_gc->domain, offset); else return -ENXIO; } @@ -195,34 +225,44 @@ static void altera_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) struct of_mm_gpio_chip *mm_gc = &altera_gc->mmchip; unsigned long status; - int base; - chip->irq_mask(&desc->irq_data); + int i; - if (altera_gc->level_trigger) - status = readl(mm_gc->regs + ALTERA_GPIO_DATA); - else { - status = readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP); - writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); - } - - status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + chained_irq_enter(chip, desc); + /* Handling for level trigger and edge trigger is different */ + if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) { + status = readl_relaxed(mm_gc->regs + ALTERA_GPIO_DATA); + status &= readl_relaxed(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); - for (base = 0; base < mm_gc->gc.ngpio; base++) { - if ((1 << base) & status) { - generic_handle_irq( - irq_linear_revmap(altera_gc->irq, base)); + for (i = 0; i < mm_gc->gc.ngpio; i++) { + if (status & BIT(i)) { + generic_handle_irq(irq_find_mapping( + altera_gc->domain, i)); + } + } + } else { + while ((status = + (readl_relaxed(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & + readl_relaxed(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { + writel_relaxed(status, + mm_gc->regs + ALTERA_GPIO_EDGE_CAP); + for (i = 0; i < mm_gc->gc.ngpio; i++) { + if (status & BIT(i)) { + generic_handle_irq(irq_find_mapping( + altera_gc->domain, i)); + } + } } } - chip->irq_eoi(irq_desc_get_irq_data(desc)); - chip->irq_unmask(&desc->irq_data); + + chained_irq_exit(chip, desc); } -static int altera_gpio_irq_map(struct irq_domain *h, unsigned int virq, +static int altera_gpio_irq_map(struct irq_domain *h, unsigned int irq, irq_hw_number_t hw_irq_num) { - irq_set_chip_data(virq, h->host_data); - irq_set_chip_and_handler(virq, &altera_irq_chip, handle_level_irq); - irq_set_irq_type(virq, IRQ_TYPE_NONE); + irq_set_chip_data(irq, h->host_data); + irq_set_chip_and_handler(irq, &altera_irq_chip, handle_level_irq); + irq_set_irq_type(irq, IRQ_TYPE_NONE); return 0; } @@ -235,30 +275,28 @@ static struct irq_domain_ops altera_gpio_irq_ops = { int altera_gpio_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; - int id, reg, ret; + int i, id, reg, ret; struct altera_gpio_chip *altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); if (altera_gc == NULL) { - ret = -ENOMEM; - pr_err("%s: registration failed with status %d\n", - node->full_name, ret); - return ret; + pr_err("%s: out of memory\n", node->full_name); + return -ENOMEM; } - altera_gc->irq = 0; + altera_gc->domain = 0; spin_lock_init(&altera_gc->gpio_lock); id = pdev->id; - if (of_property_read_u32(node, "width", ®)) + if (of_property_read_u32(node, "altr,gpio-bank-width", ®)) /*By default assume full GPIO controller*/ altera_gc->mmchip.gc.ngpio = 32; else altera_gc->mmchip.gc.ngpio = reg; if (altera_gc->mmchip.gc.ngpio > 32) { - pr_warn("%s: ngpio is greater than 32, defaulting to 32\n", - node->full_name); + dev_warn(&pdev->dev, + "ngpio is greater than 32, defaulting to 32\n"); altera_gc->mmchip.gc.ngpio = 32; } @@ -270,61 +308,50 @@ int altera_gpio_probe(struct platform_device *pdev) altera_gc->mmchip.gc.owner = THIS_MODULE; ret = of_mm_gpiochip_add(node, &altera_gc->mmchip); - if (ret) - goto err; + if (ret) { + dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); + return ret; + } platform_set_drvdata(pdev, altera_gc); - if (of_get_property(node, "interrupts", ®) == NULL) - goto skip_irq; - altera_gc->hwirq = irq_of_parse_and_map(node, 0); + altera_gc->mapped_irq = irq_of_parse_and_map(node, 0); - if (altera_gc->hwirq == NO_IRQ) + if (!altera_gc->mapped_irq) goto skip_irq; - altera_gc->irq = irq_domain_add_linear(node, altera_gc->mmchip.gc.ngpio, - &altera_gpio_irq_ops, altera_gc); + altera_gc->domain = irq_domain_add_linear(node, + altera_gc->mmchip.gc.ngpio, &altera_gpio_irq_ops, altera_gc); - if (!altera_gc->irq) { + if (!altera_gc->domain) { ret = -ENODEV; goto dispose_irq; } - if (of_property_read_u32(node, "level_trigger", ®)) { + for (i = 0; i < altera_gc->mmchip.gc.ngpio; i++) + irq_create_mapping(altera_gc->domain, i); + + if (of_property_read_u32(node, "altr,interrupt_type", ®)) { ret = -EINVAL; - pr_err("%s: level_trigger value not set in device tree\n", - node->full_name); + dev_err(&pdev->dev, + "altr,interrupt_type value not set in device tree\n"); goto teardown; } - altera_gc->level_trigger = reg; - - /* If it is not level triggered PIO - Check what edge type it is */ - if (!altera_gc->level_trigger) { - if (of_property_read_u32(node, "edge_type", ®)) { - ret = -EINVAL; - pr_err("%s: edge_type value not set in device tree\n" - , node->full_name); - goto teardown; - } - } - altera_gc->edge_type = reg; + altera_gc->interrupt_trigger = reg; - irq_set_handler_data(altera_gc->hwirq, altera_gc); - irq_set_chained_handler(altera_gc->hwirq, altera_gpio_irq_handler); + irq_set_handler_data(altera_gc->mapped_irq, altera_gc); + irq_set_chained_handler(altera_gc->mapped_irq, altera_gpio_irq_handler); return 0; teardown: - irq_domain_remove(altera_gc->irq); + irq_domain_remove(altera_gc->domain); dispose_irq: - irq_dispose_mapping(altera_gc->hwirq); + irq_dispose_mapping(altera_gc->mapped_irq); WARN_ON(gpiochip_remove(&altera_gc->mmchip.gc) < 0); -err: pr_err("%s: registration failed with status %d\n", node->full_name, ret); - devm_kfree(&pdev->dev, altera_gc); return ret; skip_irq: @@ -338,37 +365,32 @@ static int altera_gpio_remove(struct platform_device *pdev) struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev); status = gpiochip_remove(&altera_gc->mmchip.gc); - + if (status < 0) return status; - if (altera_gc->irq) { - irq_dispose_mapping(altera_gc->hwirq); - + if (altera_gc->domain) { + irq_dispose_mapping(altera_gc->mapped_irq); + for (i = 0; i < altera_gc->mmchip.gc.ngpio; i++) { - irq = irq_find_mapping(altera_gc->irq, i); + irq = irq_find_mapping(altera_gc->domain, i); if (irq > 0) irq_dispose_mapping(irq); } - irq_domain_remove(altera_gc->irq); + irq_domain_remove(altera_gc->domain); } - irq_set_handler_data(altera_gc->hwirq, NULL); - irq_set_chained_handler(altera_gc->hwirq, NULL); - devm_kfree(&pdev->dev, altera_gc); + irq_set_handler_data(altera_gc->mapped_irq, NULL); + irq_set_chained_handler(altera_gc->mapped_irq, NULL); return -EIO; } -#ifdef CONFIG_OF static struct of_device_id altera_gpio_of_match[] = { { .compatible = "altr,pio-1.0", }, {}, }; MODULE_DEVICE_TABLE(of, altera_gpio_of_match); -#else -#define altera_gpio_of_match NULL -#endif static struct platform_driver altera_gpio_driver = { .driver = { @@ -392,5 +414,6 @@ static void __exit altera_gpio_exit(void) } module_exit(altera_gpio_exit); +MODULE_AUTHOR("Tien Hock Loh "); MODULE_DESCRIPTION("Altera GPIO driver"); MODULE_LICENSE("GPL"); From c91e94c25558a8b850bba072cc5bd28c7d30cf4c Mon Sep 17 00:00:00 2001 From: Tien Hock Loh Date: Tue, 1 Apr 2014 10:35:18 +0800 Subject: [PATCH 068/201] FogBugz #183984: Do not use _relaxed() function for Altera PIO driver This is to remove the use of _relaxed() function from Altera PIO driver. The FPGA driver should be more generic and not use _relaxed() function since not all architecture supports _relaxed() function Signed-off-by: Tien Hock Loh --- drivers/gpio/gpio-altera.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index ab0738f491e74..79d7a78953eaa 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -230,8 +230,8 @@ static void altera_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) chained_irq_enter(chip, desc); /* Handling for level trigger and edge trigger is different */ if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) { - status = readl_relaxed(mm_gc->regs + ALTERA_GPIO_DATA); - status &= readl_relaxed(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); + status = readl(mm_gc->regs + ALTERA_GPIO_DATA); + status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); for (i = 0; i < mm_gc->gc.ngpio; i++) { if (status & BIT(i)) { @@ -241,10 +241,9 @@ static void altera_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) } } else { while ((status = - (readl_relaxed(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & - readl_relaxed(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { - writel_relaxed(status, - mm_gc->regs + ALTERA_GPIO_EDGE_CAP); + (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & + readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { + writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); for (i = 0; i < mm_gc->gc.ngpio; i++) { if (status & BIT(i)) { generic_handle_irq(irq_find_mapping( From 0afd4bab5e0dae5831e2130ae396376f1efb9f92 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 22 Oct 2012 11:12:50 -0600 Subject: [PATCH 069/201] FogBugz #80929: Make CACHE_L2XO a config option. Instead of enabling l2 cache by default, make it a config option. Signed-off-by: Dinh Nguyen Conflicts: arch/arm/mach-socfpga/Kconfig --- arch/arm/mach-socfpga/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index b5f8d75d51a05..96b03d425718d 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -8,3 +8,6 @@ config ARCH_SOCFPGA select HAVE_ARM_SCU select HAVE_ARM_TWD if SMP select MFD_SYSCON + select MIGHT_HAVE_CACHE_L2X0 + select SPARSE_IRQ + select USE_OF From 235b1ba48ed786f68900704f6126f36437940382 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 28 Jan 2013 17:42:37 -0600 Subject: [PATCH 070/201] FogBugz #97284: Update L2 cache settings Add in the correct tag-latency and data-latency device tree entries for L2 cache. Enable D and I prefetch per HW group's recommendation. These changes drastically improve the performance of the SDRAM. Signed-off-by: Dinh Nguyen Conflicts: arch/arm/mach-socfpga/socfpga.c --- arch/arm/mach-socfpga/socfpga.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 1e8f2e0d71632..d0b00320a20af 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -118,7 +118,8 @@ static const char *altera_dt_match[] = { }; DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA") - .l2c_aux_val = 0, + .l2c_aux_val = L310_AUX_CTRL_DATA_PREFETCH | + L310_AUX_CTRL_INSTR_PREFETCH, .l2c_aux_mask = ~0, .smp = smp_ops(socfpga_smp_ops), .map_io = socfpga_map_io, From bd6111486e67ac67f510c5586aa238380110e258 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 25 Feb 2013 11:23:22 -0600 Subject: [PATCH 071/201] FogBugz #102401: Fix hotplug support for SOCFPGA Put CPU1 into reset when it is hotplugged. Signed-off-by: Dinh Nguyen Conflicts: arch/arm/mach-socfpga/core.h --- arch/arm/mach-socfpga/core.h | 6 ++++++ arch/arm/mach-socfpga/platsmp.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h index 572b8f719ffbf..5804db1108d76 100644 --- a/arch/arm/mach-socfpga/core.h +++ b/arch/arm/mach-socfpga/core.h @@ -33,6 +33,12 @@ extern void __iomem *socfpga_scu_base_addr; extern void socfpga_init_clocks(void); extern void socfpga_sysmgr_init(void); +/*MPU Module Reset Register */ +#define RSTMGR_MPUMODRST_CPU0 0x1 /*CPU0 Reset*/ +#define RSTMGR_MPUMODRST_CPU1 0x2 /*CPU1 Reset*/ +#define RSTMGR_MPUMODRST_WDS 0x4 /*Watchdog Reset*/ +#define RSTMGR_MPUMODRST_SCUPER 0x8 /*SCU and periphs reset*/ +#define RSTMGR_MPUMODRST_L2 0x10 /*L2 Cache reset*/ extern void __iomem *sys_manager_base_addr; extern void __iomem *rst_manager_base_addr; diff --git a/arch/arm/mach-socfpga/platsmp.c b/arch/arm/mach-socfpga/platsmp.c index 5356a72bc8ce9..538b2291a1141 100644 --- a/arch/arm/mach-socfpga/platsmp.c +++ b/arch/arm/mach-socfpga/platsmp.c @@ -86,6 +86,12 @@ static void __init socfpga_smp_prepare_cpus(unsigned int max_cpus) */ static void socfpga_cpu_die(unsigned int cpu) { + /* Flush the L1 data cache. */ + flush_cache_all(); + + /* This will put CPU #1 into reset.*/ + __raw_writel(RSTMGR_MPUMODRST_CPU1, rst_manager_base_addr + 0x10); + cpu_do_idle(); /* We should have never returned from idle */ From 65ebfa4f15f4695f786c693e0bedfd244a6e1d17 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 4 Mar 2013 11:41:01 -0600 Subject: [PATCH 072/201] FogBugz #104179: Fix SCU virtual address mappping Fix: BUG: mapping for 0xfffec000 at 0xfffec000 out of vmalloc space Signed-off-by: Dinh Nguyen --- arch/arm/mach-socfpga/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h index 5804db1108d76..ae5b0467c0f66 100644 --- a/arch/arm/mach-socfpga/core.h +++ b/arch/arm/mach-socfpga/core.h @@ -48,6 +48,6 @@ extern char secondary_trampoline, secondary_trampoline_end; extern unsigned long cpu1start_addr; -#define SOCFPGA_SCU_VIRT_BASE 0xfffec000 +#define SOCFPGA_SCU_VIRT_BASE 0xfee00000 #endif From 21197785e031db4ea8d4540d59f4add3f1cf3cc0 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 14 Mar 2013 15:22:44 +0800 Subject: [PATCH 073/201] FogBugz #106327: Use framework in drivers/base/soc.c for system id Open source committees recommend to use existing framework in drivers/base/soc.c for system properties like system id (soc_id). See more detail in Documentation/ABI/testing/sysfs-devices-soc. Usage: cat /sys/devices/soc0/soc_id cat /sys/devices/soc0/family cat /sys/devices/soc0/machine cat /sys/devices/soc0/revision Signed-off-by: Ley Foon Tan Conflicts: arch/arm/mach-socfpga/Kconfig arch/arm/mach-socfpga/core.h arch/arm/mach-socfpga/socfpga.c --- arch/arm/mach-socfpga/Kconfig | 1 + arch/arm/mach-socfpga/core.h | 5 +++ arch/arm/mach-socfpga/socfpga.c | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index 96b03d425718d..9aa9331245776 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -11,3 +11,4 @@ config ARCH_SOCFPGA select MIGHT_HAVE_CACHE_L2X0 select SPARSE_IRQ select USE_OF + select SOC_BUS diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h index ae5b0467c0f66..2ec21c7796fcc 100644 --- a/arch/arm/mach-socfpga/core.h +++ b/arch/arm/mach-socfpga/core.h @@ -30,6 +30,11 @@ extern void socfpga_secondary_startup(void); extern void __iomem *socfpga_scu_base_addr; +#define SOCFPGA_SYSID_DEFAULT 0x1 +#define SOCFPGA_REVISION_DEFAULT 0x1 + +/* Sysid register map */ +#define SYSID_ID_REG 0x0 extern void socfpga_init_clocks(void); extern void socfpga_sysmgr_init(void); diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index d0b00320a20af..0ec57fe0ec246 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -45,6 +47,60 @@ static struct map_desc uart_io_desc __initdata = { .type = MT_DEVICE, }; +static void __init socfpga_soc_device_init(void) +{ + struct device_node *root; + struct device_node *sysid_node; + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + void __iomem *sysid_base; + const char *machine; + u32 id = SOCFPGA_SYSID_DEFAULT; + int err; + + root = of_find_node_by_path("/"); + if (!root) + return; + + err = of_property_read_string(root, "model", &machine); + if (err) + return; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return; + + sysid_node = of_find_compatible_node(root, NULL, "ALTR,sysid-1.0"); + if (sysid_node) { + sysid_base = of_iomap(sysid_node, 0); + if (sysid_base) { + /* Use id from Sysid hardware. */ + id = readl(sysid_base + SYSID_ID_REG); + iounmap(sysid_base); + } + of_node_put(sysid_node); + } + + of_node_put(root); + + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", id); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", + SOCFPGA_REVISION_DEFAULT); + soc_dev_attr->machine = kasprintf(GFP_KERNEL, "%s", machine); + soc_dev_attr->family = "SOCFPGA"; + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR_OR_NULL(soc_dev)) { + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr->machine); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); + return; + } + + return; +} + static void __init socfpga_scu_map_io(void) { unsigned long base; @@ -110,6 +166,7 @@ static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd) static void __init socfpga_cyclone5_init(void) { enable_periphs(); + socfpga_soc_device_init(); } static const char *altera_dt_match[] = { From 717acaf385134cf62e7e9a7c9863c6002d9248e3 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 27 Feb 2013 18:29:14 -0600 Subject: [PATCH 074/201] FogBugz #103219: Turn on ARM errata for L2 cache Turn on these ARM errata: ARM_ERRATA_754322 ARM_ERRATA_764369 ARM_ERRATA_775420 PL310_ERRATA_588369 PL310_ERRATA_727915 PL310_ERRATA_753970 PL310_ERRATA_769419 Signed-off-by: Dinh Nguyen Conflicts: arch/arm/mach-socfpga/Kconfig --- arch/arm/mach-socfpga/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index 9aa9331245776..a2814b663ca55 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -12,3 +12,10 @@ config ARCH_SOCFPGA select SPARSE_IRQ select USE_OF select SOC_BUS + select ARM_ERRATA_754322 + select ARM_ERRATA_764369 if SMP + select ARM_ERRATA_775420 + select PL310_ERRATA_588369 + select PL310_ERRATA_727915 + select PL310_ERRATA_753970 + select PL310_ERRATA_769419 From d6e36d62d52cab5ecb43c9ef7d1fa5b17b8b9251 Mon Sep 17 00:00:00 2001 From: Matthew Gerlach Date: Fri, 5 Apr 2013 09:10:26 -0700 Subject: [PATCH 075/201] FogBugz #103239: Intermittent loss of ethernet transmission. This bug does not really have anything to do with ethernet. The problem was a memory coherency problem between the cpu and a dma engine, in this case the ethernet mac. In short, while dma writes by the ethernet were not getting cached in L2, they were getting cached in L1. The good news is a single bit change to the programming of the PL310 fixes the problem. Signed-off-by: Matthew Gerlach --- arch/arm/mach-socfpga/socfpga.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 0ec57fe0ec246..22bdabb07eb58 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -176,7 +176,8 @@ static const char *altera_dt_match[] = { DT_MACHINE_START(SOCFPGA, "Altera SOCFPGA") .l2c_aux_val = L310_AUX_CTRL_DATA_PREFETCH | - L310_AUX_CTRL_INSTR_PREFETCH, + L310_AUX_CTRL_INSTR_PREFETCH | + L2C_AUX_CTRL_SHARED_OVERRIDE, .l2c_aux_mask = ~0, .smp = smp_ops(socfpga_smp_ops), .map_io = socfpga_map_io, From 02a66438d960b372d8a14de22ee8aa28efd697ea Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 20 Mar 2013 08:23:53 -0500 Subject: [PATCH 076/201] FogBugz #108269: Enable PMU through the CTI SocFPGA has a PMU(Performance Measurement Unit) that has its IRQs routed through Coresight using the CTI(Cross-Trigger Interface). This patch enables the PMU and initializes the CTI for usage by oprofile/perfmon. Signed-off-by: Dinh Nguyen v6: - Reworked socfpga_pmu_handler to be bounded - Switched from pr_err/pr_info to dev_err/dev_info - Use np=pdev - Add defines for CTI channels v5: - Add error tags to free_irq and iounmap v4: - Add CTI_MPU_IRQ_TRIG_IN and CTI_MPU_IRQ_TRIG_OUT defines - Use cortex-a9-pmu as the parent node - Wrap socfpga_pmu_platdata around CONFIG_HW_PERF_EVENTS v3: - Add platform functions to stop and start the CTI V2: - Add device tree entries for CTI - Use platform_get_irq(pdev,1) for 2nd CTI irq --- arch/arm/boot/dts/socfpga.dtsi | 18 ++++ arch/arm/include/asm/pmu.h | 3 + arch/arm/kernel/perf_event.c | 16 ++++ arch/arm/mach-socfpga/Makefile | 1 + arch/arm/mach-socfpga/socfpga.c | 20 +++++ arch/arm/mach-socfpga/socfpga_cti.c | 125 ++++++++++++++++++++++++++++ arch/arm/mach-socfpga/socfpga_cti.h | 16 ++++ 7 files changed, 199 insertions(+) create mode 100644 arch/arm/mach-socfpga/socfpga_cti.c create mode 100644 arch/arm/mach-socfpga/socfpga_cti.h diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index db50a9ad69d5d..dee58ec1b1541 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -664,6 +664,24 @@ }; }; + pmu { + #address-cells = <1>; + #size-cells = <1>; + compatible = "arm,cortex-a9-pmu"; + interrupts = <0 176 4>, <0 177 4>; + ranges; + + cti0: cti0@ff118000 { + compatible = "arm,coresight-cti"; + reg = <0xff118000 0x100>; + }; + + cti1: cti1@ff119000 { + compatible = "arm,coresight-cti"; + reg = <0xff119000 0x100>; + }; + }; + spi0: spi@fff00000 { compatible = "snps,dw-spi-mmio"; #address-cells = <1>; diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index ae1919be8f988..37a7cd3943fe1 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -38,6 +38,9 @@ struct arm_pmu_platdata { irq_handler_t pmu_handler); int (*runtime_resume)(struct device *dev); int (*runtime_suspend)(struct device *dev); + int (*init)(struct platform_device *pdev); + int (*start)(struct platform_device *pdev); + int (*stop)(struct platform_device *pdev); }; #ifdef CONFIG_HW_PERF_EVENTS diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index 4238bcba9d60f..c8be84b71f13b 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -325,6 +325,11 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) static void armpmu_release_hardware(struct arm_pmu *armpmu) { + struct platform_device *plat_device = armpmu->plat_device; + struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev); + + if (plat->stop) + plat->stop(plat_device); armpmu->free_irq(armpmu); pm_runtime_put_sync(&armpmu->plat_device->dev); } @@ -334,6 +339,7 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) { int err; struct platform_device *pmu_device = armpmu->plat_device; + struct arm_pmu_platdata *plat = dev_get_platdata(&pmu_device->dev); if (!pmu_device) return -ENODEV; @@ -344,6 +350,8 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu) armpmu_release_hardware(armpmu); return err; } + if (plat->start) + plat->start(pmu_device); return 0; } @@ -526,10 +534,18 @@ static void armpmu_init(struct arm_pmu *armpmu) int armpmu_register(struct arm_pmu *armpmu, int type) { + struct platform_device *plat_device = armpmu->plat_device; + struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev); + armpmu_init(armpmu); pm_runtime_enable(&armpmu->plat_device->dev); pr_info("enabled with %s PMU driver, %d counters available\n", armpmu->name, armpmu->num_events); + + /* Platform specific initialization. ie. CTI enable */ + if (plat->init) + plat->init(plat_device); + return perf_pmu_register(&armpmu->pmu, armpmu->name, type); } diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile index 6dd7a93a90fea..c7401d395671a 100644 --- a/arch/arm/mach-socfpga/Makefile +++ b/arch/arm/mach-socfpga/Makefile @@ -4,3 +4,4 @@ obj-y := socfpga.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o +obj-$(CONFIG_HW_PERF_EVENTS) += socfpga_cti.o diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 22bdabb07eb58..2206140f9a5c8 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -25,14 +25,32 @@ #include #include #include +#include #include "core.h" +#include "socfpga_cti.h" void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE)); void __iomem *sys_manager_base_addr; void __iomem *rst_manager_base_addr; unsigned long cpu1start_addr; +#ifdef CONFIG_HW_PERF_EVENTS +static struct arm_pmu_platdata socfpga_pmu_platdata = { + .handle_irq = socfpga_pmu_handler, + .init = socfpga_init_cti, + .start = socfpga_start_cti, + .stop = socfpga_stop_cti, +}; +#endif + +static const struct of_dev_auxdata socfpga_auxdata_lookup[] __initconst = { +#ifdef CONFIG_HW_PERF_EVENTS + OF_DEV_AUXDATA("arm,cortex-a9-pmu", 0, "arm-pmu", &socfpga_pmu_platdata), +#endif + { /* sentinel */ } +}; + static struct map_desc scu_io_desc __initdata = { .virtual = SOCFPGA_SCU_VIRT_BASE, .pfn = 0, /* run-time */ @@ -165,6 +183,8 @@ static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd) static void __init socfpga_cyclone5_init(void) { + of_platform_populate(NULL, of_default_bus_match_table, + socfpga_auxdata_lookup, NULL); enable_periphs(); socfpga_soc_device_init(); } diff --git a/arch/arm/mach-socfpga/socfpga_cti.c b/arch/arm/mach-socfpga/socfpga_cti.c new file mode 100644 index 0000000000000..10072c8984dad --- /dev/null +++ b/arch/arm/mach-socfpga/socfpga_cti.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 Altera Corporation + * + * 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 . + */ +#include +#include +#include +#include +#include + +#include "core.h" +#include "socfpga_cti.h" + +#define SOCFPGA_NUM_CTI 2 + +struct cti socfpga_cti_data[SOCFPGA_NUM_CTI]; + +irqreturn_t socfpga_pmu_handler(int irq, void *dev, irq_handler_t handler) +{ + unsigned int handled = 0; + int i; + + for (i = 0; i < SOCFPGA_NUM_CTI; i++) + if (irq == socfpga_cti_data[i].irq) { + cti_irq_ack(&socfpga_cti_data[i]); + handled = handler(irq, dev); + } + + return IRQ_RETVAL(handled); +} + +int socfpga_init_cti(struct platform_device *pdev) +{ + struct device_node *np, *np2; + void __iomem *cti0_addr; + void __iomem *cti1_addr; + u32 irq0, irq1; + + np = pdev->dev.of_node; + np2 = of_find_compatible_node(np, NULL, "arm,coresight-cti"); + if (!np2) { + dev_err(&pdev->dev, "PMU: Unable to find coresight-cti\n"); + return -1; + } + cti0_addr = of_iomap(np2, 0); + if (!cti0_addr) { + dev_err(&pdev->dev, "PMU: ioremap for CTI failed\n"); + return -1; + } + + irq0 = platform_get_irq(pdev, 0); + if (irq0 < 0) + goto free_irq0; + + np2 = of_find_compatible_node(np2, NULL, "arm,coresight-cti"); + if (!np2) { + dev_err(&pdev->dev, "PMU: Unable to find coresight-cti\n"); + goto err_iounmap; + } + cti1_addr = of_iomap(np2, 0); + if (!cti1_addr) + goto err_iounmap; + + irq1 = platform_get_irq(pdev, 1); + if (irq1 < 0) + goto free_irq1; + + /*configure CTI0 for pmu irq routing*/ + cti_init(&socfpga_cti_data[0], cti0_addr, + irq0, CTI_MPU_IRQ_TRIG_OUT); + cti_unlock(&socfpga_cti_data[0]); + cti_map_trigger(&socfpga_cti_data[0], + CTI_MPU_IRQ_TRIG_IN, CTI_MPU_IRQ_TRIG_OUT, PMU_CHANNEL_0); + + /*configure CTI1 for pmu irq routing*/ + cti_init(&socfpga_cti_data[1], cti1_addr, + irq1, CTI_MPU_IRQ_TRIG_OUT); + cti_unlock(&socfpga_cti_data[1]); + cti_map_trigger(&socfpga_cti_data[1], + CTI_MPU_IRQ_TRIG_IN, CTI_MPU_IRQ_TRIG_OUT, PMU_CHANNEL_1); + + dev_info(&pdev->dev, "PMU:CTI successfully enabled\n"); + return 0; + +free_irq1: + iounmap(cti1_addr); +err_iounmap: + free_irq(irq0, pdev); +free_irq0: + iounmap(cti0_addr); + return -1; +} + +int socfpga_start_cti(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < SOCFPGA_NUM_CTI; i++) + cti_enable(&socfpga_cti_data[i]); + + return 0; +} + +int socfpga_stop_cti(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < SOCFPGA_NUM_CTI; i++) + cti_disable(&socfpga_cti_data[i]); + + return 0; +} + diff --git a/arch/arm/mach-socfpga/socfpga_cti.h b/arch/arm/mach-socfpga/socfpga_cti.h new file mode 100644 index 0000000000000..cbeb06339eebd --- /dev/null +++ b/arch/arm/mach-socfpga/socfpga_cti.h @@ -0,0 +1,16 @@ +#ifndef __SOCFPGA_CTI_H +#define __SOCFPGA_CTI_H + +#define CTI_MPU_IRQ_TRIG_IN 1 +#define CTI_MPU_IRQ_TRIG_OUT 6 + +#define PMU_CHANNEL_0 0 +#define PMU_CHANNEL_1 1 + +#ifdef CONFIG_HW_PERF_EVENTS +extern irqreturn_t socfpga_pmu_handler(int irq, void *dev, irq_handler_t handler); +extern int socfpga_init_cti(struct platform_device *pdev); +extern int socfpga_start_cti(struct platform_device *pdev); +extern int socfpga_stop_cti(struct platform_device *pdev); +#endif +#endif /* __SOCFPGA_CTI_H */ From f8baa4d583b70b0e737a53513e7944611c45415d Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Tue, 15 Jan 2013 15:28:52 -0600 Subject: [PATCH 077/201] FogBugz #92330-1: fpga manager framework Add a class driver for supporting FPGA managers. This class supports multiple FPGAs, providing a standard ops for them. Various manufacturor's families of FPGAs can be supported by adding drivers to the fpga-mgrs/ folder. Each driver needs to register its ops using register_fpga_manager(). FPGA managers will show up as char devices in the sysfs class /sys/class/fpga and as /dev/fpga* devnodes. To get a FPGA's status: cat /sys/class/fpga/fpag0/status To program a FPGA: cat bitstream-file > /dev/fpga0 Signed-off-by: Alan Tull V3: Add raw readl/writel support for interrupt handlers Disable interrupts before initializing completion Return value in fgpa_mgr_release() Use safer snprintf instead of sprintf. Delete a debug statement Allow initializing priv in register_fpga_manager() Fix cleanup code in two functions V2: Fixes to read fpga_mgr_op function Make function names more uniform Clean up error checking --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/fpga/Kconfig | 14 ++ drivers/fpga/Makefile | 8 + drivers/fpga/fpga-mgr-fops.c | 124 ++++++++++++++ drivers/fpga/fpga-mgr.c | 258 +++++++++++++++++++++++++++++ drivers/fpga/fpga-mgr.h | 24 +++ drivers/fpga/fpga-transport-mmio.c | 120 ++++++++++++++ drivers/fpga/fpga-transport.c | 63 +++++++ include/linux/fpga.h | 184 ++++++++++++++++++++ 10 files changed, 798 insertions(+) create mode 100644 drivers/fpga/Kconfig create mode 100644 drivers/fpga/Makefile create mode 100644 drivers/fpga/fpga-mgr-fops.c create mode 100644 drivers/fpga/fpga-mgr.c create mode 100644 drivers/fpga/fpga-mgr.h create mode 100644 drivers/fpga/fpga-transport-mmio.c create mode 100644 drivers/fpga/fpga-transport.c create mode 100644 include/linux/fpga.h diff --git a/drivers/Kconfig b/drivers/Kconfig index 0e87a34b6472e..b0cbbae2c7b8c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -34,6 +34,8 @@ source "drivers/message/fusion/Kconfig" source "drivers/firewire/Kconfig" +source "drivers/fpga/Kconfig" + source "drivers/message/i2o/Kconfig" source "drivers/macintosh/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index f98b50d8251d3..afdd2aa6b27c4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_RESET_CONTROLLER) += reset/ # default. obj-y += tty/ obj-y += char/ +obj-$(CONFIG_FPGA) += fpga/ # gpu/ comes after char for AGP vs DRM startup obj-y += gpu/ diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig new file mode 100644 index 0000000000000..a706f63a7f645 --- /dev/null +++ b/drivers/fpga/Kconfig @@ -0,0 +1,14 @@ +# +# FPGA framework configuration +# + +menu "FPGA devices" + +config FPGA + tristate "FPGA Framework" + help + Say Y here if you want support for configuring FPGAs from the + kernel. The FPGA framework adds a FPGA manager class and FPGA + manager drivers. +endmenu + diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile new file mode 100644 index 0000000000000..fd3cfb49469f8 --- /dev/null +++ b/drivers/fpga/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the fpga framework and fpga manager drivers. +# + +fpga-mgr-core-y += fpga-mgr.o fpga-mgr-fops.o fpga-transport.o fpga-transport-mmio.o + +obj-$(CONFIG_FPGA) += fpga-mgr-core.o +obj-y += fpga-mgrs/ diff --git a/drivers/fpga/fpga-mgr-fops.c b/drivers/fpga/fpga-mgr-fops.c new file mode 100644 index 0000000000000..b0096cd8ee3f4 --- /dev/null +++ b/drivers/fpga/fpga-mgr-fops.c @@ -0,0 +1,124 @@ +/* + * FPGA Framework file operations + * + * Copyright (C) 2013 Altera Corporation + * + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include "fpga-mgr.h" + +static ssize_t fpga_mgr_read(struct file *file, char __user *buf, size_t count, + loff_t *offset) +{ + char *tmp; + int ret = -EFAULT; + struct fpga_manager *mgr = file->private_data; + + tmp = vmalloc(count); + if (tmp == NULL) + return -ENOMEM; + + ret = mgr->mops->read(mgr, tmp, count); + if ((ret > 0) && copy_to_user(buf, tmp, ret)) + ret = -EFAULT; + + vfree(tmp); + return ret; +} + +static ssize_t fpga_mgr_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + struct fpga_manager *mgr = file->private_data; + char *kern_buf; + int ret; + + kern_buf = memdup_user(buf, count); + if (IS_ERR(kern_buf)) + return PTR_ERR(kern_buf); + + ret = mgr->mops->write(mgr, kern_buf, count); + kfree(kern_buf); + return ret; +} + +static int fpga_mgr_open(struct inode *inode, struct file *file) +{ + struct fpga_manager *mgr; + struct fpga_manager_ops *mops; + bool fmode_wr = (file->f_mode & FMODE_WRITE) != 0; + bool fmode_rd = (file->f_mode & FMODE_READ) != 0; + int ret = 0; + + mgr = container_of(inode->i_cdev, struct fpga_manager, cdev); + if (!mgr) + return -ENODEV; + + mops = mgr->mops; + + /* Don't allow read or write if we don't have read/write fns. */ + if ((fmode_wr && !mops->write) || (fmode_rd && !mops->read)) + return -EPERM; + + /* Need to know if we are going to read or write. Can't be both. */ + if (fmode_wr && fmode_rd) + return -EPERM; + + if (test_and_set_bit_lock(FPGA_MGR_DEV_BUSY, &mgr->flags)) + return -EBUSY; + + file->private_data = mgr; + + if (fmode_wr && mops->write_init) + ret = mops->write_init(mgr); + else if (fmode_rd && mops->read_init) + ret = mops->read_init(mgr); + + return ret; +} + +static int fpga_mgr_release(struct inode *inode, struct file *file) +{ + struct fpga_manager *mgr = file->private_data; + struct fpga_manager_ops *mops = mgr->mops; + bool fmode_wr = (file->f_mode & FMODE_WRITE) != 0; + bool fmode_rd = (file->f_mode & FMODE_READ) != 0; + int ret = 0; + + if (fmode_wr && mops->write_complete) + ret = mops->write_complete(mgr); + else if (fmode_rd && mops->read_complete) + ret = mops->read_complete(mgr); + + file->private_data = NULL; + clear_bit_unlock(FPGA_MGR_DEV_BUSY, &mgr->flags); + + return ret; +} + +const struct file_operations fpga_mgr_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = fpga_mgr_read, + .write = fpga_mgr_write, + .open = fpga_mgr_open, + .release = fpga_mgr_release, +}; diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c new file mode 100644 index 0000000000000..0a72e72f9c58a --- /dev/null +++ b/drivers/fpga/fpga-mgr.c @@ -0,0 +1,258 @@ +/* + * FPGA Framework + * + * Copyright (C) 2013 Altera Corporation + * + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "fpga-mgr.h" + +static DEFINE_IDA(fpga_mgr_ida); +static int fpga_mgr_major; +static struct class *fpga_mgr_class; + +#define FPGA_MAX_MINORS 256 + +/* + * class attributes + */ +static ssize_t fpga_mgr_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fpga_manager *mgr = dev_get_drvdata(dev); + + if (!mgr) + return -ENODEV; + + return snprintf(buf, sizeof(mgr->name), "%s\n", mgr->name); +} + +static ssize_t fpga_mgr_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fpga_manager *mgr = dev_get_drvdata(dev); + + if (!mgr || !mgr->mops || !mgr->mops->status) + return -ENODEV; + + return mgr->mops->status(mgr, buf); +} + +static DEVICE_ATTR(name, S_IRUGO, fpga_mgr_name_show, NULL); +static DEVICE_ATTR(status, S_IRUGO, fpga_mgr_status_show, NULL); + +static struct attribute *fpga_mgr_attrs[] = { + &dev_attr_name.attr, + &dev_attr_status.attr, + NULL, +}; + +static const struct attribute_group fpga_mgr_group = { + .attrs = fpga_mgr_attrs, +}; + +const struct attribute_group *fpga_mgr_groups[] = { + &fpga_mgr_group, + NULL, +}; + +static int fpga_mgr_get_new_minor(struct fpga_manager *mgr, int request_nr) +{ + int nr, start; + + /* check specified minor number */ + if (request_nr >= FPGA_MAX_MINORS) { + dev_err(mgr->parent, "Out of device minors (%d)\n", request_nr); + return -ENODEV; + } + + /* + * If request_nr == -1, dynamically allocate number. + * If request_nr >= 0, attempt to get specific number. + */ + if (request_nr == -1) + start = 0; + else + start = request_nr; + + nr = ida_simple_get(&fpga_mgr_ida, start, FPGA_MAX_MINORS, GFP_KERNEL); + + /* return error code */ + if (nr < 0) + return nr; + + if ((request_nr != -1) && (request_nr != nr)) { + dev_err(mgr->parent, + "Could not get requested device minor (%d)\n", nr); + ida_simple_remove(&fpga_mgr_ida, nr); + return -ENODEV; + } + + mgr->nr = nr; + + return 0; +} + +static void fpga_mgr_free_minor(int nr) +{ + ida_simple_remove(&fpga_mgr_ida, nr); +} + +int register_fpga_manager(struct platform_device *pdev, + struct fpga_manager_ops *mops, char *name, void *priv) +{ + struct fpga_manager *mgr; + int ret; + + if (!mops) { + dev_err(&pdev->dev, + "Attempt to register with no fpga_manager_ops\n"); + return -EINVAL; + } + if (!name || (name[0] == '\0')) { + dev_err(&pdev->dev, "Attempt to register with no name!\n"); + return -EINVAL; + } + + mgr = kzalloc(sizeof(struct fpga_manager), GFP_KERNEL); + if (!mgr) + return -ENOMEM; + + platform_set_drvdata(pdev, mgr); + mgr->mops = mops; + mgr->np = pdev->dev.of_node; + mgr->parent = get_device(&pdev->dev); + mgr->priv = priv; + strlcpy(mgr->name, name, sizeof(mgr->name)); + init_completion(&mgr->status_complete); + + ret = fpga_mgr_get_new_minor(mgr, pdev->id); + if (ret) + goto error_kfree; + + ret = fpga_mgr_attach_transport(mgr); + if (ret) + goto error_attach; + + if (mops->isr) { + mgr->irq = irq_of_parse_and_map(mgr->np, 0); + if (mgr->irq == NO_IRQ) { + dev_err(mgr->parent, "failed to map interrupt\n"); + goto error_irq_map; + } + + ret = request_irq(mgr->irq, mops->isr, 0, "fpga-mgr", mgr); + if (ret < 0) { + dev_err(mgr->parent, "error requesting interrupt\n"); + goto error_irq_req; + } + } + + cdev_init(&mgr->cdev, &fpga_mgr_fops); + ret = cdev_add(&mgr->cdev, MKDEV(fpga_mgr_major, mgr->nr), 1); + if (ret) + goto error_cdev; + + mgr->dev = device_create(fpga_mgr_class, mgr->parent, + MKDEV(fpga_mgr_major, mgr->nr), mgr, + "fpga%d", mgr->nr); + if (IS_ERR(mgr->dev)) { + ret = PTR_ERR(mgr->dev); + goto error_device; + } + + dev_info(mgr->parent, "fpga manager [%s] registered as minor %d\n", + mgr->name, mgr->nr); + + return 0; + +error_device: + cdev_del(&mgr->cdev); +error_cdev: + free_irq(mgr->irq, mgr); +error_irq_req: + irq_dispose_mapping(mgr->irq); +error_irq_map: + fpga_mgr_detach_transport(mgr); +error_attach: + fpga_mgr_free_minor(mgr->nr); +error_kfree: + put_device(mgr->parent); + kfree(mgr); + return ret; +} +EXPORT_SYMBOL_GPL(register_fpga_manager); + +void remove_fpga_manager(struct platform_device *pdev) +{ + struct fpga_manager *mgr = platform_get_drvdata(pdev); + + if (mgr && mgr->mops && mgr->mops->fpga_remove) + mgr->mops->fpga_remove(mgr); + + device_destroy(fpga_mgr_class, MKDEV(fpga_mgr_major, mgr->nr)); + cdev_del(&mgr->cdev); + free_irq(mgr->irq, mgr); + irq_dispose_mapping(mgr->irq); + fpga_mgr_detach_transport(mgr); + fpga_mgr_free_minor(mgr->nr); + put_device(mgr->parent); + kfree(mgr); +} +EXPORT_SYMBOL_GPL(remove_fpga_manager); + +static int __init fpga_mgr_dev_init(void) +{ + dev_t fpga_mgr_dev; + int ret; + + pr_info("FPGA Mangager framework driver\n"); + + fpga_mgr_class = class_create(THIS_MODULE, "fpga"); + if (IS_ERR(fpga_mgr_class)) + return PTR_ERR(fpga_mgr_class); + + fpga_mgr_class->dev_groups = fpga_mgr_groups; + + ret = alloc_chrdev_region(&fpga_mgr_dev, 0, FPGA_MAX_MINORS, "fpga"); + if (ret) { + class_destroy(fpga_mgr_class); + return ret; + } + + fpga_mgr_major = MAJOR(fpga_mgr_dev); + + return 0; +} + +static void __exit fpga_mgr_dev_exit(void) +{ + unregister_chrdev_region(MKDEV(fpga_mgr_major, 0), FPGA_MAX_MINORS); + class_destroy(fpga_mgr_class); + ida_destroy(&fpga_mgr_ida); +} + +MODULE_DESCRIPTION("FPGA Manager framework driver"); +MODULE_LICENSE("GPL v2"); + +subsys_initcall(fpga_mgr_dev_init); +module_exit(fpga_mgr_dev_exit); diff --git a/drivers/fpga/fpga-mgr.h b/drivers/fpga/fpga-mgr.h new file mode 100644 index 0000000000000..2c72da342c2ab --- /dev/null +++ b/drivers/fpga/fpga-mgr.h @@ -0,0 +1,24 @@ +/* + * FPGA Framework + * + * Copyright (C) 2013 Altera Corporation + * + * 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 . + */ +#ifndef __FPGA_CORE_H +#define __FPGA_CORE_H + +extern const struct file_operations fpga_mgr_fops; + +#endif /* __FPGA_CORE_H */ diff --git a/drivers/fpga/fpga-transport-mmio.c b/drivers/fpga/fpga-transport-mmio.c new file mode 100644 index 0000000000000..5a373cd48c45c --- /dev/null +++ b/drivers/fpga/fpga-transport-mmio.c @@ -0,0 +1,120 @@ +/* + * FPGA Framework Transport + * + * Copyright (C) 2013 Altera Corporation + * + * 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 . + */ +#include +#include +#include +#include +#include +#include + +static u32 fpga_reg_readl(struct fpga_mgr_transport *transp, u32 reg_offset) +{ + return readl(transp->fpga_base_addr + reg_offset); +} + +static void fpga_reg_writel(struct fpga_mgr_transport *transp, u32 reg_offset, + u32 value) +{ + writel(value, transp->fpga_base_addr + reg_offset); +} + +static u32 fpga_reg_raw_readl(struct fpga_mgr_transport *transp, u32 reg_offset) +{ + return __raw_readl(transp->fpga_base_addr + reg_offset); +} + +static void fpga_reg_raw_writel(struct fpga_mgr_transport *transp, u32 reg_offset, + u32 value) +{ + __raw_writel(value, transp->fpga_base_addr + reg_offset); +} + +static u32 fpga_data_readl(struct fpga_mgr_transport *transp) +{ + return readl(transp->fpga_data_addr); +} + +static void fpga_data_writel(struct fpga_mgr_transport *transp, u32 value) +{ + writel(value, transp->fpga_data_addr); +} + +int fpga_attach_mmio_transport(struct fpga_manager *mgr) +{ + struct device_node *np = mgr->np; + struct fpga_mgr_transport *transp; + static void __iomem *fpga_base_addr; + static void __iomem *fpga_data_addr; + int ret = 0; + + fpga_base_addr = of_iomap(np, 0); + if (!fpga_base_addr) { + dev_err(mgr->parent, + "Need to specify fpga manager register address.\n"); + return -EINVAL; + } + + fpga_data_addr = of_iomap(np, 1); + if (!fpga_data_addr) { + dev_err(mgr->parent, + "Need to specify fpga manager data address.\n"); + ret = -ENOMEM; + goto err_baddr; + } + + transp = kzalloc(sizeof(struct fpga_mgr_transport), GFP_KERNEL); + if (!transp) { + dev_err(mgr->parent, "Could not allocate transport\n"); + ret = -ENOMEM; + goto err_daddr; + } + + transp->fpga_base_addr = fpga_base_addr; + transp->fpga_data_addr = fpga_data_addr; + + transp->reg_readl = fpga_reg_readl; + transp->reg_writel = fpga_reg_writel; + transp->reg_raw_readl = fpga_reg_raw_readl; + transp->reg_raw_writel = fpga_reg_raw_writel; + transp->data_writel = fpga_data_writel; + transp->data_readl = fpga_data_readl; + strlcpy(transp->type, "mmio", sizeof(transp->type)); + + transp->mgr = mgr; + mgr->transp = transp; + + return 0; + +err_daddr: + iounmap(fpga_data_addr); +err_baddr: + iounmap(fpga_base_addr); + return ret; +} +EXPORT_SYMBOL(fpga_attach_mmio_transport); + +void fpga_detach_mmio_transport(struct fpga_manager *mgr) +{ + struct fpga_mgr_transport *transp = mgr->transp; + + iounmap(transp->fpga_base_addr); + iounmap(transp->fpga_data_addr); + kfree(transp); +} +EXPORT_SYMBOL(fpga_detach_mmio_transport); diff --git a/drivers/fpga/fpga-transport.c b/drivers/fpga/fpga-transport.c new file mode 100644 index 0000000000000..7f68ad4ec5220 --- /dev/null +++ b/drivers/fpga/fpga-transport.c @@ -0,0 +1,63 @@ +/* + * FPGA Framework Transport + * + * Copyright (C) 2013 Altera Corporation + * + * 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 . + */ +#include +#include +#include +#include +#include +#include + +int fpga_mgr_attach_transport(struct fpga_manager *mgr) +{ + struct device_node *np = mgr->np; + const char *string; + int ret; + + ret = of_property_read_string(np, "transport", &string); + if (ret) { + dev_err(mgr->parent, + "Transport not specified for fpga manager %s\n", + mgr->name); + return -ENODEV; + } + + if (strcmp(string, "mmio") == 0) + ret = fpga_attach_mmio_transport(mgr); + else { + dev_err(mgr->parent, + "Invalid transport specified for fpga manager %s (%s)\n", + mgr->name, string); + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(fpga_mgr_attach_transport); + +void fpga_mgr_detach_transport(struct fpga_manager *mgr) +{ + struct fpga_mgr_transport *transp = mgr->transp; + const char *type = transp->type; + + if (!strncmp(type, "mmio", strlen(type))) + fpga_detach_mmio_transport(mgr); + else + BUG(); +} +EXPORT_SYMBOL(fpga_mgr_detach_transport); diff --git a/include/linux/fpga.h b/include/linux/fpga.h new file mode 100644 index 0000000000000..37f8e42caa4c1 --- /dev/null +++ b/include/linux/fpga.h @@ -0,0 +1,184 @@ +/* + * FPGA Framework + * + * Copyright (C) 2013 Altera Corporation + * + * 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 . + */ +#include +#include +#include +#include +#include +#include + +#ifndef _LINUX_FPGA_H +#define _LINUX_FPGA_H + +struct fpga_manager; +struct fpga_mgr_transport; + +/*---------------------------------------------------------------------------*/ + +struct fpga_mgr_transport { + struct fpga_manager *mgr; + char type[16]; + + u32 (*reg_readl)(struct fpga_mgr_transport *transp, u32 reg_offset); + void (*reg_writel)(struct fpga_mgr_transport *transp, u32 reg_offset, + u32 value); + u32 (*reg_raw_readl)(struct fpga_mgr_transport *transp, u32 reg_offset); + void (*reg_raw_writel)(struct fpga_mgr_transport *transp, u32 reg_offset, + u32 value); + u32 (*data_readl)(struct fpga_mgr_transport *transp); + void (*data_writel)(struct fpga_mgr_transport *transp, u32 value); + + void __iomem *fpga_base_addr; + void __iomem *fpga_data_addr; +}; + +extern int fpga_mgr_attach_transport(struct fpga_manager *mgr); +extern void fpga_mgr_detach_transport(struct fpga_manager *mgr); + +extern int fpga_attach_mmio_transport(struct fpga_manager *mgr); +extern void fpga_detach_mmio_transport(struct fpga_manager *mgr); + +/*---------------------------------------------------------------------------*/ + +/* + * fpga_manager_ops are the low level functions implemented by a specific + * fpga manager driver. Leaving any of these out that aren't needed is fine + * as they are all tested for NULL before being called. + */ +struct fpga_manager_ops { + /* Returns a string of the FPGA's status */ + int (*status)(struct fpga_manager *mgr, char *buf); + + /* Prepare the FPGA for reading its confuration data */ + int (*read_init)(struct fpga_manager *mgr); + + /* Read count bytes configuration data from the FPGA */ + ssize_t (*read)(struct fpga_manager *mgr, char *buf, size_t count); + + /* Return FPGA to a default state after reading is done */ + int (*read_complete)(struct fpga_manager *mgr); + + /* Prepare the FPGA to receive confuration data */ + int (*write_init)(struct fpga_manager *mgr); + + /* Write count bytes of configuration data to the FPGA */ + ssize_t (*write)(struct fpga_manager *mgr, char *buf, size_t count); + + /* Return FPGA to default state after writing is done */ + int (*write_complete)(struct fpga_manager *mgr); + + /* Set FPGA into a specific state during driver remove */ + void (*fpga_remove)(struct fpga_manager *mgr); + + /* FPGA mangager isr */ + irqreturn_t (*isr)(int irq, void *dev_id); +}; + +/* flag bits */ +#define FPGA_MGR_DEV_BUSY 0 + +struct fpga_manager { + struct device_node *np; + struct device *parent; + struct device *dev; + struct cdev cdev; + int irq; + struct completion status_complete; + + int nr; + char name[48]; + unsigned long flags; + struct fpga_manager_ops *mops; + struct fpga_mgr_transport *transp; + + void *priv; +}; + +#if defined(CONFIG_FPGA) || defined(CONFIG_FPGA_MODULE) + +extern int register_fpga_manager(struct platform_device *pdev, + struct fpga_manager_ops *mops, + char *name, void *priv); + +extern void remove_fpga_manager(struct platform_device *pdev); + +/* + * Read/write FPGA manager registers + */ +static inline u32 fpga_mgr_reg_readl(struct fpga_manager *mgr, u32 offset) +{ + struct fpga_mgr_transport *transp = mgr->transp; + return transp->reg_readl(transp, offset); +} + +static inline void fpga_mgr_reg_writel(struct fpga_manager *mgr, u32 offset, + u32 value) +{ + struct fpga_mgr_transport *transp = mgr->transp; + transp->reg_writel(transp, offset, value); +} + +static inline u32 fpga_mgr_reg_raw_readl(struct fpga_manager *mgr, u32 offset) +{ + struct fpga_mgr_transport *transp = mgr->transp; + return transp->reg_raw_readl(transp, offset); +} + +static inline void fpga_mgr_reg_raw_writel(struct fpga_manager *mgr, u32 offset, + u32 value) +{ + struct fpga_mgr_transport *transp = mgr->transp; + transp->reg_raw_writel(transp, offset, value); +} + +static inline void fpga_mgr_reg_set_bitsl(struct fpga_manager *mgr, + u32 offset, u32 bits) +{ + u32 val; + val = fpga_mgr_reg_readl(mgr, offset); + val |= bits; + fpga_mgr_reg_writel(mgr, offset, val); +} + +static inline void fpga_mgr_reg_clr_bitsl(struct fpga_manager *mgr, + u32 offset, u32 bits) +{ + u32 val; + val = fpga_mgr_reg_readl(mgr, offset); + val &= ~bits; + fpga_mgr_reg_writel(mgr, offset, val); +} + +/* + * Read/write FPGA configuration data + */ +static inline u32 fpga_mgr_data_readl(struct fpga_manager *mgr) +{ + struct fpga_mgr_transport *transp = mgr->transp; + return transp->data_readl(transp); +} + +static inline void fpga_mgr_data_writel(struct fpga_manager *mgr, u32 value) +{ + struct fpga_mgr_transport *transp = mgr->transp; + transp->data_writel(transp, value); +} + +#endif /* CONFIG_FPGA */ +#endif /*_LINUX_FPGA_H */ From b87994e8f60afe9f56129990482520ae6561296f Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Tue, 15 Jan 2013 15:28:52 -0600 Subject: [PATCH 078/201] FogBugz #92330-2: fpga manager driver for altera fpgas Add a FPGA manager driver supporting Altera FPGAs to the FPGA Manager framework. To get a FPGA's status: cat /sys/class/fpga/fpag0/status To program a FPGA: cat bitstream-file > /dev/fpga0 Signed-off-by: Alan Tull V3: Use raw readl/writel in interrupt handler Change module license to GPL v2 Add a comment V2: No changes (the other patch in this series had changes). Conflicts: arch/arm/configs/socfpga_defconfig --- .../bindings/fpga/altera-fpga-mgr.txt | 25 + arch/arm/boot/dts/socfpga.dtsi | 8 + arch/arm/configs/socfpga_defconfig | 2 + drivers/fpga/Kconfig | 6 + drivers/fpga/fpga-mgrs/Makefile | 5 + drivers/fpga/fpga-mgrs/altera.c | 576 ++++++++++++++++++ 6 files changed, 622 insertions(+) create mode 100644 Documentation/devicetree/bindings/fpga/altera-fpga-mgr.txt create mode 100644 drivers/fpga/fpga-mgrs/Makefile create mode 100644 drivers/fpga/fpga-mgrs/altera.c diff --git a/Documentation/devicetree/bindings/fpga/altera-fpga-mgr.txt b/Documentation/devicetree/bindings/fpga/altera-fpga-mgr.txt new file mode 100644 index 0000000000000..fbce3b0e18ab8 --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/altera-fpga-mgr.txt @@ -0,0 +1,25 @@ +Altera FPGA Manager + +Required properties: + + - compatible : should be "," + "altr,fpga-mgr-1.0", "altr,fpga-mgr"; + + - transport : the interface for register and configuration data. + Currently only memory mapped io is supported, so must be "mmio" + + - reg : base address for memory mapped io. + - The first index is for FPGA manager register access. + - The second index is for writing FPGA configuration data. + + - interrupts : interrupts for the FPGA Manager device. + +Example: + + hps_0_fpgamgr: fpgamgr@0xff706000 { + compatible = "altr,fpga-mgr-1.0", "altr,fpga-mgr"; + transport = "mmio"; + reg = <0xFF706000 0x1000 + 0xFFB90000 0x1000>; + interrupts = <0 175 4>; + }; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index dee58ec1b1541..6be0dcc03a222 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -835,5 +835,13 @@ compatible = "altr,sys-mgr", "syscon"; reg = <0xffd08000 0x4000>; }; + + hps_0_fpgamgr: fpgamgr@0xff706000 { + compatible = "altr,fpga-mgr-1.0", "altr,fpga-mgr"; + transport = "mmio"; + reg = <0xFF706000 0x1000 + 0xFFB90000 0x1000>; + interrupts = <0 175 4>; + }; }; }; diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index fa40056d79673..153f6481c4e0e 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -106,3 +106,5 @@ CONFIG_DEBUG_USER=y CONFIG_XZ_DEC=y CONFIG_MMC=y CONFIG_MMC_DW=y +CONFIG_FPGA=y +CONFIG_FPGA_MGR_ALTERA=y diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index a706f63a7f645..3af1dc5064812 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -10,5 +10,11 @@ config FPGA Say Y here if you want support for configuring FPGAs from the kernel. The FPGA framework adds a FPGA manager class and FPGA manager drivers. + +config FPGA_MGR_ALTERA + tristate "Altera" + depends on FPGA + help + FPGA manager driver support for configuring Altera FPGAs. endmenu diff --git a/drivers/fpga/fpga-mgrs/Makefile b/drivers/fpga/fpga-mgrs/Makefile new file mode 100644 index 0000000000000..3b4d59014234d --- /dev/null +++ b/drivers/fpga/fpga-mgrs/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for fpga manager drivers of various fpga families +# + +obj-$(CONFIG_FPGA_MGR_ALTERA) += altera.o diff --git a/drivers/fpga/fpga-mgrs/altera.c b/drivers/fpga/fpga-mgrs/altera.c new file mode 100644 index 0000000000000..3d47505029464 --- /dev/null +++ b/drivers/fpga/fpga-mgrs/altera.c @@ -0,0 +1,576 @@ +/* + * FPGA Manager Driver for Altera FPGAs + * + * Copyright (C) 2013 Altera Corporation + * + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Controls whether to use the Configuration with DCLK steps */ +#ifndef _ALT_FPGA_USE_DCLK +#define _ALT_FPGA_USE_DCLK 0 +#endif + +/* Register offsets */ +#define ALT_FPGAMGR_STAT_OFST 0x0 +#define ALT_FPGAMGR_CTL_OFST 0x4 +#define ALT_FPGAMGR_DCLKCNT_OFST 0x8 +#define ALT_FPGAMGR_DCLKSTAT_OFST 0xc +#define ALT_FPGAMGR_MON_GPIO_INTEN_OFST 0x830 +#define ALT_FPGAMGR_MON_GPIO_INTMSK_OFST 0x834 +#define ALT_FPGAMGR_MON_GPIO_INTTYPE_LEVEL_OFST 0x838 +#define ALT_FPGAMGR_MON_GPIO_INT_POL_OFST 0x83c +#define ALT_FPGAMGR_MON_GPIO_INTSTAT_OFST 0x840 +#define ALT_FPGAMGR_MON_GPIO_RAW_INTSTAT_OFST 0x844 +#define ALT_FPGAMGR_MON_GPIO_PORTA_EOI_OFST 0x84c +#define ALT_FPGAMGR_MON_GPIO_EXT_PORTA_OFST 0x850 + +/* Register bit defines */ +/* ALT_FPGAMGR_STAT register */ +#define ALT_FPGAMGR_STAT_POWER_UP 0x0 +#define ALT_FPGAMGR_STAT_RESET 0x1 +#define ALT_FPGAMGR_STAT_CFG 0x2 +#define ALT_FPGAMGR_STAT_INIT 0x3 +#define ALT_FPGAMGR_STAT_USER_MODE 0x4 +#define ALT_FPGAMGR_STAT_UNKNOWN 0x5 +#define ALT_FPGAMGR_STAT_STATE_MASK 0x7 +/* This is a flag value that doesn't really happen in this register field */ +#define ALT_FPGAMGR_STAT_POWER_OFF 0xf + +#define MSEL_PP16_FAST_NOAES_NODC 0x0 +#define MSEL_PP16_FAST_AES_NODC 0x1 +#define MSEL_PP16_FAST_AESOPT_DC 0x2 +#define MSEL_PP16_SLOW_NOAES_NODC 0x4 +#define MSEL_PP16_SLOW_AES_NODC 0x5 +#define MSEL_PP16_SLOW_AESOPT_DC 0x6 +#define MSEL_PP32_FAST_NOAES_NODC 0x8 +#define MSEL_PP32_FAST_AES_NODC 0x9 +#define MSEL_PP32_FAST_AESOPT_DC 0xa +#define MSEL_PP32_SLOW_NOAES_NODC 0xc +#define MSEL_PP32_SLOW_AES_NODC 0xd +#define MSEL_PP32_SLOW_AESOPT_DC 0xe +#define ALT_FPGAMGR_STAT_MSEL_MASK 0x000000f8 +#define ALT_FPGAMGR_STAT_MSEL_SHIFT 3 + +/* ALT_FPGAMGR_CTL register */ +#define ALT_FPGAMGR_CTL_EN 0x00000001 +#define ALT_FPGAMGR_CTL_NCE 0x00000002 +#define ALT_FPGAMGR_CTL_NCFGPULL 0x00000004 + +#define CDRATIO_X1 0x00000000 +#define CDRATIO_X2 0x00000040 +#define CDRATIO_X4 0x00000080 +#define CDRATIO_X8 0x000000c0 +#define ALT_FPGAMGR_CTL_CDRATIO_MASK 0x000000c0 + +#define ALT_FPGAMGR_CTL_AXICFGEN 0x00000100 + +#define CFGWDTH_16 0x00000000 +#define CFGWDTH_32 0x00000200 +#define ALT_FPGAMGR_CTL_CFGWDTH_MASK 0x00000200 + +/* ALT_FPGAMGR_DCLKSTAT register */ +#define ALT_FPGAMGR_DCLKSTAT_DCNTDONE_E_DONE 0x1 + +/* ALT_FPGAMGR_MON_GPIO_* registers share the same bit positions */ +#define ALT_FPGAMGR_MON_NSTATUS 0x0001 +#define ALT_FPGAMGR_MON_CONF_DONE 0x0002 +#define ALT_FPGAMGR_MON_INIT_DONE 0x0004 +#define ALT_FPGAMGR_MON_CRC_ERROR 0x0008 +#define ALT_FPGAMGR_MON_CVP_CONF_DONE 0x0010 +#define ALT_FPGAMGR_MON_PR_READY 0x0020 +#define ALT_FPGAMGR_MON_PR_ERROR 0x0040 +#define ALT_FPGAMGR_MON_PR_DONE 0x0080 +#define ALT_FPGAMGR_MON_NCONFIG_PIN 0x0100 +#define ALT_FPGAMGR_MON_NSTATUS_PIN 0x0200 +#define ALT_FPGAMGR_MON_CONF_DONE_PIN 0x0400 +#define ALT_FPGAMGR_MON_FPGA_POWER_ON 0x0800 +#define ALT_FPGAMGR_MON_STATUS_MASK 0x0fff + +struct cfgmgr_mode { + /* Values to set in the CTRL register */ + u32 ctrl; + + /* flag that this table entry is a valid mode */ + bool valid; +}; + +/* For ALT_FPGAMGR_STAT_MSEL field */ +static struct cfgmgr_mode cfgmgr_modes[] = { + [MSEL_PP16_FAST_NOAES_NODC] = { CFGWDTH_16 | CDRATIO_X1, 1 }, + [MSEL_PP16_FAST_AES_NODC] = { CFGWDTH_16 | CDRATIO_X2, 1 }, + [MSEL_PP16_FAST_AESOPT_DC] = { CFGWDTH_16 | CDRATIO_X4, 1 }, + [MSEL_PP16_SLOW_NOAES_NODC] = { CFGWDTH_16 | CDRATIO_X1, 1 }, + [MSEL_PP16_SLOW_AES_NODC] = { CFGWDTH_16 | CDRATIO_X2, 1 }, + [MSEL_PP16_SLOW_AESOPT_DC] = { CFGWDTH_16 | CDRATIO_X4, 1 }, + [MSEL_PP32_FAST_NOAES_NODC] = { CFGWDTH_32 | CDRATIO_X1, 1 }, + [MSEL_PP32_FAST_AES_NODC] = { CFGWDTH_32 | CDRATIO_X4, 1 }, + [MSEL_PP32_FAST_AESOPT_DC] = { CFGWDTH_32 | CDRATIO_X8, 1 }, + [MSEL_PP32_SLOW_NOAES_NODC] = { CFGWDTH_32 | CDRATIO_X1, 1 }, + [MSEL_PP32_SLOW_AES_NODC] = { CFGWDTH_32 | CDRATIO_X4, 1 }, + [MSEL_PP32_SLOW_AESOPT_DC] = { CFGWDTH_32 | CDRATIO_X8, 1 }, +}; + +static int alt_fpga_mon_status_get(struct fpga_manager *mgr) +{ + return fpga_mgr_reg_readl(mgr, ALT_FPGAMGR_MON_GPIO_EXT_PORTA_OFST) & + ALT_FPGAMGR_MON_STATUS_MASK; +} + +static int alt_fpga_state_get(struct fpga_manager *mgr) +{ + if ((alt_fpga_mon_status_get(mgr) & ALT_FPGAMGR_MON_FPGA_POWER_ON) == 0) + return ALT_FPGAMGR_STAT_POWER_OFF; + + return fpga_mgr_reg_readl(mgr, ALT_FPGAMGR_STAT_OFST) & + ALT_FPGAMGR_STAT_STATE_MASK; +} + +/* + * Set the DCLKCNT, wait for DCLKSTAT to report the count completed, and clear + * the complete status. + */ +static int alt_fpga_dclk_set_and_wait_clear(struct fpga_manager *mgr, u32 count) +{ + int timeout = 2; + u32 done; + + /* Clear any existing DONE status. */ + if (fpga_mgr_reg_readl(mgr, ALT_FPGAMGR_DCLKSTAT_OFST)) + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_DCLKSTAT_OFST, + ALT_FPGAMGR_DCLKSTAT_DCNTDONE_E_DONE); + + /* Issue the DCLK count. */ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_DCLKCNT_OFST, count); + + /* Poll DCLKSTAT to see if it completed in the timeout period. */ + do { + done = fpga_mgr_reg_readl(mgr, ALT_FPGAMGR_DCLKSTAT_OFST); + if (done == ALT_FPGAMGR_DCLKSTAT_DCNTDONE_E_DONE) { + /* clear the DONE status. */ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_DCLKSTAT_OFST, + ALT_FPGAMGR_DCLKSTAT_DCNTDONE_E_DONE); + return 0; + } + if (count <= 4) + udelay(1); + else + msleep(20); + } while (timeout--); + + return -ETIMEDOUT; +} + +static int alt_fpga_wait_for_state(struct fpga_manager *mgr, u32 state) +{ + int timeout = 2; + + /* + * HW doesn't support an interrupt for changes in state, so poll to see + * if it matches the requested state within the timeout period. + */ + do { + if ((alt_fpga_state_get(mgr) & state) != 0) + return 0; + msleep(20); + } while (timeout--); + + return -ETIMEDOUT; +} + +static void alt_fpga_enable_irqs(struct fpga_manager *mgr, u32 irqs) +{ + /* set irqs to level sensitive */ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_MON_GPIO_INTTYPE_LEVEL_OFST, 0); + + /* set interrupt polarity */ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_MON_GPIO_INT_POL_OFST, irqs); + + /* clear irqs */ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_MON_GPIO_PORTA_EOI_OFST, irqs); + + /* unmask interrupts */ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_MON_GPIO_INTMSK_OFST, 0); + + /* enable interrupts */ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_MON_GPIO_INTEN_OFST, irqs); +} + +static void alt_fpga_disable_irqs(struct fpga_manager *mgr) +{ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_MON_GPIO_INTEN_OFST, 0); +} + +static irqreturn_t alt_fpga_isr(int irq, void *dev_id) +{ + struct fpga_manager *mgr = dev_id; + u32 irqs, st; + bool conf_done, nstatus; + + /* clear irqs */ + irqs = fpga_mgr_reg_raw_readl(mgr, ALT_FPGAMGR_MON_GPIO_INTSTAT_OFST); + fpga_mgr_reg_raw_writel(mgr, ALT_FPGAMGR_MON_GPIO_PORTA_EOI_OFST, irqs); + + st = fpga_mgr_reg_raw_readl(mgr, ALT_FPGAMGR_MON_GPIO_EXT_PORTA_OFST); + conf_done = (st & ALT_FPGAMGR_MON_CONF_DONE) != 0; + nstatus = (st & ALT_FPGAMGR_MON_NSTATUS) != 0; + + /* success */ + if (conf_done && nstatus) { + /* disable irqs */ + fpga_mgr_reg_raw_writel(mgr, ALT_FPGAMGR_MON_GPIO_INTEN_OFST, 0); + complete(&mgr->status_complete); + } + + return IRQ_HANDLED; +} + +static int alt_fpga_wait_for_config_done(struct fpga_manager *mgr) +{ + int timeout, ret = 0; + + alt_fpga_disable_irqs(mgr); + reinit_completion(&mgr->status_complete); + alt_fpga_enable_irqs(mgr, ALT_FPGAMGR_MON_CONF_DONE); + + timeout = wait_for_completion_interruptible_timeout( + &mgr->status_complete, + msecs_to_jiffies(10)); + if (timeout == 0) { + dev_err(mgr->parent, "timeout\n"); + ret = -ETIMEDOUT; + } + + alt_fpga_disable_irqs(mgr); + return ret; +} + +static int alt_fpga_cfg_mode_get(struct fpga_manager *mgr) +{ + u32 msel; + + msel = fpga_mgr_reg_readl(mgr, ALT_FPGAMGR_STAT_OFST); + msel &= ALT_FPGAMGR_STAT_MSEL_MASK; + msel >>= ALT_FPGAMGR_STAT_MSEL_SHIFT; + + /* Check that this MSEL setting is supported */ + if ((msel >= sizeof(cfgmgr_modes)/sizeof(struct cfgmgr_mode)) || + !cfgmgr_modes[msel].valid) { + dev_warn(mgr->parent, "Invalid MSEL setting"); + return -1; + } + + return msel; +} + +static int alt_fpga_cfg_mode_set(struct fpga_manager *mgr) +{ + u32 ctrl_reg, mode; + + /* get value from MSEL pins */ + mode = alt_fpga_cfg_mode_get(mgr); + if (mode < 0) + return -EINVAL; + + /* Adjust CTRL for the CDRATIO */ + ctrl_reg = fpga_mgr_reg_readl(mgr, ALT_FPGAMGR_CTL_OFST); + ctrl_reg &= ~ALT_FPGAMGR_CTL_CDRATIO_MASK; + ctrl_reg &= ~ALT_FPGAMGR_CTL_CFGWDTH_MASK; + ctrl_reg |= cfgmgr_modes[mode].ctrl; + + /* Set NCE to 0. */ + ctrl_reg &= ~ALT_FPGAMGR_CTL_NCE; + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_CTL_OFST, ctrl_reg); + + return 0; +} + +static int alt_fpga_reset(struct fpga_manager *mgr) +{ + u32 ctrl_reg, status; + + /* + * Step 3: Set CTRL.NCONFIGPULL to 1 to put FPGA in reset + */ + ctrl_reg = fpga_mgr_reg_readl(mgr, ALT_FPGAMGR_CTL_OFST); + ctrl_reg |= ALT_FPGAMGR_CTL_NCFGPULL; + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_CTL_OFST, ctrl_reg); + + /* + * Step 4: Wait for STATUS.MODE to report FPGA is in reset phase + */ + status = alt_fpga_wait_for_state(mgr, ALT_FPGAMGR_STAT_RESET); + + /* + * Step 5: Set CONTROL.NCONFIGPULL to 0 to release FPGA from reset + */ + ctrl_reg &= ~ALT_FPGAMGR_CTL_NCFGPULL; + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_CTL_OFST, ctrl_reg); + + if (status) { + /* This is a failure from Step 4. */ + dev_err(mgr->parent, + "Error in step 4: Wait for RESET timeout.\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * Prepare the FPGA to receive the configuration data. + */ +static int alt_fpga_configure_init(struct fpga_manager *mgr) +{ + int ret; + + /* + * Step 1: + * - Set CTRL.CFGWDTH, CTRL.CDRATIO to match cfg mode + * - Set CTRL.NCE to 0 + */ + ret = alt_fpga_cfg_mode_set(mgr); + if (ret) + return ret; + + /* Step 2: Set CTRL.EN to 1 */ + fpga_mgr_reg_set_bitsl(mgr, ALT_FPGAMGR_CTL_OFST, ALT_FPGAMGR_CTL_EN); + + /* Steps 3 - 5: Reset the FPGA */ + ret = alt_fpga_reset(mgr); + if (ret) + return ret; + + /* Step 6: Wait for FPGA to enter configuration phase */ + if (alt_fpga_wait_for_state(mgr, ALT_FPGAMGR_STAT_CFG)) { + dev_err(mgr->parent, + "Error in step 6: Wait for CFG timeout.\n"); + return -ETIMEDOUT; + } + + /* Step 7: Clear nSTATUS interrupt */ + fpga_mgr_reg_writel(mgr, ALT_FPGAMGR_MON_GPIO_PORTA_EOI_OFST, + ALT_FPGAMGR_MON_NSTATUS); + + /* Step 8: Set CTRL.AXICFGEN to 1 to enable transfer of config data */ + fpga_mgr_reg_set_bitsl(mgr, ALT_FPGAMGR_CTL_OFST, + ALT_FPGAMGR_CTL_AXICFGEN); + + return 0; +} + +/* + * Step 9: write data to the FPGA data register + */ +static ssize_t alt_fpga_configure_write(struct fpga_manager *mgr, + char *buf, size_t count) +{ + u32 *buffer_32 = (u32 *)buf; + size_t total = count; + size_t i = 0; + + if (count <= 0) + return 0; + + /* Write out the complete 32-bit chunks. */ + while (count >= sizeof(u32)) { + fpga_mgr_data_writel(mgr, buffer_32[i++]); + count -= sizeof(u32); + } + + /* Write out remaining non 32-bit chunks. */ + switch (count) { + case 3: + fpga_mgr_data_writel(mgr, buffer_32[i++] & 0x00ffffff); + break; + case 2: + fpga_mgr_data_writel(mgr, buffer_32[i++] & 0x0000ffff); + break; + case 1: + fpga_mgr_data_writel(mgr, buffer_32[i++] & 0x000000ff); + break; + default: + /* This will never happen. */ + break; + } + + return total; +} + +static int alt_fpga_configure_complete(struct fpga_manager *mgr) +{ + u32 status; + + /* + * Step 10: + * - Observe CONF_DONE and nSTATUS (active low) + * - if CONF_DONE = 1 and nSTATUS = 1, configuration was successful + * - if CONF_DONE = 0 and nSTATUS = 0, configuration failed + */ + status = alt_fpga_wait_for_config_done(mgr); + if (status) + return status; + + /* Step 11: Clear CTRL.AXICFGEN to disable transfer of config data */ + fpga_mgr_reg_clr_bitsl(mgr, ALT_FPGAMGR_CTL_OFST, + ALT_FPGAMGR_CTL_AXICFGEN); + + /* + * Step 12: + * - Write 4 to DCLKCNT + * - Wait for STATUS.DCNTDONE = 1 + * - Clear W1C bit in STATUS.DCNTDONE + */ + if (alt_fpga_dclk_set_and_wait_clear(mgr, 4)) { + dev_err(mgr->parent, "Error: Wait for dclk(4) timeout.\n"); + return -ETIMEDOUT; + } + +#if _ALT_FPGA_USE_DCLK + /* Step 13: Wait for STATUS.MODE to report INIT or USER MODE */ + if (alt_fpga_wait_for_state(mgr, ALT_FPGAMGR_STAT_INIT | + ALT_FPGAMGR_STAT_USER_MODE)) { + dev_err(mgr->parent, + "Error in step 13: Wait for USER_MODE timeout.\n"); + return -ETIMEDOUT; + } + + /* + * Extra steps for Configuration with DCLK for Initialization Phase + * Step 14 (using 4.2.1.2 steps), 15 (using 4.2.1.2 steps) + * - Write 0x5000 to DCLKCNT == the number of clocks needed to exit + * the Initialization Phase. + * - Poll until STATUS.DCNTDONE = 1, write 1 to clear + */ + if (alt_fpga_dclk_set_and_wait_clear(mgr, 0x5000)) { + dev_err(mgr->parent, + "Error in step 15: Wait for dclk(0x5000) timeout.\n"); + return -ETIMEDOUT; + } +#endif + + /* Step 13: Wait for STATUS.MODE to report USER MODE */ + if (alt_fpga_wait_for_state(mgr, ALT_FPGAMGR_STAT_USER_MODE)) { + dev_err(mgr->parent, + "Error in step 13: Wait for USER_MODE timeout.\n"); + return -ETIMEDOUT; + } + + /* Step 14: Set CTRL.EN to 0 */ + fpga_mgr_reg_clr_bitsl(mgr, ALT_FPGAMGR_CTL_OFST, ALT_FPGAMGR_CTL_EN); + + return 0; +} + +static const char *const altera_fpga_state_str[] = { + [ALT_FPGAMGR_STAT_POWER_UP] = "power up phase", + [ALT_FPGAMGR_STAT_RESET] = "reset phase", + [ALT_FPGAMGR_STAT_CFG] = "configuration phase", + [ALT_FPGAMGR_STAT_INIT] = "initialization phase", + [ALT_FPGAMGR_STAT_USER_MODE] = "user mode", + [ALT_FPGAMGR_STAT_UNKNOWN] = "undetermined", + [ALT_FPGAMGR_STAT_POWER_OFF] = "powered off", +}; + +static int alt_fpga_status(struct fpga_manager *mgr, char *buf) +{ + u32 state; + const char *state_str = NULL; + int ret; + + state = alt_fpga_state_get(mgr); + + if (state < sizeof(altera_fpga_state_str)/sizeof(char *)) + state_str = altera_fpga_state_str[state]; + + if (state_str) + ret = sprintf(buf, "%s\n", state_str); + else + ret = sprintf(buf, "%s\n", "unknown state"); + + return ret; +} + +struct fpga_manager_ops altera_fpga_mgr_ops = { + .status = alt_fpga_status, + .write_init = alt_fpga_configure_init, + .write = alt_fpga_configure_write, + .write_complete = alt_fpga_configure_complete, + .isr = alt_fpga_isr, +}; + +static int alt_fpga_probe(struct platform_device *pdev) +{ + return register_fpga_manager(pdev, &altera_fpga_mgr_ops, + "Altera FPGA Manager", NULL); +} + +static int alt_fpga_remove(struct platform_device *pdev) +{ + remove_fpga_manager(pdev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id altera_fpga_of_match[] = { + { .compatible = "altr,fpga-mgr", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, altera_fpga_of_match); +#endif + +static struct platform_driver altera_fpga_driver = { + .remove = alt_fpga_remove, + .driver = { + .name = "altera_fpga_manager", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_fpga_of_match), + }, +}; + +static int __init alt_fpga_init(void) +{ + return platform_driver_probe(&altera_fpga_driver, alt_fpga_probe); +} + +static void __exit alt_fpga_exit(void) +{ + platform_driver_unregister(&altera_fpga_driver); +} + +module_init(alt_fpga_init); +module_exit(alt_fpga_exit); + +MODULE_AUTHOR("Alan Tull "); +MODULE_DESCRIPTION("Altera FPGA Manager"); +MODULE_LICENSE("GPL v2"); From 66e0624d142a215c014edf0ec90229115f2ed59f Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Thu, 11 Jul 2013 15:47:04 -0500 Subject: [PATCH 079/201] FogBugz #129257: fpga bridge driver Support for bringing bridges out of reset. Bridges show up in sysfs under /sys/class/fpga-bridge and can be enabled/disabled from sysfs or from the device tree. Supports enabling the following hps/fpga bridges: * fpga2sdram * fpga2hps * hps2fpga * lwhps2fpga Enable: $ echo 1 > /sys/class/fpga-bridge/fpga2hps/enable Disable: $ echo 0 > /sys/class/fpga-bridge/fpga2hps/enable Check enable/disable status (checks for all bits set): $ cat /sys/class/fpga-bridge/fpga2hps/enable (will print '0' or '1') Signed-off-by: Alan Tull V2: * Instead of enabling all fpga2sdram paths (some unused), break up into separate fpga2sdram bridges, configured by the device tree. * Break the altera specific bridges into two file to keep code more straightforward. * remove fpga2sdram bridges from socfpga.dtsi as they are confiration- specific. V3: * use devm_kzalloc * rename Documentation file to 'altera-fpga-bridge.txt' * make L3 interconnect visable/invisable for hps2fpga, lwhps2fpga bridges V4: * more bindings documentation Conflicts: arch/arm/boot/dts/socfpga.dtsi arch/arm/configs/socfpga_defconfig arch/arm/mach-socfpga/socfpga.c drivers/misc/Kconfig --- .../bindings/arm/altera/socfpga-l3.txt | 14 ++ .../bindings/arm/altera/socfpga-sdram.txt | 14 ++ .../bindings/arm/altera/socfpga-system.txt | 7 +- .../bindings/fpga/altera-fpga-bridge.txt | 75 ++++++ .../bindings/reset/socfpga-reset.txt | 7 +- arch/arm/boot/dts/socfpga.dtsi | 43 +++- arch/arm/configs/socfpga_defconfig | 2 + arch/arm/mach-socfpga/socfpga.c | 21 ++ drivers/misc/Kconfig | 2 + drivers/misc/Makefile | 1 + drivers/misc/fpga-bridge/Kconfig | 20 ++ drivers/misc/fpga-bridge/Makefile | 6 + drivers/misc/fpga-bridge/altera-fpga2sdram.c | 216 ++++++++++++++++ drivers/misc/fpga-bridge/altera-hps2fpga.c | 178 ++++++++++++++ drivers/misc/fpga-bridge/fpga-bridge.c | 232 ++++++++++++++++++ drivers/misc/fpga-bridge/fpga-bridge.h | 51 ++++ 16 files changed, 876 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-l3.txt create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-sdram.txt create mode 100644 Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt create mode 100644 drivers/misc/fpga-bridge/Kconfig create mode 100644 drivers/misc/fpga-bridge/Makefile create mode 100644 drivers/misc/fpga-bridge/altera-fpga2sdram.c create mode 100644 drivers/misc/fpga-bridge/altera-hps2fpga.c create mode 100644 drivers/misc/fpga-bridge/fpga-bridge.c create mode 100644 drivers/misc/fpga-bridge/fpga-bridge.h diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-l3.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-l3.txt new file mode 100644 index 0000000000000..c99094a3e6467 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-l3.txt @@ -0,0 +1,14 @@ +Altera SOCFPGA L3 Interconnect (NIC-301) + +Required properties: +- compatible : "altr,l3regs", "syscon"; + Note that syscon is invoked for this device to support the FPGA + bridge driver and possibly other devices in the future. See + also Documentation/devicetree/bindings/mfd/syscon.txt +- reg : Should contain 1 register ranges(address and length) + +Example: + l3regs@0xff800000 { + compatible = "altr,l3regs", "syscon"; + reg = <0xff800000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-sdram.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-sdram.txt new file mode 100644 index 0000000000000..351ab7bf423c7 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-sdram.txt @@ -0,0 +1,14 @@ +Altera SOCFPGA SDRAM Controller + +Required properties: +- compatible : "altr,sdr-ctl", "syscon"; + Note that syscon is invoked for this device to support the FPGA + bridge driver and possibly other devices in the future. See + also Documentation/devicetree/bindings/mfd/syscon.txt +- reg : Should contain 1 register ranges(address and length) + +Example: + sdrctl@0xffc25000 { + compatible = "altr,sdr-ctl", "syscon"; + reg = <0xffc25000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt index f4d04a0672824..a17c582888255 100644 --- a/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-system.txt @@ -1,13 +1,16 @@ Altera SOCFPGA System Manager Required properties: -- compatible : "altr,sys-mgr" +- compatible : "altr,sys-mgr", "syscon"; + - reg : Should contain 1 register ranges(address and length) + Note that syscon is invoked for this device. See + also Documentation/devicetree/bindings/mfd/syscon.txt - cpu1-start-addr : CPU1 start address in hex. Example: sysmgr@ffd08000 { - compatible = "altr,sys-mgr"; + compatible = "altr,sys-mgr", "syscon"; reg = <0xffd08000 0x1000>; cpu1-start-addr = <0xffd080c4>; }; diff --git a/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt b/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt new file mode 100644 index 0000000000000..60960a9b1f491 --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt @@ -0,0 +1,75 @@ +Altera FPGA Bridge Driver + +Required properties: + + - compatible : should be "," + "altr,socfpga-fpga2sdram-bridge" + "altr,socfpga-hps2fpga-bridge" + "altr,socfpga-lwhps2fpga-bridge" + "altr,socfpga-fpga2hps-bridge" + +Required for fpga2sdram bridge only: + - read-port : which read port is used for this bridge (0-3) + - write-port : which write port is used for this bridge (0-3) + - control-ports : which conrtol port(s) are used for this bridge. + index 0 is first port # (0-5) + index 1 is number of ports used (1-2) + +Optional properties: + - label : name that you want this bridge to show up as under /sys + Default is br if this is not specified + + - enable : If 'enable' is specified, the bridge will be enabled or + disabled upon load, depending on the value of this parameter. + if 'enable' is not specified, the driver will not enable + or disable the bridge by default. + +Example: + hps_fpgabridge0: fpgabridge@0 { + compatible = "altr,socfpga-hps2fpga-bridge"; + label = "hps2fpga"; + enable = <1>; + }; + + hps_fpgabridge1: fpgabridge@1 { + compatible = "altr,socfpga-lwhps2fpga-bridge"; + label = "lwhps2fpga"; + }; + + hps_fpgabridge2: fpgabridge@2 { + compatible = "altr,socfpga-fpga2hps-bridge"; + label = "fpga2hps"; + }; + + hps_fpgabridge3: fpgabridge@3 { + compatible = "altr,socfpga-fpga2sdram-bridge"; + label = "fpga2sdram0"; + read-port = <0>; + write-port = <0>; + control-ports = <0 2>; /* ports 0 and 1 */ + enable = <1>; + }; + + hps_fpgabridge4: fpgabridge@4 { + compatible = "altr,socfpga-fpga2sdram-bridge"; + label = "fpga2sdram1"; + read-port = <1>; + write-port = <1>; + control-ports = <2 1>; /* port 2 only */ + }; + + hps_fpgabridge5: fpgabridge@5 { + compatible = "altr,socfpga-fpga2sdram-bridge"; + label = "fpga2sdram2"; + read-port = <2>; + write-port = <2>; + control-ports = <3 1>; /* port 3 only */ + }; + + hps_fpgabridge6: fpgabridge@6 { + compatible = "altr,socfpga-fpga2sdram-bridge"; + label = "fpga2sdram3"; + read-port = <3>; + write-port = <3>; + control-ports = <4 2>; /* ports 4 and 5 */ + }; diff --git a/Documentation/devicetree/bindings/reset/socfpga-reset.txt b/Documentation/devicetree/bindings/reset/socfpga-reset.txt index 32c1c8bfd5dc5..c338638ee9130 100644 --- a/Documentation/devicetree/bindings/reset/socfpga-reset.txt +++ b/Documentation/devicetree/bindings/reset/socfpga-reset.txt @@ -1,13 +1,16 @@ Altera SOCFPGA Reset Manager Required properties: -- compatible : "altr,rst-mgr" +- compatible : "altr,rst-mgr", "syscon"; + Note that syscon is invoked for this device to support the FPGA + bridge driver and possibly other devices in the future. See + also Documentation/devicetree/bindings/mfd/syscon.txt - reg : Should contain 1 register ranges(address and length) - #reset-cells: 1 Example: rstmgr@ffd05000 { #reset-cells = <1>; - compatible = "altr,rst-mgr"; + compatible = "altr,rst-mgr", "syscon"; reg = <0xffd05000 0x1000>; }; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 6be0dcc03a222..8830ba3d3f047 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -503,6 +503,29 @@ status = "disabled"; }; + hps_0_fpgamgr: fpgamgr@0xff706000 { + compatible = "altr,fpga-mgr-1.0", "altr,fpga-mgr"; + transport = "mmio"; + reg = <0xFF706000 0x1000 + 0xFFB90000 0x1000>; + interrupts = <0 175 4>; + }; + + hps_fpgabridge0: fpgabridge@0 { + compatible = "altr,socfpga-hps2fpga-bridge"; + label = "hps2fpga"; + }; + + hps_fpgabridge1: fpgabridge@1 { + compatible = "altr,socfpga-lwhps2fpga-bridge"; + label = "lwhps2fpga"; + }; + + hps_fpgabridge2: fpgabridge@2 { + compatible = "altr,socfpga-fpga2hps-bridge"; + label = "fpga2hps"; + }; + i2c0: i2c@ffc04000 { #address-cells = <1>; #size-cells = <0>; @@ -682,6 +705,16 @@ }; }; + sdrctl@0xffc25000 { + compatible = "altr,sdr-ctl", "syscon"; + reg = <0xffc25000 0x1000>; + }; + + l3regs@0xff800000 { + compatible = "altr,l3regs", "syscon"; + reg = <0xff800000 0x1000>; + }; + spi0: spi@fff00000 { compatible = "snps,dw-spi-mmio"; #address-cells = <1>; @@ -783,7 +816,7 @@ }; rst: rstmgr@ffd05000 { - compatible = "altr,rst-mgr"; + compatible = "altr,rst-mgr", "syscon"; reg = <0xffd05000 0x1000>; }; @@ -835,13 +868,5 @@ compatible = "altr,sys-mgr", "syscon"; reg = <0xffd08000 0x4000>; }; - - hps_0_fpgamgr: fpgamgr@0xff706000 { - compatible = "altr,fpga-mgr-1.0", "altr,fpga-mgr"; - transport = "mmio"; - reg = <0xFF706000 0x1000 - 0xFFB90000 0x1000>; - interrupts = <0 175 4>; - }; }; }; diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 153f6481c4e0e..5ef33de5075df 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -108,3 +108,5 @@ CONFIG_MMC=y CONFIG_MMC_DW=y CONFIG_FPGA=y CONFIG_FPGA_MGR_ALTERA=y +CONFIG_FPGA_BRIDGE=y +CONFIG_ALTERA_SOCFPGA_BRIDGE=y diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 2206140f9a5c8..27179d0f54e4c 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -34,6 +34,8 @@ void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE)); void __iomem *sys_manager_base_addr; void __iomem *rst_manager_base_addr; unsigned long cpu1start_addr; +void __iomem *sdr_ctl_base_addr; +void __iomem *l3regs_base_addr; #ifdef CONFIG_HW_PERF_EVENTS static struct arm_pmu_platdata socfpga_pmu_platdata = { @@ -160,6 +162,25 @@ void __init socfpga_sysmgr_init(void) np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr"); rst_manager_base_addr = of_iomap(np, 0); + WARN_ON(!rst_manager_base_addr); + + np = of_find_compatible_node(NULL, NULL, "altr,sdr-ctl"); + if (!np) { + pr_err("SOCFPGA: Unable to find sdr-ctl\n"); + return; + } + + sdr_ctl_base_addr = of_iomap(np, 0); + WARN_ON(!sdr_ctl_base_addr); + + np = of_find_compatible_node(NULL, NULL, "altr,l3regs"); + if (!np) { + pr_err("SOCFPGA: Unable to find l3regs\n"); + return; + } + + l3regs_base_addr = of_iomap(np, 0); + WARN_ON(!l3regs_base_addr); } static void __init socfpga_init_irq(void) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ee9402324a23a..e6b11a45a6bba 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -537,4 +537,6 @@ source "drivers/misc/vmw_vmci/Kconfig" source "drivers/misc/mic/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" +source "drivers/misc/fpga-bridge/Kconfig" + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d59ce1261b384..ce4b042a84374 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_HMC6352) += hmc6352.o obj-y += eeprom/ obj-y += cb710/ +obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge/ obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o diff --git a/drivers/misc/fpga-bridge/Kconfig b/drivers/misc/fpga-bridge/Kconfig new file mode 100644 index 0000000000000..725474e9436e7 --- /dev/null +++ b/drivers/misc/fpga-bridge/Kconfig @@ -0,0 +1,20 @@ +# +# FPGA bridge manager configuration +# + +menu "FPGA Bridges" + +config FPGA_BRIDGE + tristate "FPGA Bridge Drivers" + depends on OF + help + Say Y here if you want to support bridges connected between host + processors and FPGAs or between FPGAs. + +config ALTERA_SOCFPGA_BRIDGE + tristate "Altera SoCFPGA Bridges" + depends on FPGA_BRIDGE + help + Say Y to enable drivers for FPGA bridges for Altera socfpga + devices. +endmenu diff --git a/drivers/misc/fpga-bridge/Makefile b/drivers/misc/fpga-bridge/Makefile new file mode 100644 index 0000000000000..3700f8a977d43 --- /dev/null +++ b/drivers/misc/fpga-bridge/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the fpga-bridge drivers +# + +obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o +obj-$(CONFIG_ALTERA_SOCFPGA_BRIDGE) += altera-fpga2sdram.o altera-hps2fpga.o \ No newline at end of file diff --git a/drivers/misc/fpga-bridge/altera-fpga2sdram.c b/drivers/misc/fpga-bridge/altera-fpga2sdram.c new file mode 100644 index 0000000000000..413fc09f7c444 --- /dev/null +++ b/drivers/misc/fpga-bridge/altera-fpga2sdram.c @@ -0,0 +1,216 @@ +/* + * FPGA to sdram Bridge Driver for Altera SoCFPGA Devices + * + * Copyright (C) 2013 Altera Corporation, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fpga-bridge.h" + +#define ALT_SDR_CTL_FPGAPORTRST_OFST 0x80 +#define ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK 0x00003fff +#define ALT_SDR_CTL_FPGAPORTRST_RD_SHIFT 0 +#define ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT 4 +#define ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT 8 + +static struct of_device_id altera_fpga_of_match[]; + +struct alt_fpga2sdram_data { + char name[48]; + struct platform_device *pdev; + struct device_node *np; + struct regmap *sdrctl; + int mask; +}; + +static int alt_fpga2sdram_enable_show(struct fpga_bridge *bridge) +{ + struct alt_fpga2sdram_data *priv = bridge->priv; + int value; + + regmap_read(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST, &value); + + return ((value & priv->mask) != 0); +} + +static void alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable) +{ + struct alt_fpga2sdram_data *priv = bridge->priv; + int value; + + if (enable) + value = priv->mask; + else + value = 0; + + regmap_update_bits(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST, + priv->mask, value); +} + +static int alt_fpga2sdram_get_mask(struct alt_fpga2sdram_data *priv) +{ + struct device_node *np = priv->np; + int mask, ctrl_shift, ctrl_mask; + u32 read, write, control[2]; + + if (of_property_read_u32(np, "read-port", &read)) { + dev_err(&priv->pdev->dev, + "read-port property missing\n"); + return -EINVAL; + } + if ((read < 0) || (read > 3)) { + dev_err(&priv->pdev->dev, + "read-port property out of bounds\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "write-port", &write)) { + dev_err(&priv->pdev->dev, + "write-port property invalid or missing\n"); + return -EINVAL; + } + if ((write < 0) || (write > 3)) { + dev_err(&priv->pdev->dev, + "write-port property out of bounds\n"); + return -EINVAL; + } + + /* There can be 1 or 2 control ports specified */ + if (of_property_read_u32_array(np, "control-ports", control, + ARRAY_SIZE(control))) { + dev_err(&priv->pdev->dev, + "control-ports property missing\n"); + return -EINVAL; + } + if ((control[0] < 0) || (control[0] > 5) || + (control[1] < 1) || (control[1] > 2)) { + dev_err(&priv->pdev->dev, + "control-ports property out of bounds\n"); + return -EINVAL; + } + + ctrl_shift = ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT + control[0]; + + if (control[1] == 1) + ctrl_mask = 0x1; + else + ctrl_mask = 0x3; + + mask = (1 << (ALT_SDR_CTL_FPGAPORTRST_RD_SHIFT + read)) | + (1 << (ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT + write)) | + (ctrl_mask << ctrl_shift); + + WARN_ON((mask & ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK) != mask); + priv->mask = mask; + + return 0; +} + +struct fpga_bridge_ops altera_fpga2sdram_br_ops = { + .enable_set = alt_fpga2sdram_enable_set, + .enable_show = alt_fpga2sdram_enable_show, +}; + +static struct alt_fpga2sdram_data fpga2sdram_data = { + .name = "fpga2sdram", +}; + +static int alt_fpga_bridge_probe(struct platform_device *pdev) +{ + struct alt_fpga2sdram_data *priv; + struct alt_fpga2sdram_data *data; + const struct of_device_id *of_id = of_match_device(altera_fpga_of_match, + &pdev->dev); + int ret = 0; + + data = (struct alt_fpga2sdram_data *)of_id->data; + WARN_ON(!data); + + priv = devm_kzalloc(&pdev->dev, sizeof(struct alt_fpga2sdram_data), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->np = pdev->dev.of_node; + priv->pdev = pdev; + priv->mask = data->mask; + strncpy(priv->name, data->name, ARRAY_SIZE(priv->name)); + + priv->sdrctl = syscon_regmap_lookup_by_compatible("altr,sdr-ctl"); + if (IS_ERR(priv->sdrctl)) { + devm_kfree(&pdev->dev, priv); + dev_err(&priv->pdev->dev, + "regmap for altr,sdr-ctl lookup failed.\n"); + return PTR_ERR(priv->sdrctl); + } + + ret = alt_fpga2sdram_get_mask(priv); + if (ret) { + devm_kfree(&pdev->dev, priv); + return ret; + } + + return register_fpga_bridge(pdev, &altera_fpga2sdram_br_ops, + priv->name, priv); +} + +static int alt_fpga_bridge_remove(struct platform_device *pdev) +{ + remove_fpga_bridge(pdev); + return 0; +} + +static struct of_device_id altera_fpga_of_match[] = { + { .compatible = "altr,socfpga-fpga2sdram-bridge", .data = &fpga2sdram_data }, + {}, +}; + +MODULE_DEVICE_TABLE(of, altera_fpga_of_match); + +static struct platform_driver altera_fpga_driver = { + .remove = alt_fpga_bridge_remove, + .driver = { + .name = "altera_fpga2sdram_bridge", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_fpga_of_match), + }, +}; + +static int __init alt_fpga_bridge_init(void) +{ + return platform_driver_probe(&altera_fpga_driver, + alt_fpga_bridge_probe); +} + +static void __exit alt_fpga_bridge_exit(void) +{ + platform_driver_unregister(&altera_fpga_driver); +} + +module_init(alt_fpga_bridge_init); +module_exit(alt_fpga_bridge_exit); + +MODULE_DESCRIPTION("Altera SoCFPGA FPGA to SDRAM Bridge"); +MODULE_AUTHOR("Alan Tull "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/fpga-bridge/altera-hps2fpga.c b/drivers/misc/fpga-bridge/altera-hps2fpga.c new file mode 100644 index 0000000000000..76f25422e2875 --- /dev/null +++ b/drivers/misc/fpga-bridge/altera-hps2fpga.c @@ -0,0 +1,178 @@ +/* + * FPGA to/from HPS Bridge Driver for Altera SoCFPGA Devices + * + * Copyright (C) 2013 Altera Corporation, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fpga-bridge.h" + +#define SOCFPGA_RSTMGR_BRGMODRST 0x1c +#define ALT_RSTMGR_BRGMODRST_H2F_MSK 0x00000001 +#define ALT_RSTMGR_BRGMODRST_LWH2F_MSK 0x00000002 +#define ALT_RSTMGR_BRGMODRST_F2H_MSK 0x00000004 + +#define ALT_L3_REMAP_OFST 0x0 +#define ALT_L3_REMAP_H2F_MSK 0x00000008 +#define ALT_L3_REMAP_LWH2F_MSK 0x00000010 + +static struct of_device_id altera_fpga_of_match[]; + +struct altera_hps2fpga_data { + char name[48]; + struct platform_device *pdev; + struct device_node *np; + struct regmap *rstreg; + struct regmap *l3reg; + int mask; + int remap_mask; +}; + +static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge) +{ + struct altera_hps2fpga_data *priv = bridge->priv; + unsigned int value; + + regmap_read(priv->rstreg, SOCFPGA_RSTMGR_BRGMODRST, &value); + + return ((value & priv->mask) == 0); +} + +static void alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable) +{ + struct altera_hps2fpga_data *priv = bridge->priv; + int value; + + if (enable) + value = 0; + else + value = priv->mask; + + regmap_update_bits(priv->rstreg, SOCFPGA_RSTMGR_BRGMODRST, + priv->mask, value); + + /* Allow bridge to be visible to L3 masters or not */ + if (priv->remap_mask) { + if (enable) + value = 0; + else + value = priv->remap_mask; + + regmap_update_bits(priv->l3reg, ALT_L3_REMAP_OFST, + priv->remap_mask, value); + } +} + +struct fpga_bridge_ops altera_hps2fpga_br_ops = { + .enable_set = alt_hps2fpga_enable_set, + .enable_show = alt_hps2fpga_enable_show, +}; + +static struct altera_hps2fpga_data hps2fpga_data = { + .name = "hps2fpga", + .mask = ALT_RSTMGR_BRGMODRST_H2F_MSK, + .remap_mask = ALT_L3_REMAP_H2F_MSK, +}; + +static struct altera_hps2fpga_data lwhps2fpga_data = { + .name = "lshps2fpga", + .mask = ALT_RSTMGR_BRGMODRST_LWH2F_MSK, + .remap_mask = ALT_L3_REMAP_LWH2F_MSK, +}; + +static struct altera_hps2fpga_data fpga2hps_data = { + .name = "fpga2hps", + .mask = ALT_RSTMGR_BRGMODRST_F2H_MSK, +}; + +static int alt_fpga_bridge_probe(struct platform_device *pdev) +{ + struct altera_hps2fpga_data *priv; + const struct of_device_id *of_id; + + of_id = of_match_device(altera_fpga_of_match, &pdev->dev); + priv = (struct altera_hps2fpga_data *)of_id->data; + WARN_ON(!priv); + + priv->np = pdev->dev.of_node; + priv->pdev = pdev; + + priv->rstreg = syscon_regmap_lookup_by_compatible("altr,rst-mgr"); + if (IS_ERR(priv->rstreg)) { + dev_err(&priv->pdev->dev, + "regmap for altr,rst-mgr lookup failed.\n"); + return PTR_ERR(priv->rstreg); + } + + priv->l3reg = syscon_regmap_lookup_by_compatible("altr,l3regs"); + if (IS_ERR(priv->l3reg)) { + dev_err(&priv->pdev->dev, + "regmap for altr,l3regs lookup failed.\n"); + return PTR_ERR(priv->l3reg); + } + + return register_fpga_bridge(pdev, &altera_hps2fpga_br_ops, + priv->name, priv); +} + +static int alt_fpga_bridge_remove(struct platform_device *pdev) +{ + remove_fpga_bridge(pdev); + return 0; +} + +static struct of_device_id altera_fpga_of_match[] = { + { .compatible = "altr,socfpga-hps2fpga-bridge", .data = &hps2fpga_data }, + { .compatible = "altr,socfpga-lwhps2fpga-bridge", .data = &lwhps2fpga_data }, + { .compatible = "altr,socfpga-fpga2hps-bridge", .data = &fpga2hps_data }, + {}, +}; + +MODULE_DEVICE_TABLE(of, altera_fpga_of_match); + +static struct platform_driver altera_fpga_driver = { + .remove = alt_fpga_bridge_remove, + .driver = { + .name = "altera_hps2fpga_bridge", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_fpga_of_match), + }, +}; + +static int __init alt_fpga_bridge_init(void) +{ + return platform_driver_probe(&altera_fpga_driver, + alt_fpga_bridge_probe); +} + +static void __exit alt_fpga_bridge_exit(void) +{ + platform_driver_unregister(&altera_fpga_driver); +} + +module_init(alt_fpga_bridge_init); +module_exit(alt_fpga_bridge_exit); + +MODULE_DESCRIPTION("Altera SoCFPGA HPS to FPGA Bridge"); +MODULE_AUTHOR("Alan Tull "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/fpga-bridge/fpga-bridge.c b/drivers/misc/fpga-bridge/fpga-bridge.c new file mode 100644 index 0000000000000..a8fd7a1b2a0a0 --- /dev/null +++ b/drivers/misc/fpga-bridge/fpga-bridge.c @@ -0,0 +1,232 @@ +/* + * fpga bridge driver + * + * Copyright (C) 2013 Altera Corporation, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ +#include +#include +#include +#include +#include +#include +#include "fpga-bridge.h" + +static DEFINE_IDA(fpga_bridge_ida); +static struct class *fpga_bridge_class; + +#define FPGA_MAX_DEVICES 256 + +/* + * class attributes + */ +static ssize_t fpga_bridge_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fpga_bridge *bridge = dev_get_drvdata(dev); + int enabled; + + enabled = bridge->br_ops->enable_show(bridge); + + return sprintf(buf, "%d\n", enabled); +} + +static ssize_t fpga_bridge_enable_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpga_bridge *bridge = dev_get_drvdata(dev); + bool enable; + + if ((count != 1) && (count != 2)) + return -EINVAL; + + if ((count == 2) && (buf[1] != '\n')) + return -EINVAL; + + if ((buf[0] != '0') && (buf[0] != '1')) + return -EINVAL; + + enable = (buf[0] == '1'); + bridge->br_ops->enable_set(bridge, enable); + + return count; +} + +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, fpga_bridge_enable_show, + fpga_bridge_enable_set); + +static struct attribute *fpga_bridge_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static const struct attribute_group fpga_bridge_group = { + .attrs = fpga_bridge_attrs, +}; + +const struct attribute_group *fpga_bridge_groups[] = { + &fpga_bridge_group, + NULL, +}; + +static int fpga_bridge_alloc_id(struct fpga_bridge *bridge, int request_nr) +{ + int nr, start; + + /* check specified minor number */ + if (request_nr >= FPGA_MAX_DEVICES) { + dev_err(bridge->parent, + "Out of device ids (%d)\n", request_nr); + return -ENODEV; + } + + /* + * If request_nr == -1, dynamically allocate number. + * If request_nr >= 0, attempt to get specific number. + */ + if (request_nr == -1) + start = 0; + else + start = request_nr; + + nr = ida_simple_get(&fpga_bridge_ida, start, FPGA_MAX_DEVICES, + GFP_KERNEL); + + /* return error code */ + if (nr < 0) + return nr; + + if ((request_nr != -1) && (request_nr != nr)) { + dev_err(bridge->parent, + "Could not get requested device number (%d)\n", nr); + ida_simple_remove(&fpga_bridge_ida, nr); + return -ENODEV; + } + + bridge->nr = nr; + + return 0; +} + +static void fpga_bridge_free_id(int nr) +{ + ida_simple_remove(&fpga_bridge_ida, nr); +} + +int register_fpga_bridge(struct platform_device *pdev, + struct fpga_bridge_ops *br_ops, char *name, void *priv) +{ + struct fpga_bridge *bridge; + const char *dt_label; + int enable, ret; + + if (!br_ops || !br_ops->enable_set || !br_ops->enable_show) { + dev_err(&pdev->dev, + "Attempt to register without fpga_bridge_ops\n"); + return -EINVAL; + } + if (!name || (name[0] == '\0')) { + dev_err(&pdev->dev, "Attempt to register with no name!\n"); + return -EINVAL; + } + + bridge = kzalloc(sizeof(struct fpga_bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + platform_set_drvdata(pdev, bridge); + bridge->br_ops = br_ops; + bridge->np = pdev->dev.of_node; + bridge->parent = get_device(&pdev->dev); + bridge->priv = priv; + + strlcpy(bridge->name, name, sizeof(bridge->name)); + + ret = fpga_bridge_alloc_id(bridge, pdev->id); + if (ret) + goto error_kfree; + + dt_label = of_get_property(bridge->np, "label", NULL); + if (dt_label) + snprintf(bridge->label, sizeof(bridge->label), "%s", dt_label); + else + snprintf(bridge->label, sizeof(bridge->label), + "br%d", bridge->nr); + + bridge->dev = device_create(fpga_bridge_class, bridge->parent, 0, + bridge, bridge->label); + if (IS_ERR(bridge->dev)) { + ret = PTR_ERR(bridge->dev); + goto error_device; + } + + if (!of_property_read_u32(bridge->np, "enable", &enable)) + br_ops->enable_set(bridge, enable); + + dev_info(bridge->parent, "fpga bridge [%s] registered as device %s\n", + bridge->name, bridge->label); + + return 0; + +error_device: + fpga_bridge_free_id(bridge->nr); +error_kfree: + put_device(bridge->parent); + kfree(bridge); + return ret; +} +EXPORT_SYMBOL_GPL(register_fpga_bridge); + +void remove_fpga_bridge(struct platform_device *pdev) +{ + struct fpga_bridge *bridge = platform_get_drvdata(pdev); + + if (bridge && bridge->br_ops && bridge->br_ops->fpga_bridge_remove) + bridge->br_ops->fpga_bridge_remove(bridge); + + platform_set_drvdata(pdev, NULL); + device_unregister(bridge->dev); + fpga_bridge_free_id(bridge->nr); + put_device(bridge->parent); + kfree(bridge); +} +EXPORT_SYMBOL_GPL(remove_fpga_bridge); + +static int __init fpga_bridge_dev_init(void) +{ + pr_info("fpga bridge driver\n"); + + fpga_bridge_class = class_create(THIS_MODULE, "fpga-bridge"); + if (IS_ERR(fpga_bridge_class)) + return PTR_ERR(fpga_bridge_class); + + fpga_bridge_class->dev_groups = fpga_bridge_groups; + + return 0; +} + +static void __exit fpga_bridge_dev_exit(void) +{ + class_destroy(fpga_bridge_class); + ida_destroy(&fpga_bridge_ida); +} + +MODULE_DESCRIPTION("FPGA Bridge Driver"); +MODULE_AUTHOR("Alan Tull "); +MODULE_LICENSE("GPL v2"); + +subsys_initcall(fpga_bridge_dev_init); +module_exit(fpga_bridge_dev_exit); diff --git a/drivers/misc/fpga-bridge/fpga-bridge.h b/drivers/misc/fpga-bridge/fpga-bridge.h new file mode 100644 index 0000000000000..58fba4d452c18 --- /dev/null +++ b/drivers/misc/fpga-bridge/fpga-bridge.h @@ -0,0 +1,51 @@ +#include +#include +#include + +#ifndef _LINUX_FPGA_BRIDGE_H +#define _LINUX_FPGA_BRIDGE_H + +struct fpga_bridge; + +/*---------------------------------------------------------------------------*/ + +/* + * fpga_bridge_ops are the low level functions implemented by a specific + * fpga bridge driver. + */ +struct fpga_bridge_ops { + /* Returns the FPGA bridge's status */ + int (*enable_show)(struct fpga_bridge *bridge); + + /* Enable a FPGA bridge */ + void (*enable_set)(struct fpga_bridge *bridge, bool enable); + + /* Set FPGA into a specific state during driver remove */ + void (*fpga_bridge_remove)(struct fpga_bridge *bridge); +}; + +struct fpga_bridge { + struct device_node *np; + struct device *parent; + struct device *dev; + struct cdev cdev; + + int nr; + char name[48]; + char label[48]; + unsigned long flags; + struct fpga_bridge_ops *br_ops; + + void *priv; +}; + +#if defined(CONFIG_FPGA_BRIDGE) || defined(CONFIG_FPGA_BRIDGE_MODULE) + +extern int register_fpga_bridge(struct platform_device *pdev, + struct fpga_bridge_ops *br_ops, + char *name, void *priv); + +extern void remove_fpga_bridge(struct platform_device *pdev); + +#endif /* CONFIG_FPGA_BRIDGE */ +#endif /* _LINUX_FPGA_BRIDGE_H */ From 428d6224dee1e81107ea9fd91bcb6c3d67e3cda4 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Tue, 16 Jul 2013 15:18:58 -0500 Subject: [PATCH 080/201] FogBugz #102675: do not touch fpga bridge resets by default Previously we were bringing all FPGA-hps bridges out of reset without checking to see if the FPGA was programmed. This patch changes the behavior to not bring the bridges out of reset or to set them to be in reset. Signed-off-by: Alan Tull Conflicts: arch/arm/mach-socfpga/socfpga.c --- arch/arm/mach-socfpga/socfpga.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 27179d0f54e4c..8c7bbac6b78d5 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -136,9 +136,6 @@ static void __init enable_periphs(void) { /* Release all peripherals from reset.*/ __raw_writel(0, rst_manager_base_addr + SOCFPGA_RSTMGR_MODPERRST); - - /* Release all FPGA bridges from reset.*/ - __raw_writel(0, rst_manager_base_addr + SOCFPGA_RSTMGR_BRGMODRST); } static void __init socfpga_map_io(void) From 8bebd539d787ca372c3bce740c40c7a25394b844 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Tue, 30 Jul 2013 14:07:53 -0500 Subject: [PATCH 081/201] FogBugz #142060: do not clear the mpuzero bit The remap register is write-only (!) and reads zeros. So doing a 'read, modify, write' operation on that register will clear the mpuzero bit that was set by u-boot. Signed-off-by: Alan Tull --- drivers/misc/fpga-bridge/altera-hps2fpga.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/misc/fpga-bridge/altera-hps2fpga.c b/drivers/misc/fpga-bridge/altera-hps2fpga.c index 76f25422e2875..13af155960fc5 100644 --- a/drivers/misc/fpga-bridge/altera-hps2fpga.c +++ b/drivers/misc/fpga-bridge/altera-hps2fpga.c @@ -33,6 +33,7 @@ #define ALT_RSTMGR_BRGMODRST_F2H_MSK 0x00000004 #define ALT_L3_REMAP_OFST 0x0 +#define ALT_L3_REMAP_MPUZERO_MSK 0x00000001 #define ALT_L3_REMAP_H2F_MSK 0x00000008 #define ALT_L3_REMAP_LWH2F_MSK 0x00000010 @@ -63,6 +64,7 @@ static void alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable) struct altera_hps2fpga_data *priv = bridge->priv; int value; + /* bring bridge out of reset */ if (enable) value = 0; else @@ -73,13 +75,12 @@ static void alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable) /* Allow bridge to be visible to L3 masters or not */ if (priv->remap_mask) { + value = ALT_L3_REMAP_MPUZERO_MSK; + if (enable) - value = 0; - else - value = priv->remap_mask; + value |= priv->remap_mask; - regmap_update_bits(priv->l3reg, ALT_L3_REMAP_OFST, - priv->remap_mask, value); + regmap_write(priv->l3reg, ALT_L3_REMAP_OFST, value); } } From 1c62cfac8673859dc9671002c3d9b98438a200f9 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Wed, 31 Jul 2013 09:35:54 -0500 Subject: [PATCH 082/201] FogBugz #142126: l3 remap register needs clocks and caching * Add clock so that we can reliably write to L3 remap register. * The L3 remap register is write only. So keep a cached copy of what we write to it. * Remove confusing 'enable' device tree property. This was intended for future support of dynamic device trees and is causing more trouble than is worth in the present moment. Signed-off-by: Alan Tull V2: * Removed 'enable' device tree property. --- .../bindings/fpga/altera-fpga-bridge.txt | 13 +++--- arch/arm/boot/dts/socfpga.dtsi | 3 ++ drivers/misc/fpga-bridge/altera-hps2fpga.c | 44 ++++++++++++++----- drivers/misc/fpga-bridge/fpga-bridge.c | 5 +-- 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt b/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt index 60960a9b1f491..569d89854eed8 100644 --- a/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt +++ b/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt @@ -15,30 +15,30 @@ Required for fpga2sdram bridge only: index 0 is first port # (0-5) index 1 is number of ports used (1-2) +Required for hps2fpga, lwhps2fpga, and fpga2hps: + - clocks : clocks used by this module + Optional properties: - label : name that you want this bridge to show up as under /sys Default is br if this is not specified - - enable : If 'enable' is specified, the bridge will be enabled or - disabled upon load, depending on the value of this parameter. - if 'enable' is not specified, the driver will not enable - or disable the bridge by default. - Example: hps_fpgabridge0: fpgabridge@0 { compatible = "altr,socfpga-hps2fpga-bridge"; label = "hps2fpga"; - enable = <1>; + clocks = <&l4_main_clk>; }; hps_fpgabridge1: fpgabridge@1 { compatible = "altr,socfpga-lwhps2fpga-bridge"; label = "lwhps2fpga"; + clocks = <&l4_main_clk>; }; hps_fpgabridge2: fpgabridge@2 { compatible = "altr,socfpga-fpga2hps-bridge"; label = "fpga2hps"; + clocks = <&l4_main_clk>; }; hps_fpgabridge3: fpgabridge@3 { @@ -47,7 +47,6 @@ Example: read-port = <0>; write-port = <0>; control-ports = <0 2>; /* ports 0 and 1 */ - enable = <1>; }; hps_fpgabridge4: fpgabridge@4 { diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 8830ba3d3f047..857748686e206 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -514,16 +514,19 @@ hps_fpgabridge0: fpgabridge@0 { compatible = "altr,socfpga-hps2fpga-bridge"; label = "hps2fpga"; + clocks = <&l4_main_clk>; }; hps_fpgabridge1: fpgabridge@1 { compatible = "altr,socfpga-lwhps2fpga-bridge"; label = "lwhps2fpga"; + clocks = <&l4_main_clk>; }; hps_fpgabridge2: fpgabridge@2 { compatible = "altr,socfpga-fpga2hps-bridge"; label = "fpga2hps"; + clocks = <&l4_main_clk>; }; i2c0: i2c@ffc04000 { diff --git a/drivers/misc/fpga-bridge/altera-hps2fpga.c b/drivers/misc/fpga-bridge/altera-hps2fpga.c index 13af155960fc5..4e5dd38cfa4ce 100644 --- a/drivers/misc/fpga-bridge/altera-hps2fpga.c +++ b/drivers/misc/fpga-bridge/altera-hps2fpga.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -39,14 +40,17 @@ static struct of_device_id altera_fpga_of_match[]; +/* The L3 REMAP register is write only, so keep a cached value. */ +static unsigned int l3_remap_value; + struct altera_hps2fpga_data { char name[48]; struct platform_device *pdev; struct device_node *np; struct regmap *rstreg; struct regmap *l3reg; - int mask; - int remap_mask; + unsigned int reset_mask; + unsigned int remap_mask; }; static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge) @@ -56,31 +60,33 @@ static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge) regmap_read(priv->rstreg, SOCFPGA_RSTMGR_BRGMODRST, &value); - return ((value & priv->mask) == 0); + return ((value & priv->reset_mask) == 0); } static void alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable) { struct altera_hps2fpga_data *priv = bridge->priv; - int value; + unsigned int value; /* bring bridge out of reset */ if (enable) value = 0; else - value = priv->mask; + value = priv->reset_mask; regmap_update_bits(priv->rstreg, SOCFPGA_RSTMGR_BRGMODRST, - priv->mask, value); + priv->reset_mask, value); /* Allow bridge to be visible to L3 masters or not */ if (priv->remap_mask) { - value = ALT_L3_REMAP_MPUZERO_MSK; + l3_remap_value |= ALT_L3_REMAP_MPUZERO_MSK; if (enable) - value |= priv->remap_mask; + l3_remap_value |= priv->remap_mask; + else + l3_remap_value &= ~priv->remap_mask; - regmap_write(priv->l3reg, ALT_L3_REMAP_OFST, value); + regmap_write(priv->l3reg, ALT_L3_REMAP_OFST, l3_remap_value); } } @@ -91,25 +97,27 @@ struct fpga_bridge_ops altera_hps2fpga_br_ops = { static struct altera_hps2fpga_data hps2fpga_data = { .name = "hps2fpga", - .mask = ALT_RSTMGR_BRGMODRST_H2F_MSK, + .reset_mask = ALT_RSTMGR_BRGMODRST_H2F_MSK, .remap_mask = ALT_L3_REMAP_H2F_MSK, }; static struct altera_hps2fpga_data lwhps2fpga_data = { .name = "lshps2fpga", - .mask = ALT_RSTMGR_BRGMODRST_LWH2F_MSK, + .reset_mask = ALT_RSTMGR_BRGMODRST_LWH2F_MSK, .remap_mask = ALT_L3_REMAP_LWH2F_MSK, }; static struct altera_hps2fpga_data fpga2hps_data = { .name = "fpga2hps", - .mask = ALT_RSTMGR_BRGMODRST_F2H_MSK, + .reset_mask = ALT_RSTMGR_BRGMODRST_F2H_MSK, }; static int alt_fpga_bridge_probe(struct platform_device *pdev) { struct altera_hps2fpga_data *priv; const struct of_device_id *of_id; + int rc; + struct clk *clk; of_id = of_match_device(altera_fpga_of_match, &pdev->dev); priv = (struct altera_hps2fpga_data *)of_id->data; @@ -132,6 +140,18 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) return PTR_ERR(priv->l3reg); } + clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock specified\n"); + return PTR_ERR(clk); + } + + rc = clk_prepare_enable(clk); + if (rc) { + dev_err(&pdev->dev, "could not enable clock\n"); + return -EBUSY; + } + return register_fpga_bridge(pdev, &altera_hps2fpga_br_ops, priv->name, priv); } diff --git a/drivers/misc/fpga-bridge/fpga-bridge.c b/drivers/misc/fpga-bridge/fpga-bridge.c index a8fd7a1b2a0a0..96bf4e402087e 100644 --- a/drivers/misc/fpga-bridge/fpga-bridge.c +++ b/drivers/misc/fpga-bridge/fpga-bridge.c @@ -131,7 +131,7 @@ int register_fpga_bridge(struct platform_device *pdev, { struct fpga_bridge *bridge; const char *dt_label; - int enable, ret; + int ret; if (!br_ops || !br_ops->enable_set || !br_ops->enable_show) { dev_err(&pdev->dev, @@ -173,9 +173,6 @@ int register_fpga_bridge(struct platform_device *pdev, goto error_device; } - if (!of_property_read_u32(bridge->np, "enable", &enable)) - br_ops->enable_set(bridge, enable); - dev_info(bridge->parent, "fpga bridge [%s] registered as device %s\n", bridge->name, bridge->label); From b965424a9c524c165852b5efef1c805509ed88b0 Mon Sep 17 00:00:00 2001 From: Matthew Gerlach Date: Wed, 23 Oct 2013 14:11:47 -0700 Subject: [PATCH 083/201] FogBugz #163307: align f2sdram driver with hw support The read, write, and command ports on the fpga2sdram bridge can only be reconfigured if there are no transactions to the sdram during the reconfiguration. Currently, this guarantee can only be made by code running out of onchip ram before Linux is started. Therefore, this driver only supports enabling and disabling of the ports that have already been configured. Since the driver cannot determine the port configuration in all cases, the device tree must be populated with masks for all of the ports. Signed-off-by: Matthew Gerlach --- .../bindings/fpga/altera-fpga-bridge.txt | 74 -------------- .../fpga/altera-fpga2sdram-bridge.txt | 47 +++++++++ .../bindings/fpga/altera-hps2fpga-bridge.txt | 39 ++++++++ drivers/misc/fpga-bridge/altera-fpga2sdram.c | 99 +++++++++---------- 4 files changed, 132 insertions(+), 127 deletions(-) delete mode 100644 Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt create mode 100644 Documentation/devicetree/bindings/fpga/altera-fpga2sdram-bridge.txt create mode 100644 Documentation/devicetree/bindings/fpga/altera-hps2fpga-bridge.txt diff --git a/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt b/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt deleted file mode 100644 index 569d89854eed8..0000000000000 --- a/Documentation/devicetree/bindings/fpga/altera-fpga-bridge.txt +++ /dev/null @@ -1,74 +0,0 @@ -Altera FPGA Bridge Driver - -Required properties: - - - compatible : should be "," - "altr,socfpga-fpga2sdram-bridge" - "altr,socfpga-hps2fpga-bridge" - "altr,socfpga-lwhps2fpga-bridge" - "altr,socfpga-fpga2hps-bridge" - -Required for fpga2sdram bridge only: - - read-port : which read port is used for this bridge (0-3) - - write-port : which write port is used for this bridge (0-3) - - control-ports : which conrtol port(s) are used for this bridge. - index 0 is first port # (0-5) - index 1 is number of ports used (1-2) - -Required for hps2fpga, lwhps2fpga, and fpga2hps: - - clocks : clocks used by this module - -Optional properties: - - label : name that you want this bridge to show up as under /sys - Default is br if this is not specified - -Example: - hps_fpgabridge0: fpgabridge@0 { - compatible = "altr,socfpga-hps2fpga-bridge"; - label = "hps2fpga"; - clocks = <&l4_main_clk>; - }; - - hps_fpgabridge1: fpgabridge@1 { - compatible = "altr,socfpga-lwhps2fpga-bridge"; - label = "lwhps2fpga"; - clocks = <&l4_main_clk>; - }; - - hps_fpgabridge2: fpgabridge@2 { - compatible = "altr,socfpga-fpga2hps-bridge"; - label = "fpga2hps"; - clocks = <&l4_main_clk>; - }; - - hps_fpgabridge3: fpgabridge@3 { - compatible = "altr,socfpga-fpga2sdram-bridge"; - label = "fpga2sdram0"; - read-port = <0>; - write-port = <0>; - control-ports = <0 2>; /* ports 0 and 1 */ - }; - - hps_fpgabridge4: fpgabridge@4 { - compatible = "altr,socfpga-fpga2sdram-bridge"; - label = "fpga2sdram1"; - read-port = <1>; - write-port = <1>; - control-ports = <2 1>; /* port 2 only */ - }; - - hps_fpgabridge5: fpgabridge@5 { - compatible = "altr,socfpga-fpga2sdram-bridge"; - label = "fpga2sdram2"; - read-port = <2>; - write-port = <2>; - control-ports = <3 1>; /* port 3 only */ - }; - - hps_fpgabridge6: fpgabridge@6 { - compatible = "altr,socfpga-fpga2sdram-bridge"; - label = "fpga2sdram3"; - read-port = <3>; - write-port = <3>; - control-ports = <4 2>; /* ports 4 and 5 */ - }; diff --git a/Documentation/devicetree/bindings/fpga/altera-fpga2sdram-bridge.txt b/Documentation/devicetree/bindings/fpga/altera-fpga2sdram-bridge.txt new file mode 100644 index 0000000000000..ce7184b00a76f --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/altera-fpga2sdram-bridge.txt @@ -0,0 +1,47 @@ +Altera FPGA To SDRAM Bridge Driver + + This driver manages a bridge between an FPGA and the SDRAM used by an + host processor system (HPS). The bridge contains a number read ports, + write ports, and command ports. Reconfiguring these ports requires + that no SDRAM transactions occur during reconfiguration. In other words, + the code reconfiguring the ports cannot be run out of SDRAM nor can the + FPGA access the SDRAM during the reconfiguration. This driver does not + support reconfiguring the ports. Typcially, the ports are configured by + code running out of onchip ram before Linux is started. + + This driver supports enabling and disabling of the configured ports all + at once, which allows for safe reprogramming of the FPGA from user space, + provided the new FPGA image uses the same port configuration. + User space can enable/disable the bridge by writing a "1" or a "0", + respectively, to its enable file under bridge's entry in + /sys/class/fpga-bridge. Typically, one disables the bridges before + reprogramming the FPGA. Once the FPGA is reprogrammed, the bridges + are reenabled. + +Required properties: + + - compatible : "altr,socfpga-fpga2sdram-bridge" + + - read-ports-mask : Bits 0 to 3 corresponds read ports 0 to 3. A + bit set to 1 indicates the corresponding read port should be enabled. + + - write-ports-mask : Bits 0 to 3 corresponds write ports 0 to 3. A + bit set to 1 indicates the corresponding write port should be enabled. + + - cmd-ports-mask : Bits 0 to 5 correspond to command ports 0 to 5. A + bit set to 1 indicates the corresponding command port should be enabled. + +Optional properties: + + - label : name that you want this bridge to show up as under + /sys/class/fpga-bridge. + Default is br if this is not specified + +Example: + fpga2sdram_br: fpgabridge@3 { + compatible = "altr,socfpga-fpga2sdram-bridge"; + label = "fpga2sdram"; + read-ports-mask = <3>; + write-ports-mask = <3>; + cmd-ports-mask = <0xd>; + }; diff --git a/Documentation/devicetree/bindings/fpga/altera-hps2fpga-bridge.txt b/Documentation/devicetree/bindings/fpga/altera-hps2fpga-bridge.txt new file mode 100644 index 0000000000000..6b365efde780d --- /dev/null +++ b/Documentation/devicetree/bindings/fpga/altera-hps2fpga-bridge.txt @@ -0,0 +1,39 @@ +Altera FPGA/HPS Bridge Driver + + This driver manages a bridge between a FPGA and a host processor system + (HPS). User space can enable/disable the bridge by writing a "1" or a "0", + respectively, to its enable file under bridge's entry in + /sys/class/fpga-bridge. Typically, one disables the bridges before + reprogramming the FPGA. Once the FPGA is reprogrammed, the bridges + are reenabled. + +Required properties: + + - compatible : "altr,socfpga-hps2fpga-bridge" + "altr,socfpga-lwhps2fpga-bridge" + "altr,socfpga-fpga2hps-bridge" + + - clocks : clocks used by this module + +Optional properties: + - label : name that you want this bridge to show up as under + /sys/class/fpga-bridge. Default is br if this is not specified + +Example: + hps_fpgabridge0: fpgabridge@0 { + compatible = "altr,socfpga-hps2fpga-bridge"; + label = "hps2fpga"; + clocks = <&l4_main_clk>; + }; + + hps_fpgabridge1: fpgabridge@1 { + compatible = "altr,socfpga-lwhps2fpga-bridge"; + label = "lwhps2fpga"; + clocks = <&l4_main_clk>; + }; + + hps_fpgabridge2: fpgabridge@2 { + compatible = "altr,socfpga-fpga2hps-bridge"; + label = "fpga2hps"; + clocks = <&l4_main_clk>; + }; diff --git a/drivers/misc/fpga-bridge/altera-fpga2sdram.c b/drivers/misc/fpga-bridge/altera-fpga2sdram.c index 413fc09f7c444..213dcf71edf30 100644 --- a/drivers/misc/fpga-bridge/altera-fpga2sdram.c +++ b/drivers/misc/fpga-bridge/altera-fpga2sdram.c @@ -34,6 +34,9 @@ #define ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT 4 #define ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT 8 +#define FOUR_BIT_MASK 0xf +#define SIX_BIT_MASK 0x3f + static struct of_device_id altera_fpga_of_match[]; struct alt_fpga2sdram_data { @@ -44,6 +47,8 @@ struct alt_fpga2sdram_data { int mask; }; +static atomic_t instances; + static int alt_fpga2sdram_enable_show(struct fpga_bridge *bridge) { struct alt_fpga2sdram_data *priv = bridge->priv; @@ -51,7 +56,7 @@ static int alt_fpga2sdram_enable_show(struct fpga_bridge *bridge) regmap_read(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST, &value); - return ((value & priv->mask) != 0); + return ((value & priv->mask) == priv->mask); } static void alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable) @@ -68,61 +73,41 @@ static void alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable) priv->mask, value); } +struct prop_map { + char *prop_name; + uint32_t *prop_value; + uint32_t prop_max; +}; static int alt_fpga2sdram_get_mask(struct alt_fpga2sdram_data *priv) { - struct device_node *np = priv->np; - int mask, ctrl_shift, ctrl_mask; - u32 read, write, control[2]; - - if (of_property_read_u32(np, "read-port", &read)) { - dev_err(&priv->pdev->dev, - "read-port property missing\n"); - return -EINVAL; - } - if ((read < 0) || (read > 3)) { - dev_err(&priv->pdev->dev, - "read-port property out of bounds\n"); - return -EINVAL; + int i; + uint32_t read, write, cmd; + struct prop_map map[] = { + {"read-ports-mask", &read, FOUR_BIT_MASK}, + {"write-ports-mask", &write, FOUR_BIT_MASK}, + {"cmd-ports-mask", &cmd, SIX_BIT_MASK}, + }; + for (i = 0; i < ARRAY_SIZE(map); i++) { + if (of_property_read_u32(priv->np, map[i].prop_name, + map[i].prop_value)) { + dev_err(&priv->pdev->dev, + "failed to find property, %s\n", + map[i].prop_name); + return -EINVAL; + } else if (*map[i].prop_value > map[i].prop_max) { + dev_err(&priv->pdev->dev, + "%s value 0x%x > than max 0x%x\n", + map[i].prop_name, + *map[i].prop_value, + map[i].prop_max); + return -EINVAL; + } } - if (of_property_read_u32(np, "write-port", &write)) { - dev_err(&priv->pdev->dev, - "write-port property invalid or missing\n"); - return -EINVAL; - } - if ((write < 0) || (write > 3)) { - dev_err(&priv->pdev->dev, - "write-port property out of bounds\n"); - return -EINVAL; - } - - /* There can be 1 or 2 control ports specified */ - if (of_property_read_u32_array(np, "control-ports", control, - ARRAY_SIZE(control))) { - dev_err(&priv->pdev->dev, - "control-ports property missing\n"); - return -EINVAL; - } - if ((control[0] < 0) || (control[0] > 5) || - (control[1] < 1) || (control[1] > 2)) { - dev_err(&priv->pdev->dev, - "control-ports property out of bounds\n"); - return -EINVAL; - } - - ctrl_shift = ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT + control[0]; - - if (control[1] == 1) - ctrl_mask = 0x1; - else - ctrl_mask = 0x3; - - mask = (1 << (ALT_SDR_CTL_FPGAPORTRST_RD_SHIFT + read)) | - (1 << (ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT + write)) | - (ctrl_mask << ctrl_shift); - - WARN_ON((mask & ALT_SDR_CTL_FPGAPORTRST_PORTRSTN_MSK) != mask); - priv->mask = mask; + priv->mask = + (read << ALT_SDR_CTL_FPGAPORTRST_RD_SHIFT) | + (write << ALT_SDR_CTL_FPGAPORTRST_WR_SHIFT) | + (cmd << ALT_SDR_CTL_FPGAPORTRST_CTRL_SHIFT); return 0; } @@ -143,6 +128,12 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) const struct of_device_id *of_id = of_match_device(altera_fpga_of_match, &pdev->dev); int ret = 0; + if (atomic_inc_return(&instances) > 1) { + atomic_dec(&instances); + dev_err(&pdev->dev, + "already one instance of driver\n"); + return -ENODEV; + } data = (struct alt_fpga2sdram_data *)of_id->data; WARN_ON(!data); @@ -160,7 +151,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) priv->sdrctl = syscon_regmap_lookup_by_compatible("altr,sdr-ctl"); if (IS_ERR(priv->sdrctl)) { devm_kfree(&pdev->dev, priv); - dev_err(&priv->pdev->dev, + dev_err(&pdev->dev, "regmap for altr,sdr-ctl lookup failed.\n"); return PTR_ERR(priv->sdrctl); } @@ -178,6 +169,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) static int alt_fpga_bridge_remove(struct platform_device *pdev) { remove_fpga_bridge(pdev); + atomic_dec(&instances); return 0; } @@ -199,6 +191,7 @@ static struct platform_driver altera_fpga_driver = { static int __init alt_fpga_bridge_init(void) { + atomic_set(&instances, 0); return platform_driver_probe(&altera_fpga_driver, alt_fpga_bridge_probe); } From bef5e8988241c80343cc17d2eb27d2a9c96d4fcd Mon Sep 17 00:00:00 2001 From: Matthew Gerlach Date: Tue, 5 Nov 2013 11:40:33 -0800 Subject: [PATCH 084/201] FogBugz #165941: FPGA bridge drivers need to be started early The fpga bridge framework needs be started before the individual bridge drivers, and the individual bridge drivers need to be started before any other driver for components on the FPGA. The earliest the bridge drivers successfully start is during arch_init(). Signed-off-by: Matthew Gerlach --- drivers/misc/fpga-bridge/altera-fpga2sdram.c | 2 +- drivers/misc/fpga-bridge/altera-hps2fpga.c | 2 +- drivers/misc/fpga-bridge/fpga-bridge.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/fpga-bridge/altera-fpga2sdram.c b/drivers/misc/fpga-bridge/altera-fpga2sdram.c index 213dcf71edf30..11a718410e40f 100644 --- a/drivers/misc/fpga-bridge/altera-fpga2sdram.c +++ b/drivers/misc/fpga-bridge/altera-fpga2sdram.c @@ -201,7 +201,7 @@ static void __exit alt_fpga_bridge_exit(void) platform_driver_unregister(&altera_fpga_driver); } -module_init(alt_fpga_bridge_init); +arch_initcall(alt_fpga_bridge_init); module_exit(alt_fpga_bridge_exit); MODULE_DESCRIPTION("Altera SoCFPGA FPGA to SDRAM Bridge"); diff --git a/drivers/misc/fpga-bridge/altera-hps2fpga.c b/drivers/misc/fpga-bridge/altera-hps2fpga.c index 4e5dd38cfa4ce..5fce8a1c78f61 100644 --- a/drivers/misc/fpga-bridge/altera-hps2fpga.c +++ b/drivers/misc/fpga-bridge/altera-hps2fpga.c @@ -191,7 +191,7 @@ static void __exit alt_fpga_bridge_exit(void) platform_driver_unregister(&altera_fpga_driver); } -module_init(alt_fpga_bridge_init); +arch_initcall(alt_fpga_bridge_init); module_exit(alt_fpga_bridge_exit); MODULE_DESCRIPTION("Altera SoCFPGA HPS to FPGA Bridge"); diff --git a/drivers/misc/fpga-bridge/fpga-bridge.c b/drivers/misc/fpga-bridge/fpga-bridge.c index 96bf4e402087e..6dcbf0190b07b 100644 --- a/drivers/misc/fpga-bridge/fpga-bridge.c +++ b/drivers/misc/fpga-bridge/fpga-bridge.c @@ -225,5 +225,5 @@ MODULE_DESCRIPTION("FPGA Bridge Driver"); MODULE_AUTHOR("Alan Tull "); MODULE_LICENSE("GPL v2"); -subsys_initcall(fpga_bridge_dev_init); +core_initcall_sync(fpga_bridge_dev_init); module_exit(fpga_bridge_dev_exit); From 9166e314738b07482480bba47fdccdc6c6efc80a Mon Sep 17 00:00:00 2001 From: Matthew Gerlach Date: Mon, 18 Nov 2013 13:21:13 -0800 Subject: [PATCH 085/201] FogBugz #168803: support to configure initial state of bridges This change adds support to the Altera fpga2sdram and hps2fgpa bridge drivers for a new optional device tree property, init-val. This property configures the driver to enable or disable the bridge when instantiated. If the property does not exist, the driver will leave the bridge in its current state. Signed-off-by: Matthew Gerlach --- .../fpga/altera-fpga2sdram-bridge.txt | 6 ++++ .../bindings/fpga/altera-hps2fpga-bridge.txt | 6 ++++ drivers/misc/fpga-bridge/altera-fpga2sdram.c | 29 +++++++++++++++++-- drivers/misc/fpga-bridge/altera-hps2fpga.c | 28 ++++++++++++++++-- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/fpga/altera-fpga2sdram-bridge.txt b/Documentation/devicetree/bindings/fpga/altera-fpga2sdram-bridge.txt index ce7184b00a76f..2b6c37b6d09b4 100644 --- a/Documentation/devicetree/bindings/fpga/altera-fpga2sdram-bridge.txt +++ b/Documentation/devicetree/bindings/fpga/altera-fpga2sdram-bridge.txt @@ -37,6 +37,11 @@ Optional properties: /sys/class/fpga-bridge. Default is br if this is not specified + - init-val :0 if driver should disable bridge at startup + 1 if driver should enable bridge at startup + driver leaves bridge in current state if property not specified + + Example: fpga2sdram_br: fpgabridge@3 { compatible = "altr,socfpga-fpga2sdram-bridge"; @@ -44,4 +49,5 @@ Example: read-ports-mask = <3>; write-ports-mask = <3>; cmd-ports-mask = <0xd>; + init-val = <0>; }; diff --git a/Documentation/devicetree/bindings/fpga/altera-hps2fpga-bridge.txt b/Documentation/devicetree/bindings/fpga/altera-hps2fpga-bridge.txt index 6b365efde780d..d9eb0a7434dc0 100644 --- a/Documentation/devicetree/bindings/fpga/altera-hps2fpga-bridge.txt +++ b/Documentation/devicetree/bindings/fpga/altera-hps2fpga-bridge.txt @@ -19,17 +19,23 @@ Optional properties: - label : name that you want this bridge to show up as under /sys/class/fpga-bridge. Default is br if this is not specified + - init-val : 0 if driver should disable bridge at startup + 1 if driver should enable bridge at startup + driver leaves bridge in current state if property not specified + Example: hps_fpgabridge0: fpgabridge@0 { compatible = "altr,socfpga-hps2fpga-bridge"; label = "hps2fpga"; clocks = <&l4_main_clk>; + init-val = <1>; }; hps_fpgabridge1: fpgabridge@1 { compatible = "altr,socfpga-lwhps2fpga-bridge"; label = "lwhps2fpga"; clocks = <&l4_main_clk>; + init-val = <0>; }; hps_fpgabridge2: fpgabridge@2 { diff --git a/drivers/misc/fpga-bridge/altera-fpga2sdram.c b/drivers/misc/fpga-bridge/altera-fpga2sdram.c index 11a718410e40f..d12c363414e00 100644 --- a/drivers/misc/fpga-bridge/altera-fpga2sdram.c +++ b/drivers/misc/fpga-bridge/altera-fpga2sdram.c @@ -59,9 +59,9 @@ static int alt_fpga2sdram_enable_show(struct fpga_bridge *bridge) return ((value & priv->mask) == priv->mask); } -static void alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable) +static inline void _alt_fpga2sdram_enable_set(struct alt_fpga2sdram_data *priv, + bool enable) { - struct alt_fpga2sdram_data *priv = bridge->priv; int value; if (enable) @@ -72,6 +72,10 @@ static void alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable) regmap_update_bits(priv->sdrctl, ALT_SDR_CTL_FPGAPORTRST_OFST, priv->mask, value); } +static void alt_fpga2sdram_enable_set(struct fpga_bridge *bridge, bool enable) +{ + _alt_fpga2sdram_enable_set(bridge->priv, enable); +} struct prop_map { char *prop_name; @@ -125,6 +129,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) { struct alt_fpga2sdram_data *priv; struct alt_fpga2sdram_data *data; + uint32_t init_val; const struct of_device_id *of_id = of_match_device(altera_fpga_of_match, &pdev->dev); int ret = 0; @@ -162,8 +167,26 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) return ret; } - return register_fpga_bridge(pdev, &altera_fpga2sdram_br_ops, + ret = register_fpga_bridge(pdev, &altera_fpga2sdram_br_ops, priv->name, priv); + + if (!ret) + return ret; + + if (of_property_read_u32(priv->np, "init-val", &init_val)) + dev_info(&priv->pdev->dev, "init-val not specified\n"); + else if (init_val > 1) + dev_warn(&priv->pdev->dev, "invalid init-val %u > 1\n", + init_val); + else { + dev_info(&priv->pdev->dev, + "%s bridge\n", + (init_val ? "enabling" : "disabling")); + + _alt_fpga2sdram_enable_set(priv, init_val); + } + + return ret; } static int alt_fpga_bridge_remove(struct platform_device *pdev) diff --git a/drivers/misc/fpga-bridge/altera-hps2fpga.c b/drivers/misc/fpga-bridge/altera-hps2fpga.c index 5fce8a1c78f61..072a3a50ac70e 100644 --- a/drivers/misc/fpga-bridge/altera-hps2fpga.c +++ b/drivers/misc/fpga-bridge/altera-hps2fpga.c @@ -63,9 +63,9 @@ static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge) return ((value & priv->reset_mask) == 0); } -static void alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable) +static inline void _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv, + bool enable) { - struct altera_hps2fpga_data *priv = bridge->priv; unsigned int value; /* bring bridge out of reset */ @@ -90,6 +90,11 @@ static void alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable) } } +static void alt_hps2fpga_enable_set(struct fpga_bridge *bridge, bool enable) +{ + _alt_hps2fpga_enable_set(bridge->priv, enable); +} + struct fpga_bridge_ops altera_hps2fpga_br_ops = { .enable_set = alt_hps2fpga_enable_set, .enable_show = alt_hps2fpga_enable_show, @@ -116,6 +121,7 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) { struct altera_hps2fpga_data *priv; const struct of_device_id *of_id; + uint32_t init_val; int rc; struct clk *clk; @@ -152,8 +158,24 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) return -EBUSY; } - return register_fpga_bridge(pdev, &altera_hps2fpga_br_ops, + rc = register_fpga_bridge(pdev, &altera_hps2fpga_br_ops, priv->name, priv); + if (rc) + return rc; + + if (of_property_read_u32(priv->np, "init-val", &init_val)) + dev_info(&priv->pdev->dev, "init-val not specified\n"); + else if (init_val > 1) + dev_warn(&priv->pdev->dev, "invalid init-val %u > 1\n", + init_val); + else { + dev_info(&priv->pdev->dev, "%s bridge\n", + (init_val ? "enabling" : "disabling")); + + _alt_hps2fpga_enable_set(priv, init_val); + } + + return rc; } static int alt_fpga_bridge_remove(struct platform_device *pdev) From 4e766d3670f272fded242574967ef661104f9e72 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 12 Aug 2013 11:09:10 +0800 Subject: [PATCH 086/201] FogBugz #143478: arch/arm: Move sysid from arch to drivers Removed sysid from arch/arm and read silicon ID from system manager for soc_id and revision. Signed-off-by: Ley Foon Tan Conflicts: arch/arm/mach-socfpga/core.h --- arch/arm/mach-socfpga/core.h | 15 ++++++++------- arch/arm/mach-socfpga/socfpga.c | 29 +++++++++++++---------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h index 2ec21c7796fcc..b9c1a8a1694a6 100644 --- a/arch/arm/mach-socfpga/core.h +++ b/arch/arm/mach-socfpga/core.h @@ -30,20 +30,21 @@ extern void socfpga_secondary_startup(void); extern void __iomem *socfpga_scu_base_addr; -#define SOCFPGA_SYSID_DEFAULT 0x1 -#define SOCFPGA_REVISION_DEFAULT 0x1 - -/* Sysid register map */ -#define SYSID_ID_REG 0x0 -extern void socfpga_init_clocks(void); -extern void socfpga_sysmgr_init(void); /*MPU Module Reset Register */ #define RSTMGR_MPUMODRST_CPU0 0x1 /*CPU0 Reset*/ #define RSTMGR_MPUMODRST_CPU1 0x2 /*CPU1 Reset*/ #define RSTMGR_MPUMODRST_WDS 0x4 /*Watchdog Reset*/ #define RSTMGR_MPUMODRST_SCUPER 0x8 /*SCU and periphs reset*/ #define RSTMGR_MPUMODRST_L2 0x10 /*L2 Cache reset*/ +#define SOCFPGA_ID_DEFAULT 0x1 +#define SOCFPGA_REVISION_DEFAULT 0x1 + +#define SYSMGR_SILICON_ID1_OFFSET 0x0 +#define SYSMGR_SILICON_ID1_REV_SHIFT 0 +#define SYSMGR_SILICON_ID1_REV_MASK 0x0000FFFF +#define SYSMGR_SILICON_ID1_ID_SHIFT 16 +#define SYSMGR_SILICON_ID1_ID_MASK 0xFFFF0000 extern void __iomem *sys_manager_base_addr; extern void __iomem *rst_manager_base_addr; diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 8c7bbac6b78d5..258018ce0e7e3 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -70,12 +70,11 @@ static struct map_desc uart_io_desc __initdata = { static void __init socfpga_soc_device_init(void) { struct device_node *root; - struct device_node *sysid_node; struct soc_device *soc_dev; struct soc_device_attribute *soc_dev_attr; - void __iomem *sysid_base; const char *machine; - u32 id = SOCFPGA_SYSID_DEFAULT; + u32 id = SOCFPGA_ID_DEFAULT; + u32 rev = SOCFPGA_REVISION_DEFAULT; int err; root = of_find_node_by_path("/"); @@ -86,26 +85,24 @@ static void __init socfpga_soc_device_init(void) if (err) return; + of_node_put(root); + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) return; - sysid_node = of_find_compatible_node(root, NULL, "ALTR,sysid-1.0"); - if (sysid_node) { - sysid_base = of_iomap(sysid_node, 0); - if (sysid_base) { - /* Use id from Sysid hardware. */ - id = readl(sysid_base + SYSID_ID_REG); - iounmap(sysid_base); - } - of_node_put(sysid_node); + /* Read Silicon ID from System manager */ + if (sys_manager_base_addr) { + id = __raw_readl(sys_manager_base_addr + + SYSMGR_SILICON_ID1_OFFSET); + rev = (id & SYSMGR_SILICON_ID1_REV_MASK) + >> SYSMGR_SILICON_ID1_REV_SHIFT; + id = (id & SYSMGR_SILICON_ID1_ID_MASK) + >> SYSMGR_SILICON_ID1_ID_SHIFT; } - of_node_put(root); - soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", id); - soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", - SOCFPGA_REVISION_DEFAULT); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", rev); soc_dev_attr->machine = kasprintf(GFP_KERNEL, "%s", machine); soc_dev_attr->family = "SOCFPGA"; From af40bc1b39132221dd4febac950c253bb2fcee8c Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 17 Oct 2012 09:35:59 -0500 Subject: [PATCH 087/201] dma: pl330: Add in enhancements for SOCFPGA -Add changes specific to socfpga. Signed-off-by: Dinh Nguyen --- drivers/dma/pl330.c | 9 ++++++++- include/linux/amba/pl330.h | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 73fa9b7a10ab3..3d8e498612568 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2342,7 +2342,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) pch->pl330_chid = pl330_request_channel(&pdmac->pif); if (!pch->pl330_chid) { spin_unlock_irqrestore(&pch->lock, flags); - return -ENOMEM; + return -EAGAIN; } tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch); @@ -2936,6 +2936,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) } } + if (pdat->init) { + ret = pdat->init(adev); + if (ret) + goto probe_err3; + } + pi->pcfg.periph_id = adev->periphid; ret = pl330_add(pi); if (ret) @@ -3063,6 +3069,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) static int pl330_remove(struct amba_device *adev) { struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev); + struct dma_pl330_platdata *pdat = adev->dev.platform_data; struct dma_pl330_chan *pch, *_p; struct pl330_info *pi; diff --git a/include/linux/amba/pl330.h b/include/linux/amba/pl330.h index fe93758e84036..e3408c6b54672 100644 --- a/include/linux/amba/pl330.h +++ b/include/linux/amba/pl330.h @@ -29,6 +29,14 @@ struct dma_pl330_platdata { dma_cap_mask_t cap_mask; /* Bytes to allocate for MC buffer */ unsigned mcbuf_sz; + /* Start of IRQ numbers if support multiple IRQs */ + int irq_start; + /* End of IRQ numbers if support multiple IRQs */ + int irq_end; + /* Platform specific initialization function pointer*/ + int (*init)(struct amba_device *adev); + /* Platform specific exit function pointer*/ + void (*exit)(struct amba_device *adev); }; extern bool pl330_filter(struct dma_chan *chan, void *param); From 99e7cfc7a6710df9213d8020e17b195e0b3b5477 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Fri, 13 Dec 2013 13:08:56 -0600 Subject: [PATCH 088/201] FogBugz #172665: Sample driver for DMA transfer to FPGA soft IP (FIFO) -This fpga-dma driver is an example driver for a specific FPGA soft IP -The soft IP presents a FIFO which can be read and written through the debugfs interface -The DMA transfer code uses 'generic' dma interfaces -The fpga-dma driver uses generic register and DMA definitions in the devicetree V2: -Device tree changes removed from socfpga_cyclone5.dts and documented in Documentation/devicetree/bindings/arm/altera/fpga-dma.txt V3: -Formatting changes, used Lindent script to format fpga-dma.c V4: -More formatting changes, lined up #defines, alphabetize headers Signed-off-by: Graham Moore Conflicts: arch/arm/mach-socfpga/Kconfig --- .../bindings/arm/altera/fpga-dma.txt | 25 + arch/arm/mach-socfpga/Kconfig | 6 + arch/arm/mach-socfpga/Makefile | 1 + arch/arm/mach-socfpga/fpga-dma.c | 681 ++++++++++++++++++ 4 files changed, 713 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/altera/fpga-dma.txt create mode 100644 arch/arm/mach-socfpga/fpga-dma.c diff --git a/Documentation/devicetree/bindings/arm/altera/fpga-dma.txt b/Documentation/devicetree/bindings/arm/altera/fpga-dma.txt new file mode 100644 index 0000000000000..a08e9010d4e52 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/fpga-dma.txt @@ -0,0 +1,25 @@ +Altera FPGA DMA FIFO driver + +Required properties: +- compatible : "altr,fpga-dma"; + +- reg : CSR and DATA register resource definitions (address and length). + +- reg-names : Names of the register resources. Should be "csr", "data". + +- dmas : DMA request lines. Should be <&pdma 0 &pdma 1> + +- dma-names : Names of DMA request lines. Should be "tx", "rx". + +Example: + + fpgadma: fifo { + #address-cells = <1>; + #size-cells = <1>; + compatible = "altr,fpga-dma"; + reg = <0xff230000 0x20>, <0xc0011000 0x400>; + reg-names = "csr", "data"; + dmas = <&pdma 0 &pdma 1>; + dma-names = "tx", "rx"; + }; + diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index a2814b663ca55..91fedd8eef75d 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -19,3 +19,9 @@ config ARCH_SOCFPGA select PL310_ERRATA_727915 select PL310_ERRATA_753970 select PL310_ERRATA_769419 + +config FPGADMA + tristate "FPGA DMA FIFO driver" + depends on DMA_ENGINE + help + Sample FPGA DMA driver, for testing with special FPGA FIFO image diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile index c7401d395671a..1ada39d6e953f 100644 --- a/arch/arm/mach-socfpga/Makefile +++ b/arch/arm/mach-socfpga/Makefile @@ -5,3 +5,4 @@ obj-y := socfpga.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o obj-$(CONFIG_HW_PERF_EVENTS) += socfpga_cti.o +obj-$(CONFIG_FPGADMA) += fpga-dma.o diff --git a/arch/arm/mach-socfpga/fpga-dma.c b/arch/arm/mach-socfpga/fpga-dma.c new file mode 100644 index 0000000000000..c490c49c78c21 --- /dev/null +++ b/arch/arm/mach-socfpga/fpga-dma.c @@ -0,0 +1,681 @@ +/* + * FPGA DMA transfer module + * + * Copyright (C) 2014 Altera Corporation. All rights reserved + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************/ + +static unsigned int max_burst_words = 16; +module_param(max_burst_words, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_burst_words, "Size of a burst in words " + "(in this case a word is 64 bits)"); + +static int timeout = 1000; +module_param(timeout, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 1000), " + "Pass -1 for infinite timeout"); + +#define ALT_FPGADMA_DATA_WRITE 0x00 +#define ALT_FPGADMA_DATA_READ 0x08 + +#define ALT_FPGADMA_CSR_WR_WTRMK 0x00 +#define ALT_FPGADMA_CSR_RD_WTRMK 0x04 +#define ALT_FPGADMA_CSR_BURST 0x08 +#define ALT_FPGADMA_CSR_FIFO_STATUS 0x0C +#define ALT_FPGADMA_CSR_DATA_WIDTH 0x10 +#define ALT_FPGADMA_CSR_FIFO_DEPTH 0x14 +#define ALT_FPGADMA_CSR_FIFO_CLEAR 0x18 +#define ALT_FPGADMA_CSR_ZERO 0x1C + +#define ALT_FPGADMA_CSR_BURST_TX_SINGLE (1 << 0) +#define ALT_FPGADMA_CSR_BURST_TX_BURST (1 << 1) +#define ALT_FPGADMA_CSR_BURST_RX_SINGLE (1 << 2) +#define ALT_FPGADMA_CSR_BURST_RX_BURST (1 << 3) + +#define ALT_FPGADMA_FIFO_FULL (1 << 25) +#define ALT_FPGADMA_FIFO_EMPTY (1 << 24) +#define ALT_FPGADMA_FIFO_USED_MASK ((1 << 24)-1) + +struct fpga_dma_pdata { + + struct platform_device *pdev; + + struct dentry *root; + + unsigned int data_reg_phy; + void __iomem *data_reg; + void __iomem *csr_reg; + + unsigned int fifo_size_bytes; + unsigned int fifo_depth; + unsigned int data_width; + unsigned int data_width_bytes; + unsigned char *read_buf; + unsigned char *write_buf; + + struct dma_chan *txchan; + struct dma_chan *rxchan; + dma_addr_t tx_dma_addr; + dma_addr_t rx_dma_addr; + dma_cookie_t rx_cookie; + dma_cookie_t tx_cookie; +}; + +static DECLARE_COMPLETION(dma_read_complete); +static DECLARE_COMPLETION(dma_write_complete); + +#define IS_DMA_READ (true) +#define IS_DMA_WRITE (false) + +static int fpga_dma_dma_start_rx(struct platform_device *pdev, + unsigned datalen, unsigned char *databuf, + u32 burst_size); +static int fpga_dma_dma_start_tx(struct platform_device *pdev, + unsigned datalen, unsigned char *databuf, + u32 burst_size); + +/* --------------------------------------------------------------------- */ + +static void dump_csr(struct fpga_dma_pdata *pdata) +{ + dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_WR_WTRMK %08x\n", + readl(pdata->csr_reg + ALT_FPGADMA_CSR_WR_WTRMK)); + dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_RD_WTRMK %08x\n", + readl(pdata->csr_reg + ALT_FPGADMA_CSR_RD_WTRMK)); + dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_BURST %08x\n", + readl(pdata->csr_reg + ALT_FPGADMA_CSR_BURST)); + dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_FIFO_STATUS %08x\n", + readl(pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_STATUS)); + dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_DATA_WIDTH %08x\n", + readl(pdata->csr_reg + ALT_FPGADMA_CSR_DATA_WIDTH)); + dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_FIFO_DEPTH %08x\n", + readl(pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_DEPTH)); + dev_info(&pdata->pdev->dev, "ALT_FPGADMA_CSR_ZERO %08x\n", + readl(pdata->csr_reg + ALT_FPGADMA_CSR_ZERO)); +} + +/* --------------------------------------------------------------------- */ + +static void recalc_burst_and_words(struct fpga_dma_pdata *pdata, + int *burst_size, int *num_words) +{ + /* adjust size and maxburst so that total bytes transferred + is a multiple of burst length and width */ + if (*num_words < max_burst_words) { + /* we have only a few words left, make it our burst size */ + *burst_size = *num_words; + } else { + /* here we may not transfer all words to FIFO, but next + call will pick them up... */ + *num_words = max_burst_words * (*num_words / max_burst_words); + *burst_size = max_burst_words; + } +} + +static int word_to_bytes(struct fpga_dma_pdata *pdata, int num_bytes) +{ + return (num_bytes + pdata->data_width_bytes - 1) + / pdata->data_width_bytes; +} + +static ssize_t dbgfs_write_dma(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct fpga_dma_pdata *pdata = file->private_data; + int ret = 0; + int bytes_to_transfer; + int num_words; + u32 burst_size; + int pad_index; + + *ppos = 0; + + /* get user data into kernel buffer */ + bytes_to_transfer = simple_write_to_buffer(pdata->write_buf, + pdata->fifo_size_bytes, ppos, + user_buf, count); + pad_index = bytes_to_transfer; + + num_words = word_to_bytes(pdata, bytes_to_transfer); + recalc_burst_and_words(pdata, &burst_size, &num_words); + /* we sometimes send more than asked for, padded with zeros */ + bytes_to_transfer = num_words * pdata->data_width_bytes; + for (; pad_index < bytes_to_transfer; pad_index++) + pdata->write_buf[pad_index] = 0; + + ret = fpga_dma_dma_start_tx(pdata->pdev, + bytes_to_transfer, pdata->write_buf, + burst_size); + if (ret) { + dev_err(&pdata->pdev->dev, "Error starting TX DMA %d\n", ret); + return ret; + } + + if (!wait_for_completion_timeout(&dma_write_complete, + msecs_to_jiffies(timeout))) { + dev_err(&pdata->pdev->dev, "Timeout waiting for TX DMA!\n"); + dev_err(&pdata->pdev->dev, + "count %d burst_size %d num_words %d bytes_to_transfer %d\n", + count, burst_size, num_words, bytes_to_transfer); + dmaengine_terminate_all(pdata->txchan); + return -ETIMEDOUT; + } + + return bytes_to_transfer; +} + +static ssize_t dbgfs_read_dma(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct fpga_dma_pdata *pdata = file->private_data; + int ret; + int num_words; + int num_bytes; + u32 burst_size; + + num_words = readl(pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_STATUS); + num_words &= ALT_FPGADMA_FIFO_USED_MASK; + + num_bytes = num_words * pdata->data_width_bytes; + if (num_bytes > count) { + dev_dbg(&pdata->pdev->dev, + "dbgfs_read_dma num_bytes %d > count %d\n", + num_bytes, count); + num_bytes = count; + num_words = num_bytes / (pdata->data_width_bytes); + } + if (num_bytes > pdata->fifo_size_bytes) { + dev_dbg(&pdata->pdev->dev, + "dbgfs_read_dma num_bytes %d > pdata->fifo_size_bytes %d\n", + num_bytes, pdata->fifo_size_bytes); + num_bytes = pdata->fifo_size_bytes; + num_words = num_bytes / (pdata->data_width_bytes); + } + + recalc_burst_and_words(pdata, &burst_size, &num_words); + num_bytes = num_words * pdata->data_width_bytes; + + if (num_bytes > 0) { + ret = fpga_dma_dma_start_rx(pdata->pdev, num_bytes, + pdata->read_buf, burst_size); + if (ret) { + dev_err(&pdata->pdev->dev, + "Error starting RX DMA %d\n", ret); + return ret; + } + + if (!wait_for_completion_timeout(&dma_read_complete, + msecs_to_jiffies(timeout))) { + dev_err(&pdata->pdev->dev, + "Timeout waiting for RX DMA!\n"); + dmaengine_terminate_all(pdata->rxchan); + return -ETIMEDOUT; + } + *ppos = 0; + } + return simple_read_from_buffer(user_buf, count, ppos, + pdata->read_buf, num_bytes); +} + +static const struct file_operations dbgfs_dma_fops = { + .write = dbgfs_write_dma, + .read = dbgfs_read_dma, + .open = simple_open, + .llseek = no_llseek, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t dbgfs_read_csr(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct fpga_dma_pdata *pdata = file->private_data; + dump_csr(pdata); + return 0; +} + +static const struct file_operations dbgfs_csr_fops = { + .read = dbgfs_read_csr, + .open = simple_open, + .llseek = no_llseek, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t dbgfs_write_clear(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct fpga_dma_pdata *pdata = file->private_data; + writel(1, pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_CLEAR); + return count; +} + +static const struct file_operations dbgfs_clear_fops = { + .write = dbgfs_write_clear, + .open = simple_open, + .llseek = no_llseek, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t dbgfs_write_wrwtrmk(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct fpga_dma_pdata *pdata = file->private_data; + char buf[32]; + unsigned long val; + int ret; + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, user_buf, min(count, (sizeof(buf) - 1)))) + return -EFAULT; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + writel(val, pdata->csr_reg + ALT_FPGADMA_CSR_WR_WTRMK); + return count; +} + +static const struct file_operations dbgfs_wrwtrmk_fops = { + .write = dbgfs_write_wrwtrmk, + .open = simple_open, + .llseek = no_llseek, +}; + +/* --------------------------------------------------------------------- */ + +static ssize_t dbgfs_write_rdwtrmk(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct fpga_dma_pdata *pdata = file->private_data; + char buf[32]; + int ret; + unsigned long val; + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, user_buf, min(count, (sizeof(buf) - 1)))) + return -EFAULT; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + writel(val, pdata->csr_reg + ALT_FPGADMA_CSR_RD_WTRMK); + return count; +} + +static const struct file_operations dbgfs_rdwtrmk_fops = { + .write = dbgfs_write_rdwtrmk, + .open = simple_open, + .llseek = no_llseek, +}; + +/* --------------------------------------------------------------------- */ + +static int fpga_dma_register_dbgfs(struct fpga_dma_pdata *pdata) +{ + struct dentry *d; + + d = debugfs_create_dir("fpga_dma", NULL); + if (IS_ERR(d)) + return PTR_ERR(d); + if (!d) { + dev_err(&pdata->pdev->dev, "Failed to initialize debugfs\n"); + return -ENOMEM; + } + + pdata->root = d; + + debugfs_create_file("dma", S_IWUSR | S_IRUGO, pdata->root, pdata, + &dbgfs_dma_fops); + + debugfs_create_file("csr", S_IRUGO, pdata->root, pdata, + &dbgfs_csr_fops); + + debugfs_create_file("clear", S_IWUSR, pdata->root, pdata, + &dbgfs_clear_fops); + + debugfs_create_file("wrwtrmk", S_IWUSR, pdata->root, pdata, + &dbgfs_wrwtrmk_fops); + + debugfs_create_file("rdwtrmk", S_IWUSR, pdata->root, pdata, + &dbgfs_rdwtrmk_fops); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void fpga_dma_dma_rx_done(void *arg) +{ + complete(&dma_read_complete); +} + +static void fpga_dma_dma_tx_done(void *arg) +{ + complete(&dma_write_complete); +} + +static void fpga_dma_dma_cleanup(struct platform_device *pdev, + unsigned datalen, bool do_read) +{ + struct fpga_dma_pdata *pdata = platform_get_drvdata(pdev); + if (do_read) + dma_unmap_single(&pdev->dev, pdata->rx_dma_addr, + datalen, DMA_FROM_DEVICE); + else + dma_unmap_single(&pdev->dev, pdata->tx_dma_addr, + datalen, DMA_TO_DEVICE); +} + +static int fpga_dma_dma_start_rx(struct platform_device *pdev, + unsigned datalen, unsigned char *databuf, + u32 burst_size) +{ + struct fpga_dma_pdata *pdata = platform_get_drvdata(pdev); + struct dma_chan *dmachan; + struct dma_slave_config dmaconf; + struct dma_async_tx_descriptor *dmadesc = NULL; + + int num_words; + + num_words = word_to_bytes(pdata, datalen); + + dmachan = pdata->rxchan; + memset(&dmaconf, 0, sizeof(dmaconf)); + dmaconf.direction = DMA_DEV_TO_MEM; + dmaconf.src_addr = pdata->data_reg_phy + ALT_FPGADMA_DATA_READ; + dmaconf.src_addr_width = 8; + dmaconf.src_maxburst = burst_size; + + pdata->rx_dma_addr = dma_map_single(&pdev->dev, + databuf, datalen, DMA_FROM_DEVICE); + if (dma_mapping_error(&pdev->dev, pdata->rx_dma_addr)) { + dev_err(&pdev->dev, "dma_map_single for RX failed\n"); + return -EINVAL; + } + + /* set up slave config */ + dmaengine_slave_config(dmachan, &dmaconf); + + /* get dmadesc */ + dmadesc = dmaengine_prep_slave_single(dmachan, + pdata->rx_dma_addr, + datalen, + dmaconf.direction, + DMA_PREP_INTERRUPT); + if (!dmadesc) { + fpga_dma_dma_cleanup(pdev, datalen, IS_DMA_READ); + return -ENOMEM; + } + dmadesc->callback = fpga_dma_dma_rx_done; + dmadesc->callback_param = pdata; + + /* start DMA */ + pdata->rx_cookie = dmaengine_submit(dmadesc); + if (dma_submit_error(pdata->rx_cookie)) + dev_err(&pdev->dev, "rx_cookie error on dmaengine_submit\n"); + dma_async_issue_pending(dmachan); + + return 0; +} + +static int fpga_dma_dma_start_tx(struct platform_device *pdev, + unsigned datalen, unsigned char *databuf, + u32 burst_size) +{ + struct fpga_dma_pdata *pdata = platform_get_drvdata(pdev); + struct dma_chan *dmachan; + struct dma_slave_config dmaconf; + struct dma_async_tx_descriptor *dmadesc = NULL; + + int num_words; + + num_words = word_to_bytes(pdata, datalen); + + dmachan = pdata->txchan; + memset(&dmaconf, 0, sizeof(dmaconf)); + dmaconf.direction = DMA_MEM_TO_DEV; + dmaconf.dst_addr = pdata->data_reg_phy + ALT_FPGADMA_DATA_WRITE; + dmaconf.dst_addr_width = 8; + dmaconf.dst_maxburst = burst_size; + pdata->tx_dma_addr = dma_map_single(&pdev->dev, + databuf, datalen, DMA_TO_DEVICE); + if (dma_mapping_error(&pdev->dev, pdata->tx_dma_addr)) { + dev_err(&pdev->dev, "dma_map_single for TX failed\n"); + return -EINVAL; + } + + /* set up slave config */ + dmaengine_slave_config(dmachan, &dmaconf); + + /* get dmadesc */ + dmadesc = dmaengine_prep_slave_single(dmachan, + pdata->tx_dma_addr, + datalen, + dmaconf.direction, + DMA_PREP_INTERRUPT); + if (!dmadesc) { + fpga_dma_dma_cleanup(pdev, datalen, IS_DMA_WRITE); + return -ENOMEM; + } + dmadesc->callback = fpga_dma_dma_tx_done; + dmadesc->callback_param = pdata; + + /* start DMA */ + pdata->tx_cookie = dmaengine_submit(dmadesc); + if (dma_submit_error(pdata->tx_cookie)) + dev_err(&pdev->dev, "tx_cookie error on dmaengine_submit\n"); + dma_async_issue_pending(dmachan); + + return 0; +} + +static void fpga_dma_dma_shutdown(struct fpga_dma_pdata *pdata) +{ + if (pdata->txchan) { + dmaengine_terminate_all(pdata->txchan); + dma_release_channel(pdata->txchan); + } + if (pdata->rxchan) { + dmaengine_terminate_all(pdata->rxchan); + dma_release_channel(pdata->rxchan); + } + pdata->rxchan = pdata->txchan = NULL; +} + +static int fpga_dma_dma_init(struct fpga_dma_pdata *pdata) +{ + struct platform_device *pdev = pdata->pdev; + + pdata->txchan = dma_request_slave_channel(&pdev->dev, "tx"); + if (pdata->txchan) + dev_dbg(&pdev->dev, "TX channel %s %d selected\n", + dma_chan_name(pdata->txchan), pdata->txchan->chan_id); + else + dev_err(&pdev->dev, "could not get TX dma channel\n"); + + pdata->rxchan = dma_request_slave_channel(&pdev->dev, "rx"); + if (pdata->rxchan) + dev_dbg(&pdev->dev, "RX channel %s %d selected\n", + dma_chan_name(pdata->rxchan), pdata->rxchan->chan_id); + else + dev_err(&pdev->dev, "could not get RX dma channel\n"); + + if (!pdata->rxchan && !pdata->txchan) + /* both channels not there, maybe it's + bcs dma isn't loaded... */ + return -EPROBE_DEFER; + + if (!pdata->rxchan || !pdata->txchan) + return -ENOMEM; + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void __iomem *request_and_map(struct platform_device *pdev, + const struct resource *res) +{ + void __iomem *ptr; + + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, "unable to request %s\n", res->name); + return NULL; + } + + ptr = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); + if (!ptr) + dev_err(&pdev->dev, "ioremap_nocache of %s failed!", res->name); + + return ptr; +} + +static int fpga_dma_remove(struct platform_device *pdev) +{ + struct fpga_dma_pdata *pdata = platform_get_drvdata(pdev); + dev_dbg(&pdev->dev, "fpga_dma_remove\n"); + debugfs_remove_recursive(pdata->root); + fpga_dma_dma_shutdown(pdata); + return 0; +} + +static int fpga_dma_probe(struct platform_device *pdev) +{ + struct resource *csr_reg, *data_reg; + struct fpga_dma_pdata *pdata; + int ret; + + pdata = devm_kzalloc(&pdev->dev, sizeof(struct fpga_dma_pdata), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + csr_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); + data_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "data"); + if (!csr_reg || !data_reg) { + dev_err(&pdev->dev, "registers not completely defined\n"); + return -EINVAL; + } + + pdata->csr_reg = request_and_map(pdev, csr_reg); + if (!pdata->csr_reg) + return -ENOMEM; + + pdata->data_reg = request_and_map(pdev, data_reg); + if (!pdata->data_reg) + return -ENOMEM; + pdata->data_reg_phy = data_reg->start; + + /* read HW and calculate fifo size in bytes */ + pdata->fifo_depth = readl(pdata->csr_reg + ALT_FPGADMA_CSR_FIFO_DEPTH); + pdata->data_width = readl(pdata->csr_reg + ALT_FPGADMA_CSR_DATA_WIDTH); + /* 64-bit bus to FIFO */ + pdata->data_width_bytes = pdata->data_width / sizeof(u64); + pdata->fifo_size_bytes = pdata->fifo_depth * pdata->data_width_bytes; + + pdata->read_buf = devm_kzalloc(&pdev->dev, pdata->fifo_size_bytes, + GFP_KERNEL); + if (!pdata->read_buf) + return -ENOMEM; + + pdata->write_buf = devm_kzalloc(&pdev->dev, pdata->fifo_size_bytes, + GFP_KERNEL); + if (!pdata->write_buf) + return -ENOMEM; + + ret = fpga_dma_register_dbgfs(pdata); + if (ret) + return ret; + + pdata->pdev = pdev; + platform_set_drvdata(pdev, pdata); + + ret = fpga_dma_dma_init(pdata); + if (ret) { + fpga_dma_remove(pdev); + return ret; + } + + /* OK almost ready, set up the watermarks */ + /* we may need to tweak this for single/burst, etc */ + writel(pdata->fifo_depth - max_burst_words, + pdata->csr_reg + ALT_FPGADMA_CSR_WR_WTRMK); + /* we use read watermark of 0 so that rx_burst line + is always asserted, i.e. no single-only requests */ + writel(0, pdata->csr_reg + ALT_FPGADMA_CSR_RD_WTRMK); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id fpga_dma_of_match[] = { + {.compatible = "altr,fpga-dma",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, fpga_dma_of_match); +#endif + +static struct platform_driver fpga_dma_driver = { + .probe = fpga_dma_probe, + .remove = fpga_dma_remove, + .driver = { + .name = "fpga_dma", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fpga_dma_of_match), + }, +}; + +static int __init fpga_dma_init(void) +{ + return platform_driver_probe(&fpga_dma_driver, fpga_dma_probe); +} + +static void __exit fpga_dma_exit(void) +{ + platform_driver_unregister(&fpga_dma_driver); +} + +late_initcall(fpga_dma_init); +module_exit(fpga_dma_exit); + +MODULE_AUTHOR("Graham Moore (Altera)"); +MODULE_DESCRIPTION("Altera FPGA DMA Example Driver"); +MODULE_LICENSE("GPL v2"); From 85e275bdedbf22d8da7e28ed08f1acd524a85c80 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Tue, 29 Oct 2013 12:34:01 -0500 Subject: [PATCH 089/201] FogBugz #163712: Add SDRAM ECC/EDAC support to Altera CycloneV board Firmware to support the EDAC framework for the SDRAM ECC on the Altera CycloneV board is added in this patch. - The SDRAM Controller registers are used by the FPGA bridge so these are accessed through the syscon interface. - The interrupts are included in the sdramedac device tree node and accessed with the platform functions. - The configuration of the SDRAM memory size for the EDAC framework is discovered from the memory node of the device tree. - Documentation of the bindings in devicetree/bindings/arm/altera V2: Implement review changes. - Fix copyright. - Call edac_mc_free() if error on devres_open_group(). - Remove pr_debugs. - Fix spacing in calculation for readability. - Add platform_set_drvdata() in alt_sdram_mc_remove(). - Add of_match_ptr() around .of_match_table element. V3: - Replace pr_debug() with dev_dbg(). - Remove /r line endings. V4: - Check return code of regmap_read() in probe. V5: - Clear regmap_read() variables before use. - Check return code when looking for memory size. - Include attribution in copyright. - Enable IRQ after successfully adding memory controller. V6: - Rename functions and macros from ALT to ALTR - Cleanup of code to reflect Linux conventions. - Replace MODULE_ALIAS with MODULE_AUTHOR("Altera Corporation"); Signed-off-by: Thor Thayer --- .../arm/altera/socfpga-sdram-edac.txt | 12 + arch/arm/boot/dts/socfpga.dtsi | 5 + drivers/edac/Kconfig | 9 + drivers/edac/Makefile | 2 + drivers/edac/altera_mc_edac.c | 295 ++++++++++++++++++ 5 files changed, 323 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-sdram-edac.txt create mode 100644 drivers/edac/altera_mc_edac.c diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-sdram-edac.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-sdram-edac.txt new file mode 100644 index 0000000000000..9348c53f063d8 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-sdram-edac.txt @@ -0,0 +1,12 @@ +Altera SOCFPGA SDRAM Error Detection & Correction [EDAC] + +Required properties: +- compatible : should contain "altr,sdr-edac"; +- interrupts : Should contain the SDRAM ECC IRQ in the + appropriate format for the IRQ controller. + +Example: + sdramedac@0 { + compatible = "altr,sdram-edac"; + interrupts = <0 39 4>; + }; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 857748686e206..cc56ef479f74e 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -713,6 +713,11 @@ reg = <0xffc25000 0x1000>; }; + sdramedac@0 { + compatible = "altr,sdram-edac"; + interrupts = <0 39 4>; + }; + l3regs@0xff800000 { compatible = "altr,l3regs", "syscon"; reg = <0xff800000 0x1000>; diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 878f09005fad9..4f4d3798f5186 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -368,4 +368,13 @@ config EDAC_OCTEON_PCI Support for error detection and correction on the Cavium Octeon family of SOCs. +config EDAC_ALTERA_MC + bool "Altera SDRAM Memory Controller EDAC" + depends on EDAC_MM_EDAC && ARCH_SOCFPGA + help + Support for error detection and correction on the + Altera SDRAM memory controller. Note that the + preloader must initialize the SDRAM before loading + the kernel. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 4154ed6a02c67..e15d05f3b53ee 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -64,3 +64,5 @@ obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o + +obj-$(CONFIG_EDAC_ALTERA_MC) += altera_mc_edac.o diff --git a/drivers/edac/altera_mc_edac.c b/drivers/edac/altera_mc_edac.c new file mode 100644 index 0000000000000..8fa00018a11ad --- /dev/null +++ b/drivers/edac/altera_mc_edac.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright 2011-2012 Calxeda, 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. + * + * 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. + * + * Adapted from the highbank_mc_edac driver + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "edac_core.h" +#include "edac_module.h" + +#define ALTR_EDAC_MOD_STR "altera_edac" + +/* SDRAM Controller CtrlCfg Register */ +#define ALTR_SDR_CTLCFG 0x00 + +/* SDRAM Controller CtrlCfg Register Bit Masks */ +#define ALTR_SDR_CTLCFG_ECC_EN 0x400 +#define ALTR_SDR_CTLCFG_ECC_CORR_EN 0x800 +#define ALTR_SDR_CTLCFG_GEN_SB_ERR 0x2000 +#define ALTR_SDR_CTLCFG_GEN_DB_ERR 0x4000 + +#define ALTR_SDR_CTLCFG_ECC_AUTO_EN (ALTR_SDR_CTLCFG_ECC_EN | \ + ALTR_SDR_CTLCFG_ECC_CORR_EN) + +/* SDRAM Controller DRAM Status Register */ +#define ALTR_SDR_DRAMSTS 0x38 + +/* SDRAM Controller DRAM Status Register Bit Masks */ +#define ALTR_SDR_DRAMSTS_SBEERR 0x04 +#define ALTR_SDR_DRAMSTS_DBEERR 0x08 +#define ALTR_SDR_DRAMSTS_CORR_DROP 0x10 + +/* SDRAM Controller DRAM IRQ Register */ +#define ALTR_SDR_DRAMINTR 0x3C + +/* SDRAM Controller DRAM IRQ Register Bit Masks */ +#define ALTR_SDR_DRAMINTR_INTREN 0x01 +#define ALTR_SDR_DRAMINTR_SBEMASK 0x02 +#define ALTR_SDR_DRAMINTR_DBEMASK 0x04 +#define ALTR_SDR_DRAMINTR_CORRDROPMASK 0x08 +#define ALTR_SDR_DRAMINTR_INTRCLR 0x10 + +/* SDRAM Controller Single Bit Error Count Register */ +#define ALTR_SDR_SBECOUNT 0x40 + +/* SDRAM Controller Single Bit Error Count Register Bit Masks */ +#define ALTR_SDR_SBECOUNT_COUNT 0x0F + +/* SDRAM Controller Double Bit Error Count Register */ +#define ALTR_SDR_DBECOUNT 0x44 + +/* SDRAM Controller Double Bit Error Count Register Bit Masks */ +#define ALTR_SDR_DBECOUNT_COUNT 0x0F + +/* SDRAM Controller ECC Error Address Register */ +#define ALTR_SDR_ERRADDR 0x48 + +/* SDRAM Controller ECC Error Address Register Bit Masks */ +#define ALTR_SDR_ERRADDR_ADDR 0xFFFFFFFF + +/* SDRAM Controller ECC Autocorrect Drop Count Register */ +#define ALTR_SDR_DROPCOUNT 0x4C + +/* SDRAM Controller ECC Autocorrect Drop Count Register Bit Masks */ +#define ALTR_SDR_DROPCOUNT_CORR 0x0F + +/* SDRAM Controller ECC AutoCorrect Address Register */ +#define ALTR_SDR_DROPADDR 0x50 + +/* SDRAM Controller ECC AutoCorrect Error Address Register Bit Masks */ +#define ALTR_SDR_DROPADDR_ADDR 0xFFFFFFFF + +/* Altera SDRAM Memory Controller data */ +struct altr_sdram_mc_data { + struct regmap *mc_vbase; +}; + +static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) +{ + struct mem_ctl_info *mci = dev_id; + struct altr_sdram_mc_data *drvdata = mci->pvt_info; + u32 status = 0, err_count = 0, err_addr = 0; + + /* Error Address is the same for both SBE & DBE */ + regmap_read(drvdata->mc_vbase, ALTR_SDR_ERRADDR, &err_addr); + + regmap_read(drvdata->mc_vbase, ALTR_SDR_DRAMSTS, &status); + + if (status & ALTR_SDR_DRAMSTS_DBEERR) { + regmap_read(drvdata->mc_vbase, ALTR_SDR_DBECOUNT, &err_count); + panic("\nEDAC: [%d Uncorrectable errors @ 0x%08X]\n", + err_count, err_addr); + } + if (status & ALTR_SDR_DRAMSTS_SBEERR) { + regmap_read(drvdata->mc_vbase, ALTR_SDR_SBECOUNT, &err_count); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, err_count, + err_addr >> PAGE_SHIFT, + err_addr & ~PAGE_MASK, 0, + 0, 0, -1, mci->ctl_name, ""); + } + + regmap_write(drvdata->mc_vbase, ALTR_SDR_DRAMINTR, + (ALTR_SDR_DRAMINTR_INTRCLR | ALTR_SDR_DRAMINTR_INTREN)); + + return IRQ_HANDLED; +} + +/* Get total memory size from Open Firmware DTB */ +static u32 altr_sdram_get_total_mem_size(void) +{ + struct device_node *np; + u32 retcode, reg_array[2]; + + np = of_find_node_by_type(NULL, "memory"); + if (!np) + return 0; + + retcode = of_property_read_u32_array(np, "reg", + reg_array, ARRAY_SIZE(reg_array)); + + of_node_put(np); + + if (retcode) + return 0; + + return reg_array[1]; +} + +static int altr_sdram_mc_probe(struct platform_device *pdev) +{ + struct edac_mc_layer layers[2]; + struct mem_ctl_info *mci; + struct altr_sdram_mc_data *drvdata; + struct dimm_info *dimm; + u32 read_reg = 0, mem_size; + int irq; + int res = 0, retcode; + + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = 1; + layers[0].is_virt_csrow = true; + layers[1].type = EDAC_MC_LAYER_CHANNEL; + layers[1].size = 1; + layers[1].is_virt_csrow = false; + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct altr_sdram_mc_data)); + if (!mci) + return -ENOMEM; + + mci->pdev = &pdev->dev; + drvdata = mci->pvt_info; + platform_set_drvdata(pdev, mci); + + if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) { + edac_mc_free(mci); + return -ENOMEM; + } + + /* Grab the register values from the sdr-ctl in device tree */ + drvdata->mc_vbase = syscon_regmap_lookup_by_compatible("altr,sdr-ctl"); + if (IS_ERR(drvdata->mc_vbase)) { + dev_err(&pdev->dev, + "regmap for altr,sdr-ctl lookup failed.\n"); + res = -ENODEV; + goto err; + } + + retcode = regmap_read(drvdata->mc_vbase, ALTR_SDR_CTLCFG, &read_reg); + if (retcode || ((read_reg & ALTR_SDR_CTLCFG_ECC_AUTO_EN) != + ALTR_SDR_CTLCFG_ECC_AUTO_EN)) { + dev_err(&pdev->dev, "No ECC present / ECC disabled - 0x%08X\n", + read_reg); + res = -ENODEV; + goto err; + } + + mci->mtype_cap = MEM_FLAG_DDR3; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = ALTR_EDAC_MOD_STR; + mci->mod_ver = "1"; + mci->ctl_name = dev_name(&pdev->dev); + mci->scrub_mode = SCRUB_SW_SRC; + mci->dev_name = dev_name(&pdev->dev); + + /* Grab memory size from device tree. */ + mem_size = altr_sdram_get_total_mem_size(); + dev_dbg(&pdev->dev, "EDAC Memory Size = 0x%08x\n", mem_size); + dimm = *mci->dimms; + if (mem_size <= 0) { + dev_err(&pdev->dev, "Unable to find memory size (dts)\n"); + res = -ENODEV; + goto err; + } + dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1; + dimm->grain = 8; + dimm->dtype = DEV_X8; + dimm->mtype = MEM_DDR3; + dimm->edac_mode = EDAC_SECDED; + + res = edac_mc_add_mc(mci); + if (res < 0) + goto err; + + retcode = regmap_write(drvdata->mc_vbase, ALTR_SDR_DRAMINTR, + ALTR_SDR_DRAMINTR_INTRCLR); + if (retcode) { + dev_err(&pdev->dev, "Error clearning SDRAM ECC IRQ\n"); + res = -ENODEV; + goto err; + } + + irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler, + 0, dev_name(&pdev->dev), mci); + if (res < 0) { + dev_err(&pdev->dev, "Unable to request irq %d\n", irq); + res = -ENODEV; + goto err; + } + + retcode = regmap_write(drvdata->mc_vbase, ALTR_SDR_DRAMINTR, + (ALTR_SDR_DRAMINTR_INTRCLR | ALTR_SDR_DRAMINTR_INTREN)); + if (retcode) { + dev_err(&pdev->dev, "Error enabling SDRAM ECC IRQ\n"); + res = -ENODEV; + goto err; + } + + devres_close_group(&pdev->dev, NULL); + + return 0; + +err: + dev_err(&pdev->dev, "EDAC Probe Failed; Error %d\n", res); + devres_release_group(&pdev->dev, NULL); + edac_mc_free(mci); + + return res; +} + +static int altr_sdram_mc_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id altr_sdram_ctrl_of_match[] = { + { .compatible = "altr,sdram-edac", }, + {}, +}; +MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match); + +static struct platform_driver altr_sdram_mc_edac_driver = { + .probe = altr_sdram_mc_probe, + .remove = altr_sdram_mc_remove, + .driver = { + .name = "altr_sdram_mc_edac", + .of_match_table = of_match_ptr(altr_sdram_ctrl_of_match), + }, +}; + +module_platform_driver(altr_sdram_mc_edac_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Altera Corporation"); +MODULE_DESCRIPTION("EDAC Driver for Altera SDRAM Controller"); From 3478d7547896cdaa0dd8d4827316c98a00c951dd Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Thu, 31 Oct 2013 10:48:11 -0500 Subject: [PATCH 090/201] FogBugz #165063: SDRAM ECC Error Injection for Testing The current SDRAM ECC testing was performed by shorting an address line to ground. In the case of an uncorrectable error (2 bit error) 2 address lines had to be shorted to ground. The Altera SDRAM controller has the ability to inject ECC errors (single or double bit) during write operations. This change implements the injection of SDRAM ECC errors in software. - Requires that EDAC debugging (CONFIG_EDAC_DEBUG) is defined. - Single or double bit errors are generated depending upon string size sent (1 character = 1 bit error/2 char=2bit) - To inject a single bit error (correctable) echo "1" > /sys/kernel/debug/edac/mc0/inject_ctl - To inject a double bit error (uncorrectable - panic) echo "12" > /sys/kernel/debug/edac/mc0/inject_ctl V2: - Make double bit error case very restrictive Signed-off-by: Thor Thayer --- drivers/edac/altera_mc_edac.c | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/edac/altera_mc_edac.c b/drivers/edac/altera_mc_edac.c index 8fa00018a11ad..db4b950263e00 100644 --- a/drivers/edac/altera_mc_edac.c +++ b/drivers/edac/altera_mc_edac.c @@ -128,6 +128,67 @@ static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_EDAC_DEBUG +static ssize_t altr_sdr_mc_err_inject_write(struct file *file, + const char __user *data, + size_t count, loff_t *ppos) +{ + struct mem_ctl_info *mci = file->private_data; + struct altr_sdram_mc_data *drvdata = mci->pvt_info; + u32 *ptemp; + dma_addr_t dma_handle; + u32 reg, read_reg = 0; + + ptemp = dma_alloc_coherent(mci->pdev, 16, &dma_handle, GFP_KERNEL); + if (IS_ERR(ptemp)) { + dma_free_coherent(mci->pdev, 16, ptemp, dma_handle); + dev_err(mci->pdev, "**EDAC Inject: Buffer Allocation error\n"); + return -ENOMEM; + } + + regmap_read(drvdata->mc_vbase, ALTR_SDR_CTLCFG, &read_reg); + read_reg &= ~(ALTR_SDR_CTLCFG_GEN_SB_ERR | ALTR_SDR_CTLCFG_GEN_DB_ERR); + + if (count == 3) { + dev_alert(mci->pdev, "** EDAC Inject Double bit error\n"); + regmap_write(drvdata->mc_vbase, ALTR_SDR_CTLCFG, + (read_reg | ALTR_SDR_CTLCFG_GEN_DB_ERR)); + } else { + dev_alert(mci->pdev, "** EDAC Inject Single bit error\n"); + regmap_write(drvdata->mc_vbase, ALTR_SDR_CTLCFG, + (read_reg | ALTR_SDR_CTLCFG_GEN_SB_ERR)); + } + + ptemp[0] = 0x5A5A5A5A; + ptemp[1] = 0xA5A5A5A5; + regmap_write(drvdata->mc_vbase, ALTR_SDR_CTLCFG, read_reg); + wmb(); + + reg = ptemp[0]; + read_reg = ptemp[1]; + + dma_free_coherent(mci->pdev, 16, ptemp, dma_handle); + + return count; +} + +static const struct file_operations altr_sdr_mc_debug_inject_fops = { + .open = simple_open, + .write = altr_sdr_mc_err_inject_write, + .llseek = generic_file_llseek, +}; + +static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci) +{ + if (mci->debugfs) + debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci, + &altr_sdr_mc_debug_inject_fops); +} +#else +static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci) +{} +#endif + /* Get total memory size from Open Firmware DTB */ static u32 altr_sdram_get_total_mem_size(void) { @@ -250,6 +311,8 @@ static int altr_sdram_mc_probe(struct platform_device *pdev) goto err; } + altr_sdr_mc_create_debugfs_nodes(mci); + devres_close_group(&pdev->dev, NULL); return 0; From 0641ef1c41a30136bc8b1016662b11246a4b79c9 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Fri, 13 Dec 2013 14:42:47 -0600 Subject: [PATCH 091/201] FogBugz #173183: L2 EDAC addition for Altera SOCFPGA. Add L2 ECC to the Error Detection And Correction module for tracking ECC errors in Altera's SOCFPGA Level 2 cache. This patch makes the assumption that the L2 cache has been cleaned and ECC turned on before Linux is started (i.e. in the preloader). V2 Changes: - Implement dual interrupts as suggested in socfpga.dtsi. - Put include files in alphabetical order. - Abstract the module checks for future additions. - Correct the compatible string. - Remove dependency check of Parity. V3 Changes: - Fix Copyright in header. - Cleanup return codes. - Add panic to Double Bit Error. - Implement a cleaner way of getting ECC function pointers. Signed-off-by: Thor Thayer --- .../bindings/arm/altera/socfpga-l2-ecc.txt | 15 ++ arch/arm/boot/dts/socfpga.dtsi | 8 +- drivers/edac/Kconfig | 10 + drivers/edac/Makefile | 1 + drivers/edac/altera_ecc_mgr_edac.c | 235 ++++++++++++++++++ 5 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-l2-ecc.txt create mode 100644 drivers/edac/altera_ecc_mgr_edac.c diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-l2-ecc.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-l2-ecc.txt new file mode 100644 index 0000000000000..080525bab3f96 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-l2-ecc.txt @@ -0,0 +1,15 @@ +Altera SoCFPGA L2 cache Error Detection and Correction [EDAC] + +Required Properties: +- compatible : Should be "altr,l2-edac" +- reg : Address and size for ECC error interrupt clear registers. +- interrupts : Should be single bit error interrupt, then double bit error + interrupt. Note the rising edge type. + +Example: + + l2edac@xffd08140 { + compatible = "altr,l2-edac"; + reg = <0xffd08140 0x4>; + interrupts = <0 36 1>, <0 37 1>; + }; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index cc56ef479f74e..facbb9a0f2627 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -630,7 +630,7 @@ }; L2: l2-cache@fffef000 { - compatible = "arm,pl310-cache"; + compatible = "arm,pl310-cache", "syscon"; reg = <0xfffef000 0x1000>; interrupts = <0 38 0x04>; cache-unified; @@ -718,6 +718,12 @@ interrupts = <0 39 4>; }; + l2edac@xffd08140 { + compatible = "altr,l2-edac"; + reg = <0xffd08140 0x4>; + interrupts = <0 36 1>, <0 37 1>; + }; + l3regs@0xff800000 { compatible = "altr,l3regs", "syscon"; reg = <0xff800000 0x1000>; diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 4f4d3798f5186..9c5d57de71c05 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -377,4 +377,14 @@ config EDAC_ALTERA_MC preloader must initialize the SDRAM before loading the kernel. +config EDAC_ALTERA_ECC_MGR + bool "Altera ECC Manager for EDAC" + depends on EDAC_MM_EDAC && ARCH_SOCFPGA + help + Support for error detection and correction on the + Altera memories. Ensure these requirements are met. + -L2 Note that the preloader must initialize + the L2 memory & enable the L2 ECC before + loading the kernel. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index e15d05f3b53ee..da8dacfe74cd1 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -66,3 +66,4 @@ obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o obj-$(CONFIG_EDAC_ALTERA_MC) += altera_mc_edac.o +obj-$(CONFIG_EDAC_ALTERA_ECC_MGR) += altera_ecc_mgr_edac.o \ No newline at end of file diff --git a/drivers/edac/altera_ecc_mgr_edac.c b/drivers/edac/altera_ecc_mgr_edac.c new file mode 100644 index 0000000000000..73511d270c8b5 --- /dev/null +++ b/drivers/edac/altera_ecc_mgr_edac.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2013 Altera Corporation + * Copyright 2011-2012 Calxeda, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * Adapted from the highbank_l2_edac driver + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include + +#include "edac_core.h" +#include "edac_module.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* MPU L2 Register Defines */ +#define ALTR_MPUL2_CONTROL_OFFSET 0x100 +#define ALTR_MPUL2_CTL_CACHE_EN_MASK 0x00000001 + +/* L2 ECC Management Group Defines */ +#define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00 +#define ALTR_L2_ECC_EN_MASK 0x00000001 + +struct ecc_mgr_of_data { + int (*setup)(struct platform_device *pdev, void __iomem *base); +}; + +struct altr_ecc_mgr_dev { + void __iomem *base; + int sb_irq; + int db_irq; + const struct ecc_mgr_of_data *data; + char *edac_dev_name; +}; + +static irqreturn_t altr_ecc_mgr_handler(int irq, void *dev_id) +{ + struct edac_device_ctl_info *dci = dev_id; + struct altr_ecc_mgr_dev *drvdata = dci->pvt_info; + + if (irq == drvdata->sb_irq) + edac_device_handle_ce(dci, 0, 0, drvdata->edac_dev_name); + if (irq == drvdata->db_irq) { + edac_device_handle_ue(dci, 0, 0, drvdata->edac_dev_name); + panic("\nEDAC:ECC_MGR[Uncorrectable errors]\n"); + } + + return IRQ_HANDLED; +} + +/* + * altr_l2_dependencies() + * Test for L2 cache ECC dependencies upon entry because + * the preloader/UBoot should have initialized the L2 + * memory and enabled the ECC. + * Can't turn on ECC here because accessing un-initialized + * memory will cause CE/UE errors possibly causing an ABORT. + * Bail if ECC is not on. + * Test For 1) L2 ECC is enabled and 2) L2 Cache is enabled. + */ +static int altr_l2_dependencies(struct platform_device *pdev, + void __iomem *base) +{ + u32 control; + struct regmap *l2_vbase; + + control = readl(base) & ALTR_L2_ECC_EN_MASK; + if (!control) { + dev_err(&pdev->dev, "L2: No ECC present, or ECC disabled\n"); + return -ENODEV; + } + + l2_vbase = syscon_regmap_lookup_by_compatible("arm,pl310-cache"); + if (IS_ERR(l2_vbase)) { + dev_err(&pdev->dev, + "L2 ECC:regmap for arm,pl310-cache lookup failed.\n"); + return -ENODEV; + } + + regmap_read(l2_vbase, ALTR_MPUL2_CONTROL_OFFSET, &control); + if (!(control & ALTR_MPUL2_CTL_CACHE_EN_MASK)) { + dev_err(&pdev->dev, "L2: Cache disabled\n"); + return -ENODEV; + } + + return 0; +} + +static const struct ecc_mgr_of_data l2ecc_data = { + .setup = altr_l2_dependencies, +}; + +static const struct of_device_id altr_ecc_mgr_of_match[] = { + { .compatible = "altr,l2-edac", .data = (void *)&l2ecc_data }, + {}, +}; + +/* + * altr_ecc_mgr_probe() + * This is a generic EDAC device driver that will support + * various Altera memory devices such as the L2 cache ECC and + * OCRAM ECC as well as the memories for other peripherals. + * Module specific initialization is done by passing the + * function index in the device tree. + */ +static int altr_ecc_mgr_probe(struct platform_device *pdev) +{ + struct edac_device_ctl_info *dci; + struct altr_ecc_mgr_dev *drvdata; + struct resource *r; + int res = 0; + struct device_node *np = pdev->dev.of_node; + char *ecc_name = (char *)np->name; + + dci = edac_device_alloc_ctl_info(sizeof(*drvdata), "ecc", + 1, ecc_name, 1, 0, NULL, 0, 0); + + if (!dci) + return -ENOMEM; + + drvdata = dci->pvt_info; + dci->dev = &pdev->dev; + platform_set_drvdata(pdev, dci); + drvdata->edac_dev_name = ecc_name; + + if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "%s:Unable to get mem resource\n", + ecc_name); + res = -ENODEV; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "%s:Error requesting mem region\n", + ecc_name); + res = -EBUSY; + goto err; + } + + drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); + if (!drvdata->base) { + dev_err(&pdev->dev, "%s:Unable to map regs\n", ecc_name); + res = -ENOMEM; + goto err; + } + + /* Check specific dependencies for the module */ + drvdata->data = of_match_node(altr_ecc_mgr_of_match, np)->data; + if (drvdata->data->setup) { + res = drvdata->data->setup(pdev, drvdata->base); + if (res < 0) + goto err; + } + + drvdata->sb_irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, drvdata->sb_irq, + altr_ecc_mgr_handler, + 0, dev_name(&pdev->dev), dci); + if (res < 0) + goto err; + + drvdata->db_irq = platform_get_irq(pdev, 1); + res = devm_request_irq(&pdev->dev, drvdata->db_irq, + altr_ecc_mgr_handler, + 0, dev_name(&pdev->dev), dci); + if (res < 0) + goto err; + + dci->mod_name = "ECC_MGR"; + dci->dev_name = drvdata->edac_dev_name; + + if (edac_device_add_device(dci)) + goto err; + + devres_close_group(&pdev->dev, NULL); + + return 0; +err: + devres_release_group(&pdev->dev, NULL); + edac_device_free_ctl_info(dci); + + return res; +} + +static int altr_ecc_mgr_remove(struct platform_device *pdev) +{ + struct edac_device_ctl_info *dci = platform_get_drvdata(pdev); + + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(dci); + + return 0; +} + +static struct platform_driver altr_ecc_mgr_driver = { + .probe = altr_ecc_mgr_probe, + .remove = altr_ecc_mgr_remove, + .driver = { + .name = "altr_ecc_mgr", + .of_match_table = of_match_ptr(altr_ecc_mgr_of_match), + }, +}; + +MODULE_DEVICE_TABLE(of, altr_ecc_mgr_of_match); + +module_platform_driver(altr_ecc_mgr_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Altera Corporation"); +MODULE_DESCRIPTION("EDAC Driver for Altera SoC L2 Cache"); From 13f4e619cd6e5e4b5039efe7b2db22f6525192c2 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Wed, 18 Dec 2013 15:55:56 -0600 Subject: [PATCH 092/201] FogBugz #173184: Add L2 EDAC error injection for testing. Add L2 ECC error injection to the existing L2 implementation for testing purposes. The trigger is added to the /sysfs. V2 - Cleanup according to review comments. Signed-off-by: Thor Thayer --- drivers/edac/altera_ecc_mgr_edac.c | 88 ++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/drivers/edac/altera_ecc_mgr_edac.c b/drivers/edac/altera_ecc_mgr_edac.c index 73511d270c8b5..b43f451616ecf 100644 --- a/drivers/edac/altera_ecc_mgr_edac.c +++ b/drivers/edac/altera_ecc_mgr_edac.c @@ -40,6 +40,8 @@ /* L2 ECC Management Group Defines */ #define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00 #define ALTR_L2_ECC_EN_MASK 0x00000001 +#define ALTR_L2_ECC_INJS_MASK 0x00000002 +#define ALTR_L2_ECC_INJD_MASK 0x00000004 struct ecc_mgr_of_data { int (*setup)(struct platform_device *pdev, void __iomem *base); @@ -68,6 +70,90 @@ static irqreturn_t altr_ecc_mgr_handler(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_EDAC_DEBUG +static ssize_t altr_l2_ecc_trig(struct edac_device_ctl_info *edac_dci, + const char *buffer, size_t count) +{ + u32 *ptemp, i, error_mask; + int result = 0; + unsigned long flags; + struct altr_ecc_mgr_dev *drvdata = edac_dci->pvt_info; + + ptemp = kmalloc(5000, GFP_KERNEL); + if (!ptemp) { + dev_err(edac_dci->dev, + "**EDAC L2 Inject: Buffer Allocation error\n"); + return -ENOMEM; + } + + memset(ptemp, 0, 5000); + wmb(); + flush_cache_all(); + + if (count == 3) + error_mask = ALTR_L2_ECC_EN_MASK | ALTR_L2_ECC_INJD_MASK; + else + error_mask = ALTR_L2_ECC_EN_MASK | ALTR_L2_ECC_INJS_MASK; + + dev_alert(edac_dci->dev, "%s: Trigger Error Mask (0x%X)\n", + __func__, error_mask); + + local_irq_save(flags); + /* write data out which should be corrupted. */ + for (i = 0; i < 16; i++) { + /* Read data out so we're in the correct state */ + if (ptemp[i]) + result = -1; + rmb(); + /* Toggle Error bit (it is latched) */ + writel(error_mask, drvdata->base); + writel(ALTR_L2_ECC_EN_MASK, drvdata->base); + ptemp[i] = i; + } + wmb(); + local_irq_restore(flags); + + if (result) + dev_alert(edac_dci->dev, "%s: Mem Not Cleared (%d)\n", + __func__, result); + + result = 0; + /* Read out written data. ECC error caused here */ + for (i = 0; i < 16; i++) + if (ptemp[i] != i) + result = -1; + rmb(); + + kfree(ptemp); + + if (result) + dev_alert(edac_dci->dev, "%s: Trigger Match Error (%d)\n", + __func__, result); + + return count; +} + +static struct edac_dev_sysfs_attribute altr_l2_sysfs_attributes[] = { + { + .attr = { .name = "altr_l2_trigger", + .mode = (S_IRUGO | S_IWUSR) }, + .show = NULL, + .store = altr_l2_ecc_trig + }, + { + .attr = {.name = NULL } + } +}; + +static void altr_set_sysfs_attr(struct edac_device_ctl_info *edac_dci) +{ + edac_dci->sysfs_attributes = altr_l2_sysfs_attributes; +} +#else +static void altr_set_sysfs_attr(struct edac_device_ctl_info *edac_dci) +{} +#endif + /* * altr_l2_dependencies() * Test for L2 cache ECC dependencies upon entry because @@ -194,6 +280,8 @@ static int altr_ecc_mgr_probe(struct platform_device *pdev) dci->mod_name = "ECC_MGR"; dci->dev_name = drvdata->edac_dev_name; + altr_set_sysfs_attr(dci); + if (edac_device_add_device(dci)) goto err; From d95a3b5e4c7a74c77804c3d4a0b9967ba0c04137 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Wed, 15 Jan 2014 18:13:00 -0600 Subject: [PATCH 093/201] FogBugz #178128: Conditionally enable L2 EDAC. Currently the L2 cache is the only ECC module implemented in the ECC Manager. However, as more modules are added, the need to turn each module on & off is required. The addition of conditional compiles based on a new CONFIG (L2 ECC) is added here. V2: Include dependency on CONFIG_CACHE_L2X0 Signed-off-by: Thor Thayer --- drivers/edac/Kconfig | 16 ++++++++++++---- drivers/edac/altera_ecc_mgr_edac.c | 4 +++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 9c5d57de71c05..5d1251037df69 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -382,9 +382,17 @@ config EDAC_ALTERA_ECC_MGR depends on EDAC_MM_EDAC && ARCH_SOCFPGA help Support for error detection and correction on the - Altera memories. Ensure these requirements are met. - -L2 Note that the preloader must initialize - the L2 memory & enable the L2 ECC before - loading the kernel. + Altera memories. Submodules are individually + configurable. + +config EDAC_ALTERA_L2_ECC + bool "Altera L2 Cache EDAC" + depends on EDAC_ALTERA_ECC_MGR && CACHE_L2X0 + help + Support for error detection and correction on the + Altera L2 cache memory. + Ensure the preloader is configured to initialize + the L2 memory & enable the L2 ECC before loading + the kernel. endif # EDAC diff --git a/drivers/edac/altera_ecc_mgr_edac.c b/drivers/edac/altera_ecc_mgr_edac.c index b43f451616ecf..a714c168257f2 100644 --- a/drivers/edac/altera_ecc_mgr_edac.c +++ b/drivers/edac/altera_ecc_mgr_edac.c @@ -70,7 +70,7 @@ static irqreturn_t altr_ecc_mgr_handler(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_EDAC_DEBUG +#if defined(CONFIG_EDAC_DEBUG) && defined(CONFIG_EDAC_ALTERA_L2_ECC) static ssize_t altr_l2_ecc_trig(struct edac_device_ctl_info *edac_dci, const char *buffer, size_t count) { @@ -197,7 +197,9 @@ static const struct ecc_mgr_of_data l2ecc_data = { }; static const struct of_device_id altr_ecc_mgr_of_match[] = { +#ifdef CONFIG_EDAC_ALTERA_L2_ECC { .compatible = "altr,l2-edac", .data = (void *)&l2ecc_data }, +#endif {}, }; From 92b4c44c1d96e170ac63f2337bd3393d23ce1a95 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Thu, 19 Dec 2013 17:33:11 -0600 Subject: [PATCH 094/201] FogBugz #173185: OCRAM ECC addition for Altera SOCFPGA. Add On-Chip RAM ECC to the Error Detection And Correction module for tracking ECC errors in Altera's SOCFPGA OCRAM. This patch adds code to the kernel startup that initializes OCRAM and ensures the ECC is enabled. The generic EDAC Manager driver handles the addition of OCRAM to the driver when the OCRAM "compatible" field in the device tree matches. Specific data is loaded depending upon which type of ECC (L2 or OCRAM) is being initalized. V2 Changes: - Fix error message in OCRAM initialization. - Document the device tree bindings for OCRAM & OCRAM ECC. - Change variable name from sram to iram for clarity. - Resync with main since EDAC L2 trigger was added. V3 Changes: - Remove OCRAM device tree binding file. - Add conditional compile statements for L2 clear in socfpga.c V4 Changes: - Add OCRAM only config. - Split OCRAM initialization into own files (ocram.c & ocram.h) - Conditionally compile OCRAM matching tag. V5 Changes: - Cleanup of ocramedac address in socfpga.dtsi - Use pr_err() consistently in ocram.c V6 Changes: - Cleanup of ocramedac address in socfpga-ocram-ecc.txt. - Remove unused .setup function pointer. Signed-off-by: Thor Thayer --- .../bindings/arm/altera/socfpga-ocram-ecc.txt | 16 ++++ arch/arm/boot/dts/socfpga.dtsi | 7 ++ arch/arm/mach-socfpga/Makefile | 1 + arch/arm/mach-socfpga/ocram.c | 85 +++++++++++++++++++ arch/arm/mach-socfpga/ocram.h | 30 +++++++ arch/arm/mach-socfpga/socfpga.c | 2 + drivers/edac/Kconfig | 7 ++ drivers/edac/altera_ecc_mgr_edac.c | 32 ++++++- 8 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/altera/socfpga-ocram-ecc.txt create mode 100644 arch/arm/mach-socfpga/ocram.c create mode 100644 arch/arm/mach-socfpga/ocram.h diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-ocram-ecc.txt b/Documentation/devicetree/bindings/arm/altera/socfpga-ocram-ecc.txt new file mode 100644 index 0000000000000..31ab205a7a517 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/altera/socfpga-ocram-ecc.txt @@ -0,0 +1,16 @@ +Altera SoCFPGA On-Chip RAM Error Detection and Correction [EDAC] + +OCRAM ECC Required Properties: +- compatible : Should be "altr,ocram-edac" +- reg : Address and size for ECC error interrupt clear registers. +- iram : phandle to On-Chip RAM definition. +- interrupts : Should be single bit error interrupt, then double bit error + interrupt. Note the rising edge type. + +Example: + ocramedac@ffd08144 { + compatible = "altr,ocram-edac"; + reg = <0xffd08144 0x4>; + iram = <&ocram>; + interrupts = <0 178 1>, <0 179 1>; + }; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index facbb9a0f2627..e92a3d44bbe00 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -724,6 +724,13 @@ interrupts = <0 36 1>, <0 37 1>; }; + ocramedac@ffd08144 { + compatible = "altr,ocram-edac"; + reg = <0xffd08144 0x4>; + iram = <&ocram>; + interrupts = <0 178 1>, <0 179 1>; + }; + l3regs@0xff800000 { compatible = "altr,l3regs", "syscon"; reg = <0xff800000 0x1000>; diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile index 1ada39d6e953f..4e31896555887 100644 --- a/arch/arm/mach-socfpga/Makefile +++ b/arch/arm/mach-socfpga/Makefile @@ -6,3 +6,4 @@ obj-y := socfpga.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o obj-$(CONFIG_HW_PERF_EVENTS) += socfpga_cti.o obj-$(CONFIG_FPGADMA) += fpga-dma.o +obj-$(CONFIG_EDAC_ALTERA_OCRAM_ECC) += ocram.o diff --git a/arch/arm/mach-socfpga/ocram.c b/arch/arm/mach-socfpga/ocram.c new file mode 100644 index 0000000000000..24d60c2375113 --- /dev/null +++ b/arch/arm/mach-socfpga/ocram.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 Altera Corporation + * + * 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 . + */ +#include +#include +#include + +void socfpga_init_ocram_ecc(void) +{ + struct device_node *np; + const __be32 *prop; + u32 ocr_edac_addr, iram_addr, len; + void __iomem *mapped_ocr_edac_addr; + size_t size; + struct gen_pool *gp; + + np = of_find_compatible_node(NULL, NULL, "altr,ocram-edac"); + if (!np) { + pr_err("SOCFPGA: Unable to find altr,ocram-edac in dtb\n"); + return; + } + + prop = of_get_property(np, "reg", &size); + ocr_edac_addr = be32_to_cpup(prop++); + len = be32_to_cpup(prop); + if (!prop || size < sizeof(*prop)) { + pr_err("SOCFPGA: Unable to find OCRAM ECC mapping in dtb\n"); + return; + } + + gp = of_get_named_gen_pool(np, "iram", 0); + if (!gp) { + pr_err("SOCFPGA: OCRAM cannot find gen pool\n"); + return; + } + + np = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!np) { + pr_err("SOCFPGA: Unable to find mmio-sram in dtb\n"); + return; + } + /* Determine the OCRAM address and size */ + prop = of_get_property(np, "reg", &size); + iram_addr = be32_to_cpup(prop++); + len = be32_to_cpup(prop); + + if (!prop || size < sizeof(*prop)) { + pr_err("SOCFPGA: Unable to find OCRAM mapping in dtb\n"); + return; + } + + iram_addr = gen_pool_alloc(gp, len); + if (iram_addr == 0) { + pr_err("SOCFPGA: cannot alloc from gen pool\n"); + return; + } + + memset((void *)iram_addr, 0, len); + + mapped_ocr_edac_addr = ioremap(ocr_edac_addr, 4); + + gen_pool_free(gp, iram_addr, len); + + /* Clear any pending OCRAM ECC interrupts, then enable ECC */ + writel(0x18, mapped_ocr_edac_addr); + writel(0x19, mapped_ocr_edac_addr); + + pr_alert("SOCFPGA: Success Initializing OCRAM"); + + return; +} + diff --git a/arch/arm/mach-socfpga/ocram.h b/arch/arm/mach-socfpga/ocram.h new file mode 100644 index 0000000000000..3c6eb6990663c --- /dev/null +++ b/arch/arm/mach-socfpga/ocram.h @@ -0,0 +1,30 @@ +/* + * arch/arm/mach-socfpga/ocram.h + * + * Copyright (C) 2014 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef MACH_SOCFPGA_OCRAM_H +#define MACH_SOCFPGA_OCRAM_H + +#ifdef CONFIG_EDAC_ALTERA_OCRAM_ECC +void socfpga_init_ocram_ecc(void); +#else +inline void socfpga_init_ocram_ecc(void) +{ +} +#endif + +#endif /* #ifndef MACH_SOCFPGA_OCRAM_H */ diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index 258018ce0e7e3..c21e6256c4f0d 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -29,6 +29,7 @@ #include "core.h" #include "socfpga_cti.h" +#include "ocram.h" void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE)); void __iomem *sys_manager_base_addr; @@ -202,6 +203,7 @@ static void __init socfpga_cyclone5_init(void) socfpga_auxdata_lookup, NULL); enable_periphs(); socfpga_soc_device_init(); + socfpga_init_ocram_ecc(); } static const char *altera_dt_match[] = { diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 5d1251037df69..a61e0a6f65b87 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -395,4 +395,11 @@ config EDAC_ALTERA_L2_ECC the L2 memory & enable the L2 ECC before loading the kernel. +config EDAC_ALTERA_OCRAM_ECC + bool "Altera On-Chip RAM EDAC" + depends on EDAC_ALTERA_ECC_MGR + help + Support for error detection and correction on the + Altera On-Chip RAM Memory. + endif # EDAC diff --git a/drivers/edac/altera_ecc_mgr_edac.c b/drivers/edac/altera_ecc_mgr_edac.c index a714c168257f2..3a1bc3cf6dcea 100644 --- a/drivers/edac/altera_ecc_mgr_edac.c +++ b/drivers/edac/altera_ecc_mgr_edac.c @@ -43,8 +43,16 @@ #define ALTR_L2_ECC_INJS_MASK 0x00000002 #define ALTR_L2_ECC_INJD_MASK 0x00000004 +/* OCRAM ECC Management Group Defines */ +#define ALTR_MAN_GRP_OCRAM_ECC_OFFSET 0x04 +#define ALTR_OCR_ECC_EN_MASK 0x00000001 +#define ALTR_OCR_ECC_SERR_MASK 0x00000008 +#define ALTR_OCR_ECC_DERR_MASK 0x00000010 + struct ecc_mgr_of_data { int (*setup)(struct platform_device *pdev, void __iomem *base); + int ce_clear_mask; + int ue_clear_mask; }; struct altr_ecc_mgr_dev { @@ -60,9 +68,14 @@ static irqreturn_t altr_ecc_mgr_handler(int irq, void *dev_id) struct edac_device_ctl_info *dci = dev_id; struct altr_ecc_mgr_dev *drvdata = dci->pvt_info; - if (irq == drvdata->sb_irq) + if (irq == drvdata->sb_irq) { + if (drvdata->data->ce_clear_mask) + writel(drvdata->data->ce_clear_mask, drvdata->base); edac_device_handle_ce(dci, 0, 0, drvdata->edac_dev_name); + } if (irq == drvdata->db_irq) { + if (drvdata->data->ue_clear_mask) + writel(drvdata->data->ue_clear_mask, drvdata->base); edac_device_handle_ue(dci, 0, 0, drvdata->edac_dev_name); panic("\nEDAC:ECC_MGR[Uncorrectable errors]\n"); } @@ -194,11 +207,21 @@ static int altr_l2_dependencies(struct platform_device *pdev, static const struct ecc_mgr_of_data l2ecc_data = { .setup = altr_l2_dependencies, + .ce_clear_mask = 0, + .ue_clear_mask = 0, +}; + +static const struct ecc_mgr_of_data ocramecc_data = { + .ce_clear_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_SERR_MASK), + .ue_clear_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_DERR_MASK), }; static const struct of_device_id altr_ecc_mgr_of_match[] = { #ifdef CONFIG_EDAC_ALTERA_L2_ECC { .compatible = "altr,l2-edac", .data = (void *)&l2ecc_data }, +#endif +#ifdef CONFIG_EDAC_ALTERA_OCRAM_ECC + { .compatible = "altr,ocram-edac", .data = (void *)&ocramecc_data }, #endif {}, }; @@ -219,9 +242,10 @@ static int altr_ecc_mgr_probe(struct platform_device *pdev) int res = 0; struct device_node *np = pdev->dev.of_node; char *ecc_name = (char *)np->name; + static int dev_instance; - dci = edac_device_alloc_ctl_info(sizeof(*drvdata), "ecc", - 1, ecc_name, 1, 0, NULL, 0, 0); + dci = edac_device_alloc_ctl_info(sizeof(*drvdata), ecc_name, + 1, ecc_name, 1, 0, NULL, 0, dev_instance++); if (!dci) return -ENOMEM; @@ -322,4 +346,4 @@ module_platform_driver(altr_ecc_mgr_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Altera Corporation"); -MODULE_DESCRIPTION("EDAC Driver for Altera SoC L2 Cache"); +MODULE_DESCRIPTION("EDAC Driver for Altera SoC ECC Manager"); From 92a4c61ef3fff84fa0dcd021a9aea19a9f37dc87 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Fri, 31 Jan 2014 11:26:43 -0600 Subject: [PATCH 095/201] FogBugz #180994: Conditionally enable L2 cache ECC on startup Due to a misunderstanding in the preloader/U-Boot handoff process the L2 ECC was originally enabled in preloader. The fact that L2 cache is disabled upon entry into Linux means that the L2 cache ECC can be enabled upon Linux startup. This patch enables L2 ECC early in the startup before the L2 cache is enabled. V2 - Leave aux_ctl initialization alone. - Use of_iomap() function. Signed-off-by: Thor Thayer --- arch/arm/mach-socfpga/Makefile | 2 ++ arch/arm/mach-socfpga/l2_cache.c | 45 ++++++++++++++++++++++++++++++++ arch/arm/mach-socfpga/l2_cache.h | 30 +++++++++++++++++++++ arch/arm/mach-socfpga/socfpga.c | 1 + 4 files changed, 78 insertions(+) create mode 100644 arch/arm/mach-socfpga/l2_cache.c create mode 100644 arch/arm/mach-socfpga/l2_cache.h diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile index 4e31896555887..36d508ce59b01 100644 --- a/arch/arm/mach-socfpga/Makefile +++ b/arch/arm/mach-socfpga/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_SMP) += headsmp.o platsmp.o obj-$(CONFIG_HW_PERF_EVENTS) += socfpga_cti.o obj-$(CONFIG_FPGADMA) += fpga-dma.o obj-$(CONFIG_EDAC_ALTERA_OCRAM_ECC) += ocram.o +obj-$(CONFIG_EDAC_ALTERA_L2_ECC) += l2_cache.o + diff --git a/arch/arm/mach-socfpga/l2_cache.c b/arch/arm/mach-socfpga/l2_cache.c new file mode 100644 index 0000000000000..9ced0be62ec61 --- /dev/null +++ b/arch/arm/mach-socfpga/l2_cache.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Altera Corporation + * + * 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 . + */ +#include +#include +#include + +void socfpga_init_l2_ecc(void) +{ + struct device_node *np; + void __iomem *mapped_l2_edac_addr; + + np = of_find_compatible_node(NULL, NULL, "altr,l2-edac"); + if (!np) { + pr_err("SOCFPGA: Unable to find altr,l2-edac in dtb\n"); + return; + } + + mapped_l2_edac_addr = of_iomap(np, 0); + if (!mapped_l2_edac_addr) { + pr_err("SOCFPGA: Unable to find L2 ECC mapping in dtb\n"); + return; + } + + /* Enable ECC */ + writel(0x01, mapped_l2_edac_addr); + + pr_alert("SOCFPGA: Success Initializing L2 cache ECC"); + + return; +} + diff --git a/arch/arm/mach-socfpga/l2_cache.h b/arch/arm/mach-socfpga/l2_cache.h new file mode 100644 index 0000000000000..34b7578921009 --- /dev/null +++ b/arch/arm/mach-socfpga/l2_cache.h @@ -0,0 +1,30 @@ +/* + * arch/arm/mach-socfpga/l2_cache.h + * + * Copyright (C) 2014 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef MACH_SOCFPGA_L2_CACHE_H +#define MACH_SOCFPGA_L2_CACHE_H + +#ifdef CONFIG_EDAC_ALTERA_L2_ECC +void socfpga_init_l2_ecc(void); +#else +inline void socfpga_init_l2_ecc(void) +{ +} +#endif + +#endif /* #ifndef MACH_SOCFPGA_L2_CACHE_H */ diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index c21e6256c4f0d..e22dfaa03900a 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -29,6 +29,7 @@ #include "core.h" #include "socfpga_cti.h" +#include "l2_cache.h" #include "ocram.h" void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE)); From ddd32bef68e7b39f3f4c9d0af07f782a269beef2 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Wed, 22 Jan 2014 18:12:28 -0600 Subject: [PATCH 096/201] FogBugz #179457: Abstract EDAC module triggers The EDAC module only has 1 trigger at this time(L2). Additional triggers will be added for testing the ECC functionality of future modules. The EDAC triggers can be abstracted becaue each trigger needs the 1)allocate memory, 2)perform the trigger, and then 3)free memory. V2: Breaking the modules out into separate files makes the EDAC device manager code cleaner and more modular for possible future ECC additions. V3: Cleanup of code including the following: - Fix order of header files. - Add linefeed to end of makefile. - Use devm_kzalloc() to free memory automatically. - Use new structure pointer for cleaner code. V4: - Fix free() test. Signed-off-by: Thor Thayer --- drivers/edac/Makefile | 5 +- drivers/edac/altera_ecc_l2.c | 128 +++++++++++++++++++++++ drivers/edac/altera_ecc_mgr_edac.c | 161 +++++++---------------------- drivers/edac/altera_ecc_ocram.c | 32 ++++++ drivers/edac/altera_edac.h | 55 ++++++++++ 5 files changed, 257 insertions(+), 124 deletions(-) create mode 100644 drivers/edac/altera_ecc_l2.c create mode 100644 drivers/edac/altera_ecc_ocram.c create mode 100644 drivers/edac/altera_edac.h diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index da8dacfe74cd1..c98fe600963cd 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -66,4 +66,7 @@ obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o obj-$(CONFIG_EDAC_ALTERA_MC) += altera_mc_edac.o -obj-$(CONFIG_EDAC_ALTERA_ECC_MGR) += altera_ecc_mgr_edac.o \ No newline at end of file +obj-$(CONFIG_EDAC_ALTERA_ECC_MGR) += altera_ecc_mgr_edac.o +obj-$(CONFIG_EDAC_ALTERA_L2_ECC) += altera_ecc_l2.o +obj-$(CONFIG_EDAC_ALTERA_OCRAM_ECC) += altera_ecc_ocram.o + diff --git a/drivers/edac/altera_ecc_l2.c b/drivers/edac/altera_ecc_l2.c new file mode 100644 index 0000000000000..2fe31a818aad1 --- /dev/null +++ b/drivers/edac/altera_ecc_l2.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2014 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "altera_edac.h" +#include "edac_core.h" +#include "edac_module.h" + +/* MPU L2 Register Defines */ +#define ALTR_MPUL2_CONTROL_OFFSET 0x100 +#define ALTR_MPUL2_CTL_CACHE_EN_MASK 0x00000001 + +/* L2 ECC Management Group Defines */ +#define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00 +#define ALTR_L2_ECC_EN_MASK 0x00000001 +#define ALTR_L2_ECC_INJS_MASK 0x00000002 +#define ALTR_L2_ECC_INJD_MASK 0x00000004 + +#ifdef CONFIG_EDAC_DEBUG +static void *l2_init_mem(size_t size, void **other) +{ + struct device *dev = *other; + void *ptemp = devm_kzalloc(dev, size, GFP_KERNEL); + if (!ptemp) + return NULL; + + wmb(); + flush_cache_all(); + + return ptemp; +} + +static void l2_free_mem(void *p, void *other) +{ + struct device *dev = other; + if (dev && p) + devm_kfree(dev, p); +} + +static struct edac_dev_sysfs_attribute altr_l2_sysfs_attributes[] = { + { + .attr = { .name = "altr_l2_trigger", + .mode = (S_IRUGO | S_IWUSR) }, + .show = NULL, + .store = altr_ecc_mgr_trig + }, + { + .attr = {.name = NULL } + } +}; +#endif /* #ifdef CONFIG_EDAC_DEBUG */ + +/* + * altr_l2_dependencies() + * Test for L2 cache ECC dependencies upon entry because + * the preloader/UBoot should have initialized the L2 + * memory and enabled the ECC. + * Can't turn on ECC here because accessing un-initialized + * memory will cause CE/UE errors possibly causing an ABORT. + * Bail if ECC is not on. + * Test For 1) L2 ECC is enabled and 2) L2 Cache is enabled. + */ +static int altr_l2_dependencies(struct platform_device *pdev, + void __iomem *base) +{ + u32 control; + struct regmap *l2_vbase; + + control = readl(base) & ALTR_L2_ECC_EN_MASK; + if (!control) { + dev_err(&pdev->dev, "L2: No ECC present, or ECC disabled\n"); + return -ENODEV; + } + + l2_vbase = syscon_regmap_lookup_by_compatible("arm,pl310-cache"); + if (IS_ERR(l2_vbase)) { + dev_err(&pdev->dev, + "L2 ECC:regmap for arm,pl310-cache lookup failed.\n"); + return -ENODEV; + } + + regmap_read(l2_vbase, ALTR_MPUL2_CONTROL_OFFSET, &control); + if (!(control & ALTR_MPUL2_CTL_CACHE_EN_MASK)) { + dev_err(&pdev->dev, "L2: Cache disabled\n"); + return -ENODEV; + } + + return 0; +} + +const struct ecc_mgr_prv_data l2ecc_data = { + .setup = altr_l2_dependencies, + .ce_clear_mask = 0, + .ue_clear_mask = 0, +#ifdef CONFIG_EDAC_DEBUG + .eccmgr_sysfs_attr = altr_l2_sysfs_attributes, + .init_mem = l2_init_mem, + .free_mem = l2_free_mem, + .ecc_enable_mask = ALTR_L2_ECC_EN_MASK, + .ce_set_mask = (ALTR_L2_ECC_EN_MASK | ALTR_L2_ECC_INJS_MASK), + .ue_set_mask = (ALTR_L2_ECC_EN_MASK | ALTR_L2_ECC_INJD_MASK), + .trig_alloc_sz = 5000, +#endif +}; + diff --git a/drivers/edac/altera_ecc_mgr_edac.c b/drivers/edac/altera_ecc_mgr_edac.c index 3a1bc3cf6dcea..042e62108d5ba 100644 --- a/drivers/edac/altera_ecc_mgr_edac.c +++ b/drivers/edac/altera_ecc_mgr_edac.c @@ -16,11 +16,6 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -#include -#include - -#include "edac_core.h" -#include "edac_module.h" #include #include @@ -30,52 +25,26 @@ #include #include #include -#include #include -/* MPU L2 Register Defines */ -#define ALTR_MPUL2_CONTROL_OFFSET 0x100 -#define ALTR_MPUL2_CTL_CACHE_EN_MASK 0x00000001 - -/* L2 ECC Management Group Defines */ -#define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00 -#define ALTR_L2_ECC_EN_MASK 0x00000001 -#define ALTR_L2_ECC_INJS_MASK 0x00000002 -#define ALTR_L2_ECC_INJD_MASK 0x00000004 - -/* OCRAM ECC Management Group Defines */ -#define ALTR_MAN_GRP_OCRAM_ECC_OFFSET 0x04 -#define ALTR_OCR_ECC_EN_MASK 0x00000001 -#define ALTR_OCR_ECC_SERR_MASK 0x00000008 -#define ALTR_OCR_ECC_DERR_MASK 0x00000010 - -struct ecc_mgr_of_data { - int (*setup)(struct platform_device *pdev, void __iomem *base); - int ce_clear_mask; - int ue_clear_mask; -}; - -struct altr_ecc_mgr_dev { - void __iomem *base; - int sb_irq; - int db_irq; - const struct ecc_mgr_of_data *data; - char *edac_dev_name; -}; +#include "altera_edac.h" +#include "edac_core.h" +#include "edac_module.h" static irqreturn_t altr_ecc_mgr_handler(int irq, void *dev_id) { struct edac_device_ctl_info *dci = dev_id; struct altr_ecc_mgr_dev *drvdata = dci->pvt_info; + const struct ecc_mgr_prv_data *priv = drvdata->data; if (irq == drvdata->sb_irq) { - if (drvdata->data->ce_clear_mask) - writel(drvdata->data->ce_clear_mask, drvdata->base); + if (priv->ce_clear_mask) + writel(priv->ce_clear_mask, drvdata->base); edac_device_handle_ce(dci, 0, 0, drvdata->edac_dev_name); } if (irq == drvdata->db_irq) { - if (drvdata->data->ue_clear_mask) - writel(drvdata->data->ue_clear_mask, drvdata->base); + if (priv->ue_clear_mask) + writel(priv->ue_clear_mask, drvdata->base); edac_device_handle_ue(dci, 0, 0, drvdata->edac_dev_name); panic("\nEDAC:ECC_MGR[Uncorrectable errors]\n"); } @@ -83,30 +52,33 @@ static irqreturn_t altr_ecc_mgr_handler(int irq, void *dev_id) return IRQ_HANDLED; } -#if defined(CONFIG_EDAC_DEBUG) && defined(CONFIG_EDAC_ALTERA_L2_ECC) -static ssize_t altr_l2_ecc_trig(struct edac_device_ctl_info *edac_dci, - const char *buffer, size_t count) +#ifdef CONFIG_EDAC_DEBUG +ssize_t altr_ecc_mgr_trig(struct edac_device_ctl_info *edac_dci, + const char *buffer, size_t count) { u32 *ptemp, i, error_mask; int result = 0; unsigned long flags; struct altr_ecc_mgr_dev *drvdata = edac_dci->pvt_info; + const struct ecc_mgr_prv_data *priv = drvdata->data; + void *generic_ptr = edac_dci->dev; - ptemp = kmalloc(5000, GFP_KERNEL); + if (!priv->init_mem) + return -ENOMEM; + + /* Note that generic_ptr is initialized to the device * but in + * some init_functions, this is overridden and returns data */ + ptemp = priv->init_mem(priv->trig_alloc_sz, &generic_ptr); if (!ptemp) { dev_err(edac_dci->dev, - "**EDAC L2 Inject: Buffer Allocation error\n"); + "**EDAC Error Inject: Buffer Allocation error\n"); return -ENOMEM; } - memset(ptemp, 0, 5000); - wmb(); - flush_cache_all(); - if (count == 3) - error_mask = ALTR_L2_ECC_EN_MASK | ALTR_L2_ECC_INJD_MASK; + error_mask = priv->ue_set_mask; else - error_mask = ALTR_L2_ECC_EN_MASK | ALTR_L2_ECC_INJS_MASK; + error_mask = priv->ce_set_mask; dev_alert(edac_dci->dev, "%s: Trigger Error Mask (0x%X)\n", __func__, error_mask); @@ -118,9 +90,9 @@ static ssize_t altr_l2_ecc_trig(struct edac_device_ctl_info *edac_dci, if (ptemp[i]) result = -1; rmb(); - /* Toggle Error bit (it is latched) */ + /* Toggle Error bit (it is latched), leave ECC enabled */ writel(error_mask, drvdata->base); - writel(ALTR_L2_ECC_EN_MASK, drvdata->base); + writel(priv->ecc_enable_mask, drvdata->base); ptemp[i] = i; } wmb(); @@ -130,14 +102,14 @@ static ssize_t altr_l2_ecc_trig(struct edac_device_ctl_info *edac_dci, dev_alert(edac_dci->dev, "%s: Mem Not Cleared (%d)\n", __func__, result); - result = 0; /* Read out written data. ECC error caused here */ for (i = 0; i < 16; i++) if (ptemp[i] != i) result = -1; rmb(); - kfree(ptemp); + if (priv->free_mem) + priv->free_mem(ptemp, generic_ptr); if (result) dev_alert(edac_dci->dev, "%s: Trigger Match Error (%d)\n", @@ -146,75 +118,17 @@ static ssize_t altr_l2_ecc_trig(struct edac_device_ctl_info *edac_dci, return count; } -static struct edac_dev_sysfs_attribute altr_l2_sysfs_attributes[] = { - { - .attr = { .name = "altr_l2_trigger", - .mode = (S_IRUGO | S_IWUSR) }, - .show = NULL, - .store = altr_l2_ecc_trig - }, - { - .attr = {.name = NULL } - } -}; - -static void altr_set_sysfs_attr(struct edac_device_ctl_info *edac_dci) +static void altr_set_sysfs_attr(struct edac_device_ctl_info *edac_dci, + struct edac_dev_sysfs_attribute *ecc_attr) { - edac_dci->sysfs_attributes = altr_l2_sysfs_attributes; + if (ecc_attr) + edac_dci->sysfs_attributes = ecc_attr; } #else -static void altr_set_sysfs_attr(struct edac_device_ctl_info *edac_dci) +static void altr_set_sysfs_attr(struct edac_device_ctl_info *edac_dci, + struct edac_dev_sysfs_attribute *ecc_attr) {} -#endif - -/* - * altr_l2_dependencies() - * Test for L2 cache ECC dependencies upon entry because - * the preloader/UBoot should have initialized the L2 - * memory and enabled the ECC. - * Can't turn on ECC here because accessing un-initialized - * memory will cause CE/UE errors possibly causing an ABORT. - * Bail if ECC is not on. - * Test For 1) L2 ECC is enabled and 2) L2 Cache is enabled. - */ -static int altr_l2_dependencies(struct platform_device *pdev, - void __iomem *base) -{ - u32 control; - struct regmap *l2_vbase; - - control = readl(base) & ALTR_L2_ECC_EN_MASK; - if (!control) { - dev_err(&pdev->dev, "L2: No ECC present, or ECC disabled\n"); - return -ENODEV; - } - - l2_vbase = syscon_regmap_lookup_by_compatible("arm,pl310-cache"); - if (IS_ERR(l2_vbase)) { - dev_err(&pdev->dev, - "L2 ECC:regmap for arm,pl310-cache lookup failed.\n"); - return -ENODEV; - } - - regmap_read(l2_vbase, ALTR_MPUL2_CONTROL_OFFSET, &control); - if (!(control & ALTR_MPUL2_CTL_CACHE_EN_MASK)) { - dev_err(&pdev->dev, "L2: Cache disabled\n"); - return -ENODEV; - } - - return 0; -} - -static const struct ecc_mgr_of_data l2ecc_data = { - .setup = altr_l2_dependencies, - .ce_clear_mask = 0, - .ue_clear_mask = 0, -}; - -static const struct ecc_mgr_of_data ocramecc_data = { - .ce_clear_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_SERR_MASK), - .ue_clear_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_DERR_MASK), -}; +#endif /* #ifdef CONFIG_EDAC_DEBUG */ static const struct of_device_id altr_ecc_mgr_of_match[] = { #ifdef CONFIG_EDAC_ALTERA_L2_ECC @@ -238,6 +152,7 @@ static int altr_ecc_mgr_probe(struct platform_device *pdev) { struct edac_device_ctl_info *dci; struct altr_ecc_mgr_dev *drvdata; + const struct ecc_mgr_prv_data *priv; struct resource *r; int res = 0; struct device_node *np = pdev->dev.of_node; @@ -282,9 +197,9 @@ static int altr_ecc_mgr_probe(struct platform_device *pdev) } /* Check specific dependencies for the module */ - drvdata->data = of_match_node(altr_ecc_mgr_of_match, np)->data; - if (drvdata->data->setup) { - res = drvdata->data->setup(pdev, drvdata->base); + priv = drvdata->data = of_match_node(altr_ecc_mgr_of_match, np)->data; + if (priv->setup) { + res = priv->setup(pdev, drvdata->base); if (res < 0) goto err; } @@ -306,7 +221,7 @@ static int altr_ecc_mgr_probe(struct platform_device *pdev) dci->mod_name = "ECC_MGR"; dci->dev_name = drvdata->edac_dev_name; - altr_set_sysfs_attr(dci); + altr_set_sysfs_attr(dci, priv->eccmgr_sysfs_attr); if (edac_device_add_device(dci)) goto err; diff --git a/drivers/edac/altera_ecc_ocram.c b/drivers/edac/altera_ecc_ocram.c new file mode 100644 index 0000000000000..fdd148604711d --- /dev/null +++ b/drivers/edac/altera_ecc_ocram.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + + +#include "altera_edac.h" + +/* OCRAM ECC Management Group Defines */ +#define ALTR_MAN_GRP_OCRAM_ECC_OFFSET 0x04 +#define ALTR_OCR_ECC_EN_MASK 0x00000001 +#define ALTR_OCR_ECC_INJS_MASK 0x00000002 +#define ALTR_OCR_ECC_INJD_MASK 0x00000004 +#define ALTR_OCR_ECC_SERR_MASK 0x00000008 +#define ALTR_OCR_ECC_DERR_MASK 0x00000010 + +const struct ecc_mgr_prv_data ocramecc_data = { + .ce_clear_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_SERR_MASK), + .ue_clear_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_DERR_MASK), +}; + diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h new file mode 100644 index 0000000000000..2fe20c0167b98 --- /dev/null +++ b/drivers/edac/altera_edac.h @@ -0,0 +1,55 @@ +/* + * + * Copyright (C) 2014 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#ifndef ALTERA_EDAC_H +#define ALTERA_EDAC_H + +#include +#include +#include +#include "edac_core.h" + +struct ecc_mgr_prv_data { + int (*setup)(struct platform_device *pdev, void __iomem *base); + int ce_clear_mask; + int ue_clear_mask; +#ifdef CONFIG_EDAC_DEBUG + struct edac_dev_sysfs_attribute *eccmgr_sysfs_attr; + void * (*init_mem)(size_t size, void **other); + void (*free_mem)(void *p, void *other); + int ecc_enable_mask; + int ce_set_mask; + int ue_set_mask; + int trig_alloc_sz; +#endif +}; + +struct altr_ecc_mgr_dev { + void __iomem *base; + int sb_irq; + int db_irq; + const struct ecc_mgr_prv_data *data; + char *edac_dev_name; +}; + +extern const struct ecc_mgr_prv_data l2ecc_data; +extern const struct ecc_mgr_prv_data ocramecc_data; + +ssize_t altr_ecc_mgr_trig(struct edac_device_ctl_info *edac_dci, + const char *buffer, size_t count); + +#endif /* #ifndef ALTERA_EDAC_H */ From 57a4bc16b12449e380057ad0a3748c3e9c1a3976 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Mon, 10 Feb 2014 10:00:44 -0600 Subject: [PATCH 097/201] FogBugz #173188: Add OCRAM ECC Error Injection for testing. OCRAM ECC error injection was added for testing the OCRAM EDAC functionality. The OCRAM testing function allocates memory from the general purpose allocation pool, sets the error injection bits, writes data out (corrupted by error injection logic), then reads the data back out (which triggers the EDAC handler) before releasing the gen_pool. The gen_pool free function differs from the standard memory free prototype(requires a size parameter) which necessitated changing the generic free function pointer in altera_edac.h and the prototypes for the memory free function in altera_ecc_l2.c. V2: Fix warnings when debugging is disabled. V3: Remove stray newlines in altera_ecc_mgr_edac.c & altera_ecc_ocram.c Signed-off-by: Thor Thayer --- drivers/edac/altera_ecc_l2.c | 2 +- drivers/edac/altera_ecc_mgr_edac.c | 9 +++-- drivers/edac/altera_ecc_ocram.c | 59 +++++++++++++++++++++++++++++- drivers/edac/altera_edac.h | 2 +- 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/drivers/edac/altera_ecc_l2.c b/drivers/edac/altera_ecc_l2.c index 2fe31a818aad1..20995f4d6c02e 100644 --- a/drivers/edac/altera_ecc_l2.c +++ b/drivers/edac/altera_ecc_l2.c @@ -53,7 +53,7 @@ static void *l2_init_mem(size_t size, void **other) return ptemp; } -static void l2_free_mem(void *p, void *other) +static void l2_free_mem(void *p, size_t size, void *other) { struct device *dev = other; if (dev && p) diff --git a/drivers/edac/altera_ecc_mgr_edac.c b/drivers/edac/altera_ecc_mgr_edac.c index 042e62108d5ba..5551691157058 100644 --- a/drivers/edac/altera_ecc_mgr_edac.c +++ b/drivers/edac/altera_ecc_mgr_edac.c @@ -109,7 +109,7 @@ ssize_t altr_ecc_mgr_trig(struct edac_device_ctl_info *edac_dci, rmb(); if (priv->free_mem) - priv->free_mem(ptemp, generic_ptr); + priv->free_mem(ptemp, priv->trig_alloc_sz, generic_ptr); if (result) dev_alert(edac_dci->dev, "%s: Trigger Match Error (%d)\n", @@ -119,14 +119,15 @@ ssize_t altr_ecc_mgr_trig(struct edac_device_ctl_info *edac_dci, } static void altr_set_sysfs_attr(struct edac_device_ctl_info *edac_dci, - struct edac_dev_sysfs_attribute *ecc_attr) + const struct ecc_mgr_prv_data *priv) { + struct edac_dev_sysfs_attribute *ecc_attr = priv->eccmgr_sysfs_attr; if (ecc_attr) edac_dci->sysfs_attributes = ecc_attr; } #else static void altr_set_sysfs_attr(struct edac_device_ctl_info *edac_dci, - struct edac_dev_sysfs_attribute *ecc_attr) + const struct ecc_mgr_prv_data *priv) {} #endif /* #ifdef CONFIG_EDAC_DEBUG */ @@ -221,7 +222,7 @@ static int altr_ecc_mgr_probe(struct platform_device *pdev) dci->mod_name = "ECC_MGR"; dci->dev_name = drvdata->edac_dev_name; - altr_set_sysfs_attr(dci, priv->eccmgr_sysfs_attr); + altr_set_sysfs_attr(dci, priv); if (edac_device_add_device(dci)) goto err; diff --git a/drivers/edac/altera_ecc_ocram.c b/drivers/edac/altera_ecc_ocram.c index fdd148604711d..26e31e0bfc86a 100644 --- a/drivers/edac/altera_ecc_ocram.c +++ b/drivers/edac/altera_ecc_ocram.c @@ -14,7 +14,10 @@ * this program. If not, see . */ - +#include +#include +#include +#include #include "altera_edac.h" /* OCRAM ECC Management Group Defines */ @@ -25,8 +28,60 @@ #define ALTR_OCR_ECC_SERR_MASK 0x00000008 #define ALTR_OCR_ECC_DERR_MASK 0x00000010 +#ifdef CONFIG_EDAC_DEBUG +static void *ocram_init_mem(size_t size, void **other) +{ + struct device_node *np; + struct gen_pool *gp; + void *sram_addr; + + np = of_find_compatible_node(NULL, NULL, "altr,ocram-edac"); + if (!np) + return NULL; + + gp = of_get_named_gen_pool(np, "iram", 0); + if (!gp) + return NULL; + *other = gp; + + sram_addr = (void *)gen_pool_alloc(gp, size); + if (!sram_addr) + return NULL; + + memset(sram_addr, 0, size); + wmb(); /* Ensure data is written out */ + + return sram_addr; +} + +static void ocram_free_mem(void *p, size_t size, void *other) +{ + gen_pool_free((struct gen_pool *)other, (u32)p, size); +} + +static struct edac_dev_sysfs_attribute altr_ocr_sysfs_attributes[] = { + { + .attr = { .name = "altr_ocram_trigger", + .mode = (S_IRUGO | S_IWUSR) }, + .show = NULL, + .store = altr_ecc_mgr_trig + }, + { + .attr = {.name = NULL } + } +}; +#endif /* #ifdef CONFIG_EDAC_DEBUG */ + const struct ecc_mgr_prv_data ocramecc_data = { .ce_clear_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_SERR_MASK), .ue_clear_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_DERR_MASK), +#ifdef CONFIG_EDAC_DEBUG + .eccmgr_sysfs_attr = altr_ocr_sysfs_attributes, + .init_mem = ocram_init_mem, + .free_mem = ocram_free_mem, + .ecc_enable_mask = ALTR_OCR_ECC_EN_MASK, + .ce_set_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_INJS_MASK), + .ue_set_mask = (ALTR_OCR_ECC_EN_MASK | ALTR_OCR_ECC_INJD_MASK), + .trig_alloc_sz = (32 * sizeof(u32)), +#endif }; - diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h index 2fe20c0167b98..48d726fbcb07a 100644 --- a/drivers/edac/altera_edac.h +++ b/drivers/edac/altera_edac.h @@ -30,7 +30,7 @@ struct ecc_mgr_prv_data { #ifdef CONFIG_EDAC_DEBUG struct edac_dev_sysfs_attribute *eccmgr_sysfs_attr; void * (*init_mem)(size_t size, void **other); - void (*free_mem)(void *p, void *other); + void (*free_mem)(void *p, size_t size, void *other); int ecc_enable_mask; int ce_set_mask; int ue_set_mask; From 8993c5e5bce4afa40e7afcb08ae5e4a431a810d8 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Tue, 18 Feb 2014 16:34:13 -0600 Subject: [PATCH 098/201] FogBugz #184650: Cleanup Altera license headers. A few files have incorrect headers and some files do not have any headers. The biggest issue is that some files say "GPLv2 or later". The "or later" part is the problem. V2: Remove asterisks from date. Correct lower portion of header Signed-off-by: Thor Thayer Conflicts: arch/arm/boot/dts/socfpga_arria5.dts arch/arm/boot/dts/socfpga_ice.dts arch/arm/boot/dts/socfpga_vt.dts --- arch/arm/boot/dts/socfpga.dtsi | 19 +++++++++---------- arch/arm/boot/dts/socfpga_cyclone5.dtsi | 19 +++++++++---------- arch/arm/boot/dts/socfpga_cyclone5_socdk.dts | 19 +++++++++---------- arch/arm/boot/dts/socfpga_vt.dts | 19 +++++++++---------- arch/arm/mach-socfpga/fpga-dma.c | 14 +++++++++++--- arch/arm/mach-socfpga/l2_cache.c | 19 +++++++++---------- arch/arm/mach-socfpga/l2_cache.h | 8 +++----- arch/arm/mach-socfpga/ocram.c | 19 +++++++++---------- arch/arm/mach-socfpga/ocram.h | 8 +++----- arch/arm/mach-socfpga/socfpga_cti.c | 20 ++++++++++---------- arch/arm/mach-socfpga/socfpga_cti.h | 16 ++++++++++++++++ 11 files changed, 97 insertions(+), 83 deletions(-) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index e92a3d44bbe00..9c973a1219740 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -1,18 +1,17 @@ /* - * Copyright (C) 2012 Altera + * Copyright Altera Corporation (C) 2012-2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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 . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ #include "skeleton.dtsi" diff --git a/arch/arm/boot/dts/socfpga_cyclone5.dtsi b/arch/arm/boot/dts/socfpga_cyclone5.dtsi index b972951fcdff3..f625aeb05a4b8 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5.dtsi +++ b/arch/arm/boot/dts/socfpga_cyclone5.dtsi @@ -1,18 +1,17 @@ /* - * Copyright (C) 2012 Altera Corporation + * Copyright Altera Corporation (C) 2012,2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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 . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ /dts-v1/; diff --git a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts index 45de1514af0ac..ab45af73ae64b 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts @@ -1,18 +1,17 @@ /* - * Copyright (C) 2012 Altera Corporation + * Copyright Altera Corporation (C) 2012,2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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 . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ #include "socfpga_cyclone5.dtsi" diff --git a/arch/arm/boot/dts/socfpga_vt.dts b/arch/arm/boot/dts/socfpga_vt.dts index 09792b4111105..8b3a6dfdfa9fe 100644 --- a/arch/arm/boot/dts/socfpga_vt.dts +++ b/arch/arm/boot/dts/socfpga_vt.dts @@ -1,18 +1,17 @@ /* - * Copyright (C) 2013 Altera Corporation + * Copyright Altera Corporation (C) 2012-2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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 . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ /dts-v1/; diff --git a/arch/arm/mach-socfpga/fpga-dma.c b/arch/arm/mach-socfpga/fpga-dma.c index c490c49c78c21..23ed0a0d8e28f 100644 --- a/arch/arm/mach-socfpga/fpga-dma.c +++ b/arch/arm/mach-socfpga/fpga-dma.c @@ -1,11 +1,19 @@ /* * FPGA DMA transfer module * - * Copyright (C) 2014 Altera Corporation. All rights reserved + * Copyright Altera Corporation (C) 2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . */ #include #include diff --git a/arch/arm/mach-socfpga/l2_cache.c b/arch/arm/mach-socfpga/l2_cache.c index 9ced0be62ec61..a2e3bb6ff433f 100644 --- a/arch/arm/mach-socfpga/l2_cache.c +++ b/arch/arm/mach-socfpga/l2_cache.c @@ -1,18 +1,17 @@ /* - * Copyright (C) 2014 Altera Corporation + * Copyright Altera Corporation (C) 2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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 . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ #include #include diff --git a/arch/arm/mach-socfpga/l2_cache.h b/arch/arm/mach-socfpga/l2_cache.h index 34b7578921009..de84d0d6b8b8c 100644 --- a/arch/arm/mach-socfpga/l2_cache.h +++ b/arch/arm/mach-socfpga/l2_cache.h @@ -1,10 +1,8 @@ /* - * arch/arm/mach-socfpga/l2_cache.h + * Copyright Altera Corporation (C) 2014. All rights reserved. * - * Copyright (C) 2014 Altera Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT diff --git a/arch/arm/mach-socfpga/ocram.c b/arch/arm/mach-socfpga/ocram.c index 24d60c2375113..861332c4660a9 100644 --- a/arch/arm/mach-socfpga/ocram.c +++ b/arch/arm/mach-socfpga/ocram.c @@ -1,18 +1,17 @@ /* - * Copyright (C) 2014 Altera Corporation + * Copyright Altera Corporation (C) 2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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 . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ #include #include diff --git a/arch/arm/mach-socfpga/ocram.h b/arch/arm/mach-socfpga/ocram.h index 3c6eb6990663c..c5c107084b222 100644 --- a/arch/arm/mach-socfpga/ocram.h +++ b/arch/arm/mach-socfpga/ocram.h @@ -1,10 +1,8 @@ /* - * arch/arm/mach-socfpga/ocram.h + * Copyright Altera Corporation (C) 2014. All rights reserved. * - * Copyright (C) 2014 Altera Corporation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT diff --git a/arch/arm/mach-socfpga/socfpga_cti.c b/arch/arm/mach-socfpga/socfpga_cti.c index 10072c8984dad..5361939dbee01 100644 --- a/arch/arm/mach-socfpga/socfpga_cti.c +++ b/arch/arm/mach-socfpga/socfpga_cti.c @@ -1,19 +1,19 @@ /* - * Copyright (C) 2013 Altera Corporation + * Copyright Altera Corporation (C) 2013-2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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 . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ + #include #include #include diff --git a/arch/arm/mach-socfpga/socfpga_cti.h b/arch/arm/mach-socfpga/socfpga_cti.h index cbeb06339eebd..efabc3c172468 100644 --- a/arch/arm/mach-socfpga/socfpga_cti.h +++ b/arch/arm/mach-socfpga/socfpga_cti.h @@ -1,3 +1,19 @@ +/* + * Copyright Altera Corporation (C) 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + #ifndef __SOCFPGA_CTI_H #define __SOCFPGA_CTI_H From 37a487c43d06129936e08c7ad66304621a06e04d Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 18 Feb 2014 15:58:20 -0600 Subject: [PATCH 099/201] FogBugz #184646: Turn on all peripheral clocks for a system reboot When doing a software reboot, all peripheral clocks must get turned on for the L3 interconnect to work. This code is needed when doing a "reboot" from user-space and a peripheral clock as been gated off. Why would a peripheral clock get gated? An example use case would be a .ko that gets insmod and rmmod during runtime. The insmod would turn on the IP's clock, and the rmmod would turn off the IP's clock. Doing a "reboot" would cause the system to hang. Signed-off-by: Dinh Nguyen Conflicts: arch/arm/mach-socfpga/core.h --- arch/arm/mach-socfpga/core.h | 3 +++ arch/arm/mach-socfpga/socfpga.c | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/arch/arm/mach-socfpga/core.h b/arch/arm/mach-socfpga/core.h index b9c1a8a1694a6..a09be4472e291 100644 --- a/arch/arm/mach-socfpga/core.h +++ b/arch/arm/mach-socfpga/core.h @@ -56,4 +56,7 @@ extern unsigned long cpu1start_addr; #define SOCFPGA_SCU_VIRT_BASE 0xfee00000 +/* Clock manager defines */ +#define SOCFPGA_ENABLE_PLL_REG 0xA0 + #endif diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index e22dfaa03900a..b6b627a30ee78 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -38,6 +38,7 @@ void __iomem *rst_manager_base_addr; unsigned long cpu1start_addr; void __iomem *sdr_ctl_base_addr; void __iomem *l3regs_base_addr; +void __iomem *clkmgr_base_addr; #ifdef CONFIG_HW_PERF_EVENTS static struct arm_pmu_platdata socfpga_pmu_platdata = { @@ -160,6 +161,10 @@ void __init socfpga_sysmgr_init(void) rst_manager_base_addr = of_iomap(np, 0); WARN_ON(!rst_manager_base_addr); + np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); + clkmgr_base_addr = of_iomap(np, 0); + WARN_ON(!clkmgr_base_addr); + np = of_find_compatible_node(NULL, NULL, "altr,sdr-ctl"); if (!np) { pr_err("SOCFPGA: Unable to find sdr-ctl\n"); @@ -189,6 +194,9 @@ static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd) { u32 temp; + /* Turn on all periph PLL clocks */ + writel(0xffff, clkmgr_base_addr + SOCFPGA_ENABLE_PLL_REG); + temp = readl(rst_manager_base_addr + SOCFPGA_RSTMGR_CTRL); if (mode == REBOOT_HARD) From a0855e3d244d202423dca1a52bf041fc902f4b2e Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 24 Mar 2014 17:10:13 -0500 Subject: [PATCH 100/201] FogBugz #193022: Fix warning for allmodconfig on socfpga_defconfig warning: (ARCH_SOCFPGA) selects PL310_ERRATA_753970 which has unmet direct dependencies (CACHE_PL310) Also remove VIRT_TO_BUS, because this ia no longer needed with the new USB driver. Signed-off-by: Dinh Nguyen --- arch/arm/mach-socfpga/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index 91fedd8eef75d..01f6d53c40e62 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -17,7 +17,7 @@ config ARCH_SOCFPGA select ARM_ERRATA_775420 select PL310_ERRATA_588369 select PL310_ERRATA_727915 - select PL310_ERRATA_753970 + select PL310_ERRATA_753970 if PL310 select PL310_ERRATA_769419 config FPGADMA From 652e98ee83b8b9faacddb73e19e194141b90111a Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 26 Feb 2014 18:38:05 -0600 Subject: [PATCH 101/201] FogBugz #183074: Fix PMU CTI driver for single core systems For SOCFPGA systems that are single core, there is only 1 CTI for the PMU. Update the driver to initialize the number of CTIs based on the num_possible_cpus(). Cleanup: Alpha the headers. Signed-off-by: Dinh Nguyen --- v8: - Clean up the for loop in init function - Save of socfpga_cti pointer for kfree use v7: - Made cti struct a static member of struct socfpga_cti - No need for cti and irq_local to be an array in socfpga_pmu_handler v6: - More clean up based on code reviews - A single kzalloc outside of the for loop for easy cleanup using kfree v5: - Use num_online_cpus() to correctly allocate the CTI based on CPUs that are currently online. - Clean up a stray debug print - Clean up the allocation of the socfpga_cti and cti structure - Return -ENOMEM instead of -1 v4: Dynamically allocate the PMU CTIs v3: Should be calling cti_disable in stop_cti v2: The 2nd CTI node will only get allocated when the 2nd CPU is available --- arch/arm/mach-socfpga/socfpga_cti.c | 146 ++++++++++++++++------------ 1 file changed, 82 insertions(+), 64 deletions(-) diff --git a/arch/arm/mach-socfpga/socfpga_cti.c b/arch/arm/mach-socfpga/socfpga_cti.c index 5361939dbee01..72ef9a494fdc9 100644 --- a/arch/arm/mach-socfpga/socfpga_cti.c +++ b/arch/arm/mach-socfpga/socfpga_cti.c @@ -14,8 +14,9 @@ * this program. If not, see . */ -#include #include +#include +#include #include #include #include @@ -23,20 +24,40 @@ #include "core.h" #include "socfpga_cti.h" -#define SOCFPGA_NUM_CTI 2 +#define SOCFPGA_MAX_NUM_CTI 8 + +struct socfpga_cti { + struct cti socfpga_cti_data; + void __iomem *cti_addr; + int irq; +}; -struct cti socfpga_cti_data[SOCFPGA_NUM_CTI]; +struct socfpga_cti_device { + struct device *dev; + struct socfpga_cti *socfpga_cti[SOCFPGA_MAX_NUM_CTI]; + int socfpga_ncores; +}; irqreturn_t socfpga_pmu_handler(int irq, void *dev, irq_handler_t handler) { + struct arm_pmu *armpmu = (struct arm_pmu *)dev; + struct platform_device *pdev = armpmu->plat_device; + struct socfpga_cti_device *socfpga_cti_device = + platform_get_drvdata(pdev); + int ncores = socfpga_cti_device->socfpga_ncores; + struct cti *cti; + int irq_local; unsigned int handled = 0; int i; - for (i = 0; i < SOCFPGA_NUM_CTI; i++) - if (irq == socfpga_cti_data[i].irq) { - cti_irq_ack(&socfpga_cti_data[i]); - handled = handler(irq, dev); - } + for (i = 0; i < ncores; i++) { + irq_local = socfpga_cti_device->socfpga_cti[i]->irq; + cti = &socfpga_cti_device->socfpga_cti[i]->socfpga_cti_data; + if (irq == irq_local) + cti_irq_ack(cti); + } + + handled = handler(irq, dev); return IRQ_RETVAL(handled); } @@ -44,82 +65,79 @@ irqreturn_t socfpga_pmu_handler(int irq, void *dev, irq_handler_t handler) int socfpga_init_cti(struct platform_device *pdev) { struct device_node *np, *np2; - void __iomem *cti0_addr; - void __iomem *cti1_addr; - u32 irq0, irq1; + struct socfpga_cti_device *socfpga_cti_device; + struct socfpga_cti *socfpga_cti, *socfpga_cti_error; + void __iomem *cti_addr; + int ncores; + int i; + + socfpga_cti_device = devm_kzalloc(&pdev->dev, + sizeof(*socfpga_cti_device), + GFP_KERNEL); + if (!socfpga_cti_device) + return -ENOMEM; + + ncores = num_online_cpus(); + socfpga_cti_device->socfpga_ncores = ncores; + + socfpga_cti_device->dev = &pdev->dev; np = pdev->dev.of_node; - np2 = of_find_compatible_node(np, NULL, "arm,coresight-cti"); - if (!np2) { - dev_err(&pdev->dev, "PMU: Unable to find coresight-cti\n"); - return -1; - } - cti0_addr = of_iomap(np2, 0); - if (!cti0_addr) { - dev_err(&pdev->dev, "PMU: ioremap for CTI failed\n"); - return -1; - } - irq0 = platform_get_irq(pdev, 0); - if (irq0 < 0) - goto free_irq0; + socfpga_cti = kzalloc(sizeof(socfpga_cti) * ncores, GFP_KERNEL); + if (!socfpga_cti) + return -ENOMEM; + socfpga_cti_error = socfpga_cti; + + for (i = 0; i < socfpga_cti_device->socfpga_ncores; i++) { + np2 = of_find_compatible_node(np, NULL, "arm,coresight-cti"); + + cti_addr = of_iomap(np2, 0); + if (!cti_addr) { + dev_err(&pdev->dev, "PMU: ioremap for CTI failed\n"); + kfree(socfpga_cti_error); + return -ENOMEM; + } + socfpga_cti->cti_addr = cti_addr; + + socfpga_cti->irq = platform_get_irq(pdev, i); + + /*configure CTI0 for pmu irq routing*/ + cti_init(&socfpga_cti->socfpga_cti_data, socfpga_cti->cti_addr, + socfpga_cti->irq, CTI_MPU_IRQ_TRIG_OUT); + cti_unlock(&socfpga_cti->socfpga_cti_data); + cti_map_trigger(&socfpga_cti->socfpga_cti_data, + CTI_MPU_IRQ_TRIG_IN, CTI_MPU_IRQ_TRIG_OUT, + PMU_CHANNEL_0); - np2 = of_find_compatible_node(np2, NULL, "arm,coresight-cti"); - if (!np2) { - dev_err(&pdev->dev, "PMU: Unable to find coresight-cti\n"); - goto err_iounmap; + socfpga_cti_device->socfpga_cti[i] = socfpga_cti; + socfpga_cti += sizeof(socfpga_cti); } - cti1_addr = of_iomap(np2, 0); - if (!cti1_addr) - goto err_iounmap; - - irq1 = platform_get_irq(pdev, 1); - if (irq1 < 0) - goto free_irq1; - - /*configure CTI0 for pmu irq routing*/ - cti_init(&socfpga_cti_data[0], cti0_addr, - irq0, CTI_MPU_IRQ_TRIG_OUT); - cti_unlock(&socfpga_cti_data[0]); - cti_map_trigger(&socfpga_cti_data[0], - CTI_MPU_IRQ_TRIG_IN, CTI_MPU_IRQ_TRIG_OUT, PMU_CHANNEL_0); - - /*configure CTI1 for pmu irq routing*/ - cti_init(&socfpga_cti_data[1], cti1_addr, - irq1, CTI_MPU_IRQ_TRIG_OUT); - cti_unlock(&socfpga_cti_data[1]); - cti_map_trigger(&socfpga_cti_data[1], - CTI_MPU_IRQ_TRIG_IN, CTI_MPU_IRQ_TRIG_OUT, PMU_CHANNEL_1); - - dev_info(&pdev->dev, "PMU:CTI successfully enabled\n"); - return 0; + platform_set_drvdata(pdev, socfpga_cti_device); -free_irq1: - iounmap(cti1_addr); -err_iounmap: - free_irq(irq0, pdev); -free_irq0: - iounmap(cti0_addr); - return -1; + dev_info(&pdev->dev, "PMU:CTI successfully enabled for %d cores\n", + socfpga_cti_device->socfpga_ncores); + return 0; } int socfpga_start_cti(struct platform_device *pdev) { + struct socfpga_cti_device *cti_dev = platform_get_drvdata(pdev); int i; - for (i = 0; i < SOCFPGA_NUM_CTI; i++) - cti_enable(&socfpga_cti_data[i]); + for (i = 0; i < cti_dev->socfpga_ncores; i++) + cti_enable(&cti_dev->socfpga_cti[i]->socfpga_cti_data); return 0; } int socfpga_stop_cti(struct platform_device *pdev) { + struct socfpga_cti_device *cti_dev = platform_get_drvdata(pdev); int i; - for (i = 0; i < SOCFPGA_NUM_CTI; i++) - cti_disable(&socfpga_cti_data[i]); + for (i = 0; i < cti_dev->socfpga_ncores; i++) + cti_disable(&cti_dev->socfpga_cti[i]->socfpga_cti_data); return 0; } - From 1ec4969b1d51c05174599930eb3e034d32c43ec3 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Wed, 4 Jun 2014 16:25:33 -0500 Subject: [PATCH 102/201] FogBugz #209258: hotplug: put cpu1 in wfi Use WFI when putting CPU1 to sleep. Don't hold CPU1 in reset since that results in increased power consumption. Reset CPU1 briefly during CPU1 bootup. This has been tested for hotplug and suspend/resume and results in no increased power consumption. Signed-off-by: Alan Tull v2: remove panic after cpu_do_idle loop. --- arch/arm/mach-socfpga/platsmp.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-socfpga/platsmp.c b/arch/arm/mach-socfpga/platsmp.c index 538b2291a1141..1d5f8adef4cbd 100644 --- a/arch/arm/mach-socfpga/platsmp.c +++ b/arch/arm/mach-socfpga/platsmp.c @@ -34,6 +34,10 @@ static int socfpga_boot_secondary(unsigned int cpu, struct task_struct *idle) int trampoline_size = &secondary_trampoline_end - &secondary_trampoline; if (cpu1start_addr) { + /* This will put CPU #1 into reset.*/ + __raw_writel(RSTMGR_MPUMODRST_CPU1, + rst_manager_base_addr + 0x10); + memcpy(phys_to_virt(0), &secondary_trampoline, trampoline_size); __raw_writel(virt_to_phys(socfpga_secondary_startup), @@ -89,13 +93,9 @@ static void socfpga_cpu_die(unsigned int cpu) /* Flush the L1 data cache. */ flush_cache_all(); - /* This will put CPU #1 into reset.*/ - __raw_writel(RSTMGR_MPUMODRST_CPU1, rst_manager_base_addr + 0x10); - - cpu_do_idle(); - - /* We should have never returned from idle */ - panic("cpu %d unexpectedly exit from shutdown\n", cpu); + /* Do WFI. If we wake up early, go back into WFI */ + while (1) + cpu_do_idle(); } struct smp_operations socfpga_smp_ops __initdata = { From f184c3656cd491eed13b6fab81fd22dda23a4715 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Sat, 23 Nov 2013 21:16:35 -0600 Subject: [PATCH 103/201] FogBugz #169918: Map the OCRAM in the device tree file. By mapping the OCRAM in the device tree, this enables peripheral(s) to make use of the OCRAM by a assigning an iram node, and using: of_get_named_gen_pool(np, "iram", 0); This patch also enables the generic SRAM driver that will map the OCRAM into a generic mempool. Signed-off-by: Dinh Nguyen --- v3: fix-up drivers/misc/Kconfig to match kernel.org v2: 64kB of OCRAM --- arch/arm/boot/dts/socfpga.dtsi | 5 +++++ arch/arm/configs/socfpga_defconfig | 1 + drivers/misc/Kconfig | 11 +++++++++++ 3 files changed, 17 insertions(+) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 9c973a1219740..bd5d6a6708f3d 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -689,6 +689,11 @@ }; }; + ocram: sram@ffff0000 { + compatible = "mmio-sram"; + reg = <0xffff0000 0x10000>; + }; + pmu { #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 5ef33de5075df..2d551b0703ab6 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -110,3 +110,4 @@ CONFIG_FPGA=y CONFIG_FPGA_MGR_ALTERA=y CONFIG_FPGA_BRIDGE=y CONFIG_ALTERA_SOCFPGA_BRIDGE=y +CONFIG_SRAM=y diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e6b11a45a6bba..feb793bc796f6 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -525,6 +525,17 @@ config VEXPRESS_SYSCFG bus. System Configuration interface is one of the possible means of generating transactions on this bus. +config ALTERA_HWMUTEX + tristate "Altera Hardware Mutex" + help + This option enables device driver support for Altera Hardware Mutex. + Say Y here if you want to use the Altera hardware mutex support. + +config ALTERA_SYSID + tristate "Altera System ID" + help + This enables Altera System ID soft core driver. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" From 4cfe1c66bf2bfb67d3f13b0c82072da3c3f5e57d Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Wed, 24 Jul 2013 12:24:16 -0500 Subject: [PATCH 104/201] FogBugz #119719: Document Cadence QSPI Controller device tree bindings Created spi-cadence-qspi.txt to document device tree bindings for Cadence QSPI controller Signed-off-by: Graham Moore --- .../bindings/spi/spi-cadence-qspi.txt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt diff --git a/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt b/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt new file mode 100644 index 0000000000000..d4dda9a8ae985 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt @@ -0,0 +1,34 @@ +* SPI driver for Cadence QSPI Controller + +Required properties: +- compatible : Should be "cadence,qspi". +- reg : Contains two entries, each of which is a tuple consisting of a + physical address and length. The first entry is the address and + length of the controller register set. The second entry is the + address and length of the QSPI Controller data area. +- interrupts : Unit interrupt specifier for the controller interrupt. +- master-ref-clk : Specifies the frequency of the controller input clock. +- ext-decoder : Value of 0 means no external chipselect decoder is + connected, 1 means there is an external chipselect decoder connected. +- num-chipselect : Number of chip select lines. +- fifo-depth : Size of the data FIFO in bytes. +- bus-num : Number of the SPI bus to which the controller is connected. + +Optional properties: +No optional properties + +Example: + + qspi: spi@ff705000 { + compatible = "cadence,qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xff705000 0x1000>, + <0xffa00000 0x1000>; + interrupts = <0 151 4>; + master-ref-clk = <400000000>; + ext-decoder = <0>; + num-chipselect = <4>; + fifo-depth = <128>; + bus-num = <2>; + } From 4c428d8ab390fdd6a13b86010503c929f9ef6004 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Thu, 6 Mar 2014 16:22:27 -0600 Subject: [PATCH 105/201] FogBugz #99945-2: add gpio-dwapb driver to defconfig and dts * Add the gpio-dwapb driver to the defconfig and device tree. * Fix LED definitions in device tree * Move the LED definitions that are specific to the Altera CycloneV socdk board to the proper device tree file. Signed-off-by: Alan Tull v4: remove socfpga.dtsi changes which were upstreamed fix led definitions to match upstream socfpga.dtsi enable gpios in cyclone5 and arria5 dtsi v3: fix arria5 socdk led definitions move cyclone5 led definitions to cyclone5 socdk dts move clock property to gpio main node, not gpio port nodes v2: fix cyclone5 led definitions Conflicts: arch/arm/boot/dts/socfpga_arria5.dtsi arch/arm/boot/dts/socfpga_cyclone5.dtsi --- arch/arm/boot/dts/socfpga_arria5.dtsi | 21 ++++++++++++++++ arch/arm/boot/dts/socfpga_cyclone5.dtsi | 25 +++++++++++++++++--- arch/arm/boot/dts/socfpga_cyclone5_socdk.dts | 23 ++++++++++++++++++ arch/arm/configs/socfpga_defconfig | 1 + 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/socfpga_arria5.dtsi b/arch/arm/boot/dts/socfpga_arria5.dtsi index 12d1c2ccaf5ba..9d83d409016a5 100644 --- a/arch/arm/boot/dts/socfpga_arria5.dtsi +++ b/arch/arm/boot/dts/socfpga_arria5.dtsi @@ -38,6 +38,27 @@ }; }; + gpio@ff708000 { + status = "okay"; + }; + + gpio@ff709000 { + status = "okay"; + }; + + gpio@ff70a000 { + status = "okay"; + }; + + i2c0: i2c@ffc04000 { + speed-mode = <0>; + status = "okay"; + }; + + qspi: spi@ff705000 { + status = "okay"; + }; + sysmgr@ffd08000 { cpu1-start-addr = <0xffd080c4>; }; diff --git a/arch/arm/boot/dts/socfpga_cyclone5.dtsi b/arch/arm/boot/dts/socfpga_cyclone5.dtsi index f625aeb05a4b8..c3dfa6ed399c8 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5.dtsi +++ b/arch/arm/boot/dts/socfpga_cyclone5.dtsi @@ -38,9 +38,28 @@ }; }; - ethernet@ff702000 { - phy-mode = "rgmii"; - phy-addr = <0xffffffff>; /* probe for phy addr */ + gpio@ff708000 { + status = "okay"; + }; + + gpio@ff709000 { + status = "okay"; + }; + + gpio@ff70a000 { + status = "okay"; + }; + + i2c0: i2c@ffc04000 { + speed-mode = <0>; + status = "okay"; + }; + + qspi: spi@ff705000 { + status = "okay"; + }; + + serial0@ffc02000 { status = "okay"; }; diff --git a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts index ab45af73ae64b..3f2e054e4a9f5 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts @@ -36,6 +36,29 @@ */ ethernet0 = &gmac1; }; + + leds { + compatible = "gpio-leds"; + hps0 { + label = "hps_led0"; + gpios = <&gpio1 15 1>; + }; + + hps1 { + label = "hps_led1"; + gpios = <&gpio1 14 1>; + }; + + hps2 { + label = "hps_led2"; + gpios = <&gpio1 13 1>; + }; + + hps3 { + label = "hps_led3"; + gpios = <&gpio1 12 1>; + }; + }; }; &gmac1 { diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 2d551b0703ab6..bfd36ce903677 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -80,6 +80,7 @@ CONFIG_SPI_DW_MMIO=y CONFIG_SPI_SPIDEV=y CONFIG_GPIOLIB=y CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_DWAPB=y CONFIG_GPIO_ALTERA=m # CONFIG_RTC_HCTOSYS is not set CONFIG_EXT2_FS=y From bcfcbace2afd33d39bf48473c610477acf218206 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 2 Apr 2014 15:18:00 -0500 Subject: [PATCH 106/201] FogBugz #195004: Move board specific components to board dts files Move board components from socfpga_cyclone5.dtsi and socfpga_arria5.dtsi into the boards specific files. Signed-off-by: Dinh Nguyen --- v2: Deeper cleaning --- arch/arm/boot/dts/socfpga_arria5.dtsi | 40 ------- arch/arm/boot/dts/socfpga_arria5_socdk.dts | 79 +++++++++++++ arch/arm/boot/dts/socfpga_cyclone5.dtsi | 107 ------------------ arch/arm/boot/dts/socfpga_cyclone5_socdk.dts | 89 ++++++++++++++- arch/arm/boot/dts/socfpga_cyclone5_sockit.dts | 4 + 5 files changed, 171 insertions(+), 148 deletions(-) diff --git a/arch/arm/boot/dts/socfpga_arria5.dtsi b/arch/arm/boot/dts/socfpga_arria5.dtsi index 9d83d409016a5..a2f6035acb06d 100644 --- a/arch/arm/boot/dts/socfpga_arria5.dtsi +++ b/arch/arm/boot/dts/socfpga_arria5.dtsi @@ -19,46 +19,6 @@ / { soc { - clkmgr@ffd04000 { - clocks { - osc1 { - clock-frequency = <25000000>; - }; - }; - }; - - dwmmc0@ff704000 { - num-slots = <1>; - supports-highspeed; - broken-cd; - - slot@0 { - reg = <0>; - bus-width = <4>; - }; - }; - - gpio@ff708000 { - status = "okay"; - }; - - gpio@ff709000 { - status = "okay"; - }; - - gpio@ff70a000 { - status = "okay"; - }; - - i2c0: i2c@ffc04000 { - speed-mode = <0>; - status = "okay"; - }; - - qspi: spi@ff705000 { - status = "okay"; - }; - sysmgr@ffd08000 { cpu1-start-addr = <0xffd080c4>; }; diff --git a/arch/arm/boot/dts/socfpga_arria5_socdk.dts b/arch/arm/boot/dts/socfpga_arria5_socdk.dts index d532d171e3917..c2c49486e3e08 100644 --- a/arch/arm/boot/dts/socfpga_arria5_socdk.dts +++ b/arch/arm/boot/dts/socfpga_arria5_socdk.dts @@ -44,6 +44,20 @@ */ ethernet0 = &gmac1; }; + + soc { + gpio@ff708000 { + status = "okay"; + }; + + gpio@ff709000 { + status = "okay"; + }; + + gpio@ff70a000 { + status = "okay"; + }; + }; }; &gmac1 { @@ -62,6 +76,15 @@ &i2c0 { status = "okay"; + speed-mode = <0>; + + lcd: lcd@28 { + compatible = "newhaven,nhd-0216k3z-nsw-bbw"; + reg = <0x28>; + height = <2>; + width = <16>; + brightness = <8>; + }; eeprom@51 { compatible = "atmel,24c32"; @@ -75,6 +98,62 @@ }; }; +&mmc { + num-slots = <1>; + supports-highspeed; + broken-cd; + altr,dw-mshc-ciu-div = <3>; + altr,dw-mshc-sdr-timing = <0 3>; + + slot@0 { + reg = <0>; + bus-width = <4>; + }; +}; + +&osc1 { + clock-frequency = <25000000>; +}; + +&qspi { + status = "okay"; + flash0: n25q512a@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "n25q512a"; + reg = <0>; /* chip select */ + spi-max-frequency = <100000000>; + m25p,fast-read; + page-size = <256>; + block-size = <16>; /* 2^16, 64KB */ + read-delay = <4>; /* delay value in read data capture register */ + tshsl-ns = <50>; + tsd2d-ns = <50>; + tchsh-ns = <4>; + tslch-ns = <4>; + + partition@qspi-boot { + /* 8MB for raw data. */ + label = "Flash 0 Raw Data"; + reg = <0x0 0x800000>; + }; + + partition@qspi-rootfs { + /* 56MB for jffs2 data. */ + label = "Flash 0 jffs2 Filesystem"; + reg = <0x800000 0x3800000>; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + &usb1 { status = "okay"; }; + +&watchdog0 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/socfpga_cyclone5.dtsi b/arch/arm/boot/dts/socfpga_cyclone5.dtsi index c3dfa6ed399c8..0e434b39b7cd0 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5.dtsi +++ b/arch/arm/boot/dts/socfpga_cyclone5.dtsi @@ -19,115 +19,8 @@ / { soc { - clkmgr@ffd04000 { - clocks { - osc1 { - clock-frequency = <25000000>; - }; - }; - }; - - dwmmc0@ff704000 { - num-slots = <1>; - supports-highspeed; - broken-cd; - - slot@0 { - reg = <0>; - bus-width = <4>; - }; - }; - - gpio@ff708000 { - status = "okay"; - }; - - gpio@ff709000 { - status = "okay"; - }; - - gpio@ff70a000 { - status = "okay"; - }; - - i2c0: i2c@ffc04000 { - speed-mode = <0>; - status = "okay"; - }; - - qspi: spi@ff705000 { - status = "okay"; - }; - - serial0@ffc02000 { - status = "okay"; - }; - sysmgr@ffd08000 { cpu1-start-addr = <0xffd080c4>; }; }; - - soc { - qspi: spi@ff705000 { - compatible = "cadence,qspi"; - #address-cells = <1>; - #size-cells = <0>; - reg = <0xff705000 0x1000>, - <0xffa00000 0x1000>; - interrupts = <0 151 4>; - master-ref-clk = <400000000>; - ext-decoder = <0>; /* external decoder */ - num-chipselect = <4>; - fifo-depth = <128>; - bus-num = <2>; - - flash0: n25q128@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "n25q128"; - reg = <0>; /* chip select */ - spi-max-frequency = <100000000>; - page-size = <256>; - block-size = <16>; /* 2^16, 64KB */ - quad = <1>; /* 1-support quad */ - tshsl-ns = <200>; - tsd2d-ns = <255>; - tchsh-ns = <20>; - tslch-ns = <20>; - - partition@0 { - /* 8MB for raw data. */ - label = "Flash 0 Raw Data"; - reg = <0x0 0x800000>; - }; - partition@800000 { - /* 8MB for jffs2 data. */ - label = "Flash 0 jffs2 Filesystem"; - reg = <0x800000 0x800000>; - }; - }; - - flash1: n25q128@1 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "n25q128"; - reg = <1>; /* chip select */ - spi-max-frequency = <100000000>; - page-size = <256>; - block-size = <16>; /* 2^16, 64KB */ - quad = <1>; - tshsl-ns = <200>; - tsd2d-ns = <255>; - tchsh-ns = <20>; - tslch-ns = <20>; - - partition@0 { - /* 16MB for user data. */ - label = "Flash 1 User Data"; - reg = <0x0 0x1000000>; - }; - }; - }; - }; }; diff --git a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts index 3f2e054e4a9f5..5bd86cd4d7250 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts @@ -21,7 +21,7 @@ compatible = "altr,socfpga-cyclone5", "altr,socfpga"; chosen { - bootargs = "console=ttyS0,115200"; + bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait"; }; memory { @@ -59,6 +59,41 @@ gpios = <&gpio1 12 1>; }; }; + + soc { + gpio@ff708000 { + status = "okay"; + }; + + gpio@ff709000 { + status = "okay"; + }; + + gpio@ff70a000 { + status = "okay"; + }; + }; +}; + +&can0 { + status = "okay"; +}; + +&mmc { + num-slots = <1>; + supports-highspeed; + broken-cd; + altr,dw-mshc-ciu-div = <3>; + altr,dw-mshc-sdr-timing = <0 3>; + + slot@0 { + reg = <0>; + bus-width = <4>; + }; +}; + +&osc1 { + clock-frequency = <25000000>; }; &gmac1 { @@ -73,10 +108,20 @@ txc-skew-ps = <2600>; rxdv-skew-ps = <0>; rxc-skew-ps = <2000>; + max-frame-size = <3800>; }; &i2c0 { status = "okay"; + speed-mode = <0>; + + lcd: lcd@28 { + compatible = "newhaven,nhd-0216k3z-nsw-bbw"; + reg = <0x28>; + height = <2>; + width = <16>; + brightness = <8>; + }; eeprom@51 { compatible = "atmel,24c32"; @@ -93,3 +138,45 @@ &usb1 { status = "okay"; }; + +&qspi { + status = "okay"; + flash0: n25q00@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "n25q00"; + reg = <0>; /* chip select */ + spi-max-frequency = <100000000>; + m25p,fast-read; + page-size = <256>; + block-size = <16>; /* 2^16, 64KB */ + read-delay = <4>; /* delay value in read data capture register */ + tshsl-ns = <50>; + tsd2d-ns = <50>; + tchsh-ns = <4>; + tslch-ns = <4>; + + partition@qspi-boot { + /* 8MB for raw data. */ + label = "Flash 0 Raw Data"; + reg = <0x0 0x800000>; + }; + partition@qspi-rootfs { + /* 120MB for jffs2 data. */ + label = "Flash 0 jffs2 Filesystem"; + reg = <0x800000 0x7800000>; + }; + }; +}; + +&spi0 { + status = "okay"; +}; + +&uart0 { + status = "okay"; +}; + +&watchdog0 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts index d26f155f5fd9f..93c62c0f3592b 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts @@ -39,6 +39,10 @@ }; }; +&osc1 { + clock-frequency = <25000000>; +}; + &gmac1 { status = "okay"; phy-mode = "rgmii"; From 3af6c4ce77d495d92ee5b24f8be8fead4d78d4eb Mon Sep 17 00:00:00 2001 From: Chris Rauer Date: Fri, 4 Apr 2014 10:05:51 -0700 Subject: [PATCH 107/201] FogBugz #194611: Configure I2C SDA and SCL parameters This patch slows down the i2c frequency from 100khz to 50khz to make the LCD work v2: rebased to new dts changes fixed falling time values and verified with logical analyzer Signed-off-by: Chris Rauer --- arch/arm/boot/dts/socfpga_arria5_socdk.dts | 6 ++++++ arch/arm/boot/dts/socfpga_cyclone5_socdk.dts | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/arch/arm/boot/dts/socfpga_arria5_socdk.dts b/arch/arm/boot/dts/socfpga_arria5_socdk.dts index c2c49486e3e08..186f2ea42e9a5 100644 --- a/arch/arm/boot/dts/socfpga_arria5_socdk.dts +++ b/arch/arm/boot/dts/socfpga_arria5_socdk.dts @@ -77,6 +77,12 @@ &i2c0 { status = "okay"; speed-mode = <0>; + /* + * adjust the falling times to decrease the i2c frequency to 50Khz + * because the LCD module does not work at the standard 100Khz + */ + i2c-sda-falling-time-ns = <5000>; + i2c-scl-falling-time-ns = <5000>; lcd: lcd@28 { compatible = "newhaven,nhd-0216k3z-nsw-bbw"; diff --git a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts index 5bd86cd4d7250..3f830a160f0d8 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts @@ -115,6 +115,13 @@ status = "okay"; speed-mode = <0>; + /* + * adjust the falling times to decrease the i2c frequency to 50Khz + * because the LCD module does not work at the standard 100Khz + */ + i2c-sda-falling-time-ns = <5000>; + i2c-scl-falling-time-ns = <5000>; + lcd: lcd@28 { compatible = "newhaven,nhd-0216k3z-nsw-bbw"; reg = <0x28>; From cd34adf357aef09ee668981610042c2a1979be4a Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 3 Jun 2014 15:21:10 -0500 Subject: [PATCH 108/201] FogBugz #208834: Add SD/MMC card detect Revision D of the Cyclone5 devkit adds support for a GPIO card detect for the SD/MMC. Signed-off-by: Dinh Nguyen --- arch/arm/boot/dts/socfpga_cyclone5_socdk.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts index 3f830a160f0d8..42f71b93a8487 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_socdk.dts @@ -87,6 +87,7 @@ altr,dw-mshc-sdr-timing = <0 3>; slot@0 { + cd-gpios = <&gpio1 18 0>; reg = <0>; bus-width = <4>; }; From fa2be4346f25e9ec4b9d637b8190080addd45eed Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Mon, 28 Oct 2013 11:01:37 -0500 Subject: [PATCH 109/201] FogBugz #163905: Support Denali NAND driver on socfpga platform This patch addresses a few bugs and features in the Denali NAND driver on the socfpga platform. First the bugs: - Reading ONFI parameters would cause a timeout, because the code sent the wrong commands to the controller/device. That was fixed, and in the process, code was added to wait for the appropriate interrupt. - When using the JFFS2 file system, a bug was discovered in the write OOB data function, whereby the controller was left in SPARE mode causing subsequent page reads to fail. The write OOB function was modified so that the controller is left in MAIN access mode. Now the features: - The Denali NAND controller in the socfpga platform has hardware ECC, and several status bits are different because of that. A patch from Jamie Iles was used as the basis for supporting hardware ECC in the code. - The device tree and documentation were modified to support hardware ECC. - One of our test socfpga boards had a NAND chip which was locked by default. To support writing to the chip, the UNLOCK1 and UNLOCK2 commands were implemented. The nand_unlock calls were added as well. (Note, the mtd layer has _lock, _unlock, and is probably a better place to implement this functionality than the nand driver). - The mtd_device_register call was replaced with mtd_device_parse_register so that device tree partitions are setup by the mtd layer. Example partitions were added to the device tree. - In the process of supporting our specific controller configuration, duplicate definitions of bad block table descriptions were removed from the denali code. They also exist in the nand driver code. Signed-off-by: Graham Moore --- .../devicetree/bindings/mtd/denali-nand.txt | 3 + drivers/mtd/nand/denali.c | 177 +++++++++++++----- drivers/mtd/nand/denali.h | 18 +- drivers/mtd/nand/nand_base.c | 6 + 4 files changed, 158 insertions(+), 46 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt index b04d03a1d4995..13522208f0a31 100644 --- a/Documentation/devicetree/bindings/mtd/denali-nand.txt +++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt @@ -6,6 +6,8 @@ Required properties: - reg-names: Should contain the reg names "nand_data" and "denali_reg" - interrupts : The interrupt number. - dm-mask : DMA bit mask + - have-hw-ecc-fixup : boolean indicating controller has ECC correction + in hardware, and also has slightly diffrent interrupt status bits. The device tree may optionally contain sub-nodes describing partitions of the address space. See partition.txt for more detail. @@ -20,4 +22,5 @@ nand: nand@ff900000 { reg-names = "nand_data", "denali_reg"; interrupts = <0 144 4>; dma-mask = <0xffffffff>; + have-hw-ecc-fixup; }; diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 9f2012a3e7643..29d0daf9c3806 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "denali.h" @@ -52,7 +53,12 @@ MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting." INTR_STATUS__TIME_OUT | \ INTR_STATUS__ERASE_FAIL | \ INTR_STATUS__RST_COMP | \ - INTR_STATUS__ERASE_COMP) + INTR_STATUS__ERASE_COMP | \ + INTR_STATUS__ECC_UNCOR_ERR) +/* And here we use a variable for interrupt mask, bcs we want to + * change the irq mask during init. That is, we want to enable R/B + * interrupt during init, but not at other times */ +static uint32_t denali_irq_mask = DENALI_IRQ_ALL; /* indicates whether or not the internal value for the flash bank is * valid or not */ @@ -560,7 +566,7 @@ static void denali_irq_init(struct denali_nand_info *denali) /* Disable global interrupts */ denali_set_intr_modes(denali, false); - int_mask = DENALI_IRQ_ALL; + int_mask = denali_irq_mask; /* Clear all status bits */ for (i = 0; i < denali->max_banks; ++i) @@ -589,7 +595,7 @@ static void denali_irq_enable(struct denali_nand_info *denali, */ static inline uint32_t denali_irq_detected(struct denali_nand_info *denali) { - return read_interrupt_status(denali) & DENALI_IRQ_ALL; + return read_interrupt_status(denali) & denali_irq_mask; } /* Interrupts are cleared by writing a 1 to the appropriate status bit */ @@ -852,6 +858,16 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) dev_err(denali->dev, "OOB write failed\n"); status = -EIO; } + + /* set the device back to MAIN_ACCESS */ + { + uint32_t addr; + uint32_t cmd; + addr = BANK(denali->flash_bank) | denali->page; + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, MAIN_ACCESS); + } + } else { dev_err(denali->dev, "unable to send pipeline command\n"); status = -EIO; @@ -919,7 +935,12 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, bool check_erased_page = false; unsigned int bitflips = 0; - if (irq_status & INTR_STATUS__ECC_ERR) { + if (denali->have_hw_ecc_fixup && + (irq_status & INTR_STATUS__ECC_UNCOR_ERR)) { + clear_interrupts(denali); + denali_set_intr_modes(denali, true); + check_erased_page = true; + } else if (irq_status & INTR_STATUS__ECC_ERR) { /* read the ECC errors. we'll ignore them for now */ uint32_t err_address = 0, err_correction_info = 0; uint32_t err_byte = 0, err_sector = 0, err_device = 0; @@ -1115,15 +1136,16 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - unsigned int max_bitflips; + unsigned int max_bitflips = 0; struct denali_nand_info *denali = mtd_to_denali(mtd); dma_addr_t addr = denali->buf.dma_buf; size_t size = denali->mtd.writesize + denali->mtd.oobsize; uint32_t irq_status = 0; - uint32_t irq_mask = INTR_STATUS__ECC_TRANSACTION_DONE | - INTR_STATUS__ECC_ERR; + uint32_t irq_mask = denali->have_hw_ecc_fixup ? + (INTR_STATUS__DMA_CMD_COMP) : + (INTR_STATUS__ECC_TRANSACTION_DONE | INTR_STATUS__ECC_ERR); bool check_erased_page = false; if (page != denali->page) { @@ -1152,14 +1174,24 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, denali_enable_dma(denali, false); if (check_erased_page) { - read_oob_data(&denali->mtd, chip->oob_poi, denali->page); - - /* check ECC failures that may have occurred on erased pages */ - if (check_erased_page) { + if (denali->have_hw_ecc_fixup) { + /* When we have hw ecc fixup, don't check oob. + * That code below looks jacked up anyway. I mean, + * look at it, wtf? */ if (!is_erased(buf, denali->mtd.writesize)) denali->mtd.ecc_stats.failed++; - if (!is_erased(buf, denali->mtd.oobsize)) - denali->mtd.ecc_stats.failed++; + } else { + read_oob_data(&denali->mtd, chip->oob_poi, + denali->page); + + /* check ECC failures that may have occurred on + * erased pages */ + if (check_erased_page) { + if (!is_erased(buf, denali->mtd.writesize)) + denali->mtd.ecc_stats.failed++; + if (!is_erased(buf, denali->mtd.oobsize)) + denali->mtd.ecc_stats.failed++; + } } } return max_bitflips; @@ -1215,6 +1247,13 @@ static uint8_t denali_read_byte(struct mtd_info *mtd) return result; } +static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + for (i = 0; i < len; i++) + buf[i] = denali_read_byte(mtd); +} + static void denali_select_chip(struct mtd_info *mtd, int chip) { struct denali_nand_info *denali = mtd_to_denali(mtd); @@ -1258,6 +1297,8 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, { struct denali_nand_info *denali = mtd_to_denali(mtd); uint32_t addr, id; + uint32_t pages_per_block; + uint32_t block; int i; switch (cmd) { @@ -1267,15 +1308,14 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, read_status(denali); break; case NAND_CMD_READID: - case NAND_CMD_PARAM: reset_buf(denali); /*sometimes ManufactureId read from register is not right * e.g. some of Micron MT29F32G08QAA MLC NAND chips * So here we send READID cmd to NAND insteand * */ addr = (uint32_t)MODE_11 | BANK(denali->flash_bank); - index_addr(denali, (uint32_t)addr | 0, 0x90); - index_addr(denali, (uint32_t)addr | 1, 0); + index_addr(denali, (uint32_t)addr | 0, cmd); + index_addr(denali, (uint32_t)addr | 1, col & 0xFF); for (i = 0; i < 5; i++) { index_addr_read_data(denali, (uint32_t)addr | 2, @@ -1283,6 +1323,37 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, write_byte_to_buf(denali, id); } break; + case NAND_CMD_PARAM: + reset_buf(denali); + + /* turn on R/B interrupt */ + denali_set_intr_modes(denali, false); + denali_irq_mask = DENALI_IRQ_ALL | INTR_STATUS__INT_ACT; + clear_interrupts(denali); + denali_irq_enable(denali, denali_irq_mask); + denali_set_intr_modes(denali, true); + + addr = (uint32_t)MODE_11 | BANK(denali->flash_bank); + index_addr(denali, (uint32_t)addr | 0, cmd); + index_addr(denali, (uint32_t)addr | 1, col & 0xFF); + /* Wait tR time... */ + udelay(25); + /* And then wait for R/B interrupt */ + wait_for_irq(denali, INTR_STATUS__INT_ACT); + + /* turn off R/B interrupt now */ + denali_irq_mask = DENALI_IRQ_ALL; + denali_set_intr_modes(denali, false); + denali_irq_enable(denali, denali_irq_mask); + denali_set_intr_modes(denali, true); + + for (i = 0; i < 256; i++) { + index_addr_read_data(denali, + (uint32_t)addr | 2, + &id); + write_byte_to_buf(denali, id); + } + break; case NAND_CMD_READ0: case NAND_CMD_SEQIN: denali->page = page; @@ -1293,6 +1364,18 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, case NAND_CMD_READOOB: /* TODO: Read OOB data */ break; + case NAND_CMD_UNLOCK1: + pages_per_block = mtd->erasesize / mtd->writesize; + block = page / pages_per_block; + addr = (uint32_t)MODE_10 | (block * pages_per_block); + index_addr(denali, addr, 0x10); + break; + case NAND_CMD_UNLOCK2: + pages_per_block = mtd->erasesize / mtd->writesize; + block = (page+pages_per_block-1) / pages_per_block; + addr = (uint32_t)MODE_10 | (block * pages_per_block); + index_addr(denali, addr, 0x11); + break; default: pr_err(": unsupported command received 0x%x\n", cmd); break; @@ -1368,29 +1451,6 @@ static struct nand_ecclayout nand_15bit_oob = { .eccbytes = 26, }; -static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; -static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, - .len = 4, - .veroffs = 12, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, - .len = 4, - .veroffs = 12, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - /* initialize driver data structures */ static void denali_drv_init(struct denali_nand_info *denali) { @@ -1416,6 +1476,7 @@ static void denali_drv_init(struct denali_nand_info *denali) int denali_init(struct denali_nand_info *denali) { int ret; + uint32_t val; if (denali->platform == INTEL_CE4100) { /* Due to a silicon limitation, we can only support @@ -1452,6 +1513,7 @@ int denali_init(struct denali_nand_info *denali) denali->mtd.priv = &denali->nand; /* register the driver with the NAND core subsystem */ + denali->nand.read_buf = denali_read_buf; denali->nand.select_chip = denali_select_chip; denali->nand.cmdfunc = denali_cmdfunc; denali->nand.read_byte = denali_read_byte; @@ -1514,14 +1576,19 @@ int denali_init(struct denali_nand_info *denali) * this stage requires information regarding ECC and * bad block management. */ - /* Bad block management */ - denali->nand.bbt_td = &bbt_main_descr; - denali->nand.bbt_md = &bbt_mirror_descr; + /* Bad block table description is set by nand framework, + see nand_bbt.c */ - /* skip the scan for now until we have OOB read and write support */ denali->nand.bbt_options |= NAND_BBT_USE_FLASH; - denali->nand.options |= NAND_SKIP_BBTSCAN; denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + if (denali->have_hw_ecc_fixup) { + /* We have OOB support, so allow scan of BBT + and leave the OOB alone */ + denali->nand.bbt_options |= NAND_BBT_NO_OOB; + } else { + /* skip the scan for now until we have OOB read and write support */ + denali->nand.options |= NAND_SKIP_BBTSCAN; + } /* Denali Controller only support 15bit and 8bit ECC in MRST, * so just let controller do 15bit ECC for MLC and 8bit ECC for @@ -1585,12 +1652,32 @@ int denali_init(struct denali_nand_info *denali) denali->nand.ecc.write_oob = denali_write_oob; denali->nand.erase = denali_erase; + /* Occasionally the controller is in SPARE or MAIN+SPARE + mode upon startup, and we want it to be MAIN only */ + val = ioread32(denali->flash_reg + TRANSFER_MODE); + if (val != 0) { + int i; + dev_dbg(denali->dev, + "setting TRANSFER_MODE (%08x) back to MAIN only\n", val); + /* put all banks in MAIN mode, no SPARE */ + iowrite32(0, denali->flash_reg + TRANSFER_SPARE_REG); + for (i = 0; i < 4; i++) + index_addr(denali, MODE_10 | BANK(i) | 1, + MAIN_ACCESS); + } + if (nand_scan_tail(&denali->mtd)) { ret = -ENXIO; goto failed_req_irq; } - ret = mtd_device_register(&denali->mtd, NULL, 0); + /* We use the parse function and pass the of_node bcs + we want to pick up partitions from device tree */ + ret = mtd_device_parse_register(&denali->mtd, NULL, + &(struct mtd_part_parser_data){ + .of_node = denali->dev->of_node, + }, + 0, 0); if (ret) { dev_err(denali->dev, "Spectra: Failed to register MTD: %d\n", ret); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 9668174624217..d824626dd4128 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -214,6 +214,16 @@ #define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50)) #define INTR_EN(__bank) (0x420 + ((__bank) * 0x50)) +/* + * Some versions of the IP have the ECC fixup handled in hardware. In this + * configuration we only get interrupted when the error is uncorrectable. + * Unfortunately this bit replaces INTR_STATUS__ECC_TRANSACTION_DONE from the + * old IP. + * taken from patch by Jamie Iles + * support hardware with internal ECC fixup + */ +#define INTR_STATUS__ECC_UNCOR_ERR 0x0001 + #define INTR_STATUS__ECC_TRANSACTION_DONE 0x0001 #define INTR_STATUS__ECC_ERR 0x0002 #define INTR_STATUS__DMA_CMD_COMP 0x0004 @@ -326,6 +336,11 @@ #define CHNL_ACTIVE__CHANNEL2 0x0004 #define CHNL_ACTIVE__CHANNEL3 0x0008 +#define FLASH_BURST_LENGTH 0x770 +#define CHIP_INTERLEAVE_ENABLE_AND_ALLOW_INT_READS 0X780 +#define NO_OF_BLOCKS_PER_LUN 0X790 +#define LUN_STATUS_CMD 0X7A0 + #define ACTIVE_SRC_ID 0x800 #define ACTIVE_SRC_ID__VALUE 0x00ff @@ -476,7 +491,7 @@ struct denali_nand_info { struct device *dev; int total_used_banks; uint32_t block; /* stored for future use */ - uint16_t page; + uint32_t page; void __iomem *flash_reg; /* Mapped io reg base address */ void __iomem *flash_mem; /* Mapped io reg base address */ @@ -494,6 +509,7 @@ struct denali_nand_info { uint32_t blksperchip; uint32_t bbtskipbytes; uint32_t max_banks; + bool have_hw_ecc_fixup; }; extern int denali_init(struct denali_nand_info *denali); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 4f3e80c68a266..8f164f58b722b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2502,6 +2502,8 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, struct mtd_oob_ops ops; int ret; + nand_unlock(mtd, to, len); + nand_get_device(mtd, FL_WRITING); ops.len = len; ops.datbuf = (uint8_t *)buf; @@ -2619,6 +2621,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, return -EINVAL; } + nand_unlock(mtd, to, ops->len); + nand_get_device(mtd, FL_WRITING); switch (ops->mode) { @@ -2692,6 +2696,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; + nand_unlock(mtd, instr->addr, instr->len); + /* Grab the lock and see if the device is available */ nand_get_device(mtd, FL_ERASING); From b349a5823dfbc26d44e96d7ff7cf578d0fe846e5 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Tue, 3 Jun 2014 11:52:39 -0500 Subject: [PATCH 110/201] FogBugz #208682: Denali NAND driver misidentifies Hynix chips The denali driver was not reading enough id bytes to determine the parameters for a Hynix chip. The chip was therefore not identified properly, and the page size and oob size were calculated incorrectly. This resulted in a message stating the OOB area was not big enough for 8-bit ECC. Signed-off-by: Graham Moore --- drivers/mtd/nand/denali.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 29d0daf9c3806..f6195371f189b 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -494,7 +494,7 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) addr = (uint32_t)MODE_11 | BANK(denali->flash_bank); index_addr(denali, (uint32_t)addr | 0, 0x90); index_addr(denali, (uint32_t)addr | 1, 0); - for (i = 0; i < 5; i++) + for (i = 0; i < 8; i++) index_addr_read_data(denali, addr | 2, &id_bytes[i]); maf_id = id_bytes[0]; device_id = id_bytes[1]; @@ -1316,7 +1316,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, addr = (uint32_t)MODE_11 | BANK(denali->flash_bank); index_addr(denali, (uint32_t)addr | 0, cmd); index_addr(denali, (uint32_t)addr | 1, col & 0xFF); - for (i = 0; i < 5; i++) { + for (i = 0; i < 8; i++) { index_addr_read_data(denali, (uint32_t)addr | 2, &id); From 81d28a1c068cdc8ed260ef5198a4afab300410ed Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 28 May 2014 22:40:13 -0500 Subject: [PATCH 111/201] FogBugz #206007: Fix gpio dts entry for the correct clock The correct clock for the HPS gpio(s) should be the l4_mp_clk. Signed-off-by: Dinh Nguyen --- arch/arm/boot/dts/socfpga.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index bd5d6a6708f3d..56d180121eb53 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -573,7 +573,7 @@ #size-cells = <0>; compatible = "snps,dw-apb-gpio"; reg = <0xff708000 0x1000>; - clocks = <&per_base_clk>; + clocks = <&l4_mp_clk>; status = "disabled"; gpio0: gpio-controller@0 { @@ -593,7 +593,7 @@ #size-cells = <0>; compatible = "snps,dw-apb-gpio"; reg = <0xff709000 0x1000>; - clocks = <&per_base_clk>; + clocks = <&l4_mp_clk>; status = "disabled"; gpio1: gpio-controller@0 { @@ -613,7 +613,7 @@ #size-cells = <0>; compatible = "snps,dw-apb-gpio"; reg = <0xff70a000 0x1000>; - clocks = <&per_base_clk>; + clocks = <&l4_mp_clk>; status = "disabled"; gpio2: gpio-controller@0 { From e3892d4f48900c6f414ed0de16fc8c5af7bfab6c Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 20 Nov 2013 09:39:17 -0600 Subject: [PATCH 112/201] FogBugz #169263: Correct the parent clock for l3_sp_clk and dbg_clk The l3_sp_clk's parent should be the l3_mp_clk. This will account for the extra divider that is present for the l3_mp_clk. The dbg_clk's parent should be the dbg_at_clk. This will account for the extra divider that is present for the dbg_at_clk. Signed-off-by: Dinh Nguyen --- arch/arm/boot/dts/socfpga.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 56d180121eb53..6d483c4034e9d 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -316,7 +316,7 @@ l3_sp_clk: l3_sp_clk { #clock-cells = <0>; compatible = "altr,socfpga-gate-clk"; - clocks = <&mainclk>; + clocks = <&l3_mp_clk>; div-reg = <0x64 2 2>; }; @@ -347,7 +347,7 @@ dbg_clk: dbg_clk { #clock-cells = <0>; compatible = "altr,socfpga-gate-clk"; - clocks = <&dbg_base_clk>; + clocks = <&dbg_at_clk>; div-reg = <0x68 2 2>; clk-gate = <0x60 5>; }; From 20761d90489be43900aa0e84613b310b053a8fd0 Mon Sep 17 00:00:00 2001 From: Matthew Gerlach Date: Mon, 3 Feb 2014 14:22:59 -0800 Subject: [PATCH 113/201] FogBugz #178383: add missing clock gates to socfpga.dtsi The gates for the clocks coming out of the sdram pll were missing. The change adds the missing nodes to the device tree. Signed-off-by: Matthew Gerlach --- arch/arm/boot/dts/socfpga.dtsi | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 6d483c4034e9d..e3868ea2dda61 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -471,8 +471,37 @@ clocks = <&f2s_periph_ref_clk>, <&main_qspi_clk>, <&per_qspi_clk>; clk-gate = <0xa0 11>; }; + + ddr_dqs_clk_gate: ddr_dqs_clk_gate { + #clock-cells = <0>; + compatible = "altr,socfpga-gate-clk"; + clocks = <&ddr_dqs_clk>; + clk-gate = <0xd8 0>; + }; + + ddr_2x_dqs_clk_gate: ddr_2x_dqs_clk_gate { + #clock-cells = <0>; + compatible = "altr,socfpga-gate-clk"; + clocks = <&ddr_2x_dqs_clk>; + clk-gate = <0xd8 1>; + }; + + ddr_dq_clk_gate: ddr_dq_clk_gate { + #clock-cells = <0>; + compatible = "altr,socfpga-gate-clk"; + clocks = <&ddr_dq_clk>; + clk-gate = <0xd8 2>; + }; + + h2f_user2_clk: h2f_user2_clk { + #clock-cells = <0>; + compatible = "altr,socfpga-gate-clk"; + clocks = <&h2f_usr2_clk>; + clk-gate = <0xd8 3>; + }; + }; - }; + }; gmac0: ethernet@ff700000 { compatible = "altr,socfpga-stmmac", "snps,dwmac-3.70a", "snps,dwmac"; From 6aa1db7ab808da183d678d7b8e0af4a6767f7675 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Thu, 29 Nov 2012 16:06:55 -0600 Subject: [PATCH 114/201] FogBugz #84276: lcd driver on i2c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports the Newhaven NHD‐0216K3Z‐NSW‐BBW 2x16 LCD module as i2c slave. Device will show up as a ttyLCD* * Backspace is supported to the beginning of the current line. * i.e. printf '\b' > /dev/ttyLCD0 * ESC [ 2 J * erase whole display and reset cursor to home. * i.e. printf '\e[2J' > /dev/ttyLCD0 * ESC [ 2 K * erase current line and set cursor to beginning of line. * i.e. printf '\e[2K' > /dev/ttyLCD0 * CR and LF are supported. * Vertical scroll when cursor is on bottom line and receive end of line. Set the I2C0 I2C adapter to standard speed as LCD supports 100KHz I2C. Signed-off-by: Alan Tull --- drivers/tty/Kconfig | 5 + drivers/tty/Makefile | 1 + drivers/tty/newhaven_lcd.c | 498 +++++++++++++++++++++++++++++++++++++ 3 files changed, 504 insertions(+) create mode 100644 drivers/tty/newhaven_lcd.c diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index b24aa010f68c5..53ad9f138e974 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -175,6 +175,11 @@ config BFIN_JTAG_COMM_CONSOLE bool "Console on Blackfin JTAG" depends on BFIN_JTAG_COMM=y +config NEWHAVEN_LCD + tristate "NEWHAVEN LCD" + help + Add support for a TTY device on a Newhaven I2C LCD device. + config SERIAL_NONSTANDARD bool "Non-standard serial port support" depends on HAS_IOMEM diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 58ad1c05b7f8f..f6a3d565ef2c6 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -29,5 +29,6 @@ obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_DA_TTY) += metag_da.o +obj-$(CONFIG_NEWHAVEN_LCD) += newhaven_lcd.o obj-y += ipwireless/ diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c new file mode 100644 index 0000000000000..9fd25cf798fc2 --- /dev/null +++ b/drivers/tty/newhaven_lcd.c @@ -0,0 +1,498 @@ +/* + * TTY on a LCD connected to I2C + * Supports Newhaven NHD‐0216K3Z‐NSW‐BBW + * + * Copyright (C) 2013 Altera Corporation. All rights reserved. + * + * 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 . + */ + +#define DRV_NAME "lcd-comm" +#define DEV_NAME "ttyLCD" + +#include +#include +#include +#include +#include + +#define LCD_COMMAND 0xfe +#define LCD_DISPLAY_ON 0x41 +#define LCD_DISPLAY_OFF 0x42 +#define LCD_SET_CURSOR 0x45 +#define LCD_BACKSPACE 0x4e +#define LCD_CLEAR_SCREEN 0x51 + +#define ASCII_BS 0x08 +#define ASCII_LF 0x0a +#define ASCII_CR 0x0d +#define ASCII_ESC 0x1b +#define ASCII_SPACE 0x20 + +struct lcd { + struct device *dev; + struct i2c_client *client; + struct tty_driver *lcd_tty_driver; + struct tty_port port; + unsigned int width; + unsigned int height; + char *buffer; + unsigned int top_line; + unsigned int cursor_line; + unsigned int cursor_col; +}; + +#define MAX_LCDS 1 +static struct lcd lcd_data_static[MAX_LCDS]; + +static int lcd_cmd_no_params(struct lcd *lcd_data, u8 cmd) +{ + int count; + u8 buf[2] = {LCD_COMMAND, cmd}; + + count = i2c_master_send(lcd_data->client, buf, sizeof(buf)); + if (count != sizeof(buf)) { + pr_err("%s: i2c_master_send returns %d\n", __func__, count); + return -1; + } + return 0; +} + +static int lcd_cmd_one_param(struct lcd *lcd_data, u8 cmd, u8 param) +{ + int count; + u8 buf[3] = {LCD_COMMAND, cmd, param}; + + count = i2c_master_send(lcd_data->client, buf, sizeof(buf)); + if (count != sizeof(buf)) { + pr_err("%s: i2c_master_send returns %d\n", __func__, count); + return -1; + } + return 0; +} + +static int lcd_cmd_display_on(struct lcd *lcd_data) +{ + return lcd_cmd_no_params(lcd_data, LCD_DISPLAY_ON); +} + +static int lcd_cmd_display_off(struct lcd *lcd_data) +{ + return lcd_cmd_no_params(lcd_data, LCD_DISPLAY_OFF); +} + +static int lcd_cmd_clear_screen(struct lcd *lcd_data) +{ + return lcd_cmd_no_params(lcd_data, LCD_CLEAR_SCREEN); +} + +static int lcd_cmd_backspace(struct lcd *lcd_data) +{ + return lcd_cmd_no_params(lcd_data, LCD_BACKSPACE); +} + +/* From NHD-0216K3Z-NSW-BBY Display Module datasheet. */ +#define LCD_CURSOR_LINE_MULTIPLIER 0x40 + +static int lcd_cmd_set_cursor(struct lcd *lcd_data, u8 line, u8 col) +{ + u8 cursor; + + BUG_ON((line >= lcd_data->height) || (col >= lcd_data->width)); + + cursor = col + (LCD_CURSOR_LINE_MULTIPLIER * line); + return lcd_cmd_one_param(lcd_data, LCD_SET_CURSOR, cursor); +} + +/* + * Map a line on the lcd display to a line on the buffer. + * Note that the top line on the display (line 0) may not be line 0 on the + * buffer due to scrolling. + */ +static unsigned int lcd_line_to_buf_line(struct lcd *lcd_data, + unsigned int line) +{ + unsigned int buf_line; + + buf_line = line + lcd_data->top_line; + + if (buf_line >= lcd_data->height) + buf_line -= lcd_data->height; + + return buf_line; +} + +/* Returns a pointer to the line, column position in the lcd buffer */ +static char *lcd_buf_pointer(struct lcd *lcd_data, unsigned int line, + unsigned int col) +{ + unsigned int buf_line; + char *buf; + + if ((lcd_data->cursor_line >= lcd_data->height) || + (lcd_data->cursor_col >= lcd_data->width)) + return lcd_data->buffer; + + buf_line = lcd_line_to_buf_line(lcd_data, line); + + buf = lcd_data->buffer + (buf_line * lcd_data->width) + col; + + return buf; +} + +static void lcd_clear_buffer_line(struct lcd *lcd_data, int line) +{ + char *buf; + + BUG_ON(line >= lcd_data->height); + + buf = lcd_buf_pointer(lcd_data, line, 0); + memset(buf, ASCII_SPACE, lcd_data->width); +} + +static void lcd_clear_buffer(struct lcd *lcd_data) +{ + memset(lcd_data->buffer, ASCII_SPACE, + lcd_data->width * lcd_data->height); + lcd_data->cursor_line = 0; + lcd_data->cursor_col = 0; + lcd_data->top_line = 0; +} + +static void lcd_reprint_one_line(struct lcd *lcd_data, u8 line) +{ + char *buf = lcd_buf_pointer(lcd_data, line, 0); + + lcd_cmd_set_cursor(lcd_data, line, 0); + i2c_master_send(lcd_data->client, buf, lcd_data->width); +} + +static void lcd_print_top_n_lines(struct lcd *lcd_data, u8 lines) +{ + unsigned int disp_line = 0; + + while (disp_line < lines) + lcd_reprint_one_line(lcd_data, disp_line++); +} + +static void lcd_add_char_at_cursor(struct lcd *lcd_data, char val) +{ + char *buf; + + buf = lcd_buf_pointer(lcd_data, lcd_data->cursor_line, + lcd_data->cursor_col); + + *buf = val; + + if (lcd_data->cursor_col < (lcd_data->width - 1)) + lcd_data->cursor_col++; +} + +static void lcd_crlf(struct lcd *lcd_data) +{ + if (lcd_data->cursor_line < (lcd_data->height - 1)) { + /* Next line is blank, carriage return to beginning of line. */ + lcd_data->cursor_line++; + if (lcd_data->cursor_line >= lcd_data->height) + lcd_data->cursor_line = 0; + + } else { + /* Display is full. Scroll up one line. */ + lcd_data->top_line++; + if (lcd_data->top_line >= lcd_data->height) + lcd_data->top_line = 0; + + lcd_cmd_clear_screen(lcd_data); + lcd_clear_buffer_line(lcd_data, lcd_data->cursor_line); + lcd_print_top_n_lines(lcd_data, lcd_data->height); + } + + lcd_cmd_set_cursor(lcd_data, lcd_data->height - 1, 0); + lcd_data->cursor_col = 0; +} + +static void lcd_backspace(struct lcd *lcd_data) +{ + if (lcd_data->cursor_col > 0) { + lcd_cmd_backspace(lcd_data); + lcd_data->cursor_col--; + } +} + +static int lcd_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct lcd *lcd_data = tty->driver_data; + +#ifdef DEBUG + char *dbgbuf = kzalloc(count + 1, GFP_KERNEL); + strncpy(dbgbuf, buf, count); + pr_debug("\n%s: count=%d buf[0]=%02x --->%s<---\n", __func__, count, + buf[0], dbgbuf); +#endif /* DEBUG */ + + if (count == 0) { +#ifdef DEBUG + kfree(dbgbuf); +#endif /* DEBUG */ + return 0; + } + + /* process displayable chars */ + if ((0x20 <= buf[0]) && (buf[0] <= 0x7f)) { + int i = 0; + while (i < count) { + lcd_add_char_at_cursor(lcd_data, buf[i]); + i++; + } + + /* flush the line out to the display when we get to eol */ + lcd_reprint_one_line(lcd_data, lcd_data->cursor_line); + + /* + * ECMA-48 CSI sequences (from console_codes man page) + * + * ESC [ 2 J : erase whole display. + * ESC [ 2 K : erase whole line. + */ + } else if (buf[0] == ASCII_ESC) { + if ((count >= 4) && + (buf[1] == '[') && + (buf[2] == '2') && + (buf[3] == 'J')) { + pr_debug("ESC [2J = clear screan\n"); + lcd_clear_buffer(lcd_data); + lcd_cmd_clear_screen(lcd_data); + + } else if ((count >= 4) && + (buf[1] == '[') && + (buf[2] == '2') && + (buf[3] == 'K')) { + pr_debug("ESC [2K = clear line\n"); + lcd_clear_buffer_line(lcd_data, lcd_data->cursor_line); + lcd_reprint_one_line(lcd_data, lcd_data->cursor_line); + lcd_cmd_set_cursor(lcd_data, lcd_data->cursor_line, 0); + lcd_data->cursor_col = 0; + + } else + pr_debug("Unsupported escape sequence\n"); + + } else if ((count == 2) && + (buf[0] == ASCII_CR) && (buf[1] == ASCII_LF)) { + pr_debug("ASCII_CR/LF\n"); + lcd_crlf(lcd_data); + + } else if ((count == 1) && (buf[0] == ASCII_CR)) { + pr_debug("ASCII_CR\n"); + lcd_crlf(lcd_data); + + } else if ((count == 1) && (buf[0] == ASCII_LF)) { + pr_debug("ASCII_LF\n"); + lcd_crlf(lcd_data); + + } else if ((count == 1) && (buf[0] == ASCII_BS)) { + pr_debug("ASCII_BS\n"); + lcd_backspace(lcd_data); + } else { + pr_debug("%s - Unsupported command 0x%02x\n", __func__, buf[0]); + } + +#ifdef DEBUG + kfree(dbgbuf); +#endif /* DEBUG */ + return count; +} + +static int lcd_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct lcd *lcd_data; + + lcd_data = &lcd_data_static[tty->index]; + if (lcd_data == NULL) + return -ENODEV; + + tty->driver_data = lcd_data; + + return tty_port_install(&lcd_data->port, driver, tty); +} + +static int lcd_open(struct tty_struct *tty, struct file *filp) +{ + struct lcd *lcd_data = tty->driver_data; + unsigned long flags; + + tty->driver_data = lcd_data; + spin_lock_irqsave(&lcd_data->port.lock, flags); + lcd_data->port.count++; + spin_unlock_irqrestore(&lcd_data->port.lock, flags); + tty_port_tty_set(&lcd_data->port, tty); + + return 0; +} + +static void lcd_close(struct tty_struct *tty, struct file *filp) +{ + struct lcd *lcd_data = tty->driver_data; + unsigned long flags; + bool last; + + spin_lock_irqsave(&lcd_data->port.lock, flags); + --lcd_data->port.count; + last = (lcd_data->port.count == 0); + spin_unlock_irqrestore(&lcd_data->port.lock, flags); + if (last) + tty_port_tty_set(&lcd_data->port, NULL); +} + +static int lcd_write_room(struct tty_struct *tty) +{ + struct lcd *lcd_data = tty->driver_data; + + return lcd_data->height * lcd_data->width; +} + +static const struct tty_operations lcd_ops = { + .install = lcd_install, + .open = lcd_open, + .close = lcd_close, + .write = lcd_write, + .write_room = lcd_write_room, +}; + +static int __devinit lcd_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct device_node *np = client->dev.of_node; + struct lcd *lcd_data; + struct tty_driver *lcd_tty_driver; + unsigned int width = 0, height = 0, i; + char *buffer; + int ret = -ENOMEM; + + of_property_read_u32(np, "height", &height); + of_property_read_u32(np, "width", &width); + if ((width == 0) || (height == 0)) { + dev_err(&client->dev, + "Need to specify lcd width/height in device tree\n"); + ret = -EINVAL; + goto err_devtree; + } + + for (i = 0 ; i < MAX_LCDS ; i++) + if (lcd_data_static[i].client == NULL) + break; + if (i >= MAX_LCDS) { + ret = -ENODEV; + dev_warn(&client->dev, + "More than %d I2C LCD displays found. Giving up.\n", + MAX_LCDS); + goto err_devtree; + } + lcd_data = &lcd_data_static[i]; + + buffer = kzalloc(height * width, GFP_KERNEL); + if (!buffer) + goto err_devtree; + + i2c_set_clientdata(client, lcd_data); + + lcd_data->client = client; + lcd_data->dev = &client->dev; + lcd_data->height = height; + lcd_data->width = width; + lcd_data->buffer = buffer; + + dev_set_drvdata(&client->dev, lcd_data); + tty_port_init(&lcd_data->port); + lcd_tty_driver = alloc_tty_driver(MAX_LCDS); + if (!lcd_tty_driver) + goto err_driver; + + lcd_tty_driver->driver_name = DRV_NAME; + lcd_tty_driver->name = DEV_NAME; + lcd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + lcd_tty_driver->subtype = SERIAL_TYPE_NORMAL; + lcd_tty_driver->init_termios = tty_std_termios; + tty_set_operations(lcd_tty_driver, &lcd_ops); + + ret = tty_register_driver(lcd_tty_driver); + if (ret) + goto err_register; + + lcd_data->lcd_tty_driver = lcd_tty_driver; + + lcd_clear_buffer(lcd_data); + lcd_cmd_display_on(lcd_data); + lcd_cmd_clear_screen(lcd_data); + + dev_info(&client->dev, "LCD driver initialized\n"); + + return 0; + +err_register: + put_tty_driver(lcd_data->lcd_tty_driver); +err_driver: + kfree(buffer); +err_devtree: + return ret; +} + +static int __exit lcd_remove(struct i2c_client *client) +{ + struct lcd *lcd_data = i2c_get_clientdata(client); + + lcd_cmd_display_off(lcd_data); + + tty_unregister_driver(lcd_data->lcd_tty_driver); + put_tty_driver(lcd_data->lcd_tty_driver); + kfree(lcd_data->buffer); + + return 0; +} + +static const struct of_device_id lcd_of_match[] = { + { .compatible = "newhaven,nhd‐0216k3z‐nsw‐bbw", }, +}; + +static const struct i2c_device_id lcd_id[] = { + { DRV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lcd_id); + +static struct i2c_driver lcd_i2c_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = lcd_of_match, + }, + .probe = lcd_probe, + .remove = __devexit_p(lcd_remove), + .id_table = lcd_id, +}; + +static int __init lcd_init(void) +{ + return i2c_add_driver(&lcd_i2c_driver); +} +subsys_initcall(lcd_init); + +static void __exit lcd_exit(void) +{ + i2c_del_driver(&lcd_i2c_driver); +} +module_exit(lcd_exit); + +MODULE_DESCRIPTION("LCD 2x16"); +MODULE_LICENSE("GPL"); From 242b1d9175bab458f106707eb07b39d89692783e Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Fri, 15 Feb 2013 11:06:38 -0600 Subject: [PATCH 115/201] FogBugz #100586: Set lcd backlight brightness to max Some of the Newhaven LCD modules have the backlight turned off by default. Send an I2C command to the module during initialization to set the backlight brightness to maximum to turn it on. Add documentation for device tree bindings. Signed-off-by: Alan Tull --- .../devicetree/bindings/tty/newhaven_lcd.txt | 21 +++++++++++++++++++ drivers/tty/newhaven_lcd.c | 20 +++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/tty/newhaven_lcd.txt diff --git a/Documentation/devicetree/bindings/tty/newhaven_lcd.txt b/Documentation/devicetree/bindings/tty/newhaven_lcd.txt new file mode 100644 index 0000000000000..5ff0438640d69 --- /dev/null +++ b/Documentation/devicetree/bindings/tty/newhaven_lcd.txt @@ -0,0 +1,21 @@ +* TTY on a Newhaven NHD‐0216K3Z‐NSW‐BBW LCD connected to I2C + +Required properties: +- compatible: Should be "newhaven,nhd‐0216k3z‐nsw‐bbw"; +- reg: i2c address +- height: should be 2 lines +- width: should be 16 characters +- brightness: backlight brightness. Range is 1 to 8, where + 1=OFF and 8=maximum brightness. + +Example: + +&i2c0 { + lcd: lcd@28 { + compatible = "newhaven,nhd‐0216k3z‐nsw‐bbw"; + reg = <0x28>; + height = <2>; + width = <16>; + brightness = <8>; + }; + diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index 9fd25cf798fc2..14213d05f6bba 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -33,6 +33,10 @@ #define LCD_SET_CURSOR 0x45 #define LCD_BACKSPACE 0x4e #define LCD_CLEAR_SCREEN 0x51 +#define LCD_BRIGHTNESS 0x53 + +#define LCD_BRIGHTNESS_MIN 1 +#define LCD_BRIGHTNESS_MAX 8 #define ASCII_BS 0x08 #define ASCII_LF 0x0a @@ -82,6 +86,11 @@ static int lcd_cmd_one_param(struct lcd *lcd_data, u8 cmd, u8 param) return 0; } +static int lcd_cmd_backlight_brightness(struct lcd *lcd_data, u8 brightness) +{ + return lcd_cmd_one_param(lcd_data, LCD_BRIGHTNESS, brightness); +} + static int lcd_cmd_display_on(struct lcd *lcd_data) { return lcd_cmd_no_params(lcd_data, LCD_DISPLAY_ON); @@ -376,7 +385,7 @@ static int __devinit lcd_probe(struct i2c_client *client, struct device_node *np = client->dev.of_node; struct lcd *lcd_data; struct tty_driver *lcd_tty_driver; - unsigned int width = 0, height = 0, i; + unsigned int width = 0, height = 0, i, brightness = 0; char *buffer; int ret = -ENOMEM; @@ -389,6 +398,14 @@ static int __devinit lcd_probe(struct i2c_client *client, goto err_devtree; } + of_property_read_u32(np, "brightness", &brightness); + if ((brightness < LCD_BRIGHTNESS_MIN) || + (brightness > LCD_BRIGHTNESS_MAX)) { + dev_info(&client->dev, + "lcd brighness not set or out of range, defaulting to maximum\n"); + brightness = LCD_BRIGHTNESS_MAX; + } + for (i = 0 ; i < MAX_LCDS ; i++) if (lcd_data_static[i].client == NULL) break; @@ -435,6 +452,7 @@ static int __devinit lcd_probe(struct i2c_client *client, lcd_clear_buffer(lcd_data); lcd_cmd_display_on(lcd_data); lcd_cmd_clear_screen(lcd_data); + lcd_cmd_backlight_brightness(lcd_data, brightness); dev_info(&client->dev, "LCD driver initialized\n"); From 305603b52ef2ab53df1ec5d5ec391d4bdf0fc191 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Tue, 26 Feb 2013 08:59:52 -0600 Subject: [PATCH 116/201] FogBugz #102358: i2c newhaven lcd driver uses faulty hyphen The text for the manufacturor's part number 3 byte unicode for hyphen instead of simple ascii. This happened because I copied the part number from a PDF spec sheet and pasted it. Signed-off-by: Alan Tull --- drivers/tty/newhaven_lcd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index 14213d05f6bba..b7dbc4f205e7a 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -1,6 +1,6 @@ /* * TTY on a LCD connected to I2C - * Supports Newhaven NHD‐0216K3Z‐NSW‐BBW + * Supports Newhaven NHD-0216K3Z-NSW-BBW * * Copyright (C) 2013 Altera Corporation. All rights reserved. * @@ -480,7 +480,7 @@ static int __exit lcd_remove(struct i2c_client *client) } static const struct of_device_id lcd_of_match[] = { - { .compatible = "newhaven,nhd‐0216k3z‐nsw‐bbw", }, + { .compatible = "newhaven,nhd-0216k3z-nsw-bbw", }, }; static const struct i2c_device_id lcd_id[] = { From b3eda1da6491a4f1b19fbb8ec93def002686230b Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 17 Jun 2014 14:24:34 -0500 Subject: [PATCH 117/201] tty: newhaven_lcd: Remove devinit and devexit Signed-off-by: Dinh Nguyen --- drivers/tty/newhaven_lcd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index b7dbc4f205e7a..ebc542747450d 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -379,7 +379,7 @@ static const struct tty_operations lcd_ops = { .write_room = lcd_write_room, }; -static int __devinit lcd_probe(struct i2c_client *client, +static int lcd_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { struct device_node *np = client->dev.of_node; @@ -496,7 +496,7 @@ static struct i2c_driver lcd_i2c_driver = { .of_match_table = lcd_of_match, }, .probe = lcd_probe, - .remove = __devexit_p(lcd_remove), + .remove = lcd_remove, .id_table = lcd_id, }; From 9594d22fb2a8a5c0954c58a558fd1820a63fa24a Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Thu, 21 Mar 2013 09:51:20 -0500 Subject: [PATCH 118/201] FogBugz #107683: handle backspace better in lcd driver The LCD driver was relying the TTY layer to seperate ascii control commands from printable ascii, which it mostly does. But not completely. So loop on the whole write buffer, parsing each character. Signed-off-by: Alan Tull V2: * increment buf_i in the case of an unsupported escape sequence. --- drivers/tty/newhaven_lcd.c | 123 +++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index ebc542747450d..a75e69fdb389b 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -243,6 +243,7 @@ static int lcd_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct lcd *lcd_data = tty->driver_data; + int buf_i = 0, left; #ifdef DEBUG char *dbgbuf = kzalloc(count + 1, GFP_KERNEL); @@ -258,63 +259,77 @@ static int lcd_write(struct tty_struct *tty, const unsigned char *buf, return 0; } - /* process displayable chars */ - if ((0x20 <= buf[0]) && (buf[0] <= 0x7f)) { - int i = 0; - while (i < count) { - lcd_add_char_at_cursor(lcd_data, buf[i]); - i++; - } - - /* flush the line out to the display when we get to eol */ - lcd_reprint_one_line(lcd_data, lcd_data->cursor_line); - - /* - * ECMA-48 CSI sequences (from console_codes man page) - * - * ESC [ 2 J : erase whole display. - * ESC [ 2 K : erase whole line. - */ - } else if (buf[0] == ASCII_ESC) { - if ((count >= 4) && - (buf[1] == '[') && - (buf[2] == '2') && - (buf[3] == 'J')) { - pr_debug("ESC [2J = clear screan\n"); - lcd_clear_buffer(lcd_data); - lcd_cmd_clear_screen(lcd_data); - - } else if ((count >= 4) && - (buf[1] == '[') && - (buf[2] == '2') && - (buf[3] == 'K')) { - pr_debug("ESC [2K = clear line\n"); - lcd_clear_buffer_line(lcd_data, lcd_data->cursor_line); - lcd_reprint_one_line(lcd_data, lcd_data->cursor_line); - lcd_cmd_set_cursor(lcd_data, lcd_data->cursor_line, 0); - lcd_data->cursor_col = 0; - - } else - pr_debug("Unsupported escape sequence\n"); + while (buf_i < count) { + left = count - buf_i; - } else if ((count == 2) && - (buf[0] == ASCII_CR) && (buf[1] == ASCII_LF)) { - pr_debug("ASCII_CR/LF\n"); - lcd_crlf(lcd_data); + /* process displayable chars */ + if ((0x20 <= buf[buf_i]) && (buf[buf_i] <= 0x7f)) { + while ((buf_i < count) && + ((0x20 <= buf[buf_i]) && (buf[buf_i] <= 0x7f))) { + lcd_add_char_at_cursor(lcd_data, buf[buf_i]); + buf_i++; + } - } else if ((count == 1) && (buf[0] == ASCII_CR)) { - pr_debug("ASCII_CR\n"); - lcd_crlf(lcd_data); - - } else if ((count == 1) && (buf[0] == ASCII_LF)) { - pr_debug("ASCII_LF\n"); - lcd_crlf(lcd_data); + /* flush the line out to the display when we get to eol */ + lcd_reprint_one_line(lcd_data, lcd_data->cursor_line); - } else if ((count == 1) && (buf[0] == ASCII_BS)) { - pr_debug("ASCII_BS\n"); - lcd_backspace(lcd_data); - } else { - pr_debug("%s - Unsupported command 0x%02x\n", __func__, buf[0]); + /* + * ECMA-48 CSI sequences (from console_codes man page) + * + * ESC [ 2 J : erase whole display. + * ESC [ 2 K : erase whole line. + */ + } else if (buf[buf_i] == ASCII_ESC) { + if ((left >= 4) && + (buf[buf_i + 1] == '[') && + (buf[buf_i + 2] == '2') && + (buf[buf_i + 3] == 'J')) { + pr_debug("ESC [2J = clear screan\n"); + lcd_clear_buffer(lcd_data); + lcd_cmd_clear_screen(lcd_data); + buf_i += 4; + + } else if ((left >= 4) && + (buf[buf_i + 1] == '[') && + (buf[buf_i + 2] == '2') && + (buf[buf_i + 3] == 'K')) { + pr_debug("ESC [2K = clear line\n"); + lcd_clear_buffer_line(lcd_data, lcd_data->cursor_line); + lcd_reprint_one_line(lcd_data, lcd_data->cursor_line); + lcd_cmd_set_cursor(lcd_data, lcd_data->cursor_line, 0); + lcd_data->cursor_col = 0; + buf_i += 4; + + } else { + pr_debug("Unsupported escape sequence\n"); + buf_i++; + } + + } else if ((left >= 2) && + (buf[buf_i] == ASCII_CR) && (buf[buf_i + 1] == ASCII_LF)) { + pr_debug("ASCII_CR/LF\n"); + lcd_crlf(lcd_data); + buf_i += 2; + + } else if ((left >= 1) && (buf[buf_i] == ASCII_CR)) { + pr_debug("ASCII_CR\n"); + lcd_crlf(lcd_data); + buf_i++; + + } else if ((left >= 1) && (buf[buf_i] == ASCII_LF)) { + pr_debug("ASCII_LF\n"); + lcd_crlf(lcd_data); + buf_i++; + + } else if ((left >= 1) && (buf[buf_i] == ASCII_BS)) { + pr_debug("ASCII_BS\n"); + lcd_backspace(lcd_data); + buf_i++; + + } else { + pr_debug("%s - Unsupported command 0x%02x\n", __func__, buf[buf_i]); + buf_i++; + } } #ifdef DEBUG From 8fd377029ef5cb425074b6da189bd9f0c6a0a0a7 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Wed, 24 Apr 2013 09:21:47 -0500 Subject: [PATCH 119/201] FogBugz #114479: load custom character for backslash The NewHaven display displays a Japanese character instead of a backslash character for ascii 0x5c. Work around this by loading a custom backslash character into the display module's cg ram. Move the command to clear the lcd screen to the last lcd command in the initialization to clean up. Signed-off-by: Alan Tull V2: Added #defines for some constants --- drivers/tty/newhaven_lcd.c | 54 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index a75e69fdb389b..27a4de39afe2a 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -34,6 +34,9 @@ #define LCD_BACKSPACE 0x4e #define LCD_CLEAR_SCREEN 0x51 #define LCD_BRIGHTNESS 0x53 +#define LCD_CUSTOM_CHAR 0x54 +#define LCD_BYTES_PER_FONT 8 +#define LCD_BYTES_PER_FONT_CMD (LCD_BYTES_PER_FONT + 3) #define LCD_BRIGHTNESS_MIN 1 #define LCD_BRIGHTNESS_MAX 8 @@ -43,6 +46,20 @@ #define ASCII_CR 0x0d #define ASCII_ESC 0x1b #define ASCII_SPACE 0x20 +#define ASCII_BACKSLASH 0x5c + +/* The NewHaven display has 8 custom characters that are user-loadable init + its cg ram. */ +#define CUSTOM_BACKSLASH 0x00 + +struct custom_font { + u8 mapping; + u8 font[LCD_BYTES_PER_FONT]; +}; + +struct custom_font custom_fonts[] = { + { CUSTOM_BACKSLASH, { 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00, }, }, +}; struct lcd { struct device *dev; @@ -111,6 +128,36 @@ static int lcd_cmd_backspace(struct lcd *lcd_data) return lcd_cmd_no_params(lcd_data, LCD_BACKSPACE); } +/* Note that this has to happen early on or the LCD module will not + process the command */ +static int lcd_load_custom_fonts(struct lcd *lcd_data) +{ + u8 buf[LCD_BYTES_PER_FONT_CMD]; + int count, i; + + for (i = 0; i < sizeof(custom_fonts) / sizeof(struct custom_font) ; i++) { + buf[0] = LCD_COMMAND; + buf[1] = LCD_CUSTOM_CHAR; + buf[2] = custom_fonts[i].mapping; + memcpy(buf + 3, &custom_fonts[i].font, LCD_BYTES_PER_FONT); + + count = i2c_master_send(lcd_data->client, buf, sizeof(buf)); + if (count != sizeof(buf)) { + pr_err("%s: i2c_master_send returns %d\n", __func__, count); + return -1; + } + } + return 0; +} + +static char lcd_translate_printable_char(char val) +{ + if (val == ASCII_BACKSLASH) + return CUSTOM_BACKSLASH; + + return val; +} + /* From NHD-0216K3Z-NSW-BBY Display Module datasheet. */ #define LCD_CURSOR_LINE_MULTIPLIER 0x40 @@ -244,6 +291,7 @@ static int lcd_write(struct tty_struct *tty, const unsigned char *buf, { struct lcd *lcd_data = tty->driver_data; int buf_i = 0, left; + char val; #ifdef DEBUG char *dbgbuf = kzalloc(count + 1, GFP_KERNEL); @@ -266,7 +314,8 @@ static int lcd_write(struct tty_struct *tty, const unsigned char *buf, if ((0x20 <= buf[buf_i]) && (buf[buf_i] <= 0x7f)) { while ((buf_i < count) && ((0x20 <= buf[buf_i]) && (buf[buf_i] <= 0x7f))) { - lcd_add_char_at_cursor(lcd_data, buf[buf_i]); + val = lcd_translate_printable_char(buf[buf_i]); + lcd_add_char_at_cursor(lcd_data, val); buf_i++; } @@ -465,9 +514,10 @@ static int lcd_probe(struct i2c_client *client, lcd_data->lcd_tty_driver = lcd_tty_driver; lcd_clear_buffer(lcd_data); + lcd_load_custom_fonts(lcd_data); lcd_cmd_display_on(lcd_data); - lcd_cmd_clear_screen(lcd_data); lcd_cmd_backlight_brightness(lcd_data, brightness); + lcd_cmd_clear_screen(lcd_data); dev_info(&client->dev, "LCD driver initialized\n"); From 9e3524b6bb2c925969fff4296781347ff5940252 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Thu, 25 Apr 2013 09:43:57 -0500 Subject: [PATCH 120/201] FogBugz #118160: support tilde character on newhaven lcd module The NewHaven LCD module displays a right arrow instead of tilde for an ascii 0x7e. Load a custom font into cg ram to support tilde. Signed-off-by: Alan Tull --- drivers/tty/newhaven_lcd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index 27a4de39afe2a..3109ba6db03e9 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -47,10 +47,12 @@ #define ASCII_ESC 0x1b #define ASCII_SPACE 0x20 #define ASCII_BACKSLASH 0x5c +#define ASCII_TILDE 0x7e /* The NewHaven display has 8 custom characters that are user-loadable init its cg ram. */ #define CUSTOM_BACKSLASH 0x00 +#define CUSTOM_TILDE 0x01 struct custom_font { u8 mapping; @@ -59,6 +61,7 @@ struct custom_font { struct custom_font custom_fonts[] = { { CUSTOM_BACKSLASH, { 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00, }, }, + { CUSTOM_TILDE, { 0x00, 0x00, 0x00, 0x08, 0x15, 0x02, 0x00, 0x00, }, }, }; struct lcd { @@ -154,6 +157,8 @@ static char lcd_translate_printable_char(char val) { if (val == ASCII_BACKSLASH) return CUSTOM_BACKSLASH; + else if (val == ASCII_TILDE) + return CUSTOM_TILDE; return val; } From 22c0cfba04f91501ec31686efb09ceb70bb4a3f4 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Thu, 23 May 2013 09:41:16 -0500 Subject: [PATCH 121/201] FogBugz #125882: lcd module needs time to process commands. It appears that the LCD module's controller needs a little time to process commands before more text is sent to the module. Otherwise a few characters of text gets dropped after some commands such as the command to clear the display. printf '\nhello you crazy\nlinux people' > /dev/ttyLCD0 Should display correctly with this workaround. Signed-off-by: Alan Tull --- drivers/tty/newhaven_lcd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index 3109ba6db03e9..1a4df25dd5b19 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -26,6 +26,7 @@ #include #include #include +#include #define LCD_COMMAND 0xfe #define LCD_DISPLAY_ON 0x41 @@ -90,6 +91,7 @@ static int lcd_cmd_no_params(struct lcd *lcd_data, u8 cmd) pr_err("%s: i2c_master_send returns %d\n", __func__, count); return -1; } + msleep(1); return 0; } @@ -103,6 +105,7 @@ static int lcd_cmd_one_param(struct lcd *lcd_data, u8 cmd, u8 param) pr_err("%s: i2c_master_send returns %d\n", __func__, count); return -1; } + msleep(1); return 0; } From 7497f3370adffc67e85c1ce23c79b59461ba778c Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Fri, 24 May 2013 13:48:47 -0500 Subject: [PATCH 122/201] FogBugz #101176: add sys entry to set lcd module brightness Add an entry under /sys to set/get lcd module brightness dynamically. Signed-off-by: Alan Tull --- drivers/tty/newhaven_lcd.c | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index 1a4df25dd5b19..bc62546b49615 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -72,6 +72,7 @@ struct lcd { struct tty_port port; unsigned int width; unsigned int height; + unsigned int brightness; char *buffer; unsigned int top_line; unsigned int cursor_line; @@ -395,6 +396,47 @@ static int lcd_write(struct tty_struct *tty, const unsigned char *buf, return count; } +static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lcd *lcd_data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", lcd_data->brightness); +} + +static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lcd *lcd_data = dev_get_drvdata(dev); + int ret, brightness; + + ret = sscanf(buf, "%d", &brightness); + if (ret != 1) + return -EINVAL; + + if ((brightness < LCD_BRIGHTNESS_MIN) || + (brightness > LCD_BRIGHTNESS_MAX)) { + dev_err(lcd_data->dev, "out of range (%d to %d)\n", + LCD_BRIGHTNESS_MIN, LCD_BRIGHTNESS_MAX); + return -EINVAL; + } + + lcd_data->brightness = brightness; + lcd_cmd_backlight_brightness(lcd_data, brightness); + + return count; +} +static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR, brightness_show, brightness_store); + +static struct attribute *lcd_attrs[] = { + &dev_attr_brightness.attr, + NULL, +}; + +static struct attribute_group lcd_attr_group = { + .attrs = lcd_attrs, +}; + static int lcd_install(struct tty_driver *driver, struct tty_struct *tty) { struct lcd *lcd_data; @@ -501,6 +543,7 @@ static int lcd_probe(struct i2c_client *client, lcd_data->height = height; lcd_data->width = width; lcd_data->buffer = buffer; + lcd_data->brightness = brightness; dev_set_drvdata(&client->dev, lcd_data); tty_port_init(&lcd_data->port); @@ -527,6 +570,12 @@ static int lcd_probe(struct i2c_client *client, lcd_cmd_backlight_brightness(lcd_data, brightness); lcd_cmd_clear_screen(lcd_data); + ret = sysfs_create_group(&lcd_data->dev->kobj, &lcd_attr_group); + if (ret) { + dev_err(lcd_data->dev, "Can't create sysfs attrs for lcd\n"); + return ret; + } + dev_info(&client->dev, "LCD driver initialized\n"); return 0; @@ -545,6 +594,7 @@ static int __exit lcd_remove(struct i2c_client *client) lcd_cmd_display_off(lcd_data); + sysfs_remove_group(&lcd_data->dev->kobj, &lcd_attr_group); tty_unregister_driver(lcd_data->lcd_tty_driver); put_tty_driver(lcd_data->lcd_tty_driver); kfree(lcd_data->buffer); From 30dcbd46ff03b5892ec3f38064c4441c01c84690 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Thu, 31 Jan 2013 15:47:06 -0600 Subject: [PATCH 123/201] FogBugz #98100: designware i2c driver add speed mode in devtree For Designware I2C adapter driver, add "speed-mode" property to devicetree. Signed-off-by: Alan Tull --- .../devicetree/bindings/i2c/i2c-designware.txt | 3 +++ drivers/i2c/busses/i2c-designware-platdrv.c | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-designware.txt b/Documentation/devicetree/bindings/i2c/i2c-designware.txt index 5199b0c8cf7a4..dd8431f9988f2 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-designware.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-designware.txt @@ -9,6 +9,8 @@ Required properties : Recommended properties : - clock-frequency : desired I2C bus clock frequency in Hz. + - speed-mode : 0 = standard (0 - 100Kb/s) + : 1 = fast (<= 400Kb/s) <== default Optional properties : - i2c-sda-hold-time-ns : should contain the SDA hold time in nanoseconds. @@ -29,6 +31,7 @@ Example : reg = <0xf0000 0x1000>; interrupts = <11>; clock-frequency = <400000>; + speed-mode = <1>; }; i2c@1120000 { diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 402ec3970feda..17452efefdbe7 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -119,9 +119,11 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev) static int dw_i2c_probe(struct platform_device *pdev) { struct dw_i2c_dev *dev; + struct device_node *np = pdev->dev.of_node; struct i2c_adapter *adap; struct resource *mem; int irq, r; + int speed, speed_prop, ret; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -175,8 +177,16 @@ static int dw_i2c_probe(struct platform_device *pdev) I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; + + /* Get speed from device tree. Default to fast speed. */ + speed = DW_IC_CON_SPEED_FAST; + if (np) { + ret = of_property_read_u32(np, "speed-mode", &speed_prop); + if (!ret && (speed_prop == 0)) + speed = DW_IC_CON_SPEED_STD; + } dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | - DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; + DW_IC_CON_RESTART_EN | speed; /* Try first if we can configure the device from ACPI */ r = dw_i2c_acpi_configure(pdev); From 1da3da9f4160e8f9dad114f147d4041a9d6efb74 Mon Sep 17 00:00:00 2001 From: Chris Rauer Date: Fri, 10 May 2013 09:15:45 -0700 Subject: [PATCH 124/201] FogBugz #177284: add Altera VIP framebuffer driver This adds support for the Altera framebuffer that uses the Altera VIP Frame Reader hardware. V2: -Removed old SGDMA code -Fixed more styling and formatting issues -Changed module author -Fixed license header -Added device tree bindings documentation -Fixed bug with altfb_remove -Fixed bug in altfb_probe with error path V3: -updated dev tree bindings doc -replaced of_get_property with of_property_read_u32 -fixed module license -replaced kzalloc with devm_kzalloc -replaced ioremap/request_mem_region with devm_ioremap_resource -added disable hardware function during unprobe -removed more remnants of SGDMA -renamed altfb to altvipfb V4: -changed "Altera framebuffer" to "Altera VIP framebuffer" V5: -removed static data structures -cleaned up error path -removed 16bit pixel code V6: -expanded "VIP" to "Video and Image Processing" in some descriptions -removed dependency on EMBEDDED in Kconfig V7: -changed altvipfb_start_hw and altvipfb_disable_hw to return void V8: -fixed comment in altvipfb_disable_hw V9: -added "altr,vip-frame-reader-1.0" compatible string Signed-off-by: Chris Rauer --- .../devicetree/bindings/video/altvipfb.txt | 22 ++ drivers/video/Kconfig | 10 + drivers/video/Makefile | 1 + drivers/video/altvipfb.c | 303 ++++++++++++++++++ 4 files changed, 336 insertions(+) create mode 100644 Documentation/devicetree/bindings/video/altvipfb.txt create mode 100644 drivers/video/altvipfb.c diff --git a/Documentation/devicetree/bindings/video/altvipfb.txt b/Documentation/devicetree/bindings/video/altvipfb.txt new file mode 100644 index 0000000000000..5e376184ba33e --- /dev/null +++ b/Documentation/devicetree/bindings/video/altvipfb.txt @@ -0,0 +1,22 @@ +Altera Video and Image Processing(VIP) Frame Reader bindings + +Required properties: +- compatible: "altr,vip-frame-reader-9.1" or "altr,vip-frame-reader-1.0" +- reg: Physical base address and length of the framebuffer controller's + registers. +- max-width: The width of the framebuffer in pixels. +- max-height: The height of the framebuffer in pixels. +- bits-per-color: only "8" is currently supported +- mem-word-width = the bus width of the avalon master port on the frame reader + +Example: + +alt_vip_vfr_0: vip@0xff260000 { + compatible = "altr,vip-frame-reader-1.0"; + reg = <0xff260000 0x00000080>; + max-width = <1024>; + max-height = <768>; + bits-per-color = <8>; + mem-word-width = <128>; +}; + diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8bf495ffb0207..83ef77310dbcd 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -42,6 +42,16 @@ config VIDEOMODE_HELPERS config HDMI bool +config FB_ALTERA_VIP + tristate "Altera VIP Frame Reader framebuffer support" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This driver supports the Altera Video and Image Processing(VIP) + Frame Reader + if VT source "drivers/video/console/Kconfig" endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9ad3c17d64568..b46be2e76a0d4 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ obj-y += fbdev/ +obj-$(CONFIG_FB_ALTERA_VIP) += altvipfb.o obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o ifeq ($(CONFIG_OF),y) diff --git a/drivers/video/altvipfb.c b/drivers/video/altvipfb.c new file mode 100644 index 0000000000000..b247858ba43a7 --- /dev/null +++ b/drivers/video/altvipfb.c @@ -0,0 +1,303 @@ +/* + * altvipfb.c -- Altera Video and Image Processing(VIP) Frame Reader driver + * + * This is based on a driver made by Thomas Chou and + * Walter Goossens This driver supports the Altera VIP + * Frame Reader component. More info on the hardware can be found in + * the Altera Video and Image Processing Suite User Guide at this address + * http://www.altera.com/literature/ug/ug_vip.pdf. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#define PALETTE_SIZE 256 +#define DRIVER_NAME "altvipfb" + +/* control registers */ +#define ALTVIPFB_CONTROL 0 +#define ALTVIPFB_FRAME_SELECT 12 +#define ALTVIPFB_FRAME0_BASE_ADDRESS 16 +#define ALTVIPFB_FRAME0_NUM_WORDS 20 +#define ALTVIPFB_FRAME0_SAMPLES 24 +#define ALTVIPFB_FRAME0_WIDTH 32 +#define ALTVIPFB_FRAME0_HEIGHT 36 +#define ALTVIPFB_FRAME0_INTERLACED 40 + +struct altvipfb_type; + +struct altvipfb_dev { + struct platform_device *pdev; + struct fb_info info; + struct resource *reg_res; + void __iomem *base; + int mem_word_width; + u32 pseudo_palette[PALETTE_SIZE]; +}; + +static int altvipfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + /* + * Set a single color register. The values supplied have a 32 bit + * magnitude. + * Return != 0 for invalid regno. + */ + + if (regno > 255) + return 1; + + red >>= 8; + green >>= 8; + blue >>= 8; + + if (regno < 255) { + ((u32 *)info->pseudo_palette)[regno] = + ((red & 255) << 16) | ((green & 255) << 8) | (blue & 255); + } + + return 0; +} + +static struct fb_ops altvipfb_ops = { + .owner = THIS_MODULE, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_setcolreg = altvipfb_setcolreg, +}; + +static int altvipfb_of_setup(struct altvipfb_dev *fbdev) +{ + struct device_node *np = fbdev->pdev->dev.of_node; + int ret; + u32 bits_per_color; + + ret = of_property_read_u32(np, "max-width", &fbdev->info.var.xres); + if (ret) { + dev_err(&fbdev->pdev->dev, + "Missing required parameter 'max-width'"); + return ret; + } + fbdev->info.var.xres_virtual = fbdev->info.var.xres, + + ret = of_property_read_u32(np, "max-height", &fbdev->info.var.yres); + if (ret) { + dev_err(&fbdev->pdev->dev, + "Missing required parameter 'max-height'"); + return ret; + } + fbdev->info.var.yres_virtual = fbdev->info.var.yres; + + ret = of_property_read_u32(np, "bits-per-color", &bits_per_color); + if (ret) { + dev_err(&fbdev->pdev->dev, + "Missing required parameter 'bits-per-color'"); + return ret; + } + if (bits_per_color != 8) { + dev_err(&fbdev->pdev->dev, + "bits-per-color is set to %i. Curently only 8 is supported.", + bits_per_color); + return -ENODEV; + } + fbdev->info.var.bits_per_pixel = 32; + + ret = of_property_read_u32(np, "mem-word-width", + &fbdev->mem_word_width); + if (ret) { + dev_err(&fbdev->pdev->dev, + "Missing required parameter 'mem-word-width'"); + return ret; + } + if (!(fbdev->mem_word_width >= 32 && fbdev->mem_word_width % 32 == 0)) { + dev_err(&fbdev->pdev->dev, + "mem-word-width is set to %i. must be >= 32 and multiple of 32.", + fbdev->mem_word_width); + return -ENODEV; + } + + return 0; +} + +static void altvipfb_start_hw(struct altvipfb_dev *fbdev) +{ + writel(fbdev->info.fix.smem_start, fbdev->base + + ALTVIPFB_FRAME0_BASE_ADDRESS); + writel(fbdev->info.var.xres * fbdev->info.var.yres / + (fbdev->mem_word_width/32), + fbdev->base + ALTVIPFB_FRAME0_NUM_WORDS); + writel(fbdev->info.var.xres * fbdev->info.var.yres, + fbdev->base + ALTVIPFB_FRAME0_SAMPLES); + writel(fbdev->info.var.xres, fbdev->base + ALTVIPFB_FRAME0_WIDTH); + writel(fbdev->info.var.yres, fbdev->base + ALTVIPFB_FRAME0_HEIGHT); + writel(3, fbdev->base + ALTVIPFB_FRAME0_INTERLACED); + writel(0, fbdev->base + ALTVIPFB_FRAME_SELECT); + + /* Finally set the control register to 1 to start streaming */ + writel(1, fbdev->base + ALTVIPFB_CONTROL); +} + +static void altvipfb_disable_hw(struct altvipfb_dev *fbdev) +{ + /* set the control register to 0 to stop streaming */ + writel(0, fbdev->base + ALTVIPFB_CONTROL); +} + + +static int altvipfb_setup_fb_info(struct altvipfb_dev *fbdev) +{ + struct fb_info *info = &fbdev->info; + int ret; + + strcpy(info->fix.id, DRIVER_NAME); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.accel = FB_ACCEL_NONE; + + info->fbops = &altvipfb_ops; + info->var.activate = FB_ACTIVATE_NOW; + info->var.height = -1; + info->var.width = -1; + info->var.vmode = FB_VMODE_NONINTERLACED; + + ret = altvipfb_of_setup(fbdev); + if (ret) + return ret; + + /* settings for 32bit pixels */ + info->var.red.offset = 16; + info->var.red.length = 8; + info->var.red.msb_right = 0; + info->var.green.offset = 8; + info->var.green.length = 8; + info->var.green.msb_right = 0; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->var.blue.msb_right = 0; + + info->fix.line_length = (info->var.xres * + (info->var.bits_per_pixel >> 3)); + info->fix.smem_len = info->fix.line_length * info->var.yres; + + info->pseudo_palette = fbdev->pseudo_palette; + info->flags = FBINFO_FLAG_DEFAULT; + + return 0; +} + +static int altvipfb_probe(struct platform_device *pdev) +{ + int retval; + void *fbmem_virt; + struct altvipfb_dev *fbdev; + + fbdev = devm_kzalloc(&pdev->dev, sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) + return -ENOMEM; + + fbdev->pdev = pdev; + fbdev->reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!fbdev->reg_res) + return -ENODEV; + + retval = altvipfb_setup_fb_info(fbdev); + + fbmem_virt = dma_alloc_coherent(NULL, + fbdev->info.fix.smem_len, + (void *)&(fbdev->info.fix.smem_start), + GFP_KERNEL); + if (!fbmem_virt) { + dev_err(&pdev->dev, + "altvipfb: unable to allocate %d Bytes fb memory\n", + fbdev->info.fix.smem_len); + return retval; + } + + fbdev->info.screen_base = fbmem_virt; + + retval = fb_alloc_cmap(&fbdev->info.cmap, PALETTE_SIZE, 0); + if (retval < 0) + goto err_dma_free; + + platform_set_drvdata(pdev, fbdev); + + fbdev->base = devm_ioremap_resource(&pdev->dev, fbdev->reg_res); + if (IS_ERR(fbdev->base)) { + dev_err(&pdev->dev, "devm_ioremap_resource failed\n"); + retval = PTR_ERR(fbdev->base); + goto err_dealloc_cmap; + } + + altvipfb_start_hw(fbdev); + + retval = register_framebuffer(&fbdev->info); + if (retval < 0) + goto err_dealloc_cmap; + + dev_info(&pdev->dev, "fb%d: %s frame buffer device at 0x%x+0x%x\n", + fbdev->info.node, fbdev->info.fix.id, + (unsigned)fbdev->info.fix.smem_start, + fbdev->info.fix.smem_len); + + return 0; + +err_dealloc_cmap: + fb_dealloc_cmap(&fbdev->info.cmap); +err_dma_free: + dma_free_coherent(NULL, fbdev->info.fix.smem_len, fbmem_virt, + fbdev->info.fix.smem_start); + return retval; +} + +static int altvipfb_remove(struct platform_device *dev) +{ + struct altvipfb_dev *fbdev = platform_get_drvdata(dev); + + if (fbdev) { + unregister_framebuffer(&fbdev->info); + fb_dealloc_cmap(&fbdev->info.cmap); + dma_free_coherent(NULL, fbdev->info.fix.smem_len, + fbdev->info.screen_base, + fbdev->info.fix.smem_start); + altvipfb_disable_hw(fbdev); + } + return 0; +} + + +static struct of_device_id altvipfb_match[] = { + { .compatible = "altr,vip-frame-reader-1.0" }, + { .compatible = "altr,vip-frame-reader-9.1" }, + {}, +}; +MODULE_DEVICE_TABLE(of, altvipfb_match); + +static struct platform_driver altvipfb_driver = { + .probe = altvipfb_probe, + .remove = altvipfb_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = altvipfb_match, + }, +}; +module_platform_driver(altvipfb_driver); + +MODULE_DESCRIPTION("Altera VIP Frame Reader framebuffer driver"); +MODULE_AUTHOR("Chris Rauer "); +MODULE_LICENSE("GPL v2"); From b0be83d398bb667371db9f47b9c00265b8404a1a Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 16 Jun 2014 15:39:50 -0500 Subject: [PATCH 125/201] ARM: SOCFPGA: update socfpga_defconfig Update defconfig Signed-off-by: Dinh Nguyen --- arch/arm/configs/socfpga_defconfig | 79 ++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index bfd36ce903677..be9ad2816779c 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -41,7 +41,17 @@ CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y CONFIG_IP_PNP_RARP=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_CAN=y +CONFIG_CAN_RAW=y +CONFIG_CAN_BCM=y +CONFIG_CAN_GW=y +CONFIG_CAN_DEV=y +CONFIG_CAN_CALC_BITTIMING=y +CONFIG_CAN_C_CAN=y +CONFIG_CAN_C_CAN_PLATFORM=y +CONFIG_CAN_DEBUG_DEVICES=y CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y CONFIG_MTD=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y @@ -72,6 +82,19 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_NR_UARTS=2 CONFIG_SERIAL_8250_RUNTIME_UARTS=2 CONFIG_SERIAL_8250_DW=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_STORAGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_LIBCOMPOSITE=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_PHY=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_GENERIC_PHY=y +CONFIG_USB_DWC2=y +CONFIG_MMC=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_IDMAC=y CONFIG_SPI=y CONFIG_SPI_CADENCE_QSPI=y CONFIG_SPI_DESIGNWARE=y @@ -83,14 +106,19 @@ CONFIG_GPIO_SYSFS=y CONFIG_GPIO_DWAPB=y CONFIG_GPIO_ALTERA=m # CONFIG_RTC_HCTOSYS is not set +CONFIG_WATCHDOG=y +CONFIG_DW_WATCHDOG=y CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_POSIX_ACL=y CONFIG_EXT3_FS=y -CONFIG_NFS_FS=y -CONFIG_ROOT_NFS=y +CONFIG_EXT3_DEFAULTS_TO_ORDERED=y +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT4_FS=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set # CONFIG_DNOTIFY is not set -# CONFIG_INOTIFY_USER is not set +CONFIG_INOTIFY_USER=y CONFIG_VFAT_FS=y CONFIG_NTFS_FS=y CONFIG_NTFS_RW=y @@ -105,10 +133,51 @@ CONFIG_DEBUG_INFO=y CONFIG_ENABLE_DEFAULT_TRACERS=y CONFIG_DEBUG_USER=y CONFIG_XZ_DEC=y -CONFIG_MMC=y -CONFIG_MMC_DW=y +CONFIG_I2C=y +CONFIG_I2C_DESIGNWARE_CORE=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y +CONFIG_I2C_CHARDEV=y +CONFIG_NEWHAVEN_LCD=y +CONFIG_EEPROM_AT24=y +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V2=y +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NFS_USE_KERNEL_DNS=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_CPU=y CONFIG_FPGA=y CONFIG_FPGA_MGR_ALTERA=y +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_COMMON_CLK_DEBUG=y +CONFIG_FRAME_POINTER=y +CONFIG_GENERIC_TRACER=y +CONFIG_FUNCTION_TRACER=y +CONFIG_FUNCTION_GRAPH_TRACER=y +CONFIG_DYNAMIC_FTRACE=y +CONFIG_FTRACE_MCOUNT_RECORD=y +CONFIG_OLD_MCOUNT=y CONFIG_FPGA_BRIDGE=y CONFIG_ALTERA_SOCFPGA_BRIDGE=y +CONFIG_SIGNALFD=y +CONFIG_VLAN_8021Q=y +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_GARP=y +CONFIG_IPV6=y +CONFIG_CONFIGFS_FS=y CONFIG_SRAM=y +CONFIG_PPS=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_PTP_1588_CLOCK=y + From 82005b4b808f293da1134f21162624315f76f48a Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 14 May 2013 12:46:47 +0800 Subject: [PATCH 126/201] FogBugz #111740: [PATCH] Integrates Nios II kernel (FDT) Integrates Nios II kernel (arch/nios2) from linux-nios2.git to linux-socfpga.git. Added function to translate the reg to CPU view address in FDT mode (early init). Signed-off-by: Ley Foon Tan --- drivers/of/fdt.c | 187 +++++++++++++++++++++++++++++++++++++++++ include/linux/of_fdt.h | 12 +++ 2 files changed, 199 insertions(+) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 9aa012e6ea0a6..8b140124d8692 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -921,6 +921,193 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, /* break now */ return 1; } +/** + * flat_dt_translate_address - Translate an address using the ranges property + * + * This function converts address from "node address-space" to "parent address- + * space" + */ +static int __init flat_dt_translate_address(unsigned long node, + unsigned long parent, u64 *address) +{ + unsigned long size = 0; + __be32 *prop; + __be32 *ranges; + int size_cells = 0; + int addr_cells = 0; + int paddr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; + + ranges = of_get_flat_dt_prop(node, "ranges", &size); + + if (!ranges) { + pr_warn("Address cannot be translated\n"); + return -EINVAL; + } + + if (!size) { + pr_debug("No translation possible/necessary\n"); + return 0; + } + + prop = of_get_flat_dt_prop(node, "#size-cells", NULL); + if (!prop) + return -EINVAL; + size_cells = be32_to_cpup(prop); + + prop = of_get_flat_dt_prop(node, "#address-cells", NULL); + if (!prop) + return -EINVAL; + addr_cells = be32_to_cpup(prop); + + if (parent) { + prop = of_get_flat_dt_prop(parent, "#address-cells", NULL); + if (prop) + paddr_cells = be32_to_cpup(prop); + } + if ((addr_cells <= 0) || (size_cells <= 0) || + (addr_cells > 2) || (size_cells > 2) || (paddr_cells > 2)) { + pr_warn("Translation not possible in fdt. Invalid address.\n"); + *address = 0; + return -1; + } + + while (size > 0) { + u64 from, to, tsize; + from = be32_to_cpup(ranges++); + size -= 4; + if (addr_cells == 2) { + from += (((u64)be32_to_cpup(ranges++)) << 32); + size -= 4; + } + to = be32_to_cpup(ranges++); + size -= 4; + if (paddr_cells == 2) { + to += (((u64)be32_to_cpup(ranges++)) << 32); + size -= 4; + } + tsize = be32_to_cpup(ranges++); + size -= 4; + if (size_cells == 2) { + tsize += (((u64)be32_to_cpup(ranges++)) << 32); + size -= 4; + } + pr_debug(" From %llX To %llX Size %llX\n", from, to, tsize); + if ((*address >= from) && (*address < (from + tsize))) + *address += (to - from); + } + return 1; +} + +static int __init of_scan_flat_dt_ranges(unsigned long *pnode, + unsigned long parent, unsigned long target, + u64 *address, int ignore) +{ + int rc = 0; + int depth = -1; + char *pathp; + unsigned long p = *pnode; + do { + u32 tag = be32_to_cpup((__be32 *)p); + + p += 4; + if (tag == OF_DT_END_NODE) { + if (depth--) + break; + else + continue; + } + if (tag == OF_DT_NOP) + continue; + if (tag == OF_DT_END) + break; + if (tag == OF_DT_PROP) { + u32 sz = be32_to_cpup((__be32 *)p); + p += 8; + if (be32_to_cpu(initial_boot_params->version) < 0x10) + p = ALIGN(p, sz >= 8 ? 8 : 4); + p += sz; + p = ALIGN(p, 4); + continue; + } + if (tag != OF_DT_BEGIN_NODE) { + pr_err("Invalid tag %x in flat device tree!\n", tag); + return -EINVAL; + } + pathp = (char *)p; + p = ALIGN(p + strlen(pathp) + 1, 4); + if ((*pathp) == '/') { + char *lp, *np; + for (lp = NULL, np = pathp; *np; np++) + if ((*np) == '/') + lp = np+1; + if (lp != NULL) + pathp = lp; + } + if ((ignore == 0) && (p == target)) { + rc = 1; + ignore++; + pr_debug("Found target. Start address translation\n"); + } + if (depth) { + int res; + *pnode = p; + res = of_scan_flat_dt_ranges(pnode, p, target, + address, ignore); + if (res < 0) { + /* Something bad happened */ + return -1; + } else if (res > 0) { + /* Something gooed happened. Translate. */ + rc++; + pr_debug("translate %08lX %s\n", p, pathp); + if (flat_dt_translate_address(p, parent, + address) < 0) + return -1; + } + p = *pnode; + } else + depth++; + } while (1); + *pnode = p; + return rc; +} +/** + * of_get_flat_dt_address - get address from a node + * @node: targeted node + * + * Returns address or -1 if error. + */ +__be32 __init of_get_flat_dt_address(unsigned long node) +{ + u32 *addr; + + addr = of_get_flat_dt_prop(node, "reg", NULL); + if (!addr) + return -1; + + return be32_to_cpup(addr); +} + +/** + * of_get_flat_dt_translate_address - get translated address from a node + * @node: targeted node + * + * Returns translated address or OF_BAD_ADDR if error. + */ +u64 __init of_get_flat_dt_translate_address(unsigned long node) +{ + unsigned long p = of_get_flat_dt_root(); + u64 addr64; + int rc; + + addr64 = (u64) of_get_flat_dt_address(node); + if (addr64 == -1) + return OF_BAD_ADDR; + rc = of_scan_flat_dt_ranges(&p, 0, node, &addr64, 0); + if (rc > 0) + return addr64; + return OF_BAD_ADDR; +} #ifdef CONFIG_HAVE_MEMBLOCK void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 0ff360d5b3b30..18d1357c42236 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -68,6 +68,18 @@ extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); extern u64 dt_mem_next_cell(int s, const __be32 **cellp); +extern __be32 __init of_get_flat_dt_address(unsigned long node); +extern u64 __init of_get_flat_dt_translate_address(unsigned long node); + +/* + * If BLK_DEV_INITRD, the fdt early init code will call this function, + * to be provided by the arch code. start and end are specified as + * physical addresses. + */ +#ifdef CONFIG_BLK_DEV_INITRD +extern void early_init_dt_setup_initrd_arch(u64 start, u64 end); +#endif + /* Early flat tree scan hooks */ extern int early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data); From def2911363b71e802fe0313f3bac051747a02516 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 19 Jun 2014 16:29:43 +0800 Subject: [PATCH 127/201] nios2: Port to v3.15 - update defconfig to 3.15 - rename NO_IOPORT to NO_IOPORT_MAP https://lkml.org/lkml/2014/2/19/647 - add renameat2 syscall https://lkml.org/lkml/2014/4/11/234 - Use get_signal() signal_setup_done() https://lkml.org/lkml/2014/3/2/198 Signed-off-by: Ley Foon Tan --- arch/nios2/Kconfig | 2 +- arch/nios2/configs/3c120_defconfig | 2 - arch/nios2/include/uapi/asm/unistd.h | 3 +- arch/nios2/kernel/entry.S | 3 +- arch/nios2/kernel/signal.c | 103 +++++++++------------------ arch/nios2/kernel/syscalltable.S | 3 +- 6 files changed, 40 insertions(+), 76 deletions(-) diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index 74147fb958c94..6c906a1435159 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -30,7 +30,7 @@ config GENERIC_CALIBRATE_DELAY def_bool y -config NO_IOPORT +config NO_IOPORT_MAP def_bool y config HAS_DMA diff --git a/arch/nios2/configs/3c120_defconfig b/arch/nios2/configs/3c120_defconfig index 6aa13239e338f..a7b1bebdaa3a2 100644 --- a/arch/nios2/configs/3c120_defconfig +++ b/arch/nios2/configs/3c120_defconfig @@ -48,14 +48,12 @@ CONFIG_MTD_CFI=y CONFIG_MTD_CFI_INTELEXT=y CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_PHYSMAP_OF=y -CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_LOOP=y CONFIG_NETDEVICES=y CONFIG_ALTERA_TSE=y CONFIG_MARVELL_PHY=y # CONFIG_WLAN is not set # CONFIG_INPUT_MOUSE is not set -# CONFIG_SERIO_I8042 is not set # CONFIG_SERIO_SERPORT is not set # CONFIG_VT is not set CONFIG_SERIAL_ALTERA_JTAGUART=y diff --git a/arch/nios2/include/uapi/asm/unistd.h b/arch/nios2/include/uapi/asm/unistd.h index a0bf289c65ab0..2339c103fa043 100644 --- a/arch/nios2/include/uapi/asm/unistd.h +++ b/arch/nios2/include/uapi/asm/unistd.h @@ -368,7 +368,8 @@ #define __NR_finit_module 348 #define __NR_sched_setattr 349 #define __NR_sched_getattr 350 +#define __NR_renameat2 351 -#define NR_syscalls 351 +#define NR_syscalls 352 #endif /* _UAPI_ASM_NIOS2_UNISTD_H */ diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S index d3ebcda4fd0a3..249002c7869d2 100644 --- a/arch/nios2/kernel/entry.S +++ b/arch/nios2/kernel/entry.S @@ -298,8 +298,7 @@ Lsignal_return: beq r1, r0, restore_all mov r4, sp /* pt_regs */ SAVE_SWITCH_STACK - mov r5, r0 /* oldset = 0 */ - movi r6, 1 /* in_syscall = 1 */ + movi r5, 1 /* in_syscall = 1 */ call do_notify_resume RESTORE_SWITCH_STACK br restore_all diff --git a/arch/nios2/kernel/signal.c b/arch/nios2/kernel/signal.c index f87bb80fdb7b9..22879a167e3b1 100644 --- a/arch/nios2/kernel/signal.c +++ b/arch/nios2/kernel/signal.c @@ -24,7 +24,7 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall); +static int do_signal(struct pt_regs *regs, int in_syscall); /* * Do a signal return; undo the signal stack. @@ -252,7 +252,7 @@ static inline void push_cache(unsigned long vaddr) flush_icache_range(vaddr, vaddr + 12); } -static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, +static inline void *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, size_t frame_size) { unsigned long usp; @@ -261,22 +261,19 @@ static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, usp = regs->sp; /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa.sa_flags & SA_ONSTACK) && (current->sas_ss_sp != 0)) { - if (!on_sig_stack(usp)) - usp = current->sas_ss_sp + current->sas_ss_size; - } + usp = sigsp(usp, ksig); /* Verify, is it 32 or 64 bit aligned */ return (void *)((usp - frame_size) & -8UL); } -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs *regs) +static int setup_frame(struct ksignal *ksig, sigset_t *set, + struct pt_regs *regs) { struct sigframe *frame; int err = 0; - frame = get_sigframe(ka, regs, sizeof(*frame)); + frame = get_sigframe(ksig, regs, sizeof(*frame)); if (_NSIG_WORDS > 1) err |= copy_to_user(frame->extramask, &set->sig[1], @@ -294,33 +291,26 @@ static void setup_frame(int sig, struct k_sigaction *ka, err |= __put_user(0x003b683a, (long *)(frame->retcode + 4)); if (err) - goto give_sigsegv; + return -EFAULT; push_cache((unsigned long) &frame->retcode); /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; - regs->r4 = (unsigned long) (current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig); - regs->ea = (unsigned long) ka->sa.sa_handler; - return; - -give_sigsegv: - force_sigsegv(sig, current); + regs->r4 = ksig->sig; + regs->ea = (unsigned long) ksig->ka.sa.sa_handler; + return 0; } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) +static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, + struct pt_regs *regs) { struct rt_sigframe *frame; int err = 0; - frame = get_sigframe(ka, regs, sizeof(*frame)); + frame = get_sigframe(ksig, regs, sizeof(*frame)); - err |= copy_siginfo_to_user(&frame->info, info); + err |= copy_siginfo_to_user(&frame->info, &ksig->info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); @@ -339,24 +329,17 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= __put_user(0x003b683a, (long *)(frame->retcode + 4)); if (err) - goto give_sigsegv; + return -EFAULT; push_cache((unsigned long) &frame->retcode); /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; - regs->r4 = (unsigned long) (current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig); + regs->r4 = ksig->sig; regs->r5 = (unsigned long) &frame->info; regs->r6 = (unsigned long) &frame->uc; - regs->ea = (unsigned long) ka->sa.sa_handler; - return; - -give_sigsegv: - force_sigsegv(sig, current); + regs->ea = (unsigned long) ksig->ka.sa.sa_handler; + return 0; } static inline void handle_restart(struct pt_regs *regs, struct k_sigaction *ka, @@ -386,52 +369,36 @@ static inline void handle_restart(struct pt_regs *regs, struct k_sigaction *ka, /* * OK, we're invoking a handler */ -static void handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *oldset, struct pt_regs *regs) +static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) { + int ret; + sigset_t *oldset = sigmask_to_save(); + /* set up the stack frame */ - if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(ksig, oldset, regs); else - setup_frame(sig, ka, oldset, regs); - - if (!(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, - &ka->sa.sa_mask); - sigaddset(¤t->blocked, sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - } + ret = setup_frame(ksig, oldset, regs); + + signal_setup_done(ret, ksig, 0); } -/* - * Note that 'init' is a special process: it doesn't get signals it doesn't - * want to handle. Thus you cannot kill init even with a SIGKILL even by - * mistake. - */ -static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall) +static int do_signal(struct pt_regs *regs, int in_syscall) { - struct k_sigaction ka; - siginfo_t info; - int signr; + struct ksignal ksig; /* FIXME - Do we still need to do this ? */ current->thread.kregs = regs; - if (!oldset) - oldset = ¤t->blocked; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - if (signr > 0) { + if (get_signal(&ksig)) { /* * Are we from a system call? If so, check system call * restarting. */ if (in_syscall) - handle_restart(regs, &ka, 1); + handle_restart(regs, &ksig.ka, 1); /* Whee! Actually deliver the signal. */ - handle_signal(signr, &ka, &info, oldset, regs); + handle_signal(&ksig, regs); return 1; } @@ -457,10 +424,8 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset, int in_syscall) return 0; } -asmlinkage void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, - int in_syscall) +asmlinkage void do_notify_resume(struct pt_regs *regs, int in_syscall) { - pr_debug("--> ENTERING %s\n", __func__); /* * We want the common case to go fast, which is why we may in certain * cases get here from kernel mode. Just return without doing anything @@ -470,7 +435,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, return; if (test_thread_flag(TIF_SIGPENDING)) - do_signal(regs, oldset, in_syscall); + do_signal(regs, in_syscall); if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) tracehook_notify_resume(regs); diff --git a/arch/nios2/kernel/syscalltable.S b/arch/nios2/kernel/syscalltable.S index e64ecfa14d5b8..e9c4a37fe8694 100644 --- a/arch/nios2/kernel/syscalltable.S +++ b/arch/nios2/kernel/syscalltable.S @@ -372,7 +372,8 @@ ENTRY(sys_call_table) .long sys_finit_module .long sys_sched_setattr .long sys_sched_getattr /* 350 */ + .long sys_renameat2 - .rept NR_syscalls - 351 + .rept NR_syscalls - 352 .long sys_ni_syscall .endr From 2274704f23fc8cb852fc3694d1224a855eecf324 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Mon, 23 Jun 2014 14:47:35 -0500 Subject: [PATCH 128/201] mtd: denali: use 8 bytes for READID command The Denali NAND driver reads only 5 bytes of ID, but some Hynix and Samsung have size parameters in the 6th byte. As a result, the page and oob size for a Hynix H27UAG8T2B were calculated incorrectly and the driver failed to load. The solution is to read 8 bytes of ID, as expected by the nand framework. Signed-off-by: Graham Moore -- V2: Increase size of id_bytes array to 8. --- drivers/mtd/nand/denali.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index f6195371f189b..bcda82655370f 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -479,7 +479,7 @@ static void detect_partition_feature(struct denali_nand_info *denali) static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) { uint16_t status = PASS; - uint32_t id_bytes[5], addr; + uint32_t id_bytes[8], addr; uint8_t i, maf_id, device_id; dev_dbg(denali->dev, From 50b2b584bb3b8639387636826b4ffed78953d064 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Thu, 19 Jun 2014 09:03:26 -0500 Subject: [PATCH 129/201] This patch is the set of changes to run pl330 DMA on Altera socfpga. It replaces the changes in commit 084386335c4f04635bb25b4b3d26926121dfd5e0 Signed-off-by: Graham Moore --- arch/arm/boot/dts/socfpga.dtsi | 4 +++- drivers/dma/pl330.c | 28 +++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index e3868ea2dda61..fad343c7b3999 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -82,12 +82,14 @@ <0 108 4>, <0 109 4>, <0 110 4>, - <0 111 4>; + <0 111 4>, + <0 112 4>; #dma-cells = <1>; #dma-channels = <8>; #dma-requests = <32>; clocks = <&l4_main_clk>; clock-names = "apb_pclk"; + copy-align = <3>; }; }; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 3d8e498612568..83e1455b5297c 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -1264,10 +1264,11 @@ static inline int _ldst_devtomem(unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs, int cyc) { int off = 0; + enum pl330_cond cond = (pxs->r->cfg->brst_len == 1) ? SINGLE : BURST; while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri); - off += _emit_LDP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_WFP(dry_run, &buf[off], cond, pxs->r->peri); + off += _emit_LDP(dry_run, &buf[off], cond, pxs->r->peri); off += _emit_ST(dry_run, &buf[off], ALWAYS); off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); } @@ -1279,11 +1280,12 @@ static inline int _ldst_memtodev(unsigned dry_run, u8 buf[], const struct _xfer_spec *pxs, int cyc) { int off = 0; + enum pl330_cond cond = (pxs->r->cfg->brst_len == 1) ? SINGLE : BURST; while (cyc--) { - off += _emit_WFP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_WFP(dry_run, &buf[off], cond, pxs->r->peri); off += _emit_LD(dry_run, &buf[off], ALWAYS); - off += _emit_STP(dry_run, &buf[off], SINGLE, pxs->r->peri); + off += _emit_STP(dry_run, &buf[off], cond, pxs->r->peri); off += _emit_FLUSHP(dry_run, &buf[off], pxs->r->peri); } @@ -2342,7 +2344,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) pch->pl330_chid = pl330_request_channel(&pdmac->pif); if (!pch->pl330_chid) { spin_unlock_irqrestore(&pch->lock, flags); - return -EAGAIN; + return -ENOMEM; } tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch); @@ -2850,7 +2852,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } desc->rqcfg.brst_size = pch->burst_sz; - desc->rqcfg.brst_len = 1; + desc->rqcfg.brst_len = pch->burst_len; } /* Return the last desc in the chain */ @@ -2936,12 +2938,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) } } - if (pdat->init) { - ret = pdat->init(adev); - if (ret) - goto probe_err3; - } - pi->pcfg.periph_id = adev->periphid; ret = pl330_add(pi); if (ret) @@ -3013,6 +3009,13 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->device_issue_pending = pl330_issue_pending; pd->device_slave_caps = pl330_dma_device_slave_caps; + if (adev->dev.of_node) { + u32 val; + if (!of_property_read_u32(adev->dev.of_node, + "copy-align", &val)) + pd->copy_align = val; + } + ret = dma_async_device_register(pd); if (ret) { dev_err(&adev->dev, "unable to register DMAC\n"); @@ -3069,7 +3072,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) static int pl330_remove(struct amba_device *adev) { struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev); - struct dma_pl330_platdata *pdat = adev->dev.platform_data; struct dma_pl330_chan *pch, *_p; struct pl330_info *pi; From 126531cb4c38e1141ee81fc9be9e3745034b6f0d Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Wed, 25 Jun 2014 12:16:14 -0500 Subject: [PATCH 130/201] mtd: denali: Need to read the have-hw-ecc-fixup property. Signed-off-by: Graham Moore --- drivers/mtd/nand/denali_dt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c index 35cb17f578007..9683a220cfb3f 100644 --- a/drivers/mtd/nand/denali_dt.c +++ b/drivers/mtd/nand/denali_dt.c @@ -92,6 +92,9 @@ static int denali_dt_probe(struct platform_device *ofdev) } clk_prepare_enable(dt->clk); + denali->have_hw_ecc_fixup = of_property_read_bool(ofdev->dev.of_node, + "have-hw-ecc-fixup"); + ret = denali_init(denali); if (ret) goto out_disable_clk; From 7bb3038b87e901ad85439968153503ea124b6d05 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 2 Jul 2014 17:05:45 +0800 Subject: [PATCH 131/201] FogBugz #212185: nios2: define "PG_arch_1" as PG_dcache_clean Define PG_arch_1 as PG_dcache_clean and set it when a page is flushed. Follow commit c01778001a4f5ad9c62d882776235f3f31922fdd for ARM. Signed-off-by: Yuriy Kozlov Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/cacheflush.h | 6 ++++++ arch/nios2/mm/cacheflush.c | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/arch/nios2/include/asm/cacheflush.h b/arch/nios2/include/asm/cacheflush.h index ce2605a8c44de..c9d34d7bed27f 100644 --- a/arch/nios2/include/asm/cacheflush.h +++ b/arch/nios2/include/asm/cacheflush.h @@ -14,6 +14,12 @@ #include +/* + * This flag is used to indicate that the page pointed to by a pte is clean + * and does not require cleaning before returning it to the user. + */ +#define PG_dcache_clean PG_arch_1 + struct mm_struct; extern void flush_cache_all(void); diff --git a/arch/nios2/mm/cacheflush.c b/arch/nios2/mm/cacheflush.c index 0c4d0d416f8f6..0f24ed791e50f 100644 --- a/arch/nios2/mm/cacheflush.c +++ b/arch/nios2/mm/cacheflush.c @@ -183,11 +183,12 @@ void flush_dcache_page(struct page *page) /* Flush this page if there are aliases. */ if (mapping) { if (!mapping_mapped(mapping)) { - clear_bit(PG_arch_1, &page->flags); + clear_bit(PG_dcache_clean, &page->flags); } else if (mapping) { unsigned long start = (unsigned long)page_address(page); __flush_dcache(start, start + PAGE_SIZE); flush_aliases(mapping, page); + set_bit(PG_dcache_clean, &page->flags); } } } @@ -204,7 +205,8 @@ void update_mmu_cache(struct vm_area_struct *vma, page = pfn_to_page(pfn); - if (!PageReserved(page) && !test_bit(PG_arch_1, &page->flags)) { + if (!PageReserved(page) && + !test_and_set_bit(PG_dcache_clean, &page->flags)) { unsigned long start = page_to_virt(page); struct address_space *mapping; @@ -213,7 +215,6 @@ void update_mmu_cache(struct vm_area_struct *vma, mapping = page_mapping(page); if (mapping) flush_aliases(mapping, page); - set_bit(PG_arch_1, &page->flags); } } From f25cd4e4423f714f7b5b850dd30cf1c918785d5b Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 23 Jun 2014 14:06:21 +0800 Subject: [PATCH 132/201] FogBugz #212187: nios2: Avoid flush_dcache_page() for zero page The zero page is read-only, and has its cache state cleared during boot. No further maintenance for this page is required. Follow commit 421fe93cc4b06b2f5e875cbe0f692800d4862ee5 for ARM. Signed-off-by: Yuriy Kozlov Signed-off-by: Ley Foon Tan --- arch/nios2/mm/cacheflush.c | 17 ++++++++++++++++- arch/nios2/mm/init.c | 3 +++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/nios2/mm/cacheflush.c b/arch/nios2/mm/cacheflush.c index 0f24ed791e50f..106c54ec39f57 100644 --- a/arch/nios2/mm/cacheflush.c +++ b/arch/nios2/mm/cacheflush.c @@ -178,7 +178,16 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, void flush_dcache_page(struct page *page) { - struct address_space *mapping = page_mapping(page); + struct address_space *mapping; + + /* + * The zero page is never written to, so never has any dirty + * cache lines, and therefore never needs to be flushed. + */ + if (page == ZERO_PAGE(0)) + return; + + mapping = page_mapping(page); /* Flush this page if there are aliases. */ if (mapping) { @@ -203,7 +212,13 @@ void update_mmu_cache(struct vm_area_struct *vma, if (!pfn_valid(pfn)) return; + /* + * The zero page is never written to, so never has any dirty + * cache lines, and therefore never needs to be flushed. + */ page = pfn_to_page(pfn); + if (page == ZERO_PAGE(0)) + return; if (!PageReserved(page) && !test_and_set_bit(PG_dcache_clean, &page->flags)) { diff --git a/arch/nios2/mm/init.c b/arch/nios2/mm/init.c index 8afd9ded5b9e3..f76aec410e2c3 100644 --- a/arch/nios2/mm/init.c +++ b/arch/nios2/mm/init.c @@ -65,6 +65,9 @@ void __init paging_init(void) /* pass the memory from the bootmem allocator to the main allocator */ free_area_init(zones_size); + + flush_dcache_range((unsigned long)empty_zero_page, + (unsigned long)empty_zero_page + PAGE_SIZE); } void __init mem_init(void) From 49f94a7dade3b62b51c45ca358dd812426f09956 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 23 Jun 2014 14:09:33 +0800 Subject: [PATCH 133/201] FogBugz #212188: nios2: Flush page regardless of if there is a mapping This is how ARM does it. This fixes a problem with applications such as libpcap reading stale data from mmapped buffers. Signed-off-by: Yuriy Kozlov Signed-off-by: Ley Foon Tan --- arch/nios2/mm/cacheflush.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/nios2/mm/cacheflush.c b/arch/nios2/mm/cacheflush.c index 106c54ec39f57..cf06a00c47a4d 100644 --- a/arch/nios2/mm/cacheflush.c +++ b/arch/nios2/mm/cacheflush.c @@ -190,15 +190,14 @@ void flush_dcache_page(struct page *page) mapping = page_mapping(page); /* Flush this page if there are aliases. */ - if (mapping) { - if (!mapping_mapped(mapping)) { - clear_bit(PG_dcache_clean, &page->flags); - } else if (mapping) { - unsigned long start = (unsigned long)page_address(page); - __flush_dcache(start, start + PAGE_SIZE); + if (mapping && !mapping_mapped(mapping)) { + clear_bit(PG_dcache_clean, &page->flags); + } else { + unsigned long start = (unsigned long)page_address(page); + __flush_dcache_all(start, start + PAGE_SIZE); + if (mapping) flush_aliases(mapping, page); - set_bit(PG_dcache_clean, &page->flags); - } + set_bit(PG_dcache_clean, &page->flags); } } EXPORT_SYMBOL(flush_dcache_page); From 0f7102a517209a2dba544c2f6ad938afbf02ff25 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 16 Jul 2014 10:55:25 +0800 Subject: [PATCH 134/201] FogBugz #214042: nios2: add nios2 gen2 dts support Add nios2 gen2 dts support. Software will search for altr first and then search for ALTR if can't find with altr prefix. This is to backward compatible with nios2 legacy (with ALTR prefix). Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/cpuinfo.c | 46 +++++++++++++++++++++++++------------ arch/nios2/kernel/irq.c | 3 +++ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/arch/nios2/kernel/cpuinfo.c b/arch/nios2/kernel/cpuinfo.c index e833cff6e3648..9ae38029b473f 100644 --- a/arch/nios2/kernel/cpuinfo.c +++ b/arch/nios2/kernel/cpuinfo.c @@ -35,15 +35,31 @@ struct cpuinfo cpuinfo; static inline u32 fcpu(struct device_node *cpu, const char *n) { u32 val = 0; + int ret; + char buf[strlen(n) + 1]; - of_property_read_u32(cpu, n, &val); + ret = of_property_read_u32(cpu, n, &val); + if (ret && !strncmp(n, "altr,", 5)) { + strcpy(buf, n); + strncpy(buf, "ALTR,", 5); + of_property_read_u32(cpu, buf, &val); + } return val; } static inline u32 fcpu_has(struct device_node *cpu, const char *n) { - return of_get_property(cpu, n, NULL) ? 1 : 0; + const void *ret; + char buf[strlen(n) + 1]; + + ret = of_get_property(cpu, n, NULL); + if (!ret && !strncmp(n, "altr,", 5)) { + strcpy(buf, n); + strncpy(buf, "ALTR,", 5); + ret = of_get_property(cpu, buf, NULL); + } + return ret ? 1 : 0; } void __init setup_cpuinfo(void) @@ -56,22 +72,22 @@ void __init setup_cpuinfo(void) if (!cpu) panic("%s: No CPU found in devicetree!\n", __func__); - if (!fcpu_has(cpu, "ALTR,has-initda")) + if (!fcpu_has(cpu, "altr,has-initda")) panic("initda instruction is unimplemented. Please update your " "hardware system to have more than 4-byte line data " "cache\n"); cpuinfo.cpu_clock_freq = fcpu(cpu, "clock-frequency"); - str = of_get_property(cpu, "ALTR,implementation", &len); + str = of_get_property(cpu, "altr,implementation", &len); if (str) strlcpy(cpuinfo.cpu_impl, str, sizeof(cpuinfo.cpu_impl)); else strcpy(cpuinfo.cpu_impl, ""); - cpuinfo.has_div = fcpu_has(cpu, "ALTR,has-div"); - cpuinfo.has_mul = fcpu_has(cpu, "ALTR,has-mul"); - cpuinfo.has_mulx = fcpu_has(cpu, "ALTR,has-mulx"); + cpuinfo.has_div = fcpu_has(cpu, "altr,has-div"); + cpuinfo.has_mul = fcpu_has(cpu, "altr,has-mul"); + cpuinfo.has_mulx = fcpu_has(cpu, "altr,has-mulx"); #ifdef CONFIG_NIOS2_HW_DIV_SUPPORT if (!cpuinfo.has_div) @@ -86,9 +102,9 @@ void __init setup_cpuinfo(void) err_cpu("MULX"); #endif - cpuinfo.tlb_num_ways = fcpu(cpu, "ALTR,tlb-num-ways"); + cpuinfo.tlb_num_ways = fcpu(cpu, "altr,tlb-num-ways"); if (!cpuinfo.tlb_num_ways) - panic("ALTR,tlb-num-ways can't be 0. Please check your hardware " + panic("altr,tlb-num-ways can't be 0. Please check your hardware " "system\n"); cpuinfo.icache_line_size = fcpu(cpu, "icache-line-size"); cpuinfo.icache_size = fcpu(cpu, "icache-size"); @@ -112,15 +128,15 @@ void __init setup_cpuinfo(void) "device tree dcache-size\n", CONFIG_NIOS2_DCACHE_SIZE, cpuinfo.dcache_size); - cpuinfo.tlb_pid_num_bits = fcpu(cpu, "ALTR,pid-num-bits"); + cpuinfo.tlb_pid_num_bits = fcpu(cpu, "altr,pid-num-bits"); cpuinfo.tlb_num_ways_log2 = ilog2(cpuinfo.tlb_num_ways); - cpuinfo.tlb_num_entries = fcpu(cpu, "ALTR,tlb-num-entries"); + cpuinfo.tlb_num_entries = fcpu(cpu, "altr,tlb-num-entries"); cpuinfo.tlb_num_lines = cpuinfo.tlb_num_entries / cpuinfo.tlb_num_ways; - cpuinfo.tlb_ptr_sz = fcpu(cpu, "ALTR,tlb-ptr-sz"); + cpuinfo.tlb_ptr_sz = fcpu(cpu, "altr,tlb-ptr-sz"); - cpuinfo.reset_addr = fcpu(cpu, "ALTR,reset-addr"); - cpuinfo.exception_addr = fcpu(cpu, "ALTR,exception-addr"); - cpuinfo.fast_tlb_miss_exc_addr = fcpu(cpu, "ALTR,fast-tlb-miss-addr"); + cpuinfo.reset_addr = fcpu(cpu, "altr,reset-addr"); + cpuinfo.exception_addr = fcpu(cpu, "altr,exception-addr"); + cpuinfo.fast_tlb_miss_exc_addr = fcpu(cpu, "altr,fast-tlb-miss-addr"); } #ifdef CONFIG_PROC_FS diff --git a/arch/nios2/kernel/irq.c b/arch/nios2/kernel/irq.c index e9265bd9fa450..3ab4370a7c04c 100644 --- a/arch/nios2/kernel/irq.c +++ b/arch/nios2/kernel/irq.c @@ -80,6 +80,9 @@ void __init init_IRQ(void) struct device_node *node; node = of_find_compatible_node(NULL, NULL, "ALTR,nios2-1.0"); + if (!node) + node = of_find_compatible_node(NULL, NULL, "altr,nios2-1.1"); + BUG_ON(!node); domain = irq_domain_add_linear(node, NIOS2_CPU_NR_IRQS, &irq_ops, NULL); From ef30078a97b691ed767900d5f33cc7db56994278 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 25 Jul 2013 11:27:34 +0800 Subject: [PATCH 135/201] FogBugz #138162: Add Altera hardware mutex driver Driver for Altera hardware mutex soft IP. v4: - Changed pr_debug to pr_info. v3: - Change dts compatible string from "altr,mutex-1.0" to "altr,hwmutex-1.0" v2: -Updated altera_mutex_request() for error handling message. -Updated altera_mutex_unlock(). -Used of_match_ptr. Signed-off-by: Ley Foon Tan --- .../bindings/misc/altera-hwmutex.txt | 22 ++ drivers/misc/Kconfig | 6 + drivers/misc/Makefile | 3 +- drivers/misc/altera_hwmutex.c | 323 ++++++++++++++++++ include/linux/altera_hwmutex.h | 43 +++ 5 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/misc/altera-hwmutex.txt create mode 100644 drivers/misc/altera_hwmutex.c create mode 100644 include/linux/altera_hwmutex.h diff --git a/Documentation/devicetree/bindings/misc/altera-hwmutex.txt b/Documentation/devicetree/bindings/misc/altera-hwmutex.txt new file mode 100644 index 0000000000000..6a583d08ece43 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/altera-hwmutex.txt @@ -0,0 +1,22 @@ +Altera hardware mutex +Altera hardware mutex can provide hardware assistance for synchronization and +mutual exclusion between processors in asymmetric/symmetric multiprocessing +(AMP/SMP) system or multi processes/threads in uniprocessor system. + +Required properties: +- compatible : "altr,mutex-1.0". +- reg : physical base address of the mutex and length of memory mapped + region. + +Example: + mutex0: mutex0@0x100 { + compatible = "altr,hwmutex-1.0"; + reg = <0x100 0x8>; + }; + +Example of mutex's client node that includes mutex phandle. + mclient0: mclient0@0x200 { + compatible = "client-1.0"; + reg = <0x200 0x10>; + mutex = <&mutex0>; + }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index feb793bc796f6..3952d9e72fe8e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -516,6 +516,12 @@ config SRAM the genalloc API. It is supposed to be used for small on-chip SRAM areas found on many SoCs. +config ALTERA_HWMUTEX + tristate "Altera Hardware Mutex" + help + This option enables device driver support for Altera Hardware Mutex. + Say Y here if you want to use the Altera hardware mutex support. + config VEXPRESS_SYSCFG bool "Versatile Express System Configuration driver" depends on VEXPRESS_CONFIG diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index ce4b042a84374..c5512192fc2ac 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -48,7 +48,8 @@ obj-y += ti-st/ obj-y += lis3lv02d/ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o -obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ +obj-$(CONFIG_ALTERA_STAPL) += altera-stapl/ +obj-$(CONFIG_ALTERA_HWMUTEX) += altera_hwmutex.o obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o diff --git a/drivers/misc/altera_hwmutex.c b/drivers/misc/altera_hwmutex.c new file mode 100644 index 0000000000000..2bfdc61e36d5d --- /dev/null +++ b/drivers/misc/altera_hwmutex.c @@ -0,0 +1,323 @@ +/* + * Copyright Altera Corporation (C) 2013. All rights reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_hwmutex" + + +static DEFINE_SPINLOCK(list_lock); /* protect mutex_list */ +static LIST_HEAD(mutex_list); + +/* Mutex Registers */ +#define MUTEX_REG 0x0 + +#define MUTEX_REG_VALUE_MASK 0xFFFF +#define MUTEX_REG_OWNER_OFFSET 16 +#define MUTEX_REG_OWNER_MASK 0xFFFF +#define MUTEX_GET_OWNER(reg) \ + ((reg >> MUTEX_REG_OWNER_OFFSET) & MUTEX_REG_OWNER_MASK) + +/** + * altera_mutex_request - Retrieves a pointer to an acquired mutex device + * structure + * @mutex_np: The pointer to mutex device node + * + * Returns a pointer to the mutex device structure associated with the + * supplied device node, or NULL if no corresponding mutex device was + * found. + */ +struct altera_mutex *altera_mutex_request(struct device_node *mutex_np) +{ + struct altera_mutex *mutex; + + spin_lock(&list_lock); + list_for_each_entry(mutex, &mutex_list, list) { + if (mutex_np == mutex->pdev->dev.of_node) { + if (!mutex->requested) { + mutex->requested = true; + spin_unlock(&list_lock); + return mutex; + } else { + pr_info("Mutex device is in use.\n"); + spin_unlock(&list_lock); + return NULL; + } + } + } + spin_unlock(&list_lock); + pr_info("Mutex device not found!\n"); + return NULL; +} +EXPORT_SYMBOL(altera_mutex_request); + +/** + * altera_mutex_free - Free the mutex + * @mutex: the mutex + * + * Return 0 if success. Otherwise, returns non-zero. + */ +int altera_mutex_free(struct altera_mutex *mutex) +{ + if (!mutex || !mutex->requested) + return -EINVAL; + + spin_lock(&list_lock); + mutex->requested = false; + spin_unlock(&list_lock); + + return 0; +} +EXPORT_SYMBOL(altera_mutex_free); + +static int __mutex_trylock(struct altera_mutex *mutex, u16 owner, u16 value) +{ + u32 read; + int ret = 0; + u32 data = (owner << MUTEX_REG_OWNER_OFFSET) | value; + + mutex_lock(&mutex->lock); + __raw_writel(data, mutex->regs + MUTEX_REG); + read = __raw_readl(mutex->regs + MUTEX_REG); + if (read != data) + ret = -1; + + mutex_unlock(&mutex->lock); + return ret; +} + +/** + * altera_mutex_lock - Acquires a hardware mutex, wait until it can get it. + * @mutex: the mutex to be acquired + * @owner: owner ID + * @value: the new non-zero value to write to mutex + * + * Returns 0 if mutex was successfully locked. Otherwise, returns non-zero. + * + * The mutex must later on be released by the same owner that acquired it. + * This function is not ISR callable. + */ +int altera_mutex_lock(struct altera_mutex *mutex, u16 owner, u16 value) +{ + if (!mutex || !mutex->requested) + return -EINVAL; + + while (__mutex_trylock(mutex, owner, value) != 0) + ; + + return 0; +} +EXPORT_SYMBOL(altera_mutex_lock); + +/** + * altera_mutex_trylock - Tries once to lock the hardware mutex and returns + * immediately + * @mutex: the mutex to be acquired + * @owner: owner ID + * @value: the new non-zero value to write to mutex + * + * Returns 0 if mutex was successfully locked. Otherwise, returns non-zero. + * + * The mutex must later on be released by the same owner that acquired it. + * This function is not ISR callable. + */ +int altera_mutex_trylock(struct altera_mutex *mutex, u16 owner, u16 value) +{ + if (!mutex || !mutex->requested) + return -EINVAL; + + return __mutex_trylock(mutex, owner, value); +} +EXPORT_SYMBOL(altera_mutex_trylock); + +/** + * altera_mutex_unlock - Unlock a mutex that has been locked by this owner + * previously that was locked on the + * altera_mutex_lock. Upon release, the value stored + * in the mutex is set to zero. + * @mutex: the mutex to be released + * @owner: Owner ID + * + * Returns 0 if mutex was successfully unlocked. Otherwise, returns + * non-zero. + * + * This function is not ISR callable. + */ +int altera_mutex_unlock(struct altera_mutex *mutex, u16 owner) +{ + u32 reg; + + if (!mutex || !mutex->requested) + return -EINVAL; + + mutex_lock(&mutex->lock); + + __raw_writel(owner << MUTEX_REG_OWNER_OFFSET, + mutex->regs + MUTEX_REG); + + reg = __raw_readl(mutex->regs + MUTEX_REG); + if (reg & MUTEX_REG_VALUE_MASK) { + /* Unlock failed */ + dev_dbg(&mutex->pdev->dev, + "Unlock mutex failed, owner %d and expected owner %d\n", + owner, MUTEX_GET_OWNER(reg)); + mutex_unlock(&mutex->lock); + return -EINVAL; + } + + mutex_unlock(&mutex->lock); + return 0; +} +EXPORT_SYMBOL(altera_mutex_unlock); + +/** + * altera_mutex_owned - Determines if this owner owns the mutex + * @mutex: the mutex to be queried + * @owner: Owner ID + * + * Returns 1 if the owner owns the mutex. Otherwise, returns zero. + */ +int altera_mutex_owned(struct altera_mutex *mutex, u16 owner) +{ + u32 reg; + u16 actual_owner; + int ret = 0; + + if (!mutex || !mutex->requested) + return ret; + + mutex_lock(&mutex->lock); + reg = __raw_readl(mutex->regs + MUTEX_REG); + actual_owner = MUTEX_GET_OWNER(reg); + if (actual_owner == owner) + ret = 1; + + mutex_unlock(&mutex->lock); + return ret; +} +EXPORT_SYMBOL(altera_mutex_owned); + +/** + * altera_mutex_is_locked - Determines if the mutex is locked + * @mutex: the mutex to be queried + * + * Returns 1 if the mutex is locked, 0 if unlocked. + */ +int altera_mutex_is_locked(struct altera_mutex *mutex) +{ + u32 reg; + int ret = 0; + + if (!mutex || !mutex->requested) + return ret; + + mutex_lock(&mutex->lock); + reg = __raw_readl(mutex->regs + MUTEX_REG); + reg &= MUTEX_REG_VALUE_MASK; + if (reg) + ret = 1; + + mutex_unlock(&mutex->lock); + return ret; +} +EXPORT_SYMBOL(altera_mutex_is_locked); + +static int altera_mutex_probe(struct platform_device *pdev) +{ + struct altera_mutex *mutex; + struct resource *regs; + + mutex = devm_kzalloc(&pdev->dev, sizeof(struct altera_mutex), + GFP_KERNEL); + if (!mutex) + return -ENOMEM; + + mutex->pdev = pdev; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + mutex->regs = devm_request_and_ioremap(&pdev->dev, regs); + if (!mutex->regs) + return -EADDRNOTAVAIL; + + mutex_init(&mutex->lock); + + spin_lock(&list_lock); + list_add_tail(&mutex->list, &mutex_list); + spin_unlock(&list_lock); + + platform_set_drvdata(pdev, mutex); + + return 0; +} + +static int altera_mutex_remove(struct platform_device *pdev) +{ + struct altera_mutex *mutex = platform_get_drvdata(pdev); + + spin_lock(&list_lock); + if (mutex) + list_del(&mutex->list); + spin_unlock(&list_lock); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id altera_mutex_match[] = { + { .compatible = "altr,mutex-1.0" }, + { /* Sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, altera_mutex_match); + +static struct platform_driver altera_mutex_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_mutex_match), + }, + .remove = altera_mutex_remove, +}; + +static int __init altera_mutex_init(void) +{ + return platform_driver_probe(&altera_mutex_platform_driver, + altera_mutex_probe); +} + +static void __exit altera_mutex_exit(void) +{ + platform_driver_unregister(&altera_mutex_platform_driver); +} + +module_init(altera_mutex_init); +module_exit(altera_mutex_exit); + +MODULE_AUTHOR("Ley Foon Tan "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Altera Hardware Mutex driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/altera_hwmutex.h b/include/linux/altera_hwmutex.h new file mode 100644 index 0000000000000..c3960599ac051 --- /dev/null +++ b/include/linux/altera_hwmutex.h @@ -0,0 +1,43 @@ +/* + * Copyright Altera Corporation (C) 2013. All rights reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + */ +#ifndef _ALTERA_MUTEX_H +#define _ALTERA_MUTEX_H + +#include +#include + +struct altera_mutex { + struct list_head list; + struct platform_device *pdev; + struct mutex lock; + void __iomem *regs; + bool requested; +}; + +extern struct altera_mutex *altera_mutex_request(struct device_node *mutex_np); +extern int altera_mutex_free(struct altera_mutex *mutex); + +extern int altera_mutex_lock(struct altera_mutex *mutex, u16 owner, u16 value); + +extern int altera_mutex_trylock(struct altera_mutex *mutex, u16 owner, + u16 value); +extern int altera_mutex_unlock(struct altera_mutex *mutex, u16 owner); +extern int altera_mutex_owned(struct altera_mutex *mutex, u16 owner); +extern int altera_mutex_is_locked(struct altera_mutex *mutex); + +#endif /* _ALTERA_MUTEX_H */ From 89387f85f4ffa5d9f76c0c6b53a897cd86f74904 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 5 Aug 2013 16:33:24 +0800 Subject: [PATCH 136/201] FogBugz #143451: Fix mutex compatible string Fix mutex compatible string from mutex-1.0 to hwmutex-1.0. Signed-off-by: Ley Foon Tan --- drivers/misc/altera_hwmutex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/altera_hwmutex.c b/drivers/misc/altera_hwmutex.c index 2bfdc61e36d5d..43e4e564e9f17 100644 --- a/drivers/misc/altera_hwmutex.c +++ b/drivers/misc/altera_hwmutex.c @@ -288,7 +288,7 @@ static int altera_mutex_remove(struct platform_device *pdev) } static const struct of_device_id altera_mutex_match[] = { - { .compatible = "altr,mutex-1.0" }, + { .compatible = "altr,hwmutex-1.0" }, { /* Sentinel */ } }; From 659aa33489ce6a0a0f434d34dc123704269cf823 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 24 Jul 2013 20:15:49 +0800 Subject: [PATCH 137/201] FogBugz #138164: Add Altera mailbox driver Altera mailbox specific driver that uses the mailbox common framework. v2: - Updated documentation to include mailbox client node. - Checked NULL pointer - Read PTR register first. - Changed full and pending functions to inline. - Used macro for Pending and full mask/offset. Signed-off-by: Ley Foon Tan --- .../bindings/mailbox/mailbox-altera.txt | 30 ++ drivers/mailbox/Kconfig | 7 + drivers/mailbox/Makefile | 1 + drivers/mailbox/mailbox-altera.c | 411 +++++++++++++++ drivers/mailbox/mailbox.c | 477 ++++++++++++++++++ 5 files changed, 926 insertions(+) create mode 100644 Documentation/devicetree/bindings/mailbox/mailbox-altera.txt create mode 100644 drivers/mailbox/mailbox-altera.c create mode 100644 drivers/mailbox/mailbox.c diff --git a/Documentation/devicetree/bindings/mailbox/mailbox-altera.txt b/Documentation/devicetree/bindings/mailbox/mailbox-altera.txt new file mode 100644 index 0000000000000..dbfcfb6bfb7d7 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/mailbox-altera.txt @@ -0,0 +1,30 @@ +Altera mailbox (simple) soft IP + +Required properties: +- compatible : "altr,mailbox-1.0". +- reg : physical base address of the mailbox and length of memory mapped + region. + +Optional properties: +- interrupt-parent : interrupt source phandle. +- interrupts : interrupt number. The interrupt specifier format depends on the + interrupt controller parent. + +The name of mailbox device node must be unique. This name will be used as +controller name in driver to identify the controller and the mailbox client will +use this name when requesting a mailbox channel. + +Example: + mailbox0: mailbox0@0x100 { + compatible = "altr,mailbox-1.0"; + reg = <0x100 0x8>; + interrupt-parent = < &gic_0 >; + interrupts = <5>; + }; + +Example of mailbox's client node that includes mailbox phandle. + mclient0: mclient0@0x200 { + compatible = "client-1.0"; + reg = <0x200 0x10>; + mailbox = <&mailbox0>; + }; diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index c8b5c13bcd05e..88544dcf5545f 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -50,4 +50,11 @@ config OMAP_MBOX_KFIFO_SIZE Specify the default size of mailbox's kfifo buffers (bytes). This can also be changed at runtime (via the mbox_kfifo_size module parameter). + +config ALTERA_MBOX + tristate "Altera Mailbox" + help + An implementation of the Altera Mailbox (simple) soft core. Is is used + to send message between processors. Say Y here if you want to use the + Altera mailbox support. endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index e0facb34084a4..733d0a6ec03ea 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_OMAP1_MBOX) += mailbox_omap1.o mailbox_omap1-objs := mailbox-omap1.o obj-$(CONFIG_OMAP2PLUS_MBOX) += mailbox_omap2.o mailbox_omap2-objs := mailbox-omap2.o +obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c new file mode 100644 index 0000000000000..b23113c383618 --- /dev/null +++ b/drivers/mailbox/mailbox-altera.c @@ -0,0 +1,411 @@ +/* + * Copyright Altera Corporation (C) 2013. All rights reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_CMD_REG 0x00 +#define MAILBOX_PTR_REG 0x04 +#define MAILBOX_STS_REG 0x08 +#define MAILBOX_INTMASK_REG 0x0C + +#define INT_PENDING_MSK 0x1 +#define INT_SPACE_MSK 0x2 + +#define STS_PENDING_MSK 0x1 +#define STS_FULL_MSK 0x2 +#define STS_FULL_OFT 0x1 + +#define MBOX_PENDING(status) (((status) & STS_PENDING_MSK)) +#define MBOX_FULL(status) (((status) & STS_FULL_MSK) >> STS_FULL_OFT) + +enum altera_mbox_msg { + MBOX_CMD = 0, + MBOX_PTR, +}; + +#define MBOX_POLLING_MS 1 /* polling interval 1ms */ + +struct altera_mbox { + bool is_sender; /* 1-sender, 0-receiver */ + bool intr_mode; + int irq; + int use_count; + void __iomem *mbox_base; + struct device *dev; + struct ipc_link link; + struct ipc_controller ipc_con; + struct mutex lock; + /* If the controller supports only RX polling mode */ + struct timer_list rxpoll_timer; +}; + +static inline struct altera_mbox *to_altera_mbox(struct ipc_link *link) +{ + if (!link) + return NULL; + + return container_of(link, struct altera_mbox, link); +} + +static inline int altera_mbox_full(struct altera_mbox *mbox) +{ + u32 status; + status = __raw_readl(mbox->mbox_base + MAILBOX_STS_REG); + return MBOX_FULL(status); +} + +static inline int altera_mbox_pending(struct altera_mbox *mbox) +{ + u32 status; + status = __raw_readl(mbox->mbox_base + MAILBOX_STS_REG); + return MBOX_PENDING(status); +} + +static void altera_mbox_rx_intmask(struct altera_mbox *mbox, bool enable) +{ + u32 mask; + mask = __raw_readl(mbox->mbox_base + MAILBOX_INTMASK_REG); + if (enable) + mask |= INT_PENDING_MSK; + else + mask &= ~INT_PENDING_MSK; + __raw_writel(mask, mbox->mbox_base + MAILBOX_INTMASK_REG); +} + +static void altera_mbox_tx_intmask(struct altera_mbox *mbox, bool enable) +{ + u32 mask; + mask = __raw_readl(mbox->mbox_base + MAILBOX_INTMASK_REG); + if (enable) + mask |= INT_SPACE_MSK; + else + mask &= ~INT_SPACE_MSK; + __raw_writel(mask, mbox->mbox_base + MAILBOX_INTMASK_REG); +} + +static bool altera_mbox_is_sender(struct altera_mbox *mbox) +{ + u32 reg; + /* Write a magic number to PTR register and read back this register. + * This register is read-write if it is a sender. + */ + #define MBOX_MAGIC 0xA5A5AA55 + __raw_writel(MBOX_MAGIC, mbox->mbox_base + MAILBOX_PTR_REG); + reg = __raw_readl(mbox->mbox_base + MAILBOX_PTR_REG); + if (reg == MBOX_MAGIC) { + /* Clear to 0 */ + __raw_writel(0, mbox->mbox_base + MAILBOX_PTR_REG); + return true; + } + return false; +} + +static void altera_mbox_rx_data(struct ipc_link *link) +{ + struct altera_mbox *mbox = to_altera_mbox(link); + u32 data[2]; + + if (altera_mbox_pending(mbox)) { + data[MBOX_PTR] = __raw_readl(mbox->mbox_base + MAILBOX_PTR_REG); + data[MBOX_CMD] = __raw_readl(mbox->mbox_base + MAILBOX_CMD_REG); + ipc_link_received_data(link, (void *)data); + } + + return; +} + +static void altera_mbox_poll_rx(unsigned long data) +{ + struct ipc_link *link = (struct ipc_link *)data; + struct altera_mbox *mbox = to_altera_mbox(link); + + altera_mbox_rx_data(link); + + mod_timer(&mbox->rxpoll_timer, + jiffies + msecs_to_jiffies(MBOX_POLLING_MS)); +} + +static irqreturn_t altera_mbox_tx_interrupt(int irq, void *p) +{ + struct ipc_link *link = (struct ipc_link *)p; + struct altera_mbox *mbox = to_altera_mbox(link); + + altera_mbox_tx_intmask(mbox, false); + ipc_link_txdone(link, XFER_OK); + + return IRQ_HANDLED; +} + +static irqreturn_t altera_mbox_rx_interrupt(int irq, void *p) +{ + struct ipc_link *link = (struct ipc_link *)p; + altera_mbox_rx_data(link); + return IRQ_HANDLED; +} + +static int altera_mbox_startup_sender(struct ipc_link *link) +{ + int ret; + struct altera_mbox *mbox = to_altera_mbox(link); + + if (mbox->intr_mode) { + ret = request_irq(mbox->irq, altera_mbox_tx_interrupt, 0, + mbox->ipc_con.controller_name, link); + if (ret) { + dev_err(mbox->dev, + "failed to register mailbox interrupt:%d\n", + ret); + return ret; + } + } + + return 0; +} + +static int altera_mbox_startup_receiver(struct ipc_link *link) +{ + int ret; + struct altera_mbox *mbox = to_altera_mbox(link); + + if (mbox->intr_mode) { + ret = request_irq(mbox->irq, altera_mbox_rx_interrupt, 0, + mbox->ipc_con.controller_name, link); + if (ret) { + dev_err(mbox->dev, + "failed to register mailbox interrupt:%d\n", + ret); + return ret; + } + altera_mbox_rx_intmask(mbox, true); + } else { + /* Setup polling timer */ + setup_timer(&mbox->rxpoll_timer, altera_mbox_poll_rx, + (unsigned long)link); + mod_timer(&mbox->rxpoll_timer, + jiffies + msecs_to_jiffies(MBOX_POLLING_MS)); + } + + return 0; +} + +static int altera_mbox_send_data(struct ipc_link *link, void *data) +{ + struct altera_mbox *mbox = to_altera_mbox(link); + u32 *udata = (u32 *)data; + + if (!mbox || !data) + return -EINVAL; + if (!mbox->is_sender) { + dev_warn(mbox->dev, + "failed to send. This is receiver mailbox.\n"); + return -EINVAL; + } + + if (altera_mbox_full(mbox)) + return -EBUSY; + + /* Enable interrupt before send */ + altera_mbox_tx_intmask(mbox, true); + + /* Pointer register must write before command register */ + __raw_writel(udata[MBOX_PTR], mbox->mbox_base + MAILBOX_PTR_REG); + __raw_writel(udata[MBOX_CMD], mbox->mbox_base + MAILBOX_CMD_REG); + + return 0; +} + +static bool altera_mbox_is_ready(struct ipc_link *link) +{ + struct altera_mbox *mbox = to_altera_mbox(link); + + if (WARN_ON(!mbox)) + return false; + + /* Return false if mailbox is full */ + return altera_mbox_full(mbox) ? false : true; +} + +static int altera_mbox_startup(struct ipc_link *link, void *ignored) +{ + struct altera_mbox *mbox = to_altera_mbox(link); + int ret = 0; + + if (!mbox) + return -EINVAL; + + mutex_lock(&mbox->lock); + if (!mbox->use_count) { + if (mbox->is_sender) + ret = altera_mbox_startup_sender(link); + else + ret = altera_mbox_startup_receiver(link); + + if (!ret) + mbox->use_count++; + } + mutex_unlock(&mbox->lock); + return ret; +} + +static void altera_mbox_shutdown(struct ipc_link *link) +{ + struct altera_mbox *mbox = to_altera_mbox(link); + + if (WARN_ON(!mbox)) + return; + + mutex_lock(&mbox->lock); + if (!--mbox->use_count) { + if (mbox->intr_mode) { + /* Unmask all interrupt masks */ + __raw_writel(~0, mbox->mbox_base + MAILBOX_INTMASK_REG); + free_irq(mbox->irq, link); + } else if (!mbox->is_sender) + del_timer_sync(&mbox->rxpoll_timer); + } + mutex_unlock(&mbox->lock); +} + +static struct ipc_link_ops altera_mbox_ops = { + .send_data = altera_mbox_send_data, + .startup = altera_mbox_startup, + .shutdown = altera_mbox_shutdown, + .is_ready = altera_mbox_is_ready, +}; + +static int altera_mbox_probe(struct platform_device *pdev) +{ + struct altera_mbox *mbox; + struct ipc_link *links[2] = {NULL, NULL}; + struct resource *regs; + struct device_node *np = pdev->dev.of_node; + int ret; + + mbox = devm_kzalloc(&pdev->dev, sizeof(struct altera_mbox), + GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + mbox->mbox_base = devm_request_and_ioremap(&pdev->dev, regs); + if (!mbox->mbox_base) + return -EADDRNOTAVAIL; + + mbox->dev = &pdev->dev; + mutex_init(&mbox->lock); + + /* Check is it a sender or receiver? */ + mbox->is_sender = altera_mbox_is_sender(mbox); + + mbox->irq = platform_get_irq(pdev, 0); + if (mbox->irq >= 0) + mbox->intr_mode = true; + + /* Hardware supports only one channel, link_name always set to "0". */ + snprintf(mbox->link.link_name, sizeof(mbox->link.link_name), "0"); + links[0] = &mbox->link; + mbox->ipc_con.links = links; + mbox->ipc_con.ops = &altera_mbox_ops; + + if ((strlen(np->name) + 1) > sizeof(mbox->ipc_con.controller_name)) + dev_warn(&pdev->dev, "Length of mailbox controller name is greater than %d\n", + sizeof(mbox->ipc_con.controller_name)); + + snprintf(mbox->ipc_con.controller_name, + sizeof(mbox->ipc_con.controller_name), "%s", np->name); + + dev_info(&pdev->dev, "Mailbox controller name is %s\n", + mbox->ipc_con.controller_name); + + if (mbox->is_sender) { + if (mbox->intr_mode) + mbox->ipc_con.txdone_irq = true; + else { + mbox->ipc_con.txdone_poll = true; + mbox->ipc_con.txpoll_period = MBOX_POLLING_MS; + } + } + + ret = ipc_links_register(&mbox->ipc_con); + if (ret) { + dev_err(&pdev->dev, "Register mailbox failed\n"); + goto err; + } + + platform_set_drvdata(pdev, mbox); + return 0; +err: + return ret; +} + +static int altera_mbox_remove(struct platform_device *pdev) +{ + struct altera_mbox *mbox = platform_get_drvdata(pdev); + if (!mbox) + return -EINVAL; + + ipc_links_unregister(&mbox->ipc_con); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id altera_mbox_match[] = { + { .compatible = "altr,mailbox-1.0" }, + { /* Sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, altera_mbox_match); + +static struct platform_driver altera_mbox_driver = { + .probe = altera_mbox_probe, + .remove = altera_mbox_remove, + .driver = { + .name = "altera-mailbox", + .owner = THIS_MODULE, + .of_match_table = altera_mbox_match, + }, +}; + +static int altera_mbox_init(void) +{ + return platform_driver_register(&altera_mbox_driver); +} + +static void altera_mbox_exit(void) +{ + platform_driver_unregister(&altera_mbox_driver); +} + +module_init(altera_mbox_init); +module_exit(altera_mbox_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Altera mailbox specific functions"); +MODULE_AUTHOR("Ley Foon Tan "); +MODULE_ALIAS("platform:altera-mailbox"); diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c new file mode 100644 index 0000000000000..0888be5b664cf --- /dev/null +++ b/drivers/mailbox/mailbox.c @@ -0,0 +1,477 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mailbox_internal.h" + +static LIST_HEAD(ipc_cons); +static DEFINE_MUTEX(con_mutex); + +static request_token_t _add_to_rbuf(struct ipc_chan *chan, void *mssg) +{ + request_token_t idx; + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + /* See if there is any space left */ + if (chan->msg_count == MBOX_TX_QUEUE_LEN) { + spin_unlock_irqrestore(&chan->lock, flags); + return 0; + } + + idx = chan->msg_free; + chan->msg_data[idx] = mssg; + chan->msg_count++; + + if (idx == MBOX_TX_QUEUE_LEN - 1) + chan->msg_free = 0; + else + chan->msg_free++; + + spin_unlock_irqrestore(&chan->lock, flags); + + /* To aid debugging, we return 'idx+1' instead of 1 */ + return idx + 1; +} + +static void _msg_submit(struct ipc_chan *chan) +{ + struct ipc_link *link = chan->link; + unsigned count, idx; + unsigned long flags; + void *data; + int err; + + spin_lock_irqsave(&chan->lock, flags); + + if (!chan->msg_count || chan->active_req) { + spin_unlock_irqrestore(&chan->lock, flags); + return; + } + + count = chan->msg_count; + idx = chan->msg_free; + if (idx >= count) + idx -= count; + else + idx += MBOX_TX_QUEUE_LEN - count; + + data = chan->msg_data[idx]; + + /* Try to submit a message to the IPC controller */ + err = chan->link_ops->send_data(link, data); + if (!err) { + chan->active_req = data; + chan->msg_count--; + } + + spin_unlock_irqrestore(&chan->lock, flags); +} + +static void tx_tick(struct ipc_chan *chan, enum xfer_result r) +{ + unsigned long flags; + void *mssg; + + spin_lock_irqsave(&chan->lock, flags); + mssg = chan->active_req; + chan->active_req = NULL; + spin_unlock_irqrestore(&chan->lock, flags); + + /* Submit next message */ + _msg_submit(chan); + + /* Notify the client */ + if (chan->tx_block) + complete(&chan->tx_complete); + else if (mssg && chan->txcb) + chan->txcb(chan->cl_id, mssg, r); +} + +static void poll_txdone(unsigned long data) +{ + struct ipc_con *con = (struct ipc_con *)data; + bool txdone, resched = false; + struct ipc_chan *chan; + + list_for_each_entry(chan, &con->channels, node) { + if (chan->active_req && chan->assigned) { + resched = true; + txdone = chan->link_ops->is_ready(chan->link); + if (txdone) + tx_tick(chan, XFER_OK); + } + } + + if (resched) + mod_timer(&con->poll, + jiffies + msecs_to_jiffies(con->period)); +} + +/* + * After 'startup' and before 'shutdown', the IPC controller driver + * notifies the API of data received over the link. + * The controller driver should make sure the 'RTR' is de-asserted since + * reception of the packet and until after this call returns. + * This call could be made from atomic context. + */ +void ipc_link_received_data(struct ipc_link *link, void *mssg) +{ + struct ipc_chan *chan = (struct ipc_chan *)link->api_priv; + + /* No buffering the received data */ + if (chan->rxcb) + chan->rxcb(chan->cl_id, mssg); +} +EXPORT_SYMBOL(ipc_link_received_data); + +/* + * The IPC controller driver notifies the API that the remote has + * asserted RTR and it could now send another message on the link. + */ +void ipc_link_txdone(struct ipc_link *link, enum xfer_result r) +{ + struct ipc_chan *chan = (struct ipc_chan *)link->api_priv; + + if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) { + pr_err("Controller can't run the TX ticker\n"); + return; + } + + tx_tick(chan, r); +} +EXPORT_SYMBOL(ipc_link_txdone); + +/* + * The client/protocol had received some 'ACK' packet and it notifies + * the API that the last packet was sent successfully. This only works + * if the controller doesn't get IRQ for TX done. + */ +void ipc_client_txdone(void *channel, enum xfer_result r) +{ + struct ipc_chan *chan = (struct ipc_chan *)channel; + bool txdone = true; + + if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) { + pr_err("Client can't run the TX ticker\n"); + return; + } + + if (chan->txdone_method & TXDONE_BY_POLL) + txdone = chan->link_ops->is_ready(chan->link); + + if (txdone) + tx_tick(chan, r); +} +EXPORT_SYMBOL(ipc_client_txdone); + +/* + * Called by a client to "put data on the h/w channel" so that if + * everything else is fine we don't need to do anything more locally + * for the remote to receive the data intact. + * In reality, the remote may receive it intact, corrupted or not at all. + * This could be called from atomic context as it simply + * queues the data and returns a token (request_token_t) + * against the request. + * The client is later notified of successful transmission of + * data over the channel via the 'txcb'. The client could in + * turn queue more messages from txcb. + */ +request_token_t ipc_send_message(void *channel, void *mssg) +{ + struct ipc_chan *chan = (struct ipc_chan *)channel; + request_token_t t; + + if (!chan || !chan->assigned) + return 0; + + if (chan->tx_block) + init_completion(&chan->tx_complete); + + t = _add_to_rbuf(chan, mssg); + if (!t) + pr_err("Try increasing MBOX_TX_QUEUE_LEN\n"); + + _msg_submit(chan); + + if (chan->txdone_method == TXDONE_BY_POLL) + poll_txdone((unsigned long)chan->con); + + if (chan->tx_block && chan->active_req) { + int ret; + ret = wait_for_completion_timeout(&chan->tx_complete, + chan->tx_tout); + if (ret == 0) { + t = 0; + tx_tick(chan, XFER_ERR); + } + } + + return t; +} +EXPORT_SYMBOL(ipc_send_message); + +/* + * A client driver asks for exclusive use of a channel/mailbox. + * If assigned, the channel has to be 'freed' before it could + * be assigned to some other client. + * After assignment, any packet received on this channel will be + * handed over to the client via the 'rxcb' callback. + * The 'txcb' callback is used to notify client upon sending the + * packet over the channel, which may or may not have been yet + * read by the remote processor. + */ +void *ipc_request_channel(struct ipc_client *cl) +{ + struct ipc_chan *chan; + struct ipc_con *con; + unsigned long flags; + char *con_name; + int len, ret; + + con_name = cl->chan_name; + len = strcspn(cl->chan_name, ":"); + + ret = 0; + mutex_lock(&con_mutex); + list_for_each_entry(con, &ipc_cons, node) + if (!strncmp(con->name, con_name, len)) { + ret = 1; + break; + } + mutex_unlock(&con_mutex); + + if (!ret) { + pr_err("Controller(%s) not found!\n", cl->chan_name); + return NULL; + } + + ret = 0; + list_for_each_entry(chan, &con->channels, node) { + if (!strcmp(con_name + len + 1, chan->name) + && !chan->assigned) { + spin_lock_irqsave(&chan->lock, flags); + chan->msg_free = 0; + chan->msg_count = 0; + chan->active_req = NULL; + chan->rxcb = cl->rxcb; + chan->txcb = cl->txcb; + chan->cl_id = cl->cl_id; + chan->assigned = true; + chan->tx_block = cl->tx_block; + if (!cl->tx_tout) + chan->tx_tout = ~0; + else + chan->tx_tout = msecs_to_jiffies(cl->tx_tout); + if (chan->txdone_method == TXDONE_BY_POLL + && cl->knows_txdone) + chan->txdone_method |= TXDONE_BY_ACK; + spin_unlock_irqrestore(&chan->lock, flags); + ret = 1; + break; + } + } + + if (!ret) { + pr_err("Unable to assign mailbox(%s)\n", cl->chan_name); + return NULL; + } + + ret = chan->link_ops->startup(chan->link, cl->link_data); + if (ret) { + pr_err("Unable to startup the link\n"); + ipc_free_channel((void *)chan); + return NULL; + } + + return (void *)chan; +} +EXPORT_SYMBOL(ipc_request_channel); + +/* Drop any messages queued and release the channel */ +void ipc_free_channel(void *ch) +{ + struct ipc_chan *chan = (struct ipc_chan *)ch; + unsigned long flags; + + if (!chan || !chan->assigned) + return; + + chan->link_ops->shutdown(chan->link); + + /* The queued TX requests are simply aborted, no callbacks are made */ + spin_lock_irqsave(&chan->lock, flags); + chan->assigned = false; + chan->active_req = NULL; + if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK)) + chan->txdone_method = TXDONE_BY_POLL; + spin_unlock_irqrestore(&chan->lock, flags); + + blocking_notifier_call_chain(&chan->avail, 0, NULL); +} +EXPORT_SYMBOL(ipc_free_channel); + +static struct ipc_chan *name_to_chan(const char *name) +{ + struct ipc_chan *chan = NULL; + struct ipc_con *con; + int len, found = 0; + + len = strcspn(name, ":"); + + mutex_lock(&con_mutex); + + list_for_each_entry(con, &ipc_cons, node) { + if (!strncmp(con->name, name, len)) { + list_for_each_entry(chan, &con->channels, node) { + if (!strcmp(name + len + 1, chan->name)) { + found = 1; + goto done; + } + } + } + } +done: + mutex_unlock(&con_mutex); + + if (!found) + return NULL; + + return chan; +} + +int ipc_notify_chan_register(const char *name, struct notifier_block *nb) +{ + struct ipc_chan *chan = name_to_chan(name); + + if (chan && nb) + return blocking_notifier_chain_register(&chan->avail, nb); + + return -EINVAL; +} +EXPORT_SYMBOL(ipc_notify_chan_register); + +void ipc_notify_chan_unregister(const char *name, struct notifier_block *nb) +{ + struct ipc_chan *chan = name_to_chan(name); + + if (chan && nb) + blocking_notifier_chain_unregister(&chan->avail, nb); +} +EXPORT_SYMBOL(ipc_notify_chan_unregister); + +/* + * Call for IPC controller drivers to register a controller, adding + * its channels/mailboxes to the global pool. + */ +int ipc_links_register(struct ipc_controller *ipc) +{ + int i, num_links, txdone; + struct ipc_chan *chan; + struct ipc_con *con; + + /* Sanity check */ + if (!ipc || !ipc->ops) + return -EINVAL; + + for (i = 0; ipc->links[i]; i++) + ; + if (!i) + return -EINVAL; + num_links = i; + + mutex_lock(&con_mutex); + /* Check if already populated */ + list_for_each_entry(con, &ipc_cons, node) + if (!strcmp(ipc->controller_name, con->name)) { + mutex_unlock(&con_mutex); + return -EINVAL; + } + mutex_unlock(&con_mutex); + + con = kzalloc(sizeof(*con) + sizeof(*chan) * num_links, GFP_KERNEL); + if (!con) + return -ENOMEM; + + INIT_LIST_HEAD(&con->channels); + snprintf(con->name, 16, "%s", ipc->controller_name); + + if (ipc->txdone_irq) + txdone = TXDONE_BY_IRQ; + else if (ipc->txdone_poll) + txdone = TXDONE_BY_POLL; + else /* It has to be ACK then */ + txdone = TXDONE_BY_ACK; + + if (txdone == TXDONE_BY_POLL) { + con->period = ipc->txpoll_period; + con->poll.function = &poll_txdone; + con->poll.data = (unsigned long)con; + init_timer(&con->poll); + } + + chan = (void *)con + sizeof(*con); + for (i = 0; i < num_links; i++) { + chan[i].con = con; + chan[i].assigned = false; + chan[i].link_ops = ipc->ops; + chan[i].link = ipc->links[i]; + chan[i].txdone_method = txdone; + chan[i].link->api_priv = &chan[i]; + spin_lock_init(&chan[i].lock); + BLOCKING_INIT_NOTIFIER_HEAD(&chan[i].avail); + list_add_tail(&chan[i].node, &con->channels); + snprintf(chan[i].name, 16, "%s", ipc->links[i]->link_name); + } + + mutex_lock(&con_mutex); + list_add_tail(&con->node, &ipc_cons); + mutex_unlock(&con_mutex); + + return 0; +} +EXPORT_SYMBOL(ipc_links_register); + +void ipc_links_unregister(struct ipc_controller *ipc) +{ + struct ipc_con *t, *con = NULL; + struct ipc_chan *chan; + + mutex_lock(&con_mutex); + + list_for_each_entry(t, &ipc_cons, node) + if (!strcmp(ipc->controller_name, t->name)) { + con = t; + break; + } + + if (con) + list_del(&con->node); + + mutex_unlock(&con_mutex); + + if (!con) + return; + + list_for_each_entry(chan, &con->channels, node) + ipc_free_channel((void *)chan); + + if (ipc->txdone_poll) + del_timer_sync(&con->poll); + + kfree(con); +} +EXPORT_SYMBOL(ipc_links_unregister); From 73998149a13097fa767a8cb82b3b6ea28fdc1ad2 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 29 Jul 2013 19:37:09 +0800 Subject: [PATCH 138/201] FogBugz #141478: Use "linux,mailbox-name" from DTS Uses "linux,mailbox-name" from DTS. Sopc2dts tool already support to add this property automatically. Signed-off-by: Ley Foon Tan --- .../devicetree/bindings/mailbox/mailbox-altera.txt | 4 +++- drivers/mailbox/mailbox-altera.c | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mailbox/mailbox-altera.txt b/Documentation/devicetree/bindings/mailbox/mailbox-altera.txt index dbfcfb6bfb7d7..4227e70cf73da 100644 --- a/Documentation/devicetree/bindings/mailbox/mailbox-altera.txt +++ b/Documentation/devicetree/bindings/mailbox/mailbox-altera.txt @@ -4,13 +4,14 @@ Required properties: - compatible : "altr,mailbox-1.0". - reg : physical base address of the mailbox and length of memory mapped region. +- linux,mailbox-name : Mailbox instance name Optional properties: - interrupt-parent : interrupt source phandle. - interrupts : interrupt number. The interrupt specifier format depends on the interrupt controller parent. -The name of mailbox device node must be unique. This name will be used as +The property of "linux,mailbox-name" must be unique. This name will be used as controller name in driver to identify the controller and the mailbox client will use this name when requesting a mailbox channel. @@ -20,6 +21,7 @@ Example: reg = <0x100 0x8>; interrupt-parent = < &gic_0 >; interrupts = <5>; + linux,mailbox-name = "mailbox0"; }; Example of mailbox's client node that includes mailbox phandle. diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c index b23113c383618..c59561e2778ce 100644 --- a/drivers/mailbox/mailbox-altera.c +++ b/drivers/mailbox/mailbox-altera.c @@ -302,6 +302,7 @@ static int altera_mbox_probe(struct platform_device *pdev) struct resource *regs; struct device_node *np = pdev->dev.of_node; int ret; + const char *mbox_name = NULL; mbox = devm_kzalloc(&pdev->dev, sizeof(struct altera_mbox), GFP_KERNEL); @@ -336,8 +337,14 @@ static int altera_mbox_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "Length of mailbox controller name is greater than %d\n", sizeof(mbox->ipc_con.controller_name)); + ret = of_property_read_string(np, "linux,mailbox-name", &mbox_name); + if (ret) { + dev_err(&pdev->dev, "Missing linux,mailbox-name property in device tree\n"); + goto err; + } + snprintf(mbox->ipc_con.controller_name, - sizeof(mbox->ipc_con.controller_name), "%s", np->name); + sizeof(mbox->ipc_con.controller_name), "%s", mbox_name); dev_info(&pdev->dev, "Mailbox controller name is %s\n", mbox->ipc_con.controller_name); From 168e7b667a714728cd68d863c795d7f848c7ac48 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 7 Aug 2013 10:04:23 +0800 Subject: [PATCH 139/201] FogBugz #144109: Remove extra lines from license header Follow approved GPLv2 header standard. Signed-off-by: Ley Foon Tan --- drivers/mailbox/mailbox-altera.c | 2 -- drivers/misc/altera_hwmutex.c | 2 -- include/linux/altera_hwmutex.h | 2 -- 3 files changed, 6 deletions(-) diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c index c59561e2778ce..ddd16a5a8f21a 100644 --- a/drivers/mailbox/mailbox-altera.c +++ b/drivers/mailbox/mailbox-altera.c @@ -12,8 +12,6 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, */ #include diff --git a/drivers/misc/altera_hwmutex.c b/drivers/misc/altera_hwmutex.c index 43e4e564e9f17..3171db55c8979 100644 --- a/drivers/misc/altera_hwmutex.c +++ b/drivers/misc/altera_hwmutex.c @@ -12,8 +12,6 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, */ #include diff --git a/include/linux/altera_hwmutex.h b/include/linux/altera_hwmutex.h index c3960599ac051..166502b379f6e 100644 --- a/include/linux/altera_hwmutex.h +++ b/include/linux/altera_hwmutex.h @@ -12,8 +12,6 @@ * * You should have received a copy of the GNU General Public License along with * this program. If not, see . - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, */ #ifndef _ALTERA_MUTEX_H #define _ALTERA_MUTEX_H From f1c32ba238fc8c8d5a0a66924527d2d119c09a53 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 3 Apr 2014 14:02:22 +0800 Subject: [PATCH 140/201] FogBugz #194497: mailbox: Fixed compilation warning Compilation warning when compiling for x86_64. config: make ARCH=x86_64 allmodconfig drivers/mailbox/mailbox-altera.c:337:3: warning: format '%d' expects argument of type 'int', but argument 3 has type 'long unsigned int' [-Wformat=] v2: - Change to use %zu Signed-off-by: Ley Foon Tan --- drivers/mailbox/mailbox-altera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c index ddd16a5a8f21a..5a055c1163edb 100644 --- a/drivers/mailbox/mailbox-altera.c +++ b/drivers/mailbox/mailbox-altera.c @@ -332,7 +332,7 @@ static int altera_mbox_probe(struct platform_device *pdev) mbox->ipc_con.ops = &altera_mbox_ops; if ((strlen(np->name) + 1) > sizeof(mbox->ipc_con.controller_name)) - dev_warn(&pdev->dev, "Length of mailbox controller name is greater than %d\n", + dev_warn(&pdev->dev, "Length of mailbox controller name is greater than %zu\n", sizeof(mbox->ipc_con.controller_name)); ret = of_property_read_string(np, "linux,mailbox-name", &mbox_name); From 242fd3b1bffde53f56ea39b308edf9ef781d2631 Mon Sep 17 00:00:00 2001 From: cnphoon Date: Fri, 21 Feb 2014 17:39:05 +0800 Subject: [PATCH 141/201] FogBugz #178225: Add Altera interrupt latency counter driver Adding Altera interrupt latency counter driver support. This driver works together with the Altera interrupt latency driver soft IP to measure the time from the interrupt being asserted to the execution of the interrupt service routine. This driver and soft ip supports for both edge and level interrupt. V2: - Update fifo-depth property name in device tree - Update binding document to add sysfs path and use case - Update includes header in alphabetical order - Remove global variables - Rename kfifo stucts naming to avoid confusion - Validate offset return value - Changing read and write register functions - Update on misc coding styles and format V3: - Update coding styles - Update irq_request function - Changing print out to dev_dbg() in ISR - Fix ISR return values V4: - Remove stray line Signed-off-by: Phoon Chee Nouk --- .../misc/altera-interrupt-latency-counter.txt | 49 +++ drivers/misc/altera_ilc.c | 299 ++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/altera-interrupt-latency-counter.txt create mode 100644 drivers/misc/altera_ilc.c diff --git a/Documentation/devicetree/bindings/misc/altera-interrupt-latency-counter.txt b/Documentation/devicetree/bindings/misc/altera-interrupt-latency-counter.txt new file mode 100644 index 0000000000000..09f6820576163 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/altera-interrupt-latency-counter.txt @@ -0,0 +1,49 @@ +Altera Interrupt Latency Counter soft IP +Altera Interrupt Latency Counter IP core driver provides a sysfs interface +for user to obtain interrupt latency values from Altera Interrupt Latency +Counter soft IP. + +The sysfs interface is located at path, +/sys/bus/platform/devices/{addr}.ilc/ilc_data/{int_#} +with +- {addr} = the base address of the soft ip +- {int_#} = the interrupt number + +Example use case: +# cat /sys/bus/platform/devices/c0010000.ilc/ilc_data/40 + +Required properties: +- compatible : + - "altr,ilc-1.0" +- reg : + - physical base address of the soft ip and length of memory mapped region +- interrupt-parent : + - interrupt source phandle similiar to the interrupt source node +- interrupts : + -interrupt number. The interrupt specifier format depends on the interrupt + controller parent + +Altera specific properties: +- altr,sw-fifo-depth : + - define software fifo depth needed to record latency values + +Note: +- For edge triggered interrupt, the order of loading the ILC driver relative + to driver of the actual interrupt source affects the meaning of the ILC + values. If the ILC driver is loaded first, then the count values represent + the time to the start of the interrupt handler of the of the interrupt source. + If the order is switched, then the counts represent the time to finish the + interrupt handler for the interrupt source. + +- The driver for the interrupt source must be changed to request a shared irq. + +Example: + interrupt_latency_counter_0: intc@0x10000000 { + compatible = "altr,ilc-1.0"; + reg = <0x10000000 0x00000100>; + interrupt-parent = < &interrupt_parent >; + interrupts = < 0 1 4 >; + altr,sw-fifo-depth = < 32 >; + }; + + diff --git a/drivers/misc/altera_ilc.c b/drivers/misc/altera_ilc.c new file mode 100644 index 0000000000000..5aa98cf752555 --- /dev/null +++ b/drivers/misc/altera_ilc.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2014 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_ilc" +#define CTRL_REG 0x80 +#define FREQ_REG 0x84 +#define STP_REG 0x88 +#define VLD_REG 0x8C +#define ILC_MAX_PORTS 32 +#define ILC_FIFO_DEFAULT 32 +#define ILC_ENABLE 0x01 +#define CHAR_SIZE 10 +#define POLL_INTERVAL 1 +#define GET_PORT_COUNT(_val) ((_val & 0x7C) >> 2) +#define GET_VLD_BIT(_val, _offset) (((_val) >> _offset) & 0x1) + +struct altera_ilc { + struct platform_device *pdev; + void __iomem *regs; + unsigned int port_count; + unsigned int irq; + unsigned int channel_offset; + unsigned int interrupt_channels[ILC_MAX_PORTS]; + struct kfifo kfifos[ILC_MAX_PORTS]; + struct device_attribute dev_attr[ILC_MAX_PORTS]; + struct delayed_work ilc_work; + char sysfs[ILC_MAX_PORTS][CHAR_SIZE]; + u32 fifo_depth; +}; + +static int ilc_irq_lookup(struct altera_ilc *ilc, int irq) +{ + int i; + for (i = 0; i < ilc->port_count; i++) { + if (irq == platform_get_irq(ilc->pdev, i)) + return i; + } + return -EPERM; +} + +static ssize_t ilc_show_counter(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i, id, fifo_len; + unsigned int fifo_buf[ILC_MAX_PORTS]; + char temp[10]; + struct altera_ilc *ilc = dev_get_drvdata(dev); + + fifo_len = 0; + ret = kstrtouint(attr->attr.name, 0, &id); + + for (i = 0; i < ilc->port_count; i++) { + if (id == (ilc->interrupt_channels[i])) { + /*Check for kfifo length*/ + fifo_len = kfifo_len(&ilc->kfifos[i]) + /sizeof(unsigned int); + if (fifo_len <= 0) { + dev_info(&ilc->pdev->dev, "Fifo for interrupt %s is empty\n", + attr->attr.name); + return 0; + } + /*Read from kfifo*/ + ret = kfifo_out(&ilc->kfifos[i], &fifo_buf, + kfifo_len(&ilc->kfifos[i])); + } + } + + for (i = 0; i < fifo_len; i++) { + sprintf(temp, "%u\n", fifo_buf[i]); + strcat(buf, temp); + } + + strcat(buf, "\0"); + + return strlen(buf); +} + +static struct attribute *altera_ilc_attrs[ILC_MAX_PORTS]; + +struct attribute_group altera_ilc_attr_group = { + .name = "ilc_data", + .attrs = altera_ilc_attrs, +}; + +static void ilc_work(struct work_struct *work) +{ + unsigned int ilc_value, ret, offset, stp_reg; + struct altera_ilc *ilc = + container_of(work, struct altera_ilc, ilc_work.work); + + offset = ilc_irq_lookup(ilc, ilc->irq); + if (offset < 0) { + dev_err(&ilc->pdev->dev, "Unable to lookup irq number\n"); + return; + } + + if (GET_VLD_BIT(readl(ilc->regs + VLD_REG), offset)) { + /*Read counter register*/ + ilc_value = readl(ilc->regs + (offset) * 4); + + /*Putting value into kfifo*/ + ret = kfifo_in((&ilc->kfifos[offset]), + (unsigned int *)&ilc_value, sizeof(ilc_value)); + + /*Clearing stop register*/ + stp_reg = readl(ilc->regs + STP_REG); + writel((!(0x1 << offset))&stp_reg, ilc->regs + STP_REG); + + return; + } + + /*Start workqueue to poll data valid*/ + schedule_delayed_work(&ilc->ilc_work, msecs_to_jiffies(POLL_INTERVAL)); +} + +static irqreturn_t ilc_interrupt_handler(int irq, void *p) +{ + unsigned int offset, stp_reg; + + struct altera_ilc *ilc = (struct altera_ilc *)p; + + /*Update ILC struct*/ + ilc->irq = irq; + + dev_dbg(&ilc->pdev->dev, "Interrupt %u triggered\n", + ilc->irq); + + offset = ilc_irq_lookup(ilc, irq); + if (offset < 0) { + dev_err(&ilc->pdev->dev, "Unable to lookup irq number\n"); + return IRQ_RETVAL(IRQ_NONE); + } + + /*Setting stop register*/ + stp_reg = readl(ilc->regs + STP_REG); + writel((0x1 << offset)|stp_reg, ilc->regs + STP_REG); + + /*Start workqueue to poll data valid*/ + schedule_delayed_work(&ilc->ilc_work, 0); + + return IRQ_RETVAL(IRQ_NONE); +} + +static int altera_ilc_probe(struct platform_device *pdev) +{ + struct altera_ilc *ilc; + struct resource *regs; + struct device_node *np = pdev->dev.of_node; + int ret, i; + + ilc = devm_kzalloc(&pdev->dev, sizeof(struct altera_ilc), + GFP_KERNEL); + if (!ilc) + return -ENOMEM; + + ilc->pdev = pdev; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + ilc->regs = devm_request_and_ioremap(&pdev->dev, regs); + if (!ilc->regs) + return -EADDRNOTAVAIL; + + ilc->port_count = GET_PORT_COUNT(readl(ilc->regs + CTRL_REG)); + if (ilc->port_count <= 0) { + dev_warn(&pdev->dev, "No interrupt connected to ILC\n"); + return -EPERM; + } + + /*Check for fifo depth*/ + ret = of_property_read_u32(np, "altr,sw-fifo-depth", + &(ilc->fifo_depth)); + if (ret) { + dev_warn(&pdev->dev, "Fifo depth undefined\n"); + dev_warn(&pdev->dev, "Setting fifo depth to default value (32)\n"); + ilc->fifo_depth = ILC_FIFO_DEFAULT; + } + + /*Initialize Kfifo*/ + for (i = 0; i < ilc->port_count; i++) { + ret = kfifo_alloc(&ilc->kfifos[i], (ilc->fifo_depth * + sizeof(unsigned int)), GFP_KERNEL); + if (ret) { + dev_err(&pdev->dev, "Kfifo failed to initialize\n"); + return ret; + } + } + + /*Register each of the IRQs*/ + for (i = 0; i < ilc->port_count; i++) { + ilc->interrupt_channels[i] = platform_get_irq(pdev, i); + + ret = devm_request_irq(&pdev->dev, (ilc->interrupt_channels[i]), + ilc_interrupt_handler, IRQF_SHARED, "ilc_0", + (void *)(ilc)); + + if (ret < 0) + dev_warn(&pdev->dev, "Failed to register interrupt handler"); + } + + /*Setup sysfs interface*/ + for (i = 0; (i < ilc->port_count); i++) { + sprintf(ilc->sysfs[i], "%d", (ilc->interrupt_channels[i])); + ilc->dev_attr[i].attr.name = ilc->sysfs[i]; + ilc->dev_attr[i].attr.mode = S_IRUGO; + ilc->dev_attr[i].show = ilc_show_counter; + altera_ilc_attrs[i] = &ilc->dev_attr[i].attr; + altera_ilc_attrs[i+1] = NULL; + } + ret = sysfs_create_group(&pdev->dev.kobj, &altera_ilc_attr_group); + + /*Initialize workqueue*/ + INIT_DELAYED_WORK(&ilc->ilc_work, ilc_work); + + /*Global enable ILC softIP*/ + writel(ILC_ENABLE, ilc->regs + CTRL_REG); + + platform_set_drvdata(pdev, ilc); + + dev_info(&pdev->dev, "Driver successfully loaded\n"); + + return 0; +} + +static int altera_ilc_remove(struct platform_device *pdev) +{ + int i; + struct altera_ilc *ilc = platform_get_drvdata(pdev); + + /*Remove sysfs interface*/ + sysfs_remove_group(&pdev->dev.kobj, &altera_ilc_attr_group); + + /*Free up kfifo memory*/ + for (i = 0; i < ilc->port_count; i++) + kfifo_free(&ilc->kfifos[i]); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id altera_ilc_match[] = { + { .compatible = "altr,ilc-1.0" }, + { /* Sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, altera_ilc_match); + +static struct platform_driver altera_ilc_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_ilc_match), + }, + .remove = altera_ilc_remove, +}; + +static int __init altera_ilc_init(void) +{ + return platform_driver_probe(&altera_ilc_platform_driver, + altera_ilc_probe); +} + +static void __exit altera_ilc_exit(void) +{ + platform_driver_unregister(&altera_ilc_platform_driver); +} + +module_init(altera_ilc_init); +module_exit(altera_ilc_exit); + +MODULE_AUTHOR("Chee Nouk Phoon "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Altera Interrupt Latency Counter Driver"); +MODULE_ALIAS("platform:" DRV_NAME); From d560b5196d0ac5dd01a1efa74500d88f974c99ac Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Tue, 5 Feb 2013 13:23:32 +0800 Subject: [PATCH 142/201] FogBugz #97184: Add Altera SYSID soft IP driver This is to add Altera System ID (sysid) soft IP driver. Usage: cat /sys/bus/platform/devices/[addr].sysid/sysid/id cat /sys/bus/platform/devices/[addr].sysid/sysid/timestamp Signed-off-by: Ley Foon Tan --- .../devicetree/bindings/misc/altera_sysid.txt | 18 +++ drivers/misc/altera_sysid.c | 142 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/altera_sysid.txt create mode 100644 drivers/misc/altera_sysid.c diff --git a/Documentation/devicetree/bindings/misc/altera_sysid.txt b/Documentation/devicetree/bindings/misc/altera_sysid.txt new file mode 100644 index 0000000000000..463dc152a1ecd --- /dev/null +++ b/Documentation/devicetree/bindings/misc/altera_sysid.txt @@ -0,0 +1,18 @@ +Altera Sysid IP core driver + +Required properties: +- compatible: altr,sysid-1.0 + +Optional properties: +- id: A unique 32-bit value that is based on the contents of the system. +- timestamp: A unique 32-bit value that is based on the system generation time. + +Example: + +sysid_qsys: sysid@0x10000 { + compatible = "altr,sysid-1.0"; + reg = < 0x10000 0x00000008 >; + id = < 1 >; + timestamp = < 1359538782 >; +}; + diff --git a/drivers/misc/altera_sysid.c b/drivers/misc/altera_sysid.c new file mode 100644 index 0000000000000..7472a4bf458df --- /dev/null +++ b/drivers/misc/altera_sysid.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2013 Altera Corporation + * + * 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 . + * + * Credit: + * Walter Goossens + */ + + +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_sysid" + +struct altera_sysid { + void __iomem *regs; +}; + +/* System ID Registers*/ +#define SYSID_REG_ID (0x0) +#define SYSID_REG_TIMESTAMP (0x4) + +static ssize_t altera_sysid_show_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct altera_sysid *sysid = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", readl(sysid->regs + SYSID_REG_ID)); +} + +static ssize_t altera_sysid_show_timestamp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg; + struct tm timestamp; + struct altera_sysid *sysid = dev_get_drvdata(dev); + + reg = readl(sysid->regs + SYSID_REG_TIMESTAMP); + + time_to_tm(reg, 0, ×tamp); + + return sprintf(buf, "%u (%u-%u-%u %u:%u:%u UTC)\n", reg, + (unsigned int)(timestamp.tm_year + 1900), + timestamp.tm_mon + 1, timestamp.tm_mday, timestamp.tm_hour, + timestamp.tm_min, timestamp.tm_sec); +} + +static DEVICE_ATTR(id, S_IRUGO, altera_sysid_show_id, NULL); +static DEVICE_ATTR(timestamp, S_IRUGO, altera_sysid_show_timestamp, NULL); + +static struct attribute *altera_sysid_attrs[] = { + &dev_attr_id.attr, + &dev_attr_timestamp.attr, + NULL, +}; + +struct attribute_group altera_sysid_attr_group = { + .name = "sysid", + .attrs = altera_sysid_attrs, +}; + +static int altera_sysid_probe(struct platform_device *pdev) +{ + struct altera_sysid *sysid; + struct resource *regs; + + sysid = devm_kzalloc(&pdev->dev, sizeof(struct altera_sysid), + GFP_KERNEL); + if (!sysid) + return -ENOMEM; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + sysid->regs = devm_request_and_ioremap(&pdev->dev, regs); + if (!sysid->regs) + return -ENOMEM; + + platform_set_drvdata(pdev, sysid); + + return sysfs_create_group(&pdev->dev.kobj, &altera_sysid_attr_group); +} + +static int __exit altera_sysid_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &altera_sysid_attr_group); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id altera_sysid_match[] = { + { .compatible = "altr,sysid-1.0" }, + { /* Sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, altera_sysid_match); + +static struct platform_driver altera_sysid_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = altera_sysid_match, + }, + .remove = __exit_p(altera_sysid_remove), +}; + +static int __init altera_sysid_init(void) +{ + return platform_driver_probe(&altera_sysid_platform_driver, + altera_sysid_probe); +} + +static void __exit altera_sysid_exit(void) +{ + platform_driver_unregister(&altera_sysid_platform_driver); +} + +module_init(altera_sysid_init); +module_exit(altera_sysid_exit); + +MODULE_AUTHOR("Ley Foon Tan"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Altera System ID driver"); +MODULE_ALIAS("platform:" DRV_NAME); From 3455b8de9dede63db72610784ddc37e52456dc19 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 25 Mar 2013 16:14:30 +0800 Subject: [PATCH 143/201] FogBugz #109717: Removed unused sysid driver from drivers/misc. Sysid feature is moved to arch/arm/mach-socfpga/socfpga.c (see Fogbugz 106327). Signed-off-by: Ley Foon Tan --- .../devicetree/bindings/misc/altera_sysid.txt | 18 --- drivers/misc/altera_sysid.c | 142 ------------------ 2 files changed, 160 deletions(-) delete mode 100644 Documentation/devicetree/bindings/misc/altera_sysid.txt delete mode 100644 drivers/misc/altera_sysid.c diff --git a/Documentation/devicetree/bindings/misc/altera_sysid.txt b/Documentation/devicetree/bindings/misc/altera_sysid.txt deleted file mode 100644 index 463dc152a1ecd..0000000000000 --- a/Documentation/devicetree/bindings/misc/altera_sysid.txt +++ /dev/null @@ -1,18 +0,0 @@ -Altera Sysid IP core driver - -Required properties: -- compatible: altr,sysid-1.0 - -Optional properties: -- id: A unique 32-bit value that is based on the contents of the system. -- timestamp: A unique 32-bit value that is based on the system generation time. - -Example: - -sysid_qsys: sysid@0x10000 { - compatible = "altr,sysid-1.0"; - reg = < 0x10000 0x00000008 >; - id = < 1 >; - timestamp = < 1359538782 >; -}; - diff --git a/drivers/misc/altera_sysid.c b/drivers/misc/altera_sysid.c deleted file mode 100644 index 7472a4bf458df..0000000000000 --- a/drivers/misc/altera_sysid.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2013 Altera Corporation - * - * 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 . - * - * Credit: - * Walter Goossens - */ - - -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "altera_sysid" - -struct altera_sysid { - void __iomem *regs; -}; - -/* System ID Registers*/ -#define SYSID_REG_ID (0x0) -#define SYSID_REG_TIMESTAMP (0x4) - -static ssize_t altera_sysid_show_id(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct altera_sysid *sysid = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", readl(sysid->regs + SYSID_REG_ID)); -} - -static ssize_t altera_sysid_show_timestamp(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned int reg; - struct tm timestamp; - struct altera_sysid *sysid = dev_get_drvdata(dev); - - reg = readl(sysid->regs + SYSID_REG_TIMESTAMP); - - time_to_tm(reg, 0, ×tamp); - - return sprintf(buf, "%u (%u-%u-%u %u:%u:%u UTC)\n", reg, - (unsigned int)(timestamp.tm_year + 1900), - timestamp.tm_mon + 1, timestamp.tm_mday, timestamp.tm_hour, - timestamp.tm_min, timestamp.tm_sec); -} - -static DEVICE_ATTR(id, S_IRUGO, altera_sysid_show_id, NULL); -static DEVICE_ATTR(timestamp, S_IRUGO, altera_sysid_show_timestamp, NULL); - -static struct attribute *altera_sysid_attrs[] = { - &dev_attr_id.attr, - &dev_attr_timestamp.attr, - NULL, -}; - -struct attribute_group altera_sysid_attr_group = { - .name = "sysid", - .attrs = altera_sysid_attrs, -}; - -static int altera_sysid_probe(struct platform_device *pdev) -{ - struct altera_sysid *sysid; - struct resource *regs; - - sysid = devm_kzalloc(&pdev->dev, sizeof(struct altera_sysid), - GFP_KERNEL); - if (!sysid) - return -ENOMEM; - - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) - return -ENXIO; - - sysid->regs = devm_request_and_ioremap(&pdev->dev, regs); - if (!sysid->regs) - return -ENOMEM; - - platform_set_drvdata(pdev, sysid); - - return sysfs_create_group(&pdev->dev.kobj, &altera_sysid_attr_group); -} - -static int __exit altera_sysid_remove(struct platform_device *pdev) -{ - sysfs_remove_group(&pdev->dev.kobj, &altera_sysid_attr_group); - - platform_set_drvdata(pdev, NULL); - return 0; -} - -static const struct of_device_id altera_sysid_match[] = { - { .compatible = "altr,sysid-1.0" }, - { /* Sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, altera_sysid_match); - -static struct platform_driver altera_sysid_platform_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .of_match_table = altera_sysid_match, - }, - .remove = __exit_p(altera_sysid_remove), -}; - -static int __init altera_sysid_init(void) -{ - return platform_driver_probe(&altera_sysid_platform_driver, - altera_sysid_probe); -} - -static void __exit altera_sysid_exit(void) -{ - platform_driver_unregister(&altera_sysid_platform_driver); -} - -module_init(altera_sysid_init); -module_exit(altera_sysid_exit); - -MODULE_AUTHOR("Ley Foon Tan"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Altera System ID driver"); -MODULE_ALIAS("platform:" DRV_NAME); From ad86502697c0e50d91b6a3325ea013022d6d13a5 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Mon, 12 Aug 2013 11:12:56 +0800 Subject: [PATCH 144/201] FogBugz #143478: drivers/misc: Move sysid from arch to drivers Add sysid driver. Usage: cat /sys/bus/platform/devices/[addr.sysid]/sysid/id cat /sys/bus/platform/devices/[addr.sysid]/sysid/timestamp v2: - Updated license header - Removed ID and timestamp from documentation v3: - Removed ID and timestamp from optional properties in documentation Signed-off-by: Ley Foon Tan --- .../devicetree/bindings/misc/altera_sysid.txt | 11 ++ drivers/misc/Kconfig | 17 +-- drivers/misc/Makefile | 1 + drivers/misc/altera_sysid.c | 141 ++++++++++++++++++ 4 files changed, 154 insertions(+), 16 deletions(-) create mode 100644 Documentation/devicetree/bindings/misc/altera_sysid.txt create mode 100644 drivers/misc/altera_sysid.c diff --git a/Documentation/devicetree/bindings/misc/altera_sysid.txt b/Documentation/devicetree/bindings/misc/altera_sysid.txt new file mode 100644 index 0000000000000..c3bbd576b74b6 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/altera_sysid.txt @@ -0,0 +1,11 @@ +Altera Sysid IP core driver + +Required properties: +- compatible: altr,sysid-1.0 + +Example: + +sysid_qsys: sysid@0x10000 { + compatible = "altr,sysid-1.0"; + reg = < 0x10000 0x00000008 >; +}; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3952d9e72fe8e..2477a74e7fce9 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -522,25 +522,10 @@ config ALTERA_HWMUTEX This option enables device driver support for Altera Hardware Mutex. Say Y here if you want to use the Altera hardware mutex support. -config VEXPRESS_SYSCFG - bool "Versatile Express System Configuration driver" - depends on VEXPRESS_CONFIG - default y - help - ARM Ltd. Versatile Express uses specialised platform configuration - bus. System Configuration interface is one of the possible means - of generating transactions on this bus. - -config ALTERA_HWMUTEX - tristate "Altera Hardware Mutex" - help - This option enables device driver support for Altera Hardware Mutex. - Say Y here if you want to use the Altera hardware mutex support. - config ALTERA_SYSID tristate "Altera System ID" help - This enables Altera System ID soft core driver. + This enables Altera System ID soft core driver. source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c5512192fc2ac..444580cf8d978 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -50,6 +50,7 @@ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) += altera-stapl/ obj-$(CONFIG_ALTERA_HWMUTEX) += altera_hwmutex.o +obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o diff --git a/drivers/misc/altera_sysid.c b/drivers/misc/altera_sysid.c new file mode 100644 index 0000000000000..78a722af6f397 --- /dev/null +++ b/drivers/misc/altera_sysid.c @@ -0,0 +1,141 @@ +/* + * Copyright Altera Corporation (C) 2013. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 . + * + * Credit: + * Walter Goossens + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_sysid" + +struct altera_sysid { + void __iomem *regs; +}; + +/* System ID Registers*/ +#define SYSID_REG_ID (0x0) +#define SYSID_REG_TIMESTAMP (0x4) + +static ssize_t altera_sysid_show_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct altera_sysid *sysid = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", readl(sysid->regs + SYSID_REG_ID)); +} + +static ssize_t altera_sysid_show_timestamp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int reg; + struct tm timestamp; + struct altera_sysid *sysid = dev_get_drvdata(dev); + + reg = readl(sysid->regs + SYSID_REG_TIMESTAMP); + + time_to_tm(reg, 0, ×tamp); + + return sprintf(buf, "%u (%u-%u-%u %u:%u:%u UTC)\n", reg, + (unsigned int)(timestamp.tm_year + 1900), + timestamp.tm_mon + 1, timestamp.tm_mday, timestamp.tm_hour, + timestamp.tm_min, timestamp.tm_sec); +} + +static DEVICE_ATTR(id, S_IRUGO, altera_sysid_show_id, NULL); +static DEVICE_ATTR(timestamp, S_IRUGO, altera_sysid_show_timestamp, NULL); + +static struct attribute *altera_sysid_attrs[] = { + &dev_attr_id.attr, + &dev_attr_timestamp.attr, + NULL, +}; + +struct attribute_group altera_sysid_attr_group = { + .name = "sysid", + .attrs = altera_sysid_attrs, +}; + +static int altera_sysid_probe(struct platform_device *pdev) +{ + struct altera_sysid *sysid; + struct resource *regs; + + sysid = devm_kzalloc(&pdev->dev, sizeof(struct altera_sysid), + GFP_KERNEL); + if (!sysid) + return -ENOMEM; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + sysid->regs = devm_request_and_ioremap(&pdev->dev, regs); + if (!sysid->regs) + return -ENOMEM; + + platform_set_drvdata(pdev, sysid); + + return sysfs_create_group(&pdev->dev.kobj, &altera_sysid_attr_group); +} + +static int altera_sysid_remove(struct platform_device *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &altera_sysid_attr_group); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id altera_sysid_match[] = { + { .compatible = "altr,sysid-1.0" }, + { /* Sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, altera_sysid_match); + +static struct platform_driver altera_sysid_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(altera_sysid_match), + }, + .remove = altera_sysid_remove, +}; + +static int __init altera_sysid_init(void) +{ + return platform_driver_probe(&altera_sysid_platform_driver, + altera_sysid_probe); +} + +static void __exit altera_sysid_exit(void) +{ + platform_driver_unregister(&altera_sysid_platform_driver); +} + +module_init(altera_sysid_init); +module_exit(altera_sysid_exit); + +MODULE_AUTHOR("Ley Foon Tan "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Altera System ID driver"); +MODULE_ALIAS("platform:" DRV_NAME); From 9c8b5ba56aa763b50fac383a7c620f020afa1feb Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 4 Aug 2014 16:36:47 -0500 Subject: [PATCH 145/201] Revert "FogBugz #111740: [PATCH] Integrates Nios II kernel (FDT)" This reverts commit aecabdb990d138237d19e1ac13f3ad37176a4917. --- drivers/of/fdt.c | 187 ----------------------------------------- include/linux/of_fdt.h | 12 --- 2 files changed, 199 deletions(-) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 8b140124d8692..9aa012e6ea0a6 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -921,193 +921,6 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, /* break now */ return 1; } -/** - * flat_dt_translate_address - Translate an address using the ranges property - * - * This function converts address from "node address-space" to "parent address- - * space" - */ -static int __init flat_dt_translate_address(unsigned long node, - unsigned long parent, u64 *address) -{ - unsigned long size = 0; - __be32 *prop; - __be32 *ranges; - int size_cells = 0; - int addr_cells = 0; - int paddr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT; - - ranges = of_get_flat_dt_prop(node, "ranges", &size); - - if (!ranges) { - pr_warn("Address cannot be translated\n"); - return -EINVAL; - } - - if (!size) { - pr_debug("No translation possible/necessary\n"); - return 0; - } - - prop = of_get_flat_dt_prop(node, "#size-cells", NULL); - if (!prop) - return -EINVAL; - size_cells = be32_to_cpup(prop); - - prop = of_get_flat_dt_prop(node, "#address-cells", NULL); - if (!prop) - return -EINVAL; - addr_cells = be32_to_cpup(prop); - - if (parent) { - prop = of_get_flat_dt_prop(parent, "#address-cells", NULL); - if (prop) - paddr_cells = be32_to_cpup(prop); - } - if ((addr_cells <= 0) || (size_cells <= 0) || - (addr_cells > 2) || (size_cells > 2) || (paddr_cells > 2)) { - pr_warn("Translation not possible in fdt. Invalid address.\n"); - *address = 0; - return -1; - } - - while (size > 0) { - u64 from, to, tsize; - from = be32_to_cpup(ranges++); - size -= 4; - if (addr_cells == 2) { - from += (((u64)be32_to_cpup(ranges++)) << 32); - size -= 4; - } - to = be32_to_cpup(ranges++); - size -= 4; - if (paddr_cells == 2) { - to += (((u64)be32_to_cpup(ranges++)) << 32); - size -= 4; - } - tsize = be32_to_cpup(ranges++); - size -= 4; - if (size_cells == 2) { - tsize += (((u64)be32_to_cpup(ranges++)) << 32); - size -= 4; - } - pr_debug(" From %llX To %llX Size %llX\n", from, to, tsize); - if ((*address >= from) && (*address < (from + tsize))) - *address += (to - from); - } - return 1; -} - -static int __init of_scan_flat_dt_ranges(unsigned long *pnode, - unsigned long parent, unsigned long target, - u64 *address, int ignore) -{ - int rc = 0; - int depth = -1; - char *pathp; - unsigned long p = *pnode; - do { - u32 tag = be32_to_cpup((__be32 *)p); - - p += 4; - if (tag == OF_DT_END_NODE) { - if (depth--) - break; - else - continue; - } - if (tag == OF_DT_NOP) - continue; - if (tag == OF_DT_END) - break; - if (tag == OF_DT_PROP) { - u32 sz = be32_to_cpup((__be32 *)p); - p += 8; - if (be32_to_cpu(initial_boot_params->version) < 0x10) - p = ALIGN(p, sz >= 8 ? 8 : 4); - p += sz; - p = ALIGN(p, 4); - continue; - } - if (tag != OF_DT_BEGIN_NODE) { - pr_err("Invalid tag %x in flat device tree!\n", tag); - return -EINVAL; - } - pathp = (char *)p; - p = ALIGN(p + strlen(pathp) + 1, 4); - if ((*pathp) == '/') { - char *lp, *np; - for (lp = NULL, np = pathp; *np; np++) - if ((*np) == '/') - lp = np+1; - if (lp != NULL) - pathp = lp; - } - if ((ignore == 0) && (p == target)) { - rc = 1; - ignore++; - pr_debug("Found target. Start address translation\n"); - } - if (depth) { - int res; - *pnode = p; - res = of_scan_flat_dt_ranges(pnode, p, target, - address, ignore); - if (res < 0) { - /* Something bad happened */ - return -1; - } else if (res > 0) { - /* Something gooed happened. Translate. */ - rc++; - pr_debug("translate %08lX %s\n", p, pathp); - if (flat_dt_translate_address(p, parent, - address) < 0) - return -1; - } - p = *pnode; - } else - depth++; - } while (1); - *pnode = p; - return rc; -} -/** - * of_get_flat_dt_address - get address from a node - * @node: targeted node - * - * Returns address or -1 if error. - */ -__be32 __init of_get_flat_dt_address(unsigned long node) -{ - u32 *addr; - - addr = of_get_flat_dt_prop(node, "reg", NULL); - if (!addr) - return -1; - - return be32_to_cpup(addr); -} - -/** - * of_get_flat_dt_translate_address - get translated address from a node - * @node: targeted node - * - * Returns translated address or OF_BAD_ADDR if error. - */ -u64 __init of_get_flat_dt_translate_address(unsigned long node) -{ - unsigned long p = of_get_flat_dt_root(); - u64 addr64; - int rc; - - addr64 = (u64) of_get_flat_dt_address(node); - if (addr64 == -1) - return OF_BAD_ADDR; - rc = of_scan_flat_dt_ranges(&p, 0, node, &addr64, 0); - if (rc > 0) - return addr64; - return OF_BAD_ADDR; -} #ifdef CONFIG_HAVE_MEMBLOCK void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 18d1357c42236..0ff360d5b3b30 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -68,18 +68,6 @@ extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); extern u64 dt_mem_next_cell(int s, const __be32 **cellp); -extern __be32 __init of_get_flat_dt_address(unsigned long node); -extern u64 __init of_get_flat_dt_translate_address(unsigned long node); - -/* - * If BLK_DEV_INITRD, the fdt early init code will call this function, - * to be provided by the arch code. start and end are specified as - * physical addresses. - */ -#ifdef CONFIG_BLK_DEV_INITRD -extern void early_init_dt_setup_initrd_arch(u64 start, u64 end); -#endif - /* Early flat tree scan hooks */ extern int early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data); From 343823729754e22a75e02a018627d2fa55c34531 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:05:46 -0500 Subject: [PATCH 146/201] usb: dwc2/gadget: fix phy disable sequence When the driver is removed s3c_hsotg_phy_disable is called three times instead of once. This results in decreasing of the phy reference counter below zero and thus consecutive inserts of the module fails. This patch removes calls to s3c_hsotg_phy_disable from s3c_hsotg_remove and s3c_hsotg_udc_stop. s3c_hsotg_udc_stop is called from udc-core.c only after usb_gadget_disconnect, which in turn calls s3c_hsotg_pullup, which already calls s3c_hsotg_phy_disable. s3c_hsotg_remove must be called only after udc_stop, so there is no point in disabling phy once again there. Signed-off-by: Kamil Debski Signed-off-by: Marek Szyprowski Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index f3c56a2fed5bf..ccef3a7c7da0e 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2898,8 +2898,6 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_phy_disable(hsotg); - if (!driver) hsotg->driver = NULL; @@ -3586,7 +3584,6 @@ static int s3c_hsotg_remove(struct platform_device *pdev) usb_gadget_unregister_driver(hsotg->driver); } - s3c_hsotg_phy_disable(hsotg); if (hsotg->phy) phy_exit(hsotg->phy); clk_disable_unprepare(hsotg->clk); From d4ef555afa9813eff6dc633c6cdda81bbc41a529 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:11:13 -0500 Subject: [PATCH 147/201] usb: dwc2/gadget: fix phy initialization sequence In the Generic PHY Framework a NULL phy is considered to be a valid phy thus the "if (hsotg->phy)" check does not give us the information whether the Generic PHY Framework is used. In addition to the above this patch also removes phy_init from probe and phy_exit from remove. This is not necessary when init/exit is done in the s3c_hsotg_phy_enable/disable functions. Signed-off-by: Kamil Debski Signed-off-by: Marek Szyprowski Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index ccef3a7c7da0e..70eab95ca46ae 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2748,13 +2748,14 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg) dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev); - if (hsotg->phy) { - phy_init(hsotg->phy); - phy_power_on(hsotg->phy); - } else if (hsotg->uphy) + if (hsotg->uphy) usb_phy_init(hsotg->uphy); - else if (hsotg->plat->phy_init) + else if (hsotg->plat && hsotg->plat->phy_init) hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); + else { + phy_init(hsotg->phy); + phy_power_on(hsotg->phy); + } } /** @@ -2768,13 +2769,14 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); - if (hsotg->phy) { - phy_power_off(hsotg->phy); - phy_exit(hsotg->phy); - } else if (hsotg->uphy) + if (hsotg->uphy) usb_phy_shutdown(hsotg->uphy); - else if (hsotg->plat->phy_exit) + else if (hsotg->plat && hsotg->plat->phy_exit) hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); + else { + phy_power_off(hsotg->phy); + phy_exit(hsotg->phy); + } } /** @@ -3489,9 +3491,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) if (hsotg->phy && (phy_get_bus_width(phy) == 8)) hsotg->phyif = GUSBCFG_PHYIF8; - if (hsotg->phy) - phy_init(hsotg->phy); - /* usb phy enable */ s3c_hsotg_phy_enable(hsotg); @@ -3584,8 +3583,6 @@ static int s3c_hsotg_remove(struct platform_device *pdev) usb_gadget_unregister_driver(hsotg->driver); } - if (hsotg->phy) - phy_exit(hsotg->phy); clk_disable_unprepare(hsotg->clk); return 0; From 0fcfdbae800f3d2847c7a1f32a7ee20c6498720d Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:12:41 -0500 Subject: [PATCH 148/201] usb: dwc2/gadget: move phy bus legth initialization This patch moves the part of code that initializes the PHY bus width. This results in simpler code and removes the need to check whether the Generic PHY Framework is used. Signed-off-by: Kamil Debski Signed-off-by: Marek Szyprowski Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 70eab95ca46ae..fc27b4c99eca4 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3395,6 +3395,9 @@ static int s3c_hsotg_probe(struct platform_device *pdev) return -ENOMEM; } + /* Set default UTMI width */ + hsotg->phyif = GUSBCFG_PHYIF16; + /* * Attempt to find a generic PHY, then look for an old style * USB PHY, finally fall back to pdata @@ -3413,8 +3416,15 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->plat = plat; } else hsotg->uphy = uphy; - } else + } else { hsotg->phy = phy; + /* + * If using the generic PHY framework, check if the PHY bus + * width is 8-bit and set the phyif appropriately. + */ + if (phy_get_bus_width(phy) == 8) + hsotg->phyif = GUSBCFG_PHYIF8; + } hsotg->dev = dev; @@ -3481,16 +3491,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) goto err_supplies; } - /* Set default UTMI width */ - hsotg->phyif = GUSBCFG_PHYIF16; - - /* - * If using the generic PHY framework, check if the PHY bus - * width is 8-bit and set the phyif appropriately. - */ - if (hsotg->phy && (phy_get_bus_width(phy) == 8)) - hsotg->phyif = GUSBCFG_PHYIF8; - /* usb phy enable */ s3c_hsotg_phy_enable(hsotg); From be5f5106c817cb1e7d0fb8246938cdc5dc417dd9 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:15:45 -0500 Subject: [PATCH 149/201] usb: dwc2/gadget: Fix comment text Adjust the debug text to the name of the printed variable. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index fc27b4c99eca4..35b4890dfd6f4 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2935,7 +2935,7 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) struct s3c_hsotg *hsotg = to_hsotg(gadget); unsigned long flags = 0; - dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on); + dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on); spin_lock_irqsave(&hsotg->lock, flags); if (is_on) { From 89cb8df3d8954cc53fa0988882d169e91bc56444 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:17:06 -0500 Subject: [PATCH 150/201] usb: dwc2/gadget: hide some not really needed debug messages Some DWC2/s3c-hsotg debug messages are really useless for typical user, so hide them behind dev_dbg(). Signed-off-by: Marek Szyprowski Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 35b4890dfd6f4..95b6dcb61b6b2 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2568,7 +2568,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) u32 epctrl_reg; u32 ctrl; - dev_info(hsotg->dev, "%s(ep %p)\n", __func__, ep); + dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep); if (ep == &hsotg->eps[0].ep) { dev_err(hsotg->dev, "%s: called for ep0\n", __func__); @@ -2626,7 +2626,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) struct s3c_hsotg *hs = hs_ep->parent; unsigned long flags; - dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); + dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); spin_lock_irqsave(&hs->lock, flags); From fae3abc79c495c9edee66fa812c95fe33157a4d2 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:18:31 -0500 Subject: [PATCH 151/201] usb: dwc2/gadget: ensure that all fifos have correct memory buffers Print warning if FIFOs are configured in such a way that they don't fit into the SPRAM available on the s3c hsotg module. Signed-off-by: Marek Szyprowski Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/core.h | 1 + drivers/usb/dwc2/gadget.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 1efd10cc96291..067390e275440 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -194,6 +194,7 @@ struct s3c_hsotg { struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)]; u32 phyif; + int fifo_mem; unsigned int dedicated_fifos:1; unsigned char num_of_eps; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 95b6dcb61b6b2..21d21de9b1349 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -194,6 +194,8 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg) for (ep = 1; ep <= 15; ep++) { val = addr; val |= size << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + size > hsotg->fifo_mem, + "insufficient fifo memory"); addr += size; writel(val, hsotg->regs + DPTXFSIZN(ep)); @@ -3030,19 +3032,22 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, */ static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg) { - u32 cfg2, cfg4; + u32 cfg2, cfg3, cfg4; /* check hardware configuration */ cfg2 = readl(hsotg->regs + 0x48); hsotg->num_of_eps = (cfg2 >> 10) & 0xF; - dev_info(hsotg->dev, "EPs:%d\n", hsotg->num_of_eps); + cfg3 = readl(hsotg->regs + 0x4C); + hsotg->fifo_mem = (cfg3 >> 16); cfg4 = readl(hsotg->regs + 0x50); hsotg->dedicated_fifos = (cfg4 >> 25) & 1; - dev_info(hsotg->dev, "%s fifos\n", - hsotg->dedicated_fifos ? "dedicated" : "shared"); + dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n", + hsotg->num_of_eps, + hsotg->dedicated_fifos ? "dedicated" : "shared", + hsotg->fifo_mem); } /** @@ -3495,8 +3500,8 @@ static int s3c_hsotg_probe(struct platform_device *pdev) s3c_hsotg_phy_enable(hsotg); s3c_hsotg_corereset(hsotg); - s3c_hsotg_init(hsotg); s3c_hsotg_hw_cfg(hsotg); + s3c_hsotg_init(hsotg); /* hsotg->num_of_eps holds number of EPs other than ep0 */ From 854e58da6cc0de40b51d842908afa2be53efcb50 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:20:20 -0500 Subject: [PATCH 152/201] usb: dwc2/gadget: break infinite loop in endpoint disable code This patch fixes possible freeze caused by infinite loop in interrupt context. Signed-off-by: Marek Szyprowski Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 21d21de9b1349..2220882f0efb5 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1652,6 +1652,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx) dev_err(hsotg->dev, "%s: timeout flushing fifo (GRSTCTL=%08x)\n", __func__, val); + break; } udelay(1); From 6d88969ee26dab6f9ff1ae1516bbb70bfdc69831 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:21:23 -0500 Subject: [PATCH 153/201] usb: dwc2/gadget: do not call disconnect method in pullup This leads to potential spinlock recursion in composite framework, other udc drivers also don't call it directly from pullup method. Signed-off-by: Marek Szyprowski Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2220882f0efb5..def490047bdd3 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2945,7 +2945,6 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) s3c_hsotg_phy_enable(hsotg); s3c_hsotg_core_init(hsotg); } else { - s3c_hsotg_disconnect(hsotg); s3c_hsotg_phy_disable(hsotg); } From 811f889a29ce62e92fcc74535f6efa311bcc5c82 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:23:28 -0500 Subject: [PATCH 154/201] usb: dwc2/gadget: delay enabling irq once hardware is configured properly This patch fixes kernel panic/interrupt storm/etc issues if bootloader left s3c-hsotg module in enabled state. Now interrupt handler is enabled only after proper configuration of hardware registers. Signed-off-by: Marek Szyprowski Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index def490047bdd3..343571149b262 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3459,13 +3459,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->irq = ret; - ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, - dev_name(dev), hsotg); - if (ret < 0) { - dev_err(dev, "cannot claim IRQ\n"); - goto err_clk; - } - dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq); hsotg->gadget.max_speed = USB_SPEED_HIGH; @@ -3503,6 +3496,17 @@ static int s3c_hsotg_probe(struct platform_device *pdev) s3c_hsotg_hw_cfg(hsotg); s3c_hsotg_init(hsotg); + ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, + dev_name(dev), hsotg); + if (ret < 0) { + s3c_hsotg_phy_disable(hsotg); + clk_disable_unprepare(hsotg->clk); + regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + dev_err(dev, "cannot claim IRQ\n"); + goto err_clk; + } + /* hsotg->num_of_eps holds number of EPs other than ep0 */ if (hsotg->num_of_eps == 0) { From 0495452c111a895d4c0a6cab382b996f88fecf6e Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:25:28 -0500 Subject: [PATCH 155/201] usb: dwc2/gadget: assign TX FIFO dynamically Because we have not enough memory to have each TX FIFO of size at least 3072 bytes (the maximum single packet size with 3 transactions per microframe), we create four FIFOs of lenght 1024, and four of length 3072 bytes, and assing them to endpoints dynamically according to maxpacket size value of given endpoint. Up to now there were initialized 16 TX FIFOs, but we use only 8 IN endpoints, so we can split available memory for 8 FIFOs to have more memory for each one. It needed to do some small modifications in few places in code, because there was assumption that TX FIFO numbers assigned to endpoints are the same as the endpoint numbers, which is not true since we have dynamic FIFO assigning. Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/core.h | 2 + drivers/usb/dwc2/gadget.c | 80 ++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 067390e275440..3b4bd4c4efec3 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -139,6 +139,7 @@ struct s3c_hsotg_ep { unsigned int last_load; unsigned int fifo_load; unsigned short fifo_size; + unsigned short fifo_index; unsigned char dir_in; unsigned char index; @@ -197,6 +198,7 @@ struct s3c_hsotg { int fifo_mem; unsigned int dedicated_fifos:1; unsigned char num_of_eps; + u32 fifo_map; struct dentry *debug_root; struct dentry *debug_file; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 343571149b262..e74094a40c25c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -184,14 +184,29 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg) /* start at the end of the GNPTXFSIZ, rounded up */ addr = 2048 + 1024; - size = 768; /* - * currently we allocate TX FIFOs for all possible endpoints, - * and assume that they are all the same size. + * Because we have not enough memory to have each TX FIFO of size at + * least 3072 bytes (the maximum single packet size), we create four + * FIFOs of lenght 1024, and four of length 3072 bytes, and assing + * them to endpoints dynamically according to maxpacket size value of + * given endpoint. */ - for (ep = 1; ep <= 15; ep++) { + /* 256*4=1024 bytes FIFO length */ + size = 256; + for (ep = 1; ep <= 4; ep++) { + val = addr; + val |= size << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + size > hsotg->fifo_mem, + "insufficient fifo memory"); + addr += size; + + writel(val, hsotg->regs + DPTXFSIZN(ep)); + } + /* 768*4=3072 bytes FIFO length */ + size = 768; + for (ep = 5; ep <= 8; ep++) { val = addr; val |= size << FIFOSIZE_DEPTH_SHIFT; WARN_ONCE(addr + size > hsotg->fifo_mem, @@ -1835,7 +1850,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, if (dir_in) { int epctl = readl(hsotg->regs + epctl_reg); - s3c_hsotg_txfifo_flush(hsotg, idx); + s3c_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { @@ -1984,6 +1999,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, int result, bool force) { struct s3c_hsotg_req *req, *treq; + unsigned size; list_for_each_entry_safe(req, treq, &ep->queue, queue) { /* @@ -1997,9 +2013,11 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, s3c_hsotg_complete_request(hsotg, ep, req, result); } - if(hsotg->dedicated_fifos) - if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072) - s3c_hsotg_txfifo_flush(hsotg, ep->index); + if (!hsotg->dedicated_fifos) + return; + size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; + if (size < ep->fifo_size) + s3c_hsotg_txfifo_flush(hsotg, ep->fifo_index); } /** @@ -2440,6 +2458,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, u32 epctrl; u32 mps; int dir_in; + int i, val, size; int ret = 0; dev_dbg(hsotg->dev, @@ -2512,17 +2531,8 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, break; case USB_ENDPOINT_XFER_INT: - if (dir_in) { - /* - * Allocate our TxFNum by simply using the index - * of the endpoint for the moment. We could do - * something better if the host indicates how - * many FIFOs we are expecting to use. - */ - + if (dir_in) hs_ep->periodic = 1; - epctrl |= DXEPCTL_TXFNUM(index); - } epctrl |= DXEPCTL_EPTYPE_INTERRUPT; break; @@ -2536,8 +2546,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, * if the hardware has dedicated fifos, we must give each IN EP * a unique tx-fifo even if it is non-periodic. */ - if (dir_in && hsotg->dedicated_fifos) - epctrl |= DXEPCTL_TXFNUM(index); + if (dir_in && hsotg->dedicated_fifos) { + size = hs_ep->ep.maxpacket*hs_ep->mc; + for (i = 1; i <= 8; ++i) { + if (hsotg->fifo_map & (1<regs + DPTXFSIZN(i)); + val = (val >> FIFOSIZE_DEPTH_SHIFT)*4; + if (val < size) + continue; + hsotg->fifo_map |= 1<fifo_index = i; + hs_ep->fifo_size = val; + break; + } + if (i == 8) + return -ENOMEM; + } /* for non control endpoints, set PID to D0 */ if (index) @@ -2584,6 +2611,9 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) /* terminate all requests with shutdown */ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false); + hsotg->fifo_map &= ~(1<fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; ctrl = readl(hsotg->regs + epctrl_reg); ctrl &= ~DXEPCTL_EPENA; @@ -2975,7 +3005,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, int epnum) { - u32 ptxfifo; char *dir; if (epnum == 0) @@ -3003,15 +3032,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, usb_ep_set_maxpacket_limit(&hs_ep->ep, epnum ? 1024 : EP0_MPS_LIMIT); hs_ep->ep.ops = &s3c_hsotg_ep_ops; - /* - * Read the FIFO size for the Periodic TX FIFO, even if we're - * an OUT endpoint, we may as well do this if in future the - * code is changed to make each endpoint's direction changeable. - */ - - ptxfifo = readl(hsotg->regs + DPTXFSIZN(epnum)); - hs_ep->fifo_size = FIFOSIZE_DEPTH_GET(ptxfifo) * 4; - /* * if we're using dma, we need to set the next-endpoint pointer * to be something valid. From 302002b63d025bbb324399655cc1961cf0b2018a Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:27:43 -0500 Subject: [PATCH 156/201] usb: dwc2/gadget: disable clock when it's not needed When device is stopped or suspended clock is not needed so we can disable it for this time. Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index e74094a40c25c..11f038e75f589 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2894,6 +2894,8 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, hsotg->gadget.dev.of_node = hsotg->dev->of_node; hsotg->gadget.speed = USB_SPEED_UNKNOWN; + clk_enable(hsotg->clk); + ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); if (ret) { @@ -2942,6 +2944,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); + clk_disable(hsotg->clk); + return 0; } @@ -2973,8 +2977,10 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) spin_lock_irqsave(&hsotg->lock, flags); if (is_on) { s3c_hsotg_phy_enable(hsotg); + clk_enable(hsotg->clk); s3c_hsotg_core_init(hsotg); } else { + clk_disable(hsotg->clk); s3c_hsotg_phy_disable(hsotg); } @@ -3640,6 +3646,7 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state) ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); + clk_disable(hsotg->clk); } return ret; @@ -3654,6 +3661,8 @@ static int s3c_hsotg_resume(struct platform_device *pdev) if (hsotg->driver) { dev_info(hsotg->dev, "resuming usb gadget %s\n", hsotg->driver->driver.name); + + clk_enable(hsotg->clk); ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); } From 81353e0894b734c04a1c1800d7fcc0d382f1157f Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 28 Jul 2014 13:28:53 -0500 Subject: [PATCH 157/201] usb: dwc2/gadget: avoid disabling ep0 Endpoint 0 should not be disabled, so we start loop counter from number 1. Signed-off-by: Robert Baldyga --- drivers/usb/dwc2/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 11f038e75f589..cc31088bf95a8 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2930,7 +2930,7 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, return -ENODEV; /* all endpoints should be shutdown */ - for (ep = 0; ep < hsotg->num_of_eps; ep++) + for (ep = 1; ep < hsotg->num_of_eps; ep++) s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); spin_lock_irqsave(&hsotg->lock, flags); From 4cd221b136ca1c51fa9ef23bdf59b0be24492bbb Mon Sep 17 00:00:00 2001 From: Kever Yang Date: Wed, 6 Aug 2014 21:47:00 -0500 Subject: [PATCH 158/201] usb: dwc2: add 'mode' which based on Kconfig select or dts setting According to the "dr_mode", the otg controller can work as device role and host role. Some boards always want to use host mode and some other boards want to use gadget mode. We use the dts setting to set dwc2's mode, rather than fixing it to whatever hardware says. Signed-off-by: Kever Yang Acked-by: Paul Zimmerman --- drivers/usb/dwc2/core.c | 18 ++++++++++++++++++ drivers/usb/dwc2/core.h | 5 +++++ drivers/usb/dwc2/platform.c | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 33dbdd475e400..74e168f6afda6 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -118,6 +118,7 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) { u32 greset; int count = 0; + u32 gusbcfg; dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -148,6 +149,23 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) } } while (greset & GRSTCTL_CSFTRST); + if (hsotg->dr_mode == USB_DR_MODE_HOST) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + gusbcfg |= GUSBCFG_FORCEHOSTMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg |= GUSBCFG_FORCEDEVMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } else if (hsotg->dr_mode == USB_DR_MODE_OTG) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } + /* * NOTE: This long sleep is _very_ important, otherwise the core will * not stay in host mode after a connector ID change! diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 3b4bd4c4efec3..bf015ab3b44c8 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -504,6 +504,10 @@ struct dwc2_hw_params { * a_peripheral and b_device=>b_host) this may not match * the core, but allows the software to determine * transitions + * @dr_mode: Requested mode of operation, one of following: + * - USB_DR_MODE_PERIPHERAL + * - USB_DR_MODE_HOST + * - USB_DR_MODE_OTG * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth * transfer are in process of being queued * @srp_success: Stores status of SRP request in the case of a FS PHY @@ -595,6 +599,7 @@ struct dwc2_hsotg { /** Params to actually use */ struct dwc2_core_params *core_params; enum usb_otg_state op_state; + enum usb_dr_mode dr_mode; unsigned int queuing_high_bandwidth:1; unsigned int srp_success:1; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index a10e7a3535768..5f0c4bb6fa9b3 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -42,6 +42,8 @@ #include #include +#include + #include "core.h" #include "hcd.h" @@ -171,6 +173,8 @@ static int dwc2_driver_probe(struct platform_device *dev) dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", (unsigned long)res->start, hsotg->regs); + hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node); + retval = dwc2_hcd_init(hsotg, irq, params); if (retval) return retval; From c981a8b94d51753d7e52d9f17fdaf6bd82c663a6 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Fri, 8 Aug 2014 10:08:01 -0500 Subject: [PATCH 159/201] usb: dwc2: Read GNPTXFSIZ when in forced HOST mode The documentation for GNPTXFSIZ says that "For host mode, this field is always valid." Since we're already switching to host mode for HPTXFSIZ, let's also read GNPTXFSIZ in host mode. On an rk3288 SoC, without this change we see this at bootup: dwc2 ff580000.usb: gnptxfsiz=00100400 dwc2 ff580000.usb: 128 invalid for host_nperio_tx_fifo_size. Check HW configuration. After this change we see: dwc2 ff580000.usb: gnptxfsiz=04000400 Signed-off-by: Doug Anderson --- drivers/usb/dwc2/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 74e168f6afda6..438bfceea2957 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -2697,23 +2697,23 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hwcfg2 = readl(hsotg->regs + GHWCFG2); hwcfg3 = readl(hsotg->regs + GHWCFG3); hwcfg4 = readl(hsotg->regs + GHWCFG4); - gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); grxfsiz = readl(hsotg->regs + GRXFSIZ); dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1); dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2); dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3); dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4); - dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz); dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz); - /* Force host mode to get HPTXFSIZ exact power on value */ + /* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */ gusbcfg = readl(hsotg->regs + GUSBCFG); gusbcfg |= GUSBCFG_FORCEHOSTMODE; writel(gusbcfg, hsotg->regs + GUSBCFG); usleep_range(100000, 150000); + gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); hptxfsiz = readl(hsotg->regs + HPTXFSIZ); + dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz); dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz); gusbcfg = readl(hsotg->regs + GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; From 2cbf53732ef53712e5d6f1cd7d2e49decedb2714 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 14 May 2014 09:03:54 -0500 Subject: [PATCH 160/201] usb: dwc2: Update Kconfig to support dual-role Update DWC2 kconfig and makefile to support dual-role mode. The platform file will always get compiled for the case where the controller is directly connected to the CPU. So for loadable modules, only dwc2.ko is needed. Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- v3: Add USB_GADGET=y and USB_GADGET=USB_DWC2 for peripheral and dual-role config options. v2: Remove reference to dwc2_gadget --- drivers/usb/dwc2/Kconfig | 63 ++++++++++++++++++++++----------------- drivers/usb/dwc2/Makefile | 21 ++++++------- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index f93807b3631a8..4396a1f481562 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -1,40 +1,29 @@ config USB_DWC2 - bool "DesignWare USB2 DRD Core Support" + tristate "DesignWare USB2 DRD Core Support" depends on USB help Say Y here if your system has a Dual Role Hi-Speed USB controller based on the DesignWare HSOTG IP Core. - For host mode, if you choose to build the driver as dynamically - linked modules, the core module will be called dwc2.ko, the PCI - bus interface module (if you have a PCI bus system) will be - called dwc2_pci.ko, and the platform interface module (for - controllers directly connected to the CPU) will be called - dwc2_platform.ko. For gadget mode, there will be a single - module called dwc2_gadget.ko. - - NOTE: The s3c-hsotg driver is now renamed to dwc2_gadget. The - host and gadget drivers are still currently separate drivers. - There are plans to merge the dwc2_gadget driver with the dwc2 - host driver in the near future to create a dual-role driver. + If you choose to build the driver as dynamically + linked modules, a single dwc2.ko(regardless of mode of operation) + will get built for both platform IPs and PCI. if USB_DWC2 +choice + bool "DWC2 Mode Selection" + default USB_DWC2_DUAL_ROLE if (USB && USB_GADGET) + default USB_DWC2_HOST if (USB && !USB_GADGET) + default USB_DWC2_PERIPHERAL if (!USB && USB_GADGET) + config USB_DWC2_HOST - tristate "Host only mode" + bool "Host only mode" depends on USB help The Designware USB2.0 high-speed host controller - integrated into many SoCs. - -config USB_DWC2_PLATFORM - bool "DWC2 Platform" - depends on USB_DWC2_HOST - default USB_DWC2_HOST - help - The Designware USB2.0 platform interface module for - controllers directly connected to the CPU. This is only - used for host mode. + integrated into many SoCs. Select this option if you want the + driver to operate in Host-only mode. config USB_DWC2_PCI bool "DWC2 PCI" @@ -47,11 +36,31 @@ config USB_DWC2_PCI comment "Gadget mode requires USB Gadget support to be enabled" config USB_DWC2_PERIPHERAL - tristate "Gadget only mode" - depends on USB_GADGET + bool "Gadget only mode" + depends on USB_GADGET=y || USB_GADGET=USB_DWC2 help The Designware USB2.0 high-speed gadget controller - integrated into many SoCs. + integrated into many SoCs. Select this option if you want the + driver to operate in Peripheral-only mode. This option requires + USB_GADGET=y. + +config USB_DWC2_DUAL_ROLE + bool "Dual Role mode" + depends on ((USB=y || USB=USB_DWC2) && (USB_GADGET=y || USB_GADGET=USB_DWC2)) + help + Select this option if you want the driver to work in a dual-role + mode. In this mode both host and gadget features are enabled, and + the role will be determined by the cable that gets plugged-in. This + option requires USB_GADGET=y. +endchoice + +config USB_DWC2_PLATFORM + bool + depends on !PCI + default y + help + The Designware USB2.0 platform interface module for + controllers directly connected to the CPU. config USB_DWC2_DEBUG bool "Enable Debugging Messages" diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index b73d2a527970e..302613570fab7 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -1,10 +1,17 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG -obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o +obj-$(CONFIG_USB_DWC2) += dwc2.o dwc2-y := core.o core_intr.o -dwc2-y += hcd.o hcd_intr.o -dwc2-y += hcd_queue.o hcd_ddma.o + +ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) + dwc2-y += hcd.o hcd_intr.o + dwc2-y += hcd_queue.o hcd_ddma.o +endif + +ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),) + dwc2-y += gadget.o +endif # NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to # this location and renamed gadget.c. When building for dynamically linked @@ -19,10 +26,4 @@ ifneq ($(CONFIG_USB_DWC2_PCI),) dwc2_pci-y := pci.o endif -ifneq ($(CONFIG_USB_DWC2_PLATFORM),) - obj-$(CONFIG_USB_DWC2_HOST) += dwc2_platform.o - dwc2_platform-y := platform.o -endif - -obj-$(CONFIG_USB_DWC2_PERIPHERAL) += dwc2_gadget.o -dwc2_gadget-y := gadget.o +dwc2-$(CONFIG_USB_DWC2_PLATFORM) += platform.o From 32d288dc71000214ddb12409c1de5a69ced5df4b Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 24 Jul 2014 06:53:58 -0500 Subject: [PATCH 161/201] usb: dwc2: move "samsung,s3c6400-hsotg" into common platform Move the "samsung,s3c6400-hsotg" binding as the probe function in the gadget driver will get removed when the dual-role driver is implemented. Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- drivers/usb/dwc2/gadget.c | 1 - drivers/usb/dwc2/platform.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index cc31088bf95a8..c62f6c261900f 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3678,7 +3678,6 @@ static int s3c_hsotg_resume(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id s3c_hsotg_of_ids[] = { - { .compatible = "samsung,s3c6400-hsotg", }, { .compatible = "snps,dwc2", }, { /* sentinel */ } }; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 5f0c4bb6fa9b3..dd2f8f5a2693a 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -100,6 +100,7 @@ static int dwc2_driver_remove(struct platform_device *dev) static const struct of_device_id dwc2_of_match_table[] = { { .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 }, { .compatible = "snps,dwc2", .data = NULL }, + { .compatible = "samsung,s3c6400-hsotg", .data = NULL}, {}, }; MODULE_DEVICE_TABLE(of, dwc2_of_match_table); From 19c647f8dc51cd3a043983e39a9147392d37d62c Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 14 May 2014 14:36:04 -0500 Subject: [PATCH 162/201] usb: dwc2: Update the gadget driver to use common dwc2_hsotg structure Adds the gadget data structure and appropriate data structure pointers to the common dwc2_hsotg data structure. To keep the driver data dereference code looking clean, the gadget variable declares are only available for peripheral and dual-role mode. This is needed so that the dwc2_hsotg data structure can be used by the hcd and gadget drivers. Updates gadget.c to use the dwc2_hsotg data structure and gadget pointers that have been moved into the common dwc2_hsotg structure. Along with the updating the gadget driver to use the common dwc2_hsotg structure, a few other things are required in order for this patch to build properly. Those are: - Remove gadget module defines. Since the driver probing will be handled by either the platform or pci code. - Change the gadget probe function into gadget_init. Signed-off-by: Dinh Nguyen Signed-off-by: Paul Zimmerman --- v3: Updated with paulz's suggestion to avoid double pointers. v2: Left the function parameter name as 'hsotg' and just changed its type. Conflicts: drivers/usb/dwc2/gadget.c --- drivers/usb/dwc2/core.h | 176 ++++++++++++++++------------- drivers/usb/dwc2/gadget.c | 229 ++++++++++++++------------------------ drivers/usb/dwc2/hcd.h | 10 -- 3 files changed, 185 insertions(+), 230 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index bf015ab3b44c8..f55e62d265f89 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -84,7 +84,7 @@ static const char * const s3c_hsotg_supply_names[] = { */ #define EP0_MPS_LIMIT 64 -struct s3c_hsotg; +struct dwc2_hsotg; struct s3c_hsotg_req; /** @@ -130,7 +130,7 @@ struct s3c_hsotg_req; struct s3c_hsotg_ep { struct usb_ep ep; struct list_head queue; - struct s3c_hsotg *parent; + struct dwc2_hsotg *parent; struct s3c_hsotg_req *req; struct dentry *debugfs; @@ -154,67 +154,6 @@ struct s3c_hsotg_ep { char name[10]; }; -/** - * struct s3c_hsotg - driver state. - * @dev: The parent device supplied to the probe function - * @driver: USB gadget driver - * @phy: The otg phy transceiver structure for phy control. - * @uphy: The otg phy transceiver structure for old USB phy control. - * @plat: The platform specific configuration data. This can be removed once - * all SoCs support usb transceiver. - * @regs: The memory area mapped for accessing registers. - * @irq: The IRQ number we are using - * @supplies: Definition of USB power supplies - * @phyif: PHY interface width - * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos. - * @num_of_eps: Number of available EPs (excluding EP0) - * @debug_root: root directrory for debugfs. - * @debug_file: main status file for debugfs. - * @debug_fifo: FIFO status file for debugfs. - * @ep0_reply: Request used for ep0 reply. - * @ep0_buff: Buffer for EP0 reply data, if needed. - * @ctrl_buff: Buffer for EP0 control requests. - * @ctrl_req: Request for EP0 control packets. - * @setup: NAK management for EP0 SETUP - * @last_rst: Time of last reset - * @eps: The endpoints being supplied to the gadget framework - */ -struct s3c_hsotg { - struct device *dev; - struct usb_gadget_driver *driver; - struct phy *phy; - struct usb_phy *uphy; - struct s3c_hsotg_plat *plat; - - spinlock_t lock; - - void __iomem *regs; - int irq; - struct clk *clk; - - struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)]; - - u32 phyif; - int fifo_mem; - unsigned int dedicated_fifos:1; - unsigned char num_of_eps; - u32 fifo_map; - - struct dentry *debug_root; - struct dentry *debug_file; - struct dentry *debug_fifo; - - struct usb_request *ep0_reply; - struct usb_request *ctrl_req; - u8 ep0_buff[8]; - u8 ctrl_buff[8]; - - struct usb_gadget gadget; - unsigned int setup; - unsigned long last_rst; - struct s3c_hsotg_ep *eps; -}; - /** * struct s3c_hsotg_req - data transfer request * @req: The USB gadget request @@ -229,6 +168,7 @@ struct s3c_hsotg_req { unsigned char mapped; }; +#if defined(CONFIG_USB_DWC2_PERIPHERAL) || defined(CONFIG_USB_DWC2_DUAL_ROLE) #define call_gadget(_hs, _entry) \ do { \ if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \ @@ -238,6 +178,9 @@ do { \ spin_lock(&_hs->lock); \ } \ } while (0) +#else +#define call_gadget(_hs, _entry) do {} while (0) +#endif struct dwc2_hsotg; struct dwc2_host_chan; @@ -495,11 +438,13 @@ struct dwc2_hw_params { * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic * and periodic schedules * + * These are common for both host and peripheral modes: + * * @dev: The struct device pointer * @regs: Pointer to controller regs - * @core_params: Parameters that define how the core should be configured * @hw_params: Parameters that were autodetected from the * hardware registers + * @core_params: Parameters that define how the core should be configured * @op_state: The operational State, during transitions (a_host=> * a_peripheral and b_device=>b_host) this may not match * the core, but allows the software to determine @@ -508,6 +453,8 @@ struct dwc2_hw_params { * - USB_DR_MODE_PERIPHERAL * - USB_DR_MODE_HOST * - USB_DR_MODE_OTG + * @lock: Spinlock that protects all the driver data structures + * @priv: Stores a pointer to the struct usb_hcd * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth * transfer are in process of being queued * @srp_success: Stores status of SRP request in the case of a FS PHY @@ -517,6 +464,9 @@ struct dwc2_hw_params { * interrupt * @wkp_timer: Timer object for handling Wakeup Detected interrupt * @lx_state: Lx state of connected device + * + * These are for host mode: + * * @flags: Flags for handling root port state changes * @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule. * Transfers associated with these QHs are not currently @@ -585,11 +535,31 @@ struct dwc2_hw_params { * @status_buf_dma: DMA address for status_buf * @start_work: Delayed work for handling host A-cable connection * @reset_work: Delayed work for handling a port reset - * @lock: Spinlock that protects all the driver data structures - * @priv: Stores a pointer to the struct usb_hcd * @otg_port: OTG port number * @frame_list: Frame list * @frame_list_dma: Frame list DMA address + * + * These are for peripheral mode: + * + * @driver: USB gadget driver + * @phy: The otg phy transceiver structure for phy control. + * @uphy: The otg phy transceiver structure for old USB phy control. + * @plat: The platform specific configuration data. This can be removed once + * all SoCs support usb transceiver. + * @supplies: Definition of USB power supplies + * @phyif: PHY interface width + * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos. + * @num_of_eps: Number of available EPs (excluding EP0) + * @debug_root: Root directrory for debugfs. + * @debug_file: Main status file for debugfs. + * @debug_fifo: FIFO status file for debugfs. + * @ep0_reply: Request used for ep0 reply. + * @ep0_buff: Buffer for EP0 reply data, if needed. + * @ctrl_buff: Buffer for EP0 control requests. + * @ctrl_req: Request for EP0 control packets. + * @setup: NAK management for EP0 SETUP + * @last_rst: Time of last reset + * @eps: The endpoints being supplied to the gadget framework */ struct dwc2_hsotg { struct device *dev; @@ -601,6 +571,9 @@ struct dwc2_hsotg { enum usb_otg_state op_state; enum usb_dr_mode dr_mode; + spinlock_t lock; + void *priv; + unsigned int queuing_high_bandwidth:1; unsigned int srp_success:1; @@ -609,6 +582,14 @@ struct dwc2_hsotg { struct timer_list wkp_timer; enum dwc2_lx_state lx_state; + /* DWC OTG HW Release versions */ +#define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_90a 0x4f54290a +#define DWC2_CORE_REV_2_92a 0x4f54292a +#define DWC2_CORE_REV_2_94a 0x4f54294a +#define DWC2_CORE_REV_3_00a 0x4f54300a + +#if defined(CONFIG_USB_DWC2_HOST) || defined(CONFIG_USB_DWC2_DUAL_ROLE) union dwc2_hcd_internal_flags { u32 d32; struct { @@ -655,19 +636,10 @@ struct dwc2_hsotg { struct delayed_work start_work; struct delayed_work reset_work; - spinlock_t lock; - void *priv; u8 otg_port; u32 *frame_list; dma_addr_t frame_list_dma; - /* DWC OTG HW Release versions */ -#define DWC2_CORE_REV_2_71a 0x4f54271a -#define DWC2_CORE_REV_2_90a 0x4f54290a -#define DWC2_CORE_REV_2_92a 0x4f54292a -#define DWC2_CORE_REV_2_94a 0x4f54294a -#define DWC2_CORE_REV_3_00a 0x4f54300a - #ifdef DEBUG u32 frrem_samples; u64 frrem_accum; @@ -686,6 +658,39 @@ struct dwc2_hsotg { u32 hfnum_other_samples_b; u64 hfnum_other_frrem_accum_b; #endif +#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */ + +#if defined(CONFIG_USB_DWC2_PERIPHERAL) || defined(CONFIG_USB_DWC2_DUAL_ROLE) + /* Gadget structures */ + struct usb_gadget_driver *driver; + struct phy *phy; + struct usb_phy *uphy; + struct s3c_hsotg_plat *plat; + + struct clk *clk; + + struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)]; + + u32 phyif; + int fifo_mem; + unsigned int dedicated_fifos:1; + unsigned char num_of_eps; + u32 fifo_map; + + struct dentry *debug_root; + struct dentry *debug_file; + struct dentry *debug_fifo; + + struct usb_request *ep0_reply; + struct usb_request *ctrl_req; + u8 ep0_buff[8]; + u8 ctrl_buff[8]; + + struct usb_gadget gadget; + unsigned int setup; + unsigned long last_rst; + struct s3c_hsotg_ep *eps; +#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ }; /* Reasons for halting a host channel */ @@ -955,4 +960,25 @@ extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg); */ extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg); +#if defined(CONFIG_USB_DWC2_HOST) || defined(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_hcd_get_frame_number() - Returns current frame number + * + * @hsotg: The DWC2 HCD + */ +extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg); +extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg); +extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg); +#else +static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {} +static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} +static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, + const struct dwc2_core_params *params) +{ return 0; } +#endif + #endif /* __DWC2_CORE_H__ */ diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index c62f6c261900f..ff000c90c8c95 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -38,6 +38,7 @@ #include #include "core.h" +#include "hw.h" /* conversion functions */ static inline struct s3c_hsotg_req *our_req(struct usb_request *req) @@ -50,9 +51,9 @@ static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep) return container_of(ep, struct s3c_hsotg_ep, ep); } -static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget) +static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget) { - return container_of(gadget, struct s3c_hsotg, gadget); + return container_of(gadget, struct dwc2_hsotg, gadget); } static inline void __orr32(void __iomem *ptr, u32 val) @@ -66,7 +67,7 @@ static inline void __bic32(void __iomem *ptr, u32 val) } /* forward decleration of functions */ -static void s3c_hsotg_dump(struct s3c_hsotg *hsotg); +static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg); /** * using_dma - return the DMA status of the driver. @@ -87,7 +88,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg); * * Until this issue is sorted out, we always return 'false'. */ -static inline bool using_dma(struct s3c_hsotg *hsotg) +static inline bool using_dma(struct dwc2_hsotg *hsotg) { return false; /* support is not complete */ } @@ -97,7 +98,7 @@ static inline bool using_dma(struct s3c_hsotg *hsotg) * @hsotg: The device state * @ints: A bitmask of the interrupts to enable */ -static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints) +static void s3c_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints) { u32 gsintmsk = readl(hsotg->regs + GINTMSK); u32 new_gsintmsk; @@ -115,7 +116,7 @@ static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints) * @hsotg: The device state * @ints: A bitmask of the interrupts to enable */ -static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints) +static void s3c_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints) { u32 gsintmsk = readl(hsotg->regs + GINTMSK); u32 new_gsintmsk; @@ -136,7 +137,7 @@ static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints) * Set or clear the mask for an individual endpoint's interrupt * request. */ -static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg, +static void s3c_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, unsigned int ep, unsigned int dir_in, unsigned int en) { @@ -161,7 +162,7 @@ static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg, * s3c_hsotg_init_fifo - initialise non-periodic FIFOs * @hsotg: The device instance. */ -static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg) +static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) { unsigned int ep; unsigned int addr; @@ -285,7 +286,7 @@ static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep) * This is the reverse of s3c_hsotg_map_dma(), called for the completion * of a request to ensure the buffer is ready for access by the caller. */ -static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg, +static void s3c_hsotg_unmap_dma(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req) { @@ -314,7 +315,7 @@ static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg, * * This routine is only needed for PIO */ -static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg, +static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req) { @@ -519,7 +520,7 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) * Start the given request running by setting the endpoint registers * appropriately, and writing any data to the FIFOs. */ -static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, +static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req, bool continuing) @@ -709,7 +710,7 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, * DMA memory, then we map the memory and mark our request to allow us to * cleanup on completion. */ -static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg, +static int s3c_hsotg_map_dma(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, struct usb_request *req) { @@ -738,7 +739,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, { struct s3c_hsotg_req *hs_req = our_req(req); struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; + struct dwc2_hsotg *hs = hs_ep->parent; bool first; dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", @@ -770,7 +771,7 @@ static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; + struct dwc2_hsotg *hs = hs_ep->parent; unsigned long flags = 0; int ret = 0; @@ -801,7 +802,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep, struct usb_request *req) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hsotg = hs_ep->parent; + struct dwc2_hsotg *hsotg = hs_ep->parent; dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req); @@ -816,7 +817,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep, * Convert the given wIndex into a pointer to an driver endpoint * structure, or return NULL if it is not a valid endpoint. */ -static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg, +static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, u32 windex) { struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F]; @@ -845,7 +846,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg, * Create a request and queue it on the given endpoint. This is useful as * an internal method of sending replies to certain control requests, etc. */ -static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg, +static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *ep, void *buff, int length) @@ -886,7 +887,7 @@ static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg, * @hsotg: The device state * @ctrl: USB control request */ -static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg, +static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; @@ -957,7 +958,7 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) * @hsotg: The device state * @ctrl: USB control request */ -static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, +static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; @@ -1030,8 +1031,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, return 1; } -static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg); -static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); +static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg); +static void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg); /** * s3c_hsotg_stall_ep0 - stall ep0 @@ -1039,7 +1040,8 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg); * * Set stall for ep0 as response for setup request. */ -static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) { +static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) +{ struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; u32 reg; u32 ctrl; @@ -1077,7 +1079,7 @@ static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) { * needs to work out what to do next (and whether to pass it on to the * gadget driver). */ -static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, +static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; @@ -1162,7 +1164,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep, struct usb_request *req) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hsotg = hs_ep->parent; + struct dwc2_hsotg *hsotg = hs_ep->parent; if (req->status < 0) { dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status); @@ -1184,7 +1186,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep, * Enqueue a request on EP0 if necessary to received any SETUP packets * received from the host. */ -static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg) +static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) { struct usb_request *req = hsotg->ctrl_req; struct s3c_hsotg_req *hs_req = our_req(req); @@ -1227,7 +1229,7 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg) * * Note, expects the ep to already be locked as appropriate. */ -static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg, +static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req, int result) @@ -1292,7 +1294,7 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg, * endpoint, so sort out whether we need to read the data into a request * that has been made for that endpoint. */ -static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) +static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) { struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx]; struct s3c_hsotg_req *hs_req = hs_ep->req; @@ -1357,7 +1359,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size) * currently believed that we do not need to wait for any space in * the TxFIFO. */ -static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg, +static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg, struct s3c_hsotg_req *req) { u32 ctrl; @@ -1399,7 +1401,7 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg, * transfer for an OUT endpoint has been completed, either by a short * packet or by the finish of a transfer. */ -static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg, +static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum, bool was_setup) { u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum)); @@ -1472,7 +1474,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg, * * Return the current frame number */ -static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg) +static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *hsotg) { u32 dsts; @@ -1499,7 +1501,7 @@ static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg) * as the actual data should be sent to the memory directly and we turn * on the completion interrupts to get notifications of transfer completion. */ -static void s3c_hsotg_handle_rx(struct s3c_hsotg *hsotg) +static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg) { u32 grxstsr = readl(hsotg->regs + GRXSTSP); u32 epnum, status, size; @@ -1591,7 +1593,7 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps) * Configure the maximum packet size for the given endpoint, updating * the hardware control registers to reflect this. */ -static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, +static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, unsigned int ep, unsigned int mps) { struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep]; @@ -1646,7 +1648,7 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg, * @hsotg: The driver state * @idx: The index for the endpoint (0..15) */ -static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx) +static void s3c_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx) { int timeout; int val; @@ -1682,7 +1684,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx) * Check to see if there is a request that has data to send, and if so * make an attempt to write data into the FIFO. */ -static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg, +static int s3c_hsotg_trytx(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep) { struct s3c_hsotg_req *hs_req = hs_ep->req; @@ -1715,7 +1717,7 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg, * An IN transfer has been completed, update the transfer's state and then * call the relevant completion routines. */ -static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg, +static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep) { struct s3c_hsotg_req *hs_req = hs_ep->req; @@ -1792,7 +1794,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg, * * Process and clear any interrupt pending for an individual endpoint */ -static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, +static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, int dir_in) { struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx]; @@ -1917,7 +1919,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, * Handle updating the device settings after the enumeration phase has * been completed. */ -static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) +static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) { u32 dsts = readl(hsotg->regs + DSTS); int ep0_mps = 0, ep_mps; @@ -1994,7 +1996,7 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) * Go through the requests on the given endpoint and mark them * completed with the given result code. */ -static void kill_all_requests(struct s3c_hsotg *hsotg, +static void kill_all_requests(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *ep, int result, bool force) { @@ -2028,7 +2030,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, * transactions and signal the gadget driver that this * has happened. */ -static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg) +static void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) { unsigned ep; @@ -2043,7 +2045,7 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg) * @hsotg: The device state: * @periodic: True if this is a periodic FIFO interrupt */ -static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic) +static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) { struct s3c_hsotg_ep *ep; int epno, ret; @@ -2077,7 +2079,7 @@ static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic) * * Issue a soft reset to the core, and await the core finishing it. */ -static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) +static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg) { int timeout; u32 grstctl; @@ -2125,7 +2127,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) * * Issue a soft reset to the core, and await the core finishing it. */ -static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg) +void s3c_hsotg_core_init(struct dwc2_hsotg *hsotg) { s3c_hsotg_corereset(hsotg); @@ -2259,7 +2261,7 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg) */ static irqreturn_t s3c_hsotg_irq(int irq, void *pw) { - struct s3c_hsotg *hsotg = pw; + struct dwc2_hsotg *hsotg = pw; int retry_count = 8; u32 gintsts; u32 gintmsk; @@ -2451,7 +2453,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hsotg = hs_ep->parent; + struct dwc2_hsotg *hsotg = hs_ep->parent; unsigned long flags; int index = hs_ep->index; u32 epctrl_reg; @@ -2591,7 +2593,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, static int s3c_hsotg_ep_disable(struct usb_ep *ep) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hsotg = hs_ep->parent; + struct dwc2_hsotg *hsotg = hs_ep->parent; int dir_in = hs_ep->dir_in; int index = hs_ep->index; unsigned long flags; @@ -2656,7 +2658,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) { struct s3c_hsotg_req *hs_req = our_req(req); struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; + struct dwc2_hsotg *hs = hs_ep->parent; unsigned long flags; dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); @@ -2682,7 +2684,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; + struct dwc2_hsotg *hs = hs_ep->parent; int index = hs_ep->index; u32 epreg; u32 epctl; @@ -2746,7 +2748,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); - struct s3c_hsotg *hs = hs_ep->parent; + struct dwc2_hsotg *hs = hs_ep->parent; unsigned long flags = 0; int ret = 0; @@ -2775,7 +2777,7 @@ static struct usb_ep_ops s3c_hsotg_ep_ops = { * A wrapper for platform code responsible for controlling * low-level USB code */ -static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg) +static void s3c_hsotg_phy_enable(struct dwc2_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); @@ -2798,7 +2800,7 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg) * A wrapper for platform code responsible for controlling * low-level USB code */ -static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg) +static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); @@ -2816,7 +2818,7 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg) * s3c_hsotg_init - initalize the usb core * @hsotg: The driver state */ -static void s3c_hsotg_init(struct s3c_hsotg *hsotg) +static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) { /* unmask subset of endpoint interrupts */ @@ -2866,7 +2868,7 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg) static int s3c_hsotg_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct s3c_hsotg *hsotg = to_hsotg(gadget); + struct dwc2_hsotg *hsotg = to_hsotg(gadget); int ret; if (!hsotg) { @@ -2922,7 +2924,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { - struct s3c_hsotg *hsotg = to_hsotg(gadget); + struct dwc2_hsotg *hsotg = to_hsotg(gadget); unsigned long flags = 0; int ep; @@ -2942,7 +2944,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, spin_unlock_irqrestore(&hsotg->lock, flags); - regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); + regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); clk_disable(hsotg->clk); @@ -2969,7 +2972,7 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget) */ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) { - struct s3c_hsotg *hsotg = to_hsotg(gadget); + struct dwc2_hsotg *hsotg = to_hsotg(gadget); unsigned long flags = 0; dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on); @@ -3007,7 +3010,7 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = { * creation) to give to the gadget driver. Setup the endpoint name, any * direction information and other state that may be required. */ -static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, +static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, int epnum) { @@ -3056,7 +3059,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, * * Read the USB core HW configuration registers */ -static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg) +static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) { u32 cfg2, cfg3, cfg4; /* check hardware configuration */ @@ -3080,7 +3083,7 @@ static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg) * s3c_hsotg_dump - dump state of the udc * @param: The device state */ -static void s3c_hsotg_dump(struct s3c_hsotg *hsotg) +static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) { #ifdef DEBUG struct device *dev = hsotg->dev; @@ -3139,7 +3142,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg) */ static int state_show(struct seq_file *seq, void *v) { - struct s3c_hsotg *hsotg = seq->private; + struct dwc2_hsotg *hsotg = seq->private; void __iomem *regs = hsotg->regs; int idx; @@ -3209,7 +3212,7 @@ static const struct file_operations state_fops = { */ static int fifo_show(struct seq_file *seq, void *v) { - struct s3c_hsotg *hsotg = seq->private; + struct dwc2_hsotg *hsotg = seq->private; void __iomem *regs = hsotg->regs; u32 val; int idx; @@ -3265,7 +3268,7 @@ static const char *decode_direction(int is_in) static int ep_show(struct seq_file *seq, void *v) { struct s3c_hsotg_ep *ep = seq->private; - struct s3c_hsotg *hsotg = ep->parent; + struct dwc2_hsotg *hsotg = ep->parent; struct s3c_hsotg_req *req; void __iomem *regs = hsotg->regs; int index = ep->index; @@ -3342,7 +3345,7 @@ static const struct file_operations ep_fops = { * with the same name as the device itself, in case we end up * with multiple blocks in future systems. */ -static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg) +static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) { struct dentry *root; unsigned epidx; @@ -3388,7 +3391,7 @@ static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg) * * Cleanup (remove) the debugfs files for use on module exit. */ -static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg) +static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) { unsigned epidx; @@ -3403,29 +3406,22 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg) } /** - * s3c_hsotg_probe - probe function for hsotg driver - * @pdev: The platform information for the driver + * dwc2_gadget_init - init function for gadget + * @dwc2: The data structure for the DWC2 driver. + * @irq: The IRQ number for the controller. */ -static int s3c_hsotg_probe(struct platform_device *pdev) +int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) { - struct s3c_hsotg_plat *plat = dev_get_platdata(&pdev->dev); + struct device *dev = hsotg->dev; + struct s3c_hsotg_plat *plat = dev->platform_data; struct phy *phy; struct usb_phy *uphy; - struct device *dev = &pdev->dev; struct s3c_hsotg_ep *eps; - struct s3c_hsotg *hsotg; - struct resource *res; int epnum; int ret; int i; - hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); - if (!hsotg) { - dev_err(dev, "cannot get memory\n"); - return -ENOMEM; - } - /* Set default UTMI width */ hsotg->phyif = GUSBCFG_PHYIF16; @@ -3433,14 +3429,14 @@ static int s3c_hsotg_probe(struct platform_device *pdev) * Attempt to find a generic PHY, then look for an old style * USB PHY, finally fall back to pdata */ - phy = devm_phy_get(&pdev->dev, "usb2-phy"); + phy = devm_phy_get(dev, "usb2-phy"); if (IS_ERR(phy)) { uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); if (IS_ERR(uphy)) { /* Fallback for pdata */ - plat = dev_get_platdata(&pdev->dev); + plat = dev_get_platdata(dev); if (!plat) { - dev_err(&pdev->dev, + dev_err(dev, "no platform data or transceiver defined\n"); return -EPROBE_DEFER; } @@ -3457,42 +3453,16 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->phyif = GUSBCFG_PHYIF8; } - hsotg->dev = dev; - - hsotg->clk = devm_clk_get(&pdev->dev, "otg"); + hsotg->clk = devm_clk_get(dev, "otg"); if (IS_ERR(hsotg->clk)) { dev_err(dev, "cannot get otg clock\n"); return PTR_ERR(hsotg->clk); } - platform_set_drvdata(pdev, hsotg); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - hsotg->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(hsotg->regs)) { - ret = PTR_ERR(hsotg->regs); - goto err_clk; - } - - ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "cannot find IRQ\n"); - goto err_clk; - } - - spin_lock_init(&hsotg->lock); - - hsotg->irq = ret; - - dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq); - hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); - /* reset the system */ - clk_prepare_enable(hsotg->clk); /* regulators */ @@ -3511,7 +3481,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->supplies); if (ret) { - dev_err(hsotg->dev, "failed to enable supplies: %d\n", ret); + dev_err(dev, "failed to enable supplies: %d\n", ret); goto err_supplies; } @@ -3522,7 +3492,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev) s3c_hsotg_hw_cfg(hsotg); s3c_hsotg_init(hsotg); - ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, + ret = devm_request_irq(dev, irq, s3c_hsotg_irq, 0, dev_name(dev), hsotg); if (ret < 0) { s3c_hsotg_phy_disable(hsotg); @@ -3575,13 +3545,13 @@ static int s3c_hsotg_probe(struct platform_device *pdev) ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); if (ret) { - dev_err(hsotg->dev, "failed to disable supplies: %d\n", ret); + dev_err(dev, "failed to disable supplies: %d\n", ret); goto err_ep_mem; } s3c_hsotg_phy_disable(hsotg); - ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget); + ret = usb_add_gadget_udc(dev, &hsotg->gadget); if (ret) goto err_ep_mem; @@ -3605,10 +3575,8 @@ static int s3c_hsotg_probe(struct platform_device *pdev) * s3c_hsotg_remove - remove function for hsotg driver * @pdev: The platform information for the driver */ -static int s3c_hsotg_remove(struct platform_device *pdev) +static int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) { - struct s3c_hsotg *hsotg = platform_get_drvdata(pdev); - usb_del_gadget_udc(&hsotg->gadget); s3c_hsotg_delete_debug(hsotg); @@ -3623,9 +3591,8 @@ static int s3c_hsotg_remove(struct platform_device *pdev) return 0; } -static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state) +static int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) { - struct s3c_hsotg *hsotg = platform_get_drvdata(pdev); unsigned long flags; int ret = 0; @@ -3652,9 +3619,8 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state) return ret; } -static int s3c_hsotg_resume(struct platform_device *pdev) +static int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) { - struct s3c_hsotg *hsotg = platform_get_drvdata(pdev); unsigned long flags; int ret = 0; @@ -3664,7 +3630,7 @@ static int s3c_hsotg_resume(struct platform_device *pdev) clk_enable(hsotg->clk); ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); + hsotg->supplies); } spin_lock_irqsave(&hsotg->lock, flags); @@ -3675,30 +3641,3 @@ static int s3c_hsotg_resume(struct platform_device *pdev) return ret; } - -#ifdef CONFIG_OF -static const struct of_device_id s3c_hsotg_of_ids[] = { - { .compatible = "snps,dwc2", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, s3c_hsotg_of_ids); -#endif - -static struct platform_driver s3c_hsotg_driver = { - .driver = { - .name = "s3c-hsotg", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(s3c_hsotg_of_ids), - }, - .probe = s3c_hsotg_probe, - .remove = s3c_hsotg_remove, - .suspend = s3c_hsotg_suspend, - .resume = s3c_hsotg_resume, -}; - -module_platform_driver(s3c_hsotg_driver); - -MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c-hsotg"); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index fdc6d489084ae..08db5c0f69c64 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -666,9 +666,6 @@ extern irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg); */ extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg); -extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg); -extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg); - /** * dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host, * and 0 otherwise @@ -677,13 +674,6 @@ extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg); */ extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg); -/** - * dwc2_hcd_get_frame_number() - Returns current frame number - * - * @hsotg: The DWC2 HCD - */ -extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg); - /** * dwc2_hcd_dump_state() - Dumps hsotg state * From af363f6a14a86341dd0c0503250e351f9027e590 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 14 May 2014 15:01:24 -0500 Subject: [PATCH 163/201] usb: dwc2: Add the appropriate init calls in platform code Add the proper init calls for either host, gadget or both in platform.c Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- drivers/usb/dwc2/core.h | 13 +++++++++++++ drivers/usb/dwc2/gadget.c | 2 +- drivers/usb/dwc2/platform.c | 28 ++++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index f55e62d265f89..3a49a00c665af 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -960,6 +960,19 @@ extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg); */ extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg); +/* Gadget defines */ +#if defined(CONFIG_USB_DWC2_PERIPHERAL) || defined(CONFIG_USB_DWC2_DUAL_ROLE) +extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg); +extern void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2); +extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq); +#else +static inline void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2) {} +static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) +{ return 0; } +static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) +{ return 0; } +#endif + #if defined(CONFIG_USB_DWC2_HOST) || defined(CONFIG_USB_DWC2_DUAL_ROLE) /** * dwc2_hcd_get_frame_number() - Returns current frame number diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index ff000c90c8c95..d40f765b3ab7a 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3575,7 +3575,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) * s3c_hsotg_remove - remove function for hsotg driver * @pdev: The platform information for the driver */ -static int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) +int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) { usb_del_gadget_udc(&hsotg->gadget); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index dd2f8f5a2693a..2871f351ceea1 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -92,7 +92,14 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); - dwc2_hcd_remove(hsotg); + if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) + s3c_hsotg_remove(hsotg); + else if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) + dwc2_hcd_remove(hsotg); + else { /* dual role */ + dwc2_hcd_remove(hsotg); + s3c_hsotg_remove(hsotg); + } return 0; } @@ -176,9 +183,22 @@ static int dwc2_driver_probe(struct platform_device *dev) hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node); - retval = dwc2_hcd_init(hsotg, irq, params); - if (retval) - return retval; + if (IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)) { + retval = dwc2_gadget_init(hsotg, irq); + if (retval) + return retval; + retval = dwc2_hcd_init(hsotg, irq, params); + if (retval) + return retval; + } else if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) { + retval = dwc2_hcd_init(hsotg, irq, params); + if (retval) + return retval; + } else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) { + retval = dwc2_gadget_init(hsotg, irq); + if (retval) + return retval; + } platform_set_drvdata(dev, hsotg); From bdb62ded0a2ced9caea95383a7bdf427578b744b Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 25 Jun 2014 17:08:00 -0500 Subject: [PATCH 164/201] usb: dwc2: Initialize the USB core for peripheral mode Initialize the USB driver to peripheral mode when a B-Device connector is attached. Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- drivers/usb/dwc2/hcd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 4d918ed8d3433..07a7bcd049ce6 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1355,6 +1355,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work) hsotg->op_state = OTG_STATE_B_PERIPHERAL; dwc2_core_init(hsotg, false, -1); dwc2_enable_global_interrupts(hsotg); + s3c_hsotg_core_init(hsotg); } else { /* A-Device connector (Host Mode) */ dev_dbg(hsotg->dev, "connId A\n"); From fb2d91f523234d7c261c979de59bb523831bf6a2 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 24 Jul 2014 13:40:41 -0500 Subject: [PATCH 165/201] usb: dwc2: Update common interrupt handler to call gadget interrupt handler Make dwc2_handle_common_intr call the gadget interrupt function when operating in peripheral mode. Remove the spinlock functions in s3c_hsotg_irq as dwc2_handle_common_intr() already has the spinlocks. Remove duplicate interrupt conditions that was in gadget, as those are handled by dwc2 common interrupt handler. Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- v2: Keep interrupt handler for host and peripheral modes separate --- drivers/usb/dwc2/core.h | 3 +++ drivers/usb/dwc2/core_intr.c | 3 +++ drivers/usb/dwc2/gadget.c | 50 +++--------------------------------- 3 files changed, 10 insertions(+), 46 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 3a49a00c665af..bbb0f5207dcbf 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -965,12 +965,15 @@ extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg); extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg); extern void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2); extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq); +irqreturn_t s3c_hsotg_irq(int irq, void *pw); #else static inline void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2) {} static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) { return 0; } +static inline irqreturn_t s3c_hsotg_irq(int irq, void *pw) +{ return IRQ_HANDLED; } #endif #if defined(CONFIG_USB_DWC2_HOST) || defined(CONFIG_USB_DWC2_DUAL_ROLE) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index c93918b70d032..24d4c0d9fd90d 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -472,6 +472,9 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) spin_lock(&hsotg->lock); + if (dwc2_is_device_mode(hsotg)) + retval = s3c_hsotg_irq(irq, dev); + gintsts = dwc2_read_common_intr(hsotg); if (gintsts & ~GINTSTS_PRTINT) retval = IRQ_HANDLED; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index d40f765b3ab7a..d8b86a3b7ad16 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2259,14 +2259,13 @@ void s3c_hsotg_core_init(struct dwc2_hsotg *hsotg) * @irq: The IRQ number triggered * @pw: The pw value when registered the handler. */ -static irqreturn_t s3c_hsotg_irq(int irq, void *pw) +irqreturn_t s3c_hsotg_irq(int irq, void *pw) { struct dwc2_hsotg *hsotg = pw; int retry_count = 8; u32 gintsts; u32 gintmsk; - spin_lock(&hsotg->lock); irq_retry: gintsts = readl(hsotg->regs + GINTSTS); gintmsk = readl(hsotg->regs + GINTMSK); @@ -2276,33 +2275,12 @@ static irqreturn_t s3c_hsotg_irq(int irq, void *pw) gintsts &= gintmsk; - if (gintsts & GINTSTS_OTGINT) { - u32 otgint = readl(hsotg->regs + GOTGINT); - - dev_info(hsotg->dev, "OTGInt: %08x\n", otgint); - - writel(otgint, hsotg->regs + GOTGINT); - } - - if (gintsts & GINTSTS_SESSREQINT) { - dev_dbg(hsotg->dev, "%s: SessReqInt\n", __func__); - writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS); - } - if (gintsts & GINTSTS_ENUMDONE) { writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS); s3c_hsotg_irq_enumdone(hsotg); } - if (gintsts & GINTSTS_CONIDSTSCHNG) { - dev_dbg(hsotg->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n", - readl(hsotg->regs + DSTS), - readl(hsotg->regs + GOTGCTL)); - - writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS); - } - if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { u32 daint = readl(hsotg->regs + DAINT); u32 daintmsk = readl(hsotg->regs + DAINTMSK); @@ -2383,25 +2361,6 @@ static irqreturn_t s3c_hsotg_irq(int irq, void *pw) s3c_hsotg_handle_rx(hsotg); } - if (gintsts & GINTSTS_MODEMIS) { - dev_warn(hsotg->dev, "warning, mode mismatch triggered\n"); - writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS); - } - - if (gintsts & GINTSTS_USBSUSP) { - dev_info(hsotg->dev, "GINTSTS_USBSusp\n"); - writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); - - call_gadget(hsotg, suspend); - } - - if (gintsts & GINTSTS_WKUPINT) { - dev_info(hsotg->dev, "GINTSTS_WkUpIn\n"); - writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS); - - call_gadget(hsotg, resume); - } - if (gintsts & GINTSTS_ERLYSUSP) { dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n"); writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS); @@ -2437,10 +2396,9 @@ static irqreturn_t s3c_hsotg_irq(int irq, void *pw) if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) goto irq_retry; - spin_unlock(&hsotg->lock); - return IRQ_HANDLED; } +EXPORT_SYMBOL(s3c_hsotg_irq); /** * s3c_hsotg_ep_enable - enable the given endpoint @@ -3492,8 +3450,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) s3c_hsotg_hw_cfg(hsotg); s3c_hsotg_init(hsotg); - ret = devm_request_irq(dev, irq, s3c_hsotg_irq, 0, - dev_name(dev), hsotg); + ret = devm_request_irq(dev, irq, dwc2_handle_common_intr, IRQF_SHARED, + dev_name(dev), hsotg); if (ret < 0) { s3c_hsotg_phy_disable(hsotg); clk_disable_unprepare(hsotg->clk); From 06d2638c15fba35e7003711bacf40f4c9de6a94f Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 24 Jul 2014 13:50:29 -0500 Subject: [PATCH 166/201] usb: dwc2: Add call_gadget functions for perpheral mode interrupts Update the dwc2 wakeup and suspend interrupt functions to use call_gadget when the IP is in peripheral mode. Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- drivers/usb/dwc2/core_intr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 24d4c0d9fd90d..651785daa49f2 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -337,6 +337,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; + call_gadget(hsotg, resume); } else { if (hsotg->lx_state != DWC2_L1) { u32 pcgcctl = readl(hsotg->regs + PCGCTL); @@ -397,6 +398,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", !!(dsts & DSTS_SUSPSTS), hsotg->hw_params.power_optimized); + call_gadget(hsotg, suspend); } else { if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); From ec781499f2a227b05c69bbb86f9639193828fd45 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 6 Jun 2014 11:00:49 -0500 Subject: [PATCH 167/201] usb: dwc2: gadget: Do not fail probe if there isn't a clock node Since the dwc2 hcd driver is currently not looking for a clock node during init, we should not completely fail if there isn't a clock provided. Add a check for a valid clock before calling clock functions. Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- drivers/usb/dwc2/gadget.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index d8b86a3b7ad16..a3a3d31b542f5 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2854,7 +2854,8 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, hsotg->gadget.dev.of_node = hsotg->dev->of_node; hsotg->gadget.speed = USB_SPEED_UNKNOWN; - clk_enable(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_enable(hsotg->clk); ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); @@ -2905,7 +2906,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); - clk_disable(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_disable(hsotg->clk); return 0; } @@ -2938,10 +2940,12 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) spin_lock_irqsave(&hsotg->lock, flags); if (is_on) { s3c_hsotg_phy_enable(hsotg); - clk_enable(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_enable(hsotg->clk); s3c_hsotg_core_init(hsotg); } else { - clk_disable(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_disable(hsotg->clk); s3c_hsotg_phy_disable(hsotg); } @@ -3412,16 +3416,15 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) } hsotg->clk = devm_clk_get(dev, "otg"); - if (IS_ERR(hsotg->clk)) { - dev_err(dev, "cannot get otg clock\n"); - return PTR_ERR(hsotg->clk); - } + if (IS_ERR(hsotg->clk)) + dev_warn(dev, "cannot get otg clock\n"); hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); - clk_prepare_enable(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_prepare_enable(hsotg->clk); /* regulators */ @@ -3454,7 +3457,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) dev_name(dev), hsotg); if (ret < 0) { s3c_hsotg_phy_disable(hsotg); - clk_disable_unprepare(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_disable_unprepare(hsotg->clk); regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); dev_err(dev, "cannot claim IRQ\n"); @@ -3524,7 +3528,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) err_supplies: s3c_hsotg_phy_disable(hsotg); err_clk: - clk_disable_unprepare(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_disable_unprepare(hsotg->clk); return ret; } @@ -3544,7 +3549,8 @@ int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) usb_gadget_unregister_driver(hsotg->driver); } - clk_disable_unprepare(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_disable_unprepare(hsotg->clk); return 0; } @@ -3571,7 +3577,8 @@ static int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); - clk_disable(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_disable(hsotg->clk); } return ret; @@ -3586,7 +3593,8 @@ static int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) dev_info(hsotg->dev, "resuming usb gadget %s\n", hsotg->driver->driver.name); - clk_enable(hsotg->clk); + if (!IS_ERR(hsotg->clk)) + clk_enable(hsotg->clk); ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); } From 0be56d925f8772b3d2bd536bd2c8a499d0716769 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 10 Jun 2014 10:29:15 -0500 Subject: [PATCH 168/201] usb: dwc2: initialize the spin_lock for both host and gadget Move spin_lock_init to common location for both host and gadget. Signed-off-by: Dinh Nguyen --- v4: move spin_lock_init up to make sure sure no locks can be taken before the init. --- drivers/usb/dwc2/hcd.c | 1 - drivers/usb/dwc2/platform.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 07a7bcd049ce6..c6778d9c60d57 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2824,7 +2824,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, hcd->has_tt = 1; - spin_lock_init(&hsotg->lock); ((struct wrapper_priv_data *) &hcd->hcd_priv)->hsotg = hsotg; hsotg->priv = hcd; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 2871f351ceea1..278135d21d141 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -183,6 +183,7 @@ static int dwc2_driver_probe(struct platform_device *dev) hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node); + spin_lock_init(&hsotg->lock); if (IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)) { retval = dwc2_gadget_init(hsotg, irq); if (retval) From 6a689a274dd2e0f981e3de95dbb8a1985f877e2f Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 10 Jun 2014 10:20:43 -0500 Subject: [PATCH 169/201] usb: dwc2: Add suspend/resume for gadget Move suspend/resume code to common platform code. Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- drivers/usb/dwc2/core.h | 6 ++++++ drivers/usb/dwc2/gadget.c | 4 ++-- drivers/usb/dwc2/platform.c | 23 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index bbb0f5207dcbf..5bb7e80123166 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -964,12 +964,18 @@ extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg); #if defined(CONFIG_USB_DWC2_PERIPHERAL) || defined(CONFIG_USB_DWC2_DUAL_ROLE) extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg); extern void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2); +extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2); +extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2); extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq); irqreturn_t s3c_hsotg_irq(int irq, void *pw); #else static inline void s3c_hsotg_core_init(struct dwc2_hsotg *dwc2) {} static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } +static inline int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2) +{ return 0; } +static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2) +{ return 0; } static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) { return 0; } static inline irqreturn_t s3c_hsotg_irq(int irq, void *pw) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index a3a3d31b542f5..2d98100550141 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3555,7 +3555,7 @@ int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) return 0; } -static int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) +int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) { unsigned long flags; int ret = 0; @@ -3584,7 +3584,7 @@ static int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) return ret; } -static int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) +int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; int ret = 0; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 278135d21d141..59265adea53ec 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -206,6 +206,27 @@ static int dwc2_driver_probe(struct platform_device *dev) return retval; } +static int dwc2_suspend(struct platform_device *dev, pm_message_t state) +{ + struct dwc2_hsotg *dwc2 = platform_get_drvdata(dev); + int ret = 0; + + if (dwc2_is_device_mode(dwc2)) + ret = s3c_hsotg_suspend(dwc2); + return ret; +} + +static int dwc2_resume(struct platform_device *dev) +{ + struct dwc2_hsotg *dwc2 = platform_get_drvdata(dev); + int ret = 0; + + if (dwc2_is_device_mode(dwc2)) + ret = s3c_hsotg_resume(dwc2); + + return ret; +} + static struct platform_driver dwc2_platform_driver = { .driver = { .name = dwc2_driver_name, @@ -213,6 +234,8 @@ static struct platform_driver dwc2_platform_driver = { }, .probe = dwc2_driver_probe, .remove = dwc2_driver_remove, + .suspend = dwc2_suspend, + .resume = dwc2_resume, }; module_platform_driver(dwc2_platform_driver); From a9ee73c0dc961e9e6f7e4a0571071f5014bca223 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 14 May 2014 16:40:46 -0500 Subject: [PATCH 170/201] usb: dwc2: check that the host work queue is valid The Host workqueue will not get initialized if the driver is configured for peripheral mode only. Thus we need to check for wq_otg before calling queue_work(). Signed-off-by: Dinh Nguyen Acked-by: Paul Zimmerman --- drivers/usb/dwc2/core_intr.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 651785daa49f2..12408752a2bb9 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -287,9 +287,11 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) * Release lock before scheduling workq as it holds spinlock during * scheduling. */ - spin_unlock(&hsotg->lock); - queue_work(hsotg->wq_otg, &hsotg->wf_otg); - spin_lock(&hsotg->lock); + if (hsotg->wq_otg) { + spin_unlock(&hsotg->lock); + queue_work(hsotg->wq_otg, &hsotg->wf_otg); + spin_lock(&hsotg->lock); + } /* Clear interrupt */ writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS); From 067f48c574f748cefbb822e17796bac93ed48694 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 19 Jun 2014 15:49:59 -0500 Subject: [PATCH 171/201] usb: dwc2: pci: Update pci portion of the dwc2 driver Initialize the spinlock here as the original spinlock in the host driver has been removed. Signed-off-by: Dinh Nguyen --- v4: moved spin_lock_init() up to make sure no locks can be taken before the init. --- drivers/usb/dwc2/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index c291fca5d21fc..6d33ecf2e2955 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -141,6 +141,7 @@ static int dwc2_driver_probe(struct pci_dev *dev, pci_set_master(dev); + spin_lock_init(&hsotg->lock); retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params); if (retval) { pci_disable_device(dev); From 5971a81b5f0f5fb2919cf91981e209afed983a2f Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Mon, 4 Aug 2014 16:43:12 -0500 Subject: [PATCH 172/201] Add qspi for socfpga.dtsi --- arch/arm/boot/dts/socfpga.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index fad343c7b3999..43311525d0b87 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -771,6 +771,21 @@ reg = <0xff800000 0x1000>; }; + qspi: spi@ff705000 { + compatible = "cadence,qspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xff705000 0x1000>, + <0xffa00000 0x1000>; + interrupts = <0 151 4>; + clocks = <&qspi_clk>; + ext-decoder = <0>; /* external decoder */ + num-chipselect = <4>; + fifo-depth = <128>; + bus-num = <2>; + status = "disabled"; + }; + spi0: spi@fff00000 { compatible = "snps,dw-spi-mmio"; #address-cells = <1>; From ddcd644992dda31770101f09abc2109e537a4744 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 16 Jul 2014 10:35:19 +0800 Subject: [PATCH 173/201] FogBugz #216207: nios2: fix pfn_valid range pfn_valid should be between ARCH_PFN_OFFSET (start of memory) and max_mapnr. max_mapnr is maximum page frame number and it is points to end of memory now. The original code will cause the kernel access to invalid and not exist memory location. Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/page.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/nios2/include/asm/page.h b/arch/nios2/include/asm/page.h index 6f455f4f6fc36..5caef25b366d7 100644 --- a/arch/nios2/include/asm/page.h +++ b/arch/nios2/include/asm/page.h @@ -91,7 +91,7 @@ extern struct page *mem_map; # define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) # define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && \ - (pfn) < (max_mapnr + ARCH_PFN_OFFSET)) + (pfn) < max_mapnr) # define virt_to_page(vaddr) pfn_to_page(PFN_DOWN(virt_to_phys(vaddr))) # define virt_addr_valid(vaddr) pfn_valid(PFN_DOWN(virt_to_phys(vaddr))) From 94d8b050278d48960a28e06d087e5b50d861e2dd Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 16 Jul 2014 10:40:06 +0800 Subject: [PATCH 174/201] FogBugz #216208: nios2: fix warning from get_fb_unmapped_area arch/nios2/kernel/sys_nios2.c: In function 'get_fb_unmapped_area': arch/nios2/kernel/sys_nios2.c:92:2: warning: return makes integer from pointer without a cast [enabled by default] Signed-off-by: Ley Foon Tan --- arch/nios2/kernel/sys_nios2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/nios2/kernel/sys_nios2.c b/arch/nios2/kernel/sys_nios2.c index 955ad47d2d3f7..ba5fd091c3b0c 100644 --- a/arch/nios2/kernel/sys_nios2.c +++ b/arch/nios2/kernel/sys_nios2.c @@ -89,7 +89,7 @@ unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, { struct fb_info *info = filp->private_data; - return info->screen_base; + return (unsigned long)info->screen_base; } EXPORT_SYMBOL(get_fb_unmapped_area); #endif /* CONFIG_FB */ From 8a4cd6e63d148934fe3a55cefd3e061e182ab976 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 6 Aug 2014 16:31:59 +0800 Subject: [PATCH 175/201] nios2: Port to 3.16 - Use new fdt_translate_address(). - Remove __dtb_start prototype. It is defined in generic of_fdt.h. Signed-off-by: Ley Foon Tan --- arch/nios2/include/asm/prom.h | 2 -- arch/nios2/kernel/prom.c | 25 ++++++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/arch/nios2/include/asm/prom.h b/arch/nios2/include/asm/prom.h index 7a71e4c50a421..7266e77152c56 100644 --- a/arch/nios2/include/asm/prom.h +++ b/arch/nios2/include/asm/prom.h @@ -19,8 +19,6 @@ #ifndef _ASM_NIOS2_PROM_H #define _ASM_NIOS2_PROM_H -extern int __dtb_start; - extern unsigned long early_altera_uart_or_juart_console(void); #endif /* _ASM_NIOS2_PROM_H */ diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c index 0ca89fd7b682b..a5aebdcd5882d 100644 --- a/arch/nios2/kernel/prom.c +++ b/arch/nios2/kernel/prom.c @@ -51,19 +51,20 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) void __init early_init_devtree(void *params) { - if (params && be32_to_cpup((__be32 *)params) == OF_DT_HEADER) - initial_boot_params = params; + __be32 *dtb = (u32*)__dtb_start; #if defined(CONFIG_NIOS2_DTB_AT_PHYS_ADDR) - else if (be32_to_cpup((__be32 *)CONFIG_NIOS2_DTB_PHYS_ADDR) == - OF_DT_HEADER) - initial_boot_params = (void *)CONFIG_NIOS2_DTB_PHYS_ADDR; -#endif - else if (be32_to_cpu((__be32)__dtb_start) == OF_DT_HEADER) - initial_boot_params = (void *)&__dtb_start; - else + if (be32_to_cpup((__be32 *)CONFIG_NIOS2_DTB_PHYS_ADDR) == + OF_DT_HEADER) { + params = (void *)CONFIG_NIOS2_DTB_PHYS_ADDR; + early_init_dt_scan(params); return; + } +#endif + if (be32_to_cpu((__be32) *dtb) == OF_DT_HEADER) + params = (void *)__dtb_start; + + early_init_dt_scan(params); - early_init_dt_scan(initial_boot_params); } #ifdef CONFIG_EARLY_PRINTK @@ -98,7 +99,9 @@ static int __init early_init_dt_scan_serial(unsigned long node, return 0; #endif - *addr64 = of_get_flat_dt_translate_address(node); + *addr64 = fdt_translate_address((const void *)initial_boot_params, + node); + return *addr64 == OF_BAD_ADDR ? 0 : 1; } From 829b7ac0d9cf1de3789f08f960eb9915823c7338 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 16 Oct 2012 10:40:32 -0600 Subject: [PATCH 176/201] Merge of 44132140d09af5373ce838a034d35a42fcdd5f01 spi: qspi: cadence: Add spi and qspi driver -Add spi-dw and Cadence QSPI driver. -Add entry in clock driver. Signed-off-by: Dinh Nguyen --- drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 2 + drivers/spi/spi-cadence-qspi-apb.c | 806 +++++++++++++++++++++++++++++ drivers/spi/spi-cadence-qspi-apb.h | 226 ++++++++ drivers/spi/spi-cadence-qspi.c | 562 ++++++++++++++++++++ drivers/spi/spi-cadence-qspi.h | 82 +++ drivers/spi/spi-dw-mmio.c | 50 +- drivers/spi/spi-dw-pl330.c | 223 ++++++++ drivers/spi/spi-dw.c | 47 ++ drivers/spi/spi-dw.h | 3 + include/linux/spi/spi-dw.h | 38 ++ include/linux/spi/spi.h | 2 + 12 files changed, 2045 insertions(+), 6 deletions(-) create mode 100644 drivers/spi/spi-cadence-qspi-apb.c create mode 100644 drivers/spi/spi-cadence-qspi-apb.h create mode 100644 drivers/spi/spi-cadence-qspi.c create mode 100644 drivers/spi/spi-cadence-qspi.h create mode 100644 drivers/spi/spi-dw-pl330.c create mode 100644 include/linux/spi/spi-dw.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 213b5cbb9dcce..e121717aa9e88 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -59,6 +59,12 @@ config SPI_ALTERA help This is the driver for the Altera SPI Controller. +config SPI_CADENCE_QSPI + tristate "Cadence QSPI Controller" + depends on ARCH_SOCFPGA + help + This is the driver for the Cadence QSPI Controller. + config SPI_ATH79 tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver" depends on ATH79 && GPIOLIB @@ -574,6 +580,10 @@ config SPI_DESIGNWARE help general driver for SPI controller core from DesignWare +config SPI_DW_PL330_DMA + bool "DMA support for DW SPI controller through DMA PL330 controller" + depends on SPI_DESIGNWARE && PL330_DMA + config SPI_DW_PCI tristate "PCI interface driver for DW SPI core" depends on SPI_DESIGNWARE && PCI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 929c9f5eac01d..92845baba53ac 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -24,10 +24,12 @@ obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o +obj-$(CONFIG_SPI_CADENCE_QSPI) += spi-cadence-qspi-apb.o spi-cadence-qspi.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o +obj-$(CONFIG_SPI_DW_PL330_DMA) += spi-dw-pl330.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o obj-$(CONFIG_SPI_EFM32) += spi-efm32.o diff --git a/drivers/spi/spi-cadence-qspi-apb.c b/drivers/spi/spi-cadence-qspi-apb.c new file mode 100644 index 0000000000000..275ef289309a9 --- /dev/null +++ b/drivers/spi/spi-cadence-qspi-apb.c @@ -0,0 +1,806 @@ +/* + * Driver for Cadence QSPI Controller + * + * Copyright (C) 2012 Altera Corporation + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include +#include +#include +#include "spi-cadence-qspi.h" +#include "spi-cadence-qspi-apb.h" + +static unsigned int cadence_qspi_apb_cmd2addr(const unsigned char* addr_buf, + unsigned int addr_width) +{ + unsigned int addr; + + addr = (addr_buf[0] << 16) | (addr_buf[1] << 8) | addr_buf[2]; + + if (addr_width == 4) + addr = (addr << 8) | addr_buf[3]; + + return addr; +} + +static void cadence_qspi_apb_read_fifo_data(void *dest, const void *src_ahb_addr, + unsigned int bytes) +{ + unsigned int temp; + int remaining = bytes; + unsigned int *dest_ptr = (unsigned int *)dest; + unsigned int *src_ptr = (unsigned int *)src_ahb_addr; + + while (remaining > 0) { + if (remaining >= CQSPI_FIFO_WIDTH) { + *dest_ptr = CQSPI_READL(src_ptr); + remaining -= CQSPI_FIFO_WIDTH; + } else { + /* dangling bytes */ + temp = CQSPI_READL(src_ptr); + memcpy(dest_ptr, &temp, remaining); + break; + } + dest_ptr++; + } + + return; +} + +static void cadence_qspi_apb_write_fifo_data(void *dest_ahb_addr, + const void *src, unsigned int bytes) +{ + unsigned int temp; + int remaining = bytes; + unsigned int *dest_ptr = (unsigned int *)dest_ahb_addr; + unsigned int *src_ptr = (unsigned int *)src; + + while (remaining > 0) { + if (remaining >= CQSPI_FIFO_WIDTH) { + CQSPI_WRITEL(*src_ptr, dest_ptr); + remaining -= CQSPI_FIFO_WIDTH; + } else { + /* dangling bytes */ + memcpy(&temp, src_ptr, remaining); + CQSPI_WRITEL(temp, dest_ptr); + break; + } + src_ptr++; + } + + return; +} + +/* Return 1 if idle, otherwise return 0 (busy). */ +static unsigned int cadence_qspi_wait_idle(void * reg_base) { + unsigned int count = 0; + unsigned timeout; + + timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS); + while (cadence_qspi_check_timeout(timeout)) { + if (CQSPI_REG_IS_IDLE(reg_base)) { + /* Read few times in succession to ensure it does + not transition low again */ + count++; + if (count >= CQSPI_POLL_IDLE_RETRY) + return 1; + } else { + count = 0; + } + } + + /* Timeout, in busy mode. */ + pr_err("QSPI: QSPI is still busy after %dms timeout.\n", + CQSPI_TIMEOUT_MS); + return 0; +} + +static void cadence_qspi_apb_readdata_capture(void *reg_base, + unsigned int bypass, unsigned int delay) +{ + unsigned int reg; + + cadence_qspi_apb_controller_disable(reg_base); + reg = CQSPI_READL(reg_base + CQSPI_REG_READCAPTURE); + + if (bypass) { + reg |= (1 << CQSPI_REG_READCAPTURE_BYPASS_LSB); + } else { + reg &= ~(1 << CQSPI_REG_READCAPTURE_BYPASS_LSB); + } + + reg &= ~(CQSPI_REG_READCAPTURE_DELAY_MASK + << CQSPI_REG_READCAPTURE_DELAY_LSB); + + reg |= ((delay & CQSPI_REG_READCAPTURE_DELAY_MASK) + << CQSPI_REG_READCAPTURE_DELAY_LSB); + + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_READCAPTURE); + + cadence_qspi_apb_controller_enable(reg_base); + return; +} + +static void cadence_qspi_apb_config_baudrate_div(void *reg_base, + unsigned int ref_clk_hz, unsigned int sclk_hz) +{ + unsigned int reg; + unsigned int div; + + cadence_qspi_apb_controller_disable(reg_base); + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); + reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB); + + div = ref_clk_hz / sclk_hz; + + /* Recalculate the baudrate divisor based on QSPI specification. */ + if (div > 32) + div = 32; + + /* Check if even number. */ + if (div & 1) + div = (div / 2); + else + div = (div / 2) - 1; + + pr_debug("QSPI: ref_clk %dHz sclk %dHz div 0x%x\n", ref_clk_hz, + sclk_hz, div); + + div = (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB; + reg |= div; + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); + + cadence_qspi_apb_controller_enable(reg_base); + return; +} + +static void cadence_qspi_apb_chipselect(void * reg_base, + unsigned int chip_select, unsigned int decoder_enable) +{ + unsigned int reg; + + cadence_qspi_apb_controller_disable(reg_base); + + pr_debug("QSPI: chipselect %d decode %d\n", chip_select, + decoder_enable); + + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); + /* docoder */ + if (decoder_enable) + reg |= CQSPI_REG_CONFIG_DECODE_MASK; + else { + reg &= ~CQSPI_REG_CONFIG_DECODE_MASK; + + /* Convert CS if without decoder. + * CS0 to 4b'1110 + * CS1 to 4b'1101 + * CS2 to 4b'1011 + * CS3 to 4b'0111 + */ + chip_select = 0xF & ~(1 << chip_select); + } + + reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK + << CQSPI_REG_CONFIG_CHIPSELECT_LSB); + reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK) + << CQSPI_REG_CONFIG_CHIPSELECT_LSB; + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); + + cadence_qspi_apb_controller_enable(reg_base); + return; +} + +static int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg) +{ + unsigned int timeout; + + /* Write the CMDCTRL without start execution. */ + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CMDCTRL); + /* Start execute */ + reg |= CQSPI_REG_CMDCTRL_EXECUTE_MASK; + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CMDCTRL); + + /* Polling for completion. */ + timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS); + while (cadence_qspi_check_timeout(timeout)) { + reg = CQSPI_READL(reg_base + CQSPI_REG_CMDCTRL) & + CQSPI_REG_CMDCTRL_INPROGRESS_MASK; + if (!reg) + break; + } + + if (reg != 0) { + pr_err("QSPI: flash cmd execute time out\n"); + return -EIO; + } + + /* Polling QSPI idle status. */ + if (!cadence_qspi_wait_idle(reg_base)) + return -EIO; + + return 0; +} + +/* For command RDID, RDSR. */ +static int cadence_qspi_apb_command_read(void *reg_base, + unsigned int txlen, const unsigned char *txbuf, + unsigned rxlen, unsigned char *rxbuf) +{ + unsigned int reg; + unsigned int read_len; + int status; + + if (!rxlen || rxlen > CQSPI_STIG_DATA_LEN_MAX || rxbuf == NULL) { + pr_err("QSPI: Invalid input argument, len %d rxbuf 0x%08x\n", + rxlen, (unsigned int)rxbuf); + return -EINVAL; + } + + reg = txbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; + + reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); + + /* 0 means 1 byte. */ + reg |= (((rxlen - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK) + << CQSPI_REG_CMDCTRL_RD_BYTES_LSB); + status = cadence_qspi_apb_exec_flash_cmd(reg_base, reg); + if (status != 0) + return status; + + reg = CQSPI_READL(reg_base + CQSPI_REG_CMDREADDATALOWER); + + /* Put the read value into rx_buf */ + read_len = (rxlen > 4) ? 4 : rxlen; + memcpy(rxbuf, ®, read_len); + rxbuf += read_len; + + if (rxlen > 4) { + reg = CQSPI_READL(reg_base + CQSPI_REG_CMDREADDATAUPPER); + + read_len = rxlen - read_len; + memcpy(rxbuf, ®, read_len); + } + + return 0; +} + +/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */ +static int cadence_qspi_apb_command_write(void *reg_base, unsigned txlen, + const unsigned char *txbuf) +{ + unsigned int reg; + unsigned int addr_value; + unsigned int data; + + if (!txlen || txlen > 5 || txbuf == NULL) { + pr_err("QSPI: Invalid input argument, cmdlen %d txbuf 0x%08x\n", + txlen, (unsigned int)txbuf); + return -EINVAL; + } + + reg = txbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; + if (txlen == 2 || txlen == 3) { + /* Command with data only. */ + reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB); + reg |= ((txlen - 2) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK) + << CQSPI_REG_CMDCTRL_WR_BYTES_LSB; + + memcpy(&data, &txbuf[1], txlen - 1); + /* Write the data */ + CQSPI_WRITEL(data, reg_base + CQSPI_REG_CMDWRITEDATALOWER); + } + else if (txlen == 4 || txlen == 5) { + /* Command with address */ + reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); + /* Number of bytes to write. */ + reg |= ((txlen - 2) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) + << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; + /* Get address */ + addr_value = cadence_qspi_apb_cmd2addr(&txbuf[1], + txlen >=5 ? 4 : 3); + + CQSPI_WRITEL(addr_value, reg_base + CQSPI_REG_CMDADDRESS); + } + + return cadence_qspi_apb_exec_flash_cmd(reg_base, reg); +} + +static int cadence_qspi_apb_indirect_read_setup(void *reg_base, + unsigned int ahb_phy_addr, unsigned txlen, const unsigned char *txbuf, + unsigned int addr_bytes) +{ + unsigned int reg; + unsigned int addr_value; + unsigned int dummy_clk; + unsigned int dummy_bytes; + + if ((addr_bytes == 3 && txlen < 4) || (addr_bytes == 4 && txlen < 5)) { + pr_err("QSPI: Invalid txbuf length, length %d\n", txlen); + return -EINVAL; + } + + CQSPI_WRITEL((ahb_phy_addr & CQSPI_INDIRECTTRIGGER_ADDR_MASK), + reg_base + CQSPI_REG_INDIRECTTRIGGER); + + reg = txbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB; + +#ifdef CONFIG_M25PXX_USE_FAST_READ_QUAD_OUTPUT + reg |= (CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB); +#endif /* CONFIG_M25PXX_USE_FAST_READ_QUAD_OUTPUT */ + + /* Get address */ + addr_value = cadence_qspi_apb_cmd2addr(&txbuf[1], addr_bytes); + CQSPI_WRITEL(addr_value, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR); + + /* The remaining lenght is dummy bytes. */ + dummy_bytes = txlen - addr_bytes - 1; + + /* Setup dummy clock cycles */ + if (dummy_bytes) { + + if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX) + dummy_bytes = CQSPI_DUMMY_BYTES_MAX; + + reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB); + /* Set all high to ensure chip doesn't enter XIP */ + CQSPI_WRITEL(0xFF, reg_base + CQSPI_REG_MODE_BIT); + + /* Convert to clock cycles. */ + dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE; + /* Need to minus the mode byte (8 clocks). */ + dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE; + + if (dummy_clk) + reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK) + << CQSPI_REG_RD_INSTR_DUMMY_LSB; + } + + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_RD_INSTR); + + /* Set device size */ + reg = CQSPI_READL(reg_base + CQSPI_REG_SIZE); + reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; + reg |= (addr_bytes - 1); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_SIZE); + return 0; +} + +static int cadence_qspi_apb_indirect_read_execute( + struct struct_cqspi *cadence_qspi, unsigned rxlen, + unsigned char *rxbuf) +{ + unsigned int reg = 0; + unsigned int timeout; + unsigned int watermark = CQSPI_REG_SRAM_THRESHOLD_BYTES; + unsigned int *irq_status = &(cadence_qspi->irq_status); + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + void *reg_base = cadence_qspi->iobase; + void *ahb_base = cadence_qspi->qspi_ahb_virt; + int remaining = (int)rxlen; + int ret = 0; + + if (remaining < watermark) + watermark = remaining; + + CQSPI_WRITEL(watermark, reg_base + CQSPI_REG_INDIRECTRDWATERMARK); + CQSPI_WRITEL(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES); + CQSPI_WRITEL(pdata->fifo_depth - CQSPI_REG_SRAM_RESV_WORDS, + reg_base + CQSPI_REG_SRAMPARTITION); + + /* Clear all interrupts. */ + CQSPI_WRITEL(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS); + + CQSPI_WRITEL(CQSPI_IRQ_MASK_RD, reg_base + CQSPI_REG_IRQMASK); + + CQSPI_WRITEL(CQSPI_REG_INDIRECTRD_START_MASK, + reg_base + CQSPI_REG_INDIRECTRD); + + while (remaining > 0) { + ret = wait_event_interruptible_timeout(cadence_qspi->waitqueue, + *irq_status, CQSPI_TIMEOUT_MS); + if (!ret) { + pr_err("QSPI: Indirect read timeout\n"); + ret = -ETIMEDOUT; + goto failrd; + } + + if (*irq_status & CQSPI_IRQ_STATUS_ERR) { + /* Error occurred */ + pr_err("QSPI: Indirect read error IRQ status 0x%08x\n", + *irq_status); + ret = -EPERM; + goto failrd; + } + + if (*irq_status & (CQSPI_REG_IRQ_IND_RD_OVERFLOW | + CQSPI_REG_IRQ_IND_COMP | CQSPI_REG_IRQ_WATERMARK)) { + + reg = CQSPI_GET_RD_SRAM_LEVEL(reg_base); + /* convert to bytes */ + reg *= CQSPI_FIFO_WIDTH; + reg = reg > remaining ? remaining : reg; + /* Read data from FIFO. */ + cadence_qspi_apb_read_fifo_data(rxbuf, ahb_base, reg); + rxbuf += reg; + remaining -= reg; + } + } + + /* Check indirect done status */ + timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS); + while (cadence_qspi_check_timeout(timeout)) { + reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTRD); + if (reg & CQSPI_REG_INDIRECTRD_DONE_MASK) + break; + } + + if (!(reg & CQSPI_REG_INDIRECTRD_DONE_MASK)) { + pr_err("QSPI : Indirect read completion status error with " + "reg 0x%08x\n", reg); + ret = -ETIMEDOUT; + goto failrd; + } + + /* Disable interrupt */ + CQSPI_WRITEL(0, reg_base + CQSPI_REG_IRQMASK); + + /* Clear indirect completion status */ + CQSPI_WRITEL(CQSPI_REG_INDIRECTRD_DONE_MASK, + reg_base + CQSPI_REG_INDIRECTRD); + return 0; + +failrd: + /* Disable interrupt */ + CQSPI_WRITEL(0, reg_base + CQSPI_REG_IRQMASK); + + /* Cancel the indirect read */ + CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_CANCEL_MASK, + reg_base + CQSPI_REG_INDIRECTRD); + return ret; +} + +static int cadence_qspi_apb_indirect_write_setup(void *reg_base, + unsigned int ahb_phy_addr, unsigned txlen, const unsigned char *txbuf) +{ + unsigned int reg; + unsigned int addr_bytes = (txlen >= 5) ? 4: 3; + + if (txlen < 4 || txbuf == NULL) { + pr_err("QSPI: Invalid input argument, txlen %d txbuf 0x%08x\n", + txlen, (unsigned int)txbuf); + return -EINVAL; + } + + CQSPI_WRITEL( (ahb_phy_addr & CQSPI_INDIRECTTRIGGER_ADDR_MASK), + reg_base + CQSPI_REG_INDIRECTTRIGGER); + + /* Set opcode. */ + reg = txbuf[0] << CQSPI_REG_WR_INSTR_OPCODE_LSB; + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_WR_INSTR); + + /* Setup write address. */ + reg = cadence_qspi_apb_cmd2addr(&txbuf[1], addr_bytes); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR); + + reg = CQSPI_READL(reg_base + CQSPI_REG_SIZE); + reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; + reg |= (addr_bytes - 1); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_SIZE); + return 0; +} + +static int cadence_qspi_apb_indirect_write_execute( + struct struct_cqspi *cadence_qspi, unsigned txlen, + const unsigned char *txbuf) +{ + int ret; + unsigned int timeout; + unsigned int reg = 0; + unsigned int *irq_status = &(cadence_qspi->irq_status); + void *reg_base = cadence_qspi->iobase; + void *ahb_base = cadence_qspi->qspi_ahb_virt; + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + struct cqspi_flash_pdata *f_pdata = + &(pdata->f_pdata[cadence_qspi->current_cs]); + unsigned int page_size = f_pdata->page_size; + int remaining = (int)txlen; + unsigned int write_bytes; + + CQSPI_WRITEL(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES); + + CQSPI_WRITEL(CQSPI_REG_SRAM_THRESHOLD_BYTES, reg_base + + CQSPI_REG_INDIRECTWRWATERMARK); + + CQSPI_WRITEL(CQSPI_REG_SRAM_PARTITION_WR, + reg_base + CQSPI_REG_SRAMPARTITION); + + /* Clear all interrupts. */ + CQSPI_WRITEL(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS); + + CQSPI_WRITEL(CQSPI_IRQ_MASK_WR, reg_base + CQSPI_REG_IRQMASK); + + CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_START_MASK, + reg_base + CQSPI_REG_INDIRECTWR); + + /* Write a page or remaining bytes. */ + write_bytes = remaining > page_size ? page_size : remaining; + /* Fill up the data at the begining */ + cadence_qspi_apb_write_fifo_data(ahb_base, txbuf, write_bytes); + txbuf += write_bytes; + remaining -= write_bytes; + + while (remaining > 0) { + ret = wait_event_interruptible_timeout(cadence_qspi->waitqueue, + *irq_status, CQSPI_TIMEOUT_MS); + if (!ret) { + pr_err("QSPI: Indirect write timeout\n"); + ret = -ETIMEDOUT; + goto failwr; + } + + if (*irq_status & CQSPI_IRQ_STATUS_ERR) { + /* Error occurred */ + pr_err("QSPI : Indirect write error" + "IRQ status 0x%08x\n", *irq_status); + ret = -EPERM; + goto failwr; + } + + if (*irq_status & (CQSPI_REG_IRQ_UNDERFLOW | + CQSPI_REG_IRQ_IND_COMP | CQSPI_REG_IRQ_WATERMARK)){ + /* Calculate number of bytes to write. */ + write_bytes = remaining > page_size ? + page_size : remaining; + + cadence_qspi_apb_write_fifo_data(ahb_base, txbuf, + write_bytes); + txbuf += write_bytes; + remaining -= write_bytes; + } + } + + /* Check indirect done status */ + timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS); + while (cadence_qspi_check_timeout(timeout)) { + reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTWR); + if (reg & CQSPI_REG_INDIRECTWR_DONE_MASK) + break; + } + + if (!(reg & CQSPI_REG_INDIRECTWR_DONE_MASK)) { + pr_err("QSPI: Indirect write completion status error with " + "reg 0x%08x\n", reg); + ret = -ETIMEDOUT; + goto failwr; + } + + /* Disable interrupt. */ + CQSPI_WRITEL(0, reg_base + CQSPI_REG_IRQMASK); + + /* Clear indirect completion status */ + CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_DONE_MASK, + reg_base + CQSPI_REG_INDIRECTWR); + return 0; + +failwr: + /* Disable interrupt. */ + CQSPI_WRITEL(0, reg_base + CQSPI_REG_IRQMASK); + + /* Cancel the indirect write */ + CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_CANCEL_MASK, + reg_base + CQSPI_REG_INDIRECTWR); + return ret; +} + +void cadence_qspi_apb_controller_enable(void *reg_base) +{ + unsigned int reg; + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_ENABLE_MASK; + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); + return; +} + +void cadence_qspi_apb_controller_disable(void *reg_base) +{ + unsigned int reg; + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); + reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK; + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); + return; +} + +unsigned int cadence_qspi_apb_is_controller_ready(void *reg_base) +{ + return cadence_qspi_wait_idle(reg_base); +} + +void cadence_qspi_apb_controller_init(struct struct_cqspi *cadence_qspi) +{ + cadence_qspi_apb_controller_disable(cadence_qspi->iobase); + + /* Configure the remap address register, no remap */ + CQSPI_WRITEL(0, cadence_qspi->iobase + CQSPI_REG_REMAP); + + /* Disable all interrupts. */ + CQSPI_WRITEL(0, cadence_qspi->iobase + CQSPI_REG_IRQMASK); + + /* Set read data capture to default. */ + cadence_qspi_apb_readdata_capture(cadence_qspi->iobase, 1, 0); + + cadence_qspi_apb_controller_enable(cadence_qspi->iobase); + return; +} + +void cadence_qspi_apb_delay(struct struct_cqspi *cadence_qspi, + unsigned int ref_clk, unsigned int sclk_hz) +{ + void __iomem *iobase = cadence_qspi->iobase; + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + struct cqspi_flash_pdata *f_pdata = + &(pdata->f_pdata[cadence_qspi->current_cs]); + unsigned int ref_clk_ns; + unsigned int sclk_ns; + unsigned int tshsl, tchsh, tslch, tsd2d; + unsigned int reg; + + cadence_qspi_apb_controller_disable(iobase); + + /* Convert to ns. */ + ref_clk_ns = (1000000000) / pdata->master_ref_clk_hz; + + /* Convert to ns. */ + sclk_ns = (1000000000) / sclk_hz; + + /* Plus 1 to round up 1 clock cycle. */ + tshsl = CQSPI_CAL_DELAY(f_pdata->tshsl_ns, ref_clk_ns, sclk_ns) + 1; + tchsh = CQSPI_CAL_DELAY(f_pdata->tchsh_ns, ref_clk_ns, sclk_ns) + 1; + tslch = CQSPI_CAL_DELAY(f_pdata->tslch_ns, ref_clk_ns, sclk_ns) + 1; + tsd2d = CQSPI_CAL_DELAY(f_pdata->tsd2d_ns, ref_clk_ns, sclk_ns) + 1; + + reg = ((tshsl & CQSPI_REG_DELAY_TSHSL_MASK) + << CQSPI_REG_DELAY_TSHSL_LSB); + reg |= ((tchsh & CQSPI_REG_DELAY_TCHSH_MASK) + << CQSPI_REG_DELAY_TCHSH_LSB); + reg |= ((tslch & CQSPI_REG_DELAY_TSLCH_MASK) + << CQSPI_REG_DELAY_TSLCH_LSB); + reg |= ((tsd2d & CQSPI_REG_DELAY_TSD2D_MASK) + << CQSPI_REG_DELAY_TSD2D_LSB); + CQSPI_WRITEL(reg, iobase + CQSPI_REG_DELAY); + + cadence_qspi_apb_controller_enable(iobase); + return; +} + +void cadence_qspi_switch_cs(struct struct_cqspi *cadence_qspi, + unsigned int cs) +{ + unsigned int reg; + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + struct cqspi_flash_pdata *f_pdata = &(pdata->f_pdata[cs]); + void __iomem *iobase = cadence_qspi->iobase; + + cadence_qspi_apb_controller_disable(iobase); + + /* Configure page size and block size. */ + reg = CQSPI_READL(iobase + CQSPI_REG_SIZE); + /* clear the previous value */ + reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB); + reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB); + reg |= (f_pdata->page_size << CQSPI_REG_SIZE_PAGE_LSB); + reg |= (f_pdata->block_size << CQSPI_REG_SIZE_BLOCK_LSB); + CQSPI_WRITEL(reg, iobase + CQSPI_REG_SIZE); + + /* configure the chip select */ + cadence_qspi_apb_chipselect (iobase, cs, pdata->ext_decoder); + cadence_qspi_apb_controller_enable(iobase); + return; +} + +int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, + struct spi_device *spi, unsigned int n_trans, + struct spi_transfer **spi_xfer) +{ + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + struct spi_transfer *cmd_xfer = spi_xfer[0]; + struct spi_transfer *data_xfer = (n_trans >= 2) ? spi_xfer[1] : NULL; + void __iomem *iobase = cadence_qspi->iobase; + unsigned int sclk; + int ret = 0; + + if (!cmd_xfer->len) { + pr_err("QSPI: SPI transfer length is 0.\n"); + return -EINVAL; + } + + /* Setup baudrate divisor. */ + sclk = cmd_xfer->speed_hz ? cmd_xfer->speed_hz : spi->max_speed_hz; + cadence_qspi_apb_config_baudrate_div(iobase, + pdata->master_ref_clk_hz, sclk); + + /* Switch chip select. */ + if (cadence_qspi->current_cs != spi->chip_select) { + cadence_qspi->current_cs = spi->chip_select; + cadence_qspi_switch_cs(cadence_qspi, spi->chip_select); + } + + /* Configure device delay if we change device clock. */ + cadence_qspi_apb_delay(cadence_qspi, pdata->master_ref_clk_hz, sclk); + + /* + * Use STIG command to send if the transfer length is less than + * 4 or if only one transfer. + */ + if ((cmd_xfer->len < 4) || (n_trans == 1)) { + /* STIG command */ + if (data_xfer && data_xfer->rx_buf) { + /* STIG read */ + ret = cadence_qspi_apb_command_read(iobase, + cmd_xfer->len, cmd_xfer->tx_buf, + data_xfer->len, data_xfer->rx_buf); + } else { + /* STIG write */ + ret = cadence_qspi_apb_command_write(iobase, + cmd_xfer->len, cmd_xfer->tx_buf); + } + } else if (cmd_xfer->len >= 4 && (n_trans == 2)){ + /* Indirect operation */ + if (data_xfer->rx_buf) { + /* Indirect read */ + ret = cadence_qspi_apb_indirect_read_setup(iobase, + pdata->qspi_ahb_phy, cmd_xfer->len, + cmd_xfer->tx_buf, spi->addr_width); + ret = cadence_qspi_apb_indirect_read_execute( + cadence_qspi, data_xfer->len, + data_xfer->rx_buf); + } else { + /* Indirect write */ + ret = cadence_qspi_apb_indirect_write_setup( + iobase, pdata->qspi_ahb_phy, + cmd_xfer->len, cmd_xfer->tx_buf); + ret = cadence_qspi_apb_indirect_write_execute( + cadence_qspi, data_xfer->len, + data_xfer->tx_buf); + } + } else { + pr_err("QSPI : Unknown SPI transfer.\n"); + return -EINVAL; + } + return ret; +} + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/spi/spi-cadence-qspi-apb.h b/drivers/spi/spi-cadence-qspi-apb.h new file mode 100644 index 0000000000000..ff7f56bbf122c --- /dev/null +++ b/drivers/spi/spi-cadence-qspi-apb.h @@ -0,0 +1,226 @@ +/* + * Driver for Cadence QSPI Controller + * + * Copyright (C) 2012 Altera Corporation + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#ifndef __CADENCE_QSPI_APB_H__ +#define __CADENCE_QSPI_APB_H__ + +#include "spi-cadence-qspi.h" + +/* Operation timeout value */ +#define CQSPI_TIMEOUT_MS (5000) +#define CQSPI_POLL_IDLE_RETRY (3) + +#define CQSPI_FIFO_WIDTH (4) + +/* Controller sram size in word */ +#define CQSPI_REG_SRAM_RESV_WORDS (2) +#define CQSPI_REG_SRAM_PARTITION_WR (1) + +#define CQSPI_REG_SRAM_THRESHOLD_BYTES (50) + +/* Instruction type */ +#define CQSPI_INST_TYPE_SINGLE (0) +#define CQSPI_INST_TYPE_DUAL (1) +#define CQSPI_INST_TYPE_QUAD (2) + +#define CQSPI_DUMMY_CLKS_PER_BYTE (8) +#define CQSPI_DUMMY_BYTES_MAX (4) + +#define CQSPI_STIG_DATA_LEN_MAX (8) + +#define CQSPI_INDIRECTTRIGGER_ADDR_MASK (0xFFFFF) + +/* Register map */ +#define CQSPI_REG_CONFIG 0x00 +#define CQSPI_REG_CONFIG_ENABLE_MASK (1 << 0) +#define CQSPI_REG_CONFIG_DECODE_MASK (1 << 9) +#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10 +#define CQSPI_REG_CONFIG_DMA_MASK (1 << 15) +#define CQSPI_REG_CONFIG_BAUD_LSB 19 +#define CQSPI_REG_CONFIG_IDLE_LSB 31 +#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF +#define CQSPI_REG_CONFIG_BAUD_MASK 0xF + +#define CQSPI_REG_RD_INSTR 0x04 +#define CQSPI_REG_RD_INSTR_OPCODE_LSB 0 +#define CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB 8 +#define CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB 12 +#define CQSPI_REG_RD_INSTR_TYPE_DATA_LSB 16 +#define CQSPI_REG_RD_INSTR_MODE_EN_LSB 20 +#define CQSPI_REG_RD_INSTR_DUMMY_LSB 24 +#define CQSPI_REG_RD_INSTR_TYPE_INSTR_MASK 0x3 +#define CQSPI_REG_RD_INSTR_TYPE_ADDR_MASK 0x3 +#define CQSPI_REG_RD_INSTR_TYPE_DATA_MASK 0x3 +#define CQSPI_REG_RD_INSTR_DUMMY_MASK 0x1F + +#define CQSPI_REG_WR_INSTR 0x08 +#define CQSPI_REG_WR_INSTR_OPCODE_LSB 0 + +#define CQSPI_REG_DELAY 0x0C +#define CQSPI_REG_DELAY_TSLCH_LSB 0 +#define CQSPI_REG_DELAY_TCHSH_LSB 8 +#define CQSPI_REG_DELAY_TSD2D_LSB 16 +#define CQSPI_REG_DELAY_TSHSL_LSB 24 +#define CQSPI_REG_DELAY_TSLCH_MASK 0xFF +#define CQSPI_REG_DELAY_TCHSH_MASK 0xFF +#define CQSPI_REG_DELAY_TSD2D_MASK 0xFF +#define CQSPI_REG_DELAY_TSHSL_MASK 0xFF + +#define CQSPI_REG_READCAPTURE 0x10 +#define CQSPI_REG_READCAPTURE_BYPASS_LSB 0 +#define CQSPI_REG_READCAPTURE_DELAY_LSB 1 +#define CQSPI_REG_READCAPTURE_DELAY_MASK 0xF + +#define CQSPI_REG_SIZE 0x14 +#define CQSPI_REG_SIZE_ADDRESS_LSB 0 +#define CQSPI_REG_SIZE_PAGE_LSB 4 +#define CQSPI_REG_SIZE_BLOCK_LSB 16 +#define CQSPI_REG_SIZE_ADDRESS_MASK 0xF +#define CQSPI_REG_SIZE_PAGE_MASK 0xFFF +#define CQSPI_REG_SIZE_BLOCK_MASK 0x3F + +#define CQSPI_REG_SRAMPARTITION 0x18 +#define CQSPI_REG_INDIRECTTRIGGER 0x1C + +#define CQSPI_REG_DMA 0x20 +#define CQSPI_REG_DMA_SINGLE_LSB 0 +#define CQSPI_REG_DMA_BURST_LSB 8 +#define CQSPI_REG_DMA_SINGLE_MASK 0xFF +#define CQSPI_REG_DMA_BURST_MASK 0xFF + +#define CQSPI_REG_REMAP 0x24 +#define CQSPI_REG_MODE_BIT 0x28 + +#define CQSPI_REG_SDRAMLEVEL 0x2C +#define CQSPI_REG_SDRAMLEVEL_RD_LSB 0 +#define CQSPI_REG_SDRAMLEVEL_WR_LSB 16 +#define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF +#define CQSPI_REG_SDRAMLEVEL_WR_MASK 0xFFFF + +#define CQSPI_REG_IRQSTATUS 0x40 +#define CQSPI_REG_IRQMASK 0x44 + +#define CQSPI_REG_INDIRECTRD 0x60 +#define CQSPI_REG_INDIRECTRD_START_MASK (1 << 0) +#define CQSPI_REG_INDIRECTRD_CANCEL_MASK (1 << 1) +#define CQSPI_REG_INDIRECTRD_DONE_MASK (1 << 5) + +#define CQSPI_REG_INDIRECTRDWATERMARK 0x64 +#define CQSPI_REG_INDIRECTRDSTARTADDR 0x68 +#define CQSPI_REG_INDIRECTRDBYTES 0x6C + +#define CQSPI_REG_CMDCTRL 0x90 +#define CQSPI_REG_CMDCTRL_EXECUTE_MASK (1 << 0) +#define CQSPI_REG_CMDCTRL_INPROGRESS_MASK (1 << 1) +#define CQSPI_REG_CMDCTRL_WR_BYTES_LSB 12 +#define CQSPI_REG_CMDCTRL_WR_EN_LSB 15 +#define CQSPI_REG_CMDCTRL_ADD_BYTES_LSB 16 +#define CQSPI_REG_CMDCTRL_ADDR_EN_LSB 19 +#define CQSPI_REG_CMDCTRL_RD_BYTES_LSB 20 +#define CQSPI_REG_CMDCTRL_RD_EN_LSB 23 +#define CQSPI_REG_CMDCTRL_OPCODE_LSB 24 +#define CQSPI_REG_CMDCTRL_WR_BYTES_MASK 0x7 +#define CQSPI_REG_CMDCTRL_ADD_BYTES_MASK 0x3 +#define CQSPI_REG_CMDCTRL_RD_BYTES_MASK 0x7 + +#define CQSPI_REG_INDIRECTWR 0x70 +#define CQSPI_REG_INDIRECTWR_START_MASK (1 << 0) +#define CQSPI_REG_INDIRECTWR_CANCEL_MASK (1 << 1) +#define CQSPI_REG_INDIRECTWR_DONE_MASK (1 << 5) + +#define CQSPI_REG_INDIRECTWRWATERMARK 0x74 +#define CQSPI_REG_INDIRECTWRSTARTADDR 0x78 +#define CQSPI_REG_INDIRECTWRBYTES 0x7C + +#define CQSPI_REG_CMDADDRESS 0x94 +#define CQSPI_REG_CMDREADDATALOWER 0xA0 +#define CQSPI_REG_CMDREADDATAUPPER 0xA4 +#define CQSPI_REG_CMDWRITEDATALOWER 0xA8 +#define CQSPI_REG_CMDWRITEDATAUPPER 0xAC + +/* Interrupt status bits */ +#define CQSPI_REG_IRQ_MODE_ERR (1 << 0) +#define CQSPI_REG_IRQ_UNDERFLOW (1 << 1) +#define CQSPI_REG_IRQ_IND_COMP (1 << 2) +#define CQSPI_REG_IRQ_IND_RD_REJECT (1 << 3) +#define CQSPI_REG_IRQ_WR_PROTECTED_ERR (1 << 4) +#define CQSPI_REG_IRQ_ILLEGAL_AHB_ERR (1 << 5) +#define CQSPI_REG_IRQ_WATERMARK (1 << 6) +#define CQSPI_REG_IRQ_IND_RD_OVERFLOW (1 << 12) + +#define CQSPI_IRQ_STATUS_ERR (CQSPI_REG_IRQ_MODE_ERR | \ + CQSPI_REG_IRQ_IND_RD_REJECT | \ + CQSPI_REG_IRQ_WR_PROTECTED_ERR | \ + CQSPI_REG_IRQ_ILLEGAL_AHB_ERR) + +#define CQSPI_IRQ_MASK_RD (CQSPI_REG_IRQ_MODE_ERR | \ + CQSPI_REG_IRQ_IND_RD_REJECT | \ + CQSPI_REG_IRQ_WATERMARK | \ + CQSPI_REG_IRQ_IND_RD_OVERFLOW | \ + CQSPI_REG_IRQ_IND_COMP) + +#define CQSPI_IRQ_MASK_WR (CQSPI_REG_IRQ_MODE_ERR | \ + CQSPI_REG_IRQ_WR_PROTECTED_ERR | \ + CQSPI_REG_IRQ_IND_COMP | \ + CQSPI_REG_IRQ_WATERMARK | \ + CQSPI_REG_IRQ_UNDERFLOW) + +#define CQSPI_IRQ_STATUS_MASK (0xFFFFFFFF) + +#define CQSPI_REG_IS_IDLE(base) \ + ((CQSPI_READL(base + CQSPI_REG_CONFIG) >> \ + CQSPI_REG_CONFIG_IDLE_LSB) & 0x1) + +#define CQSPI_CAL_DELAY(tdelay_ns, tref_ns, tsclk_ns) \ + ((((tdelay_ns) - (tsclk_ns)) / (tref_ns))) + +#define CQSPI_GET_RD_SRAM_LEVEL(reg_basse) \ + (((CQSPI_READL(reg_base + CQSPI_REG_SDRAMLEVEL)) >> \ + CQSPI_REG_SDRAMLEVEL_RD_LSB) & CQSPI_REG_SDRAMLEVEL_RD_MASK) + +#define CQSPI_READ_IRQ_STATUS(reg_base) \ + CQSPI_READL(reg_base + CQSPI_REG_IRQSTATUS) + +#define CQSPI_CLEAR_IRQ(reg_base, status) \ + CQSPI_WRITEL(status, reg_base + CQSPI_REG_IRQSTATUS) + +/* Functions call declaration */ +unsigned int cadence_qspi_apb_is_controller_ready(void *reg_base_addr); +void cadence_qspi_apb_controller_init(struct struct_cqspi *cadence_qspi); +int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, + struct spi_device *spi, unsigned int n_trans, + struct spi_transfer **spi_xfer); +void cadence_qspi_apb_controller_enable(void *reg_base_addr); +void cadence_qspi_apb_controller_disable(void *reg_base_addr); +#endif /* __CADENCE_QSPI_APB_H__ */ diff --git a/drivers/spi/spi-cadence-qspi.c b/drivers/spi/spi-cadence-qspi.c new file mode 100644 index 0000000000000..411f86cc488b2 --- /dev/null +++ b/drivers/spi/spi-cadence-qspi.c @@ -0,0 +1,562 @@ +/* + * Driver for Cadence QSPI Controller + * + * Copyright (C) 2012 Altera Corporation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spi-cadence-qspi.h" +#include "spi-cadence-qspi-apb.h" + +#define CADENCE_QSPI_NAME "cadence-qspi" + +unsigned int cadence_qspi_init_timeout(const unsigned long timeout_in_ms) +{ + return (jiffies + msecs_to_jiffies(timeout_in_ms)); +} + +unsigned int cadence_qspi_check_timeout(const unsigned long timeout) +{ + return (time_before(jiffies, timeout)); +} + +static irqreturn_t cadence_qspi_irq_handler(int this_irq, void *dev) +{ + struct struct_cqspi *cadence_qspi = dev; + + /* Read interrupt status */ + cadence_qspi->irq_status = CQSPI_READ_IRQ_STATUS(cadence_qspi->iobase); + + /* Clear interrupt */ + CQSPI_CLEAR_IRQ(cadence_qspi->iobase, cadence_qspi->irq_status); + + wake_up(&cadence_qspi->waitqueue); + + return IRQ_HANDLED; +} + +static void cadence_qspi_work(struct work_struct *work) +{ + struct struct_cqspi *cadence_qspi + = container_of(work, struct struct_cqspi, work); + unsigned long flags; + + spin_lock_irqsave(&cadence_qspi->lock, flags); + while ((!list_empty(&cadence_qspi->msg_queue)) && + cadence_qspi->running) { + struct spi_message *spi_msg; + struct spi_device *spi; + struct spi_transfer *spi_xfer; + struct spi_transfer *xfer[CQSPI_MAX_TRANS]; + int status = 0; + int n_trans = 0; + int next_in_queue = 0; + + spi_msg = container_of(cadence_qspi->msg_queue.next, + struct spi_message, queue); + list_del_init(&spi_msg->queue); + spin_unlock_irqrestore(&cadence_qspi->lock, flags); + spi = spi_msg->spi; + list_for_each_entry(spi_xfer, &spi_msg->transfers, + transfer_list) { + if (n_trans >= CQSPI_MAX_TRANS) { + dev_err(&spi->dev,"ERROR: Number of SPI " + "transfer is more than %d.\n", + CQSPI_MAX_TRANS); + /* Skip process the queue if number of + * transaction is greater than max 2. */ + next_in_queue = 1; + break; + } + xfer[n_trans++] = spi_xfer; + } + + /* Continue to next queue if next_in_queue is set. */ + if (next_in_queue) + continue; + + status = cadence_qspi_apb_process_queue(cadence_qspi, spi, + n_trans, xfer); + + if (!status) { + spi_msg->actual_length += xfer[0]->len; + if (n_trans > 1) + spi_msg->actual_length += xfer[1]->len; + } + + spi_msg->status = status; + spi_msg->complete(spi_msg->context); + spin_lock_irqsave(&cadence_qspi->lock, flags); + } + spin_unlock_irqrestore(&cadence_qspi->lock, flags); +} + +static int cadence_qspi_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct struct_cqspi *cadence_qspi = + spi_master_get_devdata(spi->master); + struct spi_transfer *spi_xfer; + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + unsigned long flags; + + list_for_each_entry(spi_xfer, &msg->transfers, transfer_list) { + if (spi_xfer->speed_hz > (pdata->master_ref_clk_hz / 2)) { + dev_err(&spi->dev, "speed_hz%d greater than " + "maximum %dHz\n", + spi_xfer->speed_hz, + (pdata->master_ref_clk_hz / 2)); + msg->status = -EINVAL; + return -EINVAL; + } + } + + spin_lock_irqsave(&cadence_qspi->lock, flags); + + if (!cadence_qspi->running) { + spin_unlock_irqrestore(&cadence_qspi->lock, flags); + return -ESHUTDOWN; + } + + msg->status = -EINPROGRESS; + msg->actual_length = 0; + + list_add_tail(&msg->queue, &cadence_qspi->msg_queue); + queue_work(cadence_qspi->workqueue, &cadence_qspi->work); + spin_unlock_irqrestore(&cadence_qspi->lock, flags); + return 0; +} + +static int cadence_qspi_setup(struct spi_device *spi) +{ + if (spi->chip_select > spi->master->num_chipselect) { + dev_err(&spi->dev, "%d chip select is out of range\n", + spi->chip_select); + return -EINVAL; + } + pr_debug("cadence_qspi : bits per word %d, chip select %d, " + "speed %d KHz\n", spi->bits_per_word, spi->chip_select, + spi->max_speed_hz); + return 0; +} + +static int cadence_qspi_start_queue(struct struct_cqspi *cadence_qspi) +{ + unsigned long flags; + + spin_lock_irqsave(&cadence_qspi->lock, flags); + + if (cadence_qspi->running) { + spin_unlock_irqrestore(&cadence_qspi->lock, flags); + return -EBUSY; + } + + if (!cadence_qspi_apb_is_controller_ready (cadence_qspi->iobase) ) { + spin_unlock_irqrestore(&cadence_qspi->lock, flags); + return -EBUSY; + } + + cadence_qspi->running = true; + + spin_unlock_irqrestore(&cadence_qspi->lock, flags); + + queue_work(cadence_qspi->workqueue, &cadence_qspi->work); + return 0; +} + +static int cadence_qspi_stop_queue(struct struct_cqspi *cadence_qspi) +{ + unsigned long flags; + unsigned limit = 500; + int status = 0; + + spin_lock_irqsave(&cadence_qspi->lock, flags); + cadence_qspi->running = false; + /* We will wait until controller process all the queue and ensure the + * controller is not busy. */ + while ((!list_empty(&cadence_qspi->msg_queue) || + !cadence_qspi_apb_is_controller_ready(cadence_qspi->iobase)) + && limit--) { + spin_unlock_irqrestore(&cadence_qspi->lock, flags); + msleep(10); + spin_lock_irqsave(&cadence_qspi->lock, flags); + } + + if (!list_empty(&cadence_qspi->msg_queue) || + !cadence_qspi_apb_is_controller_ready(cadence_qspi->iobase)) + status = -EBUSY; + + spin_unlock_irqrestore(&cadence_qspi->lock, flags); + return status; +} + +static int cadence_qspi_of_get_pdata(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *nc; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + struct cqspi_flash_pdata *f_pdata; + unsigned int cs; + unsigned int prop; + + if (of_property_read_u32(np, "bus-num", &prop)) { + dev_err(&pdev->dev, "couldn't determine bus-num\n"); + return -ENXIO; + } + pdata->bus_num = prop; + + if (of_property_read_u32(np, "num-chipselect", &prop)) { + dev_err(&pdev->dev, "couldn't determine num-chipselect\n"); + return -ENXIO; + } + pdata->num_chipselect = prop; + + if (of_property_read_u32(np, "master-ref-clk", &prop)) { + dev_err(&pdev->dev, "couldn't determine master-ref-clk\n"); + return -ENXIO; + } + pdata->master_ref_clk_hz = prop; + + if (of_property_read_u32(np, "ext-decoder", &prop)) { + dev_err(&pdev->dev, "couldn't determine ext-decoder\n"); + return -ENXIO; + } + pdata->ext_decoder = prop; + + if (of_property_read_u32(np, "fifo-depth", &prop)) { + dev_err(&pdev->dev, "couldn't determine fifo-depth\n"); + return -ENXIO; + } + pdata->fifo_depth = prop; + + /* Get flash devices platform data */ + for_each_child_of_node(np, nc) { + if (of_property_read_u32(nc, "reg", &cs)) { + dev_err(&pdev->dev, "couldn't determine reg\n"); + return -ENXIO; + } + + f_pdata = &(pdata->f_pdata[cs]); + + if (of_property_read_u32(nc, "page-size", &prop)) { + dev_err(&pdev->dev, "couldn't determine page-size\n"); + return -ENXIO; + } + f_pdata->page_size = prop; + + if (of_property_read_u32(nc, "block-size", &prop)) { + dev_err(&pdev->dev, "couldn't determine block-size\n"); + return -ENXIO; + } + f_pdata->block_size = prop; + + if (of_property_read_u32(nc, "tshsl-ns", &prop)) { + dev_err(&pdev->dev, "couldn't determine tshsl-ns\n"); + return -ENXIO; + } + f_pdata->tshsl_ns = prop; + + if (of_property_read_u32(nc, "tsd2d-ns", &prop)) { + dev_err(&pdev->dev, "couldn't determine tsd2d-ns\n"); + return -ENXIO; + } + f_pdata->tsd2d_ns = prop; + + if (of_property_read_u32(nc, "tchsh-ns", &prop)) { + dev_err(&pdev->dev, "couldn't determine tchsh-ns\n"); + return -ENXIO; + } + f_pdata->tchsh_ns = prop; + + if (of_property_read_u32(nc, "tslch-ns", &prop)) { + dev_err(&pdev->dev, "couldn't determine tslch-ns\n"); + return -ENXIO; + } + f_pdata->tslch_ns = prop; + } + return 0; +} + +static int cadence_qspi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct struct_cqspi *cadence_qspi; + struct resource *res; + struct resource *res_ahb; + struct cqspi_platform_data *pdata; + int status; + + master = spi_alloc_master(&pdev->dev, sizeof(*cadence_qspi)); + if (master == NULL) { + dev_err(&pdev->dev, "spi_alloc_master failed\n"); + return -ENOMEM; + } + + master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA; + master->setup = cadence_qspi_setup; + master->transfer = cadence_qspi_transfer; + master->dev.of_node = pdev->dev.of_node; + + cadence_qspi = spi_master_get_devdata(master); + cadence_qspi->pdev = pdev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "platform_get_resource failed\n"); + status = -ENXIO; + goto err_iomem; + } + + cadence_qspi->res = res; + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + status = -EBUSY; + goto err_iomem; + } + + cadence_qspi->iobase = ioremap(res->start, resource_size(res)); + if (!cadence_qspi->iobase) { + dev_err(&pdev->dev, "ioremap failed\n"); + status = -ENOMEM; + goto err_ioremap; + } + + res_ahb = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_ahb) { + dev_err(&pdev->dev, "platform_get_resource failed\n"); + status = -ENXIO; + goto err_ahbmem; + } + cadence_qspi->res_ahb = res_ahb; + + if (!request_mem_region(res_ahb->start, resource_size(res_ahb), + pdev->name)) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + status = -EBUSY; + goto err_ahbmem; + } + + cadence_qspi->qspi_ahb_virt = ioremap(res_ahb->start, + resource_size(res_ahb)); + if (!cadence_qspi->qspi_ahb_virt) { + dev_err(&pdev->dev, "ioremap res_ahb failed\n"); + status = -ENOMEM; + goto err_ahbremap; + } + + cadence_qspi->workqueue = + create_singlethread_workqueue(dev_name(master->dev.parent)); + if (!cadence_qspi->workqueue) { + dev_err(&pdev->dev, "create_workqueue failed\n"); + status = -ENOMEM; + goto err_wq; + } + + cadence_qspi->running = false; + INIT_WORK(&cadence_qspi->work, cadence_qspi_work); + spin_lock_init(&cadence_qspi->lock); + INIT_LIST_HEAD(&cadence_qspi->msg_queue); + init_waitqueue_head(&cadence_qspi->waitqueue); + status = cadence_qspi_start_queue(cadence_qspi); + if (status) { + dev_err(&pdev->dev, "problem starting queue.\n"); + goto err_start_q; + } + + cadence_qspi->irq = platform_get_irq(pdev, 0); + + if (cadence_qspi->irq < 0) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + status = -ENXIO; + goto err_irq; + } + + status = request_irq(cadence_qspi->irq, cadence_qspi_irq_handler, + 0, pdev->name, cadence_qspi); + if (status) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto err_irq; + } + + pdata = kmalloc(sizeof(struct cqspi_platform_data), GFP_KERNEL); + if (!pdata) { + status = -ENOMEM; + goto err_pdata; + } + + pdev->dev.platform_data = pdata; + pdata->qspi_ahb_phy = res_ahb->start; + + status = cadence_qspi_of_get_pdata(pdev); + if (status) { + dev_err(&pdev->dev, "Get platform data failed.\n"); + goto err_of; + } + + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->num_chipselect; + + platform_set_drvdata(pdev, master); + status = spi_register_master(master); + if (status) { + dev_err(&pdev->dev, "spi_register_master failed\n"); + goto err_of; + } + + cadence_qspi_apb_controller_init(cadence_qspi); + cadence_qspi->current_cs = -1; + + dev_info(&pdev->dev, "Cadence QSPI controller driver\n"); + return 0; + +err_of: + kfree(pdata); +err_pdata: + free_irq(cadence_qspi->irq, cadence_qspi); +err_start_q: +err_irq: + destroy_workqueue(cadence_qspi->workqueue); +err_wq: + iounmap(cadence_qspi->qspi_ahb_virt); +err_ahbremap: + release_mem_region(res_ahb->start, resource_size(res_ahb)); +err_ahbmem: + iounmap(cadence_qspi->iobase); +err_ioremap: + release_mem_region(res->start, resource_size(res)); +err_iomem: + spi_master_put(master); + dev_err(&pdev->dev, "Cadence QSPI controller probe failed\n"); + return status; +} + +static int cadence_qspi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct struct_cqspi *cadence_qspi = spi_master_get_devdata(master); + + cadence_qspi_apb_controller_disable(cadence_qspi->iobase); + + platform_set_drvdata(pdev, NULL); + destroy_workqueue(cadence_qspi->workqueue); + free_irq(cadence_qspi->irq, cadence_qspi); + iounmap(cadence_qspi->iobase); + iounmap(cadence_qspi->qspi_ahb_virt); + release_mem_region(cadence_qspi->res->start, + resource_size(cadence_qspi->res)); + release_mem_region(cadence_qspi->res_ahb->start, + resource_size(cadence_qspi->res_ahb)); + kfree(pdev->dev.platform_data); + spi_unregister_master(master); + spi_master_put(master); + + return 0; +} + +#ifdef CONFIG_PM + +static int cadence_qspi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct struct_cqspi *cadence_qspi = spi_master_get_devdata(master); + int status=0; + + /* Stop the queue */ + status = cadence_qspi_stop_queue(cadence_qspi); + if (status != 0) + return status; + /* Disable the controller to conserve the power */ + cadence_qspi_apb_controller_disable(cadence_qspi->iobase); + return 0; +} + +static int cadence_qspi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct struct_cqspi *cadence_qspi = spi_master_get_devdata(master); + int status = 0; + + cadence_qspi_apb_controller_enable(cadence_qspi->iobase); + /* Start the queue running */ + status = cadence_qspi_start_queue(cadence_qspi); + if (status != 0) { + cadence_qspi_apb_controller_disable(cadence_qspi->iobase); + dev_err(dev, "problem starting queue (%d)\n", status); + return status; + } + return 0; +} +static struct dev_pm_ops cadence_qspi__dev_pm_ops = +{ + .suspend = cadence_qspi_suspend, + .resume = cadence_qspi_resume, +}; +#define CADENCE_QSPI_DEV_PM_OPS (&cadence_qspi__dev_pm_ops) +#else +#define CADENCE_QSPI_DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static struct of_device_id cadence_qspi_of_match[] = { + { .compatible = "cadence,qspi",}, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, cadence_qspi_of_match); +#else +#define cadence_qspi_of_match NULL +#endif /* CONFIG_OF */ + + +static struct platform_driver cadence_qspi_platform_driver = +{ + .probe = cadence_qspi_probe, + .remove = cadence_qspi_remove, + .driver = { + .name = CADENCE_QSPI_NAME, + .owner = THIS_MODULE, + .pm = CADENCE_QSPI_DEV_PM_OPS, + .of_match_table = cadence_qspi_of_match, + }, +}; + +static int __init cadence_qspi_init(void) +{ + return platform_driver_register(&cadence_qspi_platform_driver); +} +static void __exit cadence_qspi_exit(void) +{ + platform_driver_unregister(&cadence_qspi_platform_driver); +} + +module_init(cadence_qspi_init); +module_exit(cadence_qspi_exit); + +MODULE_AUTHOR("Ley Foon Tan "); +MODULE_DESCRIPTION("Cadence QSPI Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" CADENCE_QSPI_NAME); diff --git a/drivers/spi/spi-cadence-qspi.h b/drivers/spi/spi-cadence-qspi.h new file mode 100644 index 0000000000000..86f5d65eade5f --- /dev/null +++ b/drivers/spi/spi-cadence-qspi.h @@ -0,0 +1,82 @@ +/* + * Driver for Cadence QSPI Controller + * + * Copyright (C) 2012 Altera Corporation + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CADENCE_QSPI_H__ +#define __CADENCE_QSPI_H__ + +#define CQSPI_MAX_TRANS (2) + +#define CQSPI_MAX_CHIP_SELECT (16) + +struct cqspi_flash_pdata { + unsigned int page_size; + unsigned int block_size; + unsigned int quad; + unsigned int tshsl_ns; + unsigned int tsd2d_ns; + unsigned int tchsh_ns; + unsigned int tslch_ns; +}; + +struct cqspi_platform_data { + unsigned int bus_num; + unsigned int num_chipselect; + unsigned int qspi_ahb_phy; + unsigned int master_ref_clk_hz; + unsigned int ext_decoder; + unsigned int fifo_depth; + struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIP_SELECT]; +}; + +struct struct_cqspi +{ + struct work_struct work; + struct workqueue_struct *workqueue; + wait_queue_head_t waitqueue; + struct list_head msg_queue; + struct platform_device *pdev; + + /* lock protects queue and registers */ + spinlock_t lock; + /* Virtual base address of the controller */ + void __iomem *iobase; + /* QSPI AHB virtual address */ + void __iomem *qspi_ahb_virt; + /* phys mem */ + struct resource *res; + /* AHB phys mem */ + struct resource *res_ahb; + /* Interrupt */ + int irq; + /* Interrupt status */ + unsigned int irq_status; + /* Current chip select */ + int current_cs; + /* Is queue running */ + bool running; +}; + +/* Kernel function hook */ +#define CQSPI_WRITEL __raw_writel +#define CQSPI_READL __raw_readl +unsigned int cadence_qspi_init_timeout(const unsigned long timeout_in_ms); +unsigned int cadence_qspi_check_timeout(const unsigned long timeout); + +#endif /* __CADENCE_QSPI_H__ */ diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index a5cba14ac3d2f..5e99e912736fa 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -17,6 +17,10 @@ #include #include #include +#include +#include +#include +#include #include "spi-dw.h" @@ -33,7 +37,9 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) struct dw_spi *dws; struct resource *mem; int ret; - +#ifdef CONFIG_OF + unsigned int prop; +#endif dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio), GFP_KERNEL); if (!dwsmmio) @@ -48,10 +54,11 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) return -EINVAL; } - dws->regs = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(dws->regs)) { - dev_err(&pdev->dev, "SPI region map failed\n"); - return PTR_ERR(dws->regs); + dws->regs = ioremap_nocache(mem->start, resource_size(mem)); + dws->paddr = mem->start; + if (!dws->regs) { + dev_err(&pdev->dev, "SPI region already mapped\n"); + return -ENOMEM; } dws->irq = platform_get_irq(pdev, 0); @@ -67,8 +74,23 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) if (ret) return ret; - dws->bus_num = pdev->id; +#ifdef CONFIG_OF + if(of_property_read_u32(pdev->dev.of_node, "num-chipselect", &prop)) { + dev_err(&pdev->dev, "couldn't determine num-chipselect\n"); + return -ENXIO; + } + dws->num_cs = prop; + + if(of_property_read_u32(pdev->dev.of_node, "bus-num", &prop)) { + dev_err(&pdev->dev, "couldn't determine bus-num\n"); + return -ENXIO; + } + dws->bus_num = prop; +#else dws->num_cs = 4; + dws->bus_num = pdev->id; +#endif + dws->max_freq = clk_get_rate(dwsmmio->clk); if (pdev->dev.of_node) { @@ -91,6 +113,11 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) } } } +#ifdef CONFIG_SPI_DW_PL330_DMA + ret = dw_spi_pl330_init(dws); + if (ret) + goto out; +#endif ret = dw_spi_add_host(&pdev->dev, dws); if (ret) @@ -114,12 +141,23 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static struct of_device_id dw_spi_mmio_of_match[] = { + { .compatible = "snps,dw-spi-mmio", }, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); +#else +#define dw_spi_mmio_of_match NULL +#endif /* CONFIG_OF */ + static struct platform_driver dw_spi_mmio_driver = { .probe = dw_spi_mmio_probe, .remove = dw_spi_mmio_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = dw_spi_mmio_of_match, }, }; module_platform_driver(dw_spi_mmio_driver); diff --git a/drivers/spi/spi-dw-pl330.c b/drivers/spi/spi-dw-pl330.c new file mode 100644 index 0000000000000..050b9b4cd5b19 --- /dev/null +++ b/drivers/spi/spi-dw-pl330.c @@ -0,0 +1,223 @@ +/* + * DMA handling for DW core with DMA PL330 controller + * + * Modified from linux/driver/spi/spi-dw-mid.c + * + * Copyright (c) 2012, Altera Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "spi-dw.h" +#include +#include + + +/*Burst size configuration*/ +enum spi_pl330_brst_sz { + PL330_DMA_BRSTSZ_1B = 0x1, + PL330_DMA_BRSTSZ_2B = 0x2, +}; + +/* TX & RX FIFO depth supported by HW */ +#define SSI_FIFO_DEPTH 0xFF + +/* Maximum burst length + Note: Can be up to 16, but now is default to 1 in the PL330 driver. + Burst transfer is not supported in PL330 driver */ +#define SSI_DMA_MAXBURST 16 + + +static int spi_pl330_dma_chan_alloc(struct dw_spi *dws) +{ + struct device_node *np = dws->master->dev.of_node; + void *filter_param_rx, *filter_param_tx; + dma_cap_mask_t mask; + int lenp; + + + /* If DMA channel already allocated */ + if (dws->rxchan && dws->txchan) + return 0; + + filter_param_tx = of_find_property(np, "tx-dma-channel", &lenp); + if (!filter_param_tx) + return -1; + filter_param_rx = of_find_property(np, "rx-dma-channel", &lenp); + if (!filter_param_rx) + return -1; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* 1. Init rx channel */ + dws->rxchan = dma_request_channel(mask, pl330_filter, filter_param_rx); + while (!dws->rxchan) { + /* all DMA channels are busy, try again */ + msleep(10); + dws->rxchan = dma_request_channel(mask, pl330_filter, filter_param_rx); + } + + /* 2. Init tx channel */ + dws->txchan = dma_request_channel(mask, pl330_filter, filter_param_tx); + while (!dws->txchan) { + /* all DMA channels are busy, try again */ + msleep(10); + dws->txchan = dma_request_channel(mask, pl330_filter, filter_param_tx); + } + + dws->dma_inited = 1; + + return 0; +} + +static void spi_pl330_dma_chan_release(struct dw_spi *dws) +{ + dma_release_channel(dws->txchan); + dma_release_channel(dws->rxchan); + dws->txchan = 0; + dws->rxchan = 0; + dws->dma_inited = 0; +} + +/* + * dws->dma_chan_done is cleared before the dma transfer starts, + * callback for rx/tx channel will each increment it by 1. + * Reaching 2 means the whole spi transaction is done. + */ +static void spi_pl330_dma_done(void *arg) +{ + struct dw_spi *dws = arg; + + if (++dws->dma_chan_done != 2) + return; + dw_spi_xfer_done(dws); +} + +static int spi_pl330_dma_transfer(struct dw_spi *dws, int cs_change) +{ + struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL; + struct dma_chan *txchan, *rxchan; + struct dma_slave_config txconf, rxconf; + u16 dma_ctrl = 0; + + /* 1. setup DMA related registers */ + if (cs_change) { + spi_enable_chip(dws, 0); + /* Setup peripheral's burst watermark for TX and RX FIFO */ + dw_writew(dws, DW_SPI_DMARDLR, SSI_DMA_MAXBURST - 1); + dw_writew(dws, DW_SPI_DMATDLR, SSI_FIFO_DEPTH - SSI_DMA_MAXBURST); + + if (dws->tx_dma) + dma_ctrl |= 0x2; + if (dws->rx_dma) + dma_ctrl |= 0x1; + dw_writew(dws, DW_SPI_DMACR, dma_ctrl); + spi_enable_chip(dws, 1); + } + + dws->dma_chan_done = 0; + txchan = dws->txchan; + rxchan = dws->rxchan; + + /* 2. Prepare the TX dma transfer */ + txconf.direction = DMA_MEM_TO_DEV; + txconf.dst_addr = dws->dma_addr; + /* Note: By default the burst_len (dst_maxburst) for DMA_MEM_TO_DEV is set + to 1 and the burst_size (src_addr_width) for memory is set to + peripheral's configuration in PL330 driver (driver/dma/pl330.c). + Therefore the config listed below can be skipped + i. txconf.dst_maxburst + ii. txconf.src_addr_width + Max DMA width is 16-bit + */ + if (dws->dma_width == 1) + txconf.dst_addr_width = PL330_DMA_BRSTSZ_1B; + else + txconf.dst_addr_width = PL330_DMA_BRSTSZ_2B; + + txchan->device->device_control(txchan, DMA_SLAVE_CONFIG, + (unsigned long) &txconf); + + memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl)); + dws->tx_sgl.dma_address = dws->tx_dma; + dws->tx_sgl.length = dws->len; + + txdesc = txchan->device->device_prep_slave_sg(txchan, + &dws->tx_sgl, + 1, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + txdesc->callback = spi_pl330_dma_done; + txdesc->callback_param = dws; + + /* 3. Prepare the RX dma transfer */ + rxconf.direction = DMA_DEV_TO_MEM; + rxconf.src_addr = dws->dma_addr; + /* Note: By default the burst_len (src_maxburst) for DMA_DEV_TO_MEM is set + to 1 and the burst_size (dst_addr_width) for memory is set to + peripheral's configuration in PL330 driver (driver/dma/pl330.c). + Therefore the config listed below can be skipped + txconf.src_maxburst + txconf.dst_addr_width + */ + if (dws->dma_width == 1) + rxconf.src_addr_width = PL330_DMA_BRSTSZ_1B; + else + rxconf.src_addr_width = PL330_DMA_BRSTSZ_2B; + + rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG, + (unsigned long) &rxconf); + + memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl)); + dws->rx_sgl.dma_address = dws->rx_dma; + dws->rx_sgl.length = dws->len; + + rxdesc = rxchan->device->device_prep_slave_sg(rxchan, + &dws->rx_sgl, + 1, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + rxdesc->callback = spi_pl330_dma_done; + rxdesc->callback_param = dws; + + /* rx must be started before tx due to spi instinct */ + rxdesc->tx_submit(rxdesc); + dma_async_issue_pending(rxchan); + txdesc->tx_submit(txdesc); + dma_async_issue_pending(txchan); + + return 0; +} + +static struct dw_spi_dma_ops pl330_dma_ops = { + .dma_transfer = spi_pl330_dma_transfer, + .dma_chan_alloc = spi_pl330_dma_chan_alloc, + .dma_chan_release = spi_pl330_dma_chan_release, +}; + +int dw_spi_pl330_init(struct dw_spi *dws) +{ + dws->fifo_len = SSI_FIFO_DEPTH; + dws->dma_ops = &pl330_dma_ops; + + return 0; +} diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 29f33143b7956..46c6a6a3e9bea 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -239,6 +239,43 @@ static void *next_transfer(struct dw_spi *dws) */ static int map_dma_buffers(struct dw_spi *dws) { +#ifdef CONFIG_SPI_DW_PL330_DMA + if (!dws->dma_inited + || !dws->cur_chip->enable_dma + || !dws->dma_ops) + return 0; + + if (dws->cur_msg->is_dma_mapped) { + if (dws->cur_transfer->tx_dma) + dws->tx_dma = dws->cur_transfer->tx_dma; + + if (dws->cur_transfer->rx_dma) + dws->rx_dma = dws->cur_transfer->rx_dma; + } else { + if (dws->cur_transfer->tx_buf != NULL) { + dws->tx_dma = dma_map_single(dws->master->dev, + (void *)dws->cur_transfer->tx_buf, + dws->cur_transfer->len, + DMA_TO_DEVICE); + if (dma_mapping_error(dws->master->dev, dws->tx_dma)) { + dev_err(&dws->master->dev, "dma_map_single Tx failed\n"); + return 0; + } + } + + if (dws->cur_transfer->rx_buf != NULL) { + dws->rx_dma = dma_map_single(dws->master->_dev, + dws->cur_transfer->rx_buf, + dws->cur_transfer->len, DMA_FROM_DEVICE); + if (dma_mapping_error(dws->master->dev, dws->rx_dma)) { + dev_err(&dws->master->dev, "dma_map_single Rx failed\n"); + dma_unmap_single(dws->master->dev, dws->tx_dma, + dws->cur_transfer->len, DMA_TO_DEVICE); + return 0; + } + } + } +#else if (!dws->cur_msg->is_dma_mapped || !dws->dma_inited || !dws->cur_chip->enable_dma @@ -250,6 +287,7 @@ static int map_dma_buffers(struct dw_spi *dws) if (dws->cur_transfer->rx_dma) dws->rx_dma = dws->cur_transfer->rx_dma; +#endif return 1; } @@ -291,6 +329,15 @@ void dw_spi_xfer_done(struct dw_spi *dws) /* Update total byte transferred return count actual bytes read */ dws->cur_msg->actual_length += dws->len; + if (dws->dma_mapped) { + if (!dws->cur_msg->is_dma_mapped) { + dma_unmap_single(&dws->master->dev, dws->rx_dma, + dws->cur_transfer->len, DMA_FROM_DEVICE); + dma_unmap_single(&dws->master->dev, dws->tx_dma, + dws->cur_transfer->len, DMA_TO_DEVICE); + } + } + /* Move to next transfer */ dws->cur_msg->state = next_transfer(dws); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 6d2acad34f64f..e3feb6e43a023 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -88,6 +88,8 @@ struct dw_spi_dma_ops { int (*dma_init)(struct dw_spi *dws); void (*dma_exit)(struct dw_spi *dws); int (*dma_transfer)(struct dw_spi *dws, int cs_change); + int (*dma_chan_alloc)(struct dw_spi *dws); + void (*dma_chan_release)(struct dw_spi *dws); }; struct dw_spi { @@ -234,4 +236,5 @@ extern void dw_spi_xfer_done(struct dw_spi *dws); /* platform related setup */ extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ +extern int dw_spi_pl330_init(struct dw_spi *dws); /* PL330 support setup */ #endif /* DW_SPI_HEADER_H */ diff --git a/include/linux/spi/spi-dw.h b/include/linux/spi/spi-dw.h new file mode 100644 index 0000000000000..fb904f1722592 --- /dev/null +++ b/include/linux/spi/spi-dw.h @@ -0,0 +1,38 @@ +/* linux/include/linux/spi/spi-dw.h + * + * Copyright (C) 2012 Altera Corporation + * + * Maintainer: Lee Booi Lim + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef LINUX_DW_SPI_H +#define LINUX_DW_SPI_H + +#include + + +#define DW_SPI_OF_COMPATIBLE "snps,dw-spi-mmio" +#define MAX_SPI_DEVICES 16 + +enum dw_spi_dma_resources { + DW_SPI_DMA_CH_RX = 0, + DW_SPI_DMA_CH_TX, + DW_SPI_DMA_CH_MAX, +}; + + + +struct dw_spi_pdata { + struct resource dwi_spi_dma_res[DW_SPI_DMA_CH_MAX]; +}; + +#endif /* LINUX_DW_SPI_H */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index e713543336f12..42079385b437f 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -101,6 +101,8 @@ struct spi_device { void *controller_data; char modalias[SPI_NAME_SIZE]; int cs_gpio; /* chip select gpio */ + u32 enable_4b_addr; /* Uses 4 bytes address mode */ + u32 addr_width; /* * likely need more hooks for more protocol options affecting how From b3dba9e1abbf54b820f12f4824429fc41cec8d8f Mon Sep 17 00:00:00 2001 From: graham Date: Fri, 28 Jun 2013 10:33:30 -0500 Subject: [PATCH 177/201] FogBugz #132611-2: Support QSPI flag status register Support the flag status register. Loadable module support. Configurable read delay. Signed-off-by: graham --- drivers/spi/Makefile | 3 +- drivers/spi/spi-cadence-qspi-apb.c | 161 +++++++++++++++++++++++++---- drivers/spi/spi-cadence-qspi-apb.h | 1 + drivers/spi/spi-cadence-qspi.c | 23 ++++- drivers/spi/spi-cadence-qspi.h | 1 + 5 files changed, 163 insertions(+), 26 deletions(-) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 92845baba53ac..d8c413ebed9e5 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -24,7 +24,8 @@ obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o -obj-$(CONFIG_SPI_CADENCE_QSPI) += spi-cadence-qspi-apb.o spi-cadence-qspi.o +obj-$(CONFIG_SPI_CADENCE_QSPI) += spi-cadence-qspi-mod.o +spi-cadence-qspi-mod-objs := spi-cadence-qspi-apb.o spi-cadence-qspi.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o diff --git a/drivers/spi/spi-cadence-qspi-apb.c b/drivers/spi/spi-cadence-qspi-apb.c index 275ef289309a9..921d5505343a2 100644 --- a/drivers/spi/spi-cadence-qspi-apb.c +++ b/drivers/spi/spi-cadence-qspi-apb.c @@ -39,11 +39,70 @@ #include "spi-cadence-qspi.h" #include "spi-cadence-qspi-apb.h" +/****************************************************************************/ + +#include + +typedef int (*PRINTF_FUNC)(const char *fmt, ...); + +#ifdef DEBUG +static void __hex_dump(unsigned int address_to_print, + const unsigned char *buffer, + int length, + PRINTF_FUNC _printfunc) +{ + int i; + int j; + for (i = 0; i < length; i += 16) { + _printfunc("%08x: ", address_to_print+i); + for (j = 0; j < 8; j++) { + if ((i+j) < length) + _printfunc("%02x ", buffer[i+j]); + else + _printfunc(" "); + } + _printfunc(" "); + for (j = 8; j < 16; j++) { + if ((i+j) < length) + _printfunc("%02x ", buffer[i+j]); + else + _printfunc(" "); + } + _printfunc(" "); + for (j = 0; j < 16; j++) { + if ((i+j) < length) + _printfunc("%c", + isprint(buffer[i+j]) ? + buffer[i+j] : '.'); + else + break; + } + _printfunc("\n"); + } +} +#endif /* #ifdef DEBUG */ + +#ifdef DEBUG +#define hex_dump(a, b, c) __hex_dump(a, b, c, (PRINTF_FUNC)&printk) +#else +#define hex_dump(a, b, c) +#endif + + +/****************************************************************************/ + + +void cadence_qspi_apb_delay(struct struct_cqspi *cadence_qspi, + unsigned int ref_clk, unsigned int sclk_hz); + static unsigned int cadence_qspi_apb_cmd2addr(const unsigned char* addr_buf, unsigned int addr_width) { unsigned int addr; + pr_debug("%s addr_buf %p addr_width %d\n", + __func__, addr_buf, addr_width); + addr = (addr_buf[0] << 16) | (addr_buf[1] << 8) | addr_buf[2]; if (addr_width == 4) @@ -129,7 +188,7 @@ static void cadence_qspi_apb_readdata_capture(void *reg_base, { unsigned int reg; - cadence_qspi_apb_controller_disable(reg_base); + pr_debug("%s %d %d\n", __func__, bypass, delay); reg = CQSPI_READL(reg_base + CQSPI_REG_READCAPTURE); if (bypass) { @@ -146,7 +205,6 @@ static void cadence_qspi_apb_readdata_capture(void *reg_base, CQSPI_WRITEL(reg, reg_base + CQSPI_REG_READCAPTURE); - cadence_qspi_apb_controller_enable(reg_base); return; } @@ -156,7 +214,8 @@ static void cadence_qspi_apb_config_baudrate_div(void *reg_base, unsigned int reg; unsigned int div; - cadence_qspi_apb_controller_disable(reg_base); + pr_debug("%s %d %d\n", __func__, ref_clk_hz, sclk_hz); + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB); @@ -179,7 +238,6 @@ static void cadence_qspi_apb_config_baudrate_div(void *reg_base, reg |= div; CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); - cadence_qspi_apb_controller_enable(reg_base); return; } @@ -188,7 +246,7 @@ static void cadence_qspi_apb_chipselect(void * reg_base, { unsigned int reg; - cadence_qspi_apb_controller_disable(reg_base); + pr_debug("%s\n", __func__); pr_debug("QSPI: chipselect %d decode %d\n", chip_select, decoder_enable); @@ -215,7 +273,6 @@ static void cadence_qspi_apb_chipselect(void * reg_base, << CQSPI_REG_CONFIG_CHIPSELECT_LSB; CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); - cadence_qspi_apb_controller_enable(reg_base); return; } @@ -259,6 +316,10 @@ static int cadence_qspi_apb_command_read(void *reg_base, unsigned int read_len; int status; + pr_debug("%s txlen %d txbuf %p rxlen %d rxbuf %p\n", + __func__, txlen, txbuf, rxlen, rxbuf); + hex_dump((unsigned int)txbuf, txbuf, txlen); + if (!rxlen || rxlen > CQSPI_STIG_DATA_LEN_MAX || rxbuf == NULL) { pr_err("QSPI: Invalid input argument, len %d rxbuf 0x%08x\n", rxlen, (unsigned int)rxbuf); @@ -281,6 +342,7 @@ static int cadence_qspi_apb_command_read(void *reg_base, /* Put the read value into rx_buf */ read_len = (rxlen > 4) ? 4 : rxlen; memcpy(rxbuf, ®, read_len); + hex_dump((unsigned int)rxbuf, rxbuf, read_len); rxbuf += read_len; if (rxlen > 4) { @@ -288,6 +350,7 @@ static int cadence_qspi_apb_command_read(void *reg_base, read_len = rxlen - read_len; memcpy(rxbuf, ®, read_len); + hex_dump((unsigned int)rxbuf, rxbuf, read_len); } return 0; @@ -301,6 +364,9 @@ static int cadence_qspi_apb_command_write(void *reg_base, unsigned txlen, unsigned int addr_value; unsigned int data; + pr_debug("%s txlen %d txbuf %p\n", __func__, txlen, txbuf); + hex_dump((unsigned int)txbuf, txbuf, txlen); + if (!txlen || txlen > 5 || txbuf == NULL) { pr_err("QSPI: Invalid input argument, cmdlen %d txbuf 0x%08x\n", txlen, (unsigned int)txbuf); @@ -343,6 +409,10 @@ static int cadence_qspi_apb_indirect_read_setup(void *reg_base, unsigned int dummy_clk; unsigned int dummy_bytes; + pr_debug("%s txlen %d txbuf %p addr_bytes %d\n", + __func__, txlen, txbuf, addr_bytes); + hex_dump((unsigned int)txbuf, txbuf, txlen); + if ((addr_bytes == 3 && txlen < 4) || (addr_bytes == 4 && txlen < 5)) { pr_err("QSPI: Invalid txbuf length, length %d\n", txlen); return -EINVAL; @@ -354,6 +424,7 @@ static int cadence_qspi_apb_indirect_read_setup(void *reg_base, reg = txbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB; #ifdef CONFIG_M25PXX_USE_FAST_READ_QUAD_OUTPUT +#error WTFO reg |= (CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB); #endif /* CONFIG_M25PXX_USE_FAST_READ_QUAD_OUTPUT */ @@ -409,6 +480,12 @@ static int cadence_qspi_apb_indirect_read_execute( int remaining = (int)rxlen; int ret = 0; +#ifdef DEBUG + unsigned char *saverxbuf = rxbuf; + unsigned saverxlen = rxlen; +#endif /* #ifdef DEBUG */ + + pr_debug("%s rxlen %d rxbuf %p\n", __func__, rxlen, rxbuf); if (remaining < watermark) watermark = remaining; @@ -477,6 +554,7 @@ static int cadence_qspi_apb_indirect_read_execute( /* Clear indirect completion status */ CQSPI_WRITEL(CQSPI_REG_INDIRECTRD_DONE_MASK, reg_base + CQSPI_REG_INDIRECTRD); + hex_dump((unsigned int)saverxbuf, saverxbuf, saverxlen); return 0; failrd: @@ -495,13 +573,17 @@ static int cadence_qspi_apb_indirect_write_setup(void *reg_base, unsigned int reg; unsigned int addr_bytes = (txlen >= 5) ? 4: 3; + pr_debug("%s txlen %d txbuf %p addr_bytes %d\n", + __func__, txlen, txbuf, addr_bytes); + hex_dump((unsigned int)txbuf, txbuf, txlen); + if (txlen < 4 || txbuf == NULL) { pr_err("QSPI: Invalid input argument, txlen %d txbuf 0x%08x\n", txlen, (unsigned int)txbuf); return -EINVAL; } - CQSPI_WRITEL( (ahb_phy_addr & CQSPI_INDIRECTTRIGGER_ADDR_MASK), + CQSPI_WRITEL((ahb_phy_addr & CQSPI_INDIRECTTRIGGER_ADDR_MASK), reg_base + CQSPI_REG_INDIRECTTRIGGER); /* Set opcode. */ @@ -537,6 +619,8 @@ static int cadence_qspi_apb_indirect_write_execute( int remaining = (int)txlen; unsigned int write_bytes; + pr_debug("%s txlen %d txbuf %p\n", __func__, txlen, txbuf); + hex_dump((unsigned int)txbuf, txbuf, txlen); CQSPI_WRITEL(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES); CQSPI_WRITEL(CQSPI_REG_SRAM_THRESHOLD_BYTES, reg_base + @@ -626,6 +710,7 @@ static int cadence_qspi_apb_indirect_write_execute( void cadence_qspi_apb_controller_enable(void *reg_base) { unsigned int reg; + pr_debug("%s\n", __func__); reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); reg |= CQSPI_REG_CONFIG_ENABLE_MASK; CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); @@ -635,6 +720,7 @@ void cadence_qspi_apb_controller_enable(void *reg_base) void cadence_qspi_apb_controller_disable(void *reg_base) { unsigned int reg; + pr_debug("%s\n", __func__); reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK; CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); @@ -656,13 +742,22 @@ void cadence_qspi_apb_controller_init(struct struct_cqspi *cadence_qspi) /* Disable all interrupts. */ CQSPI_WRITEL(0, cadence_qspi->iobase + CQSPI_REG_IRQMASK); - /* Set read data capture to default. */ - cadence_qspi_apb_readdata_capture(cadence_qspi->iobase, 1, 0); - cadence_qspi_apb_controller_enable(cadence_qspi->iobase); return; } +unsigned int calculate_ticks_for_ns(unsigned int ref_clk_hz, + unsigned int ns_val) +{ + unsigned int ticks; + ticks = ref_clk_hz; + ticks /= 1000; + ticks *= ns_val; + ticks += 999999; + ticks /= 1000000; + return ticks; +} + void cadence_qspi_apb_delay(struct struct_cqspi *cadence_qspi, unsigned int ref_clk, unsigned int sclk_hz) { @@ -675,8 +770,9 @@ void cadence_qspi_apb_delay(struct struct_cqspi *cadence_qspi, unsigned int sclk_ns; unsigned int tshsl, tchsh, tslch, tsd2d; unsigned int reg; + unsigned int tsclk; - cadence_qspi_apb_controller_disable(iobase); + pr_debug("%s %d %d\n", __func__, ref_clk, sclk_hz); /* Convert to ns. */ ref_clk_ns = (1000000000) / pdata->master_ref_clk_hz; @@ -684,11 +780,24 @@ void cadence_qspi_apb_delay(struct struct_cqspi *cadence_qspi, /* Convert to ns. */ sclk_ns = (1000000000) / sclk_hz; - /* Plus 1 to round up 1 clock cycle. */ - tshsl = CQSPI_CAL_DELAY(f_pdata->tshsl_ns, ref_clk_ns, sclk_ns) + 1; - tchsh = CQSPI_CAL_DELAY(f_pdata->tchsh_ns, ref_clk_ns, sclk_ns) + 1; - tslch = CQSPI_CAL_DELAY(f_pdata->tslch_ns, ref_clk_ns, sclk_ns) + 1; - tsd2d = CQSPI_CAL_DELAY(f_pdata->tsd2d_ns, ref_clk_ns, sclk_ns) + 1; + /* calculate the number of ref ticks for one sclk tick */ + tsclk = (pdata->master_ref_clk_hz + sclk_hz - 1) / sclk_hz; + + tshsl = calculate_ticks_for_ns(pdata->master_ref_clk_hz, + f_pdata->tshsl_ns); + /* this particular value must be at least one sclk */ + if (tshsl < tsclk) + tshsl = tsclk; + + tchsh = calculate_ticks_for_ns(pdata->master_ref_clk_hz, + f_pdata->tchsh_ns); + tslch = calculate_ticks_for_ns(pdata->master_ref_clk_hz, + f_pdata->tslch_ns); + tsd2d = calculate_ticks_for_ns(pdata->master_ref_clk_hz, + f_pdata->tsd2d_ns); + + pr_debug("%s tshsl %d tsd2d %d tchsh %d tslch %d\n", + __func__, tshsl, tsd2d, tchsh, tslch); reg = ((tshsl & CQSPI_REG_DELAY_TSHSL_MASK) << CQSPI_REG_DELAY_TSHSL_LSB); @@ -700,7 +809,6 @@ void cadence_qspi_apb_delay(struct struct_cqspi *cadence_qspi, << CQSPI_REG_DELAY_TSD2D_LSB); CQSPI_WRITEL(reg, iobase + CQSPI_REG_DELAY); - cadence_qspi_apb_controller_enable(iobase); return; } @@ -713,6 +821,7 @@ void cadence_qspi_switch_cs(struct struct_cqspi *cadence_qspi, struct cqspi_flash_pdata *f_pdata = &(pdata->f_pdata[cs]); void __iomem *iobase = cadence_qspi->iobase; + pr_debug("%s\n", __func__); cadence_qspi_apb_controller_disable(iobase); /* Configure page size and block size. */ @@ -741,16 +850,27 @@ int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, void __iomem *iobase = cadence_qspi->iobase; unsigned int sclk; int ret = 0; + struct cqspi_flash_pdata *f_pdata; + + pr_debug("%s %d\n", __func__, n_trans); if (!cmd_xfer->len) { pr_err("QSPI: SPI transfer length is 0.\n"); return -EINVAL; } - /* Setup baudrate divisor. */ - sclk = cmd_xfer->speed_hz ? cmd_xfer->speed_hz : spi->max_speed_hz; + /* Setup baudrate divisor and delays */ + f_pdata = &(pdata->f_pdata[cadence_qspi->current_cs]); + sclk = cmd_xfer->speed_hz ? + cmd_xfer->speed_hz : spi->max_speed_hz; + cadence_qspi_apb_controller_disable(iobase); cadence_qspi_apb_config_baudrate_div(iobase, pdata->master_ref_clk_hz, sclk); + cadence_qspi_apb_delay(cadence_qspi, + pdata->master_ref_clk_hz, sclk); + cadence_qspi_apb_readdata_capture(iobase, 1, + f_pdata->read_delay); + cadence_qspi_apb_controller_enable(iobase); /* Switch chip select. */ if (cadence_qspi->current_cs != spi->chip_select) { @@ -758,9 +878,6 @@ int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, cadence_qspi_switch_cs(cadence_qspi, spi->chip_select); } - /* Configure device delay if we change device clock. */ - cadence_qspi_apb_delay(cadence_qspi, pdata->master_ref_clk_hz, sclk); - /* * Use STIG command to send if the transfer length is less than * 4 or if only one transfer. diff --git a/drivers/spi/spi-cadence-qspi-apb.h b/drivers/spi/spi-cadence-qspi-apb.h index ff7f56bbf122c..9f481d58ab589 100644 --- a/drivers/spi/spi-cadence-qspi-apb.h +++ b/drivers/spi/spi-cadence-qspi-apb.h @@ -223,4 +223,5 @@ int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, struct spi_transfer **spi_xfer); void cadence_qspi_apb_controller_enable(void *reg_base_addr); void cadence_qspi_apb_controller_disable(void *reg_base_addr); + #endif /* __CADENCE_QSPI_APB_H__ */ diff --git a/drivers/spi/spi-cadence-qspi.c b/drivers/spi/spi-cadence-qspi.c index 411f86cc488b2..21617ed3d989d 100644 --- a/drivers/spi/spi-cadence-qspi.c +++ b/drivers/spi/spi-cadence-qspi.c @@ -65,6 +65,8 @@ static void cadence_qspi_work(struct work_struct *work) = container_of(work, struct struct_cqspi, work); unsigned long flags; + pr_debug("%s\n", __func__); + spin_lock_irqsave(&cadence_qspi->lock, flags); while ((!list_empty(&cadence_qspi->msg_queue)) && cadence_qspi->running) { @@ -124,6 +126,8 @@ static int cadence_qspi_transfer(struct spi_device *spi, struct spi_message *msg struct cqspi_platform_data *pdata = pdev->dev.platform_data; unsigned long flags; + pr_debug("%s\n", __func__); + list_for_each_entry(spi_xfer, &msg->transfers, transfer_list) { if (spi_xfer->speed_hz > (pdata->master_ref_clk_hz / 2)) { dev_err(&spi->dev, "speed_hz%d greater than " @@ -153,6 +157,8 @@ static int cadence_qspi_transfer(struct spi_device *spi, struct spi_message *msg static int cadence_qspi_setup(struct spi_device *spi) { + pr_debug("%s\n", __func__); + if (spi->chip_select > spi->master->num_chipselect) { dev_err(&spi->dev, "%d chip select is out of range\n", spi->chip_select); @@ -168,6 +174,7 @@ static int cadence_qspi_start_queue(struct struct_cqspi *cadence_qspi) { unsigned long flags; + pr_debug("%s\n", __func__); spin_lock_irqsave(&cadence_qspi->lock, flags); if (cadence_qspi->running) { @@ -274,6 +281,12 @@ static int cadence_qspi_of_get_pdata(struct platform_device *pdev) } f_pdata->block_size = prop; + if (of_property_read_u32(nc, "read-delay", &prop)) { + dev_err(&pdev->dev, "couldn't determine read-delay\n"); + return -ENXIO; + } + f_pdata->read_delay = prop; + if (of_property_read_u32(nc, "tshsl-ns", &prop)) { dev_err(&pdev->dev, "couldn't determine tshsl-ns\n"); return -ENXIO; @@ -310,6 +323,10 @@ static int cadence_qspi_probe(struct platform_device *pdev) struct cqspi_platform_data *pdata; int status; + pr_debug("%s\n", __func__); + pr_debug("%s %s %s\n", __func__, + pdev->name, pdev->id_entry->name); + master = spi_alloc_master(&pdev->dev, sizeof(*cadence_qspi)); if (master == NULL) { dev_err(&pdev->dev, "spi_alloc_master failed\n"); @@ -422,15 +439,15 @@ static int cadence_qspi_probe(struct platform_device *pdev) master->num_chipselect = pdata->num_chipselect; platform_set_drvdata(pdev, master); + cadence_qspi_apb_controller_init(cadence_qspi); + cadence_qspi->current_cs = -1; + pr_debug("%s call spi_register_master\n", __func__); status = spi_register_master(master); if (status) { dev_err(&pdev->dev, "spi_register_master failed\n"); goto err_of; } - cadence_qspi_apb_controller_init(cadence_qspi); - cadence_qspi->current_cs = -1; - dev_info(&pdev->dev, "Cadence QSPI controller driver\n"); return 0; diff --git a/drivers/spi/spi-cadence-qspi.h b/drivers/spi/spi-cadence-qspi.h index 86f5d65eade5f..ce9b360816c38 100644 --- a/drivers/spi/spi-cadence-qspi.h +++ b/drivers/spi/spi-cadence-qspi.h @@ -29,6 +29,7 @@ struct cqspi_flash_pdata { unsigned int page_size; unsigned int block_size; unsigned int quad; + unsigned int read_delay; unsigned int tshsl_ns; unsigned int tsd2d_ns; unsigned int tchsh_ns; From 5b0a69d439eeb3af59b0e905857997d78836364f Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 11 Mar 2014 15:35:03 -0500 Subject: [PATCH 178/201] FogBugz #189684-1: Enhance QSPI driver to use common clock framework Modify the QSPI driver so that it can get the clock from the DTS entry and use the clock driver to get the correct clock rate. Also update the GPLv2 license. Signed-off-by: Dinh Nguyen --- drivers/spi/spi-cadence-qspi.c | 35 +++++++++++++++++----------------- drivers/spi/spi-cadence-qspi.h | 22 ++++++++++----------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/drivers/spi/spi-cadence-qspi.c b/drivers/spi/spi-cadence-qspi.c index 21617ed3d989d..8f5ee8c7aa28b 100644 --- a/drivers/spi/spi-cadence-qspi.c +++ b/drivers/spi/spi-cadence-qspi.c @@ -1,23 +1,21 @@ /* * Driver for Cadence QSPI Controller * - * Copyright (C) 2012 Altera Corporation + * Copyright Altera Corporation (C) 2012-2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ - +#include #include #include #include @@ -242,12 +240,6 @@ static int cadence_qspi_of_get_pdata(struct platform_device *pdev) } pdata->num_chipselect = prop; - if (of_property_read_u32(np, "master-ref-clk", &prop)) { - dev_err(&pdev->dev, "couldn't determine master-ref-clk\n"); - return -ENXIO; - } - pdata->master_ref_clk_hz = prop; - if (of_property_read_u32(np, "ext-decoder", &prop)) { dev_err(&pdev->dev, "couldn't determine ext-decoder\n"); return -ENXIO; @@ -429,6 +421,13 @@ static int cadence_qspi_probe(struct platform_device *pdev) pdev->dev.platform_data = pdata; pdata->qspi_ahb_phy = res_ahb->start; + cadence_qspi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(cadence_qspi->clk)) { + dev_err(&pdev->dev, "cannot get qspi clk\n"); + return PTR_ERR(cadence_qspi->clk); + } + pdata->master_ref_clk_hz = clk_get_rate(cadence_qspi->clk); + status = cadence_qspi_of_get_pdata(pdev); if (status) { dev_err(&pdev->dev, "Get platform data failed.\n"); diff --git a/drivers/spi/spi-cadence-qspi.h b/drivers/spi/spi-cadence-qspi.h index ce9b360816c38..7e0a20986dd69 100644 --- a/drivers/spi/spi-cadence-qspi.h +++ b/drivers/spi/spi-cadence-qspi.h @@ -1,21 +1,19 @@ /* * Driver for Cadence QSPI Controller * - * Copyright (C) 2012 Altera Corporation + * Copyright Altera Corporation (C) 2012-2014. All rights reserved. * * 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. + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. * - * 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. + * This program is distributed in the hope 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ #ifndef __CADENCE_QSPI_H__ @@ -54,6 +52,8 @@ struct struct_cqspi struct list_head msg_queue; struct platform_device *pdev; + struct clk *clk; + /* lock protects queue and registers */ spinlock_t lock; /* Virtual base address of the controller */ From 6f0c440a787a4c5b6e928b8b103c33a8cab64714 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Mon, 9 Sep 2013 10:02:43 -0500 Subject: [PATCH 179/201] FogBugz #152777: Fix out-of-order QSPI chip select configuration. Set chip select value before using it as index into array. Signed-off-by: Graham Moore --- drivers/spi/spi-cadence-qspi-apb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-cadence-qspi-apb.c b/drivers/spi/spi-cadence-qspi-apb.c index 921d5505343a2..9a1488c75eb7d 100644 --- a/drivers/spi/spi-cadence-qspi-apb.c +++ b/drivers/spi/spi-cadence-qspi-apb.c @@ -859,6 +859,12 @@ int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, return -EINVAL; } + /* Switch chip select. */ + if (cadence_qspi->current_cs != spi->chip_select) { + cadence_qspi->current_cs = spi->chip_select; + cadence_qspi_switch_cs(cadence_qspi, spi->chip_select); + } + /* Setup baudrate divisor and delays */ f_pdata = &(pdata->f_pdata[cadence_qspi->current_cs]); sclk = cmd_xfer->speed_hz ? @@ -872,12 +878,6 @@ int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, f_pdata->read_delay); cadence_qspi_apb_controller_enable(iobase); - /* Switch chip select. */ - if (cadence_qspi->current_cs != spi->chip_select) { - cadence_qspi->current_cs = spi->chip_select; - cadence_qspi_switch_cs(cadence_qspi, spi->chip_select); - } - /* * Use STIG command to send if the transfer length is less than * 4 or if only one transfer. From 15f961dac7877464dfe31947bf81aadbb1f3020c Mon Sep 17 00:00:00 2001 From: "grmoore@altera.com" Date: Tue, 29 Apr 2014 10:29:51 -0500 Subject: [PATCH 180/201] mtd: spi-nor: add support for flag status register on Micron chips Some new Micron flash chips require reading the flag status register to determine when operations have completed. Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register. This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips. Signed-off-by: Graham Moore Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/spi-nor.c | 52 +++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 4 +++ 2 files changed, 56 insertions(+) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index c713c86567102..7da3a7067c357 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -47,6 +47,25 @@ static int read_sr(struct spi_nor *nor) return val; } +/* + * Read the flag status register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_fsr(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1); + if (ret < 0) { + pr_err("error %d reading FSR\n", ret); + return ret; + } + + return val; +} + /* * Read configuration register, returning its value in the * location. Return the configuration register value. @@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor) return -ETIMEDOUT; } +static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor) +{ + unsigned long deadline; + int sr; + int fsr; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + cond_resched(); + + sr = read_sr(nor); + if (sr < 0) { + break; + } else if (!(sr & SR_WIP)) { + fsr = read_fsr(nor); + if (fsr < 0) + break; + if (fsr & FSR_READY) + return 0; + } + } while (!time_after_eq(jiffies, deadline)); + + return -ETIMEDOUT; +} + /* * Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error. @@ -402,6 +447,7 @@ struct flash_info { #define SECT_4K_PMC 0x10 /* SPINOR_OP_BE_4K_PMC works uniformly */ #define SPI_NOR_DUAL_READ 0x20 /* Flash supports Dual Read */ #define SPI_NOR_QUAD_READ 0x40 /* Flash supports Quad Read */ +#define USE_FSR 0x80 /* use flag status register */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -488,6 +534,8 @@ const struct spi_device_id spi_nor_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) }, + { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) }, /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, @@ -965,6 +1013,10 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, else mtd->_write = spi_nor_write; + if ((info->flags & USE_FSR) && + nor->wait_till_ready == spi_nor_wait_till_ready) + nor->wait_till_ready = spi_nor_wait_till_fsr_ready; + /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { nor->erase_opcode = SPINOR_OP_BE_4K; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 53241842a7ab4..9e6294f32ba88 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -34,6 +34,7 @@ #define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */ #define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */ #define SPINOR_OP_RDCR 0x35 /* Read configuration register */ +#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */ @@ -66,6 +67,9 @@ #define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */ +/* Flag Status Register bits */ +#define FSR_READY 0x80 + /* Configuration Register bits. */ #define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */ From 3e12fc6fdf0bee1b3656437e6f15b0d7162a2f81 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Mon, 11 Aug 2014 14:22:14 -0500 Subject: [PATCH 181/201] Set spi_master addr_width parameter in m25p80, needed by Cadence QSPI driver. --- drivers/mtd/devices/m25p80.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index ed7e0a1bed3ce..4d0c82aac54e3 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -227,6 +227,8 @@ static int m25p_probe(struct spi_device *spi) if (ret) return ret; + spi->addr_width = nor->addr_width; + data = dev_get_platdata(&spi->dev); ppdata.of_node = spi->dev.of_node; From 883a7cbe8881b16e18d6ed8ef35cacb05282219f Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Wed, 6 Nov 2013 15:53:49 -0600 Subject: [PATCH 182/201] FogBugz #166244: Compilation error in spi-dw-pl330.c Updated calls to device_prep_slave_sg() because the parameter list had changed since the last time spi-dw-pl330.c was compiled. Signed-off-by: Graham Moore --- drivers/spi/spi-dw-pl330.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-dw-pl330.c b/drivers/spi/spi-dw-pl330.c index 050b9b4cd5b19..413a382935c34 100644 --- a/drivers/spi/spi-dw-pl330.c +++ b/drivers/spi/spi-dw-pl330.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -165,7 +166,8 @@ static int spi_pl330_dma_transfer(struct dw_spi *dws, int cs_change) &dws->tx_sgl, 1, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT, + NULL); txdesc->callback = spi_pl330_dma_done; txdesc->callback_param = dws; @@ -195,7 +197,8 @@ static int spi_pl330_dma_transfer(struct dw_spi *dws, int cs_change) &dws->rx_sgl, 1, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT, + NULL); rxdesc->callback = spi_pl330_dma_done; rxdesc->callback_param = dws; From 12ef95cb1c95d46a91935f5b311fbb50f10c1045 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Thu, 7 Nov 2013 11:32:07 -0600 Subject: [PATCH 183/201] FogBugz #166487: Support QSPI device DMA on SoCFPGA Support for DMA transfers was added to the Cadence QSPI driver. There is a fair amount of new code, but the flow of the driver was minimally changed. If the code is unable to initialize DMA channels, it falls back to non-DMA operation. Enabling of the DMA transfers is done through the device tree. Peripheral Request IDs for the QSPI are specified in the device tree. Device tree documentation was updated. Add code to set maxburst to 1 for qspi, so that burst patch will work properly. V2: Check error return from device_prep_slave_sg Use #defines for request byte counts Replaced pr_debug/pr_err with dev_dbg/dev_err Signed-off-by: Graham Moore --- .../bindings/spi/spi-cadence-qspi.txt | 7 +- drivers/spi/spi-cadence-qspi-apb.c | 295 +++++++++++++++++- drivers/spi/spi-cadence-qspi.c | 81 +++++ drivers/spi/spi-cadence-qspi.h | 8 + 4 files changed, 387 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt b/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt index d4dda9a8ae985..7d7678bcf27c8 100644 --- a/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt @@ -15,7 +15,9 @@ Required properties: - bus-num : Number of the SPI bus to which the controller is connected. Optional properties: -No optional properties +- enable-dma : boolean, if present, then DMA is used for reads and writes +- tx-dma-peri-id : peripheral request id for tx dma channel, required if enable-dma is present +- rx-dma-peri-id : peripheral request id for rx dma channel, required if enable-dma is present Example: @@ -31,4 +33,7 @@ Example: num-chipselect = <4>; fifo-depth = <128>; bus-num = <2>; + enable-dma; + tx-dma-peri-id = <24>; + rx-dma-peri-id = <25>; } diff --git a/drivers/spi/spi-cadence-qspi-apb.c b/drivers/spi/spi-cadence-qspi-apb.c index 9a1488c75eb7d..3ccaaa12b20e1 100644 --- a/drivers/spi/spi-cadence-qspi-apb.c +++ b/drivers/spi/spi-cadence-qspi-apb.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "spi-cadence-qspi.h" #include "spi-cadence-qspi-apb.h" @@ -91,6 +93,8 @@ static void __hex_dump(unsigned int address_to_print, /****************************************************************************/ +#define CQSPI_NUMSGLREQBYTES (0) +#define CQSPI_NUMBURSTREQBYTES (4) void cadence_qspi_apb_delay(struct struct_cqspi *cadence_qspi, unsigned int ref_clk, unsigned int sclk_hz); @@ -400,6 +404,185 @@ static int cadence_qspi_apb_command_write(void *reg_base, unsigned txlen, return cadence_qspi_apb_exec_flash_cmd(reg_base, reg); } +static void cadence_qspi_dma_done(void *arg) +{ + struct struct_cqspi *cadence_qspi = arg; + cadence_qspi->dma_done = 1; + wake_up(&cadence_qspi->waitqueue); +} + +#define CQSPI_IS_DMA_READ (true) +#define CQSPI_IS_DMA_WRITE (false) + +static void cadence_qspi_apb_dma_cleanup( + struct struct_cqspi *cadence_qspi, + unsigned datalen, bool do_read) +{ + struct platform_device *pdev = cadence_qspi->pdev; + dma_unmap_single(&pdev->dev, cadence_qspi->dma_addr, + datalen, do_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); +} + +static int cadence_qspi_apb_dma_start( + struct struct_cqspi *cadence_qspi, + unsigned datalen, unsigned char *databuf, + bool do_read) +{ + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + struct dma_chan *dmachan; + struct dma_slave_config dmaconf; + struct dma_async_tx_descriptor *dmadesc = NULL; + struct scatterlist sgl; + enum dma_data_direction data_direction; + + if (do_read) { + dmachan = cadence_qspi->rxchan; + data_direction = DMA_FROM_DEVICE; + dmaconf.direction = DMA_DEV_TO_MEM; + dmaconf.src_addr = pdata->qspi_ahb_phy; + dmaconf.src_addr_width = 4; + dmaconf.src_maxburst = 1; + } else { + dmachan = cadence_qspi->txchan; + data_direction = DMA_TO_DEVICE; + dmaconf.direction = DMA_MEM_TO_DEV; + dmaconf.dst_addr = pdata->qspi_ahb_phy; + dmaconf.dst_addr_width = 4; + dmaconf.dst_maxburst = 1; + } + + /* map the buffer address */ + cadence_qspi->dma_addr = dma_map_single(&pdev->dev, + databuf, datalen, data_direction); + if (dma_mapping_error(&pdev->dev, cadence_qspi->dma_addr)) { + dev_err(&pdev->dev, "dma_map_single failed\n"); + return -EINVAL; + } + + /* set up slave config */ + dmachan->device->device_control(dmachan, DMA_SLAVE_CONFIG, + (unsigned long) &dmaconf); + + /* get dmadesc, we use scatterlist API, with one + memory buffer in the list */ + memset(&sgl, 0, sizeof(sgl)); + sgl.dma_address = cadence_qspi->dma_addr; + sgl.length = datalen; + + dmadesc = dmachan->device->device_prep_slave_sg(dmachan, + &sgl, + 1, + dmaconf.direction, + DMA_PREP_INTERRUPT, + NULL); + if (!dmadesc) { + cadence_qspi_apb_dma_cleanup(cadence_qspi, datalen, do_read); + return -ENOMEM; + } + dmadesc->callback = cadence_qspi_dma_done; + dmadesc->callback_param = cadence_qspi; + + /* start DMA */ + cadence_qspi->dma_done = 0; + dmadesc->tx_submit(dmadesc); + dma_async_issue_pending(dmachan); + + return 0; +} + +static int cadence_qspi_apb_indirect_read_dma( + struct struct_cqspi *cadence_qspi, + unsigned rxlen, unsigned char *rxbuf) +{ + int ret = 0; + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + void *reg_base = cadence_qspi->iobase; + unsigned int reg; + unsigned int timeout; + unsigned int watermark = CQSPI_REG_SRAM_THRESHOLD_BYTES; + + if (rxlen < watermark) + watermark = rxlen; + + ret = cadence_qspi_apb_dma_start(cadence_qspi, + rxlen, rxbuf, CQSPI_IS_DMA_READ); + if (ret) + return ret; + + /* Set up qspi dma */ + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_DMA_MASK; + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); + + reg = (CQSPI_NUMBURSTREQBYTES << CQSPI_REG_DMA_BURST_LSB) + | (CQSPI_NUMSGLREQBYTES << CQSPI_REG_DMA_SINGLE_LSB); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_DMA); + + /* Set up QSPI transfer */ + CQSPI_WRITEL(watermark, reg_base + CQSPI_REG_INDIRECTRDWATERMARK); + CQSPI_WRITEL(rxlen, reg_base + CQSPI_REG_INDIRECTRDBYTES); + CQSPI_WRITEL(pdata->fifo_depth - CQSPI_REG_SRAM_RESV_WORDS, + reg_base + CQSPI_REG_SRAMPARTITION); + + /* Clear all interrupts. */ + CQSPI_WRITEL(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS); + + CQSPI_WRITEL(CQSPI_IRQ_MASK_RD, reg_base + CQSPI_REG_IRQMASK); + + /* Start qspi */ + reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTRD); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_INDIRECTRD); + CQSPI_WRITEL(CQSPI_REG_INDIRECTRD_START_MASK, + reg_base + CQSPI_REG_INDIRECTRD); + + /* Wait for dma to finish */ + if (!wait_event_interruptible_timeout(cadence_qspi->waitqueue, + cadence_qspi->dma_done, CQSPI_TIMEOUT_MS)) { + pr_err("QSPI: Indirect read DMA timeout\n"); + ret = -ETIMEDOUT; + } + + /* Check indirect done status */ + timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS); + while (cadence_qspi_check_timeout(timeout)) { + reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTRD); + if (reg & CQSPI_REG_INDIRECTRD_DONE_MASK) + break; + } + + if (!(reg & CQSPI_REG_INDIRECTRD_DONE_MASK)) { + pr_err("QSPI : Indirect read completion status error with reg 0x%08x\n", + reg); + ret = -ETIMEDOUT; + } + + if (ret != 0) { + /* We had an error, cancel the indirect read */ + CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_CANCEL_MASK, + reg_base + CQSPI_REG_INDIRECTRD); + /* and cancel DMA */ + dmaengine_terminate_all(cadence_qspi->rxchan); + } + + /* Disable interrupt */ + CQSPI_WRITEL(0, reg_base + CQSPI_REG_IRQMASK); + + /* Clear indirect completion status */ + CQSPI_WRITEL(CQSPI_REG_INDIRECTRD_DONE_MASK, + reg_base + CQSPI_REG_INDIRECTRD); + + cadence_qspi_apb_dma_cleanup(cadence_qspi, rxlen, CQSPI_IS_DMA_READ); + + /* Turn off qspi dma */ + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); + reg &= ~(CQSPI_REG_CONFIG_DMA_MASK); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); + + return ret; +} + static int cadence_qspi_apb_indirect_read_setup(void *reg_base, unsigned int ahb_phy_addr, unsigned txlen, const unsigned char *txbuf, unsigned int addr_bytes) @@ -601,6 +784,100 @@ static int cadence_qspi_apb_indirect_write_setup(void *reg_base, return 0; } +static int cadence_qspi_apb_indirect_write_dma( + struct struct_cqspi *cadence_qspi, + unsigned txlen, unsigned char *txbuf) +{ + int ret = 0; + void *reg_base = cadence_qspi->iobase; + unsigned int reg; + unsigned int timeout; + + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + struct cqspi_flash_pdata *f_pdata = + &(pdata->f_pdata[cadence_qspi->current_cs]); + + pr_debug("%s txlen %d txbuf %p\n", __func__, txlen, txbuf); + + ret = cadence_qspi_apb_dma_start(cadence_qspi, + txlen, txbuf, CQSPI_IS_DMA_WRITE); + if (ret) + return ret; + + /* Set up qspi dma */ + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_DMA_MASK; + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); + + reg = (CQSPI_NUMBURSTREQBYTES << CQSPI_REG_DMA_BURST_LSB) + | (CQSPI_NUMSGLREQBYTES << CQSPI_REG_DMA_SINGLE_LSB); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_DMA); + + /* Set up QSPI transfer */ + CQSPI_WRITEL(f_pdata->page_size, + reg_base + CQSPI_REG_INDIRECTWRWATERMARK); + CQSPI_WRITEL(txlen, reg_base + CQSPI_REG_INDIRECTWRBYTES); + CQSPI_WRITEL(CQSPI_REG_SRAM_PARTITION_WR, + reg_base + CQSPI_REG_SRAMPARTITION); + + /* Clear all interrupts. */ + CQSPI_WRITEL(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS); + + CQSPI_WRITEL(CQSPI_IRQ_MASK_WR, reg_base + CQSPI_REG_IRQMASK); + + /* Start qspi */ + reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTWR); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_INDIRECTWR); + CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_START_MASK, + reg_base + CQSPI_REG_INDIRECTWR); + + /* Wait for dma to finish */ + if (!wait_event_interruptible_timeout(cadence_qspi->waitqueue, + cadence_qspi->dma_done, CQSPI_TIMEOUT_MS)) { + pr_err("QSPI: Indirect write DMA timeout\n"); + ret = -ETIMEDOUT; + } + + /* Check indirect done status */ + timeout = cadence_qspi_init_timeout(CQSPI_TIMEOUT_MS); + while (cadence_qspi_check_timeout(timeout)) { + reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTWR); + if (reg & CQSPI_REG_INDIRECTWR_DONE_MASK) + break; + } + + if (!(reg & CQSPI_REG_INDIRECTWR_DONE_MASK)) { + pr_err("QSPI : Indirect write completion status error with reg 0x%08x\n", + reg); + ret = -ETIMEDOUT; + } + + if (ret != 0) { + /* We had an error, cancel the indirect write */ + CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_CANCEL_MASK, + reg_base + CQSPI_REG_INDIRECTWR); + /* and cancel DMA */ + dmaengine_terminate_all(cadence_qspi->txchan); + } + + /* Disable interrupt */ + CQSPI_WRITEL(0, reg_base + CQSPI_REG_IRQMASK); + + /* Clear indirect completion status */ + CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_DONE_MASK, + reg_base + CQSPI_REG_INDIRECTWR); + + cadence_qspi_apb_dma_cleanup(cadence_qspi, txlen, CQSPI_IS_DMA_WRITE); + + /* Turn off qspi dma */ + reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG); + reg &= ~(CQSPI_REG_CONFIG_DMA_MASK); + CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); + + return ret; +} + static int cadence_qspi_apb_indirect_write_execute( struct struct_cqspi *cadence_qspi, unsigned txlen, const unsigned char *txbuf) @@ -901,17 +1178,29 @@ int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, ret = cadence_qspi_apb_indirect_read_setup(iobase, pdata->qspi_ahb_phy, cmd_xfer->len, cmd_xfer->tx_buf, spi->addr_width); + if (pdata->enable_dma) { + ret = cadence_qspi_apb_indirect_read_dma( + cadence_qspi, data_xfer->len, + data_xfer->rx_buf); + } else { ret = cadence_qspi_apb_indirect_read_execute( cadence_qspi, data_xfer->len, data_xfer->rx_buf); + } } else { /* Indirect write */ ret = cadence_qspi_apb_indirect_write_setup( iobase, pdata->qspi_ahb_phy, cmd_xfer->len, cmd_xfer->tx_buf); - ret = cadence_qspi_apb_indirect_write_execute( - cadence_qspi, data_xfer->len, - data_xfer->tx_buf); + if (pdata->enable_dma) { + ret = cadence_qspi_apb_indirect_write_dma( + cadence_qspi, data_xfer->len, + (unsigned char *)data_xfer->tx_buf); + } else { + ret = cadence_qspi_apb_indirect_write_execute( + cadence_qspi, data_xfer->len, + data_xfer->tx_buf); + } } } else { pr_err("QSPI : Unknown SPI transfer.\n"); diff --git a/drivers/spi/spi-cadence-qspi.c b/drivers/spi/spi-cadence-qspi.c index 8f5ee8c7aa28b..97d92ee2ef630 100644 --- a/drivers/spi/spi-cadence-qspi.c +++ b/drivers/spi/spi-cadence-qspi.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "spi-cadence-qspi.h" #include "spi-cadence-qspi-apb.h" @@ -252,6 +254,24 @@ static int cadence_qspi_of_get_pdata(struct platform_device *pdev) } pdata->fifo_depth = prop; + pdata->enable_dma = of_property_read_bool(np, "enable-dma"); + dev_info(&pdev->dev, "DMA %senabled\n", + pdata->enable_dma ? "" : "NOT "); + + if (pdata->enable_dma) { + if (of_property_read_u32(np, "tx-dma-peri-id", &prop)) { + dev_err(&pdev->dev, "couldn't determine tx-dma-peri-id\n"); + return -ENXIO; + } + pdata->tx_dma_peri_id = prop; + + if (of_property_read_u32(np, "rx-dma-peri-id", &prop)) { + dev_err(&pdev->dev, "couldn't determine rx-dma-peri-id\n"); + return -ENXIO; + } + pdata->rx_dma_peri_id = prop; + } + /* Get flash devices platform data */ for_each_child_of_node(np, nc) { if (of_property_read_u32(nc, "reg", &cs)) { @@ -306,6 +326,62 @@ static int cadence_qspi_of_get_pdata(struct platform_device *pdev) return 0; } +static void cadence_qspi_dma_shutdown(struct struct_cqspi *cadence_qspi) +{ + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + if (cadence_qspi->txchan) + dma_release_channel(cadence_qspi->txchan); + if (cadence_qspi->rxchan) + dma_release_channel(cadence_qspi->rxchan); + pdata->enable_dma = 0; + cadence_qspi->rxchan = cadence_qspi->txchan = NULL; +} + +static bool dma_channel_filter(struct dma_chan *chan, void *param) +{ + return (chan->chan_id == (unsigned int)param); +} + +static void cadence_qspi_dma_init(struct struct_cqspi *cadence_qspi) +{ + struct platform_device *pdev = cadence_qspi->pdev; + struct cqspi_platform_data *pdata = pdev->dev.platform_data; + dma_cap_mask_t mask; + unsigned int channel_num; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + channel_num = pdata->tx_dma_peri_id; + cadence_qspi->txchan = dma_request_channel(mask, + dma_channel_filter, (void *)channel_num); + if (cadence_qspi->txchan) + dev_dbg(&pdev->dev, "TX channel %s %d selected\n", + dma_chan_name(cadence_qspi->txchan), + cadence_qspi->txchan->chan_id); + else + dev_err(&pdev->dev, "could not get dma channel %d\n", + channel_num); + + channel_num = pdata->rx_dma_peri_id; + cadence_qspi->rxchan = dma_request_channel(mask, + dma_channel_filter, (void *)channel_num); + if (cadence_qspi->rxchan) + dev_dbg(&pdev->dev, "RX channel %s %d selected\n", + dma_chan_name(cadence_qspi->rxchan), + cadence_qspi->rxchan->chan_id); + else + dev_err(&pdev->dev, "could not get dma channel %d\n", + channel_num); + + if (!cadence_qspi->rxchan || !cadence_qspi->txchan) { + /* Error, fall back to non-dma mode */ + cadence_qspi_dma_shutdown(cadence_qspi); + dev_info(&pdev->dev, "falling back to non-DMA operation\n"); + } +} + static int cadence_qspi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -447,6 +523,9 @@ static int cadence_qspi_probe(struct platform_device *pdev) goto err_of; } + if (pdata->enable_dma) + cadence_qspi_dma_init(cadence_qspi); + dev_info(&pdev->dev, "Cadence QSPI controller driver\n"); return 0; @@ -476,6 +555,8 @@ static int cadence_qspi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct struct_cqspi *cadence_qspi = spi_master_get_devdata(master); + cadence_qspi_dma_shutdown(cadence_qspi); + cadence_qspi_apb_controller_disable(cadence_qspi->iobase); platform_set_drvdata(pdev, NULL); diff --git a/drivers/spi/spi-cadence-qspi.h b/drivers/spi/spi-cadence-qspi.h index 7e0a20986dd69..59acdd785bb50 100644 --- a/drivers/spi/spi-cadence-qspi.h +++ b/drivers/spi/spi-cadence-qspi.h @@ -41,6 +41,9 @@ struct cqspi_platform_data { unsigned int master_ref_clk_hz; unsigned int ext_decoder; unsigned int fifo_depth; + unsigned int enable_dma; + unsigned int tx_dma_peri_id; + unsigned int rx_dma_peri_id; struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIP_SELECT]; }; @@ -72,6 +75,11 @@ struct struct_cqspi int current_cs; /* Is queue running */ bool running; + /* DMA support */ + struct dma_chan *txchan; + struct dma_chan *rxchan; + dma_addr_t dma_addr; + int dma_done; }; /* Kernel function hook */ From 7e04905b1476883fef8fe61b34160e85c4403ef7 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Thu, 1 May 2014 14:11:21 -0500 Subject: [PATCH 184/201] FogBugz #201449: Fix bug found by sparse tool. The sparse code analysis tools identified a locking problem in the Cadence QSPI driver. This patch is the fix. In the process, I removed a continue, renamed a variable, and reversed the logic of an if-block. Signed-off-by: Graham Moore --- drivers/spi/spi-cadence-qspi.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-cadence-qspi.c b/drivers/spi/spi-cadence-qspi.c index 97d92ee2ef630..e7c9b6b4caf07 100644 --- a/drivers/spi/spi-cadence-qspi.c +++ b/drivers/spi/spi-cadence-qspi.c @@ -76,7 +76,7 @@ static void cadence_qspi_work(struct work_struct *work) struct spi_transfer *xfer[CQSPI_MAX_TRANS]; int status = 0; int n_trans = 0; - int next_in_queue = 0; + int skip_xfer = 0; spi_msg = container_of(cadence_qspi->msg_queue.next, struct spi_message, queue); @@ -91,27 +91,26 @@ static void cadence_qspi_work(struct work_struct *work) CQSPI_MAX_TRANS); /* Skip process the queue if number of * transaction is greater than max 2. */ - next_in_queue = 1; + skip_xfer = 1; break; } xfer[n_trans++] = spi_xfer; } - /* Continue to next queue if next_in_queue is set. */ - if (next_in_queue) - continue; + if (!skip_xfer) { - status = cadence_qspi_apb_process_queue(cadence_qspi, spi, - n_trans, xfer); + status = cadence_qspi_apb_process_queue(cadence_qspi, + spi, n_trans, xfer); - if (!status) { - spi_msg->actual_length += xfer[0]->len; - if (n_trans > 1) - spi_msg->actual_length += xfer[1]->len; - } + if (!status) { + spi_msg->actual_length += xfer[0]->len; + if (n_trans > 1) + spi_msg->actual_length += xfer[1]->len; + } - spi_msg->status = status; - spi_msg->complete(spi_msg->context); + spi_msg->status = status; + spi_msg->complete(spi_msg->context); + } spin_lock_irqsave(&cadence_qspi->lock, flags); } spin_unlock_irqrestore(&cadence_qspi->lock, flags); From 756bd6383c93efb8967aa17b02116a9d896272ff Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Fri, 16 May 2014 10:59:55 -0500 Subject: [PATCH 185/201] FogBugz #177169: Implement DMA for Cadence QSPI controller. Use generic DMA functions and existing device tree entries for Cadence QSPI rather than creating our own. Signed-off-by: Graham Moore --- .../bindings/spi/spi-cadence-qspi.txt | 11 ++- drivers/spi/spi-cadence-qspi-apb.c | 81 ++++++++++++------- drivers/spi/spi-cadence-qspi.c | 42 ++-------- drivers/spi/spi-cadence-qspi.h | 1 - 4 files changed, 61 insertions(+), 74 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt b/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt index 7d7678bcf27c8..ddf55bab5f8cd 100644 --- a/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt @@ -15,9 +15,9 @@ Required properties: - bus-num : Number of the SPI bus to which the controller is connected. Optional properties: -- enable-dma : boolean, if present, then DMA is used for reads and writes -- tx-dma-peri-id : peripheral request id for tx dma channel, required if enable-dma is present -- rx-dma-peri-id : peripheral request id for rx dma channel, required if enable-dma is present +- dmas : DMA request lines. Should be <&pdma 0 &pdma 1>. If present, + then DMA is used for reads and writes +- dma-names : Names of DMA request lines. Should be "tx", "rx". Example: @@ -33,7 +33,6 @@ Example: num-chipselect = <4>; fifo-depth = <128>; bus-num = <2>; - enable-dma; - tx-dma-peri-id = <24>; - rx-dma-peri-id = <25>; + dmas = <&pdma 24 &pdma 25>; + dma-names = "tx", "rx"; } diff --git a/drivers/spi/spi-cadence-qspi-apb.c b/drivers/spi/spi-cadence-qspi-apb.c index 3ccaaa12b20e1..782c84099a91d 100644 --- a/drivers/spi/spi-cadence-qspi-apb.c +++ b/drivers/spi/spi-cadence-qspi-apb.c @@ -93,8 +93,10 @@ static void __hex_dump(unsigned int address_to_print, /****************************************************************************/ -#define CQSPI_NUMSGLREQBYTES (0) -#define CQSPI_NUMBURSTREQBYTES (4) +/* 1-beat single, 4-byte width = 4-byte single = 2**2 */ +#define CQSPI_NUMSGLREQBYTES (2) +/* 16-beat burst, 4-byte width = 64-byte bursts = 2**6 */ +#define CQSPI_NUMBURSTREQBYTES (6) void cadence_qspi_apb_delay(struct struct_cqspi *cadence_qspi, unsigned int ref_clk, unsigned int sclk_hz); @@ -423,6 +425,17 @@ static void cadence_qspi_apb_dma_cleanup( datalen, do_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } +static int calculate_burst_bytes_exp(int numbytes) +{ + int exp = CQSPI_NUMBURSTREQBYTES; + while ((1< CQSPI_FIFO_WIDTH) { + if ((numbytes % (1 << exp)) == 0) + break; + exp--; + } + return exp; +} + static int cadence_qspi_apb_dma_start( struct struct_cqspi *cadence_qspi, unsigned datalen, unsigned char *databuf, @@ -433,8 +446,15 @@ static int cadence_qspi_apb_dma_start( struct dma_chan *dmachan; struct dma_slave_config dmaconf; struct dma_async_tx_descriptor *dmadesc = NULL; - struct scatterlist sgl; enum dma_data_direction data_direction; + int burst_len_exp; + int burst_words; + + if (datalen % CQSPI_FIFO_WIDTH) + return -EINVAL; + + burst_len_exp = calculate_burst_bytes_exp(datalen); + burst_words = (1<rxchan; @@ -442,14 +462,14 @@ static int cadence_qspi_apb_dma_start( dmaconf.direction = DMA_DEV_TO_MEM; dmaconf.src_addr = pdata->qspi_ahb_phy; dmaconf.src_addr_width = 4; - dmaconf.src_maxburst = 1; + dmaconf.src_maxburst = burst_words; } else { dmachan = cadence_qspi->txchan; data_direction = DMA_TO_DEVICE; dmaconf.direction = DMA_MEM_TO_DEV; dmaconf.dst_addr = pdata->qspi_ahb_phy; dmaconf.dst_addr_width = 4; - dmaconf.dst_maxburst = 1; + dmaconf.dst_maxburst = burst_words; } /* map the buffer address */ @@ -461,21 +481,14 @@ static int cadence_qspi_apb_dma_start( } /* set up slave config */ - dmachan->device->device_control(dmachan, DMA_SLAVE_CONFIG, - (unsigned long) &dmaconf); - - /* get dmadesc, we use scatterlist API, with one - memory buffer in the list */ - memset(&sgl, 0, sizeof(sgl)); - sgl.dma_address = cadence_qspi->dma_addr; - sgl.length = datalen; - - dmadesc = dmachan->device->device_prep_slave_sg(dmachan, - &sgl, - 1, - dmaconf.direction, - DMA_PREP_INTERRUPT, - NULL); + dmaengine_slave_config(dmachan, &dmaconf); + + /* get dmadesc */ + dmadesc = dmaengine_prep_slave_single(dmachan, + cadence_qspi->dma_addr, + datalen, + dmaconf.direction, + DMA_PREP_INTERRUPT); if (!dmadesc) { cadence_qspi_apb_dma_cleanup(cadence_qspi, datalen, do_read); return -ENOMEM; @@ -501,7 +514,12 @@ static int cadence_qspi_apb_indirect_read_dma( void *reg_base = cadence_qspi->iobase; unsigned int reg; unsigned int timeout; - unsigned int watermark = CQSPI_REG_SRAM_THRESHOLD_BYTES; + int burst_len_exp; + struct cqspi_flash_pdata *f_pdata = + &(pdata->f_pdata[cadence_qspi->current_cs]); + unsigned int page_size = f_pdata->page_size; + /* use watermark of page size less one burst */ + unsigned int watermark = page_size - (1 << CQSPI_NUMBURSTREQBYTES); if (rxlen < watermark) watermark = rxlen; @@ -516,7 +534,9 @@ static int cadence_qspi_apb_indirect_read_dma( reg |= CQSPI_REG_CONFIG_DMA_MASK; CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); - reg = (CQSPI_NUMBURSTREQBYTES << CQSPI_REG_DMA_BURST_LSB) + burst_len_exp = calculate_burst_bytes_exp(rxlen); + + reg = (burst_len_exp << CQSPI_REG_DMA_BURST_LSB) | (CQSPI_NUMSGLREQBYTES << CQSPI_REG_DMA_SINGLE_LSB); CQSPI_WRITEL(reg, reg_base + CQSPI_REG_DMA); @@ -606,11 +626,6 @@ static int cadence_qspi_apb_indirect_read_setup(void *reg_base, reg = txbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB; -#ifdef CONFIG_M25PXX_USE_FAST_READ_QUAD_OUTPUT -#error WTFO - reg |= (CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB); -#endif /* CONFIG_M25PXX_USE_FAST_READ_QUAD_OUTPUT */ - /* Get address */ addr_value = cadence_qspi_apb_cmd2addr(&txbuf[1], addr_bytes); CQSPI_WRITEL(addr_value, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR); @@ -792,7 +807,7 @@ static int cadence_qspi_apb_indirect_write_dma( void *reg_base = cadence_qspi->iobase; unsigned int reg; unsigned int timeout; - + int burst_len_exp; struct platform_device *pdev = cadence_qspi->pdev; struct cqspi_platform_data *pdata = pdev->dev.platform_data; struct cqspi_flash_pdata *f_pdata = @@ -810,7 +825,9 @@ static int cadence_qspi_apb_indirect_write_dma( reg |= CQSPI_REG_CONFIG_DMA_MASK; CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG); - reg = (CQSPI_NUMBURSTREQBYTES << CQSPI_REG_DMA_BURST_LSB) + burst_len_exp = calculate_burst_bytes_exp(txlen); + + reg = (burst_len_exp << CQSPI_REG_DMA_BURST_LSB) | (CQSPI_NUMSGLREQBYTES << CQSPI_REG_DMA_SINGLE_LSB); CQSPI_WRITEL(reg, reg_base + CQSPI_REG_DMA); @@ -1178,7 +1195,8 @@ int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, ret = cadence_qspi_apb_indirect_read_setup(iobase, pdata->qspi_ahb_phy, cmd_xfer->len, cmd_xfer->tx_buf, spi->addr_width); - if (pdata->enable_dma) { + if (pdata->enable_dma && + !(data_xfer->len % CQSPI_FIFO_WIDTH)) { ret = cadence_qspi_apb_indirect_read_dma( cadence_qspi, data_xfer->len, data_xfer->rx_buf); @@ -1192,7 +1210,8 @@ int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, ret = cadence_qspi_apb_indirect_write_setup( iobase, pdata->qspi_ahb_phy, cmd_xfer->len, cmd_xfer->tx_buf); - if (pdata->enable_dma) { + if (pdata->enable_dma && + !(data_xfer->len % CQSPI_FIFO_WIDTH)) { ret = cadence_qspi_apb_indirect_write_dma( cadence_qspi, data_xfer->len, (unsigned char *)data_xfer->tx_buf); diff --git a/drivers/spi/spi-cadence-qspi.c b/drivers/spi/spi-cadence-qspi.c index e7c9b6b4caf07..105763a031c27 100644 --- a/drivers/spi/spi-cadence-qspi.c +++ b/drivers/spi/spi-cadence-qspi.c @@ -253,24 +253,10 @@ static int cadence_qspi_of_get_pdata(struct platform_device *pdev) } pdata->fifo_depth = prop; - pdata->enable_dma = of_property_read_bool(np, "enable-dma"); + pdata->enable_dma = of_property_read_bool(np, "dmas"); dev_info(&pdev->dev, "DMA %senabled\n", pdata->enable_dma ? "" : "NOT "); - if (pdata->enable_dma) { - if (of_property_read_u32(np, "tx-dma-peri-id", &prop)) { - dev_err(&pdev->dev, "couldn't determine tx-dma-peri-id\n"); - return -ENXIO; - } - pdata->tx_dma_peri_id = prop; - - if (of_property_read_u32(np, "rx-dma-peri-id", &prop)) { - dev_err(&pdev->dev, "couldn't determine rx-dma-peri-id\n"); - return -ENXIO; - } - pdata->rx_dma_peri_id = prop; - } - /* Get flash devices platform data */ for_each_child_of_node(np, nc) { if (of_property_read_u32(nc, "reg", &cs)) { @@ -337,42 +323,26 @@ static void cadence_qspi_dma_shutdown(struct struct_cqspi *cadence_qspi) cadence_qspi->rxchan = cadence_qspi->txchan = NULL; } -static bool dma_channel_filter(struct dma_chan *chan, void *param) -{ - return (chan->chan_id == (unsigned int)param); -} - static void cadence_qspi_dma_init(struct struct_cqspi *cadence_qspi) { struct platform_device *pdev = cadence_qspi->pdev; - struct cqspi_platform_data *pdata = pdev->dev.platform_data; - dma_cap_mask_t mask; - unsigned int channel_num; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - channel_num = pdata->tx_dma_peri_id; - cadence_qspi->txchan = dma_request_channel(mask, - dma_channel_filter, (void *)channel_num); + cadence_qspi->txchan = dma_request_slave_channel(&pdev->dev, "tx"); if (cadence_qspi->txchan) dev_dbg(&pdev->dev, "TX channel %s %d selected\n", dma_chan_name(cadence_qspi->txchan), cadence_qspi->txchan->chan_id); else - dev_err(&pdev->dev, "could not get dma channel %d\n", - channel_num); + dev_err(&pdev->dev, "could not get TX dma channel\n"); + - channel_num = pdata->rx_dma_peri_id; - cadence_qspi->rxchan = dma_request_channel(mask, - dma_channel_filter, (void *)channel_num); + cadence_qspi->rxchan = dma_request_slave_channel(&pdev->dev, "rx"); if (cadence_qspi->rxchan) dev_dbg(&pdev->dev, "RX channel %s %d selected\n", dma_chan_name(cadence_qspi->rxchan), cadence_qspi->rxchan->chan_id); else - dev_err(&pdev->dev, "could not get dma channel %d\n", - channel_num); + dev_err(&pdev->dev, "could not get RX dma channel\n"); if (!cadence_qspi->rxchan || !cadence_qspi->txchan) { /* Error, fall back to non-dma mode */ diff --git a/drivers/spi/spi-cadence-qspi.h b/drivers/spi/spi-cadence-qspi.h index 59acdd785bb50..6ce5046a18b2f 100644 --- a/drivers/spi/spi-cadence-qspi.h +++ b/drivers/spi/spi-cadence-qspi.h @@ -26,7 +26,6 @@ struct cqspi_flash_pdata { unsigned int page_size; unsigned int block_size; - unsigned int quad; unsigned int read_delay; unsigned int tshsl_ns; unsigned int tsd2d_ns; From b2fbee5e30278dd21cb7b14b940445705d7c91cd Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Mon, 19 May 2014 14:58:31 -0500 Subject: [PATCH 186/201] FogBugz #177169: Use managed device calls for resource mapping Use devm_request_and_ioremap instead of request_meme_region and ioremap. Signed-off-by: Graham Moore --- drivers/spi/spi-cadence-qspi.c | 50 ++++++---------------------------- 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/drivers/spi/spi-cadence-qspi.c b/drivers/spi/spi-cadence-qspi.c index 105763a031c27..1c3dcdb330f9e 100644 --- a/drivers/spi/spi-cadence-qspi.c +++ b/drivers/spi/spi-cadence-qspi.c @@ -379,49 +379,23 @@ static int cadence_qspi_probe(struct platform_device *pdev) cadence_qspi->pdev = pdev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "platform_get_resource failed\n"); - status = -ENXIO; - goto err_iomem; - } - - cadence_qspi->res = res; - - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - status = -EBUSY; - goto err_iomem; - } - - cadence_qspi->iobase = ioremap(res->start, resource_size(res)); + cadence_qspi->iobase = devm_request_and_ioremap(&pdev->dev, res); if (!cadence_qspi->iobase) { - dev_err(&pdev->dev, "ioremap failed\n"); - status = -ENOMEM; + dev_err(&pdev->dev, "devm_request_and_ioremap res 0 failed\n"); + status = -EADDRNOTAVAIL; goto err_ioremap; } + cadence_qspi->res = res; res_ahb = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res_ahb) { - dev_err(&pdev->dev, "platform_get_resource failed\n"); - status = -ENXIO; - goto err_ahbmem; - } - cadence_qspi->res_ahb = res_ahb; - - if (!request_mem_region(res_ahb->start, resource_size(res_ahb), - pdev->name)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - status = -EBUSY; - goto err_ahbmem; - } - - cadence_qspi->qspi_ahb_virt = ioremap(res_ahb->start, - resource_size(res_ahb)); + cadence_qspi->qspi_ahb_virt = + devm_request_and_ioremap(&pdev->dev, res_ahb); if (!cadence_qspi->qspi_ahb_virt) { - dev_err(&pdev->dev, "ioremap res_ahb failed\n"); - status = -ENOMEM; + dev_err(&pdev->dev, "devm_request_and_ioremap res 1 failed\n"); + status = -EADDRNOTAVAIL; goto err_ahbremap; } + cadence_qspi->res_ahb = res_ahb; cadence_qspi->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent)); @@ -506,14 +480,8 @@ static int cadence_qspi_probe(struct platform_device *pdev) err_irq: destroy_workqueue(cadence_qspi->workqueue); err_wq: - iounmap(cadence_qspi->qspi_ahb_virt); err_ahbremap: - release_mem_region(res_ahb->start, resource_size(res_ahb)); -err_ahbmem: - iounmap(cadence_qspi->iobase); err_ioremap: - release_mem_region(res->start, resource_size(res)); -err_iomem: spi_master_put(master); dev_err(&pdev->dev, "Cadence QSPI controller probe failed\n"); return status; From a43f685fb4ad3be6687a30775761e624cfb63055 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 11 Mar 2014 15:39:50 -0500 Subject: [PATCH 187/201] FogBugz #189684-2: dts part: clean up QSPI entries Moves the base QSPI dts entry into socfpga.dtsi and add board specific QSPI entry to the appropriate board file DTS files. Removes the hard-coded clock frequency. Signed-off-by: Dinh Nguyen Conflicts: arch/arm/boot/dts/socfpga.dtsi arch/arm/boot/dts/socfpga_arria5.dtsi arch/arm/boot/dts/socfpga_cyclone5.dtsi arch/arm/boot/dts/socfpga_cyclone5_socdk.dts --- .../bindings/spi/spi-cadence-qspi.txt | 3 +- arch/arm/boot/dts/socfpga.dtsi | 2 +- arch/arm/boot/dts/socfpga_arria5.dtsi | 23 ++++++++++++++ arch/arm/boot/dts/socfpga_cyclone5.dtsi | 4 +++ arch/arm/boot/dts/socfpga_cyclone5_sockit.dts | 30 +++++++++++++++++++ arch/arm/boot/dts/socfpga_vt.dts | 29 ++++++++++++++++++ 6 files changed, 88 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt b/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt index ddf55bab5f8cd..7214b2c83f31f 100644 --- a/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-cadence-qspi.txt @@ -7,7 +7,6 @@ Required properties: length of the controller register set. The second entry is the address and length of the QSPI Controller data area. - interrupts : Unit interrupt specifier for the controller interrupt. -- master-ref-clk : Specifies the frequency of the controller input clock. - ext-decoder : Value of 0 means no external chipselect decoder is connected, 1 means there is an external chipselect decoder connected. - num-chipselect : Number of chip select lines. @@ -28,7 +27,7 @@ Example: reg = <0xff705000 0x1000>, <0xffa00000 0x1000>; interrupts = <0 151 4>; - master-ref-clk = <400000000>; + clocks = <&qspi_clk>; ext-decoder = <0>; num-chipselect = <4>; fifo-depth = <128>; diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi index 43311525d0b87..502ecfd9f5664 100644 --- a/arch/arm/boot/dts/socfpga.dtsi +++ b/arch/arm/boot/dts/socfpga.dtsi @@ -776,7 +776,7 @@ #address-cells = <1>; #size-cells = <0>; reg = <0xff705000 0x1000>, - <0xffa00000 0x1000>; + <0xffa00000 0x1000>; interrupts = <0 151 4>; clocks = <&qspi_clk>; ext-decoder = <0>; /* external decoder */ diff --git a/arch/arm/boot/dts/socfpga_arria5.dtsi b/arch/arm/boot/dts/socfpga_arria5.dtsi index a2f6035acb06d..1451c165d13de 100644 --- a/arch/arm/boot/dts/socfpga_arria5.dtsi +++ b/arch/arm/boot/dts/socfpga_arria5.dtsi @@ -19,6 +19,29 @@ / { soc { + clkmgr@ffd04000 { + clocks { + osc1 { + clock-frequency = <25000000>; + }; + }; + }; + + dwmmc0@ff704000 { + num-slots = <1>; + supports-highspeed; + broken-cd; + + slot@0 { + reg = <0>; + bus-width = <4>; + }; + }; + + qspi: spi@ff705000 { + status = "okay"; + }; + sysmgr@ffd08000 { cpu1-start-addr = <0xffd080c4>; }; diff --git a/arch/arm/boot/dts/socfpga_cyclone5.dtsi b/arch/arm/boot/dts/socfpga_cyclone5.dtsi index 0e434b39b7cd0..b9c35460c58a1 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5.dtsi +++ b/arch/arm/boot/dts/socfpga_cyclone5.dtsi @@ -22,5 +22,9 @@ sysmgr@ffd08000 { cpu1-start-addr = <0xffd080c4>; }; + + qspi: spi@ff705000 { + status = "okay"; + }; }; }; diff --git a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts index 93c62c0f3592b..afd59669e05bf 100644 --- a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts +++ b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts @@ -60,3 +60,33 @@ &usb1 { status = "okay"; }; + +&qspi { + flash0: n25q00@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "n25q00"; + reg = <0>; /* chip select */ + spi-max-frequency = <100000000>; + m25p,fast-read; + page-size = <256>; + block-size = <16>; /* 2^16, 64KB */ + read-delay = <4>; /* delay value in read data capture register */ + tshsl-ns = <50>; + tsd2d-ns = <50>; + tchsh-ns = <4>; + tslch-ns = <4>; + + partition@qspi-boot { + /* 8MB for raw data. */ + label = "Flash 0 Raw Data"; + reg = <0x0 0x800000>; + }; + + partition@qspi-rootfs { + /* 120MB for jffs2 data. */ + label = "Flash 0 jffs2 Filesystem"; + reg = <0x800000 0x7800000>; + }; + }; +}; diff --git a/arch/arm/boot/dts/socfpga_vt.dts b/arch/arm/boot/dts/socfpga_vt.dts index 8b3a6dfdfa9fe..8c40909dee1d3 100644 --- a/arch/arm/boot/dts/socfpga_vt.dts +++ b/arch/arm/boot/dts/socfpga_vt.dts @@ -60,6 +60,35 @@ clock-frequency = <7000000>; }; + qspi: spi@ff705000 { + status = "okay"; + flash0: n25q128@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "n25q128"; + reg = <0>; /* chip select */ + spi-max-frequency = <100000000>; + page-size = <256>; + block-size = <16>; /* 2^16, 64KB */ + quad = <1>; /* 1-support quad */ + tshsl-ns = <200>; + tsd2d-ns = <255>; + tchsh-ns = <20>; + tslch-ns = <20>; + + partition@0 { + /* 8MB for raw data. */ + label = "Flash 0 Raw Data"; + reg = <0x0 0x800000>; + }; + partition@800000 { + /* 8MB for jffs2 data. */ + label = "Flash 0 jffs2 Filesystem"; + reg = <0x800000 0x800000>; + }; + }; + }; + timer1@ffc09000 { clock-frequency = <7000000>; }; From 664041f1384eb3e88b27b9c46c99088d1a69faf2 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Tue, 12 Aug 2014 16:56:43 -0500 Subject: [PATCH 188/201] mtd: m25p80: Add shutdown handler for m25p80 and spi-nor Add shutdown handler for m25p80 and spi-nor so that 3-byte addressing mode is set before reboot. Signed-off-by: Graham Moore [dinguyen] Cleaned up commit message Signed-off-by: Dinh Nguyen --- drivers/mtd/devices/m25p80.c | 6 ++++++ drivers/mtd/spi-nor/spi-nor.c | 9 +++++++++ include/linux/mtd/spi-nor.h | 3 +++ 3 files changed, 18 insertions(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 4d0c82aac54e3..02c9f35a46fa2 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -246,6 +246,11 @@ static int m25p_remove(struct spi_device *spi) return mtd_device_unregister(&flash->mtd); } +static void m25p_shutdown(struct spi_device *spi) +{ + struct m25p *flash = spi_get_drvdata(spi); + flash->spi_nor.shutdown(&flash->spi_nor); +} static struct spi_driver m25p80_driver = { .driver = { @@ -255,6 +260,7 @@ static struct spi_driver m25p80_driver = { .id_table = spi_nor_ids, .probe = m25p_probe, .remove = m25p_remove, + .shutdown = m25p_shutdown, /* REVISIT: many of these chips have deep power-down modes, which * should clearly be entered on suspend() to minimize power use. diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 7da3a7067c357..f164a625a6abe 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -899,6 +899,11 @@ static int set_quad_mode(struct spi_nor *nor, u32 jedec_id) } } +static void spi_nor_shutdown(struct spi_nor *nor) +{ + set_4byte(nor, nor->jedec_id, 0); +} + static int spi_nor_check(struct spi_nor *nor) { if (!nor->dev || !nor->read || !nor->write || @@ -911,6 +916,8 @@ static int spi_nor_check(struct spi_nor *nor) nor->read_id = spi_nor_read_id; if (!nor->wait_till_ready) nor->wait_till_ready = spi_nor_wait_till_ready; + if (!nor->shutdown) + nor->shutdown = spi_nor_shutdown; return 0; } @@ -977,6 +984,8 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, mutex_init(&nor->lock); + nor->jedec_id = info->jedec_id; + /* * Atmel, SST and Intel/Numonyx serial nor tend to power * up with the software protection bits set diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 9e6294f32ba88..e0b7c52c1f6bd 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -158,6 +158,8 @@ struct spi_nor { u8 read_opcode; u8 read_dummy; u8 program_opcode; + u32 jedec_id; + enum read_mode flash_read; bool sst_write_second; struct spi_nor_xfer_cfg cfg; @@ -174,6 +176,7 @@ struct spi_nor { int write_enable); const struct spi_device_id *(*read_id)(struct spi_nor *nor); int (*wait_till_ready)(struct spi_nor *nor); + void (*shutdown)(struct spi_nor *nor); int (*read)(struct spi_nor *nor, loff_t from, size_t len, size_t *retlen, u_char *read_buf); From ba3412d6e9ea9504c4ab51582d6c584b29f49b29 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Fri, 26 Apr 2013 19:56:26 +0530 Subject: [PATCH 189/201] mailbox: Introduce a new common API Introduce common framework for client/protocol drivers and controller drivers of Inter-Processor-Communication (IPC). Client driver developers should have a look at include/linux/mailbox_client.h to understand the part of the API exposed to client drivers. Similarly controller driver developers should have a look at include/linux/mailbox_controller.h Signed-off-by: Jassi Brar Conflicts: drivers/mailbox/mailbox.c --- drivers/mailbox/Makefile | 4 ++ drivers/mailbox/mailbox.c | 62 +++++++++++++++++- include/linux/mailbox.h | 28 ++++---- include/linux/mailbox_client.h | 87 ++++++++++++++++++++++++ include/linux/mailbox_controller.h | 102 +++++++++++++++++++++++++++++ 5 files changed, 268 insertions(+), 15 deletions(-) create mode 100644 include/linux/mailbox_client.h create mode 100644 include/linux/mailbox_controller.h diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 733d0a6ec03ea..c8f70394a4f5a 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -1,3 +1,7 @@ +# Generic MAILBOX API + +obj-$(CONFIG_MAILBOX) += mailbox.o + obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o obj-$(CONFIG_OMAP_MBOX) += omap-mailbox.o diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 0888be5b664cf..ed875bb6cb51b 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -14,7 +14,67 @@ #include #include -#include "mailbox_internal.h" +/* + * The length of circular buffer for queuing messages from a client. + * 'msg_count' tracks the number of buffered messages while 'msg_free' + * is the index where the next message would be buffered. + * We shouldn't need it too big because every transferr is interrupt + * triggered and if we have lots of data to transfer, the interrupt + * latencies are going to be the bottleneck, not the buffer length. + * Besides, ipc_send_message could be called from atomic context and + * the client could also queue another message from the notifier 'txcb' + * of the last transfer done. + * REVIST: If too many platforms see the "Try increasing MBOX_TX_QUEUE_LEN" + * print, it needs to be taken from config option or somesuch. + */ +#define MBOX_TX_QUEUE_LEN 20 + +#define TXDONE_BY_IRQ (1 << 0) /* controller has remote RTR irq */ +#define TXDONE_BY_POLL (1 << 1) /* controller can read status of last TX */ +#define TXDONE_BY_ACK (1 << 2) /* S/W ACK recevied by Client ticks the TX */ + +struct ipc_chan { + char name[16]; /* link_name */ + struct ipc_con *con; /* Parent Controller */ + unsigned txdone_method; + + /* Cached values from controller */ + struct ipc_link *link; + struct ipc_link_ops *link_ops; + + /* Cached values from client */ + void *cl_id; + void (*rxcb)(void *cl_id, void *mssg); + void (*txcb)(void *cl_id, void *mssg, enum xfer_result r); + bool tx_block; + unsigned long tx_tout; + struct completion tx_complete; + + void *active_req; + unsigned msg_count, msg_free; + void *msg_data[MBOX_TX_QUEUE_LEN]; + bool assigned; + /* Serialize access to the channel */ + spinlock_t lock; + /* Hook to add to the controller's list of channels */ + struct list_head node; + /* Notifier to all clients waiting on aquiring this channel */ + struct blocking_notifier_head avail; +}; + +/* Internal representation of a controller */ +struct ipc_con { + char name[16]; /* controller_name */ + struct list_head channels; + /* + * If the controller supports only TXDONE_BY_POLL, + * this timer polls all the links for txdone. + */ + struct timer_list poll; + unsigned period; + /* Hook to add to the global controller list */ + struct list_head node; +}; static LIST_HEAD(ipc_cons); static DEFINE_MUTEX(con_mutex); diff --git a/include/linux/mailbox.h b/include/linux/mailbox.h index 5161f63ec1c87..232e2c4c792ef 100644 --- a/include/linux/mailbox.h +++ b/include/linux/mailbox.h @@ -1,17 +1,17 @@ /* - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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 . + * 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. */ -int pl320_ipc_transmit(u32 *data); -int pl320_ipc_register_notifier(struct notifier_block *nb); -int pl320_ipc_unregister_notifier(struct notifier_block *nb); +#ifndef __MAILBOX_H +#define __MAILBOX_H + +enum xfer_result { + XFER_OK = 0, + XFER_ERR, +}; + +typedef unsigned request_token_t; + +#endif /* __MAILBOX_H */ diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h new file mode 100644 index 0000000000000..c43f2c72656a9 --- /dev/null +++ b/include/linux/mailbox_client.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef __MAILBOX_CLIENT_H +#define __MAILBOX_CLIENT_H + +#include + +/** + * struct ipc_client - User of a mailbox + * @chan_name: the "controller:channel" this client wants + * @cl_id: The identity to associate any tx/rx data with + * @rxcb: atomic callback to provide client the data received + * @txcb: atomic callback to tell client of data transmission + * @tx_block: if the ipc_send_message should block until data is transmitted + * @tx_tout: Max block period in ms before TX is assumed failure + * @knows_txdone: if the client could run the TX state machine. Usually if + * the client receives some ACK packet for transmission. Unused if the + * controller already has TX_Done/RTR IRQ. + * @link_data: Optional controller specific parameters during channel request + */ +struct ipc_client { + char *chan_name; + void *cl_id; + void (*rxcb)(void *cl_id, void *mssg); + void (*txcb)(void *cl_id, void *mssg, enum xfer_result r); + bool tx_block; + unsigned long tx_tout; + bool knows_txdone; + void *link_data; +}; + +/** + * The Client specifies its requirements and capabilities while asking for + * a channel/mailbox by name. It can't be called from atomic context. + * The channel is exclusively allocated and can't be used by another + * client before the owner calls ipc_free_channel. + */ +void *ipc_request_channel(struct ipc_client *cl); + +/** + * For client to submit data to the controller destined for a remote + * processor. If the client had set 'tx_block', the call will return + * either when the remote receives the data or when 'tx_tout' millisecs + * run out. + * In non-blocking mode, the requests are buffered by the API and a + * non-zero token is returned for each queued request. If the queue + * was full the returned token will be 0. Upon failure or successful + * TX, the API calls 'txcb' from atomic context, from which the client + * could submit yet another request. + * In blocking mode, 'txcb' is not called, effectively making the + * queue length 1. The returned value is 0 if TX timed out, some + * non-zero value upon success. + */ +request_token_t ipc_send_message(void *channel, void *mssg); + +/** + * The way for a client to run the TX state machine. This works + * only if the client sets 'knows_txdone' and the IPC controller + * doesn't get an IRQ for TX_Done/Remote_RTR. + */ +void ipc_client_txdone(void *channel, enum xfer_result r); + +/** + * The client relinquishes control of a mailbox by this call, + * make it available to other clients. + * The ipc_request/free_channel are light weight calls, so the + * client should avoid holding it when it doesn't need to + * transfer data. + */ +void ipc_free_channel(void *channel); + +/** + * The client make ask the API to be notified when a particular channel + * becomes available to be acquired again. + */ +int ipc_notify_chan_register(const char *name, struct notifier_block *nb); + +/** + * The client is no more interested in acquiring the channel. + */ +void ipc_notify_chan_unregister(const char *name, struct notifier_block *nb); + +#endif /* __MAILBOX_CLIENT_H */ diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h new file mode 100644 index 0000000000000..d4ef764e25412 --- /dev/null +++ b/include/linux/mailbox_controller.h @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#ifndef __MAILBOX_CONTROLLER_H +#define __MAILBOX_CONTROLLER_H + +#include + +/** + * struct ipc_link - s/w representation of a communication link + * @link_name: Literal name assigned to the link. Physically + * identical channels may have the same name. + * @api_priv: hook for the API to map its private data on the link + * Controller driver must not touch it. + */ +struct ipc_link { + char link_name[16]; + void *api_priv; +}; + +/** + * struct ipc_link - s/w representation of a communication link + * @send_data: The API asks the IPC controller driver, in atomic + * context try to transmit a message on the bus. Returns 0 if + * data is accepted for transmission, -EBUSY while rejecting + * if the remote hasn't yet read the last data sent. Actual + * transmission of data is reported by the controller via + * ipc_link_txdone (if it has some TX ACK irq). It must not + * block. + * @startup: Called when a client requests the link. The controller + * could ask clients for additional parameters of communication + * to be provided via client's link_data. This call may block. + * After this call the Controller must forward any data received + * on the link by calling ipc_link_received_data (which won't block) + * @shutdown: Called when a client relinquishes control of a link. + * This call may block too. The controller must not forwared + * any received data anymore. + * @is_ready: If the controller sets 'txdone_poll', the API calls + * this to poll status of last TX. The controller must give priority + * to IRQ method over polling and never set both txdone_poll and + * txdone_irq. Only in polling mode 'send_data' is expected to + * return -EBUSY. Used only if txdone_poll:=true && txdone_irq:=false + */ +struct ipc_link_ops { + int (*send_data)(struct ipc_link *link, void *data); + int (*startup)(struct ipc_link *link, void *params); + void (*shutdown)(struct ipc_link *link); + bool (*is_ready)(struct ipc_link *link); +}; + +/** + * struct ipc_controller - Controller of a class of communication links + * @controller_name: Literal name of the controller. + * @ops: Operators that work on each communication link + * @links: Null terminated array of links. + * @txdone_irq: Indicates if the controller can report to API when the + * last transmitted data was read by the remote. Eg, if it has some + * TX ACK irq. + * @txdone_poll: If the controller can read but not report the TX done. + * Eg, is some register shows the TX status but no interrupt rises. + * Ignored if 'txdone_irq' is set. + * @txpoll_period: If 'txdone_poll' is in effect, the API polls for + * last TX's status after these many millisecs + */ +struct ipc_controller { + char controller_name[16]; + struct ipc_link_ops *ops; + struct ipc_link **links; + bool txdone_irq; + bool txdone_poll; + unsigned txpoll_period; +}; + +/** + * The controller driver registers its communication links to the + * global pool managed by the API. + */ +int ipc_links_register(struct ipc_controller *ipc_con); + +/** + * After startup and before shutdown any data received on the link + * is pused to the API via atomic ipc_link_received_data() API. + * The controller should ACK the RX only after this call returns. + */ +void ipc_link_received_data(struct ipc_link *link, void *data); + +/** + * The controller the has IRQ for TX ACK calls this atomic API + * to tick the TX state machine. It works only if txdone_irq + * is set by the controller. + */ +void ipc_link_txdone(struct ipc_link *link, enum xfer_result r); + +/** + * Purge the links from the global pool maintained by the API. + */ +void ipc_links_unregister(struct ipc_controller *ipc_con); + +#endif /* __MAILBOX_CONTROLLER_H */ From 04faf0f656c8c3e1b7c3f81497a443c346cc4e1f Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Thu, 16 May 2013 15:45:52 -0500 Subject: [PATCH 190/201] mailbox: move the internal definitions into a private file This is needed for extracting the omap_mbox. The OMAP mailbox code has a need for exporting some pre-existing API to not break the current clients. Signed-off-by: Suman Anna --- drivers/mailbox/mailbox.c | 62 +----------------------- drivers/mailbox/mailbox_internal.h | 77 ++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 61 deletions(-) create mode 100644 drivers/mailbox/mailbox_internal.h diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index ed875bb6cb51b..0888be5b664cf 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -14,67 +14,7 @@ #include #include -/* - * The length of circular buffer for queuing messages from a client. - * 'msg_count' tracks the number of buffered messages while 'msg_free' - * is the index where the next message would be buffered. - * We shouldn't need it too big because every transferr is interrupt - * triggered and if we have lots of data to transfer, the interrupt - * latencies are going to be the bottleneck, not the buffer length. - * Besides, ipc_send_message could be called from atomic context and - * the client could also queue another message from the notifier 'txcb' - * of the last transfer done. - * REVIST: If too many platforms see the "Try increasing MBOX_TX_QUEUE_LEN" - * print, it needs to be taken from config option or somesuch. - */ -#define MBOX_TX_QUEUE_LEN 20 - -#define TXDONE_BY_IRQ (1 << 0) /* controller has remote RTR irq */ -#define TXDONE_BY_POLL (1 << 1) /* controller can read status of last TX */ -#define TXDONE_BY_ACK (1 << 2) /* S/W ACK recevied by Client ticks the TX */ - -struct ipc_chan { - char name[16]; /* link_name */ - struct ipc_con *con; /* Parent Controller */ - unsigned txdone_method; - - /* Cached values from controller */ - struct ipc_link *link; - struct ipc_link_ops *link_ops; - - /* Cached values from client */ - void *cl_id; - void (*rxcb)(void *cl_id, void *mssg); - void (*txcb)(void *cl_id, void *mssg, enum xfer_result r); - bool tx_block; - unsigned long tx_tout; - struct completion tx_complete; - - void *active_req; - unsigned msg_count, msg_free; - void *msg_data[MBOX_TX_QUEUE_LEN]; - bool assigned; - /* Serialize access to the channel */ - spinlock_t lock; - /* Hook to add to the controller's list of channels */ - struct list_head node; - /* Notifier to all clients waiting on aquiring this channel */ - struct blocking_notifier_head avail; -}; - -/* Internal representation of a controller */ -struct ipc_con { - char name[16]; /* controller_name */ - struct list_head channels; - /* - * If the controller supports only TXDONE_BY_POLL, - * this timer polls all the links for txdone. - */ - struct timer_list poll; - unsigned period; - /* Hook to add to the global controller list */ - struct list_head node; -}; +#include "mailbox_internal.h" static LIST_HEAD(ipc_cons); static DEFINE_MUTEX(con_mutex); diff --git a/drivers/mailbox/mailbox_internal.h b/drivers/mailbox/mailbox_internal.h new file mode 100644 index 0000000000000..a39dcb7f4952f --- /dev/null +++ b/drivers/mailbox/mailbox_internal.h @@ -0,0 +1,77 @@ +/* + * mailbox: interprocessor communication module + * + * 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. + */ + +#ifndef MAILBOX_INTERNAL_H +#define MAILBOX_INTERNAL_H + +#include +#include + +/* + * The length of circular buffer for queuing messages from a client. + * 'msg_count' tracks the number of buffered messages while 'msg_free' + * is the index where the next message would be buffered. + * We shouldn't need it too big because every transferr is interrupt + * triggered and if we have lots of data to transfer, the interrupt + * latencies are going to be the bottleneck, not the buffer length. + * Besides, ipc_send_message could be called from atomic context and + * the client could also queue another message from the notifier 'txcb' + * of the last transfer done. + * REVIST: If too many platforms see the "Try increasing MBOX_TX_QUEUE_LEN" + * print, it needs to be taken from config option or somesuch. + */ +#define MBOX_TX_QUEUE_LEN 20 + +#define TXDONE_BY_IRQ (1 << 0) /* controller has remote RTR irq */ +#define TXDONE_BY_POLL (1 << 1) /* controller can read status of last TX */ +#define TXDONE_BY_ACK (1 << 2) /* S/W ACK recevied by Client ticks the TX */ + +struct ipc_chan { + char name[16]; /* link_name */ + struct ipc_con *con; /* Parent Controller */ + unsigned txdone_method; + + /* Cached values from controller */ + struct ipc_link *link; + struct ipc_link_ops *link_ops; + + /* Cached values from client */ + void *cl_id; + void (*rxcb)(void *cl_id, void *mssg); + void (*txcb)(void *cl_id, void *mssg, enum xfer_result r); + bool tx_block; + unsigned long tx_tout; + struct completion tx_complete; + + void *active_req; + unsigned msg_count, msg_free; + void *msg_data[MBOX_TX_QUEUE_LEN]; + bool assigned; + /* Serialize access to the channel */ + spinlock_t lock; + /* Hook to add to the controller's list of channels */ + struct list_head node; + /* Notifier to all clients waiting on aquiring this channel */ + struct blocking_notifier_head avail; +}; + +/* Internal representation of a controller */ +struct ipc_con { + char name[16]; /* controller_name */ + struct list_head channels; + /* + * If the controller supports only TXDONE_BY_POLL, + * this timer polls all the links for txdone. + */ + struct timer_list poll; + unsigned period; + /* Hook to add to the global controller list */ + struct list_head node; +}; + +#endif /* MAILBOX_INTERNAL_H */ From ee77fff1fa1a04492b08ca5d8714d3f4aabd62bd Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Tue, 12 Aug 2014 21:29:59 -0500 Subject: [PATCH 191/201] usb: dwc2: fix usb/ethernet adapter Signed-off-by: Paul Zimmerman Signed-off-by: Dinh Nguyen Conflicts: drivers/usb/dwc2/hcd.c drivers/usb/dwc2/hcd_intr.c --- drivers/usb/dwc2/hcd.c | 54 ++++++++++++++++++++++--------------- drivers/usb/dwc2/hcd.h | 2 +- drivers/usb/dwc2/hcd_ddma.c | 2 +- drivers/usb/dwc2/hcd_intr.c | 26 ++---------------- 4 files changed, 36 insertions(+), 48 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index c6778d9c60d57..9f3c131a89a80 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -404,7 +404,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, return 0; spin_lock_irqsave(&hsotg->lock, flags); - tr_type = dwc2_hcd_select_transactions(hsotg); + tr_type = dwc2_hcd_select_transactions(hsotg, mem_flags); if (tr_type != DWC2_TRANSACTION_NONE) dwc2_hcd_queue_transactions(hsotg, tr_type); spin_unlock_irqrestore(&hsotg->lock, flags); @@ -697,8 +697,12 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, } static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, - struct dwc2_host_chan *chan, void *bufptr) + struct dwc2_host_chan *chan, + struct dwc2_hcd_urb *urb, void *bufptr, + gfp_t mem_flags) { + struct usb_hcd *hcd; + struct urb *usb_urb; u32 buf_size; if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) @@ -709,17 +713,28 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, if (!qh->dw_align_buf) { qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, &qh->dw_align_buf_dma, - GFP_ATOMIC); + mem_flags); if (!qh->dw_align_buf) return -ENOMEM; } - if (!chan->ep_is_in && chan->xfer_len) { - dma_sync_single_for_cpu(hsotg->dev, chan->xfer_dma, buf_size, - DMA_TO_DEVICE); - memcpy(qh->dw_align_buf, bufptr, chan->xfer_len); - dma_sync_single_for_device(hsotg->dev, chan->xfer_dma, buf_size, - DMA_TO_DEVICE); + if (chan->xfer_len) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + usb_urb = urb->priv; + + if (usb_urb) { + if (usb_urb->transfer_flags & + (URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG | + URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) { + hcd = dwc2_hsotg_to_hcd(hsotg); + usb_hcd_unmap_urb_for_dma(hcd, usb_urb); + } + if (!chan->ep_is_in) + memcpy(qh->dw_align_buf, bufptr, + chan->xfer_len); + } else { + dev_warn(hsotg->dev, "no URB in dwc2_urb\n"); + } } chan->align_buf = qh->dw_align_buf_dma; @@ -735,7 +750,9 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, * @qh: Transactions from the first QTD for this QH are selected and assigned * to a free host channel */ -static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, + gfp_t mem_flags) { struct dwc2_host_chan *chan; struct dwc2_hcd_urb *urb; @@ -828,7 +845,8 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) /* Non DWORD-aligned buffer case */ if (bufptr) { dev_vdbg(hsotg->dev, "Non-aligned buffer\n"); - if (dwc2_hc_setup_align_buf(hsotg, qh, chan, bufptr)) { + if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr, + mem_flags)) { dev_err(hsotg->dev, "%s: Failed to allocate memory to handle non-dword aligned buffer\n", __func__); @@ -872,7 +890,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) * Return: The types of new transactions that were assigned to host channels */ enum dwc2_transaction_type dwc2_hcd_select_transactions( - struct dwc2_hsotg *hsotg) + struct dwc2_hsotg *hsotg, gfp_t mem_flags) { enum dwc2_transaction_type ret_val = DWC2_TRANSACTION_NONE; struct list_head *qh_ptr; @@ -894,8 +912,7 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( hsotg->available_host_channels--; } qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); - if (dwc2_assign_and_init_hc(hsotg, qh)) - break; + dwc2_assign_and_init_hc(hsotg, qh, mem_flags); /* * Move the QH from the periodic ready schedule to the @@ -921,14 +938,7 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( if (list_empty(&hsotg->free_hc_list)) break; qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); - if (hsotg->core_params->uframe_sched > 0) { - if (hsotg->available_host_channels < 1) - break; - hsotg->available_host_channels--; - } - - if (dwc2_assign_and_init_hc(hsotg, qh)) - break; + dwc2_assign_and_init_hc(hsotg, qh, mem_flags); /* * Move the QH from the non-periodic inactive schedule to the diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 08db5c0f69c64..2d65a10025242 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -459,7 +459,7 @@ extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); /* Transaction Execution Functions */ extern enum dwc2_transaction_type dwc2_hcd_select_transactions( - struct dwc2_hsotg *hsotg); + struct dwc2_hsotg *hsotg, gfp_t mem_flags); extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, enum dwc2_transaction_type tr_type); diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c index 3376177e4d3c0..9ead1f1d6c21c 100644 --- a/drivers/usb/dwc2/hcd_ddma.c +++ b/drivers/usb/dwc2/hcd_ddma.c @@ -1199,7 +1199,7 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg, } } - tr_type = dwc2_hcd_select_transactions(hsotg); + tr_type = dwc2_hcd_select_transactions(hsotg, GFP_ATOMIC); if (tr_type != DWC2_TRANSACTION_NONE || continue_isoc_xfer) { if (continue_isoc_xfer) { if (tr_type == DWC2_TRANSACTION_NONE) diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 47b9eb5389b46..371a2a90d41a7 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -143,7 +143,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg) list_move(&qh->qh_list_entry, &hsotg->periodic_sched_ready); } - tr_type = dwc2_hcd_select_transactions(hsotg); + tr_type = dwc2_hcd_select_transactions(hsotg, GFP_ATOMIC); if (tr_type != DWC2_TRANSACTION_NONE) dwc2_hcd_queue_transactions(hsotg, tr_type); @@ -465,12 +465,8 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, xfer_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); } dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", @@ -560,14 +556,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, - urb->length, DMA_FROM_DEVICE); memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, frame_desc->actual_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, - urb->length, - DMA_FROM_DEVICE); } break; case DWC2_HC_XFER_FRAME_OVERRUN: @@ -594,14 +585,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, - urb->length, DMA_FROM_DEVICE); memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, frame_desc->actual_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, - urb->length, - DMA_FROM_DEVICE); } /* Skip whole frame */ @@ -772,7 +758,7 @@ static void dwc2_release_channel(struct dwc2_hsotg *hsotg, writel(haintmsk, hsotg->regs + HAINTMSK); /* Try to queue more transfers now that there's a free channel */ - tr_type = dwc2_hcd_select_transactions(hsotg); + tr_type = dwc2_hcd_select_transactions(hsotg, GFP_ATOMIC); if (tr_type != DWC2_TRANSACTION_NONE) dwc2_hcd_queue_transactions(hsotg, tr_type); } @@ -937,12 +923,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, if (chan->align_buf) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, qtd->urb->dma, - qtd->urb->length, DMA_FROM_DEVICE); memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); - dma_sync_single_for_device(hsotg->dev, qtd->urb->dma, - qtd->urb->length, DMA_FROM_DEVICE); } qtd->isoc_split_offset += len; @@ -1170,12 +1152,8 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, xfer_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); } urb->actual_length += xfer_length; From 46222af7915318cb6b89a5cc081c3951eaab4aef Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 26 Aug 2014 15:37:45 -0500 Subject: [PATCH 192/201] usb: dwc2: cap the max transfer size to 65535 Signed-off-by: Paul Zimmerman Signed-off-by: Dinh Nguyen --- drivers/usb/dwc2/core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 438bfceea2957..33c1a5816fe0b 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -2748,6 +2748,14 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >> GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT; hw->max_transfer_size = (1 << (width + 11)) - 1; + + /* Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates + * coherent buffers with this size, and if it's too large we can + * exhaust the coherent DMA pool. + */ + if (hw->max_transfer_size > 65535) + hw->max_transfer_size = 65535; + width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >> GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT; hw->max_packet_count = (1 << (width + 4)) - 1; From aea46c2683da57d2654e20e683db1a4022b52071 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Wed, 27 Aug 2014 17:59:09 -0500 Subject: [PATCH 193/201] ARM: socfpga: fix l2 ECC enabling Enable the l2 ECC in socfpga_init_irq. The l2 ECC bit must be turned on before the l2 is enabled. Alos, fix L2 cache build error when debug is enabled. Signed-off-by: Thor Thayer --- arch/arm/mach-socfpga/socfpga.c | 1 + drivers/edac/altera_ecc_l2.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index b6b627a30ee78..88e1887246100 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -188,6 +188,7 @@ static void __init socfpga_init_irq(void) { irqchip_init(); socfpga_sysmgr_init(); + socfpga_init_l2_ecc(); } static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd) diff --git a/drivers/edac/altera_ecc_l2.c b/drivers/edac/altera_ecc_l2.c index 20995f4d6c02e..2020438e77161 100644 --- a/drivers/edac/altera_ecc_l2.c +++ b/drivers/edac/altera_ecc_l2.c @@ -14,7 +14,7 @@ * this program. If not, see . */ - +#include #include #include #include From 43639ae8552efd5cfe3e176c1a0a0d9ccf4bdc04 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Fri, 29 Aug 2014 16:54:22 -0500 Subject: [PATCH 194/201] add arria5 leds Signed-off-by: Alan Tull --- arch/arm/boot/dts/socfpga_arria5_socdk.dts | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/arm/boot/dts/socfpga_arria5_socdk.dts b/arch/arm/boot/dts/socfpga_arria5_socdk.dts index 186f2ea42e9a5..3e95061cd11c5 100644 --- a/arch/arm/boot/dts/socfpga_arria5_socdk.dts +++ b/arch/arm/boot/dts/socfpga_arria5_socdk.dts @@ -45,6 +45,29 @@ ethernet0 = &gmac1; }; + leds { + compatible = "gpio-leds"; + hps0 { + label = "hps_led0"; + gpios = <&gpio0 0 1>; + }; + + hps1 { + label = "hps_led1"; + gpios = <&gpio1 11 1>; + }; + + hps2 { + label = "hps_led2"; + gpios = <&gpio0 17 1>; + }; + + hps3 { + label = "hps_led3"; + gpios = <&gpio0 18 1>; + }; + }; + soc { gpio@ff708000 { status = "okay"; From 7ca635ee8d5bf7f772e40fbffe0ce1170a5ed2f6 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Thu, 4 Sep 2014 10:53:24 +0800 Subject: [PATCH 195/201] FogBugz #228949: Add CONFIG_MARVELL_PHY to socfpga_defconfig Add CONFIG_MARVELL_PHY=y in socfpga_defconfig. It requires in RMGII or SGMII example design. Signed-off-by: Ley Foon Tan --- arch/arm/configs/socfpga_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index be9ad2816779c..006b9c58a2f97 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -180,4 +180,4 @@ CONFIG_SRAM=y CONFIG_PPS=y CONFIG_NETWORK_PHY_TIMESTAMPING=y CONFIG_PTP_1588_CLOCK=y - +CONFIG_MARVELL_PHY=y From ffeb6742c38c59179387faac4b6cb6699b5f4dac Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Fri, 5 Sep 2014 13:44:38 -0500 Subject: [PATCH 196/201] FogBugz #229601: newhaven lcd: fix kbuild test robot warnings The kbuild test robot noticed some problems with the newhaven lcd driver. 1. lcd_of_match is not NULL terminated at line 606 2. Use ARRAY_SIZE instead of dividing sizeof array with sizeof an element 3. Random config test warnings: drivers/built-in.o: In function `brightness_store': >> newhaven_lcd.c:(.text+0xd2225): undefined reference to `i2c_master_send' drivers/built-in.o: In function `lcd_init': >> newhaven_lcd.c:(.init.text+0x8a9e): undefined reference to `i2c_register_driver' drivers/built-in.o: In function `lcd_exit': >> newhaven_lcd.c:(.exit.text+0x439): undefined reference to `i2c_del_driver' drivers/built-in.o: In function `lcd_remove': >> newhaven_lcd.c:(.exit.text+0x462): undefined reference to `i2c_master_send' This patch squashes the two patches they kindly sent us plus adds a Kconfig fix for the warnings in #3 above. Signed-off-by: Fengguang Wu Signed-off-by: Alan Tull --- drivers/tty/Kconfig | 1 + drivers/tty/newhaven_lcd.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 53ad9f138e974..420f8b4563d77 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -177,6 +177,7 @@ config BFIN_JTAG_COMM_CONSOLE config NEWHAVEN_LCD tristate "NEWHAVEN LCD" + depends on I2C help Add support for a TTY device on a Newhaven I2C LCD device. diff --git a/drivers/tty/newhaven_lcd.c b/drivers/tty/newhaven_lcd.c index bc62546b49615..56d1c2d5e2bbe 100644 --- a/drivers/tty/newhaven_lcd.c +++ b/drivers/tty/newhaven_lcd.c @@ -142,7 +142,7 @@ static int lcd_load_custom_fonts(struct lcd *lcd_data) u8 buf[LCD_BYTES_PER_FONT_CMD]; int count, i; - for (i = 0; i < sizeof(custom_fonts) / sizeof(struct custom_font) ; i++) { + for (i = 0; i < ARRAY_SIZE(custom_fonts); i++) { buf[0] = LCD_COMMAND; buf[1] = LCD_CUSTOM_CHAR; buf[2] = custom_fonts[i].mapping; @@ -604,6 +604,7 @@ static int __exit lcd_remove(struct i2c_client *client) static const struct of_device_id lcd_of_match[] = { { .compatible = "newhaven,nhd-0216k3z-nsw-bbw", }, + {}, }; static const struct i2c_device_id lcd_id[] = { From 60604ec06fdb4b40d1e9ed49c880c28f215d43b3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 31 Aug 2014 12:47:06 +0800 Subject: [PATCH 197/201] spi: dw: Don't use devm_kzalloc in master->setup callback device_add() expects that any memory allocated via devm_* API is only done in the device's probe function. Fix below boot warning: WARNING: CPU: 1 PID: 1 at drivers/base/dd.c:286 driver_probe_device+0x2b4/0x2f4() Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 3.16.0-10474-g835c90b-dirty #160 [] (unwind_backtrace) from [] (show_stack+0x20/0x24) [] (show_stack) from [] (dump_stack+0x7c/0x98) [] (dump_stack) from [] (warn_slowpath_common+0x78/0x9c) [] (warn_slowpath_common) from [] (warn_slowpath_null+0x2c/0x34) [] (warn_slowpath_null) from [] (driver_probe_device+0x2b4/0x2f4) [] (driver_probe_device) from [] (__device_attach+0x50/0x54) [] (__device_attach) from [] (bus_for_each_drv+0x54/0x9c) [] (bus_for_each_drv) from [] (device_attach+0x84/0x90) [] (device_attach) from [] (bus_probe_device+0x94/0xb8) [] (bus_probe_device) from [] (device_add+0x434/0x4fc) [] (device_add) from [] (spi_add_device+0x98/0x164) [] (spi_add_device) from [] (spi_register_master+0x598/0x768) [] (spi_register_master) from [] (devm_spi_register_master+0x40/0x80) [] (devm_spi_register_master) from [] (dw_spi_add_host+0x1a8/0x258) [] (dw_spi_add_host) from [] (dw_spi_mmio_probe+0x1d4/0x294) [] (dw_spi_mmio_probe) from [] (platform_drv_probe+0x3c/0x6c) [] (platform_drv_probe) from [] (driver_probe_device+0xec/0x2f4) [] (driver_probe_device) from [] (__driver_attach+0x9c/0xa0) [] (__driver_attach) from [] (bus_for_each_dev+0x64/0x98) [] (bus_for_each_dev) from [] (driver_attach+0x2c/0x30) [] (driver_attach) from [] (bus_add_driver+0xdc/0x1f4) [] (bus_add_driver) from [] (driver_register+0x88/0x104) [] (driver_register) from [] (__platform_driver_register+0x58/0x6c) [] (__platform_driver_register) from [] (dw_spi_mmio_driver_init+0x18/0x20) [] (dw_spi_mmio_driver_init) from [] (do_one_initcall+0x90/0x1d4) [] (do_one_initcall) from [] (kernel_init_freeable+0x178/0x248) [] (kernel_init_freeable) from [] (kernel_init+0x18/0xfc) [] (kernel_init) from [] (ret_from_fork+0x14/0x20) Reported-by: Thor Thayer Signed-off-by: Axel Lin Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/spi/spi-dw.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 46c6a6a3e9bea..f56531a411509 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -594,8 +594,7 @@ static int dw_spi_setup(struct spi_device *spi) /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (!chip) { - chip = devm_kzalloc(&spi->dev, sizeof(struct chip_data), - GFP_KERNEL); + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); if (!chip) return -ENOMEM; spi_set_ctldata(spi, chip); @@ -653,6 +652,14 @@ static int dw_spi_setup(struct spi_device *spi) return 0; } +static void dw_spi_cleanup(struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata(spi); + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + /* Restart the controller, disable all interrupts, clean rx fifo */ static void spi_hw_init(struct dw_spi *dws) { @@ -708,6 +715,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->bus_num = dws->bus_num; master->num_chipselect = dws->num_cs; master->setup = dw_spi_setup; + master->cleanup = dw_spi_cleanup; master->transfer_one_message = dw_spi_transfer_one_message; master->max_speed_hz = dws->max_freq; From 73b8057eedd623269600b233f82d4378631dba8c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 27 Aug 2014 19:26:08 +0300 Subject: [PATCH 198/201] spi: dw: fix kernel crash due to NULL pointer dereference The obvious fix after the commit d9c73bb8a3a5 "spi: dw: add support for gpio controlled chip select". This patch fixes the issue by using locally defined temporary variable. Fixes: d9c73bb8a3a5 (spi: dw: add support for gpio controlled chip select) Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown Cc: --- drivers/spi/spi-dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index f56531a411509..e017c443e9fb2 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -309,7 +309,7 @@ static void giveback(struct dw_spi *dws) transfer_list); if (!last_transfer->cs_change) - spi_chip_sel(dws, dws->cur_msg->spi, 0); + spi_chip_sel(dws, msg->spi, 0); spi_finalize_current_message(dws->master); } From d2fe5b8f01d9b9eed798ffbcd6dd5f50e97e4d01 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Thu, 9 Oct 2014 17:54:51 -0500 Subject: [PATCH 199/201] FogBugz #236669: Add a Kconfig for ILC driver Without a Kconfig option, there's really no way to build the driver. Signed-off-by: Dinh Nguyen --- drivers/misc/Kconfig | 6 ++++++ drivers/misc/Makefile | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2477a74e7fce9..20363457eb413 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -527,6 +527,12 @@ config ALTERA_SYSID help This enables Altera System ID soft core driver. +config ALTERA_ILC + tristate "Altera Interrupt Latency Counter driver" + help + This enables the Interrupt Latency Counter driver for the Altera + SOCFPGA platform. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 444580cf8d978..f3fbc6f7f4962 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -50,6 +50,7 @@ obj-y += carma/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) += altera-stapl/ obj-$(CONFIG_ALTERA_HWMUTEX) += altera_hwmutex.o +obj-$(CONFIG_ALTERA_ILC) += altera_ilc.o obj-$(CONFIG_ALTERA_SYSID) += altera_sysid.o obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ From 14cef3b4c21ed3bd2445f1d592b0e54a3af4b3e8 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 1 Oct 2014 05:44:48 -0500 Subject: [PATCH 200/201] FogBugz #237341: fix fetching cpu1start_addr for SMP When CPU1 is brought out of reset, it's MMU is not turned on yet, so it will only be able to use physical addresses. For systems that have their MMU page configured for 0xC0000000, 0x80000000, or 0x40000000 "BIC 0x40000000" will work just fine, as it was just converting the virtual address of &cpu1start_addr into a physical address, ie. 0xC0000000 became 0x80000000. So for systems where the SDRAM controller was able to do a wrap-around access, this was working fine, as it was just dropping the MSB, but for systems where out of bounds memory access is not allowed, this would not allow CPU1 to correctly fetch &cpu1start_addr. This patch fixes the secondary_trampoline code to correctly fetch the physical address of cpu1start_addr directly. The patch will grab the correct PAGE_OFFSET and convert &cpu1start_addr to the physical address correctly. Signed-off-by: Dinh Nguyen --- arch/arm/mach-socfpga/headsmp.S | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-socfpga/headsmp.S b/arch/arm/mach-socfpga/headsmp.S index 95c115d8b5eee..42b77e3a10ebb 100644 --- a/arch/arm/mach-socfpga/headsmp.S +++ b/arch/arm/mach-socfpga/headsmp.S @@ -9,21 +9,21 @@ */ #include #include +#include .arch armv7-a ENTRY(secondary_trampoline) - movw r2, #:lower16:cpu1start_addr - movt r2, #:upper16:cpu1start_addr - - /* The socfpga VT cannot handle a 0xC0000000 page offset when loading - the cpu1start_addr, we bit clear it. Tested on HW and VT. */ - bic r2, r2, #0x40000000 - - ldr r0, [r2] - ldr r1, [r0] - bx r1 + adr r0, 1f + ldmia r0, {r1, r2} + sub r2, r2, #PAGE_OFFSET + ldr r3, [r2] + ldr r4, [r3] + bx r4 + .align +1: .long . + .long cpu1start_addr ENTRY(secondary_trampoline_end) ENTRY(socfpga_secondary_startup) From 9e70f262ad67bfe59979da9db32c6ffaba4f52c7 Mon Sep 17 00:00:00 2001 From: Graham Moore Date: Tue, 21 Oct 2014 15:00:22 -0500 Subject: [PATCH 201/201] FogBugz #240948: Cadence QSPI needs to have spi-nor framework enabled. Add config setting for qspi under 3.16 kernel, this is required by the new spi-nor framework. Signed-off-by: Graham Moore --- arch/arm/configs/socfpga_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig index 006b9c58a2f97..a55bb539efadc 100644 --- a/arch/arm/configs/socfpga_defconfig +++ b/arch/arm/configs/socfpga_defconfig @@ -61,6 +61,7 @@ CONFIG_MTD_NAND=y CONFIG_MTD_NAND_DENALI=y CONFIG_MTD_NAND_DENALI_DT=y CONFIG_MTD_NAND_IDS=y +CONFIG_MTD_SPI_NOR=y CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=2