···11+Allwinner A1X SoCs Timer Controller22+33+Required properties:44+55+- compatible : should be "allwinner,sunxi-timer"66+- reg : Specifies base physical address and size of the registers.77+- interrupts : The interrupt of the first timer88+- clocks: phandle to the source clock (usually a 24 MHz fixed clock)99+1010+Example:1111+1212+timer {1313+ compatible = "allwinner,sunxi-timer";1414+ reg = <0x01c20c00 0x400>;1515+ interrupts = <22>;1616+ clocks = <&osc>;1717+};
···11+/*22+ * Allwinner A1X SoCs timer handling.33+ *44+ * Copyright (C) 2012 Maxime Ripard55+ *66+ * Maxime Ripard <maxime.ripard@free-electrons.com>77+ *88+ * Based on code from99+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>1010+ * Benn Huang <benn@allwinnertech.com>1111+ *1212+ * This file is licensed under the terms of the GNU General Public1313+ * License version 2. This program is licensed "as is" without any1414+ * warranty of any kind, whether express or implied.1515+ */1616+1717+#include <linux/clk.h>1818+#include <linux/clockchips.h>1919+#include <linux/interrupt.h>2020+#include <linux/irq.h>2121+#include <linux/irqreturn.h>2222+#include <linux/of.h>2323+#include <linux/of_address.h>2424+#include <linux/of_irq.h>2525+#include <linux/sunxi_timer.h>2626+#include <linux/clk/sunxi.h>2727+2828+#define TIMER_CTL_REG 0x002929+#define TIMER_CTL_ENABLE (1 << 0)3030+#define TIMER_IRQ_ST_REG 0x043131+#define TIMER0_CTL_REG 0x103232+#define TIMER0_CTL_ENABLE (1 << 0)3333+#define TIMER0_CTL_AUTORELOAD (1 << 1)3434+#define TIMER0_CTL_ONESHOT (1 << 7)3535+#define TIMER0_INTVAL_REG 0x143636+#define TIMER0_CNTVAL_REG 0x183737+3838+#define TIMER_SCAL 163939+4040+static void __iomem *timer_base;4141+4242+static void sunxi_clkevt_mode(enum clock_event_mode mode,4343+ struct clock_event_device *clk)4444+{4545+ u32 u = readl(timer_base + TIMER0_CTL_REG);4646+4747+ switch (mode) {4848+ case CLOCK_EVT_MODE_PERIODIC:4949+ u &= ~(TIMER0_CTL_ONESHOT);5050+ writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG);5151+ break;5252+5353+ case CLOCK_EVT_MODE_ONESHOT:5454+ writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG);5555+ break;5656+ case CLOCK_EVT_MODE_UNUSED:5757+ case CLOCK_EVT_MODE_SHUTDOWN:5858+ default:5959+ writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG);6060+ break;6161+ }6262+}6363+6464+static int sunxi_clkevt_next_event(unsigned long evt,6565+ struct clock_event_device *unused)6666+{6767+ u32 u = readl(timer_base + TIMER0_CTL_REG);6868+ writel(evt, timer_base + TIMER0_CNTVAL_REG);6969+ writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD,7070+ timer_base + TIMER0_CTL_REG);7171+7272+ return 0;7373+}7474+7575+static struct clock_event_device sunxi_clockevent = {7676+ .name = "sunxi_tick",7777+ .shift = 32,7878+ .rating = 300,7979+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,8080+ .set_mode = sunxi_clkevt_mode,8181+ .set_next_event = sunxi_clkevt_next_event,8282+};8383+8484+8585+static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id)8686+{8787+ struct clock_event_device *evt = (struct clock_event_device *)dev_id;8888+8989+ writel(0x1, timer_base + TIMER_IRQ_ST_REG);9090+ evt->event_handler(evt);9191+9292+ return IRQ_HANDLED;9393+}9494+9595+static struct irqaction sunxi_timer_irq = {9696+ .name = "sunxi_timer0",9797+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,9898+ .handler = sunxi_timer_interrupt,9999+ .dev_id = &sunxi_clockevent,100100+};101101+102102+static struct of_device_id sunxi_timer_dt_ids[] = {103103+ { .compatible = "allwinner,sunxi-timer" },104104+};105105+106106+static void __init sunxi_timer_init(void)107107+{108108+ struct device_node *node;109109+ unsigned long rate = 0;110110+ struct clk *clk;111111+ int ret, irq;112112+ u32 val;113113+114114+ node = of_find_matching_node(NULL, sunxi_timer_dt_ids);115115+ if (!node)116116+ panic("No sunxi timer node");117117+118118+ timer_base = of_iomap(node, 0);119119+ if (!timer_base)120120+ panic("Can't map registers");121121+122122+ irq = irq_of_parse_and_map(node, 0);123123+ if (irq <= 0)124124+ panic("Can't parse IRQ");125125+126126+ sunxi_init_clocks();127127+128128+ clk = of_clk_get(node, 0);129129+ if (IS_ERR(clk))130130+ panic("Can't get timer clock");131131+132132+ rate = clk_get_rate(clk);133133+134134+ writel(rate / (TIMER_SCAL * HZ),135135+ timer_base + TIMER0_INTVAL_REG);136136+137137+ /* set clock source to HOSC, 16 pre-division */138138+ val = readl(timer_base + TIMER0_CTL_REG);139139+ val &= ~(0x07 << 4);140140+ val &= ~(0x03 << 2);141141+ val |= (4 << 4) | (1 << 2);142142+ writel(val, timer_base + TIMER0_CTL_REG);143143+144144+ /* set mode to auto reload */145145+ val = readl(timer_base + TIMER0_CTL_REG);146146+ writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG);147147+148148+ ret = setup_irq(irq, &sunxi_timer_irq);149149+ if (ret)150150+ pr_warn("failed to setup irq %d\n", irq);151151+152152+ /* Enable timer0 interrupt */153153+ val = readl(timer_base + TIMER_CTL_REG);154154+ writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG);155155+156156+ sunxi_clockevent.mult = div_sc(rate / TIMER_SCAL,157157+ NSEC_PER_SEC,158158+ sunxi_clockevent.shift);159159+ sunxi_clockevent.max_delta_ns = clockevent_delta2ns(0xff,160160+ &sunxi_clockevent);161161+ sunxi_clockevent.min_delta_ns = clockevent_delta2ns(0x1,162162+ &sunxi_clockevent);163163+ sunxi_clockevent.cpumask = cpumask_of(0);164164+165165+ clockevents_register_device(&sunxi_clockevent);166166+}167167+168168+struct sys_timer sunxi_timer = {169169+ .init = sunxi_timer_init,170170+};
+24
include/linux/sunxi_timer.h
···11+/*22+ * Copyright 2012 Maxime Ripard33+ *44+ * Maxime Ripard <maxime.ripard@free-electrons.com>55+ *66+ * This program is free software; you can redistribute it and/or modify77+ * it under the terms of the GNU General Public License as published by88+ * the Free Software Foundation; either version 2 of the License, or99+ * (at your option) any later version.1010+ *1111+ * This program is distributed in the hope that it will be useful,1212+ * but WITHOUT ANY WARRANTY; without even the implied warranty of1313+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1414+ * GNU General Public License for more details.1515+ */1616+1717+#ifndef __SUNXI_TIMER_H1818+#define __SUNXI_TIMER_H1919+2020+#include <asm/mach/time.h>2121+2222+extern struct sys_timer sunxi_timer;2323+2424+#endif