brintos

brintos / linux-shallow public Read only

0
0
Text · 5.7 KiB · ab55906 Raw
256 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* NXP PCF50633 ADC Driver3 *4 * (C) 2006-2008 by Openmoko, Inc.5 * Author: Balaji Rao <balajirrao@openmoko.org>6 * All rights reserved.7 *8 * Broken down from monstrous PCF50633 driver mainly by9 * Harald Welte, Andy Green and Werner Almesberger10 *11 *  NOTE: This driver does not yet support subtractive ADC mode, which means12 *  you can do only one measurement per read request.13 */14 15#include <linux/kernel.h>16#include <linux/slab.h>17#include <linux/module.h>18#include <linux/device.h>19#include <linux/platform_device.h>20#include <linux/completion.h>21 22#include <linux/mfd/pcf50633/core.h>23#include <linux/mfd/pcf50633/adc.h>24 25struct pcf50633_adc_request {26	int mux;27	int avg;28	void (*callback)(struct pcf50633 *, void *, int);29	void *callback_param;30};31 32struct pcf50633_adc_sync_request {33	int result;34	struct completion completion;35};36 37#define PCF50633_MAX_ADC_FIFO_DEPTH 838 39struct pcf50633_adc {40	struct pcf50633 *pcf;41 42	/* Private stuff */43	struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH];44	int queue_head;45	int queue_tail;46	struct mutex queue_mutex;47};48 49static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf)50{51	return platform_get_drvdata(pcf->adc_pdev);52}53 54static void adc_setup(struct pcf50633 *pcf, int channel, int avg)55{56	channel &= PCF50633_ADCC1_ADCMUX_MASK;57 58	/* kill ratiometric, but enable ACCSW biasing */59	pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);60	pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);61 62	/* start ADC conversion on selected channel */63	pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |64		    PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT);65}66 67static void trigger_next_adc_job_if_any(struct pcf50633 *pcf)68{69	struct pcf50633_adc *adc = __to_adc(pcf);70	int head;71 72	head = adc->queue_head;73 74	if (!adc->queue[head])75		return;76 77	adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg);78}79 80static int81adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)82{83	struct pcf50633_adc *adc = __to_adc(pcf);84	int head, tail;85 86	mutex_lock(&adc->queue_mutex);87 88	head = adc->queue_head;89	tail = adc->queue_tail;90 91	if (adc->queue[tail]) {92		mutex_unlock(&adc->queue_mutex);93		dev_err(pcf->dev, "ADC queue is full, dropping request\n");94		return -EBUSY;95	}96 97	adc->queue[tail] = req;98	if (head == tail)99		trigger_next_adc_job_if_any(pcf);100	adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1);101 102	mutex_unlock(&adc->queue_mutex);103 104	return 0;105}106 107static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,108	int result)109{110	struct pcf50633_adc_sync_request *req = param;111 112	req->result = result;113	complete(&req->completion);114}115 116int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)117{118	struct pcf50633_adc_sync_request req;119	int ret;120 121	init_completion(&req.completion);122 123	ret = pcf50633_adc_async_read(pcf, mux, avg,124		pcf50633_adc_sync_read_callback, &req);125	if (ret)126		return ret;127 128	wait_for_completion(&req.completion);129 130	return req.result;131}132EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);133 134int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,135			     void (*callback)(struct pcf50633 *, void *, int),136			     void *callback_param)137{138	struct pcf50633_adc_request *req;139	int ret;140 141	/* req is freed when the result is ready, in interrupt handler */142	req = kmalloc(sizeof(*req), GFP_KERNEL);143	if (!req)144		return -ENOMEM;145 146	req->mux = mux;147	req->avg = avg;148	req->callback = callback;149	req->callback_param = callback_param;150 151	ret = adc_enqueue_request(pcf, req);152	if (ret)153		kfree(req);154 155	return ret;156}157EXPORT_SYMBOL_GPL(pcf50633_adc_async_read);158 159static int adc_result(struct pcf50633 *pcf)160{161	u8 adcs1, adcs3;162	u16 result;163 164	adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1);165	adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3);166	result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK);167 168	dev_dbg(pcf->dev, "adc result = %d\n", result);169 170	return result;171}172 173static void pcf50633_adc_irq(int irq, void *data)174{175	struct pcf50633_adc *adc = data;176	struct pcf50633 *pcf = adc->pcf;177	struct pcf50633_adc_request *req;178	int head, res;179 180	mutex_lock(&adc->queue_mutex);181	head = adc->queue_head;182 183	req = adc->queue[head];184	if (WARN_ON(!req)) {185		dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n");186		mutex_unlock(&adc->queue_mutex);187		return;188	}189	adc->queue[head] = NULL;190	adc->queue_head = (head + 1) &191				      (PCF50633_MAX_ADC_FIFO_DEPTH - 1);192 193	res = adc_result(pcf);194	trigger_next_adc_job_if_any(pcf);195 196	mutex_unlock(&adc->queue_mutex);197 198	req->callback(pcf, req->callback_param, res);199	kfree(req);200}201 202static int pcf50633_adc_probe(struct platform_device *pdev)203{204	struct pcf50633_adc *adc;205 206	adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);207	if (!adc)208		return -ENOMEM;209 210	adc->pcf = dev_to_pcf50633(pdev->dev.parent);211	platform_set_drvdata(pdev, adc);212 213	pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY,214					pcf50633_adc_irq, adc);215 216	mutex_init(&adc->queue_mutex);217 218	return 0;219}220 221static void pcf50633_adc_remove(struct platform_device *pdev)222{223	struct pcf50633_adc *adc = platform_get_drvdata(pdev);224	int i, head;225 226	pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY);227 228	mutex_lock(&adc->queue_mutex);229	head = adc->queue_head;230 231	if (WARN_ON(adc->queue[head]))232		dev_err(adc->pcf->dev,233			"adc driver removed with request pending\n");234 235	for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++)236		kfree(adc->queue[i]);237 238	mutex_unlock(&adc->queue_mutex);239}240 241static struct platform_driver pcf50633_adc_driver = {242	.driver = {243		.name = "pcf50633-adc",244	},245	.probe = pcf50633_adc_probe,246	.remove_new = pcf50633_adc_remove,247};248 249module_platform_driver(pcf50633_adc_driver);250 251MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");252MODULE_DESCRIPTION("PCF50633 adc driver");253MODULE_LICENSE("GPL");254MODULE_ALIAS("platform:pcf50633-adc");255 256