Blame view

drivers/mfd/pcf50633-adc.c 5.65 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
2
3
4
5
6
7
8
9
10
  /* 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
   *
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
11
12
13
14
15
   *  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: ...
16
  #include <linux/slab.h>
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
17
  #include <linux/module.h>
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
18
19
20
21
22
23
24
25
26
27
  #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...
28
29
  	void (*callback)(struct pcf50633 *, void *, int);
  	void *callback_param;
6438a694b   Lars-Peter Clausen   mfd: pcf50633-adc...
30
  };
08c3e06a5   Balaji Rao   mfd: PCF50633 adc...
31

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

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

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

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

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