diff options
| author | Linus Walleij <linus.walleij@linaro.org> | 2017-03-24 17:32:35 -0400 |
|---|---|---|
| committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2017-04-07 10:23:08 -0400 |
| commit | 28e71e2fe8fe6cdbd1bdc61601ea50d6423d3cf0 (patch) | |
| tree | 745688ebc21f151d9a7bcbce406d77bd22f3dd3c /drivers/clocksource | |
| parent | f5bf0ee4ebf779e256bb710f638b4452d94e97fb (diff) | |
clocksource/drivers/fttmr010: Refactor to handle clock
The plain Faraday FTTMR010 timer needs a clock to figure out its
tick rate, and the gemini reads it directly from the system
controller set-up. Split the init function and add two paths for
the two compatible-strings. We only support clocking using PCLK
because of lack of documentation on how EXTCLK works.
The Gemini still works like before, but we can also support a
generic, clock-based version.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource')
| -rw-r--r-- | drivers/clocksource/timer-fttmr010.c | 119 |
1 files changed, 73 insertions, 46 deletions
diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c index e37ec3d69a7e..b4a6f1e4bc54 100644 --- a/drivers/clocksource/timer-fttmr010.c +++ b/drivers/clocksource/timer-fttmr010.c | |||
| @@ -16,17 +16,7 @@ | |||
| 16 | #include <linux/clockchips.h> | 16 | #include <linux/clockchips.h> |
| 17 | #include <linux/clocksource.h> | 17 | #include <linux/clocksource.h> |
| 18 | #include <linux/sched_clock.h> | 18 | #include <linux/sched_clock.h> |
| 19 | 19 | #include <linux/clk.h> | |
| 20 | /* | ||
| 21 | * Relevant registers in the global syscon | ||
| 22 | */ | ||
| 23 | #define GLOBAL_STATUS 0x04 | ||
| 24 | #define CPU_AHB_RATIO_MASK (0x3 << 18) | ||
| 25 | #define CPU_AHB_1_1 (0x0 << 18) | ||
| 26 | #define CPU_AHB_3_2 (0x1 << 18) | ||
| 27 | #define CPU_AHB_24_13 (0x2 << 18) | ||
| 28 | #define CPU_AHB_2_1 (0x3 << 18) | ||
| 29 | #define REG_TO_AHB_SPEED(reg) ((((reg) >> 15) & 0x7) * 10 + 130) | ||
| 30 | 20 | ||
| 31 | /* | 21 | /* |
| 32 | * Register definitions for the timers | 22 | * Register definitions for the timers |
| @@ -189,23 +179,9 @@ static struct irqaction fttmr010_timer_irq = { | |||
| 189 | .handler = fttmr010_timer_interrupt, | 179 | .handler = fttmr010_timer_interrupt, |
| 190 | }; | 180 | }; |
| 191 | 181 | ||
| 192 | static int __init gemini_timer_of_init(struct device_node *np) | 182 | static int __init fttmr010_timer_common_init(struct device_node *np) |
| 193 | { | 183 | { |
| 194 | static struct regmap *map; | ||
| 195 | int irq; | 184 | int irq; |
| 196 | int ret; | ||
| 197 | u32 val; | ||
| 198 | |||
| 199 | map = syscon_regmap_lookup_by_phandle(np, "syscon"); | ||
| 200 | if (IS_ERR(map)) { | ||
| 201 | pr_err("Can't get regmap for syscon handle"); | ||
| 202 | return -ENODEV; | ||
| 203 | } | ||
| 204 | ret = regmap_read(map, GLOBAL_STATUS, &val); | ||
| 205 | if (ret) { | ||
| 206 | pr_err("Can't read syscon status register"); | ||
| 207 | return -ENXIO; | ||
| 208 | } | ||
| 209 | 185 | ||
| 210 | base = of_iomap(np, 0); | 186 | base = of_iomap(np, 0); |
| 211 | if (!base) { | 187 | if (!base) { |
| @@ -219,26 +195,6 @@ static int __init gemini_timer_of_init(struct device_node *np) | |||
| 219 | return -EINVAL; | 195 | return -EINVAL; |
| 220 | } | 196 | } |
| 221 | 197 | ||
| 222 | tick_rate = REG_TO_AHB_SPEED(val) * 1000000; | ||
| 223 | printk(KERN_INFO "Bus: %dMHz", tick_rate / 1000000); | ||
| 224 | |||
| 225 | tick_rate /= 6; /* APB bus run AHB*(1/6) */ | ||
| 226 | |||
| 227 | switch (val & CPU_AHB_RATIO_MASK) { | ||
| 228 | case CPU_AHB_1_1: | ||
| 229 | printk(KERN_CONT "(1/1)\n"); | ||
| 230 | break; | ||
| 231 | case CPU_AHB_3_2: | ||
| 232 | printk(KERN_CONT "(3/2)\n"); | ||
| 233 | break; | ||
| 234 | case CPU_AHB_24_13: | ||
| 235 | printk(KERN_CONT "(24/13)\n"); | ||
| 236 | break; | ||
| 237 | case CPU_AHB_2_1: | ||
| 238 | printk(KERN_CONT "(2/1)\n"); | ||
| 239 | break; | ||
| 240 | } | ||
| 241 | |||
| 242 | /* | 198 | /* |
| 243 | * Reset the interrupt mask and status | 199 | * Reset the interrupt mask and status |
| 244 | */ | 200 | */ |
| @@ -273,4 +229,75 @@ static int __init gemini_timer_of_init(struct device_node *np) | |||
| 273 | 229 | ||
| 274 | return 0; | 230 | return 0; |
| 275 | } | 231 | } |
| 232 | |||
| 233 | static int __init fttmr010_timer_of_init(struct device_node *np) | ||
| 234 | { | ||
| 235 | /* | ||
| 236 | * These implementations require a clock reference. | ||
| 237 | * FIXME: we currently only support clocking using PCLK | ||
| 238 | * and using EXTCLK is not supported in the driver. | ||
| 239 | */ | ||
| 240 | struct clk *clk; | ||
| 241 | |||
| 242 | clk = of_clk_get_by_name(np, "PCLK"); | ||
| 243 | if (IS_ERR(clk)) { | ||
| 244 | pr_err("could not get PCLK"); | ||
| 245 | return PTR_ERR(clk); | ||
| 246 | } | ||
| 247 | tick_rate = clk_get_rate(clk); | ||
| 248 | |||
| 249 | return fttmr010_timer_common_init(np); | ||
| 250 | } | ||
| 251 | CLOCKSOURCE_OF_DECLARE(fttmr010, "faraday,fttmr010", fttmr010_timer_of_init); | ||
| 252 | |||
| 253 | /* | ||
| 254 | * Gemini-specific: relevant registers in the global syscon | ||
| 255 | */ | ||
| 256 | #define GLOBAL_STATUS 0x04 | ||
| 257 | #define CPU_AHB_RATIO_MASK (0x3 << 18) | ||
| 258 | #define CPU_AHB_1_1 (0x0 << 18) | ||
| 259 | #define CPU_AHB_3_2 (0x1 << 18) | ||
| 260 | #define CPU_AHB_24_13 (0x2 << 18) | ||
| 261 | #define CPU_AHB_2_1 (0x3 << 18) | ||
| 262 | #define REG_TO_AHB_SPEED(reg) ((((reg) >> 15) & 0x7) * 10 + 130) | ||
| 263 | |||
| 264 | static int __init gemini_timer_of_init(struct device_node *np) | ||
| 265 | { | ||
| 266 | static struct regmap *map; | ||
| 267 | int ret; | ||
| 268 | u32 val; | ||
| 269 | |||
| 270 | map = syscon_regmap_lookup_by_phandle(np, "syscon"); | ||
| 271 | if (IS_ERR(map)) { | ||
| 272 | pr_err("Can't get regmap for syscon handle\n"); | ||
| 273 | return -ENODEV; | ||
| 274 | } | ||
| 275 | ret = regmap_read(map, GLOBAL_STATUS, &val); | ||
| 276 | if (ret) { | ||
| 277 | pr_err("Can't read syscon status register\n"); | ||
| 278 | return -ENXIO; | ||
| 279 | } | ||
| 280 | |||
| 281 | tick_rate = REG_TO_AHB_SPEED(val) * 1000000; | ||
| 282 | pr_info("Bus: %dMHz ", tick_rate / 1000000); | ||
| 283 | |||
| 284 | tick_rate /= 6; /* APB bus run AHB*(1/6) */ | ||
| 285 | |||
| 286 | switch (val & CPU_AHB_RATIO_MASK) { | ||
| 287 | case CPU_AHB_1_1: | ||
| 288 | pr_cont("(1/1)\n"); | ||
| 289 | break; | ||
| 290 | case CPU_AHB_3_2: | ||
| 291 | pr_cont("(3/2)\n"); | ||
| 292 | break; | ||
| 293 | case CPU_AHB_24_13: | ||
| 294 | pr_cont("(24/13)\n"); | ||
| 295 | break; | ||
| 296 | case CPU_AHB_2_1: | ||
| 297 | pr_cont("(2/1)\n"); | ||
| 298 | break; | ||
| 299 | } | ||
| 300 | |||
| 301 | return fttmr010_timer_common_init(np); | ||
| 302 | } | ||
| 276 | CLOCKSOURCE_OF_DECLARE(gemini, "cortina,gemini-timer", gemini_timer_of_init); | 303 | CLOCKSOURCE_OF_DECLARE(gemini, "cortina,gemini-timer", gemini_timer_of_init); |
