sprd_battery.c 是充电驱动,这个是充电功能的核心内容,电量显示策略、温度检测策略、充电保护机制等功能在这里实现,功能实现与硬件细节剥离,调用通用接口实现逻辑控制;

1 sprdbat_probe函数:


static int sprdbat_probe(struct platform_device *pdev)
{int ret = -ENODEV;enum usb_charger_state usb_online_state = USB_CHARGER_DEFAULT;struct power_supply *ret_ptr = NULL;struct sprdbat_drivier_data *data = NULL;struct device_node *np = pdev->dev.of_node;struct power_supply_desc *battery_desc = NULL,*ac_desc = NULL, *usb_desc = NULL;struct power_supply_config battery_cfg = {}, ac_cfg = {}, usb_cfg = {};if (!np) {dev_err(&pdev->dev, "device node not found\n");return -EINVAL;}if (sprd_ext_ic_op == NULL) {dev_err(&pdev->dev, "sprd_ext_ic_op not found\n");return -EINVAL;}data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);if (data == NULL) {ret = -ENOMEM;goto err_data_alloc_failed;}data->pdata = sprdbat_parse_dt(pdev);if (IS_ERR_OR_NULL(data->pdata))return -ENOMEM;data->dev = &pdev->dev;platform_set_drvdata(pdev, data);sprdbat_data = data;//通过ADC获取温度data->pdata->channel_temp = iio_channel_get(data->dev, "adc_temp");if (IS_ERR(data->pdata->channel_temp)) {ret = PTR_ERR(data->pdata->channel_temp);goto err_iio_get_temp;}//通过ADC获取vbat电压data->pdata->channel_vbat = iio_channel_get(data->dev, "adc_vbat");if (IS_ERR(data->pdata->channel_vbat)) {ret = PTR_ERR(data->pdata->channel_vbat);goto err_iio_get_vbat;}//通过ADC获取充电电压data->pdata->channel_vchg = iio_channel_get(data->dev, "adc_vchg");if (IS_ERR(data->pdata->channel_vchg)) {ret = PTR_ERR(data->pdata->channel_vchg);goto err_iio_get_vchg;}print_pdata(sprdbat_data->pdata);battery_desc = devm_kzalloc(&pdev->dev,sizeof(struct power_supply_desc), GFP_KERNEL);if (battery_desc == NULL) {ret = -ENOMEM;goto err_desc_alloc_failed;}//注册battery 的power_supply接口,这个为上层提供电量、充电状态等的接口battery_desc->properties = sprdbat_battery_props;battery_desc->num_properties = ARRAY_SIZE(sprdbat_battery_props);battery_desc->get_property = sprdbat_battery_get_property;battery_desc->set_property = sprdbat_battery_set_property;battery_desc->property_is_writeable =sprdbat_battery_property_is_writeable;battery_desc->name = "battery";battery_desc->type = POWER_SUPPLY_TYPE_BATTERY;battery_desc->no_thermal = true;battery_cfg.drv_data = sprdbat_data;ac_desc = devm_kzalloc(&pdev->dev,sizeof(struct power_supply_desc), GFP_KERNEL);if (ac_desc == NULL) {ret = -ENOMEM;goto err_desc_alloc_failed;}ac_desc->properties = sprdbat_ac_props;ac_desc->num_properties = ARRAY_SIZE(sprdbat_ac_props);ac_desc->get_property = sprdbat_ac_get_property;ac_desc->set_property = sprdbat_ac_set_property;ac_desc->property_is_writeable =sprdbat_ac_property_is_writeable;//注册AC的充电power_supply接口ac_desc->name = "ac";ac_desc->type = POWER_SUPPLY_TYPE_MAINS;ac_desc->no_thermal = true;ac_cfg.drv_data = sprdbat_data;//usb接口的接口usb_desc = devm_kzalloc(&pdev->dev,sizeof(struct power_supply_desc), GFP_KERNEL);if (usb_desc == NULL) {ret = -ENOMEM;goto err_desc_alloc_failed;}usb_desc->properties = sprdbat_usb_props;usb_desc->num_properties = ARRAY_SIZE(sprdbat_usb_props);usb_desc->get_property = sprdbat_usb_get_property;usb_desc->name = "usb";usb_desc->type = POWER_SUPPLY_TYPE_USB;usb_desc->no_thermal = true;usb_cfg.drv_data = sprdbat_data;data->start_charge = sprdbat_start_charge;data->stop_charge = sprdbat_stop_charge;ret_ptr = power_supply_register(&pdev->dev, battery_desc, &battery_cfg);if (IS_ERR(ret_ptr)) {goto err_battery_failed;} else {data->battery = ret_ptr;data->battery->supplied_to = battery_supply_list;data->battery->num_supplicants =ARRAY_SIZE(battery_supply_list);}ret_ptr = power_supply_register(&pdev->dev, ac_desc, &ac_cfg);if (IS_ERR(ret_ptr)) {goto err_ac_failed;} else {data->ac = ret_ptr;data->ac->supplied_to = supply_list;data->ac->num_supplicants = ARRAY_SIZE(supply_list);}ret_ptr = power_supply_register(&pdev->dev, usb_desc, &usb_cfg);if (IS_ERR(ret_ptr)) {goto err_usb_failed;} else {data->usb = ret_ptr;data->usb->supplied_to = supply_list;data->usb->num_supplicants = ARRAY_SIZE(supply_list);}/** TODO: switch polling to interrupt again need open this code.* data->chg_nb.notifier_call = sprdbat_chg_event_call;* ret = power_supply_reg_notifier(&data->chg_nb);** if (ret)*   dev_err(data->dev, "failed to reg notifier: %d\n", ret);*///注册文件节点ret = sysfs_create_group(&data->battery->dev.kobj,&sprd_bat_group);if (ret) {dev_err(&pdev->dev,"failed to create sprd_bat sysfs device attributes\n");goto err_sysfs_create_gr;}//vbat检测脚data->gpio_vbat_detect = data->pdata->gpio_vbat_detect;if (data->gpio_vbat_detect > 0) {devm_gpio_request(&pdev->dev,data->gpio_vbat_detect, "vbat_detect");gpio_direction_input(data->gpio_vbat_detect);data->irq_vbat_detect = gpio_to_irq(data->gpio_vbat_detect);irq_set_status_flags(data->irq_vbat_detect, IRQ_NOAUTOEN);ret =devm_request_threaded_irq(&pdev->dev,data->irq_vbat_detect, NULL,sprdbat_vbat_detect_irq,IRQ_TYPE_LEVEL_LOW | IRQF_NO_SUSPEND,"sprdbat_vbat_detect", data);if (ret)dev_err(&pdev->dev, "failed to use vbat gpio: %d\n",ret);}//otg 使能管脚data->gpio_otg_en = data->pdata->gpio_otg_en;if (data->gpio_otg_en > 0) {devm_gpio_request(&pdev->dev,data->gpio_otg_en, "otg_en");ret = gpio_direction_output(data->gpio_otg_en, 0);if (ret)dev_err(&pdev->dev, "failed to use otg_en gpio: %d\n",ret);}data->bat_info.bat_present = 1;mutex_init(&data->lock);//充电睡眠唤醒锁wake_lock_init(&(data->charger_wake_lock), WAKE_LOCK_SUSPEND,"charger_wake_lock");//初始化电池工作队列INIT_DELAYED_WORK(&data->battery_work, sprdbat_battery_works);//初始化电池睡眠队列INIT_DELAYED_WORK(&data->battery_sleep_work,sprdbat_battery_sleep_works);//初始化电池检测中断的队列INIT_WORK(&data->vbat_detect_irq_work, sprdbat_vbat_detect_irq_works);//电池拔插队列INIT_WORK(&data->plug_work, sprdbat_plug_works);//电池充电队列INIT_DELAYED_WORK(&sprdbat_data->sprdbat_charge_work,sprdbat_charge_works);data->monitor_wqueue = create_freezable_workqueue("sprdbat_monitor");if (data->monitor_wqueue == NULL)goto err_create_wq;//充电初始化sprdchg_init(data->pdata);//库仑计的初始化sprdfgu_init(data->pdata);if (sprdchg_timer_op->timer_request)sprdchg_timer_op->timer_request(sprdbat_timer_handler,data->pdata);elseSPRDBAT_DEBUG("warning !!charge timer ops = null\n");//充电led的控制
#ifdef CONFIG_LEDS_TRIGGERSdata->charging_led.name = "sprdbat_charging_led";data->charging_led.default_trigger = "battery-charging";data->charging_led.brightness_set = sprdchg_led_brightness_set;ret = led_classdev_register(&pdev->dev, &data->charging_led);if (ret)goto err_led_reg;
#endifsprd_ext_ic_op->ic_init(sprdbat_data);sprdbat_info_init(data);SPRDBAT_DEBUG("register_usb_notifier\n");//注册usb插拔的回调函数sprdbat_data->usb_charger =usb_charger_find_by_name("usb-charger.0");if (IS_ERR(sprdbat_data->usb_charger)) {ret = -EPROBE_DEFER;dev_err(&pdev->dev,"Failed to find USB gadget: %d\n", ret);goto err_usb_find_name;}//这个函数实质上回调了工作队列plug_work,也就是调用了sprdbat_plug_workssprdbat_data->chg_usb_nb.notifier_call = sprdbat_usb_plug_event;ret = usb_charger_register_notify(sprdbat_data->usb_charger,&sprdbat_data->chg_usb_nb);if (ret != 0) {dev_err(&pdev->dev,"Failed to register notifier: %d\n", ret);goto err_usb_reg_notify;}sprdbat_data->usb_charger->get_charger_type =sprdchg_charger_is_adapter_for_usb;//获取usb的状态usb_online_state = usb_charger_get_state(sprdbat_data->usb_charger);if (usb_online_state == USB_CHARGER_PRESENT)queue_work(sprdbat_data->monitor_wqueue,&sprdbat_data->plug_work);if (data->gpio_vbat_detect > 0)enable_irq(sprdbat_data->irq_vbat_detect);queue_delayed_work(system_power_efficient_wq,&data->battery_work, 15 * HZ);SPRDBAT_DEBUG("sprdbat_probe----------end\n");return 0;err_usb_reg_notify:
err_usb_find_name:
#ifdef CONFIG_LEDS_TRIGGERSled_classdev_unregister(&data->charging_led);
err_led_reg:
#endifdestroy_workqueue(data->monitor_wqueue);
err_create_wq:sysfs_remove_group(&data->battery->dev.kobj,&sprd_bat_group);
err_sysfs_create_gr:power_supply_unregister(data->usb);
err_usb_failed:power_supply_unregister(data->ac);
err_ac_failed:power_supply_unregister(data->battery);
err_battery_failed:iio_channel_release(data->pdata->channel_vchg);
err_desc_alloc_failed:sprdbat_data = NULL;
err_iio_get_vchg:iio_channel_release(data->pdata->channel_vbat);
err_iio_get_vbat:iio_channel_release(data->pdata->channel_temp);
err_iio_get_temp:
err_data_alloc_failed:sprdbat_data = NULL;return ret;}

