当前位置:   article > 正文

Linux内核移植_emmc驱动移植

emmc驱动移植

内核移植

半导体厂商会从linux内核官网下载某个版本,将其移植到自己的CPU上,测试成功后就会将其开放给该半导体的厂商的CPU开发者,开发者下载其提供的linux内核,然后将其移植到自己的 产品上。

1、NXP官方开发板Linux内核编译测试

编译内核之前需要在ubuntu上安装lzop库,图形化配置工具还需要ncurses库支持,安装命令

  1. sudo apt-get install lzop
  2. sudo apt-get install build-essential
  3. sudo apt-get install libncurses5-dev
1.1、修改顶层Makefile
1.2、配置并编译Linux内核

编译Linux内核之前要先配置Linux内核。每个板子都有其对应的默认配置文件,这些配置文件保存在“arch/arm/configs”目录中。imx_v7_defconfig和imx_v7_mfg_defconfig都可以作为NXP官方开发板IMX6ULL EVK的默认配置文件,但是一般都使用后者,因为后者编译出来的zImage可以通过NXP官方提供的MfgTool工具进行烧写

  1. make clean //第一次编译 Linux 内核之前先清理一下
  2. make imx_v7_mfg_defconfig //配置 Linux 内核
  3. make -j16 //编译 Linux 内核

编译完成后,我们得到两个重要文件:

①、Linux 内核镜像文件:zImage。

②、NXP 官方 I.MX6ULL EVK 开发板对应的设备树文件:imx6ull-14x14-evk.dtb。

1.3、Linux内核启动测试

首先检查uboot中的环境变量bootargs

console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw

然后拷贝zImage和imx6ull-14x14-evk.dtb到tftp目录

  1. cp arch/arm/boot/zImage /home/mxh/linux/tftpboot/ -f
  2. cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/mxh/linux/tftpboot/ -f

最后启动开发板,进入uboot命令行启动

  1. tftp 80800000 zImage
  2. tftp 83000000 imx6ull-14x14-evk.dtb
  3. bootz 80800000 - 83000000

内核启动后,如果EMMC存在根文件系统,就可以进入到linux系统进行命令行操作

1.4、根文件系统缺失错误

如果不设置根文件系统路径或者路径设置错误,开发板从网络启动后会提示内核崩溃,VFS不能挂载文件系统

2、在Linux中添加自己的开发板

2.1、添加开发板默认配置文件

将arch/arm/configs 目 录 下 的 imx_v7_mfg_defconfig 重 新 复 制 一 份 , 命 名 为

imx_alientek_emmc_defconfig,命令如下:

  1. cd arch/arm/configs
  2. cp imx_v7_mfg_defconfig imx_alientek_emmc_defconfig

打开 imx_alientek_emmc_defconfig 文件,找到“CONFIG_ARCH_MULTI_V6=y”这一行,

将其屏蔽掉,

因为 I.MX6ULL 是 ARMV7 架构的,因此要屏蔽掉 V6 相关选项,否则后面做驱动实验的

时候可能会遇到驱动模块无法加载的情况。

以后 imx_alientek_emmc_defconfig 就是正点原子的 EMMC 版开发板默认配置文件了

以后就可以使用如下命令来配置正点原子 EMMC 版开发板对应的 Linux 内核了

make imx_alientek_emmc_defconfig
2.2、添加开发板对应的设备树文件

进入目录 arch/arm/boot/dts 中,复制一份 imx6ull-14x14-evk.dts,然后将其重命名为 imx6ull-alientek-emmc.dts,.dts是设备树文件,编译linux的时候会将其编译成.dtb文件,命令如下:

  1. cd arch/arm/boot/dts
  2. cp imx6ull-14x14-evk.dts imx6ull-alientek-emmc.dts

修改arch/arm/dts/Makefile文件,添加开发板设备树文件

  1. ########## arch/arm/boot/dts/Makefile代码段 ##########
  2. dtb-$(CONFIG_SOC_IMX6ULL) += \
  3. imx6ull-14x14-ddr3-arm2.dtb \
  4. imx6ull-14x14-ddr3-arm2-adc.dtb \
  5. ......
  6. ......
  7. imx6ull-14x14-evk-usb-certi.dtb \
  8. imx6ull-alientek-emmc.dtb \
  9. ......
  10. ......
2.3、编译测试

