基于Ti的SDK u-boot添加自己的板子支持(2)

U-boot 硬件相关部分移植

u-boot的基本功能是用来初始化部分系统,然后加载并启动内核。
根据具体项目的需求,我们的系统由SD卡启动。可以通过读卡器直接更新SD中的文件,因此不需要TFTP来下载和更新内核,也就不需要u-boot的网络功能。
因此具体移植的u-boot的功能相对简单, 其外设只需要SD卡和串口输出,不需要以太网, USB等其他功能外设。
仔细细分, 需要移植的功能有:
(1)增加自己板子的版号支持;Ti的SDK中,u-boot通过读板子上的一个I2C接口的EEPROM中的硬件信息来区别不同的板子,因此需要修改这部分代码来绕过这部分的判定。
(2)SD卡,串口的功能管脚复用设定;
(3)DDR SDRM参数的修改;

板子ID

首先在include/configs/am335x_cfe.h中定义:

#define SYSTEMCORP_CFE //define our own baord

在board/set/common/board_detect.c中的函数ti_i2c_eeprom_am_get()是用来读取EEPROM中的数据的。因为我们没有采用EEPROM,所以我们添加代码来人为写入数据给结构体TI_EEPROM_DATA:

int __maybe_unused ti_i2c_eeprom_am_get(int bus_addr, int dev_addr)
{
    int rc;
    struct ti_am_eeprom am_ep;
    struct ti_common_eeprom *ep;

    ep = TI_EEPROM_DATA;
    if (ep->header == TI_EEPROM_HEADER_MAGIC)
        goto already_read;
//如果定义了SYSTEMCORP_CFE, 就认为人为给ep赋值。
#if defined SYSTEMCORP_CFE
    //if ((bus_addr == -1) && (dev_addr == CONFIG_SYS_I2C_EEPROM_ADDR))
    //{
        ep->header = TI_EEPROM_HEADER_MAGIC;
        //板子的name为SET_CFE, 后续的板号判定就以该名字为标准。
        strlcpy(ep->name, "SET_CFE", TI_EEPROM_HDR_NAME_LEN + 1);
        ti_eeprom_string_cleanup(ep->name);

        strlcpy(ep->version, "1.0", TI_EEPROM_HDR_REV_LEN + 1);
        ti_eeprom_string_cleanup(ep->version);

        strlcpy(ep->serial, "00000000", TI_EEPROM_HDR_SERIAL_LEN + 1);
        ti_eeprom_string_cleanup(ep->serial);

        strlcpy(ep->config, "SKU#01", TI_EEPROM_HDR_CONFIG_LEN + 1);
        ti_eeprom_string_cleanup(ep->config);

        char mac_addr[TI_EEPROM_HDR_NO_OF_MAC_ADDR][TI_EEPROM_HDR_ETH_ALEN] = {"000001","000002","000003"};
        memcpy(ep->mac_addr, mac_addr,
            TI_EEPROM_HDR_NO_OF_MAC_ADDR * TI_EEPROM_HDR_ETH_ALEN);
        goto already_read;
    //}
#endif

#if defined SYSTEMCORP_LCD
        //if ((bus_addr == -1) && (dev_addr == CONFIG_SYS_I2C_EEPROM_ADDR))
        //{
            ep->header = TI_EEPROM_HEADER_MAGIC;

            strlcpy(ep->name, "SET_LCD", TI_EEPROM_HDR_NAME_LEN + 1);
            ti_eeprom_string_cleanup(ep->name);

            strlcpy(ep->version, "1.0", TI_EEPROM_HDR_REV_LEN + 1);
            ti_eeprom_string_cleanup(ep->version);

            strlcpy(ep->serial, "00000000", TI_EEPROM_HDR_SERIAL_LEN + 1);
            ti_eeprom_string_cleanup(ep->serial);

            strlcpy(ep->config, "SKU#01", TI_EEPROM_HDR_CONFIG_LEN + 1);
            ti_eeprom_string_cleanup(ep->config);

            char mac_addr[TI_EEPROM_HDR_NO_OF_MAC_ADDR][TI_EEPROM_HDR_ETH_ALEN] = {"000001","000002","000003"};
            memcpy(ep->mac_addr, mac_addr,
                TI_EEPROM_HDR_NO_OF_MAC_ADDR * TI_EEPROM_HDR_ETH_ALEN);
            goto already_read;
        //}
#endif

    /* Initialize with a known bad marker for i2c fails.. */
    ep->header = TI_DEAD_EEPROM_MAGIC;
    ep->name[0] = 0x0;
    ep->version[0] = 0x0;
    ep->serial[0] = 0x0;

    rc = ti_i2c_eeprom_get(bus_addr, dev_addr, TI_EEPROM_HEADER_MAGIC,
                   sizeof(am_ep), (uint8_t *)&am_ep);
    if (rc)
        return rc;

    ep->header = am_ep.header;
    strlcpy(ep->name, am_ep.name, TI_EEPROM_HDR_NAME_LEN + 1);
    ti_eeprom_string_cleanup(ep->name);

    /* BeagleBone Green '1' eeprom, board_rev: 0x1a 0x00 0x00 0x00 */
    if (am_ep.version[0] == 0x1a && am_ep.version[1] == 0x00 &&
        am_ep.version[2] == 0x00 && am_ep.version[3] == 0x00)
        strlcpy(ep->version, "BBG1", TI_EEPROM_HDR_REV_LEN + 1);
    else
        strlcpy(ep->version, am_ep.version, TI_EEPROM_HDR_REV_LEN + 1);
    ti_eeprom_string_cleanup(ep->version);
    strlcpy(ep->serial, am_ep.serial, TI_EEPROM_HDR_SERIAL_LEN + 1);
    ti_eeprom_string_cleanup(ep->serial);
    strlcpy(ep->config, am_ep.config, TI_EEPROM_HDR_CONFIG_LEN + 1);
    ti_eeprom_string_cleanup(ep->config);

    memcpy(ep->mac_addr, am_ep.mac_addr,
           TI_EEPROM_HDR_NO_OF_MAC_ADDR * TI_EEPROM_HDR_ETH_ALEN);

already_read:
    return 0;
}

