Commit | Line | Data |
---|---|---|
2b91c28f HHW |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Copyright (c) 2020 MediaTek Inc. | |
4 | ||
5 | #include <linux/interrupt.h> | |
6 | #include <linux/mfd/mt6358/core.h> | |
7 | #include <linux/mfd/mt6358/registers.h> | |
8 | #include <linux/mfd/mt6397/core.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/of.h> | |
11 | #include <linux/of_device.h> | |
12 | #include <linux/of_irq.h> | |
13 | #include <linux/platform_device.h> | |
14 | #include <linux/regmap.h> | |
15 | ||
16 | static struct irq_top_t mt6358_ints[] = { | |
17 | MT6358_TOP_GEN(BUCK), | |
18 | MT6358_TOP_GEN(LDO), | |
19 | MT6358_TOP_GEN(PSC), | |
20 | MT6358_TOP_GEN(SCK), | |
21 | MT6358_TOP_GEN(BM), | |
22 | MT6358_TOP_GEN(HK), | |
23 | MT6358_TOP_GEN(AUD), | |
24 | MT6358_TOP_GEN(MISC), | |
25 | }; | |
26 | ||
27 | static void pmic_irq_enable(struct irq_data *data) | |
28 | { | |
29 | unsigned int hwirq = irqd_to_hwirq(data); | |
30 | struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); | |
31 | struct pmic_irq_data *irqd = chip->irq_data; | |
32 | ||
33 | irqd->enable_hwirq[hwirq] = true; | |
34 | } | |
35 | ||
36 | static void pmic_irq_disable(struct irq_data *data) | |
37 | { | |
38 | unsigned int hwirq = irqd_to_hwirq(data); | |
39 | struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); | |
40 | struct pmic_irq_data *irqd = chip->irq_data; | |
41 | ||
42 | irqd->enable_hwirq[hwirq] = false; | |
43 | } | |
44 | ||
45 | static void pmic_irq_lock(struct irq_data *data) | |
46 | { | |
47 | struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); | |
48 | ||
49 | mutex_lock(&chip->irqlock); | |
50 | } | |
51 | ||
52 | static void pmic_irq_sync_unlock(struct irq_data *data) | |
53 | { | |
54 | unsigned int i, top_gp, gp_offset, en_reg, int_regs, shift; | |
55 | struct mt6397_chip *chip = irq_data_get_irq_chip_data(data); | |
56 | struct pmic_irq_data *irqd = chip->irq_data; | |
57 | ||
58 | for (i = 0; i < irqd->num_pmic_irqs; i++) { | |
59 | if (irqd->enable_hwirq[i] == irqd->cache_hwirq[i]) | |
60 | continue; | |
61 | ||
62 | /* Find out the IRQ group */ | |
63 | top_gp = 0; | |
64 | while ((top_gp + 1) < irqd->num_top && | |
65 | i >= mt6358_ints[top_gp + 1].hwirq_base) | |
66 | top_gp++; | |
67 | ||
68 | /* Find the IRQ registers */ | |
69 | gp_offset = i - mt6358_ints[top_gp].hwirq_base; | |
70 | int_regs = gp_offset / MT6358_REG_WIDTH; | |
71 | shift = gp_offset % MT6358_REG_WIDTH; | |
72 | en_reg = mt6358_ints[top_gp].en_reg + | |
73 | (mt6358_ints[top_gp].en_reg_shift * int_regs); | |
74 | ||
75 | regmap_update_bits(chip->regmap, en_reg, BIT(shift), | |
76 | irqd->enable_hwirq[i] << shift); | |
77 | ||
78 | irqd->cache_hwirq[i] = irqd->enable_hwirq[i]; | |
79 | } | |
80 | mutex_unlock(&chip->irqlock); | |
81 | } | |
82 | ||
83 | static struct irq_chip mt6358_irq_chip = { | |
84 | .name = "mt6358-irq", | |
85 | .flags = IRQCHIP_SKIP_SET_WAKE, | |
86 | .irq_enable = pmic_irq_enable, | |
87 | .irq_disable = pmic_irq_disable, | |
88 | .irq_bus_lock = pmic_irq_lock, | |
89 | .irq_bus_sync_unlock = pmic_irq_sync_unlock, | |
90 | }; | |
91 | ||
92 | static void mt6358_irq_sp_handler(struct mt6397_chip *chip, | |
93 | unsigned int top_gp) | |
94 | { | |
95 | unsigned int irq_status, sta_reg, status; | |
96 | unsigned int hwirq, virq; | |
97 | int i, j, ret; | |
98 | ||
99 | for (i = 0; i < mt6358_ints[top_gp].num_int_regs; i++) { | |
100 | sta_reg = mt6358_ints[top_gp].sta_reg + | |
101 | mt6358_ints[top_gp].sta_reg_shift * i; | |
102 | ||
103 | ret = regmap_read(chip->regmap, sta_reg, &irq_status); | |
104 | if (ret) { | |
105 | dev_err(chip->dev, | |
106 | "Failed to read IRQ status, ret=%d\n", ret); | |
107 | return; | |
108 | } | |
109 | ||
110 | if (!irq_status) | |
111 | continue; | |
112 | ||
113 | status = irq_status; | |
114 | do { | |
115 | j = __ffs(status); | |
116 | ||
117 | hwirq = mt6358_ints[top_gp].hwirq_base + | |
118 | MT6358_REG_WIDTH * i + j; | |
119 | ||
120 | virq = irq_find_mapping(chip->irq_domain, hwirq); | |
121 | if (virq) | |
122 | handle_nested_irq(virq); | |
123 | ||
124 | status &= ~BIT(j); | |
125 | } while (status); | |
126 | ||
127 | regmap_write(chip->regmap, sta_reg, irq_status); | |
128 | } | |
129 | } | |
130 | ||
131 | static irqreturn_t mt6358_irq_handler(int irq, void *data) | |
132 | { | |
133 | struct mt6397_chip *chip = data; | |
134 | struct pmic_irq_data *mt6358_irq_data = chip->irq_data; | |
135 | unsigned int bit, i, top_irq_status = 0; | |
136 | int ret; | |
137 | ||
138 | ret = regmap_read(chip->regmap, | |
139 | mt6358_irq_data->top_int_status_reg, | |
140 | &top_irq_status); | |
141 | if (ret) { | |
142 | dev_err(chip->dev, | |
143 | "Failed to read status from the device, ret=%d\n", ret); | |
144 | return IRQ_NONE; | |
145 | } | |
146 | ||
147 | for (i = 0; i < mt6358_irq_data->num_top; i++) { | |
148 | bit = BIT(mt6358_ints[i].top_offset); | |
149 | if (top_irq_status & bit) { | |
150 | mt6358_irq_sp_handler(chip, i); | |
151 | top_irq_status &= ~bit; | |
152 | if (!top_irq_status) | |
153 | break; | |
154 | } | |
155 | } | |
156 | ||
157 | return IRQ_HANDLED; | |
158 | } | |
159 | ||
160 | static int pmic_irq_domain_map(struct irq_domain *d, unsigned int irq, | |
161 | irq_hw_number_t hw) | |
162 | { | |
163 | struct mt6397_chip *mt6397 = d->host_data; | |
164 | ||
165 | irq_set_chip_data(irq, mt6397); | |
166 | irq_set_chip_and_handler(irq, &mt6358_irq_chip, handle_level_irq); | |
167 | irq_set_nested_thread(irq, 1); | |
168 | irq_set_noprobe(irq); | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
173 | static const struct irq_domain_ops mt6358_irq_domain_ops = { | |
174 | .map = pmic_irq_domain_map, | |
175 | .xlate = irq_domain_xlate_twocell, | |
176 | }; | |
177 | ||
178 | int mt6358_irq_init(struct mt6397_chip *chip) | |
179 | { | |
180 | int i, j, ret; | |
181 | struct pmic_irq_data *irqd; | |
182 | ||
183 | irqd = devm_kzalloc(chip->dev, sizeof(*irqd), GFP_KERNEL); | |
184 | if (!irqd) | |
185 | return -ENOMEM; | |
186 | ||
187 | chip->irq_data = irqd; | |
188 | ||
189 | mutex_init(&chip->irqlock); | |
190 | irqd->top_int_status_reg = MT6358_TOP_INT_STATUS0; | |
191 | irqd->num_pmic_irqs = MT6358_IRQ_NR; | |
192 | irqd->num_top = ARRAY_SIZE(mt6358_ints); | |
193 | ||
194 | irqd->enable_hwirq = devm_kcalloc(chip->dev, | |
195 | irqd->num_pmic_irqs, | |
196 | sizeof(*irqd->enable_hwirq), | |
197 | GFP_KERNEL); | |
198 | if (!irqd->enable_hwirq) | |
199 | return -ENOMEM; | |
200 | ||
201 | irqd->cache_hwirq = devm_kcalloc(chip->dev, | |
202 | irqd->num_pmic_irqs, | |
203 | sizeof(*irqd->cache_hwirq), | |
204 | GFP_KERNEL); | |
205 | if (!irqd->cache_hwirq) | |
206 | return -ENOMEM; | |
207 | ||
208 | /* Disable all interrupts for initializing */ | |
209 | for (i = 0; i < irqd->num_top; i++) { | |
210 | for (j = 0; j < mt6358_ints[i].num_int_regs; j++) | |
211 | regmap_write(chip->regmap, | |
212 | mt6358_ints[i].en_reg + | |
213 | mt6358_ints[i].en_reg_shift * j, 0); | |
214 | } | |
215 | ||
216 | chip->irq_domain = irq_domain_add_linear(chip->dev->of_node, | |
217 | irqd->num_pmic_irqs, | |
218 | &mt6358_irq_domain_ops, chip); | |
219 | if (!chip->irq_domain) { | |
220 | dev_err(chip->dev, "Could not create IRQ domain\n"); | |
221 | return -ENODEV; | |
222 | } | |
223 | ||
224 | ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, | |
225 | mt6358_irq_handler, IRQF_ONESHOT, | |
226 | mt6358_irq_chip.name, chip); | |
227 | if (ret) { | |
228 | dev_err(chip->dev, "Failed to register IRQ=%d, ret=%d\n", | |
229 | chip->irq, ret); | |
230 | return ret; | |
231 | } | |
232 | ||
233 | enable_irq_wake(chip->irq); | |
234 | return ret; | |
235 | } |