在现在的Android手机中,物理按键虽然逐渐被取代,但是对于电源键这种命脉型的功能键不可能会取消的,但是对于各家单板,硬件的IO连接都是不相同的,所以需要定制电源键的功能。
snvs_pwrkey: snvs-powerkey { compatible = "fsl,sec-v4.0-pwrkey";
regmap = <&snvs>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
linux,keycode = <KEY_POWER>;
wakeup-source;
};
上述dts中,使用linux,keycode 来表述按键码,使用GIC_SPI 来作为按键输入中断方式,并且高电平触发。
代码路径:drivers/input/keyboard/snvs_pwrkey.c
/设备树匹配dts/
static const struct of_device_id imx_snvs_pwrkey_ids[] = {
{ .compatible = "fsl,sec-v4.0-pwrkey" },
{ /* sentinel */ }
};
匹配成功后probe调用
static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
{
struct pwrkey_drv_data *pdata = NULL;
struct input_dev *input = NULL;
struct device_node *np;
int error;
struct device_node *sp;
struct platform_device * pd;
/* Get SNVS register Page */
np = pdev->dev.of_node;
if (!np)
return -ENODEV;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
//查找trust dts信息
sp = of_find_node_by_name(NULL, "trusty");
if (sp != NULL) {
pd = of_find_device_by_node(sp);
if (pd != NULL) {
pdata->trusty_dev = &(pd->dev);
dev_err(&pdev->dev, "snvs pwkey: get trusty_dev node, use Trusty mode.\n");
} else
dev_err(&pdev->dev, "snvs pwkey: failed to get trusty_dev node\n");
} else {
dev_err(&pdev->dev, "snvs pwkey: failed to find trusty node. Use normal mode.\n");
pdata->trusty_dev = NULL;
}
pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");
if (IS_ERR(pdata->snvs)) {
dev_err(&pdev->dev, "Can't get snvs syscon\n");
return PTR_ERR(pdata->snvs);
}
if (pdata->trusty_dev) {
error = trusty_fast_call32(pdata->trusty_dev, SMC_SNVS_PROBE, 0, 0, 0);
if (error < 0) {
dev_err(&pdev->dev, "snvs pwkey trusty dev failed to probe!nr=0x%x error=%d\n", SMC_SNVS_PROBE, error);
pdata->trusty_dev = NULL;
}
}
//读取dts中配置的按键码
if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
pdata->keycode = KEY_POWER;
dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
}
pdata->wakeup = of_property_read_bool(np, "wakeup-source");
//获取中断
pdata->irq = platform_get_irq(pdev, 0);
if (pdata->irq < 0) {
dev_err(&pdev->dev, "no irq defined in platform data\n");
return -EINVAL;
}
if (pdata->trusty_dev != NULL)
trusty_snvs_update_lpcr(pdata->trusty_dev, SNVS_LPCR_DEP_EN, 1);
else
regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
/* 清除中断标志位 */
if (pdata->trusty_dev != NULL)
trusty_snvs_write(pdata->trusty_dev, SNVS_LPSR_REG, SNVS_LPSR_SPO);
else
regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
/*设置定时器,用于识别长按与短按*/
setup_timer(&pdata->check_timer,
imx_imx_snvs_check_for_events, (unsigned long) pdata);
/*分配input子系统数据信息*/
input = devm_input_allocate_device(&pdev->dev);
if (!input) {
dev_err(&pdev->dev, "failed to allocate the input device\n");
return -ENOMEM;
}
/*设置input节点中的信息*/
input->name = pdev->name;
input->phys = "snvs-pwrkey/input0";
input->id.bustype = BUS_HOST;
/*将keycode附加到EV_KEY下*/
input_set_capability(input, EV_KEY, pdata->keycode);
/* 添加按键定时器释放函数*/
error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
if (error) {
dev_err(&pdev->dev, "failed to register remove action\n");
return error;
}
/*注册input系统*/
error = input_register_device(input);
if (error < 0) {
dev_err(&pdev->dev, "failed to register input device\n");
return error;
}
pdata->input = input;
platform_set_drvdata(pdev, pdata);
device_init_wakeup(&pdev->dev, pdata->wakeup);
/*申请irq资源,当power-key被按下时候,这个cb就会被调用*/
error = devm_request_irq(&pdev->dev, pdata->irq,
imx_snvs_pwrkey_interrupt,
0, pdev->name, pdev);
if (error) {
dev_err(&pdev->dev, "interrupt not available.\n");
input_unregister_device(input);
return error;
}
return 0;
}
/按键被按下后,中断函数被执行/
static void imx_imx_snvs_check_for_events(unsigned long data)
{
struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data;
struct input_dev *input = pdata->input;
u32 state;
if (pdata->trusty_dev)
state = trusty_snvs_read(pdata->trusty_dev, SNVS_HPSR_REG);
else
regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
state = state & SNVS_HPSR_BTN ? 1 : 0;
/* only report new event if status changed */
if (state ^ pdata->keystate) {
pdata->keystate = state;
input_event(input, EV_KEY, pdata->keycode, state);
input_sync(input);
pm_relax(pdata->input->dev.parent);
}
/*启动定时器,用于识别短按与长按*/
if (state) {
mod_timer(&pdata->check_timer,
jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
}
}
/定时器回调函数,这个回调函数会上报按键信息/
static void imx_imx_snvs_check_for_events(unsigned long data)
{
struct pwrkey_drv_data *pdata = (struct pwrkey_drv_data *) data;
struct input_dev *input = pdata->input;
u32 state;
if (pdata->trusty_dev)
state = trusty_snvs_read(pdata->trusty_dev, SNVS_HPSR_REG);
else
regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
state = state & SNVS_HPSR_BTN ? 1 : 0;
/* only report new event if status changed */
if (state ^ pdata->keystate) {
pdata->keystate = state;
input_event(input, EV_KEY, pdata->keycode, state);//上报power-key事件
input_sync(input);
pm_relax(pdata->input->dev.parent);
}
/* repeat check if pressed long */
if (state) {
mod_timer(&pdata->check_timer,
jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
}
}
总结:配置dts支持pwrkey即可完成自定义IO来开机动作
下一篇:Android会将power-key值进行转换为相应的运用层统一的功能码