brintos

brintos / linux-shallow public Read only

0
0
Text · 6.3 KiB · 18618a8 Raw
288 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * wm831x-auxadc.c  --  AUXADC for Wolfson WM831x PMICs4 *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;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 read the interrupt status register */183	msleep(20);184 185	ret = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1);186	if (ret < 0) {187		dev_err(wm831x->dev,188			"ISR 1 read failed: %d\n", ret);189		goto disable;190	}191 192	/* Did it complete? */193	if (ret & WM831X_AUXADC_DATA_EINT) {194		wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1,195				WM831X_AUXADC_DATA_EINT);196	} else {197		dev_err(wm831x->dev,198			"AUXADC conversion timeout\n");199		ret = -EBUSY;200		goto disable;201	}202 203	ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);204	if (ret < 0) {205		dev_err(wm831x->dev,206			"Failed to read AUXADC data: %d\n", ret);207		goto disable;208	}209 210	src = ((ret & WM831X_AUX_DATA_SRC_MASK)211	       >> WM831X_AUX_DATA_SRC_SHIFT) - 1;212 213	if (src == 14)214		src = WM831X_AUX_CAL;215 216	if (src != input) {217		dev_err(wm831x->dev, "Data from source %d not %d\n",218			src, input);219		ret = -EINVAL;220	} else {221		ret &= WM831X_AUX_DATA_MASK;222	}223 224disable:225	wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL, WM831X_AUX_ENA, 0);226out:227	mutex_unlock(&wm831x->auxadc_lock);228	return ret;229}230 231/**232 * wm831x_auxadc_read: Read a value from the WM831x AUXADC233 *234 * @wm831x: Device to read from.235 * @input: AUXADC input to read.236 */237int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)238{239	return wm831x->auxadc_read(wm831x, input);240}241EXPORT_SYMBOL_GPL(wm831x_auxadc_read);242 243/**244 * wm831x_auxadc_read_uv: Read a voltage from the WM831x AUXADC245 *246 * @wm831x: Device to read from.247 * @input: AUXADC input to read.248 */249int wm831x_auxadc_read_uv(struct wm831x *wm831x, enum wm831x_auxadc input)250{251	int ret;252 253	ret = wm831x_auxadc_read(wm831x, input);254	if (ret < 0)255		return ret;256 257	ret *= 1465;258 259	return ret;260}261EXPORT_SYMBOL_GPL(wm831x_auxadc_read_uv);262 263void wm831x_auxadc_init(struct wm831x *wm831x)264{265	int ret;266 267	mutex_init(&wm831x->auxadc_lock);268	INIT_LIST_HEAD(&wm831x->auxadc_pending);269 270	if (wm831x->irq) {271		wm831x->auxadc_read = wm831x_auxadc_read_irq;272 273		ret = request_threaded_irq(wm831x_irq(wm831x,274						      WM831X_IRQ_AUXADC_DATA),275					   NULL, wm831x_auxadc_irq,276					   IRQF_ONESHOT,277					   "auxadc", wm831x);278		if (ret < 0) {279			dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",280				ret);281			wm831x->auxadc_read = NULL;282		}283	}284 285	if (!wm831x->auxadc_read)286		wm831x->auxadc_read = wm831x_auxadc_read_polled;287}288