1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * MediaTek SoCs CPUX General Purpose Timer handling |
4 | * |
5 | * Based on timer-mediatek.c: |
6 | * Copyright (C) 2014 Matthias Brugger <matthias.bgg@gmail.com> |
7 | * |
8 | * Copyright (C) 2022 Collabora Ltd. |
9 | * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> |
10 | */ |
11 | |
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
13 | |
14 | #include <linux/clockchips.h> |
15 | #include <linux/clocksource.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/irqreturn.h> |
18 | #include <linux/sched_clock.h> |
19 | #include <linux/slab.h> |
20 | #include "timer-of.h" |
21 | |
22 | #define TIMER_SYNC_TICKS 3 |
23 | |
24 | /* cpux mcusys wrapper */ |
25 | #define CPUX_CON_REG 0x0 |
26 | #define CPUX_IDX_REG 0x4 |
27 | |
28 | /* cpux */ |
29 | #define CPUX_IDX_GLOBAL_CTRL 0x0 |
30 | #define CPUX_ENABLE BIT(0) |
31 | #define CPUX_CLK_DIV_MASK GENMASK(10, 8) |
32 | #define CPUX_CLK_DIV1 BIT(8) |
33 | #define CPUX_CLK_DIV2 BIT(9) |
34 | #define CPUX_CLK_DIV4 BIT(10) |
35 | #define CPUX_IDX_GLOBAL_IRQ 0x30 |
36 | |
37 | static u32 mtk_cpux_readl(u32 reg_idx, struct timer_of *to) |
38 | { |
39 | writel(val: reg_idx, addr: timer_of_base(to) + CPUX_IDX_REG); |
40 | return readl(addr: timer_of_base(to) + CPUX_CON_REG); |
41 | } |
42 | |
43 | static void mtk_cpux_writel(u32 val, u32 reg_idx, struct timer_of *to) |
44 | { |
45 | writel(val: reg_idx, addr: timer_of_base(to) + CPUX_IDX_REG); |
46 | writel(val, addr: timer_of_base(to) + CPUX_CON_REG); |
47 | } |
48 | |
49 | static void mtk_cpux_set_irq(struct timer_of *to, bool enable) |
50 | { |
51 | const unsigned long *irq_mask = cpumask_bits(cpu_possible_mask); |
52 | u32 val; |
53 | |
54 | val = mtk_cpux_readl(CPUX_IDX_GLOBAL_IRQ, to); |
55 | |
56 | if (enable) |
57 | val |= *irq_mask; |
58 | else |
59 | val &= ~(*irq_mask); |
60 | |
61 | mtk_cpux_writel(val, CPUX_IDX_GLOBAL_IRQ, to); |
62 | } |
63 | |
64 | static int mtk_cpux_clkevt_shutdown(struct clock_event_device *clkevt) |
65 | { |
66 | /* Clear any irq */ |
67 | mtk_cpux_set_irq(to: to_timer_of(clkevt), enable: false); |
68 | |
69 | /* |
70 | * Disabling CPUXGPT timer will crash the platform, especially |
71 | * if Trusted Firmware is using it (usually, for sleep states), |
72 | * so we only mask the IRQ and call it a day. |
73 | */ |
74 | return 0; |
75 | } |
76 | |
77 | static int mtk_cpux_clkevt_resume(struct clock_event_device *clkevt) |
78 | { |
79 | mtk_cpux_set_irq(to: to_timer_of(clkevt), enable: true); |
80 | return 0; |
81 | } |
82 | |
83 | static struct timer_of to = { |
84 | /* |
85 | * There are per-cpu interrupts for the CPUX General Purpose Timer |
86 | * but since this timer feeds the AArch64 System Timer we can rely |
87 | * on the CPU timer PPIs as well, so we don't declare TIMER_OF_IRQ. |
88 | */ |
89 | .flags = TIMER_OF_BASE | TIMER_OF_CLOCK, |
90 | |
91 | .clkevt = { |
92 | .name = "mtk-cpuxgpt" , |
93 | .cpumask = cpu_possible_mask, |
94 | .rating = 10, |
95 | .set_state_shutdown = mtk_cpux_clkevt_shutdown, |
96 | .tick_resume = mtk_cpux_clkevt_resume, |
97 | }, |
98 | }; |
99 | |
100 | static int __init mtk_cpux_init(struct device_node *node) |
101 | { |
102 | u32 freq, val; |
103 | int ret; |
104 | |
105 | /* If this fails, bad things are about to happen... */ |
106 | ret = timer_of_init(np: node, to: &to); |
107 | if (ret) { |
108 | WARN(1, "Cannot start CPUX timers.\n" ); |
109 | return ret; |
110 | } |
111 | |
112 | /* |
113 | * Check if we're given a clock with the right frequency for this |
114 | * timer, otherwise warn but keep going with the setup anyway, as |
115 | * that makes it possible to still boot the kernel, even though |
116 | * it may not work correctly (random lockups, etc). |
117 | * The reason behind this is that having an early UART may not be |
118 | * possible for everyone and this gives a chance to retrieve kmsg |
119 | * for eventual debugging even on consumer devices. |
120 | */ |
121 | freq = timer_of_rate(to: &to); |
122 | if (freq > 13000000) |
123 | WARN(1, "Requested unsupported timer frequency %u\n" , freq); |
124 | |
125 | /* Clock input is 26MHz, set DIV2 to achieve 13MHz clock */ |
126 | val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, to: &to); |
127 | val &= ~CPUX_CLK_DIV_MASK; |
128 | val |= CPUX_CLK_DIV2; |
129 | mtk_cpux_writel(val, CPUX_IDX_GLOBAL_CTRL, to: &to); |
130 | |
131 | /* Enable all CPUXGPT timers */ |
132 | val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, to: &to); |
133 | mtk_cpux_writel(val: val | CPUX_ENABLE, CPUX_IDX_GLOBAL_CTRL, to: &to); |
134 | |
135 | clockevents_config_and_register(dev: &to.clkevt, freq: timer_of_rate(to: &to), |
136 | TIMER_SYNC_TICKS, max_delta: 0xffffffff); |
137 | |
138 | return 0; |
139 | } |
140 | TIMER_OF_DECLARE(mtk_mt6795, "mediatek,mt6795-systimer" , mtk_cpux_init); |
141 | |