主题 : 地址映射(在驱动程序里读写寄存器) 复制链接 | 浏览器收藏 | 打印
级别: 新手上路
UID: 21867
精华: 0
发帖: 18
金钱: 90 两
威望: 18 点
综合积分: 36 分
注册时间: 2010-05-23
最后登录: 2011-05-12
楼主  发表于: 2010-07-03 14:33

 地址映射(在驱动程序里读写寄存器)

在编写spi的驱动程序的时候遇到了很多问题,望那个高手指点一二。
我用spi+AD7888来进行数据采集。要对spi的相关的几个寄存器进行操作。也就是在驱动程序里要读写一些相关的寄存器,但是出现了问题。我的相关代码如下:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int __init spi_adc_init(void)
{
    int ret;
    dev_t dev=0;
    base_addr=ioremap(S3C2410_PA_SPI,0x20); //remap physics address to vitrual address
    if(base_addr==NULL)//deal with error
    {
        printk(KERN_ERR "K:Failed to remap register block\n");
        return -ENOMEM;
    }
    
    if(adc_major)//major=0
    {
        dev=MKDEV(adc_major,adc_minor);
        ret=register_chrdev_region(dev,adc_number,DEVICE_NAME);
    }
    else//alloc device major
    {
        ret=alloc_chrdev_region(&dev,adc_minor,adc_number,DEVICE_NAME);
        adc_major=MAJOR(dev);
    }
    if(ret<0)
    {
        printk(KERN_WARNING "K:can't get major %d\n",adc_major);
        return ret;
    }
  
    init_MUTEX(&devp->sem);  //inital semaphore sem=1
    adc_setup_cdev(&devp);
    printk(KERN_NOTICE "K:initial!\n");
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
base_addr=ioremap(S3C2410_PA_SPI,0x20); 这一句进行了地址映射。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*remap VA to PA*/
static void __iomem *base_addr;
#define SPCON0   (*(volatile unsigned long*)(base_addr+S3C2410_SPCON))
#define SPSTA0   (*(volatile unsigned long*)(base_addr+S3C2410_SPSTA))
#define SPPRE0   (*(volatile unsigned long*)(base_addr+S3C2410_SPPRE))
#define SPTDAT0  (*(volatile unsigned long*)(base_addr+S3C2410_SPTDAT))
#define SPRDAT0  (*(volatile unsigned long*)(base_addr+S3C2410_SPRDAT))
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
上面的代码进一步抽象相关的寄存器。(这里有没有什么问题)请高手指点。

在下面的代码中我这样使用了这些寄存器。会不会有什么问题。

static void configure_spi_register(void)
{ //set prescaler value baud=PCLK/2/(Prescaler+1)=2MHZ(AD7888 fsclk=2MHZ)
    int i;
    char control_reg=0;
    SPPRE0=0x32;
    //poll,sck enable,major,sytle=A;regs-spi.h
    control_reg=S3C2410_SPCON_SMOD_POLL|
                    S3C2410_SPCON_ENSCK|
                     S3C2410_SPCON_MSTR|
                S3C2410_SPCON_CPOL_HIGH|
                S3C2410_SPCON_CPHA_FMTB|
                                  (0<<0);
    SPCON0=control_reg;
    for(i=0;i<10;i++)
        SPTDAT0=0xff;
    printk(KERN_ALERT "K:spi registers configured!\n");  
}
在加载模块的时候出现了这样的问题:
Unable to handle kernel NULL pointer dereference at virtual address 00000044
pgd = c3b20000
[00000044] *pgd=33af6031, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#1]
last sysfs file: /sys/devices/virtual/vc/vcs4/dev
Modules linked in: spi_adc(+)
CPU: 0    Not tainted  (2.6.32.2-FriendlyARM #1)
PC is at spi_adc_init+0xa4/0x164 [spi_adc]
LR is at __register_chrdev_region+0x17c/0x198
pc : [<bf0030a4>]    lr : [<c00a06f4>]    psr: 60000013
sp : c3af5f08  ip : c3af5ec8  fp : c3af5f34
r10: bf003000  r9 : 00000000  r8 : bf0004c4
r7 : bf000650  r6 : bf000530  r5 : 000a18b7  r4 : 00000000
r3 : 00000000  r2 : 00000001  r1 : 00000040  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: c000717f  Table: 33b20000  DAC: 00000015
Process insmod (pid: 735, stack limit = 0xc3af4270)
Stack: (0xc3af5f08 to 0xc3af6000)
5f00:                   c482b828 0fd00000 c482bcb8 000015eb 000a18b7 bf000530
5f20: c3af4000 c0503660 c3af5f7c c3af5f38 c003032c bf003010 00000000 00000000
5f40: 00000000 000015eb 000a18b7 bf000530 00000000 000015eb 000a18b7 bf000530
5f60: 00000000 c00310c8 c3af4000 00000000 c3af5fa4 c3af5f80 c0070344 c0030300
5f80: c009b6b0 c009b5a0 4001d838 00000000 be9dee88 00000080 00000000 c3af5fa8
5fa0: c0030f20 c0070284 4001d838 00000000 000b5038 000015eb 000a18b7 00000000
5fc0: 4001d838 00000000 be9dee88 00000080 be9dee84 be9dee88 00000001 00000000
5fe0: 00000069 be9deb54 0001778c 401d3c24 60000010 000b5038 31cc334c 33cc23cc
Backtrace:
[<bf003000>] (spi_adc_init+0x0/0x164 [spi_adc]) from [<c003032c>] (do_one_initca
ll+0x3c/0x1c8)
r8:c0503660 r7:c3af4000 r6:bf000530 r5:000a18b7 r4:000015eb
[<c00302f0>] (do_one_initcall+0x0/0x1c8) from [<c0070344>] (sys_init_module+0xd0
/0x204)
[<c0070274>] (sys_init_module+0x0/0x204) from [<c0030f20>] (ret_fast_syscall+0x0
/0x28)
r7:00000080 r6:be9dee88 r5:00000000 r4:4001d838
Code: eafffff8 e597300c e3a02001 e2831040 (e5831044)
---[ end trace 6c173b036c7a3617 ]---
Segmentation fault
l
但是模块加载上了:

[root@FriendlyARM /mnt]# lsmod
spi_adc 4139 1 - Loading 0xbf000000
[root@FriendlyARM /mnt]# mknod /dev/spi_adc c 231 0
[root@FriendlyARM /mnt]# cd /dev
[root@FriendlyARM /dev]# ls
adc                 root                tty42
audio               rtc                 tty43
backlight           rtc0                tty44
buttons             shm                 tty45
camera              spi_adc             tty46


这个问题困扰我很久了,希望得到牛人的指点。
级别: 骑士
UID: 11942
精华: 1
发帖: 144
金钱: 775 两
威望: 155 点
综合积分: 308 分
注册时间: 2009-12-25
最后登录: 2022-01-16
1楼  发表于: 2010-07-04 09:23
"LR is at __register_chrdev_region+0x17c/0x198"
级别: 新手上路
UID: 21867
精华: 0
发帖: 18
金钱: 90 两
威望: 18 点
综合积分: 36 分
注册时间: 2010-05-23
最后登录: 2011-05-12
2楼  发表于: 2010-07-04 23:36

 回 1楼(cole3) 的帖子

我还是不太明白,请大侠明说。
这里的注册我是使用了LDD3里说的方法,我以前是用miscdevice的方法一注册的,也是出现的同样的错误。
Unable to handle kernel NULL pointer dereference at virtual address 00000044
pgd = c3b20000
。。。。。。。。。。。。。。。。。。。。。。。。。。。。
*無鈳取玳
级别: 论坛版主
UID: 27
精华: 12
发帖: 5398
金钱: 40120 两
威望: 17929 点
综合积分: 11036 分
注册时间: 2008-01-16
最后登录: 2014-11-22
3楼  发表于: 2010-07-05 11:13
目前最新的2.6.32.2内核里已经包含了对S3C2440 SPI Controller的支持,驱动源代码在
drivers/spi/spi_s3c24xx.c. 论坛上也有很多这方面的帖子,你可以参考。
如果你想别人帮你了解你的问题所在,最好把完整的内核模块源代码和相关的头文件附上来。
"If you have an apple and I have an apple and we exchange apples, then you and I will
still each have one apple. But if you have an idea and I have an idea and we exchange
these ideas, then each of us will have two ideas."
级别: 新手上路
UID: 21867
精华: 0
发帖: 18
金钱: 90 两
威望: 18 点
综合积分: 36 分
注册时间: 2010-05-23
最后登录: 2011-05-12
4楼  发表于: 2010-07-05 20:04

 回 3楼(kasim) 的帖子

你好,麻烦你了,我看不明白内核中的spi子系统的代码,因为新手,所以只能当做一般的字符驱动来做了。
以下是我的代码:注册驱动参考的LDD3的scull驱动的方法。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>  
#include <linux/init.h>    //module init
#include <linux/errno.h>   //error number
#include <asm/uaccess.h>   //copy_to_user copy_from_user
#include <linux/delay.h>
#include <asm/io.h>         //iowrite8()
#include <linux/gpio.h>

#include <mach/regs-gpioj.h>   //gpio-j
#include <mach/regs-gpio.h>    //gpio ports
#include <plat/regs-spi.h>     //spi registers
#include <mach/hardware.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/semaphore.h>

#define DEVICE_NAME  "spi_adc"   //device name

int adc_major=231;
int adc_minor=0;
int adc_number=1;

#define CHANNAL_SELECT  1  //select channal command

static void configure_spi_pin(void);
static void configure_spi_register(void);
static void ad_convert(void);
static void spi_tx_data(unsigned char data);
static void spi_poll_done(void);

unsigned char dataTX=0; //send data ,select channal
unsigned char dataRX_0=0;//receive data 0
unsigned char dataRX_1=0;//receive data 1
unsigned int  convert_data=0;//read data from AD

struct adc_dev{
    struct cdev cdev;
    struct semaphore sem;
};

struct adc_dev *devp;

/*remap PA to VA*/
static void __iomem *base_addr;

/*spi ralate pin(GPJ12\GPG11\GPG12\GPG13) configure*/
static void configure_spi_pin(void)
{/*disable gpio pullup for spi*/
    s3c2410_gpio_pullup(S3C2440_GPJ12,1); //0=enable
    s3c2410_gpio_pullup(S3C2410_GPG(13),1);
    s3c2410_gpio_pullup(S3C2410_GPG(12),1);
    s3c2410_gpio_pullup(S3C2410_GPG(11),1);

    /*configure I/O ports as spi function*/
    s3c2410_gpio_cfgpin(S3C2440_GPJ12,S3C2440_GPJ12_OUTP);    //CS
    s3c2410_gpio_cfgpin(S3C2410_GPE(13),S3C2410_GPE13_SPICLK0); //SPICLK0
    s3c2410_gpio_cfgpin(S3C2410_GPE(12),S3C2410_GPE12_SPIMOSI0);//SPIMOSI0
    s3c2410_gpio_cfgpin(S3C2410_GPE(11),S3C2410_GPE11_SPIMISO0);//SPIMISO0
    printk(KERN_ALERT "K:pins function configured!\n");
    s3c2410_gpio_setpin(S3C2440_GPJ12,1);  
}

static void configure_spi_register(void)
{ //set prescaler value baud=PCLK/2/(Prescaler+1)=2MHZ(AD7888 fsclk=2MHZ)
    int i;
    char control_reg=0;
   __raw_writeb(0x32,base_addr+S3C2410_SPPRE);
    //poll,sck enable,major,sytle=A;regs-spi.h
    control_reg=S3C2410_SPCON_SMOD_POLL|
                    S3C2410_SPCON_ENSCK|
                     S3C2410_SPCON_MSTR|
                S3C2410_SPCON_CPOL_HIGH|
                S3C2410_SPCON_CPHA_FMTB|
                                  (0<<0);
    __raw_writeb(control_reg,base_addr+S3C2410_SPCON);
    for(i=0;i<10;i++)
        __raw_writeb(0xff,base_addr+S3C2410_SPTDAT);
    printk(KERN_ALERT "K:spi registers configured!\n");  
}

static void spi_poll_done(void)
{
    while(!(__raw_readb(base_addr+S3C2410_SPSTA)&0x01));  
}
/*
static void spi_tx_data(unsigned char data)
{
     __raw_writeb(data,base_addr+S3C2410_SPTDAT);
}*/

static void ad_convert(void)
{
    configure_spi_register();

    s3c2410_gpio_setpin(S3C2440_GPJ12,0);
    spi_poll_done();
    __raw_writeb(dataTX,base_addr+S3C2410_SPTDAT);;
    __raw_writeb(0xff,base_addr+S3C2410_SPTDAT);
    spi_poll_done();
    dataRX_0=__raw_readb(base_addr+S3C2410_SPRDAT);
    __raw_writeb(0xff,base_addr+S3C2410_SPTDAT);
    spi_poll_done();
    dataRX_1=__raw_readb(base_addr+S3C2410_SPRDAT);
    s3c2410_gpio_setpin(S3C2440_GPJ12,1);

    printk(KERN_ALERT "K: dataRX_0=%d\n",dataRX_0);
    printk(KERN_ALERT "K: dataRX_1=%d\n",dataRX_1);
}

static int spi_adc_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
switch(cmd)
    {
        case CHANNAL_SELECT:
            dataTX=arg;
            printk(KERN_ALERT "K:receive data=%d from user space\n",dataTX);
            break;
        default:
            return -ENOTTY;
    }
    dataTX<<=3;
    dataTX|=0X04;
    //DONTC ZERO ADD2 ADD1 ADD0 REF PM1 PM0
    //  0    0                   1   0   0
    printk(KERN_ALERT "K:change dataTX=%d\n",dataTX);
return 0;
}

static int spi_adc_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)
{
    struct adc_dev *dev=filp->private_data;
    int ret;
    if(filp->f_flags&O_NONBLOCK) //read block
    {
        ret= -EAGAIN;
        goto out;
    }
    if(down_interruptible(&dev->sem)) //own the sem
    {
        return -ERESTARTSYS;
    }
    ad_convert();
    ad_convert();
    convert_data=dataRX_0;
    convert_data<<=8;
    convert_data+=dataRX_1;
    convert_data&=0x0fff;
    printk(KERN_ALERT "K:send data=%d/tto user space\n",convert_data);
    ret=copy_to_user(buf,&convert_data,count); //success ret=0
    up(&dev->sem);  //release samephore sem
out:return ret;
}

