Android power_supply驱动开发详解

引言

目前,各个OEM其实为了降低成本,将会用各自的渠道去购买更加物美价廉的外设。
这样,其实促进了各个厂商的竞争,而开发更加简单适配的驱动程序将会成为厂商必选项。
那么,开发一个驱动,将会有哪些点需要关注,i2c读写,interrupt实现等内容将会是本文的关注点。
这里以max8925为例,来进行一个分析。

驱动注册

在每个驱动想要被系统识别,肯定是需要进行一个声明。

module_platform_driver(max8925_power_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Power supply driver for MAX8925");
MODULE_ALIAS("platform:max8925-power");

在上面的例子中,我们看到使用了module_platform_driver函数来进行注册。

/* module_platform_driver() - Helper macro for drivers that don't do
 * anything special in module init/exit.  This eliminates a lot of
 * boilerplate.  Each module may only use this macro once, and
 * calling it replaces module_init() and module_exit()
 */
#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)

这里其实只是定义了一个宏,真正的使用是在module_driver中实现。

/**
 * module_driver() - Helper macro for drivers that don't do anything
 * special in module init/exit. This eliminates a lot of boilerplate.
 * Each module may only use this macro once, and calling it replaces
 * module_init() and module_exit().
 *
 * @__driver: driver name
 * @__register: register function for this driver type
 * @__unregister: unregister function for this driver type
 * @...: Additional arguments to be passed to __register and __unregister.
 *
 * Use this macro to construct bus specific macros for registering
 * drivers, and do not use it on its own.
 */
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

由上面的定义可知,module_platform_driver宏的作用就是定义指定名称的平台设备驱动注册函数和平台设备驱动注销函数。
并且在函数体内分别通过 __register函数和 __unregister() 注册和注销该平台设备驱动。
在本例中,注册的就是 max8925_power_driver了。


driver结构体实现

在本例中,max8925_power_driver 的结构体实现如下:

static struct platform_driver max8925_power_driver = {
	.probe	= max8925_power_probe,
	.remove	= max8925_power_remove,
	.driver	= {
		.name	= "max8925-power",
	},
};
变量成员变量作用
probemax8925_power_probeprobe函数在设备驱动注册最后收尾工作,当设备的device 和其对应的driver 在总线上完成配对之后,系统就调用 platform设备的probe函数完成驱动注册最后工作
removemax8925_power_remove(1)卸载驱动的时候,remove调用 (2)设备移除的时候,与设备关联的驱动需要移除,remove调用
.namemax8925-power驱动的名字为max8925-power

那么我们可以知道,当注册匹配时,就可以进入probe函数进行注册的完成。

probe函数的实现

该函数一般实现较长,我们先来看看代码片段。

static int max8925_power_probe(struct platform_device *pdev)
{
	struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
	struct power_supply_config psy_cfg = {}; /* Only for ac and usb */
	struct max8925_power_pdata *pdata = NULL;
	struct max8925_power_info *info;
	int ret;

	pdata = max8925_power_dt_init(pdev);
	if (!pdata) {
		dev_err(&pdev->dev, "platform data isn't assigned to "
			"power supply\n");
		return -EINVAL;
	}

	info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
				GFP_KERNEL);
	if (!info)
		return -ENOMEM;
	info->chip = chip;
	info->gpm = chip->i2c;
	info->adc = chip->adc;
	platform_set_drvdata(pdev, info);

	psy_cfg.supplied_to = pdata->supplied_to;
	psy_cfg.num_supplicants = pdata->num_supplicants;

	info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
	if (IS_ERR(info->ac)) {
		ret = PTR_ERR(info->ac);
		goto out;
	}
	info->ac->dev.parent = &pdev->dev;

	info->usb = power_supply_register(&pdev->dev, &usb_desc, &psy_cfg);
	if (IS_ERR(info->usb)) {
		ret = PTR_ERR(info->usb);
		goto out_unregister_ac;
	}
	info->usb->dev.parent = &pdev->dev;

	info->battery = power_supply_register(&pdev->dev, &battery_desc, NULL);
	if (IS_ERR(info->battery)) {
		ret = PTR_ERR(info->battery);
		goto out_unregister_usb;
	}
	info->battery->dev.parent = &pdev->dev;

	info->batt_detect = pdata->batt_detect;
	info->topoff_threshold = pdata->topoff_threshold;
	info->fast_charge = pdata->fast_charge;
	info->set_charger = pdata->set_charger;
	info->no_temp_support = pdata->no_temp_support;
	info->no_insert_detect = pdata->no_insert_detect;

	max8925_init_charger(chip, info);
	return 0;
out_unregister_usb:
	power_supply_unregister(info->usb);
out_unregister_ac:
	power_supply_unregister(info->ac);
out:
	return ret;
}

