1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Copyright (c) 2019 MediaTek Inc. |
4 | |
5 | #include <linux/interrupt.h> |
6 | #include <linux/irq.h> |
7 | #include <linux/irqdomain.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/regmap.h> |
11 | #include <linux/suspend.h> |
12 | #include <linux/mfd/mt6323/core.h> |
13 | #include <linux/mfd/mt6323/registers.h> |
14 | #include <linux/mfd/mt6331/core.h> |
15 | #include <linux/mfd/mt6331/registers.h> |
16 | #include <linux/mfd/mt6397/core.h> |
17 | #include <linux/mfd/mt6397/registers.h> |
18 | |
19 | static void mt6397_irq_lock(struct irq_data *data) |
20 | { |
21 | struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(d: data); |
22 | |
23 | mutex_lock(&mt6397->irqlock); |
24 | } |
25 | |
26 | static void mt6397_irq_sync_unlock(struct irq_data *data) |
27 | { |
28 | struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(d: data); |
29 | |
30 | regmap_write(map: mt6397->regmap, reg: mt6397->int_con[0], |
31 | val: mt6397->irq_masks_cur[0]); |
32 | regmap_write(map: mt6397->regmap, reg: mt6397->int_con[1], |
33 | val: mt6397->irq_masks_cur[1]); |
34 | |
35 | mutex_unlock(lock: &mt6397->irqlock); |
36 | } |
37 | |
38 | static void mt6397_irq_disable(struct irq_data *data) |
39 | { |
40 | struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(d: data); |
41 | int shift = data->hwirq & 0xf; |
42 | int reg = data->hwirq >> 4; |
43 | |
44 | mt6397->irq_masks_cur[reg] &= ~BIT(shift); |
45 | } |
46 | |
47 | static void mt6397_irq_enable(struct irq_data *data) |
48 | { |
49 | struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(d: data); |
50 | int shift = data->hwirq & 0xf; |
51 | int reg = data->hwirq >> 4; |
52 | |
53 | mt6397->irq_masks_cur[reg] |= BIT(shift); |
54 | } |
55 | |
56 | static int mt6397_irq_set_wake(struct irq_data *irq_data, unsigned int on) |
57 | { |
58 | struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(d: irq_data); |
59 | int shift = irq_data->hwirq & 0xf; |
60 | int reg = irq_data->hwirq >> 4; |
61 | |
62 | if (on) |
63 | mt6397->wake_mask[reg] |= BIT(shift); |
64 | else |
65 | mt6397->wake_mask[reg] &= ~BIT(shift); |
66 | |
67 | return 0; |
68 | } |
69 | |
70 | static struct irq_chip mt6397_irq_chip = { |
71 | .name = "mt6397-irq" , |
72 | .irq_bus_lock = mt6397_irq_lock, |
73 | .irq_bus_sync_unlock = mt6397_irq_sync_unlock, |
74 | .irq_enable = mt6397_irq_enable, |
75 | .irq_disable = mt6397_irq_disable, |
76 | .irq_set_wake = pm_sleep_ptr(mt6397_irq_set_wake), |
77 | }; |
78 | |
79 | static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg, |
80 | int irqbase) |
81 | { |
82 | unsigned int status = 0; |
83 | int i, irq, ret; |
84 | |
85 | ret = regmap_read(map: mt6397->regmap, reg, val: &status); |
86 | if (ret) { |
87 | dev_err(mt6397->dev, "Failed to read irq status: %d\n" , ret); |
88 | return; |
89 | } |
90 | |
91 | for (i = 0; i < 16; i++) { |
92 | if (status & BIT(i)) { |
93 | irq = irq_find_mapping(domain: mt6397->irq_domain, hwirq: irqbase + i); |
94 | if (irq) |
95 | handle_nested_irq(irq); |
96 | } |
97 | } |
98 | |
99 | regmap_write(map: mt6397->regmap, reg, val: status); |
100 | } |
101 | |
102 | static irqreturn_t mt6397_irq_thread(int irq, void *data) |
103 | { |
104 | struct mt6397_chip *mt6397 = data; |
105 | |
106 | mt6397_irq_handle_reg(mt6397, reg: mt6397->int_status[0], irqbase: 0); |
107 | mt6397_irq_handle_reg(mt6397, reg: mt6397->int_status[1], irqbase: 16); |
108 | |
109 | return IRQ_HANDLED; |
110 | } |
111 | |
112 | static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq, |
113 | irq_hw_number_t hw) |
114 | { |
115 | struct mt6397_chip *mt6397 = d->host_data; |
116 | |
117 | irq_set_chip_data(irq, data: mt6397); |
118 | irq_set_chip_and_handler(irq, chip: &mt6397_irq_chip, handle: handle_level_irq); |
119 | irq_set_nested_thread(irq, nest: 1); |
120 | irq_set_noprobe(irq); |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | static const struct irq_domain_ops mt6397_irq_domain_ops = { |
126 | .map = mt6397_irq_domain_map, |
127 | }; |
128 | |
129 | static int mt6397_irq_pm_notifier(struct notifier_block *notifier, |
130 | unsigned long pm_event, void *unused) |
131 | { |
132 | struct mt6397_chip *chip = |
133 | container_of(notifier, struct mt6397_chip, pm_nb); |
134 | |
135 | switch (pm_event) { |
136 | case PM_SUSPEND_PREPARE: |
137 | regmap_write(map: chip->regmap, |
138 | reg: chip->int_con[0], val: chip->wake_mask[0]); |
139 | regmap_write(map: chip->regmap, |
140 | reg: chip->int_con[1], val: chip->wake_mask[1]); |
141 | enable_irq_wake(irq: chip->irq); |
142 | break; |
143 | |
144 | case PM_POST_SUSPEND: |
145 | regmap_write(map: chip->regmap, |
146 | reg: chip->int_con[0], val: chip->irq_masks_cur[0]); |
147 | regmap_write(map: chip->regmap, |
148 | reg: chip->int_con[1], val: chip->irq_masks_cur[1]); |
149 | disable_irq_wake(irq: chip->irq); |
150 | break; |
151 | |
152 | default: |
153 | break; |
154 | } |
155 | |
156 | return NOTIFY_DONE; |
157 | } |
158 | |
159 | int mt6397_irq_init(struct mt6397_chip *chip) |
160 | { |
161 | int ret; |
162 | |
163 | mutex_init(&chip->irqlock); |
164 | |
165 | switch (chip->chip_id) { |
166 | case MT6323_CHIP_ID: |
167 | chip->int_con[0] = MT6323_INT_CON0; |
168 | chip->int_con[1] = MT6323_INT_CON1; |
169 | chip->int_status[0] = MT6323_INT_STATUS0; |
170 | chip->int_status[1] = MT6323_INT_STATUS1; |
171 | break; |
172 | case MT6331_CHIP_ID: |
173 | chip->int_con[0] = MT6331_INT_CON0; |
174 | chip->int_con[1] = MT6331_INT_CON1; |
175 | chip->int_status[0] = MT6331_INT_STATUS_CON0; |
176 | chip->int_status[1] = MT6331_INT_STATUS_CON1; |
177 | break; |
178 | case MT6391_CHIP_ID: |
179 | case MT6397_CHIP_ID: |
180 | chip->int_con[0] = MT6397_INT_CON0; |
181 | chip->int_con[1] = MT6397_INT_CON1; |
182 | chip->int_status[0] = MT6397_INT_STATUS0; |
183 | chip->int_status[1] = MT6397_INT_STATUS1; |
184 | break; |
185 | |
186 | default: |
187 | dev_err(chip->dev, "unsupported chip: 0x%x\n" , chip->chip_id); |
188 | return -ENODEV; |
189 | } |
190 | |
191 | /* Mask all interrupt sources */ |
192 | regmap_write(map: chip->regmap, reg: chip->int_con[0], val: 0x0); |
193 | regmap_write(map: chip->regmap, reg: chip->int_con[1], val: 0x0); |
194 | |
195 | chip->pm_nb.notifier_call = mt6397_irq_pm_notifier; |
196 | chip->irq_domain = irq_domain_add_linear(of_node: chip->dev->of_node, |
197 | size: MT6397_IRQ_NR, |
198 | ops: &mt6397_irq_domain_ops, |
199 | host_data: chip); |
200 | if (!chip->irq_domain) { |
201 | dev_err(chip->dev, "could not create irq domain\n" ); |
202 | return -ENOMEM; |
203 | } |
204 | |
205 | ret = devm_request_threaded_irq(dev: chip->dev, irq: chip->irq, NULL, |
206 | thread_fn: mt6397_irq_thread, IRQF_ONESHOT, |
207 | devname: "mt6397-pmic" , dev_id: chip); |
208 | if (ret) { |
209 | dev_err(chip->dev, "failed to register irq=%d; err: %d\n" , |
210 | chip->irq, ret); |
211 | return ret; |
212 | } |
213 | |
214 | register_pm_notifier(nb: &chip->pm_nb); |
215 | return 0; |
216 | } |
217 | |