ZCU106 VCU Linux驱动转裸机驱动篇(三)

ZCU106 VCU Linux驱动转裸机

前言

之前感觉都是在做应用层的分析,今天来个驱动层面的吧

开始

前两篇都是应用层分析,今天分析驱动层面的,首先加载开机打印项

[    7.488846] xilinx-vcu-core a0140000.vcu: No reset gpio info from dts for vcu. This may lead to incorrect functionality if VCU isolation is removed post initialization.
 [    7.821372] xilinx-vcu xilinx-vcu: Could not get core_enc clock
[    7.829821] VCU PLL: enable
[    7.833245] xilinx-vcu xilinx-vcu: xvcu_probe: Probed successfully
[    7.833737] allegro: loading out-of-tree module taints kernel.
[    7.833739] allegro: loading out-of-tree module taints kernel.
[    7.867802] al5e a0100000.al5e: l2 prefetch size:0 (bits), l2 color bitdepth:10
[    7.885599] al5d a0120000.al5d: l2 prefetch size:0 (bits), l2 color bitdepth:10

可以看出,首先加载的是vcu_core驱动,然后是vcu_clk驱动,最后是vcu驱动,所以这里直接吧他们顺序读完之后直接看他们都操作了啥寄存器,然后在用户空间读出来,在逆向写入我们的硬件寄存器就好了,这波操作我真的服我自己,哈哈哈。
先来vcu_core的probe代码:

static int xvcu_core_probe(struct platform_device *pdev)
{
	struct xvcu_device *xvcu;
	struct resource *res;
	int ret;

	xvcu = devm_kzalloc(&pdev->dev, sizeof(*xvcu), GFP_KERNEL);
	if (!xvcu)
		return -ENOMEM;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vcu_slcr");
	if (!res) {
		dev_err(&pdev->dev, "get vcu_slcr memory resource failed.\n");
		return -ENODEV;
	}//获取资源

	xvcu->vcu_slcr_ba = devm_ioremap_nocache(&pdev->dev, res->start,
						 resource_size(res));//映射物理地址
	if (!xvcu->vcu_slcr_ba) {
		dev_err(&pdev->dev, "vcu_slcr register mapping failed.\n");
		return -ENOMEM;
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "logicore");
	if (!res) {
		dev_err(&pdev->dev, "get logicore memory resource failed.\n");
		return -ENODEV;
	}//获取资源

	xvcu->logicore_reg_ba = devm_ioremap_nocache(&pdev->dev, res->start,
						     resource_size(res));//映射
	if (!xvcu->logicore_reg_ba) {
		dev_err(&pdev->dev, "logicore register mapping failed.\n");
		return -ENOMEM;
	}

	dev_set_drvdata(&pdev->dev, xvcu);//设置数据

	xvcu->aclk = devm_clk_get(&pdev->dev, "aclk");
	if (IS_ERR(xvcu->aclk)) {
		dev_err(&pdev->dev, "Could not get aclk clock\n");
		return PTR_ERR(xvcu->aclk);
	}
//第一件事,使能时钟
	ret = clk_prepare_enable(xvcu->aclk);//使用时钟
	if (ret) {
		dev_err(&pdev->dev, "aclk clock enable failed\n");
		return ret;
	}

	/*
	 * Do the Gasket isolation and put the VCU out of reset
	 * Bit 0 : Gasket isolation
	 * Bit 1 : put VCU out of reset
	 */
	xvcu->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
						   GPIOD_OUT_LOW);
	if (IS_ERR(xvcu->reset_gpio)) {
		ret = PTR_ERR(xvcu->reset_gpio);
		dev_err(&pdev->dev, "failed to get reset gpio for vcu.\n");
		return ret;
	}

	if (xvcu->reset_gpio) {//这里没有走
		gpiod_set_value(xvcu->reset_gpio, 0);
		/* min 2 clock cycle of vcu pll_ref, slowest freq is 33.33KHz */
		usleep_range(60, 120);
		gpiod_set_value(xvcu->reset_gpio, 1);
		usleep_range(60, 120);
	} else {
		dev_warn(&pdev->dev, "No reset gpio info from dts for vcu. This may lead to incorrect functionality if VCU isolation is removed post initialization.\n");
	}
	//第二件事写寄存器
	iowrite32(VCU_GASKET_VALUE, xvcu->logicore_reg_ba + VCU_GASKET_INIT);

	ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, xvcu_devs,
			      ARRAY_SIZE(xvcu_devs), NULL, 0, NULL);
	if (ret) {
		dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret);
		goto err_mfd_add_devices;
	}

	dev_dbg(&pdev->dev, "Successfully added MFD devices\n");

	return 0;

