X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fadc%2Fcore.c;h=91344386fedaf5d631181d5b84fc49a7cad2f999;hb=c04b4e2e77a884014ab4d2538a90281b4b0e3a6f;hp=e4bfb238af70c9a2a719201a361051332b389696;hpb=af3be323f4f4d5ec3948b1870b2e26908d06afab;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/adc/core.c b/drivers/adc/core.c index e4bfb238af70..91344386feda 100755 --- a/drivers/adc/core.c +++ b/drivers/adc/core.c @@ -4,229 +4,269 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ - -#include -#include -#include -#include -#include #include -#include - +#include "adc_priv.h" -static struct adc_host *g_adc = NULL; +struct adc_host *g_adc = NULL; +static LIST_HEAD(adc_host_head); -struct adc_host *adc_alloc_host(int extra, struct device *dev) +struct adc_host *adc_alloc_host(struct device *dev, int extra, enum host_chn_mask mask) { struct adc_host *adc; adc = kzalloc(sizeof(struct adc_host) + extra, GFP_KERNEL); if (!adc) return NULL; + adc->mask = mask; adc->dev = dev; - g_adc = adc; + adc->chn = -1; + spin_lock_init(&adc->lock); + mutex_init(&adc->m_lock); + INIT_LIST_HEAD(&adc->req_head); + INIT_LIST_HEAD(&adc->callback_head); + + list_add_tail(&adc->entry, &adc_host_head); + return adc; } -EXPORT_SYMBOL(adc_alloc_host); + void adc_free_host(struct adc_host *adc) { + list_del(&adc->entry); kfree(adc); adc = NULL; return; } -EXPORT_SYMBOL(adc_free_host); - struct adc_client *adc_register(int chn, void (*callback)(struct adc_client *, void *, int), void *callback_param) + { - struct adc_client *client; - if(!g_adc) - { - printk(KERN_ERR "adc host has not initialized\n"); - return NULL; - } - if(chn >= MAX_ADC_CHN) - { - dev_err(g_adc->dev, "channel[%d] is greater than the maximum[%d]\n", chn, MAX_ADC_CHN); - return NULL; - } - client = kzalloc(sizeof(struct adc_client), GFP_KERNEL); - if(!client) - { - dev_err(g_adc->dev, "no memory for adc client\n"); - return NULL; - } - client->callback = callback; - client->callback_param = callback_param; - client->chn = chn; + struct adc_client *client = NULL; + struct adc_host *adc = NULL; - client->adc = g_adc; + if(chn < 0) + return NULL; + list_for_each_entry(adc, &adc_host_head, entry) { + if((chn == 0 && adc->mask == SARADC_CHN_MASK) || + (chn & adc->mask)){ + client = kzalloc(sizeof(struct adc_client), GFP_KERNEL); + if(!client) + return NULL; + client->callback = callback; + client->callback_param = callback_param; + client->chn = chn; + client->adc = adc; + client->index = adc->client_count; + init_waitqueue_head(&client->wait); + adc->client_count++; - return client; + return client; + } + } + dev_err(adc->dev, "chn(%d) is not support\n", chn); + return NULL; } EXPORT_SYMBOL(adc_register); void adc_unregister(struct adc_client *client) { + struct adc_host *adc = client->adc; + + adc->client_count--; kfree(client); client = NULL; return; } EXPORT_SYMBOL(adc_unregister); - -static void trigger_next_adc_job_if_any(struct adc_host *adc) +static inline void trigger_next_adc_job_if_any(struct adc_host *adc) { - int head = adc->queue_head; + struct adc_request *req = NULL; + + req = list_first_entry(&adc->req_head, struct adc_request, entry); + if(req){ + if(req->client == NULL){ + dev_err(adc->dev, "Abnormal: client piont is NULL...............\n"); + return; + } + adc->chn = req->client->chn; + adc->ops->start(adc); + } - if (!adc->queue[head]) - return; - adc->cur = adc->queue[head]->client; - adc->ops->start(adc); return; } -static int -adc_enqueue_request(struct adc_host *adc, struct adc_request *req) +static int adc_request_add(struct adc_host *adc, struct adc_client *client) { - int head, tail; - unsigned long flags; - - spin_lock_irqsave(&adc->lock, flags); - head = adc->queue_head; - tail = adc->queue_tail; + struct adc_request *req = NULL; - if (adc->queue[tail]) { - spin_unlock_irqrestore(&adc->lock,flags); - dev_err(adc->dev, "ADC queue is full, dropping request\n"); - return -EBUSY; - } + req = kzalloc(sizeof(struct adc_request), GFP_ATOMIC); - adc->queue[tail] = req; - if (head == tail) - trigger_next_adc_job_if_any(adc); - adc->queue_tail = (tail + 1) & (MAX_ADC_FIFO_DEPTH - 1); + if(unlikely(!req)) + return -ENOMEM; + INIT_LIST_HEAD(&req->entry); + req->client = client; + list_add_tail(&req->entry, &adc->req_head); + if(adc->chn == -1) + trigger_next_adc_job_if_any(adc); + return 0; +} +static void +adc_sync_read_callback(struct adc_client *client, void *param, int result) +{ + client->result = result; +} - spin_unlock_irqrestore(&adc->lock,flags); +static void adc_callback(struct adc_host *adc) +{ + struct adc_request *req = NULL, *n = NULL; - return 0; + list_for_each_entry_safe(req, n, &adc->callback_head, entry) { + if(req->client->flags & (1<client->callback(req->client, req->client->callback_param, req->client->result); + } + if(req->client->flags & (1<client, NULL, req->client->result); + req->client->is_finished = 1; + wake_up(&req->client->wait); + } + req->client->flags = 0; + list_del_init(&req->entry); + kfree(req); + } } -static void -adc_sync_read_callback(struct adc_client *client, void *param, int result) +void adc_finished(struct adc_host *adc, int result) { - struct adc_request *req = param; + unsigned long flags; + struct adc_request *req = NULL, *n = NULL; + + adc->ops->stop(adc); + udelay(SAMPLE_RATE); + spin_lock_irqsave(&adc->lock, flags); + list_for_each_entry_safe(req, n, &adc->req_head, entry) { + if(req->client->chn == adc->chn){ + req->client->result = result; + list_move_tail(&req->entry, &adc->callback_head); + } + } + adc->chn = -1; + if(!list_empty(&adc->req_head)) + trigger_next_adc_job_if_any(adc); + spin_unlock_irqrestore(&adc->lock, flags); - client->result = result; - complete(&req->completion); + adc_callback(adc); } -int adc_sync_read(struct adc_client *client) +void adc_core_irq_handle(struct adc_host *adc) { - struct adc_request *req = NULL; - int err, tmo, tail; + int result = 0; - if(client == NULL) { - printk(KERN_ERR "client point is NULL"); - return -EINVAL; - } - if(client->adc->is_suspended == 1) { - dev_dbg(client->adc->dev, "system enter sleep\n"); - return -1; - } - req = kzalloc(sizeof(*req), GFP_ATOMIC); - if (!req){ - dev_err(client->adc->dev, "no memory for adc request\n"); - return -ENOMEM; - } - req->chn = client->chn; - req->callback = adc_sync_read_callback; - req->callback_param = req; - req->client = client; - req->status = SYNC_READ; - - init_completion(&req->completion); - err = adc_enqueue_request(client->adc, req); - if (err) - { - dev_err(client->adc->dev, "fail to enqueue request\n"); - kfree(req); - return err; - } - tmo = wait_for_completion_timeout(&req->completion,msecs_to_jiffies(100)); - kfree(req); - req = NULL; - if(tmo == 0) { - tail = (client->adc->queue_tail - 1) & (MAX_ADC_FIFO_DEPTH - 1); - client->adc->queue[tail] = NULL; - client->adc->queue_tail = tail; - return -ETIMEDOUT; - } - return client->result; + WARN_ON(adc->chn == -1); + + mutex_lock(&adc->m_lock); + + result = adc->ops->read(adc); + adc_dbg(adc->dev, "chn[%d] read value: %d\n", adc->chn, result); + + adc_finished(adc, result); + + mutex_unlock(&adc->m_lock); } -EXPORT_SYMBOL(adc_sync_read); -int adc_async_read(struct adc_client *client) +int adc_host_read(struct adc_client *client, enum read_type type) { - int ret = 0; - struct adc_request *req = NULL; - + int tmo, ret = 0; + unsigned long flags; + struct adc_host *adc = NULL; + if(client == NULL) { - printk(KERN_ERR "client point is NULL"); + printk(KERN_ERR "client is NULL"); return -EINVAL; } - if(client->adc->is_suspended == 1) { - dev_dbg(client->adc->dev, "system enter sleep\n"); - return -1; + adc = client->adc; + if(adc->is_suspended == 1) { + dev_err(adc->dev, "adc is in suspend state\n"); + return -EIO; } - req = kzalloc(sizeof(*req), GFP_ATOMIC); - if (!req) { - dev_err(client->adc->dev, "no memory for adc request\n"); - return -ENOMEM; - } - req->chn = client->chn; - req->callback = client->callback; - req->callback_param = client->callback_param; - req->client = client; - req->status = ASYNC_READ; - ret = adc_enqueue_request(client->adc, req); - if(ret < 0) - kfree(req); + spin_lock_irqsave(&adc->lock, flags); + if(client->flags & (1<lock, flags); + adc_dbg(adc->dev, "req is exist: %s, client->index: %d\n", + (type == ADC_ASYNC_READ)?"async_read":"sync_read", client->index); + return -EEXIST; + }else if(client->flags != 0){ + client->flags |= 1<flags = 1<lock, flags); + dev_err(adc->dev, "fail to add request\n"); + return ret; + } + } + if(type == ADC_ASYNC_READ){ + spin_unlock_irqrestore(&adc->lock, flags); + return 0; + } + client->is_finished = 0; + spin_unlock_irqrestore(&adc->lock, flags); + + tmo = wait_event_timeout(client->wait, ( client->is_finished == 1 ), msecs_to_jiffies(ADC_READ_TMO)); + mutex_lock(&adc->m_lock); + if(unlikely((tmo <= 0) && (client->is_finished == 0))) { + dev_err(adc->dev, "get adc value timeout.................................\n"); + if(adc->ops->dump) + adc->ops->dump(adc); + adc_finished(adc, -1); + mutex_unlock(&adc->m_lock); + return -ETIMEDOUT; + } + mutex_unlock(&adc->m_lock); - return ret; + return client->result; } + +int adc_sync_read(struct adc_client *client) +{ + return adc_host_read(client, ADC_SYNC_READ); +} +EXPORT_SYMBOL(adc_sync_read); + +int adc_async_read(struct adc_client *client) +{ + return adc_host_read(client, ADC_ASYNC_READ); +} + EXPORT_SYMBOL(adc_async_read); -void adc_core_irq_handle(struct adc_host *adc) +int adc_get_def_ref_volt(void) { - struct adc_request *req; - int head, res; - spin_lock(&adc->lock); - head = adc->queue_head; - - req = adc->queue[head]; - if (WARN_ON(!req)) { - spin_unlock(&adc->lock); - dev_err(adc->dev, "adc irq: ADC queue empty!\n"); - return; - } - adc->queue[head] = NULL; - adc->queue_head = (head + 1) & (MAX_ADC_FIFO_DEPTH - 1); - - res = adc->ops->read(adc); - adc->ops->stop(adc); - trigger_next_adc_job_if_any(adc); - - req->callback(adc->cur, req->callback_param, res); - if(req->status == ASYNC_READ) { - kfree(req); - req = NULL; - } - spin_unlock(&adc->lock); + return g_adc->pdata->ref_volt; } -EXPORT_SYMBOL(adc_core_irq_handle); +EXPORT_SYMBOL(adc_get_def_ref_volt); +int adc_get_curr_ref_volt(void) +{ + int v = 0, volt = 0; + + if(!g_adc) + return -EINVAL; + if(!g_adc->base_client) + return g_adc->pdata->ref_volt; + volt = g_adc->pdata->get_base_volt(); + if(volt < 0) + return g_adc->pdata->ref_volt; + + v = adc_sync_read(g_adc->base_client); + if(v < 0) + return v; + + return volt * 1024 / v; +} +EXPORT_SYMBOL(adc_get_curr_ref_volt);