sandbox: add ADC driver

This commit adds implementation of Sandbox ADC device emulation.
The device provides:
- single and multi-channel conversion
- 4 channels with predefined conversion output data
- 16-bit resolution

Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
Cc: Simon Glass <sjg@chromium.org>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
diff --git a/arch/sandbox/dts/sandbox_pmic.dtsi b/arch/sandbox/dts/sandbox_pmic.dtsi
index 44a26b1..ce261b9 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 730de8a..e2c4971 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/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 67ae99b..94c8e68 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -65,3 +65,5 @@
 CONFIG_UT_ENV=y
 CONFIG_REMOTEPROC_SANDBOX=y
 CONFIG_CMD_REMOTEPROC=y
+CONFIG_ADC=y
+CONFIG_ADC_SANDBOX=y
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig
index 223b65e..e5335f7 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -19,3 +19,12 @@
 	  - 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
index eb85b8b..cebf26d 100644
--- a/drivers/adc/Makefile
+++ b/drivers/adc/Makefile
@@ -7,3 +7,4 @@
 
 obj-$(CONFIG_ADC) += adc-uclass.o
 obj-$(CONFIG_ADC_EXYNOS) += exynos-adc.o
+obj-$(CONFIG_ADC_SANDBOX) += sandbox.o
diff --git a/drivers/adc/sandbox.c b/drivers/adc/sandbox.c
new file mode 100644
index 0000000..3718922
--- /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),
+};