err_mfd_add_devices:
	/* Add the the Gasket isolation and put the VCU in reset. */
	iowrite32(0, xvcu->logicore_reg_ba + VCU_GASKET_INIT);

	clk_disable_unprepare(xvcu->aclk);

	return ret;
}

其中驱动做的事情已经在注释中写好,所以这里也直接写寄存axi的时钟以及vcu_gate使能
其次vcu probe如下:就是使能各项时钟,自己看吧~

static int xvcu_set_vcu_pll(struct xvcu_priv *xvcu)
{
	u32 refclk, coreclk, mcuclk, inte, deci;
	int ret;

	inte = xvcu_read(xvcu->logicore_reg_ba, VCU_PLL_CLK);
	deci = xvcu_read(xvcu->logicore_reg_ba, VCU_PLL_CLK_DEC);
	coreclk = xvcu_read(xvcu->logicore_reg_ba, VCU_CORE_CLK) * MHZ;
	mcuclk = xvcu_read(xvcu->logicore_reg_ba, VCU_MCU_CLK) * MHZ;
	if (!mcuclk || !coreclk) {
		dev_err(xvcu->dev, "Invalid mcu and core clock data\n");
		return -EINVAL;
	}

	refclk = (inte * MHZ) + (deci * (MHZ / FRAC));
	dev_dbg(xvcu->dev, "Ref clock from logicoreIP is %uHz\n", refclk);
	dev_dbg(xvcu->dev, "Core clock from logicoreIP is %uHz\n", coreclk);
	dev_dbg(xvcu->dev, "Mcu clock from logicoreIP is %uHz\n", mcuclk);

	ret = clk_set_rate(xvcu->pll_ref, refclk);
	if (ret)
		dev_warn(xvcu->dev, "failed to set logicoreIP refclk rate %d\n"
			 , ret);

	ret = clk_prepare_enable(xvcu->pll_ref);
	if (ret) {
		dev_err(xvcu->dev, "failed to enable pll_ref clock source %d\n",
			ret);
		return ret;
	}

	ret = clk_set_rate(xvcu->mcu_enc, mcuclk);
	if (ret)
		dev_warn(xvcu->dev, "failed to set logicoreIP mcu clk rate %d\n",
			 ret);

	ret = clk_prepare_enable(xvcu->mcu_enc);
	if (ret) {
		dev_err(xvcu->dev, "failed to enable mcu_enc %d\n", ret);
		goto error_mcu_enc;
	}

	ret = clk_set_rate(xvcu->mcu_dec, mcuclk);
	if (ret)
		dev_warn(xvcu->dev, "failed to set logicoreIP mcu clk rate %d\n",
			 ret);

	ret = clk_prepare_enable(xvcu->mcu_dec);
	if (ret) {
		dev_err(xvcu->dev, "failed to enable mcu_dec %d\n", ret);
		goto error_mcu_dec;
	}

	ret = clk_set_rate(xvcu->core_enc, coreclk);
	if (ret)
		dev_warn(xvcu->dev, "failed to set logicoreIP core clk rate %d\n",
			 ret);

	ret = clk_prepare_enable(xvcu->core_enc);
	if (ret) {
		dev_err(xvcu->dev, "failed to enable core_enc %d\n", ret);
		goto error_core_enc;
	}

	ret = clk_set_rate(xvcu->core_dec, coreclk);
	if (ret)
		dev_warn(xvcu->dev, "failed to set logicoreIP core clk rate %d\n",
			 ret);

	ret = clk_prepare_enable(xvcu->core_dec);
	if (ret) {
		dev_err(xvcu->dev, "failed to enable core_dec %d\n", ret);
		goto error_core_dec;
	}

	return 0;

error_core_dec:
	clk_disable_unprepare(xvcu->core_enc);
error_core_enc:
	clk_disable_unprepare(xvcu->mcu_dec);
error_mcu_dec:
	clk_disable_unprepare(xvcu->mcu_enc);
error_mcu_enc:
	clk_disable_unprepare(xvcu->pll_ref);

	return ret;
}

