diff options
Diffstat (limited to 'arch/sh/kernel/cpu')
29 files changed, 1433 insertions, 478 deletions
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile index 59d5b748752f..fb5dac069382 100644 --- a/arch/sh/kernel/cpu/Makefile +++ b/arch/sh/kernel/cpu/Makefile @@ -8,6 +8,5 @@ obj-$(CONFIG_CPU_SH2) += sh2/ obj-$(CONFIG_CPU_SH3) += sh3/ obj-$(CONFIG_CPU_SH4) += sh4/ -obj-$(CONFIG_SH_RTC) += rtc.o obj-$(CONFIG_UBC_WAKEUP) += ubc.o obj-$(CONFIG_SH_ADC) += adc.o diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c index 97fa37f42b84..51ec64cdf348 100644 --- a/arch/sh/kernel/cpu/clock.c +++ b/arch/sh/kernel/cpu/clock.c @@ -1,7 +1,7 @@ /* * arch/sh/kernel/cpu/clock.c - SuperH clock framework * - * Copyright (C) 2005 Paul Mundt + * Copyright (C) 2005, 2006 Paul Mundt * * This clock framework is derived from the OMAP version by: * @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/list.h> #include <linux/kref.h> #include <linux/seq_file.h> @@ -24,7 +25,7 @@ static LIST_HEAD(clock_list); static DEFINE_SPINLOCK(clock_lock); -static DECLARE_MUTEX(clock_list_sem); +static DEFINE_MUTEX(clock_list_sem); /* * Each subtype is expected to define the init routines for these clocks, @@ -140,21 +141,21 @@ void clk_disable(struct clk *clk) int clk_register(struct clk *clk) { - down(&clock_list_sem); + mutex_lock(&clock_list_sem); list_add(&clk->node, &clock_list); kref_init(&clk->kref); - up(&clock_list_sem); + mutex_unlock(&clock_list_sem); return 0; } void clk_unregister(struct clk *clk) { - down(&clock_list_sem); + mutex_lock(&clock_list_sem); list_del(&clk->node); - up(&clock_list_sem); + mutex_unlock(&clock_list_sem); } inline unsigned long clk_get_rate(struct clk *clk) @@ -198,14 +199,14 @@ struct clk *clk_get(const char *id) { struct clk *p, *clk = ERR_PTR(-ENOENT); - down(&clock_list_sem); + mutex_lock(&clock_list_sem); list_for_each_entry(p, &clock_list, node) { if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { clk = p; break; } } - up(&clock_list_sem); + mutex_unlock(&clock_list_sem); return clk; } @@ -225,7 +226,7 @@ int __init clk_init(void) { int i, ret = 0; - BUG_ON(unlikely(!master_clk.rate)); + BUG_ON(!master_clk.rate); for (i = 0; i < ARRAY_SIZE(onchip_clocks); i++) { struct clk *clk = onchip_clocks[i]; diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c index 868e68b28880..bfb90eb0b7a6 100644 --- a/arch/sh/kernel/cpu/init.c +++ b/arch/sh/kernel/cpu/init.c @@ -4,6 +4,7 @@ * CPU init code * * Copyright (C) 2002, 2003 Paul Mundt + * Copyright (C) 2003 Richard Curnow * * 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 @@ -13,6 +14,7 @@ #include <linux/kernel.h> #include <asm/processor.h> #include <asm/uaccess.h> +#include <asm/page.h> #include <asm/system.h> #include <asm/cacheflush.h> #include <asm/cache.h> @@ -51,7 +53,15 @@ static void __init cache_init(void) ccr = ctrl_inl(CCR); /* - * If the cache is already enabled .. flush it. + * At this point we don't know whether the cache is enabled or not - a + * bootloader may have enabled it. There are at least 2 things that + * could be dirty in the cache at this point: + * 1. kernel command line set up by boot loader + * 2. spilled registers from the prolog of this function + * => before re-initialising the cache, we must do a purge of the whole + * cache out to memory for safety. As long as nothing is spilled + * during the loop to lines that have already been done, this is safe. + * - RPC */ if (ccr & CCR_CACHE_ENABLE) { unsigned long ways, waysize, addrstart; @@ -98,6 +108,8 @@ static void __init cache_init(void) /* Force EMODE if possible */ if (cpu_data->dcache.ways > 1) flags |= CCR_CACHE_EMODE; + else + flags &= ~CCR_CACHE_EMODE; #endif #ifdef CONFIG_SH_WRITETHROUGH @@ -112,6 +124,9 @@ static void __init cache_init(void) /* Turn on OCRAM -- halve the OC */ flags |= CCR_CACHE_ORA; cpu_data->dcache.sets >>= 1; + + cpu_data->dcache.way_size = cpu_data->dcache.sets * + cpu_data->dcache.linesz; #endif ctrl_outl(flags, CCR); @@ -184,6 +199,10 @@ asmlinkage void __init sh_cpu_init(void) /* Init the cache */ cache_init(); + shm_align_mask = max_t(unsigned long, + cpu_data->dcache.way_size - 1, + PAGE_SIZE - 1); + /* Disable the FPU */ if (fpu_disabled) { printk("FPU Disabled\n"); diff --git a/arch/sh/kernel/cpu/irq/Makefile b/arch/sh/kernel/cpu/irq/Makefile index e3cccea15e1d..1c034c283f59 100644 --- a/arch/sh/kernel/cpu/irq/Makefile +++ b/arch/sh/kernel/cpu/irq/Makefile @@ -3,5 +3,6 @@ # obj-y += ipr.o imask.o -obj-$(CONFIG_CPU_HAS_PINT_IRQ) += pint.o -obj-$(CONFIG_CPU_HAS_INTC2_IRQ) += intc2.o +obj-$(CONFIG_CPU_HAS_PINT_IRQ) += pint.o +obj-$(CONFIG_CPU_HAS_MASKREG_IRQ) += maskreg.o +obj-$(CONFIG_CPU_HAS_INTC2_IRQ) += intc2.o diff --git a/arch/sh/kernel/cpu/irq/intc2.c b/arch/sh/kernel/cpu/irq/intc2.c index 30064bf6e154..e30e4b7aa70e 100644 --- a/arch/sh/kernel/cpu/irq/intc2.c +++ b/arch/sh/kernel/cpu/irq/intc2.c @@ -241,9 +241,9 @@ static struct intc2_init { /* 110-111 reserved/unused */ #elif defined(CONFIG_CPU_SUBTYPE_SH7780) { TIMER_IRQ, 0, 24, 0, INTC_TMU0_MSK, 2}, -#ifdef CONFIG_SH_RTC - { RTC_IRQ, 4, 0, 0, INTC_RTC_MSK, TIMER_PRIORITY }, -#endif + { 21, 1, 0, 0, INTC_RTC_MSK, TIMER_PRIORITY }, + { 22, 1, 1, 0, INTC_RTC_MSK, TIMER_PRIORITY }, + { 23, 1, 2, 0, INTC_RTC_MSK, TIMER_PRIORITY }, { SCIF0_ERI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, { SCIF0_RXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, { SCIF0_BRI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, diff --git a/arch/sh/kernel/cpu/irq/ipr.c b/arch/sh/kernel/cpu/irq/ipr.c index 0f545941fb4f..f785822cd5de 100644 --- a/arch/sh/kernel/cpu/irq/ipr.c +++ b/arch/sh/kernel/cpu/irq/ipr.c @@ -57,31 +57,27 @@ static struct hw_interrupt_type ipr_irq_type = { static void disable_ipr_irq(unsigned int irq) { - unsigned long val, flags; + unsigned long val; unsigned int addr = ipr_data[irq].addr; unsigned short mask = 0xffff ^ (0x0f << ipr_data[irq].shift); /* Set the priority in IPR to 0 */ - local_irq_save(flags); val = ctrl_inw(addr); val &= mask; ctrl_outw(val, addr); - local_irq_restore(flags); } static void enable_ipr_irq(unsigned int irq) { - unsigned long val, flags; + unsigned long val; unsigned int addr = ipr_data[irq].addr; int priority = ipr_data[irq].priority; unsigned short value = (priority << ipr_data[irq].shift); /* Set priority in IPR back to original value */ - local_irq_save(flags); val = ctrl_inw(addr); val |= value; ctrl_outw(val, addr); - local_irq_restore(flags); } static void mask_and_ack_ipr(unsigned int irq) @@ -89,6 +85,7 @@ static void mask_and_ack_ipr(unsigned int irq) disable_ipr_irq(irq); #if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ + defined(CONFIG_CPU_SUBTYPE_SH7706) || \ defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) /* This is needed when we use edge triggered setting */ /* XXX: Is it really needed? */ @@ -123,7 +120,7 @@ void __init init_IRQ(void) #ifndef CONFIG_CPU_SUBTYPE_SH7780 make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY); make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY); -#if defined(CONFIG_SH_RTC) +#ifdef RTC_IRQ make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY); #endif @@ -162,6 +159,7 @@ void __init init_IRQ(void) #endif #if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ + defined(CONFIG_CPU_SUBTYPE_SH7706) || \ defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) /* * Initialize the Interrupt Controller (INTC) @@ -192,6 +190,8 @@ void __init init_IRQ(void) /* Perform the machine specific initialisation */ if (sh_mv.mv_init_irq != NULL) sh_mv.mv_init_irq(); + + irq_ctx_init(smp_processor_id()); } #if !defined(CONFIG_CPU_HAS_PINT_IRQ) diff --git a/arch/sh/kernel/cpu/irq/maskreg.c b/arch/sh/kernel/cpu/irq/maskreg.c new file mode 100644 index 000000000000..492db31b3cab --- /dev/null +++ b/arch/sh/kernel/cpu/irq/maskreg.c @@ -0,0 +1,93 @@ +/* + * Interrupt handling for Simple external interrupt mask register + * + * Copyright (C) 2001 A&D Co., Ltd. <http://www.aandd.co.jp> + * + * This is for the machine which have single 16 bit register + * for masking external IRQ individually. + * Each bit of the register is for masking each interrupt. + * + * This file may be copied or modified under the terms of the GNU + * General Public License. See linux/COPYING for more information. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <asm/system.h> +#include <asm/io.h> + +/* address of external interrupt mask register */ +unsigned long irq_mask_register; + +/* forward declaration */ +static unsigned int startup_maskreg_irq(unsigned int irq); +static void shutdown_maskreg_irq(unsigned int irq); +static void enable_maskreg_irq(unsigned int irq); +static void disable_maskreg_irq(unsigned int irq); +static void mask_and_ack_maskreg(unsigned int); +static void end_maskreg_irq(unsigned int irq); + +/* hw_interrupt_type */ +static struct hw_interrupt_type maskreg_irq_type = { + .typename = "Mask Register", + .startup = startup_maskreg_irq, + .shutdown = shutdown_maskreg_irq, + .enable = enable_maskreg_irq, + .disable = disable_maskreg_irq, + .ack = mask_and_ack_maskreg, + .end = end_maskreg_irq +}; + +/* actual implementatin */ +static unsigned int startup_maskreg_irq(unsigned int irq) +{ + enable_maskreg_irq(irq); + return 0; /* never anything pending */ +} + +static void shutdown_maskreg_irq(unsigned int irq) +{ + disable_maskreg_irq(irq); +} + +static void disable_maskreg_irq(unsigned int irq) +{ + unsigned short val, mask = 0x01 << irq; + + BUG_ON(!irq_mask_register); + + /* Set "irq"th bit */ + val = ctrl_inw(irq_mask_register); + val |= mask; + ctrl_outw(val, irq_mask_register); +} + +static void enable_maskreg_irq(unsigned int irq) +{ + unsigned short val, mask = ~(0x01 << irq); + + BUG_ON(!irq_mask_register); + + /* Clear "irq"th bit */ + val = ctrl_inw(irq_mask_register); + val &= mask; + ctrl_outw(val, irq_mask_register); +} + +static void mask_and_ack_maskreg(unsigned int irq) +{ + disable_maskreg_irq(irq); +} + +static void end_maskreg_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_maskreg_irq(irq); +} + +void make_maskreg_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &maskreg_irq_type; + disable_maskreg_irq(irq); +} diff --git a/arch/sh/kernel/cpu/irq/pint.c b/arch/sh/kernel/cpu/irq/pint.c index 80cd8108d36a..17f47b373d6e 100644 --- a/arch/sh/kernel/cpu/irq/pint.c +++ b/arch/sh/kernel/cpu/irq/pint.c @@ -48,26 +48,22 @@ static struct hw_interrupt_type pint_irq_type = { static void disable_pint_irq(unsigned int irq) { - unsigned long val, flags; + unsigned long val; - local_irq_save(flags); val = ctrl_inw(INTC_INTER); val &= ~(1 << (irq - PINT_IRQ_BASE)); ctrl_outw(val, INTC_INTER); /* disable PINTn */ portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2); - local_irq_restore(flags); } static void enable_pint_irq(unsigned int irq) { - unsigned long val, flags; + unsigned long val; - local_irq_save(flags); val = ctrl_inw(INTC_INTER); val |= 1 << (irq - PINT_IRQ_BASE); ctrl_outw(val, INTC_INTER); /* enable PINTn */ portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2; - local_irq_restore(flags); } static void mask_and_ack_pint(unsigned int irq) diff --git a/arch/sh/kernel/cpu/rtc.c b/arch/sh/kernel/cpu/rtc.c deleted file mode 100644 index 4304cf75cfa2..000000000000 --- a/arch/sh/kernel/cpu/rtc.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * linux/arch/sh/kernel/rtc.c -- SH3 / SH4 on-chip RTC support - * - * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> - * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka - */ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/time.h> -#include <linux/bcd.h> -#include <asm/io.h> -#include <asm/rtc.h> - -void sh_rtc_gettimeofday(struct timespec *ts) -{ - unsigned int sec128, sec, sec2, min, hr, wk, day, mon, yr, yr100, cf_bit; - unsigned long flags; - - again: - do { - local_irq_save(flags); - ctrl_outb(0, RCR1); /* Clear CF-bit */ - sec128 = ctrl_inb(R64CNT); - sec = ctrl_inb(RSECCNT); - min = ctrl_inb(RMINCNT); - hr = ctrl_inb(RHRCNT); - wk = ctrl_inb(RWKCNT); - day = ctrl_inb(RDAYCNT); - mon = ctrl_inb(RMONCNT); -#if defined(CONFIG_CPU_SH4) - yr = ctrl_inw(RYRCNT); - yr100 = (yr >> 8); - yr &= 0xff; -#else - yr = ctrl_inb(RYRCNT); - yr100 = (yr == 0x99) ? 0x19 : 0x20; -#endif - sec2 = ctrl_inb(R64CNT); - cf_bit = ctrl_inb(RCR1) & RCR1_CF; - local_irq_restore(flags); - } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0); - - BCD_TO_BIN(yr100); - BCD_TO_BIN(yr); - BCD_TO_BIN(mon); - BCD_TO_BIN(day); - BCD_TO_BIN(hr); - BCD_TO_BIN(min); - BCD_TO_BIN(sec); - - if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 || - hr > 23 || min > 59 || sec > 59) { - printk(KERN_ERR - "SH RTC: invalid value, resetting to 1 Jan 2000\n"); - local_irq_save(flags); - ctrl_outb(RCR2_RESET, RCR2); /* Reset & Stop */ - ctrl_outb(0, RSECCNT); - ctrl_outb(0, RMINCNT); - ctrl_outb(0, RHRCNT); - ctrl_outb(6, RWKCNT); - ctrl_outb(1, RDAYCNT); - ctrl_outb(1, RMONCNT); -#if defined(CONFIG_CPU_SH4) - ctrl_outw(0x2000, RYRCNT); -#else - ctrl_outb(0, RYRCNT); -#endif - ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start */ - goto again; - } - -#if RTC_BIT_INVERTED != 0 - if ((sec128 & RTC_BIT_INVERTED)) - sec--; -#endif - - ts->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec); - ts->tv_nsec = ((sec128 * 1000000) / 128) * 1000; -} - -/* - * Changed to only care about tv_sec, and not the full timespec struct - * (i.e. tv_nsec). It can easily be switched to timespec for future cpus - * that support setting usec or nsec RTC values. - */ -int sh_rtc_settimeofday(const time_t secs) -{ - int retval = 0; - int real_seconds, real_minutes, cmos_minutes; - unsigned long flags; - - local_irq_save(flags); - ctrl_outb(RCR2_RESET, RCR2); /* Reset pre-scaler & stop RTC */ - - cmos_minutes = ctrl_inb(RMINCNT); - BCD_TO_BIN(cmos_minutes); - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = secs % 60; - real_minutes = secs / 60; - if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - cmos_minutes) < 30) { - BIN_TO_BCD(real_seconds); - BIN_TO_BCD(real_minutes); - ctrl_outb(real_seconds, RSECCNT); - ctrl_outb(real_minutes, RMINCNT); - } else { - printk(KERN_WARNING - "set_rtc_time: can't update from %d to %d\n", - cmos_minutes, real_minutes); - retval = -1; - } - - ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start RTC */ - local_irq_restore(flags); - - return retval; -} diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile index b54dbb9a0c86..58d3815695ff 100644 --- a/arch/sh/kernel/cpu/sh3/Makefile +++ b/arch/sh/kernel/cpu/sh3/Makefile @@ -4,10 +4,21 @@ obj-y := ex.o probe.o +# CPU subtype setup +obj-$(CONFIG_CPU_SUBTYPE_SH7705) += setup-sh7705.o +obj-$(CONFIG_CPU_SUBTYPE_SH7706) += setup-sh7709.o +obj-$(CONFIG_CPU_SUBTYPE_SH7707) += setup-sh7709.o +obj-$(CONFIG_CPU_SUBTYPE_SH7708) += setup-sh7708.o +obj-$(CONFIG_CPU_SUBTYPE_SH7709) += setup-sh7709.o +obj-$(CONFIG_CPU_SUBTYPE_SH7300) += setup-sh7300.o +obj-$(CONFIG_CPU_SUBTYPE_SH7710) += setup-sh7710.o + +# Primary on-chip clocks (common) clock-$(CONFIG_CPU_SH3) := clock-sh3.o clock-$(CONFIG_CPU_SUBTYPE_SH7300) := clock-sh7300.o clock-$(CONFIG_CPU_SUBTYPE_SH7705) := clock-sh7705.o +clock-$(CONFIG_CPU_SUBTYPE_SH7706) := clock-sh7706.o clock-$(CONFIG_CPU_SUBTYPE_SH7709) := clock-sh7709.o +clock-$(CONFIG_CPU_SUBTYPE_SH7710) := clock-sh7300.o obj-y += $(clock-y) - diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7706.c b/arch/sh/kernel/cpu/sh3/clock-sh7706.c new file mode 100644 index 000000000000..0cf96f9833bc --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7706.c @@ -0,0 +1,84 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7706.c + * + * SH7706 support for the clock framework + * + * Copyright (C) 2006 Takashi YOSHII + * + * Based on arch/sh/kernel/cpu/sh3/clock-sh7709.c + * Copyright (C) 2005 Andriy Skulysh + * + * 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 <linux/init.h> +#include <linux/kernel.h> +#include <asm/clock.h> +#include <asm/freq.h> +#include <asm/io.h> + +static int stc_multipliers[] = { 1, 2, 4, 1, 3, 6, 1, 1 }; +static int ifc_divisors[] = { 1, 2, 4, 1, 3, 1, 1, 1 }; +static int pfc_divisors[] = { 1, 2, 4, 1, 3, 6, 1, 1 }; + +static void master_clk_init(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate *= pfc_divisors[idx]; +} + +static struct clk_ops sh7706_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh7706_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4); + + clk->rate = clk->parent->rate / stc_multipliers[idx]; +} + +static struct clk_ops sh7706_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); + + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh7706_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7706_clk_ops[] = { + &sh7706_master_clk_ops, + &sh7706_module_clk_ops, + &sh7706_bus_clk_ops, + &sh7706_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7706_clk_ops)) + *ops = sh7706_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh3/ex.S b/arch/sh/kernel/cpu/sh3/ex.S index cc04e9e239ff..44daf44833f9 100644 --- a/arch/sh/kernel/cpu/sh3/ex.S +++ b/arch/sh/kernel/cpu/sh3/ex.S @@ -84,8 +84,12 @@ ENTRY(interrupt_table) .long do_IRQ ! rovi .long do_IRQ .long do_IRQ /* 5E0 */ -#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ - defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) || \ + defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7300) || \ + defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7710) .long do_IRQ ! 32 IRQ irq0 /* 600 */ .long do_IRQ ! 33 irq1 .long do_IRQ ! 34 irq2 @@ -147,6 +151,51 @@ ENTRY(interrupt_table) .long do_IRQ ! 62 PCC pcc0i .long do_IRQ ! 63 pcc1i /* 9E0 */ #endif +#if defined(CONFIG_CPU_SUBTYPE_SH7710) + .long exception_none ! 61 /* 9A0 */ + .long exception_none ! 62 + .long exception_none ! 63 + .long exception_none ! 64 /* A00 */ + .long exception_none ! 65 + .long exception_none ! 66 + .long exception_none ! 67 + .long exception_none ! 68 + .long exception_none ! 69 + .long exception_none ! 70 + .long exception_none ! 71 + .long exception_none ! 72 /* B00 */ + .long exception_none ! 73 + .long exception_none ! 74 + .long exception_none ! 75 + .long do_IRQ ! 76 DMAC2 dei4 /* B80 */ + .long do_IRQ ! 77 DMAC2 dei5 + .long exception_none ! 78 + .long do_IRQ ! 79 IPSEC ipseci /* BE0 */ + .long do_IRQ ! 80 EDMAC eint0 /* C00 */ + .long do_IRQ ! 81 EDMAC eint1 + .long do_IRQ ! 82 EDMAC eint2 + .long exception_none ! 83 /* C60 */ + .long exception_none ! 84 + .long exception_none ! 85 + .long exception_none ! 86 + .long exception_none ! 87 + .long exception_none ! 88 /* D00 */ + .long exception_none ! 89 + .long exception_none ! 90 + .long exception_none ! 91 + .long exception_none ! 92 + .long exception_none ! 93 + .long exception_none ! 94 + .long exception_none ! 95 + .long do_IRQ ! 96 SIOF eri0 /* E00 */ + .long do_IRQ ! 97 txi0 + .long do_IRQ ! 98 rxi0 + .long do_IRQ ! 99 cci0 + .long do_IRQ ! 100 eri1 /* E80 */ + .long do_IRQ ! 101 txi1 + .long do_IRQ ! 102 rxi2 + .long do_IRQ ! 103 cci3 +#endif #if defined(CONFIG_CPU_SUBTYPE_SH7300) .long do_IRQ ! 64 .long do_IRQ ! 65 @@ -195,4 +244,3 @@ ENTRY(interrupt_table) .long do_IRQ ! 108 #endif #endif - diff --git a/arch/sh/kernel/cpu/sh3/probe.c b/arch/sh/kernel/cpu/sh3/probe.c index 5cdc88638601..e67098836290 100644 --- a/arch/sh/kernel/cpu/sh3/probe.c +++ b/arch/sh/kernel/cpu/sh3/probe.c @@ -72,6 +72,12 @@ int __init detect_cpu_and_cache_system(void) cpu_data->dcache.sets = 256; cpu_data->type = CPU_SH7729; +#if defined(CONFIG_CPU_SUBTYPE_SH7706) + cpu_data->type = CPU_SH7706; +#endif +#if defined(CONFIG_CPU_SUBTYPE_SH7710) + cpu_data->type = CPU_SH7710; +#endif #if defined(CONFIG_CPU_SUBTYPE_SH7705) cpu_data->type = CPU_SH7705; diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7300.c b/arch/sh/kernel/cpu/sh3/setup-sh7300.c new file mode 100644 index 000000000000..ab4d204bfba5 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh7300.c @@ -0,0 +1,43 @@ +/* + * SH7300 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xa4430000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCI, + .irqs = { 80, 80, 80, 80 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7300_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7300_devices_setup(void) +{ + return platform_add_devices(sh7300_devices, + ARRAY_SIZE(sh7300_devices)); +} +__initcall(sh7300_devices_setup); diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c new file mode 100644 index 000000000000..a8e41c5241fa --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c @@ -0,0 +1,48 @@ +/* + * SH7705 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xa4400000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 52, 53, 55, 54 }, + }, { + .mapbase = 0xa4410000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 56, 57, 59, 58 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7705_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7705_devices_setup(void) +{ + return platform_add_devices(sh7705_devices, + ARRAY_SIZE(sh7705_devices)); +} +__initcall(sh7705_devices_setup); diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7708.c b/arch/sh/kernel/cpu/sh3/setup-sh7708.c new file mode 100644 index 000000000000..f933723911ca --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh7708.c @@ -0,0 +1,43 @@ +/* + * SH7708 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xfffffe80, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCI, + .irqs = { 23, 24, 25, 0 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7708_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7708_devices_setup(void) +{ + return platform_add_devices(sh7708_devices, + ARRAY_SIZE(sh7708_devices)); +} +__initcall(sh7708_devices_setup); diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7709.c b/arch/sh/kernel/cpu/sh3/setup-sh7709.c new file mode 100644 index 000000000000..ff43ef2a1f0c --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh7709.c @@ -0,0 +1,53 @@ +/* + * SH7707/SH7709 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xfffffe80, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCI, + .irqs = { 23, 24, 25, 0 }, + }, { + .mapbase = 0xa4000150, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 56, 57, 59, 58 }, + }, { + .mapbase = 0xa4000140, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_IRDA, + .irqs = { 52, 53, 55, 54 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7709_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7709_devices_setup(void) +{ + return platform_add_devices(sh7709_devices, + ARRAY_SIZE(sh7709_devices)); +} +__initcall(sh7709_devices_setup); diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7710.c b/arch/sh/kernel/cpu/sh3/setup-sh7710.c new file mode 100644 index 000000000000..895f99ee6a95 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/setup-sh7710.c @@ -0,0 +1,43 @@ +/* + * SH7710 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xa4400000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 52, 53, 55, 54 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7710_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7710_devices_setup(void) +{ + return platform_add_devices(sh7710_devices, + ARRAY_SIZE(sh7710_devices)); +} +__initcall(sh7710_devices_setup); diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile index 3d5cafc71ae3..8dbf3895ece7 100644 --- a/arch/sh/kernel/cpu/sh4/Makefile +++ b/arch/sh/kernel/cpu/sh4/Makefile @@ -7,6 +7,16 @@ obj-y := ex.o probe.o obj-$(CONFIG_SH_FPU) += fpu.o obj-$(CONFIG_SH_STORE_QUEUES) += sq.o +# CPU subtype setup +obj-$(CONFIG_CPU_SUBTYPE_SH7750) += setup-sh7750.o +obj-$(CONFIG_CPU_SUBTYPE_SH7751) += setup-sh7750.o +obj-$(CONFIG_CPU_SUBTYPE_SH7760) += setup-sh7760.o +obj-$(CONFIG_CPU_SUBTYPE_SH7770) += setup-sh7770.o +obj-$(CONFIG_CPU_SUBTYPE_SH7780) += setup-sh7780.o +obj-$(CONFIG_CPU_SUBTYPE_SH73180) += setup-sh73180.o +obj-$(CONFIG_CPU_SUBTYPE_SH7343) += setup-sh7343.o +obj-$(CONFIG_CPU_SUBTYPE_SH4_202) += setup-sh4-202.o + # Primary on-chip clocks (common) clock-$(CONFIG_CPU_SH4) := clock-sh4.o clock-$(CONFIG_CPU_SUBTYPE_SH73180) := clock-sh73180.o diff --git a/arch/sh/kernel/cpu/sh4/ex.S b/arch/sh/kernel/cpu/sh4/ex.S index 26a27df06505..7146893a6cca 100644 --- a/arch/sh/kernel/cpu/sh4/ex.S +++ b/arch/sh/kernel/cpu/sh4/ex.S @@ -72,6 +72,7 @@ ENTRY(interrupt_table) .long do_IRQ ! 1110 .long exception_error ! Internal hardware +#ifndef CONFIG_CPU_SUBTYPE_SH7780 .long do_IRQ ! TMU0 tuni0 /* 400 */ .long do_IRQ ! TMU1 tuni1 .long do_IRQ ! TMU2 tuni2 @@ -122,6 +123,13 @@ ENTRY(interrupt_table) .long do_IRQ ! 45 dmte5 .long do_IRQ ! 46 dmte6 .long do_IRQ ! 47 dmte7 /* 7E0 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7343) + .long do_IRQ ! 44 IIC1 ali /* 780 */ + .long do_IRQ ! 45 tacki + .long do_IRQ ! 46 waiti + .long do_IRQ ! 47 dtei /* 7E0 */ + .long do_IRQ ! 48 DMAC dei0 /* 800 */ + .long do_IRQ ! 49 dei1 /* 820 */ #else .long exception_error ! 44 /* 780 */ .long exception_error ! 45 @@ -131,7 +139,8 @@ ENTRY(interrupt_table) #if defined(CONFIG_SH_FPU) .long do_fpu_state_restore ! 48 /* 800 */ .long do_fpu_state_restore ! 49 /* 820 */ -#else +#elif !defined(CONFIG_CPU_SUBTYPE_SH7343) && \ + !defined(CONFIG_CPU_SUBTYPE_SH73180) .long exception_error .long exception_error #endif @@ -224,7 +233,7 @@ ENTRY(interrupt_table) .long exception_error .long do_IRQ ! ADC adi .long do_IRQ ! CMT cmti /* FA0 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH73180) +#elif defined(CONFIG_CPU_SUBTYPE_SH73180) || defined(CONFIG_CPU_SUBTYPE_SH7343) .long do_IRQ ! 50 0x840 .long do_IRQ ! 51 0x860 .long do_IRQ ! 52 0x880 @@ -379,5 +388,168 @@ ENTRY(interrupt_table) .long exception_error ! 141 0x13a0 .long exception_error ! 142 0x13c0 .long exception_error ! 143 0x13e0 +#elif defined(CONFIG_CPU_SUBTYPE_SH7770) + .long do_IRQ ! 50 0x840 + .long do_IRQ ! 51 0x860 + .long do_IRQ ! 52 0x880 + .long do_IRQ ! 53 0x8a0 + .long do_IRQ ! 54 0x8c0 + .long do_IRQ ! 55 0x8e0 + .long do_IRQ ! 56 0x900 + .long do_IRQ ! 57 0x920 + .long do_IRQ ! 58 0x940 + .long do_IRQ ! 59 0x960 + .long do_IRQ ! 60 0x980 + .long do_IRQ ! 61 0x9a0 + .long do_IRQ ! 62 0x9c0 + .long do_IRQ ! 63 0x9e0 + .long do_IRQ ! 64 0xa00 + .long do_IRQ ! 65 0xa20 + .long do_IRQ ! 66 0xa4d + .long do_IRQ ! 67 0xa60 + .long do_IRQ ! 68 0xa80 + .long do_IRQ ! 69 0xaa0 + .long do_IRQ ! 70 0xac0 + .long do_IRQ ! 71 0xae0 + .long do_IRQ ! 72 0xb00 + .long do_IRQ ! 73 0xb20 + .long do_IRQ ! 74 0xb40 + .long do_IRQ ! 75 0xb60 + .long do_IRQ ! 76 0xb80 + .long do_IRQ ! 77 0xba0 + .long do_IRQ ! 78 0xbc0 + .long do_IRQ ! 79 0xbe0 + .long do_IRQ ! 80 0xc00 + .long do_IRQ ! 81 0xc20 + .long do_IRQ ! 82 0xc40 + .long do_IRQ ! 83 0xc60 + .long do_IRQ ! 84 0xc80 + .long do_IRQ ! 85 0xca0 + .long do_IRQ ! 86 0xcc0 + .long do_IRQ ! 87 0xce0 + .long do_IRQ ! 88 0xd00 + .long do_IRQ ! 89 0xd20 + .long do_IRQ ! 90 0xd40 + .long do_IRQ ! 91 0xd60 + .long do_IRQ ! 92 0xd80 + .long do_IRQ ! 93 0xda0 + .long do_IRQ ! 94 0xdc0 + .long do_IRQ ! 95 0xde0 + .long do_IRQ ! 96 0xe00 + .long do_IRQ ! 97 0xe20 + .long do_IRQ ! 98 0xe40 + .long do_IRQ ! 99 0xe60 + .long do_IRQ ! 100 0xe80 + .long do_IRQ ! 101 0xea0 + .long do_IRQ ! 102 0xec0 + .long do_IRQ ! 103 0xee0 + .long do_IRQ ! 104 0xf00 + .long do_IRQ ! 105 0xf20 + .long do_IRQ ! 106 0xf40 + .long do_IRQ ! 107 0xf60 + .long do_IRQ ! 108 0xf80 +#endif +#else + .long exception_error /* 400 */ + .long exception_error + .long exception_error + .long exception_error + .long do_IRQ ! RTC ati + .long do_IRQ ! pri + .long do_IRQ ! cui + .long exception_error + .long exception_error /* 500 */ + .long exception_error + .long exception_error + .long do_IRQ ! WDT iti /* 560 */ + .long do_IRQ ! TMU-ch0 + .long do_IRQ ! TMU-ch1 + .long do_IRQ ! TMU-ch2 + .long do_IRQ ! ticpi2 /* 5E0 */ + .long do_IRQ ! 32 Hitachi UDI /* 600 */ + .long exception_error + .long do_IRQ ! 34 DMAC dmte0 + .long do_IRQ ! 35 dmte1 + .long do_IRQ ! 36 dmte2 + .long do_IRQ ! 37 dmte3 + .long do_IRQ ! 38 dmae + .long exception_error ! 39 /* 6E0 */ + .long do_IRQ ! 40 SCIF-ch0 eri /* 700 */ + .long do_IRQ ! 41 rxi + .long do_IRQ ! 42 bri + .long do_IRQ ! 43 txi + .long do_IRQ ! 44 DMAC dmte4 /* 780 */ + .long do_IRQ ! 45 dmte5 + .long do_IRQ ! 46 dmte6 + .long do_IRQ ! 47 dmte7 /* 7E0 */ +#if defined(CONFIG_SH_FPU) + .long do_fpu_state_restore ! 48 /* 800 */ + .long do_fpu_state_restore ! 49 /* 820 */ +#else + .long exception_error + .long exception_error +#endif + .long exception_error /* 840 */ + .long exception_error + .long exception_error + .long exception_error + .long exception_error + .long exception_error + .long do_IRQ ! 56 CMT /* 900 */ + .long exception_error + .long exception_error + .long exception_error + .long do_IRQ ! 60 HAC + .long exception_error + .long exception_error + .long exception_error + .long do_IRQ ! PCI serr /* A00 */ + .long do_IRQ ! INTA + .long do_IRQ ! INTB + .long do_IRQ ! INTC + .long do_IRQ ! INTD + .long do_IRQ ! err + .long do_IRQ ! pwd3 + .long do_IRQ ! pwd2 + .long do_IRQ ! pwd1 /* B00 */ + .long do_IRQ ! pwd0 + .long exception_error + .long exception_error + .long do_IRQ ! SCIF-ch1 eri /* B80 */ + .long do_IRQ ! rxi + .long do_IRQ ! bri + .long do_IRQ ! txi + .long do_IRQ ! SIOF /* C00 */ + .long exception_error + .long exception_error + .long exception_error + .long do_IRQ ! HSPI /* C80 */ + .long exception_error + .long exception_error + .long exception_error + .long do_IRQ ! MMCIF fatat /* D00 */ + .long do_IRQ ! tran + .long do_IRQ ! err + .long do_IRQ ! frdy + .long do_IRQ ! DMAC dmint8 /* D80 */ + .long do_IRQ ! dmint9 + .long do_IRQ ! dmint10 + .long do_IRQ ! dmint11 + .long do_IRQ ! TMU-ch3 /* E00 */ + .long do_IRQ ! TMU-ch4 + .long do_IRQ ! TMU-ch5 + .long exception_error + .long do_IRQ ! SSI + .long exception_error + .long exception_error + .long exception_error + .long do_IRQ ! FLCTL flste /* F00 */ + .long do_IRQ ! fltend + .long do_IRQ ! fltrq0 + .long do_IRQ ! fltrq1 + .long do_IRQ ! GPIO gpioi0 /* F80 */ + .long do_IRQ ! gpioi1 + .long do_IRQ ! gpioi2 + .long do_IRQ ! gpioi3 #endif diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c index 42427b79697b..c294de1e14a3 100644 --- a/arch/sh/kernel/cpu/sh4/probe.c +++ b/arch/sh/kernel/cpu/sh4/probe.c @@ -3,7 +3,7 @@ * * CPU Subtype Probing for SH-4. * - * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2001 - 2006 Paul Mundt * Copyright (C) 2003 Richard Curnow * * This file is subject to the terms and conditions of the GNU General Public @@ -29,7 +29,7 @@ int __init detect_cpu_and_cache_system(void) [9] = (1 << 16) }; - pvr = (ctrl_inl(CCN_PVR) >> 8) & 0xffff; + pvr = (ctrl_inl(CCN_PVR) >> 8) & 0xffffff; prr = (ctrl_inl(CCN_PRR) >> 4) & 0xff; cvr = (ctrl_inl(CCN_CVR)); @@ -38,7 +38,6 @@ int __init detect_cpu_and_cache_system(void) */ cpu_data->icache.way_incr = (1 << 13); cpu_data->icache.entry_shift = 5; - cpu_data->icache.entry_mask = 0x1fe0; cpu_data->icache.sets = 256; cpu_data->icache.ways = 1; cpu_data->icache.linesz = L1_CACHE_BYTES; @@ -48,13 +47,29 @@ int __init detect_cpu_and_cache_system(void) */ cpu_data->dcache.way_incr = (1 << 14); cpu_data->dcache.entry_shift = 5; - cpu_data->dcache.entry_mask = 0x3fe0; cpu_data->dcache.sets = 512; cpu_data->dcache.ways = 1; cpu_data->dcache.linesz = L1_CACHE_BYTES; - /* Set the FPU flag, virtually all SH-4's have one */ - cpu_data->flags |= CPU_HAS_FPU; + /* + * Setup some generic flags we can probe + * (L2 and DSP detection only work on SH-4A) + */ + if (((pvr >> 16) & 0xff) == 0x10) { + if ((cvr & 0x02000000) == 0) + cpu_data->flags |= CPU_HAS_L2_CACHE; + if ((cvr & 0x10000000) == 0) + cpu_data->flags |= CPU_HAS_DSP; + + cpu_data->flags |= CPU_HAS_LLSC; + } + + /* FPU detection works for everyone */ + if ((cvr & 0x20000000) == 1) + cpu_data->flags |= CPU_HAS_FPU; + + /* Mask off the upper chip ID */ + pvr &= 0xffff; /* * Probe the underlying processor version/revision and @@ -63,56 +78,101 @@ int __init detect_cpu_and_cache_system(void) switch (pvr) { case 0x205: cpu_data->type = CPU_SH7750; - cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER; + cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_FPU | + CPU_HAS_PERF_COUNTER | CPU_HAS_PTEA; break; case 0x206: cpu_data->type = CPU_SH7750S; - cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER; + cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_FPU | + CPU_HAS_PERF_COUNTER | CPU_HAS_PTEA; break; case 0x1100: cpu_data->type = CPU_SH7751; + cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; break; case 0x2000: cpu_data->type = CPU_SH73180; cpu_data->icache.ways = 4; cpu_data->dcache.ways = 4; - cpu_data->flags &= ~CPU_HAS_FPU; + cpu_data->flags |= CPU_HAS_LLSC; + break; + case 0x2001: + case 0x2004: + cpu_data->type = CPU_SH7770; + cpu_data->icache.ways = 4; + cpu_data->dcache.ways = 4; + + cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_LLSC; + break; + case 0x2006: + case 0x200A: + if (prr == 0x61) + cpu_data->type = CPU_SH7781; + else + cpu_data->type = CPU_SH7780; + + cpu_data->icache.ways = 4; + cpu_data->dcache.ways = 4; + + cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PERF_COUNTER | + CPU_HAS_LLSC; + break; + case 0x3000: + case 0x3003: + cpu_data->type = CPU_SH7343; + cpu_data->icache.ways = 4; + cpu_data->dcache.ways = 4; + cpu_data->flags |= CPU_HAS_LLSC; break; case 0x8000: cpu_data->type = CPU_ST40RA; + cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; break; case 0x8100: cpu_data->type = CPU_ST40GX1; + cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; break; case 0x700: cpu_data->type = CPU_SH4_501; cpu_data->icache.ways = 2; cpu_data->dcache.ways = 2; - - /* No FPU on the SH4-500 series.. */ - cpu_data->flags &= ~CPU_HAS_FPU; + cpu_data->flags |= CPU_HAS_PTEA; break; case 0x600: cpu_data->type = CPU_SH4_202; cpu_data->icache.ways = 2; cpu_data->dcache.ways = 2; + cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; break; case 0x500 ... 0x501: switch (prr) { - case 0x10: cpu_data->type = CPU_SH7750R; break; - case 0x11: cpu_data->type = CPU_SH7751R; break; - case 0x50: cpu_data->type = CPU_SH7760; break; + case 0x10: + cpu_data->type = CPU_SH7750R; + break; + case 0x11: + cpu_data->type = CPU_SH7751R; + break; + case 0x50 ... 0x5f: + cpu_data->type = CPU_SH7760; + break; } cpu_data->icache.ways = 2; cpu_data->dcache.ways = 2; + cpu_data->flags |= CPU_HAS_FPU | CPU_HAS_PTEA; + break; default: cpu_data->type = CPU_SH_NONE; break; } +#ifdef CONFIG_SH_DIRECT_MAPPED + cpu_data->icache.ways = 1; + cpu_data->dcache.ways = 1; +#endif + /* * On anything that's not a direct-mapped cache, look to the CVR * for I/D-cache specifics. @@ -121,18 +181,56 @@ int __init detect_cpu_and_cache_system(void) size = sizes[(cvr >> 20) & 0xf]; cpu_data->icache.way_incr = (size >> 1); cpu_data->icache.sets = (size >> 6); - cpu_data->icache.entry_mask = - (cpu_data->icache.way_incr - (1 << 5)); + } + /* Setup the rest of the I-cache info */ + cpu_data->icache.entry_mask = cpu_data->icache.way_incr - + cpu_data->icache.linesz; + + cpu_data->icache.way_size = cpu_data->icache.sets * + cpu_data->icache.linesz; + + /* And the rest of the D-cache */ if (cpu_data->dcache.ways > 1) { size = sizes[(cvr >> 16) & 0xf]; cpu_data->dcache.way_incr = (size >> 1); cpu_data->dcache.sets = (size >> 6); - cpu_data->dcache.entry_mask = - (cpu_data->dcache.way_incr - (1 << 5)); + } + + cpu_data->dcache.entry_mask = cpu_data->dcache.way_incr - + cpu_data->dcache.linesz; + + cpu_data->dcache.way_size = cpu_data->dcache.sets * + cpu_data->dcache.linesz; + + /* + * Setup the L2 cache desc + * + * SH-4A's have an optional PIPT L2. + */ + if (cpu_data->flags & CPU_HAS_L2_CACHE) { + /* + * Size calculation is much more sensible + * than it is for the L1. + * + * Sizes are 128KB, 258KB, 512KB, and 1MB. + */ + size = (cvr & 0xf) << 17; + + BUG_ON(!size); + + cpu_data->scache.way_incr = (1 << 16); + cpu_data->scache.entry_shift = 5; + cpu_data->scache.ways = 4; + cpu_data->scache.linesz = L1_CACHE_BYTES; + cpu_data->scache.entry_mask = + (cpu_data->scache.way_incr - cpu_data->scache.linesz); + cpu_data->scache.sets = size / + (cpu_data->scache.linesz * cpu_data->scache.ways); + cpu_data->scache.way_size = + (cpu_data->scache.sets * cpu_data->scache.linesz); } return 0; } - diff --git a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c new file mode 100644 index 000000000000..6e4e96541358 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c @@ -0,0 +1,43 @@ +/* + * SH4-202 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xffe80000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 40, 41, 43, 42 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh4202_devices[] __initdata = { + &sci_device, +}; + +static int __init sh4202_devices_setup(void) +{ + return platform_add_devices(sh4202_devices, + ARRAY_SIZE(sh4202_devices)); +} +__initcall(sh4202_devices_setup); diff --git a/arch/sh/kernel/cpu/sh4/setup-sh73180.c b/arch/sh/kernel/cpu/sh4/setup-sh73180.c new file mode 100644 index 000000000000..cc9ea1e2e5df --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh73180.c @@ -0,0 +1,43 @@ +/* + * SH73180 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xffe80000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 80, 81, 83, 82 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh73180_devices[] __initdata = { + &sci_device, +}; + +static int __init sh73180_devices_setup(void) +{ + return platform_add_devices(sh73180_devices, + ARRAY_SIZE(sh73180_devices)); +} +__initcall(sh73180_devices_setup); diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7343.c b/arch/sh/kernel/cpu/sh4/setup-sh7343.c new file mode 100644 index 000000000000..91d61cf91ba1 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7343.c @@ -0,0 +1,43 @@ +/* + * SH7343 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xffe00000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 80, 81, 83, 82 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7343_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7343_devices_setup(void) +{ + return platform_add_devices(sh7343_devices, + ARRAY_SIZE(sh7343_devices)); +} +__initcall(sh7343_devices_setup); diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c new file mode 100644 index 000000000000..50812d57c1c1 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c @@ -0,0 +1,48 @@ +/* + * SH7750/SH7751 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xffe00000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCI, + .irqs = { 23, 24, 25, 0 }, + }, { + .mapbase = 0xffe80000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 40, 41, 43, 42 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7750_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7750_devices_setup(void) +{ + return platform_add_devices(sh7750_devices, + ARRAY_SIZE(sh7750_devices)); +} +__initcall(sh7750_devices_setup); diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c new file mode 100644 index 000000000000..97f1c9af35d6 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c @@ -0,0 +1,53 @@ +/* + * SH7760 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xfe600000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 52, 53, 55, 54 }, + }, { + .mapbase = 0xfe610000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 72, 73, 75, 74 }, + }, { + .mapbase = 0xfe620000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 76, 77, 79, 78 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7760_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7760_devices_setup(void) +{ + return platform_add_devices(sh7760_devices, + ARRAY_SIZE(sh7760_devices)); +} +__initcall(sh7760_devices_setup); diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7770.c b/arch/sh/kernel/cpu/sh4/setup-sh7770.c new file mode 100644 index 000000000000..6a04cc5f5aca --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7770.c @@ -0,0 +1,53 @@ +/* + * SH7770 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xff923000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 61, 61, 61, 61 }, + }, { + .mapbase = 0xff924000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 62, 62, 62, 62 }, + }, { + .mapbase = 0xff925000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 63, 63, 63, 63 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7770_devices[] __initdata = { + &sci_device, +}; + +static int __init sh7770_devices_setup(void) +{ + return platform_add_devices(sh7770_devices, + ARRAY_SIZE(sh7770_devices)); +} +__initcall(sh7770_devices_setup); diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7780.c b/arch/sh/kernel/cpu/sh4/setup-sh7780.c new file mode 100644 index 000000000000..72493f259edc --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/setup-sh7780.c @@ -0,0 +1,79 @@ +/* + * SH7780 Setup + * + * Copyright (C) 2006 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 <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <asm/sci.h> + +static struct resource rtc_resources[] = { + [0] = { + .start = 0xffe80000, + .end = 0xffe80000 + 0x58 - 1, + .flags = IORESOURCE_IO, + }, + [1] = { + /* Period IRQ */ + .start = 21, + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* Carry IRQ */ + .start = 22, + .flags = IORESOURCE_IRQ, + }, + [3] = { + /* Alarm IRQ */ + .start = 23, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device rtc_device = { + .name = "sh-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_resources), + .resource = rtc_resources, +}; + +static struct plat_sci_port sci_platform_data[] = { + { + .mapbase = 0xffe00000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 40, 41, 43, 42 }, + }, { + .mapbase = 0xffe10000, + .flags = UPF_BOOT_AUTOCONF, + .type = PORT_SCIF, + .irqs = { 76, 77, 79, 78 }, + }, { + .flags = 0, + } +}; + +static struct platform_device sci_device = { + .name = "sh-sci", + .id = -1, + .dev = { + .platform_data = sci_platform_data, + }, +}; + +static struct platform_device *sh7780_devices[] __initdata = { + &rtc_device, + &sci_device, +}; + +static int __init sh7780_devices_setup(void) +{ + return platform_add_devices(sh7780_devices, + ARRAY_SIZE(sh7780_devices)); +} +__initcall(sh7780_devices_setup); diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c index b09805f3ee23..7bcc73f9b8df 100644 --- a/arch/sh/kernel/cpu/sh4/sq.c +++ b/arch/sh/kernel/cpu/sh4/sq.c @@ -1,49 +1,52 @@ /* - * arch/sh/kernel/cpu/sq.c + * arch/sh/kernel/cpu/sh4/sq.c * * General management API for SH-4 integrated Store Queues * - * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2001 - 2006 Paul Mundt * Copyright (C) 2001, 2002 M. R. Brown * - * Some of this code has been adopted directly from the old arch/sh/mm/sq.c - * hack that was part of the LinuxDC project. For all intents and purposes, - * this is a completely new interface that really doesn't have much in common - * with the old zone-based approach at all. In fact, it's only listed here for - * general completeness. - * * 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 <linux/init.h> +#include <linux/cpu.h> +#include <linux/bitmap.h> +#include <linux/sysdev.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/list.h> -#include <linux/proc_fs.h> -#include <linux/miscdevice.h> #include <linux/vmalloc.h> - +#include <linux/mm.h> #include <asm/io.h> #include <asm/page.h> -#include <asm/mmu_context.h> +#include <asm/cacheflush.h> #include <asm/cpu/sq.h> -static LIST_HEAD(sq_mapping_list); +struct sq_mapping; + +struct sq_mapping { + const char *name; + + unsigned long sq_addr; + unsigned long addr; + unsigned int size; + + struct sq_mapping *next; +}; + +static struct sq_mapping *sq_mapping_list; static DEFINE_SPINLOCK(sq_mapping_lock); +static kmem_cache_t *sq_cache; +static unsigned long *sq_bitmap; -/** - * sq_flush - Flush (prefetch) the store queue cache - * @addr: the store queue address to flush - * - * Executes a prefetch instruction on the specified store queue cache, - * so that the cached data is written to physical memory. - */ -inline void sq_flush(void *addr) -{ - __asm__ __volatile__ ("pref @%0" : : "r" (addr) : "memory"); -} +#define store_queue_barrier() \ +do { \ + (void)ctrl_inl(P4SEG_STORE_QUE); \ + ctrl_outl(0, P4SEG_STORE_QUE + 0); \ + ctrl_outl(0, P4SEG_STORE_QUE + 8); \ +} while (0); /** * sq_flush_range - Flush (prefetch) a specific SQ range @@ -56,152 +59,73 @@ inline void sq_flush(void *addr) void sq_flush_range(unsigned long start, unsigned int len) { volatile unsigned long *sq = (unsigned long *)start; - unsigned long dummy; /* Flush the queues */ for (len >>= 5; len--; sq += 8) - sq_flush((void *)sq); + prefetchw((void *)sq); /* Wait for completion */ - dummy = ctrl_inl(P4SEG_STORE_QUE); - - ctrl_outl(0, P4SEG_STORE_QUE + 0); - ctrl_outl(0, P4SEG_STORE_QUE + 8); + store_queue_barrier(); } -static struct sq_mapping *__sq_alloc_mapping(unsigned long virt, unsigned long phys, unsigned long size, const char *name) +static inline void sq_mapping_list_add(struct sq_mapping *map) { - struct sq_mapping *map; - - if (virt + size > SQ_ADDRMAX) - return ERR_PTR(-ENOSPC); - - map = kmalloc(sizeof(struct sq_mapping), GFP_KERNEL); - if (!map) - return ERR_PTR(-ENOMEM); + struct sq_mapping **p, *tmp; - INIT_LIST_HEAD(&map->list); + spin_lock_irq(&sq_mapping_lock); - map->sq_addr = virt; - map->addr = phys; - map->size = size + 1; - map->name = name; + p = &sq_mapping_list; + while ((tmp = *p) != NULL) + p = &tmp->next; - list_add(&map->list, &sq_mapping_list); + map->next = tmp; + *p = map; - return map; + spin_unlock_irq(&sq_mapping_lock); } -static unsigned long __sq_get_next_addr(void) +static inline void sq_mapping_list_del(struct sq_mapping *map) { - if (!list_empty(&sq_mapping_list)) { - struct list_head *pos, *tmp; - - /* - * Read one off the list head, as it will have the highest - * mapped allocation. Set the next one up right above it. - * - * This is somewhat sub-optimal, as we don't look at - * gaps between allocations or anything lower then the - * highest-level allocation. - * - * However, in the interest of performance and the general - * lack of desire to do constant list rebalancing, we don't - * worry about it. - */ - list_for_each_safe(pos, tmp, &sq_mapping_list) { - struct sq_mapping *entry; - - entry = list_entry(pos, typeof(*entry), list); - - return entry->sq_addr + entry->size; + struct sq_mapping **p, *tmp; + + spin_lock_irq(&sq_mapping_lock); + + for (p = &sq_mapping_list; (tmp = *p); p = &tmp->next) + if (tmp == map) { + *p = tmp->next; + break; } - } - return P4SEG_STORE_QUE; + spin_unlock_irq(&sq_mapping_lock); } -/** - * __sq_remap - Perform a translation from the SQ to a phys addr - * @map: sq mapping containing phys and store queue addresses. - * - * Maps the store queue address specified in the mapping to the physical - * address specified in the mapping. - */ -static struct sq_mapping *__sq_remap(struct sq_mapping *map) +static int __sq_remap(struct sq_mapping *map, unsigned long flags) { - unsigned long flags, pteh, ptel; +#if defined(CONFIG_MMU) struct vm_struct *vma; - pgprot_t pgprot; - - /* - * Without an MMU (or with it turned off), this is much more - * straightforward, as we can just load up each queue's QACR with - * the physical address appropriately masked. - */ - - ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR0); - ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR1); -#ifdef CONFIG_MMU - /* - * With an MMU on the other hand, things are slightly more involved. - * Namely, we have to have a direct mapping between the SQ addr and - * the associated physical address in the UTLB by way of setting up - * a virt<->phys translation by hand. We do this by simply specifying - * the SQ addr in UTLB.VPN and the associated physical address in - * UTLB.PPN. - * - * Notably, even though this is a special case translation, and some - * of the configuration bits are meaningless, we're still required - * to have a valid ASID context in PTEH. - * - * We could also probably get by without explicitly setting PTEA, but - * we do it here just for good measure. - */ - spin_lock_irqsave(&sq_mapping_lock, flags); - - pteh = map->sq_addr; - ctrl_outl((pteh & MMU_VPN_MASK) | get_asid(), MMU_PTEH); - - ptel = map->addr & PAGE_MASK; - ctrl_outl(((ptel >> 28) & 0xe) | (ptel & 0x1), MMU_PTEA); - - pgprot = pgprot_noncached(PAGE_KERNEL); - - ptel &= _PAGE_FLAGS_HARDWARE_MASK; - ptel |= pgprot_val(pgprot); - ctrl_outl(ptel, MMU_PTEL); - - __asm__ __volatile__ ("ldtlb" : : : "memory"); - - spin_unlock_irqrestore(&sq_mapping_lock, flags); - - /* - * Next, we need to map ourselves in the kernel page table, so that - * future accesses after a TLB flush will be handled when we take a - * page fault. - * - * Theoretically we could just do this directly and not worry about - * setting up the translation by hand ahead of time, but for the - * cases where we want a one-shot SQ mapping followed by a quick - * writeout before we hit the TLB flush, we do it anyways. This way - * we at least save ourselves the initial page fault overhead. - */ vma = __get_vm_area(map->size, VM_ALLOC, map->sq_addr, SQ_ADDRMAX); if (!vma) - return ERR_PTR(-ENOMEM); + return -ENOMEM; vma->phys_addr = map->addr; if (remap_area_pages((unsigned long)vma->addr, vma->phys_addr, - map->size, pgprot_val(pgprot))) { + map->size, flags)) { vunmap(vma->addr); - return NULL; + return -EAGAIN; } -#endif /* CONFIG_MMU */ +#else + /* + * Without an MMU (or with it turned off), this is much more + * straightforward, as we can just load up each queue's QACR with + * the physical address appropriately masked. + */ + ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR0); + ctrl_outl(((map->addr >> 26) << 2) & 0x1c, SQ_QACR1); +#endif - return map; + return 0; } /** @@ -209,42 +133,65 @@ static struct sq_mapping *__sq_remap(struct sq_mapping *map) * @phys: Physical address of mapping. * @size: Length of mapping. * @name: User invoking mapping. + * @flags: Protection flags. * * Remaps the physical address @phys through the next available store queue * address of @size length. @name is logged at boot time as well as through - * the procfs interface. - * - * A pre-allocated and filled sq_mapping pointer is returned, and must be - * cleaned up with a call to sq_unmap() when the user is done with the - * mapping. + * the sysfs interface. */ -struct sq_mapping *sq_remap(unsigned long phys, unsigned int size, const char *name) +unsigned long sq_remap(unsigned long phys, unsigned int size, + const char *name, unsigned long flags) { struct sq_mapping *map; - unsigned long virt, end; + unsigned long end; unsigned int psz; + int ret, page; /* Don't allow wraparound or zero size */ end = phys + size - 1; - if (!size || end < phys) - return NULL; + if (unlikely(!size || end < phys)) + return -EINVAL; /* Don't allow anyone to remap normal memory.. */ - if (phys < virt_to_phys(high_memory)) - return NULL; + if (unlikely(phys < virt_to_phys(high_memory))) + return -EINVAL; phys &= PAGE_MASK; + size = PAGE_ALIGN(end + 1) - phys; + + map = kmem_cache_alloc(sq_cache, GFP_KERNEL); + if (unlikely(!map)) + return -ENOMEM; + + map->addr = phys; + map->size = size; + map->name = name; + + page = bitmap_find_free_region(sq_bitmap, 0x04000000, + get_order(map->size)); + if (unlikely(page < 0)) { + ret = -ENOSPC; + goto out; + } + + map->sq_addr = P4SEG_STORE_QUE + (page << PAGE_SHIFT); + + ret = __sq_remap(map, flags); + if (unlikely(ret != 0)) + goto out; + + psz = (size + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + pr_info("sqremap: %15s [%4d page%s] va 0x%08lx pa 0x%08lx\n", + likely(map->name) ? map->name : "???", + psz, psz == 1 ? " " : "s", + map->sq_addr, map->addr); - size = PAGE_ALIGN(end + 1) - phys; - virt = __sq_get_next_addr(); - psz = (size + (PAGE_SIZE - 1)) / PAGE_SIZE; - map = __sq_alloc_mapping(virt, phys, size, name); + sq_mapping_list_add(map); - printk("sqremap: %15s [%4d page%s] va 0x%08lx pa 0x%08lx\n", - map->name ? map->name : "???", - psz, psz == 1 ? " " : "s", - map->sq_addr, map->addr); + return map->sq_addr; - return __sq_remap(map); +out: + kmem_cache_free(sq_cache, map); + return ret; } /** @@ -255,188 +202,198 @@ struct sq_mapping *sq_remap(unsigned long phys, unsigned int size, const char *n * sq_remap(). Also frees up the pte that was previously inserted into * the kernel page table and discards the UTLB translation. */ -void sq_unmap(struct sq_mapping *map) +void sq_unmap(unsigned long vaddr) { - if (map->sq_addr > (unsigned long)high_memory) - vfree((void *)(map->sq_addr & PAGE_MASK)); + struct sq_mapping **p, *map; + struct vm_struct *vma; + int page; - list_del(&map->list); - kfree(map); -} + for (p = &sq_mapping_list; (map = *p); p = &map->next) + if (map->sq_addr == vaddr) + break; -/** - * sq_clear - Clear a store queue range - * @addr: Address to start clearing from. - * @len: Length to clear. - * - * A quick zero-fill implementation for clearing out memory that has been - * remapped through the store queues. - */ -void sq_clear(unsigned long addr, unsigned int len) -{ - int i; + if (unlikely(!map)) { + printk("%s: bad store queue address 0x%08lx\n", + __FUNCTION__, vaddr); + return; + } - /* Clear out both queues linearly */ - for (i = 0; i < 8; i++) { - ctrl_outl(0, addr + i + 0); - ctrl_outl(0, addr + i + 8); + page = (map->sq_addr - P4SEG_STORE_QUE) >> PAGE_SHIFT; + bitmap_release_region(sq_bitmap, page, get_order(map->size)); + +#ifdef CONFIG_MMU + vma = remove_vm_area((void *)(map->sq_addr & PAGE_MASK)); + if (!vma) { + printk(KERN_ERR "%s: bad address 0x%08lx\n", + __FUNCTION__, map->sq_addr); + return; } +#endif + + sq_mapping_list_del(map); - sq_flush_range(addr, len); + kmem_cache_free(sq_cache, map); } -/** - * sq_vma_unmap - Unmap a VMA range - * @area: VMA containing range. - * @addr: Start of range. - * @len: Length of range. +/* + * Needlessly complex sysfs interface. Unfortunately it doesn't seem like + * there is any other easy way to add things on a per-cpu basis without + * putting the directory entries somewhere stupid and having to create + * links in sysfs by hand back in to the per-cpu directories. * - * Searches the sq_mapping_list for a mapping matching the sq addr @addr, - * and subsequently frees up the entry. Further cleanup is done by generic - * code. + * Some day we may want to have an additional abstraction per store + * queue, but considering the kobject hell we already have to deal with, + * it's simply not worth the trouble. */ -static void sq_vma_unmap(struct vm_area_struct *area, - unsigned long addr, size_t len) -{ - struct list_head *pos, *tmp; +static struct kobject *sq_kobject[NR_CPUS]; - list_for_each_safe(pos, tmp, &sq_mapping_list) { - struct sq_mapping *entry; +struct sq_sysfs_attr { + struct attribute attr; + ssize_t (*show)(char *buf); + ssize_t (*store)(const char *buf, size_t count); +}; - entry = list_entry(pos, typeof(*entry), list); +#define to_sq_sysfs_attr(attr) container_of(attr, struct sq_sysfs_attr, attr) - if (entry->sq_addr == addr) { - /* - * We could probably get away without doing the tlb flush - * here, as generic code should take care of most of this - * when unmapping the rest of the VMA range for us. Leave - * it in for added sanity for the time being.. - */ - __flush_tlb_page(get_asid(), entry->sq_addr & PAGE_MASK); +static ssize_t sq_sysfs_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct sq_sysfs_attr *sattr = to_sq_sysfs_attr(attr); - list_del(&entry->list); - kfree(entry); + if (likely(sattr->show)) + return sattr->show(buf); - return; - } - } + return -EIO; } -/** - * sq_vma_sync - Sync a VMA range - * @area: VMA containing range. - * @start: Start of range. - * @len: Length of range. - * @flags: Additional flags. - * - * Synchronizes an sq mapped range by flushing the store queue cache for - * the duration of the mapping. - * - * Used internally for user mappings, which must use msync() to prefetch - * the store queue cache. - */ -static int sq_vma_sync(struct vm_area_struct *area, - unsigned long start, size_t len, unsigned int flags) +static ssize_t sq_sysfs_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) { - sq_flush_range(start, len); + struct sq_sysfs_attr *sattr = to_sq_sysfs_attr(attr); - return 0; + if (likely(sattr->store)) + return sattr->store(buf, count); + + return -EIO; } -static struct vm_operations_struct sq_vma_ops = { - .unmap = sq_vma_unmap, - .sync = sq_vma_sync, -}; +static ssize_t mapping_show(char *buf) +{ + struct sq_mapping **list, *entry; + char *p = buf; -/** - * sq_mmap - mmap() for /dev/cpu/sq - * @file: unused. - * @vma: VMA to remap. - * - * Remap the specified vma @vma through the store queues, and setup associated - * information for the new mapping. Also build up the page tables for the new - * area. - */ -static int sq_mmap(struct file *file, struct vm_area_struct *vma) + for (list = &sq_mapping_list; (entry = *list); list = &entry->next) + p += sprintf(p, "%08lx-%08lx [%08lx]: %s\n", + entry->sq_addr, entry->sq_addr + entry->size, + entry->addr, entry->name); + + return p - buf; +} + +static ssize_t mapping_store(const char *buf, size_t count) { - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long size = vma->vm_end - vma->vm_start; - struct sq_mapping *map; + unsigned long base = 0, len = 0; - /* - * We're not interested in any arbitrary virtual address that has - * been stuck in the VMA, as we already know what addresses we - * want. Save off the size, and reposition the VMA to begin at - * the next available sq address. - */ - vma->vm_start = __sq_get_next_addr(); - vma->vm_end = vma->vm_start + size; + sscanf(buf, "%lx %lx", &base, &len); + if (!base) + return -EIO; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + if (likely(len)) { + int ret = sq_remap(base, len, "Userspace", + pgprot_val(PAGE_SHARED)); + if (ret < 0) + return ret; + } else + sq_unmap(base); - vma->vm_flags |= VM_IO | VM_RESERVED; + return count; +} - map = __sq_alloc_mapping(vma->vm_start, offset, size, "Userspace"); +static struct sq_sysfs_attr mapping_attr = + __ATTR(mapping, 0644, mapping_show, mapping_store); - if (io_remap_pfn_range(vma, map->sq_addr, map->addr >> PAGE_SHIFT, - size, vma->vm_page_prot)) - return -EAGAIN; +static struct attribute *sq_sysfs_attrs[] = { + &mapping_attr.attr, + NULL, +}; - vma->vm_ops = &sq_vma_ops; +static struct sysfs_ops sq_sysfs_ops = { + .show = sq_sysfs_show, + .store = sq_sysfs_store, +}; - return 0; -} +static struct kobj_type ktype_percpu_entry = { + .sysfs_ops = &sq_sysfs_ops, + .default_attrs = sq_sysfs_attrs, +}; -#ifdef CONFIG_PROC_FS -static int sq_mapping_read_proc(char *buf, char **start, off_t off, - int len, int *eof, void *data) +static int __devinit sq_sysdev_add(struct sys_device *sysdev) { - struct list_head *pos; - char *p = buf; + unsigned int cpu = sysdev->id; + struct kobject *kobj; - list_for_each_prev(pos, &sq_mapping_list) { - struct sq_mapping *entry; + sq_kobject[cpu] = kzalloc(sizeof(struct kobject), GFP_KERNEL); + if (unlikely(!sq_kobject[cpu])) + return -ENOMEM; - entry = list_entry(pos, typeof(*entry), list); + kobj = sq_kobject[cpu]; + kobj->parent = &sysdev->kobj; + kobject_set_name(kobj, "%s", "sq"); + kobj->ktype = &ktype_percpu_entry; - p += sprintf(p, "%08lx-%08lx [%08lx]: %s\n", entry->sq_addr, - entry->sq_addr + entry->size - 1, entry->addr, - entry->name); - } - - return p - buf; + return kobject_register(kobj); } -#endif -static struct file_operations sq_fops = { - .owner = THIS_MODULE, - .mmap = sq_mmap, -}; +static int __devexit sq_sysdev_remove(struct sys_device *sysdev) +{ + unsigned int cpu = sysdev->id; + struct kobject *kobj = sq_kobject[cpu]; -static struct miscdevice sq_dev = { - .minor = STORE_QUEUE_MINOR, - .name = "sq", - .fops = &sq_fops, + kobject_unregister(kobj); + return 0; +} + +static struct sysdev_driver sq_sysdev_driver = { + .add = sq_sysdev_add, + .remove = __devexit_p(sq_sysdev_remove), }; static int __init sq_api_init(void) { - int ret; + unsigned int nr_pages = 0x04000000 >> PAGE_SHIFT; + unsigned int size = (nr_pages + (BITS_PER_LONG - 1)) / BITS_PER_LONG; + int ret = -ENOMEM; + printk(KERN_NOTICE "sq: Registering store queue API.\n"); - create_proc_read_entry("sq_mapping", 0, 0, sq_mapping_read_proc, 0); + sq_cache = kmem_cache_create("store_queue_cache", + sizeof(struct sq_mapping), 0, 0, + NULL, NULL); + if (unlikely(!sq_cache)) + return ret; - ret = misc_register(&sq_dev); - if (ret) - remove_proc_entry("sq_mapping", NULL); + sq_bitmap = kzalloc(size, GFP_KERNEL); + if (unlikely(!sq_bitmap)) + goto out; + + ret = sysdev_driver_register(&cpu_sysdev_class, &sq_sysdev_driver); + if (unlikely(ret != 0)) + goto out; + + return 0; + +out: + kfree(sq_bitmap); + kmem_cache_destroy(sq_cache); return ret; } static void __exit sq_api_exit(void) { - misc_deregister(&sq_dev); - remove_proc_entry("sq_mapping", NULL); + sysdev_driver_unregister(&cpu_sysdev_class, &sq_sysdev_driver); + kfree(sq_bitmap); + kmem_cache_destroy(sq_cache); } module_init(sq_api_init); @@ -445,11 +402,7 @@ module_exit(sq_api_exit); MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>"); MODULE_DESCRIPTION("Simple API for SH-4 integrated Store Queues"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(STORE_QUEUE_MINOR); EXPORT_SYMBOL(sq_remap); EXPORT_SYMBOL(sq_unmap); -EXPORT_SYMBOL(sq_clear); -EXPORT_SYMBOL(sq_flush); EXPORT_SYMBOL(sq_flush_range); - |