1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * ROHM BD99954 charger driver
- *
- * Copyright (C) 2020 Rohm Semiconductors
- * Originally written by:
- * Mikko Mutanen <[email protected]>
- * Markus Laine <[email protected]>
- * Bugs added by:
- * Matti Vaittinen <[email protected]>
- */
- /*
- * The battery charging profile of BD99954.
- *
- * Curve (1) represents charging current.
- * Curve (2) represents battery voltage.
- *
- * The BD99954 data sheet divides charging to three phases.
- * a) Trickle-charge with constant current (8).
- * b) pre-charge with constant current (6)
- * c) fast-charge, first with constant current (5) phase. After
- * the battery voltage has reached target level (4) we have constant
- * voltage phase until charging current has dropped to termination
- * level (7)
- *
- * V ^ ^ I
- * . .
- * . .
- *(4)` `.` ` ` ` ` ` ` ` ` ` ` ` ` ` ----------------------------.
- * . :/ .
- * . o----+/:/ ` ` ` ` ` ` ` ` ` ` ` ` `.` ` (5)
- * . + :: + .
- * . + /- -- .
- * . +`/- + .
- * . o/- -: .
- * . .s. +` .
- * . .--+ `/ .
- * . ..`` + .: .
- * . -` + -- .
- * . (2) ...`` + :- .
- * . ...`` + -: .
- *(3)` `.`."" ` ` ` `+-------- ` ` ` ` ` ` `.:` ` ` ` ` ` ` ` ` .` ` (6)
- * . + `:. .
- * . + -: .
- * . + -:. .
- * . + .--. .
- * . (1) + `.+` ` ` `.` ` (7)
- * -..............` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` + ` ` ` .` ` (8)
- * . + -
- * -------------------------------------------------+++++++++-->
- * | trickle | pre | fast |
- *
- * Details of DT properties for different limits can be found from BD99954
- * device tree binding documentation.
- */
- #include <linux/delay.h>
- #include <linux/gpio/consumer.h>
- #include <linux/interrupt.h>
- #include <linux/i2c.h>
- #include <linux/kernel.h>
- #include <linux/linear_range.h>
- #include <linux/module.h>
- #include <linux/mod_devicetable.h>
- #include <linux/power_supply.h>
- #include <linux/property.h>
- #include <linux/regmap.h>
- #include <linux/types.h>
- #include "bd99954-charger.h"
- struct battery_data {
- u16 precharge_current; /* Trickle-charge Current */
- u16 fc_reg_voltage; /* Fast Charging Regulation Voltage */
- u16 voltage_min;
- u16 voltage_max;
- };
- /* Initial field values, converted to initial register values */
- struct bd9995x_init_data {
- u16 vsysreg_set; /* VSYS Regulation Setting */
- u16 ibus_lim_set; /* VBUS input current limitation */
- u16 icc_lim_set; /* VCC/VACP Input Current Limit Setting */
- u16 itrich_set; /* Trickle-charge Current Setting */
- u16 iprech_set; /* Pre-Charge Current Setting */
- u16 ichg_set; /* Fast-Charge constant current */
- u16 vfastchg_reg_set1; /* Fast Charging Regulation Voltage */
- u16 vprechg_th_set; /* Pre-charge Voltage Threshold Setting */
- u16 vrechg_set; /* Re-charge Battery Voltage Setting */
- u16 vbatovp_set; /* Battery Over Voltage Threshold Setting */
- u16 iterm_set; /* Charging termination current */
- };
- struct bd9995x_state {
- u8 online;
- u16 chgstm_status;
- u16 vbat_vsys_status;
- u16 vbus_vcc_status;
- };
- struct bd9995x_device {
- struct i2c_client *client;
- struct device *dev;
- struct power_supply *charger;
- struct regmap *rmap;
- struct regmap_field *rmap_fields[F_MAX_FIELDS];
- int chip_id;
- int chip_rev;
- struct bd9995x_init_data init_data;
- struct bd9995x_state state;
- struct mutex lock; /* Protect state data */
- };
- static const struct regmap_range bd9995x_readonly_reg_ranges[] = {
- regmap_reg_range(CHGSTM_STATUS, SEL_ILIM_VAL),
- regmap_reg_range(IOUT_DACIN_VAL, IOUT_DACIN_VAL),
- regmap_reg_range(VCC_UCD_STATUS, VCC_IDD_STATUS),
- regmap_reg_range(VBUS_UCD_STATUS, VBUS_IDD_STATUS),
- regmap_reg_range(CHIP_ID, CHIP_REV),
- regmap_reg_range(SYSTEM_STATUS, SYSTEM_STATUS),
- regmap_reg_range(IBATP_VAL, VBAT_AVE_VAL),
- regmap_reg_range(VTH_VAL, EXTIADP_AVE_VAL),
- };
- static const struct regmap_access_table bd9995x_writeable_regs = {
- .no_ranges = bd9995x_readonly_reg_ranges,
- .n_no_ranges = ARRAY_SIZE(bd9995x_readonly_reg_ranges),
- };
- static const struct regmap_range bd9995x_volatile_reg_ranges[] = {
- regmap_reg_range(CHGSTM_STATUS, WDT_STATUS),
- regmap_reg_range(VCC_UCD_STATUS, VCC_IDD_STATUS),
- regmap_reg_range(VBUS_UCD_STATUS, VBUS_IDD_STATUS),
- regmap_reg_range(INT0_STATUS, INT7_STATUS),
- regmap_reg_range(SYSTEM_STATUS, SYSTEM_CTRL_SET),
- regmap_reg_range(IBATP_VAL, EXTIADP_AVE_VAL), /* Measurement regs */
- };
- static const struct regmap_access_table bd9995x_volatile_regs = {
- .yes_ranges = bd9995x_volatile_reg_ranges,
- .n_yes_ranges = ARRAY_SIZE(bd9995x_volatile_reg_ranges),
- };
- static const struct regmap_range_cfg regmap_range_cfg[] = {
- {
- .selector_reg = MAP_SET,
- .selector_mask = 0xFFFF,
- .selector_shift = 0,
- .window_start = 0,
- .window_len = 0x100,
- .range_min = 0 * 0x100,
- .range_max = 3 * 0x100,
- },
- };
- static const struct regmap_config bd9995x_regmap_config = {
- .reg_bits = 8,
- .val_bits = 16,
- .reg_stride = 1,
- .max_register = 3 * 0x100,
- .cache_type = REGCACHE_RBTREE,
- .ranges = regmap_range_cfg,
- .num_ranges = ARRAY_SIZE(regmap_range_cfg),
- .val_format_endian = REGMAP_ENDIAN_LITTLE,
- .wr_table = &bd9995x_writeable_regs,
- .volatile_table = &bd9995x_volatile_regs,
- };
- enum bd9995x_chrg_fault {
- CHRG_FAULT_NORMAL,
- CHRG_FAULT_INPUT,
- CHRG_FAULT_THERMAL_SHUTDOWN,
- CHRG_FAULT_TIMER_EXPIRED,
- };
- static int bd9995x_get_prop_batt_health(struct bd9995x_device *bd)
- {
- int ret, tmp;
- ret = regmap_field_read(bd->rmap_fields[F_BATTEMP], &tmp);
- if (ret)
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- /* TODO: Check these against datasheet page 34 */
- switch (tmp) {
- case ROOM:
- return POWER_SUPPLY_HEALTH_GOOD;
- case HOT1:
- case HOT2:
- case HOT3:
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- case COLD1:
- case COLD2:
- return POWER_SUPPLY_HEALTH_COLD;
- case TEMP_DIS:
- case BATT_OPEN:
- default:
- return POWER_SUPPLY_HEALTH_UNKNOWN;
- }
- }
- static int bd9995x_get_prop_charge_type(struct bd9995x_device *bd)
- {
- int ret, tmp;
- ret = regmap_field_read(bd->rmap_fields[F_CHGSTM_STATE], &tmp);
- if (ret)
- return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
- switch (tmp) {
- case CHGSTM_TRICKLE_CHARGE:
- case CHGSTM_PRE_CHARGE:
- return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
- case CHGSTM_FAST_CHARGE:
- return POWER_SUPPLY_CHARGE_TYPE_FAST;
- case CHGSTM_TOP_OFF:
- case CHGSTM_DONE:
- case CHGSTM_SUSPEND:
- return POWER_SUPPLY_CHARGE_TYPE_NONE;
- default: /* Rest of the states are error related, no charging */
- return POWER_SUPPLY_CHARGE_TYPE_NONE;
- }
- }
- static bool bd9995x_get_prop_batt_present(struct bd9995x_device *bd)
- {
- int ret, tmp;
- ret = regmap_field_read(bd->rmap_fields[F_BATTEMP], &tmp);
- if (ret)
- return false;
- return tmp != BATT_OPEN;
- }
- static int bd9995x_get_prop_batt_voltage(struct bd9995x_device *bd)
- {
- int ret, tmp;
- ret = regmap_field_read(bd->rmap_fields[F_VBAT_VAL], &tmp);
- if (ret)
- return 0;
- tmp = min(tmp, 19200);
- return tmp * 1000;
- }
- static int bd9995x_get_prop_batt_current(struct bd9995x_device *bd)
- {
- int ret, tmp;
- ret = regmap_field_read(bd->rmap_fields[F_IBATP_VAL], &tmp);
- if (ret)
- return 0;
- return tmp * 1000;
- }
- #define DEFAULT_BATTERY_TEMPERATURE 250
- static int bd9995x_get_prop_batt_temp(struct bd9995x_device *bd)
- {
- int ret, tmp;
- ret = regmap_field_read(bd->rmap_fields[F_THERM_VAL], &tmp);
- if (ret)
- return DEFAULT_BATTERY_TEMPERATURE;
- return (200 - tmp) * 10;
- }
- static int bd9995x_power_supply_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
- {
- int ret, tmp;
- struct bd9995x_device *bd = power_supply_get_drvdata(psy);
- struct bd9995x_state state;
- mutex_lock(&bd->lock);
- state = bd->state;
- mutex_unlock(&bd->lock);
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- switch (state.chgstm_status) {
- case CHGSTM_TRICKLE_CHARGE:
- case CHGSTM_PRE_CHARGE:
- case CHGSTM_FAST_CHARGE:
- case CHGSTM_TOP_OFF:
- val->intval = POWER_SUPPLY_STATUS_CHARGING;
- break;
- case CHGSTM_DONE:
- val->intval = POWER_SUPPLY_STATUS_FULL;
- break;
- case CHGSTM_SUSPEND:
- case CHGSTM_TEMPERATURE_ERROR_1:
- case CHGSTM_TEMPERATURE_ERROR_2:
- case CHGSTM_TEMPERATURE_ERROR_3:
- case CHGSTM_TEMPERATURE_ERROR_4:
- case CHGSTM_TEMPERATURE_ERROR_5:
- case CHGSTM_TEMPERATURE_ERROR_6:
- case CHGSTM_TEMPERATURE_ERROR_7:
- case CHGSTM_THERMAL_SHUT_DOWN_1:
- case CHGSTM_THERMAL_SHUT_DOWN_2:
- case CHGSTM_THERMAL_SHUT_DOWN_3:
- case CHGSTM_THERMAL_SHUT_DOWN_4:
- case CHGSTM_THERMAL_SHUT_DOWN_5:
- case CHGSTM_THERMAL_SHUT_DOWN_6:
- case CHGSTM_THERMAL_SHUT_DOWN_7:
- case CHGSTM_BATTERY_ERROR:
- val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
- break;
- default:
- val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
- break;
- }
- break;
- case POWER_SUPPLY_PROP_MANUFACTURER:
- val->strval = BD9995X_MANUFACTURER;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = state.online;
- break;
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
- ret = regmap_field_read(bd->rmap_fields[F_IBATP_VAL], &tmp);
- if (ret)
- return ret;
- val->intval = tmp * 1000;
- break;
- case POWER_SUPPLY_PROP_CHARGE_AVG:
- ret = regmap_field_read(bd->rmap_fields[F_IBATP_AVE_VAL], &tmp);
- if (ret)
- return ret;
- val->intval = tmp * 1000;
- break;
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- /*
- * Currently the DT uses this property to give the
- * target current for fast-charging constant current phase.
- * I think it is correct in a sense.
- *
- * Yet, this prop we read and return here is the programmed
- * safety limit for combined input currents. This feels
- * also correct in a sense.
- *
- * However, this results a mismatch to DT value and value
- * read from sysfs.
- */
- ret = regmap_field_read(bd->rmap_fields[F_SEL_ILIM_VAL], &tmp);
- if (ret)
- return ret;
- val->intval = tmp * 1000;
- break;
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
- if (!state.online) {
- val->intval = 0;
- break;
- }
- ret = regmap_field_read(bd->rmap_fields[F_VFASTCHG_REG_SET1],
- &tmp);
- if (ret)
- return ret;
- /*
- * The actual range : 2560 to 19200 mV. No matter what the
- * register says
- */
- val->intval = clamp_val(tmp << 4, 2560, 19200);
- val->intval *= 1000;
- break;
- case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
- ret = regmap_field_read(bd->rmap_fields[F_ITERM_SET], &tmp);
- if (ret)
- return ret;
- /* Start step is 64 mA */
- val->intval = tmp << 6;
- /* Maximum is 1024 mA - no matter what register says */
- val->intval = min(val->intval, 1024);
- val->intval *= 1000;
- break;
- /* Battery properties which we access through charger */
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = bd9995x_get_prop_batt_present(bd);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = bd9995x_get_prop_batt_voltage(bd);
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = bd9995x_get_prop_batt_current(bd);
- break;
- case POWER_SUPPLY_PROP_CHARGE_TYPE:
- val->intval = bd9995x_get_prop_charge_type(bd);
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = bd9995x_get_prop_batt_health(bd);
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = bd9995x_get_prop_batt_temp(bd);
- break;
- case POWER_SUPPLY_PROP_TECHNOLOGY:
- val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
- break;
- case POWER_SUPPLY_PROP_MODEL_NAME:
- val->strval = "bd99954";
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- static int bd9995x_get_chip_state(struct bd9995x_device *bd,
- struct bd9995x_state *state)
- {
- int i, ret, tmp;
- struct {
- struct regmap_field *id;
- u16 *data;
- } state_fields[] = {
- {
- bd->rmap_fields[F_CHGSTM_STATE], &state->chgstm_status,
- }, {
- bd->rmap_fields[F_VBAT_VSYS_STATUS],
- &state->vbat_vsys_status,
- }, {
- bd->rmap_fields[F_VBUS_VCC_STATUS],
- &state->vbus_vcc_status,
- },
- };
- for (i = 0; i < ARRAY_SIZE(state_fields); i++) {
- ret = regmap_field_read(state_fields[i].id, &tmp);
- if (ret)
- return ret;
- *state_fields[i].data = tmp;
- }
- if (state->vbus_vcc_status & STATUS_VCC_DET ||
- state->vbus_vcc_status & STATUS_VBUS_DET)
- state->online = 1;
- else
- state->online = 0;
- return 0;
- }
- static irqreturn_t bd9995x_irq_handler_thread(int irq, void *private)
- {
- struct bd9995x_device *bd = private;
- int ret, status, mask, i;
- unsigned long tmp;
- struct bd9995x_state state;
- /*
- * The bd9995x does not seem to generate big amount of interrupts.
- * The logic regarding which interrupts can cause relevant
- * status changes seem to be pretty complex.
- *
- * So lets implement really simple and hopefully bullet-proof handler:
- * It does not really matter which IRQ we handle, we just go and
- * re-read all interesting statuses + give the framework a nudge.
- *
- * Other option would be building a _complex_ and error prone logic
- * trying to decide what could have been changed (resulting this IRQ
- * we are now handling). During the normal operation the BD99954 does
- * not seem to be generating much of interrupts so benefit from such
- * logic would probably be minimal.
- */
- ret = regmap_read(bd->rmap, INT0_STATUS, &status);
- if (ret) {
- dev_err(bd->dev, "Failed to read IRQ status\n");
- return IRQ_NONE;
- }
- ret = regmap_field_read(bd->rmap_fields[F_INT0_SET], &mask);
- if (ret) {
- dev_err(bd->dev, "Failed to read IRQ mask\n");
- return IRQ_NONE;
- }
- /* Handle only IRQs that are not masked */
- status &= mask;
- tmp = status;
- /* Lowest bit does not represent any sub-registers */
- tmp >>= 1;
- /*
- * Mask and ack IRQs we will handle (+ the idiot bit)
- */
- ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], 0);
- if (ret) {
- dev_err(bd->dev, "Failed to mask F_INT0\n");
- return IRQ_NONE;
- }
- ret = regmap_write(bd->rmap, INT0_STATUS, status);
- if (ret) {
- dev_err(bd->dev, "Failed to ack F_INT0\n");
- goto err_umask;
- }
- for_each_set_bit(i, &tmp, 7) {
- int sub_status, sub_mask;
- int sub_status_reg[] = {
- INT1_STATUS, INT2_STATUS, INT3_STATUS, INT4_STATUS,
- INT5_STATUS, INT6_STATUS, INT7_STATUS,
- };
- struct regmap_field *sub_mask_f[] = {
- bd->rmap_fields[F_INT1_SET],
- bd->rmap_fields[F_INT2_SET],
- bd->rmap_fields[F_INT3_SET],
- bd->rmap_fields[F_INT4_SET],
- bd->rmap_fields[F_INT5_SET],
- bd->rmap_fields[F_INT6_SET],
- bd->rmap_fields[F_INT7_SET],
- };
- /* Clear sub IRQs */
- ret = regmap_read(bd->rmap, sub_status_reg[i], &sub_status);
- if (ret) {
- dev_err(bd->dev, "Failed to read IRQ sub-status\n");
- goto err_umask;
- }
- ret = regmap_field_read(sub_mask_f[i], &sub_mask);
- if (ret) {
- dev_err(bd->dev, "Failed to read IRQ sub-mask\n");
- goto err_umask;
- }
- /* Ack active sub-statuses */
- sub_status &= sub_mask;
- ret = regmap_write(bd->rmap, sub_status_reg[i], sub_status);
- if (ret) {
- dev_err(bd->dev, "Failed to ack sub-IRQ\n");
- goto err_umask;
- }
- }
- ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], mask);
- if (ret)
- /* May as well retry once */
- goto err_umask;
- /* Read whole chip state */
- ret = bd9995x_get_chip_state(bd, &state);
- if (ret < 0) {
- dev_err(bd->dev, "Failed to read chip state\n");
- } else {
- mutex_lock(&bd->lock);
- bd->state = state;
- mutex_unlock(&bd->lock);
- power_supply_changed(bd->charger);
- }
- return IRQ_HANDLED;
- err_umask:
- ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], mask);
- if (ret)
- dev_err(bd->dev,
- "Failed to un-mask F_INT0 - IRQ permanently disabled\n");
- return IRQ_NONE;
- }
- static int __bd9995x_chip_reset(struct bd9995x_device *bd)
- {
- int ret, state;
- int rst_check_counter = 10;
- u16 tmp = ALLRST | OTPLD;
- ret = regmap_raw_write(bd->rmap, SYSTEM_CTRL_SET, &tmp, 2);
- if (ret < 0)
- return ret;
- do {
- ret = regmap_field_read(bd->rmap_fields[F_OTPLD_STATE], &state);
- if (ret)
- return ret;
- msleep(10);
- } while (state == 0 && --rst_check_counter);
- if (!rst_check_counter) {
- dev_err(bd->dev, "chip reset not completed\n");
- return -ETIMEDOUT;
- }
- tmp = 0;
- ret = regmap_raw_write(bd->rmap, SYSTEM_CTRL_SET, &tmp, 2);
- return ret;
- }
- static int bd9995x_hw_init(struct bd9995x_device *bd)
- {
- int ret;
- int i;
- struct bd9995x_state state;
- struct bd9995x_init_data *id = &bd->init_data;
- const struct {
- enum bd9995x_fields id;
- u16 value;
- } init_data[] = {
- /* Enable the charging trigger after SDP charger attached */
- {F_SDP_CHG_TRIG_EN, 1},
- /* Enable charging trigger after SDP charger attached */
- {F_SDP_CHG_TRIG, 1},
- /* Disable charging trigger by BC1.2 detection */
- {F_VBUS_BC_DISEN, 1},
- /* Disable charging trigger by BC1.2 detection */
- {F_VCC_BC_DISEN, 1},
- /* Disable automatic limitation of the input current */
- {F_ILIM_AUTO_DISEN, 1},
- /* Select current limitation when SDP charger attached*/
- {F_SDP_500_SEL, 1},
- /* Select current limitation when DCP charger attached */
- {F_DCP_2500_SEL, 1},
- {F_VSYSREG_SET, id->vsysreg_set},
- /* Activate USB charging and DC/DC converter */
- {F_USB_SUS, 0},
- /* DCDC clock: 1200 kHz*/
- {F_DCDC_CLK_SEL, 3},
- /* Enable charging */
- {F_CHG_EN, 1},
- /* Disable Input current Limit setting voltage measurement */
- {F_EXTIADPEN, 0},
- /* Disable input current limiting */
- {F_VSYS_PRIORITY, 1},
- {F_IBUS_LIM_SET, id->ibus_lim_set},
- {F_ICC_LIM_SET, id->icc_lim_set},
- /* Charge Termination Current Setting to 0*/
- {F_ITERM_SET, id->iterm_set},
- /* Trickle-charge Current Setting */
- {F_ITRICH_SET, id->itrich_set},
- /* Pre-charge Current setting */
- {F_IPRECH_SET, id->iprech_set},
- /* Fast Charge Current for constant current phase */
- {F_ICHG_SET, id->ichg_set},
- /* Fast Charge Voltage Regulation Setting */
- {F_VFASTCHG_REG_SET1, id->vfastchg_reg_set1},
- /* Set Pre-charge Voltage Threshold for trickle charging. */
- {F_VPRECHG_TH_SET, id->vprechg_th_set},
- {F_VRECHG_SET, id->vrechg_set},
- {F_VBATOVP_SET, id->vbatovp_set},
- /* Reverse buck boost voltage Setting */
- {F_VRBOOST_SET, 0},
- /* Disable fast-charging watchdog */
- {F_WDT_FST, 0},
- /* Disable pre-charging watchdog */
- {F_WDT_PRE, 0},
- /* Power save off */
- {F_POWER_SAVE_MODE, 0},
- {F_INT1_SET, INT1_ALL},
- {F_INT2_SET, INT2_ALL},
- {F_INT3_SET, INT3_ALL},
- {F_INT4_SET, INT4_ALL},
- {F_INT5_SET, INT5_ALL},
- {F_INT6_SET, INT6_ALL},
- {F_INT7_SET, INT7_ALL},
- };
- /*
- * Currently we initialize charger to a known state at startup.
- * If we want to allow for example the boot code to initialize
- * charger we should get rid of this.
- */
- ret = __bd9995x_chip_reset(bd);
- if (ret < 0)
- return ret;
- /* Initialize currents/voltages and other parameters */
- for (i = 0; i < ARRAY_SIZE(init_data); i++) {
- ret = regmap_field_write(bd->rmap_fields[init_data[i].id],
- init_data[i].value);
- if (ret) {
- dev_err(bd->dev, "failed to initialize charger (%d)\n",
- ret);
- return ret;
- }
- }
- ret = bd9995x_get_chip_state(bd, &state);
- if (ret < 0)
- return ret;
- mutex_lock(&bd->lock);
- bd->state = state;
- mutex_unlock(&bd->lock);
- return 0;
- }
- static enum power_supply_property bd9995x_power_supply_props[] = {
- POWER_SUPPLY_PROP_MANUFACTURER,
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
- POWER_SUPPLY_PROP_CHARGE_AVG,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
- POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
- /* Battery props we access through charger */
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_CHARGE_TYPE,
- POWER_SUPPLY_PROP_HEALTH,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_TECHNOLOGY,
- POWER_SUPPLY_PROP_MODEL_NAME,
- };
- static const struct power_supply_desc bd9995x_power_supply_desc = {
- .name = "bd9995x-charger",
- .type = POWER_SUPPLY_TYPE_USB,
- .properties = bd9995x_power_supply_props,
- .num_properties = ARRAY_SIZE(bd9995x_power_supply_props),
- .get_property = bd9995x_power_supply_get_property,
- };
- /*
- * Limit configurations for vbus-input-current and vcc-vacp-input-current
- * Minimum limit is 0 uA. Max is 511 * 32000 uA = 16352000 uA. This is
- * configured by writing a register so that each increment in register
- * value equals to 32000 uA limit increment.
- *
- * Eg, value 0x0 is limit 0, value 0x1 is limit 32000, ...
- * Describe the setting in linear_range table.
- */
- static const struct linear_range input_current_limit_ranges[] = {
- {
- .min = 0,
- .step = 32000,
- .min_sel = 0x0,
- .max_sel = 0x1ff,
- },
- };
- /* Possible trickle, pre-charging and termination current values */
- static const struct linear_range charging_current_ranges[] = {
- {
- .min = 0,
- .step = 64000,
- .min_sel = 0x0,
- .max_sel = 0x10,
- }, {
- .min = 1024000,
- .step = 0,
- .min_sel = 0x11,
- .max_sel = 0x1f,
- },
- };
- /*
- * Fast charging voltage regulation, starting re-charging limit
- * and battery over voltage protection have same possible values
- */
- static const struct linear_range charge_voltage_regulation_ranges[] = {
- {
- .min = 2560000,
- .step = 0,
- .min_sel = 0,
- .max_sel = 0xA0,
- }, {
- .min = 2560000,
- .step = 16000,
- .min_sel = 0xA0,
- .max_sel = 0x4B0,
- }, {
- .min = 19200000,
- .step = 0,
- .min_sel = 0x4B0,
- .max_sel = 0x7FF,
- },
- };
- /* Possible VSYS voltage regulation values */
- static const struct linear_range vsys_voltage_regulation_ranges[] = {
- {
- .min = 2560000,
- .step = 0,
- .min_sel = 0,
- .max_sel = 0x28,
- }, {
- .min = 2560000,
- .step = 64000,
- .min_sel = 0x28,
- .max_sel = 0x12C,
- }, {
- .min = 19200000,
- .step = 0,
- .min_sel = 0x12C,
- .max_sel = 0x1FF,
- },
- };
- /* Possible settings for switching from trickle to pre-charging limits */
- static const struct linear_range trickle_to_pre_threshold_ranges[] = {
- {
- .min = 2048000,
- .step = 0,
- .min_sel = 0,
- .max_sel = 0x20,
- }, {
- .min = 2048000,
- .step = 64000,
- .min_sel = 0x20,
- .max_sel = 0x12C,
- }, {
- .min = 19200000,
- .step = 0,
- .min_sel = 0x12C,
- .max_sel = 0x1FF
- }
- };
- /* Possible current values for fast-charging constant current phase */
- static const struct linear_range fast_charge_current_ranges[] = {
- {
- .min = 0,
- .step = 64000,
- .min_sel = 0,
- .max_sel = 0xFF,
- }
- };
- struct battery_init {
- const char *name;
- int *info_data;
- const struct linear_range *range;
- int ranges;
- u16 *data;
- };
- struct dt_init {
- char *prop;
- const struct linear_range *range;
- int ranges;
- u16 *data;
- };
- static int bd9995x_fw_probe(struct bd9995x_device *bd)
- {
- int ret;
- struct power_supply_battery_info *info;
- u32 property;
- int i;
- int regval;
- bool found;
- struct bd9995x_init_data *init = &bd->init_data;
- struct battery_init battery_inits[] = {
- {
- .name = "trickle-charging current",
- .range = &charging_current_ranges[0],
- .ranges = 2,
- .data = &init->itrich_set,
- }, {
- .name = "pre-charging current",
- .range = &charging_current_ranges[0],
- .ranges = 2,
- .data = &init->iprech_set,
- }, {
- .name = "pre-to-trickle charge voltage threshold",
- .range = &trickle_to_pre_threshold_ranges[0],
- .ranges = 2,
- .data = &init->vprechg_th_set,
- }, {
- .name = "charging termination current",
- .range = &charging_current_ranges[0],
- .ranges = 2,
- .data = &init->iterm_set,
- }, {
- .name = "charging re-start voltage",
- .range = &charge_voltage_regulation_ranges[0],
- .ranges = 2,
- .data = &init->vrechg_set,
- }, {
- .name = "battery overvoltage limit",
- .range = &charge_voltage_regulation_ranges[0],
- .ranges = 2,
- .data = &init->vbatovp_set,
- }, {
- .name = "fast-charging max current",
- .range = &fast_charge_current_ranges[0],
- .ranges = 1,
- .data = &init->ichg_set,
- }, {
- .name = "fast-charging voltage",
- .range = &charge_voltage_regulation_ranges[0],
- .ranges = 2,
- .data = &init->vfastchg_reg_set1,
- },
- };
- struct dt_init props[] = {
- {
- .prop = "rohm,vsys-regulation-microvolt",
- .range = &vsys_voltage_regulation_ranges[0],
- .ranges = 2,
- .data = &init->vsysreg_set,
- }, {
- .prop = "rohm,vbus-input-current-limit-microamp",
- .range = &input_current_limit_ranges[0],
- .ranges = 1,
- .data = &init->ibus_lim_set,
- }, {
- .prop = "rohm,vcc-input-current-limit-microamp",
- .range = &input_current_limit_ranges[0],
- .ranges = 1,
- .data = &init->icc_lim_set,
- },
- };
- /*
- * The power_supply_get_battery_info() does not support getting values
- * from ACPI. Let's fix it if ACPI is required here.
- */
- ret = power_supply_get_battery_info(bd->charger, &info);
- if (ret < 0)
- return ret;
- /* Put pointers to the generic battery info */
- battery_inits[0].info_data = &info->tricklecharge_current_ua;
- battery_inits[1].info_data = &info->precharge_current_ua;
- battery_inits[2].info_data = &info->precharge_voltage_max_uv;
- battery_inits[3].info_data = &info->charge_term_current_ua;
- battery_inits[4].info_data = &info->charge_restart_voltage_uv;
- battery_inits[5].info_data = &info->overvoltage_limit_uv;
- battery_inits[6].info_data = &info->constant_charge_current_max_ua;
- battery_inits[7].info_data = &info->constant_charge_voltage_max_uv;
- for (i = 0; i < ARRAY_SIZE(battery_inits); i++) {
- int val = *battery_inits[i].info_data;
- const struct linear_range *range = battery_inits[i].range;
- int ranges = battery_inits[i].ranges;
- if (val == -EINVAL)
- continue;
- ret = linear_range_get_selector_low_array(range, ranges, val,
- ®val, &found);
- if (ret) {
- dev_err(bd->dev, "Unsupported value for %s\n",
- battery_inits[i].name);
- power_supply_put_battery_info(bd->charger, info);
- return -EINVAL;
- }
- if (!found) {
- dev_warn(bd->dev,
- "Unsupported value for %s - using smaller\n",
- battery_inits[i].name);
- }
- *(battery_inits[i].data) = regval;
- }
- power_supply_put_battery_info(bd->charger, info);
- for (i = 0; i < ARRAY_SIZE(props); i++) {
- ret = device_property_read_u32(bd->dev, props[i].prop,
- &property);
- if (ret < 0) {
- dev_err(bd->dev, "failed to read %s", props[i].prop);
- return ret;
- }
- ret = linear_range_get_selector_low_array(props[i].range,
- props[i].ranges,
- property, ®val,
- &found);
- if (ret) {
- dev_err(bd->dev, "Unsupported value for '%s'\n",
- props[i].prop);
- return -EINVAL;
- }
- if (!found) {
- dev_warn(bd->dev,
- "Unsupported value for '%s' - using smaller\n",
- props[i].prop);
- }
- *(props[i].data) = regval;
- }
- return 0;
- }
- static void bd9995x_chip_reset(void *bd)
- {
- __bd9995x_chip_reset(bd);
- }
- static int bd9995x_probe(struct i2c_client *client)
- {
- struct device *dev = &client->dev;
- struct bd9995x_device *bd;
- struct power_supply_config psy_cfg = {};
- int ret;
- int i;
- bd = devm_kzalloc(dev, sizeof(*bd), GFP_KERNEL);
- if (!bd)
- return -ENOMEM;
- bd->client = client;
- bd->dev = dev;
- psy_cfg.drv_data = bd;
- psy_cfg.of_node = dev->of_node;
- mutex_init(&bd->lock);
- bd->rmap = devm_regmap_init_i2c(client, &bd9995x_regmap_config);
- if (IS_ERR(bd->rmap)) {
- dev_err(dev, "Failed to setup register access via i2c\n");
- return PTR_ERR(bd->rmap);
- }
- for (i = 0; i < ARRAY_SIZE(bd9995x_reg_fields); i++) {
- const struct reg_field *reg_fields = bd9995x_reg_fields;
- bd->rmap_fields[i] = devm_regmap_field_alloc(dev, bd->rmap,
- reg_fields[i]);
- if (IS_ERR(bd->rmap_fields[i])) {
- dev_err(dev, "cannot allocate regmap field\n");
- return PTR_ERR(bd->rmap_fields[i]);
- }
- }
- i2c_set_clientdata(client, bd);
- ret = regmap_field_read(bd->rmap_fields[F_CHIP_ID], &bd->chip_id);
- if (ret) {
- dev_err(dev, "Cannot read chip ID.\n");
- return ret;
- }
- if (bd->chip_id != BD99954_ID) {
- dev_err(dev, "Chip with ID=0x%x, not supported!\n",
- bd->chip_id);
- return -ENODEV;
- }
- ret = regmap_field_read(bd->rmap_fields[F_CHIP_REV], &bd->chip_rev);
- if (ret) {
- dev_err(dev, "Cannot read revision.\n");
- return ret;
- }
- dev_info(bd->dev, "Found BD99954 chip rev %d\n", bd->chip_rev);
- /*
- * We need to init the psy before we can call
- * power_supply_get_battery_info() for it
- */
- bd->charger = devm_power_supply_register(bd->dev,
- &bd9995x_power_supply_desc,
- &psy_cfg);
- if (IS_ERR(bd->charger)) {
- dev_err(dev, "Failed to register power supply\n");
- return PTR_ERR(bd->charger);
- }
- ret = bd9995x_fw_probe(bd);
- if (ret < 0) {
- dev_err(dev, "Cannot read device properties.\n");
- return ret;
- }
- ret = bd9995x_hw_init(bd);
- if (ret < 0) {
- dev_err(dev, "Cannot initialize the chip.\n");
- return ret;
- }
- ret = devm_add_action_or_reset(dev, bd9995x_chip_reset, bd);
- if (ret)
- return ret;
- return devm_request_threaded_irq(dev, client->irq, NULL,
- bd9995x_irq_handler_thread,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- BD9995X_IRQ_PIN, bd);
- }
- static const struct of_device_id bd9995x_of_match[] = {
- { .compatible = "rohm,bd99954", },
- { }
- };
- MODULE_DEVICE_TABLE(of, bd9995x_of_match);
- static struct i2c_driver bd9995x_driver = {
- .driver = {
- .name = "bd9995x-charger",
- .of_match_table = bd9995x_of_match,
- },
- .probe_new = bd9995x_probe,
- };
- module_i2c_driver(bd9995x_driver);
- MODULE_AUTHOR("Laine Markus <[email protected]>");
- MODULE_DESCRIPTION("ROHM BD99954 charger driver");
- MODULE_LICENSE("GPL");
|