static int spi_adc_open(struct inode *inode,struct file *filp)
{
    struct adc_dev *dev; //device information
    dev=container_of(inode->i_cdev,struct adc_dev,cdev);
    filp->private_data=dev; //for other methors
    printk(KERN_ALERT "K:spi device opened\n");

    configure_spi_pin();
return 0;
}
static int spi_adc_close(struct inode *inode,struct file *filp)
{
    printk(KERN_ALERT "K:spi device closed\n");
    s3c2410_gpio_setpin(S3C2440_GPJ12,1);  //set CS high , chip select canceled
return 0;
}

static struct file_operations spi_adc_fops={
    .owner    =THIS_MODULE,
    .open    =spi_adc_open,
    .ioctl    =spi_adc_ioctl,
    .read    =spi_adc_read,
    .release=spi_adc_close,
};
/*setup char device*/
static void adc_setup_cdev(struct adc_dev *dev)
{
    int err;
    int devno=MKDEV(adc_major,adc_minor);
    cdev_init(&dev->cdev,&spi_adc_fops);
    dev->cdev.owner=THIS_MODULE;
    dev->cdev.ops=&spi_adc_fops;
    err=cdev_add(&dev->cdev,devno,adc_number);
    if(err)
        printk(KERN_NOTICE "K:Error %d\n",err);
}