我们可以创建一个编译脚本imx6ull_alientek_emmc.sh

  1. #!/bin/sh
  2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
  3. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihfimx_alientek_emmc_defconfig
  4. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
  5. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16

执行shell脚本

  1. chmod 777 imx6ull_alientek_emmc.sh //给予可执行权限
  2. ./imx6ull_alientek_emmc.sh //执行 shell 脚本编译内核

启动开发板

  1. tftp 80800000 zImage
  2. tftp 83000000 imx6ull-alientek-emmc.dtb
  3. bootz 8080000083000000

3、CPU主频和网络驱动修改

3.1、CPU主频修改

开发板主频是792MHz,输入命令查看cpu信息

cat /proc/cpuinfo

BogoMIPS 是 Linux 系统中衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高,BogoMIPS 值就越大。BogoMIPS 只是粗略的计算 CPU 性能,并不十分准确。但是我们可以通过 BogoMIPS 值来大致的判断当前处理器的性能。

3.2、使能8线EMMC驱动

IMX6UL-ALPHA开发板上的EMMC采用8位数据线,而Linux内核里的4线模式,所以可以以通过修改为8线模式来提高运行速度,直接修改设备树即可,打开imx6ull0-alientek-emmc.dts,找到如下代码

  1. &usdhc2 {
  2. pinctrl-names = "default";
  3. pinctrl-0 = <&pinctrl_usdhc2>;
  4. non-removable;
  5. status = "okay";
  6. };

修改后的代码

  1. &usdhc2 {
  2. pinctrl-names = "default", "state_100mhz", "state_200mhz";
  3. pinctrl-0 = <&pinctrl_usdhc2_8bit>;
  4. pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
  5. pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
  6. bus-width = <8>;
  7. non-removable;
  8. status = "okay";
  9. };
3.3、网络驱动修改

在设备树源码文件中找到如下代码段,此处SNVS_TAMPER7/8两个引脚被初始化为了SPI4的IO,所以需要删除

  1. ########## imx6ull-alientek-emmc.dts 代码段 ##########
  2. pinctrl_spi4: spi4grp {
  3. fsl,pins = <
  4. MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1
  5. MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1
  6. # MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1 #删除此行
  7. # MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000 #删除此行
  8. >;
  9. };

在设备树源码文件中找到如下代码段,此处SNVS_TAMPER7/8两个引脚被设置为了SPI4的功能IO,所以需要删除

  1. ########## imx6ull-alientek-emmc.dts 代码段 ##########
  2. spi4 {
  3. compatible = "spi-gpio";
  4. pinctrl-names = "default";
  5. pinctrl-0 = <&pinctrl_spi4>;
  6. # pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; #删除此行
  7. ......
  8. ......
  9. # cs-gpios = <&gpio5 7 0>; #删除此行
  10. };

在设备树源码文件中找到名为“iomuxc_snvs”节点,在里面添加网络复位引脚配置信息

  1. ########## imx6ull-alientek-emmc.dts 代码段 ##########
  2. &iomuxc_snvs {
  3. pinctrl-names = "default_snvs";
  4. pinctrl-0 = <&pinctrl_hog_2>;
  5. imx6ul-evk {
  6. ......
  7. ......
  8. # enet1 复位配置
  9. pinctrl_enet1_reset: enet1resetgrp {
  10. fsl,pins = <
  11. # used for enet1 reset #
  12. MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
  13. >;
  14. };
  15. #enet2 复位配置
  16. pinctrl_enet2_reset: enet2resetgrp {
  17. fsl,pins = <
  18. # used for enet2 reset #
  19. MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
  20. >;
  21. };
  22. };
  23. };

在设备树源码文件中找到如下代码段,将ENET1和ENET2的网络时钟引脚的电气属性值由0x4001b031(默认值)改为0x4001b009

  1. ########## imx6ull-alientek-emmc.dts 代码段 ##########
  2. pinctrl_enet1: enet1grp {
  3. fsl,pins = <
  4. MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
  5. MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
  6. MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
  7. MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
  8. MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
  9. MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
  10. MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
  11. MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009 #默认值为0x4001b031
  12. >;
  13. };
  14. pinctrl_enet2: enet2grp {
  15. fsl,pins = <
  16. MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0
  17. MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0
  18. MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0
  19. MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0
  20. MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0
  21. MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0
  22. MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0
  23. MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0
  24. MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0
  25. MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b009 #默认值为0x4001b031
  26. >;
  27. };

