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/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),
+};