11a78daeaSArtur Rojek // SPDX-License-Identifier: GPL-2.0 21a78daeaSArtur Rojek /* 31a78daeaSArtur Rojek * ADC driver for the Ingenic JZ47xx SoCs 41a78daeaSArtur Rojek * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu> 51a78daeaSArtur Rojek * 61a78daeaSArtur Rojek * based on drivers/mfd/jz4740-adc.c 71a78daeaSArtur Rojek */ 81a78daeaSArtur Rojek 91a78daeaSArtur Rojek #include <dt-bindings/iio/adc/ingenic,adc.h> 101a78daeaSArtur Rojek #include <linux/clk.h> 111a78daeaSArtur Rojek #include <linux/iio/iio.h> 121a78daeaSArtur Rojek #include <linux/io.h> 131a78daeaSArtur Rojek #include <linux/iopoll.h> 145a304e1aSMaarten ter Huurne #include <linux/kernel.h> 151a78daeaSArtur Rojek #include <linux/module.h> 161a78daeaSArtur Rojek #include <linux/mutex.h> 171a78daeaSArtur Rojek #include <linux/platform_device.h> 181a78daeaSArtur Rojek 191a78daeaSArtur Rojek #define JZ_ADC_REG_ENABLE 0x00 201a78daeaSArtur Rojek #define JZ_ADC_REG_CFG 0x04 211a78daeaSArtur Rojek #define JZ_ADC_REG_CTRL 0x08 221a78daeaSArtur Rojek #define JZ_ADC_REG_STATUS 0x0c 231a78daeaSArtur Rojek #define JZ_ADC_REG_ADTCH 0x18 241a78daeaSArtur Rojek #define JZ_ADC_REG_ADBDAT 0x1c 251a78daeaSArtur Rojek #define JZ_ADC_REG_ADSDAT 0x20 265a304e1aSMaarten ter Huurne #define JZ_ADC_REG_ADCLK 0x28 271a78daeaSArtur Rojek 28a515d648SArtur Rojek #define JZ_ADC_REG_ENABLE_PD BIT(7) 29a515d648SArtur Rojek #define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1)) 301a78daeaSArtur Rojek #define JZ_ADC_REG_CFG_BAT_MD BIT(4) 315a304e1aSMaarten ter Huurne #define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0 32a515d648SArtur Rojek #define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16 33a515d648SArtur Rojek #define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8 34a515d648SArtur Rojek #define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16 351a78daeaSArtur Rojek 361a78daeaSArtur Rojek #define JZ_ADC_AUX_VREF 3300 371a78daeaSArtur Rojek #define JZ_ADC_AUX_VREF_BITS 12 381a78daeaSArtur Rojek #define JZ_ADC_BATTERY_LOW_VREF 2500 391a78daeaSArtur Rojek #define JZ_ADC_BATTERY_LOW_VREF_BITS 12 401a78daeaSArtur Rojek #define JZ4725B_ADC_BATTERY_HIGH_VREF 7500 411a78daeaSArtur Rojek #define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10 421a78daeaSArtur Rojek #define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986) 431a78daeaSArtur Rojek #define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12 44a515d648SArtur Rojek #define JZ4770_ADC_BATTERY_VREF 6600 45a515d648SArtur Rojek #define JZ4770_ADC_BATTERY_VREF_BITS 12 461a78daeaSArtur Rojek 475a304e1aSMaarten ter Huurne struct ingenic_adc; 485a304e1aSMaarten ter Huurne 491a78daeaSArtur Rojek struct ingenic_adc_soc_data { 501a78daeaSArtur Rojek unsigned int battery_high_vref; 511a78daeaSArtur Rojek unsigned int battery_high_vref_bits; 521a78daeaSArtur Rojek const int *battery_raw_avail; 531a78daeaSArtur Rojek size_t battery_raw_avail_size; 541a78daeaSArtur Rojek const int *battery_scale_avail; 551a78daeaSArtur Rojek size_t battery_scale_avail_size; 56a515d648SArtur Rojek unsigned int battery_vref_mode: 1; 57a515d648SArtur Rojek unsigned int has_aux2: 1; 58*6a294b41SPaul Cercueil const struct iio_chan_spec *channels; 59*6a294b41SPaul Cercueil unsigned int num_channels; 605a304e1aSMaarten ter Huurne int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc); 611a78daeaSArtur Rojek }; 621a78daeaSArtur Rojek 631a78daeaSArtur Rojek struct ingenic_adc { 641a78daeaSArtur Rojek void __iomem *base; 651a78daeaSArtur Rojek struct clk *clk; 661a78daeaSArtur Rojek struct mutex lock; 67a515d648SArtur Rojek struct mutex aux_lock; 681a78daeaSArtur Rojek const struct ingenic_adc_soc_data *soc_data; 691a78daeaSArtur Rojek bool low_vref_mode; 701a78daeaSArtur Rojek }; 711a78daeaSArtur Rojek 721a78daeaSArtur Rojek static void ingenic_adc_set_config(struct ingenic_adc *adc, 731a78daeaSArtur Rojek uint32_t mask, 741a78daeaSArtur Rojek uint32_t val) 751a78daeaSArtur Rojek { 761a78daeaSArtur Rojek uint32_t cfg; 771a78daeaSArtur Rojek 781a78daeaSArtur Rojek mutex_lock(&adc->lock); 791a78daeaSArtur Rojek 801a78daeaSArtur Rojek cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask; 811a78daeaSArtur Rojek cfg |= val; 821a78daeaSArtur Rojek writel(cfg, adc->base + JZ_ADC_REG_CFG); 831a78daeaSArtur Rojek 841a78daeaSArtur Rojek mutex_unlock(&adc->lock); 851a78daeaSArtur Rojek } 861a78daeaSArtur Rojek 871a78daeaSArtur Rojek static void ingenic_adc_enable(struct ingenic_adc *adc, 881a78daeaSArtur Rojek int engine, 891a78daeaSArtur Rojek bool enabled) 901a78daeaSArtur Rojek { 911a78daeaSArtur Rojek u8 val; 921a78daeaSArtur Rojek 931a78daeaSArtur Rojek mutex_lock(&adc->lock); 941a78daeaSArtur Rojek val = readb(adc->base + JZ_ADC_REG_ENABLE); 951a78daeaSArtur Rojek 961a78daeaSArtur Rojek if (enabled) 971a78daeaSArtur Rojek val |= BIT(engine); 981a78daeaSArtur Rojek else 991a78daeaSArtur Rojek val &= ~BIT(engine); 1001a78daeaSArtur Rojek 1011a78daeaSArtur Rojek writeb(val, adc->base + JZ_ADC_REG_ENABLE); 1021a78daeaSArtur Rojek mutex_unlock(&adc->lock); 1031a78daeaSArtur Rojek } 1041a78daeaSArtur Rojek 1051a78daeaSArtur Rojek static int ingenic_adc_capture(struct ingenic_adc *adc, 1061a78daeaSArtur Rojek int engine) 1071a78daeaSArtur Rojek { 1081a78daeaSArtur Rojek u8 val; 1091a78daeaSArtur Rojek int ret; 1101a78daeaSArtur Rojek 1111a78daeaSArtur Rojek ingenic_adc_enable(adc, engine, true); 1121a78daeaSArtur Rojek ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val, 1131a78daeaSArtur Rojek !(val & BIT(engine)), 250, 1000); 1141a78daeaSArtur Rojek if (ret) 1151a78daeaSArtur Rojek ingenic_adc_enable(adc, engine, false); 1161a78daeaSArtur Rojek 1171a78daeaSArtur Rojek return ret; 1181a78daeaSArtur Rojek } 1191a78daeaSArtur Rojek 1201a78daeaSArtur Rojek static int ingenic_adc_write_raw(struct iio_dev *iio_dev, 1211a78daeaSArtur Rojek struct iio_chan_spec const *chan, 1221a78daeaSArtur Rojek int val, 1231a78daeaSArtur Rojek int val2, 1241a78daeaSArtur Rojek long m) 1251a78daeaSArtur Rojek { 1261a78daeaSArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev); 1271a99dc46SArtur Rojek struct device *dev = iio_dev->dev.parent; 1281a99dc46SArtur Rojek int ret; 1291a78daeaSArtur Rojek 1301a78daeaSArtur Rojek switch (m) { 1311a78daeaSArtur Rojek case IIO_CHAN_INFO_SCALE: 1321a78daeaSArtur Rojek switch (chan->channel) { 1331a78daeaSArtur Rojek case INGENIC_ADC_BATTERY: 134a515d648SArtur Rojek if (!adc->soc_data->battery_vref_mode) 135a515d648SArtur Rojek return -EINVAL; 1361a99dc46SArtur Rojek 1371a99dc46SArtur Rojek ret = clk_enable(adc->clk); 1381a99dc46SArtur Rojek if (ret) { 1391a99dc46SArtur Rojek dev_err(dev, "Failed to enable clock: %d\n", 1401a99dc46SArtur Rojek ret); 1411a99dc46SArtur Rojek return ret; 1421a99dc46SArtur Rojek } 1431a99dc46SArtur Rojek 1441a78daeaSArtur Rojek if (val > JZ_ADC_BATTERY_LOW_VREF) { 1451a78daeaSArtur Rojek ingenic_adc_set_config(adc, 1461a78daeaSArtur Rojek JZ_ADC_REG_CFG_BAT_MD, 1471a78daeaSArtur Rojek 0); 1481a78daeaSArtur Rojek adc->low_vref_mode = false; 1491a78daeaSArtur Rojek } else { 1501a78daeaSArtur Rojek ingenic_adc_set_config(adc, 1511a78daeaSArtur Rojek JZ_ADC_REG_CFG_BAT_MD, 1521a78daeaSArtur Rojek JZ_ADC_REG_CFG_BAT_MD); 1531a78daeaSArtur Rojek adc->low_vref_mode = true; 1541a78daeaSArtur Rojek } 1551a99dc46SArtur Rojek 1561a99dc46SArtur Rojek clk_disable(adc->clk); 1571a99dc46SArtur Rojek 1581a78daeaSArtur Rojek return 0; 1591a78daeaSArtur Rojek default: 1601a78daeaSArtur Rojek return -EINVAL; 1611a78daeaSArtur Rojek } 1621a78daeaSArtur Rojek default: 1631a78daeaSArtur Rojek return -EINVAL; 1641a78daeaSArtur Rojek } 1651a78daeaSArtur Rojek } 1661a78daeaSArtur Rojek 1671a78daeaSArtur Rojek static const int jz4725b_adc_battery_raw_avail[] = { 1681a78daeaSArtur Rojek 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, 1691a78daeaSArtur Rojek }; 1701a78daeaSArtur Rojek 1711a78daeaSArtur Rojek static const int jz4725b_adc_battery_scale_avail[] = { 1721a78daeaSArtur Rojek JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, 1731a78daeaSArtur Rojek JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, 1741a78daeaSArtur Rojek }; 1751a78daeaSArtur Rojek 1761a78daeaSArtur Rojek static const int jz4740_adc_battery_raw_avail[] = { 1771a78daeaSArtur Rojek 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, 1781a78daeaSArtur Rojek }; 1791a78daeaSArtur Rojek 1801a78daeaSArtur Rojek static const int jz4740_adc_battery_scale_avail[] = { 1811a78daeaSArtur Rojek JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS, 1821a78daeaSArtur Rojek JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, 1831a78daeaSArtur Rojek }; 1841a78daeaSArtur Rojek 185a515d648SArtur Rojek static const int jz4770_adc_battery_raw_avail[] = { 186a515d648SArtur Rojek 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1, 187a515d648SArtur Rojek }; 188a515d648SArtur Rojek 189a515d648SArtur Rojek static const int jz4770_adc_battery_scale_avail[] = { 190a515d648SArtur Rojek JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS, 191a515d648SArtur Rojek }; 192a515d648SArtur Rojek 1935a304e1aSMaarten ter Huurne static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) 1945a304e1aSMaarten ter Huurne { 1955a304e1aSMaarten ter Huurne struct clk *parent_clk; 1965a304e1aSMaarten ter Huurne unsigned long parent_rate, rate; 1975a304e1aSMaarten ter Huurne unsigned int div_main, div_10us; 1985a304e1aSMaarten ter Huurne 1995a304e1aSMaarten ter Huurne parent_clk = clk_get_parent(adc->clk); 2005a304e1aSMaarten ter Huurne if (!parent_clk) { 2015a304e1aSMaarten ter Huurne dev_err(dev, "ADC clock has no parent\n"); 2025a304e1aSMaarten ter Huurne return -ENODEV; 2035a304e1aSMaarten ter Huurne } 2045a304e1aSMaarten ter Huurne parent_rate = clk_get_rate(parent_clk); 2055a304e1aSMaarten ter Huurne 2065a304e1aSMaarten ter Huurne /* 2075a304e1aSMaarten ter Huurne * The JZ4725B ADC works at 500 kHz to 8 MHz. 2085a304e1aSMaarten ter Huurne * We pick the highest rate possible. 2095a304e1aSMaarten ter Huurne * In practice we typically get 6 MHz, half of the 12 MHz EXT clock. 2105a304e1aSMaarten ter Huurne */ 2115a304e1aSMaarten ter Huurne div_main = DIV_ROUND_UP(parent_rate, 8000000); 2125a304e1aSMaarten ter Huurne div_main = clamp(div_main, 1u, 64u); 2135a304e1aSMaarten ter Huurne rate = parent_rate / div_main; 2145a304e1aSMaarten ter Huurne if (rate < 500000 || rate > 8000000) { 2155a304e1aSMaarten ter Huurne dev_err(dev, "No valid divider for ADC main clock\n"); 2165a304e1aSMaarten ter Huurne return -EINVAL; 2175a304e1aSMaarten ter Huurne } 2185a304e1aSMaarten ter Huurne 2195a304e1aSMaarten ter Huurne /* We also need a divider that produces a 10us clock. */ 2205a304e1aSMaarten ter Huurne div_10us = DIV_ROUND_UP(rate, 100000); 2215a304e1aSMaarten ter Huurne 222a515d648SArtur Rojek writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) | 223a515d648SArtur Rojek (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, 224a515d648SArtur Rojek adc->base + JZ_ADC_REG_ADCLK); 225a515d648SArtur Rojek 226a515d648SArtur Rojek return 0; 227a515d648SArtur Rojek } 228a515d648SArtur Rojek 229a515d648SArtur Rojek static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) 230a515d648SArtur Rojek { 231a515d648SArtur Rojek struct clk *parent_clk; 232a515d648SArtur Rojek unsigned long parent_rate, rate; 233a515d648SArtur Rojek unsigned int div_main, div_ms, div_10us; 234a515d648SArtur Rojek 235a515d648SArtur Rojek parent_clk = clk_get_parent(adc->clk); 236a515d648SArtur Rojek if (!parent_clk) { 237a515d648SArtur Rojek dev_err(dev, "ADC clock has no parent\n"); 238a515d648SArtur Rojek return -ENODEV; 239a515d648SArtur Rojek } 240a515d648SArtur Rojek parent_rate = clk_get_rate(parent_clk); 241a515d648SArtur Rojek 242a515d648SArtur Rojek /* 243a515d648SArtur Rojek * The JZ4770 ADC works at 20 kHz to 200 kHz. 244a515d648SArtur Rojek * We pick the highest rate possible. 245a515d648SArtur Rojek */ 246a515d648SArtur Rojek div_main = DIV_ROUND_UP(parent_rate, 200000); 247a515d648SArtur Rojek div_main = clamp(div_main, 1u, 256u); 248a515d648SArtur Rojek rate = parent_rate / div_main; 249a515d648SArtur Rojek if (rate < 20000 || rate > 200000) { 250a515d648SArtur Rojek dev_err(dev, "No valid divider for ADC main clock\n"); 251a515d648SArtur Rojek return -EINVAL; 252a515d648SArtur Rojek } 253a515d648SArtur Rojek 254a515d648SArtur Rojek /* We also need a divider that produces a 10us clock. */ 255a515d648SArtur Rojek div_10us = DIV_ROUND_UP(rate, 10000); 256a515d648SArtur Rojek /* And another, which produces a 1ms clock. */ 257a515d648SArtur Rojek div_ms = DIV_ROUND_UP(rate, 1000); 258a515d648SArtur Rojek 259a515d648SArtur Rojek writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) | 260a515d648SArtur Rojek ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) | 2615a304e1aSMaarten ter Huurne (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, 2625a304e1aSMaarten ter Huurne adc->base + JZ_ADC_REG_ADCLK); 2635a304e1aSMaarten ter Huurne 2645a304e1aSMaarten ter Huurne return 0; 2655a304e1aSMaarten ter Huurne } 2665a304e1aSMaarten ter Huurne 267*6a294b41SPaul Cercueil static const struct iio_chan_spec jz4740_channels[] = { 268*6a294b41SPaul Cercueil { 269*6a294b41SPaul Cercueil .extend_name = "aux", 270*6a294b41SPaul Cercueil .type = IIO_VOLTAGE, 271*6a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 272*6a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE), 273*6a294b41SPaul Cercueil .indexed = 1, 274*6a294b41SPaul Cercueil .channel = INGENIC_ADC_AUX, 275*6a294b41SPaul Cercueil .scan_index = -1, 276*6a294b41SPaul Cercueil }, 277*6a294b41SPaul Cercueil { 278*6a294b41SPaul Cercueil .extend_name = "battery", 279*6a294b41SPaul Cercueil .type = IIO_VOLTAGE, 280*6a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 281*6a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE), 282*6a294b41SPaul Cercueil .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | 283*6a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE), 284*6a294b41SPaul Cercueil .indexed = 1, 285*6a294b41SPaul Cercueil .channel = INGENIC_ADC_BATTERY, 286*6a294b41SPaul Cercueil .scan_index = -1, 287*6a294b41SPaul Cercueil }, 288*6a294b41SPaul Cercueil }; 289*6a294b41SPaul Cercueil 290*6a294b41SPaul Cercueil static const struct iio_chan_spec jz4770_channels[] = { 291*6a294b41SPaul Cercueil { 292*6a294b41SPaul Cercueil .extend_name = "aux", 293*6a294b41SPaul Cercueil .type = IIO_VOLTAGE, 294*6a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 295*6a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE), 296*6a294b41SPaul Cercueil .indexed = 1, 297*6a294b41SPaul Cercueil .channel = INGENIC_ADC_AUX, 298*6a294b41SPaul Cercueil .scan_index = -1, 299*6a294b41SPaul Cercueil }, 300*6a294b41SPaul Cercueil { 301*6a294b41SPaul Cercueil .extend_name = "battery", 302*6a294b41SPaul Cercueil .type = IIO_VOLTAGE, 303*6a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 304*6a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE), 305*6a294b41SPaul Cercueil .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | 306*6a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE), 307*6a294b41SPaul Cercueil .indexed = 1, 308*6a294b41SPaul Cercueil .channel = INGENIC_ADC_BATTERY, 309*6a294b41SPaul Cercueil .scan_index = -1, 310*6a294b41SPaul Cercueil }, 311*6a294b41SPaul Cercueil { 312*6a294b41SPaul Cercueil .extend_name = "aux2", 313*6a294b41SPaul Cercueil .type = IIO_VOLTAGE, 314*6a294b41SPaul Cercueil .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 315*6a294b41SPaul Cercueil BIT(IIO_CHAN_INFO_SCALE), 316*6a294b41SPaul Cercueil .indexed = 1, 317*6a294b41SPaul Cercueil .channel = INGENIC_ADC_AUX2, 318*6a294b41SPaul Cercueil .scan_index = -1, 319*6a294b41SPaul Cercueil }, 320*6a294b41SPaul Cercueil }; 321*6a294b41SPaul Cercueil 3221a78daeaSArtur Rojek static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { 3231a78daeaSArtur Rojek .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF, 3241a78daeaSArtur Rojek .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, 3251a78daeaSArtur Rojek .battery_raw_avail = jz4725b_adc_battery_raw_avail, 3261a78daeaSArtur Rojek .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail), 3271a78daeaSArtur Rojek .battery_scale_avail = jz4725b_adc_battery_scale_avail, 3281a78daeaSArtur Rojek .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail), 329a515d648SArtur Rojek .battery_vref_mode = true, 330a515d648SArtur Rojek .has_aux2 = false, 331*6a294b41SPaul Cercueil .channels = jz4740_channels, 332*6a294b41SPaul Cercueil .num_channels = ARRAY_SIZE(jz4740_channels), 3335a304e1aSMaarten ter Huurne .init_clk_div = jz4725b_adc_init_clk_div, 3341a78daeaSArtur Rojek }; 3351a78daeaSArtur Rojek 3361a78daeaSArtur Rojek static const struct ingenic_adc_soc_data jz4740_adc_soc_data = { 3371a78daeaSArtur Rojek .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF, 3381a78daeaSArtur Rojek .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS, 3391a78daeaSArtur Rojek .battery_raw_avail = jz4740_adc_battery_raw_avail, 3401a78daeaSArtur Rojek .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail), 3411a78daeaSArtur Rojek .battery_scale_avail = jz4740_adc_battery_scale_avail, 3421a78daeaSArtur Rojek .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail), 343a515d648SArtur Rojek .battery_vref_mode = true, 344a515d648SArtur Rojek .has_aux2 = false, 345*6a294b41SPaul Cercueil .channels = jz4740_channels, 346*6a294b41SPaul Cercueil .num_channels = ARRAY_SIZE(jz4740_channels), 3475a304e1aSMaarten ter Huurne .init_clk_div = NULL, /* no ADCLK register on JZ4740 */ 3481a78daeaSArtur Rojek }; 3491a78daeaSArtur Rojek 350a515d648SArtur Rojek static const struct ingenic_adc_soc_data jz4770_adc_soc_data = { 351a515d648SArtur Rojek .battery_high_vref = JZ4770_ADC_BATTERY_VREF, 352a515d648SArtur Rojek .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS, 353a515d648SArtur Rojek .battery_raw_avail = jz4770_adc_battery_raw_avail, 354a515d648SArtur Rojek .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail), 355a515d648SArtur Rojek .battery_scale_avail = jz4770_adc_battery_scale_avail, 356a515d648SArtur Rojek .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail), 357a515d648SArtur Rojek .battery_vref_mode = false, 358a515d648SArtur Rojek .has_aux2 = true, 359*6a294b41SPaul Cercueil .channels = jz4770_channels, 360*6a294b41SPaul Cercueil .num_channels = ARRAY_SIZE(jz4770_channels), 361a515d648SArtur Rojek .init_clk_div = jz4770_adc_init_clk_div, 362a515d648SArtur Rojek }; 363a515d648SArtur Rojek 3641a78daeaSArtur Rojek static int ingenic_adc_read_avail(struct iio_dev *iio_dev, 3651a78daeaSArtur Rojek struct iio_chan_spec const *chan, 3661a78daeaSArtur Rojek const int **vals, 3671a78daeaSArtur Rojek int *type, 3681a78daeaSArtur Rojek int *length, 3691a78daeaSArtur Rojek long m) 3701a78daeaSArtur Rojek { 3711a78daeaSArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev); 3721a78daeaSArtur Rojek 3731a78daeaSArtur Rojek switch (m) { 3741a78daeaSArtur Rojek case IIO_CHAN_INFO_RAW: 3751a78daeaSArtur Rojek *type = IIO_VAL_INT; 3761a78daeaSArtur Rojek *length = adc->soc_data->battery_raw_avail_size; 3771a78daeaSArtur Rojek *vals = adc->soc_data->battery_raw_avail; 3781a78daeaSArtur Rojek return IIO_AVAIL_RANGE; 3791a78daeaSArtur Rojek case IIO_CHAN_INFO_SCALE: 3801a78daeaSArtur Rojek *type = IIO_VAL_FRACTIONAL_LOG2; 3811a78daeaSArtur Rojek *length = adc->soc_data->battery_scale_avail_size; 3821a78daeaSArtur Rojek *vals = adc->soc_data->battery_scale_avail; 3831a78daeaSArtur Rojek return IIO_AVAIL_LIST; 3841a78daeaSArtur Rojek default: 3851a78daeaSArtur Rojek return -EINVAL; 3861a78daeaSArtur Rojek }; 3871a78daeaSArtur Rojek } 3881a78daeaSArtur Rojek 3891a99dc46SArtur Rojek static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev, 3901a78daeaSArtur Rojek struct iio_chan_spec const *chan, 391a515d648SArtur Rojek int *val) 3921a78daeaSArtur Rojek { 393a515d648SArtur Rojek int bit, ret, engine = (chan->channel == INGENIC_ADC_BATTERY); 3941a99dc46SArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev); 3951a99dc46SArtur Rojek 3961a99dc46SArtur Rojek ret = clk_enable(adc->clk); 3971a99dc46SArtur Rojek if (ret) { 3981a99dc46SArtur Rojek dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", 3991a99dc46SArtur Rojek ret); 4001a99dc46SArtur Rojek return ret; 4011a99dc46SArtur Rojek } 4021a78daeaSArtur Rojek 403a515d648SArtur Rojek /* We cannot sample AUX/AUX2 in parallel. */ 404a515d648SArtur Rojek mutex_lock(&adc->aux_lock); 405a515d648SArtur Rojek if (adc->soc_data->has_aux2 && engine == 0) { 406a515d648SArtur Rojek bit = BIT(chan->channel == INGENIC_ADC_AUX2); 407a515d648SArtur Rojek ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, bit); 4081a78daeaSArtur Rojek } 4091a78daeaSArtur Rojek 410a515d648SArtur Rojek ret = ingenic_adc_capture(adc, engine); 411a515d648SArtur Rojek if (ret) 412a515d648SArtur Rojek goto out; 413a515d648SArtur Rojek 4141a78daeaSArtur Rojek switch (chan->channel) { 4151a78daeaSArtur Rojek case INGENIC_ADC_AUX: 416a515d648SArtur Rojek case INGENIC_ADC_AUX2: 4171a78daeaSArtur Rojek *val = readw(adc->base + JZ_ADC_REG_ADSDAT); 4181a78daeaSArtur Rojek break; 4191a78daeaSArtur Rojek case INGENIC_ADC_BATTERY: 4201a78daeaSArtur Rojek *val = readw(adc->base + JZ_ADC_REG_ADBDAT); 4211a78daeaSArtur Rojek break; 4221a78daeaSArtur Rojek } 4231a78daeaSArtur Rojek 424a515d648SArtur Rojek ret = IIO_VAL_INT; 425a515d648SArtur Rojek out: 426a515d648SArtur Rojek mutex_unlock(&adc->aux_lock); 4271a99dc46SArtur Rojek clk_disable(adc->clk); 4281a78daeaSArtur Rojek 429a515d648SArtur Rojek return ret; 430a515d648SArtur Rojek } 431a515d648SArtur Rojek 432a515d648SArtur Rojek static int ingenic_adc_read_raw(struct iio_dev *iio_dev, 433a515d648SArtur Rojek struct iio_chan_spec const *chan, 434a515d648SArtur Rojek int *val, 435a515d648SArtur Rojek int *val2, 436a515d648SArtur Rojek long m) 437a515d648SArtur Rojek { 438a515d648SArtur Rojek struct ingenic_adc *adc = iio_priv(iio_dev); 439a515d648SArtur Rojek 440a515d648SArtur Rojek switch (m) { 441a515d648SArtur Rojek case IIO_CHAN_INFO_RAW: 4421a99dc46SArtur Rojek return ingenic_adc_read_chan_info_raw(iio_dev, chan, val); 4431a78daeaSArtur Rojek case IIO_CHAN_INFO_SCALE: 4441a78daeaSArtur Rojek switch (chan->channel) { 4451a78daeaSArtur Rojek case INGENIC_ADC_AUX: 446a515d648SArtur Rojek case INGENIC_ADC_AUX2: 4471a78daeaSArtur Rojek *val = JZ_ADC_AUX_VREF; 4481a78daeaSArtur Rojek *val2 = JZ_ADC_AUX_VREF_BITS; 4491a78daeaSArtur Rojek break; 4501a78daeaSArtur Rojek case INGENIC_ADC_BATTERY: 4511a78daeaSArtur Rojek if (adc->low_vref_mode) { 4521a78daeaSArtur Rojek *val = JZ_ADC_BATTERY_LOW_VREF; 4531a78daeaSArtur Rojek *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS; 4541a78daeaSArtur Rojek } else { 4551a78daeaSArtur Rojek *val = adc->soc_data->battery_high_vref; 4561a78daeaSArtur Rojek *val2 = adc->soc_data->battery_high_vref_bits; 4571a78daeaSArtur Rojek } 4581a78daeaSArtur Rojek break; 4591a78daeaSArtur Rojek } 4601a78daeaSArtur Rojek 4611a78daeaSArtur Rojek return IIO_VAL_FRACTIONAL_LOG2; 4621a78daeaSArtur Rojek default: 4631a78daeaSArtur Rojek return -EINVAL; 4641a78daeaSArtur Rojek } 4651a78daeaSArtur Rojek } 4661a78daeaSArtur Rojek 467155e41efSArtur Rojek static int ingenic_adc_of_xlate(struct iio_dev *iio_dev, 468155e41efSArtur Rojek const struct of_phandle_args *iiospec) 469155e41efSArtur Rojek { 470155e41efSArtur Rojek int i; 471155e41efSArtur Rojek 472155e41efSArtur Rojek if (!iiospec->args_count) 473155e41efSArtur Rojek return -EINVAL; 474155e41efSArtur Rojek 475155e41efSArtur Rojek for (i = 0; i < iio_dev->num_channels; ++i) 476155e41efSArtur Rojek if (iio_dev->channels[i].channel == iiospec->args[0]) 477155e41efSArtur Rojek return i; 478155e41efSArtur Rojek 479155e41efSArtur Rojek return -EINVAL; 480155e41efSArtur Rojek } 481155e41efSArtur Rojek 4821a78daeaSArtur Rojek static void ingenic_adc_clk_cleanup(void *data) 4831a78daeaSArtur Rojek { 4841a78daeaSArtur Rojek clk_unprepare(data); 4851a78daeaSArtur Rojek } 4861a78daeaSArtur Rojek 4871a78daeaSArtur Rojek static const struct iio_info ingenic_adc_info = { 4881a78daeaSArtur Rojek .write_raw = ingenic_adc_write_raw, 4891a78daeaSArtur Rojek .read_raw = ingenic_adc_read_raw, 4901a78daeaSArtur Rojek .read_avail = ingenic_adc_read_avail, 491155e41efSArtur Rojek .of_xlate = ingenic_adc_of_xlate, 4921a78daeaSArtur Rojek }; 4931a78daeaSArtur Rojek 4941a78daeaSArtur Rojek static int ingenic_adc_probe(struct platform_device *pdev) 4951a78daeaSArtur Rojek { 4961a78daeaSArtur Rojek struct device *dev = &pdev->dev; 4971a78daeaSArtur Rojek struct iio_dev *iio_dev; 4981a78daeaSArtur Rojek struct ingenic_adc *adc; 4991a78daeaSArtur Rojek const struct ingenic_adc_soc_data *soc_data; 5001a78daeaSArtur Rojek int ret; 5011a78daeaSArtur Rojek 5021a78daeaSArtur Rojek soc_data = device_get_match_data(dev); 5031a78daeaSArtur Rojek if (!soc_data) 5041a78daeaSArtur Rojek return -EINVAL; 5051a78daeaSArtur Rojek 5061a78daeaSArtur Rojek iio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); 5071a78daeaSArtur Rojek if (!iio_dev) 5081a78daeaSArtur Rojek return -ENOMEM; 5091a78daeaSArtur Rojek 5101a78daeaSArtur Rojek adc = iio_priv(iio_dev); 5111a78daeaSArtur Rojek mutex_init(&adc->lock); 512a515d648SArtur Rojek mutex_init(&adc->aux_lock); 5131a78daeaSArtur Rojek adc->soc_data = soc_data; 5141a78daeaSArtur Rojek 515f449aa3eSJonathan Cameron adc->base = devm_platform_ioremap_resource(pdev, 0); 51676838a8fSWei Yongjun if (IS_ERR(adc->base)) 5171a78daeaSArtur Rojek return PTR_ERR(adc->base); 5181a78daeaSArtur Rojek 5191a78daeaSArtur Rojek adc->clk = devm_clk_get(dev, "adc"); 5201a78daeaSArtur Rojek if (IS_ERR(adc->clk)) { 5211a78daeaSArtur Rojek dev_err(dev, "Unable to get clock\n"); 5221a78daeaSArtur Rojek return PTR_ERR(adc->clk); 5231a78daeaSArtur Rojek } 5241a78daeaSArtur Rojek 5251a78daeaSArtur Rojek ret = clk_prepare_enable(adc->clk); 5261a78daeaSArtur Rojek if (ret) { 5271a78daeaSArtur Rojek dev_err(dev, "Failed to enable clock\n"); 5281a78daeaSArtur Rojek return ret; 5291a78daeaSArtur Rojek } 5301a78daeaSArtur Rojek 5315a304e1aSMaarten ter Huurne /* Set clock dividers. */ 5325a304e1aSMaarten ter Huurne if (soc_data->init_clk_div) { 5335a304e1aSMaarten ter Huurne ret = soc_data->init_clk_div(dev, adc); 5345a304e1aSMaarten ter Huurne if (ret) { 5355a304e1aSMaarten ter Huurne clk_disable_unprepare(adc->clk); 5365a304e1aSMaarten ter Huurne return ret; 5375a304e1aSMaarten ter Huurne } 5385a304e1aSMaarten ter Huurne } 5395a304e1aSMaarten ter Huurne 5401a78daeaSArtur Rojek /* Put hardware in a known passive state. */ 5411a78daeaSArtur Rojek writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); 5421a78daeaSArtur Rojek writeb(0xff, adc->base + JZ_ADC_REG_CTRL); 543a515d648SArtur Rojek usleep_range(2000, 3000); /* Must wait at least 2ms. */ 5441a78daeaSArtur Rojek clk_disable(adc->clk); 5451a78daeaSArtur Rojek 5461a78daeaSArtur Rojek ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk); 5471a78daeaSArtur Rojek if (ret) { 5481a78daeaSArtur Rojek dev_err(dev, "Unable to add action\n"); 5491a78daeaSArtur Rojek return ret; 5501a78daeaSArtur Rojek } 5511a78daeaSArtur Rojek 5521a78daeaSArtur Rojek iio_dev->dev.parent = dev; 5531a78daeaSArtur Rojek iio_dev->name = "jz-adc"; 5541a78daeaSArtur Rojek iio_dev->modes = INDIO_DIRECT_MODE; 555*6a294b41SPaul Cercueil iio_dev->channels = soc_data->channels; 556*6a294b41SPaul Cercueil iio_dev->num_channels = soc_data->num_channels; 5571a78daeaSArtur Rojek iio_dev->info = &ingenic_adc_info; 5581a78daeaSArtur Rojek 5591a78daeaSArtur Rojek ret = devm_iio_device_register(dev, iio_dev); 5601a78daeaSArtur Rojek if (ret) 5611a78daeaSArtur Rojek dev_err(dev, "Unable to register IIO device\n"); 5621a78daeaSArtur Rojek 5631a78daeaSArtur Rojek return ret; 5641a78daeaSArtur Rojek } 5651a78daeaSArtur Rojek 5661a78daeaSArtur Rojek #ifdef CONFIG_OF 5671a78daeaSArtur Rojek static const struct of_device_id ingenic_adc_of_match[] = { 5681a78daeaSArtur Rojek { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, }, 5691a78daeaSArtur Rojek { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, }, 570a515d648SArtur Rojek { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, }, 5711a78daeaSArtur Rojek { }, 5721a78daeaSArtur Rojek }; 5731a78daeaSArtur Rojek MODULE_DEVICE_TABLE(of, ingenic_adc_of_match); 5741a78daeaSArtur Rojek #endif 5751a78daeaSArtur Rojek 5761a78daeaSArtur Rojek static struct platform_driver ingenic_adc_driver = { 5771a78daeaSArtur Rojek .driver = { 5781a78daeaSArtur Rojek .name = "ingenic-adc", 5791a78daeaSArtur Rojek .of_match_table = of_match_ptr(ingenic_adc_of_match), 5801a78daeaSArtur Rojek }, 5811a78daeaSArtur Rojek .probe = ingenic_adc_probe, 5821a78daeaSArtur Rojek }; 5831a78daeaSArtur Rojek module_platform_driver(ingenic_adc_driver); 5841a78daeaSArtur Rojek MODULE_LICENSE("GPL v2"); 585