Analysis of LED Driver of Marvell (Marvell PXA310 LED驱动代码分析)

1.               Abstract

抱歉这篇报告是我当初用英文写的,有兴趣的朋友可以参考。

2.               Introduction

.

3.               Driver Codes

Codes exist in drivers/misc/misc_control.c.

3.1        Data Structure

The device structure in

3.2        Initialize Codes

3.2.1          Probe Function

Codes exist in drivers/misc/misc_control.c.

static int mctl_probe(struct platform_device *pdev)

{

       struct resource *res;

       int ret;

       mctl_hardware_init();

       res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //This line is no sense since we have not registered any IO resources.

       ret = misc_register(&misc_control_dev);

       ……

       return 0;

}

 

static int mctl_hardware_init(void)

{

       pxa3xx_mfp_set_configs(misc_ctl_pins, ARRAY_SIZE(misc_ctl_pins)); //Please refer to section 3.4.1 and 3.4.2

       pxa3xx_gpio_set_direction(WIFI_LED_GPIO,GPIO_DIR_OUT);

       pxa3xx_gpio_set_level(WIFI_LED_GPIO,0);

       pxa3xx_gpio_set_direction(BT_LED_GPIO,GPIO_DIR_OUT);

       pxa3xx_gpio_set_level(BT_LED_GPIO,0);

       pxa3xx_gpio_set_direction(POWER_RED_LED_GPIO,GPIO_DIR_OUT);

       pxa3xx_gpio_set_level(POWER_RED_LED_GPIO,0);

       pxa3xx_gpio_set_direction(POWER_GREEN_LED_GPIO,GPIO_DIR_OUT);

       pxa3xx_gpio_set_level(POWER_GREEN_LED_GPIO,0);

       pxa3xx_gpio_set_direction(POWER_BLUE_LED_GPIO,GPIO_DIR_OUT);

       pxa3xx_gpio_set_level(POWER_BLUE_LED_GPIO,0);

       // Do reset for bluetooth chip

       mctl_bt_do_reset(); //It seemed that GPIO BT_RESET_GPIO is used to reset BT module

       return 0;

}

3.3        Driver Operation Codes

The following data structure is initialized in misc_register.

static const struct file_operations mctl_fops = {

      owner:THIS_MODULE,

      open:mctl_open,

      release:mctl_release,

      read:mctl_read,

      write:mctl_write,

      fasync:NULL,

      poll:NULL,

      ioctl:mctl_ioctl,

      llseek:no_llseek,

};

All functions except mctl_ioctl are empty.

static int mctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd,

         unsigned long arg)

{

       int ret;

       void __user *argp = (void __user *)arg;

       struct misc_ctl_struct m_ctl;

       switch (cmd) {

       case MCTL_GET_CTL:

              if (copy_from_user(&m_ctl, argp, sizeof(m_ctl)))

                     return -EFAULT;

              ret = do_mctl_get(&m_ctl); //Fetch the current value, all of them are the current settings  of GPIO

              if (ret)

                     return ret;

              return copy_to_user(argp, &m_ctl, sizeof(m_ctl));

              break;

       case MCTL_SET_CTL:

              if (copy_from_user(&m_ctl, argp, sizeof(m_ctl)))

                     return -EFAULT;

              ret = do_mctl_set(&m_ctl); //Set the value of the LED, which will be explained later.

              if (ret)

                     return ret;

              return 0;

              break;

       default:

              return -EINVAL;

       }

       return 0;

}

 

static int do_mctl_set(struct misc_ctl_struct *m_ctl)

{

       switch(m_ctl->ctrl){

              case CTRL_POWER_RED_LED:

                     pxa3xx_gpio_set_level(POWER_RED_LED_GPIO, !!m_ctl->val);

                     break;

              case CTRL_POWER_GREEN_LED:

                     mctl_set_power_led_mode(m_ctl->val); //We will explain it later.

                     break;

              case CTRL_POWER_BLUE_LED:

                     pxa3xx_gpio_set_level(POWER_BLUE_LED_GPIO, !!m_ctl->val);

                     break;

              case CTRL_WIFI_LED:

                     pxa3xx_gpio_set_level(WIFI_LED_GPIO, !!m_ctl->val);

                     break;

              case CTRL_BT_LED:

                     pxa3xx_gpio_set_level(BT_LED_GPIO, !!m_ctl->val);

                     break;

              case CTRL_BT_RESET:

                     if ( m_ctl->val != 0)

                            mctl_bt_do_reset();

                     break;

              ……

       }

       return 0;

}

 

int mctl_set_power_led_mode(int setting)

