PADMUX参考说明


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 04/18/2023
    1.1
  • 增加第八章DEBUG&FAQ
  • 03/31/2025

    1. 概述

    Padmux是位于kernel\drivers\sstar\padmux的一个linux platform驱动。因为gpio pin复用的关系,通常同一个gpio可以用作不同的功能,所以我们就用padmux驱动在开机的时候统一将需要使用的gpio pin功能配置好。


    2. 关键字说明

    详请参考GPIO 关键字说明


    3. 功能描述

    • 将指定pad复用为指定mode
    • 管理chip的管脚复用

    4. 硬件连接

    padmux为软件行为,无硬件连接。


    5. Uboot用法介绍

    kernel用法介绍


    6. Kernel用法介绍

    Padmux驱动定义如下:

    static struct platform_driver sstar_padmux_driver = {
        .driver =
        {
            .name           = "padmux",
            .owner          = THIS_MODULE,
            .of_match_table = sstar_padmux_of_match,
            .pm             = &sstar_padmux_pm_ops,
        },
        .probe = sstar_padmux_probe,
    };
    

    需要关注的成员如下,下面会分别介绍。

    sstar_padmux_of_match
    sstar_padmux_pm_ops
    sstar_padmux_probe
    

    6.1. sstar_padmux_of_match

    用来匹配开机需要配置的gpio table设备:

    static const struct of_device_id sstar_padmux_of_match[] = {
        {.compatible = "sstar,padmux"},
        {},
    };
    

    通过sstar-padmux在dts(例如:kernel\arch\arm64\boot\dts\sstar\pcupid-ssm001a-s01a-padmux.dtsi)中匹配到设备:

    padmux {
            compatible = "sstar,padmux";
            schematic =
    
            //I2C0 Mode1,sensorif_mipi_grp0_i2c
            <PAD_I2C0_SDA           PINMUX_FOR_I2C0_MODE_1           MDRV_PUSE_I2C0_SDA>,
            <PAD_I2C0_SCL           PINMUX_FOR_I2C0_MODE_1           MDRV_PUSE_I2C0_SCL>,
    
            //I2C1 Mode1,sensorif_mipi_grp1_i2c
            <PAD_I2C1_SCL           PINMUX_FOR_I2C1_MODE_1           MDRV_PUSE_I2C1_SCL>,
            <PAD_I2C1_SDA           PINMUX_FOR_I2C1_MODE_1           MDRV_PUSE_I2C1_SDA>,
    
            //ToucheScreen goodix
            <PAD_I2C5_SCL           PINMUX_FOR_I2C5_MODE_1           MDRV_PUSE_I2C5_SCL>,
            <PAD_I2C5_SDA           PINMUX_FOR_I2C5_MODE_1           MDRV_PUSE_I2C5_SDA>,
            <GPIO_NR                 PINMUX_FOR_UNKNOWN_MODE         MDRV_PUSE_NA>;
            status = "ok"; // ok or disable
        };
    

    6.2. sstar_padmux_pm_ops

    实现str待机对应的resume和suspend函数:

    static const struct dev_pm_ops sstar_padmux_pm_ops = {
        .suspend_late = sstar_padmux_suspend,
        .resume_early = sstar_padmux_resume,
    };
    

    6.3. sstar_padmux_probe

    在匹配到padmux设备后,系统会call sstar_padmux_probe进行gpio功能配置:

    static int sstar_padmux_probe(struct platform_device *pdev)
    {
        _mdrv_padmux_dts(pdev->dev.of_node);
        return 0;
    }
    static int _mdrv_padmux_dts(struct device_node *np)
    {
        int nPad;
    
        if (0 >= (nPad = of_property_count_elems_of_size(np, PADINFO_NAME, sizeof(pad_info_t))))
        {
            PAD_PRINT("[%s][%d] invalid dts of padmux.schematic\n", __FUNCTION__, __LINE__);
            return -1;
        }
        if (NULL == (_pPadInfo = kmalloc(nPad * sizeof(pad_info_t), GFP_KERNEL)))
        {
            PAD_PRINT("[%s][%d] kmalloc fail\n", __FUNCTION__, __LINE__);
            return -1;
        }
        if (of_property_read_u32_array(np, PADINFO_NAME, (u32 *)_pPadInfo, nPad * sizeof(pad_info_t) / sizeof(U32)))
        {
            PAD_PRINT("[%s][%d] of_property_read_u32_array fail\n", __FUNCTION__, __LINE__);
            kfree(_pPadInfo);
            _pPadInfo = NULL;
            return -1;
        }
        _nPad = nPad;
    
        {
            int i;
            PAD_PRINT("[%s][%d] *******************************\n", __FUNCTION__, __LINE__);
            for (i = 0; i < _nPad; i++)
            {
                PAD_PRINT("[%s][%d] (PadId, Mode, Puse) = (%d, 0x%02x, 0x%08x)\n", __FUNCTION__, __LINE__,
                          _pPadInfo[i].u32PadId, _pPadInfo[i].u32Mode, _pPadInfo[i].u32Puse);
                sstar_gpio_pad_val_set((U8)_pPadInfo[i].u32PadId & 0xFF, _pPadInfo[i].u32Mode);
            }
            PAD_PRINT("[%s][%d] *******************************\n", __FUNCTION__, __LINE__);
        }
        return 0;
    }
    

    最终通过sstar_gpio_pad_val_set将gpio配置成对应的功能:

    kernel\drivers\sstar\gpio\drv_gpio.c

    u8 sstar_gpio_pad_val_set(u8 gpio_index, u32 pad_mode)
    {
        return hal_gpio_pad_val_set(gpio_index, pad_mode);
    }
    

    kernel\drivers\sstar\gpio\pcupid\hal_gpio.c

    u8 hal_gpio_pad_val_set(u8 gpio_index, u32 pad_mode)
    {
        return hal_gpio_pad_set_val((u32)gpio_index, pad_mode);
    }
    

    kernel\drivers\sstar\gpio\pcupid\hal_pinmux.c

    s32 hal_gpio_pad_set_val(u32 pad_id, u32 mode)
    {
        if (FALSE == hal_gpio_check_pin(pad_id))
        {
            return 1;
        }
        else
        {
            return hal_gpio_pad_set_mode_general(pad_id, mode);
        }
    }
    static s32 hal_gpio_pad_set_mode_general(u32 pad_id, u32 mode)
    {
        u64 reg_addr     = 0;
        u16 reg_val      = 0;
        u8  mode_is_find = 0;
        u16 i, ext_item_id = 0;
    
        for (i = 0; i < m_hal_gpio_st_padmux_entry[pad_id].size; i++)
        {
            reg_addr = _RIUA_16BIT(m_hal_gpio_st_padmux_entry[pad_id].padmux[i].base,
                                   m_hal_gpio_st_padmux_entry[pad_id].padmux[i].offset);
            if (mode == m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mode)
            {
                reg_val = _GPIO_R_WORD_MASK(reg_addr, 0xFFFF);
                reg_val &= ~(m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mask);
                reg_val |= m_hal_gpio_st_padmux_entry[pad_id].padmux[i].val; // CHECK Multi-Pad Mode
                _GPIO_W_WORD_MASK(reg_addr, reg_val, 0xFFFF);
                mode_is_find             = 1;
                pad_mode_recoder[pad_id] = mode;
    #if (ENABLE_CHECK_ALL_PAD_CONFLICT == 0)
                break;
    #endif
            }
            /*else if ((m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mode >= PINMUX_FOR_PM_PAD_EXT_MODE0_1)
                     && (m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mode <= PINMUX_FOR_PM_PAD_EXT_MODE51_1))
            {
                ext_item_id = i;
            }*/
            /*else if ((m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mode >= PINMUX_FOR_SPI_EXT_EN_MODE0_1)
                     && (m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mode <= PINMUX_FOR_SPI_EXT_EN_MODE5_1))
            {
                ext_item_id = i;
            }*/
            else
            {
                if ((mode == PINMUX_FOR_GPIO_MODE)
                    && (m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mode > PRIORITY_GREATER_GPIO))
                    continue;
                reg_val = _GPIO_R_WORD_MASK(reg_addr, m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mask);
                if (reg_val == m_hal_gpio_st_padmux_entry[pad_id].padmux[i].val)
                {
                    hal_pinmux_info(
                        "[Padmux]reset PAD%d(reg 0x%x:%x; mask0x%x) t0 %s (org: %s)\n", pad_id,
                        m_hal_gpio_st_padmux_entry[pad_id].padmux[i].base,
                        m_hal_gpio_st_padmux_entry[pad_id].padmux[i].offset,
                        m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mask, m_hal_gpio_st_padmode_info_tbl[mode].pad_name,
                        m_hal_gpio_st_padmode_info_tbl[m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mode].pad_name);
                    if (m_hal_gpio_st_padmux_entry[pad_id].padmux[i].val != 0)
                    {
                        _GPIO_W_WORD_MASK(reg_addr, 0, m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mask);
                    }
                    else
                    {
                        _GPIO_W_WORD_MASK(reg_addr, m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mask,
                                          m_hal_gpio_st_padmux_entry[pad_id].padmux[i].mask);
                    }
                }
            }
        }
    
        if ((mode_is_find) && (ext_item_id))
        {
            // set external data mode
            reg_addr = _RIUA_16BIT(m_hal_gpio_st_padmux_entry[pad_id].padmux[ext_item_id].base,
                                   m_hal_gpio_st_padmux_entry[pad_id].padmux[ext_item_id].offset);
            reg_val  = _GPIO_R_WORD_MASK(reg_addr, 0xFFFF);
            reg_val &= ~(m_hal_gpio_st_padmux_entry[pad_id].padmux[ext_item_id].mask);
            reg_val |= m_hal_gpio_st_padmux_entry[pad_id].padmux[ext_item_id].val; // CHECK Multi-Pad Mode
            _GPIO_W_WORD_MASK(reg_addr, reg_val, 0xFFFF);
        }
        return (mode_is_find) ? 0 : 1;
    }
    

    通过在m_hal_gpio_st_padmux_entry里面对应的子table找到需要配置的gpio和mode设定对应的reg。

    以PAD_GPIOB_00配置成PINMUX_FOR_I2C0_MODE_4为例:

    当在dts padmux里面配置了:

     <PAD_GPIOB_00  PINMUX_FOR_I2C0_MODE_4           MDRV_PUSE_I2C0_SCL>,
     <PAD_GPIOB_01  PINMUX_FOR_I2C0_MODE_4           MDRV_PUSE_I2C0_SDA>,
    

    在probe的时候会根据m_hal_gpio_st_padmux_entry里面的gpiob_00_tbl的寄存器设定将寄存器bank 0x103c offset 0x6f的bit2设置为1,bit0~bit2的mask值为4,所以配置成i2c mode4:

    #define PADTOP_BANK    0x103C00
    #define REG_I2C0_MODE             0x6f
    #define REG_I2C0_MODE_MASK        BIT0 | BIT1 | BIT2
    {PAD_GPIOB_00, PADTOP_BANK, REG_I2Cf0_MODE, REG_I2C0_MODE_MASK, BIT2, PINMUX_FOR_I2C0_MODE_4},可以在板子通过读取reg确认设定有没有生效
    

    6.4. PadMux怎么用

    通过上面padmux驱动的介绍,我们知道只要确认padmux驱动有enable,并在dts里面将需要配置的pin和对应的mode写进去,开机padmux probe的时候就会将gpio对应的功能配置好。需要注意的是pamdux驱动依赖gpio驱动,所以这两个驱动都要build进kernel。目前公板默认的配置gpio和padmux都有默认打开buildin:

    CONFIG_SSTAR_PADMUX
    CONFIG_SSTAR_GPIO
    

    下面介绍如何在dts的padmux中配置对应的gpio功能。


    6.5. 用哪个padmux dts文件

    根据下面的flow由kernel config文件确定用哪个padmux dts文件。

    kernel\arch\arm64\configs\pcupid_ssm001a_s01a_emmc_defconfig:

    CONFIG_SSTAR_DTB_NAME="pcupid-ssm001a-s01a"
    

    kernel\arch\arm64\boot\dts\sstar\pcupid-ssm001a-s01a.dts:

    #include "pcupid.dtsi"
    #include "pcupid-ssm001a-s01a-padmux.dtsi"
    

    kernel\arch\arm64\boot\dts\sstar\pcupid-ssm001a-s01a-padmux.dtsi


    6.6. 配置gpio mode

    根据SDK release的硬件发布资料HW Checklist(具体版本参考sdk发布包的文件名),找到padmux这栏;

    以将PAD_GPIOB_00配置成I2C0为例,可以看到PAD_GPIOB_00要配置成I2C0只能将其配置成I2C0 mode 4,所以在padmux dts里面配置如下:

    <PAD_GPIOB_00    PINMUX_FOR_I2C0_MODE_4           MDRV_PUSE_I2C0_SCL>,
    <PAD_GPIOB_01    PINMUX_FOR_I2C0_MODE_4           MDRV_PUSE_I2C0_SDA>,
    

    其中MDRV_PUSE_I2C0_SCL一般只做说明注释没有特殊用途,除非对应功能驱动有特别设定。其他i2c,spi,uart,ttl,mipi的设定也可以参考hw checklist在padmux dts配置。


    6.7. 注意事项

    同一个gpio pin只能复用成一种功能,不能同时在padmux配置成两种功能。如果在padmux配置的功能不生效,需要在dts确认是否被配置成其他功能了。


    7. API参考

    API可参考GPIO API参考

    8. FAQ

    FAQ可参考GPIO FAQ