--- /dev/null
+/* include/linux/adc.h\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2 as\r
+ * published by the Free Software Foundation.\r
+ *\r
+*/\r
+\r
+#ifndef __ASM_ADC_PRIV_H\r
+#define __ASM_ADC_PRIV_H\r
+\r
+#include <linux/kernel.h>\r
+#include <linux/module.h>\r
+#include <linux/init.h>\r
+#include <linux/device.h>\r
+#include <linux/delay.h>\r
+#include <linux/slab.h>\r
+#include <linux/time.h>\r
+#include <linux/err.h>\r
+#include <linux/io.h>\r
+#include <linux/clk.h>\r
+#include <linux/interrupt.h>\r
+#include <mach/board.h>\r
+\r
+#define ADC_READ_TMO 100 // ms\r
+\r
+#define adc_writel writel_relaxed\r
+#define adc_readl readl_relaxed\r
+\r
+enum read_type{\r
+ ADC_SYNC_READ,\r
+ ADC_ASYNC_READ,\r
+};\r
+\r
+struct adc_request {\r
+ struct list_head entry;\r
+ struct adc_client *client;\r
+};\r
+struct adc_ops {\r
+ void (*start)(struct adc_host *);\r
+ void (*stop)(struct adc_host *);\r
+ int (*read)(struct adc_host *);\r
+};\r
+struct adc_host {\r
+ struct list_head entry;\r
+ struct list_head request_head;\r
+ unsigned int is_suspended;\r
+ enum host_chn_mask mask;\r
+ struct device *dev;\r
+ struct adc_client *cur;\r
+ spinlock_t lock;\r
+ unsigned int client_count;\r
+ const struct adc_ops *ops;\r
+ unsigned long priv[0];\r
+};\r
+\r
+static inline void *adc_priv(struct adc_host *adc)\r
+{\r
+ return adc->priv;\r
+}\r
+ \r
+struct adc_host *adc_alloc_host(struct device *dev, int extra, enum host_chn_mask mask);\r
+void adc_free_host(struct adc_host *adc);\r
+void adc_core_irq_handle(struct adc_host *adc);\r
+\r
+#endif\r
+\r
* 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 <linux/slab.h>
-
+#include "adc_priv.h"
-static struct adc_host *g_adc = NULL;
+struct 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;
+ spin_lock_init(&adc->lock);
+ INIT_LIST_HEAD(&adc->request_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;
-
- client->adc = g_adc;
- return client;
+{
+ struct adc_client *client = NULL;
+ struct adc_host *adc = 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;
+ }
+ }
+ 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;
+
+ if(list_empty(&adc->request_head))
+ return;
+
+ req = list_first_entry(&adc->request_head, struct adc_request, entry);
- if (!adc->queue[head])
- return;
- adc->cur = adc->queue[head]->client;
+ if(req == NULL)
+ return;
+ list_del(&req->entry);
+ adc->cur = req->client;
+ kfree(req);
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;
- }
+ list_for_each_entry(req, &adc->request_head, entry) {
+ if(req->client->index == client->index)
+ return 0;
+ }
+ req = kzalloc(sizeof(struct adc_request), GFP_KERNEL);
- 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(!req)
+ return -ENOMEM;
+ req->client = client;
+ list_add_tail(&req->entry, &adc->request_head);
- spin_unlock_irqrestore(&adc->lock,flags);
+ trigger_next_adc_job_if_any(adc);
- return 0;
+ return 0;
}
-
static void
adc_sync_read_callback(struct adc_client *client, void *param, int result)
{
- struct adc_request *req = param;
+ client->result = result;
+}
+void adc_core_irq_handle(struct adc_host *adc)
+{
+ int result = adc->ops->read(adc);
- client->result = result;
- complete(&req->completion);
+ 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);
+
+ trigger_next_adc_job_if_any(adc);
}
-int adc_sync_read(struct adc_client *client)
+int adc_host_read(struct adc_client *client, enum read_type type)
{
- struct adc_request *req = NULL;
- int err, tmo, tail;
+ int tmo;
+ 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;
- }
- 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;
+ adc = client->adc;
+ if(adc->is_suspended == 1) {
+ dev_dbg(adc->dev, "system enter sleep\n");
+ return -EIO;
}
- return client->result;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ adc_request_add(adc, client);
+ 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");
+ return -ETIMEDOUT;
+ }
+
+ 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)
{
- int ret = 0;
- struct adc_request *req = NULL;
-
- 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 = client->callback;
- req->callback_param = client->callback_param;
- req->client = client;
- req->status = ASYNC_READ;
+ return adc_host_read(client, ADC_ASYNC_READ);
+}
- ret = adc_enqueue_request(client->adc, req);
- if(ret < 0)
- kfree(req);
- return ret;
+static int __init adc_core_init(void)
+{
+ INIT_LIST_HEAD(&adc_host_head);
+ return 0;
}
-EXPORT_SYMBOL(adc_async_read);
+subsys_initcall(adc_core_init);
-void adc_core_irq_handle(struct adc_host *adc)
+static void __exit adc_core_exit(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;
}
-EXPORT_SYMBOL(adc_core_irq_handle);
+module_exit(adc_core_exit);
* it under the terms of the GNU General Public License as published by\r
* the Free Software Foundation; either version 2 of the License.\r
*/\r
-#include <linux/kernel.h>\r
-#include <linux/module.h>\r
-#include <linux/init.h>\r
-#include <linux/device.h>\r
-#include <linux/platform_device.h>\r
-#include <linux/err.h>\r
-#include <linux/clk.h>\r
-#include <linux/interrupt.h>\r
-#include <linux/io.h>\r
#include <linux/adc.h>\r
-#include <linux/delay.h>\r
-\r
\r
+#include "../adc_priv.h"\r
#include "rk28_adc.h"\r
\r
//#define ADC_TEST\r
struct resource *res;\r
int ret;\r
\r
- adc = adc_alloc_host(sizeof(struct rk28_adc_device), &pdev->dev);\r
+ adc = adc_alloc_host(&pdev->dev, sizeof(struct rk28_adc_device), SARADC_CHN_MASK);\r
if (!adc)\r
return -ENOMEM;\r
- mutex_init(&adc->queue_mutex);\r
- adc->dev = &pdev->dev;\r
- adc->is_suspended = 0;\r
adc->ops = &rk28_adc_ops;\r
dev = adc_priv(adc);\r
dev->adc = adc;\r
rk28_adc_test();\r
#endif\r
return 0;\r
-// err_iomap:\r
-// iounmap(dev->regs);\r
\r
err_ioarea:\r
release_resource(dev->ioarea);\r
#define ADC_STAS_BUSY (1<<0)
#define ADC_CTRL 0x08
-#define ADC_CTRL_CH(ch) ((ch)<<0)
+#define ADC_CTRL_CH(ch) (ch >> SARADC_CHN_SHIFT)
#define ADC_CTRL_POWER_UP (1<<3)
#define ADC_CTRL_START (1<<4)
#define ADC_CTRL_IRQ_ENABLE (1<<5)
* it under the terms of the GNU General Public License as published by\r
* the Free Software Foundation; either version 2 of the License.\r
*/\r
-#include <linux/kernel.h>\r
-#include <linux/module.h>\r
-#include <linux/init.h>\r
-#include <linux/device.h>\r
-#include <linux/platform_device.h>\r
-#include <linux/err.h>\r
-#include <linux/clk.h>\r
-#include <linux/interrupt.h>\r
-#include <linux/io.h>\r
#include <linux/adc.h>\r
-#include <linux/delay.h>\r
-#include <linux/slab.h>\r
-\r
\r
+#include "../adc_priv.h"\r
#include "rk29_adc.h"\r
\r
//#define ADC_TEST\r
struct resource *res;\r
int ret;\r
\r
- adc = adc_alloc_host(sizeof(struct rk29_adc_device), &pdev->dev);\r
+ adc = adc_alloc_host(&pdev->dev, sizeof(struct rk29_adc_device), SARADC_CHN_MASK);\r
if (!adc)\r
return -ENOMEM;\r
- spin_lock_init(&adc->lock);\r
- adc->dev = &pdev->dev;\r
- adc->is_suspended = 0;\r
adc->ops = &rk29_adc_ops;\r
dev = adc_priv(adc);\r
dev->adc = adc;\r
platform_set_drvdata(pdev, dev);\r
dev_info(&pdev->dev, "rk29 adc: driver initialized\n");\r
return 0;\r
-// err_iomap:\r
-// iounmap(dev->regs);\r
\r
err_ioarea:\r
release_resource(dev->ioarea);\r
#define ADC_STAS_BUSY (1<<0)
#define ADC_CTRL 0x08
-#define ADC_CTRL_CH(ch) (0x07 - ((ch)<<0))
+#define ADC_CTRL_CH(ch) (0x07 - ((ch >> SARADC_CHN_SHIFT)<<0))
#define ADC_CTRL_POWER_UP (1<<3)
#define ADC_CTRL_START (1<<4)
#define ADC_CTRL_IRQ_ENABLE (1<<5)
* it under the terms of the GNU General Public License as published by\r
* the Free Software Foundation; either version 2 of the License.\r
*/\r
-#include <linux/kernel.h>\r
-#include <linux/module.h>\r
-#include <linux/init.h>\r
-#include <linux/device.h>\r
-#include <linux/platform_device.h>\r
-#include <linux/err.h>\r
-#include <linux/clk.h>\r
-#include <linux/interrupt.h>\r
-#include <linux/io.h>\r
#include <linux/adc.h>\r
-#include <linux/delay.h>\r
-#include <linux/slab.h>\r
-\r
\r
+#include "../adc_priv.h"\r
#include "rk30_adc.h"\r
\r
//#define ADC_TEST\r
\r
struct rk30_adc_device {\r
- int irq;\r
+ int irq;\r
void __iomem *regs;\r
struct clk * clk;\r
struct resource *ioarea;\r
struct rk30_adc_device *dev = adc_priv(adc);\r
int chn = adc->cur->chn;\r
\r
- writel(0, dev->regs + ADC_CTRL);\r
- writel(0x08, dev->regs + ADC_DELAY_PU_SOC);\r
- writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn)|ADC_CTRL_IRQ_ENABLE, 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
return;\r
}\r
{\r
struct rk30_adc_device *dev = adc_priv(adc);\r
\r
- writel(0, dev->regs + ADC_CTRL);\r
+ adc_writel(0, dev->regs + ADC_CTRL);\r
}\r
static int rk30_adc_read(struct adc_host *adc)\r
{\r
struct rk30_adc_device *dev = adc_priv(adc);\r
\r
- return readl(dev->regs + ADC_DATA) & ADC_DATA_MASK;\r
+ return adc_readl(dev->regs + ADC_DATA) & ADC_DATA_MASK;\r
}\r
static irqreturn_t rk30_adc_irq(int irq, void *data)\r
{\r
}\r
static void adc_timer(unsigned long data)\r
{\r
- //int sync_read = 0;\r
struct adc_test_data *test=(struct adc_test_data *)data;\r
\r
- //sync_read = adc_sync_read(test->client);\r
- //dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", 0, sync_read);\r
schedule_work(&test->timer_work);\r
add_timer(&test->timer);\r
}\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", 0, sync_read);\r
+ dev_info(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
test = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);\r
\r
- test->client = adc_register(0, callback, NULL);\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 + 100;\r
+ test->timer.expires = jiffies + 1;\r
add_timer(&test->timer);\r
\r
return 0;\r
struct resource *res;\r
int ret;\r
\r
- adc = adc_alloc_host(sizeof(struct rk30_adc_device), &pdev->dev);\r
+ adc = adc_alloc_host(&pdev->dev, sizeof(struct rk30_adc_device), SARADC_CHN_MASK);\r
if (!adc)\r
return -ENOMEM;\r
- spin_lock_init(&adc->lock);\r
- adc->dev = &pdev->dev;\r
- adc->is_suspended = 0;\r
adc->ops = &rk30_adc_ops;\r
dev = adc_priv(adc);\r
dev->adc = adc;\r
platform_set_drvdata(pdev, dev);\r
dev_info(&pdev->dev, "rk30 adc: driver initialized\n");\r
return 0;\r
-// err_iomap:\r
-// iounmap(dev->regs);\r
\r
err_ioarea:\r
release_resource(dev->ioarea);\r
#define ADC_CTRL 0x08
#define ADC_DELAY_PU_SOC 0x0c
-#define ADC_CTRL_CH(ch) (ch) //(0x07 - ((ch)<<0))
+#define ADC_CTRL_CH(ch) (ch >> SARADC_CHN_SHIFT) //(0x07 - ((ch)<<0))
#define ADC_CTRL_POWER_UP (1<<3)
#define ADC_CTRL_IRQ_ENABLE (1<<5)
#define ADC_CTRL_IRQ_STATUS (1<<6)
#ifndef __ASM_ADC_CORE_H\r
#define __ASM_ADC_CORE_H\r
\r
-#define MAX_ADC_CHN 4\r
-#define MAX_ADC_FIFO_DEPTH 8\r
+#include <linux/list.h>\r
+#include <linux/wait.h>\r
\r
+enum host_chn_shift{\r
+ SARADC_CHN_SHIFT = 0,\r
+ CUSTOM_CHN_SHIFT = 28\r
+};\r
\r
-struct adc_client {\r
- int chn;\r
- int time;\r
- int result;\r
- void (*callback)(struct adc_client *, void *, int);\r
- void *callback_param;\r
-\r
- struct adc_host *adc;\r
+enum host_chn_mask{\r
+ SARADC_CHN_MASK = 0x0000000f, // saradc: 0 -- 15\r
+ CUSTOM_CHN_MASK = 0xf0000000,\r
};\r
\r
-struct adc_request {\r
- int result;\r
- int chn;\r
+struct adc_host;\r
+struct adc_client {\r
+ unsigned int index;\r
+ unsigned int chn;\r
+ unsigned int is_finished;\r
+ int result;\r
+ struct adc_host *adc;\r
+ struct list_head list;\r
+ wait_queue_head_t wait;\r
void (*callback)(struct adc_client *, void *, int);\r
void *callback_param;\r
- struct adc_client *client;\r
- /* Used in case of sync requests */\r
- struct completion completion;\r
-#define ASYNC_READ 0\r
-#define SYNC_READ 1\r
- int status;\r
-};\r
-struct adc_host;\r
-struct adc_ops {\r
- void (*start)(struct adc_host *);\r
- void (*stop)(struct adc_host *);\r
- int (*read)(struct adc_host *);\r
-};\r
- \r
- \r
-struct adc_host {\r
- struct device *dev;\r
- int is_suspended;\r
- struct adc_request *queue[MAX_ADC_FIFO_DEPTH];\r
- int queue_head;\r
- int queue_tail;\r
- spinlock_t lock;\r
- struct adc_client *cur;\r
- const struct adc_ops *ops;\r
- unsigned long private[0];\r
};\r
-static inline void *adc_priv(struct adc_host *adc)\r
-{\r
- return (void *)adc->private;\r
-}\r
- \r
-extern struct adc_host *adc_alloc_host(int extra, struct device *dev);\r
-extern void adc_free_host(struct adc_host *adc);\r
-extern void adc_core_irq_handle(struct adc_host *adc);\r
-\r
\r
#ifdef CONFIG_ADC\r
-extern struct adc_client *adc_register(int chn,\r
+struct adc_client *adc_register(int chn,\r
void (*callback)(struct adc_client *, void *, int), \r
void *callback_param);\r
-extern void adc_unregister(struct adc_client *client);\r
+void adc_unregister(struct adc_client *client);\r
\r
-extern int adc_sync_read(struct adc_client *client);\r
-extern int adc_async_read(struct adc_client *client);\r
+int adc_sync_read(struct adc_client *client);\r
+int adc_async_read(struct adc_client *client);\r
#else\r
static inline struct adc_client *adc_register(int chn,\r
void (*callback)(struct adc_client *, void *, int),\r
return NULL;\r
}\r
static inline void adc_unregister(struct adc_client *client) {}\r
-static inline int adc_sync_read(struct adc_client *client) { return -EINVAL; }\r
-static inline int adc_async_read(struct adc_client *client) { return -EINVAL; }\r
+static inline int adc_sync_read(struct adc_client *client) { return -1; }\r
+static inline int adc_async_read(struct adc_client *client) { return -1; }\r
#endif\r
\r
#endif\r