[ARM Linux系统移植] U-Boot LCD 和网络驱动移植

我们在文章 [ARM Linux系统移植] U-Boot 中添加自己的开发板 中,已经介绍了移植自己开发板前的准备工作:“抽离”和组织相关的目录和文件。

本篇文章在此基础上继续修改 LCD 驱动和网络驱动。需要修改 LCD 驱动的原因是 LCD 屏幕的参数会不一致;而修改网络驱动的原因是更换了网络相关芯片,并且引脚也发生了变化。

1. 修改 LCD 驱动

修改 LCD 的驱动,针对当前开发板,因为引脚和官方的 i.Mx 开发板一致,所以只要修改 LCD 的接口参数。LCD 相关的裸机实验已经在文章 [ARM裸机开发] LCD 显示 中介绍过了,在此基础上进行理解没有什么难度。

修改的参数对应 mx6ullevk.c(之前移植的文件叫做 mx6ull_my_emmc.c)文件中的 displays 变量。根据 [ARM裸机开发] LCD 显示 中的参数进行修改,修改内容已经用粗体标出。额外需要注意的地方是,此处像素时钟(即 pixclock 成员)的计算方式为:1 / clock(Hz) * 10^12。

board\freescale\mx6ullevk\mx6ullevk.c
  • struct display_info_t const displays[] = { {
  •     .bus = MX6UL_LCDIF1_BASE_ADDR,
  •     .addr = 0,
  •     .pixfmt = 24,
  •     .detect = NULL,
  •     .enable = do_enable_parallel_lcd,
  •     .mode   = {
  •         .name           = "TFT43AB",
  •         .xres           = 800,
  •         .yres           = 480,
  •         .pixclock       = 32258,
  •         .left_margin    = 88//HBPD
  •         .right_margin   = 40//HFPD
  •         .upper_margin   = 32//VBPD
  •         .lower_margin   = 13//VFBD
  •         .hsync_len      = 48//HSPW
  •         .vsync_len      = 3//VSPW
  •         .sync           = 0,
  •         .vmode          = FB_VMODE_NONINTERLACED
  • } } };

修改好后,重新编译并烧写。如果屏幕出现了 NXP 的 log 图样,就代表移植成功了。自己这边移植顺利,也没有特别的“坑”需要记录。

2. 修改网络驱动

网络驱动的修改步骤相对 LCD 驱动的修改要繁琐。因为使用的网络芯片和官方开发板不一样,且连接的复位引脚也不一样。我们需要修改的内容包括以下 3 个部分:

1. 芯片对应的器件 ID

2. 新复位引脚的初始化

3. 新芯片驱动

器件 ID 这边按照 I2C 接口的器件 ID 进行理解,并没有深究。

关于新芯片驱动,照理说通用的网络驱动需要是“通用”的:因为这块有标准组织规定,基本功能的寄存器定义需要一致。此处驱动不一致,也就直接按照文档进行修改了,也没有深究。

2.1 修改器件 ID

开发板上有两个网口,对应的物理地址(器件 ID)分为为 0x0 和 0x1。器件 ID 对应的修改在 mx6ull_alientek_emmc.h(之前移植的文件叫做 mx6ull_my_emmc.h) 文件中第 335 和 339 行。同时,因为更换了网络芯片,也把第 345 行跟芯片公司相关的宏进行修改。

include/configs/mx6ull_alientek_emmc.h
  1. #ifdef CONFIG_CMD_NET
  2. #define CONFIG_CMD_PING
  3. #define CONFIG_CMD_DHCP
  4. #define CONFIG_CMD_MII
  5. #define CONFIG_FEC_MXC
  6. #define CONFIG_MII
  7. #define CONFIG_FEC_ENET_DEV     1
  8.  
  9. #if (CONFIG_FEC_ENET_DEV == 0)
  10. #define IMX_FEC_BASE            ENET_BASE_ADDR
  11. #define CONFIG_FEC_MXC_PHYADDR          0x0
  12. #define CONFIG_FEC_XCV_TYPE             RMII
  13. #elif (CONFIG_FEC_ENET_DEV == 1)
  14. #define IMX_FEC_BASE            ENET2_BASE_ADDR
  15. #define CONFIG_FEC_MXC_PHYADDR      0x1
  16. #define CONFIG_FEC_XCV_TYPE     RMII
  17. #endif
  18. #define CONFIG_ETHPRIME         "FEC"
  19.  
  20. #define CONFIG_PHYLIB
  21. #define CONFIG_PHY_SMSC
  22. #endif