接着在board/set/am335_cfe/board.h中定义函数board_is_set_cfe()用来判定TI_EEPROM_DATA中的名字是否是SET_CFE。

static inline int board_is_set_cfe(void)
{
    return board_ti_is("SET_CFE"); //判定板子的名字是不是SET_CFE
}

而board_ti_is()定义在board/set/common/board_detect.c中

bool __maybe_unused board_ti_is(char *name_tag)
{
    struct ti_common_eeprom *ep = TI_EEPROM_DATA;

    if (ep->header == TI_DEAD_EEPROM_MAGIC)
        return false;
    return !strncmp(ep->name, name_tag, TI_EEPROM_HDR_NAME_LEN);//判定TI_EEPROM_DATA结构体中的名字是否是传入进来的name_tag, 也即是否是上面的SET_CFE。
}

综上, 可以通过调用函数board_is_set_cfe()来以判定是否是我们自己的板子。

管脚复用

管脚复用主要是确认SD卡和串口的配置是否正确。
进入文件board/set/am335-cfe/mux.c的函数enable_board_pin_mux(),在靠后位置添加自己板子的复用管脚信息。

void enable_board_pin_mux(void)
{
    /* Do board-specific muxes. */
    .......
    } else if (board_is_icev2()) {
        configure_module_pin_mux(mmc0_pin_mux);
        configure_module_pin_mux(gpio0_18_pin_mux);
        configure_module_pin_mux(uart3_icev2_pin_mux);
        configure_module_pin_mux(rmii1_pin_mux);
        configure_module_pin_mux(spi0_pin_mux);
    } else if (board_is_set_cfe()) { //添加自己板子的mmc0管脚
        configure_module_pin_mux(mmc0_pin_mux_cfe);     
        configure_module_pin_mux(rgmii1_pin_mux);   //如果不添加以太网接口, u-boot运行时会报出错信息,因此添加上该管脚复用,实际中没有使用以太网。
    }
    else {
        puts("Unknown board, cannot configure pinmux.");
        hang();
    }
}

当然需要在mux.c文件中添加上自己的数据结构mmc0_pin_mux_cfe。