因为这段是完成注册,所以我们来分析几个除了初始化以外其中的关键点。

max8925_power_dt_init

该函数其实是去传了一个platform_devices,这个的作用是什么呢?

  • 在内核初始化时通过device_node转换为platform_device,这种是最新的实现方式,基于设备树,在内核初始化时将设备树中的节点转化为platform_device;
  • 使用platform_device_register注册platform_device;
static struct max8925_power_pdata *
max8925_power_dt_init(struct platform_device *pdev)
{
	struct device_node *nproot = pdev->dev.parent->of_node;
	struct device_node *np;
	int batt_detect;
	int topoff_threshold;
	int fast_charge;
	int no_temp_support;
	int no_insert_detect;
	struct max8925_power_pdata *pdata;

	if (!nproot)
		return pdev->dev.platform_data;

	np = of_get_child_by_name(nproot, "charger");
	if (!np) {
		dev_err(&pdev->dev, "failed to find charger node\n");
		return NULL;
	}

	pdata = devm_kzalloc(&pdev->dev,
			sizeof(struct max8925_power_pdata),
			GFP_KERNEL);
	if (!pdata)
		goto ret;

	of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
	of_property_read_u32(np, "batt-detect", &batt_detect);
	of_property_read_u32(np, "fast-charge", &fast_charge);
	of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
	of_property_read_u32(np, "no-temp-support", &no_temp_support);

	pdata->batt_detect = batt_detect;
	pdata->fast_charge = fast_charge;
	pdata->topoff_threshold = topoff_threshold;
	pdata->no_insert_detect = no_insert_detect;
	pdata->no_temp_support = no_temp_support;

ret:
	of_node_put(np);
	return pdata;
}

这里的两个关键点:

  1. 在没有使用dts的kernel驱动中,会使用 pdev->dev.platform_data 来进行注册
  2. 在使用dts的的kernel驱动中,会去调用devm_kzalloc来获取注册。

代码实现分别为:

	if (!nproot)
		return pdev->dev.platform_data;
	pdata = devm_kzalloc(&pdev->dev,
			sizeof(struct max8925_power_pdata),
			GFP_KERNEL);

这也是现在驱动越写越容易适配和调试的原因,会在实现时就考虑到多种情况。
注册后,如果成功,那么接下来就是platform_set_drvdata这个常用的函数了。
函数实现为:

static inline void platform_set_drvdata(struct platform_device *pdev,
					void *data)
{
	dev_set_drvdata(&pdev->dev, data);
}

继续看下这个封装的调用:

static inline void dev_set_drvdata(struct device *dev, void *data)
{
	dev->driver_data = data;
}

就是把data的值赋值给了dev->driver_data,传进来的device其实是pdev,而pdev是总线设备,所以这些数据对整个驱动设备都是可见的。

因为我们试一个power设备,所以power_supply_register是必须要实现的。
定义一个struct power_supply 变量,并初始化必要字段后,调用power_supply_registe将其注册到kernel中。

	info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);

然后就是一些具体功能的init。

max8925_init_charger(chip, info);

而在这个函数中,我们可以看到注册了很多的中端。

static int max8925_init_charger(struct max8925_chip *chip,
					  struct max8925_power_info *info)
{
	int ret;

	REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
	if (!info->no_insert_detect) {
		REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
		REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
	}
	if (!info->no_temp_support) {
		REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
		REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
	}
	REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
	REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
	REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
	REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done");
	REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
	REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");

	info->usb_online = 0;
	info->bat_online = 0;

