0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include <linux/clk.h>
0015 #include <linux/clockchips.h>
0016 #include <linux/interrupt.h>
0017 #include <linux/irq.h>
0018 #include <linux/irqreturn.h>
0019 #include <linux/sched_clock.h>
0020 #include <linux/of.h>
0021 #include <linux/of_address.h>
0022 #include <linux/of_irq.h>
0023
0024 #include "timer-of.h"
0025
0026 #define TIMER_IRQ_EN_REG 0x00
0027 #define TIMER_IRQ_EN(val) BIT(val)
0028 #define TIMER_IRQ_ST_REG 0x04
0029 #define TIMER_CTL_REG(val) (0x10 * val + 0x10)
0030 #define TIMER_CTL_ENABLE BIT(0)
0031 #define TIMER_CTL_RELOAD BIT(1)
0032 #define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2)
0033 #define TIMER_CTL_CLK_SRC_OSC24M (1)
0034 #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4)
0035 #define TIMER_CTL_ONESHOT BIT(7)
0036 #define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14)
0037 #define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18)
0038
0039 #define TIMER_SYNC_TICKS 3
0040
0041
0042
0043
0044
0045
0046
0047 static void sun4i_clkevt_sync(void __iomem *base)
0048 {
0049 u32 old = readl(base + TIMER_CNTVAL_REG(1));
0050
0051 while ((old - readl(base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS)
0052 cpu_relax();
0053 }
0054
0055 static void sun4i_clkevt_time_stop(void __iomem *base, u8 timer)
0056 {
0057 u32 val = readl(base + TIMER_CTL_REG(timer));
0058 writel(val & ~TIMER_CTL_ENABLE, base + TIMER_CTL_REG(timer));
0059 sun4i_clkevt_sync(base);
0060 }
0061
0062 static void sun4i_clkevt_time_setup(void __iomem *base, u8 timer,
0063 unsigned long delay)
0064 {
0065 writel(delay, base + TIMER_INTVAL_REG(timer));
0066 }
0067
0068 static void sun4i_clkevt_time_start(void __iomem *base, u8 timer,
0069 bool periodic)
0070 {
0071 u32 val = readl(base + TIMER_CTL_REG(timer));
0072
0073 if (periodic)
0074 val &= ~TIMER_CTL_ONESHOT;
0075 else
0076 val |= TIMER_CTL_ONESHOT;
0077
0078 writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
0079 base + TIMER_CTL_REG(timer));
0080 }
0081
0082 static int sun4i_clkevt_shutdown(struct clock_event_device *evt)
0083 {
0084 struct timer_of *to = to_timer_of(evt);
0085
0086 sun4i_clkevt_time_stop(timer_of_base(to), 0);
0087
0088 return 0;
0089 }
0090
0091 static int sun4i_clkevt_set_oneshot(struct clock_event_device *evt)
0092 {
0093 struct timer_of *to = to_timer_of(evt);
0094
0095 sun4i_clkevt_time_stop(timer_of_base(to), 0);
0096 sun4i_clkevt_time_start(timer_of_base(to), 0, false);
0097
0098 return 0;
0099 }
0100
0101 static int sun4i_clkevt_set_periodic(struct clock_event_device *evt)
0102 {
0103 struct timer_of *to = to_timer_of(evt);
0104
0105 sun4i_clkevt_time_stop(timer_of_base(to), 0);
0106 sun4i_clkevt_time_setup(timer_of_base(to), 0, timer_of_period(to));
0107 sun4i_clkevt_time_start(timer_of_base(to), 0, true);
0108
0109 return 0;
0110 }
0111
0112 static int sun4i_clkevt_next_event(unsigned long evt,
0113 struct clock_event_device *clkevt)
0114 {
0115 struct timer_of *to = to_timer_of(clkevt);
0116
0117 sun4i_clkevt_time_stop(timer_of_base(to), 0);
0118 sun4i_clkevt_time_setup(timer_of_base(to), 0, evt - TIMER_SYNC_TICKS);
0119 sun4i_clkevt_time_start(timer_of_base(to), 0, false);
0120
0121 return 0;
0122 }
0123
0124 static void sun4i_timer_clear_interrupt(void __iomem *base)
0125 {
0126 writel(TIMER_IRQ_EN(0), base + TIMER_IRQ_ST_REG);
0127 }
0128
0129 static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
0130 {
0131 struct clock_event_device *evt = dev_id;
0132 struct timer_of *to = to_timer_of(evt);
0133
0134 sun4i_timer_clear_interrupt(timer_of_base(to));
0135 evt->event_handler(evt);
0136
0137 return IRQ_HANDLED;
0138 }
0139
0140 static struct timer_of to = {
0141 .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
0142
0143 .clkevt = {
0144 .name = "sun4i_tick",
0145 .rating = 350,
0146 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
0147 .set_state_shutdown = sun4i_clkevt_shutdown,
0148 .set_state_periodic = sun4i_clkevt_set_periodic,
0149 .set_state_oneshot = sun4i_clkevt_set_oneshot,
0150 .tick_resume = sun4i_clkevt_shutdown,
0151 .set_next_event = sun4i_clkevt_next_event,
0152 .cpumask = cpu_possible_mask,
0153 },
0154
0155 .of_irq = {
0156 .handler = sun4i_timer_interrupt,
0157 .flags = IRQF_TIMER | IRQF_IRQPOLL,
0158 },
0159 };
0160
0161 static u64 notrace sun4i_timer_sched_read(void)
0162 {
0163 return ~readl(timer_of_base(&to) + TIMER_CNTVAL_REG(1));
0164 }
0165
0166 static int __init sun4i_timer_init(struct device_node *node)
0167 {
0168 int ret;
0169 u32 val;
0170
0171 ret = timer_of_init(node, &to);
0172 if (ret)
0173 return ret;
0174
0175 writel(~0, timer_of_base(&to) + TIMER_INTVAL_REG(1));
0176 writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
0177 TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
0178 timer_of_base(&to) + TIMER_CTL_REG(1));
0179
0180
0181
0182
0183
0184 if (of_machine_is_compatible("allwinner,sun4i-a10") ||
0185 of_machine_is_compatible("allwinner,sun5i-a13") ||
0186 of_machine_is_compatible("allwinner,sun5i-a10s") ||
0187 of_machine_is_compatible("allwinner,suniv-f1c100s"))
0188 sched_clock_register(sun4i_timer_sched_read, 32,
0189 timer_of_rate(&to));
0190
0191 ret = clocksource_mmio_init(timer_of_base(&to) + TIMER_CNTVAL_REG(1),
0192 node->name, timer_of_rate(&to), 350, 32,
0193 clocksource_mmio_readl_down);
0194 if (ret) {
0195 pr_err("Failed to register clocksource\n");
0196 return ret;
0197 }
0198
0199 writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
0200 timer_of_base(&to) + TIMER_CTL_REG(0));
0201
0202
0203 sun4i_clkevt_time_stop(timer_of_base(&to), 0);
0204
0205
0206 sun4i_timer_clear_interrupt(timer_of_base(&to));
0207
0208 clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
0209 TIMER_SYNC_TICKS, 0xffffffff);
0210
0211
0212 val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG);
0213 writel(val | TIMER_IRQ_EN(0), timer_of_base(&to) + TIMER_IRQ_EN_REG);
0214
0215 return ret;
0216 }
0217 TIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
0218 sun4i_timer_init);
0219 TIMER_OF_DECLARE(sun8i_a23, "allwinner,sun8i-a23-timer",
0220 sun4i_timer_init);
0221 TIMER_OF_DECLARE(sun8i_v3s, "allwinner,sun8i-v3s-timer",
0222 sun4i_timer_init);
0223 TIMER_OF_DECLARE(suniv, "allwinner,suniv-f1c100s-timer",
0224 sun4i_timer_init);