2.2 新复位引脚的初始化

首先我们删除之前定义的复位引脚,并定义新的复位引脚。

board\freescale\mx6ullevk\mx6ullevk.c
  • //#define IOX_SDI IMX_GPIO_NR(5, 10)
  • //#define IOX_STCP IMX_GPIO_NR(5, 7)
  • //#define IOX_SHCP IMX_GPIO_NR(5, 11)
  • //#define IOX_OE IMX_GPIO_NR(5, 8)
  • #define ENET1_RESET IMX_GPIO_NR(57)
  • #define ENET2_RESET IMX_GPIO_NR(58)

删除了之前的复位引脚定义之后,还会影响到其他使用到它的地方,不一起删除的话就会导致编译错误。还需要删除的地方为:iox_pads 数组、iox74lv_init() 函数、iox74lv_set() 函数以及 board_init() 函数里面对 iox74lv_init()imx_iomux_v3_setup_multiple_pads(iox_pads) 函数的调用。

删除之后,如果编译出错,根据错误提示修改即可。

接着在 fec1_padsfec2_pads 数组中,添加对两个新复位引脚的复用参数。

board\freescale\mx6ullevk\mx6ullevk.c
  • static iomux_v3_cfg_t const fec1_pads[] = {
  •     MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
  •     MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
  •     ……,
  •     MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
  •     MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
  •     MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
  • };
  •  
  • static iomux_v3_cfg_t const fec2_pads[] = {
  •     MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
  •     MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
  •     ……,
  •     MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
  •     MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
  •     MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
  • };

fec1_padsfec2_pads 数组在 setup_iomux_fec() 函数中使用,其中完成了管脚的复用操作。还需要做的是操作复位引脚使硬件复位,相关代码已用粗体标出。

board\freescale\mx6ullevk\mx6ullevk.c
  • static void setup_iomux_fec(int fec_id)
  • {
  •     if (fec_id == 0)
  •     {
  •         imx_iomux_v3_setup_multiple_pads(fec1_pads,
  •             ARRAY_SIZE(fec1_pads));
  •  
  •         gpio_direction_output(ENET1_RESET, 1);
  •         gpio_set_value(ENET1_RESET, 0);
  •         mdelay(20);
  •         gpio_set_value(ENET1_RESET, 1);
  •     }
  •     else
  •     {
  •         imx_iomux_v3_setup_multiple_pads(fec2_pads,
  •             ARRAY_SIZE(fec2_pads));
  •  
  •         gpio_direction_output(ENET2_RESET, 1);
  •         gpio_set_value(ENET2_RESET, 0);
  •         mdelay(20);
  •         gpio_set_value(ENET2_RESET, 1);
  •     }
  • }

2.3 新芯片驱动

新芯片增加的驱动功能是软件复位,需要修改 phy.c 下的 genphy_update_link() 函数,对应第 225 至 239 行。

drivers/net/phy/phy.c
  1. int genphy_update_link(struct phy_device* phydev)
  2. {
  3.     unsigned int mii_reg;
  4.  
  5. #ifdef CONFIG_PHY_SMSC
  6.     static int lan8720_flag = 0;
  7.     int bmcr_reg = 0;
  8.     if (lan8720_flag == 0)
  9.     {
  10.         bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
  11.         phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
  12.         while (phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0x8000)
  13.         {
  14.             udelay(100);
  15.         }
  16.         phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg);
  17.         lan8720_flag = 1;
  18.     }
  19. #endif

至此,网络驱动就全部修改完毕。重新编译烧写后,可以发现 u-boot 原先打印的初始化网络失败的 log 消失了。

第一次烧写会遇到 Error: FEC1 address not set. 的错误,原因是 ipaddr 等环境变量没有设置。网络相关的环境变量设置在 [ARM Linux系统移植] U-Boot 网络命令 中有详细说明。在设置 ipaddr 等环境变量之后,之前的错误 log 也消失了。后续还可以使用 ping 命令进行验证,ping 通了就代表网络驱动修改正确。