{

       power_led_flip_mode = (setting & POWER_LED_MODE_MASK); //Get the flip mode

       //From now we compute the flip interval, or set it to 0 if no flip need.

       if  (power_led_flip_mode & ( POWER_LED_MODE_NORMAL | POWER_LED_MODE_SLEEP ) ) {

              power_led_flip_interval = ( setting & POWER_LED_VALUE_MASK );

              if (!power_led_flip_interval)

                     power_led_flip_interval = 2;   // set default filping interval to 2 seconds.

       }else{

              power_led_flip_interval = 0;

       }

       if  (power_led_flip_mode & POWER_LED_MODE_NORMAL) { //This is very strange, since we should also enable flip mode in POWER_LED_MODE_SLEEP

              mctl_enable_power_led_flip_hardware(); //Please refer to section 3.5

              return 0;

       }

       mctl_disable_power_led_flip_hardware(); //Disable flip now.

       // as long as GPIO mode is set and normal mode is not set. we control led with 0/1 through GPIO in full power state

       if  (power_led_flip_mode & POWER_LED_MODE_GPIO ) {

              pxa3xx_gpio_set_level(POWER_GREEN_LED_GPIO, !!( setting & POWER_LED_VALUE_MASK ));

              return 0;

       }

 

       // if only flip during sleep mode is set, we disable led in full power state.

       if  (power_led_flip_mode & POWER_LED_MODE_SLEEP) { //Close the LED during Sleep mode.

              pxa3xx_gpio_set_level(POWER_GREEN_LED_GPIO,0);

              return 0;

       }

       return -EINVAL;

}

3.4        The Pin Descriptions

3.4.1          Function pxa3xx_mfp_set_config

First of all, let us see the data structure of pxa3xx_pin_config:

struct pxa3xx_pin_config {

       unsigned int mfp_pin; //The Pin register

       unsigned int reserved:16;

       unsigned int af_sel:3; //select the function of this pin

       unsigned int edge_rise_en:1; //enable rise edge detection.

       unsigned int edge_fall_en:1; //enable fall edge detection.

       unsigned int edge_clear:1; //enable edge detection.

       unsigned int sleep_oe_n:1; //determine the direction of this pin in low power mode.

       unsigned int sleep_data:1; //the output data in low power mode.

       unsigned int sleep_sel:1; //enable low power mode output.

       unsigned int drive:3; //Drive strength and slew rate

       unsigned int pulldown_en:1; //enable pull down function

       unsigned int pullup_en:1; //enable pull up function

       unsigned int pull_sel:1; //enable pull function

};

Please refer to document [1] P129 Section 4.11 for concrete meaning of these fields.

#define PIN2REG(pin_config)                                  /

              (pin_config->af_sel << MFPR_ALT_OFFSET) |     /

                (pin_config->edge_rise_en << MFPR_ERE_OFFSET ) |/

                (pin_config->edge_fall_en << MFPR_EFE_OFFSET ) |/

                (pin_config->edge_clear << MFPR_EC_OFFSET ) |   /

                (pin_config->sleep_oe_n << MFPR_SON_OFFSET ) |      /

                (pin_config->sleep_data << MFPR_SD_OFFSET ) |   /

                (pin_config->sleep_sel << MFPR_SS_OFFSET ) |       /

                (pin_config->drive << MFPR_DRV_OFFSET ) | /

                (pin_config->pulldown_en << MFPR_PD_OFFSET ) |      /

                (pin_config->pullup_en << MFPR_PU_OFFSET ) |    /

                (pin_config->pull_sel << MFPR_PS_OFFSET );

And now you should understand the meaning of function pxa3xx_mfp_set_config .

3.4.2          Data Structure misc_ctl_pins

struct pxa3xx_pin_config misc_ctl_pins[] = {

PXA3xx_MFP_CFG("Wifi_LED",             WIFI_LED_GPIO, MFP_AF0, MFP_DS01X, 0, MFP_LPM_PULL_LOW, MFP_EDGE_NONE),

PXA3xx_MFP_CFG("Bluetooth _LED",    BT_LED_GPIO,      MFP_AF0, MFP_DS01X, 0, MFP_LPM_PULL_LOW, MFP_EDGE_NONE),

PXA3xx_MFP_CFG("POWER_RED_LED",   POWER_RED_LED_GPIO,        MFP_AF0, MFP_DS01X, 0, MFP_LPM_PULL_LOW, MFP_EDGE_NONE),

PXA3xx_MFP_CFG("POWER_GREEN_LED",     POWER_GREEN_LED_GPIO,         MFP_AF0, MFP_DS01X, 0, MFP_LPM_PULL_LOW, MFP_EDGE_NONE),

PXA3xx_MFP_CFG("POWER_BLUE_LED", POWER_BLUE_LED_GPIO,            MFP_AF0, MFP_DS01X, 0, MFP_LPM_PULL_LOW, MFP_EDGE_NONE),

PXA3xx_MFP_CFG("BT_RESET",           BT_RESET_GPIO, MFP_AF0, MFP_DS01X, 0, MFP_LPM_PULL_HIGH, MFP_EDGE_NONE),

};

 

