diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sh/kernel/cpu |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/sh/kernel/cpu')
-rw-r--r-- | arch/sh/kernel/cpu/Makefile | 16 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/adc.c | 36 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/bus.c | 195 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/init.c | 222 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/irq_imask.c | 116 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/irq_ipr.c | 339 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/rtc.c | 136 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh2/Makefile | 6 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh2/probe.c | 39 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/Makefile | 6 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/ex.S | 199 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh3/probe.c | 97 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/Makefile | 10 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/ex.S | 384 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/fpu.c | 335 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/irq_intc2.c | 222 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/probe.c | 138 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4/sq.c | 453 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/ubc.S | 59 |
19 files changed, 3008 insertions, 0 deletions
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile new file mode 100644 index 00000000000..cd43714df61 --- /dev/null +++ b/arch/sh/kernel/cpu/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the Linux/SuperH CPU-specifc backends. +# + +obj-y := irq_ipr.o irq_imask.o init.o bus.o + +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 + +USE_STANDARD_AS_RULE := true + diff --git a/arch/sh/kernel/cpu/adc.c b/arch/sh/kernel/cpu/adc.c new file mode 100644 index 00000000000..da3d6877f93 --- /dev/null +++ b/arch/sh/kernel/cpu/adc.c @@ -0,0 +1,36 @@ +/* + * linux/arch/sh/kernel/adc.c -- SH3 on-chip ADC support + * + * Copyright (C) 2004 Andriy Skulysh <askulysh@image.kiev.ua> + */ + +#include <linux/module.h> +#include <asm/adc.h> +#include <asm/io.h> + + +int adc_single(unsigned int channel) +{ + int off; + unsigned char csr; + + if (channel >= 8) return -1; + + off = (channel & 0x03) << 2; + + csr = ctrl_inb(ADCSR); + csr = channel | ADCSR_ADST | ADCSR_CKS; + ctrl_outb(csr, ADCSR); + + do { + csr = ctrl_inb(ADCSR); + } while ((csr & ADCSR_ADF) == 0); + + csr &= ~(ADCSR_ADF | ADCSR_ADST); + ctrl_outb(csr, ADCSR); + + return (((ctrl_inb(ADDRAH + off) << 8) | + ctrl_inb(ADDRAL + off)) >> 6); +} + +EXPORT_SYMBOL(adc_single); diff --git a/arch/sh/kernel/cpu/bus.c b/arch/sh/kernel/cpu/bus.c new file mode 100644 index 00000000000..ace82f4b4a5 --- /dev/null +++ b/arch/sh/kernel/cpu/bus.c @@ -0,0 +1,195 @@ +/* + * arch/sh/kernel/cpu/bus.c + * + * Virtual bus for SuperH. + * + * Copyright (C) 2004 Paul Mundt + * + * Shamelessly cloned from arch/arm/mach-omap/bus.c, which was written + * by: + * + * Copyright (C) 2003 - 2004 Nokia Corporation + * Written by Tony Lindgren <tony@atomide.com> + * Portions of code based on sa1111.c. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <asm/bus-sh.h> + +static int sh_bus_match(struct device *dev, struct device_driver *drv) +{ + struct sh_driver *shdrv = to_sh_driver(drv); + struct sh_dev *shdev = to_sh_dev(dev); + + return shdev->dev_id == shdrv->dev_id; +} + +static int sh_bus_suspend(struct device *dev, u32 state) +{ + struct sh_dev *shdev = to_sh_dev(dev); + struct sh_driver *shdrv = to_sh_driver(dev->driver); + + if (shdrv && shdrv->suspend) + return shdrv->suspend(shdev, state); + + return 0; +} + +static int sh_bus_resume(struct device *dev) +{ + struct sh_dev *shdev = to_sh_dev(dev); + struct sh_driver *shdrv = to_sh_driver(dev->driver); + + if (shdrv && shdrv->resume) + return shdrv->resume(shdev); + + return 0; +} + +static struct device sh_bus_devices[SH_NR_BUSES] = { + { + .bus_id = SH_BUS_NAME_VIRT, + }, +}; + +struct bus_type sh_bus_types[SH_NR_BUSES] = { + { + .name = SH_BUS_NAME_VIRT, + .match = sh_bus_match, + .suspend = sh_bus_suspend, + .resume = sh_bus_resume, + }, +}; + +static int sh_device_probe(struct device *dev) +{ + struct sh_dev *shdev = to_sh_dev(dev); + struct sh_driver *shdrv = to_sh_driver(dev->driver); + + if (shdrv && shdrv->probe) + return shdrv->probe(shdev); + + return -ENODEV; +} + +static int sh_device_remove(struct device *dev) +{ + struct sh_dev *shdev = to_sh_dev(dev); + struct sh_driver *shdrv = to_sh_driver(dev->driver); + + if (shdrv && shdrv->remove) + return shdrv->remove(shdev); + + return 0; +} + +int sh_device_register(struct sh_dev *dev) +{ + if (!dev) + return -EINVAL; + + if (dev->bus_id < 0 || dev->bus_id >= SH_NR_BUSES) { + printk(KERN_ERR "%s: bus_id invalid: %s bus: %d\n", + __FUNCTION__, dev->name, dev->bus_id); + return -EINVAL; + } + + dev->dev.parent = &sh_bus_devices[dev->bus_id]; + dev->dev.bus = &sh_bus_types[dev->bus_id]; + + /* This is needed for USB OHCI to work */ + if (dev->dma_mask) + dev->dev.dma_mask = dev->dma_mask; + + snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u", + dev->name, dev->dev_id); + + printk(KERN_INFO "Registering SH device '%s'. Parent at %s\n", + dev->dev.bus_id, dev->dev.parent->bus_id); + + return device_register(&dev->dev); +} + +void sh_device_unregister(struct sh_dev *dev) +{ + device_unregister(&dev->dev); +} + +int sh_driver_register(struct sh_driver *drv) +{ + if (!drv) + return -EINVAL; + + if (drv->bus_id < 0 || drv->bus_id >= SH_NR_BUSES) { + printk(KERN_ERR "%s: bus_id invalid: bus: %d device %d\n", + __FUNCTION__, drv->bus_id, drv->dev_id); + return -EINVAL; + } + + drv->drv.probe = sh_device_probe; + drv->drv.remove = sh_device_remove; + drv->drv.bus = &sh_bus_types[drv->bus_id]; + + return driver_register(&drv->drv); +} + +void sh_driver_unregister(struct sh_driver *drv) +{ + driver_unregister(&drv->drv); +} + +static int __init sh_bus_init(void) +{ + int i, ret = 0; + + for (i = 0; i < SH_NR_BUSES; i++) { + ret = device_register(&sh_bus_devices[i]); + if (ret != 0) { + printk(KERN_ERR "Unable to register bus device %s\n", + sh_bus_devices[i].bus_id); + continue; + } + + ret = bus_register(&sh_bus_types[i]); + if (ret != 0) { + printk(KERN_ERR "Unable to register bus %s\n", + sh_bus_types[i].name); + device_unregister(&sh_bus_devices[i]); + } + } + + printk(KERN_INFO "SH Virtual Bus initialized\n"); + + return ret; +} + +static void __exit sh_bus_exit(void) +{ + int i; + + for (i = 0; i < SH_NR_BUSES; i++) { + bus_unregister(&sh_bus_types[i]); + device_unregister(&sh_bus_devices[i]); + } +} + +module_init(sh_bus_init); +module_exit(sh_bus_exit); + +MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); +MODULE_DESCRIPTION("SH Virtual Bus"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(sh_bus_types); +EXPORT_SYMBOL(sh_device_register); +EXPORT_SYMBOL(sh_device_unregister); +EXPORT_SYMBOL(sh_driver_register); +EXPORT_SYMBOL(sh_driver_unregister); + diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c new file mode 100644 index 00000000000..cf94e8ef17c --- /dev/null +++ b/arch/sh/kernel/cpu/init.c @@ -0,0 +1,222 @@ +/* + * arch/sh/kernel/cpu/init.c + * + * CPU init code + * + * Copyright (C) 2002, 2003 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/init.h> +#include <linux/kernel.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/cacheflush.h> +#include <asm/cache.h> +#include <asm/io.h> + +extern void detect_cpu_and_cache_system(void); + +/* + * Generic wrapper for command line arguments to disable on-chip + * peripherals (nofpu, nodsp, and so forth). + */ +#define onchip_setup(x) \ +static int x##_disabled __initdata = 0; \ + \ +static int __init x##_setup(char *opts) \ +{ \ + x##_disabled = 1; \ + return 0; \ +} \ +__setup("no" __stringify(x), x##_setup); + +onchip_setup(fpu); +onchip_setup(dsp); + +/* + * Generic first-level cache init + */ +static void __init cache_init(void) +{ + unsigned long ccr, flags; + + if (cpu_data->type == CPU_SH_NONE) + panic("Unknown CPU"); + + jump_to_P2(); + ccr = ctrl_inl(CCR); + + /* + * If the cache is already enabled .. flush it. + */ + if (ccr & CCR_CACHE_ENABLE) { + unsigned long ways, waysize, addrstart; + + waysize = cpu_data->dcache.sets; + + /* + * If the OC is already in RAM mode, we only have + * half of the entries to flush.. + */ + if (ccr & CCR_CACHE_ORA) + waysize >>= 1; + + waysize <<= cpu_data->dcache.entry_shift; + +#ifdef CCR_CACHE_EMODE + /* If EMODE is not set, we only have 1 way to flush. */ + if (!(ccr & CCR_CACHE_EMODE)) + ways = 1; + else +#endif + ways = cpu_data->dcache.ways; + + addrstart = CACHE_OC_ADDRESS_ARRAY; + do { + unsigned long addr; + + for (addr = addrstart; + addr < addrstart + waysize; + addr += cpu_data->dcache.linesz) + ctrl_outl(0, addr); + + addrstart += cpu_data->dcache.way_incr; + } while (--ways); + } + + /* + * Default CCR values .. enable the caches + * and invalidate them immediately.. + */ + flags = CCR_CACHE_ENABLE | CCR_CACHE_INVALIDATE; + +#ifdef CCR_CACHE_EMODE + /* Force EMODE if possible */ + if (cpu_data->dcache.ways > 1) + flags |= CCR_CACHE_EMODE; +#endif + +#ifdef CONFIG_SH_WRITETHROUGH + /* Turn on Write-through caching */ + flags |= CCR_CACHE_WT; +#else + /* .. or default to Write-back */ + flags |= CCR_CACHE_CB; +#endif + +#ifdef CONFIG_SH_OCRAM + /* Turn on OCRAM -- halve the OC */ + flags |= CCR_CACHE_ORA; + cpu_data->dcache.sets >>= 1; +#endif + + ctrl_outl(flags, CCR); + back_to_P1(); +} + +#ifdef CONFIG_SH_DSP +static void __init release_dsp(void) +{ + unsigned long sr; + + /* Clear SR.DSP bit */ + __asm__ __volatile__ ( + "stc\tsr, %0\n\t" + "and\t%1, %0\n\t" + "ldc\t%0, sr\n\t" + : "=&r" (sr) + : "r" (~SR_DSP) + ); +} + +static void __init dsp_init(void) +{ + unsigned long sr; + + /* + * Set the SR.DSP bit, wait for one instruction, and then read + * back the SR value. + */ + __asm__ __volatile__ ( + "stc\tsr, %0\n\t" + "or\t%1, %0\n\t" + "ldc\t%0, sr\n\t" + "nop\n\t" + "stc\tsr, %0\n\t" + : "=&r" (sr) + : "r" (SR_DSP) + ); + + /* If the DSP bit is still set, this CPU has a DSP */ + if (sr & SR_DSP) + cpu_data->flags |= CPU_HAS_DSP; + + /* Now that we've determined the DSP status, clear the DSP bit. */ + release_dsp(); +} +#endif /* CONFIG_SH_DSP */ + +/** + * sh_cpu_init + * + * This is our initial entry point for each CPU, and is invoked on the boot + * CPU prior to calling start_kernel(). For SMP, a combination of this and + * start_secondary() will bring up each processor to a ready state prior + * to hand forking the idle loop. + * + * We do all of the basic processor init here, including setting up the + * caches, FPU, DSP, kicking the UBC, etc. By the time start_kernel() is + * hit (and subsequently platform_setup()) things like determining the + * CPU subtype and initial configuration will all be done. + * + * Each processor family is still responsible for doing its own probing + * and cache configuration in detect_cpu_and_cache_system(). + */ +asmlinkage void __init sh_cpu_init(void) +{ + /* First, probe the CPU */ + detect_cpu_and_cache_system(); + + /* Init the cache */ + cache_init(); + + /* Disable the FPU */ + if (fpu_disabled) { + printk("FPU Disabled\n"); + cpu_data->flags &= ~CPU_HAS_FPU; + disable_fpu(); + } + + /* FPU initialization */ + if ((cpu_data->flags & CPU_HAS_FPU)) { + clear_thread_flag(TIF_USEDFPU); + clear_used_math(); + } + +#ifdef CONFIG_SH_DSP + /* Probe for DSP */ + dsp_init(); + + /* Disable the DSP */ + if (dsp_disabled) { + printk("DSP Disabled\n"); + cpu_data->flags &= ~CPU_HAS_DSP; + release_dsp(); + } +#endif + +#ifdef CONFIG_UBC_WAKEUP + /* + * Some brain-damaged loaders decided it would be a good idea to put + * the UBC to sleep. This causes some issues when it comes to things + * like PTRACE_SINGLESTEP or doing hardware watchpoints in GDB. So .. + * we wake it up and hope that all is well. + */ + ubc_wakeup(); +#endif +} + diff --git a/arch/sh/kernel/cpu/irq_imask.c b/arch/sh/kernel/cpu/irq_imask.c new file mode 100644 index 00000000000..f76901e732f --- /dev/null +++ b/arch/sh/kernel/cpu/irq_imask.c @@ -0,0 +1,116 @@ +/* $Id: irq_imask.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $ + * + * linux/arch/sh/kernel/irq_imask.c + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * + * Simple interrupt handling using IMASK of SR register. + * + */ + +/* NOTE: Will not work on level 15 */ + + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/bitops.h> + +#include <asm/system.h> +#include <asm/irq.h> + +#include <linux/spinlock.h> +#include <linux/cache.h> +#include <linux/irq.h> + +/* Bitmap of IRQ masked */ +static unsigned long imask_mask = 0x7fff; +static int interrupt_priority = 0; + +static void enable_imask_irq(unsigned int irq); +static void disable_imask_irq(unsigned int irq); +static void shutdown_imask_irq(unsigned int irq); +static void mask_and_ack_imask(unsigned int); +static void end_imask_irq(unsigned int irq); + +#define IMASK_PRIORITY 15 + +static unsigned int startup_imask_irq(unsigned int irq) +{ + /* Nothing to do */ + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type imask_irq_type = { + "SR.IMASK", + startup_imask_irq, + shutdown_imask_irq, + enable_imask_irq, + disable_imask_irq, + mask_and_ack_imask, + end_imask_irq +}; + +void static inline set_interrupt_registers(int ip) +{ + unsigned long __dummy; + + asm volatile("ldc %2, r6_bank\n\t" + "stc sr, %0\n\t" + "and #0xf0, %0\n\t" + "shlr2 %0\n\t" + "cmp/eq #0x3c, %0\n\t" + "bt/s 1f ! CLI-ed\n\t" + " stc sr, %0\n\t" + "and %1, %0\n\t" + "or %2, %0\n\t" + "ldc %0, sr\n" + "1:" + : "=&z" (__dummy) + : "r" (~0xf0), "r" (ip << 4) + : "t"); +} + +static void disable_imask_irq(unsigned int irq) +{ + clear_bit(irq, &imask_mask); + if (interrupt_priority < IMASK_PRIORITY - irq) + interrupt_priority = IMASK_PRIORITY - irq; + + set_interrupt_registers(interrupt_priority); +} + +static void enable_imask_irq(unsigned int irq) +{ + set_bit(irq, &imask_mask); + interrupt_priority = IMASK_PRIORITY - ffz(imask_mask); + + set_interrupt_registers(interrupt_priority); +} + +static void mask_and_ack_imask(unsigned int irq) +{ + disable_imask_irq(irq); +} + +static void end_imask_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_imask_irq(irq); +} + +static void shutdown_imask_irq(unsigned int irq) +{ + /* Nothing to do */ +} + +void make_imask_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &imask_irq_type; + enable_irq(irq); +} diff --git a/arch/sh/kernel/cpu/irq_ipr.c b/arch/sh/kernel/cpu/irq_ipr.c new file mode 100644 index 00000000000..7ea3d2d030e --- /dev/null +++ b/arch/sh/kernel/cpu/irq_ipr.c @@ -0,0 +1,339 @@ +/* $Id: irq_ipr.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $ + * + * linux/arch/sh/kernel/irq_ipr.c + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> + * + * Interrupt handling for IPR-based IRQ. + * + * Supported system: + * On-chip supporting modules (TMU, RTC, etc.). + * On-chip supporting modules for SH7709/SH7709A/SH7729/SH7300. + * Hitachi SolutionEngine external I/O: + * MS7709SE01, MS7709ASE01, and MS7750SE01 + * + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/module.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/machvec.h> + +struct ipr_data { + unsigned int addr; /* Address of Interrupt Priority Register */ + int shift; /* Shifts of the 16-bit data */ + int priority; /* The priority */ +}; +static struct ipr_data ipr_data[NR_IRQS]; + +static void enable_ipr_irq(unsigned int irq); +static void disable_ipr_irq(unsigned int irq); + +/* shutdown is same as "disable" */ +#define shutdown_ipr_irq disable_ipr_irq + +static void mask_and_ack_ipr(unsigned int); +static void end_ipr_irq(unsigned int irq); + +static unsigned int startup_ipr_irq(unsigned int irq) +{ + enable_ipr_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type ipr_irq_type = { + "IPR-IRQ", + startup_ipr_irq, + shutdown_ipr_irq, + enable_ipr_irq, + disable_ipr_irq, + mask_and_ack_ipr, + end_ipr_irq +}; + +static void disable_ipr_irq(unsigned int irq) +{ + unsigned long val, flags; + 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 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) +{ + disable_ipr_irq(irq); + +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ + defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) + /* This is needed when we use edge triggered setting */ + /* XXX: Is it really needed? */ + if (IRQ0_IRQ <= irq && irq <= IRQ5_IRQ) { + /* Clear external interrupt request */ + int a = ctrl_inb(INTC_IRR0); + a &= ~(1 << (irq - IRQ0_IRQ)); + ctrl_outb(a, INTC_IRR0); + } +#endif +} + +static void end_ipr_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_ipr_irq(irq); +} + +void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority) +{ + disable_irq_nosync(irq); + ipr_data[irq].addr = addr; + ipr_data[irq].shift = pos*4; /* POSition (0-3) x 4 means shift */ + ipr_data[irq].priority = priority; + + irq_desc[irq].handler = &ipr_irq_type; + disable_ipr_irq(irq); +} + +#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) +static unsigned char pint_map[256]; +static unsigned long portcr_mask = 0; + +static void enable_pint_irq(unsigned int irq); +static void disable_pint_irq(unsigned int irq); + +/* shutdown is same as "disable" */ +#define shutdown_pint_irq disable_pint_irq + +static void mask_and_ack_pint(unsigned int); +static void end_pint_irq(unsigned int irq); + +static unsigned int startup_pint_irq(unsigned int irq) +{ + enable_pint_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type pint_irq_type = { + "PINT-IRQ", + startup_pint_irq, + shutdown_pint_irq, + enable_pint_irq, + disable_pint_irq, + mask_and_ack_pint, + end_pint_irq +}; + +static void disable_pint_irq(unsigned int irq) +{ + unsigned long val, flags; + + 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; + + 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) +{ + disable_pint_irq(irq); +} + +static void end_pint_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_pint_irq(irq); +} + +void make_pint_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &pint_irq_type; + disable_pint_irq(irq); +} +#endif + +void __init init_IRQ(void) +{ +#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + int i; +#endif + + 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) + make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY); +#endif + +#ifdef SCI_ERI_IRQ + make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); + make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); + make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); +#endif + +#ifdef SCIF1_ERI_IRQ + make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); + make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); + make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); + make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7300) + make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY); + make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY); + make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY); + make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY); +#endif + +#ifdef SCIF_ERI_IRQ + make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); + make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); + make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); + make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); +#endif + +#ifdef IRDA_ERI_IRQ + make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); + make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); + make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); + make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ + defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) + /* + * Initialize the Interrupt Controller (INTC) + * registers to their power on values + */ + + /* + * Enable external irq (INTC IRQ mode). + * You should set corresponding bits of PFC to "00" + * to enable these interrupts. + */ + make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY); + make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY); + make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY); + make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY); + make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY); + make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY); +#if !defined(CONFIG_CPU_SUBTYPE_SH7300) + make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY); + make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY); + enable_ipr_irq(PINT0_IRQ); + enable_ipr_irq(PINT8_IRQ); + + for(i = 0; i < 16; i++) + make_pint_irq(PINT_IRQ_BASE + i); + for(i = 0; i < 256; i++) + { + if(i & 1) pint_map[i] = 0; + else if(i & 2) pint_map[i] = 1; + else if(i & 4) pint_map[i] = 2; + else if(i & 8) pint_map[i] = 3; + else if(i & 0x10) pint_map[i] = 4; + else if(i & 0x20) pint_map[i] = 5; + else if(i & 0x40) pint_map[i] = 6; + else if(i & 0x80) pint_map[i] = 7; + } +#endif /* !CONFIG_CPU_SUBTYPE_SH7300 */ +#endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 || CONFIG_CPU_SUBTYPE_SH7300*/ + +#ifdef CONFIG_CPU_SUBTYPE_ST40 + init_IRQ_intc2(); +#endif + + /* Perform the machine specific initialisation */ + if (sh_mv.mv_init_irq != NULL) { + sh_mv.mv_init_irq(); + } +} +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ + defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) +int ipr_irq_demux(int irq) +{ +#if !defined(CONFIG_CPU_SUBTYPE_SH7300) + unsigned long creg, dreg, d, sav; + + if(irq == PINT0_IRQ) + { +#if defined(CONFIG_CPU_SUBTYPE_SH7707) + creg = PORT_PACR; + dreg = PORT_PADR; +#else + creg = PORT_PCCR; + dreg = PORT_PCDR; +#endif + sav = ctrl_inw(creg); + ctrl_outw(sav | portcr_mask, creg); + d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & ctrl_inw(INTC_INTER) & 0xff; + ctrl_outw(sav, creg); + if(d == 0) return irq; + return PINT_IRQ_BASE + pint_map[d]; + } + else if(irq == PINT8_IRQ) + { +#if defined(CONFIG_CPU_SUBTYPE_SH7707) + creg = PORT_PBCR; + dreg = PORT_PBDR; +#else + creg = PORT_PFCR; + dreg = PORT_PFDR; +#endif + sav = ctrl_inw(creg); + ctrl_outw(sav | (portcr_mask >> 16), creg); + d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & (ctrl_inw(INTC_INTER) >> 8) & 0xff; + ctrl_outw(sav, creg); + if(d == 0) return irq; + return PINT_IRQ_BASE + 8 + pint_map[d]; + } +#endif + return irq; +} +#endif + +EXPORT_SYMBOL(make_ipr_irq); + diff --git a/arch/sh/kernel/cpu/rtc.c b/arch/sh/kernel/cpu/rtc.c new file mode 100644 index 00000000000..f8361f5e788 --- /dev/null +++ b/arch/sh/kernel/cpu/rtc.c @@ -0,0 +1,136 @@ +/* + * 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 <asm/io.h> +#include <asm/rtc.h> + +#ifndef BCD_TO_BIN +#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10) +#endif + +#ifndef BIN_TO_BCD +#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10) +#endif + +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--; |