会员
周边
众包
新闻
博问
闪存
赞助商
Chat2DB
所有博客
当前博客
我的博客
我的园子
账号设置
简洁模式
...
退出登录
注册
登录
张鹏的博客
嵌入式系统
sam9260 adc module
/*
* driver/char/at91_adc.c
*
* Copyright (C) 2007 Embedall Technology Co., Ltd.
*
* Analog-to-digital Converter(ADC) Driver.
*
* 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, or
* (at your option) any later version.
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/at91_adc.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#include <asm/arch/at91_adc.h>
#include <asm/arch/at91_tc.h>
#include <asm/arch/at91_pmc.h>
#define DRV_NAME "at91_adc"
#define DEV_NAME "adc0"
#define adc_readl(adc,reg) (__raw_readl((adc)->membase + (reg)))
#define adc_writel(adc,reg,v) (__raw_writel((v), (adc)->membase + (reg)))
#define tc_readl(adc,reg) (__raw_readl((adc)->tcxbase + (reg)))
#define tc_writel(adc,reg,v) (__raw_writel((v), (adc)->tcxbase + (reg)))
#define ADC_MAX_CHANNEL CONFIG_AT91_ADC_CHANNELS
#define BUF_SIZE 8
//#define buf_cnt(channel) (((channel)->head - (channel)->tail) & ((BUF_SIZE)-1))
//#define buf_space(channel) (((channel)->tail-((channel)->head+1))&(BUF_SIZE-1))
struct adc;
static
int minor_num = 0;
static
int major_num = 0;
struct adc_channel
{
struct cdev cdev;
struct device *dev;
struct class_device *class_dev;
int id;
int adc_data;
int head;
int tail;
struct fasync_struct *fasync;
struct adc *adc;
};
struct adc
{
dev_t devt;
struct
class *
class;
void __iomem *membase;
void __iomem *tcbbase;
void __iomem *tcxbase;
unsigned
int irq;
struct adc_mode mode;
spinlock_t lock;
struct adc_channel *channel[ADC_MAX_CHANNEL];
};
static
struct adc *adc;
static
inline
int buf_in(
struct adc_channel *channel,
int v)
{
channel->adc_data = v;
//channel->head = (channel->head + 1) & (BUF_SIZE - 1);
return channel->adc_data;
}
static
int adc_init_tc(
struct adc *adc)
{
unsigned
int dummy = 0;
spin_lock(&adc->lock);
if (adc->mode.trigger != ADC_TRIGGER_TIMER) {
at91_sys_write(AT91_PMC_PCDR, 1 << AT91SAM9260_ID_TC2);
spin_unlock(&adc->lock);
return 0;
}
tc_writel(adc, AT91_TC_CCR, AT91_TC_CLKDIS);
dummy |= (AT91_TC_TIMER_CLOCK5 | AT91_TC_CPCTRG | AT91_TC_WAVE |
AT91_TC_WAVESEL_UP_AUTO | AT91_TC_ACPA_SET |
AT91_TC_ACPC_CLEAR );
tc_writel(adc, AT91_TC_CMR, dummy);
if (adc->mode.trigger_time) {
dummy = (adc->mode.trigger_time*1000000)/(1000000000/32768);
if (dummy > 0xffff) dummy = 0xffff;
tc_writel(adc, AT91_TC_RC, dummy);
tc_writel(adc, AT91_TC_RA, dummy * 3 / 5);
}
else {
tc_writel(adc, AT91_TC_RC, 32768);
tc_writel(adc, AT91_TC_RA, 32768 * 3 / 5);
}
at91_sys_write(AT91_PMC_PCER, 1 << AT91SAM9260_ID_TC2);
tc_writel(adc, AT91_TC_CCR, AT91_TC_CLKEN | AT91_TC_SWTRG);
spin_unlock(&adc->lock);
return 0;
}
static
int adc_hw_init(
struct adc *adc)
{
adc_writel(adc, AT91_ADC_CR, AT91_ADC_SWRST);
adc_writel(adc, AT91_ADC_IER, AT91_ADC_DRDY);
at91_sys_write(AT91_PMC_PCER, 1 << adc->irq);
return 0;
}
static
int adc_fasync(
int fd,
struct file *file,
int mode)
{
struct adc_channel *channel = file->private_data;
return fasync_helper(fd, file, mode, &channel->fasync);
}
static
int adc_open(
struct inode *inode,
struct file *file)
{
struct adc *adc;
struct adc_channel *channel;
channel = container_of(inode->i_cdev,
struct adc_channel, cdev);
file->private_data = channel;
adc = channel->adc;
spin_lock(&adc->lock);
at91_set_multi_drive(PIN_BASE + 0x40 + channel->id, 1);
adc_writel(adc, AT91_ADC_IER, (1 << channel->id));
adc_writel(adc, AT91_ADC_CHER, (1 << channel->id));
spin_unlock(&adc->lock);
return nonseekable_open(inode, file);
}
static
int adc_release(
struct inode *inode,
struct file *file)
{
struct adc *adc;
struct adc_channel *channel;
channel = container_of(inode->i_cdev,
struct adc_channel, cdev);
adc = channel->adc;
// adc_fasync(-1, file, 0);
spin_lock(&adc->lock);
adc_writel(adc, AT91_ADC_IDR, 1 << channel->id);
adc_writel(adc, AT91_ADC_CHDR, 1 << channel->id);
spin_unlock(&adc->lock);
return 0;
}
static ssize_t adc_read(
struct file *file,
char __user *buf,
size_t count, loff_t *f_pos)
{
return 0;
}
static ssize_t adc_write(
struct file *file,
const
char __user *buf,
size_t count, loff_t *f_pos)
{
return 0;
}
/* static void adc_sigio(struct adc_channel *channel) */
/* { */
/* if (channel->fasync) */
/* kill_fasync(&channel->fasync, SIGIO, POLL_IN); */
/* } */
static
int adc_ioctl(
struct inode *inode,
struct file *file,
unsigned
int cmd, unsigned
long arg)
{
struct adc *adc;
struct adc_channel *channel;
struct adc_mode *mode;
int ret = 0;
unsigned
int dummy = 0;
void __user *argp = (
void __user *)arg;
int __user *p = argp;
channel = container_of(inode->i_cdev,
struct adc_channel, cdev);
adc = channel->adc;
mode = &adc->mode;
switch (cmd) {
case ADCCTL_RESET:
adc_writel(adc, AT91_ADC_CR, AT91_ADC_SWRST);
return 0;
case ADCCTL_START:
adc_writel(adc, AT91_ADC_CR, AT91_ADC_START);
return 0;
case ADCCTL_SETMODE:
ret = copy_from_user(mode, argp,
sizeof(
struct adc_mode));
if (mode->trigger == ADC_TRIGGER_TIMER)
dummy |= AT91_ADC_TRGEN | AT91_ADC_TRGSEL_TC2;
else
if (mode->trigger == ADC_TRIGGER_EXT)
dummy |= AT91_ADC_TRGEN | AT91_ADC_TRGSEL_EXT;
if (mode->resolution & ADC_M_8BIT)
dummy |= AT91_ADC_LOWRES;
if (mode->sleep_mode)
dummy |= AT91_ADC_SLEEP;
if (mode->adc_clock)
dummy |= AT91_ADC_PRESCAL_(mode->adc_clock);
if (mode->startup_time)
dummy |= AT91_ADC_STARTUP_(mode->startup_time);
if (mode->sample_time)
dummy |= AT91_ADC_SHTIM_(mode->sample_time);
adc_init_tc(adc);
spin_lock(&adc->lock);
adc_writel(adc, AT91_ADC_MR, dummy);
spin_unlock(&adc->lock);
return 0;
case ADCCTL_GETMODE:
ret = copy_to_user(argp, mode,
sizeof(
struct adc_mode));
return 0;
case ADCCTL_GETDATA:
// ret = buf_cnt(channel);
// if (ret > 0) {
// if (!put_user(channel->buf[channel->tail], p))
// channel->tail = (channel->tail + 1) & (BUF_SIZE -1);
//return 0;
//}
put_user(channel->adc_data,p);
return -EFAULT;
// case ADCCTL_GETCNT:
// return put_user(buf_cnt(channel), p);
case ADCCTL_GETSTATUS:
return put_user(adc_readl(adc, AT91_ADC_SR), p);
default:
return -EINVAL;
}
return -EINVAL;
}
static
struct file_operations adc_fops = {
.owner = THIS_MODULE,
.read = adc_read,
.write = adc_write,
.open = adc_open,
.release = adc_release,
.ioctl = adc_ioctl,
.fasync = adc_fasync,
};
static irqreturn_t adc_interrupt(
int irq,
void *dev_id)
{
struct adc *adc = dev_id;
struct adc_channel *channel;
unsigned
int status;
status = adc_readl(adc, AT91_ADC_SR) & adc_readl(adc, AT91_ADC_IMR);
while (status) {
printk(KERN_DEBUG
"at91_adc: interrupt status reg 0x%08x\n",
status);
if (status & AT91_ADC_EOC0){
channel = adc->channel[0];
buf_in(channel, adc_readl(adc, AT91_ADC_CDR0));
printk(KERN_INFO
"the adc_data is %d\n",channel->adc_data);
// adc_sigio(channel);
}
if (status & AT91_ADC_EOC1){
channel = adc->channel[1];
buf_in(channel, adc_readl(adc, AT91_ADC_CDR1));
printk(KERN_INFO
"the adc_data is %d\n",channel->adc_data);
// adc_sigio(channel);
}
if (status & AT91_ADC_EOC2){
channel = adc->channel[2];
buf_in(channel, adc_readl(adc, AT91_ADC_CDR2));
printk(KERN_INFO
"the adc_data is %d\n",channel->adc_data);
// adc_sigio(channel);
}
if (status & AT91_ADC_EOC3) {
channel = adc->channel[3];
buf_in(channel, adc_readl(adc, AT91_ADC_CDR3));
printk(KERN_INFO
"the adc_data is %d\n",channel->adc_data);
// adc_sigio(channel);
}
if (status & AT91_ADC_DRDY)
adc_readl(adc, AT91_ADC_LCDR);
status = adc_readl(adc, AT91_ADC_SR) & adc_readl(adc, AT91_ADC_IMR);
}
return IRQ_HANDLED;
}
static
int __exit adc_remove(
struct platform_device *pdev)
{
struct adc_channel *channel = platform_get_drvdata(pdev);
class_device_unregister(channel->class_dev);
cdev_del(&channel->cdev);
kfree(channel);
return 0;
}
static
struct platform_device adc_channel_device[ADC_MAX_CHANNEL];
/*
static int __init adc_add_channel_device(void)
{
int i;
for (i=0; i<ADC_MAX_CHANNEL; i++){
adc_channel_device[i].name = DRV_NAME;
adc_channel_device[i].id = i;
platform_device_register(&adc_channel_device[i]);
}
return 0;
}
*/
static
int __init adc_init(
void)
{
struct adc_channel *channel;
int ret = 0;
//struct adc_channel *channel;
int ret1;
adc = kmalloc(
sizeof(
struct adc), GFP_KERNEL);
if (!adc)
return -ENOMEM;
if (!request_mem_region(AT91SAM9260_BASE_ADC, SZ_16K, DRV_NAME)){
kfree(adc);
return -EBUSY;
}
adc->membase = ioremap(AT91SAM9260_BASE_ADC, SZ_16K);
if (adc->membase == NULL)
goto adc_release_mem;
if (!request_mem_region(AT91SAM9260_BASE_TC0, SZ_16K, DRV_NAME))
goto adc_iounmap;
adc->tcbbase = ioremap(AT91SAM9260_BASE_TC0, SZ_16K);
if (adc->tcbbase == NULL)
goto adc_release_mem_tc;
adc->irq = AT91SAM9260_ID_ADC;
adc->tcxbase = adc->tcbbase + 0x80;
spin_lock_init(&adc->lock);
adc->
class = class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(adc->
class)){
printk(KERN_ERR
"at91_adc: faile to create device class\n");
goto adc_iounmap_tc;
}
ret = alloc_chrdev_region(&adc->devt, 0, 1, DEV_NAME);
if (ret < 0) {
printk(KERN_ERR
"%s: failed to allocate dev region\n", __FILE__);
goto adc_destroy_class;
}
if (request_irq(adc->irq, adc_interrupt, IRQF_SHARED, DRV_NAME, adc)){
printk(KERN_ERR
"%s: request irq failed\n", __FILE__);
goto adc_del_channel;
}
adc_hw_init(adc);
printk(KERN_INFO
"&&123456xx\n");
//platform_driver_register(&adc_driver);
printk(KERN_INFO
"Analog-to-Digital Converter (irq %d)\n", adc->irq);
// adc_add_channel_device();
printk(KERN_ERR
"&&123456&&\n");
channel = kmalloc(
sizeof(
struct adc_channel), GFP_KERNEL);
if (!channel){
printk(KERN_ERR
"&&123456&&\n");
//printk(KERN_ERR "at91_adc: failed to kmalloc channel %d\n", pdev->id);
return -ENOMEM;
}
// channel->fasync = kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
// if (!channel->fasync) return -ENOMEM;
channel->id = 0;
//channel->dev = &pdev->dev;
channel->adc = adc;
channel->head = 0;
channel->tail = 0;
cdev_init(&channel->cdev, &adc_fops);
channel->cdev.owner = THIS_MODULE;
ret1 = cdev_add(&channel->cdev, MKDEV(MAJOR(adc->devt), channel->id), 1);
if (ret1) {
//printk(KERN_ERR "at91_adc: failed to add channel %d device\n", pdev->id);
kfree(channel);
return ret1;
}
channel->class_dev = class_device_create(adc->
class, NULL,
MKDEV(MAJOR(adc->devt),
channel->id),
NULL,
DEV_NAME);
if (IS_ERR(channel->class_dev)) {
cdev_del(&channel->cdev);
kfree(channel);
return PTR_ERR(channel->class_dev);
}
adc->channel[channel->id] = channel;
//platform_set_drvdata(pdev, channel);
printk(KERN_INFO
"at91_adc. major is %d\n",
MAJOR(adc->devt));
return 0;
adc_del_channel:
unregister_chrdev_region(adc->devt, 1);
adc_destroy_class:
class_destroy(adc->
class);
adc_iounmap_tc:
iounmap(adc->tcbbase);
adc_release_mem_tc:
release_mem_region(AT91SAM9260_BASE_TC0, SZ_16K);
adc_iounmap:
iounmap(adc->membase);
adc_release_mem:
release_mem_region(AT91SAM9260_BASE_ADC, SZ_16K);
kfree(adc);
return ret;
}
static
void __exit adc_exit(
void)
{
//platform_driver_unregister(&adc_driver);
unregister_chrdev_region(adc->devt, ADC_MAX_CHANNEL);
class_destroy(adc->
class);
free_irq(adc->irq, adc);
iounmap(adc->tcbbase);
release_mem_region(AT91SAM9260_BASE_TC0, SZ_16K);
iounmap(adc->membase);
release_mem_region(AT91SAM9260_BASE_ADC, SZ_16K);
kfree(adc);
}
module_init(adc_init);
module_exit(adc_exit);
MODULE_AUTHOR(
"jiang ming bo ");
MODULE_LICENSE(
"GPL");
MODULE_DESCRIPTION(
"AT91 Analog-to-Digital Converter Driver");
posted on
2016-09-12 09:44
嵌入式操作系统
阅读(
264
) 评论(
0
)
编辑
收藏
举报
刷新页面
返回顶部
导航
博客园
首页
新随笔
联系
订阅
管理
公告