Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 1 | /* |
| 2 | * MOXA ART SoCs timer handling. |
| 3 | * |
| 4 | * Copyright (C) 2013 Jonas Jensen |
| 5 | * |
| 6 | * Jonas Jensen <jonas.jensen@gmail.com> |
| 7 | * |
| 8 | * This file is licensed under the terms of the GNU General Public |
| 9 | * License version 2. This program is licensed "as is" without any |
| 10 | * warranty of any kind, whether express or implied. |
| 11 | */ |
| 12 | |
| 13 | #include <linux/clk.h> |
| 14 | #include <linux/clockchips.h> |
| 15 | #include <linux/interrupt.h> |
| 16 | #include <linux/irq.h> |
| 17 | #include <linux/irqreturn.h> |
| 18 | #include <linux/of.h> |
| 19 | #include <linux/of_address.h> |
| 20 | #include <linux/of_irq.h> |
| 21 | #include <linux/io.h> |
| 22 | #include <linux/clocksource.h> |
Jonas Jensen | adf157e | 2013-07-26 16:03:38 +0200 | [diff] [blame] | 23 | #include <linux/bitops.h> |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 24 | #include <linux/slab.h> |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 25 | |
| 26 | #define TIMER1_BASE 0x00 |
| 27 | #define TIMER2_BASE 0x10 |
| 28 | #define TIMER3_BASE 0x20 |
| 29 | |
| 30 | #define REG_COUNT 0x0 /* writable */ |
| 31 | #define REG_LOAD 0x4 |
| 32 | #define REG_MATCH1 0x8 |
| 33 | #define REG_MATCH2 0xC |
| 34 | |
| 35 | #define TIMER_CR 0x30 |
| 36 | #define TIMER_INTR_STATE 0x34 |
| 37 | #define TIMER_INTR_MASK 0x38 |
| 38 | |
| 39 | /* |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 40 | * Moxart TIMER_CR flags: |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 41 | * |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 42 | * MOXART_CR_*_CLOCK 0: PCLK, 1: EXT1CLK |
| 43 | * MOXART_CR_*_INT overflow interrupt enable bit |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 44 | */ |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 45 | #define MOXART_CR_1_ENABLE BIT(0) |
| 46 | #define MOXART_CR_1_CLOCK BIT(1) |
| 47 | #define MOXART_CR_1_INT BIT(2) |
| 48 | #define MOXART_CR_2_ENABLE BIT(3) |
| 49 | #define MOXART_CR_2_CLOCK BIT(4) |
| 50 | #define MOXART_CR_2_INT BIT(5) |
| 51 | #define MOXART_CR_3_ENABLE BIT(6) |
| 52 | #define MOXART_CR_3_CLOCK BIT(7) |
| 53 | #define MOXART_CR_3_INT BIT(8) |
| 54 | #define MOXART_CR_COUNT_UP BIT(9) |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 55 | |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 56 | #define MOXART_TIMER1_ENABLE (MOXART_CR_2_ENABLE | MOXART_CR_1_ENABLE) |
| 57 | #define MOXART_TIMER1_DISABLE (MOXART_CR_2_ENABLE) |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 58 | |
Joel Stanley | ba36d53 | 2016-07-21 23:13:53 +0930 | [diff] [blame] | 59 | /* |
| 60 | * The ASpeed variant of the IP block has a different layout |
| 61 | * for the control register |
| 62 | */ |
| 63 | #define ASPEED_CR_1_ENABLE BIT(0) |
| 64 | #define ASPEED_CR_1_CLOCK BIT(1) |
| 65 | #define ASPEED_CR_1_INT BIT(2) |
| 66 | #define ASPEED_CR_2_ENABLE BIT(4) |
| 67 | #define ASPEED_CR_2_CLOCK BIT(5) |
| 68 | #define ASPEED_CR_2_INT BIT(6) |
| 69 | #define ASPEED_CR_3_ENABLE BIT(8) |
| 70 | #define ASPEED_CR_3_CLOCK BIT(9) |
| 71 | #define ASPEED_CR_3_INT BIT(10) |
| 72 | |
| 73 | #define ASPEED_TIMER1_ENABLE (ASPEED_CR_2_ENABLE | ASPEED_CR_1_ENABLE) |
| 74 | #define ASPEED_TIMER1_DISABLE (ASPEED_CR_2_ENABLE) |
| 75 | |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 76 | struct moxart_timer { |
| 77 | void __iomem *base; |
| 78 | unsigned int t1_disable_val; |
| 79 | unsigned int t1_enable_val; |
| 80 | unsigned int count_per_tick; |
| 81 | struct clock_event_device clkevt; |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 82 | }; |
| 83 | |
| 84 | static inline struct moxart_timer *to_moxart(struct clock_event_device *evt) |
| 85 | { |
| 86 | return container_of(evt, struct moxart_timer, clkevt); |
| 87 | } |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 88 | |
Joel Stanley | 7016474 | 2016-07-21 23:13:51 +0930 | [diff] [blame] | 89 | static inline void moxart_disable(struct clock_event_device *evt) |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 90 | { |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 91 | struct moxart_timer *timer = to_moxart(evt); |
| 92 | |
| 93 | writel(timer->t1_disable_val, timer->base + TIMER_CR); |
Joel Stanley | 7016474 | 2016-07-21 23:13:51 +0930 | [diff] [blame] | 94 | } |
| 95 | |
| 96 | static inline void moxart_enable(struct clock_event_device *evt) |
| 97 | { |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 98 | struct moxart_timer *timer = to_moxart(evt); |
| 99 | |
| 100 | writel(timer->t1_enable_val, timer->base + TIMER_CR); |
Joel Stanley | 7016474 | 2016-07-21 23:13:51 +0930 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | static int moxart_shutdown(struct clock_event_device *evt) |
| 104 | { |
| 105 | moxart_disable(evt); |
Viresh Kumar | 37ae247 | 2015-06-18 16:24:26 +0530 | [diff] [blame] | 106 | return 0; |
| 107 | } |
| 108 | |
| 109 | static int moxart_set_oneshot(struct clock_event_device *evt) |
| 110 | { |
Joel Stanley | 7016474 | 2016-07-21 23:13:51 +0930 | [diff] [blame] | 111 | moxart_disable(evt); |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 112 | writel(~0, to_moxart(evt)->base + TIMER1_BASE + REG_LOAD); |
Viresh Kumar | 37ae247 | 2015-06-18 16:24:26 +0530 | [diff] [blame] | 113 | return 0; |
| 114 | } |
| 115 | |
| 116 | static int moxart_set_periodic(struct clock_event_device *evt) |
| 117 | { |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 118 | struct moxart_timer *timer = to_moxart(evt); |
| 119 | |
| 120 | moxart_disable(evt); |
| 121 | writel(timer->count_per_tick, timer->base + TIMER1_BASE + REG_LOAD); |
| 122 | writel(0, timer->base + TIMER1_BASE + REG_MATCH1); |
Joel Stanley | 7016474 | 2016-07-21 23:13:51 +0930 | [diff] [blame] | 123 | moxart_enable(evt); |
Viresh Kumar | 37ae247 | 2015-06-18 16:24:26 +0530 | [diff] [blame] | 124 | return 0; |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 125 | } |
| 126 | |
| 127 | static int moxart_clkevt_next_event(unsigned long cycles, |
Joel Stanley | 7016474 | 2016-07-21 23:13:51 +0930 | [diff] [blame] | 128 | struct clock_event_device *evt) |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 129 | { |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 130 | struct moxart_timer *timer = to_moxart(evt); |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 131 | u32 u; |
| 132 | |
Joel Stanley | 7016474 | 2016-07-21 23:13:51 +0930 | [diff] [blame] | 133 | moxart_disable(evt); |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 134 | |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 135 | u = readl(timer->base + TIMER1_BASE + REG_COUNT) - cycles; |
| 136 | writel(u, timer->base + TIMER1_BASE + REG_MATCH1); |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 137 | |
Joel Stanley | 7016474 | 2016-07-21 23:13:51 +0930 | [diff] [blame] | 138 | moxart_enable(evt); |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 139 | |
| 140 | return 0; |
| 141 | } |
| 142 | |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 143 | static irqreturn_t moxart_timer_interrupt(int irq, void *dev_id) |
| 144 | { |
| 145 | struct clock_event_device *evt = dev_id; |
| 146 | evt->event_handler(evt); |
| 147 | return IRQ_HANDLED; |
| 148 | } |
| 149 | |
Daniel Lezcano | b7357e6 | 2016-06-06 17:57:38 +0200 | [diff] [blame] | 150 | static int __init moxart_timer_init(struct device_node *node) |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 151 | { |
| 152 | int ret, irq; |
| 153 | unsigned long pclk; |
| 154 | struct clk *clk; |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 155 | struct moxart_timer *timer; |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 156 | |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 157 | timer = kzalloc(sizeof(*timer), GFP_KERNEL); |
| 158 | if (!timer) |
| 159 | return -ENOMEM; |
| 160 | |
| 161 | timer->base = of_iomap(node, 0); |
| 162 | if (!timer->base) { |
Daniel Lezcano | b7357e6 | 2016-06-06 17:57:38 +0200 | [diff] [blame] | 163 | pr_err("%s: of_iomap failed\n", node->full_name); |
| 164 | return -ENXIO; |
| 165 | } |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 166 | |
| 167 | irq = irq_of_parse_and_map(node, 0); |
Daniel Lezcano | b7357e6 | 2016-06-06 17:57:38 +0200 | [diff] [blame] | 168 | if (irq <= 0) { |
| 169 | pr_err("%s: irq_of_parse_and_map failed\n", node->full_name); |
| 170 | return -EINVAL; |
| 171 | } |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 172 | |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 173 | clk = of_clk_get(node, 0); |
Daniel Lezcano | b7357e6 | 2016-06-06 17:57:38 +0200 | [diff] [blame] | 174 | if (IS_ERR(clk)) { |
| 175 | pr_err("%s: of_clk_get failed\n", node->full_name); |
| 176 | return PTR_ERR(clk); |
| 177 | } |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 178 | |
| 179 | pclk = clk_get_rate(clk); |
| 180 | |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 181 | if (of_device_is_compatible(node, "moxa,moxart-timer")) { |
| 182 | timer->t1_enable_val = MOXART_TIMER1_ENABLE; |
| 183 | timer->t1_disable_val = MOXART_TIMER1_DISABLE; |
Joel Stanley | ba36d53 | 2016-07-21 23:13:53 +0930 | [diff] [blame] | 184 | } else if (of_device_is_compatible(node, "aspeed,ast2400-timer")) { |
| 185 | timer->t1_enable_val = ASPEED_TIMER1_ENABLE; |
| 186 | timer->t1_disable_val = ASPEED_TIMER1_DISABLE; |
Daniel Lezcano | e2a2d38 | 2016-09-08 14:25:40 +0200 | [diff] [blame] | 187 | } else { |
| 188 | pr_err("%s: unknown platform\n", node->full_name); |
| 189 | return -EINVAL; |
| 190 | } |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 191 | |
| 192 | timer->count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ); |
| 193 | |
| 194 | timer->clkevt.name = node->name; |
| 195 | timer->clkevt.rating = 200; |
| 196 | timer->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | |
| 197 | CLOCK_EVT_FEAT_ONESHOT; |
| 198 | timer->clkevt.set_state_shutdown = moxart_shutdown; |
| 199 | timer->clkevt.set_state_periodic = moxart_set_periodic; |
| 200 | timer->clkevt.set_state_oneshot = moxart_set_oneshot; |
| 201 | timer->clkevt.tick_resume = moxart_set_oneshot; |
| 202 | timer->clkevt.set_next_event = moxart_clkevt_next_event; |
| 203 | timer->clkevt.cpumask = cpumask_of(0); |
| 204 | timer->clkevt.irq = irq; |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 205 | |
| 206 | ret = clocksource_mmio_init(timer->base + TIMER2_BASE + REG_COUNT, |
Daniel Lezcano | b7357e6 | 2016-06-06 17:57:38 +0200 | [diff] [blame] | 207 | "moxart_timer", pclk, 200, 32, |
| 208 | clocksource_mmio_readl_down); |
| 209 | if (ret) { |
| 210 | pr_err("%s: clocksource_mmio_init failed\n", node->full_name); |
| 211 | return ret; |
| 212 | } |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 213 | |
Daniel Lezcano | cf1e929 | 2016-09-08 14:17:00 +0200 | [diff] [blame] | 214 | ret = request_irq(irq, moxart_timer_interrupt, IRQF_TIMER, |
| 215 | node->name, &timer->clkevt); |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 216 | if (ret) { |
| 217 | pr_err("%s: setup_irq failed\n", node->full_name); |
| 218 | return ret; |
| 219 | } |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 220 | |
Joel Stanley | ba36d53 | 2016-07-21 23:13:53 +0930 | [diff] [blame] | 221 | /* Clear match registers */ |
| 222 | writel(0, timer->base + TIMER1_BASE + REG_MATCH1); |
| 223 | writel(0, timer->base + TIMER1_BASE + REG_MATCH2); |
| 224 | writel(0, timer->base + TIMER2_BASE + REG_MATCH1); |
| 225 | writel(0, timer->base + TIMER2_BASE + REG_MATCH2); |
| 226 | |
| 227 | /* |
| 228 | * Start timer 2 rolling as our main wall clock source, keep timer 1 |
| 229 | * disabled |
| 230 | */ |
| 231 | writel(0, timer->base + TIMER_CR); |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 232 | writel(~0, timer->base + TIMER2_BASE + REG_LOAD); |
| 233 | writel(timer->t1_disable_val, timer->base + TIMER_CR); |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 234 | |
| 235 | /* |
| 236 | * documentation is not publicly available: |
| 237 | * min_delta / max_delta obtained by trial-and-error, |
| 238 | * max_delta 0xfffffffe should be ok because count |
| 239 | * register size is u32 |
| 240 | */ |
Joel Stanley | 82fdd07 | 2016-07-21 23:13:52 +0930 | [diff] [blame] | 241 | clockevents_config_and_register(&timer->clkevt, pclk, 0x4, 0xfffffffe); |
Daniel Lezcano | b7357e6 | 2016-06-06 17:57:38 +0200 | [diff] [blame] | 242 | |
| 243 | return 0; |
Jonas Jensen | 07862c1 | 2013-07-17 10:04:57 +0200 | [diff] [blame] | 244 | } |
Daniel Lezcano | 177cf6e | 2016-06-07 00:27:44 +0200 | [diff] [blame] | 245 | CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init); |
Joel Stanley | ba36d53 | 2016-07-21 23:13:53 +0930 | [diff] [blame] | 246 | CLOCKSOURCE_OF_DECLARE(aspeed, "aspeed,ast2400-timer", moxart_timer_init); |