1.1 解析设备树:

sprdbat_parse_dt函数如下:

battery-adapt-fun:电池兼容函数索引0 即为电池 ID识别
battery-adapt-support:电池兼容开关
charger-det-gpios:充电检测管脚
otg-en-gpios:otg使能管脚
chg-end-vol-check:检查充电是否满的电压(单位为mv)
chg-bat-safety-vol:充电时蓄电池的安全电压
rechg-vol:注意这里可以去掉复充电压
adp-cdp-cur:cdp充电电流(单位为ma)
adp-dcp-cur:dcp充电电流(单位为ma)
adp-sdp-cur :sdp充电电流(单位为ma)
adp-unknown-cur:未知充电电流
adp-fchg-cur:快充充电电流
adp-cdp-cur-limit:用于POWER_SUPPLY_PROP_CURRENT_MAX节点,为CDP类型的最大限制
adp-dcp-cur-limit:为DCP类型的最大限制
adp-unknown-cur-limit:为unknown类型的最大限制
adp-fchg-cur-limit:为快充类型的最大限制
ovp-stop:过电压保护:停止充电电压(mv)
ovp-restart:过电压保护:过压恢复电压
fchg-ovp-stop:快充过电压保护:停止充电电压(mv)
fchg-ovp-restart:快充过电压保护:重启充电电压(mv)
chg-timeout :充电超时,最终在函数sprdbat_is_chg_timeout调用到
chg-rechg-timeout:复充超时
trickle-timeout:涓流超时
chg-end-cur:充电端电流
chg-polling-time:充电状态检查周期(单位:s)
chg-polling-time-fast:
cap-one-per-time:每个百分比变化最短时间
cap-valid-range-poweron:应该是保存的电池容量,不确定,设备树里暂时没有用到过
temp-support:温度检测开关
temp-comp-res:读取温度补偿电阻器
only-vol-mode:获取电池容量模式,仅电压模式
fgu-mode:库仑计模式
chg-full-condition:满电判断条件电流电压或者外置 IC
alm_soc:暂且没看到用的地方
soft-vbat-uvlo :低电压关机
rint:电池内阻
cnom:电池容量
rsense-real:fgu 对地电阻真实阻抗
rsense-spec:fgu 对地电阻真实理论值
relax-current:进入 relax 模式下的电流值
fgu-cal-ajust:fgu校准偏移
temp-tab-val:ntc电阻表电压
charge-vol-tab:对应charge-vol-tab-cap,电压对应电量,只有电压测量方式
charge-vol-tab-cap:电量表
ocv-tab-vol:开路电压测量对应ocv-tab-cap
ocv-tab-cap:电量表
discharge-vol-tab:未充电的开路电压表,只有电压测量方式
discharge-vol-tab-cap:电量表JEITA 功能:可以根据温度,提供动态修正充电电流及恒压电压的功能
jeita-temp-tab 设置的对应温度点
为调整点对充电电流及恒压电压做调整;jeita-temp-recovery-tab 设置的温度点为温区恢复点;
温度变化到更高或者更低区间后如果恢复回来有 3℃的缓冲区
每个区间对应的电流值和电压值在 jeita-cur-tab 和 jeita-cccv-tab
cnom-temp-tab:电池容量和温度的表格,
rint-temp-tab:电池内阻和温度的表格

