Commit | Line | Data |
---|---|---|
b3201b56 SR |
1 | /* |
2 | * ST SPEAr ADC driver | |
3 | * | |
4 | * Copyright 2012 Stefan Roese <sr@denx.de> | |
5 | * | |
6 | * Licensed under the GPL-2. | |
7 | */ | |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/platform_device.h> | |
11 | #include <linux/interrupt.h> | |
12 | #include <linux/device.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/clk.h> | |
17 | #include <linux/err.h> | |
18 | #include <linux/completion.h> | |
19 | #include <linux/of.h> | |
20 | #include <linux/of_address.h> | |
21 | ||
06458e27 JC |
22 | #include <linux/iio/iio.h> |
23 | #include <linux/iio/sysfs.h> | |
b3201b56 | 24 | |
b64aef70 JC |
25 | /* SPEAR registers definitions */ |
26 | #define SPEAR600_ADC_SCAN_RATE_LO(x) ((x) & 0xFFFF) | |
27 | #define SPEAR600_ADC_SCAN_RATE_HI(x) (((x) >> 0x10) & 0xFFFF) | |
28 | #define SPEAR_ADC_CLK_LOW(x) (((x) & 0xf) << 0) | |
29 | #define SPEAR_ADC_CLK_HIGH(x) (((x) & 0xf) << 4) | |
b3201b56 SR |
30 | |
31 | /* Bit definitions for SPEAR_ADC_STATUS */ | |
418880f5 | 32 | #define SPEAR_ADC_STATUS_START_CONVERSION BIT(0) |
b64aef70 | 33 | #define SPEAR_ADC_STATUS_CHANNEL_NUM(x) ((x) << 1) |
418880f5 | 34 | #define SPEAR_ADC_STATUS_ADC_ENABLE BIT(4) |
b64aef70 | 35 | #define SPEAR_ADC_STATUS_AVG_SAMPLE(x) ((x) << 5) |
418880f5 | 36 | #define SPEAR_ADC_STATUS_VREF_INTERNAL BIT(9) |
b3201b56 | 37 | |
b64aef70 JC |
38 | #define SPEAR_ADC_DATA_MASK 0x03ff |
39 | #define SPEAR_ADC_DATA_BITS 10 | |
b3201b56 | 40 | |
b64aef70 | 41 | #define SPEAR_ADC_MOD_NAME "spear-adc" |
b3201b56 | 42 | |
b64aef70 | 43 | #define SPEAR_ADC_CHANNEL_NUM 8 |
b3201b56 | 44 | |
b64aef70 JC |
45 | #define SPEAR_ADC_CLK_MIN 2500000 |
46 | #define SPEAR_ADC_CLK_MAX 20000000 | |
b3201b56 SR |
47 | |
48 | struct adc_regs_spear3xx { | |
49 | u32 status; | |
50 | u32 average; | |
51 | u32 scan_rate; | |
52 | u32 clk; /* Not avail for 1340 & 1310 */ | |
b64aef70 JC |
53 | u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM]; |
54 | u32 ch_data[SPEAR_ADC_CHANNEL_NUM]; | |
b3201b56 SR |
55 | }; |
56 | ||
57 | struct chan_data { | |
58 | u32 lsb; | |
59 | u32 msb; | |
60 | }; | |
61 | ||
62 | struct adc_regs_spear6xx { | |
63 | u32 status; | |
64 | u32 pad[2]; | |
65 | u32 clk; | |
b64aef70 JC |
66 | u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM]; |
67 | struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM]; | |
b3201b56 SR |
68 | u32 scan_rate_lo; |
69 | u32 scan_rate_hi; | |
70 | struct chan_data average; | |
71 | }; | |
72 | ||
b586e5d9 | 73 | struct spear_adc_state { |
b3201b56 SR |
74 | struct device_node *np; |
75 | struct adc_regs_spear3xx __iomem *adc_base_spear3xx; | |
76 | struct adc_regs_spear6xx __iomem *adc_base_spear6xx; | |
77 | struct clk *clk; | |
78 | struct completion completion; | |
79 | u32 current_clk; | |
80 | u32 sampling_freq; | |
81 | u32 avg_samples; | |
82 | u32 vref_external; | |
83 | u32 value; | |
84 | }; | |
85 | ||
86 | /* | |
87 | * Functions to access some SPEAr ADC register. Abstracted into | |
88 | * static inline functions, because of different register offsets | |
89 | * on different SoC variants (SPEAr300 vs SPEAr600 etc). | |
90 | */ | |
b586e5d9 | 91 | static void spear_adc_set_status(struct spear_adc_state *st, u32 val) |
b3201b56 | 92 | { |
b586e5d9 | 93 | __raw_writel(val, &st->adc_base_spear6xx->status); |
b3201b56 SR |
94 | } |
95 | ||
b586e5d9 | 96 | static void spear_adc_set_clk(struct spear_adc_state *st, u32 val) |
b3201b56 SR |
97 | { |
98 | u32 clk_high, clk_low, count; | |
b586e5d9 | 99 | u32 apb_clk = clk_get_rate(st->clk); |
b3201b56 | 100 | |
ffceca8c | 101 | count = DIV_ROUND_UP(apb_clk, val); |
b3201b56 SR |
102 | clk_low = count / 2; |
103 | clk_high = count - clk_low; | |
b586e5d9 | 104 | st->current_clk = apb_clk / count; |
b3201b56 | 105 | |
b64aef70 | 106 | __raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high), |
b586e5d9 | 107 | &st->adc_base_spear6xx->clk); |
b3201b56 SR |
108 | } |
109 | ||
b586e5d9 | 110 | static void spear_adc_set_ctrl(struct spear_adc_state *st, int n, |
b3201b56 SR |
111 | u32 val) |
112 | { | |
b586e5d9 | 113 | __raw_writel(val, &st->adc_base_spear6xx->ch_ctrl[n]); |
b3201b56 SR |
114 | } |
115 | ||
b586e5d9 | 116 | static u32 spear_adc_get_average(struct spear_adc_state *st) |
b3201b56 | 117 | { |
b586e5d9 JC |
118 | if (of_device_is_compatible(st->np, "st,spear600-adc")) { |
119 | return __raw_readl(&st->adc_base_spear6xx->average.msb) & | |
b64aef70 | 120 | SPEAR_ADC_DATA_MASK; |
b3201b56 | 121 | } else { |
b586e5d9 | 122 | return __raw_readl(&st->adc_base_spear3xx->average) & |
b64aef70 | 123 | SPEAR_ADC_DATA_MASK; |
b3201b56 SR |
124 | } |
125 | } | |
126 | ||
b586e5d9 | 127 | static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate) |
b3201b56 | 128 | { |
b586e5d9 | 129 | if (of_device_is_compatible(st->np, "st,spear600-adc")) { |
b64aef70 | 130 | __raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate), |
b586e5d9 | 131 | &st->adc_base_spear6xx->scan_rate_lo); |
b64aef70 | 132 | __raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate), |
b586e5d9 | 133 | &st->adc_base_spear6xx->scan_rate_hi); |
b3201b56 | 134 | } else { |
b586e5d9 | 135 | __raw_writel(rate, &st->adc_base_spear3xx->scan_rate); |
b3201b56 SR |
136 | } |
137 | } | |
138 | ||
e20d6090 JC |
139 | static int spear_adc_read_raw(struct iio_dev *indio_dev, |
140 | struct iio_chan_spec const *chan, | |
141 | int *val, | |
142 | int *val2, | |
143 | long mask) | |
b3201b56 | 144 | { |
b586e5d9 | 145 | struct spear_adc_state *st = iio_priv(indio_dev); |
b3201b56 SR |
146 | u32 status; |
147 | ||
148 | switch (mask) { | |
b11f98ff | 149 | case IIO_CHAN_INFO_RAW: |
b3201b56 SR |
150 | mutex_lock(&indio_dev->mlock); |
151 | ||
b64aef70 | 152 | status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) | |
b586e5d9 | 153 | SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) | |
b64aef70 JC |
154 | SPEAR_ADC_STATUS_START_CONVERSION | |
155 | SPEAR_ADC_STATUS_ADC_ENABLE; | |
b586e5d9 | 156 | if (st->vref_external == 0) |
b64aef70 | 157 | status |= SPEAR_ADC_STATUS_VREF_INTERNAL; |
b3201b56 | 158 | |
b586e5d9 JC |
159 | spear_adc_set_status(st, status); |
160 | wait_for_completion(&st->completion); /* set by ISR */ | |
161 | *val = st->value; | |
b3201b56 SR |
162 | |
163 | mutex_unlock(&indio_dev->mlock); | |
164 | ||
165 | return IIO_VAL_INT; | |
166 | ||
167 | case IIO_CHAN_INFO_SCALE: | |
b586e5d9 | 168 | *val = st->vref_external; |
b64aef70 | 169 | *val2 = SPEAR_ADC_DATA_BITS; |
bfbab2bc | 170 | return IIO_VAL_FRACTIONAL_LOG2; |
932de74b | 171 | case IIO_CHAN_INFO_SAMP_FREQ: |
b586e5d9 | 172 | *val = st->current_clk; |
932de74b | 173 | return IIO_VAL_INT; |
b3201b56 SR |
174 | } |
175 | ||
176 | return -EINVAL; | |
177 | } | |
178 | ||
932de74b JC |
179 | static int spear_adc_write_raw(struct iio_dev *indio_dev, |
180 | struct iio_chan_spec const *chan, | |
181 | int val, | |
182 | int val2, | |
183 | long mask) | |
184 | { | |
b586e5d9 | 185 | struct spear_adc_state *st = iio_priv(indio_dev); |
932de74b JC |
186 | int ret = 0; |
187 | ||
188 | if (mask != IIO_CHAN_INFO_SAMP_FREQ) | |
189 | return -EINVAL; | |
190 | ||
191 | mutex_lock(&indio_dev->mlock); | |
192 | ||
193 | if ((val < SPEAR_ADC_CLK_MIN) || | |
e8ef49f0 IC |
194 | (val > SPEAR_ADC_CLK_MAX) || |
195 | (val2 != 0)) { | |
932de74b JC |
196 | ret = -EINVAL; |
197 | goto out; | |
198 | } | |
199 | ||
b586e5d9 | 200 | spear_adc_set_clk(st, val); |
932de74b JC |
201 | |
202 | out: | |
203 | mutex_unlock(&indio_dev->mlock); | |
204 | return ret; | |
205 | } | |
206 | ||
b3201b56 SR |
207 | #define SPEAR_ADC_CHAN(idx) { \ |
208 | .type = IIO_VOLTAGE, \ | |
209 | .indexed = 1, \ | |
f1e067ba JC |
210 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
211 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
932de74b | 212 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ |
b3201b56 | 213 | .channel = idx, \ |
b3201b56 SR |
214 | } |
215 | ||
f4e4b955 | 216 | static const struct iio_chan_spec spear_adc_iio_channels[] = { |
b3201b56 SR |
217 | SPEAR_ADC_CHAN(0), |
218 | SPEAR_ADC_CHAN(1), | |
219 | SPEAR_ADC_CHAN(2), | |
220 | SPEAR_ADC_CHAN(3), | |
221 | SPEAR_ADC_CHAN(4), | |
222 | SPEAR_ADC_CHAN(5), | |
223 | SPEAR_ADC_CHAN(6), | |
224 | SPEAR_ADC_CHAN(7), | |
225 | }; | |
226 | ||
227 | static irqreturn_t spear_adc_isr(int irq, void *dev_id) | |
228 | { | |
31f8f066 | 229 | struct spear_adc_state *st = dev_id; |
b3201b56 SR |
230 | |
231 | /* Read value to clear IRQ */ | |
b586e5d9 JC |
232 | st->value = spear_adc_get_average(st); |
233 | complete(&st->completion); | |
b3201b56 SR |
234 | |
235 | return IRQ_HANDLED; | |
236 | } | |
237 | ||
b586e5d9 | 238 | static int spear_adc_configure(struct spear_adc_state *st) |
b3201b56 SR |
239 | { |
240 | int i; | |
241 | ||
242 | /* Reset ADC core */ | |
b586e5d9 JC |
243 | spear_adc_set_status(st, 0); |
244 | __raw_writel(0, &st->adc_base_spear6xx->clk); | |
b3201b56 | 245 | for (i = 0; i < 8; i++) |
b586e5d9 JC |
246 | spear_adc_set_ctrl(st, i, 0); |
247 | spear_adc_set_scanrate(st, 0); | |
b3201b56 | 248 | |
b586e5d9 | 249 | spear_adc_set_clk(st, st->sampling_freq); |
b3201b56 SR |
250 | |
251 | return 0; | |
252 | } | |
253 | ||
b586e5d9 | 254 | static const struct iio_info spear_adc_info = { |
e20d6090 | 255 | .read_raw = &spear_adc_read_raw, |
932de74b | 256 | .write_raw = &spear_adc_write_raw, |
b3201b56 SR |
257 | .driver_module = THIS_MODULE, |
258 | }; | |
259 | ||
4ae1c61f | 260 | static int spear_adc_probe(struct platform_device *pdev) |
b3201b56 SR |
261 | { |
262 | struct device_node *np = pdev->dev.of_node; | |
263 | struct device *dev = &pdev->dev; | |
b586e5d9 | 264 | struct spear_adc_state *st; |
09b9da33 | 265 | struct resource *res; |
e90ba52f | 266 | struct iio_dev *indio_dev = NULL; |
b3201b56 SR |
267 | int ret = -ENODEV; |
268 | int irq; | |
269 | ||
e90ba52f JC |
270 | indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state)); |
271 | if (!indio_dev) { | |
b3201b56 | 272 | dev_err(dev, "failed allocating iio device\n"); |
8c7f6d56 | 273 | return -ENOMEM; |
b3201b56 SR |
274 | } |
275 | ||
e90ba52f | 276 | st = iio_priv(indio_dev); |
b586e5d9 | 277 | st->np = np; |
b3201b56 SR |
278 | |
279 | /* | |
280 | * SPEAr600 has a different register layout than other SPEAr SoC's | |
281 | * (e.g. SPEAr3xx). Let's provide two register base addresses | |
282 | * to support multi-arch kernels. | |
283 | */ | |
09b9da33 AKC |
284 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
285 | st->adc_base_spear6xx = devm_ioremap_resource(&pdev->dev, res); | |
286 | if (IS_ERR(st->adc_base_spear6xx)) | |
287 | return PTR_ERR(st->adc_base_spear6xx); | |
288 | ||
b586e5d9 JC |
289 | st->adc_base_spear3xx = |
290 | (struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx; | |
b3201b56 | 291 | |
b8cc27ca | 292 | st->clk = devm_clk_get(dev, NULL); |
b586e5d9 | 293 | if (IS_ERR(st->clk)) { |
b3201b56 | 294 | dev_err(dev, "failed getting clock\n"); |
09b9da33 | 295 | return PTR_ERR(st->clk); |
b3201b56 SR |
296 | } |
297 | ||
b586e5d9 | 298 | ret = clk_prepare_enable(st->clk); |
b3201b56 SR |
299 | if (ret) { |
300 | dev_err(dev, "failed enabling clock\n"); | |
09b9da33 | 301 | return ret; |
b3201b56 SR |
302 | } |
303 | ||
304 | irq = platform_get_irq(pdev, 0); | |
a47f6e08 | 305 | if (irq <= 0) { |
b3201b56 SR |
306 | dev_err(dev, "failed getting interrupt resource\n"); |
307 | ret = -EINVAL; | |
b8cc27ca | 308 | goto errout2; |
b3201b56 SR |
309 | } |
310 | ||
b64aef70 | 311 | ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME, |
b586e5d9 | 312 | st); |
b3201b56 SR |
313 | if (ret < 0) { |
314 | dev_err(dev, "failed requesting interrupt\n"); | |
b8cc27ca | 315 | goto errout2; |
b3201b56 SR |
316 | } |
317 | ||
318 | if (of_property_read_u32(np, "sampling-frequency", | |
b586e5d9 | 319 | &st->sampling_freq)) { |
b3201b56 SR |
320 | dev_err(dev, "sampling-frequency missing in DT\n"); |
321 | ret = -EINVAL; | |
b8cc27ca | 322 | goto errout2; |
b3201b56 SR |
323 | } |
324 | ||
325 | /* | |
326 | * Optional avg_samples defaults to 0, resulting in single data | |
327 | * conversion | |
328 | */ | |
b586e5d9 | 329 | of_property_read_u32(np, "average-samples", &st->avg_samples); |
b3201b56 SR |
330 | |
331 | /* | |
332 | * Optional vref_external defaults to 0, resulting in internal vref | |
333 | * selection | |
334 | */ | |
b586e5d9 | 335 | of_property_read_u32(np, "vref-external", &st->vref_external); |
b3201b56 | 336 | |
b586e5d9 | 337 | spear_adc_configure(st); |
b3201b56 | 338 | |
e90ba52f | 339 | platform_set_drvdata(pdev, indio_dev); |
b3201b56 | 340 | |
b586e5d9 | 341 | init_completion(&st->completion); |
b3201b56 | 342 | |
e90ba52f JC |
343 | indio_dev->name = SPEAR_ADC_MOD_NAME; |
344 | indio_dev->dev.parent = dev; | |
345 | indio_dev->info = &spear_adc_info; | |
346 | indio_dev->modes = INDIO_DIRECT_MODE; | |
347 | indio_dev->channels = spear_adc_iio_channels; | |
348 | indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels); | |
b3201b56 | 349 | |
e90ba52f | 350 | ret = iio_device_register(indio_dev); |
b3201b56 | 351 | if (ret) |
b8cc27ca | 352 | goto errout2; |
b3201b56 SR |
353 | |
354 | dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq); | |
355 | ||
356 | return 0; | |
357 | ||
b3201b56 | 358 | errout2: |
b8cc27ca | 359 | clk_disable_unprepare(st->clk); |
b3201b56 SR |
360 | return ret; |
361 | } | |
362 | ||
447d4f29 | 363 | static int spear_adc_remove(struct platform_device *pdev) |
b3201b56 | 364 | { |
e90ba52f JC |
365 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); |
366 | struct spear_adc_state *st = iio_priv(indio_dev); | |
b3201b56 | 367 | |
e90ba52f | 368 | iio_device_unregister(indio_dev); |
b586e5d9 | 369 | clk_disable_unprepare(st->clk); |
b3201b56 SR |
370 | |
371 | return 0; | |
372 | } | |
373 | ||
e89b6747 | 374 | #ifdef CONFIG_OF |
b3201b56 SR |
375 | static const struct of_device_id spear_adc_dt_ids[] = { |
376 | { .compatible = "st,spear600-adc", }, | |
377 | { /* sentinel */ } | |
378 | }; | |
379 | MODULE_DEVICE_TABLE(of, spear_adc_dt_ids); | |
e89b6747 | 380 | #endif |
b3201b56 SR |
381 | |
382 | static struct platform_driver spear_adc_driver = { | |
383 | .probe = spear_adc_probe, | |
e543acf0 | 384 | .remove = spear_adc_remove, |
b3201b56 | 385 | .driver = { |
b64aef70 | 386 | .name = SPEAR_ADC_MOD_NAME, |
b3201b56 SR |
387 | .of_match_table = of_match_ptr(spear_adc_dt_ids), |
388 | }, | |
389 | }; | |
390 | ||
391 | module_platform_driver(spear_adc_driver); | |
392 | ||
393 | MODULE_AUTHOR("Stefan Roese <sr@denx.de>"); | |
394 | MODULE_DESCRIPTION("SPEAr ADC driver"); | |
395 | MODULE_LICENSE("GPL"); |