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