mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
rk30:add adc(saradc) support
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
menuconfig ADC
|
||||
bool "ADC support"
|
||||
depends on PLAT_RK
|
||||
default y
|
||||
if ADC
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
271
drivers/adc/plat/rk30_adc.c
Executable 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
29
drivers/adc/plat/rk30_adc.h
Executable 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 */
|
||||
Reference in New Issue
Block a user