在设备树源码文件中找到“fec1”和“fec2”这两个节点,修改其中的“pinctrl-0”属性值,修改后的代码如下示

  1. ########## imx6ull-alientek-emmc.dts 代码段 ##########
  2. &fec1 {
  3. pinctrl-names = "default";
  4. pinctrl-0 = <&pinctrl_enet1
  5. &pinctrl_enet1_reset>;
  6. phy-mode = "rmii";
  7. ......
  8. status = "okay";
  9. };
  10. &fec2 {
  11. pinctrl-names = "default";
  12. pinctrl-0 = <&pinctrl_enet2
  13. &pinctrl_enet2_reset>;
  14. phy-mode = "rmii";
  15. ......
  16. };

在设备树源码文件中找到如下代码段,设置ENET1的LAN8720A地址(0x0),设置ENET2的LAN8720A地址(0x1),以及其他相关设置,修改后的代码如下示

  1. ########## imx6ull-alientek-emmc.dts 代码段 ##########
  2. &fec1 {
  3. pinctrl-names = "default";
  4. pinctrl-0 = <&pinctrl_enet1
  5. &pinctrl_enet1_reset>;
  6. phy-mode = "rmii";
  7. phy-handle = <&ethphy0>;
  8. phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>; #添加了ENET1复位引脚,低电平有效
  9. phy-reset-duration = <200>; #复位低电平持续时间为200ms
  10. status = "okay";
  11. };
  12. &fec2 {
  13. pinctrl-names = "default";
  14. pinctrl-0 = <&pinctrl_enet2
  15. &pinctrl_enet2_reset>;
  16. phy-mode = "rmii";
  17. phy-handle = <&ethphy1>;
  18. phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; #添加了ENET2复位引脚,低电平有效
  19. phy-reset-duration = <200>; #复位低电平持续时间为200ms
  20. status = "okay";
  21. mdio {
  22. #address-cells = <1>;
  23. #size-cells = <0>;
  24. ethphy0: ethernet-phy@0 { #ethernet-phy@后面的数字式PHY的地址
  25. compatible = "ethernet-phy-ieee802.3-c22";
  26. #表明PHY芯片是SMSC公司的,Linux内核会找到SMSC的PHY芯片驱动来驱动LAN8720A
  27. smsc,disable-energy-detect;
  28. reg = <0>; #也表示PHY地址
  29. };
  30. ethphy1: ethernet-phy@1 { #ethernet-phy@后面的数字式PHY的地址
  31. compatible = "ethernet-phy-ieee802.3-c22";
  32. #表明PHY芯片是SMSC公司的,Linux内核会找到SMSC的PHY芯片驱动来驱动LAN8720A
  33. smsc,disable-energy-detect;
  34. reg = <1>;
  35. };
  36. };
  37. };

要 在 I.MX6ULL 上 使 用 LAN8720A , 需 要 修 改 一 下 Linux 内 核 源 码 , 打 开

drivers/net/ethernet/freescale/fec_main.c,找到函数 fec_probe,在 fec_probe 中加入如下代码

  1. static int fec_probe(struct platform_device *pdev)
  2. {
  3. struct fec_enet_private *fep;
  4. struct fec_platform_data *pdata;
  5. struct net_device *ndev;
  6. int i, irq, ret = 0;
  7. struct resource *r;
  8. const struct of_device_id *of_id;
  9. static int dev_id;
  10. struct device_node *np = pdev->dev.of_node, *phy_node;
  11. int num_tx_qs;
  12. int num_rx_qs;
  13. /* 设置 MX6UL_PAD_ENET1_TX_CLK 和 MX6UL_PAD_ENET2_TX_CLK
  14. * 这两个 IO 的复用寄存器的 SION 位为 1,以下为添加的代码 */
  15. void __iomem *IMX6U_ENET1_TX_CLK;
  16. void __iomem *IMX6U_ENET2_TX_CLK;
  17. IMX6U_ENET1_TX_CLK = ioremap(0X020E00DC, 4);
  18. writel(0X14, IMX6U_ENET1_TX_CLK);
  19. IMX6U_ENET2_TX_CLK = ioremap(0X020E00FC, 4);
  20. writel(0X14, IMX6U_ENET2_TX_CLK);
  21. ......
  22. return ret;
  23. }