首先把开机用到的寄存器全部读出来,这里给一个用户空间读取的程序,参考的网上的,如下:

int Xil_In32(uint64_t phyaddr)
{
	int fd;
	uint32_t val;
	volatile uint8_t *map_base;
	uint64_t base = phyaddr & PAGE_MASK;
	uint64_t pgoffset = phyaddr & (~PAGE_MASK);
	//open /dev/mem
	if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
	{
		perror("open /dev/mem:");
	}
	//mmap
	map_base = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
			fd, base);
	if(map_base == MAP_FAILED)
	{
		perror("mmap:");
	}
	val = *(volatile uint32_t *)(map_base + pgoffset);
	close(fd);
	munmap((void *)map_base, PAGE_SIZE);
 
	return val;
}

然后编译的话参考我之前在win10下用cmake编译的,效果如下:

root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140024
Input data 0xa0140024
Addr Val : 0x15000
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140028
Input data 0xa0140028
Addr Val : 0x7e4b0c62
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140000
Input data 0xa0140000
Addr Val : 0x0
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140020
Input data 0xa0140020
Addr Val : 0x0
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140030
Input data 0xa0140030
Addr Val : 0x1021
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140034
Input data 0xa0140034
Addr Val : 0x1031
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140040
Input data 0xa0140040
Addr Val : 0xf000
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140060
Input data 0xa0140060
Addr Val : 0x9
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140078
Input data 0xa0140078
Addr Val : 0x0
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa014007c
Input data 0xa014007c
Addr Val : 0x0
root@jokerVCU19:/mnt/cmake_gcc# ./Joker_zynqmp_readmen 0xa0140020
Input data 0xa0140020
Addr Val : 0x0

就是读取了所有的硬件寄存器,然后这里最好用nfs挂载,这样主机编译就可以直接nfs用app了,注意挂载时加上-o nolock,这里给出:

mount -t nfs 192.168.1.2:/home/jodon/nfs_root/rootfs /mnt -o nolock

然后逆向写入,代码如下:

static int xvcu_set_vcu_pll(struct xvcu_priv *xvcu)
{
	u32 refclk, coreclk, mcuclk, inte, deci;
	int ret;
	xil_printf("VCU CLK : start read %x\r\n", (u32)(xvcu->logicore_reg_ba));
	//sleep(1000);
	inte = xvcu_read(xvcu->logicore_reg_ba, VCU_PLL_CLK);
	//xil_printf("VCU CLK : pll %d\r\n", inte);
	deci = xvcu_read(xvcu->logicore_reg_ba, VCU_PLL_CLK_DEC);
	coreclk = xvcu_read(xvcu->logicore_reg_ba, VCU_CORE_CLK) * MHZ;
	mcuclk = xvcu_read(xvcu->logicore_reg_ba, VCU_MCU_CLK) * MHZ;
	if (!mcuclk || !coreclk) {
		xil_printf("VCU CLK : Invalid mcu and core clock data\r\n");
		return -1;
	}

	refclk = (inte * MHZ) + (deci * (MHZ / FRAC));
	xil_printf("vcu clk :Ref clock from logicoreIP is %uHz\r\n", refclk);
	xil_printf("vcu clk :Core clock from logicoreIP is %uHz\r\n", coreclk);
	xil_printf("vcu clk :Mcu clock from logicoreIP is %uHz\r\n", mcuclk);

	xvcu_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0x00);
	sleep(1);
	xvcu_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0x03);
	sleep(1);
	xil_printf("vcu pll set start\r\n");
	//set pll reg
	/*read from linux kernel*/
	xvcu_write(xvcu->vcu_slcr_ba, VCU_PLL_CTRL, 0x15000);
	xvcu_write(xvcu->vcu_slcr_ba, VCU_PLL_CFG, 0x7e4b0c62);
	xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL, 0x1021);
	xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL, 0x1031);
	xvcu_write(xvcu->vcu_slcr_ba, VCU_AXI_CTRL, 0xf000);

	xil_printf("vcu pll set ok\r\n");
}

此时vcu的寄存器已经可用,不然没法读取寄存器值

xil_printf("Version : %d\r\n", xvcu_get_version());

END

待续

你可能感兴趣的:(VCU,ARM,zynq,嵌入式,arm)