diff options
| author | Raghu Gandham <Raghu.Gandham@imgtec.com> | 2013-04-10 17:30:12 -0400 |
|---|---|---|
| committer | Ralf Baechle <ralf@linux-mips.org> | 2013-05-09 11:55:21 -0400 |
| commit | 0ab2b7d08ea7226dc72ff0f8c05f470566facf7c (patch) | |
| tree | 7b791c6907a47733b1256da5e20600d182b36d40 /arch/mips | |
| parent | 2675fa7c7b46842f82b2766b5abe80e16ce32977 (diff) | |
MIPS: Add new GIC clockevent driver.
Add new clockevent driver that uses the counter present on the MIPS
Global Interrupt Controller.
Signed-off-by: Raghu Gandham <Raghu.Gandham@imgtec.com>
Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
Diffstat (limited to 'arch/mips')
| -rw-r--r-- | arch/mips/Kconfig | 12 | ||||
| -rw-r--r-- | arch/mips/include/asm/gic.h | 5 | ||||
| -rw-r--r-- | arch/mips/include/asm/time.h | 3 | ||||
| -rw-r--r-- | arch/mips/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/mips/kernel/cevt-gic.c | 104 | ||||
| -rw-r--r-- | arch/mips/kernel/cevt-r4k.c | 6 | ||||
| -rw-r--r-- | arch/mips/kernel/irq-gic.c | 31 | ||||
| -rw-r--r-- | arch/mips/mti-malta/malta-int.c | 3 |
8 files changed, 163 insertions, 2 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 4b623056470e..44e29b64b139 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig | |||
| @@ -912,6 +912,9 @@ config CEVT_GT641XX | |||
| 912 | config CEVT_R4K | 912 | config CEVT_R4K |
| 913 | bool | 913 | bool |
| 914 | 914 | ||
| 915 | config CEVT_GIC | ||
| 916 | bool | ||
| 917 | |||
| 915 | config CEVT_SB1250 | 918 | config CEVT_SB1250 |
| 916 | bool | 919 | bool |
| 917 | 920 | ||
| @@ -1819,6 +1822,15 @@ config FORCE_MAX_ZONEORDER | |||
| 1819 | The page size is not necessarily 4KB. Keep this in mind | 1822 | The page size is not necessarily 4KB. Keep this in mind |
| 1820 | when choosing a value for this option. | 1823 | when choosing a value for this option. |
| 1821 | 1824 | ||
| 1825 | config CEVT_GIC | ||
| 1826 | bool "Use GIC global counter for clock events" | ||
| 1827 | depends on IRQ_GIC && !(MIPS_SEAD3 || MIPS_MT_SMTC) | ||
| 1828 | help | ||
| 1829 | Use the GIC global counter for the clock events. The R4K clock | ||
| 1830 | event driver is always present, so if the platform ends up not | ||
| 1831 | detecting a GIC, it will fall back to the R4K timer for the | ||
| 1832 | generation of clock events. | ||
| 1833 | |||
| 1822 | config BOARD_SCACHE | 1834 | config BOARD_SCACHE |
| 1823 | bool | 1835 | bool |
| 1824 | 1836 | ||
diff --git a/arch/mips/include/asm/gic.h b/arch/mips/include/asm/gic.h index 398cf548832a..7153b32de18e 100644 --- a/arch/mips/include/asm/gic.h +++ b/arch/mips/include/asm/gic.h | |||
| @@ -202,7 +202,7 @@ | |||
| 202 | #define GIC_VPE_WD_COUNT0_OFS 0x0094 | 202 | #define GIC_VPE_WD_COUNT0_OFS 0x0094 |
| 203 | #define GIC_VPE_WD_INITIAL0_OFS 0x0098 | 203 | #define GIC_VPE_WD_INITIAL0_OFS 0x0098 |
| 204 | #define GIC_VPE_COMPARE_LO_OFS 0x00a0 | 204 | #define GIC_VPE_COMPARE_LO_OFS 0x00a0 |
| 205 | #define GIC_VPE_COMPARE_HI 0x00a4 | 205 | #define GIC_VPE_COMPARE_HI_OFS 0x00a4 |
| 206 | 206 | ||
| 207 | #define GIC_VPE_EIC_SHADOW_SET_BASE 0x0100 | 207 | #define GIC_VPE_EIC_SHADOW_SET_BASE 0x0100 |
| 208 | #define GIC_VPE_EIC_SS(intr) \ | 208 | #define GIC_VPE_EIC_SS(intr) \ |
| @@ -373,7 +373,10 @@ extern void gic_init(unsigned long gic_base_addr, | |||
| 373 | unsigned long gic_addrspace_size, struct gic_intr_map *intrmap, | 373 | unsigned long gic_addrspace_size, struct gic_intr_map *intrmap, |
| 374 | unsigned int intrmap_size, unsigned int irqbase); | 374 | unsigned int intrmap_size, unsigned int irqbase); |
| 375 | extern void gic_clocksource_init(unsigned int); | 375 | extern void gic_clocksource_init(unsigned int); |
| 376 | extern unsigned int gic_compare_int (void); | ||
| 376 | extern cycle_t gic_read_count(void); | 377 | extern cycle_t gic_read_count(void); |
| 378 | extern cycle_t gic_read_compare(void); | ||
| 379 | extern void gic_write_compare(cycle_t cnt); | ||
| 377 | extern void gic_send_ipi(unsigned int intr); | 380 | extern void gic_send_ipi(unsigned int intr); |
| 378 | extern unsigned int plat_ipi_call_int_xlate(unsigned int); | 381 | extern unsigned int plat_ipi_call_int_xlate(unsigned int); |
| 379 | extern unsigned int plat_ipi_resched_int_xlate(unsigned int); | 382 | extern unsigned int plat_ipi_resched_int_xlate(unsigned int); |
diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index 47842187ae42..2d7b9df4542d 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h | |||
| @@ -53,11 +53,14 @@ extern int (*perf_irq)(void); | |||
| 53 | extern unsigned int __weak get_c0_compare_int(void); | 53 | extern unsigned int __weak get_c0_compare_int(void); |
| 54 | extern int r4k_clockevent_init(void); | 54 | extern int r4k_clockevent_init(void); |
| 55 | extern int smtc_clockevent_init(void); | 55 | extern int smtc_clockevent_init(void); |
| 56 | extern int gic_clockevent_init(void); | ||
| 56 | 57 | ||
| 57 | static inline int mips_clockevent_init(void) | 58 | static inline int mips_clockevent_init(void) |
| 58 | { | 59 | { |
| 59 | #ifdef CONFIG_MIPS_MT_SMTC | 60 | #ifdef CONFIG_MIPS_MT_SMTC |
| 60 | return smtc_clockevent_init(); | 61 | return smtc_clockevent_init(); |
| 62 | #elif defined(CONFIG_CEVT_GIC) | ||
| 63 | return (gic_clockevent_init() | r4k_clockevent_init()); | ||
| 61 | #elif defined(CONFIG_CEVT_R4K) | 64 | #elif defined(CONFIG_CEVT_R4K) |
| 62 | return r4k_clockevent_init(); | 65 | return r4k_clockevent_init(); |
| 63 | #else | 66 | #else |
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index bab8b16706ca..3d95062173c7 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile | |||
| @@ -19,6 +19,7 @@ obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o | |||
| 19 | obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o | 19 | obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o |
| 20 | obj-$(CONFIG_MIPS_MT_SMTC) += cevt-smtc.o | 20 | obj-$(CONFIG_MIPS_MT_SMTC) += cevt-smtc.o |
| 21 | obj-$(CONFIG_CEVT_DS1287) += cevt-ds1287.o | 21 | obj-$(CONFIG_CEVT_DS1287) += cevt-ds1287.o |
| 22 | obj-$(CONFIG_CEVT_GIC) += cevt-gic.o | ||
| 22 | obj-$(CONFIG_CEVT_GT641XX) += cevt-gt641xx.o | 23 | obj-$(CONFIG_CEVT_GT641XX) += cevt-gt641xx.o |
| 23 | obj-$(CONFIG_CEVT_SB1250) += cevt-sb1250.o | 24 | obj-$(CONFIG_CEVT_SB1250) += cevt-sb1250.o |
| 24 | obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o | 25 | obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o |
diff --git a/arch/mips/kernel/cevt-gic.c b/arch/mips/kernel/cevt-gic.c new file mode 100644 index 000000000000..730eaf92c018 --- /dev/null +++ b/arch/mips/kernel/cevt-gic.c | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | /* | ||
| 2 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 3 | * License. See the file "COPYING" in the main directory of this archive | ||
| 4 | * for more details. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2013 Imagination Technologies Ltd. | ||
| 7 | */ | ||
| 8 | #include <linux/clockchips.h> | ||
| 9 | #include <linux/interrupt.h> | ||
| 10 | #include <linux/percpu.h> | ||
| 11 | #include <linux/smp.h> | ||
| 12 | #include <linux/irq.h> | ||
| 13 | |||
| 14 | #include <asm/time.h> | ||
| 15 | #include <asm/gic.h> | ||
| 16 | #include <asm/mips-boards/maltaint.h> | ||
| 17 | |||
| 18 | DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device); | ||
| 19 | int gic_timer_irq_installed; | ||
| 20 | |||
| 21 | |||
| 22 | static int gic_next_event(unsigned long delta, struct clock_event_device *evt) | ||
| 23 | { | ||
| 24 | u64 cnt; | ||
| 25 | int res; | ||
| 26 | |||
| 27 | cnt = gic_read_count(); | ||
| 28 | cnt += (u64)delta; | ||
| 29 | gic_write_compare(cnt); | ||
| 30 | res = ((int)(gic_read_count() - cnt) >= 0) ? -ETIME : 0; | ||
| 31 | return res; | ||
| 32 | } | ||
| 33 | |||
| 34 | void gic_set_clock_mode(enum clock_event_mode mode, | ||
| 35 | struct clock_event_device *evt) | ||
| 36 | { | ||
| 37 | /* Nothing to do ... */ | ||
| 38 | } | ||
| 39 | |||
| 40 | irqreturn_t gic_compare_interrupt(int irq, void *dev_id) | ||
| 41 | { | ||
| 42 | struct clock_event_device *cd; | ||
| 43 | int cpu = smp_processor_id(); | ||
| 44 | |||
| 45 | gic_write_compare(gic_read_compare()); | ||
| 46 | cd = &per_cpu(gic_clockevent_device, cpu); | ||
| 47 | cd->event_handler(cd); | ||
| 48 | return IRQ_HANDLED; | ||
| 49 | } | ||
| 50 | |||
| 51 | struct irqaction gic_compare_irqaction = { | ||
| 52 | .handler = gic_compare_interrupt, | ||
| 53 | .flags = IRQF_PERCPU | IRQF_TIMER, | ||
| 54 | .name = "timer", | ||
| 55 | }; | ||
| 56 | |||
| 57 | |||
| 58 | void gic_event_handler(struct clock_event_device *dev) | ||
| 59 | { | ||
| 60 | } | ||
| 61 | |||
| 62 | int __cpuinit gic_clockevent_init(void) | ||
| 63 | { | ||
| 64 | unsigned int cpu = smp_processor_id(); | ||
| 65 | struct clock_event_device *cd; | ||
| 66 | unsigned int irq; | ||
| 67 | |||
| 68 | if (!cpu_has_counter || !gic_frequency) | ||
| 69 | return -ENXIO; | ||
| 70 | |||
| 71 | irq = MIPS_GIC_IRQ_BASE; | ||
| 72 | |||
| 73 | cd = &per_cpu(gic_clockevent_device, cpu); | ||
| 74 | |||
| 75 | cd->name = "MIPS GIC"; | ||
| 76 | cd->features = CLOCK_EVT_FEAT_ONESHOT; | ||
| 77 | |||
| 78 | clockevent_set_clock(cd, gic_frequency); | ||
| 79 | |||
| 80 | /* Calculate the min / max delta */ | ||
| 81 | cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); | ||
| 82 | cd->min_delta_ns = clockevent_delta2ns(0x300, cd); | ||
| 83 | |||
| 84 | cd->rating = 300; | ||
| 85 | cd->irq = irq; | ||
| 86 | cd->cpumask = cpumask_of(cpu); | ||
| 87 | cd->set_next_event = gic_next_event; | ||
| 88 | cd->set_mode = gic_set_clock_mode; | ||
| 89 | cd->event_handler = gic_event_handler; | ||
| 90 | |||
| 91 | clockevents_register_device(cd); | ||
| 92 | |||
| 93 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_MAP), 0x80000002); | ||
| 94 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_SMASK), GIC_VPE_SMASK_CMP_MSK); | ||
| 95 | |||
| 96 | if (gic_timer_irq_installed) | ||
| 97 | return 0; | ||
| 98 | |||
| 99 | gic_timer_irq_installed = 1; | ||
| 100 | |||
| 101 | setup_irq(irq, &gic_compare_irqaction); | ||
| 102 | irq_set_handler(irq, handle_percpu_irq); | ||
| 103 | return 0; | ||
| 104 | } | ||
diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c index 0309aefbf713..0613f468f1ad 100644 --- a/arch/mips/kernel/cevt-r4k.c +++ b/arch/mips/kernel/cevt-r4k.c | |||
| @@ -72,6 +72,9 @@ irqreturn_t c0_compare_interrupt(int irq, void *dev_id) | |||
| 72 | /* Clear Count/Compare Interrupt */ | 72 | /* Clear Count/Compare Interrupt */ |
| 73 | write_c0_compare(read_c0_compare()); | 73 | write_c0_compare(read_c0_compare()); |
| 74 | cd = &per_cpu(mips_clockevent_device, cpu); | 74 | cd = &per_cpu(mips_clockevent_device, cpu); |
| 75 | #ifdef CONFIG_CEVT_GIC | ||
| 76 | if (!gic_present) | ||
| 77 | #endif | ||
| 75 | cd->event_handler(cd); | 78 | cd->event_handler(cd); |
| 76 | } | 79 | } |
| 77 | 80 | ||
| @@ -203,6 +206,9 @@ int __cpuinit r4k_clockevent_init(void) | |||
| 203 | cd->set_mode = mips_set_clock_mode; | 206 | cd->set_mode = mips_set_clock_mode; |
| 204 | cd->event_handler = mips_event_handler; | 207 | cd->event_handler = mips_event_handler; |
| 205 | 208 | ||
| 209 | #ifdef CONFIG_CEVT_GIC | ||
| 210 | if (!gic_present) | ||
| 211 | #endif | ||
| 206 | clockevents_register_device(cd); | 212 | clockevents_register_device(cd); |
| 207 | 213 | ||
| 208 | if (cp0_timer_irq_installed) | 214 | if (cp0_timer_irq_installed) |
diff --git a/arch/mips/kernel/irq-gic.c b/arch/mips/kernel/irq-gic.c index 6a476e1d41eb..c01b307317a9 100644 --- a/arch/mips/kernel/irq-gic.c +++ b/arch/mips/kernel/irq-gic.c | |||
| @@ -33,7 +33,7 @@ static struct gic_pcpu_mask pcpu_masks[NR_CPUS]; | |||
| 33 | static struct gic_pending_regs pending_regs[NR_CPUS]; | 33 | static struct gic_pending_regs pending_regs[NR_CPUS]; |
| 34 | static struct gic_intrmask_regs intrmask_regs[NR_CPUS]; | 34 | static struct gic_intrmask_regs intrmask_regs[NR_CPUS]; |
| 35 | 35 | ||
| 36 | #ifdef CONFIG_CSRC_GIC | 36 | #if defined(CONFIG_CSRC_GIC) || defined(CONFIG_CEVT_GIC) |
| 37 | cycle_t gic_read_count(void) | 37 | cycle_t gic_read_count(void) |
| 38 | { | 38 | { |
| 39 | unsigned int hi, hi2, lo; | 39 | unsigned int hi, hi2, lo; |
| @@ -46,6 +46,24 @@ cycle_t gic_read_count(void) | |||
| 46 | 46 | ||
| 47 | return (((cycle_t) hi) << 32) + lo; | 47 | return (((cycle_t) hi) << 32) + lo; |
| 48 | } | 48 | } |
| 49 | |||
| 50 | void gic_write_compare(cycle_t cnt) | ||
| 51 | { | ||
| 52 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), | ||
| 53 | (int)(cnt >> 32)); | ||
| 54 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), | ||
| 55 | (int)(cnt & 0xffffffff)); | ||
| 56 | } | ||
| 57 | |||
| 58 | cycle_t gic_read_compare(void) | ||
| 59 | { | ||
| 60 | unsigned int hi, lo; | ||
| 61 | |||
| 62 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), hi); | ||
| 63 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), lo); | ||
| 64 | |||
| 65 | return (((cycle_t) hi) << 32) + lo; | ||
| 66 | } | ||
| 49 | #endif | 67 | #endif |
| 50 | 68 | ||
| 51 | unsigned int gic_get_timer_pending(void) | 69 | unsigned int gic_get_timer_pending(void) |
| @@ -134,6 +152,17 @@ static void __init vpe_local_setup(unsigned int numvpes) | |||
| 134 | } | 152 | } |
| 135 | } | 153 | } |
| 136 | 154 | ||
| 155 | unsigned int gic_compare_int(void) | ||
| 156 | { | ||
| 157 | unsigned int pending; | ||
| 158 | |||
| 159 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_PEND), pending); | ||
| 160 | if (pending & GIC_VPE_PEND_CMP_MSK) | ||
| 161 | return 1; | ||
| 162 | else | ||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 137 | unsigned int gic_get_int(void) | 166 | unsigned int gic_get_int(void) |
| 138 | { | 167 | { |
| 139 | unsigned int i; | 168 | unsigned int i; |
diff --git a/arch/mips/mti-malta/malta-int.c b/arch/mips/mti-malta/malta-int.c index 8e840c20629e..0a1339ac3ec8 100644 --- a/arch/mips/mti-malta/malta-int.c +++ b/arch/mips/mti-malta/malta-int.c | |||
| @@ -133,6 +133,9 @@ static void malta_ipi_irqdispatch(void) | |||
| 133 | { | 133 | { |
| 134 | int irq; | 134 | int irq; |
| 135 | 135 | ||
| 136 | if (gic_compare_int()) | ||
| 137 | do_IRQ(MIPS_GIC_IRQ_BASE); | ||
| 138 | |||
| 136 | irq = gic_get_int(); | 139 | irq = gic_get_int(); |
| 137 | if (irq < 0) | 140 | if (irq < 0) |
| 138 | return; /* interrupt has already been cleared */ | 141 | return; /* interrupt has already been cleared */ |
