-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Arve Hjønnevåg
authored and
Russell King
committed
Jan 26, 2008
1 parent
71490f5
commit 408eed3
Showing
4 changed files
with
361 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: 3042102a28501510a409fe86962f20369e325cf2 | ||
refs/heads/master: 3e4ea3728a38b224d8b35ff8a9281407f603aa20 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
obj-y += io.o idle.o | ||
obj-y += io.o idle.o irq.o timer.o | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/* linux/arch/arm/mach-msm/irq.c | ||
* | ||
* Copyright (C) 2007 Google, Inc. | ||
* | ||
* This software is licensed under the terms of the GNU General Public | ||
* License version 2, as published by the Free Software Foundation, and | ||
* may be copied, distributed, and modified under those terms. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
*/ | ||
|
||
#include <linux/init.h> | ||
#include <linux/module.h> | ||
#include <linux/sched.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/ptrace.h> | ||
#include <linux/timer.h> | ||
|
||
#include <linux/irq.h> | ||
#include <asm/hardware.h> | ||
|
||
#include <asm/io.h> | ||
|
||
#include <asm/arch/msm_iomap.h> | ||
|
||
#define VIC_REG(off) (MSM_VIC_BASE + (off)) | ||
|
||
#define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */ | ||
#define VIC_INT_SELECT1 VIC_REG(0x0004) /* 1: FIQ, 0: IRQ */ | ||
#define VIC_INT_EN0 VIC_REG(0x0010) | ||
#define VIC_INT_EN1 VIC_REG(0x0014) | ||
#define VIC_INT_ENCLEAR0 VIC_REG(0x0020) | ||
#define VIC_INT_ENCLEAR1 VIC_REG(0x0024) | ||
#define VIC_INT_ENSET0 VIC_REG(0x0030) | ||
#define VIC_INT_ENSET1 VIC_REG(0x0034) | ||
#define VIC_INT_TYPE0 VIC_REG(0x0040) /* 1: EDGE, 0: LEVEL */ | ||
#define VIC_INT_TYPE1 VIC_REG(0x0044) /* 1: EDGE, 0: LEVEL */ | ||
#define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */ | ||
#define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */ | ||
#define VIC_NO_PEND_VAL VIC_REG(0x0060) | ||
#define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */ | ||
#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ | ||
#define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */ | ||
#define VIC_IRQ_STATUS0 VIC_REG(0x0080) | ||
#define VIC_IRQ_STATUS1 VIC_REG(0x0084) | ||
#define VIC_FIQ_STATUS0 VIC_REG(0x0090) | ||
#define VIC_FIQ_STATUS1 VIC_REG(0x0094) | ||
#define VIC_RAW_STATUS0 VIC_REG(0x00A0) | ||
#define VIC_RAW_STATUS1 VIC_REG(0x00A4) | ||
#define VIC_INT_CLEAR0 VIC_REG(0x00B0) | ||
#define VIC_INT_CLEAR1 VIC_REG(0x00B4) | ||
#define VIC_SOFTINT0 VIC_REG(0x00C0) | ||
#define VIC_SOFTINT1 VIC_REG(0x00C4) | ||
#define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */ | ||
#define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */ | ||
#define VIC_IRQ_VEC_WR VIC_REG(0x00D8) | ||
#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0) | ||
#define VIC_IRQ_IN_STACK VIC_REG(0x00E4) | ||
#define VIC_TEST_BUS_SEL VIC_REG(0x00E8) | ||
|
||
#define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) | ||
#define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) | ||
|
||
static void msm_irq_ack(unsigned int irq) | ||
{ | ||
unsigned reg = VIC_INT_CLEAR0 + ((irq & 32) ? 4 : 0); | ||
irq = 1 << (irq & 31); | ||
writel(irq, reg); | ||
} | ||
|
||
static void msm_irq_mask(unsigned int irq) | ||
{ | ||
unsigned reg = VIC_INT_ENCLEAR0 + ((irq & 32) ? 4 : 0); | ||
writel(1 << (irq & 31), reg); | ||
} | ||
|
||
static void msm_irq_unmask(unsigned int irq) | ||
{ | ||
unsigned reg = VIC_INT_ENSET0 + ((irq & 32) ? 4 : 0); | ||
writel(1 << (irq & 31), reg); | ||
} | ||
|
||
static int msm_irq_set_wake(unsigned int irq, unsigned int on) | ||
{ | ||
return -EINVAL; | ||
} | ||
|
||
static int msm_irq_set_type(unsigned int irq, unsigned int flow_type) | ||
{ | ||
unsigned treg = VIC_INT_TYPE0 + ((irq & 32) ? 4 : 0); | ||
unsigned preg = VIC_INT_POLARITY0 + ((irq & 32) ? 4 : 0); | ||
int b = 1 << (irq & 31); | ||
|
||
if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) | ||
writel(readl(preg) | b, preg); | ||
if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) | ||
writel(readl(preg) & (~b), preg); | ||
|
||
if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { | ||
writel(readl(treg) | b, treg); | ||
set_irq_handler(irq, handle_edge_irq); | ||
} | ||
if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) { | ||
writel(readl(treg) & (~b), treg); | ||
set_irq_handler(irq, handle_level_irq); | ||
} | ||
return 0; | ||
} | ||
|
||
static struct irq_chip msm_irq_chip = { | ||
.name = "msm", | ||
.ack = msm_irq_ack, | ||
.mask = msm_irq_mask, | ||
.unmask = msm_irq_unmask, | ||
.set_wake = msm_irq_set_wake, | ||
.set_type = msm_irq_set_type, | ||
}; | ||
|
||
void __init msm_init_irq(void) | ||
{ | ||
unsigned n; | ||
|
||
/* select level interrupts */ | ||
writel(0, VIC_INT_TYPE0); | ||
writel(0, VIC_INT_TYPE1); | ||
|
||
/* select highlevel interrupts */ | ||
writel(0, VIC_INT_POLARITY0); | ||
writel(0, VIC_INT_POLARITY1); | ||
|
||
/* select IRQ for all INTs */ | ||
writel(0, VIC_INT_SELECT0); | ||
writel(0, VIC_INT_SELECT1); | ||
|
||
/* disable all INTs */ | ||
writel(0, VIC_INT_EN0); | ||
writel(0, VIC_INT_EN1); | ||
|
||
/* don't use 1136 vic */ | ||
writel(0, VIC_CONFIG); | ||
|
||
/* enable interrupt controller */ | ||
writel(1, VIC_INT_MASTEREN); | ||
|
||
for (n = 0; n < NR_MSM_IRQS; n++) { | ||
set_irq_chip(n, &msm_irq_chip); | ||
set_irq_handler(n, handle_level_irq); | ||
set_irq_flags(n, IRQF_VALID); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
/* linux/arch/arm/mach-msm/timer.c | ||
* | ||
* Copyright (C) 2007 Google, Inc. | ||
* | ||
* This software is licensed under the terms of the GNU General Public | ||
* License version 2, as published by the Free Software Foundation, and | ||
* may be copied, distributed, and modified under those terms. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
*/ | ||
|
||
#include <linux/init.h> | ||
#include <linux/time.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/irq.h> | ||
#include <linux/clk.h> | ||
#include <linux/clockchips.h> | ||
#include <linux/delay.h> | ||
|
||
#include <asm/mach/time.h> | ||
#include <asm/arch/msm_iomap.h> | ||
|
||
#include <asm/io.h> | ||
|
||
#define MSM_DGT_BASE (MSM_GPT_BASE + 0x10) | ||
#define MSM_DGT_SHIFT (5) | ||
|
||
#define TIMER_MATCH_VAL 0x0000 | ||
#define TIMER_COUNT_VAL 0x0004 | ||
#define TIMER_ENABLE 0x0008 | ||
#define TIMER_ENABLE_CLR_ON_MATCH_EN 2 | ||
#define TIMER_ENABLE_EN 1 | ||
#define TIMER_CLEAR 0x000C | ||
|
||
#define CSR_PROTECTION 0x0020 | ||
#define CSR_PROTECTION_EN 1 | ||
|
||
#define GPT_HZ 32768 | ||
#define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */ | ||
|
||
struct msm_clock { | ||
struct clock_event_device clockevent; | ||
struct clocksource clocksource; | ||
struct irqaction irq; | ||
uint32_t regbase; | ||
uint32_t freq; | ||
uint32_t shift; | ||
}; | ||
|
||
static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) | ||
{ | ||
struct clock_event_device *evt = dev_id; | ||
evt->event_handler(evt); | ||
return IRQ_HANDLED; | ||
} | ||
|
||
static cycle_t msm_gpt_read(void) | ||
{ | ||
return readl(MSM_GPT_BASE + TIMER_COUNT_VAL); | ||
} | ||
|
||
static cycle_t msm_dgt_read(void) | ||
{ | ||
return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT; | ||
} | ||
|
||
static int msm_timer_set_next_event(unsigned long cycles, | ||
struct clock_event_device *evt) | ||
{ | ||
struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); | ||
uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL); | ||
uint32_t alarm = now + (cycles << clock->shift); | ||
int late; | ||
|
||
writel(alarm, clock->regbase + TIMER_MATCH_VAL); | ||
now = readl(clock->regbase + TIMER_COUNT_VAL); | ||
late = now - alarm; | ||
if (late >= (-2 << clock->shift) && late < DGT_HZ*5) { | ||
printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, " | ||
"alarm already expired, now %x, alarm %x, late %d\n", | ||
cycles, clock->clockevent.name, now, alarm, late); | ||
return -ETIME; | ||
} | ||
return 0; | ||
} | ||
|
||
static void msm_timer_set_mode(enum clock_event_mode mode, | ||
struct clock_event_device *evt) | ||
{ | ||
struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); | ||
switch (mode) { | ||
case CLOCK_EVT_MODE_RESUME: | ||
case CLOCK_EVT_MODE_PERIODIC: | ||
break; | ||
case CLOCK_EVT_MODE_ONESHOT: | ||
writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); | ||
break; | ||
case CLOCK_EVT_MODE_UNUSED: | ||
case CLOCK_EVT_MODE_SHUTDOWN: | ||
writel(0, clock->regbase + TIMER_ENABLE); | ||
break; | ||
} | ||
} | ||
|
||
static struct msm_clock msm_clocks[] = { | ||
{ | ||
.clockevent = { | ||
.name = "gp_timer", | ||
.features = CLOCK_EVT_FEAT_ONESHOT, | ||
.shift = 32, | ||
.rating = 200, | ||
.set_next_event = msm_timer_set_next_event, | ||
.set_mode = msm_timer_set_mode, | ||
}, | ||
.clocksource = { | ||
.name = "gp_timer", | ||
.rating = 200, | ||
.read = msm_gpt_read, | ||
.mask = CLOCKSOURCE_MASK(32), | ||
.shift = 24, | ||
.flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
}, | ||
.irq = { | ||
.name = "gp_timer", | ||
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, | ||
.handler = msm_timer_interrupt, | ||
.dev_id = &msm_clocks[0].clockevent, | ||
.irq = INT_GP_TIMER_EXP | ||
}, | ||
.regbase = MSM_GPT_BASE, | ||
.freq = GPT_HZ | ||
}, | ||
{ | ||
.clockevent = { | ||
.name = "dg_timer", | ||
.features = CLOCK_EVT_FEAT_ONESHOT, | ||
.shift = 32 + MSM_DGT_SHIFT, | ||
.rating = 300, | ||
.set_next_event = msm_timer_set_next_event, | ||
.set_mode = msm_timer_set_mode, | ||
}, | ||
.clocksource = { | ||
.name = "dg_timer", | ||
.rating = 300, | ||
.read = msm_dgt_read, | ||
.mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), | ||
.shift = 24 - MSM_DGT_SHIFT, | ||
.flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
}, | ||
.irq = { | ||
.name = "dg_timer", | ||
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, | ||
.handler = msm_timer_interrupt, | ||
.dev_id = &msm_clocks[1].clockevent, | ||
.irq = INT_DEBUG_TIMER_EXP | ||
}, | ||
.regbase = MSM_DGT_BASE, | ||
.freq = DGT_HZ >> MSM_DGT_SHIFT, | ||
.shift = MSM_DGT_SHIFT | ||
} | ||
}; | ||
|
||
static void __init msm_timer_init(void) | ||
{ | ||
int i; | ||
int res; | ||
|
||
for (i = 0; i < ARRAY_SIZE(msm_clocks); i++) { | ||
struct msm_clock *clock = &msm_clocks[i]; | ||
struct clock_event_device *ce = &clock->clockevent; | ||
struct clocksource *cs = &clock->clocksource; | ||
writel(0, clock->regbase + TIMER_ENABLE); | ||
writel(0, clock->regbase + TIMER_CLEAR); | ||
writel(~0, clock->regbase + TIMER_MATCH_VAL); | ||
|
||
ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift); | ||
/* allow at least 10 seconds to notice that the timer wrapped */ | ||
ce->max_delta_ns = | ||
clockevent_delta2ns(0xf0000000 >> clock->shift, ce); | ||
/* 4 gets rounded down to 3 */ | ||
ce->min_delta_ns = clockevent_delta2ns(4, ce); | ||
ce->cpumask = cpumask_of_cpu(0); | ||
|
||
cs->mult = clocksource_hz2mult(clock->freq, cs->shift); | ||
res = clocksource_register(cs); | ||
if (res) | ||
printk(KERN_ERR "msm_timer_init: clocksource_register " | ||
"failed for %s\n", cs->name); | ||
|
||
res = setup_irq(clock->irq.irq, &clock->irq); | ||
if (res) | ||
printk(KERN_ERR "msm_timer_init: setup_irq " | ||
"failed for %s\n", cs->name); | ||
|
||
clockevents_register_device(ce); | ||
} | ||
} | ||
|
||
struct sys_timer msm_timer = { | ||
.init = msm_timer_init | ||
}; |