|
22 | 22 |
|
23 | 23 | #define TIMER_SYNC_TICKS (3) |
24 | 24 |
|
| 25 | +/* cpux mcusys wrapper */ |
| 26 | +#define CPUX_CON_REG 0x0 |
| 27 | +#define CPUX_IDX_REG 0x4 |
| 28 | + |
| 29 | +/* cpux */ |
| 30 | +#define CPUX_IDX_GLOBAL_CTRL 0x0 |
| 31 | + #define CPUX_ENABLE BIT(0) |
| 32 | + #define CPUX_CLK_DIV_MASK GENMASK(10, 8) |
| 33 | + #define CPUX_CLK_DIV1 BIT(8) |
| 34 | + #define CPUX_CLK_DIV2 BIT(9) |
| 35 | + #define CPUX_CLK_DIV4 BIT(10) |
| 36 | +#define CPUX_IDX_GLOBAL_IRQ 0x30 |
| 37 | + |
25 | 38 | /* gpt */ |
26 | 39 | #define GPT_IRQ_EN_REG 0x00 |
27 | 40 | #define GPT_IRQ_ENABLE(val) BIT((val) - 1) |
|
72 | 85 |
|
73 | 86 | static void __iomem *gpt_sched_reg __read_mostly; |
74 | 87 |
|
| 88 | +static u32 mtk_cpux_readl(u32 reg_idx, struct timer_of *to) |
| 89 | +{ |
| 90 | + writel(reg_idx, timer_of_base(to) + CPUX_IDX_REG); |
| 91 | + return readl(timer_of_base(to) + CPUX_CON_REG); |
| 92 | +} |
| 93 | + |
| 94 | +static void mtk_cpux_writel(u32 val, u32 reg_idx, struct timer_of *to) |
| 95 | +{ |
| 96 | + writel(reg_idx, timer_of_base(to) + CPUX_IDX_REG); |
| 97 | + writel(val, timer_of_base(to) + CPUX_CON_REG); |
| 98 | +} |
| 99 | + |
| 100 | +static void mtk_cpux_set_irq(struct timer_of *to, bool enable) |
| 101 | +{ |
| 102 | + const unsigned long *irq_mask = cpumask_bits(cpu_possible_mask); |
| 103 | + u32 val; |
| 104 | + |
| 105 | + val = mtk_cpux_readl(CPUX_IDX_GLOBAL_IRQ, to); |
| 106 | + |
| 107 | + if (enable) |
| 108 | + val |= *irq_mask; |
| 109 | + else |
| 110 | + val &= ~(*irq_mask); |
| 111 | + |
| 112 | + mtk_cpux_writel(val, CPUX_IDX_GLOBAL_IRQ, to); |
| 113 | +} |
| 114 | + |
| 115 | +static int mtk_cpux_clkevt_shutdown(struct clock_event_device *clkevt) |
| 116 | +{ |
| 117 | + /* Clear any irq */ |
| 118 | + mtk_cpux_set_irq(to_timer_of(clkevt), false); |
| 119 | + |
| 120 | + /* |
| 121 | + * Disabling CPUXGPT timer will crash the platform, especially |
| 122 | + * if Trusted Firmware is using it (usually, for sleep states), |
| 123 | + * so we only mask the IRQ and call it a day. |
| 124 | + */ |
| 125 | + return 0; |
| 126 | +} |
| 127 | + |
| 128 | +static int mtk_cpux_clkevt_resume(struct clock_event_device *clkevt) |
| 129 | +{ |
| 130 | + mtk_cpux_set_irq(to_timer_of(clkevt), true); |
| 131 | + return 0; |
| 132 | +} |
| 133 | + |
75 | 134 | static void mtk_syst_ack_irq(struct timer_of *to) |
76 | 135 | { |
77 | 136 | /* Clear and disable interrupt */ |
@@ -281,6 +340,60 @@ static struct timer_of to = { |
281 | 340 | }, |
282 | 341 | }; |
283 | 342 |
|
| 343 | +static int __init mtk_cpux_init(struct device_node *node) |
| 344 | +{ |
| 345 | + static struct timer_of to_cpux; |
| 346 | + u32 freq, val; |
| 347 | + int ret; |
| 348 | + |
| 349 | + /* |
| 350 | + * There are per-cpu interrupts for the CPUX General Purpose Timer |
| 351 | + * but since this timer feeds the AArch64 System Timer we can rely |
| 352 | + * on the CPU timer PPIs as well, so we don't declare TIMER_OF_IRQ. |
| 353 | + */ |
| 354 | + to_cpux.flags = TIMER_OF_BASE | TIMER_OF_CLOCK; |
| 355 | + to_cpux.clkevt.name = "mtk-cpuxgpt"; |
| 356 | + to_cpux.clkevt.rating = 10; |
| 357 | + to_cpux.clkevt.cpumask = cpu_possible_mask; |
| 358 | + to_cpux.clkevt.set_state_shutdown = mtk_cpux_clkevt_shutdown; |
| 359 | + to_cpux.clkevt.tick_resume = mtk_cpux_clkevt_resume; |
| 360 | + |
| 361 | + /* If this fails, bad things are about to happen... */ |
| 362 | + ret = timer_of_init(node, &to_cpux); |
| 363 | + if (ret) { |
| 364 | + WARN(1, "Cannot start CPUX timers.\n"); |
| 365 | + return ret; |
| 366 | + } |
| 367 | + |
| 368 | + /* |
| 369 | + * Check if we're given a clock with the right frequency for this |
| 370 | + * timer, otherwise warn but keep going with the setup anyway, as |
| 371 | + * that makes it possible to still boot the kernel, even though |
| 372 | + * it may not work correctly (random lockups, etc). |
| 373 | + * The reason behind this is that having an early UART may not be |
| 374 | + * possible for everyone and this gives a chance to retrieve kmsg |
| 375 | + * for eventual debugging even on consumer devices. |
| 376 | + */ |
| 377 | + freq = timer_of_rate(&to_cpux); |
| 378 | + if (freq > 13000000) |
| 379 | + WARN(1, "Requested unsupported timer frequency %u\n", freq); |
| 380 | + |
| 381 | + /* Clock input is 26MHz, set DIV2 to achieve 13MHz clock */ |
| 382 | + val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, &to_cpux); |
| 383 | + val &= ~CPUX_CLK_DIV_MASK; |
| 384 | + val |= CPUX_CLK_DIV2; |
| 385 | + mtk_cpux_writel(val, CPUX_IDX_GLOBAL_CTRL, &to_cpux); |
| 386 | + |
| 387 | + /* Enable all CPUXGPT timers */ |
| 388 | + val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, &to_cpux); |
| 389 | + mtk_cpux_writel(val | CPUX_ENABLE, CPUX_IDX_GLOBAL_CTRL, &to_cpux); |
| 390 | + |
| 391 | + clockevents_config_and_register(&to_cpux.clkevt, timer_of_rate(&to_cpux), |
| 392 | + TIMER_SYNC_TICKS, 0xffffffff); |
| 393 | + |
| 394 | + return 0; |
| 395 | +} |
| 396 | + |
284 | 397 | static int __init mtk_syst_init(struct device_node *node) |
285 | 398 | { |
286 | 399 | int ret; |
@@ -339,3 +452,4 @@ static int __init mtk_gpt_init(struct device_node *node) |
339 | 452 | } |
340 | 453 | TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init); |
341 | 454 | TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init); |
| 455 | +TIMER_OF_DECLARE(mtk_mt6795, "mediatek,mt6795-systimer", mtk_cpux_init); |
0 commit comments