diff options
Diffstat (limited to 'arch/sh/kernel')
31 files changed, 2404 insertions, 1077 deletions
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile index 8b819698df14..7a86eeb22655 100644 --- a/arch/sh/kernel/Makefile +++ b/arch/sh/kernel/Makefile | |||
| @@ -17,6 +17,4 @@ obj-$(CONFIG_SH_KGDB) += kgdb_stub.o kgdb_jmp.o | |||
| 17 | obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o | 17 | obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o |
| 18 | obj-$(CONFIG_MODULES) += module.o | 18 | obj-$(CONFIG_MODULES) += module.o |
| 19 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | 19 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o |
| 20 | 20 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | |
| 21 | USE_STANDARD_AS_RULE := true | ||
| 22 | |||
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile index cd43714df61a..5bfc33bec5d0 100644 --- a/arch/sh/kernel/cpu/Makefile +++ b/arch/sh/kernel/cpu/Makefile | |||
| @@ -2,15 +2,12 @@ | |||
| 2 | # Makefile for the Linux/SuperH CPU-specifc backends. | 2 | # Makefile for the Linux/SuperH CPU-specifc backends. |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-y := irq_ipr.o irq_imask.o init.o bus.o | 5 | obj-y += irq/ init.o bus.o clock.o |
| 6 | 6 | ||
| 7 | obj-$(CONFIG_CPU_SH2) += sh2/ | 7 | obj-$(CONFIG_CPU_SH2) += sh2/ |
| 8 | obj-$(CONFIG_CPU_SH3) += sh3/ | 8 | obj-$(CONFIG_CPU_SH3) += sh3/ |
| 9 | obj-$(CONFIG_CPU_SH4) += sh4/ | 9 | obj-$(CONFIG_CPU_SH4) += sh4/ |
| 10 | 10 | ||
| 11 | obj-$(CONFIG_SH_RTC) += rtc.o | 11 | obj-$(CONFIG_SH_RTC) += rtc.o |
| 12 | obj-$(CONFIG_UBC_WAKEUP) += ubc.o | 12 | obj-$(CONFIG_UBC_WAKEUP) += ubc.o |
| 13 | obj-$(CONFIG_SH_ADC) += adc.o | 13 | obj-$(CONFIG_SH_ADC) += adc.o |
| 14 | |||
| 15 | USE_STANDARD_AS_RULE := true | ||
| 16 | |||
diff --git a/arch/sh/kernel/cpu/bus.c b/arch/sh/kernel/cpu/bus.c index d4fee2a79373..fc6c4bd40c65 100644 --- a/arch/sh/kernel/cpu/bus.c +++ b/arch/sh/kernel/cpu/bus.c | |||
| @@ -53,21 +53,6 @@ static int sh_bus_resume(struct device *dev) | |||
| 53 | return 0; | 53 | return 0; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | static struct device sh_bus_devices[SH_NR_BUSES] = { | ||
| 57 | { | ||
| 58 | .bus_id = SH_BUS_NAME_VIRT, | ||
| 59 | }, | ||
| 60 | }; | ||
| 61 | |||
| 62 | struct bus_type sh_bus_types[SH_NR_BUSES] = { | ||
| 63 | { | ||
| 64 | .name = SH_BUS_NAME_VIRT, | ||
| 65 | .match = sh_bus_match, | ||
| 66 | .suspend = sh_bus_suspend, | ||
| 67 | .resume = sh_bus_resume, | ||
| 68 | }, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static int sh_device_probe(struct device *dev) | 56 | static int sh_device_probe(struct device *dev) |
| 72 | { | 57 | { |
| 73 | struct sh_dev *shdev = to_sh_dev(dev); | 58 | struct sh_dev *shdev = to_sh_dev(dev); |
| @@ -90,6 +75,23 @@ static int sh_device_remove(struct device *dev) | |||
| 90 | return 0; | 75 | return 0; |
| 91 | } | 76 | } |
| 92 | 77 | ||
| 78 | static struct device sh_bus_devices[SH_NR_BUSES] = { | ||
| 79 | { | ||
| 80 | .bus_id = SH_BUS_NAME_VIRT, | ||
| 81 | }, | ||
| 82 | }; | ||
| 83 | |||
| 84 | struct bus_type sh_bus_types[SH_NR_BUSES] = { | ||
| 85 | { | ||
| 86 | .name = SH_BUS_NAME_VIRT, | ||
| 87 | .match = sh_bus_match, | ||
| 88 | .probe = sh_bus_probe, | ||
| 89 | .remove = sh_bus_remove, | ||
| 90 | .suspend = sh_bus_suspend, | ||
| 91 | .resume = sh_bus_resume, | ||
| 92 | }, | ||
| 93 | }; | ||
| 94 | |||
| 93 | int sh_device_register(struct sh_dev *dev) | 95 | int sh_device_register(struct sh_dev *dev) |
| 94 | { | 96 | { |
| 95 | if (!dev) | 97 | if (!dev) |
| @@ -107,6 +109,8 @@ int sh_device_register(struct sh_dev *dev) | |||
| 107 | /* This is needed for USB OHCI to work */ | 109 | /* This is needed for USB OHCI to work */ |
| 108 | if (dev->dma_mask) | 110 | if (dev->dma_mask) |
| 109 | dev->dev.dma_mask = dev->dma_mask; | 111 | dev->dev.dma_mask = dev->dma_mask; |
| 112 | if (dev->coherent_dma_mask) | ||
| 113 | dev->dev.coherent_dma_mask = dev->coherent_dma_mask; | ||
| 110 | 114 | ||
| 111 | snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u", | 115 | snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u", |
| 112 | dev->name, dev->dev_id); | 116 | dev->name, dev->dev_id); |
| @@ -133,8 +137,6 @@ int sh_driver_register(struct sh_driver *drv) | |||
| 133 | return -EINVAL; | 137 | return -EINVAL; |
| 134 | } | 138 | } |
| 135 | 139 | ||
| 136 | drv->drv.probe = sh_device_probe; | ||
| 137 | drv->drv.remove = sh_device_remove; | ||
| 138 | drv->drv.bus = &sh_bus_types[drv->bus_id]; | 140 | drv->drv.bus = &sh_bus_types[drv->bus_id]; |
| 139 | 141 | ||
| 140 | return driver_register(&drv->drv); | 142 | return driver_register(&drv->drv); |
diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c new file mode 100644 index 000000000000..989e7fdd524d --- /dev/null +++ b/arch/sh/kernel/cpu/clock.c | |||
| @@ -0,0 +1,287 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/clock.c - SuperH clock framework | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Paul Mundt | ||
| 5 | * | ||
| 6 | * This clock framework is derived from the OMAP version by: | ||
| 7 | * | ||
| 8 | * Copyright (C) 2004 Nokia Corporation | ||
| 9 | * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> | ||
| 10 | * | ||
| 11 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 12 | * License. See the file "COPYING" in the main directory of this archive | ||
| 13 | * for more details. | ||
| 14 | */ | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/list.h> | ||
| 19 | #include <linux/kref.h> | ||
| 20 | #include <linux/seq_file.h> | ||
| 21 | #include <linux/err.h> | ||
| 22 | #include <asm/clock.h> | ||
| 23 | #include <asm/timer.h> | ||
| 24 | |||
| 25 | static LIST_HEAD(clock_list); | ||
| 26 | static DEFINE_SPINLOCK(clock_lock); | ||
| 27 | static DECLARE_MUTEX(clock_list_sem); | ||
| 28 | |||
| 29 | /* | ||
| 30 | * Each subtype is expected to define the init routines for these clocks, | ||
| 31 | * as each subtype (or processor family) will have these clocks at the | ||
| 32 | * very least. These are all provided through the CPG, which even some of | ||
| 33 | * the more quirky parts (such as ST40, SH4-202, etc.) still have. | ||
| 34 | * | ||
| 35 | * The processor-specific code is expected to register any additional | ||
| 36 | * clock sources that are of interest. | ||
| 37 | */ | ||
| 38 | static struct clk master_clk = { | ||
| 39 | .name = "master_clk", | ||
| 40 | .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, | ||
| 41 | #ifdef CONFIG_SH_PCLK_FREQ_BOOL | ||
| 42 | .rate = CONFIG_SH_PCLK_FREQ, | ||
| 43 | #endif | ||
| 44 | }; | ||
| 45 | |||
| 46 | static struct clk module_clk = { | ||
| 47 | .name = "module_clk", | ||
| 48 | .parent = &master_clk, | ||
| 49 | .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, | ||
| 50 | }; | ||
| 51 | |||
| 52 | static struct clk bus_clk = { | ||
| 53 | .name = "bus_clk", | ||
| 54 | .parent = &master_clk, | ||
| 55 | .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, | ||
| 56 | }; | ||
| 57 | |||
| 58 | static struct clk cpu_clk = { | ||
| 59 | .name = "cpu_clk", | ||
| 60 | .parent = &master_clk, | ||
| 61 | .flags = CLK_ALWAYS_ENABLED, | ||
| 62 | }; | ||
| 63 | |||
| 64 | /* | ||
| 65 | * The ordering of these clocks matters, do not change it. | ||
| 66 | */ | ||
| 67 | static struct clk *onchip_clocks[] = { | ||
| 68 | &master_clk, | ||
| 69 | &module_clk, | ||
| 70 | &bus_clk, | ||
| 71 | &cpu_clk, | ||
| 72 | }; | ||
| 73 | |||
| 74 | static void propagate_rate(struct clk *clk) | ||
| 75 | { | ||
| 76 | struct clk *clkp; | ||
| 77 | |||
| 78 | list_for_each_entry(clkp, &clock_list, node) { | ||
| 79 | if (likely(clkp->parent != clk)) | ||
| 80 | continue; | ||
| 81 | if (likely(clkp->ops && clkp->ops->recalc)) | ||
| 82 | clkp->ops->recalc(clkp); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | int __clk_enable(struct clk *clk) | ||
| 87 | { | ||
| 88 | /* | ||
| 89 | * See if this is the first time we're enabling the clock, some | ||
| 90 | * clocks that are always enabled still require "special" | ||
| 91 | * initialization. This is especially true if the clock mode | ||
| 92 | * changes and the clock needs to hunt for the proper set of | ||
| 93 | * divisors to use before it can effectively recalc. | ||
| 94 | */ | ||
| 95 | if (unlikely(atomic_read(&clk->kref.refcount) == 1)) | ||
| 96 | if (clk->ops && clk->ops->init) | ||
| 97 | clk->ops->init(clk); | ||
| 98 | |||
| 99 | if (clk->flags & CLK_ALWAYS_ENABLED) | ||
| 100 | return 0; | ||
| 101 | |||
| 102 | if (likely(clk->ops && clk->ops->enable)) | ||
| 103 | clk->ops->enable(clk); | ||
| 104 | |||
| 105 | kref_get(&clk->kref); | ||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | int clk_enable(struct clk *clk) | ||
| 110 | { | ||
| 111 | unsigned long flags; | ||
| 112 | int ret; | ||
| 113 | |||
| 114 | spin_lock_irqsave(&clock_lock, flags); | ||
| 115 | ret = __clk_enable(clk); | ||
| 116 | spin_unlock_irqrestore(&clock_lock, flags); | ||
| 117 | |||
| 118 | return ret; | ||
| 119 | } | ||
| 120 | |||
| 121 | static void clk_kref_release(struct kref *kref) | ||
| 122 | { | ||
| 123 | /* Nothing to do */ | ||
| 124 | } | ||
| 125 | |||
| 126 | void __clk_disable(struct clk *clk) | ||
| 127 | { | ||
| 128 | if (clk->flags & CLK_ALWAYS_ENABLED) | ||
| 129 | return; | ||
| 130 | |||
| 131 | kref_put(&clk->kref, clk_kref_release); | ||
| 132 | } | ||
| 133 | |||
| 134 | void clk_disable(struct clk *clk) | ||
| 135 | { | ||
| 136 | unsigned long flags; | ||
| 137 | |||
| 138 | spin_lock_irqsave(&clock_lock, flags); | ||
| 139 | __clk_disable(clk); | ||
| 140 | spin_unlock_irqrestore(&clock_lock, flags); | ||
| 141 | } | ||
| 142 | |||
| 143 | int clk_register(struct clk *clk) | ||
| 144 | { | ||
| 145 | down(&clock_list_sem); | ||
| 146 | |||
| 147 | list_add(&clk->node, &clock_list); | ||
| 148 | kref_init(&clk->kref); | ||
| 149 | |||
| 150 | up(&clock_list_sem); | ||
| 151 | |||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | void clk_unregister(struct clk *clk) | ||
| 156 | { | ||
| 157 | down(&clock_list_sem); | ||
| 158 | list_del(&clk->node); | ||
| 159 | up(&clock_list_sem); | ||
| 160 | } | ||
| 161 | |||
| 162 | inline unsigned long clk_get_rate(struct clk *clk) | ||
| 163 | { | ||
| 164 | return clk->rate; | ||
| 165 | } | ||
| 166 | |||
| 167 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
| 168 | { | ||
| 169 | int ret = -EOPNOTSUPP; | ||
| 170 | |||
| 171 | if (likely(clk->ops && clk->ops->set_rate)) { | ||
| 172 | unsigned long flags; | ||
| 173 | |||
| 174 | spin_lock_irqsave(&clock_lock, flags); | ||
| 175 | ret = clk->ops->set_rate(clk, rate); | ||
| 176 | spin_unlock_irqrestore(&clock_lock, flags); | ||
| 177 | } | ||
| 178 | |||
| 179 | if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) | ||
| 180 | propagate_rate(clk); | ||
| 181 | |||
| 182 | return ret; | ||
| 183 | } | ||
| 184 | |||
| 185 | void clk_recalc_rate(struct clk *clk) | ||
| 186 | { | ||
| 187 | if (likely(clk->ops && clk->ops->recalc)) { | ||
| 188 | unsigned long flags; | ||
| 189 | |||
| 190 | spin_lock_irqsave(&clock_lock, flags); | ||
| 191 | clk->ops->recalc(clk); | ||
| 192 | spin_unlock_irqrestore(&clock_lock, flags); | ||
| 193 | } | ||
| 194 | |||
| 195 | if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) | ||
| 196 | propagate_rate(clk); | ||
| 197 | } | ||
| 198 | |||
| 199 | struct clk *clk_get(const char *id) | ||
| 200 | { | ||
| 201 | struct clk *p, *clk = ERR_PTR(-ENOENT); | ||
| 202 | |||
| 203 | down(&clock_list_sem); | ||
| 204 | list_for_each_entry(p, &clock_list, node) { | ||
| 205 | if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | ||
| 206 | clk = p; | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | up(&clock_list_sem); | ||
| 211 | |||
| 212 | return clk; | ||
| 213 | } | ||
| 214 | |||
| 215 | void clk_put(struct clk *clk) | ||
| 216 | { | ||
| 217 | if (clk && !IS_ERR(clk)) | ||
| 218 | module_put(clk->owner); | ||
| 219 | } | ||
| 220 | |||
| 221 | void __init __attribute__ ((weak)) | ||
| 222 | arch_init_clk_ops(struct clk_ops **ops, int type) | ||
| 223 | { | ||
| 224 | } | ||
| 225 | |||
| 226 | int __init clk_init(void) | ||
| 227 | { | ||
| 228 | int i, ret = 0; | ||
| 229 | |||
| 230 | if (unlikely(!master_clk.rate)) | ||
| 231 | /* | ||
| 232 | * NOTE: This will break if the default divisor has been | ||
| 233 | * changed. | ||
| 234 | * | ||
| 235 | * No one should be changing the default on us however, | ||
| 236 | * expect that a sane value for CONFIG_SH_PCLK_FREQ will | ||
| 237 | * be defined in the event of a different divisor. | ||
| 238 | */ | ||
| 239 | master_clk.rate = get_timer_frequency() * 4; | ||
| 240 | |||
| 241 | for (i = 0; i < ARRAY_SIZE(onchip_clocks); i++) { | ||
| 242 | struct clk *clk = onchip_clocks[i]; | ||
| 243 | |||
| 244 | arch_init_clk_ops(&clk->ops, i); | ||
| 245 | ret |= clk_register(clk); | ||
| 246 | clk_enable(clk); | ||
| 247 | } | ||
| 248 | |||
| 249 | /* Kick the child clocks.. */ | ||
| 250 | propagate_rate(&master_clk); | ||
| 251 | propagate_rate(&bus_clk); | ||
| 252 | |||
| 253 | return ret; | ||
| 254 | } | ||
| 255 | |||
| 256 | int show_clocks(struct seq_file *m) | ||
| 257 | { | ||
| 258 | struct clk *clk; | ||
| 259 | |||
| 260 | list_for_each_entry_reverse(clk, &clock_list, node) { | ||
| 261 | unsigned long rate = clk_get_rate(clk); | ||
| 262 | |||
| 263 | /* | ||
| 264 | * Don't bother listing dummy clocks with no ancestry | ||
| 265 | * that only support enable and disable ops. | ||
| 266 | */ | ||
| 267 | if (unlikely(!rate && !clk->parent)) | ||
| 268 | continue; | ||
| 269 | |||
| 270 | seq_printf(m, "%-12s\t: %ld.%02ldMHz\n", clk->name, | ||
| 271 | rate / 1000000, (rate % 1000000) / 10000); | ||
| 272 | } | ||
| 273 | |||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | |||
| 277 | EXPORT_SYMBOL_GPL(clk_register); | ||
| 278 | EXPORT_SYMBOL_GPL(clk_unregister); | ||
| 279 | EXPORT_SYMBOL_GPL(clk_get); | ||
| 280 | EXPORT_SYMBOL_GPL(clk_put); | ||
| 281 | EXPORT_SYMBOL_GPL(clk_enable); | ||
| 282 | EXPORT_SYMBOL_GPL(clk_disable); | ||
| 283 | EXPORT_SYMBOL_GPL(__clk_enable); | ||
| 284 | EXPORT_SYMBOL_GPL(__clk_disable); | ||
| 285 | EXPORT_SYMBOL_GPL(clk_get_rate); | ||
| 286 | EXPORT_SYMBOL_GPL(clk_set_rate); | ||
| 287 | EXPORT_SYMBOL_GPL(clk_recalc_rate); | ||
diff --git a/arch/sh/kernel/cpu/irq/Makefile b/arch/sh/kernel/cpu/irq/Makefile new file mode 100644 index 000000000000..e3cccea15e1d --- /dev/null +++ b/arch/sh/kernel/cpu/irq/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the Linux/SuperH CPU-specifc IRQ handlers. | ||
| 3 | # | ||
| 4 | obj-y += ipr.o imask.o | ||
| 5 | |||
| 6 | obj-$(CONFIG_CPU_HAS_PINT_IRQ) += pint.o | ||
| 7 | obj-$(CONFIG_CPU_HAS_INTC2_IRQ) += intc2.o | ||
diff --git a/arch/sh/kernel/cpu/irq_imask.c b/arch/sh/kernel/cpu/irq/imask.c index a963d00a971e..baed9a550d39 100644 --- a/arch/sh/kernel/cpu/irq_imask.c +++ b/arch/sh/kernel/cpu/irq/imask.c | |||
| @@ -1,16 +1,12 @@ | |||
| 1 | /* $Id: irq_imask.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $ | 1 | /* |
| 2 | * | 2 | * arch/sh/kernel/cpu/irq/imask.c |
| 3 | * linux/arch/sh/kernel/irq_imask.c | ||
| 4 | * | 3 | * |
| 5 | * Copyright (C) 1999, 2000 Niibe Yutaka | 4 | * Copyright (C) 1999, 2000 Niibe Yutaka |
| 6 | * | 5 | * |
| 7 | * Simple interrupt handling using IMASK of SR register. | 6 | * Simple interrupt handling using IMASK of SR register. |
| 8 | * | 7 | * |
| 9 | */ | 8 | */ |
| 10 | |||
| 11 | /* NOTE: Will not work on level 15 */ | 9 | /* NOTE: Will not work on level 15 */ |
| 12 | |||
| 13 | |||
| 14 | #include <linux/ptrace.h> | 10 | #include <linux/ptrace.h> |
| 15 | #include <linux/errno.h> | 11 | #include <linux/errno.h> |
| 16 | #include <linux/kernel_stat.h> | 12 | #include <linux/kernel_stat.h> |
| @@ -19,13 +15,11 @@ | |||
| 19 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
| 20 | #include <linux/init.h> | 16 | #include <linux/init.h> |
| 21 | #include <linux/bitops.h> | 17 | #include <linux/bitops.h> |
| 22 | |||
| 23 | #include <asm/system.h> | ||
| 24 | #include <asm/irq.h> | ||
| 25 | |||
| 26 | #include <linux/spinlock.h> | 18 | #include <linux/spinlock.h> |
| 27 | #include <linux/cache.h> | 19 | #include <linux/cache.h> |
| 28 | #include <linux/irq.h> | 20 | #include <linux/irq.h> |
| 21 | #include <asm/system.h> | ||
| 22 | #include <asm/irq.h> | ||
| 29 | 23 | ||
| 30 | /* Bitmap of IRQ masked */ | 24 | /* Bitmap of IRQ masked */ |
| 31 | static unsigned long imask_mask = 0x7fff; | 25 | static unsigned long imask_mask = 0x7fff; |
| @@ -40,7 +34,7 @@ static void end_imask_irq(unsigned int irq); | |||
| 40 | #define IMASK_PRIORITY 15 | 34 | #define IMASK_PRIORITY 15 |
| 41 | 35 | ||
| 42 | static unsigned int startup_imask_irq(unsigned int irq) | 36 | static unsigned int startup_imask_irq(unsigned int irq) |
| 43 | { | 37 | { |
| 44 | /* Nothing to do */ | 38 | /* Nothing to do */ |
| 45 | return 0; /* never anything pending */ | 39 | return 0; /* never anything pending */ |
| 46 | } | 40 | } |
diff --git a/arch/sh/kernel/cpu/irq/intc2.c b/arch/sh/kernel/cpu/irq/intc2.c new file mode 100644 index 000000000000..06e8afab32e4 --- /dev/null +++ b/arch/sh/kernel/cpu/irq/intc2.c | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | /* | ||
| 2 | * Interrupt handling for INTC2-based IRQ. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) | ||
| 5 | * Copyright (C) 2005, 2006 Paul Mundt (lethal@linux-sh.org) | ||
| 6 | * | ||
| 7 | * May be copied or modified under the terms of the GNU General Public | ||
| 8 | * License. See linux/COPYING for more information. | ||
| 9 | * | ||
| 10 | * These are the "new Hitachi style" interrupts, as present on the | ||
| 11 | * Hitachi 7751, the STM ST40 STB1, SH7760, and SH7780. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/kernel.h> | ||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/irq.h> | ||
| 17 | #include <asm/system.h> | ||
| 18 | #include <asm/io.h> | ||
| 19 | #include <asm/machvec.h> | ||
| 20 | |||
| 21 | struct intc2_data { | ||
| 22 | unsigned char msk_offset; | ||
| 23 | unsigned char msk_shift; | ||
| 24 | |||
| 25 | int (*clear_irq) (int); | ||
| 26 | }; | ||
| 27 | |||
| 28 | static struct intc2_data intc2_data[NR_INTC2_IRQS]; | ||
| 29 | |||
| 30 | static void enable_intc2_irq(unsigned int irq); | ||
| 31 | static void disable_intc2_irq(unsigned int irq); | ||
| 32 | |||
| 33 | /* shutdown is same as "disable" */ | ||
| 34 | #define shutdown_intc2_irq disable_intc2_irq | ||
| 35 | |||
| 36 | static void mask_and_ack_intc2(unsigned int); | ||
| 37 | static void end_intc2_irq(unsigned int irq); | ||
| 38 | |||
| 39 | static unsigned int startup_intc2_irq(unsigned int irq) | ||
| 40 | { | ||
| 41 | enable_intc2_irq(irq); | ||
| 42 | return 0; /* never anything pending */ | ||
| 43 | } | ||
| 44 | |||
| 45 | static struct hw_interrupt_type intc2_irq_type = { | ||
| 46 | .typename = "INTC2-IRQ", | ||
| 47 | .startup = startup_intc2_irq, | ||
| 48 | .shutdown = shutdown_intc2_irq, | ||
| 49 | .enable = enable_intc2_irq, | ||
| 50 | .disable = disable_intc2_irq, | ||
| 51 | .ack = mask_and_ack_intc2, | ||
| 52 | .end = end_intc2_irq | ||
| 53 | }; | ||
| 54 | |||
| 55 | static void disable_intc2_irq(unsigned int irq) | ||
| 56 | { | ||
| 57 | int irq_offset = irq - INTC2_FIRST_IRQ; | ||
| 58 | int msk_shift, msk_offset; | ||
| 59 | |||
| 60 | /* Sanity check */ | ||
| 61 | if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS)) | ||
| 62 | return; | ||
| 63 | |||
| 64 | msk_shift = intc2_data[irq_offset].msk_shift; | ||
| 65 | msk_offset = intc2_data[irq_offset].msk_offset; | ||
| 66 | |||
| 67 | ctrl_outl(1 << msk_shift, | ||
| 68 | INTC2_BASE + INTC2_INTMSK_OFFSET + msk_offset); | ||
| 69 | } | ||
| 70 | |||
| 71 | static void enable_intc2_irq(unsigned int irq) | ||
| 72 | { | ||
| 73 | int irq_offset = irq - INTC2_FIRST_IRQ; | ||
| 74 | int msk_shift, msk_offset; | ||
| 75 | |||
| 76 | /* Sanity check */ | ||
| 77 | if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS)) | ||
| 78 | return; | ||
| 79 | |||
| 80 | msk_shift = intc2_data[irq_offset].msk_shift; | ||
| 81 | msk_offset = intc2_data[irq_offset].msk_offset; | ||
| 82 | |||
| 83 | ctrl_outl(1 << msk_shift, | ||
| 84 | INTC2_BASE + INTC2_INTMSKCLR_OFFSET + msk_offset); | ||
| 85 | } | ||
| 86 | |||
| 87 | static void mask_and_ack_intc2(unsigned int irq) | ||
| 88 | { | ||
| 89 | disable_intc2_irq(irq); | ||
| 90 | } | ||
| 91 | |||
| 92 | static void end_intc2_irq(unsigned int irq) | ||
| 93 | { | ||
| 94 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
| 95 | enable_intc2_irq(irq); | ||
| 96 | |||
| 97 | if (unlikely(intc2_data[irq - INTC2_FIRST_IRQ].clear_irq)) | ||
| 98 | intc2_data[irq - INTC2_FIRST_IRQ].clear_irq(irq); | ||
| 99 | } | ||
| 100 | |||
| 101 | /* | ||
| 102 | * Setup an INTC2 style interrupt. | ||
| 103 | * NOTE: Unlike IPR interrupts, parameters are not shifted by this code, | ||
| 104 | * allowing the use of the numbers straight out of the datasheet. | ||
| 105 | * For example: | ||
| 106 | * PIO1 which is INTPRI00[19,16] and INTMSK00[13] | ||
| 107 | * would be: ^ ^ ^ ^ | ||
| 108 | * | | | | | ||
| 109 | * make_intc2_irq(84, 0, 16, 0, 13); | ||
| 110 | */ | ||
| 111 | void make_intc2_irq(unsigned int irq, | ||
| 112 | unsigned int ipr_offset, unsigned int ipr_shift, | ||
| 113 | unsigned int msk_offset, unsigned int msk_shift, | ||
| 114 | unsigned int priority) | ||
| 115 | { | ||
| 116 | int irq_offset = irq - INTC2_FIRST_IRQ; | ||
| 117 | unsigned int flags; | ||
| 118 | unsigned long ipr; | ||
| 119 | |||
| 120 | if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS)) | ||
| 121 | return; | ||
| 122 | |||
| 123 | disable_irq_nosync(irq); | ||
| 124 | |||
| 125 | /* Fill the data we need */ | ||
| 126 | intc2_data[irq_offset].msk_offset = msk_offset; | ||
| 127 | intc2_data[irq_offset].msk_shift = msk_shift; | ||
| 128 | intc2_data[irq_offset].clear_irq = NULL; | ||
| 129 | |||
| 130 | /* Set the priority level */ | ||
| 131 | local_irq_save(flags); | ||
| 132 | |||
| 133 | ipr = ctrl_inl(INTC2_BASE + INTC2_INTPRI_OFFSET + ipr_offset); | ||
| 134 | ipr &= ~(0xf << ipr_shift); | ||
| 135 | ipr |= priority << ipr_shift; | ||
| 136 | ctrl_outl(ipr, INTC2_BASE + INTC2_INTPRI_OFFSET + ipr_offset); | ||
| 137 | |||
| 138 | local_irq_restore(flags); | ||
| 139 | |||
| 140 | irq_desc[irq].handler = &intc2_irq_type; | ||
| 141 | |||
| 142 | disable_intc2_irq(irq); | ||
| 143 | } | ||
| 144 | |||
| 145 | static struct intc2_init { | ||
| 146 | unsigned short irq; | ||
| 147 | unsigned char ipr_offset, ipr_shift; | ||
| 148 | unsigned char msk_offset, msk_shift; | ||
| 149 | unsigned char priority; | ||
| 150 | } intc2_init_data[] __initdata = { | ||
| 151 | #if defined(CONFIG_CPU_SUBTYPE_ST40) | ||
| 152 | {64, 0, 0, 0, 0, 13}, /* PCI serr */ | ||
| 153 | {65, 0, 4, 0, 1, 13}, /* PCI err */ | ||
| 154 | {66, 0, 4, 0, 2, 13}, /* PCI ad */ | ||
| 155 | {67, 0, 4, 0, 3, 13}, /* PCI pwd down */ | ||
| 156 | {72, 0, 8, 0, 5, 13}, /* DMAC INT0 */ | ||
| 157 | {73, 0, 8, 0, 6, 13}, /* DMAC INT1 */ | ||
| 158 | {74, 0, 8, 0, 7, 13}, /* DMAC INT2 */ | ||
| 159 | {75, 0, 8, 0, 8, 13}, /* DMAC INT3 */ | ||
| 160 | {76, 0, 8, 0, 9, 13}, /* DMAC INT4 */ | ||
| 161 | {78, 0, 8, 0, 11, 13}, /* DMAC ERR */ | ||
| 162 | {80, 0, 12, 0, 12, 13}, /* PIO0 */ | ||
| 163 | {84, 0, 16, 0, 13, 13}, /* PIO1 */ | ||
| 164 | {88, 0, 20, 0, 14, 13}, /* PIO2 */ | ||
| 165 | {112, 4, 0, 4, 0, 13}, /* Mailbox */ | ||
| 166 | #ifdef CONFIG_CPU_SUBTYPE_ST40GX1 | ||
| 167 | {116, 4, 4, 4, 4, 13}, /* SSC0 */ | ||
| 168 | {120, 4, 8, 4, 8, 13}, /* IR Blaster */ | ||
| 169 | {124, 4, 12, 4, 12, 13}, /* USB host */ | ||
| 170 | {128, 4, 16, 4, 16, 13}, /* Video processor BLITTER */ | ||
| 171 | {132, 4, 20, 4, 20, 13}, /* UART0 */ | ||
| 172 | {134, 4, 20, 4, 22, 13}, /* UART2 */ | ||
| 173 | {136, 4, 24, 4, 24, 13}, /* IO_PIO0 */ | ||
| 174 | {140, 4, 28, 4, 28, 13}, /* EMPI */ | ||
| 175 | {144, 8, 0, 8, 0, 13}, /* MAFE */ | ||
| 176 | {148, 8, 4, 8, 4, 13}, /* PWM */ | ||
| 177 | {152, 8, 8, 8, 8, 13}, /* SSC1 */ | ||
| 178 | {156, 8, 12, 8, 12, 13}, /* IO_PIO1 */ | ||
| 179 | {160, 8, 16, 8, 16, 13}, /* USB target */ | ||
| 180 | {164, 8, 20, 8, 20, 13}, /* UART1 */ | ||
| 181 | {168, 8, 24, 8, 24, 13}, /* Teletext */ | ||
| 182 | {172, 8, 28, 8, 28, 13}, /* VideoSync VTG */ | ||
| 183 | {173, 8, 28, 8, 29, 13}, /* VideoSync DVP0 */ | ||
| 184 | {174, 8, 28, 8, 30, 13}, /* VideoSync DVP1 */ | ||
| 185 | #endif | ||
| 186 | #elif defined(CONFIG_CPU_SUBTYPE_SH7760) | ||
| 187 | /* | ||
| 188 | * SH7760 INTC2-Style interrupts, vectors IRQ48-111 INTEVT 0x800-0xFE0 | ||
| 189 | */ | ||
| 190 | /* INTPRIO0 | INTMSK0 */ | ||
| 191 | {48, 0, 28, 0, 31, 3}, /* IRQ 4 */ | ||
| 192 | {49, 0, 24, 0, 30, 3}, /* IRQ 3 */ | ||
| 193 | {50, 0, 20, 0, 29, 3}, /* IRQ 2 */ | ||
| 194 | {51, 0, 16, 0, 28, 3}, /* IRQ 1 */ | ||
| 195 | /* 52-55 (INTEVT 0x880-0x8E0) unused/reserved */ | ||
| 196 | /* INTPRIO4 | INTMSK0 */ | ||
| 197 | {56, 4, 28, 0, 25, 3}, /* HCAN2_CHAN0 */ | ||
| 198 | {57, 4, 24, 0, 24, 3}, /* HCAN2_CHAN1 */ | ||
| 199 | {58, 4, 20, 0, 23, 3}, /* I2S_CHAN0 */ | ||
| 200 | {59, 4, 16, 0, 22, 3}, /* I2S_CHAN1 */ | ||
| 201 | {60, 4, 12, 0, 21, 3}, /* AC97_CHAN0 */ | ||
| 202 | {61, 4, 8, 0, 20, 3}, /* AC97_CHAN1 */ | ||
| 203 | {62, 4, 4, 0, 19, 3}, /* I2C_CHAN0 */ | ||
| 204 | {63, 4, 0, 0, 18, 3}, /* I2C_CHAN1 */ | ||
| 205 | /* INTPRIO8 | INTMSK0 */ | ||
| 206 | {52, 8, 16, 0, 11, 3}, /* SCIF0_ERI_IRQ */ | ||
| 207 | {53, 8, 16, 0, 10, 3}, /* SCIF0_RXI_IRQ */ | ||
| 208 | {54, 8, 16, 0, 9, 3}, /* SCIF0_BRI_IRQ */ | ||
| 209 | {55, 8, 16, 0, 8, 3}, /* SCIF0_TXI_IRQ */ | ||
| 210 | {64, 8, 28, 0, 17, 3}, /* USBHI_IRQ */ | ||
| 211 | {65, 8, 24, 0, 16, 3}, /* LCDC */ | ||
| 212 | /* 66, 67 unused */ | ||
| 213 | {68, 8, 20, 0, 14, 13}, /* DMABRGI0_IRQ */ | ||
| 214 | {69, 8, 20, 0, 13, 13}, /* DMABRGI1_IRQ */ | ||
| 215 | {70, 8, 20, 0, 12, 13}, /* DMABRGI2_IRQ */ | ||
| 216 | /* 71 unused */ | ||
| 217 | {72, 8, 12, 0, 7, 3}, /* SCIF1_ERI_IRQ */ | ||
| 218 | {73, 8, 12, 0, 6, 3}, /* SCIF1_RXI_IRQ */ | ||
| 219 | {74, 8, 12, 0, 5, 3}, /* SCIF1_BRI_IRQ */ | ||
| 220 | {75, 8, 12, 0, 4, 3}, /* SCIF1_TXI_IRQ */ | ||
| 221 | {76, 8, 8, 0, 3, 3}, /* SCIF2_ERI_IRQ */ | ||
| 222 | {77, 8, 8, 0, 2, 3}, /* SCIF2_RXI_IRQ */ | ||
| 223 | {78, 8, 8, 0, 1, 3}, /* SCIF2_BRI_IRQ */ | ||
| 224 | {79, 8, 8, 0, 0, 3}, /* SCIF2_TXI_IRQ */ | ||
| 225 | /* | INTMSK4 */ | ||
| 226 | {80, 8, 4, 4, 23, 3}, /* SIM_ERI */ | ||
| 227 | {81, 8, 4, 4, 22, 3}, /* SIM_RXI */ | ||
| 228 | {82, 8, 4, 4, 21, 3}, /* SIM_TXI */ | ||
| 229 | {83, 8, 4, 4, 20, 3}, /* SIM_TEI */ | ||
| 230 | {84, 8, 0, 4, 19, 3}, /* HSPII */ | ||
| 231 | /* INTPRIOC | INTMSK4 */ | ||
| 232 | /* 85-87 unused/reserved */ | ||
| 233 | {88, 12, 20, 4, 18, 3}, /* MMCI0 */ | ||
| 234 | {89, 12, 20, 4, 17, 3}, /* MMCI1 */ | ||
| 235 | {90, 12, 20, 4, 16, 3}, /* MMCI2 */ | ||
| 236 | {91, 12, 20, 4, 15, 3}, /* MMCI3 */ | ||
| 237 | {92, 12, 12, 4, 6, 3}, /* MFI (unsure, bug? in my 7760 manual*/ | ||
| 238 | /* 93-107 reserved/undocumented */ | ||
| 239 | {108,12, 4, 4, 1, 3}, /* ADC */ | ||
| 240 | {109,12, 0, 4, 0, 3}, /* CMTI */ | ||
| 241 | /* 110-111 reserved/unused */ | ||
| 242 | #elif defined(CONFIG_CPU_SUBTYPE_SH7780) | ||
| 243 | { TIMER_IRQ, 0, 24, 0, INTC_TMU0_MSK, 2}, | ||
| 244 | #ifdef CONFIG_SH_RTC | ||
| 245 | { RTC_IRQ, 4, 0, 0, INTC_RTC_MSK, TIMER_PRIORITY }, | ||
| 246 | #endif | ||
| 247 | { SCIF0_ERI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, | ||
| 248 | { SCIF0_RXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, | ||
| 249 | { SCIF0_BRI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, | ||
| 250 | { SCIF0_TXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, | ||
| 251 | |||
| 252 | { SCIF1_ERI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY }, | ||
| 253 | { SCIF1_RXI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY }, | ||
| 254 | { SCIF1_BRI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY }, | ||
| 255 | { SCIF1_TXI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY }, | ||
| 256 | |||
| 257 | { PCIC0_IRQ, 0x10, 8, 0, INTC_PCIC0_MSK, PCIC0_PRIORITY }, | ||
| 258 | { PCIC1_IRQ, 0x10, 0, 0, INTC_PCIC1_MSK, PCIC1_PRIORITY }, | ||
| 259 | { PCIC2_IRQ, 0x14, 24, 0, INTC_PCIC2_MSK, PCIC2_PRIORITY }, | ||
| 260 | { PCIC3_IRQ, 0x14, 16, 0, INTC_PCIC3_MSK, PCIC3_PRIORITY }, | ||
| 261 | { PCIC4_IRQ, 0x14, 8, 0, INTC_PCIC4_MSK, PCIC4_PRIORITY }, | ||
| 262 | #endif | ||
| 263 | }; | ||
| 264 | |||
| 265 | void __init init_IRQ_intc2(void) | ||
| 266 | { | ||
| 267 | int i; | ||
| 268 | |||
| 269 | for (i = 0; i < ARRAY_SIZE(intc2_init_data); i++) { | ||
| 270 | struct intc2_init *p = intc2_init_data + i; | ||
| 271 | make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift, | ||
| 272 | p-> msk_offset, p->msk_shift, p->priority); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | /* Adds a termination callback to the interrupt */ | ||
| 277 | void intc2_add_clear_irq(int irq, int (*fn)(int)) | ||
| 278 | { | ||
| 279 | if (unlikely(irq < INTC2_FIRST_IRQ)) | ||
| 280 | return; | ||
| 281 | |||
| 282 | intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn; | ||
| 283 | } | ||
| 284 | |||
diff --git a/arch/sh/kernel/cpu/irq_ipr.c b/arch/sh/kernel/cpu/irq/ipr.c index 71f92096132b..fdbd718ae5c6 100644 --- a/arch/sh/kernel/cpu/irq_ipr.c +++ b/arch/sh/kernel/cpu/irq/ipr.c | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | /* $Id: irq_ipr.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $ | 1 | /* |
| 2 | * | 2 | * arch/sh/kernel/cpu/irq/ipr.c |
| 3 | * linux/arch/sh/kernel/irq_ipr.c | ||
| 4 | * | 3 | * |
| 5 | * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi | 4 | * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi |
| 6 | * Copyright (C) 2000 Kazumoto Kojima | 5 | * Copyright (C) 2000 Kazumoto Kojima |
| @@ -109,7 +108,8 @@ static void end_ipr_irq(unsigned int irq) | |||
| 109 | enable_ipr_irq(irq); | 108 | enable_ipr_irq(irq); |
| 110 | } | 109 | } |
| 111 | 110 | ||
| 112 | void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority) | 111 | void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, |
| 112 | int priority, int maskpos) | ||
| 113 | { | 113 | { |
| 114 | disable_irq_nosync(irq); | 114 | disable_irq_nosync(irq); |
| 115 | ipr_data[irq].addr = addr; | 115 | ipr_data[irq].addr = addr; |
| @@ -120,126 +120,47 @@ void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority) | |||
| 120 | disable_ipr_irq(irq); | 120 | disable_ipr_irq(irq); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ | ||
| 124 | defined(CONFIG_CPU_SUBTYPE_SH7707) || \ | ||
| 125 | defined(CONFIG_CPU_SUBTYPE_SH7709) | ||
| 126 | static unsigned char pint_map[256]; | ||
| 127 | static unsigned long portcr_mask = 0; | ||
| 128 | |||
| 129 | static void enable_pint_irq(unsigned int irq); | ||
| 130 | static void disable_pint_irq(unsigned int irq); | ||
| 131 | |||
| 132 | /* shutdown is same as "disable" */ | ||
| 133 | #define shutdown_pint_irq disable_pint_irq | ||
| 134 | |||
| 135 | static void mask_and_ack_pint(unsigned int); | ||
| 136 | static void end_pint_irq(unsigned int irq); | ||
| 137 | |||
| 138 | static unsigned int startup_pint_irq(unsigned int irq) | ||
| 139 | { | ||
| 140 | enable_pint_irq(irq); | ||
| 141 | return 0; /* never anything pending */ | ||
| 142 | } | ||
| 143 | |||
| 144 | static struct hw_interrupt_type pint_irq_type = { | ||
| 145 | .typename = "PINT-IRQ", | ||
| 146 | .startup = startup_pint_irq, | ||
| 147 | .shutdown = shutdown_pint_irq, | ||
| 148 | .enable = enable_pint_irq, | ||
| 149 | .disable = disable_pint_irq, | ||
| 150 | .ack = mask_and_ack_pint, | ||
| 151 | .end = end_pint_irq | ||
| 152 | }; | ||
| 153 | |||
| 154 | static void disable_pint_irq(unsigned int irq) | ||
| 155 | { | ||
| 156 | unsigned long val, flags; | ||
| 157 | |||
| 158 | local_irq_save(flags); | ||
| 159 | val = ctrl_inw(INTC_INTER); | ||
| 160 | val &= ~(1 << (irq - PINT_IRQ_BASE)); | ||
| 161 | ctrl_outw(val, INTC_INTER); /* disable PINTn */ | ||
| 162 | portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2); | ||
| 163 | local_irq_restore(flags); | ||
| 164 | } | ||
| 165 | |||
| 166 | static void enable_pint_irq(unsigned int irq) | ||
| 167 | { | ||
| 168 | unsigned long val, flags; | ||
| 169 | |||
| 170 | local_irq_save(flags); | ||
| 171 | val = ctrl_inw(INTC_INTER); | ||
| 172 | val |= 1 << (irq - PINT_IRQ_BASE); | ||
| 173 | ctrl_outw(val, INTC_INTER); /* enable PINTn */ | ||
| 174 | portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2; | ||
| 175 | local_irq_restore(flags); | ||
| 176 | } | ||
| 177 | |||
| 178 | static void mask_and_ack_pint(unsigned int irq) | ||
| 179 | { | ||
| 180 | disable_pint_irq(irq); | ||
| 181 | } | ||
| 182 | |||
| 183 | static void end_pint_irq(unsigned int irq) | ||
| 184 | { | ||
| 185 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
| 186 | enable_pint_irq(irq); | ||
| 187 | } | ||
| 188 | |||
| 189 | void make_pint_irq(unsigned int irq) | ||
| 190 | { | ||
| 191 | disable_irq_nosync(irq); | ||
| 192 | irq_desc[irq].handler = &pint_irq_type; | ||
| 193 | disable_pint_irq(irq); | ||
| 194 | } | ||
| 195 | #endif | ||
| 196 | |||
| 197 | void __init init_IRQ(void) | 123 | void __init init_IRQ(void) |
| 198 | { | 124 | { |
| 199 | #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ | 125 | #ifndef CONFIG_CPU_SUBTYPE_SH7780 |
| 200 | defined(CONFIG_CPU_SUBTYPE_SH7707) || \ | 126 | make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY, 0); |
| 201 | defined(CONFIG_CPU_SUBTYPE_SH7709) | 127 | make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY, 0); |
| 202 | int i; | ||
| 203 | #endif | ||
| 204 | |||
| 205 | make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY); | ||
| 206 | make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY); | ||
| 207 | #if defined(CONFIG_SH_RTC) | 128 | #if defined(CONFIG_SH_RTC) |
| 208 | make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY); | 129 | make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY, 0); |
| 209 | #endif | 130 | #endif |
| 210 | 131 | ||
| 211 | #ifdef SCI_ERI_IRQ | 132 | #ifdef SCI_ERI_IRQ |
| 212 | make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); | 133 | make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0); |
| 213 | make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); | 134 | make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0); |
| 214 | make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); | 135 | make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0); |
| 215 | #endif | 136 | #endif |
| 216 | 137 | ||
| 217 | #ifdef SCIF1_ERI_IRQ | 138 | #ifdef SCIF1_ERI_IRQ |
| 218 | make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); | 139 | make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0); |
| 219 | make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); | 140 | make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0); |
| 220 | make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); | 141 | make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0); |
| 221 | make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); | 142 | make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0); |
| 222 | #endif | 143 | #endif |
| 223 | 144 | ||
| 224 | #if defined(CONFIG_CPU_SUBTYPE_SH7300) | 145 | #if defined(CONFIG_CPU_SUBTYPE_SH7300) |
| 225 | make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY); | 146 | make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY, 0); |
| 226 | make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY); | 147 | make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY, 0); |
| 227 | make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY); | 148 | make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY, 0); |
| 228 | make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY); | 149 | make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY, 0); |
| 229 | #endif | 150 | #endif |
| 230 | 151 | ||
| 231 | #ifdef SCIF_ERI_IRQ | 152 | #ifdef SCIF_ERI_IRQ |
| 232 | make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); | 153 | make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0); |
| 233 | make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); | 154 | make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0); |
| 234 | make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); | 155 | make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0); |
| 235 | make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); | 156 | make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0); |
| 236 | #endif | 157 | #endif |
| 237 | 158 | ||
| 238 | #ifdef IRDA_ERI_IRQ | 159 | #ifdef IRDA_ERI_IRQ |
| 239 | make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); | 160 | make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0); |
| 240 | make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); | 161 | make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0); |
| 241 | make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); | 162 | make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0); |
| 242 | make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); | 163 | make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0); |
| 243 | #endif | 164 | #endif |
| 244 | 165 | ||
| 245 | #if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ | 166 | #if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ |
| @@ -254,86 +175,32 @@ void __init init_IRQ(void) | |||
| 254 | * You should set corresponding bits of PFC to "00" | 175 | * You should set corresponding bits of PFC to "00" |
| 255 | * to enable these interrupts. | 176 | * to enable these interrupts. |
| 256 | */ | 177 | */ |
| 257 | make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY); | 178 | make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY, 0); |
| 258 | make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY); | 179 | make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY, 0); |
| 259 | make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY); | 180 | make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY, 0); |
| 260 | make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY); | 181 | make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY, 0); |
| 261 | make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY); | 182 | make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY, 0); |
| 262 | make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY); | 183 | make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY, 0); |
| 263 | #if !defined(CONFIG_CPU_SUBTYPE_SH7300) | 184 | #endif |
| 264 | make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY); | 185 | #endif |
| 265 | make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY); | ||
| 266 | enable_ipr_irq(PINT0_IRQ); | ||
| 267 | enable_ipr_irq(PINT8_IRQ); | ||
| 268 | 186 | ||
| 269 | for(i = 0; i < 16; i++) | 187 | #ifdef CONFIG_CPU_HAS_PINT_IRQ |
| 270 | make_pint_irq(PINT_IRQ_BASE + i); | 188 | init_IRQ_pint(); |
| 271 | for(i = 0; i < 256; i++) | 189 | #endif |
| 272 | { | ||
| 273 | if(i & 1) pint_map[i] = 0; | ||
| 274 | else if(i & 2) pint_map[i] = 1; | ||
| 275 | else if(i & 4) pint_map[i] = 2; | ||
| 276 | else if(i & 8) pint_map[i] = 3; | ||
| 277 | else if(i & 0x10) pint_map[i] = 4; | ||
| 278 | else if(i & 0x20) pint_map[i] = 5; | ||
| 279 | else if(i & 0x40) pint_map[i] = 6; | ||
| 280 | else if(i & 0x80) pint_map[i] = 7; | ||
| 281 | } | ||
| 282 | #endif /* !CONFIG_CPU_SUBTYPE_SH7300 */ | ||
| 283 | #endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 || CONFIG_CPU_SUBTYPE_SH7300*/ | ||
| 284 | 190 | ||
| 285 | #ifdef CONFIG_CPU_SUBTYPE_ST40 | 191 | #ifdef CONFIG_CPU_HAS_INTC2_IRQ |
| 286 | init_IRQ_intc2(); | 192 | init_IRQ_intc2(); |
| 287 | #endif | 193 | #endif |
| 288 | |||
| 289 | /* Perform the machine specific initialisation */ | 194 | /* Perform the machine specific initialisation */ |
| 290 | if (sh_mv.mv_init_irq != NULL) { | 195 | if (sh_mv.mv_init_irq != NULL) |
| 291 | sh_mv.mv_init_irq(); | 196 | sh_mv.mv_init_irq(); |
| 292 | } | ||
| 293 | } | 197 | } |
| 294 | #if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ | 198 | |
| 295 | defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) | 199 | #if !defined(CONFIG_CPU_HAS_PINT_IRQ) |
| 296 | int ipr_irq_demux(int irq) | 200 | int ipr_irq_demux(int irq) |
| 297 | { | 201 | { |
| 298 | #if !defined(CONFIG_CPU_SUBTYPE_SH7300) | ||
| 299 | unsigned long creg, dreg, d, sav; | ||
| 300 | |||
| 301 | if(irq == PINT0_IRQ) | ||
| 302 | { | ||
| 303 | #if defined(CONFIG_CPU_SUBTYPE_SH7707) | ||
| 304 | creg = PORT_PACR; | ||
| 305 | dreg = PORT_PADR; | ||
| 306 | #else | ||
| 307 | creg = PORT_PCCR; | ||
| 308 | dreg = PORT_PCDR; | ||
| 309 | #endif | ||
| 310 | sav = ctrl_inw(creg); | ||
| 311 | ctrl_outw(sav | portcr_mask, creg); | ||
| 312 | d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & ctrl_inw(INTC_INTER) & 0xff; | ||
| 313 | ctrl_outw(sav, creg); | ||
| 314 | if(d == 0) return irq; | ||
| 315 | return PINT_IRQ_BASE + pint_map[d]; | ||
| 316 | } | ||
| 317 | else if(irq == PINT8_IRQ) | ||
| 318 | { | ||
| 319 | #if defined(CONFIG_CPU_SUBTYPE_SH7707) | ||
| 320 | creg = PORT_PBCR; | ||
| 321 | dreg = PORT_PBDR; | ||
| 322 | #else | ||
| 323 | creg = PORT_PFCR; | ||
| 324 | dreg = PORT_PFDR; | ||
| 325 | #endif | ||
| 326 | sav = ctrl_inw(creg); | ||
| 327 | ctrl_outw(sav | (portcr_mask >> 16), creg); | ||
| 328 | d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & (ctrl_inw(INTC_INTER) >> 8) & 0xff; | ||
| 329 | ctrl_outw(sav, creg); | ||
| 330 | if(d == 0) return irq; | ||
| 331 | return PINT_IRQ_BASE + 8 + pint_map[d]; | ||
| 332 | } | ||
| 333 | #endif | ||
| 334 | return irq; | 202 | return irq; |
| 335 | } | 203 | } |
| 336 | #endif | 204 | #endif |
| 337 | 205 | ||
| 338 | EXPORT_SYMBOL(make_ipr_irq); | 206 | EXPORT_SYMBOL(make_ipr_irq); |
| 339 | |||
diff --git a/arch/sh/kernel/cpu/irq/pint.c b/arch/sh/kernel/cpu/irq/pint.c new file mode 100644 index 000000000000..95d6024fe1ae --- /dev/null +++ b/arch/sh/kernel/cpu/irq/pint.c | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/irq/pint.c - Interrupt handling for PINT-based IRQs. | ||
| 3 | * | ||
| 4 | * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi | ||
| 5 | * Copyright (C) 2000 Kazumoto Kojima | ||
| 6 | * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp> | ||
| 7 | * | ||
| 8 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 9 | * License. See the file "COPYING" in the main directory of this archive | ||
| 10 | * for more details. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/config.h> | ||
| 14 | #include <linux/init.h> | ||
| 15 | #include <linux/irq.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | |||
| 18 | #include <asm/system.h> | ||
| 19 | #include <asm/io.h> | ||
| 20 | #include <asm/machvec.h> | ||
| 21 | |||
| 22 | static unsigned char pint_map[256]; | ||
| 23 | static unsigned long portcr_mask; | ||
| 24 | |||
| 25 | static void enable_pint_irq(unsigned int irq); | ||
| 26 | static void disable_pint_irq(unsigned int irq); | ||
| 27 | |||
| 28 | /* shutdown is same as "disable" */ | ||
| 29 | #define shutdown_pint_irq disable_pint_irq | ||
| 30 | |||
| 31 | static void mask_and_ack_pint(unsigned int); | ||
| 32 | static void end_pint_irq(unsigned int irq); | ||
| 33 | |||
| 34 | static unsigned int startup_pint_irq(unsigned int irq) | ||
| 35 | { | ||
| 36 | enable_pint_irq(irq); | ||
| 37 | return 0; /* never anything pending */ | ||
| 38 | } | ||
| 39 | |||
| 40 | static struct hw_interrupt_type pint_irq_type = { | ||
| 41 | .typename = "PINT-IRQ", | ||
| 42 | .startup = startup_pint_irq, | ||
| 43 | .shutdown = shutdown_pint_irq, | ||
| 44 | .enable = enable_pint_irq, | ||
| 45 | .disable = disable_pint_irq, | ||
| 46 | .ack = mask_and_ack_pint, | ||
| 47 | .end = end_pint_irq | ||
| 48 | }; | ||
| 49 | |||
| 50 | static void disable_pint_irq(unsigned int irq) | ||
| 51 | { | ||
| 52 | unsigned long val, flags; | ||
| 53 | |||
| 54 | local_irq_save(flags); | ||
| 55 | val = ctrl_inw(INTC_INTER); | ||
| 56 | val &= ~(1 << (irq - PINT_IRQ_BASE)); | ||
| 57 | ctrl_outw(val, INTC_INTER); /* disable PINTn */ | ||
| 58 | portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2); | ||
| 59 | local_irq_restore(flags); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void enable_pint_irq(unsigned int irq) | ||
| 63 | { | ||
| 64 | unsigned long val, flags; | ||
| 65 | |||
| 66 | local_irq_save(flags); | ||
| 67 | val = ctrl_inw(INTC_INTER); | ||
| 68 | val |= 1 << (irq - PINT_IRQ_BASE); | ||
| 69 | ctrl_outw(val, INTC_INTER); /* enable PINTn */ | ||
| 70 | portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2; | ||
| 71 | local_irq_restore(flags); | ||
| 72 | } | ||
| 73 | |||
| 74 | static void mask_and_ack_pint(unsigned int irq) | ||
| 75 | { | ||
| 76 | disable_pint_irq(irq); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void end_pint_irq(unsigned int irq) | ||
| 80 | { | ||
| 81 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
| 82 | enable_pint_irq(irq); | ||
| 83 | } | ||
| 84 | |||
| 85 | void make_pint_irq(unsigned int irq) | ||
| 86 | { | ||
| 87 | disable_irq_nosync(irq); | ||
| 88 | irq_desc[irq].handler = &pint_irq_type; | ||
| 89 | disable_pint_irq(irq); | ||
| 90 | } | ||
| 91 | |||
| 92 | void __init init_IRQ_pint(void) | ||
| 93 | { | ||
| 94 | int i; | ||
| 95 | |||
| 96 | make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY); | ||
| 97 | make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY); | ||
| 98 | |||
| 99 | enable_irq(PINT0_IRQ); | ||
| 100 | enable_irq(PINT8_IRQ); | ||
| 101 | |||
| 102 | for(i = 0; i < 16; i++) | ||
| 103 | make_pint_irq(PINT_IRQ_BASE + i); | ||
| 104 | |||
| 105 | for(i = 0; i < 256; i++) { | ||
| 106 | if (i & 1) | ||
| 107 | pint_map[i] = 0; | ||
| 108 | else if (i & 2) | ||
| 109 | pint_map[i] = 1; | ||
| 110 | else if (i & 4) | ||
| 111 | pint_map[i] = 2; | ||
| 112 | else if (i & 8) | ||
| 113 | pint_map[i] = 3; | ||
| 114 | else if (i & 0x10) | ||
| 115 | pint_map[i] = 4; | ||
| 116 | else if (i & 0x20) | ||
| 117 | pint_map[i] = 5; | ||
| 118 | else if (i & 0x40) | ||
| 119 | pint_map[i] = 6; | ||
| 120 | else if (i & 0x80) | ||
| 121 | pint_map[i] = 7; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | int ipr_irq_demux(int irq) | ||
| 126 | { | ||
| 127 | unsigned long creg, dreg, d, sav; | ||
| 128 | |||
| 129 | if (irq == PINT0_IRQ) { | ||
| 130 | #if defined(CONFIG_CPU_SUBTYPE_SH7707) | ||
| 131 | creg = PORT_PACR; | ||
| 132 | dreg = PORT_PADR; | ||
| 133 | #else | ||
| 134 | creg = PORT_PCCR; | ||
| 135 | dreg = PORT_PCDR; | ||
| 136 | #endif | ||
| 137 | sav = ctrl_inw(creg); | ||
| 138 | ctrl_outw(sav | portcr_mask, creg); | ||
| 139 | d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & | ||
| 140 | ctrl_inw(INTC_INTER) & 0xff; | ||
| 141 | ctrl_outw(sav, creg); | ||
| 142 | |||
| 143 | if (d == 0) | ||
| 144 | return irq; | ||
| 145 | |||
| 146 | return PINT_IRQ_BASE + pint_map[d]; | ||
| 147 | } else if (irq == PINT8_IRQ) { | ||
| 148 | #if defined(CONFIG_CPU_SUBTYPE_SH7707) | ||
| 149 | creg = PORT_PBCR; | ||
| 150 | dreg = PORT_PBDR; | ||
| 151 | #else | ||
| 152 | creg = PORT_PFCR; | ||
| 153 | dreg = PORT_PFDR; | ||
| 154 | #endif | ||
| 155 | sav = ctrl_inw(creg); | ||
| 156 | ctrl_outw(sav | (portcr_mask >> 16), creg); | ||
| 157 | d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & | ||
| 158 | (ctrl_inw(INTC_INTER) >> 8) & 0xff; | ||
| 159 | ctrl_outw(sav, creg); | ||
| 160 | |||
| 161 | if (d == 0) | ||
| 162 | return irq; | ||
| 163 | |||
| 164 | return PINT_IRQ_BASE + 8 + pint_map[d]; | ||
| 165 | } | ||
| 166 | |||
| 167 | return irq; | ||
| 168 | } | ||
| 169 | |||
diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile index a64532e4dc63..b54dbb9a0c86 100644 --- a/arch/sh/kernel/cpu/sh3/Makefile +++ b/arch/sh/kernel/cpu/sh3/Makefile | |||
| @@ -4,3 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | obj-y := ex.o probe.o | 5 | obj-y := ex.o probe.o |
| 6 | 6 | ||
| 7 | clock-$(CONFIG_CPU_SH3) := clock-sh3.o | ||
| 8 | clock-$(CONFIG_CPU_SUBTYPE_SH7300) := clock-sh7300.o | ||
| 9 | clock-$(CONFIG_CPU_SUBTYPE_SH7705) := clock-sh7705.o | ||
| 10 | clock-$(CONFIG_CPU_SUBTYPE_SH7709) := clock-sh7709.o | ||
| 11 | |||
| 12 | obj-y += $(clock-y) | ||
| 13 | |||
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh3.c b/arch/sh/kernel/cpu/sh3/clock-sh3.c new file mode 100644 index 000000000000..c3c945958baf --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh3.c | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh3/clock-sh3.c | ||
| 3 | * | ||
| 4 | * Generic SH-3 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 7 | * | ||
| 8 | * FRQCR parsing hacked out of arch/sh/kernel/time.c | ||
| 9 | * | ||
| 10 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | ||
| 11 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | ||
| 12 | * Copyright (C) 2002, 2003, 2004 Paul Mundt | ||
| 13 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | ||
| 14 | * | ||
| 15 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 16 | * License. See the file "COPYING" in the main directory of this archive | ||
| 17 | * for more details. | ||
| 18 | */ | ||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <asm/clock.h> | ||
| 22 | #include <asm/freq.h> | ||
| 23 | #include <asm/io.h> | ||
| 24 | |||
| 25 | static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; | ||
| 26 | static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 }; | ||
| 27 | static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; | ||
| 28 | |||
| 29 | static void master_clk_init(struct clk *clk) | ||
| 30 | { | ||
| 31 | int frqcr = ctrl_inw(FRQCR); | ||
| 32 | int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); | ||
| 33 | |||
| 34 | clk->rate *= pfc_divisors[idx]; | ||
| 35 | } | ||
| 36 | |||
| 37 | static struct clk_ops sh3_master_clk_ops = { | ||
| 38 | .init = master_clk_init, | ||
| 39 | }; | ||
| 40 | |||
| 41 | static void module_clk_recalc(struct clk *clk) | ||
| 42 | { | ||
| 43 | int frqcr = ctrl_inw(FRQCR); | ||
| 44 | int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); | ||
| 45 | |||
| 46 | clk->rate = clk->parent->rate / pfc_divisors[idx]; | ||
| 47 | } | ||
| 48 | |||
| 49 | static struct clk_ops sh3_module_clk_ops = { | ||
| 50 | .recalc = module_clk_recalc, | ||
| 51 | }; | ||
| 52 | |||
| 53 | static void bus_clk_recalc(struct clk *clk) | ||
| 54 | { | ||
| 55 | int frqcr = ctrl_inw(FRQCR); | ||
| 56 | int idx = ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4); | ||
| 57 | |||
| 58 | clk->rate = clk->parent->rate / stc_multipliers[idx]; | ||
| 59 | } | ||
| 60 | |||
| 61 | static struct clk_ops sh3_bus_clk_ops = { | ||
| 62 | .recalc = bus_clk_recalc, | ||
| 63 | }; | ||
| 64 | |||
| 65 | static void cpu_clk_recalc(struct clk *clk) | ||
| 66 | { | ||
| 67 | int frqcr = ctrl_inw(FRQCR); | ||
| 68 | int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); | ||
| 69 | |||
| 70 | clk->rate = clk->parent->rate / ifc_divisors[idx]; | ||
| 71 | } | ||
| 72 | |||
| 73 | static struct clk_ops sh3_cpu_clk_ops = { | ||
| 74 | .recalc = cpu_clk_recalc, | ||
| 75 | }; | ||
| 76 | |||
| 77 | static struct clk_ops *sh3_clk_ops[] = { | ||
| 78 | &sh3_master_clk_ops, | ||
| 79 | &sh3_module_clk_ops, | ||
| 80 | &sh3_bus_clk_ops, | ||
| 81 | &sh3_cpu_clk_ops, | ||
| 82 | }; | ||
| 83 | |||
| 84 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
| 85 | { | ||
| 86 | if (idx < ARRAY_SIZE(sh3_clk_ops)) | ||
| 87 | *ops = sh3_clk_ops[idx]; | ||
| 88 | } | ||
| 89 | |||
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7300.c b/arch/sh/kernel/cpu/sh3/clock-sh7300.c new file mode 100644 index 000000000000..e804174b9625 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7300.c | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh3/clock-sh7300.c | ||
| 3 | * | ||
| 4 | * SH7300 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 7 | * | ||
| 8 | * FRQCR parsing hacked out of arch/sh/kernel/time.c | ||
| 9 | * | ||
| 10 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | ||
| 11 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | ||
| 12 | * Copyright (C) 2002, 2003, 2004 Paul Mundt | ||
| 13 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | ||
| 14 | * | ||
| 15 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 16 | * License. See the file "COPYING" in the main directory of this archive | ||
| 17 | * for more details. | ||
| 18 | */ | ||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <asm/clock.h> | ||
| 22 | #include <asm/freq.h> | ||
| 23 | #include <asm/io.h> | ||
| 24 | |||
| 25 | static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 }; | ||
| 26 | |||
| 27 | static void master_clk_init(struct clk *clk) | ||
| 28 | { | ||
| 29 | clk->rate *= md_table[ctrl_inw(FRQCR) & 0x0007]; | ||
| 30 | } | ||
| 31 | |||
| 32 | static struct clk_ops sh7300_master_clk_ops = { | ||
| 33 | .init = master_clk_init, | ||
| 34 | }; | ||
| 35 | |||
| 36 | static void module_clk_recalc(struct clk *clk) | ||
| 37 | { | ||
| 38 | int idx = (ctrl_inw(FRQCR) & 0x0007); | ||
| 39 | clk->rate = clk->parent->rate / md_table[idx]; | ||
| 40 | } | ||
| 41 | |||
| 42 | static struct clk_ops sh7300_module_clk_ops = { | ||
| 43 | .recalc = module_clk_recalc, | ||
| 44 | }; | ||
| 45 | |||
| 46 | static void bus_clk_recalc(struct clk *clk) | ||
| 47 | { | ||
| 48 | int idx = (ctrl_inw(FRQCR) & 0x0700) >> 8; | ||
| 49 | clk->rate = clk->parent->rate / md_table[idx]; | ||
| 50 | } | ||
| 51 | |||
| 52 | static struct clk_ops sh7300_bus_clk_ops = { | ||
| 53 | .recalc = bus_clk_recalc, | ||
| 54 | }; | ||
| 55 | |||
| 56 | static void cpu_clk_recalc(struct clk *clk) | ||
| 57 | { | ||
| 58 | int idx = (ctrl_inw(FRQCR) & 0x0070) >> 4; | ||
| 59 | clk->rate = clk->parent->rate / md_table[idx]; | ||
| 60 | } | ||
| 61 | |||
| 62 | static struct clk_ops sh7300_cpu_clk_ops = { | ||
| 63 | .recalc = cpu_clk_recalc, | ||
| 64 | }; | ||
| 65 | |||
| 66 | static struct clk_ops *sh7300_clk_ops[] = { | ||
| 67 | &sh7300_master_clk_ops, | ||
| 68 | &sh7300_module_clk_ops, | ||
| 69 | &sh7300_bus_clk_ops, | ||
| 70 | &sh7300_cpu_clk_ops, | ||
| 71 | }; | ||
| 72 | |||
| 73 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
| 74 | { | ||
| 75 | if (idx < ARRAY_SIZE(sh7300_clk_ops)) | ||
| 76 | *ops = sh7300_clk_ops[idx]; | ||
| 77 | } | ||
| 78 | |||
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7705.c b/arch/sh/kernel/cpu/sh3/clock-sh7705.c new file mode 100644 index 000000000000..dfdbf3277fd7 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7705.c | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh3/clock-sh7705.c | ||
| 3 | * | ||
| 4 | * SH7705 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 7 | * | ||
| 8 | * FRQCR parsing hacked out of arch/sh/kernel/time.c | ||
| 9 | * | ||
| 10 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | ||
| 11 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | ||
| 12 | * Copyright (C) 2002, 2003, 2004 Paul Mundt | ||
| 13 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | ||
| 14 | * | ||
| 15 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 16 | * License. See the file "COPYING" in the main directory of this archive | ||
| 17 | * for more details. | ||
| 18 | */ | ||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <asm/clock.h> | ||
| 22 | #include <asm/freq.h> | ||
| 23 | #include <asm/io.h> | ||
| 24 | |||
| 25 | /* | ||
| 26 | * SH7705 uses the same divisors as the generic SH-3 case, it's just the | ||
| 27 | * FRQCR layout that is a bit different.. | ||
| 28 | */ | ||
| 29 | static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; | ||
| 30 | static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 }; | ||
| 31 | static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; | ||
| 32 | |||
| 33 | static void master_clk_init(struct clk *clk) | ||
| 34 | { | ||
| 35 | clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0003]; | ||
| 36 | } | ||
| 37 | |||
| 38 | static struct clk_ops sh7705_master_clk_ops = { | ||
| 39 | .init = master_clk_init, | ||
| 40 | }; | ||
| 41 | |||
| 42 | static void module_clk_recalc(struct clk *clk) | ||
| 43 | { | ||
| 44 | int idx = ctrl_inw(FRQCR) & 0x0003; | ||
| 45 | clk->rate = clk->parent->rate / pfc_divisors[idx]; | ||
| 46 | } | ||
| 47 | |||
| 48 | static struct clk_ops sh7705_module_clk_ops = { | ||
| 49 | .recalc = module_clk_recalc, | ||
| 50 | }; | ||
| 51 | |||
| 52 | static void bus_clk_recalc(struct clk *clk) | ||
| 53 | { | ||
| 54 | int idx = (ctrl_inw(FRQCR) & 0x0300) >> 8; | ||
| 55 | clk->rate = clk->parent->rate / stc_multipliers[idx]; | ||
| 56 | } | ||
| 57 | |||
| 58 | static struct clk_ops sh7705_bus_clk_ops = { | ||
| 59 | .recalc = bus_clk_recalc, | ||
| 60 | }; | ||
| 61 | |||
| 62 | static void cpu_clk_recalc(struct clk *clk) | ||
| 63 | { | ||
| 64 | int idx = (ctrl_inw(FRQCR) & 0x0030) >> 4; | ||
| 65 | clk->rate = clk->parent->rate / ifc_divisors[idx]; | ||
| 66 | } | ||
| 67 | |||
| 68 | static struct clk_ops sh7705_cpu_clk_ops = { | ||
| 69 | .recalc = cpu_clk_recalc, | ||
| 70 | }; | ||
| 71 | |||
| 72 | static struct clk_ops *sh7705_clk_ops[] = { | ||
| 73 | &sh7705_master_clk_ops, | ||
| 74 | &sh7705_module_clk_ops, | ||
| 75 | &sh7705_bus_clk_ops, | ||
| 76 | &sh7705_cpu_clk_ops, | ||
| 77 | }; | ||
| 78 | |||
| 79 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
| 80 | { | ||
| 81 | if (idx < ARRAY_SIZE(sh7705_clk_ops)) | ||
| 82 | *ops = sh7705_clk_ops[idx]; | ||
| 83 | } | ||
| 84 | |||
diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7709.c b/arch/sh/kernel/cpu/sh3/clock-sh7709.c new file mode 100644 index 000000000000..10461a745e5f --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7709.c | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh3/clock-sh7709.c | ||
| 3 | * | ||
| 4 | * SH7709 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Andriy Skulysh | ||
| 7 | * | ||
| 8 | * Based on arch/sh/kernel/cpu/sh3/clock-sh7705.c | ||
| 9 | * Copyright (C) 2005 Paul Mundt | ||
| 10 | * | ||
| 11 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 12 | * License. See the file "COPYING" in the main directory of this archive | ||
| 13 | * for more details. | ||
| 14 | */ | ||
| 15 | #include <linux/init.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <asm/clock.h> | ||
| 18 | #include <asm/freq.h> | ||
| 19 | #include <asm/io.h> | ||
| 20 | |||
| 21 | static int stc_multipliers[] = { 1, 2, 4, 8, 3, 6, 1, 1 }; | ||
| 22 | static int ifc_divisors[] = { 1, 2, 4, 1, 3, 1, 1, 1 }; | ||
| 23 | static int pfc_divisors[] = { 1, 2, 4, 1, 3, 6, 1, 1 }; | ||
| 24 | |||
| 25 | static void set_bus_parent(struct clk *clk) | ||
| 26 | { | ||
| 27 | struct clk *bus_clk = clk_get("bus_clk"); | ||
| 28 | clk->parent = bus_clk; | ||
| 29 | clk_put(bus_clk); | ||
| 30 | } | ||
| 31 | |||
| 32 | static void master_clk_init(struct clk *clk) | ||
| 33 | { | ||
| 34 | int frqcr = ctrl_inw(FRQCR); | ||
| 35 | int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); | ||
| 36 | |||
| 37 | clk->rate *= pfc_divisors[idx]; | ||
| 38 | } | ||
| 39 | |||
| 40 | static struct clk_ops sh7709_master_clk_ops = { | ||
| 41 | .init = master_clk_init, | ||
| 42 | }; | ||
| 43 | |||
| 44 | static void module_clk_recalc(struct clk *clk) | ||
| 45 | { | ||
| 46 | int frqcr = ctrl_inw(FRQCR); | ||
| 47 | int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); | ||
| 48 | |||
| 49 | clk->rate = clk->parent->rate / pfc_divisors[idx]; | ||
| 50 | } | ||
| 51 | |||
| 52 | static struct clk_ops sh7709_module_clk_ops = { | ||
| 53 | #ifdef CLOCK_MODE_0_1_2_7 | ||
| 54 | .init = set_bus_parent, | ||
| 55 | #endif | ||
| 56 | .recalc = module_clk_recalc, | ||
| 57 | }; | ||
| 58 | |||
| 59 | static void bus_clk_recalc(struct clk *clk) | ||
| 60 | { | ||
| 61 | int frqcr = ctrl_inw(FRQCR); | ||
| 62 | int idx = (frqcr & 0x0080) ? | ||
| 63 | ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4) : 1; | ||
| 64 | |||
| 65 | clk->rate = clk->parent->rate * stc_multipliers[idx]; | ||
| 66 | } | ||
| 67 | |||
| 68 | static struct clk_ops sh7709_bus_clk_ops = { | ||
| 69 | .recalc = bus_clk_recalc, | ||
| 70 | }; | ||
| 71 | |||
| 72 | static void cpu_clk_recalc(struct clk *clk) | ||
| 73 | { | ||
| 74 | int frqcr = ctrl_inw(FRQCR); | ||
| 75 | int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); | ||
| 76 | |||
| 77 | clk->rate = clk->parent->rate / ifc_divisors[idx]; | ||
| 78 | } | ||
| 79 | |||
| 80 | static struct clk_ops sh7709_cpu_clk_ops = { | ||
| 81 | .init = set_bus_parent, | ||
| 82 | .recalc = cpu_clk_recalc, | ||
| 83 | }; | ||
| 84 | |||
| 85 | static struct clk_ops *sh7709_clk_ops[] = { | ||
| 86 | &sh7709_master_clk_ops, | ||
| 87 | &sh7709_module_clk_ops, | ||
| 88 | &sh7709_bus_clk_ops, | ||
| 89 | &sh7709_cpu_clk_ops, | ||
| 90 | }; | ||
| 91 | |||
| 92 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
| 93 | { | ||
| 94 | if (idx < ARRAY_SIZE(sh7709_clk_ops)) | ||
| 95 | *ops = sh7709_clk_ops[idx]; | ||
| 96 | } | ||
diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile index ead1071eac73..3d5cafc71ae3 100644 --- a/arch/sh/kernel/cpu/sh4/Makefile +++ b/arch/sh/kernel/cpu/sh4/Makefile | |||
| @@ -5,6 +5,15 @@ | |||
| 5 | obj-y := ex.o probe.o | 5 | obj-y := ex.o probe.o |
| 6 | 6 | ||
| 7 | obj-$(CONFIG_SH_FPU) += fpu.o | 7 | obj-$(CONFIG_SH_FPU) += fpu.o |
| 8 | obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += irq_intc2.o | ||
| 9 | obj-$(CONFIG_SH_STORE_QUEUES) += sq.o | 8 | obj-$(CONFIG_SH_STORE_QUEUES) += sq.o |
| 10 | 9 | ||
| 10 | # Primary on-chip clocks (common) | ||
| 11 | clock-$(CONFIG_CPU_SH4) := clock-sh4.o | ||
| 12 | clock-$(CONFIG_CPU_SUBTYPE_SH73180) := clock-sh73180.o | ||
| 13 | clock-$(CONFIG_CPU_SUBTYPE_SH7770) := clock-sh7770.o | ||
| 14 | clock-$(CONFIG_CPU_SUBTYPE_SH7780) := clock-sh7780.o | ||
| 15 | |||
| 16 | # Additional clocks by subtype | ||
| 17 | clock-$(CONFIG_CPU_SUBTYPE_SH4_202) += clock-sh4-202.o | ||
| 18 | |||
| 19 | obj-y += $(clock-y) | ||
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4-202.c b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c new file mode 100644 index 000000000000..bfdf5fe8d948 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh4/clock-sh4-202.c | ||
| 3 | * | ||
| 4 | * Additional SH4-202 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 7 | * | ||
| 8 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 9 | * License. See the file "COPYING" in the main directory of this archive | ||
| 10 | * for more details. | ||
| 11 | */ | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/err.h> | ||
| 15 | #include <asm/clock.h> | ||
| 16 | #include <asm/freq.h> | ||
| 17 | #include <asm/io.h> | ||
| 18 | |||
| 19 | #define CPG2_FRQCR3 0xfe0a0018 | ||
| 20 | |||
| 21 | static int frqcr3_divisors[] = { 1, 2, 3, 4, 6, 8, 16 }; | ||
| 22 | static int frqcr3_values[] = { 0, 1, 2, 3, 4, 5, 6 }; | ||
| 23 | |||
| 24 | static void emi_clk_recalc(struct clk *clk) | ||
| 25 | { | ||
| 26 | int idx = ctrl_inl(CPG2_FRQCR3) & 0x0007; | ||
| 27 | clk->rate = clk->parent->rate / frqcr3_divisors[idx]; | ||
| 28 | } | ||
| 29 | |||
| 30 | static inline int frqcr3_lookup(struct clk *clk, unsigned long rate) | ||
| 31 | { | ||
| 32 | int divisor = clk->parent->rate / rate; | ||
| 33 | int i; | ||
| 34 | |||
| 35 | for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) | ||
| 36 | if (frqcr3_divisors[i] == divisor) | ||
| 37 | return frqcr3_values[i]; | ||
| 38 | |||
| 39 | /* Safe fallback */ | ||
| 40 | return 5; | ||
| 41 | } | ||
| 42 | |||
| 43 | static struct clk_ops sh4202_emi_clk_ops = { | ||
| 44 | .recalc = emi_clk_recalc, | ||
| 45 | }; | ||
| 46 | |||
| 47 | static struct clk sh4202_emi_clk = { | ||
| 48 | .name = "emi_clk", | ||
| 49 | .flags = CLK_ALWAYS_ENABLED, | ||
| 50 | .ops = &sh4202_emi_clk_ops, | ||
| 51 | }; | ||
| 52 | |||
| 53 | static void femi_clk_recalc(struct clk *clk) | ||
| 54 | { | ||
| 55 | int idx = (ctrl_inl(CPG2_FRQCR3) >> 3) & 0x0007; | ||
| 56 | clk->rate = clk->parent->rate / frqcr3_divisors[idx]; | ||
| 57 | } | ||
| 58 | |||
| 59 | static struct clk_ops sh4202_femi_clk_ops = { | ||
| 60 | .recalc = femi_clk_recalc, | ||
| 61 | }; | ||
| 62 | |||
| 63 | static struct clk sh4202_femi_clk = { | ||
| 64 | .name = "femi_clk", | ||
| 65 | .flags = CLK_ALWAYS_ENABLED, | ||
| 66 | .ops = &sh4202_femi_clk_ops, | ||
| 67 | }; | ||
| 68 | |||
| 69 | static void shoc_clk_init(struct clk *clk) | ||
| 70 | { | ||
| 71 | int i; | ||
| 72 | |||
| 73 | /* | ||
| 74 | * For some reason, the shoc_clk seems to be set to some really | ||
| 75 | * insane value at boot (values outside of the allowable frequency | ||
| 76 | * range for instance). We deal with this by scaling it back down | ||
| 77 | * to something sensible just in case. | ||
| 78 | * | ||
| 79 | * Start scaling from the high end down until we find something | ||
| 80 | * that passes rate verification.. | ||
| 81 | */ | ||
| 82 | for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) { | ||
| 83 | int divisor = frqcr3_divisors[i]; | ||
| 84 | |||
| 85 | if (clk->ops->set_rate(clk, clk->parent->rate / divisor) == 0) | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | |||
| 89 | WARN_ON(i == ARRAY_SIZE(frqcr3_divisors)); /* Undefined clock */ | ||
| 90 | } | ||
| 91 | |||
| 92 | static void shoc_clk_recalc(struct clk *clk) | ||
| 93 | { | ||
| 94 | int idx = (ctrl_inl(CPG2_FRQCR3) >> 6) & 0x0007; | ||
| 95 | clk->rate = clk->parent->rate / frqcr3_divisors[idx]; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int shoc_clk_verify_rate(struct clk *clk, unsigned long rate) | ||
| 99 | { | ||
| 100 | struct clk *bclk = clk_get("bus_clk"); | ||
| 101 | unsigned long bclk_rate = clk_get_rate(bclk); | ||
| 102 | |||
| 103 | clk_put(bclk); | ||
| 104 | |||
| 105 | if (rate > bclk_rate) | ||
| 106 | return 1; | ||
| 107 | if (rate > 66000000) | ||
| 108 | return 1; | ||
| 109 | |||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | static int shoc_clk_set_rate(struct clk *clk, unsigned long rate) | ||
| 114 | { | ||
| 115 | unsigned long frqcr3; | ||
| 116 | unsigned int tmp; | ||
| 117 | |||
| 118 | /* Make sure we have something sensible to switch to */ | ||
| 119 | if (shoc_clk_verify_rate(clk, rate) != 0) | ||
| 120 | return -EINVAL; | ||
| 121 | |||
| 122 | tmp = frqcr3_lookup(clk, rate); | ||
| 123 | |||
| 124 | frqcr3 = ctrl_inl(CPG2_FRQCR3); | ||
| 125 | frqcr3 &= ~(0x0007 << 6); | ||
| 126 | frqcr3 |= tmp << 6; | ||
| 127 | ctrl_outl(frqcr3, CPG2_FRQCR3); | ||
| 128 | |||
| 129 | clk->rate = clk->parent->rate / frqcr3_divisors[tmp]; | ||
| 130 | |||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | static struct clk_ops sh4202_shoc_clk_ops = { | ||
| 135 | .init = shoc_clk_init, | ||
| 136 | .recalc = shoc_clk_recalc, | ||
| 137 | .set_rate = shoc_clk_set_rate, | ||
| 138 | }; | ||
| 139 | |||
| 140 | static struct clk sh4202_shoc_clk = { | ||
| 141 | .name = "shoc_clk", | ||
| 142 | .flags = CLK_ALWAYS_ENABLED, | ||
| 143 | .ops = &sh4202_shoc_clk_ops, | ||
| 144 | }; | ||
| 145 | |||
| 146 | static struct clk *sh4202_onchip_clocks[] = { | ||
| 147 | &sh4202_emi_clk, | ||
| 148 | &sh4202_femi_clk, | ||
| 149 | &sh4202_shoc_clk, | ||
| 150 | }; | ||
| 151 | |||
| 152 | static int __init sh4202_clk_init(void) | ||
| 153 | { | ||
| 154 | struct clk *clk = clk_get("master_clk"); | ||
| 155 | int i; | ||
| 156 | |||
| 157 | for (i = 0; i < ARRAY_SIZE(sh4202_onchip_clocks); i++) { | ||
| 158 | struct clk *clkp = sh4202_onchip_clocks[i]; | ||
| 159 | |||
| 160 | clkp->parent = clk; | ||
| 161 | clk_register(clkp); | ||
| 162 | clk_enable(clkp); | ||
| 163 | } | ||
| 164 | |||
| 165 | /* | ||
| 166 | * Now that we have the rest of the clocks registered, we need to | ||
| 167 | * force the parent clock to propagate so that these clocks will | ||
| 168 | * automatically figure out their rate. We cheat by handing the | ||
| 169 | * parent clock its current rate and forcing child propagation. | ||
| 170 | */ | ||
| 171 | clk_set_rate(clk, clk_get_rate(clk)); | ||
| 172 | |||
| 173 | clk_put(clk); | ||
| 174 | |||
| 175 | return 0; | ||
| 176 | } | ||
| 177 | |||
| 178 | arch_initcall(sh4202_clk_init); | ||
| 179 | |||
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4.c b/arch/sh/kernel/cpu/sh4/clock-sh4.c new file mode 100644 index 000000000000..dca9f87a12d6 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh4.c | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh4/clock-sh4.c | ||
| 3 | * | ||
| 4 | * Generic SH-4 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 7 | * | ||
| 8 | * FRQCR parsing hacked out of arch/sh/kernel/time.c | ||
| 9 | * | ||
| 10 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | ||
| 11 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | ||
| 12 | * Copyright (C) 2002, 2003, 2004 Paul Mundt | ||
| 13 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | ||
| 14 | * | ||
| 15 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 16 | * License. See the file "COPYING" in the main directory of this archive | ||
| 17 | * for more details. | ||
| 18 | */ | ||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <asm/clock.h> | ||
| 22 | #include <asm/freq.h> | ||
| 23 | #include <asm/io.h> | ||
| 24 | |||
| 25 | static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 1, 1 }; | ||
| 26 | #define bfc_divisors ifc_divisors /* Same */ | ||
| 27 | static int pfc_divisors[] = { 2, 3, 4, 6, 8, 2, 2, 2 }; | ||
| 28 | |||
| 29 | static void master_clk_init(struct clk *clk) | ||
| 30 | { | ||
| 31 | clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0007]; | ||
| 32 | } | ||
| 33 | |||
| 34 | static struct clk_ops sh4_master_clk_ops = { | ||
| 35 | .init = master_clk_init, | ||
| 36 | }; | ||
| 37 | |||
| 38 | static void module_clk_recalc(struct clk *clk) | ||
| 39 | { | ||
| 40 | int idx = (ctrl_inw(FRQCR) & 0x0007); | ||
| 41 | clk->rate = clk->parent->rate / pfc_divisors[idx]; | ||
| 42 | } | ||
| 43 | |||
| 44 | static struct clk_ops sh4_module_clk_ops = { | ||
| 45 | .recalc = module_clk_recalc, | ||
| 46 | }; | ||
| 47 | |||
| 48 | static void bus_clk_recalc(struct clk *clk) | ||
| 49 | { | ||
| 50 | int idx = (ctrl_inw(FRQCR) >> 3) & 0x0007; | ||
| 51 | clk->rate = clk->parent->rate / bfc_divisors[idx]; | ||
| 52 | } | ||
| 53 | |||
| 54 | static struct clk_ops sh4_bus_clk_ops = { | ||
| 55 | .recalc = bus_clk_recalc, | ||
| 56 | }; | ||
| 57 | |||
| 58 | static void cpu_clk_recalc(struct clk *clk) | ||
| 59 | { | ||
| 60 | int idx = (ctrl_inw(FRQCR) >> 6) & 0x0007; | ||
| 61 | clk->rate = clk->parent->rate / ifc_divisors[idx]; | ||
| 62 | } | ||
| 63 | |||
| 64 | static struct clk_ops sh4_cpu_clk_ops = { | ||
| 65 | .recalc = cpu_clk_recalc, | ||
| 66 | }; | ||
| 67 | |||
| 68 | static struct clk_ops *sh4_clk_ops[] = { | ||
| 69 | &sh4_master_clk_ops, | ||
| 70 | &sh4_module_clk_ops, | ||
| 71 | &sh4_bus_clk_ops, | ||
| 72 | &sh4_cpu_clk_ops, | ||
| 73 | }; | ||
| 74 | |||
| 75 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
| 76 | { | ||
| 77 | if (idx < ARRAY_SIZE(sh4_clk_ops)) | ||
| 78 | *ops = sh4_clk_ops[idx]; | ||
| 79 | } | ||
| 80 | |||
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh73180.c b/arch/sh/kernel/cpu/sh4/clock-sh73180.c new file mode 100644 index 000000000000..2fa5cb2ae68d --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh73180.c | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh4/clock-sh73180.c | ||
| 3 | * | ||
| 4 | * SH73180 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 7 | * | ||
| 8 | * FRQCR parsing hacked out of arch/sh/kernel/time.c | ||
| 9 | * | ||
| 10 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | ||
| 11 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | ||
| 12 | * Copyright (C) 2002, 2003, 2004 Paul Mundt | ||
| 13 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | ||
| 14 | * | ||
| 15 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 16 | * License. See the file "COPYING" in the main directory of this archive | ||
| 17 | * for more details. | ||
| 18 | */ | ||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <asm/clock.h> | ||
| 22 | #include <asm/freq.h> | ||
| 23 | #include <asm/io.h> | ||
| 24 | |||
| 25 | /* | ||
| 26 | * SH73180 uses a common set of divisors, so this is quite simple.. | ||
| 27 | */ | ||
| 28 | static int divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 }; | ||
| 29 | |||
| 30 | static void master_clk_init(struct clk *clk) | ||
| 31 | { | ||
| 32 | clk->rate *= divisors[ctrl_inl(FRQCR) & 0x0007]; | ||
| 33 | } | ||
| 34 | |||
| 35 | static struct clk_ops sh73180_master_clk_ops = { | ||
| 36 | .init = master_clk_init, | ||
| 37 | }; | ||
| 38 | |||
| 39 | static void module_clk_recalc(struct clk *clk) | ||
| 40 | { | ||
| 41 | int idx = (ctrl_inl(FRQCR) & 0x0007); | ||
| 42 | clk->rate = clk->parent->rate / divisors[idx]; | ||
| 43 | } | ||
| 44 | |||
| 45 | static struct clk_ops sh73180_module_clk_ops = { | ||
| 46 | .recalc = module_clk_recalc, | ||
| 47 | }; | ||
| 48 | |||
| 49 | static void bus_clk_recalc(struct clk *clk) | ||
| 50 | { | ||
| 51 | int idx = (ctrl_inl(FRQCR) >> 12) & 0x0007; | ||
| 52 | clk->rate = clk->parent->rate / divisors[idx]; | ||
| 53 | } | ||
| 54 | |||
| 55 | static struct clk_ops sh73180_bus_clk_ops = { | ||
| 56 | .recalc = bus_clk_recalc, | ||
| 57 | }; | ||
| 58 | |||
| 59 | static void cpu_clk_recalc(struct clk *clk) | ||
| 60 | { | ||
| 61 | int idx = (ctrl_inl(FRQCR) >> 20) & 0x0007; | ||
| 62 | clk->rate = clk->parent->rate / divisors[idx]; | ||
| 63 | } | ||
| 64 | |||
| 65 | static struct clk_ops sh73180_cpu_clk_ops = { | ||
| 66 | .recalc = cpu_clk_recalc, | ||
| 67 | }; | ||
| 68 | |||
| 69 | static struct clk_ops *sh73180_clk_ops[] = { | ||
| 70 | &sh73180_master_clk_ops, | ||
| 71 | &sh73180_module_clk_ops, | ||
| 72 | &sh73180_bus_clk_ops, | ||
| 73 | &sh73180_cpu_clk_ops, | ||
| 74 | }; | ||
| 75 | |||
| 76 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
| 77 | { | ||
| 78 | if (idx < ARRAY_SIZE(sh73180_clk_ops)) | ||
| 79 | *ops = sh73180_clk_ops[idx]; | ||
| 80 | } | ||
| 81 | |||
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh7770.c b/arch/sh/kernel/cpu/sh4/clock-sh7770.c new file mode 100644 index 000000000000..c8694bac6477 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh7770.c | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh4/clock-sh7770.c | ||
| 3 | * | ||
| 4 | * SH7770 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 7 | * | ||
| 8 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 9 | * License. See the file "COPYING" in the main directory of this archive | ||
| 10 | * for more details. | ||
| 11 | */ | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <asm/clock.h> | ||
| 15 | #include <asm/freq.h> | ||
| 16 | #include <asm/io.h> | ||
| 17 | |||
| 18 | static int ifc_divisors[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; | ||
| 19 | static int bfc_divisors[] = { 1, 1, 1, 1, 1, 8,12, 1 }; | ||
| 20 | static int pfc_divisors[] = { 1, 8, 1,10,12,16, 1, 1 }; | ||
| 21 | |||
| 22 | static void master_clk_init(struct clk *clk) | ||
| 23 | { | ||
| 24 | clk->rate *= pfc_divisors[(ctrl_inl(FRQCR) >> 28) & 0x000f]; | ||
| 25 | } | ||
| 26 | |||
| 27 | static struct clk_ops sh7770_master_clk_ops = { | ||
| 28 | .init = master_clk_init, | ||
| 29 | }; | ||
| 30 | |||
| 31 | static void module_clk_recalc(struct clk *clk) | ||
| 32 | { | ||
| 33 | int idx = ((ctrl_inl(FRQCR) >> 28) & 0x000f); | ||
| 34 | clk->rate = clk->parent->rate / pfc_divisors[idx]; | ||
| 35 | } | ||
| 36 | |||
| 37 | static struct clk_ops sh7770_module_clk_ops = { | ||
| 38 | .recalc = module_clk_recalc, | ||
| 39 | }; | ||
| 40 | |||
| 41 | static void bus_clk_recalc(struct clk *clk) | ||
| 42 | { | ||
| 43 | int idx = (ctrl_inl(FRQCR) & 0x000f); | ||
| 44 | clk->rate = clk->parent->rate / bfc_divisors[idx]; | ||
| 45 | } | ||
| 46 | |||
| 47 | static struct clk_ops sh7770_bus_clk_ops = { | ||
| 48 | .recalc = bus_clk_recalc, | ||
| 49 | }; | ||
| 50 | |||
| 51 | static void cpu_clk_recalc(struct clk *clk) | ||
| 52 | { | ||
| 53 | int idx = ((ctrl_inl(FRQCR) >> 24) & 0x000f); | ||
| 54 | clk->rate = clk->parent->rate / ifc_divisors[idx]; | ||
| 55 | } | ||
| 56 | |||
| 57 | static struct clk_ops sh7770_cpu_clk_ops = { | ||
| 58 | .recalc = cpu_clk_recalc, | ||
| 59 | }; | ||
| 60 | |||
| 61 | static struct clk_ops *sh7770_clk_ops[] = { | ||
| 62 | &sh7770_master_clk_ops, | ||
| 63 | &sh7770_module_clk_ops, | ||
| 64 | &sh7770_bus_clk_ops, | ||
| 65 | &sh7770_cpu_clk_ops, | ||
| 66 | }; | ||
| 67 | |||
| 68 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
| 69 | { | ||
| 70 | if (idx < ARRAY_SIZE(sh7770_clk_ops)) | ||
| 71 | *ops = sh7770_clk_ops[idx]; | ||
| 72 | } | ||
| 73 | |||
diff --git a/arch/sh/kernel/cpu/sh4/clock-sh7780.c b/arch/sh/kernel/cpu/sh4/clock-sh7780.c new file mode 100644 index 000000000000..93ad367342c9 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh7780.c | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/cpu/sh4/clock-sh7780.c | ||
| 3 | * | ||
| 4 | * SH7780 support for the clock framework | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 7 | * | ||
| 8 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 9 | * License. See the file "COPYING" in the main directory of this archive | ||
| 10 | * for more details. | ||
| 11 | */ | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <asm/clock.h> | ||
| 15 | #include <asm/freq.h> | ||
| 16 | #include <asm/io.h> | ||
| 17 | |||
| 18 | static int ifc_divisors[] = { 2, 4 }; | ||
| 19 | static int bfc_divisors[] = { 1, 1, 1, 8, 12, 16, 24, 1 }; | ||
| 20 | static int pfc_divisors[] = { 1, 24, 24, 1 }; | ||
| 21 | static int cfc_divisors[] = { 1, 1, 4, 1, 6, 1, 1, 1 }; | ||
| 22 | |||
| 23 | static void master_clk_init(struct clk *clk) | ||
| 24 | { | ||
| 25 | clk->rate *= pfc_divisors[ctrl_inl(FRQCR) & 0x0003]; | ||
| 26 | } | ||
| 27 | |||
| 28 | static struct clk_ops sh7780_master_clk_ops = { | ||
| 29 | .init = master_clk_init, | ||
| 30 | }; | ||
| 31 | |||
| 32 | static void module_clk_recalc(struct clk *clk) | ||
| 33 | { | ||
| 34 | int idx = (ctrl_inl(FRQCR) & 0x0003); | ||
| 35 | clk->rate = clk->parent->rate / pfc_divisors[idx]; | ||
| 36 | } | ||
| 37 | |||
| 38 | static struct clk_ops sh7780_module_clk_ops = { | ||
| 39 | .recalc = module_clk_recalc, | ||
| 40 | }; | ||
| 41 | |||
| 42 | static void bus_clk_recalc(struct clk *clk) | ||
| 43 | { | ||
| 44 | int idx = ((ctrl_inl(FRQCR) >> 16) & 0x0007); | ||
| 45 | clk->rate = clk->parent->rate / bfc_divisors[idx]; | ||
| 46 | } | ||
| 47 | |||
| 48 | static struct clk_ops sh7780_bus_clk_ops = { | ||
| 49 | .recalc = bus_clk_recalc, | ||
| 50 | }; | ||
| 51 | |||
| 52 | static void cpu_clk_recalc(struct clk *clk) | ||
| 53 | { | ||
| 54 | int idx = ((ctrl_inl(FRQCR) >> 24) & 0x0001); | ||
| 55 | clk->rate = clk->parent->rate / ifc_divisors[idx]; | ||
| 56 | } | ||
| 57 | |||
| 58 | static struct clk_ops sh7780_cpu_clk_ops = { | ||
| 59 | .recalc = cpu_clk_recalc, | ||
| 60 | }; | ||
| 61 | |||
| 62 | static struct clk_ops *sh7780_clk_ops[] = { | ||
| 63 | &sh7780_master_clk_ops, | ||
| 64 | &sh7780_module_clk_ops, | ||
| 65 | &sh7780_bus_clk_ops, | ||
| 66 | &sh7780_cpu_clk_ops, | ||
| 67 | }; | ||
| 68 | |||
| 69 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
| 70 | { | ||
| 71 | if (idx < ARRAY_SIZE(sh7780_clk_ops)) | ||
| 72 | *ops = sh7780_clk_ops[idx]; | ||
| 73 | } | ||
| 74 | |||
| 75 | static void shyway_clk_recalc(struct clk *clk) | ||
| 76 | { | ||
| 77 | int idx = ((ctrl_inl(FRQCR) >> 20) & 0x0007); | ||
| 78 | clk->rate = clk->parent->rate / cfc_divisors[idx]; | ||
| 79 | } | ||
| 80 | |||
| 81 | static struct clk_ops sh7780_shyway_clk_ops = { | ||
| 82 | .recalc = shyway_clk_recalc, | ||
| 83 | }; | ||
| 84 | |||
| 85 | static struct clk sh7780_shyway_clk = { | ||
| 86 | .name = "shyway_clk", | ||
| 87 | .flags = CLK_ALWAYS_ENABLED, | ||
| 88 | .ops = &sh7780_shyway_clk_ops, | ||
| 89 | }; | ||
| 90 | |||
| 91 | /* | ||
| 92 | * Additional SH7780-specific on-chip clocks that aren't already part of the | ||
| 93 | * clock framework | ||
| 94 | */ | ||
| 95 | static struct clk *sh7780_onchip_clocks[] = { | ||
| 96 | &sh7780_shyway_clk, | ||
| 97 | }; | ||
| 98 | |||
| 99 | static int __init sh7780_clk_init(void) | ||
| 100 | { | ||
| 101 | struct clk *clk = clk_get("master_clk"); | ||
| 102 | int i; | ||
| 103 | |||
| 104 | for (i = 0; i < ARRAY_SIZE(sh7780_onchip_clocks); i++) { | ||
| 105 | struct clk *clkp = sh7780_onchip_clocks[i]; | ||
| 106 | |||
| 107 | clkp->parent = clk; | ||
| 108 | clk_register(clkp); | ||
| 109 | clk_enable(clkp); | ||
| 110 | } | ||
| 111 | |||
| 112 | /* | ||
| 113 | * Now that we have the rest of the clocks registered, we need to | ||
| 114 | * force the parent clock to propagate so that these clocks will | ||
| 115 | * automatically figure out their rate. We cheat by handing the | ||
| 116 | * parent clock its current rate and forcing child propagation. | ||
| 117 | */ | ||
| 118 | clk_set_rate(clk, clk_get_rate(clk)); | ||
| 119 | |||
| 120 | clk_put(clk); | ||
| 121 | |||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | |||
| 125 | arch_initcall(sh7780_clk_init); | ||
| 126 | |||
diff --git a/arch/sh/kernel/cpu/sh4/irq_intc2.c b/arch/sh/kernel/cpu/sh4/irq_intc2.c deleted file mode 100644 index f6b16ba01932..000000000000 --- a/arch/sh/kernel/cpu/sh4/irq_intc2.c +++ /dev/null | |||
| @@ -1,222 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/sh/kernel/irq_intc2.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) | ||
| 5 | * | ||
| 6 | * May be copied or modified under the terms of the GNU General Public | ||
| 7 | * License. See linux/COPYING for more information. | ||
| 8 | * | ||
| 9 | * Interrupt handling for INTC2-based IRQ. | ||
| 10 | * | ||
| 11 | * These are the "new Hitachi style" interrupts, as present on the | ||
| 12 | * Hitachi 7751 and the STM ST40 STB1. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/irq.h> | ||
| 18 | |||
| 19 | #include <asm/system.h> | ||
| 20 | #include <asm/io.h> | ||
| 21 | #include <asm/machvec.h> | ||
| 22 | |||
| 23 | |||
| 24 | struct intc2_data { | ||
| 25 | unsigned char msk_offset; | ||
| 26 | unsigned char msk_shift; | ||
| 27 | #ifdef CONFIG_CPU_SUBTYPE_ST40 | ||
| 28 | int (*clear_irq) (int); | ||
| 29 | #endif | ||
| 30 | }; | ||
| 31 | |||
| 32 | |||
| 33 | static struct intc2_data intc2_data[NR_INTC2_IRQS]; | ||
| 34 | |||
| 35 | static void enable_intc2_irq(unsigned int irq); | ||
| 36 | static void disable_intc2_irq(unsigned int irq); | ||
| 37 | |||
| 38 | /* shutdown is same as "disable" */ | ||
| 39 | #define shutdown_intc2_irq disable_intc2_irq | ||
| 40 | |||
| 41 | static void mask_and_ack_intc2(unsigned int); | ||
| 42 | static void end_intc2_irq(unsigned int irq); | ||
| 43 | |||
| 44 | static unsigned int startup_intc2_irq(unsigned int irq) | ||
| 45 | { | ||
| 46 | enable_intc2_irq(irq); | ||
| 47 | return 0; /* never anything pending */ | ||
| 48 | } | ||
| 49 | |||
| 50 | static struct hw_interrupt_type intc2_irq_type = { | ||
| 51 | .typename = "INTC2-IRQ", | ||
| 52 | .startup = startup_intc2_irq, | ||
| 53 | .shutdown = shutdown_intc2_irq, | ||
| 54 | .enable = enable_intc2_irq, | ||
| 55 | .disable = disable_intc2_irq, | ||
| 56 | .ack = mask_and_ack_intc2, | ||
| 57 | .end = end_intc2_irq | ||
| 58 | }; | ||
| 59 | |||
| 60 | static void disable_intc2_irq(unsigned int irq) | ||
| 61 | { | ||
| 62 | int irq_offset = irq - INTC2_FIRST_IRQ; | ||
| 63 | int msk_shift, msk_offset; | ||
| 64 | |||
| 65 | // Sanity check | ||
| 66 | if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS)) | ||
| 67 | return; | ||
| 68 | |||
| 69 | msk_shift = intc2_data[irq_offset].msk_shift; | ||
| 70 | msk_offset = intc2_data[irq_offset].msk_offset; | ||
| 71 | |||
| 72 | ctrl_outl(1<<msk_shift, | ||
| 73 | INTC2_BASE+INTC2_INTMSK_OFFSET+msk_offset); | ||
| 74 | } | ||
| 75 | |||
| 76 | static void enable_intc2_irq(unsigned int irq) | ||
| 77 | { | ||
| 78 | int irq_offset = irq - INTC2_FIRST_IRQ; | ||
| 79 | int msk_shift, msk_offset; | ||
| 80 | |||
| 81 | /* Sanity check */ | ||
| 82 | if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS)) | ||
| 83 | return; | ||
| 84 | |||
| 85 | msk_shift = intc2_data[irq_offset].msk_shift; | ||
| 86 | msk_offset = intc2_data[irq_offset].msk_offset; | ||
| 87 | |||
| 88 | ctrl_outl(1<<msk_shift, | ||
| 89 | INTC2_BASE+INTC2_INTMSKCLR_OFFSET+msk_offset); | ||
| 90 | } | ||
| 91 | |||
| 92 | static void mask_and_ack_intc2(unsigned int irq) | ||
| 93 | { | ||
| 94 | disable_intc2_irq(irq); | ||
| 95 | } | ||
| 96 | |||
| 97 | static void end_intc2_irq(unsigned int irq) | ||
| 98 | { | ||
| 99 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
| 100 | enable_intc2_irq(irq); | ||
| 101 | |||
| 102 | #ifdef CONFIG_CPU_SUBTYPE_ST40 | ||
| 103 | if (intc2_data[irq - INTC2_FIRST_IRQ].clear_irq) | ||
| 104 | intc2_data[irq - INTC2_FIRST_IRQ].clear_irq (irq); | ||
| 105 | #endif | ||
| 106 | } | ||
| 107 | |||
| 108 | /* | ||
| 109 | * Setup an INTC2 style interrupt. | ||
| 110 | * NOTE: Unlike IPR interrupts, parameters are not shifted by this code, | ||
| 111 | * allowing the use of the numbers straight out of the datasheet. | ||
| 112 | * For example: | ||
| 113 | * PIO1 which is INTPRI00[19,16] and INTMSK00[13] | ||
| 114 | * would be: ^ ^ ^ ^ | ||
| 115 | * | | | | | ||
| 116 | * make_intc2_irq(84, 0, 16, 0, 13); | ||
| 117 | */ | ||
| 118 | void make_intc2_irq(unsigned int irq, | ||
| 119 | unsigned int ipr_offset, unsigned int ipr_shift, | ||
| 120 | unsigned int msk_offset, unsigned int msk_shift, | ||
| 121 | unsigned int priority) | ||
| 122 | { | ||
| 123 | int irq_offset = irq - INTC2_FIRST_IRQ; | ||
| 124 | unsigned int flags; | ||
| 125 | unsigned long ipr; | ||
| 126 | |||
| 127 | if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS)) | ||
| 128 | return; | ||
| 129 | |||
| 130 | disable_irq_nosync(irq); | ||
| 131 | |||
| 132 | /* Fill the data we need */ | ||
| 133 | intc2_data[irq_offset].msk_offset = msk_offset; | ||
| 134 | intc2_data[irq_offset].msk_shift = msk_shift; | ||
| 135 | #ifdef CONFIG_CPU_SUBTYPE_ST40 | ||
| 136 | intc2_data[irq_offset].clear_irq = NULL; | ||
| 137 | #endif | ||
| 138 | |||
| 139 | /* Set the priority level */ | ||
| 140 | local_irq_save(flags); | ||
| 141 | |||
| 142 | ipr=ctrl_inl(INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset); | ||
| 143 | ipr&=~(0xf<<ipr_shift); | ||
| 144 | ipr|=(priority)<<ipr_shift; | ||
| 145 | ctrl_outl(ipr, INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset); | ||
| 146 | |||
| 147 | local_irq_restore(flags); | ||
| 148 | |||
| 149 | irq_desc[irq].handler=&intc2_irq_type; | ||
| 150 | |||
| 151 | disable_intc2_irq(irq); | ||
| 152 | } | ||
| 153 | |||
| 154 | #ifdef CONFIG_CPU_SUBTYPE_ST40 | ||
| 155 | |||
| 156 | struct intc2_init { | ||
| 157 | unsigned short irq; | ||
| 158 | unsigned char ipr_offset, ipr_shift; | ||
| 159 | unsigned char msk_offset, msk_shift; | ||
| 160 | }; | ||
| 161 | |||
| 162 | static struct intc2_init intc2_init_data[] __initdata = { | ||
| 163 | {64, 0, 0, 0, 0}, /* PCI serr */ | ||
| 164 | {65, 0, 4, 0, 1}, /* PCI err */ | ||
| 165 | {66, 0, 4, 0, 2}, /* PCI ad */ | ||
| 166 | {67, 0, 4, 0, 3}, /* PCI pwd down */ | ||
| 167 | {72, 0, 8, 0, 5}, /* DMAC INT0 */ | ||
| 168 | {73, 0, 8, 0, 6}, /* DMAC INT1 */ | ||
| 169 | {74, 0, 8, 0, 7}, /* DMAC INT2 */ | ||
| 170 | {75, 0, 8, 0, 8}, /* DMAC INT3 */ | ||
| 171 | {76, 0, 8, 0, 9}, /* DMAC INT4 */ | ||
| 172 | {78, 0, 8, 0, 11}, /* DMAC ERR */ | ||
| 173 | {80, 0, 12, 0, 12}, /* PIO0 */ | ||
| 174 | {84, 0, 16, 0, 13}, /* PIO1 */ | ||
| 175 | {88, 0, 20, 0, 14}, /* PIO2 */ | ||
| 176 | {112, 4, 0, 4, 0}, /* Mailbox */ | ||
| 177 | #ifdef CONFIG_CPU_SUBTYPE_ST40GX1 | ||
| 178 | {116, 4, 4, 4, 4}, /* SSC0 */ | ||
| 179 | {120, 4, 8, 4, 8}, /* IR Blaster */ | ||
| 180 | {124, 4, 12, 4, 12}, /* USB host */ | ||
| 181 | {128, 4, 16, 4, 16}, /* Video processor BLITTER */ | ||
| 182 | {132, 4, 20, 4, 20}, /* UART0 */ | ||
| 183 | {134, 4, 20, 4, 22}, /* UART2 */ | ||
| 184 | {136, 4, 24, 4, 24}, /* IO_PIO0 */ | ||
| 185 | {140, 4, 28, 4, 28}, /* EMPI */ | ||
| 186 | {144, 8, 0, 8, 0}, /* MAFE */ | ||
| 187 | {148, 8, 4, 8, 4}, /* PWM */ | ||
| 188 | {152, 8, 8, 8, 8}, /* SSC1 */ | ||
| 189 | {156, 8, 12, 8, 12}, /* IO_PIO1 */ | ||
| 190 | {160, 8, 16, 8, 16}, /* USB target */ | ||
| 191 | {164, 8, 20, 8, 20}, /* UART1 */ | ||
| 192 | {168, 8, 24, 8, 24}, /* Teletext */ | ||
| 193 | {172, 8, 28, 8, 28}, /* VideoSync VTG */ | ||
| 194 | {173, 8, 28, 8, 29}, /* VideoSync DVP0 */ | ||
| 195 | {174, 8, 28, 8, 30}, /* VideoSync DVP1 */ | ||
| 196 | #endif | ||
| 197 | }; | ||
| 198 | |||
| 199 | void __init init_IRQ_intc2(void) | ||
| 200 | { | ||
| 201 | struct intc2_init *p; | ||
| 202 | |||
| 203 | printk(KERN_ALERT "init_IRQ_intc2\n"); | ||
| 204 | |||
| 205 | for (p = intc2_init_data; | ||
| 206 | p<intc2_init_data+ARRAY_SIZE(intc2_init_data); | ||
| 207 | p++) { | ||
| 208 | make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift, | ||
| 209 | p-> msk_offset, p->msk_shift, 13); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | /* Adds a termination callback to the interrupt */ | ||
| 214 | void intc2_add_clear_irq(int irq, int (*fn)(int)) | ||
| 215 | { | ||
| 216 | if (irq < INTC2_FIRST_IRQ) | ||
| 217 | return; | ||
| 218 | |||
| 219 | intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn; | ||
| 220 | } | ||
| 221 | |||
| 222 | #endif /* CONFIG_CPU_SUBTYPE_ST40 */ | ||
diff --git a/arch/sh/kernel/io.c b/arch/sh/kernel/io.c index d9932f25993b..71c9fde2fd90 100644 --- a/arch/sh/kernel/io.c +++ b/arch/sh/kernel/io.c | |||
| @@ -2,58 +2,73 @@ | |||
| 2 | * linux/arch/sh/kernel/io.c | 2 | * linux/arch/sh/kernel/io.c |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2000 Stuart Menefy | 4 | * Copyright (C) 2000 Stuart Menefy |
| 5 | * Copyright (C) 2005 Paul Mundt | ||
| 5 | * | 6 | * |
| 6 | * Provide real functions which expand to whatever the header file defined. | 7 | * Provide real functions which expand to whatever the header file defined. |
| 7 | * Also definitions of machine independent IO functions. | 8 | * Also definitions of machine independent IO functions. |
| 9 | * | ||
| 10 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 11 | * License. See the file "COPYING" in the main directory of this archive | ||
| 12 | * for more details. | ||
| 8 | */ | 13 | */ |
| 9 | |||
| 10 | #include <asm/io.h> | ||
| 11 | #include <linux/module.h> | 14 | #include <linux/module.h> |
| 15 | #include <asm/machvec.h> | ||
| 16 | #include <asm/io.h> | ||
| 12 | 17 | ||
| 13 | /* | 18 | /* |
| 14 | * Copy data from IO memory space to "real" memory space. | 19 | * Copy data from IO memory space to "real" memory space. |
| 15 | * This needs to be optimized. | 20 | * This needs to be optimized. |
| 16 | */ | 21 | */ |
| 17 | void memcpy_fromio(void * to, unsigned long from, unsigned long count) | 22 | void memcpy_fromio(void *to, volatile void __iomem *from, unsigned long count) |
| 18 | { | 23 | { |
| 19 | char *p = to; | 24 | char *p = to; |
| 20 | while (count) { | 25 | while (count) { |
| 21 | count--; | 26 | count--; |
| 22 | *p = readb(from); | 27 | *p = readb((void __iomem *)from); |
| 23 | p++; | 28 | p++; |
| 24 | from++; | 29 | from++; |
| 25 | } | 30 | } |
| 26 | } | 31 | } |
| 27 | 32 | EXPORT_SYMBOL(memcpy_fromio); | |
| 33 | |||
| 28 | /* | 34 | /* |
| 29 | * Copy data from "real" memory space to IO memory space. | 35 | * Copy data from "real" memory space to IO memory space. |
| 30 | * This needs to be optimized. | 36 | * This needs to be optimized. |
| 31 | */ | 37 | */ |
| 32 | void memcpy_toio(unsigned long to, const void * from, unsigned long count) | 38 | void memcpy_toio(volatile void __iomem *to, const void *from, unsigned long count) |
| 33 | { | 39 | { |
| 34 | const char *p = from; | 40 | const char *p = from; |
| 35 | while (count) { | 41 | while (count) { |
| 36 | count--; | 42 | count--; |
| 37 | writeb(*p, to); | 43 | writeb(*p, (void __iomem *)to); |
| 38 | p++; | 44 | p++; |
| 39 | to++; | 45 | to++; |
| 40 | } | 46 | } |
| 41 | } | 47 | } |
| 42 | 48 | EXPORT_SYMBOL(memcpy_toio); | |
| 49 | |||
| 43 | /* | 50 | /* |
| 44 | * "memset" on IO memory space. | 51 | * "memset" on IO memory space. |
| 45 | * This needs to be optimized. | 52 | * This needs to be optimized. |
| 46 | */ | 53 | */ |
| 47 | void memset_io(unsigned long dst, int c, unsigned long count) | 54 | void memset_io(volatile void __iomem *dst, int c, unsigned long count) |
| 48 | { | 55 | { |
| 49 | while (count) { | 56 | while (count) { |
| 50 | count--; | 57 | count--; |
| 51 | writeb(c, dst); | 58 | writeb(c, (void __iomem *)dst); |
| 52 | dst++; | 59 | dst++; |
| 53 | } | 60 | } |
| 54 | } | 61 | } |
| 55 | |||
| 56 | EXPORT_SYMBOL(memcpy_fromio); | ||
| 57 | EXPORT_SYMBOL(memcpy_toio); | ||
| 58 | EXPORT_SYMBOL(memset_io); | 62 | EXPORT_SYMBOL(memset_io); |
| 59 | 63 | ||
| 64 | void __iomem *ioport_map(unsigned long port, unsigned int nr) | ||
| 65 | { | ||
| 66 | return sh_mv.mv_ioport_map(port, nr); | ||
| 67 | } | ||
| 68 | EXPORT_SYMBOL(ioport_map); | ||
| 69 | |||
| 70 | void ioport_unmap(void __iomem *addr) | ||
| 71 | { | ||
| 72 | sh_mv.mv_ioport_unmap(addr); | ||
| 73 | } | ||
| 74 | EXPORT_SYMBOL(ioport_unmap); | ||
diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c index a911b0149d1f..28ec7487de8c 100644 --- a/arch/sh/kernel/io_generic.c +++ b/arch/sh/kernel/io_generic.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | * linux/arch/sh/kernel/io_generic.c | 3 | * linux/arch/sh/kernel/io_generic.c |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2000 Niibe Yutaka | 5 | * Copyright (C) 2000 Niibe Yutaka |
| 6 | * Copyright (C) 2005 Paul Mundt | ||
| 6 | * | 7 | * |
| 7 | * Generic I/O routine. These can be used where a machine specific version | 8 | * Generic I/O routine. These can be used where a machine specific version |
| 8 | * is not required. | 9 | * is not required. |
| @@ -10,21 +11,20 @@ | |||
| 10 | * This file is subject to the terms and conditions of the GNU General Public | 11 | * This file is subject to the terms and conditions of the GNU General Public |
| 11 | * License. See the file "COPYING" in the main directory of this archive | 12 | * License. See the file "COPYING" in the main directory of this archive |
| 12 | * for more details. | 13 | * for more details. |
| 13 | * | ||
| 14 | */ | 14 | */ |
| 15 | 15 | #include <linux/module.h> | |
| 16 | #include <asm/io.h> | 16 | #include <asm/io.h> |
| 17 | #include <asm/machvec.h> | 17 | #include <asm/machvec.h> |
| 18 | #include <linux/module.h> | ||
| 19 | 18 | ||
| 20 | #if defined(CONFIG_CPU_SH3) | 19 | #ifdef CONFIG_CPU_SH3 |
| 20 | /* SH3 has a PCMCIA bug that needs a dummy read from area 6 for a | ||
| 21 | * workaround. */ | ||
| 21 | /* I'm not sure SH7709 has this kind of bug */ | 22 | /* I'm not sure SH7709 has this kind of bug */ |
| 22 | #define SH3_PCMCIA_BUG_WORKAROUND 1 | 23 | #define dummy_read() ctrl_inb(0xba000000) |
| 23 | #define DUMMY_READ_AREA6 0xba000000 | 24 | #else |
| 25 | #define dummy_read() | ||
| 24 | #endif | 26 | #endif |
| 25 | 27 | ||
| 26 | #define PORT2ADDR(x) (sh_mv.mv_isa_port2addr(x)) | ||
| 27 | |||
| 28 | unsigned long generic_io_base; | 28 | unsigned long generic_io_base; |
| 29 | 29 | ||
| 30 | static inline void delay(void) | 30 | static inline void delay(void) |
| @@ -32,40 +32,40 @@ static inline void delay(void) | |||
| 32 | ctrl_inw(0xa0000000); | 32 | ctrl_inw(0xa0000000); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | unsigned char generic_inb(unsigned long port) | 35 | u8 generic_inb(unsigned long port) |
| 36 | { | 36 | { |
| 37 | return *(volatile unsigned char*)PORT2ADDR(port); | 37 | return ctrl_inb((unsigned long __force)ioport_map(port, 1)); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | unsigned short generic_inw(unsigned long port) | 40 | u16 generic_inw(unsigned long port) |
| 41 | { | 41 | { |
| 42 | return *(volatile unsigned short*)PORT2ADDR(port); | 42 | return ctrl_inw((unsigned long __force)ioport_map(port, 2)); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | unsigned int generic_inl(unsigned long port) | 45 | u32 generic_inl(unsigned long port) |
| 46 | { | 46 | { |
| 47 | return *(volatile unsigned long*)PORT2ADDR(port); | 47 | return ctrl_inl((unsigned long __force)ioport_map(port, 4)); |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | unsigned char generic_inb_p(unsigned long port) | 50 | u8 generic_inb_p(unsigned long port) |
| 51 | { | 51 | { |
| 52 | unsigned long v = *(volatile unsigned char*)PORT2ADDR(port); | 52 | unsigned long v = generic_inb(port); |
| 53 | 53 | ||
| 54 | delay(); | 54 | delay(); |
| 55 | return v; | 55 | return v; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | unsigned short generic_inw_p(unsigned long port) | 58 | u16 generic_inw_p(unsigned long port) |
| 59 | { | 59 | { |
| 60 | unsigned long v = *(volatile unsigned short*)PORT2ADDR(port); | 60 | unsigned long v = generic_inw(port); |
| 61 | 61 | ||
| 62 | delay(); | 62 | delay(); |
| 63 | return v; | 63 | return v; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | unsigned int generic_inl_p(unsigned long port) | 66 | u32 generic_inl_p(unsigned long port) |
| 67 | { | 67 | { |
| 68 | unsigned long v = *(volatile unsigned long*)PORT2ADDR(port); | 68 | unsigned long v = generic_inl(port); |
| 69 | 69 | ||
| 70 | delay(); | 70 | delay(); |
| 71 | return v; | 71 | return v; |
| @@ -77,75 +77,70 @@ unsigned int generic_inl_p(unsigned long port) | |||
| 77 | * convert the port address to real address once. | 77 | * convert the port address to real address once. |
| 78 | */ | 78 | */ |
| 79 | 79 | ||
| 80 | void generic_insb(unsigned long port, void *buffer, unsigned long count) | 80 | void generic_insb(unsigned long port, void *dst, unsigned long count) |
| 81 | { | 81 | { |
| 82 | volatile unsigned char *port_addr; | 82 | volatile u8 *port_addr; |
| 83 | unsigned char *buf=buffer; | 83 | u8 *buf = dst; |
| 84 | |||
| 85 | port_addr = (volatile unsigned char *)PORT2ADDR(port); | ||
| 86 | 84 | ||
| 87 | while(count--) | 85 | port_addr = (volatile u8 *)ioport_map(port, 1); |
| 88 | *buf++ = *port_addr; | 86 | while (count--) |
| 87 | *buf++ = *port_addr; | ||
| 89 | } | 88 | } |
| 90 | 89 | ||
| 91 | void generic_insw(unsigned long port, void *buffer, unsigned long count) | 90 | void generic_insw(unsigned long port, void *dst, unsigned long count) |
| 92 | { | 91 | { |
| 93 | volatile unsigned short *port_addr; | 92 | volatile u16 *port_addr; |
| 94 | unsigned short *buf=buffer; | 93 | u16 *buf = dst; |
| 95 | 94 | ||
| 96 | port_addr = (volatile unsigned short *)PORT2ADDR(port); | 95 | port_addr = (volatile u16 *)ioport_map(port, 2); |
| 96 | while (count--) | ||
| 97 | *buf++ = *port_addr; | ||
| 97 | 98 | ||
| 98 | while(count--) | 99 | dummy_read(); |
| 99 | *buf++ = *port_addr; | ||
| 100 | #ifdef SH3_PCMCIA_BUG_WORKAROUND | ||
| 101 | ctrl_inb (DUMMY_READ_AREA6); | ||
| 102 | #endif | ||
| 103 | } | 100 | } |
| 104 | 101 | ||
| 105 | void generic_insl(unsigned long port, void *buffer, unsigned long count) | 102 | void generic_insl(unsigned long port, void *dst, unsigned long count) |
| 106 | { | 103 | { |
| 107 | volatile unsigned long *port_addr; | 104 | volatile u32 *port_addr; |
| 108 | unsigned long *buf=buffer; | 105 | u32 *buf = dst; |
| 109 | 106 | ||
| 110 | port_addr = (volatile unsigned long *)PORT2ADDR(port); | 107 | port_addr = (volatile u32 *)ioport_map(port, 4); |
| 108 | while (count--) | ||
| 109 | *buf++ = *port_addr; | ||
| 111 | 110 | ||
| 112 | while(count--) | 111 | dummy_read(); |
| 113 | *buf++ = *port_addr; | ||
| 114 | #ifdef SH3_PCMCIA_BUG_WORKAROUND | ||
| 115 | ctrl_inb (DUMMY_READ_AREA6); | ||
| 116 | #endif | ||
| 117 | } | 112 | } |
| 118 | 113 | ||
| 119 | void generic_outb(unsigned char b, unsigned long port) | 114 | void generic_outb(u8 b, unsigned long port) |
| 120 | { | 115 | { |
| 121 | *(volatile unsigned char*)PORT2ADDR(port) = b; | 116 | ctrl_outb(b, (unsigned long __force)ioport_map(port, 1)); |
| 122 | } | 117 | } |
| 123 | 118 | ||
| 124 | void generic_outw(unsigned short b, unsigned long port) | 119 | void generic_outw(u16 b, unsigned long port) |
| 125 | { | 120 | { |
| 126 | *(volatile unsigned short*)PORT2ADDR(port) = b; | 121 | ctrl_outw(b, (unsigned long __force)ioport_map(port, 2)); |
| 127 | } | 122 | } |
| 128 | 123 | ||
| 129 | void generic_outl(unsigned int b, unsigned long port) | 124 | void generic_outl(u32 b, unsigned long port) |
| 130 | { | 125 | { |
| 131 | *(volatile unsigned long*)PORT2ADDR(port) = b; | 126 | ctrl_outl(b, (unsigned long __force)ioport_map(port, 4)); |
| 132 | } | 127 | } |
| 133 | 128 | ||
| 134 | void generic_outb_p(unsigned char b, unsigned long port) | 129 | void generic_outb_p(u8 b, unsigned long port) |
| 135 | { | 130 | { |
| 136 | *(volatile unsigned char*)PORT2ADDR(port) = b; | 131 | generic_outb(b, port); |
| 137 | delay(); | 132 | delay(); |
| 138 | } | 133 | } |
| 139 | 134 | ||
| 140 | void generic_outw_p(unsigned short b, unsigned long port) | 135 | void generic_outw_p(u16 b, unsigned long port) |
| 141 | { | 136 | { |
| 142 | *(volatile unsigned short*)PORT2ADDR(port) = b; | 137 | generic_outw(b, port); |
| 143 | delay(); | 138 | delay(); |
| 144 | } | 139 | } |
| 145 | 140 | ||
| 146 | void generic_outl_p(unsigned int b, unsigned long port) | 141 | void generic_outl_p(u32 b, unsigned long port) |
| 147 | { | 142 | { |
| 148 | *(volatile unsigned long*)PORT2ADDR(port) = b; | 143 | generic_outl(b, port); |
| 149 | delay(); | 144 | delay(); |
| 150 | } | 145 | } |
| 151 | 146 | ||
| @@ -154,90 +149,77 @@ void generic_outl_p(unsigned int b, unsigned long port) | |||
| 154 | * address. However as the port address doesn't change we only need to | 149 | * address. However as the port address doesn't change we only need to |
| 155 | * convert the port address to real address once. | 150 | * convert the port address to real address once. |
| 156 | */ | 151 | */ |
| 157 | 152 | void generic_outsb(unsigned long port, const void *src, unsigned long count) | |
| 158 | void generic_outsb(unsigned long port, const void *buffer, unsigned long count) | ||
| 159 | { | 153 | { |
| 160 | volatile unsigned char *port_addr; | 154 | volatile u8 *port_addr; |
| 161 | const unsigned char *buf=buffer; | 155 | const u8 *buf = src; |
| 162 | 156 | ||
| 163 | port_addr = (volatile unsigned char *)PORT2ADDR(port); | 157 | port_addr = (volatile u8 __force *)ioport_map(port, 1); |
| 164 | 158 | ||
| 165 | while(count--) | 159 | while (count--) |
| 166 | *port_addr = *buf++; | 160 | *port_addr = *buf++; |
| 167 | } | 161 | } |
| 168 | 162 | ||
| 169 | void generic_outsw(unsigned long port, const void *buffer, unsigned long count) | 163 | void generic_outsw(unsigned long port, const void *src, unsigned long count) |
| 170 | { | 164 | { |
| 171 | volatile unsigned short *port_addr; | 165 | volatile u16 *port_addr; |
| 172 | const unsigned short *buf=buffer; | 166 | const u16 *buf = src; |
| 173 | 167 | ||
| 174 | port_addr = (volatile unsigned short *)PORT2ADDR(port); | 168 | port_addr = (volatile u16 __force *)ioport_map(port, 2); |
| 175 | 169 | ||
| 176 | while(count--) | 170 | while (count--) |
| 177 | *port_addr = *buf++; | 171 | *port_addr = *buf++; |
| 178 | 172 | ||
| 179 | #ifdef SH3_PCMCIA_BUG_WORKAROUND | 173 | dummy_read(); |
| 180 | ctrl_inb (DUMMY_READ_AREA6); | ||
| 181 | #endif | ||
| 182 | } | 174 | } |
| 183 | 175 | ||
| 184 | void generic_outsl(unsigned long port, const void *buffer, unsigned long count) | 176 | void generic_outsl(unsigned long port, const void *src, unsigned long count) |
| 185 | { | 177 | { |
| 186 | volatile unsigned long *port_addr; | 178 | volatile u32 *port_addr; |
| 187 | const unsigned long *buf=buffer; | 179 | const u32 *buf = src; |
| 188 | 180 | ||
| 189 | port_addr = (volatile unsigned long *)PORT2ADDR(port); | 181 | port_addr = (volatile u32 __force *)ioport_map(port, 4); |
| 182 | while (count--) | ||
| 183 | *port_addr = *buf++; | ||
| 190 | 184 | ||
| 191 | while(count--) | 185 | dummy_read(); |
| 192 | *port_addr = *buf++; | ||
| 193 | |||
| 194 | #ifdef SH3_PCMCIA_BUG_WORKAROUND | ||
| 195 | ctrl_inb (DUMMY_READ_AREA6); | ||
| 196 | #endif | ||
| 197 | } | ||
| 198 | |||
| 199 | unsigned char generic_readb(unsigned long addr) | ||
| 200 | { | ||
| 201 | return *(volatile unsigned char*)addr; | ||
| 202 | } | 186 | } |
| 203 | 187 | ||
| 204 | unsigned short generic_readw(unsigned long addr) | 188 | u8 generic_readb(void __iomem *addr) |
| 205 | { | 189 | { |
| 206 | return *(volatile unsigned short*)addr; | 190 | return ctrl_inb((unsigned long __force)addr); |
| 207 | } | 191 | } |
| 208 | 192 | ||
| 209 | unsigned int generic_readl(unsigned long addr) | 193 | u16 generic_readw(void __iomem *addr) |
| 210 | { | 194 | { |
| 211 | return *(volatile unsigned long*)addr; | 195 | return ctrl_inw((unsigned long __force)addr); |
| 212 | } | 196 | } |
| 213 | 197 | ||
| 214 | void generic_writeb(unsigned char b, unsigned long addr) | 198 | u32 generic_readl(void __iomem *addr) |
| 215 | { | 199 | { |
| 216 | *(volatile unsigned char*)addr = b; | 200 | return ctrl_inl((unsigned long __force)addr); |
| 217 | } | 201 | } |
| 218 | 202 | ||
| 219 | void generic_writew(unsigned short b, unsigned long addr) | 203 | void generic_writeb(u8 b, void __iomem *addr) |
| 220 | { | 204 | { |
| 221 | *(volatile unsigned short*)addr = b; | 205 | ctrl_outb(b, (unsigned long __force)addr); |
| 222 | } | 206 | } |
| 223 | 207 | ||
| 224 | void generic_writel(unsigned int b, unsigned long addr) | 208 | void generic_writew(u16 b, void __iomem *addr) |
| 225 | { | 209 | { |
| 226 | *(volatile unsigned long*)addr = b; | 210 | ctrl_outw(b, (unsigned long __force)addr); |
| 227 | } | 211 | } |
| 228 | 212 | ||
| 229 | void * generic_ioremap(unsigned long offset, unsigned long size) | 213 | void generic_writel(u32 b, void __iomem *addr) |
| 230 | { | 214 | { |
| 231 | return (void *) P2SEGADDR(offset); | 215 | ctrl_outl(b, (unsigned long __force)addr); |
| 232 | } | 216 | } |
| 233 | EXPORT_SYMBOL(generic_ioremap); | ||
| 234 | 217 | ||
| 235 | void generic_iounmap(void *addr) | 218 | void __iomem *generic_ioport_map(unsigned long addr, unsigned int size) |
| 236 | { | 219 | { |
| 220 | return (void __iomem *)(addr + generic_io_base); | ||
| 237 | } | 221 | } |
| 238 | EXPORT_SYMBOL(generic_iounmap); | ||
| 239 | 222 | ||
| 240 | unsigned long generic_isa_port2addr(unsigned long offset) | 223 | void generic_ioport_unmap(void __iomem *addr) |
| 241 | { | 224 | { |
| 242 | return offset + generic_io_base; | ||
| 243 | } | 225 | } |
diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index 54c171225b78..6883c00728cb 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c | |||
| @@ -8,38 +8,13 @@ | |||
| 8 | * SuperH version: Copyright (C) 1999 Niibe Yutaka | 8 | * SuperH version: Copyright (C) 1999 Niibe Yutaka |
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | /* | 11 | #include <linux/irq.h> |
| 12 | * IRQs are in fact implemented a bit like signal handlers for the kernel. | ||
| 13 | * Naturally it's not a 1:1 relation, but there are similarities. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/config.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/ptrace.h> | ||
| 19 | #include <linux/errno.h> | ||
| 20 | #include <linux/kernel_stat.h> | ||
| 21 | #include <linux/signal.h> | ||
| 22 | #include <linux/sched.h> | ||
| 23 | #include <linux/ioport.h> | ||
| 24 | #include <linux/interrupt.h> | 12 | #include <linux/interrupt.h> |
| 25 | #include <linux/timex.h> | 13 | #include <linux/kernel_stat.h> |
| 26 | #include <linux/mm.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/random.h> | ||
| 29 | #include <linux/smp.h> | ||
| 30 | #include <linux/smp_lock.h> | ||
| 31 | #include <linux/init.h> | ||
| 32 | #include <linux/seq_file.h> | 14 | #include <linux/seq_file.h> |
| 33 | #include <linux/kallsyms.h> | ||
| 34 | #include <linux/bitops.h> | ||
| 35 | |||
| 36 | #include <asm/system.h> | ||
| 37 | #include <asm/io.h> | ||
| 38 | #include <asm/pgalloc.h> | ||
| 39 | #include <asm/delay.h> | ||
| 40 | #include <asm/irq.h> | 15 | #include <asm/irq.h> |
| 41 | #include <linux/irq.h> | 16 | #include <asm/processor.h> |
| 42 | 17 | #include <asm/cpu/mmu_context.h> | |
| 43 | 18 | ||
| 44 | /* | 19 | /* |
| 45 | * 'what should we do if we get a hw irq event on an illegal vector'. | 20 | * 'what should we do if we get a hw irq event on an illegal vector'. |
| @@ -66,7 +41,7 @@ int show_interrupts(struct seq_file *p, void *v) | |||
| 66 | seq_putc(p, '\n'); | 41 | seq_putc(p, '\n'); |
| 67 | } | 42 | } |
| 68 | 43 | ||
| 69 | if (i < ACTUAL_NR_IRQS) { | 44 | if (i < NR_IRQS) { |
| 70 | spin_lock_irqsave(&irq_desc[i].lock, flags); | 45 | spin_lock_irqsave(&irq_desc[i].lock, flags); |
| 71 | action = irq_desc[i].action; | 46 | action = irq_desc[i].action; |
| 72 | if (!action) | 47 | if (!action) |
| @@ -86,19 +61,32 @@ unlock: | |||
| 86 | } | 61 | } |
| 87 | #endif | 62 | #endif |
| 88 | 63 | ||
| 64 | |||
| 89 | asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, | 65 | asmlinkage int do_IRQ(unsigned long r4, unsigned long r5, |
| 90 | unsigned long r6, unsigned long r7, | 66 | unsigned long r6, unsigned long r7, |
| 91 | struct pt_regs regs) | 67 | struct pt_regs regs) |
| 92 | { | 68 | { |
| 93 | int irq; | 69 | int irq = r4; |
| 94 | 70 | ||
| 95 | irq_enter(); | 71 | irq_enter(); |
| 96 | asm volatile("stc r2_bank, %0\n\t" | 72 | |
| 97 | "shlr2 %0\n\t" | 73 | #ifdef CONFIG_CPU_HAS_INTEVT |
| 98 | "shlr2 %0\n\t" | 74 | __asm__ __volatile__ ( |
| 99 | "shlr %0\n\t" | 75 | #ifdef CONFIG_CPU_HAS_SR_RB |
| 100 | "add #-16, %0\n\t" | 76 | "stc r2_bank, %0\n\t" |
| 101 | :"=z" (irq)); | 77 | #else |
| 78 | "mov.l @%1, %0\n\t" | ||
| 79 | #endif | ||
| 80 | "shlr2 %0\n\t" | ||
| 81 | "shlr2 %0\n\t" | ||
| 82 | "shlr %0\n\t" | ||
| 83 | "add #-16, %0\n\t" | ||
| 84 | : "=z" (irq), "=r" (r4) | ||
| 85 | : "1" (INTEVT) | ||
| 86 | : "memory" | ||
| 87 | ); | ||
| 88 | #endif | ||
| 89 | |||
| 102 | irq = irq_demux(irq); | 90 | irq = irq_demux(irq); |
| 103 | __do_IRQ(irq, ®s); | 91 | __do_IRQ(irq, ®s); |
| 104 | irq_exit(); | 92 | irq_exit(); |
diff --git a/arch/sh/kernel/machine_kexec.c b/arch/sh/kernel/machine_kexec.c new file mode 100644 index 000000000000..43546525f28f --- /dev/null +++ b/arch/sh/kernel/machine_kexec.c | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | /* | ||
| 2 | * machine_kexec.c - handle transition of Linux booting another kernel | ||
| 3 | * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com> | ||
| 4 | * | ||
| 5 | * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz | ||
| 6 | * LANDISK/sh4 supported by kogiidena | ||
| 7 | * | ||
| 8 | * This source code is licensed under the GNU General Public License, | ||
| 9 | * Version 2. See the file COPYING for more details. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/mm.h> | ||
| 13 | #include <linux/kexec.h> | ||
| 14 | #include <linux/delay.h> | ||
| 15 | #include <linux/reboot.h> | ||
| 16 | #include <asm/pgtable.h> | ||
| 17 | #include <asm/pgalloc.h> | ||
| 18 | #include <asm/mmu_context.h> | ||
| 19 | #include <asm/io.h> | ||
| 20 | #include <asm/cacheflush.h> | ||
| 21 | |||
| 22 | typedef NORET_TYPE void (*relocate_new_kernel_t)( | ||
| 23 | unsigned long indirection_page, | ||
| 24 | unsigned long reboot_code_buffer, | ||
| 25 | unsigned long start_address, | ||
| 26 | unsigned long vbr_reg) ATTRIB_NORET; | ||
| 27 | |||
| 28 | const extern unsigned char relocate_new_kernel[]; | ||
| 29 | const extern unsigned int relocate_new_kernel_size; | ||
| 30 | extern void *gdb_vbr_vector; | ||
| 31 | |||
| 32 | /* | ||
| 33 | * Provide a dummy crash_notes definition while crash dump arrives to ppc. | ||
| 34 | * This prevents breakage of crash_notes attribute in kernel/ksysfs.c. | ||
| 35 | */ | ||
| 36 | void *crash_notes = NULL; | ||
| 37 | |||
| 38 | void machine_shutdown(void) | ||
| 39 | { | ||
| 40 | } | ||
| 41 | |||
| 42 | void machine_crash_shutdown(struct pt_regs *regs) | ||
| 43 | { | ||
| 44 | } | ||
| 45 | |||
| 46 | /* | ||
| 47 | * Do what every setup is needed on image and the | ||
| 48 | * reboot code buffer to allow us to avoid allocations | ||
| 49 | * later. | ||
| 50 | */ | ||
| 51 | int machine_kexec_prepare(struct kimage *image) | ||
| 52 | { | ||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | void machine_kexec_cleanup(struct kimage *image) | ||
| 57 | { | ||
| 58 | } | ||
| 59 | |||
| 60 | static void kexec_info(struct kimage *image) | ||
| 61 | { | ||
| 62 | int i; | ||
| 63 | printk("kexec information\n"); | ||
| 64 | for (i = 0; i < image->nr_segments; i++) { | ||
| 65 | printk(" segment[%d]: 0x%08x - 0x%08x (0x%08x)\n", | ||
| 66 | i, | ||
| 67 | (unsigned int)image->segment[i].mem, | ||
| 68 | (unsigned int)image->segment[i].mem + image->segment[i].memsz, | ||
| 69 | (unsigned int)image->segment[i].memsz); | ||
| 70 | } | ||
| 71 | printk(" start : 0x%08x\n\n", (unsigned int)image->start); | ||
| 72 | } | ||
| 73 | |||
| 74 | |||
| 75 | /* | ||
| 76 | * Do not allocate memory (or fail in any way) in machine_kexec(). | ||
| 77 | * We are past the point of no return, committed to rebooting now. | ||
| 78 | */ | ||
| 79 | NORET_TYPE void machine_kexec(struct kimage *image) | ||
| 80 | { | ||
| 81 | |||
| 82 | unsigned long page_list; | ||
| 83 | unsigned long reboot_code_buffer; | ||
| 84 | unsigned long vbr_reg; | ||
| 85 | relocate_new_kernel_t rnk; | ||
| 86 | |||
| 87 | #if defined(CONFIG_SH_STANDARD_BIOS) | ||
| 88 | vbr_reg = ((unsigned long )gdb_vbr_vector) - 0x100; | ||
| 89 | #else | ||
| 90 | vbr_reg = 0x80000000; // dummy | ||
| 91 | #endif | ||
| 92 | /* Interrupts aren't acceptable while we reboot */ | ||
| 93 | local_irq_disable(); | ||
| 94 | |||
| 95 | page_list = image->head; | ||
| 96 | |||
| 97 | /* we need both effective and real address here */ | ||
| 98 | reboot_code_buffer = | ||
| 99 | (unsigned long)page_address(image->control_code_page); | ||
| 100 | |||
| 101 | /* copy our kernel relocation code to the control code page */ | ||
| 102 | memcpy((void *)reboot_code_buffer, relocate_new_kernel, | ||
| 103 | relocate_new_kernel_size); | ||
| 104 | |||
| 105 | kexec_info(image); | ||
| 106 | flush_cache_all(); | ||
| 107 | |||
| 108 | /* now call it */ | ||
| 109 | rnk = (relocate_new_kernel_t) reboot_code_buffer; | ||
| 110 | (*rnk)(page_list, reboot_code_buffer, image->start, vbr_reg); | ||
| 111 | } | ||
| 112 | |||
diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index aac15e42d03b..a4dc2b532e10 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c | |||
| @@ -71,6 +71,16 @@ void cpu_idle(void) | |||
| 71 | 71 | ||
| 72 | void machine_restart(char * __unused) | 72 | void machine_restart(char * __unused) |
| 73 | { | 73 | { |
| 74 | |||
| 75 | #ifdef CONFIG_KEXEC | ||
| 76 | struct kimage *image; | ||
| 77 | image = xchg(&kexec_image, 0); | ||
| 78 | if (image) { | ||
| 79 | machine_shutdown(); | ||
| 80 | machine_kexec(image); | ||
| 81 | } | ||
| 82 | #endif | ||
| 83 | |||
| 74 | /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */ | 84 | /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */ |
| 75 | asm volatile("ldc %0, sr\n\t" | 85 | asm volatile("ldc %0, sr\n\t" |
| 76 | "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001)); | 86 | "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001)); |
diff --git a/arch/sh/kernel/relocate_kernel.S b/arch/sh/kernel/relocate_kernel.S new file mode 100644 index 000000000000..b0695cffec6e --- /dev/null +++ b/arch/sh/kernel/relocate_kernel.S | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | /* | ||
| 2 | * relocate_kernel.S - put the kernel image in place to boot | ||
| 3 | * 2005.9.17 kogiidena@eggplant.ddo.jp | ||
| 4 | * | ||
| 5 | * LANDISK/sh4 is supported. Maybe, SH archtecture works well. | ||
| 6 | * | ||
| 7 | * This source code is licensed under the GNU General Public License, | ||
| 8 | * Version 2. See the file COPYING for more details. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/config.h> | ||
| 12 | #include <linux/linkage.h> | ||
| 13 | |||
| 14 | #define PAGE_SIZE 4096 /* must be same value as in <asm/page.h> */ | ||
| 15 | |||
| 16 | |||
| 17 | .globl relocate_new_kernel | ||
| 18 | relocate_new_kernel: | ||
| 19 | /* r4 = indirection_page */ | ||
| 20 | /* r5 = reboot_code_buffer */ | ||
| 21 | /* r6 = start_address */ | ||
| 22 | /* r7 = vbr_reg */ | ||
| 23 | |||
| 24 | mov.l 10f,r8 /* 4096 */ | ||
| 25 | mov.l 11f,r9 /* 0xa0000000 */ | ||
| 26 | |||
| 27 | /* stack setting */ | ||
| 28 | add r8,r5 | ||
| 29 | mov r5,r15 | ||
| 30 | |||
| 31 | bra 1f | ||
| 32 | mov r4,r0 /* cmd = indirection_page */ | ||
| 33 | 0: | ||
| 34 | mov.l @r4+,r0 /* cmd = *ind++ */ | ||
| 35 | |||
| 36 | 1: /* addr = (cmd | 0xa0000000) & 0xfffffff0 */ | ||
| 37 | mov r0,r2 | ||
| 38 | or r9,r2 | ||
| 39 | mov #-16,r1 | ||
| 40 | and r1,r2 | ||
| 41 | |||
| 42 | /* if(cmd & IND_DESTINATION) dst = addr */ | ||
| 43 | tst #1,r0 | ||
| 44 | bt 2f | ||
| 45 | bra 0b | ||
| 46 | mov r2,r5 | ||
| 47 | |||
| 48 | 2: /* else if(cmd & IND_INDIRECTION) ind = addr */ | ||
| 49 | tst #2,r0 | ||
| 50 | bt 3f | ||
| 51 | bra 0b | ||
| 52 | mov r2,r4 | ||
| 53 | |||
| 54 | 3: /* else if(cmd & IND_DONE) goto 6 */ | ||
| 55 | tst #4,r0 | ||
| 56 | bt 4f | ||
| 57 | bra 6f | ||
| 58 | nop | ||
| 59 | |||
| 60 | 4: /* else if(cmd & IND_SOURCE) memcpy(dst,addr,PAGE_SIZE) */ | ||
| 61 | tst #8,r0 | ||
| 62 | bt 0b | ||
| 63 | |||
| 64 | mov r8,r3 | ||
| 65 | shlr2 r3 | ||
| 66 | shlr2 r3 | ||
| 67 | 5: | ||
| 68 | dt r3 | ||
| 69 | mov.l @r2+,r1 /* 16n+0 */ | ||
| 70 | mov.l r1,@r5 | ||
| 71 | add #4,r5 | ||
| 72 | mov.l @r2+,r1 /* 16n+4 */ | ||
| 73 | mov.l r1,@r5 | ||
| 74 | add #4,r5 | ||
| 75 | mov.l @r2+,r1 /* 16n+8 */ | ||
| 76 | mov.l r1,@r5 | ||
| 77 | add #4,r5 | ||
| 78 | mov.l @r2+,r1 /* 16n+12 */ | ||
| 79 | mov.l r1,@r5 | ||
| 80 | add #4,r5 | ||
| 81 | bf 5b | ||
| 82 | |||
| 83 | bra 0b | ||
| 84 | nop | ||
| 85 | 6: | ||
| 86 | #ifdef CONFIG_SH_STANDARD_BIOS | ||
| 87 | ldc r7, vbr | ||
| 88 | #endif | ||
| 89 | jmp @r6 | ||
| 90 | nop | ||
| 91 | |||
| 92 | .align 2 | ||
| 93 | 10: | ||
| 94 | .long PAGE_SIZE | ||
| 95 | 11: | ||
| 96 | .long 0xa0000000 | ||
| 97 | |||
| 98 | relocate_new_kernel_end: | ||
| 99 | |||
| 100 | .globl relocate_new_kernel_size | ||
| 101 | relocate_new_kernel_size: | ||
| 102 | .long relocate_new_kernel_end - relocate_new_kernel | ||
diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index 671b876416bf..314a275c04e0 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | * | 3 | * |
| 4 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | 4 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka |
| 5 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | 5 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> |
| 6 | * Copyright (C) 2002, 2003, 2004 Paul Mundt | 6 | * Copyright (C) 2002, 2003, 2004, 2005 Paul Mundt |
| 7 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | 7 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> |
| 8 | * | 8 | * |
| 9 | * Some code taken from i386 version. | 9 | * Some code taken from i386 version. |
| @@ -11,50 +11,21 @@ | |||
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include <linux/config.h> | 13 | #include <linux/config.h> |
| 14 | #include <linux/errno.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/sched.h> | ||
| 17 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
| 18 | #include <linux/param.h> | 15 | #include <linux/module.h> |
| 19 | #include <linux/string.h> | ||
| 20 | #include <linux/mm.h> | ||
| 21 | #include <linux/interrupt.h> | ||
| 22 | #include <linux/time.h> | ||
| 23 | #include <linux/delay.h> | ||
| 24 | #include <linux/init.h> | 16 | #include <linux/init.h> |
| 25 | #include <linux/smp.h> | ||
| 26 | #include <linux/profile.h> | 17 | #include <linux/profile.h> |
| 27 | 18 | #include <asm/clock.h> | |
| 28 | #include <asm/processor.h> | ||
| 29 | #include <asm/uaccess.h> | ||
| 30 | #include <asm/io.h> | ||
| 31 | #include <asm/irq.h> | ||
| 32 | #include <asm/delay.h> | ||
| 33 | #include <asm/machvec.h> | ||
| 34 | #include <asm/rtc.h> | 19 | #include <asm/rtc.h> |
| 35 | #include <asm/freq.h> | 20 | #include <asm/timer.h> |
| 36 | #include <asm/cpu/timer.h> | ||
| 37 | #ifdef CONFIG_SH_KGDB | ||
| 38 | #include <asm/kgdb.h> | 21 | #include <asm/kgdb.h> |
| 39 | #endif | ||
| 40 | |||
| 41 | #include <linux/timex.h> | ||
| 42 | #include <linux/irq.h> | ||
| 43 | |||
| 44 | #define TMU_TOCR_INIT 0x00 | ||
| 45 | #define TMU0_TCR_INIT 0x0020 | ||
| 46 | #define TMU_TSTR_INIT 1 | ||
| 47 | |||
| 48 | #define TMU0_TCR_CALIB 0x0000 | ||
| 49 | |||
| 50 | #ifdef CONFIG_CPU_SUBTYPE_ST40STB1 | ||
| 51 | #define CLOCKGEN_MEMCLKCR 0xbb040038 | ||
| 52 | #define MEMCLKCR_RATIO_MASK 0x7 | ||
| 53 | #endif /* CONFIG_CPU_SUBTYPE_ST40STB1 */ | ||
| 54 | 22 | ||
| 55 | extern unsigned long wall_jiffies; | 23 | extern unsigned long wall_jiffies; |
| 56 | #define TICK_SIZE (tick_nsec / 1000) | 24 | struct sys_timer *sys_timer; |
| 57 | DEFINE_SPINLOCK(tmu0_lock); | 25 | |
| 26 | /* Move this somewhere more sensible.. */ | ||
| 27 | DEFINE_SPINLOCK(rtc_lock); | ||
| 28 | EXPORT_SYMBOL(rtc_lock); | ||
| 58 | 29 | ||
| 59 | /* XXX: Can we initialize this in a routine somewhere? Dreamcast doesn't want | 30 | /* XXX: Can we initialize this in a routine somewhere? Dreamcast doesn't want |
| 60 | * these routines anywhere... */ | 31 | * these routines anywhere... */ |
| @@ -66,98 +37,14 @@ void (*rtc_get_time)(struct timespec *); | |||
| 66 | int (*rtc_set_time)(const time_t); | 37 | int (*rtc_set_time)(const time_t); |
| 67 | #endif | 38 | #endif |
| 68 | 39 | ||
| 69 | #if defined(CONFIG_CPU_SUBTYPE_SH7300) | ||
| 70 | static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 }; | ||
| 71 | #endif | ||
| 72 | #if defined(CONFIG_CPU_SH3) | ||
| 73 | static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; | ||
| 74 | static int stc_values[] = { 0, 1, 4, 2, 5, 0, 0, 0 }; | ||
| 75 | #define bfc_divisors stc_multipliers | ||
| 76 | #define bfc_values stc_values | ||
| 77 | static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 }; | ||
| 78 | static int ifc_values[] = { 0, 1, 4, 2, 0, 0, 0, 0 }; | ||
| 79 | static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; | ||
| 80 | static int pfc_values[] = { 0, 1, 4, 2, 5, 0, 0, 0 }; | ||
| 81 | #elif defined(CONFIG_CPU_SH4) | ||
| 82 | #if defined(CONFIG_CPU_SUBTYPE_SH73180) | ||
| 83 | static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 }; | ||
| 84 | static int ifc_values[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 85 | #define bfc_divisors ifc_divisors /* Same */ | ||
| 86 | #define bfc_values ifc_values | ||
| 87 | #define pfc_divisors ifc_divisors /* Same */ | ||
| 88 | #define pfc_values ifc_values | ||
| 89 | #else | ||
| 90 | static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 1, 1 }; | ||
| 91 | static int ifc_values[] = { 0, 1, 2, 3, 0, 4, 0, 5 }; | ||
| 92 | #define bfc_divisors ifc_divisors /* Same */ | ||
| 93 | #define bfc_values ifc_values | ||
| 94 | static int pfc_divisors[] = { 2, 3, 4, 6, 8, 2, 2, 2 }; | ||
| 95 | static int pfc_values[] = { 0, 0, 1, 2, 0, 3, 0, 4 }; | ||
| 96 | #endif | ||
| 97 | #else | ||
| 98 | #error "Unknown ifc/bfc/pfc/stc values for this processor" | ||
| 99 | #endif | ||
| 100 | |||
| 101 | /* | 40 | /* |
| 102 | * Scheduler clock - returns current time in nanosec units. | 41 | * Scheduler clock - returns current time in nanosec units. |
| 103 | */ | 42 | */ |
| 104 | unsigned long long sched_clock(void) | 43 | unsigned long long __attribute__ ((weak)) sched_clock(void) |
| 105 | { | 44 | { |
| 106 | return (unsigned long long)jiffies * (1000000000 / HZ); | 45 | return (unsigned long long)jiffies * (1000000000 / HZ); |
| 107 | } | 46 | } |
| 108 | 47 | ||
| 109 | static unsigned long do_gettimeoffset(void) | ||
| 110 | { | ||
| 111 | int count; | ||
| 112 | unsigned long flags; | ||
| 113 | |||
| 114 | static int count_p = 0x7fffffff; /* for the first call after boot */ | ||
| 115 | static unsigned long jiffies_p = 0; | ||
| 116 | |||
| 117 | /* | ||
| 118 | * cache volatile jiffies temporarily; we have IRQs turned off. | ||
| 119 | */ | ||
| 120 | unsigned long jiffies_t; | ||
| 121 | |||
| 122 | spin_lock_irqsave(&tmu0_lock, flags); | ||
| 123 | /* timer count may underflow right here */ | ||
| 124 | count = ctrl_inl(TMU0_TCNT); /* read the latched count */ | ||
| 125 | |||
| 126 | jiffies_t = jiffies; | ||
| 127 | |||
| 128 | /* | ||
| 129 | * avoiding timer inconsistencies (they are rare, but they happen)... | ||
| 130 | * there is one kind of problem that must be avoided here: | ||
| 131 | * 1. the timer counter underflows | ||
| 132 | */ | ||
| 133 | |||
| 134 | if( jiffies_t == jiffies_p ) { | ||
| 135 | if( count > count_p ) { | ||
| 136 | /* the nutcase */ | ||
| 137 | |||
| 138 | if(ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */ | ||
| 139 | /* | ||
| 140 | * We cannot detect lost timer interrupts ... | ||
| 141 | * well, that's why we call them lost, don't we? :) | ||
| 142 | * [hmm, on the Pentium and Alpha we can ... sort of] | ||
| 143 | */ | ||
| 144 | count -= LATCH; | ||
| 145 | } else { | ||
| 146 | printk("do_slow_gettimeoffset(): hardware timer problem?\n"); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } else | ||
| 150 | jiffies_p = jiffies_t; | ||
| 151 | |||
| 152 | count_p = count; | ||
| 153 | spin_unlock_irqrestore(&tmu0_lock, flags); | ||
| 154 | |||
| 155 | count = ((LATCH-1) - count) * TICK_SIZE; | ||
| 156 | count = (count + LATCH/2) / LATCH; | ||
| 157 | |||
| 158 | return count; | ||
| 159 | } | ||
| 160 | |||
| 161 | void do_gettimeofday(struct timeval *tv) | 48 | void do_gettimeofday(struct timeval *tv) |
| 162 | { | 49 | { |
| 163 | unsigned long seq; | 50 | unsigned long seq; |
| @@ -166,7 +53,7 @@ void do_gettimeofday(struct timeval *tv) | |||
| 166 | 53 | ||
| 167 | do { | 54 | do { |
| 168 | seq = read_seqbegin(&xtime_lock); | 55 | seq = read_seqbegin(&xtime_lock); |
| 169 | usec = do_gettimeoffset(); | 56 | usec = get_timer_offset(); |
| 170 | 57 | ||
| 171 | lost = jiffies - wall_jiffies; | 58 | lost = jiffies - wall_jiffies; |
| 172 | if (lost) | 59 | if (lost) |
| @@ -202,7 +89,7 @@ int do_settimeofday(struct timespec *tv) | |||
| 202 | * wall time. Discover what correction gettimeofday() would have | 89 | * wall time. Discover what correction gettimeofday() would have |
| 203 | * made, and then undo it! | 90 | * made, and then undo it! |
| 204 | */ | 91 | */ |
| 205 | nsec -= 1000 * (do_gettimeoffset() + | 92 | nsec -= 1000 * (get_timer_offset() + |
| 206 | (jiffies - wall_jiffies) * (1000000 / HZ)); | 93 | (jiffies - wall_jiffies) * (1000000 / HZ)); |
| 207 | 94 | ||
| 208 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | 95 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); |
| @@ -224,10 +111,10 @@ EXPORT_SYMBOL(do_settimeofday); | |||
| 224 | static long last_rtc_update; | 111 | static long last_rtc_update; |
| 225 | 112 | ||
| 226 | /* | 113 | /* |
| 227 | * timer_interrupt() needs to keep up the real-time clock, | 114 | * handle_timer_tick() needs to keep up the real-time clock, |
| 228 | * as well as call the "do_timer()" routine every clocktick | 115 | * as well as call the "do_timer()" routine every clocktick |
| 229 | */ | 116 | */ |
| 230 | static inline void do_timer_interrupt(int irq, struct pt_regs *regs) | 117 | void handle_timer_tick(struct pt_regs *regs) |
| 231 | { | 118 | { |
| 232 | do_timer(regs); | 119 | do_timer(regs); |
| 233 | #ifndef CONFIG_SMP | 120 | #ifndef CONFIG_SMP |
| @@ -252,337 +139,35 @@ static inline void do_timer_interrupt(int irq, struct pt_regs *regs) | |||
| 252 | if (rtc_set_time(xtime.tv_sec) == 0) | 139 | if (rtc_set_time(xtime.tv_sec) == 0) |
| 253 | last_rtc_update = xtime.tv_sec; | 140 | last_rtc_update = xtime.tv_sec; |
| 254 | else | 141 | else |
| 255 | last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ | 142 | /* do it again in 60s */ |
| 143 | last_rtc_update = xtime.tv_sec - 600; | ||
| 256 | } | 144 | } |
| 257 | } | 145 | } |
| 258 | 146 | ||
| 259 | /* | 147 | static struct sysdev_class timer_sysclass = { |
| 260 | * This is the same as the above, except we _also_ save the current | 148 | set_kset_name("timer"), |
| 261 | * Time Stamp Counter value at the time of the timer interrupt, so that | ||
| 262 | * we later on can estimate the time of day more exactly. | ||
| 263 | */ | ||
| 264 | static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
| 265 | { | ||
| 266 | unsigned long timer_status; | ||
| 267 | |||
| 268 | /* Clear UNF bit */ | ||
| 269 | timer_status = ctrl_inw(TMU0_TCR); | ||
| 270 | timer_status &= ~0x100; | ||
| 271 | ctrl_outw(timer_status, TMU0_TCR); | ||
| 272 | |||
| 273 | /* | ||
| 274 | * Here we are in the timer irq handler. We just have irqs locally | ||
| 275 | * disabled but we don't know if the timer_bh is running on the other | ||
| 276 | * CPU. We need to avoid to SMP race with it. NOTE: we don' t need | ||
| 277 | * the irq version of write_lock because as just said we have irq | ||
| 278 | * locally disabled. -arca | ||
| 279 | */ | ||
| 280 | write_seqlock(&xtime_lock); | ||
| 281 | do_timer_interrupt(irq, regs); | ||
| 282 | write_sequnlock(&xtime_lock); | ||
| 283 | |||
| 284 | return IRQ_HANDLED; | ||
| 285 | } | ||
| 286 | |||
| 287 | /* | ||
| 288 | * Hah! We'll see if this works (switching from usecs to nsecs). | ||
| 289 | */ | ||
| 290 | static unsigned int __init get_timer_frequency(void) | ||
| 291 | { | ||
| 292 | u32 freq; | ||
| 293 | struct timespec ts1, ts2; | ||
| 294 | unsigned long diff_nsec; | ||
| 295 | unsigned long factor; | ||
| 296 | |||
| 297 | /* Setup the timer: We don't want to generate interrupts, just | ||
| 298 | * have it count down at its natural rate. | ||
| 299 | */ | ||
| 300 | ctrl_outb(0, TMU_TSTR); | ||
| 301 | #if !defined(CONFIG_CPU_SUBTYPE_SH7300) | ||
| 302 | ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); | ||
| 303 | #endif | ||
| 304 | ctrl_outw(TMU0_TCR_CALIB, TMU0_TCR); | ||
| 305 | ctrl_outl(0xffffffff, TMU0_TCOR); | ||
| 306 | ctrl_outl(0xffffffff, TMU0_TCNT); | ||
| 307 | |||
| 308 | rtc_get_time(&ts2); | ||
| 309 | |||
| 310 | do { | ||
| 311 | rtc_get_time(&ts1); | ||
| 312 | } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec); | ||
| 313 | |||
| 314 | /* actually start the timer */ | ||
| 315 | ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); | ||
| 316 | |||
| 317 | do { | ||
| 318 | rtc_get_time(&ts2); | ||
| 319 | } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec); | ||
| 320 | |||
| 321 | freq = 0xffffffff - ctrl_inl(TMU0_TCNT); | ||
| 322 | if (ts2.tv_nsec < ts1.tv_nsec) { | ||
| 323 | ts2.tv_nsec += 1000000000; | ||
| 324 | ts2.tv_sec--; | ||
| 325 | } | ||
| 326 | |||
| 327 | diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec); | ||
| 328 | |||
| 329 | /* this should work well if the RTC has a precision of n Hz, where | ||
| 330 | * n is an integer. I don't think we have to worry about the other | ||
| 331 | * cases. */ | ||
| 332 | factor = (1000000000 + diff_nsec/2) / diff_nsec; | ||
| 333 | |||
| 334 | if (factor * diff_nsec > 1100000000 || | ||
| 335 | factor * diff_nsec < 900000000) | ||
| 336 | panic("weird RTC (diff_nsec %ld)", diff_nsec); | ||
| 337 | |||
| 338 | return freq * factor; | ||
| 339 | } | ||
| 340 | |||
| 341 | void (*board_time_init)(void); | ||
| 342 | void (*board_timer_setup)(struct irqaction *irq); | ||
| 343 | |||
| 344 | static unsigned int sh_pclk_freq __initdata = CONFIG_SH_PCLK_FREQ; | ||
| 345 | |||
| 346 | static int __init sh_pclk_setup(char *str) | ||
| 347 | { | ||
| 348 | unsigned int freq; | ||
| 349 | |||
| 350 | if (get_option(&str, &freq)) | ||
| 351 | sh_pclk_freq = freq; | ||
| 352 | |||
| 353 | return 1; | ||
| 354 | } | ||
| 355 | __setup("sh_pclk=", sh_pclk_setup); | ||
| 356 | |||
| 357 | static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL}; | ||
| 358 | |||
| 359 | void get_current_frequency_divisors(unsigned int *ifc, unsigned int *bfc, unsigned int *pfc) | ||
| 360 | { | ||
| 361 | unsigned int frqcr = ctrl_inw(FRQCR); | ||
| 362 | |||
| 363 | #if defined(CONFIG_CPU_SH3) | ||
| 364 | #if defined(CONFIG_CPU_SUBTYPE_SH7300) | ||
| 365 | *ifc = md_table[((frqcr & 0x0070) >> 4)]; | ||
| 366 | *bfc = md_table[((frqcr & 0x0700) >> 8)]; | ||
| 367 | *pfc = md_table[frqcr & 0x0007]; | ||
| 368 | #elif defined(CONFIG_CPU_SUBTYPE_SH7705) | ||
| 369 | *bfc = stc_multipliers[(frqcr & 0x0300) >> 8]; | ||
| 370 | *ifc = ifc_divisors[(frqcr & 0x0030) >> 4]; | ||
| 371 | *pfc = pfc_divisors[frqcr & 0x0003]; | ||
| 372 | #else | ||
| 373 | unsigned int tmp; | ||
| 374 | |||
| 375 | tmp = (frqcr & 0x8000) >> 13; | ||
| 376 | tmp |= (frqcr & 0x0030) >> 4; | ||
| 377 | *bfc = stc_multipliers[tmp]; | ||
| 378 | tmp = (frqcr & 0x4000) >> 12; | ||
| 379 | tmp |= (frqcr & 0x000c) >> 2; | ||
| 380 | *ifc = ifc_divisors[tmp]; | ||
| 381 | tmp = (frqcr & 0x2000) >> 11; | ||
| 382 | tmp |= frqcr & 0x0003; | ||
| 383 | *pfc = pfc_divisors[tmp]; | ||
| 384 | #endif | ||
| 385 | #elif defined(CONFIG_CPU_SH4) | ||
| 386 | #if defined(CONFIG_CPU_SUBTYPE_SH73180) | ||
| 387 | *ifc = ifc_divisors[(frqcr>> 20) & 0x0007]; | ||
| 388 | *bfc = bfc_divisors[(frqcr>> 12) & 0x0007]; | ||
| 389 | *pfc = pfc_divisors[frqcr & 0x0007]; | ||
| 390 | #else | ||
| 391 | *ifc = ifc_divisors[(frqcr >> 6) & 0x0007]; | ||
| 392 | *bfc = bfc_divisors[(frqcr >> 3) & 0x0007]; | ||
| 393 | *pfc = pfc_divisors[frqcr & 0x0007]; | ||
| 394 | #endif | ||
| 395 | #endif | ||
| 396 | } | ||
| 397 | |||
| 398 | /* | ||
| 399 | * This bit of ugliness builds up accessor routines to get at both | ||
| 400 | * the divisors and the physical values. | ||
| 401 | */ | ||
| 402 | #define _FREQ_TABLE(x) \ | ||
| 403 | unsigned int get_##x##_divisor(unsigned int value) \ | ||
| 404 | { return x##_divisors[value]; } \ | ||
| 405 | \ | ||
| 406 | unsigned int get_##x##_value(unsigned int divisor) \ | ||
| 407 | { return x##_values[(divisor - 1)]; } | ||
| 408 | |||
| 409 | _FREQ_TABLE(ifc); | ||
| 410 | _FREQ_TABLE(bfc); | ||
| 411 | _FREQ_TABLE(pfc); | ||
| 412 | |||
| 413 | #ifdef CONFIG_CPU_SUBTYPE_ST40STB1 | ||
| 414 | |||
| 415 | /* | ||
| 416 | * The ST40 divisors are totally different so we set the cpu data | ||
| 417 | * clocks using a different algorithm | ||
| 418 | * | ||
| 419 | * I've just plugged this from the 2.4 code | ||
| 420 | * - Alex Bennee <kernel-hacker@bennee.com> | ||
| 421 | */ | ||
| 422 | #define CCN_PVR_CHIP_SHIFT 24 | ||
| 423 | #define CCN_PVR_CHIP_MASK 0xff | ||
| 424 | #define CCN_PVR_CHIP_ST40STB1 0x4 | ||
| 425 | |||
| 426 | |||
| 427 | struct frqcr_data { | ||
| 428 | unsigned short frqcr; | ||
| 429 | |||
| 430 | struct { | ||
| 431 | unsigned char multiplier; | ||
| 432 | unsigned char divisor; | ||
| 433 | } factor[3]; | ||
| 434 | }; | ||
| 435 | |||
| 436 | static struct frqcr_data st40_frqcr_table[] = { | ||
| 437 | { 0x000, {{1,1}, {1,1}, {1,2}}}, | ||
| 438 | { 0x002, {{1,1}, {1,1}, {1,4}}}, | ||
| 439 | { 0x004, {{1,1}, {1,1}, {1,8}}}, | ||
| 440 | { 0x008, {{1,1}, {1,2}, {1,2}}}, | ||
| 441 | { 0x00A, {{1,1}, {1,2}, {1,4}}}, | ||
| 442 | { 0x00C, {{1,1}, {1,2}, {1,8}}}, | ||
| 443 | { 0x011, {{1,1}, {2,3}, {1,6}}}, | ||
| 444 | { 0x013, {{1,1}, {2,3}, {1,3}}}, | ||
| 445 | { 0x01A, {{1,1}, {1,2}, {1,4}}}, | ||
| 446 | { 0x01C, {{1,1}, {1,2}, {1,8}}}, | ||
| 447 | { 0x023, {{1,1}, {2,3}, {1,3}}}, | ||
| 448 | { 0x02C, {{1,1}, {1,2}, {1,8}}}, | ||
| 449 | { 0x048, {{1,2}, {1,2}, {1,4}}}, | ||
| 450 | { 0x04A, {{1,2}, {1,2}, {1,6}}}, | ||
| 451 | { 0x04C, {{1,2}, {1,2}, {1,8}}}, | ||
| 452 | { 0x05A, {{1,2}, {1,3}, {1,6}}}, | ||
| 453 | { 0x05C, {{1,2}, {1,3}, {1,6}}}, | ||
| 454 | { 0x063, {{1,2}, {1,4}, {1,4}}}, | ||
| 455 | { 0x06C, {{1,2}, {1,4}, {1,8}}}, | ||
| 456 | { 0x091, {{1,3}, {1,3}, {1,6}}}, | ||
| 457 | { 0x093, {{1,3}, {1,3}, {1,6}}}, | ||
| 458 | { 0x0A3, {{1,3}, {1,6}, {1,6}}}, | ||
| 459 | { 0x0DA, {{1,4}, {1,4}, {1,8}}}, | ||
| 460 | { 0x0DC, {{1,4}, {1,4}, {1,8}}}, | ||
| 461 | { 0x0EC, {{1,4}, {1,8}, {1,8}}}, | ||
| 462 | { 0x123, {{1,4}, {1,4}, {1,8}}}, | ||
| 463 | { 0x16C, {{1,4}, {1,8}, {1,8}}}, | ||
| 464 | }; | 149 | }; |
| 465 | 150 | ||
| 466 | struct memclk_data { | 151 | static int __init timer_init_sysfs(void) |
| 467 | unsigned char multiplier; | ||
| 468 | unsigned char divisor; | ||
| 469 | }; | ||
| 470 | |||
| 471 | static struct memclk_data st40_memclk_table[8] = { | ||
| 472 | {1,1}, // 000 | ||
| 473 | {1,2}, // 001 | ||
| 474 | {1,3}, // 010 | ||
| 475 | {2,3}, // 011 | ||
| 476 | {1,4}, // 100 | ||
| 477 | {1,6}, // 101 | ||
| 478 | {1,8}, // 110 | ||
| 479 | {1,8} // 111 | ||
| 480 | }; | ||
| 481 | |||
| 482 | static void st40_specific_time_init(unsigned int module_clock, unsigned short frqcr) | ||
| 483 | { | 152 | { |
| 484 | unsigned int cpu_clock, master_clock, bus_clock, memory_clock; | 153 | int ret = sysdev_class_register(&timer_sysclass); |
| 485 | struct frqcr_data *d; | 154 | if (ret != 0) |
| 486 | int a; | 155 | return ret; |
| 487 | unsigned long memclkcr; | ||
| 488 | struct memclk_data *e; | ||
| 489 | 156 | ||
| 490 | for (a = 0; a < ARRAY_SIZE(st40_frqcr_table); a++) { | 157 | sys_timer->dev.cls = &timer_sysclass; |
| 491 | d = &st40_frqcr_table[a]; | 158 | return sysdev_register(&sys_timer->dev); |
| 492 | 159 | } | |
| 493 | if (d->frqcr == (frqcr & 0x1ff)) | ||
| 494 | break; | ||
| 495 | } | ||
| 496 | 160 | ||
| 497 | if (a == ARRAY_SIZE(st40_frqcr_table)) { | 161 | device_initcall(timer_init_sysfs); |
| 498 | d = st40_frqcr_table; | ||
| 499 | 162 | ||
| 500 | printk("ERROR: Unrecognised FRQCR value (0x%x), " | 163 | void (*board_time_init)(void); |
| 501 | "using default multipliers\n", frqcr); | ||
| 502 | } | ||
| 503 | |||
| 504 | memclkcr = ctrl_inl(CLOCKGEN_MEMCLKCR); | ||
| 505 | e = &st40_memclk_table[memclkcr & MEMCLKCR_RATIO_MASK]; | ||
| 506 | |||
| 507 | printk(KERN_INFO "Clock multipliers: CPU: %d/%d Bus: %d/%d " | ||
| 508 | "Mem: %d/%d Periph: %d/%d\n", | ||
| 509 | d->factor[0].multiplier, d->factor[0].divisor, | ||
| 510 | d->factor[1].multiplier, d->factor[1].divisor, | ||
| 511 | e->multiplier, e->divisor, | ||
| 512 | d->factor[2].multiplier, d->factor[2].divisor); | ||
| 513 | |||
| 514 | master_clock = module_clock * d->factor[2].divisor | ||
| 515 | / d->factor[2].multiplier; | ||
| 516 | bus_clock = master_clock * d->factor[1].multiplier | ||
| 517 | / d->factor[1].divisor; | ||
| 518 | memory_clock = master_clock * e->multiplier | ||
| 519 | / e->divisor; | ||
| 520 | cpu_clock = master_clock * d->factor[0].multiplier | ||
| 521 | / d->factor[0].divisor; | ||
| 522 | |||
| 523 | current_cpu_data.cpu_clock = cpu_clock; | ||
| 524 | current_cpu_data.master_clock = master_clock; | ||
| 525 | current_cpu_data.bus_clock = bus_clock; | ||
| 526 | current_cpu_data.memory_clock = memory_clock; | ||
| 527 | current_cpu_data.module_clock = module_clock; | ||
| 528 | } | ||
| 529 | #endif | ||
| 530 | 164 | ||
| 531 | void __init time_init(void) | 165 | void __init time_init(void) |
| 532 | { | 166 | { |
| 533 | unsigned int timer_freq = 0; | ||
| 534 | unsigned int ifc, pfc, bfc; | ||
| 535 | unsigned long interval; | ||
| 536 | #ifdef CONFIG_CPU_SUBTYPE_ST40STB1 | ||
| 537 | unsigned long pvr; | ||
| 538 | unsigned short frqcr; | ||
| 539 | #endif | ||
| 540 | |||
| 541 | if (board_time_init) | 167 | if (board_time_init) |
| 542 | board_time_init(); | 168 | board_time_init(); |
| 543 | 169 | ||
| 544 | /* | 170 | clk_init(); |
| 545 | * If we don't have an RTC (such as with the SH7300), don't attempt to | ||
| 546 | * probe the timer frequency. Rely on an either hardcoded peripheral | ||
| 547 | * clock value, or on the sh_pclk command line option. Note that we | ||
| 548 | * still need to have CONFIG_SH_PCLK_FREQ set in order for things like | ||
| 549 | * CLOCK_TICK_RATE to be sane. | ||
| 550 | */ | ||
| 551 | current_cpu_data.module_clock = sh_pclk_freq; | ||
| 552 | |||
| 553 | #ifdef CONFIG_SH_PCLK_CALC | ||
| 554 | /* XXX: Switch this over to a more generic test. */ | ||
| 555 | { | ||
| 556 | unsigned int freq; | ||
| 557 | |||
| 558 | /* | ||
| 559 | * If we've specified a peripheral clock frequency, and we have | ||
| 560 | * an RTC, compare it against the autodetected value. Complain | ||
| 561 | * if there's a mismatch. | ||
| 562 | */ | ||
| 563 | timer_freq = get_timer_frequency(); | ||
| 564 | freq = timer_freq * 4; | ||
| 565 | |||
| 566 | if (sh_pclk_freq && (sh_pclk_freq/100*99 > freq || sh_pclk_freq/100*101 < freq)) { | ||
| 567 | printk(KERN_NOTICE "Calculated peripheral clock value " | ||
| 568 | "%d differs from sh_pclk value %d, fixing..\n", | ||
| 569 | freq, sh_pclk_freq); | ||
| 570 | current_cpu_data.module_clock = freq; | ||
| 571 | } | ||
| 572 | } | ||
| 573 | #endif | ||
| 574 | |||
| 575 | #ifdef CONFIG_CPU_SUBTYPE_ST40STB1 | ||
| 576 | /* XXX: Update ST40 code to use board_time_init() */ | ||
| 577 | pvr = ctrl_inl(CCN_PVR); | ||
| 578 | frqcr = ctrl_inw(FRQCR); | ||
| 579 | printk("time.c ST40 Probe: PVR %08lx, FRQCR %04hx\n", pvr, frqcr); | ||
| 580 | |||
| 581 | if (((pvr >> CCN_PVR_CHIP_SHIFT) & CCN_PVR_CHIP_MASK) == CCN_PVR_CHIP_ST40STB1) | ||
| 582 | st40_specific_time_init(current_cpu_data.module_clock, frqcr); | ||
| 583 | else | ||
| 584 | #endif | ||
| 585 | get_current_frequency_divisors(&ifc, &bfc, &pfc); | ||
| 586 | 171 | ||
| 587 | if (rtc_get_time) { | 172 | if (rtc_get_time) { |
| 588 | rtc_get_time(&xtime); | 173 | rtc_get_time(&xtime); |
| @@ -594,51 +179,12 @@ void __init time_init(void) | |||
| 594 | set_normalized_timespec(&wall_to_monotonic, | 179 | set_normalized_timespec(&wall_to_monotonic, |
| 595 | -xtime.tv_sec, -xtime.tv_nsec); | 180 | -xtime.tv_sec, -xtime.tv_nsec); |
| 596 | 181 | ||
| 597 | if (board_timer_setup) { | ||
| 598 | board_timer_setup(&irq0); | ||
| 599 | } else { | ||
| 600 | setup_irq(TIMER_IRQ, &irq0); | ||
| 601 | } | ||
| 602 | |||
| 603 | /* | 182 | /* |
| 604 | * for ST40 chips the current_cpu_data should already be set | 183 | * Find the timer to use as the system timer, it will be |
| 605 | * so not having valid pfc/bfc/ifc shouldn't be a problem | 184 | * initialized for us. |
| 606 | */ | 185 | */ |
| 607 | if (!current_cpu_data.master_clock) | 186 | sys_timer = get_sys_timer(); |
| 608 | current_cpu_data.master_clock = current_cpu_data.module_clock * pfc; | 187 | printk(KERN_INFO "Using %s for system timer\n", sys_timer->name); |
| 609 | if (!current_cpu_data.bus_clock) | ||
| 610 | current_cpu_data.bus_clock = current_cpu_data.master_clock / bfc; | ||
| 611 | if (!current_cpu_data.cpu_clock) | ||
| 612 | current_cpu_data.cpu_clock = current_cpu_data.master_clock / ifc; | ||
| 613 | |||
| 614 | printk("CPU clock: %d.%02dMHz\n", | ||
| 615 | (current_cpu_data.cpu_clock / 1000000), | ||
| 616 | (current_cpu_data.cpu_clock % 1000000)/10000); | ||
| 617 | printk("Bus clock: %d.%02dMHz\n", | ||
| 618 | (current_cpu_data.bus_clock / 1000000), | ||
| 619 | (current_cpu_data.bus_clock % 1000000)/10000); | ||
| 620 | #ifdef CONFIG_CPU_SUBTYPE_ST40STB1 | ||
| 621 | printk("Memory clock: %d.%02dMHz\n", | ||
| 622 | (current_cpu_data.memory_clock / 1000000), | ||
| 623 | (current_cpu_data.memory_clock % 1000000)/10000); | ||
| 624 | #endif | ||
| 625 | printk("Module clock: %d.%02dMHz\n", | ||
| 626 | (current_cpu_data.module_clock / 1000000), | ||
| 627 | (current_cpu_data.module_clock % 1000000)/10000); | ||
| 628 | |||
| 629 | interval = (current_cpu_data.module_clock/4 + HZ/2) / HZ; | ||
| 630 | |||
| 631 | printk("Interval = %ld\n", interval); | ||
| 632 | |||
| 633 | /* Start TMU0 */ | ||
| 634 | ctrl_outb(0, TMU_TSTR); | ||
| 635 | #if !defined(CONFIG_CPU_SUBTYPE_SH7300) | ||
| 636 | ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); | ||
| 637 | #endif | ||
| 638 | ctrl_outw(TMU0_TCR_INIT, TMU0_TCR); | ||
| 639 | ctrl_outl(interval, TMU0_TCOR); | ||
| 640 | ctrl_outl(interval, TMU0_TCNT); | ||
| 641 | ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); | ||
| 642 | 188 | ||
| 643 | #if defined(CONFIG_SH_KGDB) | 189 | #if defined(CONFIG_SH_KGDB) |
| 644 | /* | 190 | /* |
diff --git a/arch/sh/kernel/timers/Makefile b/arch/sh/kernel/timers/Makefile new file mode 100644 index 000000000000..151a6a304cec --- /dev/null +++ b/arch/sh/kernel/timers/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the various Linux/SuperH timers | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-y := timer.o | ||
| 6 | |||
| 7 | obj-$(CONFIG_SH_TMU) += timer-tmu.o | ||
| 8 | |||
diff --git a/arch/sh/kernel/timers/timer-tmu.c b/arch/sh/kernel/timers/timer-tmu.c new file mode 100644 index 000000000000..96a64cb13106 --- /dev/null +++ b/arch/sh/kernel/timers/timer-tmu.c | |||
| @@ -0,0 +1,229 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/timers/timer-tmu.c - TMU Timer Support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Paul Mundt | ||
| 5 | * | ||
| 6 | * TMU handling code hacked out of arch/sh/kernel/time.c | ||
| 7 | * | ||
| 8 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | ||
| 9 | * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> | ||
| 10 | * Copyright (C) 2002, 2003, 2004 Paul Mundt | ||
| 11 | * Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org> | ||
| 12 | * | ||
| 13 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 14 | * License. See the file "COPYING" in the main directory of this archive | ||
| 15 | * for more details. | ||
| 16 | */ | ||
| 17 | #include <linux/init.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/spinlock.h> | ||
| 21 | #include <linux/seqlock.h> | ||
| 22 | #include <asm/timer.h> | ||
| 23 | #include <asm/rtc.h> | ||
| 24 | #include <asm/io.h> | ||
| 25 | #include <asm/irq.h> | ||
| 26 | #include <asm/clock.h> | ||
| 27 | |||
| 28 | #define TMU_TOCR_INIT 0x00 | ||
| 29 | #define TMU0_TCR_INIT 0x0020 | ||
| 30 | #define TMU_TSTR_INIT 1 | ||
| 31 | |||
| 32 | #define TMU0_TCR_CALIB 0x0000 | ||
| 33 | |||
| 34 | static DEFINE_SPINLOCK(tmu0_lock); | ||
| 35 | |||
| 36 | static unsigned long tmu_timer_get_offset(void) | ||
| 37 | { | ||
| 38 | int count; | ||
| 39 | unsigned long flags; | ||
| 40 | |||
| 41 | static int count_p = 0x7fffffff; /* for the first call after boot */ | ||
| 42 | static unsigned long jiffies_p = 0; | ||
| 43 | |||
| 44 | /* | ||
| 45 | * cache volatile jiffies temporarily; we have IRQs turned off. | ||
| 46 | */ | ||
| 47 | unsigned long jiffies_t; | ||
| 48 | |||
| 49 | spin_lock_irqsave(&tmu0_lock, flags); | ||
| 50 | /* timer count may underflow right here */ | ||
| 51 | count = ctrl_inl(TMU0_TCNT); /* read the latched count */ | ||
| 52 | |||
| 53 | jiffies_t = jiffies; | ||
| 54 | |||
| 55 | /* | ||
| 56 | * avoiding timer inconsistencies (they are rare, but they happen)... | ||
| 57 | * there is one kind of problem that must be avoided here: | ||
| 58 | * 1. the timer counter underflows | ||
| 59 | */ | ||
| 60 | |||
| 61 | if (jiffies_t == jiffies_p) { | ||
| 62 | if (count > count_p) { | ||
| 63 | /* the nutcase */ | ||
| 64 | if (ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */ | ||
| 65 | count -= LATCH; | ||
| 66 | } else { | ||
| 67 | printk("%s (): hardware timer problem?\n", | ||
| 68 | __FUNCTION__); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } else | ||
| 72 | jiffies_p = jiffies_t; | ||
| 73 | |||
| 74 | count_p = count; | ||
| 75 | spin_unlock_irqrestore(&tmu0_lock, flags); | ||
| 76 | |||
| 77 | count = ((LATCH-1) - count) * TICK_SIZE; | ||
| 78 | count = (count + LATCH/2) / LATCH; | ||
| 79 | |||
| 80 | return count; | ||
| 81 | } | ||
| 82 | |||
| 83 | static irqreturn_t tmu_timer_interrupt(int irq, void *dev_id, | ||
| 84 | struct pt_regs *regs) | ||
| 85 | { | ||
| 86 | unsigned long timer_status; | ||
| 87 | |||
| 88 | /* Clear UNF bit */ | ||
| 89 | timer_status = ctrl_inw(TMU0_TCR); | ||
| 90 | timer_status &= ~0x100; | ||
| 91 | ctrl_outw(timer_status, TMU0_TCR); | ||
| 92 | |||
| 93 | /* | ||
| 94 | * Here we are in the timer irq handler. We just have irqs locally | ||
| 95 | * disabled but we don't know if the timer_bh is running on the other | ||
| 96 | * CPU. We need to avoid to SMP race with it. NOTE: we don' t need | ||
| 97 | * the irq version of write_lock because as just said we have irq | ||
| 98 | * locally disabled. -arca | ||
| 99 | */ | ||
| 100 | write_seqlock(&xtime_lock); | ||
| 101 | handle_timer_tick(regs); | ||
| 102 | write_sequnlock(&xtime_lock); | ||
| 103 | |||
| 104 | return IRQ_HANDLED; | ||
| 105 | } | ||
| 106 | |||
| 107 | static struct irqaction tmu_irq = { | ||
| 108 | .name = "timer", | ||
| 109 | .handler = tmu_timer_interrupt, | ||
| 110 | .flags = SA_INTERRUPT, | ||
| 111 | .mask = CPU_MASK_NONE, | ||
| 112 | }; | ||
| 113 | |||
| 114 | /* | ||
| 115 | * Hah! We'll see if this works (switching from usecs to nsecs). | ||
| 116 | */ | ||
| 117 | static unsigned long tmu_timer_get_frequency(void) | ||
| 118 | { | ||
| 119 | u32 freq; | ||
| 120 | struct timespec ts1, ts2; | ||
| 121 | unsigned long diff_nsec; | ||
| 122 | unsigned long factor; | ||
| 123 | |||
| 124 | /* Setup the timer: We don't want to generate interrupts, just | ||
| 125 | * have it count down at its natural rate. | ||
| 126 | */ | ||
| 127 | ctrl_outb(0, TMU_TSTR); | ||
| 128 | #if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760) | ||
| 129 | ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); | ||
| 130 | #endif | ||
| 131 | ctrl_outw(TMU0_TCR_CALIB, TMU0_TCR); | ||
| 132 | ctrl_outl(0xffffffff, TMU0_TCOR); | ||
| 133 | ctrl_outl(0xffffffff, TMU0_TCNT); | ||
| 134 | |||
| 135 | rtc_get_time(&ts2); | ||
| 136 | |||
| 137 | do { | ||
| 138 | rtc_get_time(&ts1); | ||
| 139 | } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec); | ||
| 140 | |||
| 141 | /* actually start the timer */ | ||
| 142 | ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); | ||
| 143 | |||
| 144 | do { | ||
| 145 | rtc_get_time(&ts2); | ||
| 146 | } while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec); | ||
| 147 | |||
| 148 | freq = 0xffffffff - ctrl_inl(TMU0_TCNT); | ||
| 149 | if (ts2.tv_nsec < ts1.tv_nsec) { | ||
| 150 | ts2.tv_nsec += 1000000000; | ||
| 151 | ts2.tv_sec--; | ||
| 152 | } | ||
| 153 | |||
| 154 | diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec); | ||
| 155 | |||
| 156 | /* this should work well if the RTC has a precision of n Hz, where | ||
| 157 | * n is an integer. I don't think we have to worry about the other | ||
| 158 | * cases. */ | ||
| 159 | factor = (1000000000 + diff_nsec/2) / diff_nsec; | ||
| 160 | |||
| 161 | if (factor * diff_nsec > 1100000000 || | ||
| 162 | factor * diff_nsec < 900000000) | ||
| 163 | panic("weird RTC (diff_nsec %ld)", diff_nsec); | ||
| 164 | |||
| 165 | return freq * factor; | ||
| 166 | } | ||
| 167 | |||
| 168 | static void tmu_clk_init(struct clk *clk) | ||
| 169 | { | ||
| 170 | u8 divisor = TMU0_TCR_INIT & 0x7; | ||
| 171 | ctrl_outw(TMU0_TCR_INIT, TMU0_TCR); | ||
| 172 | clk->rate = clk->parent->rate / (4 << (divisor << 1)); | ||
| 173 | } | ||
| 174 | |||
| 175 | static void tmu_clk_recalc(struct clk *clk) | ||
| 176 | { | ||
| 177 | u8 divisor = ctrl_inw(TMU0_TCR) & 0x7; | ||
| 178 | clk->rate = clk->parent->rate / (4 << (divisor << 1)); | ||
| 179 | } | ||
| 180 | |||
| 181 | static struct clk_ops tmu_clk_ops = { | ||
| 182 | .init = tmu_clk_init, | ||
| 183 | .recalc = tmu_clk_recalc, | ||
| 184 | }; | ||
| 185 | |||
| 186 | static struct clk tmu0_clk = { | ||
| 187 | .name = "tmu0_clk", | ||
| 188 | .ops = &tmu_clk_ops, | ||
| 189 | }; | ||
| 190 | |||
| 191 | static int tmu_timer_init(void) | ||
| 192 | { | ||
| 193 | unsigned long interval; | ||
| 194 | |||
| 195 | setup_irq(TIMER_IRQ, &tmu_irq); | ||
| 196 | |||
| 197 | tmu0_clk.parent = clk_get("module_clk"); | ||
| 198 | |||
| 199 | /* Start TMU0 */ | ||
| 200 | ctrl_outb(0, TMU_TSTR); | ||
| 201 | #if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760) | ||
| 202 | ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); | ||
| 203 | #endif | ||
| 204 | |||
| 205 | clk_register(&tmu0_clk); | ||
| 206 | clk_enable(&tmu0_clk); | ||
| 207 | |||
| 208 | interval = (clk_get_rate(&tmu0_clk) + HZ / 2) / HZ; | ||
| 209 | printk(KERN_INFO "Interval = %ld\n", interval); | ||
| 210 | |||
| 211 | ctrl_outl(interval, TMU0_TCOR); | ||
| 212 | ctrl_outl(interval, TMU0_TCNT); | ||
| 213 | |||
| 214 | ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); | ||
| 215 | |||
| 216 | return 0; | ||
| 217 | } | ||
| 218 | |||
| 219 | struct sys_timer_ops tmu_timer_ops = { | ||
| 220 | .init = tmu_timer_init, | ||
| 221 | .get_frequency = tmu_timer_get_frequency, | ||
| 222 | .get_offset = tmu_timer_get_offset, | ||
| 223 | }; | ||
| 224 | |||
| 225 | struct sys_timer tmu_timer = { | ||
| 226 | .name = "tmu", | ||
| 227 | .ops = &tmu_timer_ops, | ||
| 228 | }; | ||
| 229 | |||
diff --git a/arch/sh/kernel/timers/timer.c b/arch/sh/kernel/timers/timer.c new file mode 100644 index 000000000000..dc1f631053a8 --- /dev/null +++ b/arch/sh/kernel/timers/timer.c | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | /* | ||
| 2 | * arch/sh/kernel/timers/timer.c - Common timer code | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Paul Mundt | ||
| 5 | * | ||
| 6 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 7 | * License. See the file "COPYING" in the main directory of this archive | ||
| 8 | * for more details. | ||
| 9 | */ | ||
| 10 | #include <linux/kernel.h> | ||
| 11 | #include <linux/init.h> | ||
| 12 | #include <linux/timer.h> | ||
| 13 | #include <linux/string.h> | ||
| 14 | #include <asm/timer.h> | ||
| 15 | |||
| 16 | static struct sys_timer *sys_timers[] __initdata = { | ||
| 17 | #ifdef CONFIG_SH_TMU | ||
| 18 | &tmu_timer, | ||
| 19 | #endif | ||
| 20 | NULL, | ||
| 21 | }; | ||
| 22 | |||
| 23 | static char timer_override[10] __initdata; | ||
| 24 | static int __init timer_setup(char *str) | ||
| 25 | { | ||
| 26 | if (str) | ||
| 27 | strlcpy(timer_override, str, sizeof(timer_override)); | ||
| 28 | return 1; | ||
| 29 | } | ||
| 30 | __setup("timer=", timer_setup); | ||
| 31 | |||
| 32 | struct sys_timer *get_sys_timer(void) | ||
| 33 | { | ||
| 34 | int i; | ||
| 35 | |||
| 36 | for (i = 0; i < ARRAY_SIZE(sys_timers); i++) { | ||
| 37 | struct sys_timer *t = sys_timers[i]; | ||
| 38 | |||
| 39 | if (unlikely(!t)) | ||
| 40 | break; | ||
| 41 | if (unlikely(timer_override[0])) | ||
| 42 | if ((strcmp(timer_override, t->name) != 0)) | ||
| 43 | continue; | ||
| 44 | if (likely(t->ops->init() == 0)) | ||
| 45 | return t; | ||
| 46 | } | ||
| 47 | |||
| 48 | return NULL; | ||
| 49 | } | ||
| 50 | |||
