rk3399修改vpll clk频率

1、添加pll_vpll小数分频修改接口
driver/clk/clk.c


static struct clk_core *clk_get_top_core(struct clk_core *core)
{
	struct clk_core *parent = core;
	parent = core->parent;
	if (parent)
	{
		printk(KERN_CRIT "get parent clk name %s \n",__clk_get_name(parent->hw->clk));
		if(strcmp(__clk_get_name(parent->hw->clk), "pll_vpll") == 0)
		{
			return parent;
		}
		else{
			parent = clk_get_top_core(parent);
		}
	}
	else
	{
		parent = NULL;
	}
	
	return parent;
}

int clk_set_frac(struct clk *clk, unsigned int frac)
{
	int ret = -1;
	struct clk_core *vpll_core = NULL;
	/* save parent rate, if it exists */
	vpll_core = clk_get_top_core(clk->core);
	if (!vpll_core)
	{
		printk(KERN_CRIT "cks:vpll_core == NULL\n");
		return 0;
	}

	printk(KERN_CRIT "%s cks set frac %d\n",__func__,frac);
	/* prevent racing with updates to the clock topology */
	clk_prepare_lock();
	
	if (vpll_core->ops->set_frac)
		ret = vpll_core->ops->set_frac(vpll_core->hw, frac);

	clk_prepare_unlock();

	return ret;
}
EXPORT_SYMBOL_GPL(clk_set_frac);

kernel/linux/clk-provider.h:192
struct clk_ops {
+int (*set_frac)(struct clk_hw *hw,unsigned int frac);
}
2、提供vpll_core->ops->set_frac(vpll_core->hw, frac);平台相关接口
drivers/clk/rockchip/clk-pll.c

static int rockchip_rk3399_vpll_set_frac(struct clk_hw *hw, unsigned int frac)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
	u32 pllcon;
	int ret;
	printk(KERN_CRIT "%s frac : %d",__func__, frac);
	/* xPLL CON2 is not HIWORD_MASK */
	pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2));
	pllcon &= ~(RK3399_PLLCON2_FRAC_MASK << RK3399_PLLCON2_FRAC_SHIFT);
	pllcon |= frac << RK3399_PLLCON2_FRAC_SHIFT;
	writel_relaxed(pllcon, pll->reg_base + RK3399_PLLCON(2));
		/* wait for the pll to lock */
	ret = rockchip_rk3399_pll_wait_lock(pll);
	if (ret) {
		pr_warn("%s: pll update unsucessful, trying to restore old params\n",
			__func__);
	}
	return 0;
}
static const struct clk_ops rockchip_rk3399_pll_clk_ops = {
+.set_frac = rockchip_rk3399_vpll_set_frac,
}

3、取clk及dev拿到ops接口进行修改
drivers/gpu/drm/rockchip/rockchip_drm_vop.c


#define DEVICE_NAME  "usrclk"
static struct class *cdev_class; 

static ssize_t frac_clk_read(struct file *, char *, size_t, loff_t*); 
static ssize_t frac_clk_write(struct file *, const char *, size_t, loff_t*); 

static int global_var = 0;      //CDEV_ZHU设备的全局变量
dev_t dev = 0;                 //这里是动态分配设备号和动态创建设备结点需要用到的
struct cdev  dev_c;
static struct clk *vop_dclk;

//初始化字符设备驱动的 file_operations 结构体 
struct file_operations frac_clk_fops =  
{ 
      read: frac_clk_read, 
      write: frac_clk_write, 
};

static int frac_clk_init(void) 
{ 
	int ret,err; 
	//注册设备驱动 
	ret = alloc_chrdev_region(&dev, 0, 1,DEVICE_NAME);//动态分配设备号
	if (ret) 
	{ 
		printk("clk frac register failure\n"); 
		unregister_chrdev_region(dev,1);
		return ret;
	}
	else
	{
		printk("frac clk register success\n"); 
	}

	cdev_init(&dev_c, &frac_clk_fops);

	err = cdev_add(&dev_c, dev, 1);

	if(err)
	{
		printk(KERN_NOTICE "error %d adding FC_dev\n",err);
		unregister_chrdev_region(dev, 1);
		return err;
	}
	cdev_class = class_create(THIS_MODULE, DEVICE_NAME);//动态创建设备结点
	if(IS_ERR(cdev_class))
	{  
		printk("ERR:cannot create a cdev_class\n");  
		unregister_chrdev_region(dev, 1);
		return -1;
	}
	device_create(cdev_class,NULL, dev, 0, DEVICE_NAME);
	return ret; 
} 

static void  frac_clk_exit(void) 
{ 
	//注销设备驱动 
	device_destroy(cdev_class, dev);
	class_destroy(cdev_class);
	unregister_chrdev_region(dev,1);
	printk("globalvar_exit \n");
} 

static ssize_t frac_clk_read(struct file *filp, char *buf, size_t len, loff_t *off) 
{ 
	//将 global_var 从内核空间复制到用户空间 
	if(copy_to_user(buf, &global_var, sizeof(int))) 
	{ 
		return -EFAULT;     
	}

	return sizeof(int); 
}

static ssize_t frac_clk_write(struct file *filp, const char *buf, size_t len, loff_t *off) 
{ 
	//将用户空间的数据复制到内核空间的 global_var 
	if(copy_from_user(&global_var, buf, sizeof(int))) 
	{ 
		return -EFAULT; 
	}
	printk(KERN_CRIT "get user data : %d\n",global_var);
	clk_set_frac(vop_dclk, global_var);

	return sizeof(int); 
}

static int init_dclk(struct device *dev)
{	
	vop_dclk = devm_clk_get(dev, "dclk_vop");
	if (IS_ERR(vop_dclk)) {
			dev_err(dev, "failed to get dclk source\n");
			return PTR_ERR(vop_dclk);
	}
	//clk_set_frac(vop_dclk, 1111);

	printk(KERN_CRIT "init_dclk get dclk successffly clk name %s\n",__clk_get_name(vop_dclk));
	if (strcmp(__clk_get_name(vop_dclk), "dclk_vop0") == 0)
	{
		return 0;
	}
	return -1;
}

static int vop_bind(struct device *dev, struct device *master, void *data)
+	if(init_dclk(vop->dev)==0)
+	{
+		frac_clk_init();
+	}
		

4、中断事件函数

static irqreturn_t vop_isr(int irq, void *data)

5、应用层传入小数分频调整时钟

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
int main()
{
    int fd, num;
    //打开"/dev/CDEV_ZHU"
    fd = open("/dev/usrclk", O_RDWR, S_IRUSR | S_IWUSR);
    if (fd != -1 )
    {
      //初次读 global_var 
        read(fd, &num, sizeof(int));
        printf("The globalvar is %d\n", num);

      //写 global_var 
        printf("Please input the num written to globalvar\n");
        scanf("%d", &num);
        write(fd, &num, sizeof(int));

      //再次读 global_var 
        read(fd, &num, sizeof(int));
        printf("The globalvar is %d\n", num);

        //关闭“/dev/usrclk” 
        close(fd);
    }
    else
    {
        printf("Device open failure\n");
    }

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值