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