[U-Boot] [PATCH 6/7] Exynos: add internal ADC driver

Simon Glass
2015-09-01T00:33:28Z

Hi Przemyslaw,

On 28 August 2015 at 07:59, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This commit adds driver for Voltage Level Monitor based
> on 12-bit resolution internal ADC with 9-channel multiplexer.
>
> New function:
> - exynos_adc_read_channel(int channel) - returns an 12-bit average
> value after 8 time conversion, done by the hardware.
> The default sample rate is: 600kSPS. The channel range: 0-9.
>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> ---
> arch/arm/mach-exynos/Makefile | 1 +
> arch/arm/mach-exynos/adc.c | 83 +++++++++++++++++++++++++++++++++
> arch/arm/mach-exynos/include/mach/adc.h | 45 ++++++++++++++++++
> arch/arm/mach-exynos/include/mach/cpu.h | 4 +-
> 4 files changed, 131 insertions(+), 2 deletions(-)
> create mode 100644 arch/arm/mach-exynos/adc.c

Can we add this to the device tree? Maybe we should even have a driver
model uclass for ADC?

>
> diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
> index 8542f89..4b7f91f 100644
> --- a/arch/arm/mach-exynos/Makefile
> +++ b/arch/arm/mach-exynos/Makefile
> @@ -7,6 +7,7 @@
>
> obj-y += clock.o power.o soc.o system.o pinmux.o tzpc.o
>
> +obj-$(CONFIG_EXYNOS5420) += adc.o
> obj-$(CONFIG_EXYNOS5420) += sec_boot.o
>
> ifdef CONFIG_SPL_BUILD
> diff --git a/arch/arm/mach-exynos/adc.c b/arch/arm/mach-exynos/adc.c
> new file mode 100644
> index 0000000..e8dd619
> --- /dev/null
> +++ b/arch/arm/mach-exynos/adc.c
> @@ -0,0 +1,83 @@
> +/*
> + * Copyright (C) 2015 Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +#include <common.h>
> +#include <errno.h>
> +#include <asm/arch/adc.h>
> +#include <asm/arch/power.h>
> +
> +extern void sdelay(unsigned long loops);
> +
> +struct exynos_adc_v2 *exynos_adc_probe(void)
> +{
> + struct exynos_adc_v2 *adc;
> +
> + adc = (struct exynos_adc_v2 *)samsung_get_base_adc();
> + if (!adc) {
> + error("Invalid ADC base address!");
> + return NULL;
> + }
> +
> + /* Check HW version */
> + if (readl(&adc->version) != ADC_V2_VERSION) {
> + error("This driver supports only ADC v2!");
> + return NULL;
> + }
> +
> + /* ADC Reset */
> + writel(ADC_V2_CON1_SOFT_RESET, &adc->con1);
> +
> + return adc;
> +}
> +
> +static void exynos_adc_start_conversion(struct exynos_adc_v2 *adc, int channel)
> +{
> + unsigned int cfg;
> +
> + /* Disable INT - will read status only */
> + writel(0x0, &adc->int_en);
> +
> + /* CON2 - set conversion parameters */
> + cfg = ADC_V2_CON2_C_TIME(3); /* Conversion times: (1 << 3) = 8 */
> + cfg |= ADC_V2_CON2_OSEL(OSEL_BINARY);
> + cfg |= ADC_V2_CON2_CHAN_SEL(channel);
> + cfg |= ADC_V2_CON2_ESEL(ESEL_ADC_EVAL_TIME_20CLK);
> + cfg |= ADC_V2_CON2_HIGHF(HIGHF_CONV_RATE_600KSPS);
> + writel(cfg, &adc->con2);
> +
> + /* Start conversion */
> + cfg = readl(&adc->con1);
> + writel(cfg | ADC_V2_CON1_STC_EN, &adc->con1);
> +}
> +
> +int exynos_adc_read_channel(int channel)
> +{
> + struct exynos_adc_v2 *adc;
> + int timeout_us = ADC_V2_CONV_TIMEOUT_US;
> +
> + adc = exynos_adc_probe();
> + if (!adc) {
> + error("Can't init ADC!");
> + return -ENODEV;
> + }
> +
> + if (channel > ADC_V2_MAX_CHANNEL) {
> + error("ADC: max channel is: %d.", ADC_V2_MAX_CHANNEL);
> + return -ENODEV;
> + }
> +
> + exynos_adc_start_conversion(adc, channel);
> +
> + while (ADC_V2_GET_STATUS_FLAG(readl(&adc->status)) != FLAG_CONV_END) {
> + sdelay(4);
> + if (!timeout_us--) {
> + error("ADC conversion timeout!");
> + return -ETIME;
> + }
> + }
> +
> + return readl(&adc->dat) & ADC_V2_DAT_MASK;
> +}
> diff --git a/arch/arm/mach-exynos/include/mach/adc.h b/arch/arm/mach-exynos/include/mach/adc.h
> index a0e26d7..228acf4 100644
> --- a/arch/arm/mach-exynos/include/mach/adc.h
> +++ b/arch/arm/mach-exynos/include/mach/adc.h
> @@ -9,6 +9,38 @@
> #ifndef __ASM_ARM_ARCH_ADC_H_
> #define __ASM_ARM_ARCH_ADC_H_
>
> +#define ADC_V2_CON1_SOFT_RESET (0x2 << 1)
> +#define ADC_V2_CON1_STC_EN (0x1)
> +
> +#define ADC_V2_CON2_OSEL(x) (((x) & 0x1) << 10)
> +#define OSEL_2S (0x0)
> +#define OSEL_BINARY (0x1)
> +#define ADC_V2_CON2_ESEL(x) (((x) & 0x1) << 9)
> +#define ESEL_ADC_EVAL_TIME_40CLK (0x0)
> +#define ESEL_ADC_EVAL_TIME_20CLK (0x1)
> +#define ADC_V2_CON2_HIGHF(x) (((x) & 0x1) << 8)
> +#define HIGHF_CONV_RATE_30KSPS (0x0)
> +#define HIGHF_CONV_RATE_600KSPS (0x1)
> +#define ADC_V2_CON2_C_TIME(x) (((x) & 0x7) << 4)
> +#define ADC_V2_CON2_CHAN_SEL(x) ((x) & 0xf)
> +
> +#define ADC_V2_GET_STATUS_FLAG(x) (((x) >> 2) & 0x1)
> +#define FLAG_CONV_END (0x1)
> +
> +#define ADC_V2_INT_DISABLE (0x0)
> +#define ADC_V2_INT_ENABLE (0x1)
> +#define INT_NOT_GENERATED (0x0)
> +#define INT_GENERATED (0x1)
> +
> +#define ADC_V2_VERSION (0x80000008)
> +
> +#define ADC_V2_MAX_CHANNEL (9)
> +
> +/* For default 8 time convertion with sample rate 600 kSPS - 15us timeout */
> +#define ADC_V2_CONV_TIMEOUT_US (15)
> +
> +#define ADC_V2_DAT_MASK (0xfff)
> +
> #ifndef __ASSEMBLY__
> struct s5p_adc {
> unsigned int adccon;
> @@ -21,6 +53,19 @@ struct s5p_adc {
> unsigned int adcmux;
> unsigned int adcclrintpndnup;
> };
> +
> +struct exynos_adc_v2 {
> + unsigned int con1;
> + unsigned int con2;
> + unsigned int status;
> + unsigned int dat;
> + unsigned int int_en;
> + unsigned int int_status;
> + unsigned int reserved[2];
> + unsigned int version;
> +};
> +
> +int exynos_adc_read_channel(int channel);
> #endif
>
> #endif /* __ASM_ARM_ARCH_ADC_H_ */
> diff --git a/arch/arm/mach-exynos/include/mach/cpu.h b/arch/arm/mach-exynos/include/mach/cpu.h
> index 14a1692..75933e7 100644
> --- a/arch/arm/mach-exynos/include/mach/cpu.h
> +++ b/arch/arm/mach-exynos/include/mach/cpu.h
> @@ -167,10 +167,11 @@
> #define EXYNOS5420_USB_HOST_EHCI_BASE 0x12110000
> #define EXYNOS5420_MMC_BASE 0x12200000
> #define EXYNOS5420_SROMC_BASE 0x12250000
> -#define EXYNOS5420_USB3PHY_BASE 0x12500000
> +#define EXYNOS5420_USB3PHY_BASE 0x12500000

unrelated change?

> #define EXYNOS5420_UART_BASE 0x12C00000
> #define EXYNOS5420_I2C_BASE 0x12C60000
> #define EXYNOS5420_I2C_8910_BASE 0x12E00000
> +#define EXYNOS5420_ADC_BASE 0x12D10000

Then we can drop this.

> #define EXYNOS5420_SPI_BASE 0x12D20000
> #define EXYNOS5420_I2S_BASE 0x12D60000
> #define EXYNOS5420_PWMTIMER_BASE 0x12DD0000
> @@ -186,7 +187,6 @@
> #define EXYNOS5420_USBPHY_BASE DEVICE_NOT_AVAILABLE
> #define EXYNOS5420_USBOTG_BASE DEVICE_NOT_AVAILABLE
> #define EXYNOS5420_FIMD_BASE DEVICE_NOT_AVAILABLE
> -#define EXYNOS5420_ADC_BASE DEVICE_NOT_AVAILABLE
> #define EXYNOS5420_MODEM_BASE DEVICE_NOT_AVAILABLE
> #define EXYNOS5420_USB_HOST_XHCI_BASE DEVICE_NOT_AVAILABLE
>
> --
> 1.9.1
>

Regards,
Simon