static struct module_pin_mux mmc0_pin_mux_cfe[] = {
    {OFFSET(mmc0_dat3), (MODE(0) | RXACTIVE | PULLUP_EN)},  /* MMC0_DAT3 */
    {OFFSET(mmc0_dat2), (MODE(0) | RXACTIVE | PULLUP_EN)},  /* MMC0_DAT2 */
    {OFFSET(mmc0_dat1), (MODE(0) | RXACTIVE | PULLUP_EN)},  /* MMC0_DAT1 */
    {OFFSET(mmc0_dat0), (MODE(0) | RXACTIVE | PULLUP_EN)},  /* MMC0_DAT0 */
    {OFFSET(mmc0_clk), (MODE(0) | RXACTIVE | PULLUP_EN)},   /* MMC0_CLK */
    {OFFSET(mmc0_cmd), (MODE(0) | RXACTIVE | PULLUP_EN)},   /* MMC0_CMD */
    //{OFFSET(mcasp0_aclkr), (MODE(4) | RXACTIVE)},     /* MMC0_WP */
    //{OFFSET(spi0_cs1), (MODE(7) | RXACTIVE | PULLUP_EN)}, /* GPIO0_6 */
    {OFFSET(ecap0_in_pwm0_out), (MODE(5) | RXACTIVE)},      /* MMC0_WP */
    {OFFSET(spi0_cs1), (MODE(5) | RXACTIVE | PULLUP_EN)},   /* GPIO0_6 */
    {-1},
};

对于串口管脚,TiSDK的u-boot是把它单独列出来的,在board/set/am335-cfe/board.c文件中,
函数set_uart_mux_conf()用来配置用户所选择的串口。
宏CONFIG_CONS_INDEX是通过menuconfig菜单ARM architecture->UART used for console来设定的。用户可以输入1~6来分别选择AM33596个UART的其中之一。常见硬件设计一般将UART0选择为调式终端。因此CONFIG_CONS_INDEX == 1。

void set_uart_mux_conf(void)
{
#if CONFIG_CONS_INDEX == 1
    enable_uart0_pin_mux();
#elif CONFIG_CONS_INDEX == 2
    enable_uart1_pin_mux();
#elif CONFIG_CONS_INDEX == 3
    enable_uart2_pin_mux();
#elif CONFIG_CONS_INDEX == 4
    enable_uart3_pin_mux();
#elif CONFIG_CONS_INDEX == 5
    enable_uart4_pin_mux();
#elif CONFIG_CONS_INDEX == 6
    enable_uart5_pin_mux();
#endif
}

函数enable_uart0_pin_mux()的定义在board/set/am335-cfe/mux.c中:

void enable_uart0_pin_mux(void)
{
    configure_module_pin_mux(uart0_pin_mux);
}

需要确定的是数据结构体uart0_pin_mux是否和硬件一致:

static struct module_pin_mux uart0_pin_mux[] = {
    {OFFSET(uart0_rxd), (MODE(0) | PULLUP_EN | RXACTIVE)},  /* UART0_RXD */
    {OFFSET(uart0_txd), (MODE(0) | PULLUDEN)},      /* UART0_TXD */
    {-1},
};

DDR SDRAM参数移植

我们采用的DDR芯片是samsung的K4B2G1646Q-BY,
根据芯片的datasheet计算寄存器的值,具体信息可以参考:
http://processors.wiki.ti.com/index.php/AM335x_DDR_PHY_register_configuration_for_DDR3_using_Software_Leveling

因为本人手上有一个比较低版本的u-boot代码可以在自己的系统上运行, 因此直接copy其数据值。
在arch/arm/include/asm/arch-am33xx/ddr_defs.h中添加:

/* Samsung K4B2G1646Q-BY */
#define K4B2G1646QBY_EMIF_READ_LATENCY      0x07
#define K4B2G1646QBY_EMIF_TIM1          0x0AAAD4DB
#define K4B2G1646QBY_EMIF_TIM2          0x26437FDA
#define K4B2G1646QBY_EMIF_TIM3          0x501F83FF
#define K4B2G1646QBY_EMIF_SDCFG         0x61C052B2
#define K4B2G1646QBY_EMIF_SDREF         0x0000049E
#define K4B2G1646QBY_ZQ_CFG         0x50074BE4
#define K4B2G1646QBY_DLL_LOCK_DIFF      0x1
#define K4B2G1646QBY_RATIO          0x80
#define K4B2G1646QBY_INVERT_CLKOUT      0x0
#define K4B2G1646QBY_RD_DQS         0x3B
#define K4B2G1646QBY_WR_DQS         0x3E
#define K4B2G1646QBY_PHY_FIFO_WE        0x98
#define K4B2G1646QBY_PHY_WR_DATA        0x78
#define K4B2G1646QBY_IOCTRL_VALUE       0x18B

