diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/adc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/adc/sandbox.c | 174 |
3 files changed, 184 insertions, 0 deletions
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 @@ 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 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), +}; |