108d6300aSPrzemyslaw Marczak /*
208d6300aSPrzemyslaw Marczak * Copyright (C) 2015 Samsung Electronics
308d6300aSPrzemyslaw Marczak * Przemyslaw Marczak <p.marczak@samsung.com>
408d6300aSPrzemyslaw Marczak *
508d6300aSPrzemyslaw Marczak * SPDX-License-Identifier: GPL-2.0+
608d6300aSPrzemyslaw Marczak */
708d6300aSPrzemyslaw Marczak #include <common.h>
808d6300aSPrzemyslaw Marczak #include <errno.h>
908d6300aSPrzemyslaw Marczak #include <dm.h>
1008d6300aSPrzemyslaw Marczak #include <adc.h>
1108d6300aSPrzemyslaw Marczak #include <sandbox-adc.h>
1208d6300aSPrzemyslaw Marczak
1308d6300aSPrzemyslaw Marczak /**
1408d6300aSPrzemyslaw Marczak * struct sandbox_adc_priv - sandbox ADC device's operation status and data
1508d6300aSPrzemyslaw Marczak *
1608d6300aSPrzemyslaw Marczak * @conversion_status - conversion status: ACTIVE (started) / INACTIVE (stopped)
1708d6300aSPrzemyslaw Marczak * @conversion_mode - conversion mode: single or multi-channel
1808d6300aSPrzemyslaw Marczak * @active_channel - active channel number, valid for single channel mode
1908d6300aSPrzemyslaw Marczak * data[] - channels data
2008d6300aSPrzemyslaw Marczak */
2108d6300aSPrzemyslaw Marczak struct sandbox_adc_priv {
2208d6300aSPrzemyslaw Marczak int conversion_status;
2308d6300aSPrzemyslaw Marczak int conversion_mode;
2408d6300aSPrzemyslaw Marczak int active_channel_mask;
2508d6300aSPrzemyslaw Marczak unsigned int data[4];
2608d6300aSPrzemyslaw Marczak };
2708d6300aSPrzemyslaw Marczak
sandbox_adc_start_channel(struct udevice * dev,int channel)2808d6300aSPrzemyslaw Marczak int sandbox_adc_start_channel(struct udevice *dev, int channel)
2908d6300aSPrzemyslaw Marczak {
3008d6300aSPrzemyslaw Marczak struct sandbox_adc_priv *priv = dev_get_priv(dev);
3108d6300aSPrzemyslaw Marczak
3208d6300aSPrzemyslaw Marczak /* Set single-channel mode */
3308d6300aSPrzemyslaw Marczak priv->conversion_mode = SANDBOX_ADC_MODE_SINGLE_CHANNEL;
3408d6300aSPrzemyslaw Marczak /* Select channel */
3508d6300aSPrzemyslaw Marczak priv->active_channel_mask = 1 << channel;
3608d6300aSPrzemyslaw Marczak /* Start conversion */
3708d6300aSPrzemyslaw Marczak priv->conversion_status = SANDBOX_ADC_ACTIVE;
3808d6300aSPrzemyslaw Marczak
3908d6300aSPrzemyslaw Marczak return 0;
4008d6300aSPrzemyslaw Marczak }
4108d6300aSPrzemyslaw Marczak
sandbox_adc_start_channels(struct udevice * dev,unsigned int channel_mask)4208d6300aSPrzemyslaw Marczak int sandbox_adc_start_channels(struct udevice *dev, unsigned int channel_mask)
4308d6300aSPrzemyslaw Marczak {
4408d6300aSPrzemyslaw Marczak struct sandbox_adc_priv *priv = dev_get_priv(dev);
4508d6300aSPrzemyslaw Marczak
4608d6300aSPrzemyslaw Marczak /* Set single-channel mode */
4708d6300aSPrzemyslaw Marczak priv->conversion_mode = SANDBOX_ADC_MODE_MULTI_CHANNEL;
4808d6300aSPrzemyslaw Marczak /* Select channel */
4908d6300aSPrzemyslaw Marczak priv->active_channel_mask = channel_mask;
5008d6300aSPrzemyslaw Marczak /* Start conversion */
5108d6300aSPrzemyslaw Marczak priv->conversion_status = SANDBOX_ADC_ACTIVE;
5208d6300aSPrzemyslaw Marczak
5308d6300aSPrzemyslaw Marczak return 0;
5408d6300aSPrzemyslaw Marczak }
5508d6300aSPrzemyslaw Marczak
sandbox_adc_channel_data(struct udevice * dev,int channel,unsigned int * data)5608d6300aSPrzemyslaw Marczak int sandbox_adc_channel_data(struct udevice *dev, int channel,
5708d6300aSPrzemyslaw Marczak unsigned int *data)
5808d6300aSPrzemyslaw Marczak {
5908d6300aSPrzemyslaw Marczak struct sandbox_adc_priv *priv = dev_get_priv(dev);
6008d6300aSPrzemyslaw Marczak
6108d6300aSPrzemyslaw Marczak /* For single-channel conversion mode, check if channel was selected */
6208d6300aSPrzemyslaw Marczak if ((priv->conversion_mode == SANDBOX_ADC_MODE_SINGLE_CHANNEL) &&
6308d6300aSPrzemyslaw Marczak !(priv->active_channel_mask & (1 << channel))) {
64*90aa625cSMasahiro Yamada pr_err("Request for an inactive channel!");
6508d6300aSPrzemyslaw Marczak return -EINVAL;
6608d6300aSPrzemyslaw Marczak }
6708d6300aSPrzemyslaw Marczak
6808d6300aSPrzemyslaw Marczak /* The conversion must be started before reading the data */
6908d6300aSPrzemyslaw Marczak if (priv->conversion_status == SANDBOX_ADC_INACTIVE)
7008d6300aSPrzemyslaw Marczak return -EIO;
7108d6300aSPrzemyslaw Marczak
7208d6300aSPrzemyslaw Marczak *data = priv->data[channel];
7308d6300aSPrzemyslaw Marczak
7408d6300aSPrzemyslaw Marczak return 0;
7508d6300aSPrzemyslaw Marczak }
7608d6300aSPrzemyslaw Marczak
sandbox_adc_channels_data(struct udevice * dev,unsigned int channel_mask,struct adc_channel * channels)7708d6300aSPrzemyslaw Marczak int sandbox_adc_channels_data(struct udevice *dev, unsigned int channel_mask,
7808d6300aSPrzemyslaw Marczak struct adc_channel *channels)
7908d6300aSPrzemyslaw Marczak {
8008d6300aSPrzemyslaw Marczak struct sandbox_adc_priv *priv = dev_get_priv(dev);
8108d6300aSPrzemyslaw Marczak int i;
8208d6300aSPrzemyslaw Marczak
8308d6300aSPrzemyslaw Marczak /* Return error for single-channel conversion mode */
8408d6300aSPrzemyslaw Marczak if (priv->conversion_mode == SANDBOX_ADC_MODE_SINGLE_CHANNEL) {
85*90aa625cSMasahiro Yamada pr_err("ADC in single-channel mode!");
8608d6300aSPrzemyslaw Marczak return -EPERM;
8708d6300aSPrzemyslaw Marczak }
8808d6300aSPrzemyslaw Marczak /* Check channel selection */
8908d6300aSPrzemyslaw Marczak if (!(priv->active_channel_mask & channel_mask)) {
90*90aa625cSMasahiro Yamada pr_err("Request for an inactive channel!");
9108d6300aSPrzemyslaw Marczak return -EINVAL;
9208d6300aSPrzemyslaw Marczak }
9308d6300aSPrzemyslaw Marczak /* The conversion must be started before reading the data */
9408d6300aSPrzemyslaw Marczak if (priv->conversion_status == SANDBOX_ADC_INACTIVE)
9508d6300aSPrzemyslaw Marczak return -EIO;
9608d6300aSPrzemyslaw Marczak
9708d6300aSPrzemyslaw Marczak for (i = 0; i < SANDBOX_ADC_CHANNELS; i++) {
9808d6300aSPrzemyslaw Marczak if (!((channel_mask >> i) & 0x1))
9908d6300aSPrzemyslaw Marczak continue;
10008d6300aSPrzemyslaw Marczak
10108d6300aSPrzemyslaw Marczak channels->data = priv->data[i];
10208d6300aSPrzemyslaw Marczak channels->id = i;
10308d6300aSPrzemyslaw Marczak channels++;
10408d6300aSPrzemyslaw Marczak }
10508d6300aSPrzemyslaw Marczak
10608d6300aSPrzemyslaw Marczak return 0;
10708d6300aSPrzemyslaw Marczak }
10808d6300aSPrzemyslaw Marczak
sandbox_adc_stop(struct udevice * dev)10908d6300aSPrzemyslaw Marczak int sandbox_adc_stop(struct udevice *dev)
11008d6300aSPrzemyslaw Marczak {
11108d6300aSPrzemyslaw Marczak struct sandbox_adc_priv *priv = dev_get_priv(dev);
11208d6300aSPrzemyslaw Marczak
11308d6300aSPrzemyslaw Marczak /* Start conversion */
11408d6300aSPrzemyslaw Marczak priv->conversion_status = SANDBOX_ADC_INACTIVE;
11508d6300aSPrzemyslaw Marczak
11608d6300aSPrzemyslaw Marczak return 0;
11708d6300aSPrzemyslaw Marczak }
11808d6300aSPrzemyslaw Marczak
sandbox_adc_probe(struct udevice * dev)11908d6300aSPrzemyslaw Marczak int sandbox_adc_probe(struct udevice *dev)
12008d6300aSPrzemyslaw Marczak {
12108d6300aSPrzemyslaw Marczak struct sandbox_adc_priv *priv = dev_get_priv(dev);
12208d6300aSPrzemyslaw Marczak
12308d6300aSPrzemyslaw Marczak /* Stop conversion */
12408d6300aSPrzemyslaw Marczak priv->conversion_status = SANDBOX_ADC_INACTIVE;
12508d6300aSPrzemyslaw Marczak /* Set single-channel mode */
12608d6300aSPrzemyslaw Marczak priv->conversion_mode = SANDBOX_ADC_MODE_SINGLE_CHANNEL;
12708d6300aSPrzemyslaw Marczak /* Deselect all channels */
12808d6300aSPrzemyslaw Marczak priv->active_channel_mask = 0;
12908d6300aSPrzemyslaw Marczak
13008d6300aSPrzemyslaw Marczak /* Set sandbox test data */
13108d6300aSPrzemyslaw Marczak priv->data[0] = SANDBOX_ADC_CHANNEL0_DATA;
13208d6300aSPrzemyslaw Marczak priv->data[1] = SANDBOX_ADC_CHANNEL1_DATA;
13308d6300aSPrzemyslaw Marczak priv->data[2] = SANDBOX_ADC_CHANNEL2_DATA;
13408d6300aSPrzemyslaw Marczak priv->data[3] = SANDBOX_ADC_CHANNEL3_DATA;
13508d6300aSPrzemyslaw Marczak
13608d6300aSPrzemyslaw Marczak return 0;
13708d6300aSPrzemyslaw Marczak }
13808d6300aSPrzemyslaw Marczak
sandbox_adc_ofdata_to_platdata(struct udevice * dev)13908d6300aSPrzemyslaw Marczak int sandbox_adc_ofdata_to_platdata(struct udevice *dev)
14008d6300aSPrzemyslaw Marczak {
14108d6300aSPrzemyslaw Marczak struct adc_uclass_platdata *uc_pdata = dev_get_uclass_platdata(dev);
14208d6300aSPrzemyslaw Marczak
14308d6300aSPrzemyslaw Marczak uc_pdata->data_mask = SANDBOX_ADC_DATA_MASK;
14408d6300aSPrzemyslaw Marczak uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
14508d6300aSPrzemyslaw Marczak uc_pdata->data_timeout_us = 0;
14608d6300aSPrzemyslaw Marczak
14708d6300aSPrzemyslaw Marczak /* Mask available channel bits: [0:3] */
14808d6300aSPrzemyslaw Marczak uc_pdata->channel_mask = (1 << SANDBOX_ADC_CHANNELS) - 1;
14908d6300aSPrzemyslaw Marczak
15008d6300aSPrzemyslaw Marczak return 0;
15108d6300aSPrzemyslaw Marczak }
15208d6300aSPrzemyslaw Marczak
15308d6300aSPrzemyslaw Marczak static const struct adc_ops sandbox_adc_ops = {
15408d6300aSPrzemyslaw Marczak .start_channel = sandbox_adc_start_channel,
15508d6300aSPrzemyslaw Marczak .start_channels = sandbox_adc_start_channels,
15608d6300aSPrzemyslaw Marczak .channel_data = sandbox_adc_channel_data,
15708d6300aSPrzemyslaw Marczak .channels_data = sandbox_adc_channels_data,
15808d6300aSPrzemyslaw Marczak .stop = sandbox_adc_stop,
15908d6300aSPrzemyslaw Marczak };
16008d6300aSPrzemyslaw Marczak
16108d6300aSPrzemyslaw Marczak static const struct udevice_id sandbox_adc_ids[] = {
16208d6300aSPrzemyslaw Marczak { .compatible = "sandbox,adc" },
16308d6300aSPrzemyslaw Marczak { }
16408d6300aSPrzemyslaw Marczak };
16508d6300aSPrzemyslaw Marczak
16608d6300aSPrzemyslaw Marczak U_BOOT_DRIVER(sandbox_adc) = {
16708d6300aSPrzemyslaw Marczak .name = "sandbox-adc",
16808d6300aSPrzemyslaw Marczak .id = UCLASS_ADC,
16908d6300aSPrzemyslaw Marczak .of_match = sandbox_adc_ids,
17008d6300aSPrzemyslaw Marczak .ops = &sandbox_adc_ops,
17108d6300aSPrzemyslaw Marczak .probe = sandbox_adc_probe,
17208d6300aSPrzemyslaw Marczak .ofdata_to_platdata = sandbox_adc_ofdata_to_platdata,
17308d6300aSPrzemyslaw Marczak .priv_auto_alloc_size = sizeof(struct sandbox_adc_priv),
17408d6300aSPrzemyslaw Marczak };
175