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