root/drivers/iio/adc/ingenic-adc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ingenic_adc_set_config
  2. ingenic_adc_enable
  3. ingenic_adc_capture
  4. ingenic_adc_write_raw
  5. jz4725b_adc_init_clk_div
  6. ingenic_adc_read_avail
  7. ingenic_adc_read_raw
  8. ingenic_adc_clk_cleanup
  9. ingenic_adc_probe

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

/* [<][>][^][v][top][bottom][index][help] */