summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/cpu/armv7/s5p-common/cpu_info.c14
-rw-r--r--arch/arm/dts/exynos5422-odroidxu3.dts12
-rw-r--r--arch/arm/dts/exynos54xx.dtsi7
-rw-r--r--arch/arm/dts/exynos5800-peach-pi.dts1
-rw-r--r--arch/arm/mach-exynos/clock.c16
-rw-r--r--arch/arm/mach-exynos/clock_init_exynos5.c2
-rw-r--r--arch/arm/mach-exynos/common_setup.h4
-rw-r--r--arch/arm/mach-exynos/dmc_init_ddr3.c8
-rw-r--r--arch/arm/mach-exynos/include/mach/adc.h44
-rw-r--r--arch/arm/mach-exynos/include/mach/cpu.h6
-rw-r--r--arch/arm/mach-exynos/include/mach/gpio.h4
-rw-r--r--arch/arm/mach-exynos/pinmux.c4
-rw-r--r--arch/arm/mach-exynos/power.c2
-rw-r--r--arch/sandbox/dts/sandbox_pmic.dtsi2
-rw-r--r--arch/sandbox/dts/test.dts6
-rw-r--r--board/samsung/common/Makefile5
-rw-r--r--board/samsung/common/board.c4
-rw-r--r--board/samsung/common/exynos5-dt-types.c196
-rw-r--r--board/samsung/common/exynos5-dt.c12
-rw-r--r--board/samsung/common/misc.c10
-rw-r--r--board/samsung/odroid/odroid.c15
-rw-r--r--board/samsung/smdk2410/smdk2410.c10
-rw-r--r--board/samsung/smdkv310/smdkv310.c8
-rw-r--r--configs/odroid-xu3_defconfig5
-rw-r--r--configs/sandbox_defconfig2
-rw-r--r--doc/device-tree-bindings/adc/adc.txt62
-rw-r--r--doc/device-tree-bindings/exynos/soc.txt21
-rw-r--r--doc/device-tree-bindings/pmic/s2mps11.txt17
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/adc/Kconfig30
-rw-r--r--drivers/adc/Makefile10
-rw-r--r--drivers/adc/adc-uclass.c409
-rw-r--r--drivers/adc/exynos-adc.c145
-rw-r--r--drivers/adc/sandbox.c174
-rw-r--r--drivers/mmc/s5p_sdhci.c14
-rw-r--r--drivers/power/pmic/Kconfig14
-rw-r--r--drivers/power/pmic/Makefile1
-rw-r--r--drivers/power/pmic/s2mps11.c62
-rw-r--r--drivers/power/regulator/regulator-uclass.c7
-rw-r--r--include/adc.h288
-rw-r--r--include/configs/odroid_xu3.h12
-rw-r--r--include/dm/uclass-id.h1
-rw-r--r--include/power/regulator.h16
-rw-r--r--include/power/s2mps11.h109
-rw-r--r--include/power/sandbox_pmic.h4
-rw-r--r--include/samsung/exynos5-dt-types.h27
-rw-r--r--include/sandbox-adc.h31
-rw-r--r--test/dm/Makefile1
-rw-r--r--test/dm/adc.c165
50 files changed, 1966 insertions, 56 deletions
diff --git a/arch/arm/cpu/armv7/s5p-common/cpu_info.c b/arch/arm/cpu/armv7/s5p-common/cpu_info.c
index 154d67490d..764c6614ea 100644
--- a/arch/arm/cpu/armv7/s5p-common/cpu_info.c
+++ b/arch/arm/cpu/armv7/s5p-common/cpu_info.c
@@ -5,9 +5,12 @@
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
+#include <fdtdec.h>
#include <asm/io.h>
#include <asm/arch/clk.h>
+DECLARE_GLOBAL_DATA_PTR;
+
/* Default is s5pc100 */
unsigned int s5p_cpu_id = 0xC100;
/* Default is EVT1 */
@@ -30,7 +33,16 @@ u32 get_device_type(void)
#ifdef CONFIG_DISPLAY_CPUINFO
int print_cpuinfo(void)
{
- printf("CPU: %s%X @ ", s5p_get_cpu_name(), s5p_cpu_id);
+ const char *cpu_model;
+ int len;
+
+ /* For SoC with no real CPU ID in naming convention. */
+ cpu_model = fdt_getprop(gd->fdt_blob, 0, "cpu-model", &len);
+ if (cpu_model)
+ printf("CPU: %.*s @ ", len, cpu_model);
+ else
+ printf("CPU: %s%X @ ", s5p_get_cpu_name(), s5p_cpu_id);
+
print_freq(get_arm_clk(), "\n");
return 0;
diff --git a/arch/arm/dts/exynos5422-odroidxu3.dts b/arch/arm/dts/exynos5422-odroidxu3.dts
index d0a8621fda..690c747289 100644
--- a/arch/arm/dts/exynos5422-odroidxu3.dts
+++ b/arch/arm/dts/exynos5422-odroidxu3.dts
@@ -31,6 +31,18 @@
0xb0000000 0xea00000>;
};
+ adc@12D10000 {
+ u-boot,dm-pre-reloc;
+ status = "okay";
+ };
+
+ i2c@12CA0000 {
+ s2mps11_pmic@66 {
+ compatible = "samsung,s2mps11-pmic";
+ reg = <0x66>;
+ };
+ };
+
ehci@12110000 {
samsung,vbus-gpio = <&gpx2 6 GPIO_ACTIVE_HIGH>;
};
diff --git a/arch/arm/dts/exynos54xx.dtsi b/arch/arm/dts/exynos54xx.dtsi
index bd3619d751..daa6a33c5b 100644
--- a/arch/arm/dts/exynos54xx.dtsi
+++ b/arch/arm/dts/exynos54xx.dtsi
@@ -42,6 +42,13 @@
xhci1 = "/xhci@12400000";
};
+ adc@12D10000 {
+ compatible = "samsung,exynos-adc-v2";
+ reg = <0x12D10000 0x100>;
+ interrupts = <0 106 0>;
+ status = "disabled";
+ };
+
i2c@12CA0000 {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/dts/exynos5800-peach-pi.dts b/arch/arm/dts/exynos5800-peach-pi.dts
index 1d7ff23c93..76826dc23b 100644
--- a/arch/arm/dts/exynos5800-peach-pi.dts
+++ b/arch/arm/dts/exynos5800-peach-pi.dts
@@ -12,6 +12,7 @@
/ {
model = "Samsung/Google Peach Pi board based on Exynos5800";
+ cpu-model = "Exynos5800";
compatible = "google,pit-rev#", "google,pit",
"google,peach", "samsung,exynos5800", "samsung,exynos5";
diff --git a/arch/arm/mach-exynos/clock.c b/arch/arm/mach-exynos/clock.c
index 18eadf545f..3d31f9d524 100644
--- a/arch/arm/mach-exynos/clock.c
+++ b/arch/arm/mach-exynos/clock.c
@@ -159,8 +159,8 @@ static int exynos_get_pll_clk(int pllreg, unsigned int r, unsigned int k)
div = PLL_DIV_1024;
else if (proid_is_exynos4412())
div = PLL_DIV_65535;
- else if (proid_is_exynos5250() || proid_is_exynos5420()
- || proid_is_exynos5800())
+ else if (proid_is_exynos5250() || proid_is_exynos5420() ||
+ proid_is_exynos5422())
div = PLL_DIV_65536;
else
return 0;
@@ -346,7 +346,7 @@ static struct clk_bit_info *get_clk_bit_info(int peripheral)
int i;
struct clk_bit_info *info;
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
info = exynos542x_bit_info;
else
info = exynos5_bit_info;
@@ -558,7 +558,7 @@ static unsigned long exynos542x_get_periph_rate(int peripheral)
unsigned long clock_get_periph_rate(int peripheral)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
return exynos542x_get_periph_rate(peripheral);
return exynos5_get_periph_rate(peripheral);
} else {
@@ -1576,7 +1576,7 @@ static unsigned long exynos4_get_i2c_clk(void)
unsigned long get_pll_clk(int pllreg)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
return exynos542x_get_pll_clk(pllreg);
return exynos5_get_pll_clk(pllreg);
} else if (cpu_is_exynos4()) {
@@ -1692,7 +1692,7 @@ void set_mmc_clk(int dev_index, unsigned int div)
div -= 1;
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
exynos5420_set_mmc_clk(dev_index, div);
else
exynos5_set_mmc_clk(dev_index, div);
@@ -1708,7 +1708,7 @@ unsigned long get_lcd_clk(void)
} else if (cpu_is_exynos5()) {
if (proid_is_exynos5420())
return exynos5420_get_lcd_clk();
- else if (proid_is_exynos5800())
+ else if (proid_is_exynos5422())
return exynos5800_get_lcd_clk();
else
return exynos5_get_lcd_clk();
@@ -1740,7 +1740,7 @@ void set_mipi_clk(void)
int set_spi_clk(int periph_id, unsigned int rate)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
return exynos5420_set_spi_clk(periph_id, rate);
return exynos5_set_spi_clk(periph_id, rate);
}
diff --git a/arch/arm/mach-exynos/clock_init_exynos5.c b/arch/arm/mach-exynos/clock_init_exynos5.c
index 0200fd154f..1b7498d9d5 100644
--- a/arch/arm/mach-exynos/clock_init_exynos5.c
+++ b/arch/arm/mach-exynos/clock_init_exynos5.c
@@ -971,7 +971,7 @@ static void exynos5420_system_clock_init(void)
void system_clock_init(void)
{
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
exynos5420_system_clock_init();
else
exynos5250_system_clock_init();
diff --git a/arch/arm/mach-exynos/common_setup.h b/arch/arm/mach-exynos/common_setup.h
index 67aac2d575..2829fb269e 100644
--- a/arch/arm/mach-exynos/common_setup.h
+++ b/arch/arm/mach-exynos/common_setup.h
@@ -78,7 +78,7 @@ static inline void configure_l2_ctlr(void)
CACHE_TAG_RAM_LATENCY_2_CYCLES |
CACHE_DATA_RAM_LATENCY_2_CYCLES;
- if (proid_is_exynos5420() || proid_is_exynos5800()) {
+ if (proid_is_exynos5420() || proid_is_exynos5422()) {
val |= CACHE_ECC_AND_PARITY |
CACHE_TAG_RAM_LATENCY_3_CYCLES |
CACHE_DATA_RAM_LATENCY_3_CYCLES;
@@ -97,7 +97,7 @@ static inline void configure_l2_actlr(void)
{
uint32_t val;
- if (proid_is_exynos5420() || proid_is_exynos5800()) {
+ if (proid_is_exynos5420() || proid_is_exynos5422()) {
mrc_l2_aux_ctlr(val);
val |= CACHE_ENABLE_FORCE_L2_LOGIC |
CACHE_DISABLE_CLEAN_EVICT;
diff --git a/arch/arm/mach-exynos/dmc_init_ddr3.c b/arch/arm/mach-exynos/dmc_init_ddr3.c
index 7c0b12ae51..25a9df9364 100644
--- a/arch/arm/mach-exynos/dmc_init_ddr3.c
+++ b/arch/arm/mach-exynos/dmc_init_ddr3.c
@@ -20,8 +20,8 @@
#define TIMEOUT_US 10000
#define NUM_BYTE_LANES 4
#define DEFAULT_DQS 8
-#define DEFAULT_DQS_X4 (DEFAULT_DQS << 24) || (DEFAULT_DQS << 16) \
- || (DEFAULT_DQS << 8) || (DEFAULT_DQS << 0)
+#define DEFAULT_DQS_X4 ((DEFAULT_DQS << 24) || (DEFAULT_DQS << 16) \
+ || (DEFAULT_DQS << 8) || (DEFAULT_DQS << 0))
#ifdef CONFIG_EXYNOS5250
static void reset_phy_ctrl(void)
@@ -856,10 +856,10 @@ int ddr3_mem_ctrl_init(struct mem_timings *mem, int reset)
*/
val = readl(&drex0->concontrol);
val |= CONCONTROL_UPDATE_MODE;
- writel(val , &drex0->concontrol);
+ writel(val, &drex0->concontrol);
val = readl(&drex1->concontrol);
val |= CONCONTROL_UPDATE_MODE;
- writel(val , &drex1->concontrol);
+ writel(val, &drex1->concontrol);
return 0;
}
diff --git a/arch/arm/mach-exynos/include/mach/adc.h b/arch/arm/mach-exynos/include/mach/adc.h
index a0e26d7052..9af51ab381 100644
--- a/arch/arm/mach-exynos/include/mach/adc.h
+++ b/arch/arm/mach-exynos/include/mach/adc.h
@@ -9,6 +9,39 @@
#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_MASK 0xf
+#define ADC_V2_CON2_CHAN_SEL(x) ((x) & ADC_V2_CON2_CHAN_SEL_MASK)
+
+#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 +54,17 @@ 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;
+};
#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 cb3d2cc06f..14a1692467 100644
--- a/arch/arm/mach-exynos/include/mach/cpu.h
+++ b/arch/arm/mach-exynos/include/mach/cpu.h
@@ -237,7 +237,7 @@ static inline void s5p_set_cpu_id(void)
* Exynos5800 is a variant of Exynos5420
* and has product id 0x5422
*/
- s5p_cpu_id = 0x5800;
+ s5p_cpu_id = 0x5422;
break;
}
}
@@ -267,7 +267,7 @@ IS_EXYNOS_TYPE(exynos4210, 0x4210)
IS_EXYNOS_TYPE(exynos4412, 0x4412)
IS_EXYNOS_TYPE(exynos5250, 0x5250)
IS_EXYNOS_TYPE(exynos5420, 0x5420)
-IS_EXYNOS_TYPE(exynos5800, 0x5800)
+IS_EXYNOS_TYPE(exynos5422, 0x5422)
#define SAMSUNG_BASE(device, base) \
static inline unsigned int __attribute__((no_instrument_function)) \
@@ -278,7 +278,7 @@ static inline unsigned int __attribute__((no_instrument_function)) \
return EXYNOS4X12_##base; \
return EXYNOS4_##base; \
} else if (cpu_is_exynos5()) { \
- if (proid_is_exynos5420() || proid_is_exynos5800()) \
+ if (proid_is_exynos5420() || proid_is_exynos5422()) \
return EXYNOS5420_##base; \
return EXYNOS5_##base; \
} \
diff --git a/arch/arm/mach-exynos/include/mach/gpio.h b/arch/arm/mach-exynos/include/mach/gpio.h
index 9699954a7d..7fc8e61f9c 100644
--- a/arch/arm/mach-exynos/include/mach/gpio.h
+++ b/arch/arm/mach-exynos/include/mach/gpio.h
@@ -1398,7 +1398,7 @@ static struct gpio_info exynos5420_gpio_data[EXYNOS5420_GPIO_NUM_PARTS] = {
static inline struct gpio_info *get_gpio_data(void)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
return exynos5420_gpio_data;
else
return exynos5_gpio_data;
@@ -1415,7 +1415,7 @@ static inline struct gpio_info *get_gpio_data(void)
static inline unsigned int get_bank_num(void)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
return EXYNOS5420_GPIO_NUM_PARTS;
else
return EXYNOS5_GPIO_NUM_PARTS;
diff --git a/arch/arm/mach-exynos/pinmux.c b/arch/arm/mach-exynos/pinmux.c
index 179b29449e..12eb79cb0c 100644
--- a/arch/arm/mach-exynos/pinmux.c
+++ b/arch/arm/mach-exynos/pinmux.c
@@ -737,10 +737,10 @@ static int exynos4x12_mmc_config(int peripheral, int flags)
return -1;
}
for (i = start; i < (start + 7); i++) {
+ gpio_set_pull(i, S5P_GPIO_PULL_NONE);
if (i == (start + 2))
continue;
gpio_cfg_pin(i, func);
- gpio_set_pull(i, S5P_GPIO_PULL_NONE);
gpio_set_drv(i, S5P_GPIO_DRV_4X);
}
if (flags & PINMUX_FLAG_8BIT_MODE) {
@@ -858,7 +858,7 @@ static int exynos4x12_pinmux_config(int peripheral, int flags)
int exynos_pinmux_config(int peripheral, int flags)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
return exynos5420_pinmux_config(peripheral, flags);
else if (proid_is_exynos5250())
return exynos5_pinmux_config(peripheral, flags);
diff --git a/arch/arm/mach-exynos/power.c b/arch/arm/mach-exynos/power.c
index 1b12051656..cd2d6618ac 100644
--- a/arch/arm/mach-exynos/power.c
+++ b/arch/arm/mach-exynos/power.c
@@ -125,7 +125,7 @@ static void exynos5420_set_usbdev_phy_ctrl(unsigned int enable)
void set_usbdrd_phy_ctrl(unsigned int enable)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420() || proid_is_exynos5800())
+ if (proid_is_exynos5420() || proid_is_exynos5422())
exynos5420_set_usbdev_phy_ctrl(enable);
else
exynos5_set_usbdrd_phy_ctrl(enable);
diff --git a/arch/sandbox/dts/sandbox_pmic.dtsi b/arch/sandbox/dts/sandbox_pmic.dtsi
index 44a26b18ca..ce261b930e 100644
--- a/arch/sandbox/dts/sandbox_pmic.dtsi
+++ b/arch/sandbox/dts/sandbox_pmic.dtsi
@@ -55,7 +55,7 @@
regulator-always-on;
};
- buck2 {
+ buck2: buck2 {
regulator-name = "SUPPLY_3.3V";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 730de8a57f..e2c4971d74 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -189,6 +189,12 @@
};
};
+ adc@0 {
+ compatible = "sandbox,adc";
+ vdd-supply = <&buck2>;
+ vss-microvolts = <0>;
+ };
+
leds {
compatible = "gpio-leds";
diff --git a/board/samsung/common/Makefile b/board/samsung/common/Makefile
index 6cbd90661b..ef1a8f318f 100644
--- a/board/samsung/common/Makefile
+++ b/board/samsung/common/Makefile
@@ -11,5 +11,8 @@ obj-$(CONFIG_MISC_COMMON) += misc.o
ifndef CONFIG_SPL_BUILD
obj-$(CONFIG_BOARD_COMMON) += board.o
-obj-$(CONFIG_EXYNOS5_DT) += exynos5-dt.o
+ifdef CONFIG_EXYNOS5_DT
+obj-y += exynos5-dt.o
+obj-$(CONFIG_BOARD_TYPES) += exynos5-dt-types.o
+endif
endif
diff --git a/board/samsung/common/board.c b/board/samsung/common/board.c
index d32c75de50..1334c22ddd 100644
--- a/board/samsung/common/board.c
+++ b/board/samsung/common/board.c
@@ -304,8 +304,8 @@ int checkboard(void)
printf("Board: %s\n", board_info ? board_info : "unknown");
#ifdef CONFIG_BOARD_TYPES
board_info = get_board_type();
-
- printf("Model: %s\n", board_info ? board_info : "unknown");
+ if (board_info)
+ printf("Type: %s\n", board_info);
#endif
return 0;
}
diff --git a/board/samsung/common/exynos5-dt-types.c b/board/samsung/common/exynos5-dt-types.c
new file mode 100644
index 0000000000..48fd1f7d96
--- /dev/null
+++ b/board/samsung/common/exynos5-dt-types.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <adc.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/s2mps11.h>
+#include <samsung/exynos5-dt-types.h>
+#include <samsung/misc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct udevice_id board_ids[] = {
+ { .compatible = "samsung,odroidxu3", .data = EXYNOS5_BOARD_ODROID_XU3 },
+ { .compatible = "samsung,exynos5", .data = EXYNOS5_BOARD_GENERIC },
+ { },
+};
+
+/**
+ * Odroix XU3/4 board revisions:
+ * Rev ADCmax Board
+ * 0.1 0 XU3 0.1
+ * 0.2 410 XU3 0.2 | XU3L - no DISPLAYPORT (probe I2C0:0x40 / INA231)
+ * 0.3 1408 XU4 0.1
+ * Use +10 % for ADC value tolerance.
+ */
+struct odroid_rev_info odroid_info[] = {
+ { EXYNOS5_BOARD_ODROID_XU3_REV01, 1, 10, "xu3" },
+ { EXYNOS5_BOARD_ODROID_XU3_REV02, 2, 410, "xu3" },
+ { EXYNOS5_BOARD_ODROID_XU4_REV01, 1, 1408, "xu4" },
+ { EXYNOS5_BOARD_ODROID_UNKNOWN, 0, 4095, "unknown" },
+};
+
+static unsigned int odroid_get_rev(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(odroid_info); i++) {
+ if (odroid_info[i].board_type == gd->board_type)
+ return odroid_info[i].board_rev;
+ }
+
+ return 0;
+}
+
+static int odroid_get_board_type(void)
+{
+ unsigned int adcval;
+ int ret, i;
+
+ ret = adc_channel_single_shot("adc", CONFIG_ODROID_REV_AIN, &adcval);
+ if (ret)
+ goto rev_default;
+
+ for (i = 0; i < ARRAY_SIZE(odroid_info); i++) {
+ /* ADC tolerance: +20 % */
+ if (adcval < odroid_info[i].adc_val)
+ return odroid_info[i].board_type;
+ }
+
+rev_default:
+ return EXYNOS5_BOARD_ODROID_XU3;
+}
+
+/**
+ * odroid_get_type_str - returns pointer to one of the board type string.
+ * Board types: "xu3", "xu3-lite", "xu4". However the "xu3lite" can be
+ * detected only when the i2c controller is ready to use. Fortunately,
+ * XU3 and XU3L are compatible, and the information about board lite
+ * revision is needed before booting the linux, to set proper environment
+ * variable: $fdtfile.
+ */
+static const char *odroid_get_type_str(void)
+{
+ const char *type_xu3l = "xu3-lite";
+ struct udevice *dev, *chip;
+ int i, ret;
+
+ if (gd->board_type != EXYNOS5_BOARD_ODROID_XU3_REV02)
+ goto exit;
+
+ ret = pmic_get("s2mps11", &dev);
+ if (ret)
+ goto exit;
+
+ /* Enable LDO26: 3.0V */
+ ret = pmic_reg_write(dev, S2MPS11_REG_L26CTRL,
+ S2MPS11_LDO26_ENABLE);
+ if (ret)
+ goto exit;
+
+ /* Check XU3Lite by probe INA231 I2C0:0x40 */
+ ret = uclass_get_device(UCLASS_I2C, 0, &dev);
+ if (ret)
+ goto exit;
+
+ ret = dm_i2c_probe(dev, 0x40, 0x0, &chip);
+ if (ret)
+ return type_xu3l;
+
+exit:
+ for (i = 0; i < ARRAY_SIZE(odroid_info); i++) {
+ if (odroid_info[i].board_type == gd->board_type)
+ return odroid_info[i].name;
+ }
+
+ return NULL;
+}
+
+bool board_is_odroidxu3(void)
+{
+ if (gd->board_type >= EXYNOS5_BOARD_ODROID_XU3 &&
+ gd->board_type <= EXYNOS5_BOARD_ODROID_XU3_REV02)
+ return true;
+
+ return false;
+}
+
+bool board_is_odroidxu4(void)
+{
+ if (gd->board_type == EXYNOS5_BOARD_ODROID_XU4_REV01)
+ return true;
+
+ return false;
+}
+
+bool board_is_generic(void)
+{
+ if (gd->board_type == EXYNOS5_BOARD_GENERIC)
+ return true;
+
+ return false;
+}
+
+/**
+ * get_board_rev() - return detected board revision.
+ *
+ * @return: return board revision number for XU3 or 0 for generic
+ */
+u32 get_board_rev(void)
+{
+ if (board_is_generic())
+ return 0;
+
+ return odroid_get_rev();
+}
+
+/**
+ * get_board_type() - returns board type string.
+ *
+ * @return: return board type string for XU3 or empty string for generic
+ */
+const char *get_board_type(void)
+{
+ const char *generic = "";
+
+ if (board_is_generic())
+ return generic;
+
+ return odroid_get_type_str();
+}
+
+/**
+ * set_board_type() - set board type in gd->board_type.
+ * As default type set EXYNOS5_BOARD_GENERIC, if detect Odroid,
+ * then set its proper type.
+ */
+void set_board_type(void)
+{
+ const struct udevice_id *of_match = board_ids;
+ int ret;
+
+ gd->board_type = EXYNOS5_BOARD_GENERIC;
+
+ while (of_match->compatible) {
+ ret = fdt_node_check_compatible(gd->fdt_blob, 0,
+ of_match->compatible);
+ if (ret)
+ of_match++;
+
+ gd->board_type = of_match->data;
+ break;
+ }
+
+ /* If Odroid, then check its revision */
+ if (board_is_odroidxu3())
+ gd->board_type = odroid_get_board_type();
+}
diff --git a/board/samsung/common/exynos5-dt.c b/board/samsung/common/exynos5-dt.c
index 4250f722da..4d9e151756 100644
--- a/board/samsung/common/exynos5-dt.c
+++ b/board/samsung/common/exynos5-dt.c
@@ -27,7 +27,10 @@
#include <power/pmic.h>
#include <power/max77686_pmic.h>
#include <power/regulator.h>
+#include <power/s2mps11.h>
#include <power/s5m8767.h>
+#include <samsung/exynos5-dt-types.h>
+#include <samsung/misc.h>
#include <tmu.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -335,15 +338,24 @@ int board_usb_init(int index, enum usb_init_type init)
#ifdef CONFIG_SET_DFU_ALT_INFO
char *get_dfu_alt_system(char *interface, char *devstr)
{
+ char *info = "Not supported!";
+
+ if (board_is_odroidxu4())
+ return info;
+
return getenv("dfu_alt_system");
}
char *get_dfu_alt_boot(char *interface, char *devstr)
{
+ char *info = "Not supported!";
struct mmc *mmc;
char *alt_boot;
int dev_num;
+ if (board_is_odroidxu4())
+ return info;
+
dev_num = simple_strtoul(devstr, NULL, 10);
mmc = find_mmc_device(dev_num);
diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c
index e0e2c48632..da0d4db1f9 100644
--- a/board/samsung/common/misc.c
+++ b/board/samsung/common/misc.c
@@ -85,6 +85,9 @@ void set_board_info(void)
#ifdef CONFIG_BOARD_TYPES
bdtype = get_board_type();
+ if (!bdtype)
+ bdtype = "";
+
sprintf(info, "%s%s", bdname, bdtype);
setenv("boardname", info);
#endif
@@ -256,9 +259,9 @@ static int mode_leave_menu(int mode)
cmd = find_cmd(mode_name[mode][1]);
if (cmd) {
printf("Enter: %s %s\n", mode_name[mode][0],
- mode_info[mode]);
+ mode_info[mode]);
lcd_printf("\n\n\t%s %s\n", mode_name[mode][0],
- mode_info[mode]);
+ mode_info[mode]);
lcd_puts("\n\tDo not turn off device before finish!\n");
cmd_result = run_command(mode_cmd[mode], 0);
@@ -315,8 +318,7 @@ static void display_download_menu(int mode)
for (i = 0; i <= BOOT_MODE_EXIT; i++)
lcd_printf("\t%s %s - %s\n\n", selection[i],
- mode_name[i][0],
- mode_info[i]);
+ mode_name[i][0], mode_info[i]);
}
static void download_menu(void)
diff --git a/board/samsung/odroid/odroid.c b/board/samsung/odroid/odroid.c
index 32155f1184..36d493d514 100644
--- a/board/samsung/odroid/odroid.c
+++ b/board/samsung/odroid/odroid.c
@@ -33,13 +33,6 @@ enum {
ODROID_TYPES,
};
-static const char *mmc_regulators[] = {
- "VDDQ_EMMC_1.8V",
- "VDDQ_EMMC_2.8V",
- "TFLASH_2.8V",
- NULL,
-};
-
void set_board_type(void)
{
/* Set GPA1 pin 1 to HI - enable XCL205 output */
@@ -428,6 +421,13 @@ int exynos_init(void)
int exynos_power_init(void)
{
+ const char *mmc_regulators[] = {
+ "VDDQ_EMMC_1.8V",
+ "VDDQ_EMMC_2.8V",
+ "TFLASH_2.8V",
+ NULL,
+ };
+
if (regulator_list_autoset(mmc_regulators, NULL, true))
error("Unable to init all mmc regulators");
@@ -450,7 +450,6 @@ static int s5pc210_phy_control(int on)
return regulator_set_mode(dev, OPMODE_ON);
else
return regulator_set_mode(dev, OPMODE_LPM);
-
}
struct s3c_plat_otg_data s5pc210_otg_data = {
diff --git a/board/samsung/smdk2410/smdk2410.c b/board/samsung/smdk2410/smdk2410.c
index b75a0e34dd..6e678c744b 100644
--- a/board/samsung/smdk2410/smdk2410.c
+++ b/board/samsung/smdk2410/smdk2410.c
@@ -18,11 +18,11 @@ DECLARE_GLOBAL_DATA_PTR;
#define FCLK_SPEED 1
-#if FCLK_SPEED==0 /* Fout = 203MHz, Fin = 12MHz for Audio */
+#if (FCLK_SPEED == 0) /* Fout = 203MHz, Fin = 12MHz for Audio */
#define M_MDIV 0xC3
#define M_PDIV 0x4
#define M_SDIV 0x1
-#elif FCLK_SPEED==1 /* Fout = 202.8MHz */
+#elif (FCLK_SPEED == 1) /* Fout = 202.8MHz */
#define M_MDIV 0xA1
#define M_PDIV 0x3
#define M_SDIV 0x1
@@ -30,11 +30,11 @@ DECLARE_GLOBAL_DATA_PTR;
#define USB_CLOCK 1
-#if USB_CLOCK==0
+#if (USB_CLOCK == 0)
#define U_M_MDIV 0xA1
#define U_M_PDIV 0x3
#define U_M_SDIV 0x1
-#elif USB_CLOCK==1
+#elif (USB_CLOCK == 1)
#define U_M_MDIV 0x48
#define U_M_PDIV 0x3
#define U_M_SDIV 0x2
@@ -44,7 +44,7 @@ static inline void pll_delay(unsigned long loops)
{
__asm__ volatile ("1:\n"
"subs %0, %1, #1\n"
- "bne 1b":"=r" (loops):"0" (loops));
+ "bne 1b" : "=r" (loops) : "0" (loops));
}
/*
diff --git a/board/samsung/smdkv310/smdkv310.c b/board/samsung/smdkv310/smdkv310.c
index cb7f9b0ac8..fc0e8d252b 100644
--- a/board/samsung/smdkv310/smdkv310.c
+++ b/board/samsung/smdkv310/smdkv310.c
@@ -55,16 +55,16 @@ int dram_init(void)
void dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
- gd->bd->bi_dram[0].size = get_ram_size((long *)PHYS_SDRAM_1, \
+ gd->bd->bi_dram[0].size = get_ram_size((long *)PHYS_SDRAM_1,
PHYS_SDRAM_1_SIZE);
gd->bd->bi_dram[1].start = PHYS_SDRAM_2;
- gd->bd->bi_dram[1].size = get_ram_size((long *)PHYS_SDRAM_2, \
+ gd->bd->bi_dram[1].size = get_ram_size((long *)PHYS_SDRAM_2,
PHYS_SDRAM_2_SIZE);
gd->bd->bi_dram[2].start = PHYS_SDRAM_3;
- gd->bd->bi_dram[2].size = get_ram_size((long *)PHYS_SDRAM_3, \
+ gd->bd->bi_dram[2].size = get_ram_size((long *)PHYS_SDRAM_3,
PHYS_SDRAM_3_SIZE);
gd->bd->bi_dram[3].start = PHYS_SDRAM_4;
- gd->bd->bi_dram[3].size = get_ram_size((long *)PHYS_SDRAM_4, \
+ gd->bd->bi_dram[3].size = get_ram_size((long *)PHYS_SDRAM_4,
PHYS_SDRAM_4_SIZE);
}
diff --git a/configs/odroid-xu3_defconfig b/configs/odroid-xu3_defconfig
index 479af9e71a..4ab91fa828 100644
--- a/configs/odroid-xu3_defconfig
+++ b/configs/odroid-xu3_defconfig
@@ -9,7 +9,12 @@ CONFIG_SYS_PROMPT="ODROID-XU3 # "
# CONFIG_CMD_SETEXPR is not set
CONFIG_DM_I2C_COMPAT=y
CONFIG_DM_PMIC=y
+CONFIG_CMD_PMIC=y
+CONFIG_ERRNO_STR=y
CONFIG_DM_REGULATOR=y
+CONFIG_PMIC_S2MPS11=y
CONFIG_USB=y
CONFIG_DM_USB=y
CONFIG_VIDEO_BRIDGE=y
+CONFIG_ADC=y
+CONFIG_ADC_EXYNOS=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 67ae99b638..94c8e685f0 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -65,3 +65,5 @@ CONFIG_UT_DM=y
CONFIG_UT_ENV=y
CONFIG_REMOTEPROC_SANDBOX=y
CONFIG_CMD_REMOTEPROC=y
+CONFIG_ADC=y
+CONFIG_ADC_SANDBOX=y
diff --git a/doc/device-tree-bindings/adc/adc.txt b/doc/device-tree-bindings/adc/adc.txt
new file mode 100644
index 0000000000..463de3c8c2
--- /dev/null
+++ b/doc/device-tree-bindings/adc/adc.txt
@@ -0,0 +1,62 @@
+ADC device binding
+
+There are no mandatory properties for ADC. However, if Voltage info is required,
+then there are two options:
+- use microvolts constraint or
+- use regulator phandle to enable/read supply's Voltage
+
+Properties and constraints:
+*optional and always checked, Voltage polarity info:
+- vdd-polarity-negative: positive reference Voltage has a negative polarity
+- vss-polarity-negative: negative reference Voltage has a negative polarity
+
+Chose one option, for each supply (Vdd/Vss):
+
+*optional and always checked, supply Voltage constants:
+- vdd-supply: phandle to Vdd regulator's node
+- vss-supply: phandle to Vss regulator's node
+
+*optional and checked only if the above corresponding, doesn't exist:
+- vdd-microvolts: positive reference Voltage value [uV]
+- vss-microvolts: negative reference Voltage value [uV]
+
+Example with constant 'Vdd' value:
+adc@1000000 {
+ compatible = "some-adc";
+ reg = <0xaabb000 0x100>;
+ status = "enabled";
+ vdd-microvolts = <1800000>;
+};
+
+Example of supply phandle usage, for the ADC's VDD/VSS references as below:
+ _______ _______
+ |Sandbox| |Sandbox|
+ : PMIC : : ADC :
+ . . . .
+ | | (Vdd) | AIN0|-->
+ | BUCK2|-------|VDDref |
+ | (3.3V)| _|VSSref |
+ |_______| | |_______|
+ _|_
+
+For the above PMIC, the node can be defined as follows:
+sandbox_pmic {
+ compatible = "sandbox,pmic";
+ ...
+ buck2: buck2 {
+ regulator-name = "SUPPLY_3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ ...
+};
+
+For the above ADC, the node can be defined as follows:
+adc@0 {
+ compatible = "sandbox,adc";
+ vdd-supply = <&buck2>;
+ vss-microvolts = <0>;
+};
+
+The ADC uclass code, will enable the supply before start of the conversion,
+but it will not configure the regulator settings.
diff --git a/doc/device-tree-bindings/exynos/soc.txt b/doc/device-tree-bindings/exynos/soc.txt
new file mode 100644
index 0000000000..9ba6f3b9f8
--- /dev/null
+++ b/doc/device-tree-bindings/exynos/soc.txt
@@ -0,0 +1,21 @@
+Exynos SoC model
+
+The "cpu-model" property is a non-standard extension for the device tree root
+node. Since the cpu id of some Exynos variants does not correspond to product
+name, this property fills the gap.
+
+For almost all Exynos based boards in the kernel, the product name corresponds
+to the device tree file name. The same name is generated in U-Boot, so the new
+property allows doing it automatically.
+
+Required properties:
+ - cpu-model : Exynos product name
+
+Example:
+
+/ {
+ model = "Samsung/Google Peach Pi board based on Exynos5800";
+ cpu-model = "Exynos5800";
+
+ compatible = ...
+};
diff --git a/doc/device-tree-bindings/pmic/s2mps11.txt b/doc/device-tree-bindings/pmic/s2mps11.txt
new file mode 100644
index 0000000000..422f14f13e
--- /dev/null
+++ b/doc/device-tree-bindings/pmic/s2mps11.txt
@@ -0,0 +1,17 @@
+SAMSUNG, S2MPS11 PMIC
+
+This file describes the binding info for the PMIC driver:
+- drivers/power/pmic/s2mps11.c
+
+Required properties:
+- compatible: "samsung,s2mps11-pmic"
+- reg = 0x66
+
+With those two properties, the pmic device can be used for read/write only.
+
+Example:
+
+s2mps11@66 {
+ compatible = "samsung,s2mps11-pmic";
+ reg = <0x66>;
+};
diff --git a/drivers/Kconfig b/drivers/Kconfig
index ba88b5ea37..c481e93356 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -4,6 +4,8 @@ source "drivers/core/Kconfig"
# types of drivers sorted in alphabetical order
+source "drivers/adc/Kconfig"
+
source "drivers/block/Kconfig"
source "drivers/clk/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 4f49bfddb8..ad29a4fd04 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SPL_SATA_SUPPORT) += block/
else
+obj-y += adc/
obj-$(CONFIG_DM_DEMO) += demo/
obj-$(CONFIG_BIOSEMU) += bios_emulator/
obj-y += block/
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
new file mode 100644
index 0000000000..e5335f7234
--- /dev/null
+++ b/drivers/adc/Kconfig
@@ -0,0 +1,30 @@
+config ADC
+ bool "Enable ADC drivers using Driver Model"
+ help
+ This enables ADC API for drivers, which allows driving ADC features
+ by single and multi-channel methods for:
+ - start/stop/get data for conversion of a single-channel selected by
+ a number or multi-channels selected by a bitmask
+ - get data mask (ADC resolution)
+ ADC reference Voltage supply options:
+ - methods for get Vdd/Vss reference Voltage values with polarity
+ - support supply's phandle with auto-enable
+ - supply polarity setting in fdt
+
+config ADC_EXYNOS
+ bool "Enable Exynos 54xx ADC driver"
+ help
+ This enables basic driver for Exynos ADC compatible with Exynos54xx.
+ It provides:
+ - 10 analog input channels
+ - 12-bit resolution
+ - 600 KSPS of sample rate
+
+config ADC_SANDBOX
+ bool "Enable Sandbox ADC test driver"
+ help
+ This enables driver for Sandbox ADC device emulation.
+ It provides:
+ - 4 analog input channels
+ - 16-bit resolution
+ - single and multi-channel conversion mode
diff --git a/drivers/adc/Makefile b/drivers/adc/Makefile
new file mode 100644
index 0000000000..cebf26de07
--- /dev/null
+++ b/drivers/adc/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (C) 2015 Samsung Electronics
+# Przemyslaw Marczak <p.marczak@samsung.com>
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_ADC) += adc-uclass.o
+obj-$(CONFIG_ADC_EXYNOS) += exynos-adc.o
+obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
diff --git a/drivers/adc/adc-uclass.c b/drivers/adc/adc-uclass.c
new file mode 100644
index 0000000000..9233fcdb6c
--- /dev/null
+++ b/drivers/adc/adc-uclass.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <adc.h>
+#include <power/regulator.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define ADC_UCLASS_PLATDATA_SIZE sizeof(struct adc_uclass_platdata)
+#define CHECK_NUMBER true
+#define CHECK_MASK (!CHECK_NUMBER)
+
+/* TODO: add support for timer uclass (for early calls) */
+#ifdef CONFIG_SANDBOX_ARCH
+#define sdelay(x) udelay(x)
+#else
+extern void sdelay(unsigned long loops);
+#endif
+
+static int check_channel(struct udevice *dev, int value, bool number_or_mask,
+ const char *caller_function)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ unsigned mask = number_or_mask ? (1 << value) : value;
+
+ /* For the real ADC hardware, some ADC channels can be inactive.
+ * For example if device has 4 analog channels, and only channels
+ * 1-st and 3-rd are valid, then channel mask is: 0b1010, so request
+ * with mask 0b1110 should return an error.
+ */
+ if ((uc_pdata->channel_mask >= mask) && (uc_pdata->channel_mask & mask))
+ return 0;
+
+ printf("Error in %s/%s().\nWrong channel selection for device: %s\n",
+ __FILE__, caller_function, dev->name);
+
+ return -EINVAL;
+}
+
+static int adc_supply_enable(struct udevice *dev)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ const char *supply_type;
+ int ret = 0;
+
+ if (uc_pdata->vdd_supply) {
+ supply_type = "vdd";
+ ret = regulator_set_enable(uc_pdata->vdd_supply, true);
+ }
+
+ if (!ret && uc_pdata->vss_supply) {
+ supply_type = "vss";
+ ret = regulator_set_enable(uc_pdata->vss_supply, true);
+ }
+
+ if (ret)
+ error("%s: can't enable %s-supply!", dev->name, supply_type);
+
+ return ret;
+}
+
+int adc_data_mask(struct udevice *dev, unsigned int *data_mask)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+
+ if (!uc_pdata)
+ return -ENOSYS;
+
+ *data_mask = uc_pdata->data_mask;
+ return 0;
+}
+
+int adc_stop(struct udevice *dev)
+{
+ const struct adc_ops *ops = dev_get_driver_ops(dev);
+
+ if (!ops->stop)
+ return -ENOSYS;
+
+ return ops->stop(dev);
+}
+
+int adc_start_channel(struct udevice *dev, int channel)
+{
+ const struct adc_ops *ops = dev_get_driver_ops(dev);
+ int ret;
+
+ if (!ops->start_channel)
+ return -ENOSYS;
+
+ ret = check_channel(dev, channel, CHECK_NUMBER, __func__);
+ if (ret)
+ return ret;
+
+ ret = adc_supply_enable(dev);
+ if (ret)
+ return ret;
+
+ return ops->start_channel(dev, channel);
+}
+
+int adc_start_channels(struct udevice *dev, unsigned int channel_mask)
+{
+ const struct adc_ops *ops = dev_get_driver_ops(dev);
+ int ret;
+
+ if (!ops->start_channels)
+ return -ENOSYS;
+
+ ret = check_channel(dev, channel_mask, CHECK_MASK, __func__);
+ if (ret)
+ return ret;
+
+ ret = adc_supply_enable(dev);
+ if (ret)
+ return ret;
+
+ return ops->start_channels(dev, channel_mask);
+}
+
+int adc_channel_data(struct udevice *dev, int channel, unsigned int *data)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ const struct adc_ops *ops = dev_get_driver_ops(dev);
+ unsigned int timeout_us = uc_pdata->data_timeout_us;
+ int ret;
+
+ if (!ops->channel_data)
+ return -ENOSYS;
+
+ ret = check_channel(dev, channel, CHECK_NUMBER, __func__);
+ if (ret)
+ return ret;
+
+ do {
+ ret = ops->channel_data(dev, channel, data);
+ if (!ret || ret != -EBUSY)
+ break;
+
+ /* TODO: use timer uclass (for early calls). */
+ sdelay(5);
+ } while (timeout_us--);
+
+ return ret;
+}
+
+int adc_channels_data(struct udevice *dev, unsigned int channel_mask,
+ struct adc_channel *channels)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ unsigned int timeout_us = uc_pdata->multidata_timeout_us;
+ const struct adc_ops *ops = dev_get_driver_ops(dev);
+ int ret;
+
+ if (!ops->channels_data)
+ return -ENOSYS;
+
+ ret = check_channel(dev, channel_mask, CHECK_MASK, __func__);
+ if (ret)
+ return ret;
+
+ do {
+ ret = ops->channels_data(dev, channel_mask, channels);
+ if (!ret || ret != -EBUSY)
+ break;
+
+ /* TODO: use timer uclass (for early calls). */
+ sdelay(5);
+ } while (timeout_us--);
+
+ return ret;
+}
+
+int adc_channel_single_shot(const char *name, int channel, unsigned int *data)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_name(UCLASS_ADC, name, &dev);
+ if (ret)
+ return ret;
+
+ ret = adc_start_channel(dev, channel);
+ if (ret)
+ return ret;
+
+ ret = adc_channel_data(dev, channel, data);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int _adc_channels_single_shot(struct udevice *dev,
+ unsigned int channel_mask,
+ struct adc_channel *channels)
+{
+ unsigned int data;
+ int channel, ret;
+
+ for (channel = 0; channel <= ADC_MAX_CHANNEL; channel++) {
+ /* Check channel bit. */
+ if (!((channel_mask >> channel) & 0x1))
+ continue;
+
+ ret = adc_start_channel(dev, channel);
+ if (ret)
+ return ret;
+
+ ret = adc_channel_data(dev, channel, &data);
+ if (ret)
+ return ret;
+
+ channels->id = channel;
+ channels->data = data;
+ channels++;
+ }
+
+ return 0;
+}
+
+int adc_channels_single_shot(const char *name, unsigned int channel_mask,
+ struct adc_channel *channels)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_name(UCLASS_ADC, name, &dev);
+ if (ret)
+ return ret;
+
+ ret = adc_start_channels(dev, channel_mask);
+ if (ret)
+ goto try_manual;
+
+ ret = adc_channels_data(dev, channel_mask, channels);
+ if (ret)
+ return ret;
+
+ return 0;
+
+try_manual:
+ if (ret != -ENOSYS)
+ return ret;
+
+ return _adc_channels_single_shot(dev, channel_mask, channels);
+}
+
+static int adc_vdd_platdata_update(struct udevice *dev)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ int ret;
+
+ /* Warning!
+ * This function can't return supply device before its bind.
+ * Please pay attention to proper fdt scan sequence. If ADC device
+ * will bind before its supply regulator device, then the below 'get'
+ * will return an error.
+ */
+ ret = device_get_supply_regulator(dev, "vdd-supply",
+ &uc_pdata->vdd_supply);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_value(uc_pdata->vdd_supply);
+ if (ret < 0)
+ return ret;
+
+ uc_pdata->vdd_microvolts = ret;
+
+ return 0;
+}
+
+static int adc_vss_platdata_update(struct udevice *dev)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ int ret;
+
+ ret = device_get_supply_regulator(dev, "vss-supply",
+ &uc_pdata->vss_supply);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_value(uc_pdata->vss_supply);
+ if (ret < 0)
+ return ret;
+
+ uc_pdata->vss_microvolts = ret;
+
+ return 0;
+}
+
+int adc_vdd_value(struct udevice *dev, int *uV)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ int ret, value_sign = uc_pdata->vdd_polarity_negative ? -1 : 1;
+
+ if (!uc_pdata->vdd_supply)
+ goto nodev;
+
+ /* Update the regulator Value. */
+ ret = adc_vdd_platdata_update(dev);
+ if (ret)
+ return ret;
+nodev:
+ if (uc_pdata->vdd_microvolts == -ENODATA)
+ return -ENODATA;
+
+ *uV = uc_pdata->vdd_microvolts * value_sign;
+
+ return 0;
+}
+
+int adc_vss_value(struct udevice *dev, int *uV)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ int ret, value_sign = uc_pdata->vss_polarity_negative ? -1 : 1;
+
+ if (!uc_pdata->vss_supply)
+ goto nodev;
+
+ /* Update the regulator Value. */
+ ret = adc_vss_platdata_update(dev);
+ if (ret)
+ return ret;
+nodev:
+ if (uc_pdata->vss_microvolts == -ENODATA)
+ return -ENODATA;
+
+ *uV = uc_pdata->vss_microvolts * value_sign;
+
+ return 0;
+}
+
+static int adc_vdd_platdata_set(struct udevice *dev)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ int ret, offset = dev->of_offset;
+ const void *fdt = gd->fdt_blob;
+ char *prop;
+
+ prop = "vdd-polarity-negative";
+ uc_pdata->vdd_polarity_negative = fdtdec_get_bool(fdt, offset, prop);
+
+ ret = adc_vdd_platdata_update(dev);
+ if (ret != -ENOENT)
+ return ret;
+
+ /* No vdd-supply phandle. */
+ prop = "vdd-microvolts";
+ uc_pdata->vdd_microvolts = fdtdec_get_int(fdt, offset, prop, -ENODATA);
+
+ return 0;
+}
+
+static int adc_vss_platdata_set(struct udevice *dev)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ int ret, offset = dev->of_offset;
+ const void *fdt = gd->fdt_blob;
+ char *prop;
+
+ prop = "vss-polarity-negative";
+ uc_pdata->vss_polarity_negative = fdtdec_get_bool(fdt, offset, prop);
+
+ ret = adc_vss_platdata_update(dev);
+ if (ret != -ENOENT)
+ return ret;
+
+ /* No vss-supply phandle. */
+ prop = "vss-microvolts";
+ uc_pdata->vss_microvolts = fdtdec_get_int(fdt, offset, prop, -ENODATA);
+
+ return 0;
+}
+
+static int adc_pre_probe(struct udevice *dev)
+{
+ int ret;
+
+ /* Set ADC VDD platdata: polarity, uV, regulator (phandle). */
+ ret = adc_vdd_platdata_set(dev);
+ if (ret)
+ error("%s: Can't update Vdd. Error: %d", dev->name, ret);
+
+ /* Set ADC VSS platdata: polarity, uV, regulator (phandle). */
+ ret = adc_vss_platdata_set(dev);
+ if (ret)
+ error("%s: Can't update Vss. Error: %d", dev->name, ret);
+
+ return 0;
+}
+
+UCLASS_DRIVER(adc) = {
+ .id = UCLASS_ADC,
+ .name = "adc",
+ .pre_probe = adc_pre_probe,
+ .per_device_platdata_auto_alloc_size = ADC_UCLASS_PLATDATA_SIZE,
+};
diff --git a/drivers/adc/exynos-adc.c b/drivers/adc/exynos-adc.c
new file mode 100644
index 0000000000..534e68db8b
--- /dev/null
+++ b/drivers/adc/exynos-adc.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <adc.h>
+#include <asm/arch/adc.h>
+
+struct exynos_adc_priv {
+ int active_channel;
+ struct exynos_adc_v2 *regs;
+};
+
+int exynos_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct exynos_adc_priv *priv = dev_get_priv(dev);
+ struct exynos_adc_v2 *regs = priv->regs;
+
+ if (channel != priv->active_channel) {
+ error("Requested channel is not active!");
+ return -EINVAL;
+ }
+
+ if (ADC_V2_GET_STATUS_FLAG(readl(&regs->status)) != FLAG_CONV_END)
+ return -EBUSY;
+
+ *data = readl(&regs->dat) & ADC_V2_DAT_MASK;
+
+ return 0;
+}
+
+int exynos_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct exynos_adc_priv *priv = dev_get_priv(dev);
+ struct exynos_adc_v2 *regs = priv->regs;
+ unsigned int cfg;
+
+ /* Choose channel */
+ cfg = readl(&regs->con2);
+ cfg &= ~ADC_V2_CON2_CHAN_SEL_MASK;
+ cfg |= ADC_V2_CON2_CHAN_SEL(channel);
+ writel(cfg, &regs->con2);
+
+ /* Start conversion */
+ cfg = readl(&regs->con1);
+ writel(cfg | ADC_V2_CON1_STC_EN, &regs->con1);
+
+ priv->active_channel = channel;
+
+ return 0;
+}
+
+int exynos_adc_stop(struct udevice *dev)
+{
+ struct exynos_adc_priv *priv = dev_get_priv(dev);
+ struct exynos_adc_v2 *regs = priv->regs;
+ unsigned int cfg;
+
+ /* Stop conversion */
+ cfg = readl(&regs->con1);
+ cfg |= ~ADC_V2_CON1_STC_EN;
+
+ writel(cfg, &regs->con1);
+
+ priv->active_channel = -1;
+
+ return 0;
+}
+
+int exynos_adc_probe(struct udevice *dev)
+{
+ struct exynos_adc_priv *priv = dev_get_priv(dev);
+ struct exynos_adc_v2 *regs = priv->regs;
+ unsigned int cfg;
+
+ /* Check HW version */
+ if (readl(&regs->version) != ADC_V2_VERSION) {
+ error("This driver supports only ADC v2!");
+ return -ENXIO;
+ }
+
+ /* ADC Reset */
+ writel(ADC_V2_CON1_SOFT_RESET, &regs->con1);
+
+ /* Disable INT - will read status only */
+ writel(0x0, &regs->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_ESEL(ESEL_ADC_EVAL_TIME_20CLK);
+ cfg |= ADC_V2_CON2_HIGHF(HIGHF_CONV_RATE_600KSPS);
+ writel(cfg, &regs->con2);
+
+ priv->active_channel = -1;
+
+ return 0;
+}
+
+int exynos_adc_ofdata_to_platdata(struct udevice *dev)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+ struct exynos_adc_priv *priv = dev_get_priv(dev);
+
+ priv->regs = (struct exynos_adc_v2 *)dev_get_addr(dev);
+ if (priv->regs == (struct exynos_adc_v2 *)FDT_ADDR_T_NONE) {
+ error("Dev: %s - can't get address!", dev->name);
+ return -ENODATA;
+ }
+
+ uc_pdata->data_mask = ADC_V2_DAT_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = ADC_V2_CONV_TIMEOUT_US;
+
+ /* Mask available channel bits: [0:9] */
+ uc_pdata->channel_mask = (2 << ADC_V2_MAX_CHANNEL) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops exynos_adc_ops = {
+ .start_channel = exynos_adc_start_channel,
+ .channel_data = exynos_adc_channel_data,
+ .stop = exynos_adc_stop,
+};
+
+static const struct udevice_id exynos_adc_ids[] = {
+ { .compatible = "samsung,exynos-adc-v2" },
+ { }
+};
+
+U_BOOT_DRIVER(exynos_adc) = {
+ .name = "exynos-adc",
+ .id = UCLASS_ADC,
+ .of_match = exynos_adc_ids,
+ .ops = &exynos_adc_ops,
+ .probe = exynos_adc_probe,
+ .ofdata_to_platdata = exynos_adc_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct exynos_adc_priv),
+};
diff --git a/drivers/adc/sandbox.c b/drivers/adc/sandbox.c
new file mode 100644
index 0000000000..371892237a
--- /dev/null
+++ b/drivers/adc/sandbox.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <adc.h>
+#include <sandbox-adc.h>
+
+/**
+ * struct sandbox_adc_priv - sandbox ADC device's operation status and data
+ *
+ * @conversion_status - conversion status: ACTIVE (started) / INACTIVE (stopped)
+ * @conversion_mode - conversion mode: single or multi-channel
+ * @active_channel - active channel number, valid for single channel mode
+ * data[] - channels data
+ */
+struct sandbox_adc_priv {
+ int conversion_status;
+ int conversion_mode;
+ int active_channel_mask;
+ unsigned int data[4];
+};
+
+int sandbox_adc_start_channel(struct udevice *dev, int channel)
+{
+ struct sandbox_adc_priv *priv = dev_get_priv(dev);
+
+ /* Set single-channel mode */
+ priv->conversion_mode = SANDBOX_ADC_MODE_SINGLE_CHANNEL;
+ /* Select channel */
+ priv->active_channel_mask = 1 << channel;
+ /* Start conversion */
+ priv->conversion_status = SANDBOX_ADC_ACTIVE;
+
+ return 0;
+}
+
+int sandbox_adc_start_channels(struct udevice *dev, unsigned int channel_mask)
+{
+ struct sandbox_adc_priv *priv = dev_get_priv(dev);
+
+ /* Set single-channel mode */
+ priv->conversion_mode = SANDBOX_ADC_MODE_MULTI_CHANNEL;
+ /* Select channel */
+ priv->active_channel_mask = channel_mask;
+ /* Start conversion */
+ priv->conversion_status = SANDBOX_ADC_ACTIVE;
+
+ return 0;
+}
+
+int sandbox_adc_channel_data(struct udevice *dev, int channel,
+ unsigned int *data)
+{
+ struct sandbox_adc_priv *priv = dev_get_priv(dev);
+
+ /* For single-channel conversion mode, check if channel was selected */
+ if ((priv->conversion_mode == SANDBOX_ADC_MODE_SINGLE_CHANNEL) &&
+ !(priv->active_channel_mask & (1 << channel))) {
+ error("Request for an inactive channel!");
+ return -EINVAL;
+ }
+
+ /* The conversion must be started before reading the data */
+ if (priv->conversion_status == SANDBOX_ADC_INACTIVE)
+ return -EIO;
+
+ *data = priv->data[channel];
+
+ return 0;
+}
+
+int sandbox_adc_channels_data(struct udevice *dev, unsigned int channel_mask,
+ struct adc_channel *channels)
+{
+ struct sandbox_adc_priv *priv = dev_get_priv(dev);
+ int i;
+
+ /* Return error for single-channel conversion mode */
+ if (priv->conversion_mode == SANDBOX_ADC_MODE_SINGLE_CHANNEL) {
+ error("ADC in single-channel mode!");
+ return -EPERM;
+ }
+ /* Check channel selection */
+ if (!(priv->active_channel_mask & channel_mask)) {
+ error("Request for an inactive channel!");
+ return -EINVAL;
+ }
+ /* The conversion must be started before reading the data */
+ if (priv->conversion_status == SANDBOX_ADC_INACTIVE)
+ return -EIO;
+
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++) {
+ if (!((channel_mask >> i) & 0x1))
+ continue;
+
+ channels->data = priv->data[i];
+ channels->id = i;
+ channels++;
+ }
+
+ return 0;
+}
+
+int sandbox_adc_stop(struct udevice *dev)
+{
+ struct sandbox_adc_priv *priv = dev_get_priv(dev);
+
+ /* Start conversion */
+ priv->conversion_status = SANDBOX_ADC_INACTIVE;
+
+ return 0;
+}
+
+int sandbox_adc_probe(struct udevice *dev)
+{
+ struct sandbox_adc_priv *priv = dev_get_priv(dev);
+
+ /* Stop conversion */
+ priv->conversion_status = SANDBOX_ADC_INACTIVE;
+ /* Set single-channel mode */
+ priv->conversion_mode = SANDBOX_ADC_MODE_SINGLE_CHANNEL;
+ /* Deselect all channels */
+ priv->active_channel_mask = 0;
+
+ /* Set sandbox test data */
+ priv->data[0] = SANDBOX_ADC_CHANNEL0_DATA;
+ priv->data[1] = SANDBOX_ADC_CHANNEL1_DATA;
+ priv->data[2] = SANDBOX_ADC_CHANNEL2_DATA;
+ priv->data[3] = SANDBOX_ADC_CHANNEL3_DATA;
+
+ return 0;
+}
+
+int sandbox_adc_ofdata_to_platdata(struct udevice *dev)
+{
+ struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
+
+ uc_pdata->data_mask = SANDBOX_ADC_DATA_MASK;
+ uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
+ uc_pdata->data_timeout_us = 0;
+
+ /* Mask available channel bits: [0:3] */
+ uc_pdata->channel_mask = (1 << SANDBOX_ADC_CHANNELS) - 1;
+
+ return 0;
+}
+
+static const struct adc_ops sandbox_adc_ops = {
+ .start_channel = sandbox_adc_start_channel,
+ .start_channels = sandbox_adc_start_channels,
+ .channel_data = sandbox_adc_channel_data,
+ .channels_data = sandbox_adc_channels_data,
+ .stop = sandbox_adc_stop,
+};
+
+static const struct udevice_id sandbox_adc_ids[] = {
+ { .compatible = "sandbox,adc" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_adc) = {
+ .name = "sandbox-adc",
+ .id = UCLASS_ADC,
+ .of_match = sandbox_adc_ids,
+ .ops = &sandbox_adc_ops,
+ .probe = sandbox_adc_probe,
+ .ofdata_to_platdata = sandbox_adc_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct sandbox_adc_priv),
+};
diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c
index 15ecfee961..44353c72f4 100644
--- a/drivers/mmc/s5p_sdhci.c
+++ b/drivers/mmc/s5p_sdhci.c
@@ -106,6 +106,12 @@ static int do_sdhci_init(struct sdhci_host *host)
flag = host->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
dev_id = host->index + PERIPH_ID_SDMMC0;
+ ret = exynos_pinmux_config(dev_id, flag);
+ if (ret) {
+ printf("external SD not configured\n");
+ return ret;
+ }
+
if (dm_gpio_is_valid(&host->pwr_gpio)) {
dm_gpio_set_value(&host->pwr_gpio, 1);
ret = exynos_pinmux_config(dev_id, flag);
@@ -121,12 +127,6 @@ static int do_sdhci_init(struct sdhci_host *host)
debug("no SD card detected (%d)\n", ret);
return -ENODEV;
}
-
- ret = exynos_pinmux_config(dev_id, flag);
- if (ret) {
- printf("external SD not configured\n");
- return ret;
- }
}
return s5p_sdhci_core_init(host);
@@ -193,7 +193,7 @@ static int process_nodes(const void *blob, int node_list[], int count)
}
ret = do_sdhci_init(host);
- if (ret) {
+ if (ret && ret != -ENODEV) {
printf("%s: failed to initialize dev %d (%d)\n", __func__, i, ret);
failed++;
}
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index 547fd1aaa6..fb29843a5a 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -33,6 +33,20 @@ config DM_PMIC_MAX77686
This config enables implementation of driver-model pmic uclass features
for PMIC MAX77686. The driver implements read/write operations.
+config PMIC_S2MPS11
+ bool "Enable Driver Model for PMIC Samsung S2MPS11"
+ depends on DM_PMIC
+ ---help---
+ The Samsung S2MPS11 PMIC provides:
+ - 38 adjustable LDO regulators
+ - 9 High-Efficiency Buck Converters
+ - 1 BuckBoost Converter
+ - RTC with two alarms
+ - Backup battery charger
+ - I2C Configuration Interface
+ This driver provides access to I/O interface only.
+ Binding info: doc/device-tree-bindings/pmic/s2mps11.txt
+
config DM_PMIC_SANDBOX
bool "Enable Driver Model for emulated Sandbox PMIC "
depends on DM_PMIC
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 00fde71b2c..91e78f8149 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -8,6 +8,7 @@
obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
obj-$(CONFIG_DM_PMIC_PFUZE100) += pfuze100.o
+obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o
obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o
obj-$(CONFIG_PMIC_ACT8846) += act8846.o
obj-$(CONFIG_PMIC_TPS65090) += tps65090.o
diff --git a/drivers/power/pmic/s2mps11.c b/drivers/power/pmic/s2mps11.c
new file mode 100644
index 0000000000..9d83059c40
--- /dev/null
+++ b/drivers/power/pmic/s2mps11.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <power/pmic.h>
+#include <power/s2mps11.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int s2mps11_reg_count(struct udevice *dev)
+{
+ return S2MPS11_REG_COUNT;
+}
+
+static int s2mps11_write(struct udevice *dev, uint reg, const uint8_t *buff,
+ int len)
+{
+ int ret;
+
+ ret = dm_i2c_write(dev, reg, buff, len);
+ if (ret)
+ error("write error to device: %p register: %#x!", dev, reg);
+
+ return ret;
+}
+
+static int s2mps11_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+ int ret;
+
+ ret = dm_i2c_read(dev, reg, buff, len);
+ if (ret)
+ error("read error from device: %p register: %#x!", dev, reg);
+
+ return ret;
+}
+
+static struct dm_pmic_ops s2mps11_ops = {
+ .reg_count = s2mps11_reg_count,
+ .read = s2mps11_read,
+ .write = s2mps11_write,
+};
+
+static const struct udevice_id s2mps11_ids[] = {
+ { .compatible = "samsung,s2mps11-pmic" },
+ { }
+};
+
+U_BOOT_DRIVER(pmic_s2mps11) = {
+ .name = "s2mps11_pmic",
+ .id = UCLASS_PMIC,
+ .of_match = s2mps11_ids,
+ .ops = &s2mps11_ops,
+};
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
index a5170df916..4241a4c7f2 100644
--- a/drivers/power/regulator/regulator-uclass.c
+++ b/drivers/power/regulator/regulator-uclass.c
@@ -138,6 +138,13 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp)
return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp);
}
+int device_get_supply_regulator(struct udevice *dev, const char *supply_name,
+ struct udevice **devp)
+{
+ return uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+ supply_name, devp);
+}
+
int regulator_autoset(struct udevice *dev)
{
struct dm_regulator_uclass_platdata *uc_pdata;
diff --git a/include/adc.h b/include/adc.h
new file mode 100644
index 0000000000..4b14017849
--- /dev/null
+++ b/include/adc.h
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _ADC_H_
+#define _ADC_H_
+
+/* ADC_CHANNEL() - ADC channel bit mask, to select only required channels */
+#define ADC_CHANNEL(x) (1 << x)
+
+/* The last possible selected channel with 32-bit mask */
+#define ADC_MAX_CHANNEL 31
+
+/**
+ * adc_data_format: define the ADC output data format, can be useful when
+ * the device's input Voltage range is bipolar.
+ * - ADC_DATA_FORMAT_BIN - binary offset
+ * - ADC_DATA_FORMAT_2S - two's complement
+ *
+ * Note: Device's driver should fill the 'data_format' field of its uclass's
+ * platform data using one of the above data format types.
+ */
+enum adc_data_format {
+ ADC_DATA_FORMAT_BIN,
+ ADC_DATA_FORMAT_2S,
+};
+
+/**
+ * struct adc_channel - structure to hold channel conversion data.
+ * Useful to keep the result of a multi-channel conversion output.
+ *
+ * @id - channel id
+ * @data - channel conversion data
+ */
+struct adc_channel {
+ int id;
+ unsigned int data;
+};
+
+/**
+ * struct adc_uclass_platdata - basic ADC info
+ *
+ * Note: The positive/negative reference Voltage is only a name and it doesn't
+ * provide an information about the value polarity. It is possible, for both
+ * values to be a negative or positive. For this purpose the uclass's platform
+ * data provides a bool fields: 'vdd/vss_supply_is_negative'. This is useful,
+ * since the regulator API returns only a positive Voltage values.
+ *
+ * To get the reference Voltage values with polarity, use functions:
+ * - adc_vdd_value()
+ * - adc_vss_value()
+ * Those are useful for some cases of ADC's references, e.g.:
+ * * Vdd: +3.3V; Vss: -3.3V -> 6.6 Vdiff
+ * * Vdd: +3.3V; Vss: +0.3V -> 3.0 Vdiff
+ * * Vdd: +3.3V; Vss: 0.0V -> 3.3 Vdiff
+ * The last one is usually standard and doesn't require the fdt polarity info.
+ *
+ * For more informations read binding info:
+ * - doc/device-tree-bindings/adc/adc.txt
+ *
+ * @data_mask - conversion output data mask
+ * @data_timeout_us - single channel conversion timeout
+ * @multidata_timeout_us - multi channel conversion timeout
+ * @channel_mask - bit mask of available channels [0:31]
+ * @vdd_supply - positive reference Voltage supply (regulator)
+ * @vss_supply - negative reference Voltage supply (regulator)
+ * @vdd_polarity_negative - positive reference Voltage has negative polarity
+ * @vss_polarity_negative - negative reference Voltage has negative polarity
+ * @vdd_microvolts - positive reference Voltage value
+ * @vss_microvolts - negative reference Voltage value
+ */
+struct adc_uclass_platdata {
+ int data_format;
+ unsigned int data_mask;
+ unsigned int data_timeout_us;
+ unsigned int multidata_timeout_us;
+ unsigned int channel_mask;
+ struct udevice *vdd_supply;
+ struct udevice *vss_supply;
+ bool vdd_polarity_negative;
+ bool vss_polarity_negative;
+ int vdd_microvolts;
+ int vss_microvolts;
+};
+
+/**
+ * struct adc_ops - ADC device operations for single/multi-channel operation.
+ */
+struct adc_ops {
+ /**
+ * start_channel() - start conversion with its default parameters
+ * for the given channel number.
+ *
+ * @dev: ADC device to init
+ * @channel: analog channel number
+ * @return: 0 if OK, -ve on error
+ */
+ int (*start_channel)(struct udevice *dev, int channel);
+
+ /**
+ * start_channels() - start conversion with its default parameters
+ * for the channel numbers selected by the bit mask.
+ *
+ * This is optional, useful when the hardware supports multichannel
+ * conversion by the single software trigger.
+ *
+ * @dev: ADC device to init
+ * @channel_mask: bit mask of selected analog channels
+ * @return: 0 if OK, -ve on error
+ */
+ int (*start_channels)(struct udevice *dev, unsigned int channel_mask);
+
+ /**
+ * channel_data() - get conversion output data for the given channel.
+ *
+ * Note: The implementation of this function should only check, that
+ * the conversion data is available at the call time. If the hardware
+ * requires some delay to get the data, then this function should
+ * return with -EBUSY value. The ADC API will call it in a loop,
+ * until the data is available or the timeout expires. The maximum
+ * timeout for this operation is defined by the field 'data_timeout_us'
+ * in ADC uclasses platform data structure.
+ *
+ * @dev: ADC device to trigger
+ * @channel: selected analog channel number
+ * @data: returned pointer to selected channel's output data
+ * @return: 0 if OK, -EBUSY if busy, and other negative on error
+ */
+ int (*channel_data)(struct udevice *dev, int channel,
+ unsigned int *data);
+
+ /**
+ * channels_data() - get conversion data for the selected channels.
+ *
+ * This is optional, useful when multichannel conversion is supported
+ * by the hardware, by the single software trigger.
+ *
+ * For the proper implementation, please look at the 'Note' for the
+ * above method. The only difference is in used timeout value, which
+ * is defined by field 'multidata_timeout_us'.
+ *
+ * @dev: ADC device to trigger
+ * @channel_mask: bit mask of selected analog channels
+ * @channels: returned pointer to array of output data for channels
+ * selected by the given mask
+ * @return: 0 if OK, -ve on error
+ */
+ int (*channels_data)(struct udevice *dev, unsigned int channel_mask,
+ struct adc_channel *channels);
+
+ /**
+ * stop() - stop conversion of the given ADC device
+ *
+ * @dev: ADC device to stop
+ * @return: 0 if OK, -ve on error
+ */
+ int (*stop)(struct udevice *dev);
+};
+
+/**
+ * adc_start_channel() - start conversion for given device/channel and exit.
+ *
+ * @dev: ADC device
+ * @channel: analog channel number
+ * @return: 0 if OK, -ve on error
+ */
+int adc_start_channel(struct udevice *dev, int channel);
+
+/**
+ * adc_start_channels() - start conversion for given device/channels and exit.
+ *
+ * Note:
+ * To use this function, device must implement method: start_channels().
+ *
+ * @dev: ADC device to start
+ * @channel_mask: channel selection - a bit mask
+ * @channel_mask: bit mask of analog channels
+ * @return: 0 if OK, -ve on error
+ */
+int adc_start_channels(struct udevice *dev, unsigned int channel_mask);
+
+/**
+ * adc_channel_data() - get conversion data for the given device channel number.
+ *
+ * @dev: ADC device to read
+ * @channel: analog channel number
+ * @data: pointer to returned channel's data
+ * @return: 0 if OK, -ve on error
+ */
+int adc_channel_data(struct udevice *dev, int channel, unsigned int *data);
+
+/**
+ * adc_channels_data() - get conversion data for the channels selected by mask
+ *
+ * Note:
+ * To use this function, device must implement methods:
+ * - start_channels()
+ * - channels_data()
+ *
+ * @dev: ADC device to read
+ * @channel_mask: channel selection - a bit mask
+ * @channels: pointer to structure array of returned data for each channel
+ * @return: 0 if OK, -ve on error
+ */
+int adc_channels_data(struct udevice *dev, unsigned int channel_mask,
+ struct adc_channel *channels);
+
+/**
+ * adc_data_mask() - get data mask (ADC resolution bitmask) for given ADC device
+ *
+ * This can be used if adc uclass platform data is filled.
+ *
+ * @dev: ADC device to check
+ * @data_mask: pointer to the returned data bitmask
+ * @return: 0 if OK, -ve on error
+ */
+int adc_data_mask(struct udevice *dev, unsigned int *data_mask);
+
+/**
+ * adc_channel_single_shot() - get output data of conversion for the ADC
+ * device's channel. This function searches for the device with the given name,
+ * starts the given channel conversion and returns the output data.
+ *
+ * Note: To use this function, device must implement metods:
+ * - start_channel()
+ * - channel_data()
+ *
+ * @name: device's name to search
+ * @channel: device's input channel to init
+ * @data: pointer to conversion output data
+ * @return: 0 if OK, -ve on error
+ */
+int adc_channel_single_shot(const char *name, int channel, unsigned int *data);
+
+/**
+ * adc_channels_single_shot() - get ADC conversion output data for the selected
+ * device's channels. This function searches for the device by the given name,
+ * starts the selected channels conversion and returns the output data as array
+ * of type 'struct adc_channel'.
+ *
+ * Note: This function can be used if device implements one of ADC's single
+ * or multi-channel operation API. If multi-channel operation is not supported,
+ * then each selected channel is triggered by the sequence start/data in a loop.
+ *
+ * @name: device's name to search
+ * @channel_mask: channel selection - a bit mask
+ * @channels: pointer to conversion output data for the selected channels
+ * @return: 0 if OK, -ve on error
+ */
+int adc_channels_single_shot(const char *name, unsigned int channel_mask,
+ struct adc_channel *channels);
+
+/**
+ * adc_vdd_value() - get the ADC device's positive reference Voltage value
+ *
+ * Note: Depending on bool value 'vdd_supply_is_negative' of platform data,
+ * the returned uV value can be negative, and it's not an error.
+ *
+ * @dev: ADC device to check
+ * @uV: Voltage value with polarization sign (uV)
+ * @return: 0 on success or -ve on error
+*/
+int adc_vdd_value(struct udevice *dev, int *uV);
+
+/**
+ * adc_vss_value() - get the ADC device's negative reference Voltage value
+ *
+ * Note: Depending on bool value 'vdd_supply_is_negative' of platform data,
+ * the returned uV value can be negative, and it's not an error.
+ *
+ * @dev: ADC device to check
+ * @uV: Voltage value with polarization sign (uV)
+ * @return: 0 on success or -ve on error
+*/
+int adc_vss_value(struct udevice *dev, int *uV);
+
+/**
+ * adc_stop() - stop operation for given ADC device.
+ *
+ * @dev: ADC device to stop
+ * @return: 0 if OK, -ve on error
+ */
+int adc_stop(struct udevice *dev);
+
+#endif
diff --git a/include/configs/odroid_xu3.h b/include/configs/odroid_xu3.h
index 3c701587bf..648e48bcf4 100644
--- a/include/configs/odroid_xu3.h
+++ b/include/configs/odroid_xu3.h
@@ -94,6 +94,8 @@
"boot.scr fat 0 1;" \
"boot.cmd fat 0 1;" \
"exynos5422-odroidxu3.dtb fat 0 1;" \
+ "exynos5422-odroidxu3-lite.dtb fat 0 1;" \
+ "exynos5422-odroidxu4.dtb fat 0 1;" \
"boot part 0 1;" \
"root part 0 2\0"
@@ -113,9 +115,19 @@
/* Enable: board/samsung/common/misc.c to use set_dfu_alt_info() */
#define CONFIG_MISC_COMMON
+#define CONFIG_MISC_INIT_R
#define CONFIG_SET_DFU_ALT_INFO
#define CONFIG_SET_DFU_ALT_BUF_LEN (SZ_1K)
+/* Set soc_rev, soc_id, board_rev, boardname, fdtfile */
+#define CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
+#define CONFIG_ODROID_REV_AIN 9
+#define CONFIG_REVISION_TAG
+#define CONFIG_BOARD_TYPES
+
+#undef CONFIG_SYS_BOARD
+#define CONFIG_SYS_BOARD "odroid"
+
/* Define new extra env settings, including DFU settings */
#undef CONFIG_EXTRA_ENV_SETTINGS
#define CONFIG_EXTRA_ENV_SETTINGS \
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 886a44c134..d0cf4ce6a0 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -25,6 +25,7 @@ enum uclass_id {
UCLASS_SIMPLE_BUS, /* bus with child devices */
/* U-Boot uclasses start here - in alphabetical order */
+ UCLASS_ADC, /* Analog-to-digital converter */
UCLASS_CLK, /* Clock source, e.g. used by peripherals */
UCLASS_CPU, /* CPU, typically part of an SoC */
UCLASS_CROS_EC, /* Chrome OS EC */
diff --git a/include/power/regulator.h b/include/power/regulator.h
index 1a51c3f07b..63c0814fe8 100644
--- a/include/power/regulator.h
+++ b/include/power/regulator.h
@@ -419,4 +419,20 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp);
*/
int regulator_get_by_platname(const char *platname, struct udevice **devp);
+/**
+ * device_get_supply_regulator: returns the pointer to the supply regulator.
+ * Search by phandle, found in device's node.
+ *
+ * Note: Please pay attention to proper order of device bind sequence.
+ * The regulator device searched by the phandle, must be binded before
+ * this function call.
+ *
+ * @dev - device with supply phandle
+ * @supply_name - phandle name of regulator
+ * @devp - returned pointer to the supply device
+ * @return 0 on success or negative value of errno.
+ */
+int device_get_supply_regulator(struct udevice *dev, const char *supply_name,
+ struct udevice **devp);
+
#endif /* _INCLUDE_REGULATOR_H_ */
diff --git a/include/power/s2mps11.h b/include/power/s2mps11.h
new file mode 100644
index 0000000000..5da47198a4
--- /dev/null
+++ b/include/power/s2mps11.h
@@ -0,0 +1,109 @@
+#ifndef __S2MPS11__H__
+#define __S2MPS11__H__
+
+enum s2mps11_reg {
+ S2MPS11_REG_ID = 0,
+ S2MPS11_REG_INT1,
+ S2MPS11_REG_INT2,
+ S2MPS11_REG_INT3,
+ S2MPS11_REG_INT1M,
+ S2MPS11_REG_INT2M,
+ S2MPS11_REG_INT3M,
+ S2MPS11_REG_STATUS1,
+ S2MPS11_REG_STATUS2,
+ S2MPS11_REG_OFFSRC,
+ S2MPS11_REG_PWRONSRC,
+ S2MPS11_REG_RTC_CTRL,
+ S2MPS11_REG_CTRL1,
+ S2MPS11_REG_ETC_TEST,
+ S2MPS11_REG_RSVD3,
+ S2MPS11_REG_BU_CHG,
+ S2MPS11_REG_RAMP,
+ S2MPS11_REG_RAMP_BUCK,
+ S2MPS11_REG_LDO1_8,
+ S2MPS11_REG_LDO9_16,
+ S2MPS11_REG_LDO17_24,
+ S2MPS11_REG_LDO25_32,
+ S2MPS11_REG_LDO33_38,
+ S2MPS11_REG_LDO1_8_OVC,
+ S2MPS11_REG_LDO9_16_OVC,
+ S2MPS11_REG_LDO17_24_OVC,
+ S2MPS11_REG_LDO25_32_OVC,
+ S2MPS11_REG_LDO33_38_OVC,
+ S2MPS11_REG_RESERVED1,
+ S2MPS11_REG_RESERVED2,
+ S2MPS11_REG_RESERVED3,
+ S2MPS11_REG_RESERVED4,
+ S2MPS11_REG_RESERVED5,
+ S2MPS11_REG_RESERVED6,
+ S2MPS11_REG_RESERVED7,
+ S2MPS11_REG_RESERVED8,
+ S2MPS11_REG_WDRSTEN_CTRL,
+ S2MPS11_REG_B1CTRL1,
+ S2MPS11_REG_B1CTRL2,
+ S2MPS11_REG_B2CTRL1,
+ S2MPS11_REG_B2CTRL2,
+ S2MPS11_REG_B3CTRL1,
+ S2MPS11_REG_B3CTRL2,
+ S2MPS11_REG_B4CTRL1,
+ S2MPS11_REG_B4CTRL2,
+ S2MPS11_REG_B5CTRL1,
+ S2MPS11_REG_BUCK5_SW,
+ S2MPS11_REG_B5CTRL2,
+ S2MPS11_REG_B5CTRL3,
+ S2MPS11_REG_B5CTRL4,
+ S2MPS11_REG_B5CTRL5,
+ S2MPS11_REG_B6CTRL1,
+ S2MPS11_REG_B6CTRL2,
+ S2MPS11_REG_B7CTRL1,
+ S2MPS11_REG_B7CTRL2,
+ S2MPS11_REG_B8CTRL1,
+ S2MPS11_REG_B8CTRL2,
+ S2MPS11_REG_B9CTRL1,
+ S2MPS11_REG_B9CTRL2,
+ S2MPS11_REG_B10CTRL1,
+ S2MPS11_REG_B10CTRL2,
+ S2MPS11_REG_L1CTRL,
+ S2MPS11_REG_L2CTRL,
+ S2MPS11_REG_L3CTRL,
+ S2MPS11_REG_L4CTRL,
+ S2MPS11_REG_L5CTRL,
+ S2MPS11_REG_L6CTRL,
+ S2MPS11_REG_L7CTRL,
+ S2MPS11_REG_L8CTRL,
+ S2MPS11_REG_L9CTRL,
+ S2MPS11_REG_L10CTRL,
+ S2MPS11_REG_L11CTRL,
+ S2MPS11_REG_L12CTRL,
+ S2MPS11_REG_L13CTRL,
+ S2MPS11_REG_L14CTRL,
+ S2MPS11_REG_L15CTRL,
+ S2MPS11_REG_L16CTRL,
+ S2MPS11_REG_L17CTRL,
+ S2MPS11_REG_L18CTRL,
+ S2MPS11_REG_L19CTRL,
+ S2MPS11_REG_L20CTRL,
+ S2MPS11_REG_L21CTRL,
+ S2MPS11_REG_L22CTRL,
+ S2MPS11_REG_L23CTRL,
+ S2MPS11_REG_L24CTRL,
+ S2MPS11_REG_L25CTRL,
+ S2MPS11_REG_L26CTRL,
+ S2MPS11_REG_L27CTRL,
+ S2MPS11_REG_L28CTRL,
+ S2MPS11_REG_L29CTRL,
+ S2MPS11_REG_L30CTRL,
+ S2MPS11_REG_L31CTRL,
+ S2MPS11_REG_L32CTRL,
+ S2MPS11_REG_L33CTRL,
+ S2MPS11_REG_L34CTRL,
+ S2MPS11_REG_L35CTRL,
+ S2MPS11_REG_L36CTRL,
+ S2MPS11_REG_L37CTRL,
+ S2MPS11_REG_L38CTRL,
+ S2MPS11_REG_COUNT,
+};
+
+#define S2MPS11_LDO26_ENABLE 0xec
+
+#endif
diff --git a/include/power/sandbox_pmic.h b/include/power/sandbox_pmic.h
index 8547674971..7fdbfb9fc6 100644
--- a/include/power/sandbox_pmic.h
+++ b/include/power/sandbox_pmic.h
@@ -126,6 +126,10 @@ enum {
#define SANDBOX_BUCK1_AUTOSET_EXPECTED_UA 200000
#define SANDBOX_BUCK1_AUTOSET_EXPECTED_ENABLE true
+/* BUCK2: for testing sandbox ADC's supply */
+#define SANDBOX_BUCK2_INITIAL_EXPECTED_UV 3000000
+#define SANDBOX_BUCK2_SET_UV 3300000
+
/* LDO1/2 for testing regulator_list_autoset() */
#define SANDBOX_LDO1_AUTOSET_EXPECTED_UV 1800000
#define SANDBOX_LDO1_AUTOSET_EXPECTED_UA 100000
diff --git a/include/samsung/exynos5-dt-types.h b/include/samsung/exynos5-dt-types.h
new file mode 100644
index 0000000000..479e2e793d
--- /dev/null
+++ b/include/samsung/exynos5-dt-types.h
@@ -0,0 +1,27 @@
+#ifndef _EXYNOS5_DT_H_
+#define _EXYNOS5_DT_H_
+
+enum {
+ EXYNOS5_BOARD_GENERIC,
+
+ EXYNOS5_BOARD_ODROID_XU3,
+ EXYNOS5_BOARD_ODROID_XU3_REV01,
+ EXYNOS5_BOARD_ODROID_XU3_REV02,
+ EXYNOS5_BOARD_ODROID_XU4_REV01,
+ EXYNOS5_BOARD_ODROID_UNKNOWN,
+
+ EXYNOS5_BOARD_COUNT,
+};
+
+struct odroid_rev_info {
+ int board_type;
+ int board_rev;
+ int adc_val;
+ const char *name;
+};
+
+bool board_is_generic(void);
+bool board_is_odroidxu3(void);
+bool board_is_odroidxu4(void);
+
+#endif
diff --git a/include/sandbox-adc.h b/include/sandbox-adc.h
new file mode 100644
index 0000000000..79ff01c6ab
--- /dev/null
+++ b/include/sandbox-adc.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _SANDBOX_ADC_H_
+#define _SANDBOX_ADC_H_
+
+#define SANDBOX_ADC_DEVNAME "adc@0"
+#define SANDBOX_ADC_DATA_MASK 0xffff /* 16-bits resolution */
+#define SANDBOX_ADC_CHANNELS 4
+#define SANDBOX_ADC_CHANNEL0_DATA 0x0
+#define SANDBOX_ADC_CHANNEL1_DATA 0x1000
+#define SANDBOX_ADC_CHANNEL2_DATA 0x2000
+#define SANDBOX_ADC_CHANNEL3_DATA 0x3000
+
+enum sandbox_adc_mode {
+ SANDBOX_ADC_MODE_SINGLE_CHANNEL = 0,
+ SANDBOX_ADC_MODE_MULTI_CHANNEL,
+};
+
+enum sandbox_adc_status {
+ SANDBOX_ADC_INACTIVE = 0,
+ SANDBOX_ADC_ACTIVE,
+};
+
+#define SANDBOX_ADC_VSS_VALUE 0
+
+#endif
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 7b3626cb32..39630f68c8 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -33,4 +33,5 @@ obj-y += syscon.o
obj-$(CONFIG_DM_USB) += usb.o
obj-$(CONFIG_DM_PMIC) += pmic.o
obj-$(CONFIG_DM_REGULATOR) += regulator.o
+obj-$(CONFIG_ADC) += adc.o
endif
diff --git a/test/dm/adc.c b/test/dm/adc.c
new file mode 100644
index 0000000000..b0d4fe5b23
--- /dev/null
+++ b/test/dm/adc.c
@@ -0,0 +1,165 @@
+/*
+ * Tests for the driver model ADC API
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <adc.h>
+#include <dm.h>
+#include <dm/root.h>
+#include <dm/util.h>
+#include <dm/test.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <power/regulator.h>
+#include <power/sandbox_pmic.h>
+#include <sandbox-adc.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int dm_test_adc_bind(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc", &dev));
+ ut_asserteq_str(SANDBOX_ADC_DEVNAME, dev->name);
+
+ return 0;
+}
+DM_TEST(dm_test_adc_bind, DM_TESTF_SCAN_FDT);
+
+static int dm_test_adc_wrong_channel_selection(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc", &dev));
+ ut_asserteq(-EINVAL, adc_start_channel(dev, SANDBOX_ADC_CHANNELS));
+
+ return 0;
+}
+DM_TEST(dm_test_adc_wrong_channel_selection, DM_TESTF_SCAN_FDT);
+
+static int dm_test_adc_supply(struct unit_test_state *uts)
+{
+ struct udevice *supply;
+ struct udevice *dev;
+ int uV;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc", &dev));
+
+ /* Test Vss value - predefined 0 uV */
+ ut_assertok(adc_vss_value(dev, &uV));
+ ut_asserteq(SANDBOX_ADC_VSS_VALUE, uV);
+
+ /* Test Vdd initial value - buck2 */
+ ut_assertok(adc_vdd_value(dev, &uV));
+ ut_asserteq(SANDBOX_BUCK2_INITIAL_EXPECTED_UV, uV);
+
+ /* Change Vdd value - buck2 manual preset */
+ ut_assertok(regulator_get_by_devname(SANDBOX_BUCK2_DEVNAME, &supply));
+ ut_assertok(regulator_set_value(supply, SANDBOX_BUCK2_SET_UV));
+ ut_asserteq(SANDBOX_BUCK2_SET_UV, regulator_get_value(supply));
+
+ /* Update ADC platdata and get new Vdd value */
+ ut_assertok(adc_vdd_value(dev, &uV));
+ ut_asserteq(SANDBOX_BUCK2_SET_UV, uV);
+
+ /* Disable buck2 and test ADC supply enable function */
+ ut_assertok(regulator_set_enable(supply, false));
+ ut_asserteq(false, regulator_get_enable(supply));
+ /* adc_start_channel() should enable the supply regulator */
+ ut_assertok(adc_start_channel(dev, 0));
+ ut_asserteq(true, regulator_get_enable(supply));
+
+ return 0;
+}
+DM_TEST(dm_test_adc_supply, DM_TESTF_SCAN_FDT);
+
+struct adc_channel adc_channel_test_data[] = {
+ { 0, SANDBOX_ADC_CHANNEL0_DATA },
+ { 1, SANDBOX_ADC_CHANNEL1_DATA },
+ { 2, SANDBOX_ADC_CHANNEL2_DATA },
+ { 3, SANDBOX_ADC_CHANNEL3_DATA },
+};
+
+static int dm_test_adc_single_channel_conversion(struct unit_test_state *uts)
+{
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, data;
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc", &dev));
+ /* Test each ADC channel's value */
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) {
+ ut_assertok(adc_start_channel(dev, tdata->id));
+ ut_assertok(adc_channel_data(dev, tdata->id, &data));
+ ut_asserteq(tdata->data, data);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_adc_single_channel_conversion, DM_TESTF_SCAN_FDT);
+
+static int dm_test_adc_multi_channel_conversion(struct unit_test_state *uts)
+{
+ struct adc_channel channels[SANDBOX_ADC_CHANNELS];
+ struct udevice *dev;
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, channel_mask;
+
+ channel_mask = ADC_CHANNEL(0) | ADC_CHANNEL(1) |
+ ADC_CHANNEL(2) | ADC_CHANNEL(3);
+
+ /* Start multi channel conversion */
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc", &dev));
+ ut_assertok(adc_start_channels(dev, channel_mask));
+ ut_assertok(adc_channels_data(dev, channel_mask, channels));
+
+ /* Compare the expected and returned conversion data. */
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++)
+ ut_asserteq(tdata->data, channels[i].data);
+
+ return 0;
+}
+DM_TEST(dm_test_adc_multi_channel_conversion, DM_TESTF_SCAN_FDT);
+
+static int dm_test_adc_single_channel_shot(struct unit_test_state *uts)
+{
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, data;
+
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) {
+ /* Start single channel conversion */
+ ut_assertok(adc_channel_single_shot("adc", tdata->id, &data));
+ /* Compare the expected and returned conversion data. */
+ ut_asserteq(tdata->data, data);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_adc_single_channel_shot, DM_TESTF_SCAN_FDT);
+
+static int dm_test_adc_multi_channel_shot(struct unit_test_state *uts)
+{
+ struct adc_channel channels[SANDBOX_ADC_CHANNELS];
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, channel_mask;
+
+ channel_mask = ADC_CHANNEL(0) | ADC_CHANNEL(1) |
+ ADC_CHANNEL(2) | ADC_CHANNEL(3);
+
+ /* Start single call and multi channel conversion */
+ ut_assertok(adc_channels_single_shot("adc", channel_mask, channels));
+
+ /* Compare the expected and returned conversion data. */
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++)
+ ut_asserteq(tdata->data, channels[i].data);
+
+ return 0;
+}
+DM_TEST(dm_test_adc_multi_channel_shot, DM_TESTF_SCAN_FDT);