Blame view

drivers/mfd/pcf50633-adc.c 5.92 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
22
23
24
25
26
27
28
29
30
31
32
  #include <linux/module.h>
  #include <linux/init.h>
  #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...
33
34
  	void (*callback)(struct pcf50633 *, void *, int);
  	void *callback_param;
6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
35
  };
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
36

6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
37
38
  struct pcf50633_adc_sync_request {
  	int result;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
39
  	struct completion completion;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
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
75
  };
  
  #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...
76
  	head = adc->queue_head;
bd8ef1026   Paul Fertser   mfd: revise locki...
77
  	if (!adc->queue[head])
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
78
  		return;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  
  	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...
96
97
  		dev_err(pcf->dev, "ADC queue is full, dropping request
  ");
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
98
99
100
101
  		return -EBUSY;
  	}
  
  	adc->queue[tail] = req;
bd8ef1026   Paul Fertser   mfd: revise locki...
102
103
  	if (head == tail)
  		trigger_next_adc_job_if_any(pcf);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
104
105
106
  	adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1);
  
  	mutex_unlock(&adc->queue_mutex);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
107
108
  	return 0;
  }
6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
109
110
  static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,
  	int result)
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
111
  {
6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
112
  	struct pcf50633_adc_sync_request *req = param;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
113
114
115
116
117
118
119
  
  	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...
120
121
  	struct pcf50633_adc_sync_request req;
  	int ret;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
122

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

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

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

6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
132
  	return req.result;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  }
  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...
151
  	return adc_enqueue_request(pcf, req);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
  }
  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...
175
  	int head, res;
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  
  	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...
190
191
  	res = adc_result(pcf);
  	trigger_next_adc_job_if_any(pcf);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
192
  	mutex_unlock(&adc->queue_mutex);
bd8ef1026   Paul Fertser   mfd: revise locki...
193
  	req->callback(pcf, req->callback_param, res);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
194
  	kfree(req);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
195
196
197
198
  }
  
  static int __devinit pcf50633_adc_probe(struct platform_device *pdev)
  {
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
199
200
201
202
203
  	struct pcf50633_adc *adc;
  
  	adc = kzalloc(sizeof(*adc), GFP_KERNEL);
  	if (!adc)
  		return -ENOMEM;
68d641efd   Lars-Peter Clausen   mfd: Fix memleak ...
204
  	adc->pcf = dev_to_pcf50633(pdev->dev.parent);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
205
  	platform_set_drvdata(pdev, adc);
68d641efd   Lars-Peter Clausen   mfd: Fix memleak ...
206
  	pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY,
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  					pcf50633_adc_irq, adc);
  
  	mutex_init(&adc->queue_mutex);
  
  	return 0;
  }
  
  static int __devexit pcf50633_adc_remove(struct platform_device *pdev)
  {
  	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);
  	kfree(adc);
  
  	return 0;
  }
  
  static struct platform_driver pcf50633_adc_driver = {
  	.driver = {
  		.name = "pcf50633-adc",
  	},
  	.probe = pcf50633_adc_probe,
  	.remove = __devexit_p(pcf50633_adc_remove),
  };
65349d60d   Mark Brown   mfd: Convert MFD ...
245
  module_platform_driver(pcf50633_adc_driver);
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
246
247
248
249
250
  
  MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
  MODULE_DESCRIPTION("PCF50633 adc driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:pcf50633-adc");