162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ADC driver for the Ingenic JZ47xx SoCs 462306a36Sopenharmony_ci * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * based on drivers/mfd/jz4740-adc.c 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <dt-bindings/iio/adc/ingenic,adc.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/iio/buffer.h> 1262306a36Sopenharmony_ci#include <linux/iio/iio.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/iopoll.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1962306a36Sopenharmony_ci#include <linux/mutex.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/property.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define JZ_ADC_REG_ENABLE 0x00 2562306a36Sopenharmony_ci#define JZ_ADC_REG_CFG 0x04 2662306a36Sopenharmony_ci#define JZ_ADC_REG_CTRL 0x08 2762306a36Sopenharmony_ci#define JZ_ADC_REG_STATUS 0x0c 2862306a36Sopenharmony_ci#define JZ_ADC_REG_ADSAME 0x10 2962306a36Sopenharmony_ci#define JZ_ADC_REG_ADWAIT 0x14 3062306a36Sopenharmony_ci#define JZ_ADC_REG_ADTCH 0x18 3162306a36Sopenharmony_ci#define JZ_ADC_REG_ADBDAT 0x1c 3262306a36Sopenharmony_ci#define JZ_ADC_REG_ADSDAT 0x20 3362306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD 0x24 3462306a36Sopenharmony_ci#define JZ_ADC_REG_ADCLK 0x28 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define JZ_ADC_REG_ENABLE_PD BIT(7) 3762306a36Sopenharmony_ci#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1)) 3862306a36Sopenharmony_ci#define JZ_ADC_REG_CFG_BAT_MD BIT(4) 3962306a36Sopenharmony_ci#define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10) 4062306a36Sopenharmony_ci#define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16) 4162306a36Sopenharmony_ci#define JZ_ADC_REG_CFG_CMD_SEL BIT(22) 4262306a36Sopenharmony_ci#define JZ_ADC_REG_CFG_VBAT_SEL BIT(30) 4362306a36Sopenharmony_ci#define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10)) 4462306a36Sopenharmony_ci#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0 4562306a36Sopenharmony_ci#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16 4662306a36Sopenharmony_ci#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8 4762306a36Sopenharmony_ci#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_YNADC BIT(7) 5062306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_YPADC BIT(8) 5162306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_XNADC BIT(9) 5262306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_XPADC BIT(10) 5362306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFPYP BIT(11) 5462306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFPXP BIT(12) 5562306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFPXN BIT(13) 5662306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFPAUX BIT(14) 5762306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFPVDD33 BIT(15) 5862306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFNYN BIT(16) 5962306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFNXP BIT(17) 6062306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFNXN BIT(18) 6162306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_VREFAUX BIT(19) 6262306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_YNGRU BIT(20) 6362306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_XNGRU BIT(21) 6462306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_XPGRU BIT(22) 6562306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_YPSUP BIT(23) 6662306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_XNSUP BIT(24) 6762306a36Sopenharmony_ci#define JZ_ADC_REG_ADCMD_XPSUP BIT(25) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define JZ_ADC_AUX_VREF 3300 7062306a36Sopenharmony_ci#define JZ_ADC_AUX_VREF_BITS 12 7162306a36Sopenharmony_ci#define JZ_ADC_BATTERY_LOW_VREF 2500 7262306a36Sopenharmony_ci#define JZ_ADC_BATTERY_LOW_VREF_BITS 12 7362306a36Sopenharmony_ci#define JZ4725B_ADC_BATTERY_HIGH_VREF 7500 7462306a36Sopenharmony_ci#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10 7562306a36Sopenharmony_ci#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986) 7662306a36Sopenharmony_ci#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12 7762306a36Sopenharmony_ci#define JZ4760_ADC_BATTERY_VREF 2500 7862306a36Sopenharmony_ci#define JZ4770_ADC_BATTERY_VREF 1200 7962306a36Sopenharmony_ci#define JZ4770_ADC_BATTERY_VREF_BITS 12 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define JZ_ADC_IRQ_AUX BIT(0) 8262306a36Sopenharmony_ci#define JZ_ADC_IRQ_BATTERY BIT(1) 8362306a36Sopenharmony_ci#define JZ_ADC_IRQ_TOUCH BIT(2) 8462306a36Sopenharmony_ci#define JZ_ADC_IRQ_PEN_DOWN BIT(3) 8562306a36Sopenharmony_ci#define JZ_ADC_IRQ_PEN_UP BIT(4) 8662306a36Sopenharmony_ci#define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5) 8762306a36Sopenharmony_ci#define JZ_ADC_IRQ_SLEEP BIT(7) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct ingenic_adc; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct ingenic_adc_soc_data { 9262306a36Sopenharmony_ci unsigned int battery_high_vref; 9362306a36Sopenharmony_ci unsigned int battery_high_vref_bits; 9462306a36Sopenharmony_ci const int *battery_raw_avail; 9562306a36Sopenharmony_ci size_t battery_raw_avail_size; 9662306a36Sopenharmony_ci const int *battery_scale_avail; 9762306a36Sopenharmony_ci size_t battery_scale_avail_size; 9862306a36Sopenharmony_ci unsigned int battery_vref_mode: 1; 9962306a36Sopenharmony_ci unsigned int has_aux_md: 1; 10062306a36Sopenharmony_ci const struct iio_chan_spec *channels; 10162306a36Sopenharmony_ci unsigned int num_channels; 10262306a36Sopenharmony_ci int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc); 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct ingenic_adc { 10662306a36Sopenharmony_ci void __iomem *base; 10762306a36Sopenharmony_ci struct clk *clk; 10862306a36Sopenharmony_ci struct mutex lock; 10962306a36Sopenharmony_ci struct mutex aux_lock; 11062306a36Sopenharmony_ci const struct ingenic_adc_soc_data *soc_data; 11162306a36Sopenharmony_ci bool low_vref_mode; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void ingenic_adc_set_adcmd(struct iio_dev *iio_dev, unsigned long mask) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct ingenic_adc *adc = iio_priv(iio_dev); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci mutex_lock(&adc->lock); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Init ADCMD */ 12162306a36Sopenharmony_ci readl(adc->base + JZ_ADC_REG_ADCMD); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (mask & 0x3) { 12462306a36Sopenharmony_ci /* Second channel (INGENIC_ADC_TOUCH_YP): sample YP vs. GND */ 12562306a36Sopenharmony_ci writel(JZ_ADC_REG_ADCMD_XNGRU 12662306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 12762306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_YPADC, 12862306a36Sopenharmony_ci adc->base + JZ_ADC_REG_ADCMD); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* First channel (INGENIC_ADC_TOUCH_XP): sample XP vs. GND */ 13162306a36Sopenharmony_ci writel(JZ_ADC_REG_ADCMD_YNGRU 13262306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 13362306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_XPADC, 13462306a36Sopenharmony_ci adc->base + JZ_ADC_REG_ADCMD); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (mask & 0xc) { 13862306a36Sopenharmony_ci /* Fourth channel (INGENIC_ADC_TOUCH_YN): sample YN vs. GND */ 13962306a36Sopenharmony_ci writel(JZ_ADC_REG_ADCMD_XNGRU 14062306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 14162306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_YNADC, 14262306a36Sopenharmony_ci adc->base + JZ_ADC_REG_ADCMD); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Third channel (INGENIC_ADC_TOUCH_XN): sample XN vs. GND */ 14562306a36Sopenharmony_ci writel(JZ_ADC_REG_ADCMD_YNGRU 14662306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 14762306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_XNADC, 14862306a36Sopenharmony_ci adc->base + JZ_ADC_REG_ADCMD); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (mask & 0x30) { 15262306a36Sopenharmony_ci /* Sixth channel (INGENIC_ADC_TOUCH_YD): sample YP vs. YN */ 15362306a36Sopenharmony_ci writel(JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 15462306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_YPADC, 15562306a36Sopenharmony_ci adc->base + JZ_ADC_REG_ADCMD); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Fifth channel (INGENIC_ADC_TOUCH_XD): sample XP vs. XN */ 15862306a36Sopenharmony_ci writel(JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 15962306a36Sopenharmony_ci | JZ_ADC_REG_ADCMD_XPADC, 16062306a36Sopenharmony_ci adc->base + JZ_ADC_REG_ADCMD); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* We're done */ 16462306a36Sopenharmony_ci writel(0, adc->base + JZ_ADC_REG_ADCMD); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci mutex_unlock(&adc->lock); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void ingenic_adc_set_config(struct ingenic_adc *adc, 17062306a36Sopenharmony_ci uint32_t mask, 17162306a36Sopenharmony_ci uint32_t val) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci uint32_t cfg; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci mutex_lock(&adc->lock); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask; 17862306a36Sopenharmony_ci cfg |= val; 17962306a36Sopenharmony_ci writel(cfg, adc->base + JZ_ADC_REG_CFG); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci mutex_unlock(&adc->lock); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void ingenic_adc_enable_unlocked(struct ingenic_adc *adc, 18562306a36Sopenharmony_ci int engine, 18662306a36Sopenharmony_ci bool enabled) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci u8 val; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci val = readb(adc->base + JZ_ADC_REG_ENABLE); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (enabled) 19362306a36Sopenharmony_ci val |= BIT(engine); 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci val &= ~BIT(engine); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci writeb(val, adc->base + JZ_ADC_REG_ENABLE); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void ingenic_adc_enable(struct ingenic_adc *adc, 20162306a36Sopenharmony_ci int engine, 20262306a36Sopenharmony_ci bool enabled) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci mutex_lock(&adc->lock); 20562306a36Sopenharmony_ci ingenic_adc_enable_unlocked(adc, engine, enabled); 20662306a36Sopenharmony_ci mutex_unlock(&adc->lock); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int ingenic_adc_capture(struct ingenic_adc *adc, 21062306a36Sopenharmony_ci int engine) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci u32 cfg; 21362306a36Sopenharmony_ci u8 val; 21462306a36Sopenharmony_ci int ret; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * Disable CMD_SEL temporarily, because it causes wrong VBAT readings, 21862306a36Sopenharmony_ci * probably due to the switch of VREF. We must keep the lock here to 21962306a36Sopenharmony_ci * avoid races with the buffer enable/disable functions. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci mutex_lock(&adc->lock); 22262306a36Sopenharmony_ci cfg = readl(adc->base + JZ_ADC_REG_CFG); 22362306a36Sopenharmony_ci writel(cfg & ~JZ_ADC_REG_CFG_CMD_SEL, adc->base + JZ_ADC_REG_CFG); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ingenic_adc_enable_unlocked(adc, engine, true); 22662306a36Sopenharmony_ci ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val, 22762306a36Sopenharmony_ci !(val & BIT(engine)), 250, 1000); 22862306a36Sopenharmony_ci if (ret) 22962306a36Sopenharmony_ci ingenic_adc_enable_unlocked(adc, engine, false); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci writel(cfg, adc->base + JZ_ADC_REG_CFG); 23262306a36Sopenharmony_ci mutex_unlock(&adc->lock); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int ingenic_adc_write_raw(struct iio_dev *iio_dev, 23862306a36Sopenharmony_ci struct iio_chan_spec const *chan, 23962306a36Sopenharmony_ci int val, 24062306a36Sopenharmony_ci int val2, 24162306a36Sopenharmony_ci long m) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct ingenic_adc *adc = iio_priv(iio_dev); 24462306a36Sopenharmony_ci struct device *dev = iio_dev->dev.parent; 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci switch (m) { 24862306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 24962306a36Sopenharmony_ci switch (chan->channel) { 25062306a36Sopenharmony_ci case INGENIC_ADC_BATTERY: 25162306a36Sopenharmony_ci if (!adc->soc_data->battery_vref_mode) 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ret = clk_enable(adc->clk); 25562306a36Sopenharmony_ci if (ret) { 25662306a36Sopenharmony_ci dev_err(dev, "Failed to enable clock: %d\n", 25762306a36Sopenharmony_ci ret); 25862306a36Sopenharmony_ci return ret; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (val > JZ_ADC_BATTERY_LOW_VREF) { 26262306a36Sopenharmony_ci ingenic_adc_set_config(adc, 26362306a36Sopenharmony_ci JZ_ADC_REG_CFG_BAT_MD, 26462306a36Sopenharmony_ci 0); 26562306a36Sopenharmony_ci adc->low_vref_mode = false; 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci ingenic_adc_set_config(adc, 26862306a36Sopenharmony_ci JZ_ADC_REG_CFG_BAT_MD, 26962306a36Sopenharmony_ci JZ_ADC_REG_CFG_BAT_MD); 27062306a36Sopenharmony_ci adc->low_vref_mode = true; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci clk_disable(adc->clk); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci default: 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci default: 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic const int jz4725b_adc_battery_raw_avail[] = { 28562306a36Sopenharmony_ci 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic const int jz4725b_adc_battery_scale_avail[] = { 28962306a36Sopenharmony_ci JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, 29062306a36Sopenharmony_ci JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const int jz4740_adc_battery_raw_avail[] = { 29462306a36Sopenharmony_ci 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic const int jz4740_adc_battery_scale_avail[] = { 29862306a36Sopenharmony_ci JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS, 29962306a36Sopenharmony_ci JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic const int jz4760_adc_battery_scale_avail[] = { 30362306a36Sopenharmony_ci JZ4760_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic const int jz4770_adc_battery_raw_avail[] = { 30762306a36Sopenharmony_ci 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic const int jz4770_adc_battery_scale_avail[] = { 31162306a36Sopenharmony_ci JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS, 31262306a36Sopenharmony_ci}; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct clk *parent_clk; 31762306a36Sopenharmony_ci unsigned long parent_rate, rate; 31862306a36Sopenharmony_ci unsigned int div_main, div_10us; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci parent_clk = clk_get_parent(adc->clk); 32162306a36Sopenharmony_ci if (!parent_clk) { 32262306a36Sopenharmony_ci dev_err(dev, "ADC clock has no parent\n"); 32362306a36Sopenharmony_ci return -ENODEV; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci parent_rate = clk_get_rate(parent_clk); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * The JZ4725B ADC works at 500 kHz to 8 MHz. 32962306a36Sopenharmony_ci * We pick the highest rate possible. 33062306a36Sopenharmony_ci * In practice we typically get 6 MHz, half of the 12 MHz EXT clock. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci div_main = DIV_ROUND_UP(parent_rate, 8000000); 33362306a36Sopenharmony_ci div_main = clamp(div_main, 1u, 64u); 33462306a36Sopenharmony_ci rate = parent_rate / div_main; 33562306a36Sopenharmony_ci if (rate < 500000 || rate > 8000000) { 33662306a36Sopenharmony_ci dev_err(dev, "No valid divider for ADC main clock\n"); 33762306a36Sopenharmony_ci return -EINVAL; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* We also need a divider that produces a 10us clock. */ 34162306a36Sopenharmony_ci div_10us = DIV_ROUND_UP(rate, 100000); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) | 34462306a36Sopenharmony_ci (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, 34562306a36Sopenharmony_ci adc->base + JZ_ADC_REG_ADCLK); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct clk *parent_clk; 35362306a36Sopenharmony_ci unsigned long parent_rate, rate; 35462306a36Sopenharmony_ci unsigned int div_main, div_ms, div_10us; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci parent_clk = clk_get_parent(adc->clk); 35762306a36Sopenharmony_ci if (!parent_clk) { 35862306a36Sopenharmony_ci dev_err(dev, "ADC clock has no parent\n"); 35962306a36Sopenharmony_ci return -ENODEV; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci parent_rate = clk_get_rate(parent_clk); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* 36462306a36Sopenharmony_ci * The JZ4770 ADC works at 20 kHz to 200 kHz. 36562306a36Sopenharmony_ci * We pick the highest rate possible. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci div_main = DIV_ROUND_UP(parent_rate, 200000); 36862306a36Sopenharmony_ci div_main = clamp(div_main, 1u, 256u); 36962306a36Sopenharmony_ci rate = parent_rate / div_main; 37062306a36Sopenharmony_ci if (rate < 20000 || rate > 200000) { 37162306a36Sopenharmony_ci dev_err(dev, "No valid divider for ADC main clock\n"); 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* We also need a divider that produces a 10us clock. */ 37662306a36Sopenharmony_ci div_10us = DIV_ROUND_UP(rate, 10000); 37762306a36Sopenharmony_ci /* And another, which produces a 1ms clock. */ 37862306a36Sopenharmony_ci div_ms = DIV_ROUND_UP(rate, 1000); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) | 38162306a36Sopenharmony_ci ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) | 38262306a36Sopenharmony_ci (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, 38362306a36Sopenharmony_ci adc->base + JZ_ADC_REG_ADCLK); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic const struct iio_chan_spec jz4740_channels[] = { 38962306a36Sopenharmony_ci { 39062306a36Sopenharmony_ci .extend_name = "aux", 39162306a36Sopenharmony_ci .type = IIO_VOLTAGE, 39262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 39362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 39462306a36Sopenharmony_ci .indexed = 1, 39562306a36Sopenharmony_ci .channel = INGENIC_ADC_AUX, 39662306a36Sopenharmony_ci .scan_index = -1, 39762306a36Sopenharmony_ci }, 39862306a36Sopenharmony_ci { 39962306a36Sopenharmony_ci .extend_name = "battery", 40062306a36Sopenharmony_ci .type = IIO_VOLTAGE, 40162306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 40262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 40362306a36Sopenharmony_ci .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | 40462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 40562306a36Sopenharmony_ci .indexed = 1, 40662306a36Sopenharmony_ci .channel = INGENIC_ADC_BATTERY, 40762306a36Sopenharmony_ci .scan_index = -1, 40862306a36Sopenharmony_ci }, 40962306a36Sopenharmony_ci}; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic const struct iio_chan_spec jz4760_channels[] = { 41262306a36Sopenharmony_ci { 41362306a36Sopenharmony_ci .extend_name = "aux", 41462306a36Sopenharmony_ci .type = IIO_VOLTAGE, 41562306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 41662306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 41762306a36Sopenharmony_ci .indexed = 1, 41862306a36Sopenharmony_ci .channel = INGENIC_ADC_AUX0, 41962306a36Sopenharmony_ci .scan_index = -1, 42062306a36Sopenharmony_ci }, 42162306a36Sopenharmony_ci { 42262306a36Sopenharmony_ci .extend_name = "aux1", 42362306a36Sopenharmony_ci .type = IIO_VOLTAGE, 42462306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 42562306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 42662306a36Sopenharmony_ci .indexed = 1, 42762306a36Sopenharmony_ci .channel = INGENIC_ADC_AUX, 42862306a36Sopenharmony_ci .scan_index = -1, 42962306a36Sopenharmony_ci }, 43062306a36Sopenharmony_ci { 43162306a36Sopenharmony_ci .extend_name = "aux2", 43262306a36Sopenharmony_ci .type = IIO_VOLTAGE, 43362306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 43462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 43562306a36Sopenharmony_ci .indexed = 1, 43662306a36Sopenharmony_ci .channel = INGENIC_ADC_AUX2, 43762306a36Sopenharmony_ci .scan_index = -1, 43862306a36Sopenharmony_ci }, 43962306a36Sopenharmony_ci { 44062306a36Sopenharmony_ci .extend_name = "battery", 44162306a36Sopenharmony_ci .type = IIO_VOLTAGE, 44262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 44362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 44462306a36Sopenharmony_ci .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | 44562306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 44662306a36Sopenharmony_ci .indexed = 1, 44762306a36Sopenharmony_ci .channel = INGENIC_ADC_BATTERY, 44862306a36Sopenharmony_ci .scan_index = -1, 44962306a36Sopenharmony_ci }, 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic const struct iio_chan_spec jz4770_channels[] = { 45362306a36Sopenharmony_ci { 45462306a36Sopenharmony_ci .type = IIO_VOLTAGE, 45562306a36Sopenharmony_ci .indexed = 1, 45662306a36Sopenharmony_ci .channel = INGENIC_ADC_TOUCH_XP, 45762306a36Sopenharmony_ci .scan_index = 0, 45862306a36Sopenharmony_ci .scan_type = { 45962306a36Sopenharmony_ci .sign = 'u', 46062306a36Sopenharmony_ci .realbits = 12, 46162306a36Sopenharmony_ci .storagebits = 16, 46262306a36Sopenharmony_ci }, 46362306a36Sopenharmony_ci }, 46462306a36Sopenharmony_ci { 46562306a36Sopenharmony_ci .type = IIO_VOLTAGE, 46662306a36Sopenharmony_ci .indexed = 1, 46762306a36Sopenharmony_ci .channel = INGENIC_ADC_TOUCH_YP, 46862306a36Sopenharmony_ci .scan_index = 1, 46962306a36Sopenharmony_ci .scan_type = { 47062306a36Sopenharmony_ci .sign = 'u', 47162306a36Sopenharmony_ci .realbits = 12, 47262306a36Sopenharmony_ci .storagebits = 16, 47362306a36Sopenharmony_ci }, 47462306a36Sopenharmony_ci }, 47562306a36Sopenharmony_ci { 47662306a36Sopenharmony_ci .type = IIO_VOLTAGE, 47762306a36Sopenharmony_ci .indexed = 1, 47862306a36Sopenharmony_ci .channel = INGENIC_ADC_TOUCH_XN, 47962306a36Sopenharmony_ci .scan_index = 2, 48062306a36Sopenharmony_ci .scan_type = { 48162306a36Sopenharmony_ci .sign = 'u', 48262306a36Sopenharmony_ci .realbits = 12, 48362306a36Sopenharmony_ci .storagebits = 16, 48462306a36Sopenharmony_ci }, 48562306a36Sopenharmony_ci }, 48662306a36Sopenharmony_ci { 48762306a36Sopenharmony_ci .type = IIO_VOLTAGE, 48862306a36Sopenharmony_ci .indexed = 1, 48962306a36Sopenharmony_ci .channel = INGENIC_ADC_TOUCH_YN, 49062306a36Sopenharmony_ci .scan_index = 3, 49162306a36Sopenharmony_ci .scan_type = { 49262306a36Sopenharmony_ci .sign = 'u', 49362306a36Sopenharmony_ci .realbits = 12, 49462306a36Sopenharmony_ci .storagebits = 16, 49562306a36Sopenharmony_ci }, 49662306a36Sopenharmony_ci }, 49762306a36Sopenharmony_ci { 49862306a36Sopenharmony_ci .type = IIO_VOLTAGE, 49962306a36Sopenharmony_ci .indexed = 1, 50062306a36Sopenharmony_ci .channel = INGENIC_ADC_TOUCH_XD, 50162306a36Sopenharmony_ci .scan_index = 4, 50262306a36Sopenharmony_ci .scan_type = { 50362306a36Sopenharmony_ci .sign = 'u', 50462306a36Sopenharmony_ci .realbits = 12, 50562306a36Sopenharmony_ci .storagebits = 16, 50662306a36Sopenharmony_ci }, 50762306a36Sopenharmony_ci }, 50862306a36Sopenharmony_ci { 50962306a36Sopenharmony_ci .type = IIO_VOLTAGE, 51062306a36Sopenharmony_ci .indexed = 1, 51162306a36Sopenharmony_ci .channel = INGENIC_ADC_TOUCH_YD, 51262306a36Sopenharmony_ci .scan_index = 5, 51362306a36Sopenharmony_ci .scan_type = { 51462306a36Sopenharmony_ci .sign = 'u', 51562306a36Sopenharmony_ci .realbits = 12, 51662306a36Sopenharmony_ci .storagebits = 16, 51762306a36Sopenharmony_ci }, 51862306a36Sopenharmony_ci }, 51962306a36Sopenharmony_ci { 52062306a36Sopenharmony_ci .extend_name = "aux", 52162306a36Sopenharmony_ci .type = IIO_VOLTAGE, 52262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 52362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 52462306a36Sopenharmony_ci .indexed = 1, 52562306a36Sopenharmony_ci .channel = INGENIC_ADC_AUX, 52662306a36Sopenharmony_ci .scan_index = -1, 52762306a36Sopenharmony_ci }, 52862306a36Sopenharmony_ci { 52962306a36Sopenharmony_ci .extend_name = "battery", 53062306a36Sopenharmony_ci .type = IIO_VOLTAGE, 53162306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 53262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 53362306a36Sopenharmony_ci .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | 53462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 53562306a36Sopenharmony_ci .indexed = 1, 53662306a36Sopenharmony_ci .channel = INGENIC_ADC_BATTERY, 53762306a36Sopenharmony_ci .scan_index = -1, 53862306a36Sopenharmony_ci }, 53962306a36Sopenharmony_ci { 54062306a36Sopenharmony_ci .extend_name = "aux2", 54162306a36Sopenharmony_ci .type = IIO_VOLTAGE, 54262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 54362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 54462306a36Sopenharmony_ci .indexed = 1, 54562306a36Sopenharmony_ci .channel = INGENIC_ADC_AUX2, 54662306a36Sopenharmony_ci .scan_index = -1, 54762306a36Sopenharmony_ci }, 54862306a36Sopenharmony_ci}; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { 55162306a36Sopenharmony_ci .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF, 55262306a36Sopenharmony_ci .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, 55362306a36Sopenharmony_ci .battery_raw_avail = jz4725b_adc_battery_raw_avail, 55462306a36Sopenharmony_ci .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail), 55562306a36Sopenharmony_ci .battery_scale_avail = jz4725b_adc_battery_scale_avail, 55662306a36Sopenharmony_ci .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail), 55762306a36Sopenharmony_ci .battery_vref_mode = true, 55862306a36Sopenharmony_ci .has_aux_md = false, 55962306a36Sopenharmony_ci .channels = jz4740_channels, 56062306a36Sopenharmony_ci .num_channels = ARRAY_SIZE(jz4740_channels), 56162306a36Sopenharmony_ci .init_clk_div = jz4725b_adc_init_clk_div, 56262306a36Sopenharmony_ci}; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic const struct ingenic_adc_soc_data jz4740_adc_soc_data = { 56562306a36Sopenharmony_ci .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF, 56662306a36Sopenharmony_ci .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS, 56762306a36Sopenharmony_ci .battery_raw_avail = jz4740_adc_battery_raw_avail, 56862306a36Sopenharmony_ci .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail), 56962306a36Sopenharmony_ci .battery_scale_avail = jz4740_adc_battery_scale_avail, 57062306a36Sopenharmony_ci .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail), 57162306a36Sopenharmony_ci .battery_vref_mode = true, 57262306a36Sopenharmony_ci .has_aux_md = false, 57362306a36Sopenharmony_ci .channels = jz4740_channels, 57462306a36Sopenharmony_ci .num_channels = ARRAY_SIZE(jz4740_channels), 57562306a36Sopenharmony_ci .init_clk_div = NULL, /* no ADCLK register on JZ4740 */ 57662306a36Sopenharmony_ci}; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic const struct ingenic_adc_soc_data jz4760_adc_soc_data = { 57962306a36Sopenharmony_ci .battery_high_vref = JZ4760_ADC_BATTERY_VREF, 58062306a36Sopenharmony_ci .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS, 58162306a36Sopenharmony_ci .battery_raw_avail = jz4770_adc_battery_raw_avail, 58262306a36Sopenharmony_ci .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail), 58362306a36Sopenharmony_ci .battery_scale_avail = jz4760_adc_battery_scale_avail, 58462306a36Sopenharmony_ci .battery_scale_avail_size = ARRAY_SIZE(jz4760_adc_battery_scale_avail), 58562306a36Sopenharmony_ci .battery_vref_mode = false, 58662306a36Sopenharmony_ci .has_aux_md = true, 58762306a36Sopenharmony_ci .channels = jz4760_channels, 58862306a36Sopenharmony_ci .num_channels = ARRAY_SIZE(jz4760_channels), 58962306a36Sopenharmony_ci .init_clk_div = jz4770_adc_init_clk_div, 59062306a36Sopenharmony_ci}; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic const struct ingenic_adc_soc_data jz4770_adc_soc_data = { 59362306a36Sopenharmony_ci .battery_high_vref = JZ4770_ADC_BATTERY_VREF, 59462306a36Sopenharmony_ci .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS, 59562306a36Sopenharmony_ci .battery_raw_avail = jz4770_adc_battery_raw_avail, 59662306a36Sopenharmony_ci .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail), 59762306a36Sopenharmony_ci .battery_scale_avail = jz4770_adc_battery_scale_avail, 59862306a36Sopenharmony_ci .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail), 59962306a36Sopenharmony_ci .battery_vref_mode = false, 60062306a36Sopenharmony_ci .has_aux_md = true, 60162306a36Sopenharmony_ci .channels = jz4770_channels, 60262306a36Sopenharmony_ci .num_channels = ARRAY_SIZE(jz4770_channels), 60362306a36Sopenharmony_ci .init_clk_div = jz4770_adc_init_clk_div, 60462306a36Sopenharmony_ci}; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int ingenic_adc_read_avail(struct iio_dev *iio_dev, 60762306a36Sopenharmony_ci struct iio_chan_spec const *chan, 60862306a36Sopenharmony_ci const int **vals, 60962306a36Sopenharmony_ci int *type, 61062306a36Sopenharmony_ci int *length, 61162306a36Sopenharmony_ci long m) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct ingenic_adc *adc = iio_priv(iio_dev); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci switch (m) { 61662306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 61762306a36Sopenharmony_ci *type = IIO_VAL_INT; 61862306a36Sopenharmony_ci *length = adc->soc_data->battery_raw_avail_size; 61962306a36Sopenharmony_ci *vals = adc->soc_data->battery_raw_avail; 62062306a36Sopenharmony_ci return IIO_AVAIL_RANGE; 62162306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 62262306a36Sopenharmony_ci *type = IIO_VAL_FRACTIONAL_LOG2; 62362306a36Sopenharmony_ci *length = adc->soc_data->battery_scale_avail_size; 62462306a36Sopenharmony_ci *vals = adc->soc_data->battery_scale_avail; 62562306a36Sopenharmony_ci return IIO_AVAIL_LIST; 62662306a36Sopenharmony_ci default: 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev, 63262306a36Sopenharmony_ci struct iio_chan_spec const *chan, 63362306a36Sopenharmony_ci int *val) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci int cmd, ret, engine = (chan->channel == INGENIC_ADC_BATTERY); 63662306a36Sopenharmony_ci struct ingenic_adc *adc = iio_priv(iio_dev); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ret = clk_enable(adc->clk); 63962306a36Sopenharmony_ci if (ret) { 64062306a36Sopenharmony_ci dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", 64162306a36Sopenharmony_ci ret); 64262306a36Sopenharmony_ci return ret; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* We cannot sample the aux channels in parallel. */ 64662306a36Sopenharmony_ci mutex_lock(&adc->aux_lock); 64762306a36Sopenharmony_ci if (adc->soc_data->has_aux_md && engine == 0) { 64862306a36Sopenharmony_ci switch (chan->channel) { 64962306a36Sopenharmony_ci case INGENIC_ADC_AUX0: 65062306a36Sopenharmony_ci cmd = 0; 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci case INGENIC_ADC_AUX: 65362306a36Sopenharmony_ci cmd = 1; 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci case INGENIC_ADC_AUX2: 65662306a36Sopenharmony_ci cmd = 2; 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, cmd); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci ret = ingenic_adc_capture(adc, engine); 66462306a36Sopenharmony_ci if (ret) 66562306a36Sopenharmony_ci goto out; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci switch (chan->channel) { 66862306a36Sopenharmony_ci case INGENIC_ADC_AUX0: 66962306a36Sopenharmony_ci case INGENIC_ADC_AUX: 67062306a36Sopenharmony_ci case INGENIC_ADC_AUX2: 67162306a36Sopenharmony_ci *val = readw(adc->base + JZ_ADC_REG_ADSDAT); 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case INGENIC_ADC_BATTERY: 67462306a36Sopenharmony_ci *val = readw(adc->base + JZ_ADC_REG_ADBDAT); 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci ret = IIO_VAL_INT; 67962306a36Sopenharmony_ciout: 68062306a36Sopenharmony_ci mutex_unlock(&adc->aux_lock); 68162306a36Sopenharmony_ci clk_disable(adc->clk); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return ret; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int ingenic_adc_read_raw(struct iio_dev *iio_dev, 68762306a36Sopenharmony_ci struct iio_chan_spec const *chan, 68862306a36Sopenharmony_ci int *val, 68962306a36Sopenharmony_ci int *val2, 69062306a36Sopenharmony_ci long m) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct ingenic_adc *adc = iio_priv(iio_dev); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci switch (m) { 69562306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 69662306a36Sopenharmony_ci return ingenic_adc_read_chan_info_raw(iio_dev, chan, val); 69762306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 69862306a36Sopenharmony_ci switch (chan->channel) { 69962306a36Sopenharmony_ci case INGENIC_ADC_AUX0: 70062306a36Sopenharmony_ci case INGENIC_ADC_AUX: 70162306a36Sopenharmony_ci case INGENIC_ADC_AUX2: 70262306a36Sopenharmony_ci *val = JZ_ADC_AUX_VREF; 70362306a36Sopenharmony_ci *val2 = JZ_ADC_AUX_VREF_BITS; 70462306a36Sopenharmony_ci break; 70562306a36Sopenharmony_ci case INGENIC_ADC_BATTERY: 70662306a36Sopenharmony_ci if (adc->low_vref_mode) { 70762306a36Sopenharmony_ci *val = JZ_ADC_BATTERY_LOW_VREF; 70862306a36Sopenharmony_ci *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS; 70962306a36Sopenharmony_ci } else { 71062306a36Sopenharmony_ci *val = adc->soc_data->battery_high_vref; 71162306a36Sopenharmony_ci *val2 = adc->soc_data->battery_high_vref_bits; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci break; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return IIO_VAL_FRACTIONAL_LOG2; 71762306a36Sopenharmony_ci default: 71862306a36Sopenharmony_ci return -EINVAL; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic int ingenic_adc_fwnode_xlate(struct iio_dev *iio_dev, 72362306a36Sopenharmony_ci const struct fwnode_reference_args *iiospec) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci int i; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (!iiospec->nargs) 72862306a36Sopenharmony_ci return -EINVAL; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci for (i = 0; i < iio_dev->num_channels; ++i) 73162306a36Sopenharmony_ci if (iio_dev->channels[i].channel == iiospec->args[0]) 73262306a36Sopenharmony_ci return i; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return -EINVAL; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic const struct iio_info ingenic_adc_info = { 73862306a36Sopenharmony_ci .write_raw = ingenic_adc_write_raw, 73962306a36Sopenharmony_ci .read_raw = ingenic_adc_read_raw, 74062306a36Sopenharmony_ci .read_avail = ingenic_adc_read_avail, 74162306a36Sopenharmony_ci .fwnode_xlate = ingenic_adc_fwnode_xlate, 74262306a36Sopenharmony_ci}; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int ingenic_adc_buffer_enable(struct iio_dev *iio_dev) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct ingenic_adc *adc = iio_priv(iio_dev); 74762306a36Sopenharmony_ci int ret; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ret = clk_enable(adc->clk); 75062306a36Sopenharmony_ci if (ret) { 75162306a36Sopenharmony_ci dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", 75262306a36Sopenharmony_ci ret); 75362306a36Sopenharmony_ci return ret; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* It takes significant time for the touchscreen hw to stabilize. */ 75762306a36Sopenharmony_ci msleep(50); 75862306a36Sopenharmony_ci ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 75962306a36Sopenharmony_ci JZ_ADC_REG_CFG_SAMPLE_NUM(4) | 76062306a36Sopenharmony_ci JZ_ADC_REG_CFG_PULL_UP(4)); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci writew(80, adc->base + JZ_ADC_REG_ADWAIT); 76362306a36Sopenharmony_ci writew(2, adc->base + JZ_ADC_REG_ADSAME); 76462306a36Sopenharmony_ci writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL); 76562306a36Sopenharmony_ci writel(0, adc->base + JZ_ADC_REG_ADTCH); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, 76862306a36Sopenharmony_ci JZ_ADC_REG_CFG_CMD_SEL); 76962306a36Sopenharmony_ci ingenic_adc_set_adcmd(iio_dev, iio_dev->active_scan_mask[0]); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ingenic_adc_enable(adc, 2, true); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int ingenic_adc_buffer_disable(struct iio_dev *iio_dev) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct ingenic_adc *adc = iio_priv(iio_dev); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci ingenic_adc_enable(adc, 2, false); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, 0); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci writeb(0xff, adc->base + JZ_ADC_REG_CTRL); 78562306a36Sopenharmony_ci writeb(0xff, adc->base + JZ_ADC_REG_STATUS); 78662306a36Sopenharmony_ci ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0); 78762306a36Sopenharmony_ci writew(0, adc->base + JZ_ADC_REG_ADSAME); 78862306a36Sopenharmony_ci writew(0, adc->base + JZ_ADC_REG_ADWAIT); 78962306a36Sopenharmony_ci clk_disable(adc->clk); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct iio_buffer_setup_ops ingenic_buffer_setup_ops = { 79562306a36Sopenharmony_ci .postenable = &ingenic_adc_buffer_enable, 79662306a36Sopenharmony_ci .predisable = &ingenic_adc_buffer_disable 79762306a36Sopenharmony_ci}; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic irqreturn_t ingenic_adc_irq(int irq, void *data) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct iio_dev *iio_dev = data; 80262306a36Sopenharmony_ci struct ingenic_adc *adc = iio_priv(iio_dev); 80362306a36Sopenharmony_ci unsigned long mask = iio_dev->active_scan_mask[0]; 80462306a36Sopenharmony_ci unsigned int i; 80562306a36Sopenharmony_ci u32 tdat[3]; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tdat); mask >>= 2, i++) { 80862306a36Sopenharmony_ci if (mask & 0x3) 80962306a36Sopenharmony_ci tdat[i] = readl(adc->base + JZ_ADC_REG_ADTCH); 81062306a36Sopenharmony_ci else 81162306a36Sopenharmony_ci tdat[i] = 0; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci iio_push_to_buffers(iio_dev, tdat); 81562306a36Sopenharmony_ci writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return IRQ_HANDLED; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int ingenic_adc_probe(struct platform_device *pdev) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 82362306a36Sopenharmony_ci struct iio_dev *iio_dev; 82462306a36Sopenharmony_ci struct ingenic_adc *adc; 82562306a36Sopenharmony_ci const struct ingenic_adc_soc_data *soc_data; 82662306a36Sopenharmony_ci int irq, ret; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci soc_data = device_get_match_data(dev); 82962306a36Sopenharmony_ci if (!soc_data) 83062306a36Sopenharmony_ci return -EINVAL; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci iio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); 83362306a36Sopenharmony_ci if (!iio_dev) 83462306a36Sopenharmony_ci return -ENOMEM; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci adc = iio_priv(iio_dev); 83762306a36Sopenharmony_ci mutex_init(&adc->lock); 83862306a36Sopenharmony_ci mutex_init(&adc->aux_lock); 83962306a36Sopenharmony_ci adc->soc_data = soc_data; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 84262306a36Sopenharmony_ci if (irq < 0) 84362306a36Sopenharmony_ci return irq; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0, 84662306a36Sopenharmony_ci dev_name(dev), iio_dev); 84762306a36Sopenharmony_ci if (ret < 0) { 84862306a36Sopenharmony_ci dev_err(dev, "Failed to request irq: %d\n", ret); 84962306a36Sopenharmony_ci return ret; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci adc->base = devm_platform_ioremap_resource(pdev, 0); 85362306a36Sopenharmony_ci if (IS_ERR(adc->base)) 85462306a36Sopenharmony_ci return PTR_ERR(adc->base); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci adc->clk = devm_clk_get_prepared(dev, "adc"); 85762306a36Sopenharmony_ci if (IS_ERR(adc->clk)) { 85862306a36Sopenharmony_ci dev_err(dev, "Unable to get clock\n"); 85962306a36Sopenharmony_ci return PTR_ERR(adc->clk); 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci ret = clk_enable(adc->clk); 86362306a36Sopenharmony_ci if (ret) { 86462306a36Sopenharmony_ci dev_err(dev, "Failed to enable clock\n"); 86562306a36Sopenharmony_ci return ret; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* Set clock dividers. */ 86962306a36Sopenharmony_ci if (soc_data->init_clk_div) { 87062306a36Sopenharmony_ci ret = soc_data->init_clk_div(dev, adc); 87162306a36Sopenharmony_ci if (ret) { 87262306a36Sopenharmony_ci clk_disable_unprepare(adc->clk); 87362306a36Sopenharmony_ci return ret; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Put hardware in a known passive state. */ 87862306a36Sopenharmony_ci writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); 87962306a36Sopenharmony_ci writeb(0xff, adc->base + JZ_ADC_REG_CTRL); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* JZ4760B specific */ 88262306a36Sopenharmony_ci if (device_property_present(dev, "ingenic,use-internal-divider")) 88362306a36Sopenharmony_ci ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL, 88462306a36Sopenharmony_ci JZ_ADC_REG_CFG_VBAT_SEL); 88562306a36Sopenharmony_ci else 88662306a36Sopenharmony_ci ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL, 0); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci usleep_range(2000, 3000); /* Must wait at least 2ms. */ 88962306a36Sopenharmony_ci clk_disable(adc->clk); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci iio_dev->name = "jz-adc"; 89262306a36Sopenharmony_ci iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; 89362306a36Sopenharmony_ci iio_dev->setup_ops = &ingenic_buffer_setup_ops; 89462306a36Sopenharmony_ci iio_dev->channels = soc_data->channels; 89562306a36Sopenharmony_ci iio_dev->num_channels = soc_data->num_channels; 89662306a36Sopenharmony_ci iio_dev->info = &ingenic_adc_info; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci ret = devm_iio_device_register(dev, iio_dev); 89962306a36Sopenharmony_ci if (ret) 90062306a36Sopenharmony_ci dev_err(dev, "Unable to register IIO device\n"); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return ret; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic const struct of_device_id ingenic_adc_of_match[] = { 90662306a36Sopenharmony_ci { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, }, 90762306a36Sopenharmony_ci { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, }, 90862306a36Sopenharmony_ci { .compatible = "ingenic,jz4760-adc", .data = &jz4760_adc_soc_data, }, 90962306a36Sopenharmony_ci { .compatible = "ingenic,jz4760b-adc", .data = &jz4760_adc_soc_data, }, 91062306a36Sopenharmony_ci { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, }, 91162306a36Sopenharmony_ci { }, 91262306a36Sopenharmony_ci}; 91362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ingenic_adc_of_match); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic struct platform_driver ingenic_adc_driver = { 91662306a36Sopenharmony_ci .driver = { 91762306a36Sopenharmony_ci .name = "ingenic-adc", 91862306a36Sopenharmony_ci .of_match_table = ingenic_adc_of_match, 91962306a36Sopenharmony_ci }, 92062306a36Sopenharmony_ci .probe = ingenic_adc_probe, 92162306a36Sopenharmony_ci}; 92262306a36Sopenharmony_cimodule_platform_driver(ingenic_adc_driver); 92362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 924