2. 各个工作队列的作用:

2.1 sprdbat_battery_works函数

这个函数是在probe函数中最后调用到的,也就是一开始probe的时候就会调用到的

static void sprdbat_battery_works(struct work_struct *work)
{SPRDBAT_DEBUG("sprdbat_battery_works\n");mutex_lock(&sprdbat_data->lock);//由设备树可知,only_vol_mode是不存在的,所以读取vbat的电压和开路电压if (!sprdbat_data->pdata->only_vol_mode) {sprdbat_data->bat_info.vbat_vol = sprdbat_read_vbat_vol();sprdbat_data->bat_info.vbat_ocv = sprdfgu_read_vbat_ocv();}//更新电池信息里的温度sprdbat_data->bat_info.last_temp =sprdbat_data->bat_info.cur_temp;if (jeita_debug_enable)sprdbat_data->bat_info.cur_temp = jeita_debug;elsesprdbat_data->bat_info.cur_temp = sprdbat_read_temp();//读取现在库仑计读数sprdbat_data->bat_info.bat_current = sprdfgu_read_batcurrent();//读取充电电压    sprdbat_data->bat_info.vchg_vol = sprdchg_read_vchg_vol();//读取现在充电平均电压sprdbat_data->bat_info.avg_chg_vol =sprdbat_get_avgval_from_buff(sprdbat_data->bat_info.vchg_vol,chg_vol_buff, VOL_BUFF_CNT, 0);//读取现在库仑计平均读数sprdbat_data->bat_info.bat_current_avg =sprdbat_get_avgval_from_buff(sprdbat_data->bat_info.bat_current,current_buff, CUR_BUFF_CNT, 1);if (sprdbat_data->pdata->only_vol_mode) {if (sprdbat_data->bat_info.module_state ==POWER_SUPPLY_STATUS_DISCHARGING ||sprdbat_data->bat_info.module_state ==POWER_SUPPLY_STATUS_UNKNOWN) {sprdbat_data->bat_info.vbat_vol =sprdbat_read_vbat_vol();sprdbat_data->bat_info.vbat_ocv =sprdfgu_read_vbat_ocv();sprdbat_update_capacty();}} else {//更新电池电量sprdbat_update_capacty();}mutex_unlock(&sprdbat_data->lock);sprdbat_print_battery_log();//不断轮询queue_delayed_work(system_power_efficient_wq,&sprdbat_data->battery_work,15 * HZ);
}

其中函数sprdbat_update_capacty更新电池电量:

