Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: PCF50633 adc driver

This patch adds basic support for the PCF50633 ADC. The subtractive mode
is not supported yet.

Since we don't have adc subsystem, it currently lives in drivers/mfd.

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Acked-by: Jonathan Cameron <jonathan.cameron@gmail.com>
Signed-off-by: Samuel Ortiz <sameo@openedhand.com>

authored by

Balaji Rao and committed by
Samuel Ortiz
08c3e06a f52046b1

+357
+7
drivers/mfd/Kconfig
··· 226 226 facilities, and registers devices for the various functions 227 227 so that function-specific drivers can bind to them. 228 228 229 + config PCF50633_ADC 230 + tristate "Support for NXP PCF50633 ADC" 231 + depends on MFD_PCF50633 232 + help 233 + Say yes here if you want to include support for ADC in the 234 + NXP PCF50633 chip. 235 + 229 236 endmenu 230 237 231 238 menu "Multimedia Capabilities Port drivers"
+1
drivers/mfd/Makefile
··· 39 39 obj-$(CONFIG_PMIC_DA903X) += da903x.o 40 40 41 41 obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o 42 + obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
+277
drivers/mfd/pcf50633-adc.c
··· 1 + /* NXP PCF50633 ADC Driver 2 + * 3 + * (C) 2006-2008 by Openmoko, Inc. 4 + * Author: Balaji Rao <balajirrao@openmoko.org> 5 + * All rights reserved. 6 + * 7 + * Broken down from monstrous PCF50633 driver mainly by 8 + * Harald Welte, Andy Green and Werner Almesberger 9 + * 10 + * This program is free software; you can redistribute it and/or modify it 11 + * under the terms of the GNU General Public License as published by the 12 + * Free Software Foundation; either version 2 of the License, or (at your 13 + * option) any later version. 14 + * 15 + * NOTE: This driver does not yet support subtractive ADC mode, which means 16 + * you can do only one measurement per read request. 17 + */ 18 + 19 + #include <linux/kernel.h> 20 + #include <linux/module.h> 21 + #include <linux/init.h> 22 + #include <linux/device.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/completion.h> 25 + 26 + #include <linux/mfd/pcf50633/core.h> 27 + #include <linux/mfd/pcf50633/adc.h> 28 + 29 + struct pcf50633_adc_request { 30 + int mux; 31 + int avg; 32 + int result; 33 + void (*callback)(struct pcf50633 *, void *, int); 34 + void *callback_param; 35 + 36 + /* Used in case of sync requests */ 37 + struct completion completion; 38 + 39 + }; 40 + 41 + #define PCF50633_MAX_ADC_FIFO_DEPTH 8 42 + 43 + struct pcf50633_adc { 44 + struct pcf50633 *pcf; 45 + 46 + /* Private stuff */ 47 + struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH]; 48 + int queue_head; 49 + int queue_tail; 50 + struct mutex queue_mutex; 51 + }; 52 + 53 + static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf) 54 + { 55 + return platform_get_drvdata(pcf->adc_pdev); 56 + } 57 + 58 + static void adc_setup(struct pcf50633 *pcf, int channel, int avg) 59 + { 60 + channel &= PCF50633_ADCC1_ADCMUX_MASK; 61 + 62 + /* kill ratiometric, but enable ACCSW biasing */ 63 + pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00); 64 + pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01); 65 + 66 + /* start ADC conversion on selected channel */ 67 + pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg | 68 + PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT); 69 + } 70 + 71 + static void trigger_next_adc_job_if_any(struct pcf50633 *pcf) 72 + { 73 + struct pcf50633_adc *adc = __to_adc(pcf); 74 + int head; 75 + 76 + mutex_lock(&adc->queue_mutex); 77 + 78 + head = adc->queue_head; 79 + 80 + if (!adc->queue[head]) { 81 + mutex_unlock(&adc->queue_mutex); 82 + return; 83 + } 84 + mutex_unlock(&adc->queue_mutex); 85 + 86 + adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg); 87 + } 88 + 89 + static int 90 + adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req) 91 + { 92 + struct pcf50633_adc *adc = __to_adc(pcf); 93 + int head, tail; 94 + 95 + mutex_lock(&adc->queue_mutex); 96 + 97 + head = adc->queue_head; 98 + tail = adc->queue_tail; 99 + 100 + if (adc->queue[tail]) { 101 + mutex_unlock(&adc->queue_mutex); 102 + return -EBUSY; 103 + } 104 + 105 + adc->queue[tail] = req; 106 + adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1); 107 + 108 + mutex_unlock(&adc->queue_mutex); 109 + 110 + trigger_next_adc_job_if_any(pcf); 111 + 112 + return 0; 113 + } 114 + 115 + static void 116 + pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result) 117 + { 118 + struct pcf50633_adc_request *req = param; 119 + 120 + req->result = result; 121 + complete(&req->completion); 122 + } 123 + 124 + int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg) 125 + { 126 + struct pcf50633_adc_request *req; 127 + 128 + /* req is freed when the result is ready, in interrupt handler */ 129 + req = kzalloc(sizeof(*req), GFP_KERNEL); 130 + if (!req) 131 + return -ENOMEM; 132 + 133 + req->mux = mux; 134 + req->avg = avg; 135 + req->callback = pcf50633_adc_sync_read_callback; 136 + req->callback_param = req; 137 + 138 + init_completion(&req->completion); 139 + adc_enqueue_request(pcf, req); 140 + wait_for_completion(&req->completion); 141 + 142 + return req->result; 143 + } 144 + EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read); 145 + 146 + int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg, 147 + void (*callback)(struct pcf50633 *, void *, int), 148 + void *callback_param) 149 + { 150 + struct pcf50633_adc_request *req; 151 + 152 + /* req is freed when the result is ready, in interrupt handler */ 153 + req = kmalloc(sizeof(*req), GFP_KERNEL); 154 + if (!req) 155 + return -ENOMEM; 156 + 157 + req->mux = mux; 158 + req->avg = avg; 159 + req->callback = callback; 160 + req->callback_param = callback_param; 161 + 162 + adc_enqueue_request(pcf, req); 163 + 164 + return 0; 165 + } 166 + EXPORT_SYMBOL_GPL(pcf50633_adc_async_read); 167 + 168 + static int adc_result(struct pcf50633 *pcf) 169 + { 170 + u8 adcs1, adcs3; 171 + u16 result; 172 + 173 + adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1); 174 + adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3); 175 + result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK); 176 + 177 + dev_dbg(pcf->dev, "adc result = %d\n", result); 178 + 179 + return result; 180 + } 181 + 182 + static void pcf50633_adc_irq(int irq, void *data) 183 + { 184 + struct pcf50633_adc *adc = data; 185 + struct pcf50633 *pcf = adc->pcf; 186 + struct pcf50633_adc_request *req; 187 + int head; 188 + 189 + mutex_lock(&adc->queue_mutex); 190 + head = adc->queue_head; 191 + 192 + req = adc->queue[head]; 193 + if (WARN_ON(!req)) { 194 + dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n"); 195 + mutex_unlock(&adc->queue_mutex); 196 + return; 197 + } 198 + adc->queue[head] = NULL; 199 + adc->queue_head = (head + 1) & 200 + (PCF50633_MAX_ADC_FIFO_DEPTH - 1); 201 + 202 + mutex_unlock(&adc->queue_mutex); 203 + 204 + req->callback(pcf, req->callback_param, adc_result(pcf)); 205 + kfree(req); 206 + 207 + trigger_next_adc_job_if_any(pcf); 208 + } 209 + 210 + static int __devinit pcf50633_adc_probe(struct platform_device *pdev) 211 + { 212 + struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data; 213 + struct pcf50633_adc *adc; 214 + 215 + adc = kzalloc(sizeof(*adc), GFP_KERNEL); 216 + if (!adc) 217 + return -ENOMEM; 218 + 219 + adc->pcf = pdata->pcf; 220 + platform_set_drvdata(pdev, adc); 221 + 222 + pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ADCRDY, 223 + pcf50633_adc_irq, adc); 224 + 225 + mutex_init(&adc->queue_mutex); 226 + 227 + return 0; 228 + } 229 + 230 + static int __devexit pcf50633_adc_remove(struct platform_device *pdev) 231 + { 232 + struct pcf50633_adc *adc = platform_get_drvdata(pdev); 233 + int i, head; 234 + 235 + pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY); 236 + 237 + mutex_lock(&adc->queue_mutex); 238 + head = adc->queue_head; 239 + 240 + if (WARN_ON(adc->queue[head])) 241 + dev_err(adc->pcf->dev, 242 + "adc driver removed with request pending\n"); 243 + 244 + for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++) 245 + kfree(adc->queue[i]); 246 + 247 + mutex_unlock(&adc->queue_mutex); 248 + kfree(adc); 249 + 250 + return 0; 251 + } 252 + 253 + static struct platform_driver pcf50633_adc_driver = { 254 + .driver = { 255 + .name = "pcf50633-adc", 256 + }, 257 + .probe = pcf50633_adc_probe, 258 + .remove = __devexit_p(pcf50633_adc_remove), 259 + }; 260 + 261 + static int __init pcf50633_adc_init(void) 262 + { 263 + return platform_driver_register(&pcf50633_adc_driver); 264 + } 265 + module_init(pcf50633_adc_init); 266 + 267 + static void __exit pcf50633_adc_exit(void) 268 + { 269 + platform_driver_unregister(&pcf50633_adc_driver); 270 + } 271 + module_exit(pcf50633_adc_exit); 272 + 273 + MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); 274 + MODULE_DESCRIPTION("PCF50633 adc driver"); 275 + MODULE_LICENSE("GPL"); 276 + MODULE_ALIAS("platform:pcf50633-adc"); 277 +
+72
include/linux/mfd/pcf50633/adc.h
··· 1 + /* 2 + * adc.h -- Driver for NXP PCF50633 ADC 3 + * 4 + * (C) 2006-2008 by Openmoko, Inc. 5 + * All rights reserved. 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License as published by the 9 + * Free Software Foundation; either version 2 of the License, or (at your 10 + * option) any later version. 11 + */ 12 + 13 + #ifndef __LINUX_MFD_PCF50633_ADC_H 14 + #define __LINUX_MFD_PCF50633_ADC_H 15 + 16 + #include <linux/mfd/pcf50633/core.h> 17 + #include <linux/platform_device.h> 18 + 19 + /* ADC Registers */ 20 + #define PCF50633_REG_ADCC3 0x52 21 + #define PCF50633_REG_ADCC2 0x53 22 + #define PCF50633_REG_ADCC1 0x54 23 + #define PCF50633_REG_ADCS1 0x55 24 + #define PCF50633_REG_ADCS2 0x56 25 + #define PCF50633_REG_ADCS3 0x57 26 + 27 + #define PCF50633_ADCC1_ADCSTART 0x01 28 + #define PCF50633_ADCC1_RES_10BIT 0x02 29 + #define PCF50633_ADCC1_AVERAGE_NO 0x00 30 + #define PCF50633_ADCC1_AVERAGE_4 0x04 31 + #define PCF50633_ADCC1_AVERAGE_8 0x08 32 + #define PCF50633_ADCC1_AVERAGE_16 0x0c 33 + #define PCF50633_ADCC1_MUX_BATSNS_RES 0x00 34 + #define PCF50633_ADCC1_MUX_BATSNS_SUBTR 0x10 35 + #define PCF50633_ADCC1_MUX_ADCIN2_RES 0x20 36 + #define PCF50633_ADCC1_MUX_ADCIN2_SUBTR 0x30 37 + #define PCF50633_ADCC1_MUX_BATTEMP 0x60 38 + #define PCF50633_ADCC1_MUX_ADCIN1 0x70 39 + #define PCF50633_ADCC1_AVERAGE_MASK 0x0c 40 + #define PCF50633_ADCC1_ADCMUX_MASK 0xf0 41 + 42 + #define PCF50633_ADCC2_RATIO_NONE 0x00 43 + #define PCF50633_ADCC2_RATIO_BATTEMP 0x01 44 + #define PCF50633_ADCC2_RATIO_ADCIN1 0x02 45 + #define PCF50633_ADCC2_RATIO_BOTH 0x03 46 + #define PCF50633_ADCC2_RATIOSETTL_100US 0x04 47 + 48 + #define PCF50633_ADCC3_ACCSW_EN 0x01 49 + #define PCF50633_ADCC3_NTCSW_EN 0x04 50 + #define PCF50633_ADCC3_RES_DIV_TWO 0x10 51 + #define PCF50633_ADCC3_RES_DIV_THREE 0x00 52 + 53 + #define PCF50633_ADCS3_REF_NTCSW 0x00 54 + #define PCF50633_ADCS3_REF_ACCSW 0x10 55 + #define PCF50633_ADCS3_REF_2V0 0x20 56 + #define PCF50633_ADCS3_REF_VISA 0x30 57 + #define PCF50633_ADCS3_REF_2V0_2 0x70 58 + #define PCF50633_ADCS3_ADCRDY 0x80 59 + 60 + #define PCF50633_ADCS3_ADCDAT1L_MASK 0x03 61 + #define PCF50633_ADCS3_ADCDAT2L_MASK 0x0c 62 + #define PCF50633_ADCS3_ADCDAT2L_SHIFT 2 63 + #define PCF50633_ASCS3_REF_MASK 0x70 64 + 65 + extern int 66 + pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg, 67 + void (*callback)(struct pcf50633 *, void *, int), 68 + void *callback_param); 69 + extern int 70 + pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg); 71 + 72 + #endif /* __LINUX_PCF50633_ADC_H */