19bf85fbcSTomer Maimon // SPDX-License-Identifier: GPL-2.0 29bf85fbcSTomer Maimon // Copyright (c) 2019 Nuvoton Technology corporation. 39bf85fbcSTomer Maimon 49bf85fbcSTomer Maimon #include <linux/clk.h> 59bf85fbcSTomer Maimon #include <linux/device.h> 69bf85fbcSTomer Maimon #include <linux/mfd/syscon.h> 79bf85fbcSTomer Maimon #include <linux/io.h> 89bf85fbcSTomer Maimon #include <linux/iio/iio.h> 99bf85fbcSTomer Maimon #include <linux/interrupt.h> 109bf85fbcSTomer Maimon #include <linux/kernel.h> 119bf85fbcSTomer Maimon #include <linux/module.h> 129bf85fbcSTomer Maimon #include <linux/platform_device.h> 139bf85fbcSTomer Maimon #include <linux/regmap.h> 149bf85fbcSTomer Maimon #include <linux/regulator/consumer.h> 159bf85fbcSTomer Maimon #include <linux/spinlock.h> 169bf85fbcSTomer Maimon #include <linux/uaccess.h> 179bf85fbcSTomer Maimon 189bf85fbcSTomer Maimon struct npcm_adc { 199bf85fbcSTomer Maimon bool int_status; 209bf85fbcSTomer Maimon u32 adc_sample_hz; 219bf85fbcSTomer Maimon struct device *dev; 229bf85fbcSTomer Maimon void __iomem *regs; 239bf85fbcSTomer Maimon struct clk *adc_clk; 249bf85fbcSTomer Maimon wait_queue_head_t wq; 259bf85fbcSTomer Maimon struct regulator *vref; 269bf85fbcSTomer Maimon struct regmap *rst_regmap; 279bf85fbcSTomer Maimon }; 289bf85fbcSTomer Maimon 299bf85fbcSTomer Maimon /* NPCM7xx reset module */ 309bf85fbcSTomer Maimon #define NPCM7XX_IPSRST1_OFFSET 0x020 319bf85fbcSTomer Maimon #define NPCM7XX_IPSRST1_ADC_RST BIT(27) 329bf85fbcSTomer Maimon 339bf85fbcSTomer Maimon /* ADC registers */ 349bf85fbcSTomer Maimon #define NPCM_ADCCON 0x00 359bf85fbcSTomer Maimon #define NPCM_ADCDATA 0x04 369bf85fbcSTomer Maimon 379bf85fbcSTomer Maimon /* ADCCON Register Bits */ 389bf85fbcSTomer Maimon #define NPCM_ADCCON_ADC_INT_EN BIT(21) 399bf85fbcSTomer Maimon #define NPCM_ADCCON_REFSEL BIT(19) 409bf85fbcSTomer Maimon #define NPCM_ADCCON_ADC_INT_ST BIT(18) 419bf85fbcSTomer Maimon #define NPCM_ADCCON_ADC_EN BIT(17) 429bf85fbcSTomer Maimon #define NPCM_ADCCON_ADC_RST BIT(16) 439bf85fbcSTomer Maimon #define NPCM_ADCCON_ADC_CONV BIT(13) 449bf85fbcSTomer Maimon 459bf85fbcSTomer Maimon #define NPCM_ADCCON_CH_MASK GENMASK(27, 24) 469bf85fbcSTomer Maimon #define NPCM_ADCCON_CH(x) ((x) << 24) 479bf85fbcSTomer Maimon #define NPCM_ADCCON_DIV_SHIFT 1 489bf85fbcSTomer Maimon #define NPCM_ADCCON_DIV_MASK GENMASK(8, 1) 499bf85fbcSTomer Maimon #define NPCM_ADC_DATA_MASK(x) ((x) & GENMASK(9, 0)) 509bf85fbcSTomer Maimon 519bf85fbcSTomer Maimon #define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN) 529bf85fbcSTomer Maimon 539bf85fbcSTomer Maimon /* ADC General Definition */ 549bf85fbcSTomer Maimon #define NPCM_RESOLUTION_BITS 10 559bf85fbcSTomer Maimon #define NPCM_INT_VREF_MV 2000 569bf85fbcSTomer Maimon 579bf85fbcSTomer Maimon #define NPCM_ADC_CHAN(ch) { \ 589bf85fbcSTomer Maimon .type = IIO_VOLTAGE, \ 599bf85fbcSTomer Maimon .indexed = 1, \ 609bf85fbcSTomer Maimon .channel = ch, \ 619bf85fbcSTomer Maimon .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 629bf85fbcSTomer Maimon .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 639bf85fbcSTomer Maimon BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 649bf85fbcSTomer Maimon } 659bf85fbcSTomer Maimon 669bf85fbcSTomer Maimon static const struct iio_chan_spec npcm_adc_iio_channels[] = { 679bf85fbcSTomer Maimon NPCM_ADC_CHAN(0), 689bf85fbcSTomer Maimon NPCM_ADC_CHAN(1), 699bf85fbcSTomer Maimon NPCM_ADC_CHAN(2), 709bf85fbcSTomer Maimon NPCM_ADC_CHAN(3), 719bf85fbcSTomer Maimon NPCM_ADC_CHAN(4), 729bf85fbcSTomer Maimon NPCM_ADC_CHAN(5), 739bf85fbcSTomer Maimon NPCM_ADC_CHAN(6), 749bf85fbcSTomer Maimon NPCM_ADC_CHAN(7), 759bf85fbcSTomer Maimon }; 769bf85fbcSTomer Maimon 779bf85fbcSTomer Maimon static irqreturn_t npcm_adc_isr(int irq, void *data) 789bf85fbcSTomer Maimon { 799bf85fbcSTomer Maimon u32 regtemp; 809bf85fbcSTomer Maimon struct iio_dev *indio_dev = data; 819bf85fbcSTomer Maimon struct npcm_adc *info = iio_priv(indio_dev); 829bf85fbcSTomer Maimon 839bf85fbcSTomer Maimon regtemp = ioread32(info->regs + NPCM_ADCCON); 849bf85fbcSTomer Maimon if (regtemp & NPCM_ADCCON_ADC_INT_ST) { 859bf85fbcSTomer Maimon iowrite32(regtemp, info->regs + NPCM_ADCCON); 869bf85fbcSTomer Maimon wake_up_interruptible(&info->wq); 879bf85fbcSTomer Maimon info->int_status = true; 889bf85fbcSTomer Maimon } 899bf85fbcSTomer Maimon 909bf85fbcSTomer Maimon return IRQ_HANDLED; 919bf85fbcSTomer Maimon } 929bf85fbcSTomer Maimon 939bf85fbcSTomer Maimon static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel) 949bf85fbcSTomer Maimon { 959bf85fbcSTomer Maimon int ret; 969bf85fbcSTomer Maimon u32 regtemp; 979bf85fbcSTomer Maimon 989bf85fbcSTomer Maimon /* Select ADC channel */ 999bf85fbcSTomer Maimon regtemp = ioread32(info->regs + NPCM_ADCCON); 1009bf85fbcSTomer Maimon regtemp &= ~NPCM_ADCCON_CH_MASK; 1019bf85fbcSTomer Maimon info->int_status = false; 1029bf85fbcSTomer Maimon iowrite32(regtemp | NPCM_ADCCON_CH(channel) | 1039bf85fbcSTomer Maimon NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON); 1049bf85fbcSTomer Maimon 1059bf85fbcSTomer Maimon ret = wait_event_interruptible_timeout(info->wq, info->int_status, 1069bf85fbcSTomer Maimon msecs_to_jiffies(10)); 1079bf85fbcSTomer Maimon if (ret == 0) { 1089bf85fbcSTomer Maimon regtemp = ioread32(info->regs + NPCM_ADCCON); 1099bf85fbcSTomer Maimon if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) { 1109bf85fbcSTomer Maimon /* if conversion failed - reset ADC module */ 1119bf85fbcSTomer Maimon regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET, 1129bf85fbcSTomer Maimon NPCM7XX_IPSRST1_ADC_RST); 1139bf85fbcSTomer Maimon msleep(100); 1149bf85fbcSTomer Maimon regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET, 1159bf85fbcSTomer Maimon 0x0); 1169bf85fbcSTomer Maimon msleep(100); 1179bf85fbcSTomer Maimon 1189bf85fbcSTomer Maimon /* Enable ADC and start conversion module */ 1199bf85fbcSTomer Maimon iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV, 1209bf85fbcSTomer Maimon info->regs + NPCM_ADCCON); 1219bf85fbcSTomer Maimon dev_err(info->dev, "RESET ADC Complete\n"); 1229bf85fbcSTomer Maimon } 1239bf85fbcSTomer Maimon return -ETIMEDOUT; 1249bf85fbcSTomer Maimon } 1259bf85fbcSTomer Maimon if (ret < 0) 1269bf85fbcSTomer Maimon return ret; 1279bf85fbcSTomer Maimon 1289bf85fbcSTomer Maimon *val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA)); 1299bf85fbcSTomer Maimon 1309bf85fbcSTomer Maimon return 0; 1319bf85fbcSTomer Maimon } 1329bf85fbcSTomer Maimon 1339bf85fbcSTomer Maimon static int npcm_adc_read_raw(struct iio_dev *indio_dev, 1349bf85fbcSTomer Maimon struct iio_chan_spec const *chan, int *val, 1359bf85fbcSTomer Maimon int *val2, long mask) 1369bf85fbcSTomer Maimon { 1379bf85fbcSTomer Maimon int ret; 1389bf85fbcSTomer Maimon int vref_uv; 1399bf85fbcSTomer Maimon struct npcm_adc *info = iio_priv(indio_dev); 1409bf85fbcSTomer Maimon 1419bf85fbcSTomer Maimon switch (mask) { 1429bf85fbcSTomer Maimon case IIO_CHAN_INFO_RAW: 1439bf85fbcSTomer Maimon mutex_lock(&indio_dev->mlock); 1449bf85fbcSTomer Maimon ret = npcm_adc_read(info, val, chan->channel); 1459bf85fbcSTomer Maimon mutex_unlock(&indio_dev->mlock); 1469bf85fbcSTomer Maimon if (ret) { 1479bf85fbcSTomer Maimon dev_err(info->dev, "NPCM ADC read failed\n"); 1489bf85fbcSTomer Maimon return ret; 1499bf85fbcSTomer Maimon } 1509bf85fbcSTomer Maimon return IIO_VAL_INT; 1519bf85fbcSTomer Maimon case IIO_CHAN_INFO_SCALE: 152*4e63ed6bSTomer Maimon if (!IS_ERR(info->vref)) { 1539bf85fbcSTomer Maimon vref_uv = regulator_get_voltage(info->vref); 1549bf85fbcSTomer Maimon *val = vref_uv / 1000; 1559bf85fbcSTomer Maimon } else { 1569bf85fbcSTomer Maimon *val = NPCM_INT_VREF_MV; 1579bf85fbcSTomer Maimon } 1589bf85fbcSTomer Maimon *val2 = NPCM_RESOLUTION_BITS; 1599bf85fbcSTomer Maimon return IIO_VAL_FRACTIONAL_LOG2; 1609bf85fbcSTomer Maimon case IIO_CHAN_INFO_SAMP_FREQ: 1619bf85fbcSTomer Maimon *val = info->adc_sample_hz; 1629bf85fbcSTomer Maimon return IIO_VAL_INT; 1639bf85fbcSTomer Maimon default: 1649bf85fbcSTomer Maimon return -EINVAL; 1659bf85fbcSTomer Maimon } 1669bf85fbcSTomer Maimon 1679bf85fbcSTomer Maimon return 0; 1689bf85fbcSTomer Maimon } 1699bf85fbcSTomer Maimon 1709bf85fbcSTomer Maimon static const struct iio_info npcm_adc_iio_info = { 1719bf85fbcSTomer Maimon .read_raw = &npcm_adc_read_raw, 1729bf85fbcSTomer Maimon }; 1739bf85fbcSTomer Maimon 1749bf85fbcSTomer Maimon static const struct of_device_id npcm_adc_match[] = { 1759bf85fbcSTomer Maimon { .compatible = "nuvoton,npcm750-adc", }, 1769bf85fbcSTomer Maimon { /* sentinel */ } 1779bf85fbcSTomer Maimon }; 1789bf85fbcSTomer Maimon MODULE_DEVICE_TABLE(of, npcm_adc_match); 1799bf85fbcSTomer Maimon 1809bf85fbcSTomer Maimon static int npcm_adc_probe(struct platform_device *pdev) 1819bf85fbcSTomer Maimon { 1829bf85fbcSTomer Maimon int ret; 1839bf85fbcSTomer Maimon int irq; 1849bf85fbcSTomer Maimon u32 div; 1859bf85fbcSTomer Maimon u32 reg_con; 1869bf85fbcSTomer Maimon struct resource *res; 1879bf85fbcSTomer Maimon struct npcm_adc *info; 1889bf85fbcSTomer Maimon struct iio_dev *indio_dev; 1899bf85fbcSTomer Maimon struct device *dev = &pdev->dev; 1909bf85fbcSTomer Maimon struct device_node *np = pdev->dev.of_node; 1919bf85fbcSTomer Maimon 1929bf85fbcSTomer Maimon indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); 1939bf85fbcSTomer Maimon if (!indio_dev) 1949bf85fbcSTomer Maimon return -ENOMEM; 1959bf85fbcSTomer Maimon info = iio_priv(indio_dev); 1969bf85fbcSTomer Maimon 1979bf85fbcSTomer Maimon info->dev = &pdev->dev; 1989bf85fbcSTomer Maimon 1999bf85fbcSTomer Maimon res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2009bf85fbcSTomer Maimon info->regs = devm_ioremap_resource(&pdev->dev, res); 2019bf85fbcSTomer Maimon if (IS_ERR(info->regs)) 2029bf85fbcSTomer Maimon return PTR_ERR(info->regs); 2039bf85fbcSTomer Maimon 2049bf85fbcSTomer Maimon info->adc_clk = devm_clk_get(&pdev->dev, NULL); 2059bf85fbcSTomer Maimon if (IS_ERR(info->adc_clk)) { 2069bf85fbcSTomer Maimon dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n"); 2079bf85fbcSTomer Maimon return PTR_ERR(info->adc_clk); 2089bf85fbcSTomer Maimon } 2099bf85fbcSTomer Maimon 2109bf85fbcSTomer Maimon /* calculate ADC clock sample rate */ 2119bf85fbcSTomer Maimon reg_con = ioread32(info->regs + NPCM_ADCCON); 2129bf85fbcSTomer Maimon div = reg_con & NPCM_ADCCON_DIV_MASK; 2139bf85fbcSTomer Maimon div = div >> NPCM_ADCCON_DIV_SHIFT; 2149bf85fbcSTomer Maimon info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); 2159bf85fbcSTomer Maimon 2169bf85fbcSTomer Maimon if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) { 2179bf85fbcSTomer Maimon info->rst_regmap = syscon_regmap_lookup_by_compatible 2189bf85fbcSTomer Maimon ("nuvoton,npcm750-rst"); 2199bf85fbcSTomer Maimon if (IS_ERR(info->rst_regmap)) { 2209bf85fbcSTomer Maimon dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n"); 2219bf85fbcSTomer Maimon ret = PTR_ERR(info->rst_regmap); 2229bf85fbcSTomer Maimon goto err_disable_clk; 2239bf85fbcSTomer Maimon } 2249bf85fbcSTomer Maimon } 2259bf85fbcSTomer Maimon 2269bf85fbcSTomer Maimon irq = platform_get_irq(pdev, 0); 2279bf85fbcSTomer Maimon if (irq <= 0) { 2289bf85fbcSTomer Maimon dev_err(dev, "failed getting interrupt resource\n"); 2299bf85fbcSTomer Maimon ret = -EINVAL; 2309bf85fbcSTomer Maimon goto err_disable_clk; 2319bf85fbcSTomer Maimon } 2329bf85fbcSTomer Maimon 2339bf85fbcSTomer Maimon ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0, 2349bf85fbcSTomer Maimon "NPCM_ADC", indio_dev); 2359bf85fbcSTomer Maimon if (ret < 0) { 2369bf85fbcSTomer Maimon dev_err(dev, "failed requesting interrupt\n"); 2379bf85fbcSTomer Maimon goto err_disable_clk; 2389bf85fbcSTomer Maimon } 2399bf85fbcSTomer Maimon 2409bf85fbcSTomer Maimon reg_con = ioread32(info->regs + NPCM_ADCCON); 2419bf85fbcSTomer Maimon info->vref = devm_regulator_get_optional(&pdev->dev, "vref"); 2429bf85fbcSTomer Maimon if (!IS_ERR(info->vref)) { 2439bf85fbcSTomer Maimon ret = regulator_enable(info->vref); 2449bf85fbcSTomer Maimon if (ret) { 2459bf85fbcSTomer Maimon dev_err(&pdev->dev, "Can't enable ADC reference voltage\n"); 2469bf85fbcSTomer Maimon goto err_disable_clk; 2479bf85fbcSTomer Maimon } 2489bf85fbcSTomer Maimon 2499bf85fbcSTomer Maimon iowrite32(reg_con & ~NPCM_ADCCON_REFSEL, 2509bf85fbcSTomer Maimon info->regs + NPCM_ADCCON); 2519bf85fbcSTomer Maimon } else { 2529bf85fbcSTomer Maimon /* 2539bf85fbcSTomer Maimon * Any error which is not ENODEV indicates the regulator 2549bf85fbcSTomer Maimon * has been specified and so is a failure case. 2559bf85fbcSTomer Maimon */ 2569bf85fbcSTomer Maimon if (PTR_ERR(info->vref) != -ENODEV) { 2579bf85fbcSTomer Maimon ret = PTR_ERR(info->vref); 2589bf85fbcSTomer Maimon goto err_disable_clk; 2599bf85fbcSTomer Maimon } 2609bf85fbcSTomer Maimon 2619bf85fbcSTomer Maimon /* Use internal reference */ 2629bf85fbcSTomer Maimon iowrite32(reg_con | NPCM_ADCCON_REFSEL, 2639bf85fbcSTomer Maimon info->regs + NPCM_ADCCON); 2649bf85fbcSTomer Maimon } 2659bf85fbcSTomer Maimon 2669bf85fbcSTomer Maimon init_waitqueue_head(&info->wq); 2679bf85fbcSTomer Maimon 2689bf85fbcSTomer Maimon reg_con = ioread32(info->regs + NPCM_ADCCON); 2699bf85fbcSTomer Maimon reg_con |= NPCM_ADC_ENABLE; 2709bf85fbcSTomer Maimon 2719bf85fbcSTomer Maimon /* Enable the ADC Module */ 2729bf85fbcSTomer Maimon iowrite32(reg_con, info->regs + NPCM_ADCCON); 2739bf85fbcSTomer Maimon 2749bf85fbcSTomer Maimon /* Start ADC conversion */ 2759bf85fbcSTomer Maimon iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON); 2769bf85fbcSTomer Maimon 2779bf85fbcSTomer Maimon platform_set_drvdata(pdev, indio_dev); 2789bf85fbcSTomer Maimon indio_dev->name = dev_name(&pdev->dev); 2799bf85fbcSTomer Maimon indio_dev->dev.parent = &pdev->dev; 2809bf85fbcSTomer Maimon indio_dev->info = &npcm_adc_iio_info; 2819bf85fbcSTomer Maimon indio_dev->modes = INDIO_DIRECT_MODE; 2829bf85fbcSTomer Maimon indio_dev->channels = npcm_adc_iio_channels; 2839bf85fbcSTomer Maimon indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels); 2849bf85fbcSTomer Maimon 2859bf85fbcSTomer Maimon ret = iio_device_register(indio_dev); 2869bf85fbcSTomer Maimon if (ret) { 2879bf85fbcSTomer Maimon dev_err(&pdev->dev, "Couldn't register the device.\n"); 2889bf85fbcSTomer Maimon goto err_iio_register; 2899bf85fbcSTomer Maimon } 2909bf85fbcSTomer Maimon 2919bf85fbcSTomer Maimon pr_info("NPCM ADC driver probed\n"); 2929bf85fbcSTomer Maimon 2939bf85fbcSTomer Maimon return 0; 2949bf85fbcSTomer Maimon 2959bf85fbcSTomer Maimon err_iio_register: 2969bf85fbcSTomer Maimon iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); 2979bf85fbcSTomer Maimon if (!IS_ERR(info->vref)) 2989bf85fbcSTomer Maimon regulator_disable(info->vref); 2999bf85fbcSTomer Maimon err_disable_clk: 3009bf85fbcSTomer Maimon clk_disable_unprepare(info->adc_clk); 3019bf85fbcSTomer Maimon 3029bf85fbcSTomer Maimon return ret; 3039bf85fbcSTomer Maimon } 3049bf85fbcSTomer Maimon 3059bf85fbcSTomer Maimon static int npcm_adc_remove(struct platform_device *pdev) 3069bf85fbcSTomer Maimon { 3079bf85fbcSTomer Maimon struct iio_dev *indio_dev = platform_get_drvdata(pdev); 3089bf85fbcSTomer Maimon struct npcm_adc *info = iio_priv(indio_dev); 3099bf85fbcSTomer Maimon u32 regtemp; 3109bf85fbcSTomer Maimon 3119bf85fbcSTomer Maimon iio_device_unregister(indio_dev); 3129bf85fbcSTomer Maimon 3139bf85fbcSTomer Maimon regtemp = ioread32(info->regs + NPCM_ADCCON); 3149bf85fbcSTomer Maimon iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); 3159bf85fbcSTomer Maimon if (!IS_ERR(info->vref)) 3169bf85fbcSTomer Maimon regulator_disable(info->vref); 3179bf85fbcSTomer Maimon clk_disable_unprepare(info->adc_clk); 3189bf85fbcSTomer Maimon 3199bf85fbcSTomer Maimon return 0; 3209bf85fbcSTomer Maimon } 3219bf85fbcSTomer Maimon 3229bf85fbcSTomer Maimon static struct platform_driver npcm_adc_driver = { 3239bf85fbcSTomer Maimon .probe = npcm_adc_probe, 3249bf85fbcSTomer Maimon .remove = npcm_adc_remove, 3259bf85fbcSTomer Maimon .driver = { 3269bf85fbcSTomer Maimon .name = "npcm_adc", 3279bf85fbcSTomer Maimon .of_match_table = npcm_adc_match, 3289bf85fbcSTomer Maimon }, 3299bf85fbcSTomer Maimon }; 3309bf85fbcSTomer Maimon 3319bf85fbcSTomer Maimon module_platform_driver(npcm_adc_driver); 3329bf85fbcSTomer Maimon 3339bf85fbcSTomer Maimon MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver"); 3349bf85fbcSTomer Maimon MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>"); 3359bf85fbcSTomer Maimon MODULE_LICENSE("GPL v2"); 336