Lines Matching +full:adc +full:- +full:channels

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>
98 const struct iio_chan_spec *channels; member
100 int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc);
114 struct ingenic_adc *adc = iio_priv(iio_dev); in ingenic_adc_set_adcmd() local
116 mutex_lock(&adc->lock); in ingenic_adc_set_adcmd()
119 readl(adc->base + JZ_ADC_REG_ADCMD); in ingenic_adc_set_adcmd()
126 adc->base + JZ_ADC_REG_ADCMD); in ingenic_adc_set_adcmd()
132 adc->base + JZ_ADC_REG_ADCMD); in ingenic_adc_set_adcmd()
140 adc->base + JZ_ADC_REG_ADCMD); in ingenic_adc_set_adcmd()
146 adc->base + JZ_ADC_REG_ADCMD); in ingenic_adc_set_adcmd()
153 adc->base + JZ_ADC_REG_ADCMD); in ingenic_adc_set_adcmd()
158 adc->base + JZ_ADC_REG_ADCMD); in ingenic_adc_set_adcmd()
162 writel(0, adc->base + JZ_ADC_REG_ADCMD); in ingenic_adc_set_adcmd()
164 mutex_unlock(&adc->lock); in ingenic_adc_set_adcmd()
167 static void ingenic_adc_set_config(struct ingenic_adc *adc, in ingenic_adc_set_config() argument
173 mutex_lock(&adc->lock); in ingenic_adc_set_config()
175 cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask; in ingenic_adc_set_config()
177 writel(cfg, adc->base + JZ_ADC_REG_CFG); in ingenic_adc_set_config()
179 mutex_unlock(&adc->lock); in ingenic_adc_set_config()
182 static void ingenic_adc_enable_unlocked(struct ingenic_adc *adc, in ingenic_adc_enable_unlocked() argument
188 val = readb(adc->base + JZ_ADC_REG_ENABLE); in ingenic_adc_enable_unlocked()
195 writeb(val, adc->base + JZ_ADC_REG_ENABLE); in ingenic_adc_enable_unlocked()
198 static void ingenic_adc_enable(struct ingenic_adc *adc, in ingenic_adc_enable() argument
202 mutex_lock(&adc->lock); in ingenic_adc_enable()
203 ingenic_adc_enable_unlocked(adc, engine, enabled); in ingenic_adc_enable()
204 mutex_unlock(&adc->lock); in ingenic_adc_enable()
207 static int ingenic_adc_capture(struct ingenic_adc *adc, in ingenic_adc_capture() argument
219 mutex_lock(&adc->lock); in ingenic_adc_capture()
220 cfg = readl(adc->base + JZ_ADC_REG_CFG); in ingenic_adc_capture()
221 writel(cfg & ~JZ_ADC_REG_CFG_CMD_SEL, adc->base + JZ_ADC_REG_CFG); in ingenic_adc_capture()
223 ingenic_adc_enable_unlocked(adc, engine, true); in ingenic_adc_capture()
224 ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val, in ingenic_adc_capture()
227 ingenic_adc_enable_unlocked(adc, engine, false); in ingenic_adc_capture()
229 writel(cfg, adc->base + JZ_ADC_REG_CFG); in ingenic_adc_capture()
230 mutex_unlock(&adc->lock); in ingenic_adc_capture()
241 struct ingenic_adc *adc = iio_priv(iio_dev); in ingenic_adc_write_raw() local
242 struct device *dev = iio_dev->dev.parent; in ingenic_adc_write_raw()
247 switch (chan->channel) { in ingenic_adc_write_raw()
249 if (!adc->soc_data->battery_vref_mode) in ingenic_adc_write_raw()
250 return -EINVAL; in ingenic_adc_write_raw()
252 ret = clk_enable(adc->clk); in ingenic_adc_write_raw()
260 ingenic_adc_set_config(adc, in ingenic_adc_write_raw()
263 adc->low_vref_mode = false; in ingenic_adc_write_raw()
265 ingenic_adc_set_config(adc, in ingenic_adc_write_raw()
268 adc->low_vref_mode = true; in ingenic_adc_write_raw()
271 clk_disable(adc->clk); in ingenic_adc_write_raw()
275 return -EINVAL; in ingenic_adc_write_raw()
278 return -EINVAL; in ingenic_adc_write_raw()
283 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
292 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
305 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1,
312 static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) in jz4725b_adc_init_clk_div() argument
318 parent_clk = clk_get_parent(adc->clk); in jz4725b_adc_init_clk_div()
320 dev_err(dev, "ADC clock has no parent\n"); in jz4725b_adc_init_clk_div()
321 return -ENODEV; in jz4725b_adc_init_clk_div()
326 * The JZ4725B ADC works at 500 kHz to 8 MHz. in jz4725b_adc_init_clk_div()
334 dev_err(dev, "No valid divider for ADC main clock\n"); in jz4725b_adc_init_clk_div()
335 return -EINVAL; in jz4725b_adc_init_clk_div()
341 writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) | in jz4725b_adc_init_clk_div()
342 (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, in jz4725b_adc_init_clk_div()
343 adc->base + JZ_ADC_REG_ADCLK); in jz4725b_adc_init_clk_div()
348 static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) in jz4770_adc_init_clk_div() argument
354 parent_clk = clk_get_parent(adc->clk); in jz4770_adc_init_clk_div()
356 dev_err(dev, "ADC clock has no parent\n"); in jz4770_adc_init_clk_div()
357 return -ENODEV; in jz4770_adc_init_clk_div()
362 * The JZ4770 ADC works at 20 kHz to 200 kHz. in jz4770_adc_init_clk_div()
369 dev_err(dev, "No valid divider for ADC main clock\n"); in jz4770_adc_init_clk_div()
370 return -EINVAL; in jz4770_adc_init_clk_div()
378 writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) | in jz4770_adc_init_clk_div()
379 ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) | in jz4770_adc_init_clk_div()
380 (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, in jz4770_adc_init_clk_div()
381 adc->base + JZ_ADC_REG_ADCLK); in jz4770_adc_init_clk_div()
394 .scan_index = -1,
405 .scan_index = -1,
417 .scan_index = -1,
426 .scan_index = -1,
435 .scan_index = -1,
446 .scan_index = -1,
524 .scan_index = -1,
535 .scan_index = -1,
544 .scan_index = -1,
557 .channels = jz4740_channels,
571 .channels = jz4740_channels,
585 .channels = jz4760_channels,
599 .channels = jz4770_channels,
611 struct ingenic_adc *adc = iio_priv(iio_dev); in ingenic_adc_read_avail() local
616 *length = adc->soc_data->battery_raw_avail_size; in ingenic_adc_read_avail()
617 *vals = adc->soc_data->battery_raw_avail; in ingenic_adc_read_avail()
621 *length = adc->soc_data->battery_scale_avail_size; in ingenic_adc_read_avail()
622 *vals = adc->soc_data->battery_scale_avail; in ingenic_adc_read_avail()
625 return -EINVAL; in ingenic_adc_read_avail()
633 int cmd, ret, engine = (chan->channel == INGENIC_ADC_BATTERY); in ingenic_adc_read_chan_info_raw()
634 struct ingenic_adc *adc = iio_priv(iio_dev); in ingenic_adc_read_chan_info_raw() local
636 ret = clk_enable(adc->clk); in ingenic_adc_read_chan_info_raw()
638 dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", in ingenic_adc_read_chan_info_raw()
643 /* We cannot sample the aux channels in parallel. */ in ingenic_adc_read_chan_info_raw()
644 mutex_lock(&adc->aux_lock); in ingenic_adc_read_chan_info_raw()
645 if (adc->soc_data->has_aux_md && engine == 0) { in ingenic_adc_read_chan_info_raw()
646 switch (chan->channel) { in ingenic_adc_read_chan_info_raw()
658 ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, cmd); in ingenic_adc_read_chan_info_raw()
661 ret = ingenic_adc_capture(adc, engine); in ingenic_adc_read_chan_info_raw()
665 switch (chan->channel) { in ingenic_adc_read_chan_info_raw()
669 *val = readw(adc->base + JZ_ADC_REG_ADSDAT); in ingenic_adc_read_chan_info_raw()
672 *val = readw(adc->base + JZ_ADC_REG_ADBDAT); in ingenic_adc_read_chan_info_raw()
678 mutex_unlock(&adc->aux_lock); in ingenic_adc_read_chan_info_raw()
679 clk_disable(adc->clk); in ingenic_adc_read_chan_info_raw()
690 struct ingenic_adc *adc = iio_priv(iio_dev); in ingenic_adc_read_raw() local
696 switch (chan->channel) { in ingenic_adc_read_raw()
704 if (adc->low_vref_mode) { in ingenic_adc_read_raw()
708 *val = adc->soc_data->battery_high_vref; in ingenic_adc_read_raw()
709 *val2 = adc->soc_data->battery_high_vref_bits; in ingenic_adc_read_raw()
716 return -EINVAL; in ingenic_adc_read_raw()
725 if (!iiospec->args_count) in ingenic_adc_of_xlate()
726 return -EINVAL; in ingenic_adc_of_xlate()
728 for (i = 0; i < iio_dev->num_channels; ++i) in ingenic_adc_of_xlate()
729 if (iio_dev->channels[i].channel == iiospec->args[0]) in ingenic_adc_of_xlate()
732 return -EINVAL; in ingenic_adc_of_xlate()
749 struct ingenic_adc *adc = iio_priv(iio_dev); in ingenic_adc_buffer_enable() local
752 ret = clk_enable(adc->clk); in ingenic_adc_buffer_enable()
754 dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", in ingenic_adc_buffer_enable()
761 ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, in ingenic_adc_buffer_enable()
765 writew(80, adc->base + JZ_ADC_REG_ADWAIT); in ingenic_adc_buffer_enable()
766 writew(2, adc->base + JZ_ADC_REG_ADSAME); in ingenic_adc_buffer_enable()
767 writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL); in ingenic_adc_buffer_enable()
768 writel(0, adc->base + JZ_ADC_REG_ADTCH); in ingenic_adc_buffer_enable()
770 ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, in ingenic_adc_buffer_enable()
772 ingenic_adc_set_adcmd(iio_dev, iio_dev->active_scan_mask[0]); in ingenic_adc_buffer_enable()
774 ingenic_adc_enable(adc, 2, true); in ingenic_adc_buffer_enable()
781 struct ingenic_adc *adc = iio_priv(iio_dev); in ingenic_adc_buffer_disable() local
783 ingenic_adc_enable(adc, 2, false); in ingenic_adc_buffer_disable()
785 ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, 0); in ingenic_adc_buffer_disable()
787 writeb(0xff, adc->base + JZ_ADC_REG_CTRL); in ingenic_adc_buffer_disable()
788 writeb(0xff, adc->base + JZ_ADC_REG_STATUS); in ingenic_adc_buffer_disable()
789 ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0); in ingenic_adc_buffer_disable()
790 writew(0, adc->base + JZ_ADC_REG_ADSAME); in ingenic_adc_buffer_disable()
791 writew(0, adc->base + JZ_ADC_REG_ADWAIT); in ingenic_adc_buffer_disable()
792 clk_disable(adc->clk); in ingenic_adc_buffer_disable()
805 struct ingenic_adc *adc = iio_priv(iio_dev); in ingenic_adc_irq() local
806 unsigned long mask = iio_dev->active_scan_mask[0]; in ingenic_adc_irq()
812 tdat[i] = readl(adc->base + JZ_ADC_REG_ADTCH); in ingenic_adc_irq()
818 writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS); in ingenic_adc_irq()
825 struct device *dev = &pdev->dev; in ingenic_adc_probe()
827 struct ingenic_adc *adc; in ingenic_adc_probe() local
833 return -EINVAL; in ingenic_adc_probe()
835 iio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); in ingenic_adc_probe()
837 return -ENOMEM; in ingenic_adc_probe()
839 adc = iio_priv(iio_dev); in ingenic_adc_probe()
840 mutex_init(&adc->lock); in ingenic_adc_probe()
841 mutex_init(&adc->aux_lock); in ingenic_adc_probe()
842 adc->soc_data = soc_data; in ingenic_adc_probe()
855 adc->base = devm_platform_ioremap_resource(pdev, 0); in ingenic_adc_probe()
856 if (IS_ERR(adc->base)) in ingenic_adc_probe()
857 return PTR_ERR(adc->base); in ingenic_adc_probe()
859 adc->clk = devm_clk_get(dev, "adc"); in ingenic_adc_probe()
860 if (IS_ERR(adc->clk)) { in ingenic_adc_probe()
862 return PTR_ERR(adc->clk); in ingenic_adc_probe()
865 ret = clk_prepare_enable(adc->clk); in ingenic_adc_probe()
872 if (soc_data->init_clk_div) { in ingenic_adc_probe()
873 ret = soc_data->init_clk_div(dev, adc); in ingenic_adc_probe()
875 clk_disable_unprepare(adc->clk); in ingenic_adc_probe()
881 writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); in ingenic_adc_probe()
882 writeb(0xff, adc->base + JZ_ADC_REG_CTRL); in ingenic_adc_probe()
885 if (device_property_present(dev, "ingenic,use-internal-divider")) in ingenic_adc_probe()
886 ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL, in ingenic_adc_probe()
889 ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL, 0); in ingenic_adc_probe()
892 clk_disable(adc->clk); in ingenic_adc_probe()
894 ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk); in ingenic_adc_probe()
900 iio_dev->name = "jz-adc"; in ingenic_adc_probe()
901 iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; in ingenic_adc_probe()
902 iio_dev->setup_ops = &ingenic_buffer_setup_ops; in ingenic_adc_probe()
903 iio_dev->channels = soc_data->channels; in ingenic_adc_probe()
904 iio_dev->num_channels = soc_data->num_channels; in ingenic_adc_probe()
905 iio_dev->info = &ingenic_adc_info; in ingenic_adc_probe()
915 { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
916 { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
917 { .compatible = "ingenic,jz4760-adc", .data = &jz4760_adc_soc_data, },
918 { .compatible = "ingenic,jz4760b-adc", .data = &jz4760_adc_soc_data, },
919 { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, },
926 .name = "ingenic-adc",