TI AM335x Linux MUX hacking
/********************************************************************************************* * TI AM335x Linux MUX hacking * 声明: * 1. 本文主要是对TI的AM335x Linux驱动中的引脚复用配置代码进行跟踪; * 2. 参考书籍:AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual * * 2015-6-25 阵雨 深圳 南山平山村 曾剑锋 *********************************************************************************************/ \\\\\\\\\\\\\-*- 目录 -*-///////////// | 一、跟踪板级文件: | 二、跟踪am335x_evm_init()函数: | 三、跟踪board_mux参数: | 四、跟踪am33xx_mux_init()函数: | 五、跟踪am33xx_muxmodes参数: | 六、跟踪omap_mux_init()函数: | 七、跟踪omap_mux_init_list()函数: | 八、跟踪omap_mux_init_signals函数: \\\\\\\\\\\\\\\\\\\\////////////////// 一、跟踪板级文件: ...... MACHINE_START(AM335XEVM, "am335xevm") /* Maintainer: Texas Instruments */ .atag_offset = 0x100, .map_io = am335x_evm_map_io, .init_early = am33xx_init_early, .init_irq = ti81xx_init_irq, .handle_irq = omap3_intc_handle_irq, .timer = &omap3_am33xx_timer, .init_machine = am335x_evm_init, // 跟踪该函数 MACHINE_END ^ | MACHINE_START(AM335XIAEVM, "am335xiaevm") | /* Maintainer: Texas Instruments */ | .atag_offset = 0x100, | .map_io = am335x_evm_map_io, | .init_irq = ti81xx_init_irq, | .init_early = am33xx_init_early, | .timer = &omap3_am33xx_timer, | .init_machine = am335x_evm_init, -------------+ MACHINE_END 二、跟踪am335x_evm_init()函数: static void __init am335x_evm_init(void) { am33xx_cpuidle_init(); am33xx_mux_init(board_mux); // 跟踪函数、参数 omap_serial_init(); am335x_evm_i2c_init(); omap_sdrc_init(NULL, NULL); usb_musb_init(&musb_board_data); omap_board_config = am335x_evm_config; omap_board_config_size = ARRAY_SIZE(am335x_evm_config); /* Create an alias for icss clock */ if (clk_add_alias("pruss", NULL, "pruss_uart_gclk", NULL)) pr_warn("failed to create an alias: icss_uart_gclk --> pruss\n"); /* Create an alias for gfx/sgx clock */ if (clk_add_alias("sgx_ck", NULL, "gfx_fclk", NULL)) pr_warn("failed to create an alias: gfx_fclk --> sgx_ck\n"); } 三、跟踪board_mux参数: 1. 跟踪board_mux[]数组: static struct omap_board_mux board_mux[] __initdata = { /* * Setting SYSBOOT[5] should set xdma_event_intr0 pin to mode 3 thereby * allowing clkout1 to be available on xdma_event_intr0. * However, on some boards (like EVM-SK), SYSBOOT[5] isn't properly * latched. * To be extra cautious, setup the pin-mux manually. * If any modules/usecase requries it in different mode, then subsequent * module init call will change the mux accordingly. * * 参考书籍:AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual -- 760页 * -------------------------------------------------------------------- * | Offset | Acronym | Register | Description Section | * -------------------------------------------------------------------- * | 9B0h | conf_xdma_event_intr0 | | Section 9.3.51 | * -------------------------------------------------------------------- * 观察上面的内容和接下来要配置引脚,我们只需要将Acronym中的conf_前缀去掉, * 然后将剩下的字符串大写,就能配置对应的引脚了,如: * 1. conf_xdma_event_intr0 * 2. xdma_event_intr0 * 3. XDMA_EVENT_INTR0 * | * | */ V AM33XX_MUX(XDMA_EVENT_INTR0, OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT), // 跟踪宏 AM33XX_MUX(I2C0_SDA, OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW | AM33XX_INPUT_EN | AM33XX_PIN_OUTPUT), AM33XX_MUX(I2C0_SCL, OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW | AM33XX_INPUT_EN | AM33XX_PIN_OUTPUT), { .reg_offset = OMAP_MUX_TERMINATOR }, //后面的程序通过判断这个表示来结束注册 }; 2. 跟踪struct omap_board_mux结构体: /** * struct omap_board_mux - data for initializing mux registers * @reg_offset: mux register offset from the mux base * @mux_value: desired mux value to set */ struct omap_board_mux { u16 reg_offset; u16 value; }; 3. 跟踪AM33XX_MUX()宏: /* If pin is not defined as input, pull would get disabled. * If defined as input, flags supplied will determine pull on/off. */ // 以此为例: // AM33XX_MUX(XDMA_EVENT_INTR0, OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT) // // .reg_offset = (AM33XX_CONTROL_PADCONF_XDMA_EVENT_INTR0_OFFSET) // 跟踪宏 // .value = (((OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT) & AM33XX_INPUT_EN) \ // ? (OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT) \ // : ((mux_value) | AM33XX_PULL_DISA)) #define AM33XX_MUX(mode0, mux_value) \ { \ .reg_offset = (AM33XX_CONTROL_PADCONF_##mode0##_OFFSET), \ .value = (((mux_value) & AM33XX_INPUT_EN) ? (mux_value)\ : ((mux_value) | AM33XX_PULL_DISA)), \ } 4. 跟踪模式宏声明: /* 34xx mux mode options for each pin. See TRM for options */ #define OMAP_MUX_MODE0 0 #define OMAP_MUX_MODE1 1 #define OMAP_MUX_MODE2 2 #define OMAP_MUX_MODE3 3 #define OMAP_MUX_MODE4 4 #define OMAP_MUX_MODE5 5 #define OMAP_MUX_MODE6 6 #define OMAP_MUX_MODE7 7 5. 跟踪输出宏声明: /* Definition of output pin could have pull disabled, but * this has not been done due to two reasons * 1. AM33XX_MUX will take care of it * 2. If pull was disabled for out macro, combining out & in pull on macros * would disable pull resistor and AM33XX_MUX cannot take care of the * correct pull setting and unintentionally pull would get disabled */ #define AM33XX_PIN_OUTPUT (0) 6. 跟踪引脚配置偏移宏,并对比数据手册: // 参考书籍:AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual -- 760页 // ----------------------------------------------------------------------- // | Offset | Acronym | Register | Description Section | // +--------+-----------------------+------------+-----------------------+ // | 9B0h | conf_xdma_event_intr0 | | Section 9.3.51 | // ----------------------------------------------------------------------- #define AM33XX_CONTROL_PADCONF_XDMA_EVENT_INTR0_OFFSET 0x09B0 四、跟踪am33xx_mux_init()函数: 1. 跟踪am33xx_mux_init()函数: int __init am33xx_mux_init(struct omap_board_mux *board_subset) { return omap_mux_init("core", 0, AM33XX_CONTROL_PADCONF_MUX_PBASE, // 跟踪参数 AM33XX_CONTROL_PADCONF_MUX_SIZE, am33xx_muxmodes, NULL, board_subset, NULL); } 2. 跟踪AM33XX_CONTROL_PADCONF_MUX_PBASE宏: // 参考书籍:AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual -- 158页 // ---------------------------------------------------------------------------------------------------- // | Region Name | Start Address (hex) | End Address (hex) | Size | Description | // +-----------------+---------------------+--------------------+--------+----------------------------+ // | Control Module | 0x44E1_0000 | 0x44E1_1FFF | 128KB | Control Module Registers | // ---------------------------------------------------------------------------------------------------- #define AM33XX_CONTROL_PADCONF_MUX_PBASE 0x44E10000LU 3. 跟踪AM33XX_CONTROL_PADCONF_MUX_SIZE宏: // 参考书籍:AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual -- 761页 // 这里的大小没有搞懂,在datasheet中是1444h,而这里是B34h,没搞懂 // ---------------------------------------------------------------- // | Offset | Acronym | Register | Description Section | // +--------+-------------------+----------+----------------------+ // | 1444h | ddr_data1_ioctrl | | Section 9.3.92 | // ---------------------------------------------------------------- #define AM33XX_CONTROL_PADCONF_VREFN_OFFSET 0x0B34 #define AM33XX_CONTROL_PADCONF_MUX_SIZE \ (AM33XX_CONTROL_PADCONF_VREFN_OFFSET + 0x4) 五、跟踪am33xx_muxmodes参数: 1. 跟踪am33xx_muxmodes[]数组: /* AM33XX pin mux super set */ static struct omap_mux am33xx_muxmodes[] = { _AM33XX_MUXENTRY(GPMC_AD0, 0, "gpmc_ad0", "mmc1_dat0", NULL, NULL, NULL, NULL, NULL, "gpio1_0"), _AM33XX_MUXENTRY(GPMC_AD1, 0, "gpmc_ad1", "mmc1_dat1", NULL, NULL, NULL, NULL, NULL, "gpio1_1"), _AM33XX_MUXENTRY(GPMC_AD2, 0, "gpmc_ad2", "mmc1_dat2", NULL, NULL, NULL, NULL, NULL, "gpio1_2"), _AM33XX_MUXENTRY(GPMC_AD3, 0, "gpmc_ad3", "mmc1_dat3", NULL, NULL, NULL, NULL, NULL, "gpio1_3"), _AM33XX_MUXENTRY(GPMC_AD4, 0, "gpmc_ad4", "mmc1_dat4", NULL, NULL, NULL, NULL, NULL, "gpio1_4"), ...... { .reg_offset = OMAP_MUX_TERMINATOR }, //后面的程序通过判断这个表示来结束注册 } 2. 跟踪struct omap_mux结构体: /** * struct omap_mux - data for omap mux register offset and it's value * @reg_offset: mux register offset from the mux base * @gpio: GPIO number * @muxnames: available signal modes for a ball * @balls: available balls on the package * @partition: mux partition */ struct omap_mux { u16 reg_offset; u16 gpio; #ifdef CONFIG_OMAP_MUX char *muxnames[OMAP_MUX_NR_MODES]; #ifdef CONFIG_DEBUG_FS char *balls[OMAP_MUX_NR_SIDES]; #endif #endif }; 2. 跟踪_AM33XX_MUXENTRY宏: // // 以此为例: // _AM33XX_MUXENTRY(GPMC_AD0, 0, "gpmc_ad0", "mmc1_dat0", NULL, NULL, NULL, NULL, NULL, "gpio1_0") // // .reg_offset = AM33XX_CONTROL_PADCONF_GPMC_AD0_OFFSET // .gpio = 0 // 相当于取下面muxnames中的第0个 // .muxnames = {"gpmc_ad0", "mmc1_dat0", NULL, NULL, NULL, NULL, NULL, "gpio1_0"} #define _AM33XX_MUXENTRY(M0, g, m0, m1, m2, m3, m4, m5, m6, m7) \ { \ .reg_offset = (AM33XX_CONTROL_PADCONF_##M0##_OFFSET), \ .gpio = (g), \ .muxnames = { m0, m1, m2, m3, m4, m5, m6, m7 }, \ } 3. 跟踪AM33XX_CONTROL_PADCONF_GPMC_AD0_OFFSET宏,并对比datasheet: // 参考书籍:AM335x ARM® Cortex™-A8 Microprocessors (MPUs) Technical Reference Manual -- 758页 // ------------------------------------------------------------------------------------------------------------ // | Offset | Acronym | Register | Description Section // +--------+----------------+-----------+--------------------------------------------------------------------- // | 800h | conf_gpmc_ad0 | | See the device datasheet for information on default pin Section 9.3.51 // ------------------------------------------------------------------------------------------------------------ #define AM33XX_CONTROL_PADCONF_GPMC_AD0_OFFSET 0x0800 六、跟踪omap_mux_init()函数: int __init omap_mux_init(const char *name, u32 flags, u32 mux_pbase, u32 mux_size, struct omap_mux *superset, struct omap_mux *package_subset, struct omap_board_mux *board_mux, struct omap_ball *package_balls) { struct omap_mux_partition *partition; partition = kzalloc(sizeof(struct omap_mux_partition), GFP_KERNEL); if (!partition) return -ENOMEM; partition->name = name; // 分区的意思相当于模块的意思 partition->flags = flags; partition->size = mux_size; partition->phys = mux_pbase; partition->base = ioremap(mux_pbase, mux_size); // 重新映射IO地址 if (!partition->base) { pr_err("%s: Could not ioremap mux partition at 0x%08x\n", __func__, partition->phys); kfree(partition); return -ENODEV; } INIT_LIST_HEAD(&partition->muxmodes); // 初始化分区头结点链表 list_add_tail(&partition->node, &mux_partitions); // 将分区加入分区链表 mux_partitions_cnt++; // 分区总数统计 pr_info("%s: Add partition: #%d: %s, flags: %x\n", __func__, mux_partitions_cnt, partition->name, partition->flags); omap_mux_init_package(superset, package_subset, package_balls); // 两个都是null omap_mux_init_list(partition, superset); // 跟踪函数 omap_mux_init_signals(partition, board_mux); // 跟踪函数 return 0; } 七、跟踪omap_mux_init_list()函数: 1. 跟踪omap_mux_init_list()函数: /* * Note if CONFIG_OMAP_MUX is not selected, we will only initialize * the GPIO to mux offset mapping that is needed for dynamic muxing * of GPIO pins for off-idle. */ static void __init omap_mux_init_list(struct omap_mux_partition *partition, struct omap_mux *superset) { while (superset->reg_offset != OMAP_MUX_TERMINATOR) { struct omap_mux *entry; // 去掉一些不符合要求的的配置引脚 #ifdef CONFIG_OMAP_MUX if (!superset->muxnames || !superset->muxnames[0]) { superset++; continue; } #else /* Skip pins that are not muxed as GPIO by bootloader */ if (!OMAP_MODE_GPIO(omap_mux_read(partition, superset->reg_offset))) { superset++; continue; } #endif entry = omap_mux_list_add(partition, superset); if (!entry) { pr_err("%s: Could not add entry\n", __func__); return; } superset++; } } 2. 跟踪omap_mux_list_add()函数: static struct omap_mux * __init omap_mux_list_add( struct omap_mux_partition *partition, struct omap_mux *src) { struct omap_mux_entry *entry; struct omap_mux *m; entry = kzalloc(sizeof(struct omap_mux_entry), GFP_KERNEL); if (!entry) return NULL; m = &entry->mux; entry->mux = *src; #ifdef CONFIG_OMAP_MUX if (omap_mux_copy_names(src, m)) { // 将数据另存的感觉 kfree(entry); return NULL; } #endif mutex_lock(&muxmode_mutex); list_add_tail(&entry->node, &partition->muxmodes); // 将节点放入分区节点链表中 mutex_unlock(&muxmode_mutex); return m; } 3. 跟踪omap_mux_copy_names()函数: // 这个函数的大概意思也就是转存的感觉 static int __init omap_mux_copy_names(struct omap_mux *src, struct omap_mux *dst) { int i; for (i = 0; i < OMAP_MUX_NR_MODES; i++) { if (src->muxnames[i]) { dst->muxnames[i] = kstrdup(src->muxnames[i], GFP_KERNEL); if (!dst->muxnames[i]) goto free; } } #ifdef CONFIG_DEBUG_FS for (i = 0; i < OMAP_MUX_NR_SIDES; i++) { if (src->balls[i]) { dst->balls[i] = kstrdup(src->balls[i], GFP_KERNEL); if (!dst->balls[i]) goto free; } } #endif return 0; free: omap_mux_free_names(dst); return -ENOMEM; } 八、跟踪omap_mux_init_signals函数: 1. 跟踪omap_mux_init_signals()函数: static void omap_mux_init_signals(struct omap_mux_partition *partition, struct omap_board_mux *board_mux) { omap_mux_set_cmdline_signals(); // 不知道这里是干啥的,不跟踪 omap_mux_write_array(partition, board_mux); // 跟踪该函数 } 2. 跟踪omap_mux_write_array()函数: void omap_mux_write_array(struct omap_mux_partition *partition, struct omap_board_mux *board_mux) { if (!board_mux) return; while (board_mux->reg_offset != OMAP_MUX_TERMINATOR) { omap_mux_write(partition, board_mux->value, // 跟踪函数 board_mux->reg_offset); board_mux++; } } 3. 跟踪am33xx_mux_init()函数: /* * int __init am33xx_mux_init(struct omap_board_mux *board_subset) * { * return omap_mux_init("core", 0 /* flag = 0 */, AM33XX_CONTROL_PADCONF_MUX_PBASE, * AM33XX_CONTROL_PADCONF_MUX_SIZE, am33xx_muxmodes, * NULL, board_subset, NULL); * } */ void omap_mux_write(struct omap_mux_partition *partition, u16 val, u16 reg) { if (partition->flags & OMAP_MUX_REG_8BIT) __raw_writeb(val, partition->base + reg); else __raw_writew(val, partition->base + reg); } 4. 跟踪OMAP_MUX_REG_8BIT宏: /* * omap_mux_init flags definition: * * OMAP_MUX_REG_8BIT: Ensure that access to padconf is done in 8 bits. * The default value is 16 bits. * OMAP_MUX_GPIO_IN_MODE3: The GPIO is selected in mode3. * The default is mode4. */ #define OMAP_MUX_REG_8BIT (1 << 0)