1 // SPDX-License-Identifier: GPL-2.0
3 * ADC driver for the Ingenic JZ47xx SoCs
4 * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>
6 * based on drivers/mfd/jz4740-adc.c
9 #include <dt-bindings/iio/adc/ingenic,adc.h>
10 #include <linux/clk.h>
11 #include <linux/iio/iio.h>
13 #include <linux/iopoll.h>
14 #include <linux/module.h>
15 #include <linux/mutex.h>
16 #include <linux/platform_device.h>
18 #define JZ_ADC_REG_ENABLE 0x00
19 #define JZ_ADC_REG_CFG 0x04
20 #define JZ_ADC_REG_CTRL 0x08
21 #define JZ_ADC_REG_STATUS 0x0c
22 #define JZ_ADC_REG_ADTCH 0x18
23 #define JZ_ADC_REG_ADBDAT 0x1c
24 #define JZ_ADC_REG_ADSDAT 0x20
26 #define JZ_ADC_REG_CFG_BAT_MD BIT(4)
28 #define JZ_ADC_AUX_VREF 3300
29 #define JZ_ADC_AUX_VREF_BITS 12
30 #define JZ_ADC_BATTERY_LOW_VREF 2500
31 #define JZ_ADC_BATTERY_LOW_VREF_BITS 12
32 #define JZ4725B_ADC_BATTERY_HIGH_VREF 7500
33 #define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10
34 #define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986)
35 #define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12
37 struct ingenic_adc_soc_data
{
38 unsigned int battery_high_vref
;
39 unsigned int battery_high_vref_bits
;
40 const int *battery_raw_avail
;
41 size_t battery_raw_avail_size
;
42 const int *battery_scale_avail
;
43 size_t battery_scale_avail_size
;
50 const struct ingenic_adc_soc_data
*soc_data
;
54 static void ingenic_adc_set_config(struct ingenic_adc
*adc
,
61 mutex_lock(&adc
->lock
);
63 cfg
= readl(adc
->base
+ JZ_ADC_REG_CFG
) & ~mask
;
65 writel(cfg
, adc
->base
+ JZ_ADC_REG_CFG
);
67 mutex_unlock(&adc
->lock
);
68 clk_disable(adc
->clk
);
71 static void ingenic_adc_enable(struct ingenic_adc
*adc
,
77 mutex_lock(&adc
->lock
);
78 val
= readb(adc
->base
+ JZ_ADC_REG_ENABLE
);
85 writeb(val
, adc
->base
+ JZ_ADC_REG_ENABLE
);
86 mutex_unlock(&adc
->lock
);
89 static int ingenic_adc_capture(struct ingenic_adc
*adc
,
95 ingenic_adc_enable(adc
, engine
, true);
96 ret
= readb_poll_timeout(adc
->base
+ JZ_ADC_REG_ENABLE
, val
,
97 !(val
& BIT(engine
)), 250, 1000);
99 ingenic_adc_enable(adc
, engine
, false);
104 static int ingenic_adc_write_raw(struct iio_dev
*iio_dev
,
105 struct iio_chan_spec
const *chan
,
110 struct ingenic_adc
*adc
= iio_priv(iio_dev
);
113 case IIO_CHAN_INFO_SCALE
:
114 switch (chan
->channel
) {
115 case INGENIC_ADC_BATTERY
:
116 if (val
> JZ_ADC_BATTERY_LOW_VREF
) {
117 ingenic_adc_set_config(adc
,
118 JZ_ADC_REG_CFG_BAT_MD
,
120 adc
->low_vref_mode
= false;
122 ingenic_adc_set_config(adc
,
123 JZ_ADC_REG_CFG_BAT_MD
,
124 JZ_ADC_REG_CFG_BAT_MD
);
125 adc
->low_vref_mode
= true;
136 static const int jz4725b_adc_battery_raw_avail
[] = {
137 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS
) - 1,
140 static const int jz4725b_adc_battery_scale_avail
[] = {
141 JZ4725B_ADC_BATTERY_HIGH_VREF
, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS
,
142 JZ_ADC_BATTERY_LOW_VREF
, JZ_ADC_BATTERY_LOW_VREF_BITS
,
145 static const int jz4740_adc_battery_raw_avail
[] = {
146 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS
) - 1,
149 static const int jz4740_adc_battery_scale_avail
[] = {
150 JZ4740_ADC_BATTERY_HIGH_VREF
, JZ4740_ADC_BATTERY_HIGH_VREF_BITS
,
151 JZ_ADC_BATTERY_LOW_VREF
, JZ_ADC_BATTERY_LOW_VREF_BITS
,
154 static const struct ingenic_adc_soc_data jz4725b_adc_soc_data
= {
155 .battery_high_vref
= JZ4725B_ADC_BATTERY_HIGH_VREF
,
156 .battery_high_vref_bits
= JZ4725B_ADC_BATTERY_HIGH_VREF_BITS
,
157 .battery_raw_avail
= jz4725b_adc_battery_raw_avail
,
158 .battery_raw_avail_size
= ARRAY_SIZE(jz4725b_adc_battery_raw_avail
),
159 .battery_scale_avail
= jz4725b_adc_battery_scale_avail
,
160 .battery_scale_avail_size
= ARRAY_SIZE(jz4725b_adc_battery_scale_avail
),
163 static const struct ingenic_adc_soc_data jz4740_adc_soc_data
= {
164 .battery_high_vref
= JZ4740_ADC_BATTERY_HIGH_VREF
,
165 .battery_high_vref_bits
= JZ4740_ADC_BATTERY_HIGH_VREF_BITS
,
166 .battery_raw_avail
= jz4740_adc_battery_raw_avail
,
167 .battery_raw_avail_size
= ARRAY_SIZE(jz4740_adc_battery_raw_avail
),
168 .battery_scale_avail
= jz4740_adc_battery_scale_avail
,
169 .battery_scale_avail_size
= ARRAY_SIZE(jz4740_adc_battery_scale_avail
),
172 static int ingenic_adc_read_avail(struct iio_dev
*iio_dev
,
173 struct iio_chan_spec
const *chan
,
179 struct ingenic_adc
*adc
= iio_priv(iio_dev
);
182 case IIO_CHAN_INFO_RAW
:
184 *length
= adc
->soc_data
->battery_raw_avail_size
;
185 *vals
= adc
->soc_data
->battery_raw_avail
;
186 return IIO_AVAIL_RANGE
;
187 case IIO_CHAN_INFO_SCALE
:
188 *type
= IIO_VAL_FRACTIONAL_LOG2
;
189 *length
= adc
->soc_data
->battery_scale_avail_size
;
190 *vals
= adc
->soc_data
->battery_scale_avail
;
191 return IIO_AVAIL_LIST
;
197 static int ingenic_adc_read_raw(struct iio_dev
*iio_dev
,
198 struct iio_chan_spec
const *chan
,
203 struct ingenic_adc
*adc
= iio_priv(iio_dev
);
207 case IIO_CHAN_INFO_RAW
:
208 clk_enable(adc
->clk
);
209 ret
= ingenic_adc_capture(adc
, chan
->channel
);
211 clk_disable(adc
->clk
);
215 switch (chan
->channel
) {
216 case INGENIC_ADC_AUX
:
217 *val
= readw(adc
->base
+ JZ_ADC_REG_ADSDAT
);
219 case INGENIC_ADC_BATTERY
:
220 *val
= readw(adc
->base
+ JZ_ADC_REG_ADBDAT
);
224 clk_disable(adc
->clk
);
227 case IIO_CHAN_INFO_SCALE
:
228 switch (chan
->channel
) {
229 case INGENIC_ADC_AUX
:
230 *val
= JZ_ADC_AUX_VREF
;
231 *val2
= JZ_ADC_AUX_VREF_BITS
;
233 case INGENIC_ADC_BATTERY
:
234 if (adc
->low_vref_mode
) {
235 *val
= JZ_ADC_BATTERY_LOW_VREF
;
236 *val2
= JZ_ADC_BATTERY_LOW_VREF_BITS
;
238 *val
= adc
->soc_data
->battery_high_vref
;
239 *val2
= adc
->soc_data
->battery_high_vref_bits
;
244 return IIO_VAL_FRACTIONAL_LOG2
;
250 static void ingenic_adc_clk_cleanup(void *data
)
255 static const struct iio_info ingenic_adc_info
= {
256 .write_raw
= ingenic_adc_write_raw
,
257 .read_raw
= ingenic_adc_read_raw
,
258 .read_avail
= ingenic_adc_read_avail
,
261 static const struct iio_chan_spec ingenic_channels
[] = {
263 .extend_name
= "aux",
265 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
) |
266 BIT(IIO_CHAN_INFO_SCALE
),
268 .channel
= INGENIC_ADC_AUX
,
271 .extend_name
= "battery",
273 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
) |
274 BIT(IIO_CHAN_INFO_SCALE
),
275 .info_mask_separate_available
= BIT(IIO_CHAN_INFO_RAW
) |
276 BIT(IIO_CHAN_INFO_SCALE
),
278 .channel
= INGENIC_ADC_BATTERY
,
282 static int ingenic_adc_probe(struct platform_device
*pdev
)
284 struct device
*dev
= &pdev
->dev
;
285 struct iio_dev
*iio_dev
;
286 struct ingenic_adc
*adc
;
287 struct resource
*mem_base
;
288 const struct ingenic_adc_soc_data
*soc_data
;
291 soc_data
= device_get_match_data(dev
);
295 iio_dev
= devm_iio_device_alloc(dev
, sizeof(*adc
));
299 adc
= iio_priv(iio_dev
);
300 mutex_init(&adc
->lock
);
301 adc
->soc_data
= soc_data
;
303 mem_base
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
304 adc
->base
= devm_ioremap_resource(dev
, mem_base
);
305 if (IS_ERR(adc
->base
))
306 return PTR_ERR(adc
->base
);
308 adc
->clk
= devm_clk_get(dev
, "adc");
309 if (IS_ERR(adc
->clk
)) {
310 dev_err(dev
, "Unable to get clock\n");
311 return PTR_ERR(adc
->clk
);
314 ret
= clk_prepare_enable(adc
->clk
);
316 dev_err(dev
, "Failed to enable clock\n");
320 /* Put hardware in a known passive state. */
321 writeb(0x00, adc
->base
+ JZ_ADC_REG_ENABLE
);
322 writeb(0xff, adc
->base
+ JZ_ADC_REG_CTRL
);
323 clk_disable(adc
->clk
);
325 ret
= devm_add_action_or_reset(dev
, ingenic_adc_clk_cleanup
, adc
->clk
);
327 dev_err(dev
, "Unable to add action\n");
331 iio_dev
->dev
.parent
= dev
;
332 iio_dev
->name
= "jz-adc";
333 iio_dev
->modes
= INDIO_DIRECT_MODE
;
334 iio_dev
->channels
= ingenic_channels
;
335 iio_dev
->num_channels
= ARRAY_SIZE(ingenic_channels
);
336 iio_dev
->info
= &ingenic_adc_info
;
338 ret
= devm_iio_device_register(dev
, iio_dev
);
340 dev_err(dev
, "Unable to register IIO device\n");
346 static const struct of_device_id ingenic_adc_of_match
[] = {
347 { .compatible
= "ingenic,jz4725b-adc", .data
= &jz4725b_adc_soc_data
, },
348 { .compatible
= "ingenic,jz4740-adc", .data
= &jz4740_adc_soc_data
, },
351 MODULE_DEVICE_TABLE(of
, ingenic_adc_of_match
);
354 static struct platform_driver ingenic_adc_driver
= {
356 .name
= "ingenic-adc",
357 .of_match_table
= of_match_ptr(ingenic_adc_of_match
),
359 .probe
= ingenic_adc_probe
,
361 module_platform_driver(ingenic_adc_driver
);
362 MODULE_LICENSE("GPL v2");