-
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.
clocksource: sunxi: Add Allwinner A1X Timer Driver
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> CC: Thomas Gleixner <tglx@linutronix.de> CC: John Stultz <johnstul@us.ibm.com>
- Loading branch information
Maxime Ripard
committed
Nov 16, 2012
1 parent
404525d
commit b2ac5d7
Showing
5 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
17 changes: 17 additions & 0 deletions
17
Documentation/devicetree/bindings/timer/allwinner,sunxi-timer.txt
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,17 @@ | ||
Allwinner A1X SoCs Timer Controller | ||
|
||
Required properties: | ||
|
||
- compatible : should be "allwinner,sunxi-timer" | ||
- reg : Specifies base physical address and size of the registers. | ||
- interrupts : The interrupt of the first timer | ||
- clocks: phandle to the source clock (usually a 24 MHz fixed clock) | ||
|
||
Example: | ||
|
||
timer { | ||
compatible = "allwinner,sunxi-timer"; | ||
reg = <0x01c20c00 0x400>; | ||
interrupts = <22>; | ||
clocks = <&osc>; | ||
}; |
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
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
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,170 @@ | ||
/* | ||
* Allwinner A1X SoCs timer handling. | ||
* | ||
* Copyright (C) 2012 Maxime Ripard | ||
* | ||
* Maxime Ripard <maxime.ripard@free-electrons.com> | ||
* | ||
* Based on code from | ||
* Allwinner Technology Co., Ltd. <www.allwinnertech.com> | ||
* Benn Huang <benn@allwinnertech.com> | ||
* | ||
* This file is licensed under the terms of the GNU General Public | ||
* License version 2. This program is licensed "as is" without any | ||
* warranty of any kind, whether express or implied. | ||
*/ | ||
|
||
#include <linux/clk.h> | ||
#include <linux/clockchips.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/irq.h> | ||
#include <linux/irqreturn.h> | ||
#include <linux/of.h> | ||
#include <linux/of_address.h> | ||
#include <linux/of_irq.h> | ||
#include <linux/sunxi_timer.h> | ||
#include <linux/clk/sunxi.h> | ||
|
||
#define TIMER_CTL_REG 0x00 | ||
#define TIMER_CTL_ENABLE (1 << 0) | ||
#define TIMER_IRQ_ST_REG 0x04 | ||
#define TIMER0_CTL_REG 0x10 | ||
#define TIMER0_CTL_ENABLE (1 << 0) | ||
#define TIMER0_CTL_AUTORELOAD (1 << 1) | ||
#define TIMER0_CTL_ONESHOT (1 << 7) | ||
#define TIMER0_INTVAL_REG 0x14 | ||
#define TIMER0_CNTVAL_REG 0x18 | ||
|
||
#define TIMER_SCAL 16 | ||
|
||
static void __iomem *timer_base; | ||
|
||
static void sunxi_clkevt_mode(enum clock_event_mode mode, | ||
struct clock_event_device *clk) | ||
{ | ||
u32 u = readl(timer_base + TIMER0_CTL_REG); | ||
|
||
switch (mode) { | ||
case CLOCK_EVT_MODE_PERIODIC: | ||
u &= ~(TIMER0_CTL_ONESHOT); | ||
writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG); | ||
break; | ||
|
||
case CLOCK_EVT_MODE_ONESHOT: | ||
writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG); | ||
break; | ||
case CLOCK_EVT_MODE_UNUSED: | ||
case CLOCK_EVT_MODE_SHUTDOWN: | ||
default: | ||
writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG); | ||
break; | ||
} | ||
} | ||
|
||
static int sunxi_clkevt_next_event(unsigned long evt, | ||
struct clock_event_device *unused) | ||
{ | ||
u32 u = readl(timer_base + TIMER0_CTL_REG); | ||
writel(evt, timer_base + TIMER0_CNTVAL_REG); | ||
writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD, | ||
timer_base + TIMER0_CTL_REG); | ||
|
||
return 0; | ||
} | ||
|
||
static struct clock_event_device sunxi_clockevent = { | ||
.name = "sunxi_tick", | ||
.shift = 32, | ||
.rating = 300, | ||
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
.set_mode = sunxi_clkevt_mode, | ||
.set_next_event = sunxi_clkevt_next_event, | ||
}; | ||
|
||
|
||
static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) | ||
{ | ||
struct clock_event_device *evt = (struct clock_event_device *)dev_id; | ||
|
||
writel(0x1, timer_base + TIMER_IRQ_ST_REG); | ||
evt->event_handler(evt); | ||
|
||
return IRQ_HANDLED; | ||
} | ||
|
||
static struct irqaction sunxi_timer_irq = { | ||
.name = "sunxi_timer0", | ||
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | ||
.handler = sunxi_timer_interrupt, | ||
.dev_id = &sunxi_clockevent, | ||
}; | ||
|
||
static struct of_device_id sunxi_timer_dt_ids[] = { | ||
{ .compatible = "allwinner,sunxi-timer" }, | ||
}; | ||
|
||
static void __init sunxi_timer_init(void) | ||
{ | ||
struct device_node *node; | ||
unsigned long rate = 0; | ||
struct clk *clk; | ||
int ret, irq; | ||
u32 val; | ||
|
||
node = of_find_matching_node(NULL, sunxi_timer_dt_ids); | ||
if (!node) | ||
panic("No sunxi timer node"); | ||
|
||
timer_base = of_iomap(node, 0); | ||
if (!timer_base) | ||
panic("Can't map registers"); | ||
|
||
irq = irq_of_parse_and_map(node, 0); | ||
if (irq <= 0) | ||
panic("Can't parse IRQ"); | ||
|
||
sunxi_init_clocks(); | ||
|
||
clk = of_clk_get(node, 0); | ||
if (IS_ERR(clk)) | ||
panic("Can't get timer clock"); | ||
|
||
rate = clk_get_rate(clk); | ||
|
||
writel(rate / (TIMER_SCAL * HZ), | ||
timer_base + TIMER0_INTVAL_REG); | ||
|
||
/* set clock source to HOSC, 16 pre-division */ | ||
val = readl(timer_base + TIMER0_CTL_REG); | ||
val &= ~(0x07 << 4); | ||
val &= ~(0x03 << 2); | ||
val |= (4 << 4) | (1 << 2); | ||
writel(val, timer_base + TIMER0_CTL_REG); | ||
|
||
/* set mode to auto reload */ | ||
val = readl(timer_base + TIMER0_CTL_REG); | ||
writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG); | ||
|
||
ret = setup_irq(irq, &sunxi_timer_irq); | ||
if (ret) | ||
pr_warn("failed to setup irq %d\n", irq); | ||
|
||
/* Enable timer0 interrupt */ | ||
val = readl(timer_base + TIMER_CTL_REG); | ||
writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG); | ||
|
||
sunxi_clockevent.mult = div_sc(rate / TIMER_SCAL, | ||
NSEC_PER_SEC, | ||
sunxi_clockevent.shift); | ||
sunxi_clockevent.max_delta_ns = clockevent_delta2ns(0xff, | ||
&sunxi_clockevent); | ||
sunxi_clockevent.min_delta_ns = clockevent_delta2ns(0x1, | ||
&sunxi_clockevent); | ||
sunxi_clockevent.cpumask = cpumask_of(0); | ||
|
||
clockevents_register_device(&sunxi_clockevent); | ||
} | ||
|
||
struct sys_timer sunxi_timer = { | ||
.init = sunxi_timer_init, | ||
}; |
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,24 @@ | ||
/* | ||
* Copyright 2012 Maxime Ripard | ||
* | ||
* Maxime Ripard <maxime.ripard@free-electrons.com> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* 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. | ||
*/ | ||
|
||
#ifndef __SUNXI_TIMER_H | ||
#define __SUNXI_TIMER_H | ||
|
||
#include <asm/mach/time.h> | ||
|
||
extern struct sys_timer sunxi_timer; | ||
|
||
#endif |