1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2016 MediaTek Inc. |
4 | * Author: Zhiyong Tao <zhiyong.tao@mediatek.com> |
5 | */ |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/err.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/property.h> |
15 | #include <linux/iopoll.h> |
16 | #include <linux/io.h> |
17 | #include <linux/iio/iio.h> |
18 | |
19 | /* Register definitions */ |
20 | #define MT6577_AUXADC_CON0 0x00 |
21 | #define MT6577_AUXADC_CON1 0x04 |
22 | #define MT6577_AUXADC_CON2 0x10 |
23 | #define MT6577_AUXADC_STA BIT(0) |
24 | |
25 | #define MT6577_AUXADC_DAT0 0x14 |
26 | #define MT6577_AUXADC_RDY0 BIT(12) |
27 | |
28 | #define MT6577_AUXADC_MISC 0x94 |
29 | #define MT6577_AUXADC_PDN_EN BIT(14) |
30 | |
31 | #define MT6577_AUXADC_DAT_MASK 0xfff |
32 | #define MT6577_AUXADC_SLEEP_US 1000 |
33 | #define MT6577_AUXADC_TIMEOUT_US 10000 |
34 | #define MT6577_AUXADC_POWER_READY_MS 1 |
35 | #define MT6577_AUXADC_SAMPLE_READY_US 25 |
36 | |
37 | struct mtk_auxadc_compatible { |
38 | bool sample_data_cali; |
39 | bool check_global_idle; |
40 | }; |
41 | |
42 | struct mt6577_auxadc_device { |
43 | void __iomem *reg_base; |
44 | struct clk *adc_clk; |
45 | struct mutex lock; |
46 | const struct mtk_auxadc_compatible *dev_comp; |
47 | }; |
48 | |
49 | static const struct mtk_auxadc_compatible mt8186_compat = { |
50 | .sample_data_cali = false, |
51 | .check_global_idle = false, |
52 | }; |
53 | |
54 | static const struct mtk_auxadc_compatible mt8173_compat = { |
55 | .sample_data_cali = false, |
56 | .check_global_idle = true, |
57 | }; |
58 | |
59 | static const struct mtk_auxadc_compatible mt6765_compat = { |
60 | .sample_data_cali = true, |
61 | .check_global_idle = false, |
62 | }; |
63 | |
64 | #define MT6577_AUXADC_CHANNEL(idx) { \ |
65 | .type = IIO_VOLTAGE, \ |
66 | .indexed = 1, \ |
67 | .channel = (idx), \ |
68 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ |
69 | } |
70 | |
71 | static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = { |
72 | MT6577_AUXADC_CHANNEL(0), |
73 | MT6577_AUXADC_CHANNEL(1), |
74 | MT6577_AUXADC_CHANNEL(2), |
75 | MT6577_AUXADC_CHANNEL(3), |
76 | MT6577_AUXADC_CHANNEL(4), |
77 | MT6577_AUXADC_CHANNEL(5), |
78 | MT6577_AUXADC_CHANNEL(6), |
79 | MT6577_AUXADC_CHANNEL(7), |
80 | MT6577_AUXADC_CHANNEL(8), |
81 | MT6577_AUXADC_CHANNEL(9), |
82 | MT6577_AUXADC_CHANNEL(10), |
83 | MT6577_AUXADC_CHANNEL(11), |
84 | MT6577_AUXADC_CHANNEL(12), |
85 | MT6577_AUXADC_CHANNEL(13), |
86 | MT6577_AUXADC_CHANNEL(14), |
87 | MT6577_AUXADC_CHANNEL(15), |
88 | }; |
89 | |
90 | /* For Voltage calculation */ |
91 | #define VOLTAGE_FULL_RANGE 1500 /* VA voltage */ |
92 | #define AUXADC_PRECISE 4096 /* 12 bits */ |
93 | |
94 | static int mt_auxadc_get_cali_data(int rawdata, bool enable_cali) |
95 | { |
96 | return rawdata; |
97 | } |
98 | |
99 | static inline void mt6577_auxadc_mod_reg(void __iomem *reg, |
100 | u32 or_mask, u32 and_mask) |
101 | { |
102 | u32 val; |
103 | |
104 | val = readl(addr: reg); |
105 | val |= or_mask; |
106 | val &= ~and_mask; |
107 | writel(val, addr: reg); |
108 | } |
109 | |
110 | static int mt6577_auxadc_read(struct iio_dev *indio_dev, |
111 | struct iio_chan_spec const *chan) |
112 | { |
113 | u32 val; |
114 | void __iomem *reg_channel; |
115 | int ret; |
116 | struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); |
117 | |
118 | reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 + |
119 | chan->channel * 0x04; |
120 | |
121 | mutex_lock(&adc_dev->lock); |
122 | |
123 | mt6577_auxadc_mod_reg(reg: adc_dev->reg_base + MT6577_AUXADC_CON1, |
124 | or_mask: 0, and_mask: 1 << chan->channel); |
125 | |
126 | /* read channel and make sure old ready bit == 0 */ |
127 | ret = readl_poll_timeout(reg_channel, val, |
128 | ((val & MT6577_AUXADC_RDY0) == 0), |
129 | MT6577_AUXADC_SLEEP_US, |
130 | MT6577_AUXADC_TIMEOUT_US); |
131 | if (ret < 0) { |
132 | dev_err(indio_dev->dev.parent, |
133 | "wait for channel[%d] ready bit clear time out\n" , |
134 | chan->channel); |
135 | goto err_timeout; |
136 | } |
137 | |
138 | /* set bit to trigger sample */ |
139 | mt6577_auxadc_mod_reg(reg: adc_dev->reg_base + MT6577_AUXADC_CON1, |
140 | or_mask: 1 << chan->channel, and_mask: 0); |
141 | |
142 | /* we must delay here for hardware sample channel data */ |
143 | udelay(MT6577_AUXADC_SAMPLE_READY_US); |
144 | |
145 | if (adc_dev->dev_comp->check_global_idle) { |
146 | /* check MTK_AUXADC_CON2 if auxadc is idle */ |
147 | ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, |
148 | val, ((val & MT6577_AUXADC_STA) == 0), |
149 | MT6577_AUXADC_SLEEP_US, |
150 | MT6577_AUXADC_TIMEOUT_US); |
151 | if (ret < 0) { |
152 | dev_err(indio_dev->dev.parent, |
153 | "wait for auxadc idle time out\n" ); |
154 | goto err_timeout; |
155 | } |
156 | } |
157 | |
158 | /* read channel and make sure ready bit == 1 */ |
159 | ret = readl_poll_timeout(reg_channel, val, |
160 | ((val & MT6577_AUXADC_RDY0) != 0), |
161 | MT6577_AUXADC_SLEEP_US, |
162 | MT6577_AUXADC_TIMEOUT_US); |
163 | if (ret < 0) { |
164 | dev_err(indio_dev->dev.parent, |
165 | "wait for channel[%d] data ready time out\n" , |
166 | chan->channel); |
167 | goto err_timeout; |
168 | } |
169 | |
170 | /* read data */ |
171 | val = readl(addr: reg_channel) & MT6577_AUXADC_DAT_MASK; |
172 | |
173 | mutex_unlock(lock: &adc_dev->lock); |
174 | |
175 | return val; |
176 | |
177 | err_timeout: |
178 | |
179 | mutex_unlock(lock: &adc_dev->lock); |
180 | |
181 | return -ETIMEDOUT; |
182 | } |
183 | |
184 | static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev, |
185 | struct iio_chan_spec const *chan, |
186 | int *val, |
187 | int *val2, |
188 | long info) |
189 | { |
190 | struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); |
191 | |
192 | switch (info) { |
193 | case IIO_CHAN_INFO_PROCESSED: |
194 | *val = mt6577_auxadc_read(indio_dev, chan); |
195 | if (*val < 0) { |
196 | dev_err(indio_dev->dev.parent, |
197 | "failed to sample data on channel[%d]\n" , |
198 | chan->channel); |
199 | return *val; |
200 | } |
201 | if (adc_dev->dev_comp->sample_data_cali) |
202 | *val = mt_auxadc_get_cali_data(rawdata: *val, enable_cali: true); |
203 | |
204 | /* Convert adc raw data to voltage: 0 - 1500 mV */ |
205 | *val = *val * VOLTAGE_FULL_RANGE / AUXADC_PRECISE; |
206 | |
207 | return IIO_VAL_INT; |
208 | |
209 | default: |
210 | return -EINVAL; |
211 | } |
212 | } |
213 | |
214 | static const struct iio_info mt6577_auxadc_info = { |
215 | .read_raw = &mt6577_auxadc_read_raw, |
216 | }; |
217 | |
218 | static int mt6577_auxadc_resume(struct device *dev) |
219 | { |
220 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
221 | struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); |
222 | int ret; |
223 | |
224 | ret = clk_prepare_enable(clk: adc_dev->adc_clk); |
225 | if (ret) { |
226 | pr_err("failed to enable auxadc clock\n" ); |
227 | return ret; |
228 | } |
229 | |
230 | mt6577_auxadc_mod_reg(reg: adc_dev->reg_base + MT6577_AUXADC_MISC, |
231 | MT6577_AUXADC_PDN_EN, and_mask: 0); |
232 | mdelay(MT6577_AUXADC_POWER_READY_MS); |
233 | |
234 | return 0; |
235 | } |
236 | |
237 | static int mt6577_auxadc_suspend(struct device *dev) |
238 | { |
239 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
240 | struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); |
241 | |
242 | mt6577_auxadc_mod_reg(reg: adc_dev->reg_base + MT6577_AUXADC_MISC, |
243 | or_mask: 0, MT6577_AUXADC_PDN_EN); |
244 | clk_disable_unprepare(clk: adc_dev->adc_clk); |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | static void mt6577_power_off(void *data) |
250 | { |
251 | struct mt6577_auxadc_device *adc_dev = data; |
252 | |
253 | mt6577_auxadc_mod_reg(reg: adc_dev->reg_base + MT6577_AUXADC_MISC, |
254 | or_mask: 0, MT6577_AUXADC_PDN_EN); |
255 | } |
256 | |
257 | static int mt6577_auxadc_probe(struct platform_device *pdev) |
258 | { |
259 | struct mt6577_auxadc_device *adc_dev; |
260 | unsigned long adc_clk_rate; |
261 | struct iio_dev *indio_dev; |
262 | int ret; |
263 | |
264 | indio_dev = devm_iio_device_alloc(parent: &pdev->dev, sizeof_priv: sizeof(*adc_dev)); |
265 | if (!indio_dev) |
266 | return -ENOMEM; |
267 | |
268 | adc_dev = iio_priv(indio_dev); |
269 | indio_dev->name = dev_name(dev: &pdev->dev); |
270 | indio_dev->info = &mt6577_auxadc_info; |
271 | indio_dev->modes = INDIO_DIRECT_MODE; |
272 | indio_dev->channels = mt6577_auxadc_iio_channels; |
273 | indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels); |
274 | |
275 | adc_dev->reg_base = devm_platform_ioremap_resource(pdev, index: 0); |
276 | if (IS_ERR(ptr: adc_dev->reg_base)) |
277 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: adc_dev->reg_base), |
278 | fmt: "failed to get auxadc base address\n" ); |
279 | |
280 | adc_dev->adc_clk = devm_clk_get_enabled(dev: &pdev->dev, id: "main" ); |
281 | if (IS_ERR(ptr: adc_dev->adc_clk)) |
282 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: adc_dev->adc_clk), |
283 | fmt: "failed to enable auxadc clock\n" ); |
284 | |
285 | adc_clk_rate = clk_get_rate(clk: adc_dev->adc_clk); |
286 | if (!adc_clk_rate) |
287 | return dev_err_probe(dev: &pdev->dev, err: -EINVAL, fmt: "null clock rate\n" ); |
288 | |
289 | adc_dev->dev_comp = device_get_match_data(dev: &pdev->dev); |
290 | |
291 | mutex_init(&adc_dev->lock); |
292 | |
293 | mt6577_auxadc_mod_reg(reg: adc_dev->reg_base + MT6577_AUXADC_MISC, |
294 | MT6577_AUXADC_PDN_EN, and_mask: 0); |
295 | mdelay(MT6577_AUXADC_POWER_READY_MS); |
296 | platform_set_drvdata(pdev, data: indio_dev); |
297 | |
298 | ret = devm_add_action_or_reset(&pdev->dev, mt6577_power_off, adc_dev); |
299 | if (ret) |
300 | return dev_err_probe(dev: &pdev->dev, err: ret, |
301 | fmt: "Failed to add action to managed power off\n" ); |
302 | |
303 | ret = devm_iio_device_register(&pdev->dev, indio_dev); |
304 | if (ret < 0) |
305 | return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "failed to register iio device\n" ); |
306 | |
307 | return 0; |
308 | } |
309 | |
310 | static DEFINE_SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops, |
311 | mt6577_auxadc_suspend, |
312 | mt6577_auxadc_resume); |
313 | |
314 | static const struct of_device_id mt6577_auxadc_of_match[] = { |
315 | { .compatible = "mediatek,mt2701-auxadc" , .data = &mt8173_compat }, |
316 | { .compatible = "mediatek,mt2712-auxadc" , .data = &mt8173_compat }, |
317 | { .compatible = "mediatek,mt7622-auxadc" , .data = &mt8173_compat }, |
318 | { .compatible = "mediatek,mt8173-auxadc" , .data = &mt8173_compat }, |
319 | { .compatible = "mediatek,mt8186-auxadc" , .data = &mt8186_compat }, |
320 | { .compatible = "mediatek,mt6765-auxadc" , .data = &mt6765_compat }, |
321 | { } |
322 | }; |
323 | MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match); |
324 | |
325 | static struct platform_driver mt6577_auxadc_driver = { |
326 | .driver = { |
327 | .name = "mt6577-auxadc" , |
328 | .of_match_table = mt6577_auxadc_of_match, |
329 | .pm = pm_sleep_ptr(&mt6577_auxadc_pm_ops), |
330 | }, |
331 | .probe = mt6577_auxadc_probe, |
332 | }; |
333 | module_platform_driver(mt6577_auxadc_driver); |
334 | |
335 | MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>" ); |
336 | MODULE_DESCRIPTION("MTK AUXADC Device Driver" ); |
337 | MODULE_LICENSE("GPL v2" ); |
338 | |