linux/drivers/iio/adc/ingenic-adc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * ADC driver for the Ingenic JZ47xx SoCs
   4 * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>
   5 *
   6 * based on drivers/mfd/jz4740-adc.c
   7 */
   8
   9#include <dt-bindings/iio/adc/ingenic,adc.h>
  10#include <linux/clk.h>
  11#include <linux/iio/iio.h>
  12#include <linux/io.h>
  13#include <linux/iopoll.h>
  14#include <linux/module.h>
  15#include <linux/mutex.h>
  16#include <linux/platform_device.h>
  17
  18#define JZ_ADC_REG_ENABLE               0x00
  19#define JZ_ADC_REG_CFG                  0x04
  20#define JZ_ADC_REG_CTRL                 0x08
  21#define JZ_ADC_REG_STATUS               0x0c
  22#define JZ_ADC_REG_ADTCH                0x18
  23#define JZ_ADC_REG_ADBDAT               0x1c
  24#define JZ_ADC_REG_ADSDAT               0x20
  25
  26#define JZ_ADC_REG_CFG_BAT_MD           BIT(4)
  27
  28#define JZ_ADC_AUX_VREF                         3300
  29#define JZ_ADC_AUX_VREF_BITS                    12
  30#define JZ_ADC_BATTERY_LOW_VREF                 2500
  31#define JZ_ADC_BATTERY_LOW_VREF_BITS            12
  32#define JZ4725B_ADC_BATTERY_HIGH_VREF           7500
  33#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS      10
  34#define JZ4740_ADC_BATTERY_HIGH_VREF            (7500 * 0.986)
  35#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS       12
  36
  37struct ingenic_adc_soc_data {
  38        unsigned int battery_high_vref;
  39        unsigned int battery_high_vref_bits;
  40        const int *battery_raw_avail;
  41        size_t battery_raw_avail_size;
  42        const int *battery_scale_avail;
  43        size_t battery_scale_avail_size;
  44};
  45
  46struct ingenic_adc {
  47        void __iomem *base;
  48        struct clk *clk;
  49        struct mutex lock;
  50        const struct ingenic_adc_soc_data *soc_data;
  51        bool low_vref_mode;
  52};
  53
  54static void ingenic_adc_set_config(struct ingenic_adc *adc,
  55                                   uint32_t mask,
  56                                   uint32_t val)
  57{
  58        uint32_t cfg;
  59
  60        clk_enable(adc->clk);
  61        mutex_lock(&adc->lock);
  62
  63        cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask;
  64        cfg |= val;
  65        writel(cfg, adc->base + JZ_ADC_REG_CFG);
  66
  67        mutex_unlock(&adc->lock);
  68        clk_disable(adc->clk);
  69}
  70
  71static void ingenic_adc_enable(struct ingenic_adc *adc,
  72                               int engine,
  73                               bool enabled)
  74{
  75        u8 val;
  76
  77        mutex_lock(&adc->lock);
  78        val = readb(adc->base + JZ_ADC_REG_ENABLE);
  79
  80        if (enabled)
  81                val |= BIT(engine);
  82        else
  83                val &= ~BIT(engine);
  84
  85        writeb(val, adc->base + JZ_ADC_REG_ENABLE);
  86        mutex_unlock(&adc->lock);
  87}
  88
  89static int ingenic_adc_capture(struct ingenic_adc *adc,
  90                               int engine)
  91{
  92        u8 val;
  93        int ret;
  94
  95        ingenic_adc_enable(adc, engine, true);
  96        ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val,
  97                                 !(val & BIT(engine)), 250, 1000);
  98        if (ret)
  99                ingenic_adc_enable(adc, engine, false);
 100
 101        return ret;
 102}
 103
 104static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
 105                                 struct iio_chan_spec const *chan,
 106                                 int val,
 107                                 int val2,
 108                                 long m)
 109{
 110        struct ingenic_adc *adc = iio_priv(iio_dev);
 111
 112        switch (m) {
 113        case IIO_CHAN_INFO_SCALE:
 114                switch (chan->channel) {
 115                case INGENIC_ADC_BATTERY:
 116                        if (val > JZ_ADC_BATTERY_LOW_VREF) {
 117                                ingenic_adc_set_config(adc,
 118                                                       JZ_ADC_REG_CFG_BAT_MD,
 119                                                       0);
 120                                adc->low_vref_mode = false;
 121                        } else {
 122                                ingenic_adc_set_config(adc,
 123                                                       JZ_ADC_REG_CFG_BAT_MD,
 124                                                       JZ_ADC_REG_CFG_BAT_MD);
 125                                adc->low_vref_mode = true;
 126                        }
 127                        return 0;
 128                default:
 129                        return -EINVAL;
 130                }
 131        default:
 132                return -EINVAL;
 133        }
 134}
 135
 136static const int jz4725b_adc_battery_raw_avail[] = {
 137        0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
 138};
 139
 140static const int jz4725b_adc_battery_scale_avail[] = {
 141        JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
 142        JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
 143};
 144
 145static const int jz4740_adc_battery_raw_avail[] = {
 146        0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
 147};
 148
 149static const int jz4740_adc_battery_scale_avail[] = {
 150        JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
 151        JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
 152};
 153
 154static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
 155        .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF,
 156        .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
 157        .battery_raw_avail = jz4725b_adc_battery_raw_avail,
 158        .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
 159        .battery_scale_avail = jz4725b_adc_battery_scale_avail,
 160        .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
 161};
 162
 163static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
 164        .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF,
 165        .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
 166        .battery_raw_avail = jz4740_adc_battery_raw_avail,
 167        .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
 168        .battery_scale_avail = jz4740_adc_battery_scale_avail,
 169        .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
 170};
 171
 172static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
 173                                  struct iio_chan_spec const *chan,
 174                                  const int **vals,
 175                                  int *type,
 176                                  int *length,
 177                                  long m)
 178{
 179        struct ingenic_adc *adc = iio_priv(iio_dev);
 180
 181        switch (m) {
 182        case IIO_CHAN_INFO_RAW:
 183                *type = IIO_VAL_INT;
 184                *length = adc->soc_data->battery_raw_avail_size;
 185                *vals = adc->soc_data->battery_raw_avail;
 186                return IIO_AVAIL_RANGE;
 187        case IIO_CHAN_INFO_SCALE:
 188                *type = IIO_VAL_FRACTIONAL_LOG2;
 189                *length = adc->soc_data->battery_scale_avail_size;
 190                *vals = adc->soc_data->battery_scale_avail;
 191                return IIO_AVAIL_LIST;
 192        default:
 193                return -EINVAL;
 194        };
 195}
 196
 197static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
 198                                struct iio_chan_spec const *chan,
 199                                int *val,
 200                                int *val2,
 201                                long m)
 202{
 203        struct ingenic_adc *adc = iio_priv(iio_dev);
 204        int ret;
 205
 206        switch (m) {
 207        case IIO_CHAN_INFO_RAW:
 208                clk_enable(adc->clk);
 209                ret = ingenic_adc_capture(adc, chan->channel);
 210                if (ret) {
 211                        clk_disable(adc->clk);
 212                        return ret;
 213                }
 214
 215                switch (chan->channel) {
 216                case INGENIC_ADC_AUX:
 217                        *val = readw(adc->base + JZ_ADC_REG_ADSDAT);
 218                        break;
 219                case INGENIC_ADC_BATTERY:
 220                        *val = readw(adc->base + JZ_ADC_REG_ADBDAT);
 221                        break;
 222                }
 223
 224                clk_disable(adc->clk);
 225
 226                return IIO_VAL_INT;
 227        case IIO_CHAN_INFO_SCALE:
 228                switch (chan->channel) {
 229                case INGENIC_ADC_AUX:
 230                        *val = JZ_ADC_AUX_VREF;
 231                        *val2 = JZ_ADC_AUX_VREF_BITS;
 232                        break;
 233                case INGENIC_ADC_BATTERY:
 234                        if (adc->low_vref_mode) {
 235                                *val = JZ_ADC_BATTERY_LOW_VREF;
 236                                *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS;
 237                        } else {
 238                                *val = adc->soc_data->battery_high_vref;
 239                                *val2 = adc->soc_data->battery_high_vref_bits;
 240                        }
 241                        break;
 242                }
 243
 244                return IIO_VAL_FRACTIONAL_LOG2;
 245        default:
 246                return -EINVAL;
 247        }
 248}
 249
 250static void ingenic_adc_clk_cleanup(void *data)
 251{
 252        clk_unprepare(data);
 253}
 254
 255static const struct iio_info ingenic_adc_info = {
 256        .write_raw = ingenic_adc_write_raw,
 257        .read_raw = ingenic_adc_read_raw,
 258        .read_avail = ingenic_adc_read_avail,
 259};
 260
 261static const struct iio_chan_spec ingenic_channels[] = {
 262        {
 263                .extend_name = "aux",
 264                .type = IIO_VOLTAGE,
 265                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 266                                      BIT(IIO_CHAN_INFO_SCALE),
 267                .indexed = 1,
 268                .channel = INGENIC_ADC_AUX,
 269        },
 270        {
 271                .extend_name = "battery",
 272                .type = IIO_VOLTAGE,
 273                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 274                                      BIT(IIO_CHAN_INFO_SCALE),
 275                .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
 276                                                BIT(IIO_CHAN_INFO_SCALE),
 277                .indexed = 1,
 278                .channel = INGENIC_ADC_BATTERY,
 279        },
 280};
 281
 282static int ingenic_adc_probe(struct platform_device *pdev)
 283{
 284        struct device *dev = &pdev->dev;
 285        struct iio_dev *iio_dev;
 286        struct ingenic_adc *adc;
 287        struct resource *mem_base;
 288        const struct ingenic_adc_soc_data *soc_data;
 289        int ret;
 290
 291        soc_data = device_get_match_data(dev);
 292        if (!soc_data)
 293                return -EINVAL;
 294
 295        iio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
 296        if (!iio_dev)
 297                return -ENOMEM;
 298
 299        adc = iio_priv(iio_dev);
 300        mutex_init(&adc->lock);
 301        adc->soc_data = soc_data;
 302
 303        mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 304        adc->base = devm_ioremap_resource(dev, mem_base);
 305        if (IS_ERR(adc->base)) {
 306                dev_err(dev, "Unable to ioremap mmio resource\n");
 307                return PTR_ERR(adc->base);
 308        }
 309
 310        adc->clk = devm_clk_get(dev, "adc");
 311        if (IS_ERR(adc->clk)) {
 312                dev_err(dev, "Unable to get clock\n");
 313                return PTR_ERR(adc->clk);
 314        }
 315
 316        ret = clk_prepare_enable(adc->clk);
 317        if (ret) {
 318                dev_err(dev, "Failed to enable clock\n");
 319                return ret;
 320        }
 321
 322        /* Put hardware in a known passive state. */
 323        writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
 324        writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
 325        clk_disable(adc->clk);
 326
 327        ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk);
 328        if (ret) {
 329                dev_err(dev, "Unable to add action\n");
 330                return ret;
 331        }
 332
 333        iio_dev->dev.parent = dev;
 334        iio_dev->name = "jz-adc";
 335        iio_dev->modes = INDIO_DIRECT_MODE;
 336        iio_dev->channels = ingenic_channels;
 337        iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
 338        iio_dev->info = &ingenic_adc_info;
 339
 340        ret = devm_iio_device_register(dev, iio_dev);
 341        if (ret)
 342                dev_err(dev, "Unable to register IIO device\n");
 343
 344        return ret;
 345}
 346
 347#ifdef CONFIG_OF
 348static const struct of_device_id ingenic_adc_of_match[] = {
 349        { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
 350        { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
 351        { },
 352};
 353MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
 354#endif
 355
 356static struct platform_driver ingenic_adc_driver = {
 357        .driver = {
 358                .name = "ingenic-adc",
 359                .of_match_table = of_match_ptr(ingenic_adc_of_match),
 360        },
 361        .probe = ingenic_adc_probe,
 362};
 363module_platform_driver(ingenic_adc_driver);
 364MODULE_LICENSE("GPL v2");
 365