update adc drivers: fix: 'adc get value timeout' and 'adc get zero, but voltage is NOT zero'

This commit is contained in:
kfx
2012-06-19 10:07:08 +08:00
parent 5b8b44094c
commit 79409ef39e
6 changed files with 131 additions and 54 deletions

View File

@@ -19,6 +19,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <mach/board.h>
@@ -27,8 +28,15 @@
#define adc_writel writel_relaxed
#define adc_readl readl_relaxed
#if 0
#define adc_dbg(dev, format, arg...) \
dev_printk(KERN_INFO , dev , format , ## arg)
#else
#define adc_dbg(dev, format, arg...)
#endif
enum read_type{
ADC_SYNC_READ,
ADC_SYNC_READ = 0,
ADC_ASYNC_READ,
};
@@ -40,17 +48,19 @@ struct adc_ops {
void (*start)(struct adc_host *);
void (*stop)(struct adc_host *);
int (*read)(struct adc_host *);
void (*dump)(struct adc_host *);
};
struct adc_host {
struct list_head entry;
struct list_head request_head;
struct list_head req_head;
unsigned int is_suspended;
enum host_chn_mask mask;
struct device *dev;
struct adc_client *cur;
unsigned int chn;
spinlock_t lock;
unsigned int client_count;
const struct adc_ops *ops;
struct work_struct work;
unsigned long priv[0];
};

View File

@@ -9,6 +9,7 @@
struct list_head adc_host_head;
static void adc_host_work(struct work_struct *work);
struct adc_host *adc_alloc_host(struct device *dev, int extra, enum host_chn_mask mask)
{
struct adc_host *adc;
@@ -18,8 +19,10 @@ struct adc_host *adc_alloc_host(struct device *dev, int extra, enum host_chn_mas
return NULL;
adc->mask = mask;
adc->dev = dev;
adc->chn = -1;
spin_lock_init(&adc->lock);
INIT_LIST_HEAD(&adc->request_head);
INIT_LIST_HEAD(&adc->req_head);
INIT_WORK(&adc->work, adc_host_work);
list_add_tail(&adc->entry, &adc_host_head);
@@ -77,36 +80,38 @@ static inline void trigger_next_adc_job_if_any(struct adc_host *adc)
{
struct adc_request *req = NULL;
if(list_empty(&adc->request_head))
if(adc->chn != -1)
return;
req = list_first_entry(&adc->req_head, struct adc_request, entry);
if(req){
adc->chn = req->client->chn;
adc->ops->start(adc);
}
req = list_first_entry(&adc->request_head, struct adc_request, entry);
if(req == NULL)
return;
list_del_init(&req->entry);
adc->cur = req->client;
kfree(req);
adc->ops->start(adc);
return;
}
static void adc_host_work(struct work_struct *work)
{
unsigned long flags;
struct adc_host *adc =
container_of(work, struct adc_host, work);
spin_lock_irqsave(&adc->lock, flags);
trigger_next_adc_job_if_any(adc);
spin_unlock_irqrestore(&adc->lock, flags);
}
static int adc_request_add(struct adc_host *adc, struct adc_client *client)
{
struct adc_request *req = NULL;
list_for_each_entry(req, &adc->request_head, entry) {
if(req->client->index == client->index)
return 0;
}
req = kzalloc(sizeof(struct adc_request), GFP_ATOMIC);
if(!req)
return -ENOMEM;
INIT_LIST_HEAD(&req->entry);
req->client = client;
list_add_tail(&req->entry, &adc->request_head);
list_add_tail(&req->entry, &adc->req_head);
trigger_next_adc_job_if_any(adc);
return 0;
}
static void
@@ -114,18 +119,41 @@ adc_sync_read_callback(struct adc_client *client, void *param, int result)
{
client->result = result;
}
static void adc_finished(struct adc_host *adc, int result)
{
struct adc_request *req = NULL, *n = NULL;
adc_dbg(adc->dev, "chn[%d] read value: %d\n", adc->chn, result);
adc->ops->stop(adc);
list_for_each_entry_safe(req, n, &adc->req_head, entry) {
if(req->client->chn == adc->chn){
if(req->client->flags & (1<<ADC_ASYNC_READ)){
req->client->callback(req->client, req->client->callback_param, result);
}
if(req->client->flags & (1<<ADC_SYNC_READ)){
adc_sync_read_callback(req->client, NULL, result);
req->client->is_finished = 1;
wake_up(&req->client->wait);
}
req->client->result = result;
req->client->flags = 0;
list_del_init(&req->entry);
kfree(req);
}
}
adc->chn = -1;
}
void adc_core_irq_handle(struct adc_host *adc)
{
int result = adc->ops->read(adc);
int result = 0;
spin_lock(&adc->lock);
adc->ops->stop(adc);
adc->cur->callback(adc->cur, adc->cur->callback_param, result);
adc_sync_read_callback(adc->cur, NULL, result);
adc->cur->is_finished = 1;
wake_up(&adc->cur->wait);
result = adc->ops->read(adc);
trigger_next_adc_job_if_any(adc);
adc_finished(adc, result);
if(!list_empty(&adc->req_head))
schedule_work(&adc->work);
spin_unlock(&adc->lock);
}
@@ -141,29 +169,45 @@ int adc_host_read(struct adc_client *client, enum read_type type)
}
adc = client->adc;
if(adc->is_suspended == 1) {
dev_dbg(adc->dev, "system enter sleep\n");
dev_err(adc->dev, "adc is in suspend state\n");
return -EIO;
}
spin_lock_irqsave(&adc->lock, flags);
ret = adc_request_add(adc, client);
if(ret < 0){
spin_unlock_irqrestore(&adc->lock, flags);
dev_err(adc->dev, "No memory for req\n");
return ret;
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);
if(type == ADC_ASYNC_READ)
return 0;
tmo = wait_event_timeout(client->wait, ( client->is_finished == 1 ), msecs_to_jiffies(ADC_READ_TMO));
if(tmo <= 0) {
adc->ops->stop(adc);
dev_dbg(adc->dev, "get adc value timeout\n");
spin_lock_irqsave(&adc->lock, flags);
if(unlikely((tmo <= 0) && (client->is_finished == 0))) {
if(adc->ops->dump)
adc->ops->dump(adc);
dev_err(adc->dev, "get adc value timeout.................................\n");
adc_finished(adc, -1);
spin_unlock_irqrestore(&adc->lock, flags);
return -ETIMEDOUT;
}
spin_unlock_irqrestore(&adc->lock, flags);
return client->result;
}

View File

@@ -21,7 +21,7 @@ struct rk28_adc_device {
static void rk28_adc_start(struct adc_host *adc)
{
struct rk28_adc_device *dev = adc_priv(adc);
int chn = adc->cur->chn;
int chn = adc->chn;
writel(ADC_CTRL_IRQ_ENABLE|ADC_CTRL_POWER_UP|ADC_CTRL_START|ADC_CTRL_CH(chn),
dev->regs + ADC_CTRL);

View File

@@ -21,7 +21,7 @@ struct rk29_adc_device {
static void rk29_adc_start(struct adc_host *adc)
{
struct rk29_adc_device *dev = adc_priv(adc);
int chn = adc->cur->chn;
int chn = adc->chn;
writel(0, dev->regs + ADC_CTRL);
writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn), dev->regs + ADC_CTRL);

View File

@@ -19,12 +19,22 @@ struct rk30_adc_device {
struct resource *ioarea;
struct adc_host *adc;
};
static void rk30_adc_dump(struct adc_host *adc)
{
struct rk30_adc_device *dev = adc_priv(adc);
dev_info(adc->dev, "[0x00-0x0c]: 0x%08x 0x%08x 0x%08x 0x%08x\n",
adc_readl(dev->regs + 0x00),
adc_readl(dev->regs + 0x04),
adc_readl(dev->regs + 0x08),
adc_readl(dev->regs + 0x0c));
}
static void rk30_adc_start(struct adc_host *adc)
{
struct rk30_adc_device *dev = adc_priv(adc);
int chn = adc->cur->chn;
int chn = adc->chn;
adc_writel(0, dev->regs + ADC_CTRL);
//adc_writel(0, dev->regs + ADC_CTRL);
adc_writel(0x08, dev->regs + ADC_DELAY_PU_SOC);
adc_writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn)|ADC_CTRL_IRQ_ENABLE, dev->regs + ADC_CTRL);
@@ -53,8 +63,11 @@ static const struct adc_ops rk30_adc_ops = {
.start = rk30_adc_start,
.stop = rk30_adc_stop,
.read = rk30_adc_read,
.dump = rk30_adc_dump,
};
#ifdef ADC_TEST
#define CHN_NR 3
struct workqueue_struct *adc_wq;
struct adc_test_data {
struct adc_client *client;
struct timer_list timer;
@@ -62,14 +75,17 @@ struct adc_test_data {
};
static void callback(struct adc_client *client, void *param, int result)
{
dev_info(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);
if(result < 70)
dev_info(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);
else
dev_dbg(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);
return;
}
static void adc_timer(unsigned long data)
{
struct adc_test_data *test=(struct adc_test_data *)data;
schedule_work(&test->timer_work);
queue_work(adc_wq, &test->timer_work);
add_timer(&test->timer);
}
static void adc_timer_work(struct work_struct *work)
@@ -79,20 +95,26 @@ static void adc_timer_work(struct work_struct *work)
timer_work);
adc_async_read(test->client);
sync_read = adc_sync_read(test->client);
dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);
if(sync_read < 70)
dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);
else
dev_dbg(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);
}
static int rk30_adc_test(void)
{
struct adc_test_data *test = NULL;
int i;
struct adc_test_data *test[CHN_NR];
test = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);
test->client = adc_register(1, callback, NULL);
INIT_WORK(&test->timer_work, adc_timer_work);
setup_timer(&test->timer, adc_timer, (unsigned long)test);
test->timer.expires = jiffies + 1;
add_timer(&test->timer);
adc_wq = create_singlethread_workqueue("adc_test");
for(i = 0; i < CHN_NR; i++){
test[i] = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);
test[i]->client = adc_register(i, callback, NULL);
INIT_WORK(&test[i]->timer_work, adc_timer_work);
setup_timer(&test[i]->timer, adc_timer, (unsigned long)test[i]);
test[i]->timer.expires = jiffies + 1;
add_timer(&test[i]->timer);
}
return 0;

View File

@@ -27,6 +27,7 @@ struct adc_client {
unsigned int index;
unsigned int chn;
unsigned int is_finished;
unsigned int flags;
int result;
struct adc_host *adc;
struct list_head list;