Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

power: supply: ab8500: Standardize BTI resistance

The Battery Type Indicator (BTI) resistor is a resistor mounted
between a special terminal on the battery and ground. By sending
a fixed current (such as 7mA) through this resistor and measuring
the voltage over it, the resistance can be determined, and this
verifies the battery type.

Typical side view of the battery:

o o o
GND BTI +3.8V

Typical example of the electrical layout:

+3.8 V BTI
| |
| + |
_______ [ ] 7kOhm
___ |
| |
| |
GND GND

By verifying this resistance before attempting to charge the
battery we add an additional level of security.

In some systems this is used for plug-and-play of batteries with
different capacity. In other cases, this is merely used to verify
that the right type of battery is connected, if several batteries
have the same physical shape and can be plugged into the same
slot. Sometimes this is just a surplus security mechanism.

Nokia and Samsung among many other vendors are known to use these
BTI resistors.

Add the BTI properties to struct power_supply_battery_info and
switch the AB8500 charger code over to using it.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>

authored by

Linus Walleij and committed by
Sebastian Reichel
1f918e0f 0e8b903b

+51 -32
-12
drivers/power/supply/ab8500-bm.h
··· 328 328 }; 329 329 330 330 /** 331 - * struct ab8500_battery_type - different batteries supported 332 - * @resis_high: battery upper resistance limit 333 - * @resis_low: battery lower resistance limit 334 - */ 335 - struct ab8500_battery_type { 336 - int resis_high; 337 - int resis_low; 338 - }; 339 - 340 - /** 341 331 * struct ab8500_bm_capacity_levels - ab8500 capacity level data 342 332 * @critical: critical capacity level in percent 343 333 * @low: low capacity level in percent ··· 377 387 * @temp_hysteresis temperature hysteresis 378 388 * @maxi maximization parameters 379 389 * @cap_levels capacity in percent for the different capacity levels 380 - * @bat_type table of supported battery types 381 390 * @chg_params charger parameters 382 391 * @fg_params fuel gauge parameters 383 392 */ ··· 399 410 int temp_hysteresis; 400 411 const struct ab8500_maxim_parameters *maxi; 401 412 const struct ab8500_bm_capacity_levels *cap_levels; 402 - struct ab8500_battery_type *bat_type; 403 413 const struct ab8500_bm_charger_parameters *chg_params; 404 414 const struct ab8500_fg_parameters *fg_params; 405 415 };
+7 -7
drivers/power/supply/ab8500_bmdata.c
··· 73 73 } 74 74 }; 75 75 76 - /* Default battery type for reference designs is the unknown type */ 77 - static struct ab8500_battery_type bat_type_thermistor_unknown = { 78 - .resis_high = 0, 79 - .resis_low = 0, 80 - }; 81 - 82 76 static const struct ab8500_bm_capacity_levels cap_levels = { 83 77 .critical = 2, 84 78 .low = 10, ··· 130 136 .enable_overshoot = false, 131 137 .fg_res = 100, 132 138 .cap_levels = &cap_levels, 133 - .bat_type = &bat_type_thermistor_unknown, 134 139 .interval_charging = 5, 135 140 .interval_not_charging = 120, 136 141 .maxi = &ab8500_maxi_params, ··· 205 212 bi->factory_internal_resistance_uohm = 300000; 206 213 bi->resist_table = temp_to_batres_tbl_thermistor; 207 214 bi->resist_table_size = ARRAY_SIZE(temp_to_batres_tbl_thermistor); 215 + } 216 + 217 + /* The default battery is emulated by a resistor at 7K */ 218 + if (bi->bti_resistance_ohm < 0 || 219 + bi->bti_resistance_tolerance < 0) { 220 + bi->bti_resistance_ohm = 7000; 221 + bi->bti_resistance_tolerance = 20; 208 222 } 209 223 210 224 if (!bi->ocv_table[0]) {
+6 -8
drivers/power/supply/ab8500_btemp.c
··· 237 237 */ 238 238 static int ab8500_btemp_id(struct ab8500_btemp *di) 239 239 { 240 + struct power_supply_battery_info *bi = di->bm->bi; 240 241 int res; 241 - u8 i; 242 242 243 243 di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA; 244 244 ··· 248 248 return -ENXIO; 249 249 } 250 250 251 - if ((res <= di->bm->bat_type->resis_high) && 252 - (res >= di->bm->bat_type->resis_low)) { 253 - dev_info(di->dev, "Battery detected on BATTEMP" 254 - " low %d < res %d < high: %d" 255 - " index: %d\n", 256 - di->bm->bat_type->resis_low, res, 257 - di->bm->bat_type->resis_high, i); 251 + if (power_supply_battery_bti_in_range(bi, res)) { 252 + dev_info(di->dev, "Battery detected on BATCTRL (pin C3)" 253 + " resistance %d Ohm = %d Ohm +/- %d%%\n", 254 + res, bi->bti_resistance_ohm, 255 + bi->bti_resistance_tolerance); 258 256 } else { 259 257 dev_warn(di->dev, "Battery identified as unknown" 260 258 ", resistance %d Ohm\n", res);
-4
drivers/power/supply/ab8500_fg.c
··· 2241 2241 if (!di->flags.batt_id_received && 2242 2242 (bi && (bi->technology != 2243 2243 POWER_SUPPLY_TECHNOLOGY_UNKNOWN))) { 2244 - const struct ab8500_battery_type *b; 2245 - 2246 - b = di->bm->bat_type; 2247 - 2248 2244 di->flags.batt_id_received = true; 2249 2245 2250 2246 di->bat_cap.max_mah_design =
+25 -1
drivers/power/supply/power_supply_core.c
··· 607 607 info->temp_min = INT_MIN; 608 608 info->temp_max = INT_MAX; 609 609 info->factory_internal_resistance_uohm = -EINVAL; 610 - info->resist_table = NULL; 610 + info->resist_table = NULL; 611 + info->bti_resistance_ohm = -EINVAL; 612 + info->bti_resistance_tolerance = -EINVAL; 611 613 612 614 for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) { 613 615 info->ocv_table[index] = NULL; ··· 939 937 return power_supply_ocv2cap_simple(table, table_len, ocv); 940 938 } 941 939 EXPORT_SYMBOL_GPL(power_supply_batinfo_ocv2cap); 940 + 941 + bool power_supply_battery_bti_in_range(struct power_supply_battery_info *info, 942 + int resistance) 943 + { 944 + int low, high; 945 + 946 + /* Nothing like this can be checked */ 947 + if (info->bti_resistance_ohm <= 0) 948 + return false; 949 + 950 + /* This will be extremely strict and unlikely to work */ 951 + if (info->bti_resistance_tolerance <= 0) 952 + return (info->bti_resistance_ohm == resistance); 953 + 954 + low = info->bti_resistance_ohm - 955 + (info->bti_resistance_ohm * info->bti_resistance_tolerance) / 100; 956 + high = info->bti_resistance_ohm + 957 + (info->bti_resistance_ohm * info->bti_resistance_tolerance) / 100; 958 + 959 + return ((resistance >= low) && (resistance <= high)); 960 + } 961 + EXPORT_SYMBOL_GPL(power_supply_battery_bti_in_range); 942 962 943 963 int power_supply_get_property(struct power_supply *psy, 944 964 enum power_supply_property psp,
+13
include/linux/power_supply.h
··· 498 498 * by temperature: highest temperature with lowest resistance first, lowest 499 499 * temperature with highest resistance last. 500 500 * @resist_table_size: the number of items in the resist_table. 501 + * @bti_resistance_ohm: The Battery Type Indicator (BIT) nominal resistance 502 + * in ohms for this battery, if an identification resistor is mounted 503 + * between a third battery terminal and ground. This scheme is used by a lot 504 + * of mobile device batteries. 505 + * @bti_resistance_tolerance: The tolerance in percent of the BTI resistance, 506 + * for example 10 for +/- 10%, if the bti_resistance is set to 7000 and the 507 + * tolerance is 10% we will detect a proper battery if the BTI resistance 508 + * is between 6300 and 7700 Ohm. 501 509 * 502 510 * This is the recommended struct to manage static battery parameters, 503 511 * populated by power_supply_get_battery_info(). Most platform drivers should ··· 632 624 int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX]; 633 625 struct power_supply_resistance_temp_table *resist_table; 634 626 int resist_table_size; 627 + int bti_resistance_ohm; 628 + int bti_resistance_tolerance; 635 629 }; 636 630 637 631 extern struct atomic_notifier_head power_supply_notifier; ··· 677 667 int table_len, int temp); 678 668 extern struct power_supply_maintenance_charge_table * 679 669 power_supply_get_maintenance_charging_setting(struct power_supply_battery_info *info, int index); 670 + extern bool power_supply_battery_bti_in_range(struct power_supply_battery_info *info, 671 + int resistance); 680 672 extern void power_supply_changed(struct power_supply *psy); 681 673 extern int power_supply_am_i_supplied(struct power_supply *psy); 682 674 int power_supply_get_property_from_supplier(struct power_supply *psy, ··· 695 683 696 684 return (mt != NULL); 697 685 } 686 + 698 687 699 688 #ifdef CONFIG_POWER_SUPPLY 700 689 extern int power_supply_is_system_supplied(void);