diff options
| -rw-r--r-- | arch/arm/mach-msm/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/mach-msm/irq.c | 154 | ||||
| -rw-r--r-- | arch/arm/mach-msm/timer.c | 205 |
3 files changed, 360 insertions, 1 deletions
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index feb9e469df9a..a42d1a019cd2 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile | |||
| @@ -1,2 +1,2 @@ | |||
| 1 | obj-y += io.o idle.o | 1 | obj-y += io.o idle.o irq.o timer.o |
| 2 | 2 | ||
diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c new file mode 100644 index 000000000000..24158040b789 --- /dev/null +++ b/arch/arm/mach-msm/irq.c | |||
| @@ -0,0 +1,154 @@ | |||
| 1 | /* linux/arch/arm/mach-msm/irq.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * | ||
| 5 | * This software is licensed under the terms of the GNU General Public | ||
| 6 | * License version 2, as published by the Free Software Foundation, and | ||
| 7 | * may be copied, distributed, and modified under those terms. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/sched.h> | ||
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/ptrace.h> | ||
| 21 | #include <linux/timer.h> | ||
| 22 | |||
| 23 | #include <linux/irq.h> | ||
| 24 | #include <asm/hardware.h> | ||
| 25 | |||
| 26 | #include <asm/io.h> | ||
| 27 | |||
| 28 | #include <asm/arch/msm_iomap.h> | ||
| 29 | |||
| 30 | #define VIC_REG(off) (MSM_VIC_BASE + (off)) | ||
| 31 | |||
| 32 | #define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */ | ||
| 33 | #define VIC_INT_SELECT1 VIC_REG(0x0004) /* 1: FIQ, 0: IRQ */ | ||
| 34 | #define VIC_INT_EN0 VIC_REG(0x0010) | ||
| 35 | #define VIC_INT_EN1 VIC_REG(0x0014) | ||
| 36 | #define VIC_INT_ENCLEAR0 VIC_REG(0x0020) | ||
| 37 | #define VIC_INT_ENCLEAR1 VIC_REG(0x0024) | ||
| 38 | #define VIC_INT_ENSET0 VIC_REG(0x0030) | ||
| 39 | #define VIC_INT_ENSET1 VIC_REG(0x0034) | ||
| 40 | #define VIC_INT_TYPE0 VIC_REG(0x0040) /* 1: EDGE, 0: LEVEL */ | ||
| 41 | #define VIC_INT_TYPE1 VIC_REG(0x0044) /* 1: EDGE, 0: LEVEL */ | ||
| 42 | #define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */ | ||
| 43 | #define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */ | ||
| 44 | #define VIC_NO_PEND_VAL VIC_REG(0x0060) | ||
| 45 | #define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */ | ||
| 46 | #define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ | ||
| 47 | #define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */ | ||
| 48 | #define VIC_IRQ_STATUS0 VIC_REG(0x0080) | ||
| 49 | #define VIC_IRQ_STATUS1 VIC_REG(0x0084) | ||
| 50 | #define VIC_FIQ_STATUS0 VIC_REG(0x0090) | ||
| 51 | #define VIC_FIQ_STATUS1 VIC_REG(0x0094) | ||
| 52 | #define VIC_RAW_STATUS0 VIC_REG(0x00A0) | ||
| 53 | #define VIC_RAW_STATUS1 VIC_REG(0x00A4) | ||
| 54 | #define VIC_INT_CLEAR0 VIC_REG(0x00B0) | ||
| 55 | #define VIC_INT_CLEAR1 VIC_REG(0x00B4) | ||
| 56 | #define VIC_SOFTINT0 VIC_REG(0x00C0) | ||
| 57 | #define VIC_SOFTINT1 VIC_REG(0x00C4) | ||
| 58 | #define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */ | ||
| 59 | #define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */ | ||
| 60 | #define VIC_IRQ_VEC_WR VIC_REG(0x00D8) | ||
| 61 | #define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0) | ||
| 62 | #define VIC_IRQ_IN_STACK VIC_REG(0x00E4) | ||
| 63 | #define VIC_TEST_BUS_SEL VIC_REG(0x00E8) | ||
| 64 | |||
| 65 | #define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) | ||
| 66 | #define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) | ||
| 67 | |||
| 68 | static void msm_irq_ack(unsigned int irq) | ||
| 69 | { | ||
| 70 | unsigned reg = VIC_INT_CLEAR0 + ((irq & 32) ? 4 : 0); | ||
| 71 | irq = 1 << (irq & 31); | ||
| 72 | writel(irq, reg); | ||
| 73 | } | ||
| 74 | |||
| 75 | static void msm_irq_mask(unsigned int irq) | ||
| 76 | { | ||
| 77 | unsigned reg = VIC_INT_ENCLEAR0 + ((irq & 32) ? 4 : 0); | ||
| 78 | writel(1 << (irq & 31), reg); | ||
| 79 | } | ||
| 80 | |||
| 81 | static void msm_irq_unmask(unsigned int irq) | ||
| 82 | { | ||
| 83 | unsigned reg = VIC_INT_ENSET0 + ((irq & 32) ? 4 : 0); | ||
| 84 | writel(1 << (irq & 31), reg); | ||
| 85 | } | ||
| 86 | |||
| 87 | static int msm_irq_set_wake(unsigned int irq, unsigned int on) | ||
| 88 | { | ||
| 89 | return -EINVAL; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int msm_irq_set_type(unsigned int irq, unsigned int flow_type) | ||
| 93 | { | ||
| 94 | unsigned treg = VIC_INT_TYPE0 + ((irq & 32) ? 4 : 0); | ||
| 95 | unsigned preg = VIC_INT_POLARITY0 + ((irq & 32) ? 4 : 0); | ||
| 96 | int b = 1 << (irq & 31); | ||
| 97 | |||
| 98 | if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) | ||
| 99 | writel(readl(preg) | b, preg); | ||
| 100 | if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) | ||
| 101 | writel(readl(preg) & (~b), preg); | ||
| 102 | |||
| 103 | if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { | ||
| 104 | writel(readl(treg) | b, treg); | ||
| 105 | set_irq_handler(irq, handle_edge_irq); | ||
| 106 | } | ||
| 107 | if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) { | ||
| 108 | writel(readl(treg) & (~b), treg); | ||
| 109 | set_irq_handler(irq, handle_level_irq); | ||
| 110 | } | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | static struct irq_chip msm_irq_chip = { | ||
| 115 | .name = "msm", | ||
| 116 | .ack = msm_irq_ack, | ||
| 117 | .mask = msm_irq_mask, | ||
| 118 | .unmask = msm_irq_unmask, | ||
| 119 | .set_wake = msm_irq_set_wake, | ||
| 120 | .set_type = msm_irq_set_type, | ||
| 121 | }; | ||
| 122 | |||
| 123 | void __init msm_init_irq(void) | ||
| 124 | { | ||
| 125 | unsigned n; | ||
| 126 | |||
| 127 | /* select level interrupts */ | ||
| 128 | writel(0, VIC_INT_TYPE0); | ||
| 129 | writel(0, VIC_INT_TYPE1); | ||
| 130 | |||
| 131 | /* select highlevel interrupts */ | ||
| 132 | writel(0, VIC_INT_POLARITY0); | ||
| 133 | writel(0, VIC_INT_POLARITY1); | ||
| 134 | |||
| 135 | /* select IRQ for all INTs */ | ||
| 136 | writel(0, VIC_INT_SELECT0); | ||
| 137 | writel(0, VIC_INT_SELECT1); | ||
| 138 | |||
| 139 | /* disable all INTs */ | ||
| 140 | writel(0, VIC_INT_EN0); | ||
| 141 | writel(0, VIC_INT_EN1); | ||
| 142 | |||
| 143 | /* don't use 1136 vic */ | ||
| 144 | writel(0, VIC_CONFIG); | ||
| 145 | |||
| 146 | /* enable interrupt controller */ | ||
| 147 | writel(1, VIC_INT_MASTEREN); | ||
| 148 | |||
| 149 | for (n = 0; n < NR_MSM_IRQS; n++) { | ||
| 150 | set_irq_chip(n, &msm_irq_chip); | ||
| 151 | set_irq_handler(n, handle_level_irq); | ||
| 152 | set_irq_flags(n, IRQF_VALID); | ||
| 153 | } | ||
| 154 | } | ||
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c new file mode 100644 index 000000000000..bd4732d1ab3e --- /dev/null +++ b/arch/arm/mach-msm/timer.c | |||
| @@ -0,0 +1,205 @@ | |||
| 1 | /* linux/arch/arm/mach-msm/timer.c | ||
| 2 | * | ||
| 3 | * Copyright (C) 2007 Google, Inc. | ||
| 4 | * | ||
| 5 | * This software is licensed under the terms of the GNU General Public | ||
| 6 | * License version 2, as published by the Free Software Foundation, and | ||
| 7 | * may be copied, distributed, and modified under those terms. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/time.h> | ||
| 18 | #include <linux/interrupt.h> | ||
| 19 | #include <linux/irq.h> | ||
| 20 | #include <linux/clk.h> | ||
| 21 | #include <linux/clockchips.h> | ||
| 22 | #include <linux/delay.h> | ||
| 23 | |||
| 24 | #include <asm/mach/time.h> | ||
| 25 | #include <asm/arch/msm_iomap.h> | ||
| 26 | |||
| 27 | #include <asm/io.h> | ||
| 28 | |||
| 29 | #define MSM_DGT_BASE (MSM_GPT_BASE + 0x10) | ||
| 30 | #define MSM_DGT_SHIFT (5) | ||
| 31 | |||
| 32 | #define TIMER_MATCH_VAL 0x0000 | ||
| 33 | #define TIMER_COUNT_VAL 0x0004 | ||
| 34 | #define TIMER_ENABLE 0x0008 | ||
| 35 | #define TIMER_ENABLE_CLR_ON_MATCH_EN 2 | ||
| 36 | #define TIMER_ENABLE_EN 1 | ||
| 37 | #define TIMER_CLEAR 0x000C | ||
| 38 | |||
| 39 | #define CSR_PROTECTION 0x0020 | ||
| 40 | #define CSR_PROTECTION_EN 1 | ||
| 41 | |||
| 42 | #define GPT_HZ 32768 | ||
| 43 | #define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */ | ||
| 44 | |||
| 45 | struct msm_clock { | ||
| 46 | struct clock_event_device clockevent; | ||
| 47 | struct clocksource clocksource; | ||
| 48 | struct irqaction irq; | ||
| 49 | uint32_t regbase; | ||
| 50 | uint32_t freq; | ||
| 51 | uint32_t shift; | ||
| 52 | }; | ||
| 53 | |||
| 54 | static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) | ||
| 55 | { | ||
| 56 | struct clock_event_device *evt = dev_id; | ||
| 57 | evt->event_handler(evt); | ||
| 58 | return IRQ_HANDLED; | ||
| 59 | } | ||
| 60 | |||
| 61 | static cycle_t msm_gpt_read(void) | ||
| 62 | { | ||
| 63 | return readl(MSM_GPT_BASE + TIMER_COUNT_VAL); | ||
| 64 | } | ||
| 65 | |||
| 66 | static cycle_t msm_dgt_read(void) | ||
| 67 | { | ||
| 68 | return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT; | ||
| 69 | } | ||
| 70 | |||
| 71 | static int msm_timer_set_next_event(unsigned long cycles, | ||
| 72 | struct clock_event_device *evt) | ||
| 73 | { | ||
| 74 | struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); | ||
| 75 | uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL); | ||
| 76 | uint32_t alarm = now + (cycles << clock->shift); | ||
| 77 | int late; | ||
| 78 | |||
| 79 | writel(alarm, clock->regbase + TIMER_MATCH_VAL); | ||
| 80 | now = readl(clock->regbase + TIMER_COUNT_VAL); | ||
| 81 | late = now - alarm; | ||
| 82 | if (late >= (-2 << clock->shift) && late < DGT_HZ*5) { | ||
| 83 | printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, " | ||
| 84 | "alarm already expired, now %x, alarm %x, late %d\n", | ||
| 85 | cycles, clock->clockevent.name, now, alarm, late); | ||
| 86 | return -ETIME; | ||
| 87 | } | ||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | static void msm_timer_set_mode(enum clock_event_mode mode, | ||
| 92 | struct clock_event_device *evt) | ||
| 93 | { | ||
| 94 | struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); | ||
| 95 | switch (mode) { | ||
| 96 | case CLOCK_EVT_MODE_RESUME: | ||
| 97 | case CLOCK_EVT_MODE_PERIODIC: | ||
| 98 | break; | ||
| 99 | case CLOCK_EVT_MODE_ONESHOT: | ||
| 100 | writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); | ||
| 101 | break; | ||
| 102 | case CLOCK_EVT_MODE_UNUSED: | ||
| 103 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
| 104 | writel(0, clock->regbase + TIMER_ENABLE); | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | static struct msm_clock msm_clocks[] = { | ||
| 110 | { | ||
| 111 | .clockevent = { | ||
| 112 | .name = "gp_timer", | ||
| 113 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
| 114 | .shift = 32, | ||
| 115 | .rating = 200, | ||
| 116 | .set_next_event = msm_timer_set_next_event, | ||
| 117 | .set_mode = msm_timer_set_mode, | ||
| 118 | }, | ||
| 119 | .clocksource = { | ||
| 120 | .name = "gp_timer", | ||
| 121 | .rating = 200, | ||
| 122 | .read = msm_gpt_read, | ||
| 123 | .mask = CLOCKSOURCE_MASK(32), | ||
| 124 | .shift = 24, | ||
| 125 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
| 126 | }, | ||
| 127 | .irq = { | ||
| 128 | .name = "gp_timer", | ||
| 129 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, | ||
| 130 | .handler = msm_timer_interrupt, | ||
| 131 | .dev_id = &msm_clocks[0].clockevent, | ||
| 132 | .irq = INT_GP_TIMER_EXP | ||
| 133 | }, | ||
| 134 | .regbase = MSM_GPT_BASE, | ||
| 135 | .freq = GPT_HZ | ||
| 136 | }, | ||
| 137 | { | ||
| 138 | .clockevent = { | ||
| 139 | .name = "dg_timer", | ||
| 140 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
| 141 | .shift = 32 + MSM_DGT_SHIFT, | ||
| 142 | .rating = 300, | ||
| 143 | .set_next_event = msm_timer_set_next_event, | ||
| 144 | .set_mode = msm_timer_set_mode, | ||
| 145 | }, | ||
| 146 | .clocksource = { | ||
| 147 | .name = "dg_timer", | ||
| 148 | .rating = 300, | ||
| 149 | .read = msm_dgt_read, | ||
| 150 | .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), | ||
| 151 | .shift = 24 - MSM_DGT_SHIFT, | ||
| 152 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
| 153 | }, | ||
| 154 | .irq = { | ||
| 155 | .name = "dg_timer", | ||
| 156 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, | ||
| 157 | .handler = msm_timer_interrupt, | ||
| 158 | .dev_id = &msm_clocks[1].clockevent, | ||
| 159 | .irq = INT_DEBUG_TIMER_EXP | ||
| 160 | }, | ||
| 161 | .regbase = MSM_DGT_BASE, | ||
| 162 | .freq = DGT_HZ >> MSM_DGT_SHIFT, | ||
| 163 | .shift = MSM_DGT_SHIFT | ||
| 164 | } | ||
| 165 | }; | ||
| 166 | |||
| 167 | static void __init msm_timer_init(void) | ||
| 168 | { | ||
| 169 | int i; | ||
| 170 | int res; | ||
| 171 | |||
| 172 | for (i = 0; i < ARRAY_SIZE(msm_clocks); i++) { | ||
| 173 | struct msm_clock *clock = &msm_clocks[i]; | ||
| 174 | struct clock_event_device *ce = &clock->clockevent; | ||
| 175 | struct clocksource *cs = &clock->clocksource; | ||
| 176 | writel(0, clock->regbase + TIMER_ENABLE); | ||
| 177 | writel(0, clock->regbase + TIMER_CLEAR); | ||
| 178 | writel(~0, clock->regbase + TIMER_MATCH_VAL); | ||
| 179 | |||
| 180 | ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift); | ||
| 181 | /* allow at least 10 seconds to notice that the timer wrapped */ | ||
| 182 | ce->max_delta_ns = | ||
| 183 | clockevent_delta2ns(0xf0000000 >> clock->shift, ce); | ||
| 184 | /* 4 gets rounded down to 3 */ | ||
| 185 | ce->min_delta_ns = clockevent_delta2ns(4, ce); | ||
| 186 | ce->cpumask = cpumask_of_cpu(0); | ||
| 187 | |||
| 188 | cs->mult = clocksource_hz2mult(clock->freq, cs->shift); | ||
| 189 | res = clocksource_register(cs); | ||
| 190 | if (res) | ||
| 191 | printk(KERN_ERR "msm_timer_init: clocksource_register " | ||
| 192 | "failed for %s\n", cs->name); | ||
| 193 | |||
| 194 | res = setup_irq(clock->irq.irq, &clock->irq); | ||
| 195 | if (res) | ||
| 196 | printk(KERN_ERR "msm_timer_init: setup_irq " | ||
| 197 | "failed for %s\n", cs->name); | ||
| 198 | |||
| 199 | clockevents_register_device(ce); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | struct sys_timer msm_timer = { | ||
| 204 | .init = msm_timer_init | ||
| 205 | }; | ||
