From d0e05bb3d611c7c3d37cadd8c1016ee2e22beefa Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 18 May 2012 10:20:33 +0900 Subject: sh: mach-se: Migrate 7724SE off of deprecated dynamic IRQ API. The generic hardirq layer provides all of the routines that we need these days, so we don't require any of the dynamic IRQ API wrapping, and can call in to irq_alloc_descs() directly. Signed-off-by: Paul Mundt --- arch/sh/boards/mach-se/7724/irq.c | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/arch/sh/boards/mach-se/7724/irq.c b/arch/sh/boards/mach-se/7724/irq.c index c6342ce7768d..5d1d3ec9a6cd 100644 --- a/arch/sh/boards/mach-se/7724/irq.c +++ b/arch/sh/boards/mach-se/7724/irq.c @@ -17,8 +17,10 @@ #include #include #include -#include -#include +#include +#include +#include +#include #include struct fpga_irq { @@ -111,7 +113,7 @@ static void se7724_irq_demux(unsigned int irq, struct irq_desc *desc) */ void __init init_se7724_IRQ(void) { - int i, nid = cpu_to_node(boot_cpu_data); + int irq_base, i; __raw_writew(0xffff, IRQ0_MR); /* mask all */ __raw_writew(0xffff, IRQ1_MR); /* mask all */ @@ -121,28 +123,16 @@ void __init init_se7724_IRQ(void) __raw_writew(0x0000, IRQ2_SR); /* clear irq */ __raw_writew(0x002a, IRQ_MODE); /* set irq type */ - for (i = 0; i < SE7724_FPGA_IRQ_NR; i++) { - int irq, wanted; - - wanted = SE7724_FPGA_IRQ_BASE + i; - - irq = create_irq_nr(wanted, nid); - if (unlikely(irq == 0)) { - pr_err("%s: failed hooking irq %d for FPGA\n", - __func__, wanted); - return; - } - - if (unlikely(irq != wanted)) { - pr_err("%s: got irq %d but wanted %d, bailing.\n", - __func__, irq, wanted); - destroy_irq(irq); - return; - } + irq_base = irq_alloc_descs(SE7724_FPGA_IRQ_BASE, SE7724_FPGA_IRQ_BASE, + SE7724_FPGA_IRQ_NR, numa_node_id()); + if (IS_ERR_VALUE(irq_base)) { + pr_err("%s: failed hooking irqs for FPGA\n", __func__); + return; + } - irq_set_chip_and_handler_name(irq, &se7724_irq_chip, + for (i = 0; i < SE7724_FPGA_IRQ_NR; i++) + irq_set_chip_and_handler_name(irq_base + i, &se7724_irq_chip, handle_level_irq, "level"); - } irq_set_chained_handler(IRQ0_IRQ, se7724_irq_demux); irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); -- cgit v1.2.3-59-g8ed1b From 051f923d922d105f4d32e64cba1ed6f5a749d530 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 18 May 2012 23:20:09 +0900 Subject: sh: hd64461: Migrate off of deprecated dynamic IRQ API. Switches from create_irq_nr() to irq_alloc_descs(). Signed-off-by: Paul Mundt --- arch/sh/cchips/hd6446x/hd64461.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/arch/sh/cchips/hd6446x/hd64461.c b/arch/sh/cchips/hd6446x/hd64461.c index eb4ea4d44d59..e9735616bdc8 100644 --- a/arch/sh/cchips/hd6446x/hd64461.c +++ b/arch/sh/cchips/hd6446x/hd64461.c @@ -73,10 +73,7 @@ static void hd64461_irq_demux(unsigned int irq, struct irq_desc *desc) int __init setup_hd64461(void) { - int i, nid = cpu_to_node(boot_cpu_data); - - if (!MACH_HD64461) - return 0; + int irq_base, i; printk(KERN_INFO "HD64461 configured at 0x%x on irq %d(mapped into %d to %d)\n", @@ -89,28 +86,16 @@ int __init setup_hd64461(void) #endif __raw_writew(0xffff, HD64461_NIMR); - /* IRQ 80 -> 95 belongs to HD64461 */ - for (i = HD64461_IRQBASE; i < HD64461_IRQBASE + 16; i++) { - unsigned int irq; - - irq = create_irq_nr(i, nid); - if (unlikely(irq == 0)) { - pr_err("%s: failed hooking irq %d for HD64461\n", - __func__, i); - return -EBUSY; - } - - if (unlikely(irq != i)) { - pr_err("%s: got irq %d but wanted %d, bailing.\n", - __func__, irq, i); - destroy_irq(irq); - return -EINVAL; - } - - irq_set_chip_and_handler(i, &hd64461_irq_chip, - handle_level_irq); + irq_base = irq_alloc_descs(HD64461_IRQBASE, HD64461_IRQBASE, 16, -1); + if (IS_ERR_VALUE(irq_base)) { + pr_err("%s: failed hooking irqs for HD64461\n", __func__); + return irq_base; } + for (i = 0; i < 16; i++) + irq_set_chip_and_handler(irq_base + i, &hd64461_irq_chip, + handle_level_irq); + irq_set_chained_handler(CONFIG_HD64461_IRQ, hd64461_irq_demux); irq_set_irq_type(CONFIG_HD64461_IRQ, IRQ_TYPE_LEVEL_LOW); -- cgit v1.2.3-59-g8ed1b From 3b1267b90f6b7c080024101696c0454f455761f4 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 18 May 2012 23:36:44 +0900 Subject: sh: dreamcast: Migrate off of deprecated dynamic IRQ API. Switches from create_irq_nr() to irq_alloc_descs(). Signed-off-by: Paul Mundt --- arch/sh/boards/mach-dreamcast/irq.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/arch/sh/boards/mach-dreamcast/irq.c b/arch/sh/boards/mach-dreamcast/irq.c index f63d323f411f..2789647abebe 100644 --- a/arch/sh/boards/mach-dreamcast/irq.c +++ b/arch/sh/boards/mach-dreamcast/irq.c @@ -8,10 +8,11 @@ * This file is part of the LinuxDC project (www.linuxdc.org) * Released under the terms of the GNU GPL v2.0 */ - #include #include -#include +#include +#include +#include #include /* @@ -141,26 +142,15 @@ int systemasic_irq_demux(int irq) void systemasic_irq_init(void) { - int i, nid = cpu_to_node(boot_cpu_data); - - /* Assign all virtual IRQs to the System ASIC int. handler */ - for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++) { - unsigned int irq; - - irq = create_irq_nr(i, nid); - if (unlikely(irq == 0)) { - pr_err("%s: failed hooking irq %d for systemasic\n", - __func__, i); - return; - } + int irq_base, i; - if (unlikely(irq != i)) { - pr_err("%s: got irq %d but wanted %d, bailing.\n", - __func__, irq, i); - destroy_irq(irq); - return; - } + irq_base = irq_alloc_descs(HW_EVENT_IRQ_BASE, HW_EVENT_IRQ_BASE, + HW_EVENT_IRQ_MAX - HW_EVENT_IRQ_BASE, -1); + if (IS_ERR_VALUE(irq_base)) { + pr_err("%s: failed hooking irqs\n", __func__); + return; + } + for (i = HW_EVENT_IRQ_BASE; i < HW_EVENT_IRQ_MAX; i++) irq_set_chip_and_handler(i, &systemasic_int, handle_level_irq); - } } -- cgit v1.2.3-59-g8ed1b From 76b386624576eb00a7c7cad0e713952121708598 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 13:59:35 +0900 Subject: sh: Kill off now unused arch_probe_nr_irqs(). Now that legacy pre-allocation is done away with, we can just use the generic stub in kernel/softirq.c. Signed-off-by: Paul Mundt --- arch/sh/kernel/irq.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index dadce735f746..063af10ff3c1 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -231,16 +231,6 @@ void __init init_IRQ(void) irq_ctx_init(smp_processor_id()); } -#ifdef CONFIG_SPARSE_IRQ -int __init arch_probe_nr_irqs(void) -{ - /* - * No pre-allocated IRQs. - */ - return 0; -} -#endif - #ifdef CONFIG_HOTPLUG_CPU static void route_irq(struct irq_data *data, unsigned int irq, unsigned int cpu) { -- cgit v1.2.3-59-g8ed1b From 49453264997f232008efae457553d82381f9614f Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 15:03:46 +0900 Subject: sh64: Convert to unwinder API. This switches over to use the sh unwinder API which brings it all in line with the general sh routines (which we shuffle around at the same time), and lets us kill off more sh64-specific cruft. Signed-off-by: Paul Mundt --- arch/sh/include/asm/kdebug.h | 2 ++ arch/sh/kernel/cpu/sh5/unwind.c | 63 +++++++++++++++++++++++++++-------------- arch/sh/kernel/dumpstack.c | 58 +++++++++++++++++++++++++++++++++++++ arch/sh/kernel/traps_32.c | 50 -------------------------------- arch/sh/kernel/traps_64.c | 26 ----------------- 5 files changed, 101 insertions(+), 98 deletions(-) diff --git a/arch/sh/include/asm/kdebug.h b/arch/sh/include/asm/kdebug.h index a6201f10c273..8d6a831e7ba1 100644 --- a/arch/sh/include/asm/kdebug.h +++ b/arch/sh/include/asm/kdebug.h @@ -10,6 +10,8 @@ enum die_val { DIE_SSTEP, }; +/* arch/sh/kernel/dumpstack.c */ extern void printk_address(unsigned long address, int reliable); +extern void dump_mem(const char *str, unsigned long bottom, unsigned long top); #endif /* __ASM_SH_KDEBUG_H */ diff --git a/arch/sh/kernel/cpu/sh5/unwind.c b/arch/sh/kernel/cpu/sh5/unwind.c index b205b25eaf45..10aed41757fc 100644 --- a/arch/sh/kernel/cpu/sh5/unwind.c +++ b/arch/sh/kernel/cpu/sh5/unwind.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include static u8 regcache[63]; @@ -199,8 +201,11 @@ static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc, return 0; } -/* Don't put this on the stack since we'll want to call sh64_unwind - * when we're close to underflowing the stack anyway. */ +/* + * Don't put this on the stack since we'll want to call in to + * sh64_unwinder_dump() when we're close to underflowing the stack + * anyway. + */ static struct pt_regs here_regs; extern const char syscall_ret; @@ -208,17 +213,19 @@ extern const char ret_from_syscall; extern const char ret_from_exception; extern const char ret_from_irq; -static void sh64_unwind_inner(struct pt_regs *regs); +static void sh64_unwind_inner(const struct stacktrace_ops *ops, + void *data, struct pt_regs *regs); -static void unwind_nested (unsigned long pc, unsigned long fp) +static inline void unwind_nested(const struct stacktrace_ops *ops, void *data, + unsigned long pc, unsigned long fp) { if ((fp >= __MEMORY_START) && - ((fp & 7) == 0)) { - sh64_unwind_inner((struct pt_regs *) fp); - } + ((fp & 7) == 0)) + sh64_unwind_inner(ops, data, (struct pt_regs *)fp); } -static void sh64_unwind_inner(struct pt_regs *regs) +static void sh64_unwind_inner(const struct stacktrace_ops *ops, + void *data, struct pt_regs *regs) { unsigned long pc, fp; int ofs = 0; @@ -232,29 +239,29 @@ static void sh64_unwind_inner(struct pt_regs *regs) int cond; unsigned long next_fp, next_pc; - if (pc == ((unsigned long) &syscall_ret & ~1)) { + if (pc == ((unsigned long)&syscall_ret & ~1)) { printk("SYSCALL\n"); - unwind_nested(pc,fp); + unwind_nested(ops, data, pc, fp); return; } - if (pc == ((unsigned long) &ret_from_syscall & ~1)) { + if (pc == ((unsigned long)&ret_from_syscall & ~1)) { printk("SYSCALL (PREEMPTED)\n"); - unwind_nested(pc,fp); + unwind_nested(ops, data, pc, fp); return; } /* In this case, the PC is discovered by lookup_prev_stack_frame but it has 4 taken off it to look like the 'caller' */ - if (pc == ((unsigned long) &ret_from_exception & ~1)) { + if (pc == ((unsigned long)&ret_from_exception & ~1)) { printk("EXCEPTION\n"); - unwind_nested(pc,fp); + unwind_nested(ops, data, pc, fp); return; } - if (pc == ((unsigned long) &ret_from_irq & ~1)) { + if (pc == ((unsigned long)&ret_from_irq & ~1)) { printk("IRQ\n"); - unwind_nested(pc,fp); + unwind_nested(ops, data, pc, fp); return; } @@ -263,8 +270,7 @@ static void sh64_unwind_inner(struct pt_regs *regs) pc -= ofs; - printk("[<%08lx>] ", pc); - print_symbol("%s\n", pc); + ops->address(data, pc, 1); if (first_pass) { /* If the innermost frame is a leaf function, it's @@ -287,10 +293,13 @@ static void sh64_unwind_inner(struct pt_regs *regs) } printk("\n"); - } -void sh64_unwind(struct pt_regs *regs) +static void sh64_unwinder_dump(struct task_struct *task, + struct pt_regs *regs, + unsigned long *sp, + const struct stacktrace_ops *ops, + void *data) { if (!regs) { /* @@ -320,7 +329,17 @@ void sh64_unwind(struct pt_regs *regs) ); } - printk("\nCall Trace:\n"); - sh64_unwind_inner(regs); + sh64_unwind_inner(ops, data, regs); } +static struct unwinder sh64_unwinder = { + .name = "sh64-unwinder", + .dump = sh64_unwinder_dump, + .rating = 150, +}; + +static int __init sh64_unwinder_init(void) +{ + return unwinder_register(&sh64_unwinder); +} +early_initcall(sh64_unwinder_init); diff --git a/arch/sh/kernel/dumpstack.c b/arch/sh/kernel/dumpstack.c index 694158b9a50f..7617dc4129ac 100644 --- a/arch/sh/kernel/dumpstack.c +++ b/arch/sh/kernel/dumpstack.c @@ -2,13 +2,48 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs * Copyright (C) 2009 Matt Fleming + * Copyright (C) 2002 - 2012 Paul Mundt + * + * 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 +void dump_mem(const char *str, unsigned long bottom, unsigned long top) +{ + unsigned long p; + int i; + + printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); + + for (p = bottom & ~31; p < top; ) { + printk("%04lx: ", p & 0xffff); + + for (i = 0; i < 8; i++, p += 4) { + unsigned int val; + + if (p < bottom || p >= top) + printk(" "); + else { + if (__get_user(val, (unsigned int __user *)p)) { + printk("\n"); + return; + } + printk("%08x ", val); + } + } + printk("\n"); + } +} + void printk_address(unsigned long address, int reliable) { printk(" [<%p>] %s%pS\n", (void *) address, @@ -106,3 +141,26 @@ void show_trace(struct task_struct *tsk, unsigned long *sp, debug_show_held_locks(tsk); } + +void show_stack(struct task_struct *tsk, unsigned long *sp) +{ + unsigned long stack; + + if (!tsk) + tsk = current; + if (tsk == current) + sp = (unsigned long *)current_stack_pointer; + else + sp = (unsigned long *)tsk->thread.sp; + + stack = (unsigned long)sp; + dump_mem("Stack: ", stack, THREAD_SIZE + + (unsigned long)task_stack_page(tsk)); + show_trace(tsk, sp, NULL); +} + +void dump_stack(void) +{ + show_stack(NULL, NULL); +} +EXPORT_SYMBOL(dump_stack); diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index a37175deb73f..b8f5a51841ec 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -48,33 +48,6 @@ #define TRAP_ILLEGAL_SLOT_INST 13 #endif -static void dump_mem(const char *str, unsigned long bottom, unsigned long top) -{ - unsigned long p; - int i; - - printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); - - for (p = bottom & ~31; p < top; ) { - printk("%04lx: ", p & 0xffff); - - for (i = 0; i < 8; i++, p += 4) { - unsigned int val; - - if (p < bottom || p >= top) - printk(" "); - else { - if (__get_user(val, (unsigned int __user *)p)) { - printk("\n"); - return; - } - printk("%08x ", val); - } - } - printk("\n"); - } -} - static DEFINE_SPINLOCK(die_lock); void die(const char * str, struct pt_regs * regs, long err) @@ -900,26 +873,3 @@ void __init trap_init(void) set_exception_table_vec(TRAP_UBC, breakpoint_trap_handler); #endif } - -void show_stack(struct task_struct *tsk, unsigned long *sp) -{ - unsigned long stack; - - if (!tsk) - tsk = current; - if (tsk == current) - sp = (unsigned long *)current_stack_pointer; - else - sp = (unsigned long *)tsk->thread.sp; - - stack = (unsigned long)sp; - dump_mem("Stack: ", stack, THREAD_SIZE + - (unsigned long)task_stack_page(tsk)); - show_trace(tsk, sp, NULL); -} - -void dump_stack(void) -{ - show_stack(NULL, NULL); -} -EXPORT_SYMBOL(dump_stack); diff --git a/arch/sh/kernel/traps_64.c b/arch/sh/kernel/traps_64.c index 8dae93ed8aff..ba95d63e623d 100644 --- a/arch/sh/kernel/traps_64.c +++ b/arch/sh/kernel/traps_64.c @@ -253,32 +253,6 @@ int do_unknown_trapa(unsigned long scId, struct pt_regs *regs) return -ENOSYS; } -void show_stack(struct task_struct *tsk, unsigned long *sp) -{ -#ifdef CONFIG_KALLSYMS - extern void sh64_unwind(struct pt_regs *regs); - struct pt_regs *regs; - - regs = tsk ? tsk->thread.kregs : NULL; - - sh64_unwind(regs); -#else - printk(KERN_ERR "Can't backtrace on sh64 without CONFIG_KALLSYMS\n"); -#endif -} - -void show_task(unsigned long *sp) -{ - show_stack(NULL, sp); -} - -void dump_stack(void) -{ - show_task(NULL); -} -/* Needed by any user of WARN_ON in view of the defn in include/asm-sh/bug.h */ -EXPORT_SYMBOL(dump_stack); - static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk) { -- cgit v1.2.3-59-g8ed1b From 3a898c0f36f9fe312cb6e98865a6833110e67cb2 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 15:05:29 +0900 Subject: sh64: Ensure KALLSYMS is enabled for unwinder use. Since sh64 depends on kallsyms unconditionally for its stack unwinding to be of any use, make sure it's selected. In practice we don't have any case where it's disabled anyways, so moving to this to a select is fine. Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 3e723aaa5e18..931310b49bce 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -55,6 +55,7 @@ config SUPERH32 config SUPERH64 def_bool ARCH = "sh64" + select KALLSYMS config ARCH_DEFCONFIG string -- cgit v1.2.3-59-g8ed1b From b98b35815f40f01337e25e3f0d10d57b7cec5126 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 15:24:39 +0900 Subject: sh: mach-x3proto: Migrate to linear irq domain. In the interest of getting off of the create_irq() API we can get all of the functionality we're interested in through a linear IRQ domain. Fairly straightforward conversion utilizing a single linear domain. Signed-off-by: Paul Mundt --- arch/sh/boards/Kconfig | 1 + arch/sh/boards/mach-x3proto/gpio.c | 57 +++++++++++++++++++++----------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig index d893411022d5..f2024a91319f 100644 --- a/arch/sh/boards/Kconfig +++ b/arch/sh/boards/Kconfig @@ -292,6 +292,7 @@ config SH_X3PROTO bool "SH-X3 Prototype board" depends on CPU_SUBTYPE_SHX3 select NO_IOPORT if !PCI + select IRQ_DOMAIN config SH_MAGIC_PANEL_R2 bool "Magic Panel R2" diff --git a/arch/sh/boards/mach-x3proto/gpio.c b/arch/sh/boards/mach-x3proto/gpio.c index f33b2b57019c..3ea65e9b56e8 100644 --- a/arch/sh/boards/mach-x3proto/gpio.c +++ b/arch/sh/boards/mach-x3proto/gpio.c @@ -3,7 +3,7 @@ * * Renesas SH-X3 Prototype Baseboard GPIO Support. * - * Copyright (C) 2010 Paul Mundt + * Copyright (C) 2010 - 2012 Paul Mundt * * 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 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,7 @@ #define KEYDETR 0xb81c0004 static DEFINE_SPINLOCK(x3proto_gpio_lock); -static unsigned int x3proto_gpio_irq_map[NR_BASEBOARD_GPIOS] = { 0, }; +static struct irq_domain *x3proto_irq_domain; static int x3proto_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { @@ -49,7 +50,14 @@ static int x3proto_gpio_get(struct gpio_chip *chip, unsigned gpio) static int x3proto_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) { - return x3proto_gpio_irq_map[gpio]; + int virq; + + if (gpio < chip->ngpio) + virq = irq_create_mapping(x3proto_irq_domain, gpio); + else + virq = -ENXIO; + + return virq; } static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) @@ -62,9 +70,8 @@ static void x3proto_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) chip->irq_mask_ack(data); mask = __raw_readw(KEYDETR); - for_each_set_bit(pin, &mask, NR_BASEBOARD_GPIOS) - generic_handle_irq(x3proto_gpio_to_irq(NULL, pin)); + generic_handle_irq(irq_linear_revmap(x3proto_irq_domain, pin)); chip->irq_unmask(data); } @@ -78,10 +85,23 @@ struct gpio_chip x3proto_gpio_chip = { .ngpio = NR_BASEBOARD_GPIOS, }; +static int x3proto_gpio_irq_map(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler_name(virq, &dummy_irq_chip, handle_simple_irq, + "gpio"); + + return 0; +} + +static struct irq_domain_ops x3proto_gpio_irq_ops = { + .map = x3proto_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + int __init x3proto_gpio_setup(void) { - int ilsel; - int ret, i; + int ilsel, ret; ilsel = ilsel_enable(ILSEL_KEY); if (unlikely(ilsel < 0)) @@ -91,21 +111,10 @@ int __init x3proto_gpio_setup(void) if (unlikely(ret)) goto err_gpio; - for (i = 0; i < NR_BASEBOARD_GPIOS; i++) { - unsigned long flags; - int irq = create_irq(); - - if (unlikely(irq < 0)) { - ret = -EINVAL; - goto err_irq; - } - - spin_lock_irqsave(&x3proto_gpio_lock, flags); - x3proto_gpio_irq_map[i] = irq; - irq_set_chip_and_handler_name(irq, &dummy_irq_chip, - handle_simple_irq, "gpio"); - spin_unlock_irqrestore(&x3proto_gpio_lock, flags); - } + x3proto_irq_domain = irq_domain_add_linear(NULL, NR_BASEBOARD_GPIOS, + &x3proto_gpio_irq_ops, NULL); + if (unlikely(!x3proto_irq_domain)) + goto err_irq; pr_info("registering '%s' support, handling GPIOs %u -> %u, " "bound to IRQ %u\n", @@ -119,10 +128,6 @@ int __init x3proto_gpio_setup(void) return 0; err_irq: - for (; i >= 0; --i) - if (x3proto_gpio_irq_map[i]) - destroy_irq(x3proto_gpio_irq_map[i]); - ret = gpiochip_remove(&x3proto_gpio_chip); if (unlikely(ret)) pr_err("Failed deregistering GPIO\n"); -- cgit v1.2.3-59-g8ed1b From 5df38b9b7676e4e46c5c13e75f023ffb82542980 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 18:24:32 +0900 Subject: sh: se722: Move FPGA IRQs to irqdomain and generic irq chip. This implements a total rewrite of the rather buggy SE7722 FPGA IRQ code, utilizing a linear irq domain as well as the generic irq chip type. While the interaction between the two APIs is a bit clunky (ie, revmap lookup for gc irq_base), they work well enough together that it's easy enough to work with going forward. While we're at it, deal with irq_mask_ack/unmask of the chained IRQ in the demux handler to prevent smc91x screaming about spurious interrupts. There's also some more improvement that can be made to the irqdomain code to create backing irqdescs for the entire linear range in one bang instead of iterating over the number of hwirqs and doing it irq-at-a-time. This is easily dealt with at a later point, though. Signed-off-by: Paul Mundt --- arch/sh/boards/Kconfig | 2 + arch/sh/boards/mach-se/7722/irq.c | 131 ++++++++++++++++++++++------------ arch/sh/boards/mach-se/7722/setup.c | 6 +- arch/sh/include/mach-se/mach/se7722.h | 10 +-- 4 files changed, 94 insertions(+), 55 deletions(-) diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig index f2024a91319f..525b9e32cd14 100644 --- a/arch/sh/boards/Kconfig +++ b/arch/sh/boards/Kconfig @@ -44,6 +44,8 @@ config SH_7721_SOLUTION_ENGINE config SH_7722_SOLUTION_ENGINE bool "SolutionEngine7722" select SOLUTION_ENGINE + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN depends on CPU_SUBTYPE_SH7722 help Select 7722 SolutionEngine if configuring for a Hitachi SH772 diff --git a/arch/sh/boards/mach-se/7722/irq.c b/arch/sh/boards/mach-se/7722/irq.c index aac92f21ebd2..f5e2af1bf040 100644 --- a/arch/sh/boards/mach-se/7722/irq.c +++ b/arch/sh/boards/mach-se/7722/irq.c @@ -1,79 +1,96 @@ /* - * linux/arch/sh/boards/se/7722/irq.c + * Hitachi UL SolutionEngine 7722 FPGA IRQ Support. * * Copyright (C) 2007 Nobuhiro Iwamatsu - * - * Hitachi UL SolutionEngine 7722 Support. + * Copyright (C) 2012 Paul Mundt * * 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. */ +#define DRV_NAME "SE7722-FPGA" +#define pr_fmt(fmt) DRV_NAME ": " fmt + +#define irq_reg_readl ioread16 +#define irq_reg_writel iowrite16 + #include #include #include -#include -#include +#include +#include +#include +#include #include -unsigned int se7722_fpga_irq[SE7722_FPGA_IRQ_NR] = { 0, }; +#define IRQ01_BASE_ADDR 0x11800000 +#define IRQ01_MODE_REG 0 +#define IRQ01_STS_REG 4 +#define IRQ01_MASK_REG 8 -static void disable_se7722_irq(struct irq_data *data) -{ - unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); - __raw_writew(__raw_readw(IRQ01_MASK) | 1 << bit, IRQ01_MASK); -} +static void __iomem *se7722_irq_regs; +struct irq_domain *se7722_irq_domain; -static void enable_se7722_irq(struct irq_data *data) +static void se7722_irq_demux(unsigned int irq, struct irq_desc *desc) { - unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); - __raw_writew(__raw_readw(IRQ01_MASK) & ~(1 << bit), IRQ01_MASK); -} + struct irq_data *data = irq_get_irq_data(irq); + struct irq_chip *chip = irq_data_get_irq_chip(data); + unsigned long mask; + int bit; -static struct irq_chip se7722_irq_chip __read_mostly = { - .name = "SE7722-FPGA", - .irq_mask = disable_se7722_irq, - .irq_unmask = enable_se7722_irq, -}; + chip->irq_mask_ack(data); -static void se7722_irq_demux(unsigned int irq, struct irq_desc *desc) + mask = ioread16(se7722_irq_regs + IRQ01_STS_REG); + + for_each_set_bit(bit, &mask, SE7722_FPGA_IRQ_NR) + generic_handle_irq(irq_linear_revmap(se7722_irq_domain, bit)); + + chip->irq_unmask(data); +} + +static void __init se7722_domain_init(void) { - unsigned short intv = __raw_readw(IRQ01_STS); - unsigned int ext_irq = 0; + int i; - intv &= (1 << SE7722_FPGA_IRQ_NR) - 1; + se7722_irq_domain = irq_domain_add_linear(NULL, SE7722_FPGA_IRQ_NR, + &irq_domain_simple_ops, NULL); + if (unlikely(!se7722_irq_domain)) { + printk("Failed to get IRQ domain\n"); + return; + } - for (; intv; intv >>= 1, ext_irq++) { - if (!(intv & 1)) - continue; + for (i = 0; i < SE7722_FPGA_IRQ_NR; i++) { + int irq = irq_create_mapping(se7722_irq_domain, i); - generic_handle_irq(se7722_fpga_irq[ext_irq]); + if (unlikely(irq == 0)) { + printk("Failed to allocate IRQ %d\n", i); + return; + } } } -/* - * Initialize IRQ setting - */ -void __init init_se7722_IRQ(void) +static void __init se7722_gc_init(void) { - int i, irq; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + unsigned int irq_base; - __raw_writew(0, IRQ01_MASK); /* disable all irqs */ - __raw_writew(0x2000, 0xb03fffec); /* mrshpc irq enable */ + irq_base = irq_linear_revmap(se7722_irq_domain, 0); - for (i = 0; i < SE7722_FPGA_IRQ_NR; i++) { - irq = create_irq(); - if (irq < 0) - return; - se7722_fpga_irq[i] = irq; + gc = irq_alloc_generic_chip(DRV_NAME, 1, irq_base, se7722_irq_regs, + handle_level_irq); + if (unlikely(!gc)) + return; - irq_set_chip_and_handler_name(se7722_fpga_irq[i], - &se7722_irq_chip, - handle_level_irq, - "level"); + ct = gc->chip_types; + ct->chip.irq_mask = irq_gc_mask_set_bit; + ct->chip.irq_unmask = irq_gc_mask_clr_bit; - irq_set_chip_data(se7722_fpga_irq[i], (void *)i); - } + ct->regs.mask = IRQ01_MASK_REG; + + irq_setup_generic_chip(gc, IRQ_MSK(SE7722_FPGA_IRQ_NR), + IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); irq_set_chained_handler(IRQ0_IRQ, se7722_irq_demux); irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); @@ -81,3 +98,25 @@ void __init init_se7722_IRQ(void) irq_set_chained_handler(IRQ1_IRQ, se7722_irq_demux); irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW); } + +/* + * Initialize FPGA IRQs + */ +void __init init_se7722_IRQ(void) +{ + se7722_irq_regs = ioremap(IRQ01_BASE_ADDR, SZ_16); + if (unlikely(!se7722_irq_regs)) { + printk("Failed to remap IRQ01 regs\n"); + return; + } + + /* + * All FPGA IRQs disabled by default + */ + iowrite16(0, se7722_irq_regs + IRQ01_MASK_REG); + + __raw_writew(0x2000, 0xb03fffec); /* mrshpc irq enable */ + + se7722_domain_init(); + se7722_gc_init(); +} diff --git a/arch/sh/boards/mach-se/7722/setup.c b/arch/sh/boards/mach-se/7722/setup.c index e1963fecd761..2ec0111fdf9b 100644 --- a/arch/sh/boards/mach-se/7722/setup.c +++ b/arch/sh/boards/mach-se/7722/setup.c @@ -2,6 +2,7 @@ * linux/arch/sh/boards/se/7722/setup.c * * Copyright (C) 2007 Nobuhiro Iwamatsu + * Copyright (C) 2012 Paul Mundt * * Hitachi UL SolutionEngine 7722 Support. * @@ -15,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -142,10 +144,10 @@ static int __init se7722_devices_setup(void) /* Wire-up dynamic vectors */ cf_ide_resources[2].start = cf_ide_resources[2].end = - se7722_fpga_irq[SE7722_FPGA_IRQ_MRSHPC0]; + irq_find_mapping(se7722_irq_domain, SE7722_FPGA_IRQ_MRSHPC0); smc91x_eth_resources[1].start = smc91x_eth_resources[1].end = - se7722_fpga_irq[SE7722_FPGA_IRQ_SMC]; + irq_find_mapping(se7722_irq_domain, SE7722_FPGA_IRQ_SMC); return platform_add_devices(se7722_devices, ARRAY_SIZE(se7722_devices)); } diff --git a/arch/sh/include/mach-se/mach/se7722.h b/arch/sh/include/mach-se/mach/se7722.h index 16505bfb8a9e..5508dc42e4d2 100644 --- a/arch/sh/include/mach-se/mach/se7722.h +++ b/arch/sh/include/mach-se/mach/se7722.h @@ -80,12 +80,6 @@ #define IRQ0_IRQ 32 #define IRQ1_IRQ 33 -#define IRQ01_MODE 0xb1800000 -#define IRQ01_STS 0xb1800004 -#define IRQ01_MASK 0xb1800008 - -/* Bits in IRQ01_* registers */ - #define SE7722_FPGA_IRQ_USB 0 /* IRQ0 */ #define SE7722_FPGA_IRQ_SMC 1 /* IRQ0 */ #define SE7722_FPGA_IRQ_MRSHPC0 2 /* IRQ1 */ @@ -94,8 +88,10 @@ #define SE7722_FPGA_IRQ_MRSHPC3 5 /* IRQ1 */ #define SE7722_FPGA_IRQ_NR 6 +struct irq_domain; + /* arch/sh/boards/se/7722/irq.c */ -extern unsigned int se7722_fpga_irq[]; +extern struct irq_domain *se7722_irq_domain; void init_se7722_IRQ(void); -- cgit v1.2.3-59-g8ed1b From 197b58e6651426bec8b2582013258b52cd15a444 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 19:07:18 +0900 Subject: sh: se7343: Move CPLD IRQs to irqdomain and generic irq chip. Follows the se7722 change, see there for more information. Signed-off-by: Paul Mundt --- arch/sh/boards/Kconfig | 2 + arch/sh/boards/mach-se/7343/irq.c | 129 ++++++++++++++++++++++------------ arch/sh/boards/mach-se/7343/setup.c | 10 +-- arch/sh/include/mach-se/mach/se7343.h | 7 +- 4 files changed, 97 insertions(+), 51 deletions(-) diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig index 525b9e32cd14..1a280048e2c3 100644 --- a/arch/sh/boards/Kconfig +++ b/arch/sh/boards/Kconfig @@ -81,6 +81,8 @@ config SH_7780_SOLUTION_ENGINE config SH_7343_SOLUTION_ENGINE bool "SolutionEngine7343" select SOLUTION_ENGINE + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN depends on CPU_SUBTYPE_SH7343 help Select 7343 SolutionEngine if configuring for a Hitachi diff --git a/arch/sh/boards/mach-se/7343/irq.c b/arch/sh/boards/mach-se/7343/irq.c index fd45ffc48340..7646bf0486c2 100644 --- a/arch/sh/boards/mach-se/7343/irq.c +++ b/arch/sh/boards/mach-se/7343/irq.c @@ -1,86 +1,129 @@ /* - * linux/arch/sh/boards/se/7343/irq.c + * Hitachi UL SolutionEngine 7343 FPGA IRQ Support. * * Copyright (C) 2008 Yoshihiro Shimoda + * Copyright (C) 2012 Paul Mundt * - * Based on linux/arch/sh/boards/se/7722/irq.c + * Based on linux/arch/sh/boards/se/7343/irq.c * Copyright (C) 2007 Nobuhiro Iwamatsu * * 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. */ +#define DRV_NAME "SE7343-FPGA" +#define pr_fmt(fmt) DRV_NAME ": " fmt + +#define irq_reg_readl ioread16 +#define irq_reg_writel iowrite16 + #include #include #include +#include #include +#include #include -unsigned int se7343_fpga_irq[SE7343_FPGA_IRQ_NR] = { 0, }; +#define PA_CPLD_BASE_ADDR 0x11400000 +#define PA_CPLD_ST_REG 0x08 /* CPLD Interrupt status register */ +#define PA_CPLD_IMSK_REG 0x0a /* CPLD Interrupt mask register */ -static void disable_se7343_irq(struct irq_data *data) -{ - unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); - __raw_writew(__raw_readw(PA_CPLD_IMSK) | 1 << bit, PA_CPLD_IMSK); -} +static void __iomem *se7343_irq_regs; +struct irq_domain *se7343_irq_domain; -static void enable_se7343_irq(struct irq_data *data) +static void se7343_irq_demux(unsigned int irq, struct irq_desc *desc) { - unsigned int bit = (unsigned int)irq_data_get_irq_chip_data(data); - __raw_writew(__raw_readw(PA_CPLD_IMSK) & ~(1 << bit), PA_CPLD_IMSK); -} + struct irq_data *data = irq_get_irq_data(irq); + struct irq_chip *chip = irq_data_get_irq_chip(data); + unsigned long mask; + int bit; -static struct irq_chip se7343_irq_chip __read_mostly = { - .name = "SE7343-FPGA", - .irq_mask = disable_se7343_irq, - .irq_unmask = enable_se7343_irq, -}; + chip->irq_mask_ack(data); -static void se7343_irq_demux(unsigned int irq, struct irq_desc *desc) + mask = ioread16(se7343_irq_regs + PA_CPLD_ST_REG); + + for_each_set_bit(bit, &mask, SE7343_FPGA_IRQ_NR) + generic_handle_irq(irq_linear_revmap(se7343_irq_domain, bit)); + + chip->irq_unmask(data); +} + +static void __init se7343_domain_init(void) { - unsigned short intv = __raw_readw(PA_CPLD_ST); - unsigned int ext_irq = 0; + int i; - intv &= (1 << SE7343_FPGA_IRQ_NR) - 1; + se7343_irq_domain = irq_domain_add_linear(NULL, SE7343_FPGA_IRQ_NR, + &irq_domain_simple_ops, NULL); + if (unlikely(!se7343_irq_domain)) { + printk("Failed to get IRQ domain\n"); + return; + } - for (; intv; intv >>= 1, ext_irq++) { - if (!(intv & 1)) - continue; + for (i = 0; i < SE7343_FPGA_IRQ_NR; i++) { + int irq = irq_create_mapping(se7343_irq_domain, i); - generic_handle_irq(se7343_fpga_irq[ext_irq]); + if (unlikely(irq == 0)) { + printk("Failed to allocate IRQ %d\n", i); + return; + } } } -/* - * Initialize IRQ setting - */ -void __init init_7343se_IRQ(void) +static void __init se7343_gc_init(void) { - int i, irq; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + unsigned int irq_base; - __raw_writew(0, PA_CPLD_IMSK); /* disable all irqs */ - __raw_writew(0x2000, 0xb03fffec); /* mrshpc irq enable */ + irq_base = irq_linear_revmap(se7343_irq_domain, 0); - for (i = 0; i < SE7343_FPGA_IRQ_NR; i++) { - irq = create_irq(); - if (irq < 0) - return; - se7343_fpga_irq[i] = irq; + gc = irq_alloc_generic_chip(DRV_NAME, 1, irq_base, se7343_irq_regs, + handle_level_irq); + if (unlikely(!gc)) + return; - irq_set_chip_and_handler_name(se7343_fpga_irq[i], - &se7343_irq_chip, - handle_level_irq, - "level"); + ct = gc->chip_types; + ct->chip.irq_mask = irq_gc_mask_set_bit; + ct->chip.irq_unmask = irq_gc_mask_clr_bit; - irq_set_chip_data(se7343_fpga_irq[i], (void *)i); - } + ct->regs.mask = PA_CPLD_IMSK_REG; + + irq_setup_generic_chip(gc, IRQ_MSK(SE7343_FPGA_IRQ_NR), + IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); irq_set_chained_handler(IRQ0_IRQ, se7343_irq_demux); irq_set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW); + irq_set_chained_handler(IRQ1_IRQ, se7343_irq_demux); irq_set_irq_type(IRQ1_IRQ, IRQ_TYPE_LEVEL_LOW); + irq_set_chained_handler(IRQ4_IRQ, se7343_irq_demux); irq_set_irq_type(IRQ4_IRQ, IRQ_TYPE_LEVEL_LOW); + irq_set_chained_handler(IRQ5_IRQ, se7343_irq_demux); irq_set_irq_type(IRQ5_IRQ, IRQ_TYPE_LEVEL_LOW); } + +/* + * Initialize IRQ setting + */ +void __init init_7343se_IRQ(void) +{ + se7343_irq_regs = ioremap(PA_CPLD_BASE_ADDR, SZ_16); + if (unlikely(!se7343_irq_regs)) { + pr_err("Failed to remap CPLD\n"); + return; + } + + /* + * All FPGA IRQs disabled by default + */ + iowrite16(0, se7343_irq_regs + PA_CPLD_IMSK_REG); + + __raw_writew(0x2000, 0xb03fffec); /* mrshpc irq enable */ + + se7343_domain_init(); + se7343_gc_init(); +} diff --git a/arch/sh/boards/mach-se/7343/setup.c b/arch/sh/boards/mach-se/7343/setup.c index d2370af56d77..8ce4f2a202a8 100644 --- a/arch/sh/boards/mach-se/7343/setup.c +++ b/arch/sh/boards/mach-se/7343/setup.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -145,11 +146,12 @@ static struct platform_device *sh7343se_platform_devices[] __initdata = { static int __init sh7343se_devices_setup(void) { /* Wire-up dynamic vectors */ - serial_platform_data[0].irq = se7343_fpga_irq[SE7343_FPGA_IRQ_UARTA]; - serial_platform_data[1].irq = se7343_fpga_irq[SE7343_FPGA_IRQ_UARTB]; - + serial_platform_data[0].irq = irq_find_mapping(se7343_irq_domain, + SE7343_FPGA_IRQ_UARTA); + serial_platform_data[1].irq = irq_find_mapping(se7343_irq_domain, + SE7343_FPGA_IRQ_UARTB); usb_resources[2].start = usb_resources[2].end = - se7343_fpga_irq[SE7343_FPGA_IRQ_USB]; + irq_find_mapping(se7343_irq_domain, SE7343_FPGA_IRQ_USB); return platform_add_devices(sh7343se_platform_devices, ARRAY_SIZE(sh7343se_platform_devices)); diff --git a/arch/sh/include/mach-se/mach/se7343.h b/arch/sh/include/mach-se/mach/se7343.h index 8d8170d6cc43..2ec6f75a44de 100644 --- a/arch/sh/include/mach-se/mach/se7343.h +++ b/arch/sh/include/mach-se/mach/se7343.h @@ -49,9 +49,6 @@ #define PA_LED 0xb0C00000 /* LED */ #define LED_SHIFT 0 #define PA_DIPSW 0xb0900000 /* Dip switch 31 */ -#define PA_CPLD_MODESET 0xb1400004 /* CPLD Mode set register */ -#define PA_CPLD_ST 0xb1400008 /* CPLD Interrupt status register */ -#define PA_CPLD_IMSK 0xb140000a /* CPLD Interrupt mask register */ /* Area 5 */ #define PA_EXT5 0x14000000 #define PA_EXT5_SIZE 0x04000000 @@ -134,8 +131,10 @@ #define SE7343_FPGA_IRQ_NR 12 +struct irq_domain; + /* arch/sh/boards/se/7343/irq.c */ -extern unsigned int se7343_fpga_irq[]; +extern struct irq_domain *se7343_irq_domain; void init_7343se_IRQ(void); -- cgit v1.2.3-59-g8ed1b From be9c00295b34760ea2f9667929049f094116b5a8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 19:10:45 +0900 Subject: sh: intc: Kill off deprecated dynamic IRQ API. Now that all of the users have been converted away, kill off the remnants of the old API. Signed-off-by: Paul Mundt --- drivers/sh/intc/Makefile | 2 +- drivers/sh/intc/dynamic.c | 65 ----------------------------------------------- 2 files changed, 1 insertion(+), 66 deletions(-) delete mode 100644 drivers/sh/intc/dynamic.c diff --git a/drivers/sh/intc/Makefile b/drivers/sh/intc/Makefile index bb5df868d77a..44f006d09471 100644 --- a/drivers/sh/intc/Makefile +++ b/drivers/sh/intc/Makefile @@ -1,4 +1,4 @@ -obj-y := access.o chip.o core.o dynamic.o handle.o virq.o +obj-y := access.o chip.o core.o handle.o virq.o obj-$(CONFIG_INTC_BALANCING) += balancing.o obj-$(CONFIG_INTC_USERIMASK) += userimask.o diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c deleted file mode 100644 index 5fea1ee8799a..000000000000 --- a/drivers/sh/intc/dynamic.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Dynamic IRQ management - * - * Copyright (C) 2010 Paul Mundt - * - * Modelled after arch/x86/kernel/apic/io_apic.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. - */ -#define pr_fmt(fmt) "intc: " fmt - -#include -#include -#include -#include -#include "internals.h" /* only for activate_irq() damage.. */ - -/* - * The IRQ bitmap provides a global map of bound IRQ vectors for a - * given platform. Allocation of IRQs are either static through the CPU - * vector map, or dynamic in the case of board mux vectors or MSI. - * - * As this is a central point for all IRQ controllers on the system, - * each of the available sources are mapped out here. This combined with - * sparseirq makes it quite trivial to keep the vector map tightly packed - * when dynamically creating IRQs, as well as tying in to otherwise - * unused irq_desc positions in the sparse array. - */ - -/* - * Dynamic IRQ allocation and deallocation - */ -unsigned int create_irq_nr(unsigned int irq_want, int node) -{ - int irq = irq_alloc_desc_at(irq_want, node); - if (irq < 0) - return 0; - - activate_irq(irq); - return irq; -} - -int create_irq(void) -{ - int irq = irq_alloc_desc(numa_node_id()); - if (irq >= 0) - activate_irq(irq); - - return irq; -} - -void destroy_irq(unsigned int irq) -{ - irq_free_desc(irq); -} - -void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs) -{ - int i; - - for (i = 0; i < nr_vecs; i++) - irq_reserve_irq(evt2irq(vectors[i].vect)); -} -- cgit v1.2.3-59-g8ed1b From a60977a51333a8108f0574aa26094d66b7fedf34 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 14:59:26 +0900 Subject: sh: clkfwk: Move to common clk_div_table accessors for div4/div6. This plugs in a generic clk_div_table, based on the div4 version. div6 is then adopted to use it for encapsulating its div table, which permits us to start div6/4 unification, as well as preparation for other div types. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 46 +++++++++++++++++++++++++++++++++++----------- include/linux/sh_clk.h | 5 +++-- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index f0d015dd0fef..9dea32907795 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -71,6 +71,22 @@ static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) return clk_rate_table_round(clk, clk->freq_table, rate); } +/* + * Div/mult table lookup helpers + */ +static inline struct clk_div_table *clk_to_div_table(struct clk *clk) +{ + return clk->priv; +} + +static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) +{ + return clk_to_div_table(clk)->div_mult_table; +} + +/* + * div6 support + */ static int sh_clk_div6_divisors[64] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, @@ -78,14 +94,18 @@ static int sh_clk_div6_divisors[64] = { 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 }; -static struct clk_div_mult_table sh_clk_div6_table = { +static struct clk_div_mult_table div6_div_mult_table = { .divisors = sh_clk_div6_divisors, .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), }; +static struct clk_div_table sh_clk_div6_table = { + .div_mult_table = &div6_div_mult_table, +}; + static unsigned long sh_clk_div6_recalc(struct clk *clk) { - struct clk_div_mult_table *table = &sh_clk_div6_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); unsigned int idx; clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, @@ -98,7 +118,7 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk) static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) { - struct clk_div_mult_table *table = &sh_clk_div6_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); u32 value; int ret, i; @@ -223,7 +243,8 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, { struct clk *clkp; void *freq_table; - int nr_divs = sh_clk_div6_table.nr_divisors; + struct clk_div_table *table = &sh_clk_div6_table; + int nr_divs = table->div_mult_table->nr_divisors; int freq_table_size = sizeof(struct cpufreq_frequency_table); int ret = 0; int k; @@ -239,6 +260,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, clkp = clks + k; clkp->ops = ops; + clkp->priv = table; clkp->freq_table = freq_table + (k * freq_table_size); clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; ret = clk_register(clkp); @@ -262,10 +284,12 @@ int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) &sh_clk_div6_reparent_clk_ops); } +/* + * div4 support + */ static unsigned long sh_clk_div4_recalc(struct clk *clk) { - struct clk_div4_table *d4t = clk->priv; - struct clk_div_mult_table *table = d4t->div_mult_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); unsigned int idx; clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, @@ -278,8 +302,7 @@ static unsigned long sh_clk_div4_recalc(struct clk *clk) static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) { - struct clk_div4_table *d4t = clk->priv; - struct clk_div_mult_table *table = d4t->div_mult_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); u32 value; int ret; @@ -308,7 +331,7 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) { - struct clk_div4_table *d4t = clk->priv; + struct clk_div_table *dt = clk_to_div_table(clk); unsigned long value; int idx = clk_rate_table_find(clk, clk->freq_table, rate); if (idx < 0) @@ -319,8 +342,9 @@ static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) value |= (idx << clk->enable_bit); sh_clk_write(value, clk); - if (d4t->kick) - d4t->kick(clk); + /* XXX: Should use a post-change notifier */ + if (dt->kick) + dt->kick(clk); return 0; } diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index c513b73cd7cb..706b803df7b7 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -18,7 +18,6 @@ struct clk_mapping { struct kref ref; }; - struct sh_clk_ops { #ifdef CONFIG_SH_CLK_CPG_LEGACY void (*init)(struct clk *clk); @@ -149,11 +148,13 @@ static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr) .flags = _flags, \ } -struct clk_div4_table { +struct clk_div_table { struct clk_div_mult_table *div_mult_table; void (*kick)(struct clk *clk); }; +#define clk_div4_table clk_div_table + int sh_clk_div4_register(struct clk *clks, int nr, struct clk_div4_table *table); int sh_clk_div4_enable_register(struct clk *clks, int nr, -- cgit v1.2.3-59-g8ed1b From 1111cc1e8080b5ff46f5b945acb2f99d6176b2d1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 15:21:43 +0900 Subject: sh: clkfwk: Introduce a div_mask for variable div types. This plugs in a div_mask for the clock and sets it up for the existing div6/4 cases. This will make it possible to support other div types, as well as share more div6/4 infrastructure. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 10 +++++----- include/linux/sh_clk.h | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 9dea32907795..9386bd21c003 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -111,7 +111,7 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk) clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, table, NULL); - idx = sh_clk_read(clk) & 0x003f; + idx = sh_clk_read(clk) & clk->div_mask; return clk->freq_table[idx].frequency; } @@ -159,7 +159,7 @@ static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) return idx; value = sh_clk_read(clk); - value &= ~0x3f; + value &= ~clk->div_mask; value |= idx; sh_clk_write(value, clk); return 0; @@ -185,7 +185,7 @@ static void sh_clk_div6_disable(struct clk *clk) value = sh_clk_read(clk); value |= 0x100; /* stop clock */ - value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ + value |= clk->div_mask; /* VDIV bits must be non-zero, overwrite divider */ sh_clk_write(value, clk); } @@ -295,7 +295,7 @@ static unsigned long sh_clk_div4_recalc(struct clk *clk) clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, table, &clk->arch_flags); - idx = (sh_clk_read(clk) >> clk->enable_bit) & 0x000f; + idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; return clk->freq_table[idx].frequency; } @@ -338,7 +338,7 @@ static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) return idx; value = sh_clk_read(clk); - value &= ~(0xf << clk->enable_bit); + value &= ~(clk->div_mask << clk->enable_bit); value |= (idx << clk->enable_bit); sh_clk_write(value, clk); diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index 706b803df7b7..d540b8153178 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -30,6 +30,10 @@ struct sh_clk_ops { long (*round_rate)(struct clk *clk, unsigned long rate); }; +#define SH_CLK_DIV_MSK(div) ((1 << (div)) - 1) +#define SH_CLK_DIV4_MSK SH_CLK_DIV_MSK(4) +#define SH_CLK_DIV6_MSK SH_CLK_DIV_MSK(6) + struct clk { struct list_head node; struct clk *parent; @@ -51,6 +55,7 @@ struct clk { unsigned int enable_bit; void __iomem *mapped_reg; + unsigned int div_mask; unsigned long arch_flags; void *priv; struct clk_mapping *mapping; @@ -145,6 +150,7 @@ static inline int __deprecated sh_clk_mstp32_register(struct clk *clks, int nr) .enable_reg = (void __iomem *)_reg, \ .enable_bit = _shift, \ .arch_flags = _div_bitmap, \ + .div_mask = SH_CLK_DIV4_MSK, \ .flags = _flags, \ } @@ -167,6 +173,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, { \ .enable_reg = (void __iomem *)_reg, \ .flags = _flags, \ + .div_mask = SH_CLK_DIV6_MSK, \ .parent_table = _parents, \ .parent_num = _num_parents, \ .src_shift = _src_shift, \ @@ -177,6 +184,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, { \ .parent = _parent, \ .enable_reg = (void __iomem *)_reg, \ + .div_mask = SH_CLK_DIV6_MSK, \ .flags = _flags, \ } -- cgit v1.2.3-59-g8ed1b From 75f5f8a56e0fdf6d32b3ae9c44c10bc0acd3857c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 15:26:01 +0900 Subject: sh: clkfwk: Use shared sh_clk_div_recalc(). This generalizes the div4 recalc routine for use by div6 and others, then makes it the default. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 62 +++++++++++++++++++++----------------------------- include/linux/sh_clk.h | 2 ++ 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 9386bd21c003..84aeeb8fe013 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -66,11 +66,6 @@ int __init sh_clk_mstp_register(struct clk *clks, int nr) return ret; } -static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) -{ - return clk_rate_table_round(clk, clk->freq_table, rate); -} - /* * Div/mult table lookup helpers */ @@ -84,6 +79,27 @@ static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) return clk_to_div_table(clk)->div_mult_table; } +/* + * Common div ops + */ +static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) +{ + return clk_rate_table_round(clk, clk->freq_table, rate); +} + +static unsigned long sh_clk_div_recalc(struct clk *clk) +{ + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); + unsigned int idx; + + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, clk->arch_flags ? &clk->arch_flags : NULL); + + idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; + + return clk->freq_table[idx].frequency; +} + /* * div6 support */ @@ -103,19 +119,6 @@ static struct clk_div_table sh_clk_div6_table = { .div_mult_table = &div6_div_mult_table, }; -static unsigned long sh_clk_div6_recalc(struct clk *clk) -{ - struct clk_div_mult_table *table = clk_to_div_mult_table(clk); - unsigned int idx; - - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, NULL); - - idx = sh_clk_read(clk) & clk->div_mask; - - return clk->freq_table[idx].frequency; -} - static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) { struct clk_div_mult_table *table = clk_to_div_mult_table(clk); @@ -190,7 +193,7 @@ static void sh_clk_div6_disable(struct clk *clk) } static struct sh_clk_ops sh_clk_div6_clk_ops = { - .recalc = sh_clk_div6_recalc, + .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div6_set_rate, .enable = sh_clk_div6_enable, @@ -198,7 +201,7 @@ static struct sh_clk_ops sh_clk_div6_clk_ops = { }; static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { - .recalc = sh_clk_div6_recalc, + .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div6_set_rate, .enable = sh_clk_div6_enable, @@ -287,19 +290,6 @@ int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) /* * div4 support */ -static unsigned long sh_clk_div4_recalc(struct clk *clk) -{ - struct clk_div_mult_table *table = clk_to_div_mult_table(clk); - unsigned int idx; - - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, &clk->arch_flags); - - idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; - - return clk->freq_table[idx].frequency; -} - static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) { struct clk_div_mult_table *table = clk_to_div_mult_table(clk); @@ -361,13 +351,13 @@ static void sh_clk_div4_disable(struct clk *clk) } static struct sh_clk_ops sh_clk_div4_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, }; static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, @@ -375,7 +365,7 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { }; static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index d540b8153178..35a04f19fb53 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -172,6 +172,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, _num_parents, _src_shift, _src_width) \ { \ .enable_reg = (void __iomem *)_reg, \ + .enable_bit = 0, /* unused */ \ .flags = _flags, \ .div_mask = SH_CLK_DIV6_MSK, \ .parent_table = _parents, \ @@ -184,6 +185,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, { \ .parent = _parent, \ .enable_reg = (void __iomem *)_reg, \ + .enable_bit = 0, /* unused */ \ .div_mask = SH_CLK_DIV6_MSK, \ .flags = _flags, \ } -- cgit v1.2.3-59-g8ed1b From 0fa22168e00106797f28b2655aaefd0d16a6e67b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 15:52:10 +0900 Subject: sh: clkfwk: Use shared sh_clk_div_set_rate() Follows the sh_clk_div_recalc() change. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 70 +++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 84aeeb8fe013..29ee5f7072a4 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -100,6 +100,28 @@ static unsigned long sh_clk_div_recalc(struct clk *clk) return clk->freq_table[idx].frequency; } +static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) +{ + struct clk_div_table *dt = clk_to_div_table(clk); + unsigned long value; + int idx; + + idx = clk_rate_table_find(clk, clk->freq_table, rate); + if (idx < 0) + return idx; + + value = sh_clk_read(clk); + value &= ~(clk->div_mask << clk->enable_bit); + value |= (idx << clk->enable_bit); + sh_clk_write(value, clk); + + /* XXX: Should use a post-change notifier */ + if (dt->kick) + dt->kick(clk); + + return 0; +} + /* * div6 support */ @@ -152,28 +174,12 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) -{ - unsigned long value; - int idx; - - idx = clk_rate_table_find(clk, clk->freq_table, rate); - if (idx < 0) - return idx; - - value = sh_clk_read(clk); - value &= ~clk->div_mask; - value |= idx; - sh_clk_write(value, clk); - return 0; -} - static int sh_clk_div6_enable(struct clk *clk) { unsigned long value; int ret; - ret = sh_clk_div6_set_rate(clk, clk->rate); + ret = sh_clk_div_set_rate(clk, clk->rate); if (ret == 0) { value = sh_clk_read(clk); value &= ~0x100; /* clear stop bit to enable clock */ @@ -195,7 +201,7 @@ static void sh_clk_div6_disable(struct clk *clk) static struct sh_clk_ops sh_clk_div6_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, - .set_rate = sh_clk_div6_set_rate, + .set_rate = sh_clk_div_set_rate, .enable = sh_clk_div6_enable, .disable = sh_clk_div6_disable, }; @@ -203,7 +209,7 @@ static struct sh_clk_ops sh_clk_div6_clk_ops = { static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, - .set_rate = sh_clk_div6_set_rate, + .set_rate = sh_clk_div_set_rate, .enable = sh_clk_div6_enable, .disable = sh_clk_div6_disable, .set_parent = sh_clk_div6_set_parent, @@ -319,26 +325,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) -{ - struct clk_div_table *dt = clk_to_div_table(clk); - unsigned long value; - int idx = clk_rate_table_find(clk, clk->freq_table, rate); - if (idx < 0) - return idx; - - value = sh_clk_read(clk); - value &= ~(clk->div_mask << clk->enable_bit); - value |= (idx << clk->enable_bit); - sh_clk_write(value, clk); - - /* XXX: Should use a post-change notifier */ - if (dt->kick) - dt->kick(clk); - - return 0; -} - static int sh_clk_div4_enable(struct clk *clk) { sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk); @@ -352,13 +338,13 @@ static void sh_clk_div4_disable(struct clk *clk) static struct sh_clk_ops sh_clk_div4_clk_ops = { .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div4_set_rate, + .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, }; static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div4_set_rate, + .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, .disable = sh_clk_div4_disable, @@ -366,7 +352,7 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div4_set_rate, + .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, .disable = sh_clk_div4_disable, -- cgit v1.2.3-59-g8ed1b From 764f4e4e33d18cde4dcaf8a0d860b749c6d6d08b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 16:34:48 +0900 Subject: sh: clkfwk: Use shared sh_clk_div_enable/disable(). This introduces a new flag for clocks that need to have their divisor ratio set back to their initial mask at disable time to prevent interactivity problems with the clock stop bit (presently div6 only). With this in place it's possible to handle the corner case on top of the div4 op without any particular need for leaving things split out. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 77 ++++++++++++++++++++++---------------------------- include/linux/sh_clk.h | 6 ++-- 2 files changed, 38 insertions(+), 45 deletions(-) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 29ee5f7072a4..06537f2b2fb8 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -14,6 +14,8 @@ #include #include +#define CPG_CKSTP_BIT BIT(8) + static unsigned int sh_clk_read(struct clk *clk) { if (clk->flags & CLK_ENABLE_REG_8BIT) @@ -122,6 +124,30 @@ static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) return 0; } +static int sh_clk_div_enable(struct clk *clk) +{ + sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); + return 0; +} + +static void sh_clk_div_disable(struct clk *clk) +{ + unsigned int val; + + val = sh_clk_read(clk); + val |= CPG_CKSTP_BIT; + + /* + * div6 clocks require the divisor field to be non-zero or the + * above CKSTP toggle silently fails. Ensure that the divisor + * array is reset to its initial state on disable. + */ + if (clk->flags & CLK_MASK_DIV_ON_DISABLE) + val |= clk->div_mask; + + sh_clk_write(val, clk); +} + /* * div6 support */ @@ -174,44 +200,20 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div6_enable(struct clk *clk) -{ - unsigned long value; - int ret; - - ret = sh_clk_div_set_rate(clk, clk->rate); - if (ret == 0) { - value = sh_clk_read(clk); - value &= ~0x100; /* clear stop bit to enable clock */ - sh_clk_write(value, clk); - } - return ret; -} - -static void sh_clk_div6_disable(struct clk *clk) -{ - unsigned long value; - - value = sh_clk_read(clk); - value |= 0x100; /* stop clock */ - value |= clk->div_mask; /* VDIV bits must be non-zero, overwrite divider */ - sh_clk_write(value, clk); -} - static struct sh_clk_ops sh_clk_div6_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div_set_rate, - .enable = sh_clk_div6_enable, - .disable = sh_clk_div6_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, }; static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div_set_rate, - .enable = sh_clk_div6_enable, - .disable = sh_clk_div6_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, .set_parent = sh_clk_div6_set_parent, }; @@ -325,17 +327,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div4_enable(struct clk *clk) -{ - sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk); - return 0; -} - -static void sh_clk_div4_disable(struct clk *clk) -{ - sh_clk_write(sh_clk_read(clk) | (1 << 8), clk); -} - static struct sh_clk_ops sh_clk_div4_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, @@ -346,16 +337,16 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div4_enable, - .disable = sh_clk_div4_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, }; static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div4_enable, - .disable = sh_clk_div4_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, .set_parent = sh_clk_div4_set_parent, }; diff --git a/include/linux/sh_clk.h b/include/linux/sh_clk.h index 35a04f19fb53..50910913b268 100644 --- a/include/linux/sh_clk.h +++ b/include/linux/sh_clk.h @@ -69,6 +69,8 @@ struct clk { #define CLK_ENABLE_REG_16BIT BIT(2) #define CLK_ENABLE_REG_8BIT BIT(3) +#define CLK_MASK_DIV_ON_DISABLE BIT(4) + #define CLK_ENABLE_REG_MASK (CLK_ENABLE_REG_32BIT | \ CLK_ENABLE_REG_16BIT | \ CLK_ENABLE_REG_8BIT) @@ -173,7 +175,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, { \ .enable_reg = (void __iomem *)_reg, \ .enable_bit = 0, /* unused */ \ - .flags = _flags, \ + .flags = _flags | CLK_MASK_DIV_ON_DISABLE, \ .div_mask = SH_CLK_DIV6_MSK, \ .parent_table = _parents, \ .parent_num = _num_parents, \ @@ -187,7 +189,7 @@ int sh_clk_div4_reparent_register(struct clk *clks, int nr, .enable_reg = (void __iomem *)_reg, \ .enable_bit = 0, /* unused */ \ .div_mask = SH_CLK_DIV6_MSK, \ - .flags = _flags, \ + .flags = _flags | CLK_MASK_DIV_ON_DISABLE, \ } int sh_clk_div6_register(struct clk *clks, int nr); -- cgit v1.2.3-59-g8ed1b From e3c87607731e1a8937567e92a52eedee1bec622d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 16:43:42 +0900 Subject: sh: clkfwk: Consolidate div6/div4 clk_ops definitions. Everything with the exception of the _reparent ops are now shared, so switch everything over to common types. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 06537f2b2fb8..eeaec796a395 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -148,6 +148,20 @@ static void sh_clk_div_disable(struct clk *clk) sh_clk_write(val, clk); } +static struct sh_clk_ops sh_clk_div_clk_ops = { + .recalc = sh_clk_div_recalc, + .set_rate = sh_clk_div_set_rate, + .round_rate = sh_clk_div_round_rate, +}; + +static struct sh_clk_ops sh_clk_div_enable_clk_ops = { + .recalc = sh_clk_div_recalc, + .set_rate = sh_clk_div_set_rate, + .round_rate = sh_clk_div_round_rate, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, +}; + /* * div6 support */ @@ -200,14 +214,6 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) return 0; } -static struct sh_clk_ops sh_clk_div6_clk_ops = { - .recalc = sh_clk_div_recalc, - .round_rate = sh_clk_div_round_rate, - .set_rate = sh_clk_div_set_rate, - .enable = sh_clk_div_enable, - .disable = sh_clk_div_disable, -}; - static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, @@ -286,7 +292,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, int __init sh_clk_div6_register(struct clk *clks, int nr) { - return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops); + return sh_clk_div6_register_ops(clks, nr, &sh_clk_div_enable_clk_ops); } int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) @@ -327,20 +333,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) return 0; } -static struct sh_clk_ops sh_clk_div4_clk_ops = { - .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div_set_rate, - .round_rate = sh_clk_div_round_rate, -}; - -static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { - .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div_set_rate, - .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div_enable, - .disable = sh_clk_div_disable, -}; - static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, @@ -385,14 +377,14 @@ static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, int __init sh_clk_div4_register(struct clk *clks, int nr, struct clk_div4_table *table) { - return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops); + return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div_clk_ops); } int __init sh_clk_div4_enable_register(struct clk *clks, int nr, struct clk_div4_table *table) { return sh_clk_div4_register_ops(clks, nr, table, - &sh_clk_div4_enable_clk_ops); + &sh_clk_div_enable_clk_ops); } int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, -- cgit v1.2.3-59-g8ed1b From 609d7558f232e583a31951c65a6ee43d81c65720 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 16:55:05 +0900 Subject: sh: clkfwk: Consolidate div clk registration helper. This consolidates the div6/4 versions of the clk registration wrapper. The existing wrappers with their own sh_clk_ops are maintained for API compatability, though in the future it should be possible to be rid of them entirely. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 182 +++++++++++++++++++++------------------------------ 1 file changed, 75 insertions(+), 107 deletions(-) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index eeaec796a395..07e9fb4f8041 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -162,6 +162,72 @@ static struct sh_clk_ops sh_clk_div_enable_clk_ops = { .disable = sh_clk_div_disable, }; +static int __init sh_clk_init_parent(struct clk *clk) +{ + u32 val; + + if (clk->parent) + return 0; + + if (!clk->parent_table || !clk->parent_num) + return 0; + + if (!clk->src_width) { + pr_err("sh_clk_init_parent: cannot select parent clock\n"); + return -EINVAL; + } + + val = (sh_clk_read(clk) >> clk->src_shift); + val &= (1 << clk->src_width) - 1; + + if (val >= clk->parent_num) { + pr_err("sh_clk_init_parent: parent table size failed\n"); + return -EINVAL; + } + + clk_reparent(clk, clk->parent_table[val]); + if (!clk->parent) { + pr_err("sh_clk_init_parent: unable to set parent"); + return -EINVAL; + } + + return 0; +} + +static int __init sh_clk_div_register_ops(struct clk *clks, int nr, + struct clk_div_table *table, struct sh_clk_ops *ops) +{ + struct clk *clkp; + void *freq_table; + int nr_divs = table->div_mult_table->nr_divisors; + int freq_table_size = sizeof(struct cpufreq_frequency_table); + int ret = 0; + int k; + + freq_table_size *= (nr_divs + 1); + freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); + if (!freq_table) { + pr_err("%s: unable to alloc memory\n", __func__); + return -ENOMEM; + } + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + + clkp->ops = ops; + clkp->priv = table; + + clkp->freq_table = freq_table + (k * freq_table_size); + clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; + + ret = clk_register(clkp); + if (ret == 0) + ret = sh_clk_init_parent(clkp); + } + + return ret; +} + /* * div6 support */ @@ -223,82 +289,16 @@ static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .set_parent = sh_clk_div6_set_parent, }; -static int __init sh_clk_init_parent(struct clk *clk) -{ - u32 val; - - if (clk->parent) - return 0; - - if (!clk->parent_table || !clk->parent_num) - return 0; - - if (!clk->src_width) { - pr_err("sh_clk_init_parent: cannot select parent clock\n"); - return -EINVAL; - } - - val = (sh_clk_read(clk) >> clk->src_shift); - val &= (1 << clk->src_width) - 1; - - if (val >= clk->parent_num) { - pr_err("sh_clk_init_parent: parent table size failed\n"); - return -EINVAL; - } - - clk_reparent(clk, clk->parent_table[val]); - if (!clk->parent) { - pr_err("sh_clk_init_parent: unable to set parent"); - return -EINVAL; - } - - return 0; -} - -static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, - struct sh_clk_ops *ops) -{ - struct clk *clkp; - void *freq_table; - struct clk_div_table *table = &sh_clk_div6_table; - int nr_divs = table->div_mult_table->nr_divisors; - int freq_table_size = sizeof(struct cpufreq_frequency_table); - int ret = 0; - int k; - - freq_table_size *= (nr_divs + 1); - freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); - if (!freq_table) { - pr_err("sh_clk_div6_register: unable to alloc memory\n"); - return -ENOMEM; - } - - for (k = 0; !ret && (k < nr); k++) { - clkp = clks + k; - - clkp->ops = ops; - clkp->priv = table; - clkp->freq_table = freq_table + (k * freq_table_size); - clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; - ret = clk_register(clkp); - if (ret < 0) - break; - - ret = sh_clk_init_parent(clkp); - } - - return ret; -} - int __init sh_clk_div6_register(struct clk *clks, int nr) { - return sh_clk_div6_register_ops(clks, nr, &sh_clk_div_enable_clk_ops); + return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, + &sh_clk_div_enable_clk_ops); } int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) { - return sh_clk_div6_register_ops(clks, nr, - &sh_clk_div6_reparent_clk_ops); + return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, + &sh_clk_div6_reparent_clk_ops); } /* @@ -342,54 +342,22 @@ static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .set_parent = sh_clk_div4_set_parent, }; -static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, - struct clk_div4_table *table, struct sh_clk_ops *ops) -{ - struct clk *clkp; - void *freq_table; - int nr_divs = table->div_mult_table->nr_divisors; - int freq_table_size = sizeof(struct cpufreq_frequency_table); - int ret = 0; - int k; - - freq_table_size *= (nr_divs + 1); - freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); - if (!freq_table) { - pr_err("sh_clk_div4_register: unable to alloc memory\n"); - return -ENOMEM; - } - - for (k = 0; !ret && (k < nr); k++) { - clkp = clks + k; - - clkp->ops = ops; - clkp->priv = table; - - clkp->freq_table = freq_table + (k * freq_table_size); - clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; - - ret = clk_register(clkp); - } - - return ret; -} - int __init sh_clk_div4_register(struct clk *clks, int nr, struct clk_div4_table *table) { - return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div_clk_ops); + return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div_clk_ops); } int __init sh_clk_div4_enable_register(struct clk *clks, int nr, struct clk_div4_table *table) { - return sh_clk_div4_register_ops(clks, nr, table, - &sh_clk_div_enable_clk_ops); + return sh_clk_div_register_ops(clks, nr, table, + &sh_clk_div_enable_clk_ops); } int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, struct clk_div4_table *table) { - return sh_clk_div4_register_ops(clks, nr, table, - &sh_clk_div4_reparent_clk_ops); + return sh_clk_div_register_ops(clks, nr, table, + &sh_clk_div4_reparent_clk_ops); } -- cgit v1.2.3-59-g8ed1b From 123df01e8e046d6065089e1bff29aa3fc48d4420 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 19:24:18 +0900 Subject: sh: intc: Allocate subgroup virq backing desc directly. This switches to using irq_alloc_desc() directly for subgroup IRQs. We still need to call activate_irq() on these in order to make them requestable, at least up until these get moved in to their own irq domain.. Signed-off-by: Paul Mundt --- drivers/sh/intc/virq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c index 93cec21e788b..f30ac9354ff2 100644 --- a/drivers/sh/intc/virq.c +++ b/drivers/sh/intc/virq.c @@ -219,12 +219,14 @@ restart: if (radix_tree_deref_retry(entry)) goto restart; - irq = create_irq(); + irq = irq_alloc_desc(numa_node_id()); if (unlikely(irq < 0)) { pr_err("no more free IRQs, bailing..\n"); break; } + activate_irq(irq); + pr_info("Setting up a chained VIRQ from %d -> %d\n", irq, entry->pirq); -- cgit v1.2.3-59-g8ed1b From db218b3d55627ef515d3b32888b269ed319c4c8d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 14 Jun 2012 14:05:24 +0900 Subject: sh64: Use generic unaligned access control/counters. This switches over from the special-casing that sh64 had and to the model that's being employed for sh32. Signed-off-by: Paul Mundt --- arch/sh/kernel/traps_64.c | 150 ++++++++++++---------------------------------- 1 file changed, 37 insertions(+), 113 deletions(-) diff --git a/arch/sh/kernel/traps_64.c b/arch/sh/kernel/traps_64.c index ba95d63e623d..488c1cd557f1 100644 --- a/arch/sh/kernel/traps_64.c +++ b/arch/sh/kernel/traps_64.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -264,20 +264,20 @@ static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_na die_if_no_fixup(str, regs, error_code); } -static int read_opcode(unsigned long long pc, unsigned long *result_opcode, int from_user_mode) +static int read_opcode(reg_size_t pc, insn_size_t *result_opcode, int from_user_mode) { int get_user_error; unsigned long aligned_pc; - unsigned long opcode; + insn_size_t opcode; if ((pc & 3) == 1) { /* SHmedia */ aligned_pc = pc & ~3; if (from_user_mode) { - if (!access_ok(VERIFY_READ, aligned_pc, sizeof(unsigned long))) { + if (!access_ok(VERIFY_READ, aligned_pc, sizeof(insn_size_t))) { get_user_error = -EFAULT; } else { - get_user_error = __get_user(opcode, (unsigned long *)aligned_pc); + get_user_error = __get_user(opcode, (insn_size_t *)aligned_pc); *result_opcode = opcode; } return get_user_error; @@ -285,7 +285,7 @@ static int read_opcode(unsigned long long pc, unsigned long *result_opcode, int /* If the fault was in the kernel, we can either read * this directly, or if not, we fault. */ - *result_opcode = *(unsigned long *) aligned_pc; + *result_opcode = *(insn_size_t *)aligned_pc; return 0; } } else if ((pc & 1) == 0) { @@ -311,17 +311,23 @@ static int address_is_sign_extended(__u64 a) #endif } +/* return -1 for fault, 0 for OK */ static int generate_and_check_address(struct pt_regs *regs, - __u32 opcode, + insn_size_t opcode, int displacement_not_indexed, int width_shift, __u64 *address) { - /* return -1 for fault, 0 for OK */ - __u64 base_address, addr; int basereg; + switch (1 << width_shift) { + case 1: inc_unaligned_byte_access(); break; + case 2: inc_unaligned_word_access(); break; + case 4: inc_unaligned_dword_access(); break; + case 8: inc_unaligned_multi_access(); break; + } + basereg = (opcode >> 20) & 0x3f; base_address = regs->regs[basereg]; if (displacement_not_indexed) { @@ -338,28 +344,28 @@ static int generate_and_check_address(struct pt_regs *regs, } /* Check sign extended */ - if (!address_is_sign_extended(addr)) { + if (!address_is_sign_extended(addr)) return -1; - } /* Check accessible. For misaligned access in the kernel, assume the address is always accessible (and if not, just fault when the load/store gets done.) */ if (user_mode(regs)) { - if (addr >= TASK_SIZE) { + inc_unaligned_user_access(); + + if (addr >= TASK_SIZE) return -1; - } - /* Do access_ok check later - it depends on whether it's a load or a store. */ - } + } else + inc_unaligned_kernel_access(); *address = addr; + + perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, addr); + unaligned_fixups_notify(current, opcode, regs); + return 0; } -static int user_mode_unaligned_fixup_count = 10; -static int user_mode_unaligned_fixup_enable = 1; -static int kernel_mode_unaligned_fixup_count = 32; - static void misaligned_kernel_word_load(__u64 address, int do_sign_extend, __u64 *result) { unsigned short x; @@ -389,7 +395,7 @@ static void misaligned_kernel_word_store(__u64 address, __u64 value) } static int misaligned_load(struct pt_regs *regs, - __u32 opcode, + insn_size_t opcode, int displacement_not_indexed, int width_shift, int do_sign_extend) @@ -401,11 +407,8 @@ static int misaligned_load(struct pt_regs *regs, error = generate_and_check_address(regs, opcode, displacement_not_indexed, width_shift, &address); - if (error < 0) { + if (error < 0) return error; - } - - perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address); destreg = (opcode >> 4) & 0x3f; if (user_mode(regs)) { @@ -464,11 +467,10 @@ static int misaligned_load(struct pt_regs *regs, } return 0; - } static int misaligned_store(struct pt_regs *regs, - __u32 opcode, + insn_size_t opcode, int displacement_not_indexed, int width_shift) { @@ -479,11 +481,8 @@ static int misaligned_store(struct pt_regs *regs, error = generate_and_check_address(regs, opcode, displacement_not_indexed, width_shift, &address); - if (error < 0) { + if (error < 0) return error; - } - - perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address); srcreg = (opcode >> 4) & 0x3f; if (user_mode(regs)) { @@ -537,13 +536,12 @@ static int misaligned_store(struct pt_regs *regs, } return 0; - } /* Never need to fix up misaligned FPU accesses within the kernel since that's a real error. */ static int misaligned_fpu_load(struct pt_regs *regs, - __u32 opcode, + insn_size_t opcode, int displacement_not_indexed, int width_shift, int do_paired_load) @@ -555,11 +553,8 @@ static int misaligned_fpu_load(struct pt_regs *regs, error = generate_and_check_address(regs, opcode, displacement_not_indexed, width_shift, &address); - if (error < 0) { + if (error < 0) return error; - } - - perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, address); destreg = (opcode >> 4) & 0x3f; if (user_mode(regs)) { @@ -615,12 +610,10 @@ static int misaligned_fpu_load(struct pt_regs *regs, die ("Misaligned FPU load inside kernel", regs, 0); return -1; } - - } static int misaligned_fpu_store(struct pt_regs *regs, - __u32 opcode, + insn_size_t opcode, int displacement_not_indexed, int width_shift, int do_paired_load) @@ -632,11 +625,8 @@ static int misaligned_fpu_store(struct pt_regs *regs, error = generate_and_check_address(regs, opcode, displacement_not_indexed, width_shift, &address); - if (error < 0) { + if (error < 0) return error; - } - - perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, address); srcreg = (opcode >> 4) & 0x3f; if (user_mode(regs)) { @@ -697,11 +687,13 @@ static int misaligned_fpu_store(struct pt_regs *regs, static int misaligned_fixup(struct pt_regs *regs) { - unsigned long opcode; + insn_size_t opcode; int error; int major, minor; + unsigned int user_action; - if (!user_mode_unaligned_fixup_enable) + user_action = unaligned_user_action(); + if (!(user_action & UM_FIXUP)) return -1; error = read_opcode(regs->pc, &opcode, user_mode(regs)); @@ -711,23 +703,6 @@ static int misaligned_fixup(struct pt_regs *regs) major = (opcode >> 26) & 0x3f; minor = (opcode >> 16) & 0xf; - if (user_mode(regs) && (user_mode_unaligned_fixup_count > 0)) { - --user_mode_unaligned_fixup_count; - /* Only do 'count' worth of these reports, to remove a potential DoS against syslog */ - printk("Fixing up unaligned userspace access in \"%s\" pid=%d pc=0x%08x ins=0x%08lx\n", - current->comm, task_pid_nr(current), (__u32)regs->pc, opcode); - } else if (!user_mode(regs) && (kernel_mode_unaligned_fixup_count > 0)) { - --kernel_mode_unaligned_fixup_count; - if (in_interrupt()) { - printk("Fixing up unaligned kernelspace access in interrupt pc=0x%08x ins=0x%08lx\n", - (__u32)regs->pc, opcode); - } else { - printk("Fixing up unaligned kernelspace access in \"%s\" pid=%d pc=0x%08x ins=0x%08lx\n", - current->comm, task_pid_nr(current), (__u32)regs->pc, opcode); - } - } - - switch (major) { case (0x84>>2): /* LD.W */ error = misaligned_load(regs, opcode, 1, 1, 1); @@ -855,57 +830,6 @@ static int misaligned_fixup(struct pt_regs *regs) } -static ctl_table unaligned_table[] = { - { - .procname = "kernel_reports", - .data = &kernel_mode_unaligned_fixup_count, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, - { - .procname = "user_reports", - .data = &user_mode_unaligned_fixup_count, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, - { - .procname = "user_enable", - .data = &user_mode_unaligned_fixup_enable, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec}, - {} -}; - -static ctl_table unaligned_root[] = { - { - .procname = "unaligned_fixup", - .mode = 0555, - .child = unaligned_table - }, - {} -}; - -static ctl_table sh64_root[] = { - { - .procname = "sh64", - .mode = 0555, - .child = unaligned_root - }, - {} -}; -static struct ctl_table_header *sysctl_header; -static int __init init_sysctl(void) -{ - sysctl_header = register_sysctl_table(sh64_root); - return 0; -} - -__initcall(init_sysctl); - - asmlinkage void do_debug_interrupt(unsigned long code, struct pt_regs *regs) { u64 peek_real_address_q(u64 addr); -- cgit v1.2.3-59-g8ed1b From 37c9ee0161332291c8d13bc40084d24c744ed842 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 14 Jun 2012 14:12:18 +0900 Subject: sh64: Kill off old exception debugging helpers. There's not much here that we can't get at through alternate means (aside from the TLB contents, but that doesn't belong here anyways). Most of this information is already provided by the sh32 routines, which we'll consolidate on next. Signed-off-by: Paul Mundt --- arch/sh/kernel/traps_64.c | 12 --- arch/sh/lib64/Makefile | 2 +- arch/sh/lib64/dbg.c | 248 ---------------------------------------------- 3 files changed, 1 insertion(+), 261 deletions(-) delete mode 100644 arch/sh/lib64/dbg.c diff --git a/arch/sh/kernel/traps_64.c b/arch/sh/kernel/traps_64.c index 488c1cd557f1..c902c29400a3 100644 --- a/arch/sh/kernel/traps_64.c +++ b/arch/sh/kernel/traps_64.c @@ -32,15 +32,6 @@ #include #include -#undef DEBUG_EXCEPTION -#ifdef DEBUG_EXCEPTION -/* implemented in ../lib/dbg.c */ -extern void show_excp_regs(char *fname, int trapnr, int signr, - struct pt_regs *regs); -#else -#define show_excp_regs(a, b, c, d) -#endif - static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk); @@ -239,7 +230,6 @@ DO_ERROR(12, SIGILL, "reserved instruction", reserved_inst, current) /* Called with interrupts disabled */ asmlinkage void do_exception_error(unsigned long ex, struct pt_regs *regs) { - show_excp_regs(__func__, -1, -1, regs); die_if_kernel("exception", regs, ex); } @@ -256,8 +246,6 @@ int do_unknown_trapa(unsigned long scId, struct pt_regs *regs) static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk) { - show_excp_regs(fn_name, trapnr, signr, regs); - if (user_mode(regs)) force_sig(signr, tsk); diff --git a/arch/sh/lib64/Makefile b/arch/sh/lib64/Makefile index 1fee75aa1f98..69779ff741df 100644 --- a/arch/sh/lib64/Makefile +++ b/arch/sh/lib64/Makefile @@ -10,7 +10,7 @@ # # Panic should really be compiled as PIC -lib-y := udelay.o dbg.o panic.o memcpy.o memset.o \ +lib-y := udelay.o panic.o memcpy.o memset.o \ copy_user_memcpy.o copy_page.o strcpy.o strlen.o # Extracted from libgcc diff --git a/arch/sh/lib64/dbg.c b/arch/sh/lib64/dbg.c deleted file mode 100644 index 6152a6a6d9c6..000000000000 --- a/arch/sh/lib64/dbg.c +++ /dev/null @@ -1,248 +0,0 @@ -/*-------------------------------------------------------------------------- --- --- Identity : Linux50 Debug Funcions --- --- File : arch/sh/lib64/dbg.c --- --- Copyright 2000, 2001 STMicroelectronics Limited. --- Copyright 2004 Richard Curnow (evt_debug etc) --- ---------------------------------------------------------------------------*/ -#include -#include -#include -#include -#include -#include - -typedef u64 regType_t; - -static regType_t getConfigReg(u64 id) -{ - register u64 reg __asm__("r2"); - asm volatile ("getcfg %1, 0, %0":"=r" (reg):"r"(id)); - return (reg); -} - -/* ======================================================================= */ - -static char *szTab[] = { "4k", "64k", "1M", "512M" }; -static char *protTab[] = { "----", - "---R", - "--X-", - "--XR", - "-W--", - "-W-R", - "-WX-", - "-WXR", - "U---", - "U--R", - "U-X-", - "U-XR", - "UW--", - "UW-R", - "UWX-", - "UWXR" -}; -#define ITLB_BASE 0x00000000 -#define DTLB_BASE 0x00800000 -#define MAX_TLBs 64 -/* PTE High */ -#define GET_VALID(pte) ((pte) & 0x1) -#define GET_SHARED(pte) ((pte) & 0x2) -#define GET_ASID(pte) ((pte >> 2) & 0x0ff) -#define GET_EPN(pte) ((pte) & 0xfffff000) - -/* PTE Low */ -#define GET_CBEHAVIOR(pte) ((pte) & 0x3) -#define GET_PAGE_SIZE(pte) szTab[((pte >> 3) & 0x3)] -#define GET_PROTECTION(pte) protTab[((pte >> 6) & 0xf)] -#define GET_PPN(pte) ((pte) & 0xfffff000) - -#define PAGE_1K_MASK 0x00000000 -#define PAGE_4K_MASK 0x00000010 -#define PAGE_64K_MASK 0x00000080 -#define MMU_PAGESIZE_MASK (PAGE_64K_MASK | PAGE_4K_MASK) -#define PAGE_1MB_MASK MMU_PAGESIZE_MASK -#define PAGE_1K (1024) -#define PAGE_4K (1024 * 4) -#define PAGE_64K (1024 * 64) -#define PAGE_1MB (1024 * 1024) - -#define HOW_TO_READ_TLB_CONTENT \ - "[ ID] PPN EPN ASID Share CB P.Size PROT.\n" - -void print_single_tlb(unsigned long tlb, int single_print) -{ - regType_t pteH; - regType_t pteL; - unsigned int valid, shared, asid, epn, cb, ppn; - char *pSize; - char *pProt; - - /* - ** in case of single print is true, this implies: - ** 1) print the TLB in any case also if NOT VALID - ** 2) print out the header - */ - - pteH = getConfigReg(tlb); - valid = GET_VALID(pteH); - if (single_print) - printk(HOW_TO_READ_TLB_CONTENT); - else if (!valid) - return; - - pteL = getConfigReg(tlb + 1); - - shared = GET_SHARED(pteH); - asid = GET_ASID(pteH); - epn = GET_EPN(pteH); - cb = GET_CBEHAVIOR(pteL); - pSize = GET_PAGE_SIZE(pteL); - pProt = GET_PROTECTION(pteL); - ppn = GET_PPN(pteL); - printk("[%c%2ld] 0x%08x 0x%08x %03d %02x %02x %4s %s\n", - ((valid) ? ' ' : 'u'), ((tlb & 0x0ffff) / TLB_STEP), - ppn, epn, asid, shared, cb, pSize, pProt); -} - -void print_dtlb(void) -{ - int count; - unsigned long tlb; - - printk(" ================= SH-5 D-TLBs Status ===================\n"); - printk(HOW_TO_READ_TLB_CONTENT); - tlb = DTLB_BASE; - for (count = 0; count < MAX_TLBs; count++, tlb += TLB_STEP) - print_single_tlb(tlb, 0); - printk - (" =============================================================\n"); -} - -void print_itlb(void) -{ - int count; - unsigned long tlb; - - printk(" ================= SH-5 I-TLBs Status ===================\n"); - printk(HOW_TO_READ_TLB_CONTENT); - tlb = ITLB_BASE; - for (count = 0; count < MAX_TLBs; count++, tlb += TLB_STEP) - print_single_tlb(tlb, 0); - printk - (" =============================================================\n"); -} - -void show_excp_regs(char *from, int trapnr, int signr, struct pt_regs *regs) -{ - - unsigned long long ah, al, bh, bl, ch, cl; - - printk("\n"); - printk("EXCEPTION - %s: task %d; Linux trap # %d; signal = %d\n", - ((from) ? from : "???"), current->pid, trapnr, signr); - - asm volatile ("getcon " __EXPEVT ", %0":"=r"(ah)); - asm volatile ("getcon " __EXPEVT ", %0":"=r"(al)); - ah = (ah) >> 32; - al = (al) & 0xffffffff; - asm volatile ("getcon " __KCR1 ", %0":"=r"(bh)); - asm volatile ("getcon " __KCR1 ", %0":"=r"(bl)); - bh = (bh) >> 32; - bl = (bl) & 0xffffffff; - asm volatile ("getcon " __INTEVT ", %0":"=r"(ch)); - asm volatile ("getcon " __INTEVT ", %0":"=r"(cl)); - ch = (ch) >> 32; - cl = (cl) & 0xffffffff; - printk("EXPE: %08Lx%08Lx KCR1: %08Lx%08Lx INTE: %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - - asm volatile ("getcon " __PEXPEVT ", %0":"=r"(ah)); - asm volatile ("getcon " __PEXPEVT ", %0":"=r"(al)); - ah = (ah) >> 32; - al = (al) & 0xffffffff; - asm volatile ("getcon " __PSPC ", %0":"=r"(bh)); - asm volatile ("getcon " __PSPC ", %0":"=r"(bl)); - bh = (bh) >> 32; - bl = (bl) & 0xffffffff; - asm volatile ("getcon " __PSSR ", %0":"=r"(ch)); - asm volatile ("getcon " __PSSR ", %0":"=r"(cl)); - ch = (ch) >> 32; - cl = (cl) & 0xffffffff; - printk("PEXP: %08Lx%08Lx PSPC: %08Lx%08Lx PSSR: %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - - ah = (regs->pc) >> 32; - al = (regs->pc) & 0xffffffff; - bh = (regs->regs[18]) >> 32; - bl = (regs->regs[18]) & 0xffffffff; - ch = (regs->regs[15]) >> 32; - cl = (regs->regs[15]) & 0xffffffff; - printk("PC : %08Lx%08Lx LINK: %08Lx%08Lx SP : %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - - ah = (regs->sr) >> 32; - al = (regs->sr) & 0xffffffff; - asm volatile ("getcon " __TEA ", %0":"=r"(bh)); - asm volatile ("getcon " __TEA ", %0":"=r"(bl)); - bh = (bh) >> 32; - bl = (bl) & 0xffffffff; - asm volatile ("getcon " __KCR0 ", %0":"=r"(ch)); - asm volatile ("getcon " __KCR0 ", %0":"=r"(cl)); - ch = (ch) >> 32; - cl = (cl) & 0xffffffff; - printk("SR : %08Lx%08Lx TEA : %08Lx%08Lx KCR0: %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - - ah = (regs->regs[0]) >> 32; - al = (regs->regs[0]) & 0xffffffff; - bh = (regs->regs[1]) >> 32; - bl = (regs->regs[1]) & 0xffffffff; - ch = (regs->regs[2]) >> 32; - cl = (regs->regs[2]) & 0xffffffff; - printk("R0 : %08Lx%08Lx R1 : %08Lx%08Lx R2 : %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - - ah = (regs->regs[3]) >> 32; - al = (regs->regs[3]) & 0xffffffff; - bh = (regs->regs[4]) >> 32; - bl = (regs->regs[4]) & 0xffffffff; - ch = (regs->regs[5]) >> 32; - cl = (regs->regs[5]) & 0xffffffff; - printk("R3 : %08Lx%08Lx R4 : %08Lx%08Lx R5 : %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - - ah = (regs->regs[6]) >> 32; - al = (regs->regs[6]) & 0xffffffff; - bh = (regs->regs[7]) >> 32; - bl = (regs->regs[7]) & 0xffffffff; - ch = (regs->regs[8]) >> 32; - cl = (regs->regs[8]) & 0xffffffff; - printk("R6 : %08Lx%08Lx R7 : %08Lx%08Lx R8 : %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - - ah = (regs->regs[9]) >> 32; - al = (regs->regs[9]) & 0xffffffff; - bh = (regs->regs[10]) >> 32; - bl = (regs->regs[10]) & 0xffffffff; - ch = (regs->regs[11]) >> 32; - cl = (regs->regs[11]) & 0xffffffff; - printk("R9 : %08Lx%08Lx R10 : %08Lx%08Lx R11 : %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - printk("....\n"); - - ah = (regs->tregs[0]) >> 32; - al = (regs->tregs[0]) & 0xffffffff; - bh = (regs->tregs[1]) >> 32; - bl = (regs->tregs[1]) & 0xffffffff; - ch = (regs->tregs[2]) >> 32; - cl = (regs->tregs[2]) & 0xffffffff; - printk("T0 : %08Lx%08Lx T1 : %08Lx%08Lx T2 : %08Lx%08Lx\n", - ah, al, bh, bl, ch, cl); - printk("....\n"); - - print_dtlb(); - print_itlb(); -} -- cgit v1.2.3-59-g8ed1b From 5f857bce21cfd0531dc7d4daac74d976caf6166b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 14 Jun 2012 14:18:51 +0900 Subject: sh: Consolidate die definitions for trap handlers. This kills off the _64 versions and consolidates on the more robust _32 versions instead. Signed-off-by: Paul Mundt --- arch/sh/include/asm/bug.h | 4 +++ arch/sh/kernel/traps.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ arch/sh/kernel/traps_32.c | 71 ----------------------------------------------- arch/sh/kernel/traps_64.c | 31 --------------------- 4 files changed, 75 insertions(+), 102 deletions(-) diff --git a/arch/sh/include/asm/bug.h b/arch/sh/include/asm/bug.h index 2b87d86bfc41..dcf278075429 100644 --- a/arch/sh/include/asm/bug.h +++ b/arch/sh/include/asm/bug.h @@ -110,6 +110,10 @@ do { \ #include struct pt_regs; + +/* arch/sh/kernel/traps.c */ extern void die(const char *str, struct pt_regs *regs, long err) __attribute__ ((noreturn)); +extern void die_if_kernel(const char *str, struct pt_regs *regs, long err); +extern void die_if_no_fixup(const char *str, struct pt_regs *regs, long err); #endif /* __ASM_SH_BUG_H */ diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index a87e58a9e38f..72246bc06884 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -6,9 +6,80 @@ #include #include #include +#include +#include +#include #include #include +static DEFINE_SPINLOCK(die_lock); + +void die(const char *str, struct pt_regs *regs, long err) +{ + static int die_counter; + + oops_enter(); + + spin_lock_irq(&die_lock); + console_verbose(); + bust_spinlocks(1); + + printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); + print_modules(); + show_regs(regs); + + printk("Process: %s (pid: %d, stack limit = %p)\n", current->comm, + task_pid_nr(current), task_stack_page(current) + 1); + + if (!user_mode(regs) || in_interrupt()) + dump_mem("Stack: ", regs->regs[15], THREAD_SIZE + + (unsigned long)task_stack_page(current)); + + notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV); + + bust_spinlocks(0); + add_taint(TAINT_DIE); + spin_unlock_irq(&die_lock); + oops_exit(); + + if (kexec_should_crash(current)) + crash_kexec(regs); + + if (in_interrupt()) + panic("Fatal exception in interrupt"); + + if (panic_on_oops) + panic("Fatal exception"); + + do_exit(SIGSEGV); +} + +void die_if_kernel(const char *str, struct pt_regs *regs, long err) +{ + if (!user_mode(regs)) + die(str, regs, err); +} + +/* + * try and fix up kernelspace address errors + * - userspace errors just cause EFAULT to be returned, resulting in SEGV + * - kernel/userspace interfaces cause a jump to an appropriate handler + * - other kernel errors are bad + */ +void die_if_no_fixup(const char *str, struct pt_regs *regs, long err) +{ + if (!user_mode(regs)) { + const struct exception_table_entry *fixup; + fixup = search_exception_tables(regs->pc); + if (fixup) { + regs->pc = fixup->fixup; + return; + } + + die(str, regs, err); + } +} + #ifdef CONFIG_GENERIC_BUG static void handle_BUG(struct pt_regs *regs) { diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index b8f5a51841ec..5f513a64dedf 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -16,13 +16,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include @@ -48,75 +46,6 @@ #define TRAP_ILLEGAL_SLOT_INST 13 #endif -static DEFINE_SPINLOCK(die_lock); - -void die(const char * str, struct pt_regs * regs, long err) -{ - static int die_counter; - - oops_enter(); - - spin_lock_irq(&die_lock); - console_verbose(); - bust_spinlocks(1); - - printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); - print_modules(); - show_regs(regs); - - printk("Process: %s (pid: %d, stack limit = %p)\n", current->comm, - task_pid_nr(current), task_stack_page(current) + 1); - - if (!user_mode(regs) || in_interrupt()) - dump_mem("Stack: ", regs->regs[15], THREAD_SIZE + - (unsigned long)task_stack_page(current)); - - notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV); - - bust_spinlocks(0); - add_taint(TAINT_DIE); - spin_unlock_irq(&die_lock); - oops_exit(); - - if (kexec_should_crash(current)) - crash_kexec(regs); - - if (in_interrupt()) - panic("Fatal exception in interrupt"); - - if (panic_on_oops) - panic("Fatal exception"); - - do_exit(SIGSEGV); -} - -static inline void die_if_kernel(const char *str, struct pt_regs *regs, - long err) -{ - if (!user_mode(regs)) - die(str, regs, err); -} - -/* - * try and fix up kernelspace address errors - * - userspace errors just cause EFAULT to be returned, resulting in SEGV - * - kernel/userspace interfaces cause a jump to an appropriate handler - * - other kernel errors are bad - */ -static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) -{ - if (!user_mode(regs)) { - const struct exception_table_entry *fixup; - fixup = search_exception_tables(regs->pc); - if (fixup) { - regs->pc = fixup->fixup; - return; - } - - die(str, regs, err); - } -} - static inline void sign_extend(unsigned int count, unsigned char *dst) { #ifdef __LITTLE_ENDIAN__ diff --git a/arch/sh/kernel/traps_64.c b/arch/sh/kernel/traps_64.c index c902c29400a3..75bef61892d2 100644 --- a/arch/sh/kernel/traps_64.c +++ b/arch/sh/kernel/traps_64.c @@ -41,37 +41,6 @@ asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \ do_unhandled_exception(trapnr, signr, str, __stringify(name), error_code, regs, current); \ } -static DEFINE_SPINLOCK(die_lock); - -void die(const char * str, struct pt_regs * regs, long err) -{ - console_verbose(); - spin_lock_irq(&die_lock); - printk("%s: %lx\n", str, (err & 0xffffff)); - show_regs(regs); - spin_unlock_irq(&die_lock); - do_exit(SIGSEGV); -} - -static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err) -{ - if (!user_mode(regs)) - die(str, regs, err); -} - -static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) -{ - if (!user_mode(regs)) { - const struct exception_table_entry *fixup; - fixup = search_exception_tables(regs->pc); - if (fixup) { - regs->pc = fixup->fixup; - return; - } - die(str, regs, err); - } -} - DO_ERROR(13, SIGILL, "illegal slot instruction", illegal_slot_inst, current) DO_ERROR(87, SIGSEGV, "address error (exec)", address_error_exec, current) -- cgit v1.2.3-59-g8ed1b From 0375a73c6f1be69d5a9b439a7e95b92e54b3c09b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 14 Jun 2012 14:46:36 +0900 Subject: sh64: Attempt to make reserved insn trap handler resemble C. This has been long overdue. No functional changes. Signed-off-by: Paul Mundt --- arch/sh/kernel/traps_64.c | 386 +++++++++++++++++++++++----------------------- 1 file changed, 195 insertions(+), 191 deletions(-) diff --git a/arch/sh/kernel/traps_64.c b/arch/sh/kernel/traps_64.c index 75bef61892d2..f87d20da1791 100644 --- a/arch/sh/kernel/traps_64.c +++ b/arch/sh/kernel/traps_64.c @@ -32,195 +32,6 @@ #include #include -static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, - unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk); - -#define DO_ERROR(trapnr, signr, str, name, tsk) \ -asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \ -{ \ - do_unhandled_exception(trapnr, signr, str, __stringify(name), error_code, regs, current); \ -} - -DO_ERROR(13, SIGILL, "illegal slot instruction", illegal_slot_inst, current) -DO_ERROR(87, SIGSEGV, "address error (exec)", address_error_exec, current) - - -/* Implement misaligned load/store handling for kernel (and optionally for user - mode too). Limitation : only SHmedia mode code is handled - there is no - handling at all for misaligned accesses occurring in SHcompact code yet. */ - -static int misaligned_fixup(struct pt_regs *regs); - -asmlinkage void do_address_error_load(unsigned long error_code, struct pt_regs *regs) -{ - if (misaligned_fixup(regs) < 0) { - do_unhandled_exception(7, SIGSEGV, "address error(load)", - "do_address_error_load", - error_code, regs, current); - } - return; -} - -asmlinkage void do_address_error_store(unsigned long error_code, struct pt_regs *regs) -{ - if (misaligned_fixup(regs) < 0) { - do_unhandled_exception(8, SIGSEGV, "address error(store)", - "do_address_error_store", - error_code, regs, current); - } - return; -} - -#if defined(CONFIG_SH64_ID2815_WORKAROUND) - -#define OPCODE_INVALID 0 -#define OPCODE_USER_VALID 1 -#define OPCODE_PRIV_VALID 2 - -/* getcon/putcon - requires checking which control register is referenced. */ -#define OPCODE_CTRL_REG 3 - -/* Table of valid opcodes for SHmedia mode. - Form a 10-bit value by concatenating the major/minor opcodes i.e. - opcode[31:26,20:16]. The 6 MSBs of this value index into the following - array. The 4 LSBs select the bit-pair in the entry (bits 1:0 correspond to - LSBs==4'b0000 etc). */ -static unsigned long shmedia_opcode_table[64] = { - 0x55554044,0x54445055,0x15141514,0x14541414,0x00000000,0x10001000,0x01110055,0x04050015, - 0x00000444,0xc0000000,0x44545515,0x40405555,0x55550015,0x10005555,0x55555505,0x04050000, - 0x00000555,0x00000404,0x00040445,0x15151414,0x00000000,0x00000000,0x00000000,0x00000000, - 0x00000055,0x40404444,0x00000404,0xc0009495,0x00000000,0x00000000,0x00000000,0x00000000, - 0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, - 0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, - 0x80005050,0x04005055,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, - 0x81055554,0x00000404,0x55555555,0x55555555,0x00000000,0x00000000,0x00000000,0x00000000 -}; - -void do_reserved_inst(unsigned long error_code, struct pt_regs *regs) -{ - /* Workaround SH5-101 cut2 silicon defect #2815 : - in some situations, inter-mode branches from SHcompact -> SHmedia - which should take ITLBMISS or EXECPROT exceptions at the target - falsely take RESINST at the target instead. */ - - unsigned long opcode = 0x6ff4fff0; /* guaranteed reserved opcode */ - unsigned long pc, aligned_pc; - int get_user_error; - int trapnr = 12; - int signr = SIGILL; - char *exception_name = "reserved_instruction"; - - pc = regs->pc; - if ((pc & 3) == 1) { - /* SHmedia : check for defect. This requires executable vmas - to be readable too. */ - aligned_pc = pc & ~3; - if (!access_ok(VERIFY_READ, aligned_pc, sizeof(unsigned long))) { - get_user_error = -EFAULT; - } else { - get_user_error = __get_user(opcode, (unsigned long *)aligned_pc); - } - if (get_user_error >= 0) { - unsigned long index, shift; - unsigned long major, minor, combined; - unsigned long reserved_field; - reserved_field = opcode & 0xf; /* These bits are currently reserved as zero in all valid opcodes */ - major = (opcode >> 26) & 0x3f; - minor = (opcode >> 16) & 0xf; - combined = (major << 4) | minor; - index = major; - shift = minor << 1; - if (reserved_field == 0) { - int opcode_state = (shmedia_opcode_table[index] >> shift) & 0x3; - switch (opcode_state) { - case OPCODE_INVALID: - /* Trap. */ - break; - case OPCODE_USER_VALID: - /* Restart the instruction : the branch to the instruction will now be from an RTE - not from SHcompact so the silicon defect won't be triggered. */ - return; - case OPCODE_PRIV_VALID: - if (!user_mode(regs)) { - /* Should only ever get here if a module has - SHcompact code inside it. If so, the same fix up is needed. */ - return; /* same reason */ - } - /* Otherwise, user mode trying to execute a privileged instruction - - fall through to trap. */ - break; - case OPCODE_CTRL_REG: - /* If in privileged mode, return as above. */ - if (!user_mode(regs)) return; - /* In user mode ... */ - if (combined == 0x9f) { /* GETCON */ - unsigned long regno = (opcode >> 20) & 0x3f; - if (regno >= 62) { - return; - } - /* Otherwise, reserved or privileged control register, => trap */ - } else if (combined == 0x1bf) { /* PUTCON */ - unsigned long regno = (opcode >> 4) & 0x3f; - if (regno >= 62) { - return; - } - /* Otherwise, reserved or privileged control register, => trap */ - } else { - /* Trap */ - } - break; - default: - /* Fall through to trap. */ - break; - } - } - /* fall through to normal resinst processing */ - } else { - /* Error trying to read opcode. This typically means a - real fault, not a RESINST any more. So change the - codes. */ - trapnr = 87; - exception_name = "address error (exec)"; - signr = SIGSEGV; - } - } - - do_unhandled_exception(trapnr, signr, exception_name, "do_reserved_inst", error_code, regs, current); -} - -#else /* CONFIG_SH64_ID2815_WORKAROUND */ - -/* If the workaround isn't needed, this is just a straightforward reserved - instruction */ -DO_ERROR(12, SIGILL, "reserved instruction", reserved_inst, current) - -#endif /* CONFIG_SH64_ID2815_WORKAROUND */ - -/* Called with interrupts disabled */ -asmlinkage void do_exception_error(unsigned long ex, struct pt_regs *regs) -{ - die_if_kernel("exception", regs, ex); -} - -int do_unknown_trapa(unsigned long scId, struct pt_regs *regs) -{ - /* Syscall debug */ - printk("System call ID error: [0x1#args:8 #syscall:16 0x%lx]\n", scId); - - die_if_kernel("unknown trapa", regs, scId); - - return -ENOSYS; -} - -static void do_unhandled_exception(int trapnr, int signr, char *str, char *fn_name, - unsigned long error_code, struct pt_regs *regs, struct task_struct *tsk) -{ - if (user_mode(regs)) - force_sig(signr, tsk); - - die_if_no_fixup(str, regs, error_code); -} - static int read_opcode(reg_size_t pc, insn_size_t *result_opcode, int from_user_mode) { int get_user_error; @@ -784,7 +595,201 @@ static int misaligned_fixup(struct pt_regs *regs) regs->pc += 4; /* Skip the instruction that's just been emulated */ return 0; } +} + +static void do_unhandled_exception(int signr, char *str, unsigned long error, + struct pt_regs *regs) +{ + if (user_mode(regs)) + force_sig(signr, current); + + die_if_no_fixup(str, regs, error); +} + +#define DO_ERROR(signr, str, name) \ +asmlinkage void do_##name(unsigned long error_code, struct pt_regs *regs) \ +{ \ + do_unhandled_exception(signr, str, error_code, regs); \ +} + +DO_ERROR(SIGILL, "illegal slot instruction", illegal_slot_inst) +DO_ERROR(SIGSEGV, "address error (exec)", address_error_exec) + +#if defined(CONFIG_SH64_ID2815_WORKAROUND) + +#define OPCODE_INVALID 0 +#define OPCODE_USER_VALID 1 +#define OPCODE_PRIV_VALID 2 + +/* getcon/putcon - requires checking which control register is referenced. */ +#define OPCODE_CTRL_REG 3 + +/* Table of valid opcodes for SHmedia mode. + Form a 10-bit value by concatenating the major/minor opcodes i.e. + opcode[31:26,20:16]. The 6 MSBs of this value index into the following + array. The 4 LSBs select the bit-pair in the entry (bits 1:0 correspond to + LSBs==4'b0000 etc). */ +static unsigned long shmedia_opcode_table[64] = { + 0x55554044,0x54445055,0x15141514,0x14541414,0x00000000,0x10001000,0x01110055,0x04050015, + 0x00000444,0xc0000000,0x44545515,0x40405555,0x55550015,0x10005555,0x55555505,0x04050000, + 0x00000555,0x00000404,0x00040445,0x15151414,0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000055,0x40404444,0x00000404,0xc0009495,0x00000000,0x00000000,0x00000000,0x00000000, + 0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, + 0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, + 0x80005050,0x04005055,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555,0x55555555, + 0x81055554,0x00000404,0x55555555,0x55555555,0x00000000,0x00000000,0x00000000,0x00000000 +}; + +/* Workaround SH5-101 cut2 silicon defect #2815 : + in some situations, inter-mode branches from SHcompact -> SHmedia + which should take ITLBMISS or EXECPROT exceptions at the target + falsely take RESINST at the target instead. */ +void do_reserved_inst(unsigned long error_code, struct pt_regs *regs) +{ + insn_size_t opcode = 0x6ff4fff0; /* guaranteed reserved opcode */ + unsigned long pc, aligned_pc; + unsigned long index, shift; + unsigned long major, minor, combined; + unsigned long reserved_field; + int opcode_state; + int get_user_error; + int signr = SIGILL; + char *exception_name = "reserved_instruction"; + + pc = regs->pc; + + /* SHcompact is not handled */ + if (unlikely((pc & 3) == 0)) + goto out; + + /* SHmedia : check for defect. This requires executable vmas + to be readable too. */ + aligned_pc = pc & ~3; + if (!access_ok(VERIFY_READ, aligned_pc, sizeof(insn_size_t))) + get_user_error = -EFAULT; + else + get_user_error = __get_user(opcode, (insn_size_t *)aligned_pc); + + if (get_user_error < 0) { + /* + * Error trying to read opcode. This typically means a + * real fault, not a RESINST any more. So change the + * codes. + */ + exception_name = "address error (exec)"; + signr = SIGSEGV; + goto out; + } + + /* These bits are currently reserved as zero in all valid opcodes */ + reserved_field = opcode & 0xf; + if (unlikely(reserved_field)) + goto out; /* invalid opcode */ + + major = (opcode >> 26) & 0x3f; + minor = (opcode >> 16) & 0xf; + combined = (major << 4) | minor; + index = major; + shift = minor << 1; + opcode_state = (shmedia_opcode_table[index] >> shift) & 0x3; + switch (opcode_state) { + case OPCODE_INVALID: + /* Trap. */ + break; + case OPCODE_USER_VALID: + /* + * Restart the instruction: the branch to the instruction + * will now be from an RTE not from SHcompact so the + * silicon defect won't be triggered. + */ + return; + case OPCODE_PRIV_VALID: + if (!user_mode(regs)) { + /* + * Should only ever get here if a module has + * SHcompact code inside it. If so, the same fix + * up is needed. + */ + return; /* same reason */ + } + + /* + * Otherwise, user mode trying to execute a privileged + * instruction - fall through to trap. + */ + break; + case OPCODE_CTRL_REG: + /* If in privileged mode, return as above. */ + if (!user_mode(regs)) + return; + + /* In user mode ... */ + if (combined == 0x9f) { /* GETCON */ + unsigned long regno = (opcode >> 20) & 0x3f; + + if (regno >= 62) + return; + + /* reserved/privileged control register => trap */ + } else if (combined == 0x1bf) { /* PUTCON */ + unsigned long regno = (opcode >> 4) & 0x3f; + + if (regno >= 62) + return; + + /* reserved/privileged control register => trap */ + } + + break; + default: + /* Fall through to trap. */ + break; + } +out: + do_unhandled_exception(signr, exception_name, error_code, regs); +} + +#else /* CONFIG_SH64_ID2815_WORKAROUND */ + +/* If the workaround isn't needed, this is just a straightforward reserved + instruction */ +DO_ERROR(SIGILL, "reserved instruction", reserved_inst) + +#endif /* CONFIG_SH64_ID2815_WORKAROUND */ + +/* Called with interrupts disabled */ +asmlinkage void do_exception_error(unsigned long ex, struct pt_regs *regs) +{ + die_if_kernel("exception", regs, ex); +} + +asmlinkage int do_unknown_trapa(unsigned long scId, struct pt_regs *regs) +{ + /* Syscall debug */ + printk("System call ID error: [0x1#args:8 #syscall:16 0x%lx]\n", scId); + + die_if_kernel("unknown trapa", regs, scId); + + return -ENOSYS; +} + +/* Implement misaligned load/store handling for kernel (and optionally for user + mode too). Limitation : only SHmedia mode code is handled - there is no + handling at all for misaligned accesses occurring in SHcompact code yet. */ + +asmlinkage void do_address_error_load(unsigned long error_code, struct pt_regs *regs) +{ + if (misaligned_fixup(regs) < 0) + do_unhandled_exception(SIGSEGV, "address error(load)", + error_code, regs); +} + +asmlinkage void do_address_error_store(unsigned long error_code, struct pt_regs *regs) +{ + if (misaligned_fixup(regs) < 0) + do_unhandled_exception(SIGSEGV, "address error(store)", + error_code, regs); } asmlinkage void do_debug_interrupt(unsigned long code, struct pt_regs *regs) @@ -797,10 +802,9 @@ asmlinkage void do_debug_interrupt(unsigned long code, struct pt_regs *regs) of access we make to them - just go direct to their physical addresses. */ exp_cause = peek_real_address_q(DM_EXP_CAUSE_PHY); - if (exp_cause & ~4) { + if (exp_cause & ~4) printk("DM.EXP_CAUSE had unexpected bits set (=%08lx)\n", (unsigned long)(exp_cause & 0xffffffff)); - } show_state(); /* Clear all DEBUGINT causes */ poke_real_address_q(DM_EXP_CAUSE_PHY, 0x0); -- cgit v1.2.3-59-g8ed1b From 0412ddc82223ea2bb3a9db21355e5fe0862a97e5 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 14 Jun 2012 15:05:53 +0900 Subject: sh64: Fix up section mismatch warnings. WARNING: vmlinux.o(.cpuinit.text+0x280): Section mismatch in reference from the function cpu_probe() to the function .init.text:sh64_tlb_init() The function __cpuinit cpu_probe() references a function __init sh64_tlb_init(). If sh64_tlb_init is only used by cpu_probe then annotate sh64_tlb_init with a matching annotation. sh64_tlb_init() simply needs to be __cpuinit annotated, so fix that up. Signed-off-by: Paul Mundt --- arch/sh/mm/tlb-sh5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sh/mm/tlb-sh5.c b/arch/sh/mm/tlb-sh5.c index 3aea25dc431a..ff1c40a31cbc 100644 --- a/arch/sh/mm/tlb-sh5.c +++ b/arch/sh/mm/tlb-sh5.c @@ -17,7 +17,7 @@ /** * sh64_tlb_init - Perform initial setup for the DTLB and ITLB. */ -int __init sh64_tlb_init(void) +int __cpuinit sh64_tlb_init(void) { /* Assign some sane DTLB defaults */ cpu_data->dtlb.entries = 64; -- cgit v1.2.3-59-g8ed1b From b3c185a7614cd95ea9b68d89a8d1ee4227ee9018 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 20 Jun 2012 17:29:04 +0900 Subject: sh: pfc: Split out gpio chip support. This implements a bit of rework for the PFC code, making the core itself slightly more pluggable and moving out the gpio chip handling completely. The API is preserved in such a way that platforms that depend on it for early configuration are still able to do so, while making it possible to migrate to alternate interfaces going forward. This is the first step of chainsawing necessary to support the pinctrl API, with the eventual goal being able to decouple pin function state from the gpio API while retaining gpio chip tie-in for gpio pin functions only, relying on the pinctrl/pinmux API for non-gpio function demux. Signed-off-by: Paul Mundt --- drivers/sh/Makefile | 2 +- drivers/sh/pfc-gpio.c | 309 ++++++++++++++++++++++++++++++++++++++++ drivers/sh/pfc.c | 376 ++++++++++++++----------------------------------- include/linux/sh_pfc.h | 37 ++++- 4 files changed, 447 insertions(+), 277 deletions(-) create mode 100644 drivers/sh/pfc-gpio.c diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 7139ad2f2086..be5b2934f067 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -6,5 +6,5 @@ obj-y := intc/ obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_GENERIC_GPIO) += pfc.o +obj-$(CONFIG_GENERIC_GPIO) += pfc.o pfc-gpio.o obj-y += pm_runtime.o diff --git a/drivers/sh/pfc-gpio.c b/drivers/sh/pfc-gpio.c new file mode 100644 index 000000000000..d74e5a96024b --- /dev/null +++ b/drivers/sh/pfc-gpio.c @@ -0,0 +1,309 @@ +/* + * SuperH Pin Function Controller GPIO driver. + * + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +struct sh_pfc_chip { + struct sh_pfc *pfc; + struct gpio_chip gpio_chip; +}; + +static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct sh_pfc_chip, gpio_chip); +} + +static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) +{ + return gpio_to_pfc_chip(gc)->pfc; +} + +static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + if (!pfc) + goto err_out; + + spin_lock_irqsave(&pfc->lock, flags); + + if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_unlock; + + /* setup pin function here if no data is associated with pin */ + + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_unlock; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= pinmux_type; + + ret = 0; + err_unlock: + spin_unlock_irqrestore(&pfc->lock, flags); + err_out: + return ret; +} + +static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int pinmux_type; + + if (!pfc) + return; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&pfc->lock, flags); +} + +static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + BUG(); + else + sh_pfc_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + sh_gpio_set_value(pfc, offset, value); + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + return -EINVAL; + + return sh_pfc_read_bit(dr, bit); +} + +static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + return sh_gpio_get_value(gpio_to_pfc(gc), offset); +} + +static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); +} + +static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + pinmux_enum_t enum_id; + pinmux_enum_t *enum_ids; + int i, k, pos; + + pos = 0; + enum_id = 0; + while (1) { + pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); + if (pos <= 0 || !enum_id) + break; + + for (i = 0; i < pfc->gpio_irq_size; i++) { + enum_ids = pfc->gpio_irq[i].enum_ids; + for (k = 0; enum_ids[k]; k++) { + if (enum_ids[k] == enum_id) + return pfc->gpio_irq[i].irq; + } + } + } + + return -ENOSYS; +} + +static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) +{ + struct sh_pfc *pfc = chip->pfc; + struct gpio_chip *gc = &chip->gpio_chip; + + gc->request = sh_gpio_request; + gc->free = sh_gpio_free; + gc->direction_input = sh_gpio_direction_input; + gc->get = sh_gpio_get; + gc->direction_output = sh_gpio_direction_output; + gc->set = sh_gpio_set; + gc->to_irq = sh_gpio_to_irq; + + WARN_ON(pfc->first_gpio != 0); /* needs testing */ + + gc->label = pfc->name; + gc->owner = THIS_MODULE; + gc->base = pfc->first_gpio; + gc->ngpio = (pfc->last_gpio - pfc->first_gpio) + 1; +} + +int sh_pfc_register_gpiochip(struct sh_pfc *pfc) +{ + struct sh_pfc_chip *chip; + int ret; + + chip = kzalloc(sizeof(struct sh_pfc_chip), GFP_KERNEL); + if (unlikely(!chip)) + return -ENOMEM; + + chip->pfc = pfc; + + sh_pfc_gpio_setup(chip); + + ret = gpiochip_add(&chip->gpio_chip); + if (unlikely(ret < 0)) + kfree(chip); + + pr_info("%s handling gpio %d -> %d\n", + pfc->name, pfc->first_gpio, pfc->last_gpio); + + return ret; +} +EXPORT_SYMBOL_GPL(sh_pfc_register_gpiochip); + +static int sh_pfc_gpio_match(struct gpio_chip *gc, void *data) +{ + return !!strstr(gc->label, data); +} + +static int __devinit sh_pfc_gpio_probe(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip; + struct gpio_chip *gc; + + gc = gpiochip_find("_pfc", sh_pfc_gpio_match); + if (unlikely(!gc)) { + pr_err("Cant find gpio chip\n"); + return -ENODEV; + } + + chip = gpio_to_pfc_chip(gc); + platform_set_drvdata(pdev, chip); + + pr_info("attaching to GPIO chip %s\n", chip->pfc->name); + + return 0; +} + +static int __devexit sh_pfc_gpio_remove(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&chip->gpio_chip); + if (unlikely(ret < 0)) + return ret; + + kfree(chip); + return 0; +} + +static struct platform_driver sh_pfc_gpio_driver = { + .probe = sh_pfc_gpio_probe, + .remove = __devexit_p(sh_pfc_gpio_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device sh_pfc_gpio_device = { + .name = KBUILD_MODNAME, + .id = -1, +}; + +static int __init sh_pfc_gpio_init(void) +{ + int rc; + + rc = platform_driver_register(&sh_pfc_gpio_driver); + if (likely(!rc)) { + rc = platform_device_register(&sh_pfc_gpio_device); + if (unlikely(rc)) + platform_driver_unregister(&sh_pfc_gpio_driver); + } + + return rc; +} + +static void __exit sh_pfc_gpio_exit(void) +{ + platform_device_unregister(&sh_pfc_gpio_device); + platform_driver_unregister(&sh_pfc_gpio_driver); +} + +module_init(sh_pfc_gpio_init); +module_exit(sh_pfc_gpio_exit); + +MODULE_AUTHOR("Magnus Damm, Paul Mundt"); +MODULE_DESCRIPTION("GPIO driver for SuperH pin function controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pfc-gpio"); diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index 522c6c46d1be..8a9ae09603d6 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -1,7 +1,8 @@ /* - * Pinmuxed GPIO support for SuperH. + * SuperH Pin Function Controller support. * * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt * * 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 @@ -11,70 +12,74 @@ #include #include -#include +#include #include -#include #include #include -#include #include -#include #include #include -static void pfc_iounmap(struct pinmux_info *pip) +static struct sh_pfc *sh_pfc __read_mostly; + +static inline bool sh_pfc_initialized(void) +{ + return !!sh_pfc; +} + +static void pfc_iounmap(struct sh_pfc *pfc) { int k; - for (k = 0; k < pip->num_resources; k++) - if (pip->window[k].virt) - iounmap(pip->window[k].virt); + for (k = 0; k < pfc->num_resources; k++) + if (pfc->window[k].virt) + iounmap(pfc->window[k].virt); - kfree(pip->window); - pip->window = NULL; + kfree(pfc->window); + pfc->window = NULL; } -static int pfc_ioremap(struct pinmux_info *pip) +static int pfc_ioremap(struct sh_pfc *pfc) { struct resource *res; int k; - if (!pip->num_resources) + if (!pfc->num_resources) return 0; - pip->window = kzalloc(pip->num_resources * sizeof(*pip->window), + pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window), GFP_NOWAIT); - if (!pip->window) + if (!pfc->window) goto err1; - for (k = 0; k < pip->num_resources; k++) { - res = pip->resource + k; + for (k = 0; k < pfc->num_resources; k++) { + res = pfc->resource + k; WARN_ON(resource_type(res) != IORESOURCE_MEM); - pip->window[k].phys = res->start; - pip->window[k].size = resource_size(res); - pip->window[k].virt = ioremap_nocache(res->start, + pfc->window[k].phys = res->start; + pfc->window[k].size = resource_size(res); + pfc->window[k].virt = ioremap_nocache(res->start, resource_size(res)); - if (!pip->window[k].virt) + if (!pfc->window[k].virt) goto err2; } return 0; err2: - pfc_iounmap(pip); + pfc_iounmap(pfc); err1: return -1; } -static void __iomem *pfc_phys_to_virt(struct pinmux_info *pip, +static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc, unsigned long address) { struct pfc_window *window; int k; /* scan through physical windows and convert address */ - for (k = 0; k < pip->num_resources; k++) { - window = pip->window + k; + for (k = 0; k < pfc->num_resources; k++) { + window = pfc->window + k; if (address < window->phys) continue; @@ -135,8 +140,7 @@ static void gpio_write_raw_reg(void __iomem *mapped_reg, BUG(); } -static int gpio_read_bit(struct pinmux_data_reg *dr, - unsigned long in_pos) +int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos) { unsigned long pos; @@ -147,9 +151,10 @@ static int gpio_read_bit(struct pinmux_data_reg *dr, return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; } +EXPORT_SYMBOL_GPL(sh_pfc_read_bit); -static void gpio_write_bit(struct pinmux_data_reg *dr, - unsigned long in_pos, unsigned long value) +void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, + unsigned long value) { unsigned long pos; @@ -166,8 +171,9 @@ static void gpio_write_bit(struct pinmux_data_reg *dr, gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); } +EXPORT_SYMBOL_GPL(sh_pfc_write_bit); -static void config_reg_helper(struct pinmux_info *gpioc, +static void config_reg_helper(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long in_pos, void __iomem **mapped_regp, @@ -176,7 +182,7 @@ static void config_reg_helper(struct pinmux_info *gpioc, { int k; - *mapped_regp = pfc_phys_to_virt(gpioc, crp->reg); + *mapped_regp = pfc_phys_to_virt(pfc, crp->reg); if (crp->field_width) { *maskp = (1 << crp->field_width) - 1; @@ -189,14 +195,14 @@ static void config_reg_helper(struct pinmux_info *gpioc, } } -static int read_config_reg(struct pinmux_info *gpioc, +static int read_config_reg(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long field) { void __iomem *mapped_reg; unsigned long mask, pos; - config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); pr_debug("read_reg: addr = %lx, field = %ld, " "r_width = %ld, f_width = %ld\n", @@ -205,14 +211,14 @@ static int read_config_reg(struct pinmux_info *gpioc, return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; } -static void write_config_reg(struct pinmux_info *gpioc, +static void write_config_reg(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long field, unsigned long value) { void __iomem *mapped_reg; unsigned long mask, pos, data; - config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " "r_width = %ld, f_width = %ld\n", @@ -225,30 +231,30 @@ static void write_config_reg(struct pinmux_info *gpioc, data &= mask; data |= value; - if (gpioc->unlock_reg) - gpio_write_raw_reg(pfc_phys_to_virt(gpioc, gpioc->unlock_reg), + if (pfc->unlock_reg) + gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg), 32, ~data); gpio_write_raw_reg(mapped_reg, crp->reg_width, data); } -static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) +static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio) { - struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; struct pinmux_data_reg *data_reg; int k, n; - if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + if (!enum_in_range(gpiop->enum_id, &pfc->data)) return -1; k = 0; while (1) { - data_reg = gpioc->data_regs + k; + data_reg = pfc->data_regs + k; if (!data_reg->reg_width) break; - data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg); + data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg); for (n = 0; n < data_reg->reg_width; n++) { if (data_reg->enum_ids[n] == gpiop->enum_id) { @@ -267,17 +273,17 @@ static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) return -1; } -static void setup_data_regs(struct pinmux_info *gpioc) +static void setup_data_regs(struct sh_pfc *pfc) { struct pinmux_data_reg *drp; int k; - for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) - setup_data_reg(gpioc, k); + for (k = pfc->first_gpio; k <= pfc->last_gpio; k++) + setup_data_reg(pfc, k); k = 0; while (1) { - drp = gpioc->data_regs + k; + drp = pfc->data_regs + k; if (!drp->reg_width) break; @@ -288,23 +294,24 @@ static void setup_data_regs(struct pinmux_info *gpioc) } } -static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, +int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, struct pinmux_data_reg **drp, int *bitp) { - struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; int k, n; - if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + if (!enum_in_range(gpiop->enum_id, &pfc->data)) return -1; k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; - *drp = gpioc->data_regs + k; + *drp = pfc->data_regs + k; *bitp = n; return 0; } +EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg); -static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, +static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id, struct pinmux_cfg_reg **crp, int *fieldp, int *valuep, unsigned long **cntp) @@ -315,7 +322,7 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, k = 0; while (1) { - config_reg = gpioc->cfg_regs + k; + config_reg = pfc->cfg_regs + k; r_width = config_reg->reg_width; f_width = config_reg->field_width; @@ -350,15 +357,15 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, return -1; } -static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, - int pos, pinmux_enum_t *enum_idp) +int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, + pinmux_enum_t *enum_idp) { - pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; - pinmux_enum_t *data = gpioc->gpio_data; + pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id; + pinmux_enum_t *data = pfc->gpio_data; int k; - if (!enum_in_range(enum_id, &gpioc->data)) { - if (!enum_in_range(enum_id, &gpioc->mark)) { + if (!enum_in_range(enum_id, &pfc->data)) { + if (!enum_in_range(enum_id, &pfc->mark)) { pr_err("non data/mark enum_id for gpio %d\n", gpio); return -1; } @@ -369,7 +376,7 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, return pos + 1; } - for (k = 0; k < gpioc->gpio_data_size; k++) { + for (k = 0; k < pfc->gpio_data_size; k++) { if (data[k] == enum_id) { *enum_idp = data[k + 1]; return k + 1; @@ -379,11 +386,10 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); return -1; } +EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum); -enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; - -static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, - int pinmux_type, int cfg_mode) +int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, + int cfg_mode) { struct pinmux_cfg_reg *cr = NULL; pinmux_enum_t enum_id; @@ -398,19 +404,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, break; case PINMUX_TYPE_OUTPUT: - range = &gpioc->output; + range = &pfc->output; break; case PINMUX_TYPE_INPUT: - range = &gpioc->input; + range = &pfc->input; break; case PINMUX_TYPE_INPUT_PULLUP: - range = &gpioc->input_pu; + range = &pfc->input_pu; break; case PINMUX_TYPE_INPUT_PULLDOWN: - range = &gpioc->input_pd; + range = &pfc->input_pd; break; default: @@ -422,7 +428,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, field = 0; value = 0; while (1) { - pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); + pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id); if (pos <= 0) goto out_err; @@ -430,7 +436,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, break; /* first check if this is a function enum */ - in_range = enum_in_range(enum_id, &gpioc->function); + in_range = enum_in_range(enum_id, &pfc->function); if (!in_range) { /* not a function enum */ if (range) { @@ -467,19 +473,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, if (!in_range) continue; - if (get_config_reg(gpioc, enum_id, &cr, + if (get_config_reg(pfc, enum_id, &cr, &field, &value, &cntp) != 0) goto out_err; switch (cfg_mode) { case GPIO_CFG_DRYRUN: if (!*cntp || - (read_config_reg(gpioc, cr, field) != value)) + (read_config_reg(pfc, cr, field) != value)) continue; break; case GPIO_CFG_REQ: - write_config_reg(gpioc, cr, field, value); + write_config_reg(pfc, cr, field, value); *cntp = *cntp + 1; break; @@ -493,89 +499,18 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, out_err: return -1; } +EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); -static DEFINE_SPINLOCK(gpio_lock); - -static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip) -{ - return container_of(chip, struct pinmux_info, chip); -} - -static int sh_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - struct pinmux_data_reg *dummy; - unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; - - if (!gpioc) - goto err_out; - - spin_lock_irqsave(&gpio_lock, flags); - - if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err_unlock; - - /* setup pin function here if no data is associated with pin */ - - if (get_data_reg(gpioc, offset, &dummy, &i) != 0) - pinmux_type = PINMUX_TYPE_FUNCTION; - else - pinmux_type = PINMUX_TYPE_GPIO; - - if (pinmux_type == PINMUX_TYPE_FUNCTION) { - if (pinmux_config_gpio(gpioc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_unlock; - - if (pinmux_config_gpio(gpioc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - } - - gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[offset].flags |= pinmux_type; - - ret = 0; - err_unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - err_out: - return ret; -} - -static void sh_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; - int pinmux_type; - - if (!gpioc) - return; - - spin_lock_irqsave(&gpio_lock, flags); - - pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE; - pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE); - gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE; - - spin_unlock_irqrestore(&gpio_lock, flags); -} - -static int pinmux_direction(struct pinmux_info *gpioc, - unsigned gpio, int new_pinmux_type) +int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, + int new_pinmux_type) { int pinmux_type; int ret = -EINVAL; - if (!gpioc) + if (!pfc) goto err_out; - pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; switch (pinmux_type) { case PINMUX_TYPE_GPIO: @@ -584,156 +519,55 @@ static int pinmux_direction(struct pinmux_info *gpioc, case PINMUX_TYPE_INPUT: case PINMUX_TYPE_INPUT_PULLUP: case PINMUX_TYPE_INPUT_PULLDOWN: - pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE); + sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); break; default: goto err_out; } - if (pinmux_config_gpio(gpioc, gpio, + if (sh_pfc_config_gpio(pfc, gpio, new_pinmux_type, GPIO_CFG_DRYRUN) != 0) goto err_out; - if (pinmux_config_gpio(gpioc, gpio, + if (sh_pfc_config_gpio(pfc, gpio, new_pinmux_type, GPIO_CFG_REQ) != 0) BUG(); - gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[gpio].flags |= new_pinmux_type; + pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[gpio].flags |= new_pinmux_type; ret = 0; err_out: return ret; } +EXPORT_SYMBOL_GPL(sh_pfc_set_direction); -static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +int register_sh_pfc(struct sh_pfc *pfc) { - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; + int (*initroutine)(struct sh_pfc *) = NULL; int ret; - spin_lock_irqsave(&gpio_lock, flags); - ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT); - spin_unlock_irqrestore(&gpio_lock, flags); - - return ret; -} - -static void sh_gpio_set_value(struct pinmux_info *gpioc, - unsigned gpio, int value) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) - BUG(); - else - gpio_write_bit(dr, bit, value); -} + if (sh_pfc) + return -EBUSY; -static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; - int ret; - - sh_gpio_set_value(gpioc, offset, value); - spin_lock_irqsave(&gpio_lock, flags); - ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); - spin_unlock_irqrestore(&gpio_lock, flags); - - return ret; -} - -static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) - return -EINVAL; - - return gpio_read_bit(dr, bit); -} - -static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - return sh_gpio_get_value(chip_to_pinmux(chip), offset); -} - -static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - sh_gpio_set_value(chip_to_pinmux(chip), offset, value); -} - -static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - pinmux_enum_t enum_id; - pinmux_enum_t *enum_ids; - int i, k, pos; - - pos = 0; - enum_id = 0; - while (1) { - pos = get_gpio_enum_id(gpioc, offset, pos, &enum_id); - if (pos <= 0 || !enum_id) - break; - - for (i = 0; i < gpioc->gpio_irq_size; i++) { - enum_ids = gpioc->gpio_irq[i].enum_ids; - for (k = 0; enum_ids[k]; k++) { - if (enum_ids[k] == enum_id) - return gpioc->gpio_irq[i].irq; - } - } - } - - return -ENOSYS; -} - -int register_pinmux(struct pinmux_info *pip) -{ - struct gpio_chip *chip = &pip->chip; - int ret; - - pr_info("%s handling gpio %d -> %d\n", - pip->name, pip->first_gpio, pip->last_gpio); - - ret = pfc_ioremap(pip); - if (ret < 0) + ret = pfc_ioremap(pfc); + if (unlikely(ret < 0)) return ret; - setup_data_regs(pip); - - chip->request = sh_gpio_request; - chip->free = sh_gpio_free; - chip->direction_input = sh_gpio_direction_input; - chip->get = sh_gpio_get; - chip->direction_output = sh_gpio_direction_output; - chip->set = sh_gpio_set; - chip->to_irq = sh_gpio_to_irq; - - WARN_ON(pip->first_gpio != 0); /* needs testing */ + spin_lock_init(&pfc->lock); - chip->label = pip->name; - chip->owner = THIS_MODULE; - chip->base = pip->first_gpio; - chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; + setup_data_regs(pfc); - ret = gpiochip_add(chip); - if (ret < 0) - pfc_iounmap(pip); + sh_pfc = pfc; + pr_info("%s support registered\n", pfc->name); - return ret; -} + initroutine = symbol_request(sh_pfc_register_gpiochip); + if (initroutine) { + (*initroutine)(pfc); + symbol_put_addr(initroutine); + } -int unregister_pinmux(struct pinmux_info *pip) -{ - pr_info("%s deregistering\n", pip->name); - pfc_iounmap(pip); - return gpiochip_remove(&pip->chip); + return 0; } diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index 5c15aed9c4b2..95dad340794a 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -89,7 +89,7 @@ struct pfc_window { unsigned long size; }; -struct pinmux_info { +struct sh_pfc { char *name; pinmux_enum_t reserved_id; struct pinmux_range data; @@ -112,17 +112,44 @@ struct pinmux_info { struct pinmux_irq *gpio_irq; unsigned int gpio_irq_size; + spinlock_t lock; + struct resource *resource; unsigned int num_resources; struct pfc_window *window; unsigned long unlock_reg; - - struct gpio_chip chip; }; -int register_pinmux(struct pinmux_info *pip); -int unregister_pinmux(struct pinmux_info *pip); +/* XXX compat for now */ +#define pinmux_info sh_pfc + +/* drivers/sh/pfc-gpio.c */ +int sh_pfc_register_gpiochip(struct sh_pfc *pfc); + +/* drivers/sh/pfc.c */ +int register_sh_pfc(struct sh_pfc *pfc); + +int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos); +void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, + unsigned long value); +int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, + struct pinmux_data_reg **drp, int *bitp); +int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, + pinmux_enum_t *enum_idp); +int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, + int cfg_mode); +int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, + int new_pinmux_type); + +/* xxx */ +static inline int register_pinmux(struct pinmux_info *pip) +{ + struct sh_pfc *pfc = pip; + return register_sh_pfc(pfc); +} + +enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; /* helper macro for port */ #define PORT_1(fn, pfx, sfx) fn(pfx, sfx) -- cgit v1.2.3-59-g8ed1b From b16b2a262c234d6abd8899d41769278337fff5b5 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 20 Jun 2012 18:17:56 +0900 Subject: sh: pfc: Make gpio chip support optional where possible. This implements some Kconfig knobs for ensuring that the PFC gpio chip can be disabled or built as a module in the cases where it's optional, or forcibly enabled in cases where it's not. Signed-off-by: Paul Mundt --- drivers/sh/Kconfig | 15 +++++++++++++++ drivers/sh/Makefile | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index f168a6159961..d7dbfee1bc70 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig @@ -2,4 +2,19 @@ menu "SuperH / SH-Mobile Driver Options" source "drivers/sh/intc/Kconfig" +comment "Pin function controller options" + +config SH_PFC + # XXX move off the gpio dependency + depends on GENERIC_GPIO + select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB + def_bool y + +config GPIO_SH_PFC + tristate "SuperH PFC GPIO support" + depends on SH_PFC && GPIOLIB + help + This enables support for GPIOs within the SoC's pin function + controller. + endmenu diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index be5b2934f067..f5d93e8de090 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -6,5 +6,6 @@ obj-y := intc/ obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_GENERIC_GPIO) += pfc.o pfc-gpio.o +obj-$(CONFIG_SH_PFC) += pfc.o +obj-$(CONFIG_GPIO_SH_PFC) += pfc-gpio.o obj-y += pm_runtime.o -- cgit v1.2.3-59-g8ed1b From 9f65b2b60c42c1e2d885acede4443b53f141c987 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 20 Jun 2012 22:29:14 +0900 Subject: sh: pfc: Kill off unused pinmux bias flags. WANT_PULLUP/DOWN were never interfaced with anything, so just kill them off. Signed-off-by: Paul Mundt --- include/linux/sh_pfc.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index 95dad340794a..c06a47313a25 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -25,8 +25,6 @@ typedef unsigned short pinmux_flag_t; #define PINMUX_TYPE_INPUT_PULLDOWN 6 #define PINMUX_FLAG_TYPE (0x7) -#define PINMUX_FLAG_WANT_PULLUP (1 << 3) -#define PINMUX_FLAG_WANT_PULLDOWN (1 << 4) #define PINMUX_FLAG_DBIT_SHIFT 5 #define PINMUX_FLAG_DBIT (0x1f << PINMUX_FLAG_DBIT_SHIFT) -- cgit v1.2.3-59-g8ed1b From 06d5631f56460917af3d9417ef63811cf0cad9ce Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 21 Jun 2012 00:03:41 +0900 Subject: sh: pfc: Verify pin type encoding size at build time. The encoding is tightly packed, and future changes (such as pinconf-generic support) can easily lead to a situation where we violate the encoding constraints and trample data bit/reg bits. This plugs in some sanity checks by way of a BUILD_BUG_ON() to blow up if we fail to fit. Signed-off-by: Paul Mundt --- drivers/sh/pfc.c | 5 +++++ include/linux/sh_pfc.h | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index 8a9ae09603d6..ce4579ebd602 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -549,6 +549,11 @@ int register_sh_pfc(struct sh_pfc *pfc) int (*initroutine)(struct sh_pfc *) = NULL; int ret; + /* + * Ensure that the type encoding fits + */ + BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1)); + if (sh_pfc) return -EBUSY; diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index c06a47313a25..ed1d8234f6ae 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -16,15 +16,18 @@ typedef unsigned short pinmux_enum_t; typedef unsigned short pinmux_flag_t; -#define PINMUX_TYPE_NONE 0 -#define PINMUX_TYPE_FUNCTION 1 -#define PINMUX_TYPE_GPIO 2 -#define PINMUX_TYPE_OUTPUT 3 -#define PINMUX_TYPE_INPUT 4 -#define PINMUX_TYPE_INPUT_PULLUP 5 -#define PINMUX_TYPE_INPUT_PULLDOWN 6 +enum { + PINMUX_TYPE_NONE, -#define PINMUX_FLAG_TYPE (0x7) + PINMUX_TYPE_FUNCTION, + PINMUX_TYPE_GPIO, + PINMUX_TYPE_OUTPUT, + PINMUX_TYPE_INPUT, + PINMUX_TYPE_INPUT_PULLUP, + PINMUX_TYPE_INPUT_PULLDOWN, + + PINMUX_FLAG_TYPE, /* must be last */ +}; #define PINMUX_FLAG_DBIT_SHIFT 5 #define PINMUX_FLAG_DBIT (0x1f << PINMUX_FLAG_DBIT_SHIFT) @@ -36,7 +39,9 @@ struct pinmux_gpio { pinmux_flag_t flags; }; -#define PINMUX_GPIO(gpio, data_or_mark) [gpio] = { data_or_mark } +#define PINMUX_GPIO(gpio, data_or_mark) \ + [gpio] = { .enum_id = data_or_mark, .flags = PINMUX_TYPE_NONE } + #define PINMUX_DATA(data_or_mark, ids...) data_or_mark, ids, 0 struct pinmux_cfg_reg { -- cgit v1.2.3-59-g8ed1b From afae021abeadc58aec5074f26a1d62912773edf7 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Jul 2012 11:49:30 +0900 Subject: sh: pfc: Shuffle PFC support core. This follows the intc/clk changes and shuffles the PFC support code under its own directory. This will facilitate better code sharing, and allow us to trim down the exported interface by quite a margin. Signed-off-by: Paul Mundt --- drivers/sh/Kconfig | 16 +- drivers/sh/Makefile | 4 +- drivers/sh/pfc-gpio.c | 309 -------------------------- drivers/sh/pfc.c | 578 ------------------------------------------------ drivers/sh/pfc/Kconfig | 14 ++ drivers/sh/pfc/Makefile | 2 + drivers/sh/pfc/core.c | 578 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/sh/pfc/gpio.c | 309 ++++++++++++++++++++++++++ include/linux/sh_pfc.h | 4 +- 9 files changed, 908 insertions(+), 906 deletions(-) delete mode 100644 drivers/sh/pfc-gpio.c delete mode 100644 drivers/sh/pfc.c create mode 100644 drivers/sh/pfc/Kconfig create mode 100644 drivers/sh/pfc/Makefile create mode 100644 drivers/sh/pfc/core.c create mode 100644 drivers/sh/pfc/gpio.c diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index d7dbfee1bc70..d860ef743568 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig @@ -1,20 +1,6 @@ menu "SuperH / SH-Mobile Driver Options" source "drivers/sh/intc/Kconfig" - -comment "Pin function controller options" - -config SH_PFC - # XXX move off the gpio dependency - depends on GENERIC_GPIO - select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB - def_bool y - -config GPIO_SH_PFC - tristate "SuperH PFC GPIO support" - depends on SH_PFC && GPIOLIB - help - This enables support for GPIOs within the SoC's pin function - controller. +source "drivers/sh/pfc/Kconfig" endmenu diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index f5d93e8de090..e57895b1a425 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -5,7 +5,7 @@ obj-y := intc/ obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ +obj-$(CONFIG_SH_PFC) += pfc/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_SH_PFC) += pfc.o -obj-$(CONFIG_GPIO_SH_PFC) += pfc-gpio.o + obj-y += pm_runtime.o diff --git a/drivers/sh/pfc-gpio.c b/drivers/sh/pfc-gpio.c deleted file mode 100644 index d74e5a96024b..000000000000 --- a/drivers/sh/pfc-gpio.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * SuperH Pin Function Controller GPIO driver. - * - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2009 - 2012 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -struct sh_pfc_chip { - struct sh_pfc *pfc; - struct gpio_chip gpio_chip; -}; - -static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) -{ - return container_of(gc, struct sh_pfc_chip, gpio_chip); -} - -static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) -{ - return gpio_to_pfc_chip(gc)->pfc; -} - -static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - struct pinmux_data_reg *dummy; - unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; - - if (!pfc) - goto err_out; - - spin_lock_irqsave(&pfc->lock, flags); - - if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err_unlock; - - /* setup pin function here if no data is associated with pin */ - - if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) - pinmux_type = PINMUX_TYPE_FUNCTION; - else - pinmux_type = PINMUX_TYPE_GPIO; - - if (pinmux_type == PINMUX_TYPE_FUNCTION) { - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_unlock; - - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - } - - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= pinmux_type; - - ret = 0; - err_unlock: - spin_unlock_irqrestore(&pfc->lock, flags); - err_out: - return ret; -} - -static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int pinmux_type; - - if (!pfc) - return; - - spin_lock_irqsave(&pfc->lock, flags); - - pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; - sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; - - spin_unlock_irqrestore(&pfc->lock, flags); -} - -static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; -} - -static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) - BUG(); - else - sh_pfc_write_bit(dr, bit, value); -} - -static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - sh_gpio_set_value(pfc, offset, value); - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; -} - -static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) - return -EINVAL; - - return sh_pfc_read_bit(dr, bit); -} - -static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - return sh_gpio_get_value(gpio_to_pfc(gc), offset); -} - -static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) -{ - sh_gpio_set_value(gpio_to_pfc(gc), offset, value); -} - -static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - pinmux_enum_t enum_id; - pinmux_enum_t *enum_ids; - int i, k, pos; - - pos = 0; - enum_id = 0; - while (1) { - pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); - if (pos <= 0 || !enum_id) - break; - - for (i = 0; i < pfc->gpio_irq_size; i++) { - enum_ids = pfc->gpio_irq[i].enum_ids; - for (k = 0; enum_ids[k]; k++) { - if (enum_ids[k] == enum_id) - return pfc->gpio_irq[i].irq; - } - } - } - - return -ENOSYS; -} - -static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) -{ - struct sh_pfc *pfc = chip->pfc; - struct gpio_chip *gc = &chip->gpio_chip; - - gc->request = sh_gpio_request; - gc->free = sh_gpio_free; - gc->direction_input = sh_gpio_direction_input; - gc->get = sh_gpio_get; - gc->direction_output = sh_gpio_direction_output; - gc->set = sh_gpio_set; - gc->to_irq = sh_gpio_to_irq; - - WARN_ON(pfc->first_gpio != 0); /* needs testing */ - - gc->label = pfc->name; - gc->owner = THIS_MODULE; - gc->base = pfc->first_gpio; - gc->ngpio = (pfc->last_gpio - pfc->first_gpio) + 1; -} - -int sh_pfc_register_gpiochip(struct sh_pfc *pfc) -{ - struct sh_pfc_chip *chip; - int ret; - - chip = kzalloc(sizeof(struct sh_pfc_chip), GFP_KERNEL); - if (unlikely(!chip)) - return -ENOMEM; - - chip->pfc = pfc; - - sh_pfc_gpio_setup(chip); - - ret = gpiochip_add(&chip->gpio_chip); - if (unlikely(ret < 0)) - kfree(chip); - - pr_info("%s handling gpio %d -> %d\n", - pfc->name, pfc->first_gpio, pfc->last_gpio); - - return ret; -} -EXPORT_SYMBOL_GPL(sh_pfc_register_gpiochip); - -static int sh_pfc_gpio_match(struct gpio_chip *gc, void *data) -{ - return !!strstr(gc->label, data); -} - -static int __devinit sh_pfc_gpio_probe(struct platform_device *pdev) -{ - struct sh_pfc_chip *chip; - struct gpio_chip *gc; - - gc = gpiochip_find("_pfc", sh_pfc_gpio_match); - if (unlikely(!gc)) { - pr_err("Cant find gpio chip\n"); - return -ENODEV; - } - - chip = gpio_to_pfc_chip(gc); - platform_set_drvdata(pdev, chip); - - pr_info("attaching to GPIO chip %s\n", chip->pfc->name); - - return 0; -} - -static int __devexit sh_pfc_gpio_remove(struct platform_device *pdev) -{ - struct sh_pfc_chip *chip = platform_get_drvdata(pdev); - int ret; - - ret = gpiochip_remove(&chip->gpio_chip); - if (unlikely(ret < 0)) - return ret; - - kfree(chip); - return 0; -} - -static struct platform_driver sh_pfc_gpio_driver = { - .probe = sh_pfc_gpio_probe, - .remove = __devexit_p(sh_pfc_gpio_remove), - .driver = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE, - }, -}; - -static struct platform_device sh_pfc_gpio_device = { - .name = KBUILD_MODNAME, - .id = -1, -}; - -static int __init sh_pfc_gpio_init(void) -{ - int rc; - - rc = platform_driver_register(&sh_pfc_gpio_driver); - if (likely(!rc)) { - rc = platform_device_register(&sh_pfc_gpio_device); - if (unlikely(rc)) - platform_driver_unregister(&sh_pfc_gpio_driver); - } - - return rc; -} - -static void __exit sh_pfc_gpio_exit(void) -{ - platform_device_unregister(&sh_pfc_gpio_device); - platform_driver_unregister(&sh_pfc_gpio_driver); -} - -module_init(sh_pfc_gpio_init); -module_exit(sh_pfc_gpio_exit); - -MODULE_AUTHOR("Magnus Damm, Paul Mundt"); -MODULE_DESCRIPTION("GPIO driver for SuperH pin function controller"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:pfc-gpio"); diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c deleted file mode 100644 index ce4579ebd602..000000000000 --- a/drivers/sh/pfc.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * SuperH Pin Function Controller support. - * - * Copyright (C) 2008 Magnus Damm - * Copyright (C) 2009 - 2012 Paul Mundt - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct sh_pfc *sh_pfc __read_mostly; - -static inline bool sh_pfc_initialized(void) -{ - return !!sh_pfc; -} - -static void pfc_iounmap(struct sh_pfc *pfc) -{ - int k; - - for (k = 0; k < pfc->num_resources; k++) - if (pfc->window[k].virt) - iounmap(pfc->window[k].virt); - - kfree(pfc->window); - pfc->window = NULL; -} - -static int pfc_ioremap(struct sh_pfc *pfc) -{ - struct resource *res; - int k; - - if (!pfc->num_resources) - return 0; - - pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window), - GFP_NOWAIT); - if (!pfc->window) - goto err1; - - for (k = 0; k < pfc->num_resources; k++) { - res = pfc->resource + k; - WARN_ON(resource_type(res) != IORESOURCE_MEM); - pfc->window[k].phys = res->start; - pfc->window[k].size = resource_size(res); - pfc->window[k].virt = ioremap_nocache(res->start, - resource_size(res)); - if (!pfc->window[k].virt) - goto err2; - } - - return 0; - -err2: - pfc_iounmap(pfc); -err1: - return -1; -} - -static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc, - unsigned long address) -{ - struct pfc_window *window; - int k; - - /* scan through physical windows and convert address */ - for (k = 0; k < pfc->num_resources; k++) { - window = pfc->window + k; - - if (address < window->phys) - continue; - - if (address >= (window->phys + window->size)) - continue; - - return window->virt + (address - window->phys); - } - - /* no windows defined, register must be 1:1 mapped virt:phys */ - return (void __iomem *)address; -} - -static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) -{ - if (enum_id < r->begin) - return 0; - - if (enum_id > r->end) - return 0; - - return 1; -} - -static unsigned long gpio_read_raw_reg(void __iomem *mapped_reg, - unsigned long reg_width) -{ - switch (reg_width) { - case 8: - return ioread8(mapped_reg); - case 16: - return ioread16(mapped_reg); - case 32: - return ioread32(mapped_reg); - } - - BUG(); - return 0; -} - -static void gpio_write_raw_reg(void __iomem *mapped_reg, - unsigned long reg_width, - unsigned long data) -{ - switch (reg_width) { - case 8: - iowrite8(data, mapped_reg); - return; - case 16: - iowrite16(data, mapped_reg); - return; - case 32: - iowrite32(data, mapped_reg); - return; - } - - BUG(); -} - -int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos) -{ - unsigned long pos; - - pos = dr->reg_width - (in_pos + 1); - - pr_debug("read_bit: addr = %lx, pos = %ld, " - "r_width = %ld\n", dr->reg, pos, dr->reg_width); - - return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; -} -EXPORT_SYMBOL_GPL(sh_pfc_read_bit); - -void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, - unsigned long value) -{ - unsigned long pos; - - pos = dr->reg_width - (in_pos + 1); - - pr_debug("write_bit addr = %lx, value = %d, pos = %ld, " - "r_width = %ld\n", - dr->reg, !!value, pos, dr->reg_width); - - if (value) - set_bit(pos, &dr->reg_shadow); - else - clear_bit(pos, &dr->reg_shadow); - - gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); -} -EXPORT_SYMBOL_GPL(sh_pfc_write_bit); - -static void config_reg_helper(struct sh_pfc *pfc, - struct pinmux_cfg_reg *crp, - unsigned long in_pos, - void __iomem **mapped_regp, - unsigned long *maskp, - unsigned long *posp) -{ - int k; - - *mapped_regp = pfc_phys_to_virt(pfc, crp->reg); - - if (crp->field_width) { - *maskp = (1 << crp->field_width) - 1; - *posp = crp->reg_width - ((in_pos + 1) * crp->field_width); - } else { - *maskp = (1 << crp->var_field_width[in_pos]) - 1; - *posp = crp->reg_width; - for (k = 0; k <= in_pos; k++) - *posp -= crp->var_field_width[k]; - } -} - -static int read_config_reg(struct sh_pfc *pfc, - struct pinmux_cfg_reg *crp, - unsigned long field) -{ - void __iomem *mapped_reg; - unsigned long mask, pos; - - config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); - - pr_debug("read_reg: addr = %lx, field = %ld, " - "r_width = %ld, f_width = %ld\n", - crp->reg, field, crp->reg_width, crp->field_width); - - return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; -} - -static void write_config_reg(struct sh_pfc *pfc, - struct pinmux_cfg_reg *crp, - unsigned long field, unsigned long value) -{ - void __iomem *mapped_reg; - unsigned long mask, pos, data; - - config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); - - pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " - "r_width = %ld, f_width = %ld\n", - crp->reg, value, field, crp->reg_width, crp->field_width); - - mask = ~(mask << pos); - value = value << pos; - - data = gpio_read_raw_reg(mapped_reg, crp->reg_width); - data &= mask; - data |= value; - - if (pfc->unlock_reg) - gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg), - 32, ~data); - - gpio_write_raw_reg(mapped_reg, crp->reg_width, data); -} - -static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio) -{ - struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; - struct pinmux_data_reg *data_reg; - int k, n; - - if (!enum_in_range(gpiop->enum_id, &pfc->data)) - return -1; - - k = 0; - while (1) { - data_reg = pfc->data_regs + k; - - if (!data_reg->reg_width) - break; - - data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg); - - for (n = 0; n < data_reg->reg_width; n++) { - if (data_reg->enum_ids[n] == gpiop->enum_id) { - gpiop->flags &= ~PINMUX_FLAG_DREG; - gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); - gpiop->flags &= ~PINMUX_FLAG_DBIT; - gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); - return 0; - } - } - k++; - } - - BUG(); - - return -1; -} - -static void setup_data_regs(struct sh_pfc *pfc) -{ - struct pinmux_data_reg *drp; - int k; - - for (k = pfc->first_gpio; k <= pfc->last_gpio; k++) - setup_data_reg(pfc, k); - - k = 0; - while (1) { - drp = pfc->data_regs + k; - - if (!drp->reg_width) - break; - - drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg, - drp->reg_width); - k++; - } -} - -int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, - struct pinmux_data_reg **drp, int *bitp) -{ - struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; - int k, n; - - if (!enum_in_range(gpiop->enum_id, &pfc->data)) - return -1; - - k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; - n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; - *drp = pfc->data_regs + k; - *bitp = n; - return 0; -} -EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg); - -static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id, - struct pinmux_cfg_reg **crp, - int *fieldp, int *valuep, - unsigned long **cntp) -{ - struct pinmux_cfg_reg *config_reg; - unsigned long r_width, f_width, curr_width, ncomb; - int k, m, n, pos, bit_pos; - - k = 0; - while (1) { - config_reg = pfc->cfg_regs + k; - - r_width = config_reg->reg_width; - f_width = config_reg->field_width; - - if (!r_width) - break; - - pos = 0; - m = 0; - for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { - if (f_width) - curr_width = f_width; - else - curr_width = config_reg->var_field_width[m]; - - ncomb = 1 << curr_width; - for (n = 0; n < ncomb; n++) { - if (config_reg->enum_ids[pos + n] == enum_id) { - *crp = config_reg; - *fieldp = m; - *valuep = n; - *cntp = &config_reg->cnt[m]; - return 0; - } - } - pos += ncomb; - m++; - } - k++; - } - - return -1; -} - -int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, - pinmux_enum_t *enum_idp) -{ - pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id; - pinmux_enum_t *data = pfc->gpio_data; - int k; - - if (!enum_in_range(enum_id, &pfc->data)) { - if (!enum_in_range(enum_id, &pfc->mark)) { - pr_err("non data/mark enum_id for gpio %d\n", gpio); - return -1; - } - } - - if (pos) { - *enum_idp = data[pos + 1]; - return pos + 1; - } - - for (k = 0; k < pfc->gpio_data_size; k++) { - if (data[k] == enum_id) { - *enum_idp = data[k + 1]; - return k + 1; - } - } - - pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); - return -1; -} -EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum); - -int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, - int cfg_mode) -{ - struct pinmux_cfg_reg *cr = NULL; - pinmux_enum_t enum_id; - struct pinmux_range *range; - int in_range, pos, field, value; - unsigned long *cntp; - - switch (pinmux_type) { - - case PINMUX_TYPE_FUNCTION: - range = NULL; - break; - - case PINMUX_TYPE_OUTPUT: - range = &pfc->output; - break; - - case PINMUX_TYPE_INPUT: - range = &pfc->input; - break; - - case PINMUX_TYPE_INPUT_PULLUP: - range = &pfc->input_pu; - break; - - case PINMUX_TYPE_INPUT_PULLDOWN: - range = &pfc->input_pd; - break; - - default: - goto out_err; - } - - pos = 0; - enum_id = 0; - field = 0; - value = 0; - while (1) { - pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id); - if (pos <= 0) - goto out_err; - - if (!enum_id) - break; - - /* first check if this is a function enum */ - in_range = enum_in_range(enum_id, &pfc->function); - if (!in_range) { - /* not a function enum */ - if (range) { - /* - * other range exists, so this pin is - * a regular GPIO pin that now is being - * bound to a specific direction. - * - * for this case we only allow function enums - * and the enums that match the other range. - */ - in_range = enum_in_range(enum_id, range); - - /* - * special case pass through for fixed - * input-only or output-only pins without - * function enum register association. - */ - if (in_range && enum_id == range->force) - continue; - } else { - /* - * no other range exists, so this pin - * must then be of the function type. - * - * allow function type pins to select - * any combination of function/in/out - * in their MARK lists. - */ - in_range = 1; - } - } - - if (!in_range) - continue; - - if (get_config_reg(pfc, enum_id, &cr, - &field, &value, &cntp) != 0) - goto out_err; - - switch (cfg_mode) { - case GPIO_CFG_DRYRUN: - if (!*cntp || - (read_config_reg(pfc, cr, field) != value)) - continue; - break; - - case GPIO_CFG_REQ: - write_config_reg(pfc, cr, field, value); - *cntp = *cntp + 1; - break; - - case GPIO_CFG_FREE: - *cntp = *cntp - 1; - break; - } - } - - return 0; - out_err: - return -1; -} -EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); - -int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, - int new_pinmux_type) -{ - int pinmux_type; - int ret = -EINVAL; - - if (!pfc) - goto err_out; - - pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; - - switch (pinmux_type) { - case PINMUX_TYPE_GPIO: - break; - case PINMUX_TYPE_OUTPUT: - case PINMUX_TYPE_INPUT: - case PINMUX_TYPE_INPUT_PULLUP: - case PINMUX_TYPE_INPUT_PULLDOWN: - sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); - break; - default: - goto err_out; - } - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_out; - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - - pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[gpio].flags |= new_pinmux_type; - - ret = 0; - err_out: - return ret; -} -EXPORT_SYMBOL_GPL(sh_pfc_set_direction); - -int register_sh_pfc(struct sh_pfc *pfc) -{ - int (*initroutine)(struct sh_pfc *) = NULL; - int ret; - - /* - * Ensure that the type encoding fits - */ - BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1)); - - if (sh_pfc) - return -EBUSY; - - ret = pfc_ioremap(pfc); - if (unlikely(ret < 0)) - return ret; - - spin_lock_init(&pfc->lock); - - setup_data_regs(pfc); - - sh_pfc = pfc; - pr_info("%s support registered\n", pfc->name); - - initroutine = symbol_request(sh_pfc_register_gpiochip); - if (initroutine) { - (*initroutine)(pfc); - symbol_put_addr(initroutine); - } - - return 0; -} diff --git a/drivers/sh/pfc/Kconfig b/drivers/sh/pfc/Kconfig new file mode 100644 index 000000000000..95b04f4edb88 --- /dev/null +++ b/drivers/sh/pfc/Kconfig @@ -0,0 +1,14 @@ +comment "Pin function controller options" + +config SH_PFC + # XXX move off the gpio dependency + depends on GENERIC_GPIO + select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB + def_bool y + +config GPIO_SH_PFC + tristate "SuperH PFC GPIO support" + depends on SH_PFC && GPIOLIB + help + This enables support for GPIOs within the SoC's pin function + controller. diff --git a/drivers/sh/pfc/Makefile b/drivers/sh/pfc/Makefile new file mode 100644 index 000000000000..d81707744b27 --- /dev/null +++ b/drivers/sh/pfc/Makefile @@ -0,0 +1,2 @@ +obj-y += core.o +obj-$(CONFIG_GPIO_SH_PFC) += gpio.o diff --git a/drivers/sh/pfc/core.c b/drivers/sh/pfc/core.c new file mode 100644 index 000000000000..ce4579ebd602 --- /dev/null +++ b/drivers/sh/pfc/core.c @@ -0,0 +1,578 @@ +/* + * SuperH Pin Function Controller support. + * + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct sh_pfc *sh_pfc __read_mostly; + +static inline bool sh_pfc_initialized(void) +{ + return !!sh_pfc; +} + +static void pfc_iounmap(struct sh_pfc *pfc) +{ + int k; + + for (k = 0; k < pfc->num_resources; k++) + if (pfc->window[k].virt) + iounmap(pfc->window[k].virt); + + kfree(pfc->window); + pfc->window = NULL; +} + +static int pfc_ioremap(struct sh_pfc *pfc) +{ + struct resource *res; + int k; + + if (!pfc->num_resources) + return 0; + + pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window), + GFP_NOWAIT); + if (!pfc->window) + goto err1; + + for (k = 0; k < pfc->num_resources; k++) { + res = pfc->resource + k; + WARN_ON(resource_type(res) != IORESOURCE_MEM); + pfc->window[k].phys = res->start; + pfc->window[k].size = resource_size(res); + pfc->window[k].virt = ioremap_nocache(res->start, + resource_size(res)); + if (!pfc->window[k].virt) + goto err2; + } + + return 0; + +err2: + pfc_iounmap(pfc); +err1: + return -1; +} + +static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc, + unsigned long address) +{ + struct pfc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < pfc->num_resources; k++) { + window = pfc->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + return window->virt + (address - window->phys); + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return (void __iomem *)address; +} + +static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) +{ + if (enum_id < r->begin) + return 0; + + if (enum_id > r->end) + return 0; + + return 1; +} + +static unsigned long gpio_read_raw_reg(void __iomem *mapped_reg, + unsigned long reg_width) +{ + switch (reg_width) { + case 8: + return ioread8(mapped_reg); + case 16: + return ioread16(mapped_reg); + case 32: + return ioread32(mapped_reg); + } + + BUG(); + return 0; +} + +static void gpio_write_raw_reg(void __iomem *mapped_reg, + unsigned long reg_width, + unsigned long data) +{ + switch (reg_width) { + case 8: + iowrite8(data, mapped_reg); + return; + case 16: + iowrite16(data, mapped_reg); + return; + case 32: + iowrite32(data, mapped_reg); + return; + } + + BUG(); +} + +int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + + pr_debug("read_bit: addr = %lx, pos = %ld, " + "r_width = %ld\n", dr->reg, pos, dr->reg_width); + + return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; +} +EXPORT_SYMBOL_GPL(sh_pfc_read_bit); + +void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, + unsigned long value) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + + pr_debug("write_bit addr = %lx, value = %d, pos = %ld, " + "r_width = %ld\n", + dr->reg, !!value, pos, dr->reg_width); + + if (value) + set_bit(pos, &dr->reg_shadow); + else + clear_bit(pos, &dr->reg_shadow); + + gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); +} +EXPORT_SYMBOL_GPL(sh_pfc_write_bit); + +static void config_reg_helper(struct sh_pfc *pfc, + struct pinmux_cfg_reg *crp, + unsigned long in_pos, + void __iomem **mapped_regp, + unsigned long *maskp, + unsigned long *posp) +{ + int k; + + *mapped_regp = pfc_phys_to_virt(pfc, crp->reg); + + if (crp->field_width) { + *maskp = (1 << crp->field_width) - 1; + *posp = crp->reg_width - ((in_pos + 1) * crp->field_width); + } else { + *maskp = (1 << crp->var_field_width[in_pos]) - 1; + *posp = crp->reg_width; + for (k = 0; k <= in_pos; k++) + *posp -= crp->var_field_width[k]; + } +} + +static int read_config_reg(struct sh_pfc *pfc, + struct pinmux_cfg_reg *crp, + unsigned long field) +{ + void __iomem *mapped_reg; + unsigned long mask, pos; + + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); + + pr_debug("read_reg: addr = %lx, field = %ld, " + "r_width = %ld, f_width = %ld\n", + crp->reg, field, crp->reg_width, crp->field_width); + + return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; +} + +static void write_config_reg(struct sh_pfc *pfc, + struct pinmux_cfg_reg *crp, + unsigned long field, unsigned long value) +{ + void __iomem *mapped_reg; + unsigned long mask, pos, data; + + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); + + pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " + "r_width = %ld, f_width = %ld\n", + crp->reg, value, field, crp->reg_width, crp->field_width); + + mask = ~(mask << pos); + value = value << pos; + + data = gpio_read_raw_reg(mapped_reg, crp->reg_width); + data &= mask; + data |= value; + + if (pfc->unlock_reg) + gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg), + 32, ~data); + + gpio_write_raw_reg(mapped_reg, crp->reg_width, data); +} + +static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio) +{ + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; + struct pinmux_data_reg *data_reg; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &pfc->data)) + return -1; + + k = 0; + while (1) { + data_reg = pfc->data_regs + k; + + if (!data_reg->reg_width) + break; + + data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg); + + for (n = 0; n < data_reg->reg_width; n++) { + if (data_reg->enum_ids[n] == gpiop->enum_id) { + gpiop->flags &= ~PINMUX_FLAG_DREG; + gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); + gpiop->flags &= ~PINMUX_FLAG_DBIT; + gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); + return 0; + } + } + k++; + } + + BUG(); + + return -1; +} + +static void setup_data_regs(struct sh_pfc *pfc) +{ + struct pinmux_data_reg *drp; + int k; + + for (k = pfc->first_gpio; k <= pfc->last_gpio; k++) + setup_data_reg(pfc, k); + + k = 0; + while (1) { + drp = pfc->data_regs + k; + + if (!drp->reg_width) + break; + + drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg, + drp->reg_width); + k++; + } +} + +int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, + struct pinmux_data_reg **drp, int *bitp) +{ + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; + int k, n; + + if (!enum_in_range(gpiop->enum_id, &pfc->data)) + return -1; + + k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; + n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; + *drp = pfc->data_regs + k; + *bitp = n; + return 0; +} +EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg); + +static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id, + struct pinmux_cfg_reg **crp, + int *fieldp, int *valuep, + unsigned long **cntp) +{ + struct pinmux_cfg_reg *config_reg; + unsigned long r_width, f_width, curr_width, ncomb; + int k, m, n, pos, bit_pos; + + k = 0; + while (1) { + config_reg = pfc->cfg_regs + k; + + r_width = config_reg->reg_width; + f_width = config_reg->field_width; + + if (!r_width) + break; + + pos = 0; + m = 0; + for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { + if (f_width) + curr_width = f_width; + else + curr_width = config_reg->var_field_width[m]; + + ncomb = 1 << curr_width; + for (n = 0; n < ncomb; n++) { + if (config_reg->enum_ids[pos + n] == enum_id) { + *crp = config_reg; + *fieldp = m; + *valuep = n; + *cntp = &config_reg->cnt[m]; + return 0; + } + } + pos += ncomb; + m++; + } + k++; + } + + return -1; +} + +int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, + pinmux_enum_t *enum_idp) +{ + pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id; + pinmux_enum_t *data = pfc->gpio_data; + int k; + + if (!enum_in_range(enum_id, &pfc->data)) { + if (!enum_in_range(enum_id, &pfc->mark)) { + pr_err("non data/mark enum_id for gpio %d\n", gpio); + return -1; + } + } + + if (pos) { + *enum_idp = data[pos + 1]; + return pos + 1; + } + + for (k = 0; k < pfc->gpio_data_size; k++) { + if (data[k] == enum_id) { + *enum_idp = data[k + 1]; + return k + 1; + } + } + + pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); + return -1; +} +EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum); + +int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, + int cfg_mode) +{ + struct pinmux_cfg_reg *cr = NULL; + pinmux_enum_t enum_id; + struct pinmux_range *range; + int in_range, pos, field, value; + unsigned long *cntp; + + switch (pinmux_type) { + + case PINMUX_TYPE_FUNCTION: + range = NULL; + break; + + case PINMUX_TYPE_OUTPUT: + range = &pfc->output; + break; + + case PINMUX_TYPE_INPUT: + range = &pfc->input; + break; + + case PINMUX_TYPE_INPUT_PULLUP: + range = &pfc->input_pu; + break; + + case PINMUX_TYPE_INPUT_PULLDOWN: + range = &pfc->input_pd; + break; + + default: + goto out_err; + } + + pos = 0; + enum_id = 0; + field = 0; + value = 0; + while (1) { + pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id); + if (pos <= 0) + goto out_err; + + if (!enum_id) + break; + + /* first check if this is a function enum */ + in_range = enum_in_range(enum_id, &pfc->function); + if (!in_range) { + /* not a function enum */ + if (range) { + /* + * other range exists, so this pin is + * a regular GPIO pin that now is being + * bound to a specific direction. + * + * for this case we only allow function enums + * and the enums that match the other range. + */ + in_range = enum_in_range(enum_id, range); + + /* + * special case pass through for fixed + * input-only or output-only pins without + * function enum register association. + */ + if (in_range && enum_id == range->force) + continue; + } else { + /* + * no other range exists, so this pin + * must then be of the function type. + * + * allow function type pins to select + * any combination of function/in/out + * in their MARK lists. + */ + in_range = 1; + } + } + + if (!in_range) + continue; + + if (get_config_reg(pfc, enum_id, &cr, + &field, &value, &cntp) != 0) + goto out_err; + + switch (cfg_mode) { + case GPIO_CFG_DRYRUN: + if (!*cntp || + (read_config_reg(pfc, cr, field) != value)) + continue; + break; + + case GPIO_CFG_REQ: + write_config_reg(pfc, cr, field, value); + *cntp = *cntp + 1; + break; + + case GPIO_CFG_FREE: + *cntp = *cntp - 1; + break; + } + } + + return 0; + out_err: + return -1; +} +EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); + +int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, + int new_pinmux_type) +{ + int pinmux_type; + int ret = -EINVAL; + + if (!pfc) + goto err_out; + + pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err_out; + } + + if (sh_pfc_config_gpio(pfc, gpio, + new_pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_out; + + if (sh_pfc_config_gpio(pfc, gpio, + new_pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + + pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[gpio].flags |= new_pinmux_type; + + ret = 0; + err_out: + return ret; +} +EXPORT_SYMBOL_GPL(sh_pfc_set_direction); + +int register_sh_pfc(struct sh_pfc *pfc) +{ + int (*initroutine)(struct sh_pfc *) = NULL; + int ret; + + /* + * Ensure that the type encoding fits + */ + BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1)); + + if (sh_pfc) + return -EBUSY; + + ret = pfc_ioremap(pfc); + if (unlikely(ret < 0)) + return ret; + + spin_lock_init(&pfc->lock); + + setup_data_regs(pfc); + + sh_pfc = pfc; + pr_info("%s support registered\n", pfc->name); + + initroutine = symbol_request(sh_pfc_register_gpiochip); + if (initroutine) { + (*initroutine)(pfc); + symbol_put_addr(initroutine); + } + + return 0; +} diff --git a/drivers/sh/pfc/gpio.c b/drivers/sh/pfc/gpio.c new file mode 100644 index 000000000000..d74e5a96024b --- /dev/null +++ b/drivers/sh/pfc/gpio.c @@ -0,0 +1,309 @@ +/* + * SuperH Pin Function Controller GPIO driver. + * + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +struct sh_pfc_chip { + struct sh_pfc *pfc; + struct gpio_chip gpio_chip; +}; + +static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct sh_pfc_chip, gpio_chip); +} + +static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) +{ + return gpio_to_pfc_chip(gc)->pfc; +} + +static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + if (!pfc) + goto err_out; + + spin_lock_irqsave(&pfc->lock, flags); + + if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_unlock; + + /* setup pin function here if no data is associated with pin */ + + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_unlock; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= pinmux_type; + + ret = 0; + err_unlock: + spin_unlock_irqrestore(&pfc->lock, flags); + err_out: + return ret; +} + +static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int pinmux_type; + + if (!pfc) + return; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&pfc->lock, flags); +} + +static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + BUG(); + else + sh_pfc_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + sh_gpio_set_value(pfc, offset, value); + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + return -EINVAL; + + return sh_pfc_read_bit(dr, bit); +} + +static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + return sh_gpio_get_value(gpio_to_pfc(gc), offset); +} + +static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); +} + +static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + pinmux_enum_t enum_id; + pinmux_enum_t *enum_ids; + int i, k, pos; + + pos = 0; + enum_id = 0; + while (1) { + pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); + if (pos <= 0 || !enum_id) + break; + + for (i = 0; i < pfc->gpio_irq_size; i++) { + enum_ids = pfc->gpio_irq[i].enum_ids; + for (k = 0; enum_ids[k]; k++) { + if (enum_ids[k] == enum_id) + return pfc->gpio_irq[i].irq; + } + } + } + + return -ENOSYS; +} + +static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) +{ + struct sh_pfc *pfc = chip->pfc; + struct gpio_chip *gc = &chip->gpio_chip; + + gc->request = sh_gpio_request; + gc->free = sh_gpio_free; + gc->direction_input = sh_gpio_direction_input; + gc->get = sh_gpio_get; + gc->direction_output = sh_gpio_direction_output; + gc->set = sh_gpio_set; + gc->to_irq = sh_gpio_to_irq; + + WARN_ON(pfc->first_gpio != 0); /* needs testing */ + + gc->label = pfc->name; + gc->owner = THIS_MODULE; + gc->base = pfc->first_gpio; + gc->ngpio = (pfc->last_gpio - pfc->first_gpio) + 1; +} + +int sh_pfc_register_gpiochip(struct sh_pfc *pfc) +{ + struct sh_pfc_chip *chip; + int ret; + + chip = kzalloc(sizeof(struct sh_pfc_chip), GFP_KERNEL); + if (unlikely(!chip)) + return -ENOMEM; + + chip->pfc = pfc; + + sh_pfc_gpio_setup(chip); + + ret = gpiochip_add(&chip->gpio_chip); + if (unlikely(ret < 0)) + kfree(chip); + + pr_info("%s handling gpio %d -> %d\n", + pfc->name, pfc->first_gpio, pfc->last_gpio); + + return ret; +} +EXPORT_SYMBOL_GPL(sh_pfc_register_gpiochip); + +static int sh_pfc_gpio_match(struct gpio_chip *gc, void *data) +{ + return !!strstr(gc->label, data); +} + +static int __devinit sh_pfc_gpio_probe(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip; + struct gpio_chip *gc; + + gc = gpiochip_find("_pfc", sh_pfc_gpio_match); + if (unlikely(!gc)) { + pr_err("Cant find gpio chip\n"); + return -ENODEV; + } + + chip = gpio_to_pfc_chip(gc); + platform_set_drvdata(pdev, chip); + + pr_info("attaching to GPIO chip %s\n", chip->pfc->name); + + return 0; +} + +static int __devexit sh_pfc_gpio_remove(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&chip->gpio_chip); + if (unlikely(ret < 0)) + return ret; + + kfree(chip); + return 0; +} + +static struct platform_driver sh_pfc_gpio_driver = { + .probe = sh_pfc_gpio_probe, + .remove = __devexit_p(sh_pfc_gpio_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device sh_pfc_gpio_device = { + .name = KBUILD_MODNAME, + .id = -1, +}; + +static int __init sh_pfc_gpio_init(void) +{ + int rc; + + rc = platform_driver_register(&sh_pfc_gpio_driver); + if (likely(!rc)) { + rc = platform_device_register(&sh_pfc_gpio_device); + if (unlikely(rc)) + platform_driver_unregister(&sh_pfc_gpio_driver); + } + + return rc; +} + +static void __exit sh_pfc_gpio_exit(void) +{ + platform_device_unregister(&sh_pfc_gpio_device); + platform_driver_unregister(&sh_pfc_gpio_driver); +} + +module_init(sh_pfc_gpio_init); +module_exit(sh_pfc_gpio_exit); + +MODULE_AUTHOR("Magnus Damm, Paul Mundt"); +MODULE_DESCRIPTION("GPIO driver for SuperH pin function controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pfc-gpio"); diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index ed1d8234f6ae..f522550fc32b 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -127,10 +127,10 @@ struct sh_pfc { /* XXX compat for now */ #define pinmux_info sh_pfc -/* drivers/sh/pfc-gpio.c */ +/* drivers/sh/pfc/gpio.c */ int sh_pfc_register_gpiochip(struct sh_pfc *pfc); -/* drivers/sh/pfc.c */ +/* drivers/sh/pfc/core.c */ int register_sh_pfc(struct sh_pfc *pfc); int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos); -- cgit v1.2.3-59-g8ed1b From 72c7afa10f272710028f244da65d35e571144085 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Jul 2012 11:59:29 +0900 Subject: sh: pfc: Dumb GPIO stringification. This implements fairly simplistic stringification of existing pinmux GPIOs for easy enum id -> string mapping, which will subsequently be used by the pinctrl support code. Signed-off-by: Paul Mundt --- include/linux/sh_pfc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index f522550fc32b..8c4cbcb9064d 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -11,6 +11,7 @@ #ifndef __SH_PFC_H #define __SH_PFC_H +#include #include typedef unsigned short pinmux_enum_t; @@ -37,10 +38,11 @@ enum { struct pinmux_gpio { pinmux_enum_t enum_id; pinmux_flag_t flags; + const char *name; }; #define PINMUX_GPIO(gpio, data_or_mark) \ - [gpio] = { .enum_id = data_or_mark, .flags = PINMUX_TYPE_NONE } + [gpio] = { .name = __stringify(gpio), .enum_id = data_or_mark, .flags = PINMUX_TYPE_NONE } #define PINMUX_DATA(data_or_mark, ids...) data_or_mark, ids, 0 -- cgit v1.2.3-59-g8ed1b From ca5481c68e9fbcea62bb3c78ae6cccf99ca8fb73 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Jul 2012 12:08:14 +0900 Subject: sh: pfc: Rudimentary pinctrl-backed GPIO support. This begins the migration of the PFC core to the pinctrl subsystem. Initial support is very basic, with the bulk of the implementation simply being nopped out in such a way to allow registration with the pinctrl core to succeed. The gpio chip driver is stripped down considerably now relying purely on pinctrl API calls to manage the bulk of its operations. This provides a basis for further PFC refactoring, including decoupling pin functions from the GPIO API, establishing pin groups, and so forth. These will all be dealt with incrementally so as to introduce as few growing and migratory pains to tree-wide PFC pinmux users today. When the interfaces have been well established and in-tree users have been migrated off of the legacy interfaces it will be possible to strip down the core considerably, leading to eventual drivers/pinctrl rehoming. Signed-off-by: Paul Mundt --- drivers/sh/pfc/Kconfig | 13 ++ drivers/sh/pfc/Makefile | 1 + drivers/sh/pfc/core.c | 81 +++++------ drivers/sh/pfc/gpio.c | 102 ++----------- drivers/sh/pfc/pinctrl.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sh_pfc.h | 5 +- 6 files changed, 440 insertions(+), 133 deletions(-) create mode 100644 drivers/sh/pfc/pinctrl.c diff --git a/drivers/sh/pfc/Kconfig b/drivers/sh/pfc/Kconfig index 95b04f4edb88..b743aaa543f8 100644 --- a/drivers/sh/pfc/Kconfig +++ b/drivers/sh/pfc/Kconfig @@ -4,8 +4,21 @@ config SH_PFC # XXX move off the gpio dependency depends on GENERIC_GPIO select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB + select PINCTRL_SH_PFC def_bool y +# +# Placeholder for now, rehome to drivers/pinctrl once the PFC APIs +# have settled. +# +config PINCTRL_SH_PFC + tristate "SuperH PFC pin controller driver" + depends on SH_PFC + select PINCTRL + select PINMUX + select PINCONF + select GENERIC_PINCONF + config GPIO_SH_PFC tristate "SuperH PFC GPIO support" depends on SH_PFC && GPIOLIB diff --git a/drivers/sh/pfc/Makefile b/drivers/sh/pfc/Makefile index d81707744b27..7916027cce37 100644 --- a/drivers/sh/pfc/Makefile +++ b/drivers/sh/pfc/Makefile @@ -1,2 +1,3 @@ obj-y += core.o +obj-$(CONFIG_PINCTRL_SH_PFC) += pinctrl.o obj-$(CONFIG_GPIO_SH_PFC) += gpio.o diff --git a/drivers/sh/pfc/core.c b/drivers/sh/pfc/core.c index ce4579ebd602..02e9f62e2b28 100644 --- a/drivers/sh/pfc/core.c +++ b/drivers/sh/pfc/core.c @@ -19,6 +19,7 @@ #include #include #include +#include static struct sh_pfc *sh_pfc __read_mostly; @@ -501,49 +502,6 @@ int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, } EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); -int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, - int new_pinmux_type) -{ - int pinmux_type; - int ret = -EINVAL; - - if (!pfc) - goto err_out; - - pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; - - switch (pinmux_type) { - case PINMUX_TYPE_GPIO: - break; - case PINMUX_TYPE_OUTPUT: - case PINMUX_TYPE_INPUT: - case PINMUX_TYPE_INPUT_PULLUP: - case PINMUX_TYPE_INPUT_PULLDOWN: - sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); - break; - default: - goto err_out; - } - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_out; - - if (sh_pfc_config_gpio(pfc, gpio, - new_pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - - pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[gpio].flags |= new_pinmux_type; - - ret = 0; - err_out: - return ret; -} -EXPORT_SYMBOL_GPL(sh_pfc_set_direction); - int register_sh_pfc(struct sh_pfc *pfc) { int (*initroutine)(struct sh_pfc *) = NULL; @@ -563,16 +521,49 @@ int register_sh_pfc(struct sh_pfc *pfc) spin_lock_init(&pfc->lock); + pinctrl_provide_dummies(); setup_data_regs(pfc); sh_pfc = pfc; - pr_info("%s support registered\n", pfc->name); + /* + * Initialize pinctrl bindings first + */ + initroutine = symbol_request(sh_pfc_register_pinctrl); + if (initroutine) { + ret = (*initroutine)(pfc); + symbol_put_addr(initroutine); + + if (unlikely(ret != 0)) + goto err; + } + + /* + * Then the GPIO chip + */ initroutine = symbol_request(sh_pfc_register_gpiochip); if (initroutine) { - (*initroutine)(pfc); + ret = (*initroutine)(pfc); symbol_put_addr(initroutine); + + /* + * If the GPIO chip fails to come up we still leave the + * PFC state as it is, given that there are already + * extant users of it that have succeeded by this point. + */ + if (unlikely(ret != 0)) { + pr_notice("failed to init GPIO chip, ignoring...\n"); + ret = 0; + } } + pr_info("%s support registered\n", pfc->name); + return 0; + +err: + pfc_iounmap(pfc); + sh_pfc = NULL; + + return ret; } diff --git a/drivers/sh/pfc/gpio.c b/drivers/sh/pfc/gpio.c index d74e5a96024b..f37f0c6d89b3 100644 --- a/drivers/sh/pfc/gpio.c +++ b/drivers/sh/pfc/gpio.c @@ -16,6 +16,7 @@ #include #include #include +#include struct sh_pfc_chip { struct sh_pfc *pfc; @@ -34,80 +35,12 @@ static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) { - struct sh_pfc *pfc = gpio_to_pfc(gc); - struct pinmux_data_reg *dummy; - unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; - - if (!pfc) - goto err_out; - - spin_lock_irqsave(&pfc->lock, flags); - - if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err_unlock; - - /* setup pin function here if no data is associated with pin */ - - if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) - pinmux_type = PINMUX_TYPE_FUNCTION; - else - pinmux_type = PINMUX_TYPE_GPIO; - - if (pinmux_type == PINMUX_TYPE_FUNCTION) { - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_unlock; - - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - } - - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= pinmux_type; - - ret = 0; - err_unlock: - spin_unlock_irqrestore(&pfc->lock, flags); - err_out: - return ret; + return pinctrl_request_gpio(offset); } static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) { - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int pinmux_type; - - if (!pfc) - return; - - spin_lock_irqsave(&pfc->lock, flags); - - pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; - sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; - - spin_unlock_irqrestore(&pfc->lock, flags); -} - -static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; + pinctrl_free_gpio(offset); } static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) @@ -121,22 +54,6 @@ static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) sh_pfc_write_bit(dr, bit, value); } -static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, - int value) -{ - struct sh_pfc *pfc = gpio_to_pfc(gc); - unsigned long flags; - int ret; - - sh_gpio_set_value(pfc, offset, value); - - spin_lock_irqsave(&pfc->lock, flags); - ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; -} - static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) { struct pinmux_data_reg *dr = NULL; @@ -148,6 +65,19 @@ static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) return sh_pfc_read_bit(dr, bit); } +static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_gpio_direction_input(offset); +} + +static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); + + return pinctrl_gpio_direction_output(offset); +} + static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) { return sh_gpio_get_value(gpio_to_pfc(gc), offset); diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c new file mode 100644 index 000000000000..6008328594ff --- /dev/null +++ b/drivers/sh/pfc/pinctrl.c @@ -0,0 +1,371 @@ +/* + * SuperH Pin Function Controller pinmux support. + * + * Copyright (C) 2012 Paul Mundt + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sh_pfc_pinctrl { + struct pinctrl_dev *pctl; + struct sh_pfc *pfc; + + struct pinctrl_pin_desc *pads; + unsigned int nr_pads; +}; + +static struct sh_pfc_pinctrl *sh_pfc_pmx; + +/* + * No group support yet + */ +static int sh_pfc_get_noop_count(struct pinctrl_dev *pctldev) +{ + return 0; +} + +static const char *sh_pfc_get_noop_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return NULL; +} + +static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, + const unsigned **pins, unsigned *num_pins) +{ + return -ENOTSUPP; +} + +static struct pinctrl_ops sh_pfc_pinctrl_ops = { + .get_groups_count = sh_pfc_get_noop_count, + .get_group_name = sh_pfc_get_noop_name, + .get_group_pins = sh_pfc_get_group_pins, +}; + + +/* + * No function support yet + */ +static int sh_pfc_get_function_groups(struct pinctrl_dev *pctldev, unsigned func, + const char * const **groups, + unsigned * const num_groups) +{ + return 0; +} + +static int sh_pfc_noop_enable(struct pinctrl_dev *pctldev, unsigned func, + unsigned group) +{ + return 0; +} + +static void sh_pfc_noop_disable(struct pinctrl_dev *pctldev, unsigned func, + unsigned group) +{ +} + +static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + spin_lock_irqsave(&pfc->lock, flags); + + if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err; + + /* setup pin function here if no data is associated with pin */ + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) { + pinmux_type = PINMUX_TYPE_FUNCTION; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + goto err; + } else + pinmux_type = PINMUX_TYPE_GPIO; + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= pinmux_type; + + ret = 0; + +err: + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void sh_pfc_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + unsigned long flags; + int pinmux_type; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&pfc->lock, flags); +} + +static int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, bool input) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + unsigned long flags; + int pinmux_type, new_pinmux_type; + int ret = -EINVAL; + + new_pinmux_type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err; + } + + if (sh_pfc_config_gpio(pfc, offset, + new_pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err; + + if (sh_pfc_config_gpio(pfc, offset, + new_pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= new_pinmux_type; + + ret = 0; + +err: + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static struct pinmux_ops sh_pfc_pinmux_ops = { + .get_functions_count = sh_pfc_get_noop_count, + .get_function_name = sh_pfc_get_noop_name, + .get_function_groups = sh_pfc_get_function_groups, + .enable = sh_pfc_noop_enable, + .disable = sh_pfc_noop_disable, + .gpio_request_enable = sh_pfc_gpio_request_enable, + .gpio_disable_free = sh_pfc_gpio_disable_free, + .gpio_set_direction = sh_pfc_gpio_set_direction, +}; + +static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + return -ENOTSUPP; +} + +static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long config) +{ + return -EINVAL; +} + +static struct pinconf_ops sh_pfc_pinconf_ops = { + .is_generic = true, + .pin_config_get = sh_pfc_pinconf_get, + .pin_config_set = sh_pfc_pinconf_set, +}; + +static struct pinctrl_gpio_range sh_pfc_gpio_range = { + .name = KBUILD_MODNAME, + .id = 0, +}; + +static struct pinctrl_desc sh_pfc_pinctrl_desc = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .pctlops = &sh_pfc_pinctrl_ops, + .pmxops = &sh_pfc_pinmux_ops, + .confops = &sh_pfc_pinconf_ops, +}; + +int sh_pfc_register_pinctrl(struct sh_pfc *pfc) +{ + sh_pfc_pmx = kmalloc(sizeof(struct sh_pfc_pinctrl), GFP_KERNEL); + if (unlikely(!sh_pfc_pmx)) + return -ENOMEM; + + sh_pfc_pmx->pfc = pfc; + + return 0; +} + +/* pinmux ranges -> pinctrl pin descs */ +static int __devinit sh_pfc_map_gpios(struct sh_pfc *pfc, + struct sh_pfc_pinctrl *pmx) +{ + int i; + + pmx->nr_pads = pfc->last_gpio - pfc->first_gpio + 1; + + pmx->pads = kmalloc(sizeof(struct pinctrl_pin_desc) * pmx->nr_pads, + GFP_KERNEL); + if (unlikely(!pmx->pads)) { + pmx->nr_pads = 0; + return -ENOMEM; + } + + /* + * We don't necessarily have a 1:1 mapping between pin and linux + * GPIO number, as the latter maps to the associated enum_id. + * Care needs to be taken to translate back to pin space when + * dealing with any pin configurations. + */ + for (i = 0; i < pmx->nr_pads; i++) { + struct pinctrl_pin_desc *pin = pmx->pads + i; + struct pinmux_gpio *gpio = pfc->gpios + i; + + pin->number = pfc->first_gpio + i; + pin->name = gpio->name; + } + + sh_pfc_pinctrl_desc.pins = pmx->pads; + sh_pfc_pinctrl_desc.npins = pmx->nr_pads; + + return 0; +} + +static int __devinit sh_pfc_pinctrl_probe(struct platform_device *pdev) +{ + struct sh_pfc *pfc; + int ret; + + if (unlikely(!sh_pfc_pmx)) + return -ENODEV; + + pfc = sh_pfc_pmx->pfc; + + ret = sh_pfc_map_gpios(pfc, sh_pfc_pmx); + if (unlikely(ret != 0)) + return ret; + + sh_pfc_pmx->pctl = pinctrl_register(&sh_pfc_pinctrl_desc, &pdev->dev, + sh_pfc_pmx); + if (IS_ERR(sh_pfc_pmx->pctl)) { + ret = PTR_ERR(sh_pfc_pmx->pctl); + goto out; + } + + sh_pfc_gpio_range.npins = pfc->last_gpio - pfc->first_gpio + 1; + sh_pfc_gpio_range.base = pfc->first_gpio; + sh_pfc_gpio_range.pin_base = pfc->first_gpio; + + pinctrl_add_gpio_range(sh_pfc_pmx->pctl, &sh_pfc_gpio_range); + + platform_set_drvdata(pdev, sh_pfc_pmx); + + return 0; + +out: + kfree(sh_pfc_pmx->pads); + kfree(sh_pfc_pmx); + return ret; +} + +static int __devexit sh_pfc_pinctrl_remove(struct platform_device *pdev) +{ + struct sh_pfc_pinctrl *pmx = platform_get_drvdata(pdev); + + pinctrl_remove_gpio_range(pmx->pctl, &sh_pfc_gpio_range); + pinctrl_unregister(pmx->pctl); + + platform_set_drvdata(pdev, NULL); + + kfree(sh_pfc_pmx->pads); + kfree(sh_pfc_pmx); + + return 0; +} + +static struct platform_driver sh_pfc_pinctrl_driver = { + .probe = sh_pfc_pinctrl_probe, + .remove = __devexit_p(sh_pfc_pinctrl_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device sh_pfc_pinctrl_device = { + .name = KBUILD_MODNAME, + .id = -1, +}; + +static int __init sh_pfc_pinctrl_init(void) +{ + int rc; + + rc = platform_driver_register(&sh_pfc_pinctrl_driver); + if (likely(!rc)) { + rc = platform_device_register(&sh_pfc_pinctrl_device); + if (unlikely(rc)) + platform_driver_unregister(&sh_pfc_pinctrl_driver); + } + + return rc; +} + +static void __exit sh_pfc_pinctrl_exit(void) +{ + platform_driver_unregister(&sh_pfc_pinctrl_driver); +} + +subsys_initcall(sh_pfc_pinctrl_init); +module_exit(sh_pfc_pinctrl_exit); diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h index 8c4cbcb9064d..c19a0925829a 100644 --- a/include/linux/sh_pfc.h +++ b/include/linux/sh_pfc.h @@ -132,6 +132,9 @@ struct sh_pfc { /* drivers/sh/pfc/gpio.c */ int sh_pfc_register_gpiochip(struct sh_pfc *pfc); +/* drivers/sh/pfc/pinctrl.c */ +int sh_pfc_register_pinctrl(struct sh_pfc *pfc); + /* drivers/sh/pfc/core.c */ int register_sh_pfc(struct sh_pfc *pfc); @@ -144,8 +147,6 @@ int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, pinmux_enum_t *enum_idp); int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, int cfg_mode); -int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, - int new_pinmux_type); /* xxx */ static inline int register_pinmux(struct pinmux_info *pip) -- cgit v1.2.3-59-g8ed1b From d93a891ff9e21a017e4d66d29784614768db567a Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 11 Jul 2012 17:17:10 +0900 Subject: sh: pfc: pinctrl legacy function support. This maps out all of the function types to pinctrl function groups. Presently this is restricted to one pin per function to maintain compatability with legacy behaviour. This will be extended as groups are introduced and exiting users migrated. Signed-off-by: Paul Mundt --- drivers/sh/pfc/pinctrl.c | 165 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 132 insertions(+), 33 deletions(-) diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c index 6008328594ff..f62659aa68cc 100644 --- a/drivers/sh/pfc/pinctrl.c +++ b/drivers/sh/pfc/pinctrl.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -25,8 +26,13 @@ struct sh_pfc_pinctrl { struct pinctrl_dev *pctl; struct sh_pfc *pfc; + struct pinmux_gpio **functions; + unsigned int nr_functions; + struct pinctrl_pin_desc *pads; unsigned int nr_pads; + + spinlock_t lock; }; static struct sh_pfc_pinctrl *sh_pfc_pmx; @@ -57,14 +63,30 @@ static struct pinctrl_ops sh_pfc_pinctrl_ops = { .get_group_pins = sh_pfc_get_group_pins, }; +static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + return pmx->nr_functions; +} + +static const char *sh_pfc_get_function_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + return pmx->functions[selector]->name; +} -/* - * No function support yet - */ static int sh_pfc_get_function_groups(struct pinctrl_dev *pctldev, unsigned func, const char * const **groups, unsigned * const num_groups) { + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + *groups = &pmx->functions[func]->name; + *num_groups = 1; + return 0; } @@ -79,41 +101,50 @@ static void sh_pfc_noop_disable(struct pinctrl_dev *pctldev, unsigned func, { } +static inline int sh_pfc_config_function(struct sh_pfc *pfc, unsigned offset) +{ + if (sh_pfc_config_gpio(pfc, offset, + PINMUX_TYPE_FUNCTION, + GPIO_CFG_DRYRUN) != 0) + return -EINVAL; + + if (sh_pfc_config_gpio(pfc, offset, + PINMUX_TYPE_FUNCTION, + GPIO_CFG_REQ) != 0) + return -EINVAL; + + return 0; +} + static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) { struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); struct sh_pfc *pfc = pmx->pfc; - struct pinmux_data_reg *dummy; unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; + int ret, pinmux_type; spin_lock_irqsave(&pfc->lock, flags); - if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err; - - /* setup pin function here if no data is associated with pin */ - if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) { - pinmux_type = PINMUX_TYPE_FUNCTION; - - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err; + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; - if (sh_pfc_config_gpio(pfc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) + switch (pinmux_type) { + case PINMUX_TYPE_FUNCTION: + pr_notice_once("Use of GPIO API for function requests is " + "deprecated, convert to pinctrl\n"); + /* handle for now */ + ret = sh_pfc_config_function(pfc, offset); + if (unlikely(ret < 0)) goto err; - } else - pinmux_type = PINMUX_TYPE_GPIO; - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= pinmux_type; + break; + case PINMUX_TYPE_GPIO: + break; + default: + pr_err("Unsupported mux type (%d), bailing...\n", pinmux_type); + return -ENOTSUPP; + } ret = 0; @@ -138,9 +169,6 @@ static void sh_pfc_gpio_disable_free(struct pinctrl_dev *pctldev, sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; - spin_unlock_irqrestore(&pfc->lock, flags); } @@ -195,8 +223,8 @@ err: } static struct pinmux_ops sh_pfc_pinmux_ops = { - .get_functions_count = sh_pfc_get_noop_count, - .get_function_name = sh_pfc_get_noop_name, + .get_functions_count = sh_pfc_get_functions_count, + .get_function_name = sh_pfc_get_function_name, .get_function_groups = sh_pfc_get_function_groups, .enable = sh_pfc_noop_enable, .disable = sh_pfc_noop_disable, @@ -208,6 +236,13 @@ static struct pinmux_ops sh_pfc_pinmux_ops = { static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { + enum pin_config_param param = (enum pin_config_param)(*config); + + switch (param) { + default: + break; + } + return -ENOTSUPP; } @@ -238,19 +273,44 @@ static struct pinctrl_desc sh_pfc_pinctrl_desc = { int sh_pfc_register_pinctrl(struct sh_pfc *pfc) { - sh_pfc_pmx = kmalloc(sizeof(struct sh_pfc_pinctrl), GFP_KERNEL); + sh_pfc_pmx = kzalloc(sizeof(struct sh_pfc_pinctrl), GFP_KERNEL); if (unlikely(!sh_pfc_pmx)) return -ENOMEM; + spin_lock_init(&sh_pfc_pmx->lock); + sh_pfc_pmx->pfc = pfc; return 0; } +static inline void __devinit sh_pfc_map_one_gpio(struct sh_pfc *pfc, + struct sh_pfc_pinctrl *pmx, + struct pinmux_gpio *gpio, + unsigned offset) +{ + struct pinmux_data_reg *dummy; + unsigned long flags; + int bit; + + gpio->flags &= ~PINMUX_FLAG_TYPE; + + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &bit) == 0) + gpio->flags |= PINMUX_TYPE_GPIO; + else { + gpio->flags |= PINMUX_TYPE_FUNCTION; + + spin_lock_irqsave(&pmx->lock, flags); + pmx->nr_functions++; + spin_unlock_irqrestore(&pmx->lock, flags); + } +} + /* pinmux ranges -> pinctrl pin descs */ static int __devinit sh_pfc_map_gpios(struct sh_pfc *pfc, struct sh_pfc_pinctrl *pmx) { + unsigned long flags; int i; pmx->nr_pads = pfc->last_gpio - pfc->first_gpio + 1; @@ -262,6 +322,8 @@ static int __devinit sh_pfc_map_gpios(struct sh_pfc *pfc, return -ENOMEM; } + spin_lock_irqsave(&pfc->lock, flags); + /* * We don't necessarily have a 1:1 mapping between pin and linux * GPIO number, as the latter maps to the associated enum_id. @@ -274,14 +336,43 @@ static int __devinit sh_pfc_map_gpios(struct sh_pfc *pfc, pin->number = pfc->first_gpio + i; pin->name = gpio->name; + + sh_pfc_map_one_gpio(pfc, pmx, gpio, i); } + spin_unlock_irqrestore(&pfc->lock, flags); + sh_pfc_pinctrl_desc.pins = pmx->pads; sh_pfc_pinctrl_desc.npins = pmx->nr_pads; return 0; } +static int __devinit sh_pfc_map_functions(struct sh_pfc *pfc, + struct sh_pfc_pinctrl *pmx) +{ + unsigned long flags; + int i, fn; + + pmx->functions = kzalloc(pmx->nr_functions * sizeof(void *), + GFP_KERNEL); + if (unlikely(!pmx->functions)) + return -ENOMEM; + + spin_lock_irqsave(&pmx->lock, flags); + + for (i = fn = 0; i < pmx->nr_pads; i++) { + struct pinmux_gpio *gpio = pfc->gpios + i; + + if ((gpio->flags & PINMUX_FLAG_TYPE) == PINMUX_TYPE_FUNCTION) + pmx->functions[fn++] = gpio; + } + + spin_unlock_irqrestore(&pmx->lock, flags); + + return 0; +} + static int __devinit sh_pfc_pinctrl_probe(struct platform_device *pdev) { struct sh_pfc *pfc; @@ -296,11 +387,15 @@ static int __devinit sh_pfc_pinctrl_probe(struct platform_device *pdev) if (unlikely(ret != 0)) return ret; + ret = sh_pfc_map_functions(pfc, sh_pfc_pmx); + if (unlikely(ret != 0)) + goto free_pads; + sh_pfc_pmx->pctl = pinctrl_register(&sh_pfc_pinctrl_desc, &pdev->dev, sh_pfc_pmx); if (IS_ERR(sh_pfc_pmx->pctl)) { ret = PTR_ERR(sh_pfc_pmx->pctl); - goto out; + goto free_functions; } sh_pfc_gpio_range.npins = pfc->last_gpio - pfc->first_gpio + 1; @@ -313,9 +408,12 @@ static int __devinit sh_pfc_pinctrl_probe(struct platform_device *pdev) return 0; -out: +free_functions: + kfree(sh_pfc_pmx->functions); +free_pads: kfree(sh_pfc_pmx->pads); kfree(sh_pfc_pmx); + return ret; } @@ -328,6 +426,7 @@ static int __devexit sh_pfc_pinctrl_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); + kfree(sh_pfc_pmx->functions); kfree(sh_pfc_pmx->pads); kfree(sh_pfc_pmx); -- cgit v1.2.3-59-g8ed1b From a2d3afffd98f9cafa87d7efa57cba0d86abfad87 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 11 Jul 2012 17:21:04 +0900 Subject: sh: pfc: Make pr_fmt consistent across pfc drivers. Signed-off-by: Paul Mundt --- drivers/sh/pfc/core.c | 2 +- drivers/sh/pfc/gpio.c | 2 +- drivers/sh/pfc/pinctrl.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/sh/pfc/core.c b/drivers/sh/pfc/core.c index 02e9f62e2b28..b81d33535d24 100644 --- a/drivers/sh/pfc/core.c +++ b/drivers/sh/pfc/core.c @@ -8,7 +8,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt #include #include diff --git a/drivers/sh/pfc/gpio.c b/drivers/sh/pfc/gpio.c index f37f0c6d89b3..62bca98474a9 100644 --- a/drivers/sh/pfc/gpio.c +++ b/drivers/sh/pfc/gpio.c @@ -8,7 +8,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt #include #include diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c index f62659aa68cc..fe1750eb6f1f 100644 --- a/drivers/sh/pfc/pinctrl.c +++ b/drivers/sh/pfc/pinctrl.c @@ -7,7 +7,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt #include #include -- cgit v1.2.3-59-g8ed1b From 159ac0737e66f20a913248668d67444afde53b53 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 17 Jul 2012 15:18:37 +0900 Subject: sh: pfc: Error out on pinctrl init resolution failure. pinctrl support is required for correct operation, failure to locate the init routine is fatal. Signed-off-by: Paul Mundt --- drivers/sh/pfc/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/sh/pfc/core.c b/drivers/sh/pfc/core.c index b81d33535d24..68169373c98b 100644 --- a/drivers/sh/pfc/core.c +++ b/drivers/sh/pfc/core.c @@ -536,6 +536,9 @@ int register_sh_pfc(struct sh_pfc *pfc) if (unlikely(ret != 0)) goto err; + } else { + pr_err("failed to initialize pinctrl bindings\n"); + goto err; } /* -- cgit v1.2.3-59-g8ed1b From 1acbbb4ed846ec6a546834c86a3bd4a5bad4ee4d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 17 Jul 2012 15:21:47 +0900 Subject: sh: pfc: Export pinctrl binding init symbol. symbol_request() requires the registration symbol to be exported, make sure it is. Signed-off-by: Paul Mundt --- drivers/sh/pfc/pinctrl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c index fe1750eb6f1f..4eaf24b36a81 100644 --- a/drivers/sh/pfc/pinctrl.c +++ b/drivers/sh/pfc/pinctrl.c @@ -283,6 +283,7 @@ int sh_pfc_register_pinctrl(struct sh_pfc *pfc) return 0; } +EXPORT_SYMBOL_GPL(sh_pfc_register_pinctrl); static inline void __devinit sh_pfc_map_one_gpio(struct sh_pfc *pfc, struct sh_pfc_pinctrl *pmx, -- cgit v1.2.3-59-g8ed1b From e3e79454a7c81a5e48c6d6b31068b12936e7d780 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 17 Jul 2012 15:23:11 +0900 Subject: sh: pfc: Ignore pinmux GPIOs with invalid enum IDs. If we encounter invalid entries in the pinmux GPIO range, make sure we've still got a dummy pin definition but don't otherwise map it. Signed-off-by: Paul Mundt --- drivers/sh/pfc/pinctrl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c index 4eaf24b36a81..e79e379917fc 100644 --- a/drivers/sh/pfc/pinctrl.c +++ b/drivers/sh/pfc/pinctrl.c @@ -338,6 +338,10 @@ static int __devinit sh_pfc_map_gpios(struct sh_pfc *pfc, pin->number = pfc->first_gpio + i; pin->name = gpio->name; + /* XXX */ + if (unlikely(!gpio->enum_id)) + continue; + sh_pfc_map_one_gpio(pfc, pmx, gpio, i); } -- cgit v1.2.3-59-g8ed1b From e3f805e897320c3c25d7d5f67e0fb59c5a7199bc Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 17 Jul 2012 15:48:18 +0900 Subject: sh: pfc: pinctrl legacy group support. This follows the function support by simply doing 1 pin per group encapsulation in order to keep with legacy behaviour. This will be built on incrementally as SoCs define their own pin groups. Signed-off-by: Paul Mundt --- drivers/sh/pfc/pinctrl.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c index e79e379917fc..45e80ee70766 100644 --- a/drivers/sh/pfc/pinctrl.c +++ b/drivers/sh/pfc/pinctrl.c @@ -37,29 +37,35 @@ struct sh_pfc_pinctrl { static struct sh_pfc_pinctrl *sh_pfc_pmx; -/* - * No group support yet - */ -static int sh_pfc_get_noop_count(struct pinctrl_dev *pctldev) +static int sh_pfc_get_groups_count(struct pinctrl_dev *pctldev) { - return 0; + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + return pmx->nr_pads; } -static const char *sh_pfc_get_noop_name(struct pinctrl_dev *pctldev, +static const char *sh_pfc_get_group_name(struct pinctrl_dev *pctldev, unsigned selector) { - return NULL; + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + return pmx->pads[selector].name; } static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, const unsigned **pins, unsigned *num_pins) { - return -ENOTSUPP; + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + + *pins = &pmx->pads[group].number; + *num_pins = 1; + + return 0; } static struct pinctrl_ops sh_pfc_pinctrl_ops = { - .get_groups_count = sh_pfc_get_noop_count, - .get_group_name = sh_pfc_get_noop_name, + .get_groups_count = sh_pfc_get_groups_count, + .get_group_name = sh_pfc_get_group_name, .get_group_pins = sh_pfc_get_group_pins, }; -- cgit v1.2.3-59-g8ed1b From 5440711073157576eb4658c19019b66b25140860 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 20 Jul 2012 16:18:21 +0900 Subject: sh: pfc: Prefer DRV_NAME over KBUILD_MODNAME. While this code is still being shuffled around the KBUILD_MODNAME value isn't particularly useful, switch to something a bit more useful. Signed-off-by: Paul Mundt --- drivers/sh/pfc/pinctrl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c index 45e80ee70766..900afa5f4069 100644 --- a/drivers/sh/pfc/pinctrl.c +++ b/drivers/sh/pfc/pinctrl.c @@ -7,7 +7,9 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ -#define pr_fmt(fmt) "sh_pfc " KBUILD_MODNAME ": " fmt +#define DRV_NAME "pinctrl-sh_pfc" + +#define pr_fmt(fmt) DRV_NAME " " KBUILD_MODNAME ": " fmt #include #include @@ -265,12 +267,12 @@ static struct pinconf_ops sh_pfc_pinconf_ops = { }; static struct pinctrl_gpio_range sh_pfc_gpio_range = { - .name = KBUILD_MODNAME, + .name = DRV_NAME, .id = 0, }; static struct pinctrl_desc sh_pfc_pinctrl_desc = { - .name = KBUILD_MODNAME, + .name = DRV_NAME, .owner = THIS_MODULE, .pctlops = &sh_pfc_pinctrl_ops, .pmxops = &sh_pfc_pinmux_ops, @@ -448,13 +450,13 @@ static struct platform_driver sh_pfc_pinctrl_driver = { .probe = sh_pfc_pinctrl_probe, .remove = __devexit_p(sh_pfc_pinctrl_remove), .driver = { - .name = KBUILD_MODNAME, + .name = DRV_NAME, .owner = THIS_MODULE, }, }; static struct platform_device sh_pfc_pinctrl_device = { - .name = KBUILD_MODNAME, + .name = DRV_NAME, .id = -1, }; -- cgit v1.2.3-59-g8ed1b From fdd85ec3eb8cc1b663678a3efa16ee59a32e0277 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 20 Jul 2012 16:39:09 +0900 Subject: sh: pfc: pin config get/set support. This implements simple support for adjusting the pin config value via the pinctrl API. The pinconf-generic code is abandoned for now until we've got a chance to revamp the pinmux_type state tracking that's needed by legacy code. Signed-off-by: Paul Mundt --- drivers/sh/pfc/Kconfig | 1 - drivers/sh/pfc/pinctrl.c | 147 +++++++++++++++++++++++++++++++---------------- 2 files changed, 97 insertions(+), 51 deletions(-) diff --git a/drivers/sh/pfc/Kconfig b/drivers/sh/pfc/Kconfig index b743aaa543f8..804f9ad1bf4a 100644 --- a/drivers/sh/pfc/Kconfig +++ b/drivers/sh/pfc/Kconfig @@ -17,7 +17,6 @@ config PINCTRL_SH_PFC select PINCTRL select PINMUX select PINCONF - select GENERIC_PINCONF config GPIO_SH_PFC tristate "SuperH PFC GPIO support" diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c index 900afa5f4069..0802b6c0d653 100644 --- a/drivers/sh/pfc/pinctrl.c +++ b/drivers/sh/pfc/pinctrl.c @@ -65,10 +65,17 @@ static int sh_pfc_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, return 0; } +static void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned offset) +{ + seq_printf(s, "%s", DRV_NAME); +} + static struct pinctrl_ops sh_pfc_pinctrl_ops = { .get_groups_count = sh_pfc_get_groups_count, .get_group_name = sh_pfc_get_group_name, .get_group_pins = sh_pfc_get_group_pins, + .pin_dbg_show = sh_pfc_pin_dbg_show, }; static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) @@ -124,6 +131,59 @@ static inline int sh_pfc_config_function(struct sh_pfc *pfc, unsigned offset) return 0; } +static int sh_pfc_reconfig_pin(struct sh_pfc *pfc, unsigned offset, + int new_type) +{ + unsigned long flags; + int pinmux_type; + int ret = -EINVAL; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + + /* + * See if the present config needs to first be de-configured. + */ + switch (pinmux_type) { + case PINMUX_TYPE_GPIO: + break; + case PINMUX_TYPE_OUTPUT: + case PINMUX_TYPE_INPUT: + case PINMUX_TYPE_INPUT_PULLUP: + case PINMUX_TYPE_INPUT_PULLDOWN: + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + break; + default: + goto err; + } + + /* + * Dry run + */ + if (sh_pfc_config_gpio(pfc, offset, new_type, + GPIO_CFG_DRYRUN) != 0) + goto err; + + /* + * Request + */ + if (sh_pfc_config_gpio(pfc, offset, new_type, + GPIO_CFG_REQ) != 0) + goto err; + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= new_type; + + ret = 0; + +err: + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + + static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset) @@ -185,49 +245,9 @@ static int sh_pfc_gpio_set_direction(struct pinctrl_dev *pctldev, unsigned offset, bool input) { struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); - struct sh_pfc *pfc = pmx->pfc; - unsigned long flags; - int pinmux_type, new_pinmux_type; - int ret = -EINVAL; + int type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; - new_pinmux_type = input ? PINMUX_TYPE_INPUT : PINMUX_TYPE_OUTPUT; - - spin_lock_irqsave(&pfc->lock, flags); - - pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; - - switch (pinmux_type) { - case PINMUX_TYPE_GPIO: - break; - case PINMUX_TYPE_OUTPUT: - case PINMUX_TYPE_INPUT: - case PINMUX_TYPE_INPUT_PULLUP: - case PINMUX_TYPE_INPUT_PULLDOWN: - sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); - break; - default: - goto err; - } - - if (sh_pfc_config_gpio(pfc, offset, - new_pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err; - - if (sh_pfc_config_gpio(pfc, offset, - new_pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - - pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - pfc->gpios[offset].flags |= new_pinmux_type; - - ret = 0; - -err: - spin_unlock_irqrestore(&pfc->lock, flags); - - return ret; + return sh_pfc_reconfig_pin(pmx->pfc, offset, type); } static struct pinmux_ops sh_pfc_pinmux_ops = { @@ -244,26 +264,53 @@ static struct pinmux_ops sh_pfc_pinmux_ops = { static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { - enum pin_config_param param = (enum pin_config_param)(*config); + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; - switch (param) { - default: - break; - } + *config = pfc->gpios[pin].flags & PINMUX_FLAG_TYPE; - return -ENOTSUPP; + return 0; } static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long config) { - return -EINVAL; + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct sh_pfc *pfc = pmx->pfc; + + /* Validate the new type */ + if (config >= PINMUX_FLAG_TYPE) + return -EINVAL; + + return sh_pfc_reconfig_pin(pmx->pfc, pin, config); +} + +static void sh_pfc_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin) +{ + const char *pinmux_type_str[] = { + [PINMUX_TYPE_NONE] = "none", + [PINMUX_TYPE_FUNCTION] = "function", + [PINMUX_TYPE_GPIO] = "gpio", + [PINMUX_TYPE_OUTPUT] = "output", + [PINMUX_TYPE_INPUT] = "input", + [PINMUX_TYPE_INPUT_PULLUP] = "input bias pull up", + [PINMUX_TYPE_INPUT_PULLDOWN] = "input bias pull down", + }; + unsigned long config; + int rc; + + rc = sh_pfc_pinconf_get(pctldev, pin, &config); + if (unlikely(rc != 0)) + return; + + seq_printf(s, " %s", pinmux_type_str[config]); } static struct pinconf_ops sh_pfc_pinconf_ops = { - .is_generic = true, .pin_config_get = sh_pfc_pinconf_get, .pin_config_set = sh_pfc_pinconf_set, + .pin_config_dbg_show = sh_pfc_pinconf_dbg_show, }; static struct pinctrl_gpio_range sh_pfc_gpio_range = { -- cgit v1.2.3-59-g8ed1b