Blame view

drivers/mfd/pcf50633-adc.c 5.87 KB
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /* NXP PCF50633 ADC Driver
   *
   * (C) 2006-2008 by Openmoko, Inc.
   * Author: Balaji Rao <balajirrao@openmoko.org>
   * All rights reserved.
   *
   * Broken down from monstrous PCF50633 driver mainly by
   * Harald Welte, Andy Green and Werner Almesberger
   *
   *  This program is free software; you can redistribute  it and/or modify it
   *  under  the terms of  the GNU General  Public License as published by the
   *  Free Software Foundation;  either version 2 of the  License, or (at your
   *  option) any later version.
   *
   *  NOTE: This driver does not yet support subtractive ADC mode, which means
   *  you can do only one measurement per read request.
   */
  
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
20
  #include <linux/slab.h>
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
21
  #include <linux/module.h>
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
22
23
24
25
26
27
28
29
30
31
  #include <linux/device.h>
  #include <linux/platform_device.h>
  #include <linux/completion.h>
  
  #include <linux/mfd/pcf50633/core.h>
  #include <linux/mfd/pcf50633/adc.h>
  
  struct pcf50633_adc_request {
  	int mux;
  	int avg;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
32
33
  	void (*callback)(struct pcf50633 *, void *, int);
  	void *callback_param;
6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
34
  };
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
35

