• imx51-linux的cpuinfo之分析


    这两天客户提出来,我们的平板cat /proc/cpuinfo出来的信息中的serial怎么是0.
    客户就是上帝啊,没办法,分析找问题贝。
    我们先看一下目前的cat /proc/cpuinfo的信息:
    Processor       : ARMv7 Processor rev 5 (v7l)                                   
    BogoMIPS        : 799.53                                                        
    Features        : swp half thumb fastmult vfp edsp neon vfpv3                   
    CPU implementer : 0x41                                                          
    CPU architecture: 7                                                             
    CPU variant     : 0x2                                                           
    CPU part        : 0xc08                                                         
    CPU revision    : 5                                                             
                                                                                    
    Hardware        : Freescale MX51 F101 Board                                     
    Revision        : 51030                                                         
    Serial          : 0000000000000000 

    我们找到kernel中的cpuinfo的文件,路径在fs/proc/cpuinfo.c 。
    我们首先看一下它的init函数:
    static int __init proc_cpuinfo_init(void)
    {
        proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);
        return 0;
    }
    嗯,很明星,我们cat /proc/cpuinfo的文件就是在该init中创建的。我们注意到创建该文件时传入了fops。我们再看一下proc_cpuinfo_operations这个fops定义:
    static const struct file_operations proc_cpuinfo_operations = {
        .open        = cpuinfo_open,
        .read        = seq_read,
        .llseek        = seq_lseek,
        .release    = seq_release,
    };

    我们执行cat /proc/cpuinfo时实际就是执行了open和read这两个函数。
    我们下面分别分析一下open和read分别做了什么事情。
    1,open
    open定义如下:
    static int cpuinfo_open(struct inode *inode, struct file *file)
    {
        return seq_open(file, &cpuinfo_op);
    }
    我们发现调用open的时候传入了cpuinfo_op这个结构。这个结构就是cpuinfo的实际操作方法。这个cpuinfo_op是每种cpu架构都必须特别定义的。我们用的是arm架构,我们找到它的定义:
    arch/arm/kernel/setup.c :
    const struct seq_operations cpuinfo_op = {
        .start    = c_start,
        .next    = c_next,
        .stop    = c_stop,
        .show    = c_show
    };
    我们知道这个结构体后,我们继续看open,
    int seq_open(struct file *file, const struct seq_operations *op)
    {
        struct seq_file *p = file->private_data;

        if (!p) { //如果file->private_data为空,则为它申请空间
            p = kmalloc(sizeof(*p), GFP_KERNEL);
            if (!p)
                return -ENOMEM;
            file->private_data = p;
        }
        memset(p, 0, sizeof(*p));//清0
        mutex_init(&p->lock); //初始化mutex
        p->op = op;  //将上面传进来的cpuinfo_op赋值给file

        file->f_version = 0;
        file->f_mode &= ~FMODE_PWRITE;
        return 0;
    }
    我们看到seq_open的主要作用是将ops保持到file->private_data中。

    2,read
    我们上面说cat /proc/cpuinfo就相对于执行open和read,我们下面来看看热啊的。
    ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
    {
        struct seq_file *m = (struct seq_file *)file->private_data;//看清楚了,把刚才上面open中的cpuinfo_op取出来了!下面就可以使用这个结构里面的方法了!
        size_t copied = 0;
        loff_t pos;
        size_t n;
        void *p;
        int err = 0;

        mutex_lock(&m->lock);
        ......
         pos = m->index;
        p = m->op->start(m, &pos);//执行cpuinfo_op中的start方法
        while (1) {
            err = PTR_ERR(p);
            if (!p || IS_ERR(p))
                break;
            err = m->op->show(m, p);//执行cpuinfo_op中show方法
            if (err < 0)
                break;
            if (unlikely(err))
                m->count = 0;
            if (unlikely(!m->count)) {
                p = m->op->next(m, p, &pos);
                m->index = pos;
                continue;
            }
            if (m->count < m->size)
                goto Fill;
            m->op->stop(m, p);
            kfree(m->buf);
            m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
            if (!m->buf)
                goto Enomem;
            m->count = 0;
            m->version = 0;
            pos = m->index;
            p = m->op->start(m, &pos);
        }
        m->op->stop(m, p);
        ......
        
    }
    我们看到read方法中主要执行了cpuinfo_op中方法:
    const struct seq_operations cpuinfo_op = {
        .start    = c_start,
        .next    = c_next,
        .stop    = c_stop,
        .show    = c_show
    };
    我们下面一个一个来分析,
    static void *c_start(struct seq_file *m, loff_t *pos)
    {
        return *pos < 1 ? (void *)1 : NULL;
    }
    c_start主要验证文件的位置。
    static void *c_next(struct seq_file *m, void *v, loff_t *pos)
    {
        ++*pos;
        return NULL;
    }
    c_next移动文件位置的指针,指向下一个。
    static void c_stop(struct seq_file *m, void *v)
    {
    }
    c_stop没有做事情。

    static int c_show(struct seq_file *m, void *v)
    {
        int i;
        /*打印cpu的processor,例如例子中的Processor       : ARMv7 Processor rev 5 (v7l)*/
        seq_printf(m, "Processor : %s rev %d (%s) ",
               cpu_name, read_cpuid_id() & 15, elf_platform);

    #if defined(CONFIG_SMP)//如果是多核处理器,则分别打印cpu的processor信息和主频信息
        for_each_online_cpu(i) {
            /*
             * glibc reads /proc/cpuinfo to determine the number of
             * online processors, looking for lines beginning with
             * "processor".  Give glibc what it expects.
             */
            seq_printf(m, "processor : %d ", i);
            seq_printf(m, "BogoMIPS : %lu.%02lu ",
                   per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ),
                   (per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100);
        }
    #else /* CONFIG_SMP */
        seq_printf(m, "BogoMIPS : %lu.%02lu ",
               loops_per_jiffy / (500000/HZ),
               (loops_per_jiffy / (5000/HZ)) % 100);
    #endif

        /* dump out the processor features */
        seq_puts(m, "Features : ");//下面打印feature信息

        for (i = 0; hwcap_str; i++)
            if (elf_hwcap & (1 << i))
                seq_printf(m, "%s ", hwcap_str);

        seq_printf(m, " CPU implementer : 0x%02x ", read_cpuid_id() >> 24);
        seq_printf(m, "CPU architecture: %s ", proc_arch[cpu_architecture()]);

        if ((read_cpuid_id() & 0x0008f000) == 0x00000000) {
            /* pre-ARM7 */
            seq_printf(m, "CPU part : %07x ", read_cpuid_id() >> 4);
        } else {
            if ((read_cpuid_id() & 0x0008f000) == 0x00007000) {
                /* ARM7 */
                seq_printf(m, "CPU variant : 0x%02x ",
                       (read_cpuid_id() >> 16) & 127);
            } else {
                /* post-ARM7 */
                seq_printf(m, "CPU variant : 0x%x ",
                       (read_cpuid_id() >> 20) & 15);
            }
            seq_printf(m, "CPU part : 0x%03x ",
                   (read_cpuid_id() >> 4) & 0xfff);
        }
        seq_printf(m, "CPU revision : %d ", read_cpuid_id() & 15);

        seq_puts(m, " ");

        seq_printf(m, "Hardware : %s ", machine_name);
        seq_printf(m, "Revision : %04x ", system_rev);
        seq_printf(m, "Serial : %08x%08x ",
               system_serial_high, system_serial_low);//这里我们终于看到serial打印的地方了。我们发现主要打印 system_serial_high,和system_serial_low两个变量的值。如果没有赋值,则打印0。我们要做的工作就是为这两个变量赋值。

        return 0;
    }
    好了,问题分析差不多了,下面就是实现它。这个值就是cpu的uuid,Unique ID是芯片的唯一的ID,是芯片的产线上的信息。每个芯片都有不同的值,每种芯片都有不一样的方法去读。
    我们平板用的是imx51,以imx51为例子,它是通过IIM读Fuse的数据。地址是:(0x83F98000 + 0x820) ~ (0x83F98000 + 0x83C),共8个字节。
    具体实现,代码奉上:
    在driver/char/mxc_iim.c中的probe加上:
        /*via iim, read cpu UID*/
    //open iim
        iim_data->clk = clk_get(NULL, "iim_clk");
        if (IS_ERR(iim_data->clk)) {
            dev_err(iim_data->dev, "No IIM clock defined ");
            return -ENODEV;
        }
        clk_enable(iim_data->clk);

        mxc_iim_disable_irq();

    //read iim
        addr = 0x820; //uid start addr
        for(i=0;i<32;i+=4){
            bank = (addr + i - iim_data->bank_start) >> 10;
            row  = ((addr + i - iim_data->bank_start) & 0x3ff) >> 2;

            dev_dbg(iim_data->dev, "Read fuse at bank:%d row:%d ",
                    bank, row);
            mutex_lock(&iim_data->mutex);
            fuse_val = sense_fuse(bank, row, 0);
            serial[i/4] = fuse_val;
            mutex_unlock(&iim_data->mutex);
            dev_dbg(iim_data->dev, "fuses at addr0x%x(bank:%d, row:%d) = 0x%x ",
                 addr + i, bank, row, fuse_val);
        }
        system_serial_low = ((serial[3]<<24)&0xff000000) + ((serial[2]<<16)&0x00ff0000) + ((serial[1]<<8)&0x0000ff00) + (serial[0]&0x000000ff);

        system_serial_high = ((serial[7]<<24)&0xff000000) + ((serial[6]<<16)&0x00ff0000) + ((serial[5]<<8)&0x0000ff00) + (serial[4]&0x000000ff);

        dev_info(iim_data->dev, "system_serial_high:0x%x, system_serial_low:0x%x", system_serial_high, system_serial_low);


    OK,至此就非常完美地实现了!
  • 相关阅读:
    centos 7遇到的问题
    Exceptions&Files
    关于Transtion属性收藏
    游戏主循环知识积累
    display:inline、block、inline-block的区别
    Sublime text 3快捷键收藏
    业务逻辑详解随记
    探究Struts2运行机制,知识积累
    将博客搬至CSDN
    url随记
  • 原文地址:https://www.cnblogs.com/LoongEmbedded/p/5298401.html
Copyright © 2020-2023  润新知