static int __init spi_adc_init(void)
{
    int ret;
    dev_t dev=0;
//remap physics address to vitrual address
    base_addr=ioremap(S3C2410_PA_SPI,0x20);
    if(base_addr==NULL)//deal with error
    {
        printk(KERN_ERR "K:Failed to remap register block\n");
        return -ENOMEM;
    }
    
    if(adc_major)
    {
        dev=MKDEV(adc_major,adc_minor);
        ret=register_chrdev_region(dev,adc_number,"spi_adc");
    }
    else//alloc device major
    {
        ret=alloc_chrdev_region(&dev,adc_minor,adc_number,"spi_adc");
        adc_major=MAJOR(dev);
    }
    if(ret<0)
    {
        printk(KERN_WARNING "K:can't get major %d\n",adc_major);
        return ret;
    }
  
    init_MUTEX(&devp->sem);  //inital semaphore sem=1
    adc_setup_cdev(devp);
    printk(KERN_NOTICE "K:initial!\n");
return 0;
}

static void __exit spi_adc_exit(void)
{
    int devno=0;
    iounmap(base_addr);
    devno=MKDEV(adc_major,adc_minor);
    cdev_del(&devp->cdev);
    unregister_chrdev_region(devno,adc_number);
    printk(KERN_NOTICE "K:exit!\n");
}

module_init(spi_adc_init);
module_exit(spi_adc_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("http://amwha.cublog.cn");
MODULE_DESCRIPTION("serial adc7888 convert");
级别: 新手上路
UID: 21867
精华: 0
发帖: 18
金钱: 90 两
威望: 18 点
综合积分: 36 分
注册时间: 2010-05-23
最后登录: 2011-05-12
5楼  发表于: 2010-10-01 09:49
没有人回答,我自己来答
在驱动程序里读写寄存器分两步。
第一步:把硬件的首地址映射到内核空间中去(ioremap())
第二步:使用ioread8,iowrite8,........... 等一系统的io内存操作函数,以基地址加偏移的方法读写。

在上面的这一段代码中,主要的问题是没有分配内存,也就是没有为cdev结构分配内存空间,即kmalloc()
级别: 新手上路
UID: 43354
精华: 0
发帖: 17
金钱: 85 两
威望: 17 点
综合积分: 34 分
注册时间: 2011-04-16
最后登录: 2017-09-13
6楼  发表于: 2012-08-03 11:44
为什么我映射后无法读写呢,写入的值和读出的值不一样