108c3e06aSBalaji Rao /* NXP PCF50633 ADC Driver 208c3e06aSBalaji Rao * 308c3e06aSBalaji Rao * (C) 2006-2008 by Openmoko, Inc. 408c3e06aSBalaji Rao * Author: Balaji Rao <balajirrao@openmoko.org> 508c3e06aSBalaji Rao * All rights reserved. 608c3e06aSBalaji Rao * 708c3e06aSBalaji Rao * Broken down from monstrous PCF50633 driver mainly by 808c3e06aSBalaji Rao * Harald Welte, Andy Green and Werner Almesberger 908c3e06aSBalaji Rao * 1008c3e06aSBalaji Rao * This program is free software; you can redistribute it and/or modify it 1108c3e06aSBalaji Rao * under the terms of the GNU General Public License as published by the 1208c3e06aSBalaji Rao * Free Software Foundation; either version 2 of the License, or (at your 1308c3e06aSBalaji Rao * option) any later version. 1408c3e06aSBalaji Rao * 1508c3e06aSBalaji Rao * NOTE: This driver does not yet support subtractive ADC mode, which means 1608c3e06aSBalaji Rao * you can do only one measurement per read request. 1708c3e06aSBalaji Rao */ 1808c3e06aSBalaji Rao 1908c3e06aSBalaji Rao #include <linux/kernel.h> 205a0e3ad6STejun Heo #include <linux/slab.h> 2108c3e06aSBalaji Rao #include <linux/module.h> 2208c3e06aSBalaji Rao #include <linux/init.h> 2308c3e06aSBalaji Rao #include <linux/device.h> 2408c3e06aSBalaji Rao #include <linux/platform_device.h> 2508c3e06aSBalaji Rao #include <linux/completion.h> 2608c3e06aSBalaji Rao 2708c3e06aSBalaji Rao #include <linux/mfd/pcf50633/core.h> 2808c3e06aSBalaji Rao #include <linux/mfd/pcf50633/adc.h> 2908c3e06aSBalaji Rao 3008c3e06aSBalaji Rao struct pcf50633_adc_request { 3108c3e06aSBalaji Rao int mux; 3208c3e06aSBalaji Rao int avg; 3308c3e06aSBalaji Rao void (*callback)(struct pcf50633 *, void *, int); 3408c3e06aSBalaji Rao void *callback_param; 356438a694SLars-Peter Clausen }; 3608c3e06aSBalaji Rao 376438a694SLars-Peter Clausen struct pcf50633_adc_sync_request { 386438a694SLars-Peter Clausen int result; 3908c3e06aSBalaji Rao struct completion completion; 4008c3e06aSBalaji Rao }; 4108c3e06aSBalaji Rao 4208c3e06aSBalaji Rao #define PCF50633_MAX_ADC_FIFO_DEPTH 8 4308c3e06aSBalaji Rao 4408c3e06aSBalaji Rao struct pcf50633_adc { 4508c3e06aSBalaji Rao struct pcf50633 *pcf; 4608c3e06aSBalaji Rao 4708c3e06aSBalaji Rao /* Private stuff */ 4808c3e06aSBalaji Rao struct pcf50633_adc_request *queue[PCF50633_MAX_ADC_FIFO_DEPTH]; 4908c3e06aSBalaji Rao int queue_head; 5008c3e06aSBalaji Rao int queue_tail; 5108c3e06aSBalaji Rao struct mutex queue_mutex; 5208c3e06aSBalaji Rao }; 5308c3e06aSBalaji Rao 5408c3e06aSBalaji Rao static inline struct pcf50633_adc *__to_adc(struct pcf50633 *pcf) 5508c3e06aSBalaji Rao { 5608c3e06aSBalaji Rao return platform_get_drvdata(pcf->adc_pdev); 5708c3e06aSBalaji Rao } 5808c3e06aSBalaji Rao 5908c3e06aSBalaji Rao static void adc_setup(struct pcf50633 *pcf, int channel, int avg) 6008c3e06aSBalaji Rao { 6108c3e06aSBalaji Rao channel &= PCF50633_ADCC1_ADCMUX_MASK; 6208c3e06aSBalaji Rao 6308c3e06aSBalaji Rao /* kill ratiometric, but enable ACCSW biasing */ 6408c3e06aSBalaji Rao pcf50633_reg_write(pcf, PCF50633_REG_ADCC2, 0x00); 6508c3e06aSBalaji Rao pcf50633_reg_write(pcf, PCF50633_REG_ADCC3, 0x01); 6608c3e06aSBalaji Rao 6708c3e06aSBalaji Rao /* start ADC conversion on selected channel */ 6808c3e06aSBalaji Rao pcf50633_reg_write(pcf, PCF50633_REG_ADCC1, channel | avg | 6908c3e06aSBalaji Rao PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT); 7008c3e06aSBalaji Rao } 7108c3e06aSBalaji Rao 7208c3e06aSBalaji Rao static void trigger_next_adc_job_if_any(struct pcf50633 *pcf) 7308c3e06aSBalaji Rao { 7408c3e06aSBalaji Rao struct pcf50633_adc *adc = __to_adc(pcf); 7508c3e06aSBalaji Rao int head; 7608c3e06aSBalaji Rao 7708c3e06aSBalaji Rao head = adc->queue_head; 7808c3e06aSBalaji Rao 79bd8ef102SPaul Fertser if (!adc->queue[head]) 8008c3e06aSBalaji Rao return; 8108c3e06aSBalaji Rao 8208c3e06aSBalaji Rao adc_setup(pcf, adc->queue[head]->mux, adc->queue[head]->avg); 8308c3e06aSBalaji Rao } 8408c3e06aSBalaji Rao 8508c3e06aSBalaji Rao static int 8608c3e06aSBalaji Rao adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req) 8708c3e06aSBalaji Rao { 8808c3e06aSBalaji Rao struct pcf50633_adc *adc = __to_adc(pcf); 8908c3e06aSBalaji Rao int head, tail; 9008c3e06aSBalaji Rao 9108c3e06aSBalaji Rao mutex_lock(&adc->queue_mutex); 9208c3e06aSBalaji Rao 9308c3e06aSBalaji Rao head = adc->queue_head; 9408c3e06aSBalaji Rao tail = adc->queue_tail; 9508c3e06aSBalaji Rao 9608c3e06aSBalaji Rao if (adc->queue[tail]) { 9708c3e06aSBalaji Rao mutex_unlock(&adc->queue_mutex); 98bd8ef102SPaul Fertser dev_err(pcf->dev, "ADC queue is full, dropping request\n"); 9908c3e06aSBalaji Rao return -EBUSY; 10008c3e06aSBalaji Rao } 10108c3e06aSBalaji Rao 10208c3e06aSBalaji Rao adc->queue[tail] = req; 103bd8ef102SPaul Fertser if (head == tail) 104bd8ef102SPaul Fertser trigger_next_adc_job_if_any(pcf); 10508c3e06aSBalaji Rao adc->queue_tail = (tail + 1) & (PCF50633_MAX_ADC_FIFO_DEPTH - 1); 10608c3e06aSBalaji Rao 10708c3e06aSBalaji Rao mutex_unlock(&adc->queue_mutex); 10808c3e06aSBalaji Rao 10908c3e06aSBalaji Rao return 0; 11008c3e06aSBalaji Rao } 11108c3e06aSBalaji Rao 1126438a694SLars-Peter Clausen static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, 1136438a694SLars-Peter Clausen int result) 11408c3e06aSBalaji Rao { 1156438a694SLars-Peter Clausen struct pcf50633_adc_sync_request *req = param; 11608c3e06aSBalaji Rao 11708c3e06aSBalaji Rao req->result = result; 11808c3e06aSBalaji Rao complete(&req->completion); 11908c3e06aSBalaji Rao } 12008c3e06aSBalaji Rao 12108c3e06aSBalaji Rao int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg) 12208c3e06aSBalaji Rao { 1236438a694SLars-Peter Clausen struct pcf50633_adc_sync_request req; 1246438a694SLars-Peter Clausen int ret; 12508c3e06aSBalaji Rao 1266438a694SLars-Peter Clausen init_completion(&req.completion); 12708c3e06aSBalaji Rao 1286438a694SLars-Peter Clausen ret = pcf50633_adc_async_read(pcf, mux, avg, 1296438a694SLars-Peter Clausen pcf50633_adc_sync_read_callback, &req); 1306438a694SLars-Peter Clausen if (ret) 1316438a694SLars-Peter Clausen return ret; 13208c3e06aSBalaji Rao 1336438a694SLars-Peter Clausen wait_for_completion(&req.completion); 134bd8ef102SPaul Fertser 1356438a694SLars-Peter Clausen return req.result; 13608c3e06aSBalaji Rao } 13708c3e06aSBalaji Rao EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read); 13808c3e06aSBalaji Rao 13908c3e06aSBalaji Rao int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg, 14008c3e06aSBalaji Rao void (*callback)(struct pcf50633 *, void *, int), 14108c3e06aSBalaji Rao void *callback_param) 14208c3e06aSBalaji Rao { 14308c3e06aSBalaji Rao struct pcf50633_adc_request *req; 14408c3e06aSBalaji Rao 14508c3e06aSBalaji Rao /* req is freed when the result is ready, in interrupt handler */ 14608c3e06aSBalaji Rao req = kmalloc(sizeof(*req), GFP_KERNEL); 14708c3e06aSBalaji Rao if (!req) 14808c3e06aSBalaji Rao return -ENOMEM; 14908c3e06aSBalaji Rao 15008c3e06aSBalaji Rao req->mux = mux; 15108c3e06aSBalaji Rao req->avg = avg; 15208c3e06aSBalaji Rao req->callback = callback; 15308c3e06aSBalaji Rao req->callback_param = callback_param; 15408c3e06aSBalaji Rao 155bd8ef102SPaul Fertser return adc_enqueue_request(pcf, req); 15608c3e06aSBalaji Rao } 15708c3e06aSBalaji Rao EXPORT_SYMBOL_GPL(pcf50633_adc_async_read); 15808c3e06aSBalaji Rao 15908c3e06aSBalaji Rao static int adc_result(struct pcf50633 *pcf) 16008c3e06aSBalaji Rao { 16108c3e06aSBalaji Rao u8 adcs1, adcs3; 16208c3e06aSBalaji Rao u16 result; 16308c3e06aSBalaji Rao 16408c3e06aSBalaji Rao adcs1 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS1); 16508c3e06aSBalaji Rao adcs3 = pcf50633_reg_read(pcf, PCF50633_REG_ADCS3); 16608c3e06aSBalaji Rao result = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK); 16708c3e06aSBalaji Rao 16808c3e06aSBalaji Rao dev_dbg(pcf->dev, "adc result = %d\n", result); 16908c3e06aSBalaji Rao 17008c3e06aSBalaji Rao return result; 17108c3e06aSBalaji Rao } 17208c3e06aSBalaji Rao 17308c3e06aSBalaji Rao static void pcf50633_adc_irq(int irq, void *data) 17408c3e06aSBalaji Rao { 17508c3e06aSBalaji Rao struct pcf50633_adc *adc = data; 17608c3e06aSBalaji Rao struct pcf50633 *pcf = adc->pcf; 17708c3e06aSBalaji Rao struct pcf50633_adc_request *req; 178bd8ef102SPaul Fertser int head, res; 17908c3e06aSBalaji Rao 18008c3e06aSBalaji Rao mutex_lock(&adc->queue_mutex); 18108c3e06aSBalaji Rao head = adc->queue_head; 18208c3e06aSBalaji Rao 18308c3e06aSBalaji Rao req = adc->queue[head]; 18408c3e06aSBalaji Rao if (WARN_ON(!req)) { 18508c3e06aSBalaji Rao dev_err(pcf->dev, "pcf50633-adc irq: ADC queue empty!\n"); 18608c3e06aSBalaji Rao mutex_unlock(&adc->queue_mutex); 18708c3e06aSBalaji Rao return; 18808c3e06aSBalaji Rao } 18908c3e06aSBalaji Rao adc->queue[head] = NULL; 19008c3e06aSBalaji Rao adc->queue_head = (head + 1) & 19108c3e06aSBalaji Rao (PCF50633_MAX_ADC_FIFO_DEPTH - 1); 19208c3e06aSBalaji Rao 193bd8ef102SPaul Fertser res = adc_result(pcf); 194bd8ef102SPaul Fertser trigger_next_adc_job_if_any(pcf); 195bd8ef102SPaul Fertser 19608c3e06aSBalaji Rao mutex_unlock(&adc->queue_mutex); 19708c3e06aSBalaji Rao 198bd8ef102SPaul Fertser req->callback(pcf, req->callback_param, res); 19908c3e06aSBalaji Rao kfree(req); 20008c3e06aSBalaji Rao } 20108c3e06aSBalaji Rao 202f791be49SBill Pemberton static int pcf50633_adc_probe(struct platform_device *pdev) 20308c3e06aSBalaji Rao { 20408c3e06aSBalaji Rao struct pcf50633_adc *adc; 20508c3e06aSBalaji Rao 2068a105ca2SJingoo Han adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL); 20708c3e06aSBalaji Rao if (!adc) 20808c3e06aSBalaji Rao return -ENOMEM; 20908c3e06aSBalaji Rao 21068d641efSLars-Peter Clausen adc->pcf = dev_to_pcf50633(pdev->dev.parent); 21108c3e06aSBalaji Rao platform_set_drvdata(pdev, adc); 21208c3e06aSBalaji Rao 21368d641efSLars-Peter Clausen pcf50633_register_irq(adc->pcf, PCF50633_IRQ_ADCRDY, 21408c3e06aSBalaji Rao pcf50633_adc_irq, adc); 21508c3e06aSBalaji Rao 21608c3e06aSBalaji Rao mutex_init(&adc->queue_mutex); 21708c3e06aSBalaji Rao 21808c3e06aSBalaji Rao return 0; 21908c3e06aSBalaji Rao } 22008c3e06aSBalaji Rao 2214740f73fSBill Pemberton static int pcf50633_adc_remove(struct platform_device *pdev) 22208c3e06aSBalaji Rao { 22308c3e06aSBalaji Rao struct pcf50633_adc *adc = platform_get_drvdata(pdev); 22408c3e06aSBalaji Rao int i, head; 22508c3e06aSBalaji Rao 22608c3e06aSBalaji Rao pcf50633_free_irq(adc->pcf, PCF50633_IRQ_ADCRDY); 22708c3e06aSBalaji Rao 22808c3e06aSBalaji Rao mutex_lock(&adc->queue_mutex); 22908c3e06aSBalaji Rao head = adc->queue_head; 23008c3e06aSBalaji Rao 23108c3e06aSBalaji Rao if (WARN_ON(adc->queue[head])) 23208c3e06aSBalaji Rao dev_err(adc->pcf->dev, 23308c3e06aSBalaji Rao "adc driver removed with request pending\n"); 23408c3e06aSBalaji Rao 23508c3e06aSBalaji Rao for (i = 0; i < PCF50633_MAX_ADC_FIFO_DEPTH; i++) 23608c3e06aSBalaji Rao kfree(adc->queue[i]); 23708c3e06aSBalaji Rao 23808c3e06aSBalaji Rao mutex_unlock(&adc->queue_mutex); 23908c3e06aSBalaji Rao 24008c3e06aSBalaji Rao return 0; 24108c3e06aSBalaji Rao } 24208c3e06aSBalaji Rao 24308c3e06aSBalaji Rao static struct platform_driver pcf50633_adc_driver = { 24408c3e06aSBalaji Rao .driver = { 24508c3e06aSBalaji Rao .name = "pcf50633-adc", 24608c3e06aSBalaji Rao }, 24708c3e06aSBalaji Rao .probe = pcf50633_adc_probe, 24884449216SBill Pemberton .remove = pcf50633_adc_remove, 24908c3e06aSBalaji Rao }; 25008c3e06aSBalaji Rao 25165349d60SMark Brown module_platform_driver(pcf50633_adc_driver); 25208c3e06aSBalaji Rao 25308c3e06aSBalaji Rao MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); 25408c3e06aSBalaji Rao MODULE_DESCRIPTION("PCF50633 adc driver"); 25508c3e06aSBalaji Rao MODULE_LICENSE("GPL"); 25608c3e06aSBalaji Rao MODULE_ALIAS("platform:pcf50633-adc"); 25708c3e06aSBalaji Rao 258