将DDR的参数赋值给u-boot中的DDR 数据结构体。
在board/set/am335-cfe/board.c中添加:

static const struct ddr_data ddr3_set_cfe_data = {
    .datardsratio0 = K4B2G1646QBY_RD_DQS,
    .datawdsratio0 = K4B2G1646QBY_WR_DQS,
    .datafwsratio0 = K4B2G1646QBY_PHY_FIFO_WE,
    .datawrsratio0 = K4B2G1646QBY_PHY_WR_DATA,
};
static const struct cmd_control ddr3_set_cfe_cmd_ctrl_data = {
    .cmd0csratio = K4B2G1646QBY_RATIO,
    .cmd0iclkout = K4B2G1646QBY_INVERT_CLKOUT,

    .cmd1csratio = K4B2G1646QBY_RATIO,
    .cmd1iclkout = K4B2G1646QBY_INVERT_CLKOUT,

    .cmd2csratio = K4B2G1646QBY_RATIO,
    .cmd2iclkout = K4B2G1646QBY_INVERT_CLKOUT,
};

static struct emif_regs ddr3_set_cfe_emif_reg_data = {
    .sdram_config = K4B2G1646QBY_EMIF_SDCFG,
    .ref_ctrl = K4B2G1646QBY_EMIF_SDREF,
    .sdram_tim1 = K4B2G1646QBY_EMIF_TIM1,
    .sdram_tim2 = K4B2G1646QBY_EMIF_TIM2,
    .sdram_tim3 = K4B2G1646QBY_EMIF_TIM3,
    .zq_config = K4B2G1646QBY_ZQ_CFG,
    .emif_ddr_phy_ctlr_1 = K4B2G1646QBY_EMIF_READ_LATENCY,
};

const struct ctrl_ioregs ioregs_set_cfe = {
    .cm0ioctl       = K4B2G1646QBY_IOCTRL_VALUE,
    .cm1ioctl       = K4B2G1646QBY_IOCTRL_VALUE,
    .cm2ioctl       = K4B2G1646QBY_IOCTRL_VALUE,
    .dt0ioctl       = K4B2G1646QBY_IOCTRL_VALUE,
    .dt1ioctl       = K4B2G1646QBY_IOCTRL_VALUE,
};
...

void sdram_init(void)
{
    if (read_eeprom() < 0)
        puts("Could not get board ID.\n");  
...
    if (board_is_set_cfe())//判断是自己的板子后,写入DDR寄存器的参数。ioregs_set_cfe,ddr3_set_cfe_data,ddr3_set_cfe_cmd_ctrl_data,ddr3_set_cfe_emif_reg_data即为上面定义的数据结构体。
        config_ddr(400, &ioregs_set_cfe, &ddr3_set_cfe_data,
               &ddr3_set_cfe_cmd_ctrl_data, &ddr3_set_cfe_emif_reg_data,
               0);

    else
        config_ddr(266, &ioregs, &ddr2_data,
               &ddr2_cmd_ctrl_data, &ddr2_emif_reg_data, 0);
}

测试

经过上述的移植,重新编译:

rm -fr am335x_cfe/
make ARCH=arm  CROSS_COMPILE=arm-linux-gnueabihf- O=am335x_cfe am335x_set_cfe_defconfig all

通过menuconfig命令来确认串口选择是否是UART0

 make ARCH=arm  CROSS_COMPILE=arm-linux-gnueabihf- O=am335x_cfe menuconfig

编译通过后, 在板子上测试,串口有输出,表示u-boot的移植基本成功。
附注:
在调试的过程中, 如果串口没有输出, 很大可能是DDR 参数配置不对。
本人调试过程中还碰到其他一些问题,但是属于个例,没有在此列出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值