输入“make menuconfig”,打开图形化配置解密,选择使能LAN8720A的驱动,路径如下

-> Device Drivers -> Network device support -> PHY Device support and infrastructure -> Drivers for SMSC PHYs

在 Linux 中对 LAN8720A 进行一次软复位,找到LAN8720A的驱动文件 “drivers/net/phy/smsc.c”,在函数“smsc_phy_reset”中添加LAN8720A复位代码,修改后的代码如下

  1. static int smsc_phy_reset(struct phy_device *phydev){
  2. int err, phy_reset;
  3. int msec = 1;
  4. struct device_node *np;
  5. int timeout = 50000;
  6. if(phydev->addr == 0) /* 获取FEC1网卡对应的设备节点 */ {
  7. np = of_find_node_by_path("/soc/aips-bus@02100000/
  8. ethernet@02188000");
  9. if(np == NULL) {
  10. return -EINVAL;
  11. }
  12. }
  13. if(phydev->addr == 1) /* 获取FEC2网卡对应的设备节点 */ {
  14. np = of_find_node_by_path("/soc/aips-bus@02000000/
  15. ethernet@020b4000");
  16. if(np == NULL) {
  17. return -EINVAL;
  18. }
  19. }
  20. //从设备树中获取复位时间
  21. err = of_property_read_u32(np, "phy-reset-duration", &msec);
  22. /* A sane reset duration should not be longer than 1s */
  23. if (!err && msec > 1000)
  24. msec = 1;
  25. //从设备树中获取复位IO
  26. phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
  27. if (!gpio_is_valid(phy_reset))
  28. return;
  29. //设置PHY的复位IO,复位LAN8720A
  30. gpio_direction_output(phy_reset, 0);
  31. gpio_set_value(phy_reset, 0);
  32. msleep(msec);
  33. gpio_set_value(phy_reset, 1);
  34. int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
  35. if (rc < 0)
  36. return rc;
  37. /* If the SMSC PHY is in power down mode, then set it
  38. * in all capable mode before using it.
  39. */
  40. if ((rc & MII_LAN83C185_MODE_MASK) ==
  41. MII_LAN83C185_MODE_POWERDOWN) {
  42. /* set "all capable" mode and reset the phy */
  43. rc |= MII_LAN83C185_MODE_ALL;
  44. phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
  45. }
  46. //未修改之前在上面的函数里面,只有Powerdown模式时才会软复位LAN8720A
  47. //此处将其移出来,这样每次调用smsc_phy_reset函数,LAN8720A都会被软复位
  48. phy_write(phydev, MII_BMCR, BMCR_RESET);
  49. /* wait end of reset (max 500 ms) */
  50. do {
  51. udelay(10);
  52. if (timeout-- == 0)
  53. return -1;
  54. rc = phy_read(phydev, MII_BMCR);
  55. } while (rc & BMCR_RESET);
  56. return 0;
  57. }

因为smsc_phy_reset函数中用到了gpio_direction_output和gpio_set_value函数,所以需要在“smsc.c”中添加如下头文件

  1. #include <linux/of_gpio.h>
  2. #include <linux/io.h>

网络驱动测试:修改好设备树和内核后重新编译,下载并启动开发板后,使用如下步骤进行测试

输入"ifconfig -a"来查看开发板中存在的网卡

输入"ifconfig eth0 up"和"ifconfig eth1 up"命令,打开eth0(ENET2)和eth1(ENET1)

使用"ifconfig eth0 192.168.10.51"和"ifconfig eth0 192.168.10.52"命令,配置网卡IP地址。配置成功后,ping以下Ubuntu主机(192.168.10.100),ping成功说明网络驱动修改成功

4、内核移植总结

  • 一般情况下,设计自已的硬件时都会参考半导体厂商官方的开发板

  • 在半导体厂商维护的Linux内核中查找可以参考的板子(半导体厂商官方开发板)

  • 编译出参考板子对应的zImage和.dtb文件,尝试在自已的板子上启动

  • 大部分情况下会启动起来,如果不能的话就需要调试Linux内核

  • 修改相应的驱动,NAND/EMMC/SD卡等,内核已经提供,重点是网络驱动,需要根据自已的外设PHY芯片设置复位引脚、地址信息等

  • Linux内核启动以后需要根文件系统,如果没有的话系统会崩溃

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/982577
推荐阅读
相关标签
  

闽ICP备14008679号