* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*/
-
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/err.h>
#include <linux/adc.h>
+#include "adc_priv.h"
+struct adc_host *g_adc = NULL;
+static LIST_HEAD(adc_host_head);
-static struct adc_host *g_adc = NULL;
-
-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<<ADC_ASYNC_READ)){
+ req->client->callback(req->client, req->client->callback_param, req->client->result);
+ }
+ if(req->client->flags & (1<<ADC_SYNC_READ)){
+ adc_sync_read_callback(req->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<<type)){
+ spin_unlock_irqrestore(&adc->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<<type;
+ }else{
+ client->flags = 1<<type;
+ ret = adc_request_add(adc, client);
+ if(ret < 0){
+ spin_unlock_irqrestore(&adc->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);