	/* check for power - can miss interrupt at boot time */
	if (start_measure(info, MEASURE_VCHG) * 2000 > 500000)
		info->ac_online = 1;
	else
		info->ac_online = 0;

	ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
	if (ret >= 0) {
		/*
		 * If battery detection is enabled, ID pin of battery is
		 * connected to MBDET pin of MAX8925. It could be used to
		 * detect battery presence.
		 * Otherwise, we have to assume that battery is always on.
		 */
		if (info->batt_detect)
			info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1;
		else
			info->bat_online = 1;
		if (ret & MAX8925_CHG_AC_RANGE_MASK)
			info->ac_online = 1;
		else
			info->ac_online = 0;
	}
	/* disable charge */
	max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
	/* set charging current in charge topoff mode */
	max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5,
			 info->topoff_threshold << 5);
	/* set charing current in fast charge mode */
	max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge);

	return 0;
}

我们在这边就不一一分析,而是只关注中断申请函数,REQUEST_IRQ。

中断的申请

仍然是以本例中的代码,我们展开分析。
REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
REQUEST_IRQ是一个宏定义,实现如下:

#define REQUEST_IRQ(_irq, _name)					\
do {									\
	ret = request_threaded_irq(chip->irq_base + _irq, NULL,		\
				    max8925_charger_handler,		\
				    IRQF_ONESHOT, _name, info);		\
	if (ret)							\
		dev_err(chip->dev, "Failed to request IRQ #%d: %d\n",	\
			_irq, ret);					\
} while (0)

request_threaded_irq申请的中断,handler不在中断上下文里执行。
而是在新创建的线程里执行。
这样,该handler非常像执行workqueue。
即拥有所有workqueue的特性,但是省掉了创建,初始化,调度workqueue的繁多步骤。

我们可以看到,在这个例子里面,注册的handler为max8925_charger_handler。

中断的实现

上面注册了max8925_charger_handler,那么当出现中断的时候,就会调用对应的handler。

static irqreturn_t max8925_charger_handler(int irq, void *data)
{
	struct max8925_power_info *info = (struct max8925_power_info *)data;
	struct max8925_chip *chip = info->chip;

	switch (irq - chip->irq_base) {
	case MAX8925_IRQ_VCHG_DC_R:
		info->ac_online = 1;
		__set_charger(info, 1);
		dev_dbg(chip->dev, "Adapter inserted\n");
		break;
	case MAX8925_IRQ_VCHG_DC_F:
		info->ac_online = 0;
		__set_charger(info, 0);
		dev_dbg(chip->dev, "Adapter removed\n");
		break;
	case MAX8925_IRQ_VCHG_THM_OK_F:
		/* Battery is not ready yet */
		dev_dbg(chip->dev, "Battery temperature is out of range\n");
	case MAX8925_IRQ_VCHG_DC_OVP:
		dev_dbg(chip->dev, "Error detection\n");
		__set_charger(info, 0);
		break;
	case MAX8925_IRQ_VCHG_THM_OK_R:
		/* Battery is ready now */
		dev_dbg(chip->dev, "Battery temperature is in range\n");
		break;
	case MAX8925_IRQ_VCHG_SYSLOW_R:
		/* VSYS is low */
		dev_info(chip->dev, "Sys power is too low\n");
		break;
	case MAX8925_IRQ_VCHG_SYSLOW_F:
		dev_dbg(chip->dev, "Sys power is above low threshold\n");
		break;
	case MAX8925_IRQ_VCHG_DONE:
		__set_charger(info, 0);
		dev_dbg(chip->dev, "Charging is done\n");
		break;
	case MAX8925_IRQ_VCHG_TOPOFF:
		dev_dbg(chip->dev, "Charging in top-off mode\n");
		break;
	case MAX8925_IRQ_VCHG_TMR_FAULT:
		__set_charger(info, 0);
		dev_dbg(chip->dev, "Safe timer is expired\n");
		break;
	case MAX8925_IRQ_VCHG_RST:
		__set_charger(info, 0);
		dev_dbg(chip->dev, "Charger is reset\n");
		break;
	}
	return IRQ_HANDLED;
}

在这个函数中,我们可以看到会有不同的中断进行对应的处理。
刚才我们举例的REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
其实也会有对应的case来完成。

	case MAX8925_IRQ_VCHG_DC_OVP:
		dev_dbg(chip->dev, "Error detection\n");
		__set_charger(info, 0);
		break;

以上,就是一个驱动的简单实现流程和分析。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值