static void sprdbat_update_capacty(void)
{uint32_t fgu_capacity;int flush_time = 0;int period_time = 0;struct timespec64 cur_time;int chging_flag;if (sprdbat_data->bat_info.capacity == ~0U)return;if (sprdbat_data->pdata->only_vol_mode) {if (sprdbat_data->bat_info.module_state ==POWER_SUPPLY_STATUS_CHARGING)chging_flag = 1;elsechging_flag = 0;fgu_capacity = sprdfgu_only_vol_read_capacity(chging_flag);} else {fgu_capacity = sprdfgu_read_capacity();}cur_time = ktime_to_timespec64(ktime_get_boottime());if (POWER_SUPPLY_STATUS_CHARGING ==sprdbat_data->bat_info.module_state) {if (sprdbat_data->bat_info.capacity >= 99) {trickle_time = cur_time.tv_sec -trickle_s_time;} else {trickle_s_time = cur_time.tv_sec;trickle_time = 0;}} else {//直接进入到这里//涓流时间trickle_s_time=现在时间trickle_s_time = cur_time.tv_sec;//trickle_time = 涓流超时时间+周期变化时间+1trickle_time = sprdbat_data->pdata->trickle_timeout +sprdbat_data->pdata->cap_one_per_time + 1;}SPRDBAT_DEBUG("trickle_s_time: = %lld,trickle_time: = %d\n",trickle_s_time, trickle_time);//刷新时间 =  现在时间 - 电量变化时间//sprdbat_update_capacity_time这个时间会在fgu_capacity != sprdbat_data->bat_info.capacity产生变化flush_time =(int)(cur_time.tv_sec -sprdbat_data->sprdbat_update_capacity_time);//周期时间为每次调用sprdbat_update_capacty的cur_time-上一次调用该函数的时间period_time =(int)(cur_time.tv_sec -sprdbat_data->sprdbat_last_query_time);sprdbat_data->sprdbat_last_query_time = cur_time.tv_sec;SPRDBAT_DEBUG("fgu_cap: = %d,flush: = %d,period:=%d\n",fgu_capacity, flush_time, period_time);//根据不同状态来确定充电时间:switch (sprdbat_data->bat_info.module_state) {case POWER_SUPPLY_STATUS_CHARGING://如果是充电状态,但是计算出来的电量跟上一次的小,那就属于不正常的情况if (fgu_capacity < sprdbat_data->bat_info.capacity) {//电流大于0,避免下降if (sprdfgu_read_batcurrent() >= 0) {pr_info("avoid vol jumping\n");fgu_capacity = sprdbat_data->bat_info.capacity;} else {//假设周期时间小于最大更新时间,则正常减1if (period_time <sprdbat_data->pdata->cap_one_per_time) {fgu_capacity =sprdbat_data->bat_info.capacity - 1;SPRDBAT_DEBUG("cap decrease fgu_cap:=%d\n",fgu_capacity);}//精度化一下电量if ((sprdbat_data->bat_info.capacity -fgu_capacity) >=(flush_time /sprdbat_data->pdata->cap_one_per_time)) {fgu_capacity =sprdbat_data->bat_info.capacity -flush_time /sprdbat_data->pdata->cap_one_per_time;}}} else if (fgu_capacity > sprdbat_data->bat_info.capacity) {//假设周期时间小于最大更新时间,则正常加1if (period_time < sprdbat_data->pdata->cap_one_per_time) {fgu_capacity =sprdbat_data->bat_info.capacity + 1;SPRDBAT_DEBUG("avoid  jumping! fgu_cap: = %d\n",fgu_capacity);}//精度化一下电量if ((fgu_capacity - sprdbat_data->bat_info.capacity) >=(flush_time /sprdbat_data->pdata->cap_one_per_time)) {fgu_capacity =sprdbat_data->bat_info.capacity +flush_time /sprdbat_data->pdata->cap_one_per_time;}}//我认为是还未更新到正常电量,但是adc算出的已经是100,但这时候显示的是99;if ((sprdbat_data->bat_info.capacity != 100)&& (fgu_capacity >= 100)) {fgu_capacity = 99;}//涓流充电流程if ((sprdbat_data->bat_info.capacity >= 99) &&(trickle_time >= sprdbat_data->pdata->trickle_timeout) &&(sprdbat_data->pdata->trickle_timeout > 0)) {SPRDBAT_DEBUG("cap is full, but charge continue\n");sprdbat_change_module_state(SPRDBAT_CHARGING_TO_FULL_E);}//低电量关机if (sprdbat_data->bat_info.vbat_vol <=(sprdbat_data->pdata->soft_vbat_uvlo -SPRDBAT_SHUTDOWN_OFSSET)) {fgu_capacity = 0;SPRDBAT_DEBUG("soft uvlo, shutdown by kernel.. vol:%d",sprdbat_data->bat_info.vbat_vol);orderly_poweroff(true);}break;case POWER_SUPPLY_STATUS_NOT_CHARGING:case POWER_SUPPLY_STATUS_DISCHARGING://未充电状态不应该是计算出来的电量大于原来的电量,所以继续保持原来的电量if (fgu_capacity >= sprdbat_data->bat_info.capacity) {fgu_capacity = sprdbat_data->bat_info.capacity;} else {//否则则慢慢下降if (period_time < sprdbat_data->pdata->cap_one_per_time) {fgu_capacity =sprdbat_data->bat_info.capacity - 1;SPRDBAT_DEBUG("avoid jumping! fgu_capacity: = %d\n",fgu_capacity);}//同样也是精度化if ((sprdbat_data->bat_info.capacity - fgu_capacity) >=(flush_time /sprdbat_data->pdata->cap_one_per_time)) {fgu_capacity =sprdbat_data->bat_info.capacity -flush_time /sprdbat_data->pdata->cap_one_per_time;}}break;case POWER_SUPPLY_STATUS_FULL://展讯平台是根据电量来复充的sprdbat_data->sprdbat_update_capacity_time = cur_time.tv_sec;//假设计算出来的电压小于复充电压-150且不在充电的情况if ((sprdbat_data->bat_info.vbat_ocv <(sprdbat_data->pdata->rechg_vol - 150))&& sprdfgu_read_batcurrent() < 0) {SPRDBAT_DEBUG("vbat_ocv < rechg_vol -150\n");//从满电状态转换为充电状态sprdbat_change_module_state(SPRDBAT_FULL_TO_CHARGING_E);}//电量保持100%if (fgu_capacity != 100)fgu_capacity = 100;if (sprdbat_data->bat_info.vbat_vol <=(sprdbat_data->pdata->soft_vbat_uvlo -SPRDBAT_SHUTDOWN_OFSSET)) {fgu_capacity = 0;SPRDBAT_DEBUG("soft uvlo, shutdown by kernel status full\n");SPRDBAT_DEBUG("vol:%d",sprdbat_data->bat_info.vbat_vol);orderly_poweroff(true);}break;default:break;}//低电压关机if (sprdbat_data->bat_info.vbat_vol <=sprdbat_data->pdata->soft_vbat_uvlo) {fgu_capacity = 0;SPRDBAT_DEBUG("soft uvlo, vbat very low,level..0.. vol:%d",sprdbat_data->bat_info.vbat_vol);}//更新时间状态,并且用power_supply_changed给上层切换状态if (fgu_capacity != sprdbat_data->bat_info.capacity) {sprdbat_data->bat_info.capacity = fgu_capacity;sprdbat_data->sprdbat_update_capacity_time = cur_time.tv_sec;sprdfgu_record_cap(sprdbat_data->bat_info.capacity);power_supply_changed(sprdbat_data->battery);} else {if (sprdbat_data->bat_info.cur_temp !=sprdbat_data->bat_info.last_temp)power_supply_changed(sprdbat_data->battery);}}

2.2 sprdbat_battery_sleep_works函数

此函数是为了唤醒电量计算的功能,在sprdbat_resume函数调用:


static void sprdbat_battery_sleep_works(struct work_struct *work)
{SPRDBAT_DEBUG("sprdbat_battery_sleep_works\n");if (!queue_delayed_work(system_power_efficient_wq,&sprdbat_data->battery_work, 0)) {cancel_delayed_work_sync(&sprdbat_data->battery_work);queue_delayed_work(system_power_efficient_wq,&sprdbat_data->battery_work, 0);}
}

其本质意义就是重新调用电池计算的功能定时器;

2.3 sprdbat_vbat_detect_irq_works函数:

此函数是在sprdbat_vbat_detect_irq中断检测到调用的:

当vbat检测管脚为低电量时,则进入该中断

devm_request_threaded_irq(&pdev->dev,data->irq_vbat_detect, NULL,sprdbat_vbat_detect_irq,IRQ_TYPE_LEVEL_LOW | IRQF_NO_SUSPEND,"sprdbat_vbat_detect", data);static __used irqreturn_t sprdbat_vbat_detect_irq(int irq, void *dev_id)
{disable_irq_nosync(sprdbat_data->irq_vbat_detect);SPRDBAT_DEBUG("battery detect handle!!!!\n");queue_work(sprdbat_data->monitor_wqueue,&sprdbat_data->vbat_detect_irq_work);return IRQ_HANDLED;
}

sprdbat_vbat_detect_irq_works函数:


static void sprdbat_vbat_detect_irq_works(struct work_struct *work)
{int value;value = gpio_get_value(sprdbat_data->gpio_vbat_detect);SPRDBAT_DEBUG("bat_detect value:0x%x\n", value);mutex_lock(&sprdbat_data->lock);//假设高电平进入此中断,那属于不正常的情况if (value) {if (!sprdbat_data->bat_info.bat_present) {sprdbat_data->bat_info.bat_present = 1;//电池拔出后重新插入sprdbat_change_module_state(SPRDBAT_CHG_UNSPEC_RESTART_E);//假设是不是在非充电状态if (POWER_SUPPLY_STATUS_DISCHARGING !=sprdbat_data->bat_info.module_state)sprdbat_data->start_charge();SPRDBAT_DEBUG("vbat_detect-start_charge!!!!\n");}irq_set_irq_type(sprdbat_data->irq_vbat_detect,IRQ_TYPE_LEVEL_LOW);} else {//电池拔出sprdbat_data->bat_info.bat_present = 0;sprdbat_change_module_state(SPRDBAT_CHG_UNSPEC_E);//停止充电sprdbat_data->stop_charge();SPRDBAT_DEBUG("vbat_detect-stop_charge!!!!\n");irq_set_irq_type(sprdbat_data->irq_vbat_detect,IRQ_TYPE_LEVEL_HIGH);}enable_irq(sprdbat_data->irq_vbat_detect);mutex_unlock(&sprdbat_data->lock);
}

2.4 sprdbat_plug_works函数

在usb插入的回调函数sprdbat_usb_plug_event中会使用,在probe如果初始状态也是会使用:


static void sprdbat_plug_works(struct work_struct *work)
{if (usb_charger_get_state(sprdbat_data->usb_charger)== USB_CHARGER_PRESENT)plugin_callback();elseplugout_callback();
}static int plugin_callback(void)
{SPRDBAT_DEBUG("charger plug in interrupt happen\n");mutex_lock(&sprdbat_data->lock);sprdbat_data->sprdbat_vbat_ovp_cnt = 0;//排除异常情况if (sprdbat_data->bat_info.module_state!= POWER_SUPPLY_STATUS_DISCHARGING) {mutex_unlock(&sprdbat_data->lock);return 0;}sprdbat_data->bat_info.adp_type = sprdchg_charger_is_adapter();if ((sprdbat_data->bat_info.adp_type == SDP_TYPE) ||(sprdbat_data->bat_info.adp_type == CDP_TYPE)) {sprdbat_data->bat_info.usb_online = 1;power_supply_changed(sprdbat_data->usb);} else {sprdbat_data->bat_info.ac_online = 1;power_supply_changed(sprdbat_data->ac);}sprdbat_data->bat_info.chgr_temp= sprdbat_get_avg_chgr_temp(NORMAL_TEMP, true);//充电器插入sprdbat_change_module_state(SPRDBAT_ADP_PLUGIN_E);sprdbat_adp_plug_nodify(1);//快充检测sprdbat_fchg_detect();sprdbat_charge_prepare();//开始充电,回调内部充电里面的函数sprdbat_data->start_charge();if (sprdchg_timer_op->timer_enable) {u32 polling_time = sprdbat_data->pdata->chg_polling_time;if (sprdbat_data->pdata->only_vol_mode)sprdchg_timer_op->timer_enable(polling_time, ONE_TIME);elsesprdchg_timer_op->timer_enable(polling_time,PERIOD_TIME);}mutex_unlock(&sprdbat_data->lock);SPRDBAT_DEBUG("plugin_callback:adp_type:%d\n",sprdbat_data->bat_info.adp_type);SPRDBAT_DEBUG("plugin_callback: end...\n");return 0;
}static int plugout_callback(void)
{uint32_t adp_type = sprdbat_data->bat_info.adp_type;SPRDBAT_DEBUG("charger plug out interrupt happen\n");mutex_lock(&sprdbat_data->lock);if (sprdbat_data->bat_info.module_state== POWER_SUPPLY_STATUS_DISCHARGING) {mutex_unlock(&sprdbat_data->lock);return 0;}disable_irq_nosync(sprdbat_data->irq_vchg_ovi);if (sprdchg_timer_op->timer_disable)sprdchg_timer_op->timer_disable();sprdbat_change_module_state(SPRDBAT_ADP_PLUGOUT_E);sprdbat_data->stop_charge();if ((sprd_fchg_op != NULL) && sprd_fchg_op->fchg_deinit)sprd_fchg_op->fchg_deinit();sprdbat_adp_plug_nodify(0);sprdbat_data->bat_info.module_state = POWER_SUPPLY_STATUS_DISCHARGING;sprdbat_data->bat_info.adp_type = SDP_TYPE;sprdbat_data->bat_info.ac_online = 0;sprdbat_data->bat_info.usb_online = 0;sprdbat_data->fchg_det = 0;mutex_unlock(&sprdbat_data->lock);if (sprd_ext_ic_op->set_input_cur_limit) {unsigned int limit = sprdbat_data->pdata->adp_sdp_cur_limit;sprd_ext_ic_op->set_input_cur_limit(limit);sprdbat_data->bat_info.input_cur_limit = limit;}if ((adp_type == SDP_TYPE) || (adp_type == CDP_TYPE))power_supply_changed(sprdbat_data->usb);elsepower_supply_changed(sprdbat_data->ac);return 0;
}

2.5 sprdbat_charge_works函数

这个函数会在sprdbat_change_module_state中使用,还有sprdbat_timer_handler函数中使用定时器中断;


static void sprdbat_charge_works(struct work_struct *work)
{SPRDBAT_DEBUG("sprdbat_charge_works----------start\n");mutex_lock(&sprdbat_data->lock);//只有电压模式暂时不考虑if (!sprdbat_data->pdata->only_vol_mode) {sprdbat_data->bat_info.vbat_vol = sprdbat_read_vbat_vol();sprdbat_data->bat_info.vbat_ocv = sprdfgu_read_vbat_ocv();}//读取电流sprdbat_data->bat_info.bat_current = sprdfgu_read_batcurrent();//假设外部充电IC存在,则喂狗if (sprd_ext_ic_op->timer_callback_ext)sprd_ext_ic_op->timer_callback_ext();//没有充电则是返回if (sprdbat_data->bat_info.module_state ==POWER_SUPPLY_STATUS_DISCHARGING) {SPRDBAT_DEBUG("not charing return\n");mutex_unlock(&sprdbat_data->lock);return;}if (sprdbat_data->pdata->only_vol_mode &&!sprdchg_timer_op->timer_enable) {mutex_unlock(&sprdbat_data->lock);return;}//只有电压模式暂时不考虑if (sprdbat_data->pdata->only_vol_mode) {unsigned int poll_time_fast =sprdbat_data->pdata->chg_polling_time_fast;unsigned int poll_time =sprdbat_data->pdata->chg_polling_time;if (sprdbat_data->bat_info.chg_stop_flags ==SPRDBAT_CHG_END_NONE_BIT) {if (sprdbat_data->bat_info.chging_on) {sprd_ext_ic_op->charge_stop_ext(SPRDBAT_CHG_END_NONE_BIT);sprdbat_data->bat_info.chging_on = 0;sprdchg_timer_op->timer_disable();sprdchg_timer_op->timer_enable(poll_time_fast,ONE_TIME);mutex_unlock(&sprdbat_data->lock);return;}sprdbat_data->bat_info.vbat_vol =sprdbat_read_vbat_vol();sprdbat_data->bat_info.vbat_ocv =sprdfgu_read_vbat_ocv();sprdbat_update_capacty();sprdbat_data->bat_info.chging_on = 1;sprd_ext_ic_op->charge_start_ext();msleep(20);sprdchg_timer_op->timer_disable();sprdchg_timer_op->timer_enable(poll_time, ONE_TIME);} else {//读取vbat_volsprdbat_data->bat_info.vbat_vol =sprdbat_read_vbat_vol();//读取vbat开路电压sprdbat_data->bat_info.vbat_ocv =sprdfgu_read_vbat_ocv();//更新电量sprdbat_update_capacty();sprdchg_timer_op->timer_disable();sprdchg_timer_op->timer_enable(poll_time, ONE_TIME);}}if (sprdbat_data->bat_info.chg_stop_flags & SPRDBAT_CHG_END_FULL_BIT)//充满标志位,如果判断开路电压降低到rechg-volsprdbat_chg_rechg_monitor();sprdbat_chg_status_monitor();sprdbat_chg_timeout_monitor();sprdbat_chg_ovp_monitor();sprdbat_temp_monitor();sprdbat_chgr_temp_monitor();sprdbat_fault_monitor();mutex_unlock(&sprdbat_data->lock);sprdbat_chg_print_log();SPRDBAT_DEBUG("sprdbat_charge_works----------end\n");}

2.5.1 充满电监控函数sprdbat_chg_status_monitor


static void sprdbat_chg_status_monitor(void)
{int chg_status = POWER_SUPPLY_STATUS_CHARGING;SPRDBAT_DEBUG(" %s,ocv=%d, cur=%d,chg_end_vol_l=%d,chg_end_cur=%d\n",__func__, sprdbat_data->bat_info.vbat_ocv,sprdbat_data->bat_info.bat_current,sprdbat_data->pdata->chg_end_vol_l,sprdbat_data->pdata->chg_end_cur);//这个暂且不理if (sprdbat_data->pdata->only_vol_mode) {if (sprdbat_data->bat_info.vbat_vol >sprdbat_data->pdata->chg_end_vol_l) {sprdbat_data->chg_full_trigger_cnt++;if (sprdbat_data->chg_full_trigger_cnt >= 2) {sprdbat_data->chg_full_trigger_cnt = 0;if (sprdbat_data->bat_info.capacity >= 99 &&trickle_time >=sprdbat_data->pdata->cap_one_per_time) {sprdbat_change_module_state(SPRDBAT_CHG_FULL_E);sprdbat_data->stop_charge();} else {sprdfgu_force_set_soc(1000);}}} else {sprdbat_data->chg_full_trigger_cnt = 0;}return;}//chg_full_condition 的设备树由chg-full-condition决定,并且是0,决定充电结束条件if (sprdbat_data->pdata->chg_full_condition == FROM_EXT_IC) {chg_status = sprd_ext_ic_op->get_charging_status();if (chg_status == POWER_SUPPLY_STATUS_FULL) {SPRDBAT_DEBUG("chg full\n");/* capacity is high enough, set the status to full */if (sprdbat_data->bat_info.capacity >= 99 &&trickle_time >=sprdbat_data->pdata->cap_one_per_time)sprdbat_change_module_state(SPRDBAT_CHG_FULL_E);elsesprdfgu_force_set_soc(1000);} else {SPRDBAT_DEBUG("chging or fault\n");}} else if (sprdbat_data->pdata->chg_full_condition == VOL_AND_CUR) {//两个条件同时成立两次,一是vbat的电压大于截止充电电压条件,二是vbat的充电电流小于充电电流if ((sprdbat_data->bat_info.vbat_vol >sprdbat_data->pdata->chg_end_vol_l)&& (sprdbat_data->bat_info.bat_current <sprdbat_data->pdata->chg_end_cur)) {sprdbat_data->chg_full_trigger_cnt++;if (sprdbat_data->chg_full_trigger_cnt >= 2) {SPRDBAT_DEBUG("charge full stop charge\n");sprdbat_data->chg_full_trigger_cnt = 0;/* cap is high enough, set the status to full */if (sprdbat_data->bat_info.capacity >= 99 &&trickle_time >=sprdbat_data->pdata->cap_one_per_time) {sprdbat_change_module_state(SPRDBAT_CHG_FULL_E);sprdbat_data->stop_charge();} else {sprdfgu_force_set_soc(1000);}}} else {sprdbat_data->chg_full_trigger_cnt = 0;}} else if (sprdbat_data->pdata->chg_full_condition == VOL_AND_STATUS) {if ((sprdbat_data->bat_info.vbat_vol >sprdbat_data->pdata->chg_end_vol_l|| sprd_ext_ic_op->get_charging_status())&& (sprdbat_data->bat_info.bat_current <sprdbat_data->pdata->chg_end_cur)) {sprdbat_data->chg_full_trigger_cnt++;if (sprdbat_data->chg_full_trigger_cnt >= 2) {SPRDBAT_DEBUG("charge full stop charge\n");sprdbat_data->chg_full_trigger_cnt = 0;/* cap is high enough, set the status to full */if (sprdbat_data->bat_info.capacity >= 99 &&trickle_time >=sprdbat_data->pdata->cap_one_per_time) {sprdbat_change_module_state(SPRDBAT_CHG_FULL_E);sprdbat_data->stop_charge();} else {sprdfgu_force_set_soc(1000);}}} else {sprdbat_data->chg_full_trigger_cnt = 0;}} else {SPRDBAT_DEBUG("bad chg_full_condition\n");}
}

2.5.2 充电超时监控函数sprdbat_chg_timeout_monitor

充电超时后如果满足电量充足则状态设置为满电状态,如果电池电压过低则需要重新启动充电


static void sprdbat_chg_timeout_monitor(void)
{SPRDBAT_DEBUG("sprdbat_chg_timeout_monitor enter\n");if (sprdbat_data->bat_info.chg_stop_flags &SPRDBAT_CHG_END_TIMEOUT_BIT) {SPRDBAT_DEBUG("sprdbat_chg_timeout_monitor recharge\n");sprdbat_change_module_state(SPRDBAT_CHG_TIMEOUT_RESTART_E);sprdbat_data->start_charge();}if (sprdbat_data->bat_info.chg_stop_flags == SPRDBAT_CHG_END_NONE_BIT) {if (sprdbat_is_chg_timeout()) {SPRDBAT_DEBUG("sprdbat_chg_timeout_monitor chg timeout\n");if (sprdbat_data->bat_info.vbat_ocv >sprdbat_data->pdata->rechg_vol) {sprdbat_change_module_state(SPRDBAT_CHG_FULL_E);sprdbat_data->stop_charge();} else {sprdbat_data->bat_info.chg_this_timeout =sprdbat_data->pdata->chg_rechg_timeout;sprdbat_change_module_state(SPRDBAT_CHG_TIMEOUT_E);sprdbat_data->stop_charge();}}}
}

2.5.3 充电器过压保护监控函数

充电器过压采用轮询方式轮询用户配置电压参数修改充电状态,快充电压如果下降2000mv 则退出快充,重新设定充电电流。

ovp-stop = <6500>; //充电过压保护
ovp-restart = <5800>; //过压恢复电压
fchg-ovp-stop = <11000>; //快充过压电压
fchg-ovp-restart = <10000>; //快充过压恢复电压

static void sprdbat_chg_ovp_monitor(void)
{int ovp_restart, ovp_stop;if (sprdbat_data->fchg_det) {ovp_restart = sprdbat_data->pdata->fchg_ovp_restart;ovp_stop = sprdbat_data->pdata->fchg_ovp_stop;} else {ovp_restart = sprdbat_data->pdata->ovp_restart;ovp_stop = sprdbat_data->pdata->ovp_stop;}SPRDBAT_DEBUG("%s chg_vol = %d,ovp_stop =%d,ovp_restart=%d\n",__func__, sprdbat_data->bat_info.avg_chg_vol,ovp_stop, ovp_restart);if (sprdbat_data->bat_info.chg_stop_flags & SPRDBAT_CHG_END_OVP_BIT) {if (sprdbat_data->bat_info.avg_chg_vol <= ovp_restart) {SPRDBAT_DEBUG("charge vol low restart chg\n");sprdbat_change_module_state(SPRDBAT_OVI_RESTART_E);sprdbat_data->start_charge();} else {SPRDBAT_DEBUG("sprdbat_chg_ovp_monitor ovp return ");}} else if (sprdbat_data->bat_info.avg_chg_vol >= ovp_stop) {SPRDBAT_DEBUG("charge vol is too high\n");sprdbat_change_module_state(SPRDBAT_OVI_STOP_E);sprdbat_data->stop_charge();}if (sprdbat_data->fchg_det) {/*if vbus vol <(vbus - 2000mv),exit*/uint32_t fchg_l = sprdbat_data->pdata->fchg_vol - 2000;if (sprdbat_data->bat_info.avg_chg_vol <= fchg_l) {SPRDBAT_DEBUG("fchg_l_low:%d\n", fchg_l);sprdbat_data->fchg_det = 0;if ((sprd_fchg_op != NULL)&& (sprd_fchg_op->fchg_deinit))sprd_fchg_op->fchg_deinit();sprdbat_charge_prepare();sprdbat_data->start_charge();power_supply_changed(sprdbat_data->battery);}}
}

2.5.4 充电器温度监控函数

具体参考《SL8541E充电介绍.pdf》

2.5.5 sprdbat_chgr_temp_monitor函数

暂且不知道作用

2.5.6 sprdbat_fault_monitor监控错误函数


static void sprdbat_fault_monitor(void)
{int chg_fault, status, vbat_ovp, terminal_voltage;SPRDBAT_DEBUG("sprdbat_fault_monitor enter\n");status = sprdbat_data->cur_temp_status;terminal_voltage = sprdbat_data->pdata->jeita_tab[status].z;vbat_ovp = terminal_voltage + VBAT_OVP_THRESHOLD;chg_fault = sprd_ext_ic_op->get_charging_fault();if (chg_fault == SPRDBAT_CHG_END_NONE_BIT)sprdbat_fault_recovery_monitor();if (chg_fault & SPRDBAT_CHG_END_OTP_COLD_BIT)SPRDBAT_DEBUG(" power cold\n");if (chg_fault & SPRDBAT_CHG_END_OTP_OVERHEAT_BIT)SPRDBAT_DEBUG("power hot\n");if (chg_fault & SPRDBAT_CHG_END_TIMEOUT_BIT) {SPRDBAT_DEBUG("  safe time expire\n");sprdbat_change_module_state(SPRDBAT_CHG_TIMEOUT_E);}if (chg_fault & SPRDBAT_CHG_END_BAT_OVP_BIT) {if (sprdbat_data->sprdbat_vbat_ovp_cnt > VBAT_OVP_CNT_THRESHOLD &&sprdbat_data->bat_info.vbat_vol > vbat_ovp) {SPRDBAT_DEBUG("fault: vbat ovp\n");sprdbat_change_module_state(SPRDBAT_VBAT_OVP_E);} else {SPRDBAT_DEBUG("warning: vbat ovp\n");sprdbat_fchg_detect();sprdbat_charge_prepare();sprdbat_data->start_charge();sprdbat_data->sprdbat_vbat_ovp_cnt++;}} else {sprdbat_data->sprdbat_vbat_ovp_cnt = 0;}if (chg_fault == SPRDBAT_CHG_END_UNSPEC)SPRDBAT_DEBUG(" unspec fault\n");}

3. 其他状态位

3.1 充电状态:

enum sprdbat_event {
SPRDBAT_ADP_PLUGIN_E, //充电器插入
SPRDBAT_ADP_PLUGOUT_E, //充电器拔出
SPRDBAT_OVI_STOP_E, //充电器电压过高
SPRDBAT_OVI_RESTART_E, //充电器过压后恢复
SPRDBAT_OTP_COLD_STOP_E, //电池温度过低
SPRDBAT_OTP_OVERHEAT_STOP_E, //电池温度过高
SPRDBAT_OTP_COLD_RESTART_E, //电池温度从低温恢复
SPRDBAT_OTP_OVERHEAT_RESTART_E, //电池温度从高温恢复
SPRDBAT_CHG_FULL_E, //充满电
SPRDBAT_RECHARGE_E, //满电后复充
SPRDBAT_CHG_TIMEOUT_E, //充电超时
SPRDBAT_CHG_TIMEOUT_RESTART_E, //超时后重新启动充电
SPRDBAT_VBAT_OVP_E, //电池过压
SPRDBAT_VBAT_OVP_RESTART_E, //电池过压恢复
SPRDBAT_CHG_UNSPEC_E, //电池拔出
SPRDBAT_CHG_UNSPEC_RESTART_E, //电池拔出后重新插入
SPRDBAT_FULL_TO_CHARGING_E, //满电强制启动充电
SPRDBAT_CHARGING_TO_FULL_E, //充电强制显示 100
SPRDBAT_CHG_FORCE_STOP_E, //强制启动充电
SPRDBAT_CHG_FORCE_START_E, //强制关闭充电
};

展讯sprd_battery.c 充电驱动相关推荐

  1. 展讯平台安卓充电驱动框架

    1.充电架构介绍 1.1 充电软件框架 Android 层充电构架如图 1-1 所示,充电设备驱动按照标准的 linux power supply 架构设计,通过 uevetd守护进程 Healthd ...

  2. 1.3【展讯平台】Android 驱动(Kernel)、系统(framework) 定制,调试日志

    前言 [展讯平台]Android 4.4 驱动(Kernel).系统(framework) 定制,调试日志 正文 1:提高串口日志等级 查看 adb shell cat /proc/sys/kerne ...

  3. 【展讯平台】Android 驱动(Kernel)、系统(framework) 定制,调试日志,持续更新中..

    前言 [展讯平台]Android 4.4 驱动(Kernel).系统(framework) 定制,调试日志 正文 1:提高串口日志等级 查看 adb shell cat /proc/sys/kerne ...

  4. 展讯8910DM:LED驱动调试,支持一线脉冲调节

    LED采用艾为电子的AW36404DNR,模组原理图如下: AW36404是一款低压降电流吸收器LED驱动器,支持闪光灯和手电筒模式.集成在芯片中的电流调节接收器使LED电流在输入时能够保持恒定电压, ...

  5. 展讯平台-LCD驱动

    所谓驱动者,三分硬件,三分格式,四分软件.对于展讯平台的LCD驱动,首先就要了解一点基本的硬件知识. 一.LCD的接口 其实LCD的接口有很多,但是不管是在手机还是电脑,液晶屏的接口也最常用的有两个, ...

  6. [置顶] 展讯充电管理模块浅析(一)

    展讯充电管理模块浅析 电池在电子产品中所占的地位就不用说了.不过电池在物理接口上比较简单,就两条线:正极.负极,这个小学生科普知识都知道:不过真正用到电子产品中时,有关电池方面的东西还是有点多的. 参 ...

  7. 展讯充电管理模块浅析(一)

    展讯充电管理模块浅析 电池在电子产品中所占的地位就不用说了.不过电池在物理接口上比较简单,就两条线:正极.负极,这个小学生科普知识都知道:不过真正用到电子产品中时,有关电池方面的东西还是有点多的. 参 ...

  8. 展讯平台-sensor驱动

        驱动者,三分硬件,三分格式,四分软件.     在手机中,我们常将camera直接称为sensor,展讯平台的sensor就像lcd一样,已经十分的成熟了.沿袭前面的模式,先介绍一下硬件的基本 ...

  9. 展讯 Camera 驱动流程 576 i 480i CVBS NTSC 摄像头调试

    本文主要研究展讯平台Camera驱动和HAL层代码架构,熟悉展讯Camera的控制流程. 平台:Sprd-展讯平台 Hal版本:[HAL3] 知识点如下: 从HAL层到deiver层 1.Camera ...

最新文章

  1. 阿里云发布新一代CDN 6.0 主打云与大数据融合
  2. 学习汇编语言 -王爽,自已完成的一道课程设计题 (5)
  3. 二维数组动态分配内存
  4. Request —— 获取请求行数据 获取请求头数据 获取请求体数据
  5. WebBrowser控件判断完全加载中DocumentCompleted和Navigated的关系
  6. AB1601定时器timer0,timer1简介
  7. 经典C语言程序100例之八四
  8. 语音交互编程语言了解一下?
  9. 当.NET遇到机器学习
  10. 一秒点击屏幕次数测试_安卓App性能专项测试流畅度深度解析
  11. 在 phpMyAdmin 里添加新用户帐号
  12. 使用runnable创建线程
  13. Xmind快捷键笔记
  14. C#操作XML的完整例子——XmlDocument篇
  15. 2019如何新建流程图_用Word制作流程图,居然还有这么多小技巧
  16. 百度免费开放长语音识别功能
  17. ae合成复制脚本_【脚本】AE脚本精选系列 | 合成复制脚本 True Comp Duplicator v3.9.7...
  18. 微信公众号后台开发总结
  19. 天刀论剑显示服务器,《天涯明月刀ol》服务器维护公告 天下镖和论剑优化
  20. 网站域名后缀index.html的去除方法

热门文章

  1. 美国80后恶搞希拉里与名人发短信场景
  2. Java 是否应该使用通配符导入( wildcard imports)
  3. 《你心柔软,却有力量》-林清玄--读书笔记
  4. 全球医疗实力排名:各国差距有多大?
  5. 高斯投影法正反算代码MATLAB版本
  6. API查q绑定带反查sgk带接口API文件源码
  7. iOS 系统方法获取当前位置经纬度
  8. 苹果手机微信声音小怎么调大声_【泽云广场|好声音KTV】19.9元畅享门市价198元下午场欢唱+茶水!79.9元尊享全天场欢唱+酒水小吃附带纸巾!越夜越时尚!...
  9. 华为不要php开发,华为手机开发者选项的利与弊!有些设置最好不要打开
  10. 上汽通用凯迪拉克汽车的多媒体系统不能识别u盘的解决方法