22.修复nanopi t2 cpu调频不起作用 nxp-cpufreq nxp-cpufreq.0: Cannot get regulator for DVS supply vdd

    技术2022-07-11  151

    修复nanopi t2 cpu调频不起作用 nanopi t2 s5p4418超频

    Cannot get regulator for DVS supply vdd_arm_1.3V

    nanopi cpu为s5p4418,标称速度可以达到1400Mhz,实际使用的时候发现只有400Mhz,sys/devices/system/cpu/cpu0下面没有cpufreq目录。说明cpu调频驱动有问题。 抓一下log看看

    [root@minicoco ~]# dmesg | grep cpu [ 0.000000] Initializing cgroup subsys cpuset [ 0.000000] Initializing cgroup subsys cpu [ 0.000000] PERCPU: Embedded 9 pages/cpu @c136a000 s12352 r8192 d16320 u36864 [ 0.000000] pcpu-alloc: s12352 r8192 d16320 u36864 alloc=9*4096 [ 0.000000] pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3 [ 0.048000] Initializing cgroup subsys cpuacct [ 0.048000] CPU0: thread -1, cpu 0, socket 10, mpidr 80000a00 [ 0.092000] CPU1: thread -1, cpu 1, socket 10, mpidr 80000a01 [ 0.132000] CPU2: thread -1, cpu 2, socket 10, mpidr 80000a02 [ 0.172000] CPU3: thread -1, cpu 3, socket 10, mpidr 80000a03 [ 2.076000] nxp-cpufreq nxp-cpufreq.0: Cannot get regulator for DVS supply vdd_arm_1.3V [ 2.084000] nxp-cpufreq: probe of nxp-cpufreq.0 failed with error -1 [ 2.092000] cpuidle: using governor ladder [ 2.096000] cpuidle: using governor menu

    问题出在nxp-cpufreq nxp-cpufreq.0: Cannot get regulator for DVS supply vdd_arm_1.3V这一句 nxp-cpufreq是调频驱动,去代码里面看看。 driver/cpufreq/nxp-cpufreq.c: regulator_get这里调用出错了,regulator_get是电源管理的一个函数,这里是获取vdd_arm_1.3V这个电压配置失败。nanopi t2的电源管理芯片是axp-228,再看一下电源管理器日志

    [root@minicoco ~]# dmesg | grep axp [ 0.416000] axp_mfd 3-0034: failed reading at 0x03 [ 0.420000] [AXP22-MFD] try to read chip id failed!

    可以看到电源管理驱动加载出问题了,看一下代码。 driver/power/axp-power/axp22-mfd.h: driver/power/axp-power/axp-rw.h: i2c_smbus_read_byte_data出了问题,这个是i2c读取数据的方法。问题可以确定出在i2c通信总线上了。 看一下i2c总线注册有没有问题。 arch/arm/plat-s5p4418/nanopi2/device.c 这里乍一看也没什么问题,但是最好吧i2c注册放到axp驱动注册的前面去。i2c驱动注册的是GPIO模拟方式,通道是3,管脚使用了GPIOE30、GPIOE31,从axp驱动的参数中也能看出vdd_arm_1.3V这个配置就是在这里注册的。看一下电路图 管脚也正确。尝试打印了几个输出也无用。这里读取的器件地址是0x68,寄存器是0x03,值为芯片ID。看一下axp数据手册。 发现没有0x03这个寄存器,以为是驱动不匹配,注释掉芯片ID的读取直接初始化axp芯片,发现也不能通信,看来问题还在i2c通信上面。 由于管脚都没有露出来,所以没办法用示波器测量i2c通信输出。于是只能自己用GPIO模拟i2c通信试试看,GPIO模拟i2c通信文章传送门

    根据手册尝试关闭电源

    测试axp22

    向0x32寄存器的第7位写入1关闭电源。

    #include <stdio.h> #include <linux/ioctl.h> #include <sys/ioctl.h> #include <unistd.h> #include <fcntl.h> #include <unistd.h> int main(){ int ret,cmd; fd = open("/dev/char_test_dev_1",O_RDWR); ret = fd; if(ret < 0){ perror("open /dev/char_test_dev_1 error"); return ret; } unsigned char data[] = {0x68,0x32,0x80}; cmd = _IOC(_IOC_WRITE,0x00,0x00,0x03); ret = ioctl(fd,cmd,data); if(ret < 0){ perror("ioctl error"); return ret; } close(ret); return 0; }

    一开始也是失败,最后吧驱动的速度调到一个很低的值成功关闭电源。看来硬件上没有问题,应该是i2c速度太高。 驱动代码

    #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <asm/io.h> #include <linux/gpio.h> #include <mach/soc.h> #include <linux/delay.h> #include <mach/platform.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/slab.h> #include <asm/uaccess.h> MODULE_LICENSE("GPL"); dev_t devid; struct cdev char_dev; struct class * char_class; int buffer_size = 100; char * char_data; #pragma pack(4) static struct GPIO_B{ unsigned int out_put; unsigned int out_enb; unsigned int detect_md_0; unsigned int detect_md_1; unsigned int int_enb; unsigned int event_detect; unsigned int pad; unsigned int resv; unsigned int func0; unsigned int func1; unsigned int DETMODEEX; unsigned int DETENB; unsigned int SLEW; unsigned int SLEW_DISABLE_DEFAULT; unsigned int DRV1; unsigned int DRV1_DISABLE_DEFAULT; unsigned int DRV0; unsigned int DRV0_DISABLE_DEFAULT; unsigned int pull_sell; unsigned int PULLSEL_DISABLE_DEFAULT; unsigned int pull_enb; }* gpio_b; #pragma pack() #define SCL_PIN (30) #define SDA_PIN (31) #define SCL_H (gpio_b->out_put |= (1 << SCL_PIN)) #define SCL_L (gpio_b->out_put &= ~(1 << SCL_PIN)) #define SDA_H (gpio_b->out_put |= (1 << SDA_PIN)) #define SDA_L (gpio_b->out_put &= ~(1 << SDA_PIN)) #define SDA_OUT (gpio_b->out_enb |= (1 << SDA_PIN)) #define SDA_IN (gpio_b->out_enb &= ~(1 << SDA_PIN)) #define SDA_VAL ((gpio_b->pad >> SDA_PIN) & 0x01) #define cycle_ns 1000000 //iic周期,单位纳秒 /********************************************** //IIC Start **********************************************/ inline void IIC_Start(void) { SCL_H; SDA_H; ndelay(cycle_ns); SDA_L; ndelay(cycle_ns); SCL_L; ndelay(cycle_ns/2); //等待电平稳定 } /********************************************** //IIC Stop **********************************************/ inline void IIC_Stop(void) { SCL_L; SDA_L; ndelay(cycle_ns); SCL_H; ndelay(cycle_ns); SDA_H; ndelay(400); } /********************************************** // 通过I2C总线写一个字节 **********************************************/ inline int Write_IIC_Byte(unsigned char IIC_Byte) { unsigned char i; int ret; for(i=0;i<8;i++) { if(IIC_Byte & 0x80) SDA_H; else SDA_L; ndelay(cycle_ns/2); //等待电平稳定 SCL_H; ndelay(cycle_ns); //维持采样时间 SCL_L; ndelay(cycle_ns/2); //等待电平稳定 IIC_Byte<<=1; } SDA_H; ndelay(cycle_ns/2); //等待电平稳定 SDA_IN; SCL_H; ndelay(cycle_ns/2); //等待电平稳定 if(!SDA_VAL){ ret = 0; }else{ ret = -1; } ndelay(cycle_ns/2); //等待电平稳定 SDA_OUT; SCL_L; SDA_L; ndelay(40); //下次传输间隔ack return ret; } static long ioctl(struct file * fl, unsigned int cmd, unsigned long arg){ unsigned int dir,size,i; dir = _IOC_DIR(cmd); size = _IOC_SIZE(cmd); if(dir == _IOC_WRITE){ i = size; while(i > 0){ i = copy_from_user(char_data,(unsigned char *)arg,i); } IIC_Start(); for (i = 0; i < size; ++i) { if(Write_IIC_Byte(char_data[i]) != 0){ printk("iic no ack\n"); break; } } IIC_Stop(); } return 0; } struct file_operations my_opts = { .owner = THIS_MODULE, .unlocked_ioctl = ioctl }; static int __init iic_init(void){ int ret = 0; devid = MKDEV(241, 1); //换算设备号 ret = register_chrdev_region(devid, 1, "char_test");//注册设备,在/proc/drivers下面可以看到 if (ret < 0) goto err0; cdev_init(&char_dev,&my_opts); //绑定opt结构体 char_dev.owner = THIS_MODULE; ret = cdev_add(&char_dev,devid,1); //注册字符设备驱动 if (ret < 0) goto err1; char_class = class_create(THIS_MODULE,"char_test"); //在/sys/class中创建文件夹 device_create(char_class,NULL,devid,NULL,"char_test_dev_%d",1);//在上一步文件夹中创建char_test_dev_1 char_data = kzalloc(buffer_size,GFP_KERNEL); gpio_b = (struct GPIO_B *)ioremap(0xc001e000,sizeof(struct GPIO_B)); //映射地址 gpio_b->func1 |= (1 << (SDA_PIN % 16) * 2); gpio_b->func1 |= (1 << (SCL_PIN % 16) * 2); gpio_b->out_enb |= (1 << SDA_PIN); gpio_b->out_enb |= (1 << SCL_PIN); gpio_b->pull_enb |= (1 << SDA_PIN); gpio_b->pull_sell |= (1 << SDA_PIN); SDA_H; SCL_H; printk("iic init\n"); return 0; err1: unregister_chrdev_region(devid, 1); err0: return ret; } static void __exit iic_exit(void){ SDA_H; SCL_H; iounmap(gpio_b); unregister_chrdev_region(devid, 1); cdev_del(&char_dev); device_destroy(char_class,devid); class_destroy(char_class); printk("iic exit\n"); } module_init(iic_init); module_exit(iic_exit);

    调整i2c速度

    于是修改内核驱动中i2c的速度 最后成功了,重新开机sys/devices/system/cpu/cpu0下也有cpufreq目录了。收工!

    Processed: 0.019, SQL: 9