• 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
    ");
            /* 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
    ");
        } 
    
    三、跟踪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
    ",
                    __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
    ", __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
    ", __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)
  • 相关阅读:
    品Spring:真没想到,三十步才能完成一个bean实例的创建
    品Spring:对@Autowired和@Value注解的处理方法
    品Spring:对@Resource注解的处理方法
    品Spring:对@PostConstruct和@PreDestroy注解的处理方法
    品Spring:详细解说bean后处理器
    品Spring:bean工厂后处理器的调用规则
    品Spring:注解之王@Configuration和它的一众“小弟们”
    品Spring:SpringBoot发起bean定义注册的“二次攻坚战”
    品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”
    .net的retrofit--WebApiClient底层篇
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4599595.html
Copyright © 2020-2023  润新知