ret = iio_read_channel_processed(di->bat_ctrl, &vbtemp); if (ret < 0) {
dev_err(di->dev, "%s ADC conversion failed, using previous value",
__func__); return prev;
}
prev = vbtemp; return vbtemp;
}
/** *ab8500_btemp_get_batctrl_res()-getbatteryresistance *@di:pointertotheab8500_btempstructure * *Thisfunctionreturnsthebatterypackidentificationresistance. *ReturnsvalueinOhms.
*/ staticint ab8500_btemp_get_batctrl_res(struct ab8500_btemp *di)
{ int ret; int batctrl = 0; int res; int inst_curr; int i;
if (!di->fg)
di->fg = ab8500_fg_get(); if (!di->fg) {
dev_err(di->dev, "No fg found\n"); return -EINVAL;
}
ret = ab8500_fg_inst_curr_start(di->fg);
if (ret) {
dev_err(di->dev, "Failed to start current measurement\n"); return ret;
}
do {
msleep(20);
} while (!ab8500_fg_inst_curr_started(di->fg));
i = 0;
do {
batctrl += ab8500_btemp_read_batctrl_voltage(di);
i++;
msleep(20);
} while (!ab8500_fg_inst_curr_done(di->fg));
batctrl /= i;
ret = ab8500_fg_inst_curr_finalize(di->fg, &inst_curr); if (ret) {
dev_err(di->dev, "Failed to finalize current measurement\n"); return ret;
}
res = ab8500_btemp_batctrl_volt_to_res(di, batctrl, inst_curr);
/** *ab8500_btemp_periodic_work()-Measuringthetemperatureperiodically *@work:pointertothework_structstructure * *Workfunctionformeasuringthetemperatureperiodically
*/ staticvoid ab8500_btemp_periodic_work(struct work_struct *work)
{ int interval; int bat_temp; struct ab8500_btemp *di = container_of(work, struct ab8500_btemp, btemp_periodic_work.work); /* Assume 25 degrees celsius as start temperature */ staticint prev = 25; int ret;
if (!di->initialized) { /* Identify the battery */ if (ab8500_btemp_id(di) < 0)
dev_warn(di->dev, "failed to identify the battery\n");
}
/* Failover if a reading is erroneous, use last measurement */
ret = thermal_zone_get_temp(di->tz, &bat_temp); if (ret) {
dev_err(di->dev, "error reading temperature\n");
bat_temp = prev;
} else { /* Convert from millicentigrades to centigrades */
bat_temp /= 1000;
prev = bat_temp;
}
/* Go through all properties for the psy */ for (j = 0; j < ext->desc->num_properties; j++) { enum power_supply_property prop;
prop = ext->desc->properties[j];
if (power_supply_get_property(ext, prop, &ret)) continue;
switch (prop) { case POWER_SUPPLY_PROP_PRESENT: switch (ext->desc->type) { case POWER_SUPPLY_TYPE_MAINS: /* AC disconnected */ if (!ret.intval && di->events.ac_conn) {
di->events.ac_conn = false;
} /* AC connected */ elseif (ret.intval && !di->events.ac_conn) {
di->events.ac_conn = true; if (!di->events.usb_conn)
ab8500_btemp_periodic(di, true);
} break; case POWER_SUPPLY_TYPE_USB: /* USB disconnected */ if (!ret.intval && di->events.usb_conn) {
di->events.usb_conn = false;
} /* USB connected */ elseif (ret.intval && !di->events.usb_conn) {
di->events.usb_conn = true; if (!di->events.ac_conn)
ab8500_btemp_periodic(di, true);
} break; default: break;
} break; default: break;
}
} return0;
}
/* Create a work queue for the btemp */
di->btemp_wq =
alloc_workqueue("ab8500_btemp_wq", WQ_MEM_RECLAIM, 0); if (di->btemp_wq == NULL) {
dev_err(dev, "failed to create work queue\n"); return -ENOMEM;
}
/* Kick off periodic temperature measurements */
ab8500_btemp_periodic(di, true);
staticint ab8500_btemp_probe(struct platform_device *pdev)
{ struct power_supply_config psy_cfg = {}; struct device *dev = &pdev->dev; struct ab8500_btemp *di; int irq, i, ret = 0;
u8 val;
di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); if (!di) return -ENOMEM;
di->bm = &ab8500_bm_data;
/* get parent data */
di->dev = dev;
di->parent = dev_get_drvdata(pdev->dev.parent);
/* Get thermal zone and ADC */
di->tz = thermal_zone_get_zone_by_name("battery-thermal"); if (IS_ERR(di->tz)) {
ret = PTR_ERR(di->tz); /* *Thisusuallyjustmeansweareprobingbeforethethermal *zone,sojustdefer.
*/ if (ret == -ENODEV)
ret = -EPROBE_DEFER; return dev_err_probe(dev, ret, "failed to get battery thermal zone\n");
}
di->bat_ctrl = devm_iio_channel_get(dev, "bat_ctrl"); if (IS_ERR(di->bat_ctrl)) {
ret = dev_err_probe(dev, PTR_ERR(di->bat_ctrl), "failed to get BAT CTRL ADC channel\n"); return ret;
}
/* Init work for measuring temperature periodically */
INIT_DEFERRABLE_WORK(&di->btemp_periodic_work,
ab8500_btemp_periodic_work);
/* Set BTEMP thermal limits. Low and Med are fixed */
di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
ret = abx500_get_register_interruptible(dev, AB8500_CHARGER,
AB8500_BTEMP_HIGH_TH, &val); if (ret < 0) {
dev_err(dev, "%s ab8500 read failed\n", __func__); return ret;
} switch (val) { case BTEMP_HIGH_TH_57_0: case BTEMP_HIGH_TH_57_1:
di->btemp_ranges.btemp_high_limit =
BTEMP_THERMAL_HIGH_LIMIT_57; break; case BTEMP_HIGH_TH_52:
di->btemp_ranges.btemp_high_limit =
BTEMP_THERMAL_HIGH_LIMIT_52; break; case BTEMP_HIGH_TH_62:
di->btemp_ranges.btemp_high_limit =
BTEMP_THERMAL_HIGH_LIMIT_62; break;
}
/* Register BTEMP power supply class */
di->btemp_psy = devm_power_supply_register(dev, &ab8500_btemp_desc,
&psy_cfg); if (IS_ERR(di->btemp_psy)) {
dev_err(dev, "failed to register BTEMP psy\n"); return PTR_ERR(di->btemp_psy);
}
/* Register interrupts */ for (i = 0; i < ARRAY_SIZE(ab8500_btemp_irq); i++) {
irq = platform_get_irq_byname(pdev, ab8500_btemp_irq[i].name); if (irq < 0) return irq;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.