高通平台一般会把音量下键绑在PMIC RESIN上,这样除了长按电源键重启外,还可以按音量下键重启。

先从内核文档(Kernel3.18)了解下RESIN:

Qualcomm QPNP power-on

The qpnp-power-on is a driver which supports the power-on(PON)
peripheral on Qualcomm PMICs. The supported functionality includes
power on/off reason, key press/release detection, PMIC reset configurations
and other PON specifc features. The PON module supports multiple physical
power-on (KPDPWR_N, CBLPWR) and reset (KPDPWR_N, RESIN, KPDPWR+RESIN) sources.

dts配置:

pm8909_pon: qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
interrupts = <0x0 0x8 0x0>,
<0x0 0x8 0x1>;
interrupt-names = "kpdpwr", "resin";
qcom,pon-dbc-delay = <15625>;
qcom,system-reset;
qcom,clear-warm-reset;
qcom,store-hard-reset-reason;

qcom,pon_1 {
qcom,pon-type = <0>;
qcom,support-reset = <1>;
qcom,pull-up = <1>;
qcom,s1-timer = <10256>;
qcom,s2-timer = <2000>;
qcom,s2-type = <1>;
linux,code = <116>;
};

qcom,pon_2 {
qcom,pon-type = <1>;
qcom,pull-up = <1>;
linux,code = <114>;
};
};

具体node/prop含义都在下面这个文件里:

devicetree/bindings/platform/msm/qpnp-power-on.txt

pon-type就是复位源,linux,code就是对应的按键,116是电源键,114是音量下键,具体上报是在中断里派遣:

static int
qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
{
int rc;
struct qpnp_pon_config *cfg = NULL;
u8 pon_rt_sts = 0, pon_rt_bit = 0;
u32 key_status;
u64 elapsed_us;
...
pr_debug("PMIC input: code=%d, sts=0x%hhx\n",
cfg->key_code, pon_rt_sts);
key_status = pon_rt_sts & pon_rt_bit;
...
static irqreturn_t qpnp_kpdpwr_irq(int irq, void *_pon)
{
int rc;
struct qpnp_pon *pon = _pon;

rc = qpnp_pon_input_dispatch(pon, PON_KPDPWR);

注意看文档描述,还支持bark中断,这样可以添加一些自定义的处理了。

那音量下键没有接到PMIC RESIN,而是接到了GPIO上,内核是怎么获取按键状态的呢。

看下gpio的配置:

gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
pinctrl-0 = <&gpio_key_active>;
pinctrl-1 = <&gpio_key_suspend>;

vol_up {
label = "volume_up";
gpios = <&msm_gpio 90 0x1>;
linux,input-type = <1>;
linux,code = <115>;
gpio-key,wakeup;
debounce-interval = <15>;
};

vol_down {
label = "volume_down";
gpios = <&msm_gpio 91 0x1>;
linux,input-type = <1>;
linux,code = <114>;
gpio-key,wakeup;
debounce-interval = <15>;
};

驱动解析这个设备树配置的是drivers/input/keyboard/gpio_keys.c,具体是在gpio_keys_get_devtree_pdata里:

#ifdef CONFIG_OF
/*
* Translate OpenFirmware node properties into platform_data
*/
static struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
...
for_each_child_of_node(node, pp) {
int gpio;
enum of_gpio_flags flags;

if (!of_find_property(pp, "gpios", NULL)) {
pdata->nbuttons--;
dev_warn(dev, "Found button without gpios\n");
continue;
}

gpio = of_get_gpio_flags(pp, 0, &flags);
if (gpio < 0) {
error = gpio;
if (error != -EPROBE_DEFER)
dev_err(dev,
"Failed to get gpio flags, error: %d\n",
error);
return ERR_PTR(error);
}

button = &pdata->buttons[i++];

button->gpio = gpio;
button->active_low = flags & OF_GPIO_ACTIVE_LOW;

if (of_property_read_u32(pp, "linux,code", &button->code)) {
dev_err(dev, "Button without keycode: 0x%x\n",
button->gpio);
return ERR_PTR(-EINVAL);
}

button->desc = of_get_property(pp, "label", NULL);

if (of_property_read_u32(pp, "linux,input-type", &button->type))
button->type = EV_KEY;

button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);

if (of_property_read_u32(pp, "debounce-interval",
&button->debounce_interval))
button->debounce_interval = 5;
}
...

ok. 状态上报是在:

static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
int state;

state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; //here

if (type == EV_ABS) {
if (state)
input_event(input, type, button->code, button->value);
} else {
input_event(input, type, button->code, !!state);
}
input_sync(input);
}

什么时候上报的了?中断isr处理:

static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;

BUG_ON(irq != bdata->irq);

if (bdata->button->wakeup)
pm_stay_awake(bdata->input->dev.parent);
if (bdata->timer_debounce)
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(bdata->timer_debounce));
else
schedule_work(&bdata->work);

return IRQ_HANDLED;
}
static int gpio_keys_setup_key(struct platform_device *pdev,
struct input_dev *input,
struct gpio_button_data *bdata,
const struct gpio_keys_button *button)
{
...
if (gpio_is_valid(button->gpio)) {
INIT_WORK(&bdata->work, gpio_keys_gpio_work_func);
setup_timer(&bdata->timer,
gpio_keys_gpio_timer, (unsigned long)bdata);

isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
}

那内核其他模块如何获取按键状态? 我想可以参考gpio_keys_gpio_report_event把state取出应该就可以了。