From 750233099a3b484315674af206d0cf47b2f386ef Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Tue, 22 Apr 2025 14:29:06 +0800 Subject: [PATCH 01/17] power: supply: rk817_battery: Consider the effect of contact resistance Incorporate calculations for negative terminal contact resistance to minimize errors in thermistor resistance measurements, particularly deviations in high-temperature regions caused by voltage drops across the contact resistance. Change-Id: Iaae24e44649a31c3f81fd4f7e96293d6d14204f3 Signed-off-by: Shengfei Xu --- drivers/power/supply/rk817_battery.c | 283 ++++++++++++++++----------- 1 file changed, 168 insertions(+), 115 deletions(-) diff --git a/drivers/power/supply/rk817_battery.c b/drivers/power/supply/rk817_battery.c index 75cf99f8d068..953b6d8da6ea 100644 --- a/drivers/power/supply/rk817_battery.c +++ b/drivers/power/supply/rk817_battery.c @@ -96,6 +96,8 @@ module_param_named(dbg_level, dbg_enable, int, 0644); #define DEFAULT_MONITOR_SEC 5 #define DEFAULT_SAMPLE_RES 20 +#define CONTACT_RES_MAX 50 + /* sleep */ #define SLP_CURR_MAX 40 #define SLP_CURR_MIN 6 @@ -220,11 +222,7 @@ enum rk817_battery_fields { RELAX_VOL2_H, RELAX_VOL2_L, RELAX_CUR1_H, RELAX_CUR1_L, RELAX_CUR2_H, RELAX_CUR2_L, - OCV_THRE_VOL, - OCV_VOL_H, OCV_VOL_L, - OCV_VOL0_H, OCV_VOL0_L, - OCV_CUR_H, OCV_CUR_L, - OCV_CUR0_H, OCV_CUR0_L, + CHRG_BAT_TAB2, CHRG_INCC_ZERO, CHRG_BATHIAUTO_EN, PWRON_VOL_H, PWRON_VOL_L, PWRON_CUR_H, PWRON_CUR_L, OFF_CNT, @@ -244,16 +242,16 @@ enum rk817_battery_fields { VCALIB0_H, VCALIB0_L, VCALIB1_H, VCALIB1_L, IOFFSET_H, IOFFSET_L, - BAT_R0, SOC_REG0, SOC_REG1, SOC_REG2, + POWER_SAVE0, SOC_REG0, SOC_REG1, SOC_REG2, REMAIN_CAP_REG2, REMAIN_CAP_REG1, REMAIN_CAP_REG0, NEW_FCC_REG2, NEW_FCC_REG1, NEW_FCC_REG0, - RESET_MODE, + RESET_MODE, POWER_SAVE1, CONTACT_RES, FG_INIT, HALT_CNT_REG, CALC_REST_REGL, UPDATE_LEVE_REG, VOL_ADC_B3, VOL_ADC_B2, VOL_ADC_B1, VOL_ADC_B0, VOL_ADC_K3, VOL_ADC_K2, VOL_ADC_K1, VOL_ADC_K0, BAT_EXS, CHG_STS, BAT_OVP_STS, CHRG_IN_CLAMP, CHIP_NAME_H, CHIP_NAME_L, CHRG_CUR_SEL, CHRG_VOL_SEL, - PLUG_IN_STS, BAT_LTS_TS, USB_SYS_EN, + PLUG_IN_STS, BAT_LTS_TS, USB_SYS_EN, TS_GPIO_FUN, F_MAX_FIELDS }; @@ -324,16 +322,10 @@ static const struct reg_field rk817_battery_reg_fields[] = { [RELAX_CUR2_H] = REG_FIELD(0x60, 0, 7), [RELAX_CUR2_L] = REG_FIELD(0x61, 0, 7), - [OCV_THRE_VOL] = REG_FIELD(0x62, 0, 7), + [CHRG_BAT_TAB2] = REG_FIELD(0x62, 7, 7), + [CHRG_INCC_ZERO] = REG_FIELD(0x62, 6, 6), + [CHRG_BATHIAUTO_EN] = REG_FIELD(0x62, 5, 5), - [OCV_VOL_H] = REG_FIELD(0x63, 0, 7), - [OCV_VOL_L] = REG_FIELD(0x64, 0, 7), - [OCV_VOL0_H] = REG_FIELD(0x65, 0, 7), - [OCV_VOL0_L] = REG_FIELD(0x66, 0, 7), - [OCV_CUR_H] = REG_FIELD(0x67, 0, 7), - [OCV_CUR_L] = REG_FIELD(0x68, 0, 7), - [OCV_CUR0_H] = REG_FIELD(0x69, 0, 7), - [OCV_CUR0_L] = REG_FIELD(0x6A, 0, 7), [PWRON_VOL_H] = REG_FIELD(0x6B, 0, 7), [PWRON_VOL_L] = REG_FIELD(0x6C, 0, 7), [PWRON_CUR_H] = REG_FIELD(0x6D, 0, 7), @@ -388,7 +380,7 @@ static const struct reg_field rk817_battery_reg_fields[] = { [IOFFSET_H] = REG_FIELD(0x97, 0, 7), [IOFFSET_L] = REG_FIELD(0x98, 0, 7), - [BAT_R0] = REG_FIELD(0x99, 0, 7), + [POWER_SAVE0] = REG_FIELD(0x99, 0, 7), [SOC_REG0] = REG_FIELD(0x9A, 0, 7), [SOC_REG1] = REG_FIELD(0x9B, 0, 7), [SOC_REG2] = REG_FIELD(0x9C, 0, 7), @@ -400,6 +392,8 @@ static const struct reg_field rk817_battery_reg_fields[] = { [NEW_FCC_REG1] = REG_FIELD(0xA1, 0, 7), [NEW_FCC_REG2] = REG_FIELD(0xA2, 0, 7), [RESET_MODE] = REG_FIELD(0xA3, 0, 3), + [POWER_SAVE1] = REG_FIELD(0xA4, 0, 7), + [CONTACT_RES] = REG_FIELD(0xA5, 0, 6), [FG_INIT] = REG_FIELD(0xA5, 7, 7), [HALT_CNT_REG] = REG_FIELD(0xA6, 0, 7), @@ -426,6 +420,7 @@ static const struct reg_field rk817_battery_reg_fields[] = { [CHIP_NAME_H] = REG_FIELD(0xED, 0, 7), [CHIP_NAME_L] = REG_FIELD(0xEE, 0, 7), [PLUG_IN_STS] = REG_FIELD(0xF0, 6, 6), + [TS_GPIO_FUN] = REG_FIELD(0xFE, 2, 2), }; struct temp_chrg_table { @@ -437,6 +432,16 @@ struct temp_chrg_table { int chrg_voltage_index; }; +struct contact_res_info { + int ts40uA_adc2vol; + int ts40uA_current_avg; + int ts40uA_update; + + int ts10uA_adc2vol; + int ts10uA_current_avg; + int ts10uA_update; +}; + struct battery_platform_data { u32 *ocv_table; u32 ocv_size; @@ -462,6 +467,8 @@ struct battery_platform_data { u32 design_max_voltage; int fake_full_soc; int charge_stay_awake; + int contact_res; + int get_contact_res_by_soft; }; struct rk817_battery_device { @@ -477,6 +484,7 @@ struct rk817_battery_device { struct delayed_work bat_delay_work; struct delayed_work calib_delay_work; struct timer_list caltimer; + struct contact_res_info ts_info; int sample_res; int bat_res; @@ -489,7 +497,6 @@ struct rk817_battery_device { int voltage_usb; int voltage_sys; int voltage_avg; - int voltage_ocv; int voltage_relax; int voltage_k;/* VCALIB0 VCALIB1 */ int voltage_b; @@ -531,7 +538,6 @@ struct rk817_battery_device { u32 pwroff_min; u8 halt_cnt; bool is_halt; - bool is_max_soc_offset; bool is_sw_reset; bool is_ocv_calib; bool is_first_on; @@ -832,39 +838,6 @@ static void rk817_bat_set_relax_sample(struct rk817_battery_device *battery) __func__, pdata->sleep_enter_current, pdata->sleep_exit_current); } -static int rk817_bat_get_ocv_voltage(struct rk817_battery_device *battery) -{ - int vol, val = 0, vol_temp; - - val = rk817_bat_field_read(battery, OCV_VOL_H) << 8; - val |= rk817_bat_field_read(battery, OCV_VOL_L); - vol = battery->voltage_k * val / 1000 + battery->voltage_b; - - if (battery->chip_id == RK809_ID) { - vol_temp = vol * battery->pdata->bat_res_up / - battery->pdata->bat_res_down + vol; - vol = vol_temp; - } - - return vol; -} - -static int rk817_bat_get_ocv0_voltage0(struct rk817_battery_device *battery) -{ - int vol, val = 0, vol_temp; - - val = rk817_bat_field_read(battery, OCV_VOL0_H) << 8; - val |= rk817_bat_field_read(battery, OCV_VOL0_L); - vol = battery->voltage_k * val / 1000 + battery->voltage_b; - if (battery->chip_id == RK809_ID) { - vol_temp = vol * battery->pdata->bat_res_up / - battery->pdata->bat_res_down + vol; - vol = vol_temp; - } - - return vol; -} - /* power on battery voltage */ static int rk817_bat_get_pwron_voltage(struct rk817_battery_device *battery) { @@ -996,36 +969,6 @@ static int rk817_bat_get_relax_current(struct rk817_battery_device *battery) return (relax_cur1 < relax_cur2) ? relax_cur1 : relax_cur2; } -static int rk817_bat_get_ocv_current(struct rk817_battery_device *battery) -{ - int cur, val = 0; - - val = rk817_bat_field_read(battery, OCV_CUR_H) << 8; - val |= rk817_bat_field_read(battery, OCV_CUR_L); - - if (val & 0x8000) - val -= 0x10000; - - cur = ADC_TO_CURRENT(val, battery->sample_res); - - return cur; -} - -static int rk817_bat_get_ocv_current0(struct rk817_battery_device *battery) -{ - int cur, val = 0; - - val = rk817_bat_field_read(battery, OCV_CUR0_H) << 8; - val |= rk817_bat_field_read(battery, OCV_CUR0_L); - - if (val & 0x8000) - val -= 0x10000; - - cur = ADC_TO_CURRENT(val, battery->sample_res); - - return cur; -} - static int rk817_bat_get_pwron_current(struct rk817_battery_device *battery) { int cur, val = 0; @@ -1435,18 +1378,20 @@ static void rk817_bat_disable_usb2vsys(struct rk817_battery_device *battery) static void rk817_bat_enable_charge(struct rk817_battery_device *battery) { - DBG("enable charge by BAT_LTS_TS: 0xFA\n"); - rk817_bat_field_write(battery, BAT_LTS_TS, 0xFA); + DBG("enable charge by BAT_LTS_TS\n"); + rk817_bat_field_write(battery, BAT_LTS_TS, 0xFF); } static void rk817_bat_disable_charge(struct rk817_battery_device *battery) { - DBG("disable charge by BAT_LTS_TS: 0x05\n"); - rk817_bat_field_write(battery, BAT_LTS_TS, 0x05); + DBG("disable charge by BAT_LTS_TS\n"); + rk817_bat_field_write(battery, BAT_LTS_TS, 0x00); } static void rk817_bat_init_ts_detect(struct rk817_battery_device *battery) { + int contact_res; + if (!battery->pdata->ntc_size) return; @@ -1454,10 +1399,20 @@ static void rk817_bat_init_ts_detect(struct rk817_battery_device *battery) rk817_bat_field_write(battery, TS_ADC_EN, ENABLE); /* source current to TS pin */ rk817_bat_field_write(battery, TS_FUN, TS_FUN_SOURCE_CURRENT); + /* TS pin function: TS pin function */ + rk817_bat_field_write(battery, TS_GPIO_FUN, 0); + /* ts pin flow out current in active state */ rk817_bat_field_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_20uA); - battery->pdata->ntc_factor = (FLOW_OUT_20uA + 1) * 10; + + if (battery->pdata->contact_res != 0) { + contact_res = battery->pdata->contact_res; + rk817_bat_field_write(battery, CONTACT_RES, contact_res); + } else { + contact_res = rk817_bat_field_read(battery, CONTACT_RES); + battery->pdata->contact_res = contact_res; + } rk817_bat_enable_charge(battery); } @@ -1514,22 +1469,118 @@ static int rk817_bat_get_bat_ts(struct rk817_battery_device *battery) return temp_value; } -static int rk817_bat_get_nts_res(struct rk817_battery_device *battery) +static int rk817_bat_get_ntc_res(struct rk817_battery_device *battery) { int temp_value, res; int adc_to_vol; + int bat_current; temp_value = rk817_bat_get_bat_ts(battery); adc_to_vol = temp_value * 1200 / 65536; + bat_current = rk817_bat_get_avg_current(battery); - res = adc_to_vol * 1000 / battery->pdata->ntc_factor; + res = (adc_to_vol * 1000 - bat_current * battery->pdata->contact_res) / + battery->pdata->ntc_factor; - DBG("NTC: ADC: value: 0x%x, adc2vol:%d, res: %d\n", - temp_value, adc_to_vol, res); + DBG("NTC: ADC: value: 0x%x, adc2vol:%d, res: %d, contact_res: %d\n", + temp_value, adc_to_vol, res, battery->pdata->contact_res); return res; } +static void rk817_bat_get_ts_40uA_res(struct rk817_battery_device *battery) +{ + int temp_value; + + temp_value = rk817_bat_get_bat_ts(battery); + battery->ts_info.ts40uA_adc2vol = temp_value * 1200 / 65536; + + battery->ts_info.ts40uA_current_avg = rk817_bat_get_avg_current(battery); + DBG("NTC: ADC: value: 0x%x, adc2vol:%d\n", + temp_value, battery->ts_info.ts40uA_adc2vol); +} + +static void rk817_bat_get_ts_10uA_res(struct rk817_battery_device *battery) +{ + int temp_value; + + temp_value = rk817_bat_get_bat_ts(battery); + battery->ts_info.ts10uA_adc2vol = temp_value * 1200 / 65536; + + battery->ts_info.ts10uA_current_avg = rk817_bat_get_avg_current(battery); + DBG("NTC: ADC: value: 0x%x, adc2vol:%d\n", + temp_value, battery->ts_info.ts10uA_adc2vol); +} + +/* + * get negative terminal contact resistance + * res = (ts40uA_adc2vol * 1000 - ts40uA_current_avg * contact_res) / 40uA = + * (ts10uA_adc2vol * 1000 - ts10uA_current_avg * contact_res) / 20uA = + * contact_res = (ts40uA_adc2vol * 1000 - 2 * ts10uA_adc2vol * 1000) / + * (ts40uA_current_avgi - 2 * ts10uA_current_avg) + */ +static void rk817_bat_get_contact_res(struct rk817_battery_device *battery) +{ + int contact_res; + + DBG("ts40uA_adc2vol: %d, ts40uA_current_avg: %d\n", + battery->ts_info.ts40uA_adc2vol, battery->ts_info.ts40uA_current_avg); + DBG("ts10uA_adc2vol: %d, ts10uA_current_avg: %d\n", + battery->ts_info.ts10uA_adc2vol, battery->ts_info.ts10uA_current_avg); + + contact_res = + (battery->ts_info.ts40uA_adc2vol * 1000 - 4 * battery->ts_info.ts10uA_adc2vol * 1000) / + (battery->ts_info.ts40uA_current_avg - 4 * battery->ts_info.ts10uA_current_avg); + DBG("contact_res: %d\n", battery->pdata->contact_res); + if ((contact_res > 0) && (contact_res < CONTACT_RES_MAX)) { + battery->pdata->contact_res = contact_res; + rk817_bat_field_write(battery, CONTACT_RES, contact_res); + rk817_bat_field_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_20uA); + battery->pdata->ntc_factor = (FLOW_OUT_20uA + 1) * 10; + } +} + +/* + * Calculate the contact resistance between the battery and its holder, + * as it affects the system's ability to measure battery temperature + */ +static void rk817_bat_calculate_contact_res(struct rk817_battery_device *battery) +{ + if (!battery->pdata->ntc_size) + return; + if (!battery->pdata->get_contact_res_by_soft) + return; + if (battery->pdata->contact_res) + return; + if ((battery->temperature < 150) || (battery->temperature > 450)) + return; + + if ((battery->ts_info.ts40uA_update == 1) && (battery->ts_info.ts10uA_update == 0)) + rk817_bat_get_ts_40uA_res(battery); + else if ((battery->ts_info.ts40uA_update == 1) && (battery->ts_info.ts10uA_update == 1)) { + rk817_bat_get_ts_10uA_res(battery); + if (abs(battery->ts_info.ts40uA_current_avg - battery->ts_info.ts10uA_current_avg) < 10) { + rk817_bat_get_contact_res(battery); + } else { + DBG("contact_res: %d\n", battery->pdata->contact_res); + battery->ts_info.ts40uA_update = 0; + battery->ts_info.ts10uA_update = 0; + } + } + + if ((battery->ts_info.ts40uA_update == 0) && (battery->pdata->contact_res == 0)) { + /* ts pin flow out current(40uA) in active state */ + rk817_bat_field_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_40uA); + battery->pdata->ntc_factor = (FLOW_OUT_40uA + 1) * 10; + battery->ts_info.ts40uA_update = 1; + } else if ((battery->ts_info.ts10uA_update == 0) && (battery->pdata->contact_res == 0)) { + /* ts pin flow out current(10uA) in active state */ + rk817_bat_field_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_10uA); + battery->pdata->ntc_factor = (FLOW_OUT_10uA + 1) * 10; + battery->ts_info.ts10uA_update = 1; + } +} + static void rk817_bat_update_temperature(struct rk817_battery_device *battery) { u32 ntc_size, *ntc_table; @@ -1539,7 +1590,7 @@ static void rk817_bat_update_temperature(struct rk817_battery_device *battery) ntc_size = battery->pdata->ntc_size; if (ntc_size) { - res = rk817_bat_get_nts_res(battery); + res = rk817_bat_get_ntc_res(battery); if (res == 0) return; @@ -1677,7 +1728,7 @@ static u8 is_rk817_bat_initialized(struct rk817_battery_device *battery) static void rk817_bat_calc_sm_linek(struct rk817_battery_device *battery) { - long expected_voltage, expected_res2voltage; + long expected_voltage = 0, expected_res2voltage; int expected_rsoc; int current_avg; int soc2vol; @@ -1686,8 +1737,9 @@ static void rk817_bat_calc_sm_linek(struct rk817_battery_device *battery) current_avg = rk817_bat_get_avg_current(battery); soc2vol = rk817_bat_soc2vol(battery, battery->rsoc); - expected_voltage = battery->pdata->pwroff_vol + - soc2vol * (soc2vol - battery->voltage_avg) / battery->pdata->pwroff_vol; + if (soc2vol > battery->voltage_avg) + expected_voltage = battery->pdata->pwroff_vol + + soc2vol * (soc2vol - battery->voltage_avg) / battery->pdata->pwroff_vol; expected_res2voltage = battery->pdata->pwroff_vol + (soc2vol * abs(current_avg) * battery->bat_res) / battery->pdata->pwroff_vol / 1000; @@ -1815,10 +1867,8 @@ static void rk817_bat_not_first_pwron(struct rk817_battery_device *battery) if (battery->nac < 0) battery->nac = 0; - DBG("dsoc=%d cap=%d v=%d ov=%d rv=%d min=%d psoc=%d pcap=%d\n", + DBG("dsoc=%d cap=%d v=%d min=%d psoc=%d pcap=%d\n", battery->dsoc, battery->nac, rk817_bat_get_battery_voltage(battery), - rk817_bat_get_ocv_voltage(battery), - rk817_bat_get_relax_voltage(battery), battery->pwroff_min, rk817_bat_get_prev_dsoc(battery), rk817_bat_get_prev_cap(battery)); } @@ -1893,7 +1943,6 @@ static void rk817_bat_init_fg(struct rk817_battery_device *battery) battery->voltage_avg = rk817_bat_get_battery_voltage(battery); battery->voltage_sys = rk817_bat_get_sys_voltage(battery); - battery->voltage_ocv = rk817_bat_get_ocv_voltage(battery); battery->voltage_relax = rk817_bat_get_relax_voltage(battery); battery->current_avg = rk817_bat_get_avg_current(battery); battery->dbg_pwr_dsoc = battery->dsoc; @@ -2157,6 +2206,18 @@ static int rk817_bat_parse_dt(struct rk817_battery_device *battery) pdata->ntc_size = 0; battery->temperature = VIRTUAL_TEMPERATURE; } else { + ret = of_property_read_u32(np, + "ntc_contact_res", + &pdata->contact_res); + if (ret) { + pdata->contact_res = 0; + ret = of_property_read_u32(np, + "get_contact_res_by_soft", + &pdata->get_contact_res_by_soft); + if (ret) + pdata->get_contact_res_by_soft = 0; + DBG("get_contact_res_by_soft: %d\n", pdata->get_contact_res_by_soft); + } /* get ntc degree base value */ ret = of_property_read_u32_index(np, "ntc_degree_from", 1, &pdata->ntc_degree_from); @@ -2238,7 +2299,7 @@ static int rk817_get_capacity_leve(struct rk817_battery_device *battery) return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; dsoc = (battery->dsoc + 500) / 1000; - if (dsoc < 1) + if (dsoc < 1 && battery->current_avg < 0) return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; else if (dsoc <= 20) return POWER_SUPPLY_CAPACITY_LEVEL_LOW; @@ -2325,12 +2386,6 @@ static int rk817_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: val->intval = rk817_battery_time_to_full(battery); break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - val->intval = 4500 * 1000; - break; - case POWER_SUPPLY_PROP_CURRENT_MAX: - val->intval = 5000 * 1000; - break; default: return -EINVAL; } @@ -2436,9 +2491,9 @@ static void rk817_bat_power_supply_changed(struct rk817_battery_device *battery) status = battery->chrg_status; old_soc = battery->dsoc; power_supply_changed(battery->bat); - DBG("changed: dsoc=%d, rsoc=%d, v=%d, ov=%d c=%d, cap=%d, f=%d\n", + DBG("changed: dsoc=%d, rsoc=%d, v=%d, c=%d, cap=%d, f=%d\n", battery->dsoc, battery->rsoc, battery->voltage_avg, - battery->voltage_ocv, battery->current_avg, + battery->current_avg, battery->remain_cap, battery->fcc); } @@ -2448,16 +2503,12 @@ static void rk817_battery_debug_info(struct rk817_battery_device *battery) rk817_bat_get_sys_voltage(battery); rk817_bat_get_USB_voltage(battery); rk817_bat_get_pwron_voltage(battery); - rk817_bat_get_ocv_voltage(battery); - rk817_bat_get_ocv0_voltage0(battery); rk817_bat_current_calibration(battery); rk817_bat_get_avg_current(battery); rk817_bat_get_relax_cur1(battery); rk817_bat_get_relax_cur2(battery); rk817_bat_get_relax_current(battery); - rk817_bat_get_ocv_current(battery); - rk817_bat_get_ocv_current0(battery); rk817_bat_get_pwron_current(battery); rk817_bat_get_ocv_count(battery); rk817_bat_save_dsoc(battery, battery->dsoc); @@ -2869,12 +2920,13 @@ static void rk817_bat_output_info(struct rk817_battery_device *battery) rk817_bat_print_time(battery); if (battery->pdata->ntc_size) { index = battery->charge_index; - DBG("Temperature: %d charger current: %dmA, index: %d, charger voltage: %dmV, index: %d\n", + DBG("Temperature: %d charger current: %dmA, index: %d, charger voltage: %dmV, index: %d, contact_res: %d\n", battery->temperature, battery->pdata->tc_table[index].chrg_current, battery->pdata->tc_table[index].chrg_current_index, battery->pdata->tc_table[index].chrg_voltage, - battery->pdata->tc_table[index].chrg_voltage_index); + battery->pdata->tc_table[index].chrg_voltage_index, + battery->pdata->contact_res); } DBG("info END.\n"); } @@ -2895,6 +2947,7 @@ static void rk817_battery_work(struct work_struct *work) rk817_bat_save_data(battery); rk817_bat_stay_awake(battery); rk817_bat_update_temperature(battery); + rk817_bat_calculate_contact_res(battery); rk817_bat_output_info(battery); if (rk817_bat_field_read(battery, CUR_CALIB_UPD)) { From 23397aa1ac8d49d8411e13e1253bf9d89618ee0f Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Wed, 7 May 2025 17:32:59 +0800 Subject: [PATCH 02/17] power: supply: rk817_battery: Add auto-stage switching for TS crossflow Added automatic stage switching functionality for the TS crossflow current source. To mitigate the impact of contact resistance and enhance the sampling accuracy of the thermistor voltage, the current strategy prioritizes using higher current ranges of the TS constant current source while remaining within the ADC's sampling range. The system now employs software-based monitoring of the thermistor's voltage to dynamically adjust the current source's output stage. Change-Id: I92e9165280c932b987b85530a9b6792c6b817187 Signed-off-by: Shengfei Xu --- drivers/power/supply/rk817_battery.c | 95 +++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/drivers/power/supply/rk817_battery.c b/drivers/power/supply/rk817_battery.c index 953b6d8da6ea..ad2871c53c7b 100644 --- a/drivers/power/supply/rk817_battery.c +++ b/drivers/power/supply/rk817_battery.c @@ -130,6 +130,9 @@ module_param_named(dbg_level, dbg_enable, int, 0644); /* OCV Table Percentage Accuracy: 5.000% */ #define OCV_TABLE_STEP 5000 +#define TS_MAX_VOL 1100 +#define TS_MIN_VOL 500 + enum ts_fun { TS_FUN_SOURCE_CURRENT, TS_FUN_VOLTAGE_INPUT, @@ -223,7 +226,7 @@ enum rk817_battery_fields { RELAX_CUR1_H, RELAX_CUR1_L, RELAX_CUR2_H, RELAX_CUR2_L, CHRG_BAT_TAB2, CHRG_INCC_ZERO, CHRG_BATHIAUTO_EN, - PWRON_VOL_H, PWRON_VOL_L, + VOL_ADC_TSCUR_SEL_SWITCH, PWRON_VOL_H, PWRON_VOL_L, PWRON_CUR_H, PWRON_CUR_L, OFF_CNT, Q_INIT_H3, Q_INIT_H2, Q_INIT_L1, Q_INIT_L0, @@ -326,6 +329,7 @@ static const struct reg_field rk817_battery_reg_fields[] = { [CHRG_INCC_ZERO] = REG_FIELD(0x62, 6, 6), [CHRG_BATHIAUTO_EN] = REG_FIELD(0x62, 5, 5), + [VOL_ADC_TSCUR_SEL_SWITCH] = REG_FIELD(0x66, 7, 7), [PWRON_VOL_H] = REG_FIELD(0x6B, 0, 7), [PWRON_VOL_L] = REG_FIELD(0x6C, 0, 7), [PWRON_CUR_H] = REG_FIELD(0x6D, 0, 7), @@ -1416,10 +1420,21 @@ static void rk817_bat_init_ts_detect(struct rk817_battery_device *battery) rk817_bat_enable_charge(battery); } -static void rk817_bat_temperature_chrg(struct rk817_battery_device *battery, int temp) +static void rk817_bat_temperature_chrg(struct rk817_battery_device *battery, int now_temp) { int i, up_temp, down_temp; - int now_temp = temp; + int num; + + if (!battery->pdata->tc_count) + return; + + num = battery->pdata->tc_count; + if ((now_temp < battery->pdata->tc_table[0].temp_down) || + (now_temp > battery->pdata->tc_table[num - 1].temp_up)) + rk817_bat_disable_charge(battery); + else if ((now_temp > battery->pdata->tc_table[0].temp_down + 2) && + (now_temp < battery->pdata->tc_table[num - 1].temp_up - 2)) + rk817_bat_enable_charge(battery); for (i = 0; i < battery->pdata->tc_count; i++) { up_temp = battery->pdata->tc_table[i].temp_up; @@ -1452,7 +1467,7 @@ static void rk817_bat_temperature_chrg(struct rk817_battery_device *battery, int battery->pdata->tc_table[i].chrg_voltage, battery->pdata->tc_table[i].chrg_voltage_index); } else - rk817_bat_enable_charge(battery); + rk817_bat_disable_charge(battery); battery->charge_index = i; } @@ -1469,21 +1484,81 @@ static int rk817_bat_get_bat_ts(struct rk817_battery_device *battery) return temp_value; } +static int rk817_bat_tscure_sel_switch(struct rk817_battery_device *battery, int tsvol) +{ + int current_gear = battery->pdata->ntc_factor; + int new_gear = current_gear; + int i, index = -1; + /* Define gear sequence and corresponding parameters */ + const struct { + int gear; + int sel_switch; + int flow_out; + } gear_table[] = { + {40, 0, FLOW_OUT_30uA}, + {30, 0, FLOW_OUT_20uA}, + {20, 0, FLOW_OUT_10uA}, + {10, 0, FLOW_OUT_10uA} /* Last gear */ + }; + + /* find current gear index */ + for (i = 0; i < ARRAY_SIZE(gear_table); i++) { + if (gear_table[i].gear == current_gear) { + index = i; + break; + } + } + + /* invalid current gear */ + if (index < 0) + return -EINVAL; + + /* gear switching logic */ + if (tsvol > TS_MAX_VOL) { + /* Move to lower gear */ + if (index < ARRAY_SIZE(gear_table) - 1) { + new_gear = gear_table[index + 1].gear; + rk817_bat_field_write(battery, + VOL_ADC_TSCUR_SEL, + gear_table[index + 1].flow_out); + } + } else if (tsvol < TS_MIN_VOL) { + /* Move to higher gear */ + if (index > 0) { + new_gear = gear_table[index - 1].gear; + rk817_bat_field_write(battery, + VOL_ADC_TSCUR_SEL, + gear_table[index - 1].flow_out); + } + } + + /* Update gear only if changed */ + if (new_gear != current_gear) { + battery->pdata->ntc_factor = new_gear; + DBG("%s: Gear changed from %d to %d\n", __func__, current_gear, new_gear); + } + + return 0; +} + static int rk817_bat_get_ntc_res(struct rk817_battery_device *battery) { int temp_value, res; - int adc_to_vol; int bat_current; + int tsvol; temp_value = rk817_bat_get_bat_ts(battery); - adc_to_vol = temp_value * 1200 / 65536; + tsvol = temp_value * 1200 / 65536; bat_current = rk817_bat_get_avg_current(battery); - res = (adc_to_vol * 1000 - bat_current * battery->pdata->contact_res) / + res = (tsvol * 1000 - bat_current * battery->pdata->contact_res) / battery->pdata->ntc_factor; - DBG("NTC: ADC: value: 0x%x, adc2vol:%d, res: %d, contact_res: %d\n", - temp_value, adc_to_vol, res, battery->pdata->contact_res); + DBG("NTC: ADC: value: 0x%x, adc2vol:%d, res: %d, contact_res: %d, current: %d, ntc_factor: %duA\n", + temp_value, tsvol, res, battery->pdata->contact_res, + bat_current, battery->pdata->ntc_factor); + + rk817_bat_tscure_sel_switch(battery, tsvol); return res; } @@ -1552,7 +1627,7 @@ static void rk817_bat_calculate_contact_res(struct rk817_battery_device *battery return; if (battery->pdata->contact_res) return; - if ((battery->temperature < 150) || (battery->temperature > 450)) + if ((battery->temperature < 150) || (battery->temperature > 350)) return; if ((battery->ts_info.ts40uA_update == 1) && (battery->ts_info.ts10uA_update == 0)) From fedcfc93348aea6f79285c2805e3d700187334eb Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Wed, 14 May 2025 10:35:22 +0800 Subject: [PATCH 03/17] power: supply: rk817_battery: Resolve data overflow issue Data overflow during fast-charging may cause the battery percentage to freeze near full capacity. Change-Id: I1c9d8108196c7a102e1fd251cbda86cbfbaeffe8 Signed-off-by: Shengfei Xu --- drivers/power/supply/rk817_battery.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/power/supply/rk817_battery.c b/drivers/power/supply/rk817_battery.c index ad2871c53c7b..14f51d405c13 100644 --- a/drivers/power/supply/rk817_battery.c +++ b/drivers/power/supply/rk817_battery.c @@ -94,6 +94,7 @@ module_param_named(dbg_level, dbg_enable, int, 0644); #define DEFAULT_SLP_FILTER_CUR 100 #define DEFAULT_PWROFF_VOL_THRESD 3400 #define DEFAULT_MONITOR_SEC 5 + #define DEFAULT_SAMPLE_RES 20 #define CONTACT_RES_MAX 50 @@ -2715,11 +2716,7 @@ static void rk817_bat_smooth_algorithm(struct rk817_battery_device *battery) long cap_change; long ydsoc = 0; - /* charge and discharge switch */ - if ((battery->sm_linek * battery->current_avg <= 0)) { - DBG("<%s>. linek mode, retinit sm linek..\n", __func__); - rk817_bat_calc_sm_linek(battery); - } + rk817_bat_calc_sm_linek(battery); battery->remain_cap = rk817_bat_get_capacity_uah(battery); old_cap = battery->sm_remain_cap; @@ -2767,8 +2764,7 @@ static void rk817_bat_smooth_algorithm(struct rk817_battery_device *battery) rk817_bat_update_soc(battery, ydsoc); battery->sm_remain_cap = battery->remain_cap; - - rk817_bat_calc_sm_linek(battery); + /* rk817_bat_calc_sm_linek(battery); */ DBG("smooth: smooth_soc = %d, dsoc = %d\n", battery->smooth_soc, battery->dsoc); @@ -3346,7 +3342,7 @@ static void rk817_bat_relax_vol_calib(struct rk817_battery_device *battery) static void rk817_bat_resume_profile_smoothing(struct rk817_battery_device *battery) { int delta_cap = 0, old_cap = 0; - unsigned long charge_soc; + uint64_t charge_soc; int interval_sec = 0; long cap_change; long ydsoc = 0; @@ -3387,7 +3383,7 @@ static void rk817_bat_resume_profile_smoothing(struct rk817_battery_device *batt DBG("<%s>. k=%d, ydsoc=%ld; cap:old=%d, new:%d; delta_cap=%d\n", __func__, battery->sm_linek, ydsoc, old_cap, - battery->sm_remain_cap, delta_cap); + battery->remain_cap, delta_cap); /* finish: * 1, suspend online: battery->sleep_chrg_online = 1 @@ -3405,7 +3401,14 @@ static void rk817_bat_resume_profile_smoothing(struct rk817_battery_device *batt battery->smooth_soc = battery->dsoc; battery->delta_cap_remainder = 0; battery->sm_remain_cap = battery->remain_cap; + } else { + battery->smooth_soc += ydsoc; + battery->dsoc += ydsoc; + battery->delta_cap_remainder = cap_change % (10 * DIV(battery->fcc)); + battery->sm_remain_cap = battery->remain_cap; } + DBG("resume: interval_sec: %d,ydsoc: %ld, charge_soc: %lld, dsoc: %d\n", + interval_sec, ydsoc, charge_soc, battery->dsoc); } else { /* discharge mode, but ydsoc > 0, from charge status to dischrage */ @@ -3415,6 +3418,9 @@ static void rk817_bat_resume_profile_smoothing(struct rk817_battery_device *batt battery->delta_cap_remainder = cap_change % (10 * DIV(battery->fcc)); battery->sm_remain_cap = battery->remain_cap; } + DBG("resume: sm_remain_cap: %d, remain_cap: %d, delta_cap: %d delta_cap_remainder: %d,ydsoc: %d, dsoc: %d\n", + battery->sm_remain_cap, battery->remain_cap, delta_cap, + battery->delta_cap_remainder, interval_sec, battery->dsoc); } if (rk817_bat_field_read(battery, CHG_STS) == CHARGE_FINISH) { From cefe8cd2e370a0f2bb958aebae7dd1e7e8c3981f Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Thu, 15 May 2025 15:37:59 +0800 Subject: [PATCH 04/17] power: supply: rk817_battery: Smooth charging/discharging curves Retain the remainder of the smoothing coefficient calculation for use in the next iteration to ensure continuity. Change-Id: Iea5769149a9df4d294a721e43df7c2e6583a8e4f Signed-off-by: Shengfei Xu --- drivers/power/supply/rk817_battery.c | 70 ++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/drivers/power/supply/rk817_battery.c b/drivers/power/supply/rk817_battery.c index 14f51d405c13..8981ce333d49 100644 --- a/drivers/power/supply/rk817_battery.c +++ b/drivers/power/supply/rk817_battery.c @@ -44,7 +44,6 @@ module_param_named(dbg_level, dbg_enable, int, 0644); #define BAT_INFO(fmt, args...) pr_info(fmt, ##args) #define DRIVER_VERSION "1.10" -#define SFT_SET_KB 1 #define DIV(x) ((x) ? (x) : 1) #define ENABLE 0x01 @@ -74,7 +73,7 @@ module_param_named(dbg_level, dbg_enable, int, 0644); (adc_value / 1000 * 1720 / 3600 / samp_res) /* Adjust full capacity to a reduced value */ -#define UPDATE_REDUCE_FCC(fcc) ((fcc) * 995 / 1000) +#define UPDATE_REDUCE_FCC(fcc) ((fcc) * 990 / 1000) /* Raise the maximum capacity value */ #define UPDATE_RAISE_FCC(fcc) ((fcc) * 1005 / 1000) /* Effective full capacity */ @@ -474,6 +473,7 @@ struct battery_platform_data { int charge_stay_awake; int contact_res; int get_contact_res_by_soft; + int nominal_voltage; }; struct rk817_battery_device { @@ -530,6 +530,10 @@ struct rk817_battery_device { int sm_remain_cap; int delta_cap_remainder; int sm_linek; + int charger_ramainder1; + int charger_ramainder2; + int discharger_ramainder1; + int discharger_ramainder2; int smooth_soc; unsigned long sleep_dischrg_sec; unsigned long sleep_sum_sec; @@ -1805,14 +1809,20 @@ static u8 is_rk817_bat_initialized(struct rk817_battery_device *battery) static void rk817_bat_calc_sm_linek(struct rk817_battery_device *battery) { long expected_voltage = 0, expected_res2voltage; + int linek, linek_step1, linek_step2; + bool is_discharging; + int nominal_voltage; int expected_rsoc; int current_avg; int soc2vol; int status; - int linek; + status = rk817_bat_get_charge_status(battery); current_avg = rk817_bat_get_avg_current(battery); soc2vol = rk817_bat_soc2vol(battery, battery->rsoc); + is_discharging = (status == CHRG_OFF) || ((status == CC_OR_CV_CHRG) && (current_avg < 0)) || + ((status == CHARGE_FINISH) && (current_avg < FINISH_CURR_THRESD)); + if (soc2vol > battery->voltage_avg) expected_voltage = battery->pdata->pwroff_vol + soc2vol * (soc2vol - battery->voltage_avg) / battery->pdata->pwroff_vol; @@ -1832,18 +1842,50 @@ static void rk817_bat_calc_sm_linek(struct rk817_battery_device *battery) DBG("expected_voltage: %ld, RSOC: %d expected_rsoc: %d delta_rsoc: %d\n", expected_voltage, battery->rsoc, expected_rsoc, battery->delta_rsoc); + if (battery->pdata->nominal_voltage != 0) + nominal_voltage = battery->pdata->nominal_voltage; + else + nominal_voltage = soc2vol; - status = rk817_bat_get_charge_status(battery); - if ((status == CHRG_OFF) || ((status == CC_OR_CV_CHRG) && (current_avg < 0)) || - ((status == CHARGE_FINISH) && (current_avg < FINISH_CURR_THRESD))) { + DBG("nominal_voltage: %d, pdata->nominal_voltage: %d soc2vol: %d\n", + nominal_voltage, battery->pdata->nominal_voltage, soc2vol); + + if (is_discharging) { /* When the discharge current is less than 30A and the charging IC reports a * full charge status, the system will determine that the current operation is *in discharge mode. */ - linek = -(MAX_PERCENTAGE - battery->rsoc + battery->dsoc) * 1000; - linek /= (MAX_PERCENTAGE - battery->delta_rsoc); + linek_step1 = -(MAX_PERCENTAGE - battery->rsoc + battery->dsoc) * 1000; + linek_step2 = (linek_step1 + battery->discharger_ramainder1) / (MAX_PERCENTAGE - battery->delta_rsoc); + battery->discharger_ramainder1 = (linek_step1 + battery->discharger_ramainder1) % (MAX_PERCENTAGE - battery->delta_rsoc); + DBG("discharge: ocv_voltage %d linek_step1: %d linek_step2: %d: discharger_ramainder1: %d\n", + soc2vol, linek_step1, linek_step2, battery->discharger_ramainder1); + + linek_step2 *= soc2vol; + linek = (linek_step2 + battery->discharger_ramainder2) / DIV(nominal_voltage); + DBG("discharge: (linek_step2 + battery->discharger_ramainder2): %d, linek_step1: %d, linek_step2: %d, linek: %d\n", + (linek_step2 + battery->discharger_ramainder2), linek_step1, linek_step2, linek); + battery->discharger_ramainder2 = (linek_step2 + battery->discharger_ramainder2) % DIV(nominal_voltage); + DBG("discharge: ocv_voltage %d linek_step2: %d linek: %d: discharger_ramainder2: %d\n", + soc2vol, linek_step2, linek, battery->discharger_ramainder2); + + battery->charger_ramainder1 = 0; + battery->charger_ramainder2 = 0; } else { - linek = MAX_PERCENTAGE * 1000 / (MAX_PERCENTAGE - battery->rsoc + battery->dsoc); + linek_step1 = (battery->charger_ramainder1 + MAX_PERCENTAGE * 1000) / (battery->fake_full_soc + battery->dsoc - battery->rsoc); + battery->charger_ramainder1 = (battery->charger_ramainder1 + MAX_PERCENTAGE * 1000) % (battery->fake_full_soc + battery->dsoc - battery->rsoc); + DBG("charge: ocv_voltage %d linek: %d: charger_ramainder1: %d: fake_full_soc: %d\n", + soc2vol, linek_step1, battery->charger_ramainder1, battery->fake_full_soc); + + linek_step1 *= soc2vol; + linek = (linek_step1 + battery->charger_ramainder2) / DIV(nominal_voltage); + battery->charger_ramainder2 = (linek_step1 + battery->charger_ramainder2) % DIV(nominal_voltage); + DBG("charge: ocv_voltage %d linek_step1: %d linek: %d: charger_ramainder2: %d: fake_full_soc: %d\n", + soc2vol, linek_step1, linek, battery->charger_ramainder2, battery->fake_full_soc); + DBG("charge: ocv_voltage %d linek: %d\n", soc2vol, linek); + + battery->discharger_ramainder1 = 0; + battery->discharger_ramainder2 = 0; } DBG("expected_voltage %ld expected_rsoc: %d\n", expected_voltage, expected_rsoc); DBG("ocv_voltage %d sd_ocv_voltage: %ld, linek: %d\n", soc2vol, expected_voltage, linek); @@ -2251,10 +2293,18 @@ static int rk817_bat_parse_dt(struct rk817_battery_device *battery) if (ret < 0) dev_info(dev, "fake_full_soc missing!\n"); else { - if ((pdata->fake_full_soc > 100) || (pdata->fake_full_soc < 0)) + if ((pdata->fake_full_soc > 100) || (pdata->fake_full_soc < 89)) pdata->fake_full_soc = 100; } + ret = of_property_read_u32(np, "nominal_voltage", &pdata->nominal_voltage); + if (ret < 0) { + dev_info(dev, "nominal_voltage missing!\n"); + } else { + if ((pdata->nominal_voltage > 4500) || (pdata->nominal_voltage < 3600)) + pdata->nominal_voltage = 0; + } + if (battery->chip_id == RK809_ID) { ret = of_property_read_u32(np, "bat_res_up", &pdata->bat_res_up); From 57f548caa1a95ee36ab8033ee4c1063dfefd2bce Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Sun, 18 May 2025 16:04:45 +0800 Subject: [PATCH 05/17] power: supply: rk817_battery: Implement temperature filtering functionality Implement temperature filtering functionality to mitigate abrupt temperature fluctuations caused by probabilistic acquisition of erroneous thermistor resistance values. Change-Id: Iedda4af3f75e3cee1b132e659857a2c032681d3a Signed-off-by: Shengfei Xu --- drivers/power/supply/rk817_battery.c | 102 +++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/drivers/power/supply/rk817_battery.c b/drivers/power/supply/rk817_battery.c index 8981ce333d49..7691871f1eb2 100644 --- a/drivers/power/supply/rk817_battery.c +++ b/drivers/power/supply/rk817_battery.c @@ -132,6 +132,14 @@ module_param_named(dbg_level, dbg_enable, int, 0644); #define TS_MAX_VOL 1100 #define TS_MIN_VOL 500 +/* Number of samples in moving window */ +#define SAMPLE_SIZE 10 +/* 2.0 threshold (scaled by 10) */ +#define TEMP_DIFF_THRESH 20 +/* 5.0 jump threshold (scaled by 10) */ +#define JUMP_DETECT_THRESH 50 +/* Resistance value scaling factor */ +#define RES_SCALE 1000 enum ts_fun { TS_FUN_SOURCE_CURRENT, @@ -446,6 +454,17 @@ struct contact_res_info { int ts10uA_update; }; +struct ntc_filter { + /* Sample buffer (scaled by RES_SCALE) */ + u32 samples[SAMPLE_SIZE]; + /* Current write index */ + u32 idx; + /* Valid sample count */ + u32 count; + /* Consecutive discard counter */ + u32 discard_cnt; +}; + struct battery_platform_data { u32 *ocv_table; u32 ocv_size; @@ -491,6 +510,7 @@ struct rk817_battery_device { struct timer_list caltimer; struct contact_res_info ts_info; + struct ntc_filter *f; int sample_res; int bat_res; bool is_first_power_on; @@ -1661,24 +1681,22 @@ static void rk817_bat_calculate_contact_res(struct rk817_battery_device *battery } } -static void rk817_bat_update_temperature(struct rk817_battery_device *battery) +/* Convert resistance to temperature (0.1) */ +static int rk817_bat_res_to_temp(struct rk817_battery_device *battery, u32 res) { + int temperature = VIRTUAL_TEMPERATURE; u32 ntc_size, *ntc_table; - int i, res; + int i; ntc_table = battery->pdata->ntc_table; ntc_size = battery->pdata->ntc_size; if (ntc_size) { - res = rk817_bat_get_ntc_res(battery); - if (res == 0) - return; - if (res < ntc_table[ntc_size - 1]) { - battery->temperature = (ntc_size + battery->pdata->ntc_degree_from) * 10; + temperature = (ntc_size + battery->pdata->ntc_degree_from) * 10; DBG("bat ntc upper max degree: R=%d\n", res); } else if (res > ntc_table[0]) { - battery->temperature = battery->pdata->ntc_degree_from * 10; + temperature = battery->pdata->ntc_degree_from * 10; DBG("bat ntc lower min degree: R=%d\n", res); } else { for (i = 0; i < ntc_size; i++) { @@ -1687,12 +1705,72 @@ static void rk817_bat_update_temperature(struct rk817_battery_device *battery) } if (i <= 0) - battery->temperature = - (battery->pdata->ntc_degree_from) * 10; + temperature = battery->pdata->ntc_degree_from * 10; else - battery->temperature = - (i + battery->pdata->ntc_degree_from) * 10; + temperature = (i + battery->pdata->ntc_degree_from) * 10; } + DBG("Temperature: %d\n", temperature); + } + return temperature; +} + +/* + * Process new sample and return filtered value + */ +static int32_t rk817_bat_ntc_filter(struct rk817_battery_device *battery, u32 new_res) +{ + struct ntc_filter *f = battery->f; + int avg_temp, new_temp, temp_diff; + u32 avg_res, sum = 0; + int i; + + /* Calculate current average */ + for (i = 0; i < f->count; i++) + sum += f->samples[i]; + + /* Temperature validation logic */ + if (f->count > 0) { + avg_res = (int32_t)(sum / f->count); + avg_temp = rk817_bat_res_to_temp(battery, avg_res); + new_temp = rk817_bat_res_to_temp(battery, new_res); + temp_diff = abs(new_temp - avg_temp); + + /* Check if sample should be discarded */ + if (temp_diff > TEMP_DIFF_THRESH) { + /* Detect temperature jump condition */ + if (temp_diff < JUMP_DETECT_THRESH && ++f->discard_cnt >= SAMPLE_SIZE) { + f->discard_cnt = 0; + goto accept_sample; /* Valid jump detected */ + } + /* Return average, discard new sample */ + return avg_res; + } + /* Reset counter on valid sample */ + f->discard_cnt = 0; + } + +accept_sample: + /* Store valid sample */ + f->samples[f->idx] = new_res; + f->idx = (f->idx + 1) % SAMPLE_SIZE; + if (f->count < SAMPLE_SIZE) + f->count++; + + /* Recalculate average */ + sum = 0; + for (i = 0; i < f->count; i++) + sum += f->samples[i]; + return (int32_t)(sum / f->count); +} + +static void rk817_bat_update_temperature(struct rk817_battery_device *battery) +{ + int res; + + if (battery->pdata->ntc_size) { + res = rk817_bat_get_ntc_res(battery); + res = rk817_bat_ntc_filter(battery, res); + battery->temperature = rk817_bat_res_to_temp(battery, res); DBG("Temperature: %d\n", battery->temperature); rk817_bat_temperature_chrg(battery, battery->temperature / 10); } From d0eb6d8d55d6d47e1b11251853825a0a8f37adf6 Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Sat, 17 May 2025 15:25:44 +0800 Subject: [PATCH 06/17] power: supply: rk817_battery: Support rk817b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RK817b fuel gauge has incorporated the following enhancements: The constant voltage (CV) charging levels have been expanded. However, after modifying the level switching function bit, the level value must be forcibly written again to activate the change; otherwise, the level switching cannot be increased. The constant current source for temperature testing has added 80μA, 100μA, 120μA, and 160μA levels to elevate the constant current value, thereby reducing the impact of negative contact resistance on sampling accuracy Change-Id: I9f253c3a89f70a126ba4d72483afe02ea5be2f52 Signed-off-by: Shengfei Xu --- drivers/power/supply/rk817_battery.c | 95 ++++++++++++++++++++-------- drivers/power/supply/rk817_charger.c | 37 ++++++----- 2 files changed, 89 insertions(+), 43 deletions(-) diff --git a/drivers/power/supply/rk817_battery.c b/drivers/power/supply/rk817_battery.c index 7691871f1eb2..6b59291515df 100644 --- a/drivers/power/supply/rk817_battery.c +++ b/drivers/power/supply/rk817_battery.c @@ -146,13 +146,20 @@ enum ts_fun { TS_FUN_VOLTAGE_INPUT, }; -enum tscur_sel { +enum tscur_sel1 { FLOW_OUT_10uA, FLOW_OUT_20uA, FLOW_OUT_30uA, FLOW_OUT_40uA, }; +enum tscur_sel2 { + FLOW_OUT_80uA, + FLOW_OUT_100uA, + FLOW_OUT_120uA, + FLOW_OUT_160uA, +}; + enum charge_current { CHRG_CUR_1000MA, CHRG_CUR_1500MA, @@ -262,7 +269,7 @@ enum rk817_battery_fields { VOL_ADC_K3, VOL_ADC_K2, VOL_ADC_K1, VOL_ADC_K0, BAT_EXS, CHG_STS, BAT_OVP_STS, CHRG_IN_CLAMP, CHIP_NAME_H, CHIP_NAME_L, CHRG_CUR_SEL, CHRG_VOL_SEL, - PLUG_IN_STS, BAT_LTS_TS, USB_SYS_EN, TS_GPIO_FUN, + PLUG_IN_STS, BAT_LTS_TS, USB_SYS_EN, TS_GPIO_FUN, RK817B_FLAG, F_MAX_FIELDS }; @@ -433,6 +440,7 @@ static const struct reg_field rk817_battery_reg_fields[] = { [CHIP_NAME_L] = REG_FIELD(0xEE, 0, 7), [PLUG_IN_STS] = REG_FIELD(0xF0, 6, 6), [TS_GPIO_FUN] = REG_FIELD(0xFE, 2, 2), + [RK817B_FLAG] = REG_FIELD(0xFF, 0, 0), }; struct temp_chrg_table { @@ -590,6 +598,7 @@ struct rk817_battery_device { u8 plugout_trigger; int chip_id; int is_register_chg_psy; + bool is_rk817b; }; static u64 get_boot_sec(void) @@ -683,6 +692,13 @@ static int rk817_bat_field_write(struct rk817_battery_device *battery, return regmap_field_write(battery->rmap_fields[field_id], val); } +static int rk817_bat_field_force_write(struct rk817_battery_device *battery, + enum rk817_battery_fields field_id, + unsigned int val) +{ + return regmap_field_force_write(battery->rmap_fields[field_id], val); +} + /*cal_offset: current offset value*/ static int rk817_bat_get_coffset(struct rk817_battery_device *battery) { @@ -1432,7 +1448,9 @@ static void rk817_bat_init_ts_detect(struct rk817_battery_device *battery) rk817_bat_field_write(battery, TS_GPIO_FUN, 0); /* ts pin flow out current in active state */ - rk817_bat_field_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_20uA); + if (battery->is_rk817b) + rk817_bat_field_force_write(battery, VOL_ADC_TSCUR_SEL_SWITCH, 0); + rk817_bat_field_force_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_20uA); battery->pdata->ntc_factor = (FLOW_OUT_20uA + 1) * 10; if (battery->pdata->contact_res != 0) { @@ -1484,9 +1502,11 @@ static void rk817_bat_temperature_chrg(struct rk817_battery_device *battery, int if ((battery->pdata->tc_table[i].chrg_voltage != 0) && (battery->pdata->tc_table[i].chrg_voltage_index != 0xff)) { rk817_bat_disable_usb2vsys(battery); - rk817_bat_field_write(battery, - CHRG_VOL_SEL, - battery->pdata->tc_table[i].chrg_voltage_index); + if (battery->is_rk817b) + rk817_bat_field_force_write(battery, CHRG_BAT_TAB2, 0); + rk817_bat_field_force_write(battery, + CHRG_VOL_SEL, + battery->pdata->tc_table[i].chrg_voltage_index); rk817_bat_enable_usb2vsys(battery); DBG("T change: charger voltage: %d, index: %d\n", battery->pdata->tc_table[i].chrg_voltage, @@ -1513,17 +1533,23 @@ static int rk817_bat_tscure_sel_switch(struct rk817_battery_device *battery, int { int current_gear = battery->pdata->ntc_factor; int new_gear = current_gear; + int prev_index, next_index; int i, index = -1; /* Define gear sequence and corresponding parameters */ const struct { int gear; int sel_switch; int flow_out; + bool is_high_current; } gear_table[] = { - {40, 0, FLOW_OUT_30uA}, - {30, 0, FLOW_OUT_20uA}, - {20, 0, FLOW_OUT_10uA}, - {10, 0, FLOW_OUT_10uA} /* Last gear */ + {160, 1, FLOW_OUT_120uA, true}, + {120, 1, FLOW_OUT_100uA, true}, + {100, 1, FLOW_OUT_80uA, true}, + {80, 0, FLOW_OUT_40uA, false}, + {40, 0, FLOW_OUT_30uA, false}, + {30, 0, FLOW_OUT_20uA, false}, + {20, 0, FLOW_OUT_10uA, false}, + {10, 0, FLOW_OUT_10uA, false} }; /* find current gear index */ @@ -1539,21 +1565,29 @@ static int rk817_bat_tscure_sel_switch(struct rk817_battery_device *battery, int return -EINVAL; /* gear switching logic */ - if (tsvol > TS_MAX_VOL) { - /* Move to lower gear */ - if (index < ARRAY_SIZE(gear_table) - 1) { - new_gear = gear_table[index + 1].gear; - rk817_bat_field_write(battery, - VOL_ADC_TSCUR_SEL, - gear_table[index + 1].flow_out); + if (tsvol > TS_MAX_VOL && index < ARRAY_SIZE(gear_table) - 1) { + next_index = index + 1; + if (!gear_table[next_index].is_high_current || battery->is_rk817b) { + new_gear = gear_table[next_index].gear; + if (battery->is_rk817b && gear_table[next_index].sel_switch) + rk817_bat_field_force_write(battery, + VOL_ADC_TSCUR_SEL_SWITCH, + gear_table[next_index].sel_switch); + rk817_bat_field_force_write(battery, + VOL_ADC_TSCUR_SEL, + gear_table[next_index].flow_out); } - } else if (tsvol < TS_MIN_VOL) { - /* Move to higher gear */ - if (index > 0) { - new_gear = gear_table[index - 1].gear; - rk817_bat_field_write(battery, - VOL_ADC_TSCUR_SEL, - gear_table[index - 1].flow_out); + } else if (tsvol < TS_MIN_VOL && index > 0) { + prev_index = index - 1; + if (!gear_table[prev_index].is_high_current || battery->is_rk817b) { + new_gear = gear_table[prev_index].gear; + if (battery->is_rk817b && gear_table[prev_index].sel_switch) + rk817_bat_field_force_write(battery, + VOL_ADC_TSCUR_SEL_SWITCH, + gear_table[prev_index].sel_switch); + rk817_bat_field_force_write(battery, + VOL_ADC_TSCUR_SEL, + gear_table[prev_index].flow_out); } } @@ -1635,7 +1669,9 @@ static void rk817_bat_get_contact_res(struct rk817_battery_device *battery) if ((contact_res > 0) && (contact_res < CONTACT_RES_MAX)) { battery->pdata->contact_res = contact_res; rk817_bat_field_write(battery, CONTACT_RES, contact_res); - rk817_bat_field_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_20uA); + if (battery->is_rk817b) + rk817_bat_field_force_write(battery, VOL_ADC_TSCUR_SEL_SWITCH, 0); + rk817_bat_field_force_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_20uA); battery->pdata->ntc_factor = (FLOW_OUT_20uA + 1) * 10; } } @@ -1670,12 +1706,16 @@ static void rk817_bat_calculate_contact_res(struct rk817_battery_device *battery if ((battery->ts_info.ts40uA_update == 0) && (battery->pdata->contact_res == 0)) { /* ts pin flow out current(40uA) in active state */ - rk817_bat_field_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_40uA); + if (battery->is_rk817b) + rk817_bat_field_force_write(battery, VOL_ADC_TSCUR_SEL_SWITCH, 0); + rk817_bat_field_force_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_40uA); battery->pdata->ntc_factor = (FLOW_OUT_40uA + 1) * 10; battery->ts_info.ts40uA_update = 1; } else if ((battery->ts_info.ts10uA_update == 0) && (battery->pdata->contact_res == 0)) { /* ts pin flow out current(10uA) in active state */ - rk817_bat_field_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_10uA); + if (battery->is_rk817b) + rk817_bat_field_force_write(battery, VOL_ADC_TSCUR_SEL_SWITCH, 0); + rk817_bat_field_force_write(battery, VOL_ADC_TSCUR_SEL, FLOW_OUT_10uA); battery->pdata->ntc_factor = (FLOW_OUT_10uA + 1) * 10; battery->ts_info.ts10uA_update = 1; } @@ -1831,6 +1871,7 @@ static void rk817_bat_init_info(struct rk817_battery_device *battery) battery->monitor_ms = battery->pdata->monitor_sec * TIMER_MS_COUNTS; battery->sample_res = battery->pdata->sample_res; battery->fake_full_soc = battery->pdata->fake_full_soc * 1000; + battery->is_rk817b = rk817_bat_field_read(battery, RK817B_FLAG); DBG("battery->qmax :%d\n", battery->qmax); } diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c index 852002437e94..d063d61ce27d 100644 --- a/drivers/power/supply/rk817_charger.c +++ b/drivers/power/supply/rk817_charger.c @@ -198,6 +198,7 @@ enum charger_state { }; enum rk817_charge_fields { + CHRG_BAT_TAB2, BOOST_EN, OTG_EN, OTG_SLP_EN, CHRG_CLK_SEL, CHRG_EN, CHRG_VOL_SEL, CHRG_CT_EN, CHRG_CUR_SEL, USB_VLIM_EN, USB_VLIM_SEL, USB_ILIM_EN, USB_ILIM_SEL, @@ -211,11 +212,12 @@ enum rk817_charge_fields { USB_EXS, USB_EFF, BAT_DIS_ILIM_STS, BAT_SYS_CMP_DLY, BAT_DIS_ILIM_EN, BAT_DISCHRG_ILIM, - PLUG_IN_STS, SOC_REG0, SOC_REG1, SOC_REG2, + PLUG_IN_STS, SOC_REG0, SOC_REG1, SOC_REG2, RK817B_FLAG, F_MAX_FIELDS }; static const struct reg_field rk817_charge_reg_fields[] = { + [CHRG_BAT_TAB2] = REG_FIELD(0x62, 7, 7), [SOC_REG0] = REG_FIELD(0x9A, 0, 7), [SOC_REG1] = REG_FIELD(0x9B, 0, 7), [SOC_REG2] = REG_FIELD(0x9C, 0, 7), @@ -262,6 +264,7 @@ static const struct reg_field rk817_charge_reg_fields[] = { [BAT_DISCHRG_ILIM] = REG_FIELD(0xEC, 0, 2), [PLUG_IN_STS] = REG_FIELD(0xf0, 6, 6), [CHRG_CLK_SEL] = REG_FIELD(0xF3, 6, 6), + [RK817B_FLAG] = REG_FIELD(0xFF, 0, 0), }; struct charger_platform_data { @@ -333,6 +336,7 @@ struct rk817_charger { u8 plugout_trigger; int plugin_irq; int plugout_irq; + bool is_rk817b; }; static enum power_supply_property rk817_ac_props[] = { @@ -483,6 +487,13 @@ static int rk817_charge_field_write(struct rk817_charger *charge, return regmap_field_write(charge->rmap_fields[field_id], val); } +static int rk817_charge_field_force_write(struct rk817_charger *charge, + enum rk817_charge_fields field_id, + unsigned int val) +{ + return regmap_field_force_write(charge->rmap_fields[field_id], val); +} + static int rk817_charge_get_otg_state(struct rk817_charger *charge) { return regulator_is_enabled(charge->otg5v_rdev); @@ -503,13 +514,8 @@ static void rk817_charge_otg_disable(struct rk817_charger *charge) int ret; ret = regulator_disable(charge->otg5v_rdev); - - if (ret) { + if (ret) DBG("disable otg5v failed:%d\n", ret); - return; - } - - return; } static void rk817_charge_otg_enable(struct rk817_charger *charge) @@ -517,13 +523,8 @@ static void rk817_charge_otg_enable(struct rk817_charger *charge) int ret; ret = regulator_enable(charge->otg5v_rdev); - - if (ret) { + if (ret) DBG("enable otg5v failed:%d\n", ret); - return; - } - - return; } #ifdef CONFIG_PM_SLEEP @@ -626,9 +627,12 @@ static void rk817_charge_set_chrg_voltage(struct rk817_charger *charge, dev_err(charge->dev, "the charge voltage is error!\n"); } else { voltage = (chrg_vol - 4100) / 50; - rk817_charge_field_write(charge, - CHRG_VOL_SEL, - CHRG_VOL_4100MV + voltage); + if (charge->is_rk817b) + rk817_charge_field_force_write(charge, CHRG_BAT_TAB2, 0); + rk817_charge_field_force_write(charge, + CHRG_VOL_SEL, + CHRG_VOL_4100MV + voltage); + } } @@ -1295,6 +1299,7 @@ static void rk817_charge_pre_init(struct rk817_charger *charge) charge->min_input_voltage = charge->pdata->min_input_voltage; charge->chrg_finish_cur = charge->pdata->chrg_finish_cur; charge->chrg_term_mode = charge->pdata->chrg_term_mode; + charge->is_rk817b = rk817_charge_field_read(charge, RK817B_FLAG); rk817_charge_set_input_voltage(charge, charge->min_input_voltage); From 43d933d9e474f805daf14cebe665a200560bb62b Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Tue, 3 Jun 2025 10:13:56 +0800 Subject: [PATCH 07/17] power: supply: rk817_battery: Optimize temperature filtering functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Compatibility with abrupt temperature changes: Detect scenarios involving consecutive rapid temperature shifts (valid when 5 consecutive abrupt changes occur); 2. Single-spike filtration: Eliminate isolated jumps caused by charging/discharging processes; 3. Automatic detection of maximum RMS output: Monitor the constant current source’s effective output limits Change-Id: Id8351c8fc987df7afa8a839af9e920b2946e8905 Signed-off-by: Shengfei Xu --- drivers/power/supply/rk817_battery.c | 132 +++++++++++++++++---------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/drivers/power/supply/rk817_battery.c b/drivers/power/supply/rk817_battery.c index 6b59291515df..444bce95c51b 100644 --- a/drivers/power/supply/rk817_battery.c +++ b/drivers/power/supply/rk817_battery.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * rk817 battery driver + * rk817 battery driver * * Copyright (C) 2018 Rockchip Electronics Co., Ltd. * @@ -138,8 +138,8 @@ module_param_named(dbg_level, dbg_enable, int, 0644); #define TEMP_DIFF_THRESH 20 /* 5.0 jump threshold (scaled by 10) */ #define JUMP_DETECT_THRESH 50 -/* Resistance value scaling factor */ -#define RES_SCALE 1000 +/* Required consecutive samples for trend confirmation */ +#define TREND_SAMPLES 5 enum ts_fun { TS_FUN_SOURCE_CURRENT, @@ -268,8 +268,8 @@ enum rk817_battery_fields { VOL_ADC_B3, VOL_ADC_B2, VOL_ADC_B1, VOL_ADC_B0, VOL_ADC_K3, VOL_ADC_K2, VOL_ADC_K1, VOL_ADC_K0, BAT_EXS, CHG_STS, BAT_OVP_STS, CHRG_IN_CLAMP, - CHIP_NAME_H, CHIP_NAME_L, CHRG_CUR_SEL, CHRG_VOL_SEL, - PLUG_IN_STS, BAT_LTS_TS, USB_SYS_EN, TS_GPIO_FUN, RK817B_FLAG, + CHIP_NAME_H, CHIP_NAME_L, CHIP_VER, CHRG_CUR_SEL, CHRG_VOL_SEL, + PLUG_IN_STS, BAT_LTS_TS, USB_SYS_EN, TS_GPIO_FUN, F_MAX_FIELDS }; @@ -437,10 +437,10 @@ static const struct reg_field rk817_battery_reg_fields[] = { [BAT_OVP_STS] = REG_FIELD(0xEB, 3, 3), [CHRG_IN_CLAMP] = REG_FIELD(0xEB, 2, 2), [CHIP_NAME_H] = REG_FIELD(0xED, 0, 7), - [CHIP_NAME_L] = REG_FIELD(0xEE, 0, 7), + [CHIP_NAME_L] = REG_FIELD(0xEE, 4, 7), + [CHIP_VER] = REG_FIELD(0xEE, 0, 3), [PLUG_IN_STS] = REG_FIELD(0xF0, 6, 6), [TS_GPIO_FUN] = REG_FIELD(0xFE, 2, 2), - [RK817B_FLAG] = REG_FIELD(0xFF, 0, 0), }; struct temp_chrg_table { @@ -471,6 +471,10 @@ struct ntc_filter { u32 count; /* Consecutive discard counter */ u32 discard_cnt; + /* 1=rising, -1=falling, 0=undefined */ + int last_trend; + /* Buffer for trend samples */ + u32 trend_samples[TREND_SAMPLES]; }; struct battery_platform_data { @@ -518,7 +522,7 @@ struct rk817_battery_device { struct timer_list caltimer; struct contact_res_info ts_info; - struct ntc_filter *f; + struct ntc_filter f; int sample_res; int bat_res; bool is_first_power_on; @@ -1542,14 +1546,14 @@ static int rk817_bat_tscure_sel_switch(struct rk817_battery_device *battery, int int flow_out; bool is_high_current; } gear_table[] = { - {160, 1, FLOW_OUT_120uA, true}, - {120, 1, FLOW_OUT_100uA, true}, - {100, 1, FLOW_OUT_80uA, true}, - {80, 0, FLOW_OUT_40uA, false}, - {40, 0, FLOW_OUT_30uA, false}, - {30, 0, FLOW_OUT_20uA, false}, - {20, 0, FLOW_OUT_10uA, false}, - {10, 0, FLOW_OUT_10uA, false} + {160, 1, FLOW_OUT_160uA, true}, + {120, 1, FLOW_OUT_120uA, true}, + {100, 1, FLOW_OUT_100uA, true}, + {80, 1, FLOW_OUT_80uA, true}, + {40, 0, FLOW_OUT_40uA, false}, + {30, 0, FLOW_OUT_30uA, false}, + {20, 0, FLOW_OUT_20uA, false}, + {10, 0, FLOW_OUT_10uA, false}, }; /* find current gear index */ @@ -1569,13 +1573,14 @@ static int rk817_bat_tscure_sel_switch(struct rk817_battery_device *battery, int next_index = index + 1; if (!gear_table[next_index].is_high_current || battery->is_rk817b) { new_gear = gear_table[next_index].gear; - if (battery->is_rk817b && gear_table[next_index].sel_switch) + + if (battery->is_rk817b && gear_table[next_index].sel_switch) + rk817_bat_field_force_write(battery, + VOL_ADC_TSCUR_SEL_SWITCH, + gear_table[next_index].sel_switch); rk817_bat_field_force_write(battery, - VOL_ADC_TSCUR_SEL_SWITCH, - gear_table[next_index].sel_switch); - rk817_bat_field_force_write(battery, - VOL_ADC_TSCUR_SEL, - gear_table[next_index].flow_out); + VOL_ADC_TSCUR_SEL, + gear_table[next_index].flow_out); } } else if (tsvol < TS_MIN_VOL && index > 0) { prev_index = index - 1; @@ -1756,51 +1761,74 @@ static int rk817_bat_res_to_temp(struct rk817_battery_device *battery, u32 res) /* * Process new sample and return filtered value + * 1. Added last_trend to track consistent direction of temperature changes + * 2. Defined TREND_SAMPLES macro for clear threshold of required consecutive samples + * 3. Improved jump detection: replaces last 5 samples when 5 consecutive consistent trends detected + * 4. Reduced redundant temperature conversion calculations + * 5. Modified FIFO behavior: replaces newest 5 samples instead of oldest when trend confirmed */ static int32_t rk817_bat_ntc_filter(struct rk817_battery_device *battery, u32 new_res) { - struct ntc_filter *f = battery->f; - int avg_temp, new_temp, temp_diff; - u32 avg_res, sum = 0; + struct ntc_filter *f = &battery->f; + int avg_temp, new_temp, temp_diff, current_trend, replace_idx; + u32 avg_res = 0, sum = 0, new_sum = 0; int i; - /* Calculate current average */ - for (i = 0; i < f->count; i++) - sum += f->samples[i]; - - /* Temperature validation logic */ + /* Calculate current window average */ if (f->count > 0) { - avg_res = (int32_t)(sum / f->count); - avg_temp = rk817_bat_res_to_temp(battery, avg_res); - new_temp = rk817_bat_res_to_temp(battery, new_res); - temp_diff = abs(new_temp - avg_temp); + for (i = 0; i < f->count; i++) + sum += f->samples[i]; - /* Check if sample should be discarded */ - if (temp_diff > TEMP_DIFF_THRESH) { - /* Detect temperature jump condition */ - if (temp_diff < JUMP_DETECT_THRESH && ++f->discard_cnt >= SAMPLE_SIZE) { + avg_res = sum / f->count; + avg_temp = rk817_bat_res_to_temp(battery, avg_res); + DBG("avg_res2temperature: %d\n", avg_temp); + new_temp = rk817_bat_res_to_temp(battery, new_res); + DBG("new_res2temperature: %d\n", avg_temp); + temp_diff = abs(new_temp - avg_temp); + DBG("temperature:avg_res2temp: %d, new_res2temp: %d, temp_diff: %d\n", + avg_temp, new_temp, temp_diff); + + /* Jump detection logic */ + if (temp_diff > TEMP_DIFF_THRESH && temp_diff < JUMP_DETECT_THRESH) { + current_trend = (new_temp > avg_temp) ? 1 : -1; + + /* Store trend sample */ + f->trend_samples[f->discard_cnt % TREND_SAMPLES] = new_res; + + /* Check trend consistency */ + if (current_trend == f->last_trend || f->discard_cnt == 0) { + f->last_trend = current_trend; + if (++f->discard_cnt >= TREND_SAMPLES) { + /* Valid trend confirmed: replace last 5 samples */ + for (i = 0; i < TREND_SAMPLES; i++) { + replace_idx = (f->idx - TREND_SAMPLES + i) % SAMPLE_SIZE; + if (replace_idx < 0) + replace_idx += SAMPLE_SIZE; + f->samples[replace_idx] = f->trend_samples[i]; + } + f->discard_cnt = 0; + goto recalc_avg; + } + } else { f->discard_cnt = 0; - goto accept_sample; /* Valid jump detected */ } - /* Return average, discard new sample */ return avg_res; } - /* Reset counter on valid sample */ f->discard_cnt = 0; } -accept_sample: /* Store valid sample */ f->samples[f->idx] = new_res; f->idx = (f->idx + 1) % SAMPLE_SIZE; if (f->count < SAMPLE_SIZE) f->count++; - /* Recalculate average */ - sum = 0; +recalc_avg: + /* Recalculate window average */ for (i = 0; i < f->count; i++) - sum += f->samples[i]; - return (int32_t)(sum / f->count); + new_sum += f->samples[i]; + + return (int32_t)(new_sum / f->count); } static void rk817_bat_update_temperature(struct rk817_battery_device *battery) @@ -1811,7 +1839,7 @@ static void rk817_bat_update_temperature(struct rk817_battery_device *battery) res = rk817_bat_get_ntc_res(battery); res = rk817_bat_ntc_filter(battery, res); battery->temperature = rk817_bat_res_to_temp(battery, res); - DBG("Temperature: %d\n", battery->temperature); + DBG("Temperature Update: %d\n", battery->temperature); rk817_bat_temperature_chrg(battery, battery->temperature / 10); } } @@ -1871,7 +1899,7 @@ static void rk817_bat_init_info(struct rk817_battery_device *battery) battery->monitor_ms = battery->pdata->monitor_sec * TIMER_MS_COUNTS; battery->sample_res = battery->pdata->sample_res; battery->fake_full_soc = battery->pdata->fake_full_soc * 1000; - battery->is_rk817b = rk817_bat_field_read(battery, RK817B_FLAG); + battery->is_rk817b = rk817_bat_field_read(battery, CHIP_VER); DBG("battery->qmax :%d\n", battery->qmax); } @@ -2797,7 +2825,8 @@ static void rk817_bat_update_fg_info(struct rk817_battery_device *battery) } /* (battery->chrg_status != CHARGE_FINISH) */ - if (battery->chrg_status == CC_OR_CV_CHRG) + if ((rk817_bat_get_charge_status(battery) != CHARGE_FINISH) && + !rk817_bat_fake_finish_mode(battery)) battery->finish_base = get_boot_sec(); } @@ -2823,8 +2852,10 @@ static void rk817_bat_lowpwr_check(struct rk817_battery_device *battery) if ((base2sec(time) > MINUTE(1)) || (battery->voltage_avg <= pwr_off_thresd - 50)) { battery->fake_offline = 1; - if (battery->voltage_avg <= pwr_off_thresd - 50) + if (battery->voltage_avg <= pwr_off_thresd - 50) { battery->dsoc -= 1000; + battery->smooth_soc = battery->dsoc; + } DBG("low power, soc=%d, current=%d\n", battery->dsoc, battery->current_avg); } @@ -3142,6 +3173,7 @@ static void rk817_bat_output_info(struct rk817_battery_device *battery) int index; DBG("info start:\n"); + DBG("info, is_rk817b: %d\n", battery->is_rk817b); DBG("info: voltage_k %d\n", battery->voltage_k); DBG("info: voltage_b %d\n", battery->voltage_b); DBG("info: voltage %d\n", battery->voltage_avg); From d9131bbe37a67de558460e1673e4e0441762dbcd Mon Sep 17 00:00:00 2001 From: Xuhui Lin Date: Fri, 4 Jul 2025 18:35:40 +0800 Subject: [PATCH 08/17] soc: rockchip: rockchip_thunderboot_mmc: Don't continue if timeout happens when loading firmware If mmc read timeout and continue start decom, the phenomenon is that decom error, and it's more difficult to locate the true reason. Change-Id: I95b2f2ac46764485bced6f86a715cfc5b7c80ef0 Signed-off-by: Xuhui Lin --- drivers/soc/rockchip/rockchip_thunderboot_mmc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/soc/rockchip/rockchip_thunderboot_mmc.c b/drivers/soc/rockchip/rockchip_thunderboot_mmc.c index 588677256a96..4c73ae4613fe 100644 --- a/drivers/soc/rockchip/rockchip_thunderboot_mmc.c +++ b/drivers/soc/rockchip/rockchip_thunderboot_mmc.c @@ -58,13 +58,17 @@ static int rk_tb_mmc_thread(void *p) if (readl_poll_timeout(regs + SDMMC_STATUS, status, !(status & (BIT(10) | GENMASK(7, 4))), 100, - 500 * USEC_PER_MSEC)) + 500 * USEC_PER_MSEC)) { dev_err(dev, "Controller is occupied!\n"); + goto out; + } if (readl_poll_timeout(regs + SDMMC_IDSTS, status, !(status & GENMASK(16, 13)), 100, - 500 * USEC_PER_MSEC)) + 500 * USEC_PER_MSEC)) { dev_err(dev, "DMA is still running!\n"); + goto out; + } status = readl_relaxed(regs + SDMMC_RINTSTS); if (status & SDMMC_INTR_ERROR) { From 3ee1df0be9e7a05befc4dd0ef0ed38cc5fde3c49 Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Wed, 19 Mar 2025 18:08:56 +0800 Subject: [PATCH 09/17] drm/rockchip: drv: use drm_format_info_bpp() to get bpp The actual number of bits per pixel need consider block size. Signed-off-by: Sandy Huang Change-Id: Ib0202cb0e8f8167261ddfda2b272a6ff3eae9d47 --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 99819921eeea..aa4020b6e6ed 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -291,10 +291,6 @@ EXPORT_SYMBOL(drm_mode_convert_to_origin_mode); uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info) { - /* use whatever a driver has set */ - if (info->cpp[0]) - return info->cpp[0] * 8; - switch (info->format) { case DRM_FORMAT_YUV420_8BIT: return 12; @@ -303,11 +299,8 @@ uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info) case DRM_FORMAT_VUY101010: return 30; default: - break; + return drm_format_info_bpp(info, 0); } - - /* all attempts failed */ - return 0; } EXPORT_SYMBOL(rockchip_drm_get_bpp); From 26b5e473bed1104e495e0153674bba0b6ce359d0 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Wed, 2 Jul 2025 11:25:33 +0800 Subject: [PATCH 10/17] dmaengine: pl330: Fix NULL pointer dereference in pl330_tasklet() Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010 Mem abort info: ESR = 0x0000000096000005 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x05: level 1 translation fault Data abort info: ISV = 0, ISS = 0x00000005 CM = 0, WnR = 0 user pgtable: 4k pages, 39-bit VAs, pgdp=0000000007a01000 [0000000000000010] pgd=0000000000000000, p4d=0000000000000000, pud=0000000000000000 Internal error: Oops: 0000000096000005 [#1] PREEMPT SMP Modules linked in: bcmdhd(O) CPU: 3 PID: 16268 Comm: brcm_patchram_p Tainted: G W O 6.1.118 #135 Hardware name: Rockchip RK3308B EVB AUDIO DDR3 V11 Board + Rockchip RK3308 RGB ExtBoard V10 (DT) pstate: 600000c5 (nZCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : pl330_tasklet+0xf8/0x51c lr : pl330_tasklet+0x44/0x51c sp : ffffffc008d23e50 x29: ffffffc008d23e50 x28: 0000000000000001 x27: 0000000000000101 x26: ffffff80018c3848 x25: ffffff80018c3848 x24: ffffffc00877d0f0 x23: ffffff80018c3848 x22: ffffff80018c3870 x21: 0000000000000000 x20: 0000000000000000 x19: ffffff80018c37a0 x18: ffffffc00b8c3b28 x17: ffffffc015c48000 x16: ffffffc008d20000 x15: 0000000000000000 x14: ffffff8002dd170c x13: 0000000000000801 x12: 0000000000000001 x11: 0000000000000003 x10: 0000000000000000 x9 : 000000000029316c x8 : ffffff801e684200 x7 : 000000000000028e x6 : 0000000000000c00 x5 : ffffff80018c3870 x4 : 0000000000000000 x3 : ffffff80018c3848 x2 : 0000000000000001 x1 : ffffff80018c3848 x0 : 0000000000000000 Call trace: pl330_tasklet+0xf8/0x51c tasklet_action_common.constprop.0+0x8c/0xd0 tasklet_hi_action+0x24/0x2c handle_softirqs+0x1b0/0x1f0 _stext+0x10/0x18 ____do_softirq+0xc/0x14 call_on_irq_stack+0x24/0x34 do_softirq_own_stack+0x18/0x20 __irq_exit_rcu+0x64/0xac irq_exit_rcu+0xc/0x14 el1_interrupt+0x34/0x5c el1h_64_irq_handler+0x14/0x1c el1h_64_irq+0x64/0x68 exit_rcu+0x0/0x6c do_group_exit+0x30/0x8c get_signal+0x174/0x5e4 do_notify_resume+0x128/0x938 el0_svc_compat+0x30/0x3c el0t_32_sync_handler+0xac/0xf4 el0t_32_sync+0x14c/0x150 Signed-off-by: Sugar Zhang Change-Id: I42e960bb3975d86754c0dab50f95ac5a1e951b31 --- drivers/dma/pl330.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 31b8ed6c31ca..be1f331a2bf6 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2280,6 +2280,11 @@ static void pl330_tasklet(struct tasklet_struct *t) spin_lock_irqsave(&pch->lock, flags); + if (!pch->thread) { + spin_unlock_irqrestore(&pch->lock, flags); + return; + } + /* Pick up ripe tomatoes */ list_for_each_entry_safe(desc, _dt, &pch->work_list, node) { if (desc->status == DONE) { From 7ee99a09682c7a290daaa44b74aa1f31d5807b96 Mon Sep 17 00:00:00 2001 From: Ziyuan Xu Date: Mon, 7 Jul 2025 16:22:05 +0800 Subject: [PATCH 11/17] spi: spi-rockchip-sfc: prefer asynchronous probing when CONFIG_ROCKCHIP_THUNDER_BOOT=y Save boot time about 5ms on rv1126b-evb2-v10-tb-400w board. Signed-off-by: Ziyuan Xu Change-Id: Ie70a3e96c88d71bbc78729cbc64ead792fe319fc --- drivers/spi/spi-rockchip-sfc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index 3e767e30246f..500503c6338b 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -1214,6 +1214,9 @@ static struct platform_driver rockchip_sfc_driver = { .name = "rockchip-sfc", .of_match_table = rockchip_sfc_dt_ids, .pm = &rockchip_sfc_pm_ops, +#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT + .probe_type = PROBE_PREFER_ASYNCHRONOUS, +#endif }, .probe = rockchip_sfc_probe, .remove = rockchip_sfc_remove, From 185cbe4eeab90895877e5d5315f3addc184b2b32 Mon Sep 17 00:00:00 2001 From: Ziyuan Xu Date: Tue, 8 Jul 2025 17:48:53 +0800 Subject: [PATCH 12/17] Revert "arm64: dts: rockchip: rv1126b-evb2-v10-tb-400w: Add rndis support" The RV1126B-EVB2 doesn't support u3, and other usb nodes had beed defined in base dtsi. This reverts commit 2b322acaa76be3cb38650e9b7a3293cf3c50cc0a. Change-Id: Ie960b9cfabfc2f79ffe5e67124bb36ec042eb6c5 Signed-off-by: Ziyuan Xu --- .../dts/rockchip/rv1126b-evb2-v10-tb-400w.dts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10-tb-400w.dts b/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10-tb-400w.dts index ca8d8a5e66a1..c1ba05974b77 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10-tb-400w.dts +++ b/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10-tb-400w.dts @@ -65,19 +65,3 @@ */ reg = <0x41320000 0x14c8000>; }; - -&usb2phy { - status = "okay"; -}; - -&usb2phy_otg { - status = "okay"; -}; - -&usb3phy { - status = "okay"; -}; - -&usb_drd_dwc3 { - status = "okay"; -}; From 1a93d3ca2365eb355d1ec86f3b2cb60a986e2a7d Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 8 Jul 2025 15:16:43 +0800 Subject: [PATCH 13/17] phy: rockchip-snps-pcie3: increase sram init timeout It's reported that one of RK3568 boards could be timeout when doing s2r test. Increasing the timeout value solves the problem. At this time, reuse RK_PCIE_SRAM_INIT_TIMEOUT instead. Change-Id: I9a935104cf3cf6058d69181c4604749692292ff9 Signed-off-by: Shawn Lin --- drivers/phy/rockchip/phy-rockchip-snps-pcie3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c index a370f8feff45..2933c6fdfb46 100644 --- a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -119,7 +119,7 @@ static int rockchip_p3phy_rk3568_init(struct rockchip_p3phy_priv *priv) ret = regmap_read_poll_timeout(priv->phy_grf, GRF_PCIE30PHY_STATUS0, reg, SRAM_INIT_DONE(reg), - 0, 500); + 0, RK_PCIE_SRAM_INIT_TIMEOUT); if (ret) { dev_err(&priv->phy->dev, "%s: lock failed 0x%x, check input refclk and power supply\n", __func__, reg); From d5de9fa8533aa02d64b8cd1bd462f60ad2be62fa Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Thu, 3 Jul 2025 10:36:31 +0800 Subject: [PATCH 14/17] PCI: rockchip: dw: Add PCIE_DW_ROCKCHIP_RC_DMATEST macro limit for dma test Change-Id: Ife44ec0ef94bf731ba7b7056833b942608801ed4 Signed-off-by: Jon Lin --- drivers/pci/controller/dwc/Kconfig | 20 ++++++++++++------- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 7 +++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index a9f528f1d842..ed736f841610 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -97,13 +97,6 @@ config PCIE_RK_THREADED_INIT help Enables threaded initialize Rockchip DW based PCIe controller. -config PCIE_DW_DMATEST - bool "DesignWare PCIe DMA test" - depends on PCIE_DW_ROCKCHIP - depends on !ROCKCHIP_PCIE_DMA_OBJ - help - Enables support for the DW PCIe controller DMA test. - config PCIE_DW_ROCKCHIP_EP bool "Rockchip DesignWare PCIe EP controller" select PCIE_DW @@ -112,6 +105,19 @@ config PCIE_DW_ROCKCHIP_EP help Enables support for the DW PCIe controller in the Rockchip SoC. +config PCIE_DW_DMATEST + bool "DesignWare PCIe DMA test" + depends on (PCIE_DW_ROCKCHIP || PCIE_DW_ROCKCHIP_EP || PCIE_FUNC_RKEP) + depends on !ROCKCHIP_PCIE_DMA_OBJ + help + Enables support for the DW PCIe controller DMA test. + +config PCIE_DW_ROCKCHIP_RC_DMATEST + bool "DesignWare PCIe Rockchip RC Enable DMA test" + depends on PCIE_DW_DMATEST + help + Enables support for the DW PCIe controller DMA test in the Rockchip SoC. + config PCI_EXYNOS tristate "Samsung Exynos PCIe controller" depends on ARCH_EXYNOS || COMPILE_TEST diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index f9c84def2614..236c9793a3a5 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -573,13 +573,16 @@ static int rk_pcie_init_dma_trx(struct rk_pcie *rk_pcie) if (!rk_pcie_udma_enabled(rk_pcie)) return 0; +#ifdef PCIE_DW_ROCKCHIP_RC_DMATEST rk_pcie->dma_obj = pcie_dw_dmatest_register(rk_pcie->pci->dev, true); if (IS_ERR(rk_pcie->dma_obj)) { dev_err(rk_pcie->pci->dev, "failed to prepare dmatest\n"); return -EINVAL; - } else if (!rk_pcie->dma_obj) { /* !CONFIG_ROCKCHIP_PCIE_DMA_OBJ */ - return 0; } +#endif + + if (!rk_pcie->dma_obj) + return 0; /* Enable client write and read interrupt */ rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK, 0xc000000); From 5ae472ab4e8e04d02f7510730bad0d5896981bda Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Wed, 9 Jul 2025 11:13:29 +0800 Subject: [PATCH 15/17] arm64: configs: rockchip_linux_defconfig enable CONFIG_PCIE_FUNC_RKEP Change-Id: I9e500c5cf8a57e7644a5377a227a4f862a5f9361 Signed-off-by: Jon Lin --- arch/arm64/configs/rockchip_linux_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig index 6bd10a73cbb4..16dcb4cbfb59 100644 --- a/arch/arm64/configs/rockchip_linux_defconfig +++ b/arch/arm64/configs/rockchip_linux_defconfig @@ -158,6 +158,7 @@ CONFIG_BLK_DEV_RAM_COUNT=1 CONFIG_BLK_DEV_NVME=y CONFIG_RK628_MISC=y CONFIG_RK628_MISC_HDMITX=y +CONFIG_PCIE_FUNC_RKEP=y CONFIG_SRAM=y CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y From d464908e13cfd89265a75b66a115d5e83480d835 Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Tue, 27 May 2025 15:35:11 +0800 Subject: [PATCH 16/17] phy: rockchip: inno-hdmi: Support automatic calculation of the phy pll frequency division coefficient If the required frequency is not in the pre_pll_cfg_table, the automatically calculated frequency division coefficient will be used. The automatic calculation function has been tested at following frequencies(unit is Mhz): 594/371.25/297/185.625/148.5/108/92.8125/74.25/59.4/33.75/27/25.2 Change-Id: If58e12c284dc315c82043600edec6cb313423550 Signed-off-by: Algea Cao --- .../phy/rockchip/phy-rockchip-inno-hdmi-phy.c | 275 ++++++++++++++++-- 1 file changed, 253 insertions(+), 22 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi-phy.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi-phy.c index 4c474c596247..3cb3937c4f90 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi-phy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi-phy.c @@ -476,6 +476,243 @@ static irqreturn_t inno_hdmi_phy_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#define FREF 24000000 +#define FFBD_FRAC_MAX 16777216 + +static int inno_hdmi_phy_pll_cal(struct inno_hdmi_phy *inno, struct pre_pll_config *cfg, + u64 pixelclk, u64 tmdsclock) +{ + u32 j, k, i, nf = 0, nr = 1, tmds_no, tmds_a, tmds_b, tmds_c; + u32 pclk_no = 0, prepclk_no = 0, div_5 = 0; + u8 tmdsa[4] = {1, 2, 3, 5}; + u8 tmdsbc[4] = {1, 2, 4, 8}; + u8 pclkb[4] = {2, 3, 4, 5}; + u8 pclkc[4] = {1, 2, 4, 8}; + u32 pclka, pclkd; + u32 rem = 0; + u64 frac_div = 0; + u64 fvco; + u64 frefdiv; + bool frac_supported = true; + bool frac_cal = false; + + dev_dbg(inno->dev, "pixelclk:%llu,tmdsclock:%llu\n", pixelclk, tmdsclock); + + if (pixelclk > tmdsclock && pixelclk < 340000000) { + dev_dbg(inno->dev, "hdmi1.4 resolution can't support yuv420 mode\n"); + return 0; + } + + if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228) + frac_supported = false; + + /* VCO frequency shall not be higher than 3.2Ghz */ + i = DIV_ROUND_UP_ULL(3200000000ULL, tmdsclock * 4); + +continue_cal: + /* + * If the current parameters can not get the correct clock, + * return here and continue to calculate with next set of + * parameters until the allowed range is exceeded or get + * the correct clock. + */ + for (; nr < 31; nr++) { + frefdiv = FREF / nr; + nf = 0; + /* VCO frequency shall not be lower than 1.4Ghz */ + while (((tmdsclock * 4 * --i) > 1400000000ULL) && i > 0) { + fvco = tmdsclock * 4 * i; + div_5 = 0; + + div_u64_rem(fvco, frefdiv, &rem); + dev_dbg(inno->dev, "i:%u rem:%u frefdiv:%llu fvco:%llu\n", + i, rem, frefdiv, fvco); + /* fvco = (fref / nr) * nf */ + if (!frac_cal && !rem && (div_u64(fvco, frefdiv) <= 4096)) { + nf = div_u64(fvco, frefdiv); + tmds_no = i; + break; + /* fvco = (fref / nr) * (nf + frac_div / 2^24) */ + } else if (frac_cal && (div_u64(fvco, frefdiv) <= 4096)) { + nf = div_u64(fvco, frefdiv); + tmds_no = i; + + frac_div = (u64)rem * FFBD_FRAC_MAX; + frac_div = div_u64(frac_div, frefdiv); + dev_dbg(inno->dev, "frac_div:%llu\n", frac_div); + break; + } + } + + if (nf) + break; + + i = DIV_ROUND_UP_ULL(3200000000ULL, tmdsclock * 4); + } + + if (nr == 31) { + if (frac_supported) { + /* + * RK3528/RK3328 support fraction calculation. If this clk can't + * be calculated with integers, using fraction to + * calculate. + */ + if (!frac_cal) { + frac_cal = 1; + nr = 1; + goto continue_cal; + } + } + + dev_dbg(inno->dev, "can't support tmdsclock:%llu\n", tmdsclock); + return 0; + } + + if (tmdsclock > 340000000) { + for (k = 0; k < 4; k++) { + /* + * HDMI2.0 is 1/40 mode, tmds lane clk is 1/4 pixel clk. + * so f_linkclk must be four times that of f_tmdsclk, + * tmds_divb must be four times that of tmds_divc. + * The cycle starts from tmdsbc[2]. + */ + for (j = 2; j < 4; j++) { + if (tmdsa[k] * tmdsbc[j] == (4 * tmds_no)) + break; + } + if (j < 4) + break; + } + } else { + for (k = 0; k < 4; k++) { + for (j = 0; j < 4; j++) { + if (tmdsa[k] * tmdsbc[j] == tmds_no) + break; + } + if (j < 4) + break; + } + } + + if (k == 4) { + nf = 0; + goto continue_cal; + } + + tmds_a = k; + tmds_b = j; + if (tmdsclock > 340000000) + tmds_c = j - 2; + else + tmds_c = j; + + dev_dbg(inno->dev, "tmds_a %d (%d) tmds_b %d (%d) tmds_c %d (%d)\n", + tmds_a, tmdsa[tmds_a], tmds_b, tmdsbc[tmds_b], tmds_c, + tmdsbc[tmds_c]); + + /* In yuv420 mode f_pclk is twice of f_prepclk */ + if (pixelclk > tmdsclock) { + div_u64_rem(fvco * 2, pixelclk, &rem); + if (rem) + goto continue_cal; + + prepclk_no = div_u64(fvco * 2, pixelclk); + + if (div_u64(fvco, pixelclk) == 5) { + div_5 = 1; + } else { + if (prepclk_no % 4) + goto continue_cal; + + pclk_no = prepclk_no / 4; + } + } else { + div_u64_rem(fvco, pixelclk, &rem); + if (rem) + goto continue_cal; + + prepclk_no = div_u64(fvco, pixelclk); + + if (div_u64(fvco, pixelclk) == 5) { + div_5 = 1; + } else { + if (prepclk_no % 2) + goto continue_cal; + + pclk_no = prepclk_no / 2; + } + } + + dev_dbg(inno->dev, "prepclk_no:%d,pclk_no:%d,div_5:%d\n", + prepclk_no, pclk_no, div_5); + + for (k = 0; k < 4; k++) { + for (j = 0; j < 4; j++) { + if (pclkb[k] * pclkc[j] == prepclk_no) + break; + } + + if (j < 4) { + pclka = 1; + break; + } + } + + if (k == 4) { + for (j = 0; j < 4; j++) { + if ((prepclk_no % pclkc[j]) == 0 && + (prepclk_no / pclkc[j]) < 32) { + pclka = prepclk_no / pclkc[j]; + break; + } + } + } else { + pclka = 1; + } + + if (j == 4) + goto continue_cal; + + /* pixel clk directly divided by 5 from fvco */ + if (div_5) { + pclkd = 1; + } else { + if (k == 4) { + if (pclk_no % pclka) + goto continue_cal; + else + pclkd = pclk_no / pclka; + } else { + if (pclk_no % pclkb[k]) + goto continue_cal; + else + pclkd = pclk_no / pclkb[k]; + } + } + + if (cfg) { + cfg->pixclock = pixelclk; + cfg->tmdsclock = tmdsclock; + cfg->prediv = nr; + cfg->fbdiv = nf; + cfg->tmds_div_a = tmds_a; + cfg->tmds_div_b = tmds_b; + cfg->tmds_div_c = tmds_c; + cfg->pclk_div_a = pclka; + cfg->pclk_div_b = k; + cfg->pclk_div_c = j; + cfg->pclk_div_d = pclkd; + cfg->vco_div_5_en = div_5; + cfg->fracdiv = frac_div; + } + + dev_dbg(inno->dev, "%llu, %llu, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %llu\n", + pixelclk, tmdsclock, nr, nf, tmds_a, tmds_b, tmds_c, pclka, k, j, pclkd, + div_5, frac_div); + + return pixelclk; +} + static int inno_hdmi_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate); @@ -595,36 +832,25 @@ static unsigned long inno_hdmi_phy_clk_recalc_rate(struct clk_hw *hw, static long inno_hdmi_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { - int i; const struct pre_pll_config *cfg = pre_pll_cfg_table; struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw); u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate); + /* Limit pixel clock under 600MHz */ + if (rate > 600000000) + return -EINVAL; + for (; cfg->pixclock != ~0UL; cfg++) if (cfg->pixclock == rate) break; - /* XXX: Limit pixel clock under 600MHz */ - if (cfg->pixclock > 600000000) - return -EINVAL; + if (cfg->pixclock == ~0UL) { + if (!inno_hdmi_phy_pll_cal(inno, NULL, rate, tmdsclock)) + return -EINVAL; - /* - * If there is no dts phy cfg table, use default phy cfg table. - * The tmds clock maximum is 594MHz. So there is no need to check - * whether tmds clock is out of range. - */ - if (!inno->phy_cfg) - return cfg->pixclock; - - /* Check if tmds clock is out of dts phy config's range. */ - for (i = 0; inno->phy_cfg[i].tmdsclock != ~0UL; i++) { - if (inno->phy_cfg[i].tmdsclock >= tmdsclock) - break; + return rate; } - if (inno->phy_cfg[i].tmdsclock == ~0UL) - return -EINVAL; - return cfg->pixclock; } @@ -633,6 +859,7 @@ static int inno_hdmi_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, { struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw); const struct pre_pll_config *cfg = pre_pll_cfg_table; + struct pre_pll_config rc = {0}; u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate); dev_dbg(inno->dev, "%s rate %lu tmdsclk %u\n", @@ -645,13 +872,17 @@ static int inno_hdmi_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, if (cfg->pixclock == rate && cfg->tmdsclock == tmdsclock) break; + rc = *cfg; + if (cfg->pixclock == ~0UL) { - dev_err(inno->dev, "unsupported rate %lu\n", rate); - return -EINVAL; + if (!inno_hdmi_phy_pll_cal(inno, &rc, rate, tmdsclock)) { + dev_err(inno->dev, "unsupported rate %lu\n", rate); + return -EINVAL; + } } if (inno->plat_data->ops->pre_pll_update) - inno->plat_data->ops->pre_pll_update(inno, cfg); + inno->plat_data->ops->pre_pll_update(inno, &rc); inno->pixclock = rate; inno->tmdsclock = tmdsclock; From a1d62b81aec54ac9382ba495ad6803a12e83cb30 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 9 Jul 2025 16:43:40 +0800 Subject: [PATCH 17/17] pwm: rockchip: Remove redundant pwmchip_remove() The pwmchip_add() has been replaced with devm_pwmchip_add(), so the pwmchip_remove() is needless. Fixes: 19b3f8d8302a ("pwm: rockchip: add pwm clk_osc control for wave generator mode") Change-Id: I2f75be757fc6b8e73f376fe2ec1c251b9a2701be Signed-off-by: Damon Ding --- drivers/pwm/pwm-rockchip.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 19a158a077c3..1bec576efb82 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -2499,8 +2499,6 @@ static int rockchip_pwm_remove(struct platform_device *pdev) } } - pwmchip_remove(&pc->chip); - if (pc->oneshot_en) clk_disable(pc->pclk); clk_unprepare(pc->clk_osc);