• MarvellLinux研究—mfp.c/.h源代码分析


    Marvell-Linux研究—mfp.c/.h源代码分析

     

    转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

    作者联系方式:李先静 <xianjimli at hotmail dot com>

    更新时间:2007-7-9

     

    Multi-Function PinPXA3xx中的一个新概念,它可以让一个Pin具有多个功能,达到复用的效果,从而减少PIN的个数。比如说,一个Pin可以作为GPIO,可以作为时钟信号,也可以作为地址线或者数据线,完全根据软件配置决定它的实际用途。

     

    虽然同一个Pin可以用作多种不同的用途,但在任意时刻只有一种用途,这是很容易理解的,否则就会乱套了。但是在不同时刻,它能否根据需要动态切换Pin的功能呢?我想从理论是可行的,但对硬件设计可能有特别的要求,才能保证不会互相冲突。我在代码中没有看到这种用法,不知道实际中是否有人这样用呢。同一个Pin具有相同的配置,但是用于不同设备倒是正常的,像数据线和地址线就属此类。

     

    下面我们来看看代码:

     

      21 struct mhn_pin_config {
      22         mfp_pin_t    mfp_pin;
      23         unsigned int reserved:16;
      24         unsigned int af_sel:3;
      25         unsigned int edge_rise_en:1;
      26         unsigned int edge_fall_en:1;
      27         unsigned int edge_clear:1;
      28         unsigned int sleep_oe_n:1;
      29         unsigned int sleep_data:1;
      30         unsigned int sleep_sel:1;
      31         unsigned int drive:3;
      32         unsigned int pulldown_en:1;
      33         unsigned int pullup_en:1;
      34         unsigned int pull_sel:1;
      35 };

    (mfp.h)

     

    这个结构用于描述MFP的配置,它与MFPRx寄存器中的位域基本上一一对应,这里我们简单说明一下:

     

    mfp_pin MFPID,用宏MFP_REGmfp_pin为参数,可以计算出mfp_pinMFPR寄存器地址。

     

    af_sel 其中af代表alternate function,用于选择实际的功能,它最多有八种可能的选择,可参照《PXA300 and PXA310 Developers Manual 1》的4.3/4.4节。

     

    edge_rise_en/ edge_fall_en/ edge_clear 用于控制上升沿/下降沿中断的禁用或启用。我有点疑惑的是,如果该Pin配置为GPIO,而GPIO有自己的上升沿/下降沿配置,两者是否冲突呢?

     

    sleep_oe_n 在低功耗模式 (low power mode)下,它决定Pin作为输入还是输出, 1表示输入,0表示输出。

     

    sleep_data在低功耗模式 (low power mode)下,如果作为输出,它决定输出高电平还是低电平。

     

    sleep_sel 控制在正模式和低功耗模式之间切换时的行为。可以参考《PXA300 and PXA310 Developers Manual 1》的表4.8

     

    drive 控制Pin的驱动能力,可以在1mA10mA之间调整,另外可以调整slew rateslew rate决定电平翻转的速度。

     

    pulldown_en/ pullup_en 是否启用内部下/上拉电阻,只有作为Input Pin时才有用。我们知道下/上拉电阻的主要目的就是防止Pin悬空,悬空的Pin可以看作一个天线,它容易受外界干扰,造成Pin值的不确实性。下/上拉电阻能够给Input Pin一个默认的输入值,下拉电阻接地,默认输入低电平,上拉电阻接电源,默认输入高电平。当真正有外接输入时,它们自动无效,同时避免短路,起到限流作用。

     

    pull_sel 用于决定pulldown_en/ pullup_en的有效性,在低功耗模式下,pull_sel的实际值不改变,但有效值相当于1。也就是说在低功耗模式下,pulldown_en/ pullup_en的值始终决定默认输入,这可以防止干扰信号唤醒CPU

     

    在《PXA300 and PXA310 Developers Manual 1》的表4.6中,对pulldown_en/ pullup_en的描述是自相矛盾的,比如对pullup_en的描述,前面说,1 = The internal pullup resistor of the pad is enabled,后面又说,The resistor is only enabled if PULL_SEL=1(or is effectively 1) and if PULLUP_EN is 0。前者说1启用内部上拉电阻,后者又说0启用内部上拉电阻,我想前者是对的后者是错的。

     

     113 #define MHN_MFP_CFG(desc, pin, af, drv, rdh, lpm, edge) /
     114 {                                                       /
     115         .mfp_pin = pin,                                 /
     116         .af_sel  = af,                                  /
     117         .reserved       = 0,                         /
     118         .drive          = drv,                          /
     119         .sleep_sel      = rdh,                          /
     120         .sleep_oe_n     = ((lpm) & 0x1),           /
     121         .sleep_data     = (((lpm) & 0x2)  >>1),            /
     122         .pullup_en      = (((lpm) & 0x4)  >>2),             /
     123         .pulldown_en    = (((lpm) & 0x8)  >>3),           /
     124         .pull_sel       = (((lpm) & 0x10) >>4),              /
     125         .edge_clear     = (!(edge)),                    /
     126         .edge_fall_en   = ((edge) & 0x1),                /
     127         .edge_rise_en   = (((edge) & 0x2) >>1),          /
     128 }

    (mfp.h)

     

    MHN_MFP_CFG的功能很简单,但是很常用,它简化了MFP配置的初始化。

     

     145 #define PIN2REG(pin_config)                                     /
     146                 (pin_config->af_sel << MFPR_ALT_OFFSET) |       /
     147                 (pin_config->edge_rise_en << MFPR_ERE_OFFSET ) |/
     148                 (pin_config->edge_fall_en << MFPR_EFE_OFFSET ) |/
     149                 (pin_config->edge_clear << MFPR_EC_OFFSET ) |   /
     150                 (pin_config->sleep_oe_n << MFPR_SON_OFFSET ) |  /
     151                 (pin_config->sleep_data << MFPR_SD_OFFSET ) |   /
     152                 (pin_config->sleep_sel << MFPR_SS_OFFSET ) |    /
     153                 (pin_config->drive << MFPR_DRV_OFFSET ) |       /
     154                 (pin_config->pulldown_en << MFPR_PD_OFFSET ) |  /
     155                 (pin_config->pullup_en << MFPR_PU_OFFSET ) |    /
     156                 (pin_config->pull_sel << MFPR_PS_OFFSET );
     (mfp.h)

     

    这个宏也很常用,它把mhn_pin_config结构转换成MFPR寄存器的格式,这样就可以直接写入MFPR寄存器了。

     

    44 int mhn_mfp_set_config(struct mhn_pin_config *pin_config)
     45 {
     46         unsigned long flags;
     47         mfp_pin_t mfp_pin;
     48         uint32_t  mfp_reg;
     49
     50         spin_lock_irqsave(&mfp_spin_lock, flags);
     51
     52         mfp_pin = pin_config->mfp_pin;
     53         mfp_reg = PIN2REG(pin_config);
     54
     55 #if defined(CONFIG_MONAHANS_GPIOEX)
     56         if (IS_GPIO_EXP_PIN(mfp_pin)){
     57                 spin_unlock_irqrestore(&mfp_spin_lock, flags);
     58                 return 0;
     59         }
     60 #endif
     61
     62 #ifdef CONFIG_MFP_DEBUG
     63         if ((pin_config == NULL) ||
     64             (MFP_OFFSET(mfp_pin) > MHN_MAX_MFP_OFFSET) ||
     65             (MFP_OFFSET(mfp_pin) < MHN_MIN_MFP_OFFSET)) {
     66                 spin_unlock_irqrestore(&mfp_spin_lock, flags);
     67                 return -1;
     68         }
     69 #endif
     70
     71         MFP_REG(mfp_pin) = mfp_reg;
     72         mfp_reg = MFP_REG(mfp_pin);     /* read back */
     73
     74         spin_unlock_irqrestore(&mfp_spin_lock, flags);
     75         
     76         return 0;
     77 }

     

    该函数让配置生效,它先用PIN2REG把配置转换成寄存器的格式,然后用MFP_REG得到Pin对应的寄存器,并把值写入寄存器,最后为了确保写入操作完成,再把寄存器的值读回来。通过回读的方式确保写操作完成,在多级流水线的CPU中是常用的手法。

     

    120 int mhn_mfp_set_afds(mfp_pin_t pin, int af, int ds)
    121 {
    122         unsigned long flags;
    123         uint32_t mfp_reg;
    124
    125 #if defined(CONFIG_MONAHANS_GPIOEX)
    126         if (IS_GPIO_EXP_PIN(pin))
    127                 return 0;
    128 #endif
    129
    130 #ifdef CONFIG_MFP_DEBUG
    131         if ((MFP_OFFSET(pin) > MHN_MAX_MFP_OFFSET) ||
    132             (MFP_OFFSET(pin) < MHN_MIN_MFP_OFFSET))
    133                 return -1;
    134 #endif
    135
    136         spin_lock_irqsave(&mfp_spin_lock, flags);
    137
    138         mfp_reg = MFP_REG(pin);
    139         mfp_reg &= ~(MFP_AF_MASK | MFP_DRV_MASK);
    140         mfp_reg |= (((af & 0x7) << MFPR_ALT_OFFSET) |
    141                     ((ds & 0x7) << MFPR_DRV_OFFSET));
    142         MFP_REG(pin) = mfp_reg;
    143         mfp_reg = MFP_REG(pin);
    144
    145         spin_unlock_irqrestore(&mfp_spin_lock, flags);
    146
    147         return 0;
    148 }

     

    这里只要明白AF代表Alternate Function,而ds代表drive strength,就明白这个函数的功能了,它就是用来设置Pin的可选功能和驱动能力的。其实现与mhn_mfp_set_config类似,但它只修改部分设置,所以要先读取原来的设置,修改它并写回去,最后再读回来以确认设置完成。

     

    150 int mhn_mfp_set_rdh(mfp_pin_t pin, int rdh)
    151 {
    152         unsigned long flags;
    153         uint32_t mfp_reg;
    154
    155 #if defined(CONFIG_MONAHANS_GPIOEX)
    156         if (IS_GPIO_EXP_PIN(pin))
    157                 return 0;
    158 #endif
    159
    160 #ifdef CONFIG_MFP_DEBUG
    161         if ((MFP_OFFSET(pin) > MHN_MAX_MFP_OFFSET) ||
    162             (MFP_OFFSET(pin) < MHN_MIN_MFP_OFFSET))
    163                 return -1;
    164 #endif
    165         
    166         spin_lock_irqsave(&mfp_spin_lock, flags);
    167
    168         mfp_reg = MFP_REG(pin);
    169         mfp_reg &= ~MFP_RDH_MASK;
    170
    171         if (likely(rdh))
    172                 mfp_reg |= (1u << MFPR_SS_OFFSET);
    173
    174         MFP_REG(pin) = mfp_reg;
    175         mfp_reg = MFP_REG(pin);
    176
    177         spin_unlock_irqrestore(&mfp_spin_lock, flags);
    178
    179         return 0;
    180 }

     

    这里其实它并不是修改ASCR[RDH],而是修改SLEEP_SEL,因为SLEEP_SEL决定ASCR[RDH]的值是否有效,具体方法与前面类似。RDH意义暂时还不完全明白,以后再补充吧。

     

    182 int mhn_mfp_set_lpm(mfp_pin_t pin, int lpm)
    183 {
    184         unsigned long flags;
    185         uint32_t mfp_reg;
    186
    187 #if defined(CONFIG_MONAHANS_GPIOEX)
    188         if (IS_GPIO_EXP_PIN(pin))
    189                 return 0;
    190 #endif
    191
    192 #ifdef CONFIG_MFP_DEBUG
    193         if ((MFP_OFFSET(pin) > MHN_MAX_MFP_OFFSET) ||
    194             (MFP_OFFSET(pin) < MHN_MIN_MFP_OFFSET))
    195                 return -1;
    196 #endif
    197         spin_lock_irqsave(&mfp_spin_lock, flags);
    198
    199         mfp_reg = MFP_REG(pin);
    200         mfp_reg &= ~(MFP_LPM_MASK);
    201         
    202         if (lpm & 0x1) mfp_reg |= 1u << MFPR_SON_OFFSET;
    203         if (lpm & 0x2) mfp_reg |= 1u << MFPR_SD_OFFSET;
    204         if (lpm & 0x4) mfp_reg |= 1u << MFPR_PU_OFFSET;
    205         if (lpm & 0x8) mfp_reg |= 1u << MFPR_PD_OFFSET;
    206         if (lpm &0x10) mfp_reg |= 1u << MFPR_PS_OFFSET;
    207
    208         MFP_REG(pin) = mfp_reg;
    209         mfp_reg = MFP_REG(pin);
    210
    211         spin_unlock_irqrestore(&mfp_spin_lock, flags);
    212
    213         return 0;
    214 }

     

    这里的lpm代表low power mode,它用来设置低功耗模式下的配置,包括数据方向是输出还是输入,输出的数据和输入的默认值等。

     

    216 int mhn_mfp_set_edge(mfp_pin_t pin, int edge)
    217 {
    218         unsigned long flags;
    219         uint32_t mfp_reg;
    220
    221 #if defined(CONFIG_MONAHANS_GPIOEX)
    222         if (IS_GPIO_EXP_PIN(pin))
    223                 return 0;
    224 #endif
    225
    226 #ifdef CONFIG_MFP_DEBUG
    227         if ((MFP_OFFSET(pin) > MHN_MAX_MFP_OFFSET) ||
    228             (MFP_OFFSET(pin) < MHN_MIN_MFP_OFFSET))
    229                 return -1;
    230 #endif
    231         spin_lock_irqsave(&mfp_spin_lock, flags);
    232
    233         mfp_reg = MFP_REG(pin);
    234
    235         /* Clear bits - EDGE_CLEAR, EDGE_RISE_EN, EDGE_FALL_EN */
    236         mfp_reg &= ~(MFP_EDGE_MASK);
    237
    238         switch (edge) {
    239         case MFP_EDGE_RISE:
    240                 mfp_reg |= (1u << MFPR_ERE_OFFSET);
    241                 break;
    242         case MFP_EDGE_FALL:
    243                 mfp_reg |= (1u << MFPR_EFE_OFFSET);
    244                 break;
    245         case MFP_EDGE_BOTH:
    246                 mfp_reg |= (3u << MFPR_ERE_OFFSET);
    247                 break;
    248         case MFP_EDGE_NONE:
    249                 mfp_reg |= (1u << MFPR_EC_OFFSET);
    250                 break;
    251         default:
    252                 spin_unlock_irqrestore(&mfp_spin_lock, flags);
    253                 return -EINVAL;
    254         }
    255
    256         MFP_REG(pin) = mfp_reg;
    257         mfp_reg = MFP_REG(pin);
    258
    259         spin_unlock_irqrestore(&mfp_spin_lock, flags);
    260
    261         return 0;
    262 }

     

    设置电平切换检查,可以在上升沿检查,可以在下降沿检查,可以两者都检查,或者都不检查,后者设置EDGE_CLEAR位。

     

    270 int mhn_mfp_set_pull(mfp_pin_t pin, int pull)
    271 {
    272         unsigned long flags;
    273         uint32_t mfp_reg;
    274
    275 #if defined(CONFIG_MONAHANS_GPIOEX)
    276         if (IS_GPIO_EXP_PIN(pin))
    277                 return 0;
    278 #endif
    279
    280 #ifdef CONFIG_MFP_DEBUG
    281         if ((MFP_OFFSET(pin) > MHN_MAX_MFP_OFFSET) ||
    282             (MFP_OFFSET(pin) < MHN_MIN_MFP_OFFSET))
    283                 return -1;
    284 #endif
    285         spin_lock_irqsave(&mfp_spin_lock, flags);
    286
    287         mfp_reg = MFP_REG(pin);
    288         mfp_reg &= ~MFP_PULL_MASK;
    289
    290         mfp_reg |= (pull & 0x7u) << MFPR_PD_OFFSET;
    291
    292         MFP_REG(pin) = mfp_reg;
    293         mfp_reg = MFP_REG(pin);
    294
    295         spin_unlock_irqrestore(&mfp_spin_lock, flags);
    296
    297         return 0;
    298 }

     

    启用/禁用内部上/下拉电阻,给Input Pin设置默认值。

     

    300 static struct mfp_regs context;
    301 void mhn_mfp_save(void)
    302 {
    303         int i, offset;
    304
    305         /* specify the membase */
    306         context.membase = (unsigned char *)KSEG0(PADBASE);
    307
    308         for (i = 0; i < MAX_MFP_PINS; i++) {
    309                 offset = i << 2;
    310                 context.mfp[i] = readl(context.membase + offset);
    311         }
    312 }
    313
    314 void mhn_mfp_restore(void)
    315 {
    316         int i, offset;
    317
    318         /* check the membase */
    319         if (context.membase == NULL)
    320                 return;
    321
    322         for (i = 0; i < MAX_MFP_PINS; i++) {
    323                 offset = i << 2;
    324                 writel(context.mfp[i], context.membase + offset);
    325         }
    326 }

     

    保存和恢复MFP的设置,供电源管理在系统SuspendResume时调用,因为寄存器是连接的,所以其实现很简单。

     

    ~~end~~

     
  • 相关阅读:
    ASCII码对照表
    有种美叫做放弃
    OCX和DLL的区别
    江湖经验:喝酒的学问技巧
    TC2.0实现多文件编译
    希尔排序
    CalcOpticalFlowPyrLK的使用(转)
    简单选择排序的实现
    VS2008中解决方案窗口的问题
    用友T3用友通行政单位没有损益类科目,如何做期间损益结转?
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167708.html
Copyright © 2020-2023  润新知