linux/drivers/mfd/wm831x-auxadc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * wm831x-auxadc.c  --  AUXADC for Wolfson WM831x PMICs
   4 *
   5 * Copyright 2009-2011 Wolfson Microelectronics PLC.
   6 *
   7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/delay.h>
  13#include <linux/mfd/core.h>
  14#include <linux/slab.h>
  15#include <linux/list.h>
  16
  17#include <linux/mfd/wm831x/core.h>
  18#include <linux/mfd/wm831x/pdata.h>
  19#include <linux/mfd/wm831x/irq.h>
  20#include <linux/mfd/wm831x/auxadc.h>
  21#include <linux/mfd/wm831x/otp.h>
  22#include <linux/mfd/wm831x/regulator.h>
  23
  24struct wm831x_auxadc_req {
  25        struct list_head list;
  26        enum wm831x_auxadc input;
  27        int val;
  28        struct completion done;
  29};
  30
  31static int wm831x_auxadc_read_irq(struct wm831x *wm831x,
  32                                  enum wm831x_auxadc input)
  33{
  34        struct wm831x_auxadc_req *req;
  35        int ret;
  36        bool ena = false;
  37
  38        req = kzalloc(sizeof(*req), GFP_KERNEL);
  39        if (!req)
  40                return -ENOMEM;
  41
  42        init_completion(&req->done);
  43        req->input = input;
  44        req->val = -ETIMEDOUT;
  45
  46        mutex_lock(&wm831x->auxadc_lock);
  47
  48        /* Enqueue the request */
  49        list_add(&req->list, &wm831x->auxadc_pending);
  50
  51        ena = !wm831x->auxadc_active;
  52
  53        if (ena) {
  54                ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
  55                                      WM831X_AUX_ENA, WM831X_AUX_ENA);
  56                if (ret != 0) {
  57                        dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n",
  58                                ret);
  59                        goto out;
  60                }
  61        }
  62
  63        /* Enable the conversion if not already running */
  64        if (!(wm831x->auxadc_active & (1 << input))) {
  65                ret = wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
  66                                      1 << input, 1 << input);
  67                if (ret != 0) {
  68                        dev_err(wm831x->dev,
  69                                "Failed to set AUXADC source: %d\n", ret);
  70                        goto out;
  71                }
  72
  73                wm831x->auxadc_active |= 1 << input;
  74        }
  75
  76        /* We convert at the fastest rate possible */
  77        if (ena) {
  78                ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
  79                                      WM831X_AUX_CVT_ENA |
  80                                      WM831X_AUX_RATE_MASK,
  81                                      WM831X_AUX_CVT_ENA |
  82                                      WM831X_AUX_RATE_MASK);
  83                if (ret != 0) {
  84                        dev_err(wm831x->dev, "Failed to start AUXADC: %d\n",
  85                                ret);
  86                        goto out;
  87                }
  88        }
  89
  90        mutex_unlock(&wm831x->auxadc_lock);
  91
  92        /* Wait for an interrupt */
  93        wait_for_completion_timeout(&req->done, msecs_to_jiffies(500));
  94
  95        mutex_lock(&wm831x->auxadc_lock);
  96        ret = req->val;
  97
  98out:
  99        list_del(&req->list);
 100        mutex_unlock(&wm831x->auxadc_lock);
 101
 102        kfree(req);
 103
 104        return ret;
 105}
 106
 107static irqreturn_t wm831x_auxadc_irq(int irq, void *irq_data)
 108{
 109        struct wm831x *wm831x = irq_data;
 110        struct wm831x_auxadc_req *req;
 111        int ret, input, val;
 112
 113        ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
 114        if (ret < 0) {
 115                dev_err(wm831x->dev,
 116                        "Failed to read AUXADC data: %d\n", ret);
 117                return IRQ_NONE;
 118        }
 119
 120        input = ((ret & WM831X_AUX_DATA_SRC_MASK)
 121                 >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
 122
 123        if (input == 14)
 124                input = WM831X_AUX_CAL;
 125
 126        val = ret & WM831X_AUX_DATA_MASK;
 127
 128        mutex_lock(&wm831x->auxadc_lock);
 129
 130        /* Disable this conversion, we're about to complete all users */
 131        wm831x_set_bits(wm831x, WM831X_AUXADC_SOURCE,
 132                        1 << input, 0);
 133        wm831x->auxadc_active &= ~(1 << input);
 134
 135        /* Turn off the entire convertor if idle */
 136        if (!wm831x->auxadc_active)
 137                wm831x_reg_write(wm831x, WM831X_AUXADC_CONTROL, 0);
 138
 139        /* Wake up any threads waiting for this request */
 140        list_for_each_entry(req, &wm831x->auxadc_pending, list) {
 141                if (req->input == input) {
 142                        req->val = val;
 143                        complete(&req->done);
 144                }
 145        }
 146
 147        mutex_unlock(&wm831x->auxadc_lock);
 148
 149        return IRQ_HANDLED;
 150}
 151
 152static int wm831x_auxadc_read_polled(struct wm831x *wm831x,
 153                                     enum wm831x_auxadc input)
 154{
 155        int ret, src, timeout;
 156
 157        mutex_lock(&wm831x->auxadc_lock);
 158
 159        ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
 160                              WM831X_AUX_ENA, WM831X_AUX_ENA);
 161        if (ret < 0) {
 162                dev_err(wm831x->dev, "Failed to enable AUXADC: %d\n", ret);
 163                goto out;
 164        }
 165
 166        /* We force a single source at present */
 167        src = input;
 168        ret = wm831x_reg_write(wm831x, WM831X_AUXADC_SOURCE,
 169                               1 << src);
 170        if (ret < 0) {
 171                dev_err(wm831x->dev, "Failed to set AUXADC source: %d\n", ret);
 172                goto out;
 173        }
 174
 175        ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
 176                              WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
 177        if (ret < 0) {
 178                dev_err(wm831x->dev, "Failed to start AUXADC: %d\n", ret);
 179                goto disable;
 180        }
 181
 182        /* If we're not using interrupts then poll the
 183         * interrupt status register */
 184        timeout = 5;
 185        while (timeout) {
 186                msleep(1);
 187
 188                ret = wm831x_reg_read(wm831x,
 189                                      WM831X_INTERRUPT_STATUS_1);
 190                if (ret < 0) {
 191                        dev_err(wm831x->dev,
 192                                "ISR 1 read failed: %d\n", ret);
 193                        goto disable;
 194                }
 195
 196                /* Did it complete? */
 197                if (ret & WM831X_AUXADC_DATA_EINT) {
 198                        wm831x_reg_write(wm831x,
 199                                         WM831X_INTERRUPT_STATUS_1,
 200                                         WM831X_AUXADC_DATA_EINT);
 201                        break;
 202                } else {
 203                        dev_err(wm831x->dev,
 204                                "AUXADC conversion timeout\n");
 205                        ret = -EBUSY;
 206                        goto disable;
 207                }
 208        }
 209
 210        ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
 211        if (ret < 0) {
 212                dev_err(wm831x->dev,
 213                        "Failed to read AUXADC data: %d\n", ret);
 214                goto disable;
 215        }
 216
 217        src = ((ret & WM831X_AUX_DATA_SRC_MASK)
 218               >> WM831X_AUX_DATA_SRC_SHIFT) - 1;
 219
 220        if (src == 14)
 221                src = WM831X_AUX_CAL;
 222
 223        if (src != input) {
 224                dev_err(wm831x->dev, "Data from source %d not %d\n",
 225                        src, input);
 226                ret = -EINVAL;
 227        } else {
 228                ret &= WM831X_AUX_DATA_MASK;
 229        }
 230
 231disable:
 232        wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);
 233out:
 234        mutex_unlock(&wm831x->auxadc_lock);
 235        return ret;
 236}
 237
 238/**
 239 * wm831x_auxadc_read: Read a value from the WM831x AUXADC
 240 *
 241 * @wm831x: Device to read from.
 242 * @input: AUXADC input to read.
 243 */
 244int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
 245{
 246        return wm831x->auxadc_read(wm831x, input);
 247}
 248EXPORT_SYMBOL_GPL(wm831x_auxadc_read);
 249
 250/**
 251 * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC
 252 *
 253 * @wm831x: Device to read from.
 254 * @input: AUXADC input to read.
 255 */
 256int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)
 257{
 258        int ret;
 259
 260        ret = wm831x_auxadc_read(wm831x, input);
 261        if (ret < 0)
 262                return ret;
 263
 264        ret *= 1465;
 265
 266        return ret;
 267}
 268EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);
 269
 270void wm831x_auxadc_init(struct wm831x *wm831x)
 271{
 272        int ret;
 273
 274        mutex_init(&wm831x->auxadc_lock);
 275        INIT_LIST_HEAD(&wm831x->auxadc_pending);
 276
 277        if (wm831x->irq) {
 278                wm831x->auxadc_read = wm831x_auxadc_read_irq;
 279
 280                ret = request_threaded_irq(wm831x_irq(wm831x,
 281                                                      WM831X_IRQ_AUXADC_DATA),
 282                                           NULL, wm831x_auxadc_irq,
 283                                           IRQF_ONESHOT,
 284                                           "auxadc", wm831x);
 285                if (ret < 0) {
 286                        dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
 287                                ret);
 288                        wm831x->auxadc_read = NULL;
 289                }
 290        }
 291
 292        if (!wm831x->auxadc_read)
 293                wm831x->auxadc_read = wm831x_auxadc_read_polled;
 294}
 295