Android9电源键驱动分析

一、概述

在现在的Android手机中,物理按键虽然逐渐被取代,但是对于电源键这种命脉型的功能键不可能会取消的,但是对于各家单板,硬件的IO连接都是不相同的,所以需要定制电源键的功能。

二、power-key驱动分析

2.1、dts配置power-key

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 来作为按键输入中断方式,并且高电平触发。

2.2、使用input子系统上报

代码路径: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值进行转换为相应的运用层统一的功能码

你可能感兴趣的:(Android)