diff options
author | Chanwoo Choi <cw00.choi@samsung.com> | 2014-07-21 22:04:00 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2014-07-23 16:55:47 -0400 |
commit | e49d99e0ecc88191c2e51a6535b1d0df635f1f3b (patch) | |
tree | b0c0e52ffa946189f39a0a266022d04244d94305 /drivers/iio/adc | |
parent | d3f1621960223799b19bca9b503c333a3833d7f7 (diff) |
iio: adc: exynos_adc: Add exynos_adc_data structure to improve readability
This patchset add 'exynos_adc_data' structure which includes some functions
to control ADC operation and specific data according to ADC version (v1 or v2).
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
Reviewed-by: Tomasz Figa <t.figa@samsung.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r-- | drivers/iio/adc/exynos_adc.c | 226 |
1 files changed, 147 insertions, 79 deletions
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 010578f1d762..66d27c638d83 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c | |||
@@ -39,11 +39,6 @@ | |||
39 | #include <linux/iio/machine.h> | 39 | #include <linux/iio/machine.h> |
40 | #include <linux/iio/driver.h> | 40 | #include <linux/iio/driver.h> |
41 | 41 | ||
42 | enum adc_version { | ||
43 | ADC_V1, | ||
44 | ADC_V2 | ||
45 | }; | ||
46 | |||
47 | /* EXYNOS4412/5250 ADC_V1 registers definitions */ | 42 | /* EXYNOS4412/5250 ADC_V1 registers definitions */ |
48 | #define ADC_V1_CON(x) ((x) + 0x00) | 43 | #define ADC_V1_CON(x) ((x) + 0x00) |
49 | #define ADC_V1_DLY(x) ((x) + 0x08) | 44 | #define ADC_V1_DLY(x) ((x) + 0x08) |
@@ -85,6 +80,7 @@ enum adc_version { | |||
85 | #define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100)) | 80 | #define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100)) |
86 | 81 | ||
87 | struct exynos_adc { | 82 | struct exynos_adc { |
83 | struct exynos_adc_data *data; | ||
88 | void __iomem *regs; | 84 | void __iomem *regs; |
89 | void __iomem *enable_reg; | 85 | void __iomem *enable_reg; |
90 | struct clk *clk; | 86 | struct clk *clk; |
@@ -97,43 +93,139 @@ struct exynos_adc { | |||
97 | unsigned int version; | 93 | unsigned int version; |
98 | }; | 94 | }; |
99 | 95 | ||
100 | static const struct of_device_id exynos_adc_match[] = { | 96 | struct exynos_adc_data { |
101 | { .compatible = "samsung,exynos-adc-v1", .data = (void *)ADC_V1 }, | 97 | int num_channels; |
102 | { .compatible = "samsung,exynos-adc-v2", .data = (void *)ADC_V2 }, | 98 | |
103 | {}, | 99 | void (*init_hw)(struct exynos_adc *info); |
100 | void (*exit_hw)(struct exynos_adc *info); | ||
101 | void (*clear_irq)(struct exynos_adc *info); | ||
102 | void (*start_conv)(struct exynos_adc *info, unsigned long addr); | ||
104 | }; | 103 | }; |
105 | MODULE_DEVICE_TABLE(of, exynos_adc_match); | ||
106 | 104 | ||
107 | static inline unsigned int exynos_adc_get_version(struct platform_device *pdev) | 105 | static void exynos_adc_v1_init_hw(struct exynos_adc *info) |
108 | { | 106 | { |
109 | const struct of_device_id *match; | 107 | u32 con1; |
110 | 108 | ||
111 | match = of_match_node(exynos_adc_match, pdev->dev.of_node); | 109 | writel(1, info->enable_reg); |
112 | return (unsigned int)match->data; | 110 | |
111 | /* set default prescaler values and Enable prescaler */ | ||
112 | con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; | ||
113 | |||
114 | /* Enable 12-bit ADC resolution */ | ||
115 | con1 |= ADC_V1_CON_RES; | ||
116 | writel(con1, ADC_V1_CON(info->regs)); | ||
117 | } | ||
118 | |||
119 | static void exynos_adc_v1_exit_hw(struct exynos_adc *info) | ||
120 | { | ||
121 | u32 con; | ||
122 | |||
123 | writel(0, info->enable_reg); | ||
124 | |||
125 | con = readl(ADC_V1_CON(info->regs)); | ||
126 | con |= ADC_V1_CON_STANDBY; | ||
127 | writel(con, ADC_V1_CON(info->regs)); | ||
128 | } | ||
129 | |||
130 | static void exynos_adc_v1_clear_irq(struct exynos_adc *info) | ||
131 | { | ||
132 | writel(1, ADC_V1_INTCLR(info->regs)); | ||
113 | } | 133 | } |
114 | 134 | ||
115 | static void exynos_adc_hw_init(struct exynos_adc *info) | 135 | static void exynos_adc_v1_start_conv(struct exynos_adc *info, |
136 | unsigned long addr) | ||
137 | { | ||
138 | u32 con1; | ||
139 | |||
140 | writel(addr, ADC_V1_MUX(info->regs)); | ||
141 | |||
142 | con1 = readl(ADC_V1_CON(info->regs)); | ||
143 | writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); | ||
144 | } | ||
145 | |||
146 | static const struct exynos_adc_data exynos_adc_v1_data = { | ||
147 | .num_channels = MAX_ADC_V1_CHANNELS, | ||
148 | |||
149 | .init_hw = exynos_adc_v1_init_hw, | ||
150 | .exit_hw = exynos_adc_v1_exit_hw, | ||
151 | .clear_irq = exynos_adc_v1_clear_irq, | ||
152 | .start_conv = exynos_adc_v1_start_conv, | ||
153 | }; | ||
154 | |||
155 | static void exynos_adc_v2_init_hw(struct exynos_adc *info) | ||
116 | { | 156 | { |
117 | u32 con1, con2; | 157 | u32 con1, con2; |
118 | 158 | ||
119 | if (info->version == ADC_V2) { | 159 | writel(1, info->enable_reg); |
120 | con1 = ADC_V2_CON1_SOFT_RESET; | ||
121 | writel(con1, ADC_V2_CON1(info->regs)); | ||
122 | 160 | ||
123 | con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | | 161 | con1 = ADC_V2_CON1_SOFT_RESET; |
124 | ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); | 162 | writel(con1, ADC_V2_CON1(info->regs)); |
125 | writel(con2, ADC_V2_CON2(info->regs)); | ||
126 | 163 | ||
127 | /* Enable interrupts */ | 164 | con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | |
128 | writel(1, ADC_V2_INT_EN(info->regs)); | 165 | ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); |
129 | } else { | 166 | writel(con2, ADC_V2_CON2(info->regs)); |
130 | /* set default prescaler values and Enable prescaler */ | ||
131 | con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; | ||
132 | 167 | ||
133 | /* Enable 12-bit ADC resolution */ | 168 | /* Enable interrupts */ |
134 | con1 |= ADC_V1_CON_RES; | 169 | writel(1, ADC_V2_INT_EN(info->regs)); |
135 | writel(con1, ADC_V1_CON(info->regs)); | 170 | } |
136 | } | 171 | |
172 | static void exynos_adc_v2_exit_hw(struct exynos_adc *info) | ||
173 | { | ||
174 | u32 con; | ||
175 | |||
176 | writel(0, info->enable_reg); | ||
177 | |||
178 | con = readl(ADC_V2_CON1(info->regs)); | ||
179 | con &= ~ADC_CON_EN_START; | ||
180 | writel(con, ADC_V2_CON1(info->regs)); | ||
181 | } | ||
182 | |||
183 | static void exynos_adc_v2_clear_irq(struct exynos_adc *info) | ||
184 | { | ||
185 | writel(1, ADC_V2_INT_ST(info->regs)); | ||
186 | } | ||
187 | |||
188 | static void exynos_adc_v2_start_conv(struct exynos_adc *info, | ||
189 | unsigned long addr) | ||
190 | { | ||
191 | u32 con1, con2; | ||
192 | |||
193 | con2 = readl(ADC_V2_CON2(info->regs)); | ||
194 | con2 &= ~ADC_V2_CON2_ACH_MASK; | ||
195 | con2 |= ADC_V2_CON2_ACH_SEL(addr); | ||
196 | writel(con2, ADC_V2_CON2(info->regs)); | ||
197 | |||
198 | con1 = readl(ADC_V2_CON1(info->regs)); | ||
199 | writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs)); | ||
200 | } | ||
201 | |||
202 | static const struct exynos_adc_data exynos_adc_v2_data = { | ||
203 | .num_channels = MAX_ADC_V2_CHANNELS, | ||
204 | |||
205 | .init_hw = exynos_adc_v2_init_hw, | ||
206 | .exit_hw = exynos_adc_v2_exit_hw, | ||
207 | .clear_irq = exynos_adc_v2_clear_irq, | ||
208 | .start_conv = exynos_adc_v2_start_conv, | ||
209 | }; | ||
210 | |||
211 | static const struct of_device_id exynos_adc_match[] = { | ||
212 | { | ||
213 | .compatible = "samsung,exynos-adc-v1", | ||
214 | .data = &exynos_adc_v1_data, | ||
215 | }, { | ||
216 | .compatible = "samsung,exynos-adc-v2", | ||
217 | .data = &exynos_adc_v2_data, | ||
218 | }, | ||
219 | {}, | ||
220 | }; | ||
221 | MODULE_DEVICE_TABLE(of, exynos_adc_match); | ||
222 | |||
223 | static struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev) | ||
224 | { | ||
225 | const struct of_device_id *match; | ||
226 | |||
227 | match = of_match_node(exynos_adc_match, pdev->dev.of_node); | ||
228 | return (struct exynos_adc_data *)match->data; | ||
137 | } | 229 | } |
138 | 230 | ||
139 | static int exynos_read_raw(struct iio_dev *indio_dev, | 231 | static int exynos_read_raw(struct iio_dev *indio_dev, |
@@ -144,7 +236,6 @@ static int exynos_read_raw(struct iio_dev *indio_dev, | |||
144 | { | 236 | { |
145 | struct exynos_adc *info = iio_priv(indio_dev); | 237 | struct exynos_adc *info = iio_priv(indio_dev); |
146 | unsigned long timeout; | 238 | unsigned long timeout; |
147 | u32 con1, con2; | ||
148 | int ret; | 239 | int ret; |
149 | 240 | ||
150 | if (mask != IIO_CHAN_INFO_RAW) | 241 | if (mask != IIO_CHAN_INFO_RAW) |
@@ -154,28 +245,15 @@ static int exynos_read_raw(struct iio_dev *indio_dev, | |||
154 | reinit_completion(&info->completion); | 245 | reinit_completion(&info->completion); |
155 | 246 | ||
156 | /* Select the channel to be used and Trigger conversion */ | 247 | /* Select the channel to be used and Trigger conversion */ |
157 | if (info->version == ADC_V2) { | 248 | if (info->data->start_conv) |
158 | con2 = readl(ADC_V2_CON2(info->regs)); | 249 | info->data->start_conv(info, chan->address); |
159 | con2 &= ~ADC_V2_CON2_ACH_MASK; | ||
160 | con2 |= ADC_V2_CON2_ACH_SEL(chan->address); | ||
161 | writel(con2, ADC_V2_CON2(info->regs)); | ||
162 | |||
163 | con1 = readl(ADC_V2_CON1(info->regs)); | ||
164 | writel(con1 | ADC_CON_EN_START, | ||
165 | ADC_V2_CON1(info->regs)); | ||
166 | } else { | ||
167 | writel(chan->address, ADC_V1_MUX(info->regs)); | ||
168 | |||
169 | con1 = readl(ADC_V1_CON(info->regs)); | ||
170 | writel(con1 | ADC_CON_EN_START, | ||
171 | ADC_V1_CON(info->regs)); | ||
172 | } | ||
173 | 250 | ||
174 | timeout = wait_for_completion_timeout | 251 | timeout = wait_for_completion_timeout |
175 | (&info->completion, EXYNOS_ADC_TIMEOUT); | 252 | (&info->completion, EXYNOS_ADC_TIMEOUT); |
176 | if (timeout == 0) { | 253 | if (timeout == 0) { |
177 | dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n"); | 254 | dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n"); |
178 | exynos_adc_hw_init(info); | 255 | if (info->data->init_hw) |
256 | info->data->init_hw(info); | ||
179 | ret = -ETIMEDOUT; | 257 | ret = -ETIMEDOUT; |
180 | } else { | 258 | } else { |
181 | *val = info->value; | 259 | *val = info->value; |
@@ -193,13 +271,11 @@ static irqreturn_t exynos_adc_isr(int irq, void *dev_id) | |||
193 | struct exynos_adc *info = (struct exynos_adc *)dev_id; | 271 | struct exynos_adc *info = (struct exynos_adc *)dev_id; |
194 | 272 | ||
195 | /* Read value */ | 273 | /* Read value */ |
196 | info->value = readl(ADC_V1_DATX(info->regs)) & | 274 | info->value = readl(ADC_V1_DATX(info->regs)) & ADC_DATX_MASK; |
197 | ADC_DATX_MASK; | 275 | |
198 | /* clear irq */ | 276 | /* clear irq */ |
199 | if (info->version == ADC_V2) | 277 | if (info->data->clear_irq) |
200 | writel(1, ADC_V2_INT_ST(info->regs)); | 278 | info->data->clear_irq(info); |
201 | else | ||
202 | writel(1, ADC_V1_INTCLR(info->regs)); | ||
203 | 279 | ||
204 | complete(&info->completion); | 280 | complete(&info->completion); |
205 | 281 | ||
@@ -277,6 +353,12 @@ static int exynos_adc_probe(struct platform_device *pdev) | |||
277 | 353 | ||
278 | info = iio_priv(indio_dev); | 354 | info = iio_priv(indio_dev); |
279 | 355 | ||
356 | info->data = exynos_adc_get_data(pdev); | ||
357 | if (!info->data) { | ||
358 | dev_err(&pdev->dev, "failed getting exynos_adc_data\n"); | ||
359 | return -EINVAL; | ||
360 | } | ||
361 | |||
280 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 362 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
281 | info->regs = devm_ioremap_resource(&pdev->dev, mem); | 363 | info->regs = devm_ioremap_resource(&pdev->dev, mem); |
282 | if (IS_ERR(info->regs)) | 364 | if (IS_ERR(info->regs)) |
@@ -319,10 +401,6 @@ static int exynos_adc_probe(struct platform_device *pdev) | |||
319 | if (ret) | 401 | if (ret) |
320 | goto err_disable_reg; | 402 | goto err_disable_reg; |
321 | 403 | ||
322 | writel(1, info->enable_reg); | ||
323 | |||
324 | info->version = exynos_adc_get_version(pdev); | ||
325 | |||
326 | platform_set_drvdata(pdev, indio_dev); | 404 | platform_set_drvdata(pdev, indio_dev); |
327 | 405 | ||
328 | indio_dev->name = dev_name(&pdev->dev); | 406 | indio_dev->name = dev_name(&pdev->dev); |
@@ -331,11 +409,7 @@ static int exynos_adc_probe(struct platform_device *pdev) | |||
331 | indio_dev->info = &exynos_adc_iio_info; | 409 | indio_dev->info = &exynos_adc_iio_info; |
332 | indio_dev->modes = INDIO_DIRECT_MODE; | 410 | indio_dev->modes = INDIO_DIRECT_MODE; |
333 | indio_dev->channels = exynos_adc_iio_channels; | 411 | indio_dev->channels = exynos_adc_iio_channels; |
334 | 412 | indio_dev->num_channels = info->data->num_channels; | |
335 | if (info->version == ADC_V1) | ||
336 | indio_dev->num_channels = MAX_ADC_V1_CHANNELS; | ||
337 | else | ||
338 | indio_dev->num_channels = MAX_ADC_V2_CHANNELS; | ||
339 | 413 | ||
340 | ret = request_irq(info->irq, exynos_adc_isr, | 414 | ret = request_irq(info->irq, exynos_adc_isr, |
341 | 0, dev_name(&pdev->dev), info); | 415 | 0, dev_name(&pdev->dev), info); |
@@ -349,7 +423,8 @@ static int exynos_adc_probe(struct platform_device *pdev) | |||
349 | if (ret) | 423 | if (ret) |
350 | goto err_irq; | 424 | goto err_irq; |
351 | 425 | ||
352 | exynos_adc_hw_init(info); | 426 | if (info->data->init_hw) |
427 | info->data->init_hw(info); | ||
353 | 428 | ||
354 | ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev); | 429 | ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev); |
355 | if (ret < 0) { | 430 | if (ret < 0) { |
@@ -366,7 +441,8 @@ err_of_populate: | |||
366 | err_irq: | 441 | err_irq: |
367 | free_irq(info->irq, info); | 442 | free_irq(info->irq, info); |
368 | err_disable_clk: | 443 | err_disable_clk: |
369 | writel(0, info->enable_reg); | 444 | if (info->data->exit_hw) |
445 | info->data->exit_hw(info); | ||
370 | clk_disable_unprepare(info->clk); | 446 | clk_disable_unprepare(info->clk); |
371 | err_disable_reg: | 447 | err_disable_reg: |
372 | regulator_disable(info->vdd); | 448 | regulator_disable(info->vdd); |
@@ -382,7 +458,8 @@ static int exynos_adc_remove(struct platform_device *pdev) | |||
382 | exynos_adc_remove_devices); | 458 | exynos_adc_remove_devices); |
383 | iio_device_unregister(indio_dev); | 459 | iio_device_unregister(indio_dev); |
384 | free_irq(info->irq, info); | 460 | free_irq(info->irq, info); |
385 | writel(0, info->enable_reg); | 461 | if (info->data->exit_hw) |
462 | info->data->exit_hw(info); | ||
386 | clk_disable_unprepare(info->clk); | 463 | clk_disable_unprepare(info->clk); |
387 | regulator_disable(info->vdd); | 464 | regulator_disable(info->vdd); |
388 | 465 | ||
@@ -394,19 +471,10 @@ static int exynos_adc_suspend(struct device *dev) | |||
394 | { | 471 | { |
395 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | 472 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
396 | struct exynos_adc *info = iio_priv(indio_dev); | 473 | struct exynos_adc *info = iio_priv(indio_dev); |
397 | u32 con; | ||
398 | 474 | ||
399 | if (info->version == ADC_V2) { | 475 | if (info->data->exit_hw) |
400 | con = readl(ADC_V2_CON1(info->regs)); | 476 | info->data->exit_hw(info); |
401 | con &= ~ADC_CON_EN_START; | ||
402 | writel(con, ADC_V2_CON1(info->regs)); | ||
403 | } else { | ||
404 | con = readl(ADC_V1_CON(info->regs)); | ||
405 | con |= ADC_V1_CON_STANDBY; | ||
406 | writel(con, ADC_V1_CON(info->regs)); | ||
407 | } | ||
408 | 477 | ||
409 | writel(0, info->enable_reg); | ||
410 | clk_disable_unprepare(info->clk); | 478 | clk_disable_unprepare(info->clk); |
411 | regulator_disable(info->vdd); | 479 | regulator_disable(info->vdd); |
412 | 480 | ||
@@ -427,8 +495,8 @@ static int exynos_adc_resume(struct device *dev) | |||
427 | if (ret) | 495 | if (ret) |
428 | return ret; | 496 | return ret; |
429 | 497 | ||
430 | writel(1, info->enable_reg); | 498 | if (info->data->init_hw) |
431 | exynos_adc_hw_init(info); | 499 | info->data->init_hw(info); |
432 | 500 | ||
433 | return 0; | 501 | return 0; |
434 | } | 502 | } |