rk30:add adc(saradc) support

This commit is contained in:
kfx
2012-03-12 18:19:25 +08:00
parent dcfe492512
commit 4e26df8057
6 changed files with 340 additions and 4 deletions

View File

@@ -28,13 +28,35 @@
static u64 dma_dmamask = DMA_BIT_MASK(32);
#ifdef CONFIG_TSADC_RK30
static struct resource rk30_tsadc_resource[] = {
#ifdef CONFIG_ADC_RK30
static struct resource rk30_adc_resource[] = {
{
.start = IRQ_SARADC,
.end = IRQ_SARADC,
.flags = IORESOURCE_IRQ,
},
{
.start = RK30_SARADC_PHYS,
.end = RK30_SARADC_PHYS + RK30_SARADC_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
struct platform_device device_adc = {
.name = "rk30-adc",
.id = -1,
.num_resources = ARRAY_SIZE(rk30_adc_resource),
.resource = rk30_adc_resource,
};
#endif
#ifdef CONFIG_TSADC_RK30
static struct resource rk30_tsadc_resource[] = {
{
.start = IRQ_TSADC,
.end = IRQ_TSADC,
.flags = IORESOURCE_IRQ,
},
{
.start = RK30_TSADC_PHYS,
.end = RK30_TSADC_PHYS + RK30_TSADC_SIZE - 1,
@@ -994,6 +1016,9 @@ static int __init rk30_init_devices(void)
#ifdef CONFIG_LCDC_RK30
platform_device_register(&device_lcdc);
#endif
#ifdef CONFIG_ADC_RK30
platform_device_register(&device_adc);
#endif
#ifdef CONFIG_TSADC_RK30
platform_device_register(&device_tsadc);
#endif

View File

@@ -4,6 +4,7 @@
menuconfig ADC
bool "ADC support"
depends on PLAT_RK
default y
if ADC

View File

@@ -13,16 +13,25 @@ config ADC_NULL
config ADC_RK28
bool "RK28 adc interface"
depends on ARCH_RK2818 || ARCH_RK2808 || ARCH_RK2816
help
This supports the use of the ADC interface on rk28 processors.
config ADC_RK29
bool "RK29 adc interface"
depends on ARCH_RK29
help
This supports the use of the ADC interface on rk29 processors.
config ADC_RK30
bool "RK30 adc interface"
depends on ARCH_RK30
help
This supports the use of the ADC interface on rk30 processors.
endchoice
config TSADC_RK30
bool "RK30 tsadc interface"
depends on ARCH_RK30
help
This supports the use of the TSADC interface on rk30 processors.
This supports the use of the TSADC interface on rk30 processors.

View File

@@ -3,4 +3,5 @@
#
obj-$(CONFIG_ADC_RK28) += rk28_adc.o
obj-$(CONFIG_ADC_RK29) += rk29_adc.o
obj-$(CONFIG_TSADC_RK30) += rk30_tsadc.o
obj-$(CONFIG_ADC_RK29) += rk30_adc.o
obj-$(CONFIG_TSADC_RK30) += rk30_tsadc.o

271
drivers/adc/plat/rk30_adc.c Executable file
View File

@@ -0,0 +1,271 @@
/* drivers/adc/chips/rk30_adc.c
*
* This program is free software; you can redistribute it and/or modify
* 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/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/adc.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include "rk30_adc.h"
//#define ADC_TEST
struct rk30_adc_device {
int irq;
void __iomem *regs;
struct clk * clk;
struct resource *ioarea;
struct adc_host *adc;
};
static void rk30_adc_start(struct adc_host *adc)
{
struct rk30_adc_device *dev = adc_priv(adc);
int chn = adc->cur->chn;
writel(0, dev->regs + ADC_CTRL);
writel(ADC_CTRL_POWER_UP|ADC_CTRL_CH(chn), dev->regs + ADC_CTRL);
udelay(SAMPLE_RATE);
writel(readl(dev->regs + ADC_CTRL)|ADC_CTRL_IRQ_ENABLE|ADC_CTRL_START,
dev->regs + ADC_CTRL);
return;
}
static void rk30_adc_stop(struct adc_host *adc)
{
struct rk30_adc_device *dev = adc_priv(adc);
writel(0, dev->regs + ADC_CTRL);
}
static int rk30_adc_read(struct adc_host *adc)
{
struct rk30_adc_device *dev = adc_priv(adc);
udelay(SAMPLE_RATE);
return readl(dev->regs + ADC_DATA) & ADC_DATA_MASK;
}
static irqreturn_t rk30_adc_irq(int irq, void *data)
{
struct rk30_adc_device *dev = data;
adc_core_irq_handle(dev->adc);
return IRQ_HANDLED;
}
static const struct adc_ops rk30_adc_ops = {
.start = rk30_adc_start,
.stop = rk30_adc_stop,
.read = rk30_adc_read,
};
#ifdef ADC_TEST
struct adc_test_data {
struct adc_client *client;
struct timer_list timer;
struct work_struct timer_work;
};
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);
return;
}
static void adc_timer(unsigned long data)
{
//int sync_read = 0;
struct adc_test_data *test=(struct adc_test_data *)data;
//sync_read = adc_sync_read(test->client);
//dev_info(test->client->adc->dev, "[chn%d] sync_read = %d\n", 0, sync_read);
schedule_work(&test->timer_work);
add_timer(&test->timer);
}
static void adc_timer_work(struct work_struct *work)
{
int sync_read = 0;
struct adc_test_data *test = container_of(work, struct adc_test_data,
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", 0, sync_read);
}
static int rk30_adc_test(void)
{
struct adc_test_data *test = NULL;
test = kzalloc(sizeof(struct adc_test_data), GFP_KERNEL);
test->client = adc_register(0, callback, NULL);
INIT_WORK(&test->timer_work, adc_timer_work);
setup_timer(&test->timer, adc_timer, (unsigned long)test);
test->timer.expires = jiffies + 100;
add_timer(&test->timer);
return 0;
}
#endif
static int rk30_adc_probe(struct platform_device *pdev)
{
struct adc_host *adc = NULL;
struct rk30_adc_device *dev;
struct resource *res;
int ret;
adc = adc_alloc_host(sizeof(struct rk30_adc_device), &pdev->dev);
if (!adc)
return -ENOMEM;
spin_lock_init(&adc->lock);
adc->dev = &pdev->dev;
adc->is_suspended = 0;
adc->ops = &rk30_adc_ops;
dev = adc_priv(adc);
dev->adc = adc;
dev->irq = platform_get_irq(pdev, 0);
if (dev->irq <= 0) {
dev_err(&pdev->dev, "failed to get adc irq\n");
ret = -ENOENT;
goto err_alloc;
}
ret = request_irq(dev->irq, rk30_adc_irq, 0, pdev->name, dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to attach adc irq\n");
goto err_alloc;
}
dev->clk = clk_get(&pdev->dev, "saradc");
if (IS_ERR(dev->clk)) {
dev_err(&pdev->dev, "failed to get adc clock\n");
ret = PTR_ERR(dev->clk);
goto err_irq;
}
ret = clk_set_rate(dev->clk, ADC_CLK_RATE * 1000 * 1000);
if(ret < 0) {
dev_err(&pdev->dev, "failed to set adc clk\n");
goto err_clk;
}
clk_enable(dev->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto err_clk;
}
dev->ioarea = request_mem_region(res->start, (res->end - res->start) + 1,
pdev->name);
if(dev->ioarea == NULL) {
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto err_clk;
}
dev->regs = ioremap(res->start, (res->end - res->start) + 1);
if (!dev->regs) {
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto err_ioarea;
}
platform_set_drvdata(pdev, dev);
dev_info(&pdev->dev, "rk30 adc: driver initialized\n");
return 0;
// err_iomap:
// iounmap(dev->regs);
err_ioarea:
release_resource(dev->ioarea);
kfree(dev->ioarea);
clk_disable(dev->clk);
err_clk:
clk_put(dev->clk);
err_irq:
free_irq(dev->irq, dev);
err_alloc:
adc_free_host(dev->adc);
return ret;
}
static int rk30_adc_remove(struct platform_device *pdev)
{
struct rk30_adc_device *dev = platform_get_drvdata(pdev);
iounmap(dev->regs);
release_resource(dev->ioarea);
kfree(dev->ioarea);
free_irq(dev->irq, dev);
clk_disable(dev->clk);
clk_put(dev->clk);
adc_free_host(dev->adc);
return 0;
}
#ifdef CONFIG_PM
static int rk30_adc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct rk30_adc_device *dev = platform_get_drvdata(pdev);
dev->adc->is_suspended = 1;
return 0;
}
static int rk30_adc_resume(struct platform_device *pdev)
{
struct rk30_adc_device *dev = platform_get_drvdata(pdev);
dev->adc->is_suspended = 0;
return 0;
}
#else
#define rk30_adc_suspend NULL
#define rk30_adc_resume NULL
#endif
static struct platform_driver rk30_adc_driver = {
.driver = {
.name = "rk30-adc",
.owner = THIS_MODULE,
},
.probe = rk30_adc_probe,
.remove = __devexit_p(rk30_adc_remove),
.suspend = rk30_adc_suspend,
.resume = rk30_adc_resume,
};
static int __init rk30_adc_init(void)
{
return platform_driver_register(&rk30_adc_driver);
}
subsys_initcall(rk30_adc_init);
static void __exit rk30_adc_exit(void)
{
platform_driver_unregister(&rk30_adc_driver);
}
module_exit(rk30_adc_exit);
MODULE_DESCRIPTION("Driver for ADC");
MODULE_AUTHOR("kfx, kfx@rock-chips.com");
MODULE_LICENSE("GPL");
static int __init adc_test_init(void)
{
#ifdef ADC_TEST
rk30_adc_test();
#endif
return 0;
}
module_init(adc_test_init);

29
drivers/adc/plat/rk30_adc.h Executable file
View File

@@ -0,0 +1,29 @@
/* drivers/adc/chips/rk29_adc.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#ifndef __ASM_RK30_ADC_H
#define __ASM_RK30_ADC_H
#define ADC_DATA 0x00
#define ADC_DATA_MASK 0x3ff
#define ADC_STAS 0x04
#define ADC_STAS_BUSY (1<<0)
#define ADC_CTRL 0x08
#define ADC_CTRL_CH(ch) (0x07 - ((ch)<<0))
#define ADC_CTRL_POWER_UP (1<<3)
#define ADC_CTRL_START (1<<4)
#define ADC_CTRL_IRQ_ENABLE (1<<5)
#define ADC_CTRL_IRQ_STATUS (1<<6)
#define ADC_CLK_RATE 1 //1M
#define SAMPLE_RATE (20/ADC_CLK_RATE) //20 CLK
#endif /* __ASM_RK30_ADC_H */