| /* | |
| * 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))) { | |
| pr_err("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) { | |
| pr_err("ADC in single-channel mode!"); | |
| return -EPERM; | |
| } | |
| /* Check channel selection */ | |
| if (!(priv->active_channel_mask & channel_mask)) { | |
| pr_err("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), | |
| }; |