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

修复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目录了。收工!

猜你喜欢

转载自blog.csdn.net/qq_16054639/article/details/107060975