Commit | Line | Data |
---|---|---|
ace4cdfe ZT |
1 | /* |
2 | * Copyright (c) 2016 MediaTek Inc. | |
3 | * Author: Zhiyong Tao <zhiyong.tao@mediatek.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | ||
15 | #include <linux/clk.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/err.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/of.h> | |
21 | #include <linux/of_device.h> | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/iopoll.h> | |
24 | #include <linux/io.h> | |
25 | #include <linux/iio/iio.h> | |
26 | ||
27 | /* Register definitions */ | |
28 | #define MT6577_AUXADC_CON0 0x00 | |
29 | #define MT6577_AUXADC_CON1 0x04 | |
30 | #define MT6577_AUXADC_CON2 0x10 | |
31 | #define MT6577_AUXADC_STA BIT(0) | |
32 | ||
33 | #define MT6577_AUXADC_DAT0 0x14 | |
34 | #define MT6577_AUXADC_RDY0 BIT(12) | |
35 | ||
36 | #define MT6577_AUXADC_MISC 0x94 | |
37 | #define MT6577_AUXADC_PDN_EN BIT(14) | |
38 | ||
39 | #define MT6577_AUXADC_DAT_MASK 0xfff | |
40 | #define MT6577_AUXADC_SLEEP_US 1000 | |
41 | #define MT6577_AUXADC_TIMEOUT_US 10000 | |
42 | #define MT6577_AUXADC_POWER_READY_MS 1 | |
43 | #define MT6577_AUXADC_SAMPLE_READY_US 25 | |
44 | ||
45 | struct mt6577_auxadc_device { | |
46 | void __iomem *reg_base; | |
47 | struct clk *adc_clk; | |
48 | struct mutex lock; | |
49 | }; | |
50 | ||
51 | #define MT6577_AUXADC_CHANNEL(idx) { \ | |
52 | .type = IIO_VOLTAGE, \ | |
53 | .indexed = 1, \ | |
54 | .channel = (idx), \ | |
55 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ | |
56 | } | |
57 | ||
58 | static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = { | |
59 | MT6577_AUXADC_CHANNEL(0), | |
60 | MT6577_AUXADC_CHANNEL(1), | |
61 | MT6577_AUXADC_CHANNEL(2), | |
62 | MT6577_AUXADC_CHANNEL(3), | |
63 | MT6577_AUXADC_CHANNEL(4), | |
64 | MT6577_AUXADC_CHANNEL(5), | |
65 | MT6577_AUXADC_CHANNEL(6), | |
66 | MT6577_AUXADC_CHANNEL(7), | |
67 | MT6577_AUXADC_CHANNEL(8), | |
68 | MT6577_AUXADC_CHANNEL(9), | |
69 | MT6577_AUXADC_CHANNEL(10), | |
70 | MT6577_AUXADC_CHANNEL(11), | |
71 | MT6577_AUXADC_CHANNEL(12), | |
72 | MT6577_AUXADC_CHANNEL(13), | |
73 | MT6577_AUXADC_CHANNEL(14), | |
74 | MT6577_AUXADC_CHANNEL(15), | |
75 | }; | |
76 | ||
77 | static inline void mt6577_auxadc_mod_reg(void __iomem *reg, | |
78 | u32 or_mask, u32 and_mask) | |
79 | { | |
80 | u32 val; | |
81 | ||
82 | val = readl(reg); | |
83 | val |= or_mask; | |
84 | val &= ~and_mask; | |
85 | writel(val, reg); | |
86 | } | |
87 | ||
88 | static int mt6577_auxadc_read(struct iio_dev *indio_dev, | |
89 | struct iio_chan_spec const *chan) | |
90 | { | |
91 | u32 val; | |
92 | void __iomem *reg_channel; | |
93 | int ret; | |
94 | struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); | |
95 | ||
96 | reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 + | |
97 | chan->channel * 0x04; | |
98 | ||
99 | mutex_lock(&adc_dev->lock); | |
100 | ||
101 | mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, | |
102 | 0, 1 << chan->channel); | |
103 | ||
104 | /* read channel and make sure old ready bit == 0 */ | |
105 | ret = readl_poll_timeout(reg_channel, val, | |
106 | ((val & MT6577_AUXADC_RDY0) == 0), | |
107 | MT6577_AUXADC_SLEEP_US, | |
108 | MT6577_AUXADC_TIMEOUT_US); | |
109 | if (ret < 0) { | |
110 | dev_err(indio_dev->dev.parent, | |
111 | "wait for channel[%d] ready bit clear time out\n", | |
112 | chan->channel); | |
113 | goto err_timeout; | |
114 | } | |
115 | ||
116 | /* set bit to trigger sample */ | |
117 | mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, | |
118 | 1 << chan->channel, 0); | |
119 | ||
120 | /* we must delay here for hardware sample channel data */ | |
121 | udelay(MT6577_AUXADC_SAMPLE_READY_US); | |
122 | ||
123 | /* check MTK_AUXADC_CON2 if auxadc is idle */ | |
124 | ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val, | |
125 | ((val & MT6577_AUXADC_STA) == 0), | |
126 | MT6577_AUXADC_SLEEP_US, | |
127 | MT6577_AUXADC_TIMEOUT_US); | |
128 | if (ret < 0) { | |
129 | dev_err(indio_dev->dev.parent, | |
130 | "wait for auxadc idle time out\n"); | |
131 | goto err_timeout; | |
132 | } | |
133 | ||
134 | /* read channel and make sure ready bit == 1 */ | |
135 | ret = readl_poll_timeout(reg_channel, val, | |
136 | ((val & MT6577_AUXADC_RDY0) != 0), | |
137 | MT6577_AUXADC_SLEEP_US, | |
138 | MT6577_AUXADC_TIMEOUT_US); | |
139 | if (ret < 0) { | |
140 | dev_err(indio_dev->dev.parent, | |
141 | "wait for channel[%d] data ready time out\n", | |
142 | chan->channel); | |
143 | goto err_timeout; | |
144 | } | |
145 | ||
146 | /* read data */ | |
147 | val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK; | |
148 | ||
149 | mutex_unlock(&adc_dev->lock); | |
150 | ||
151 | return val; | |
152 | ||
153 | err_timeout: | |
154 | ||
155 | mutex_unlock(&adc_dev->lock); | |
156 | ||
157 | return -ETIMEDOUT; | |
158 | } | |
159 | ||
160 | static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev, | |
161 | struct iio_chan_spec const *chan, | |
162 | int *val, | |
163 | int *val2, | |
164 | long info) | |
165 | { | |
166 | switch (info) { | |
167 | case IIO_CHAN_INFO_PROCESSED: | |
168 | *val = mt6577_auxadc_read(indio_dev, chan); | |
169 | if (*val < 0) { | |
170 | dev_err(indio_dev->dev.parent, | |
171 | "failed to sample data on channel[%d]\n", | |
172 | chan->channel); | |
173 | return *val; | |
174 | } | |
175 | return IIO_VAL_INT; | |
176 | ||
177 | default: | |
178 | return -EINVAL; | |
179 | } | |
180 | } | |
181 | ||
182 | static const struct iio_info mt6577_auxadc_info = { | |
183 | .driver_module = THIS_MODULE, | |
184 | .read_raw = &mt6577_auxadc_read_raw, | |
185 | }; | |
186 | ||
187 | static int mt6577_auxadc_probe(struct platform_device *pdev) | |
188 | { | |
189 | struct mt6577_auxadc_device *adc_dev; | |
190 | unsigned long adc_clk_rate; | |
191 | struct resource *res; | |
192 | struct iio_dev *indio_dev; | |
193 | int ret; | |
194 | ||
195 | indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); | |
196 | if (!indio_dev) | |
197 | return -ENOMEM; | |
198 | ||
199 | adc_dev = iio_priv(indio_dev); | |
200 | indio_dev->dev.parent = &pdev->dev; | |
201 | indio_dev->name = dev_name(&pdev->dev); | |
202 | indio_dev->info = &mt6577_auxadc_info; | |
203 | indio_dev->modes = INDIO_DIRECT_MODE; | |
204 | indio_dev->channels = mt6577_auxadc_iio_channels; | |
205 | indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels); | |
206 | ||
207 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
208 | adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res); | |
209 | if (IS_ERR(adc_dev->reg_base)) { | |
210 | dev_err(&pdev->dev, "failed to get auxadc base address\n"); | |
211 | return PTR_ERR(adc_dev->reg_base); | |
212 | } | |
213 | ||
214 | adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main"); | |
215 | if (IS_ERR(adc_dev->adc_clk)) { | |
216 | dev_err(&pdev->dev, "failed to get auxadc clock\n"); | |
217 | return PTR_ERR(adc_dev->adc_clk); | |
218 | } | |
219 | ||
220 | ret = clk_prepare_enable(adc_dev->adc_clk); | |
221 | if (ret) { | |
222 | dev_err(&pdev->dev, "failed to enable auxadc clock\n"); | |
223 | return ret; | |
224 | } | |
225 | ||
226 | adc_clk_rate = clk_get_rate(adc_dev->adc_clk); | |
227 | if (!adc_clk_rate) { | |
228 | ret = -EINVAL; | |
229 | dev_err(&pdev->dev, "null clock rate\n"); | |
230 | goto err_disable_clk; | |
231 | } | |
232 | ||
233 | mutex_init(&adc_dev->lock); | |
234 | ||
235 | mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, | |
236 | MT6577_AUXADC_PDN_EN, 0); | |
237 | mdelay(MT6577_AUXADC_POWER_READY_MS); | |
238 | ||
239 | platform_set_drvdata(pdev, indio_dev); | |
240 | ||
241 | ret = iio_device_register(indio_dev); | |
242 | if (ret < 0) { | |
243 | dev_err(&pdev->dev, "failed to register iio device\n"); | |
244 | goto err_power_off; | |
245 | } | |
246 | ||
247 | return 0; | |
248 | ||
249 | err_power_off: | |
250 | mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, | |
251 | 0, MT6577_AUXADC_PDN_EN); | |
252 | err_disable_clk: | |
253 | clk_disable_unprepare(adc_dev->adc_clk); | |
254 | return ret; | |
255 | } | |
256 | ||
257 | static int mt6577_auxadc_remove(struct platform_device *pdev) | |
258 | { | |
259 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | |
260 | struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); | |
261 | ||
262 | iio_device_unregister(indio_dev); | |
263 | ||
264 | mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, | |
265 | 0, MT6577_AUXADC_PDN_EN); | |
266 | ||
267 | clk_disable_unprepare(adc_dev->adc_clk); | |
268 | ||
269 | return 0; | |
270 | } | |
271 | ||
272 | static const struct of_device_id mt6577_auxadc_of_match[] = { | |
273 | { .compatible = "mediatek,mt2701-auxadc", }, | |
274 | { .compatible = "mediatek,mt8173-auxadc", }, | |
275 | { } | |
276 | }; | |
277 | MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match); | |
278 | ||
279 | static struct platform_driver mt6577_auxadc_driver = { | |
280 | .driver = { | |
281 | .name = "mt6577-auxadc", | |
282 | .of_match_table = mt6577_auxadc_of_match, | |
283 | }, | |
284 | .probe = mt6577_auxadc_probe, | |
285 | .remove = mt6577_auxadc_remove, | |
286 | }; | |
287 | module_platform_driver(mt6577_auxadc_driver); | |
288 | ||
289 | MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>"); | |
290 | MODULE_DESCRIPTION("MTK AUXADC Device Driver"); | |
291 | MODULE_LICENSE("GPL v2"); |