#include <linux/err.h>\r
#include <linux/io.h>\r
#include <linux/clk.h>\r
+#include <linux/workqueue.h>\r
#include <linux/interrupt.h>\r
#include <mach/board.h>\r
\r
#define adc_writel writel_relaxed\r
#define adc_readl readl_relaxed\r
\r
+#if 0\r
+#define adc_dbg(dev, format, arg...) \\r
+ dev_printk(KERN_INFO , dev , format , ## arg)\r
+#else\r
+#define adc_dbg(dev, format, arg...)\r
+#endif\r
+\r
enum read_type{\r
- ADC_SYNC_READ,\r
+ ADC_SYNC_READ = 0,\r
ADC_ASYNC_READ,\r
};\r
\r
void (*start)(struct adc_host *);\r
void (*stop)(struct adc_host *);\r
int (*read)(struct adc_host *);\r
+ void (*dump)(struct adc_host *);\r
};\r
struct adc_host {\r
struct list_head entry;\r
- struct list_head request_head;\r
+ struct list_head req_head;\r
unsigned int is_suspended;\r
enum host_chn_mask mask;\r
struct device *dev;\r
- struct adc_client *cur;\r
+ unsigned int chn;\r
spinlock_t lock;\r
unsigned int client_count;\r
const struct adc_ops *ops;\r
+ struct work_struct work;\r
unsigned long priv[0];\r
};\r
\r
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;
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);
{
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
{
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);
}
}
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;
}
struct resource *ioarea;\r
struct adc_host *adc;\r
};\r
+static void rk30_adc_dump(struct adc_host *adc)\r
+{\r
+ struct rk30_adc_device *dev = adc_priv(adc);\r
+\r
+ dev_info(adc->dev, "[0x00-0x0c]: 0x%08x 0x%08x 0x%08x 0x%08x\n",\r
+ adc_readl(dev->regs + 0x00),\r
+ adc_readl(dev->regs + 0x04),\r
+ adc_readl(dev->regs + 0x08),\r
+ adc_readl(dev->regs + 0x0c));\r
+}\r
static void rk30_adc_start(struct adc_host *adc)\r
{\r
struct rk30_adc_device *dev = adc_priv(adc);\r
- int chn = adc->cur->chn;\r
+ int chn = adc->chn;\r
\r
- adc_writel(0, dev->regs + ADC_CTRL);\r
+ //adc_writel(0, dev->regs + ADC_CTRL);\r
adc_writel(0x08, dev->regs + ADC_DELAY_PU_SOC);\r
adc_writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn)|ADC_CTRL_IRQ_ENABLE, dev->regs + ADC_CTRL);\r
\r
.start = rk30_adc_start,\r
.stop = rk30_adc_stop,\r
.read = rk30_adc_read,\r
+ .dump = rk30_adc_dump,\r
};\r
#ifdef ADC_TEST\r
+#define CHN_NR 3\r
+struct workqueue_struct *adc_wq;\r
struct adc_test_data {\r
struct adc_client *client;\r
struct timer_list timer;\r
};\r
static void callback(struct adc_client *client, void *param, int result)\r
{\r
- dev_info(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);\r
+ if(result < 70)\r
+ dev_info(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);\r
+ else\r
+ dev_dbg(client->adc->dev, "[chn%d] async_read = %d\n", client->chn, result);\r
return;\r
}\r
static void adc_timer(unsigned long data)\r
{\r
struct adc_test_data *test=(struct adc_test_data *)data;\r
\r
- schedule_work(&test->timer_work);\r
+ queue_work(adc_wq, &test->timer_work);\r
add_timer(&test->timer);\r
}\r
static void adc_timer_work(struct work_struct *work)\r
timer_work);\r
adc_async_read(test->client);\r
sync_read = adc_sync_read(test->client);\r
- dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);\r
+ if(sync_read < 70)\r
+ dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);\r
+ else\r
+ dev_dbg(test->client->adc->dev, "[chn%d] sync_read = %d\n", test->client->chn, sync_read);\r
}\r
\r
static int rk30_adc_test(void)\r
{\r
- struct adc_test_data *test = NULL;\r
-\r
- test = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);\r
- \r
- test->client = adc_register(1, callback, NULL);\r
- INIT_WORK(&test->timer_work, adc_timer_work);\r
- setup_timer(&test->timer, adc_timer, (unsigned long)test);\r
- test->timer.expires = jiffies + 1;\r
- add_timer(&test->timer);\r
+ int i;\r
+ struct adc_test_data *test[CHN_NR];\r
+\r
+ adc_wq = create_singlethread_workqueue("adc_test");\r
+ for(i = 0; i < CHN_NR; i++){\r
+ test[i] = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);\r
+ test[i]->client = adc_register(i, callback, NULL);\r
+ INIT_WORK(&test[i]->timer_work, adc_timer_work);\r
+ setup_timer(&test[i]->timer, adc_timer, (unsigned long)test[i]);\r
+ test[i]->timer.expires = jiffies + 1;\r
+ add_timer(&test[i]->timer);\r
+ }\r
\r
return 0;\r
\r