blob: 24d4af63bd9be535d50d38a4afc7427b9f451c16 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +01002/*
3 * Copyright (C) 2015 Samsung Electronics
4 * Przemyslaw Marczak <p.marczak@samsung.com>
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +01005 */
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +01006#include <errno.h>
7#include <dm.h>
8#include <adc.h>
9#include <sandbox-adc.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060010#include <linux/printk.h>
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +010011
12/**
13 * struct sandbox_adc_priv - sandbox ADC device's operation status and data
14 *
15 * @conversion_status - conversion status: ACTIVE (started) / INACTIVE (stopped)
16 * @conversion_mode - conversion mode: single or multi-channel
17 * @active_channel - active channel number, valid for single channel mode
18 * data[] - channels data
19 */
20struct sandbox_adc_priv {
21 int conversion_status;
22 int conversion_mode;
23 int active_channel_mask;
24 unsigned int data[4];
25};
26
27int sandbox_adc_start_channel(struct udevice *dev, int channel)
28{
29 struct sandbox_adc_priv *priv = dev_get_priv(dev);
30
31 /* Set single-channel mode */
32 priv->conversion_mode = SANDBOX_ADC_MODE_SINGLE_CHANNEL;
33 /* Select channel */
34 priv->active_channel_mask = 1 << channel;
35 /* Start conversion */
36 priv->conversion_status = SANDBOX_ADC_ACTIVE;
37
38 return 0;
39}
40
41int sandbox_adc_start_channels(struct udevice *dev, unsigned int channel_mask)
42{
43 struct sandbox_adc_priv *priv = dev_get_priv(dev);
44
45 /* Set single-channel mode */
46 priv->conversion_mode = SANDBOX_ADC_MODE_MULTI_CHANNEL;
47 /* Select channel */
48 priv->active_channel_mask = channel_mask;
49 /* Start conversion */
50 priv->conversion_status = SANDBOX_ADC_ACTIVE;
51
52 return 0;
53}
54
55int sandbox_adc_channel_data(struct udevice *dev, int channel,
56 unsigned int *data)
57{
58 struct sandbox_adc_priv *priv = dev_get_priv(dev);
59
60 /* For single-channel conversion mode, check if channel was selected */
61 if ((priv->conversion_mode == SANDBOX_ADC_MODE_SINGLE_CHANNEL) &&
62 !(priv->active_channel_mask & (1 << channel))) {
Masahiro Yamada81e10422017-09-16 14:10:41 +090063 pr_err("Request for an inactive channel!");
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +010064 return -EINVAL;
65 }
66
67 /* The conversion must be started before reading the data */
68 if (priv->conversion_status == SANDBOX_ADC_INACTIVE)
69 return -EIO;
70
71 *data = priv->data[channel];
72
73 return 0;
74}
75
76int sandbox_adc_channels_data(struct udevice *dev, unsigned int channel_mask,
77 struct adc_channel *channels)
78{
79 struct sandbox_adc_priv *priv = dev_get_priv(dev);
80 int i;
81
82 /* Return error for single-channel conversion mode */
83 if (priv->conversion_mode == SANDBOX_ADC_MODE_SINGLE_CHANNEL) {
Masahiro Yamada81e10422017-09-16 14:10:41 +090084 pr_err("ADC in single-channel mode!");
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +010085 return -EPERM;
86 }
87 /* Check channel selection */
88 if (!(priv->active_channel_mask & channel_mask)) {
Masahiro Yamada81e10422017-09-16 14:10:41 +090089 pr_err("Request for an inactive channel!");
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +010090 return -EINVAL;
91 }
92 /* The conversion must be started before reading the data */
93 if (priv->conversion_status == SANDBOX_ADC_INACTIVE)
94 return -EIO;
95
96 for (i = 0; i < SANDBOX_ADC_CHANNELS; i++) {
97 if (!((channel_mask >> i) & 0x1))
98 continue;
99
100 channels->data = priv->data[i];
101 channels->id = i;
102 channels++;
103 }
104
105 return 0;
106}
107
108int sandbox_adc_stop(struct udevice *dev)
109{
110 struct sandbox_adc_priv *priv = dev_get_priv(dev);
111
112 /* Start conversion */
113 priv->conversion_status = SANDBOX_ADC_INACTIVE;
114
115 return 0;
116}
117
118int sandbox_adc_probe(struct udevice *dev)
119{
120 struct sandbox_adc_priv *priv = dev_get_priv(dev);
121
122 /* Stop conversion */
123 priv->conversion_status = SANDBOX_ADC_INACTIVE;
124 /* Set single-channel mode */
125 priv->conversion_mode = SANDBOX_ADC_MODE_SINGLE_CHANNEL;
126 /* Deselect all channels */
127 priv->active_channel_mask = 0;
128
129 /* Set sandbox test data */
130 priv->data[0] = SANDBOX_ADC_CHANNEL0_DATA;
131 priv->data[1] = SANDBOX_ADC_CHANNEL1_DATA;
132 priv->data[2] = SANDBOX_ADC_CHANNEL2_DATA;
133 priv->data[3] = SANDBOX_ADC_CHANNEL3_DATA;
134
135 return 0;
136}
137
Simon Glassaad29ae2020-12-03 16:55:21 -0700138int sandbox_adc_of_to_plat(struct udevice *dev)
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +0100139{
Simon Glass71fa5b42020-12-03 16:55:18 -0700140 struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +0100141
142 uc_pdata->data_mask = SANDBOX_ADC_DATA_MASK;
143 uc_pdata->data_format = ADC_DATA_FORMAT_BIN;
144 uc_pdata->data_timeout_us = 0;
145
146 /* Mask available channel bits: [0:3] */
147 uc_pdata->channel_mask = (1 << SANDBOX_ADC_CHANNELS) - 1;
148
149 return 0;
150}
151
152static const struct adc_ops sandbox_adc_ops = {
153 .start_channel = sandbox_adc_start_channel,
154 .start_channels = sandbox_adc_start_channels,
155 .channel_data = sandbox_adc_channel_data,
156 .channels_data = sandbox_adc_channels_data,
157 .stop = sandbox_adc_stop,
158};
159
160static const struct udevice_id sandbox_adc_ids[] = {
161 { .compatible = "sandbox,adc" },
162 { }
163};
164
165U_BOOT_DRIVER(sandbox_adc) = {
166 .name = "sandbox-adc",
167 .id = UCLASS_ADC,
168 .of_match = sandbox_adc_ids,
169 .ops = &sandbox_adc_ops,
170 .probe = sandbox_adc_probe,
Simon Glassaad29ae2020-12-03 16:55:21 -0700171 .of_to_plat = sandbox_adc_of_to_plat,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700172 .priv_auto = sizeof(struct sandbox_adc_priv),
Przemyslaw Marczak1bc7f232015-10-27 13:08:06 +0100173};