#define PXA3xx_MFP_CFG(desc, pin, af, drv, rdh, lpm, edge) /

{                                               /

       .mfp_pin = pin,                               /

       .af_sel    = af,                                 /

       .reserved       = 0,                      /

       .drive            = drv,                         /

       .sleep_sel      = rdh,                         /

       .sleep_oe_n  = ((lpm) & 0x1),         /

       .sleep_data   = (((lpm) & 0x2)  >>1),          /

       .pullup_en    = (((lpm) & 0x4)  >>2),          /

       .pulldown_en      = (((lpm) & 0x8)  >>3),          /

       .pull_sel = (((lpm) & 0x10) >>4),           /

       .edge_clear   = (!(edge)),                 /

       .edge_fall_en      = ((edge) & 0x1),              /

       .edge_rise_en      = (((edge) & 0x2) >>1),           /

}

3.4.3          Function pxa3xx_mfp_set_afds

static void mctl_disable_power_led_flip_hardware(void)

{

       OMCR10 = 0;

       // set pin back to GPIO

       pxa3xx_mfp_set_afds(POWER_GREEN_LED_GPIO, MFP_AF0, MFP_DS01X); //Please refer to following, When it is not used, reset the multi-function PIN of GPIO19 to 0, which is the primary function of it: GPIO_19. See the explanation in Section 3.5

}

 

int pxa3xx_mfp_set_afds(unsigned int pin, int af, int ds)

{

       //ds is driver strength, in our case it is always 0. Please refer to document [1] P130 for details.

       uint32_t mfp_reg;

       unsigned long flags;

#if defined(CONFIG_PXA3xx_GPIOEX)

       if (IS_GPIO_EXP_PIN(pin))

              return 0;

#endif

       spin_lock_irqsave(&mfp_spin_lock, flags);

       mfp_reg = MFP_REG(pin); //Get the PIN Address, which is started from 0x40E1_0000

       mfp_reg &= ~(MFP_AF_MASK | MFP_DRV_MASK); //Clear AF and DRV fields

       mfp_reg |= (((af & 0x7) << MFPR_ALT_OFFSET) |

                  ((ds & 0x7) << MFPR_DRV_OFFSET)); //Then set AF and DRV fields, AF is the function fields, and DRV is the driver strength fields

       MFP_REG(pin) = mfp_reg;

       mfp_reg = MFP_REG(pin);

       spin_unlock_irqrestore(&mfp_spin_lock, flags);

       return 0;

}

3.5        The Operating System Timers

3.5.1          Function mctl_enable_power_led_flip_hardware

Please refer to document [1] P418 Section 11.4 for details.

static void mctl_enable_power_led_flip_hardware(void)

{

       if ( power_led_flip_interval == 0 )

              return;

       // set pin to ostimer 10 chout 0

       pxa3xx_mfp_set_afds(POWER_GREEN_LED_GPIO, MFP_AF6, MFP_DS01X); //Please refer to section 3.4.3

//#define POWER_GREEN_LED_GPIO       (MFP_PIN_GPIO19)

//#define       MFP_PIN_GPIO19        ((0x02BC << 16) | (19))

//From document [1] Section 4.7.3, you may found that the PIN is GPIO19; and then in document [1] Section 4.4 (P76)  you may found the function multiplexer of this PIN, in our case it should be CHOUT0, which is 6.

       OMCR10 = 0; //Reset channel 10

       OSMR10 = power_led_flip_interval; //Set the time match register of channel 10.

       OSCR10 = 0;            //reset counter value;

       OMCR10 = ( OMCR_R | OMCR_P | OMCR_C | OMCR_CRES_SEC ); // OMCR_R means reset the timer on match, OMCR_P means it is a periodic timer, OMCR_C means channel to match against is OSCRx, OMCR_CRES_SEC is 3 (0X11), and therefore means the counter resolution is 1 second.

       OSCR10 = 0;            //set again to trigger the counting.

}

3.5.2          How to Find the Meaning of Multi-Function Pin

From the above section, we can understand how to find the meaning of MFP.

First, from the codes, you can get the address of this PIN;

Then go into document [1] Section 4.7.3, find the name of this PIN;

Then go into document [1] Section 4.4, find the multiplexed function of this named PIN;

Then go into document [1] Section 4.11, to check out how to set the register.

4.               Future Works

I do not know if following works are necessary:

5.               Questions

6.               References

[1]. <!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 680460288 22 0 262145 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 680460288 22 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; line-height:12.0pt; mso-pagination:none; text-autospace:none; font-size:10.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋体; mso-fareast-language:EN-US;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.Section1 {page:Section1;} --> PXA3xx_Processor_Family_Developer_Manual_Vol._I_Version_1.0_Rev._C.pdf

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值