6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
36
37
  struct pcf50633_adc_sync_request {
  	int result;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
38
  	struct completion completion;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  };
  
  #define PCF50633_MAX_ADC_FIFO_DEPTH 8
  
  struct pcf50633_adc {
  	struct pcf50633 *pcf;
  
  	/* Private stuff */
  	struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH];
  	int queue_head;
  	int queue_tail;
  	struct mutex queue_mutex;
  };
  
  static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf)
  {
  	return platform_get_drvdata(pcf->adc_pdev);
  }
  
  static void adc_setup(struct pcf50633 *pcf, int channel, int avg)
  {
  	channel &= PCF50633_ADCC1_ADCMUX_MASK;
  
  	/* kill ratiometric, but enable ACCSW biasing */
  	pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00);
  	pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01);
  
  	/* start ADC conversion on selected channel */
  	pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
  		    PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT);
  }
  
  static void trigger_next_adc_job_if_any(struct pcf50633 *pcf)
  {
  	struct pcf50633_adc *adc = __to_adc(pcf);
  	int head;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
75
  	head = adc->queue_head;
bd8ef1026   Paul Fertser   mfd: revise locki...
76
  	if (!adc->queue[head])
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
77
  		return;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  
  	adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg);
  }
  
  static int
  adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
  {
  	struct pcf50633_adc *adc = __to_adc(pcf);
  	int head, tail;
  
  	mutex_lock(&adc->queue_mutex);
  
  	head = adc->queue_head;
  	tail = adc->queue_tail;
  
  	if (adc->queue[tail]) {
  		mutex_unlock(&adc->queue_mutex);
bd8ef1026   Paul Fertser   mfd: revise locki...
95
96
  		dev_err(pcf->dev, "ADC queue is full, dropping request
  ");
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
97
98
99
100
  		return -EBUSY;
  	}
  
  	adc->queue[tail] = req;
bd8ef1026   Paul Fertser   mfd: revise locki...
101
102
  	if (head == tail)
  		trigger_next_adc_job_if_any(pcf);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
103
104
105
  	adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
  
  	mutex_unlock(&adc->queue_mutex);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
106
107
  	return 0;
  }
6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
108
109
  static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,
  	int result)
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
110
  {
6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
111
  	struct pcf50633_adc_sync_request *req = param;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
112
113
114
115
116
117
118
  
  	req->result = result;
  	complete(&req->completion);
  }
  
  int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
  {
6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
119
120
  	struct pcf50633_adc_sync_request req;
  	int ret;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
121

6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
122
  	init_completion(&req.completion);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
123

6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
124
125
126
127
  	ret = pcf50633_adc_async_read(pcf, mux, avg,
  		pcf50633_adc_sync_read_callback, &req);
  	if (ret)
  		return ret;
bd8ef1026   Paul Fertser   mfd: revise locki...
128

6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
129
  	wait_for_completion(&req.completion);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
130

6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
131
  	return req.result;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  }
  EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
  
  int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
  			     void (*callback)(struct pcf50633 *, void *, int),
  			     void *callback_param)
  {
  	struct pcf50633_adc_request *req;
  
  	/* req is freed when the result is ready, in interrupt handler */
  	req = kmalloc(sizeof(*req), GFP_KERNEL);
  	if (!req)
  		return -ENOMEM;
  
  	req->mux = mux;
  	req->avg = avg;
  	req->callback = callback;
  	req->callback_param = callback_param;
bd8ef1026   Paul Fertser   mfd: revise locki...
150
  	return adc_enqueue_request(pcf, req);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  }
  EXPORT_SYMBOL_GPL(pcf50633_adc_async_read);
  
  static int adc_result(struct pcf50633 *pcf)
  {
  	u8 adcs1, adcs3;
  	u16 result;
  
  	adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1);
  	adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3);
  	result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK);
  
  	dev_dbg(pcf->dev, "adc result = %d
  ", result);
  
  	return result;
  }
  
  static void pcf50633_adc_irq(int irq, void *data)
  {
  	struct pcf50633_adc *adc = data;
  	struct pcf50633 *pcf = adc->pcf;
  	struct pcf50633_adc_request *req;
bd8ef1026   Paul Fertser   mfd: revise locki...
174
  	int head, res;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  
  	mutex_lock(&adc->queue_mutex);
  	head = adc->queue_head;
  
  	req = adc->queue[head];
  	if (WARN_ON(!req)) {
  		dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!
  ");
  		mutex_unlock(&adc->queue_mutex);
  		return;
  	}
  	adc->queue[head] = NULL;
  	adc->queue_head = (head + 1) &
  				      (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
bd8ef1026   Paul Fertser   mfd: revise locki...
189
190
  	res = adc_result(pcf);
  	trigger_next_adc_job_if_any(pcf);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
191
  	mutex_unlock(&adc->queue_mutex);
bd8ef1026   Paul Fertser   mfd: revise locki...
192
  	req->callback(pcf, req->callback_param, res);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
193
  	kfree(req);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
194
  }
f791be492   Bill Pemberton   mfd: remove use o...
195
  static int pcf50633_adc_probe(struct platform_device *pdev)
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
196
  {
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
197
  	struct pcf50633_adc *adc;
8a105ca20   Jingoo Han   mfd: pcf50633-adc...
198
  	adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
199
200
  	if (!adc)
  		return -ENOMEM;
68d641efd   Lars-Peter Clausen   mfd: Fix memleak ...
201
  	adc->pcf = dev_to_pcf50633(pdev->dev.parent);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
202
  	platform_set_drvdata(pdev, adc);
68d641efd   Lars-Peter Clausen   mfd: Fix memleak ...
203
  	pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY,
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
204
205
206
207
208
209
  					pcf50633_adc_irq, adc);
  
  	mutex_init(&adc->queue_mutex);
  
  	return 0;
  }
4740f73fe   Bill Pemberton   mfd: remove use o...
210
  static int pcf50633_adc_remove(struct platform_device *pdev)
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  {
  	struct pcf50633_adc *adc = platform_get_drvdata(pdev);
  	int i, head;
  
  	pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY);
  
  	mutex_lock(&adc->queue_mutex);
  	head = adc->queue_head;
  
  	if (WARN_ON(adc->queue[head]))
  		dev_err(adc->pcf->dev,
  			"adc driver removed with request pending
  ");
  
  	for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++)
  		kfree(adc->queue[i]);
  
  	mutex_unlock(&adc->queue_mutex);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
229
230
231
232
233
234
235
236
237
  
  	return 0;
  }
  
  static struct platform_driver pcf50633_adc_driver = {
  	.driver = {
  		.name = "pcf50633-adc",
  	},
  	.probe = pcf50633_adc_probe,
84449216b   Bill Pemberton   mfd: remove use o...
238
  	.remove = pcf50633_adc_remove,
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
239
  };
65349d60d   Mark Brown   mfd: Convert MFD ...
240
  module_platform_driver(pcf50633_adc_driver);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
241
242
243
244
245
  
  MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
  MODULE_DESCRIPTION("PCF50633 adc driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:pcf50633-adc");