From 08d6300a35bf2eb7915f0fa2fea9fa60b5075b71 Mon Sep 17 00:00:00 2001
From: Przemyslaw Marczak
Date: Tue, 27 Oct 2015 13:08:06 +0100
Subject: [PATCH] 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
Cc: Simon Glass
Signed-off-by: Minkyu Kang
---
arch/sandbox/dts/sandbox_pmic.dtsi | 2 +-
arch/sandbox/dts/test.dts | 6 +
configs/sandbox_defconfig | 2 +
drivers/adc/Kconfig | 9 ++
drivers/adc/Makefile | 1 +
drivers/adc/sandbox.c | 174 +++++++++++++++++++++++++++++
6 files changed, 193 insertions(+), 1 deletion(-)
create mode 100644 drivers/adc/sandbox.c
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/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/drivers/adc/Kconfig b/drivers/adc/Kconfig
index 223b65eada..e5335f7234 100644
--- a/drivers/adc/Kconfig
+++ b/drivers/adc/Kconfig
@@ -19,3 +19,12 @@ config ADC_EXYNOS
- 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 eb85b8b1a6..cebf26de07 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 0000000000..371892237a
--- /dev/null
+++ b/drivers/adc/sandbox.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 Samsung Electronics
+ * Przemyslaw Marczak
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * 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),
+};
--
2.39.5