sandbox: add ADC driver
authorPrzemyslaw Marczak <p.marczak@samsung.com>
Tue, 27 Oct 2015 12:08:06 +0000 (13:08 +0100)
committerMinkyu Kang <mk7.kang@samsung.com>
Mon, 2 Nov 2015 01:38:00 +0000 (10:38 +0900)
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>
arch/sandbox/dts/sandbox_pmic.dtsi
arch/sandbox/dts/test.dts
configs/sandbox_defconfig
drivers/adc/Kconfig
drivers/adc/Makefile
drivers/adc/sandbox.c [new file with mode: 0644]

index 44a26b18ca6c0e458711b51a115f4b7d1cc0161b..ce261b930e13abe4f8b7903e22319b09c3a39d2d 100644 (file)
@@ -55,7 +55,7 @@
                regulator-always-on;
        };
 
-       buck2 {
+       buck2: buck2 {
                regulator-name = "SUPPLY_3.3V";
                regulator-min-microvolt = <3300000>;
                regulator-max-microvolt = <3300000>;
index 730de8a57ffca2f4bc159e54e2049874d4fb42ad..e2c4971d74025835d6a8217b5916bd7b81752210 100644 (file)
                };
        };
 
+       adc@0 {
+               compatible = "sandbox,adc";
+               vdd-supply = <&buck2>;
+               vss-microvolts = <0>;
+       };
+
        leds {
                compatible = "gpio-leds";
 
index 67ae99b638ce629023ed8172254739e4cdbb7808..94c8e685f0130ec75f9d86fa99ab98826d287bc7 100644 (file)
@@ -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
index 223b65eadaf17fc2812f07f7440f8b96f7d564b0..e5335f7234b779769f40d7a652c4dcd2fbea3157 100644 (file)
@@ -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
index eb85b8b1a6bfe021c2f02a5142be6d36b0d5cde5..cebf26de078044dbcbecc1aea7c92172352d8037 100644 (file)
@@ -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 (file)
index 0000000..3718922
--- /dev/null
@@ -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),
+};