SSU_PADMUX参考说明


1. Padmux是什么

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


2. PadMux驱动介绍

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

2.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\arm\boot\dts\pioneer5-ssc028a-s01a-demo-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
    };

2.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,
};

2.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);
            MDrv_GPIO_PadVal_Set((U8)_pPadInfo[i].u32PadId & 0xFF, _pPadInfo[i].u32Mode);
        }
        PAD_PRINT("[%s][%d] *******************************\n", __FUNCTION__, __LINE__);
    }

    return 0;
}

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

kernel\drivers\sstar\gpio\mdrv_gpio.c

int MDrv_GPIO_PadVal_Set(U8 u8IndexGPIO, U32 u32PadMode)
{
    return MHal_GPIO_PadVal_Set(u8IndexGPIO, u32PadMode);
}

kernel\drivers\sstar\gpio\pioneer5\mhal_gpio.c

int MHal_GPIO_PadVal_Set(U8 u8IndexGPIO, U32 u32PadMode)
{
    return HalPadSetVal((U32)u8IndexGPIO, u32PadMode);
}

kernel\drivers\sstar\gpio\pioneer5\mhal_pinmux.c

S32 HalPadSetVal(U32 u32PadID, U32 u32Mode)
{
    if (FALSE == _HalCheckPin(u32PadID)) {
        return FALSE;
    }
    if(u32PadID>=PAD_ETH_RN && u32PadID <= PAD_SAR_GPIO3)
        return HalPadSetMode_MISC(u32PadID, u32Mode);
    return HalPadSetMode_General(u32PadID, u32Mode);
}
static S32 HalPadSetMode_General(U32 u32PadID, U32 u32Mode)
{
    U32 u32RegAddr   = 0;
    U16 u16RegVal    = 0;
    U8  u8ModeIsFind = 0;
    U16 i            = 0;

    for (i = 0; i < m_stPadMuxEntry[u32PadID].size; i++)
    {
        u32RegAddr = _RIUA_16BIT(m_stPadMuxEntry[u32PadID].padmux[i].base, m_stPadMuxEntry[u32PadID].padmux[i].offset);
        if (u32Mode == m_stPadMuxEntry[u32PadID].padmux[i].mode)
        {
            u16RegVal = _GPIO_R_WORD_MASK(u32RegAddr, 0xFFFF);
            u16RegVal &= ~(m_stPadMuxEntry[u32PadID].padmux[i].mask);
            u16RegVal |= m_stPadMuxEntry[u32PadID].padmux[i].val; // CHECK Multi-Pad Mode
            _GPIO_W_WORD_MASK(u32RegAddr, u16RegVal, 0xFFFF);
            u8ModeIsFind       = 1;
            Pad_Mode[u32PadID] = u32Mode;
            #if (ENABLE_CHECK_ALL_PAD_CONFLICT == 0)
            break;
            #endif
        }
        else
        {
            if ((u32Mode == PINMUX_FOR_GPIO_MODE) && (m_stPadMuxEntry[u32PadID].padmux[i].mode > PRIORITY_GREATER_GPIO))
                continue;
            u16RegVal = _GPIO_R_WORD_MASK(u32RegAddr, m_stPadMuxEntry[u32PadID].padmux[i].mask);
            if (u16RegVal == m_stPadMuxEntry[u32PadID].padmux[i].val)
            {
                printk(KERN_INFO "[Padmux]reset PAD%d(reg 0x%x:%x; mask0x%x) t0 %s (org: %s)\n", u32PadID,
                    m_stPadMuxEntry[u32PadID].padmux[i].base, m_stPadMuxEntry[u32PadID].padmux[i].offset,
                    m_stPadMuxEntry[u32PadID].padmux[i].mask, m_stPadModeInfoTbl[u32Mode].u8PadName,
                    m_stPadModeInfoTbl[m_stPadMuxEntry[u32PadID].padmux[i].mode].u8PadName);
                if (m_stPadMuxEntry[u32PadID].padmux[i].val != 0)
                {
                    _GPIO_W_WORD_MASK(u32RegAddr, 0, m_stPadMuxEntry[u32PadID].padmux[i].mask);
                }
                else
                {
                    _GPIO_W_WORD_MASK(u32RegAddr, m_stPadMuxEntry[u32PadID].padmux[i].mask,
                                    m_stPadMuxEntry[u32PadID].padmux[i].mask);
                }
            }
        }
    }
    return (u8ModeIsFind) ? 0 : 1;
}

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

以PAD_PM_PWM0配置成PINMUX_FOR_PWM0_MODE_1为例:

当在dts padmux里面配置了:

<PAD_PM_PWM0      PINMUX_FOR_PWM0_MODE_1    MDRV_PUSE_PWM0>,

在probe的时候会根据m_stPadMuxTbl的寄存器设定将寄存器bank 0x103c offset 0x65的bit2设置为1:

#define PADTOP_BANK    0x103C00
#define REG_PWM0_MODE                0x51
#define REG_PWM0_MODE_MASK           BIT0 | BIT1
{PAD_PM_PWM0, PM_PADTOP_BANK, REG_PWM0_MODE, REG_PWM0_MODE_MASK, BIT0, PINMUX_FOR_PWM0_MODE_1},

可以在板子通过读取reg确认设定有没有生效:


3. PadMux怎么用

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

CONFIG_MS_GPIO=y

CONFIG_MS_PADMUX=y

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

3.1. 用哪个padmux dts文件

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

kernel\arch\arm\configs\pioneer5_ssc028a_s01a_demo_defconfig:

CONFIG_SS_DTB_NAME="pioneer5-ssc028a-s01a-demo"

kernel\arch\arm\boot\dts\pioneer3-ssc020a-s01a-demo.dts:

#include "pioneer5.dtsi"
#include "pioneer5-demo.dtsi"
#include "pioneer5-ssc028a-s01a-demo-padmux.dtsi"

kernel\arch\arm\boot\dts\pioneer5-ssc028a-s01a-demo-padmux.dtsi


3.2. 配置gpio mode

根据SDK release的硬件发布资料SSU9383_2L HW Checklist V1.0_20220924.xlsx,找到padmux这栏;

以将PAD_PM_PWM0配置成PWM0为例,可以看到PAD_PM_PWM0要配置成PWM0只能将其配置成pwm0 mode 1,所以在padmux dts里面配置如下:

<PAD_PM_PWM0      PINMUX_FOR_PWM0_MODE_1    MDRV_PUSE_PWM0>,

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


4. 注意事项

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