diff options
author | Chanwoo Choi <cw00.choi@samsung.com> | 2014-07-28 08:44:00 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2014-08-07 12:48:44 -0400 |
commit | 145b0a5d18565615724045dbc2ada32324faa395 (patch) | |
tree | e70377413b61a0d088e9033fedf714e02168db42 /drivers/iio/adc | |
parent | 249535d894216f5dcd922accfb435d32d417d56f (diff) |
iio: adc: exynos_adc: Add support for s3c24xx ADC
This patch add support for s3c2410/s3c2416/s3c2440/s3c2443 ADC. The s3c24xx
is alomost same as ADCv1. But, There are a little difference as following:
- ADCMUX register address
- ADCDAT mask (10 bit or 12 bit ADC resolution according to SoC version)
- s3c24xx/s3c64xx has not included ADC_PHY enable register
Signed-off-by: Chanwoo Choi <cw00.choi@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/Kconfig | 2 | ||||
-rw-r--r-- | drivers/iio/adc/exynos_adc.c | 109 |
2 files changed, 101 insertions, 10 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 6655fd41acd9..88bdc8f612e2 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig | |||
@@ -129,7 +129,7 @@ config AT91_ADC | |||
129 | 129 | ||
130 | config EXYNOS_ADC | 130 | config EXYNOS_ADC |
131 | tristate "Exynos ADC driver support" | 131 | tristate "Exynos ADC driver support" |
132 | depends on ARCH_EXYNOS || (OF && COMPILE_TEST) | 132 | depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) |
133 | help | 133 | help |
134 | Core support for the ADC block found in the Samsung EXYNOS series | 134 | Core support for the ADC block found in the Samsung EXYNOS series |
135 | of SoCs for drivers such as the touchscreen and hwmon to use to share | 135 | of SoCs for drivers such as the touchscreen and hwmon to use to share |
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 62631a778b1f..c59012d2d06f 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c | |||
@@ -47,6 +47,9 @@ | |||
47 | #define ADC_V1_INTCLR(x) ((x) + 0x18) | 47 | #define ADC_V1_INTCLR(x) ((x) + 0x18) |
48 | #define ADC_V1_MUX(x) ((x) + 0x1c) | 48 | #define ADC_V1_MUX(x) ((x) + 0x1c) |
49 | 49 | ||
50 | /* S3C2410 ADC registers definitions */ | ||
51 | #define ADC_S3C2410_MUX(x) ((x) + 0x18) | ||
52 | |||
50 | /* Future ADC_V2 registers definitions */ | 53 | /* Future ADC_V2 registers definitions */ |
51 | #define ADC_V2_CON1(x) ((x) + 0x00) | 54 | #define ADC_V2_CON1(x) ((x) + 0x00) |
52 | #define ADC_V2_CON2(x) ((x) + 0x04) | 55 | #define ADC_V2_CON2(x) ((x) + 0x04) |
@@ -63,6 +66,8 @@ | |||
63 | 66 | ||
64 | /* Bit definitions for S3C2410 ADC */ | 67 | /* Bit definitions for S3C2410 ADC */ |
65 | #define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3) | 68 | #define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3) |
69 | #define ADC_S3C2410_DATX_MASK 0x3FF | ||
70 | #define ADC_S3C2416_CON_RES_SEL (1u << 3) | ||
66 | 71 | ||
67 | /* Bit definitions for ADC_V2 */ | 72 | /* Bit definitions for ADC_V2 */ |
68 | #define ADC_V2_CON1_SOFT_RESET (1u << 2) | 73 | #define ADC_V2_CON1_SOFT_RESET (1u << 2) |
@@ -80,6 +85,7 @@ | |||
80 | 85 | ||
81 | /* Bit definitions common for ADC_V1 and ADC_V2 */ | 86 | /* Bit definitions common for ADC_V1 and ADC_V2 */ |
82 | #define ADC_CON_EN_START (1u << 0) | 87 | #define ADC_CON_EN_START (1u << 0) |
88 | #define ADC_CON_EN_START_MASK (0x3 << 0) | ||
83 | #define ADC_DATX_MASK 0xFFF | 89 | #define ADC_DATX_MASK 0xFFF |
84 | 90 | ||
85 | #define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100)) | 91 | #define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100)) |
@@ -103,6 +109,8 @@ struct exynos_adc { | |||
103 | struct exynos_adc_data { | 109 | struct exynos_adc_data { |
104 | int num_channels; | 110 | int num_channels; |
105 | bool needs_sclk; | 111 | bool needs_sclk; |
112 | bool needs_adc_phy; | ||
113 | u32 mask; | ||
106 | 114 | ||
107 | void (*init_hw)(struct exynos_adc *info); | 115 | void (*init_hw)(struct exynos_adc *info); |
108 | void (*exit_hw)(struct exynos_adc *info); | 116 | void (*exit_hw)(struct exynos_adc *info); |
@@ -174,7 +182,8 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info) | |||
174 | { | 182 | { |
175 | u32 con1; | 183 | u32 con1; |
176 | 184 | ||
177 | writel(1, info->enable_reg); | 185 | if (info->data->needs_adc_phy) |
186 | writel(1, info->enable_reg); | ||
178 | 187 | ||
179 | /* set default prescaler values and Enable prescaler */ | 188 | /* set default prescaler values and Enable prescaler */ |
180 | con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; | 189 | con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; |
@@ -188,7 +197,8 @@ static void exynos_adc_v1_exit_hw(struct exynos_adc *info) | |||
188 | { | 197 | { |
189 | u32 con; | 198 | u32 con; |
190 | 199 | ||
191 | writel(0, info->enable_reg); | 200 | if (info->data->needs_adc_phy) |
201 | writel(0, info->enable_reg); | ||
192 | 202 | ||
193 | con = readl(ADC_V1_CON(info->regs)); | 203 | con = readl(ADC_V1_CON(info->regs)); |
194 | con |= ADC_V1_CON_STANDBY; | 204 | con |= ADC_V1_CON_STANDBY; |
@@ -213,6 +223,8 @@ static void exynos_adc_v1_start_conv(struct exynos_adc *info, | |||
213 | 223 | ||
214 | static const struct exynos_adc_data exynos_adc_v1_data = { | 224 | static const struct exynos_adc_data exynos_adc_v1_data = { |
215 | .num_channels = MAX_ADC_V1_CHANNELS, | 225 | .num_channels = MAX_ADC_V1_CHANNELS, |
226 | .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ | ||
227 | .needs_adc_phy = true, | ||
216 | 228 | ||
217 | .init_hw = exynos_adc_v1_init_hw, | 229 | .init_hw = exynos_adc_v1_init_hw, |
218 | .exit_hw = exynos_adc_v1_exit_hw, | 230 | .exit_hw = exynos_adc_v1_exit_hw, |
@@ -220,6 +232,53 @@ static const struct exynos_adc_data exynos_adc_v1_data = { | |||
220 | .start_conv = exynos_adc_v1_start_conv, | 232 | .start_conv = exynos_adc_v1_start_conv, |
221 | }; | 233 | }; |
222 | 234 | ||
235 | static void exynos_adc_s3c2416_start_conv(struct exynos_adc *info, | ||
236 | unsigned long addr) | ||
237 | { | ||
238 | u32 con1; | ||
239 | |||
240 | /* Enable 12 bit ADC resolution */ | ||
241 | con1 = readl(ADC_V1_CON(info->regs)); | ||
242 | con1 |= ADC_S3C2416_CON_RES_SEL; | ||
243 | writel(con1, ADC_V1_CON(info->regs)); | ||
244 | |||
245 | /* Select channel for S3C2416 */ | ||
246 | writel(addr, ADC_S3C2410_MUX(info->regs)); | ||
247 | |||
248 | con1 = readl(ADC_V1_CON(info->regs)); | ||
249 | writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); | ||
250 | } | ||
251 | |||
252 | static struct exynos_adc_data const exynos_adc_s3c2416_data = { | ||
253 | .num_channels = MAX_ADC_V1_CHANNELS, | ||
254 | .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ | ||
255 | |||
256 | .init_hw = exynos_adc_v1_init_hw, | ||
257 | .exit_hw = exynos_adc_v1_exit_hw, | ||
258 | .start_conv = exynos_adc_s3c2416_start_conv, | ||
259 | }; | ||
260 | |||
261 | static void exynos_adc_s3c2443_start_conv(struct exynos_adc *info, | ||
262 | unsigned long addr) | ||
263 | { | ||
264 | u32 con1; | ||
265 | |||
266 | /* Select channel for S3C2433 */ | ||
267 | writel(addr, ADC_S3C2410_MUX(info->regs)); | ||
268 | |||
269 | con1 = readl(ADC_V1_CON(info->regs)); | ||
270 | writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); | ||
271 | } | ||
272 | |||
273 | static struct exynos_adc_data const exynos_adc_s3c2443_data = { | ||
274 | .num_channels = MAX_ADC_V1_CHANNELS, | ||
275 | .mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */ | ||
276 | |||
277 | .init_hw = exynos_adc_v1_init_hw, | ||
278 | .exit_hw = exynos_adc_v1_exit_hw, | ||
279 | .start_conv = exynos_adc_s3c2443_start_conv, | ||
280 | }; | ||
281 | |||
223 | static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info, | 282 | static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info, |
224 | unsigned long addr) | 283 | unsigned long addr) |
225 | { | 284 | { |
@@ -231,8 +290,18 @@ static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info, | |||
231 | writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); | 290 | writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); |
232 | } | 291 | } |
233 | 292 | ||
293 | static struct exynos_adc_data const exynos_adc_s3c24xx_data = { | ||
294 | .num_channels = MAX_ADC_V1_CHANNELS, | ||
295 | .mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */ | ||
296 | |||
297 | .init_hw = exynos_adc_v1_init_hw, | ||
298 | .exit_hw = exynos_adc_v1_exit_hw, | ||
299 | .start_conv = exynos_adc_s3c64xx_start_conv, | ||
300 | }; | ||
301 | |||
234 | static struct exynos_adc_data const exynos_adc_s3c64xx_data = { | 302 | static struct exynos_adc_data const exynos_adc_s3c64xx_data = { |
235 | .num_channels = MAX_ADC_V1_CHANNELS, | 303 | .num_channels = MAX_ADC_V1_CHANNELS, |
304 | .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ | ||
236 | 305 | ||
237 | .init_hw = exynos_adc_v1_init_hw, | 306 | .init_hw = exynos_adc_v1_init_hw, |
238 | .exit_hw = exynos_adc_v1_exit_hw, | 307 | .exit_hw = exynos_adc_v1_exit_hw, |
@@ -244,7 +313,8 @@ static void exynos_adc_v2_init_hw(struct exynos_adc *info) | |||
244 | { | 313 | { |
245 | u32 con1, con2; | 314 | u32 con1, con2; |
246 | 315 | ||
247 | writel(1, info->enable_reg); | 316 | if (info->data->needs_adc_phy) |
317 | writel(1, info->enable_reg); | ||
248 | 318 | ||
249 | con1 = ADC_V2_CON1_SOFT_RESET; | 319 | con1 = ADC_V2_CON1_SOFT_RESET; |
250 | writel(con1, ADC_V2_CON1(info->regs)); | 320 | writel(con1, ADC_V2_CON1(info->regs)); |
@@ -261,7 +331,8 @@ static void exynos_adc_v2_exit_hw(struct exynos_adc *info) | |||
261 | { | 331 | { |
262 | u32 con; | 332 | u32 con; |
263 | 333 | ||
264 | writel(0, info->enable_reg); | 334 | if (info->data->needs_adc_phy) |
335 | writel(0, info->enable_reg); | ||
265 | 336 | ||
266 | con = readl(ADC_V2_CON1(info->regs)); | 337 | con = readl(ADC_V2_CON1(info->regs)); |
267 | con &= ~ADC_CON_EN_START; | 338 | con &= ~ADC_CON_EN_START; |
@@ -289,6 +360,8 @@ static void exynos_adc_v2_start_conv(struct exynos_adc *info, | |||
289 | 360 | ||
290 | static const struct exynos_adc_data exynos_adc_v2_data = { | 361 | static const struct exynos_adc_data exynos_adc_v2_data = { |
291 | .num_channels = MAX_ADC_V2_CHANNELS, | 362 | .num_channels = MAX_ADC_V2_CHANNELS, |
363 | .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ | ||
364 | .needs_adc_phy = true, | ||
292 | 365 | ||
293 | .init_hw = exynos_adc_v2_init_hw, | 366 | .init_hw = exynos_adc_v2_init_hw, |
294 | .exit_hw = exynos_adc_v2_exit_hw, | 367 | .exit_hw = exynos_adc_v2_exit_hw, |
@@ -298,7 +371,9 @@ static const struct exynos_adc_data exynos_adc_v2_data = { | |||
298 | 371 | ||
299 | static const struct exynos_adc_data exynos3250_adc_data = { | 372 | static const struct exynos_adc_data exynos3250_adc_data = { |
300 | .num_channels = MAX_EXYNOS3250_ADC_CHANNELS, | 373 | .num_channels = MAX_EXYNOS3250_ADC_CHANNELS, |
374 | .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ | ||
301 | .needs_sclk = true, | 375 | .needs_sclk = true, |
376 | .needs_adc_phy = true, | ||
302 | 377 | ||
303 | .init_hw = exynos_adc_v2_init_hw, | 378 | .init_hw = exynos_adc_v2_init_hw, |
304 | .exit_hw = exynos_adc_v2_exit_hw, | 379 | .exit_hw = exynos_adc_v2_exit_hw, |
@@ -308,6 +383,18 @@ static const struct exynos_adc_data exynos3250_adc_data = { | |||
308 | 383 | ||
309 | static const struct of_device_id exynos_adc_match[] = { | 384 | static const struct of_device_id exynos_adc_match[] = { |
310 | { | 385 | { |
386 | .compatible = "samsung,s3c2410-adc", | ||
387 | .data = &exynos_adc_s3c24xx_data, | ||
388 | }, { | ||
389 | .compatible = "samsung,s3c2416-adc", | ||
390 | .data = &exynos_adc_s3c2416_data, | ||
391 | }, { | ||
392 | .compatible = "samsung,s3c2440-adc", | ||
393 | .data = &exynos_adc_s3c24xx_data, | ||
394 | }, { | ||
395 | .compatible = "samsung,s3c2443-adc", | ||
396 | .data = &exynos_adc_s3c2443_data, | ||
397 | }, { | ||
311 | .compatible = "samsung,s3c6410-adc", | 398 | .compatible = "samsung,s3c6410-adc", |
312 | .data = &exynos_adc_s3c64xx_data, | 399 | .data = &exynos_adc_s3c64xx_data, |
313 | }, { | 400 | }, { |
@@ -373,9 +460,10 @@ static int exynos_read_raw(struct iio_dev *indio_dev, | |||
373 | static irqreturn_t exynos_adc_isr(int irq, void *dev_id) | 460 | static irqreturn_t exynos_adc_isr(int irq, void *dev_id) |
374 | { | 461 | { |
375 | struct exynos_adc *info = (struct exynos_adc *)dev_id; | 462 | struct exynos_adc *info = (struct exynos_adc *)dev_id; |
463 | u32 mask = info->data->mask; | ||
376 | 464 | ||
377 | /* Read value */ | 465 | /* Read value */ |
378 | info->value = readl(ADC_V1_DATX(info->regs)) & ADC_DATX_MASK; | 466 | info->value = readl(ADC_V1_DATX(info->regs)) & mask; |
379 | 467 | ||
380 | /* clear irq */ | 468 | /* clear irq */ |
381 | if (info->data->clear_irq) | 469 | if (info->data->clear_irq) |
@@ -468,10 +556,13 @@ static int exynos_adc_probe(struct platform_device *pdev) | |||
468 | if (IS_ERR(info->regs)) | 556 | if (IS_ERR(info->regs)) |
469 | return PTR_ERR(info->regs); | 557 | return PTR_ERR(info->regs); |
470 | 558 | ||
471 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); | 559 | |
472 | info->enable_reg = devm_ioremap_resource(&pdev->dev, mem); | 560 | if (info->data->needs_adc_phy) { |
473 | if (IS_ERR(info->enable_reg)) | 561 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
474 | return PTR_ERR(info->enable_reg); | 562 | info->enable_reg = devm_ioremap_resource(&pdev->dev, mem); |
563 | if (IS_ERR(info->enable_reg)) | ||
564 | return PTR_ERR(info->enable_reg); | ||
565 | } | ||
475 | 566 | ||
476 | irq = platform_get_irq(pdev, 0); | 567 | irq = platform_get_irq(pdev, 0); |
477 | if (irq < 0) { | 568 | if (irq < 0) { |