static s64 depl_pbat(s64 ocv, s64 ibat, s64 rbat) { s64 pbat; pbat = ocv - div64_s64(ibat * rbat, 1000); pbat = div64_s64(pbat * ibat, 1000000); return pbat; }
static int palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan) { s64 k; int d1; int d2; int ret; int x1 = adc->adc_info[adc_chan].x1; int x2 = adc->adc_info[adc_chan].x2; ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, adc->adc_info[adc_chan].trim1_reg, &d1); if (ret < 0) { dev_err(adc->dev, "TRIM read failed: %d\n", ret); goto scrub; } ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, adc->adc_info[adc_chan].trim2_reg, &d2); if (ret < 0) { dev_err(adc->dev, "TRIM read failed: %d\n", ret); goto scrub; } /* Gain Calculation */ k = PRECISION_MULTIPLIER; k += div64_s64(PRECISION_MULTIPLIER * (d2 - d1), x2 - x1); adc->adc_info[adc_chan].gain = k; /* Offset Calculation */ adc->adc_info[adc_chan].offset = (d1 * PRECISION_MULTIPLIER); adc->adc_info[adc_chan].offset -= ((k - PRECISION_MULTIPLIER) * x1); scrub: return ret; }
static int calc_gamma(struct dynamic_aid_info d_aid, int ibr, int *result) { int ret, iv, c; int numerator, denominator; s64 t1, t2; int *vd, *mtp, *res; struct rgb_t (*offset_color)[d_aid.iv_max]; struct rgb64_t *m_voltage; offset_color = (struct rgb_t(*)[])d_aid.param.offset_color; m_voltage = d_aid.m_voltage; iv = d_aid.iv_max - 1; ret = 0; /* iv == (IV_MAX - 1) ~ 1; */ for (; iv > 0; iv--) { mtp = &d_aid.mtp[iv*CI_MAX]; res = &result[iv*CI_MAX]; numerator = d_aid.param.formular[iv].numerator; denominator = d_aid.param.formular[iv].denominator; for (c = 0; c < CI_MAX; c++) { if (iv == 1) { t1 = d_aid.vreg - m_voltage[iv].rgb[c]; t2 = d_aid.vreg - m_voltage[iv+1].rgb[c]; } else if (iv == d_aid.iv_max - 1) { t1 = d_aid.vreg - m_voltage[iv].rgb[c]; t2 = d_aid.vreg; } else { t1 = m_voltage[0].rgb[c] - m_voltage[iv].rgb[c]; t2 = m_voltage[0].rgb[c] - m_voltage[iv+1].rgb[c]; } res[c] = div64_s64((t1 + 1) * denominator, t2) - numerator; res[c] -= mtp[c]; if (offset_color) res[c] += offset_color[ibr][iv].rgb[c]; if ((res[c] > 255) && (iv != d_aid.iv_max - 1)) res[c] = 255; } } /* iv == 0; */ vd = (int *)&d_aid.param.gamma_default[0]; res = &result[0]; for (c = 0; c < CI_MAX; c++) res[c] = vd[c]; #ifdef DYNAMIC_AID_DEBUG aid_dbg("Gamma (%d) =\n", d_aid.ibr_tbl[ibr]); for (iv = 0; iv < d_aid.iv_max; iv++) { aid_dbg("[%d] = ", iv); for (c = 0; c < CI_MAX; c++) aid_dbg("%X ", result[iv*CI_MAX+c]); aid_dbg("\n"); } #endif return ret; }
static int gadc_thermal_tdiode_adc_to_temp( struct gadc_thermal_platform_data *pdata, int *val, int *val2) { /* * Series resistance cancellation using multi-current ADC measurement. * diode temp = ((adc2 - k * adc1) - (b2 - k * b1)) / (m2 - k * m1) * - adc1 : ADC raw with current source 400uA * - m1, b1 : calculated with current source 400uA * - adc2 : ADC raw with current source 800uA * - m2, b2 : calculated with current source 800uA * - k : 2 (= 800uA / 400uA) */ const s64 m1 = -0.00571005 * TDIODE_PRECISION_MULTIPLIER; const s64 b1 = 2524.29891 * TDIODE_PRECISION_MULTIPLIER; const s64 m2 = -0.005519811 * TDIODE_PRECISION_MULTIPLIER; const s64 b2 = 2579.354349 * TDIODE_PRECISION_MULTIPLIER; s64 temp = TDIODE_PRECISION_MULTIPLIER; temp *= (s64)((*val2) - 2 * (*val)); temp -= (b2 - 2 * b1); temp = div64_s64(temp, (m2 - 2 * m1)); temp = min_t(s64, max_t(s64, temp, TDIODE_MIN_TEMP), TDIODE_MAX_TEMP); return temp; };
/* calculate maximum allowed current (in mA) limited by equivalent * series resistance (esr) */ static s64 calc_ibat_esr(s64 ocv, s64 esr) { if (ocv <= pdata->vsys_min) return 0; else if (esr <= 0) return 0; else return div64_s64(1000 * (ocv - pdata->vsys_min), esr); }
static unsigned int depl_calc(struct depl_driver *drv) { unsigned int capacity; s64 ocv; s64 rbat; s64 ibat_pos; s64 ibat_tbat; s64 ibat_lcm; s64 pbat_lcm; s64 pbat_nom; s64 pbat_gain; s64 depl; capacity = depl_psy_capacity(drv); if (capacity >= 100) return 0; ocv = drv->get_ocv(drv, capacity); rbat = depl_rbat(drv, capacity); ibat_pos = depl_ibat_possible(drv, ocv, rbat); ibat_tbat = depl_ibat(drv, depl_psy_temp(drv)); ibat_lcm = min(ibat_pos, ibat_tbat); pbat_lcm = depl_pbat(ocv, ibat_lcm, rbat); pbat_nom = depl_pbat(drv->pdata->vcharge, drv->pdata->ibat_nom, rbat); pbat_gain = div64_s64(drv->manager->max * 1000, pbat_nom); depl = drv->manager->max - div64_s64(pbat_gain * pbat_lcm, 1000); pr_debug("capacity : %u\n", capacity); pr_debug("ocv : %lld\n", ocv); pr_debug("rbat : %lld\n", rbat); pr_debug("ibat_pos : %lld\n", ibat_pos); pr_debug("ibat_tbat: %lld\n", ibat_tbat); pr_debug("ibat_lcm : %lld\n", ibat_lcm); pr_debug("pbat_lcm : %lld\n", pbat_lcm); pr_debug("pbat_nom : %lld\n", pbat_nom); pr_debug("pbat_gain: %lld\n", pbat_gain); pr_debug("depletion: %lld\n", depl); depl = clamp_t(s64, depl, 0, depl_maximum(drv)); return depl; }
/* bi-linearly interpolate from table */ static int bilinear_interpolate(int *array, int *xaxis, int *yaxis, int x_size, int y_size, int x, int y) { s64 r; int d; int yi1, yi2; int xi1, xi2; int q11, q12, q21, q22; int x1, x2, y1, y2; if (x_size <= 0 || y_size <= 0) return 0; if (x_size == 1 && y_size == 1) return array[0]; /* Given that x is within xaxis range, find x1 and x2 that * satisfy x1 >= x >= x2 */ for (xi2 = 1; xi2 < x_size - 1; xi2++) if (x > xaxis[xi2]) break; xi1 = xi2 - 1; xi2 = x_size > 1 ? xi2 : 0; x1 = xaxis[xi1]; x2 = xaxis[xi2]; for (yi2 = 1; yi2 < y_size - 1; yi2++) if (y > yaxis[yi2]) break; yi1 = yi2 - 1; yi2 = y_size > 1 ? yi2 : 0; y1 = yaxis[yi1]; y2 = yaxis[yi2]; if (x_size == 1) return interpolate(y, y1, array[yi1], y2, array[yi2]); if (y_size == 1) return interpolate(x, x1, array[xi1], x2, array[xi2]); q11 = array[xi1 + yi1 * x_size]; q12 = array[xi1 + yi2 * x_size]; q21 = array[xi2 + yi1 * x_size]; q22 = array[xi2 + yi2 * x_size]; r = (s64)q11 * (x2 - x) * (y2 - y); r += (s64)q21 * (x - x1) * (y2 - y); r += (s64)q12 * (x2 - x) * (y - y1); r += (s64)q22 * (x - x1) * (y - y1); d = ((x2-x1)*(y2-y1)); r = d ? div64_s64(r, d) : 0; return r; }
static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_comp comp, int64_t die_temp) { int64_t temp_var = 0, sign_coeff = 0, sys_gain_coeff = 0, old; old = *result; *result = *result * 1000000; if (comp.revision == QPNP_IADC_VER_3_1) { /* revision 3.1 */ if (comp.sys_gain > 127) sys_gain_coeff = -QPNP_COEFF_6 * (comp.sys_gain - 128); else sys_gain_coeff = QPNP_COEFF_6 * comp.sys_gain; } else if (comp.revision != QPNP_IADC_VER_3_0) { /* unsupported revision, do not compensate */ *result = old; return 0; } if (!comp.ext_rsense) { /* internal rsense */ switch (comp.id) { case COMP_ID_TSMC: temp_var = ((QPNP_COEFF_2 * die_temp) - QPNP_COEFF_3_TYPEB); break; case COMP_ID_GF: default: temp_var = ((QPNP_COEFF_2 * die_temp) - QPNP_COEFF_3_TYPEA); break; } temp_var = div64_s64(temp_var, QPNP_COEFF_4); if (comp.revision == QPNP_IADC_VER_3_0) temp_var = QPNP_COEFF_1 * (1000000 - temp_var); else if (comp.revision == QPNP_IADC_VER_3_1) temp_var = 1000000 * (1000000 - temp_var); *result = div64_s64(*result * 1000000, temp_var); } sign_coeff = *result < 0 ? QPNP_COEFF_7 : QPNP_COEFF_5; if (comp.ext_rsense) { /* external rsense and current charging */ temp_var = div64_s64((-sign_coeff * die_temp) + QPNP_COEFF_8, QPNP_COEFF_4); temp_var = 1000000000 - temp_var; if (comp.revision == QPNP_IADC_VER_3_1) { sys_gain_coeff = (1000000 + div64_s64(sys_gain_coeff, QPNP_COEFF_4)); temp_var = div64_s64(temp_var * sys_gain_coeff, 1000000000); } *result = div64_s64(*result, temp_var); } pr_debug("%lld compensated into %lld\n", old, *result); return 0; }
static int64_t of_batterydata_convert_battery_id_kohm(int batt_id_uv, int rpull_up, int vadc_vdd) { int64_t resistor_value_kohm, denom; if (batt_id_uv == 0) { /* vadc not correct or batt id line grounded, report 0 kohms */ return 0; } /* calculate the battery id resistance reported via ADC */ denom = div64_s64(vadc_vdd * 1000000LL, batt_id_uv) - 1000000LL; if (denom == 0) { /* batt id connector might be open, return 0 kohms */ return 0; } resistor_value_kohm = div64_s64(rpull_up * 1000000LL + denom/2, denom); pr_debug("batt id voltage = %d, resistor value = %lld\n", batt_id_uv, resistor_value_kohm); return resistor_value_kohm; }
static int64_t of_batterydata_convert_battery_id_kohm(int batt_id_uv, int rpull_up, int vadc_vdd) { int64_t resistor_value_kohm, denom; if (batt_id_uv == 0) { return 0; } denom = div64_s64(vadc_vdd * 1000000LL, batt_id_uv) - 1000000LL; if (denom == 0) { return 0; } resistor_value_kohm = div64_s64(rpull_up * 1000000LL + denom/2, denom); pr_debug("batt id voltage = %d, resistor value = %lld\n", batt_id_uv, resistor_value_kohm); return resistor_value_kohm; }
static int bdisp_dbg_perf(struct seq_file *s, void *data) { struct bdisp_dev *bdisp = s->private; struct bdisp_request *request = &bdisp->dbg.copy_request; s64 avg_time_us; int avg_fps, min_fps, max_fps, last_fps; if (!request->nb_req) { seq_puts(s, "No request\n"); return 0; } avg_time_us = div64_s64(bdisp->dbg.tot_duration, request->nb_req); if (avg_time_us > SECOND) avg_fps = 0; else avg_fps = SECOND / (s32)avg_time_us; if (bdisp->dbg.min_duration > SECOND) min_fps = 0; else min_fps = SECOND / (s32)bdisp->dbg.min_duration; if (bdisp->dbg.max_duration > SECOND) max_fps = 0; else max_fps = SECOND / (s32)bdisp->dbg.max_duration; if (bdisp->dbg.last_duration > SECOND) last_fps = 0; else last_fps = SECOND / (s32)bdisp->dbg.last_duration; seq_printf(s, "HW processing (%d requests):\n", request->nb_req); seq_printf(s, " Average: %5lld us (%3d fps)\n", avg_time_us, avg_fps); seq_printf(s, " Min-Max: %5lld us (%3d fps) - %5lld us (%3d fps)\n", bdisp->dbg.min_duration, min_fps, bdisp->dbg.max_duration, max_fps); seq_printf(s, " Last: %5lld us (%3d fps)\n", bdisp->dbg.last_duration, last_fps); return 0; }
static s64 depl_ibat_possible(struct depl_driver *drv, s64 ocv, s64 rbat) { return div64_s64(1000 * (ocv - drv->pdata->vsys_min), rbat); }
static void __vic_handler(unsigned int irq, struct irq_desc *desc) { void __iomem *base; u32 stat[2], pend = 0; int i = 0, gic = irq; int cpu, n; static u32 vic_nr[4] = { 0, 1, 0, 1}; #if defined (CONFIG_CPU_S5P4418_SMP_ISR) static u32 vic_mask[3] = { 0, } ; #endif #if (DEBUG_TIMESTAMP) long long ts = ktime_to_us(ktime_get()); static long long max = 0; #endif stat[0] = readl_relaxed(VIC0_INT_BASE+VIC_IRQ_STATUS); stat[1] = readl_relaxed(VIC1_INT_BASE+VIC_IRQ_STATUS); cpu = raw_smp_processor_id(); /* 1st usb-otg */ if (stat[1] & (1<<(IRQ_PHY_USB20OTG - 32))) { pend = (1<<(IRQ_PHY_USB20OTG - 32)); irq = IRQ_PHY_USB20OTG; writel_relaxed(0, (VIC1_INT_BASE + VIC_PL192_VECT_ADDR) ); goto irq_hnd; } /* 2nd event timer */ if (stat[0] & (1<<IRQ_PHY_TIMER_INT1)) { pend = (1<<IRQ_PHY_TIMER_INT1); irq = IRQ_PHY_TIMER_INT1; writel_relaxed(0, (VIC0_INT_BASE + VIC_PL192_VECT_ADDR) ); goto irq_hnd; } /* * Other round-robin vic groupt */ n = vic_nr[cpu]; for (i = 0; 2 > i; i++, n ^= 1) { pend = stat[n]; if (pend) { vic_nr[cpu] = !n; base = n ? VIC1_INT_BASE : VIC0_INT_BASE; irq = readl_relaxed(base + VIC_PL192_VECT_ADDR); writel_relaxed(0, base + VIC_PL192_VECT_ADDR); break; } } #if defined (CONFIG_CPU_S5P4418_SMP_ISR) pr_debug("%s: cpu.%d vic[%s] gic irq=%d, vic=%d, stat=0x%02x [0x%08x:0x%08x:0x%08x]\n", __func__, cpu, i?"1":"0", gic, irq, pend, vic_mask[0], vic_mask[1], vic_mask[2]); #endif if (0 == pend) goto irq_eoi; irq_hnd: #if defined (CONFIG_CPU_S5P4418_SMP_ISR) raw_spin_lock(&smp_irq_lock); if (vic_mask[irq>>5] & (1<<(irq&0x1f))) { writel_relaxed(31, GIC_CPUI_BASE + GIC_CPU_EOI); raw_spin_unlock(&smp_irq_lock); return; } vic_mask[irq>>5] |= (1<<(irq&0x1f)); raw_spin_unlock(&smp_irq_lock); #endif /* vic descriptor */ desc = irq_desc + irq; if (desc) generic_handle_irq_desc(irq, desc); else printk(KERN_ERR "Error, not registered vic irq=%d !!!\n", irq); #if defined (CONFIG_CPU_S5P4418_SMP_ISR) raw_spin_lock(&smp_irq_lock); vic_mask[irq>>5] &= ~(1<<(irq&0x1f)); raw_spin_unlock(&smp_irq_lock); #endif #if (DEBUG_TIMESTAMP) ts = ktime_to_us(ktime_get()) - ts; if (ts > 2000) { max = ts; printk("[cpu.%d irq.%d, %03lldms]\n", cpu, irq, div64_s64(ts, 1000)); } #endif irq_eoi: writel_relaxed(31, GIC_CPUI_BASE + GIC_CPU_EOI); return; }
static s64 calc_pbat(s64 ocv, s64 ibat, s64 esr) { s64 vsys; vsys = ocv - div64_s64(ibat * esr, 1000); return div64_s64(vsys * ibat, 1000000); }
/* * Find the maximum frequency that results in dynamic and leakage current that * is less than the regulator current limit. * temp_C - valid or -EINVAL * power_mW - valid or -1 (infinite) or -EINVAL */ static unsigned int edp_calculate_maxf( struct tegra_edp_cpu_leakage_params *params, int temp_C, int power_mW, int iddq_mA, int n_cores_idx) { unsigned int voltage_mV, freq_KHz; unsigned int cur_effective = regulator_cur - edp_reg_override_mA; int f, i, j, k; s64 leakage_mA, dyn_mA, leakage_calc_step; s64 leakage_mW, dyn_mW; for (f = freq_voltage_lut_size - 1; f >= 0; f--) { freq_KHz = freq_voltage_lut[f].freq / 1000; voltage_mV = freq_voltage_lut[f].voltage_mV; /* Constrain Volt-Temp. Eg. at Tj >= 70C, no VDD_CPU > 1.24V */ if (temp_C > params->volt_temp_cap.temperature && voltage_mV > params->volt_temp_cap.voltage_limit_mV) continue; /* Calculate leakage current */ leakage_mA = 0; for (i = 0; i <= 3; i++) { for (j = 0; j <= 3; j++) { for (k = 0; k <= 3; k++) { leakage_calc_step = params->leakage_consts_ijk [i][j][k] * edp_pow(iddq_mA, i); /* Convert (mA)^i to (A)^i */ leakage_calc_step = div64_s64(leakage_calc_step, edp_pow(1000, i)); leakage_calc_step *= edp_pow(voltage_mV, j); /* Convert (mV)^i to (V)^i */ leakage_calc_step = div64_s64(leakage_calc_step, edp_pow(1000, j)); leakage_calc_step *= edp_pow(temp_C, k); /* leakage_consts_ijk was X 100,000 */ leakage_calc_step = div64_s64(leakage_calc_step, 100000); leakage_mA += leakage_calc_step; } } } /* leakage cannot be negative => leakage model has error */ if (leakage_mA <= 0) { pr_err("VDD_CPU EDP failed: IDDQ too high (%d mA)\n", iddq_mA); return -EINVAL; } leakage_mA *= params->leakage_consts_n[n_cores_idx]; /* leakage_const_n was pre-multiplied by 1,000,000 */ leakage_mA = div64_s64(leakage_mA, 1000000); /* Calculate dynamic current */ dyn_mA = voltage_mV * freq_KHz / 1000; /* Convert mV to V */ dyn_mA = div64_s64(dyn_mA, 1000); dyn_mA *= params->dyn_consts_n[n_cores_idx]; /* dyn_const_n was pre-multiplied by 1,000,000 */ dyn_mA = div64_s64(dyn_mA, 1000000); if (power_mW != -1) { leakage_mW = leakage_mA * voltage_mV; dyn_mW = dyn_mA * voltage_mV; if (div64_s64(leakage_mW + dyn_mW, 1000) <= power_mW) return freq_KHz; } else if ((leakage_mA + dyn_mA) <= cur_effective) { return freq_KHz; } } return -EINVAL; }
static inline int32_t div_fp(s64 x, s64 y) { return div64_s64((int64_t)x << FRAC_BITS, y); }
static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_chip *iadc, int64_t die_temp) { int64_t temp_var = 0, sys_gain_coeff = 0, old; int32_t coeff_a = 0, coeff_b = 0; int version = 0; version = qpnp_adc_get_revid_version(iadc->dev); if (version == -EINVAL) return 0; old = *result; *result = *result * 1000000; if (iadc->iadc_comp.sys_gain > 127) sys_gain_coeff = -QPNP_COEFF_6 * (iadc->iadc_comp.sys_gain - 128); else sys_gain_coeff = QPNP_COEFF_6 * iadc->iadc_comp.sys_gain; switch (version) { case QPNP_REV_ID_8941_3_1: switch (iadc->iadc_comp.id) { case COMP_ID_GF: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ coeff_a = QPNP_COEFF_2; coeff_b = -QPNP_COEFF_3_TYPEA; } else { if (*result < 0) { /* charge */ coeff_a = QPNP_COEFF_5; coeff_b = QPNP_COEFF_6; } else { /* discharge */ coeff_a = -QPNP_COEFF_7; coeff_b = QPNP_COEFF_6; } } break; case COMP_ID_TSMC: default: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ coeff_a = QPNP_COEFF_2; coeff_b = -QPNP_COEFF_3_TYPEB; } else { if (*result < 0) { /* charge */ coeff_a = QPNP_COEFF_5; coeff_b = QPNP_COEFF_6; } else { /* discharge */ coeff_a = -QPNP_COEFF_7; coeff_b = QPNP_COEFF_6; } } break; } break; case QPNP_REV_ID_8026_2_1: case QPNP_REV_ID_8026_2_2: /* pm8026 rev 2.1 and 2.2 */ switch (iadc->iadc_comp.id) { case COMP_ID_GF: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ if (*result < 0) { /* charge */ coeff_a = 0; coeff_b = 0; } else { coeff_a = QPNP_COEFF_25; coeff_b = 0; } } else { if (*result < 0) { /* charge */ coeff_a = 0; coeff_b = 0; } else { /* discharge */ coeff_a = 0; coeff_b = 0; } } break; case COMP_ID_TSMC: default: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ if (*result < 0) { /* charge */ coeff_a = 0; coeff_b = 0; } else { coeff_a = QPNP_COEFF_26; coeff_b = 0; } } else { if (*result < 0) { /* charge */ coeff_a = 0; coeff_b = 0; } else { /* discharge */ coeff_a = 0; coeff_b = 0; } } break; } break; case QPNP_REV_ID_8026_1_0: /* pm8026 rev 1.0 */ switch (iadc->iadc_comp.id) { case COMP_ID_GF: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ if (*result < 0) { /* charge */ coeff_a = QPNP_COEFF_9; coeff_b = -QPNP_COEFF_17; } else { coeff_a = QPNP_COEFF_10; coeff_b = QPNP_COEFF_18; } } else { if (*result < 0) { /* charge */ coeff_a = -QPNP_COEFF_11; coeff_b = 0; } else { /* discharge */ coeff_a = -QPNP_COEFF_17; coeff_b = -QPNP_COEFF_19; } } break; case COMP_ID_TSMC: default: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ if (*result < 0) { /* charge */ coeff_a = QPNP_COEFF_13; coeff_b = -QPNP_COEFF_20; } else { coeff_a = QPNP_COEFF_14; coeff_b = QPNP_COEFF_21; } } else { if (*result < 0) { /* charge */ coeff_a = -QPNP_COEFF_15; coeff_b = 0; } else { /* discharge */ coeff_a = -QPNP_COEFF_12; coeff_b = -QPNP_COEFF_19; } } break; } break; case QPNP_REV_ID_8110_1_0: /* pm8110 rev 1.0 */ switch (iadc->iadc_comp.id) { case COMP_ID_GF: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ if (*result < 0) { /* charge */ coeff_a = QPNP_COEFF_24; coeff_b = -QPNP_COEFF_22; } else { coeff_a = QPNP_COEFF_24; coeff_b = -QPNP_COEFF_23; } } break; case COMP_ID_SMIC: default: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ if (*result < 0) { /* charge */ coeff_a = QPNP_COEFF_24; coeff_b = -QPNP_COEFF_22; } else { coeff_a = QPNP_COEFF_24; coeff_b = -QPNP_COEFF_23; } } break; } break; case QPNP_REV_ID_8110_2_0: die_temp -= 25000; /* pm8110 rev 2.0 */ switch (iadc->iadc_comp.id) { case COMP_ID_GF: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ if (*result < 0) { /* charge */ coeff_a = 0; coeff_b = 0; } else { coeff_a = QPNP_COEFF_27; coeff_b = 0; } } break; case COMP_ID_SMIC: default: if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ if (*result < 0) { /* charge */ coeff_a = 0; coeff_b = 0; } else { coeff_a = QPNP_COEFF_28; coeff_b = 0; } } break; } break; default: case QPNP_REV_ID_8026_2_0: /* pm8026 rev 1.0 */ coeff_a = 0; coeff_b = 0; break; } temp_var = (coeff_a * die_temp) + coeff_b; temp_var = div64_s64(temp_var, QPNP_COEFF_4); temp_var = 1000 * (1000000 - temp_var); if (!iadc->iadc_comp.ext_rsense) { /* internal rsense */ *result = div64_s64(*result * 1000, temp_var); } if (iadc->iadc_comp.ext_rsense) { /* external rsense */ sys_gain_coeff = (1000000 + div64_s64(sys_gain_coeff, QPNP_COEFF_4)); temp_var = div64_s64(temp_var * sys_gain_coeff, 1000000); *result = div64_s64(*result * 1000, temp_var); } pr_debug("%lld compensated into %lld, a: %d, b: %d, sys_gain: %lld\n", old, *result, coeff_a, coeff_b, sys_gain_coeff); return 0; }
/* * Find the maximum frequency that results in dynamic and leakage current that * is less than the regulator current limit. * temp_C - valid or -EINVAL * power_mW - valid or -1 (infinite) or -EINVAL */ static unsigned int edp_calculate_maxf( struct tegra_edp_cpu_leakage_params *params, int temp_C, int power_mW, int iddq_mA, int n_cores_idx) { unsigned int voltage_mV, freq_KHz = 0; unsigned int cur_effective = regulator_cur - edp_reg_override_mA; int f, i, j, k, r = 0; s64 leakage_mA, dyn_mA, leakage_calc_step; s64 leakage_mW, dyn_mW; for (f = freq_voltage_lut_size - 1; f >= 0; f--) { freq_KHz = freq_voltage_lut[f].freq / 1000; voltage_mV = freq_voltage_lut[f].voltage_mV; /* Constrain Volt-Temp. Eg. at Tj >= 70C, no VDD_CPU > 1.24V */ if (temp_C > params->volt_temp_cap.temperature && voltage_mV > params->volt_temp_cap.voltage_limit_mV) continue; /* Calculate leakage current */ leakage_mA = 0; for (i = 0; i <= 3; i++) { for (j = 0; j <= 3; j++) { for (k = 0; k <= 3; k++) { leakage_calc_step = params->leakage_consts_ijk [i][j][k] * edp_pow(iddq_mA, i); /* Convert (mA)^i to (A)^i */ leakage_calc_step = div64_s64(leakage_calc_step, edp_pow(1000, i)); leakage_calc_step *= edp_pow(voltage_mV, j); /* Convert (mV)^j to (V)^j */ leakage_calc_step = div64_s64(leakage_calc_step, edp_pow(1000, j)); leakage_calc_step *= edp_pow(temp_C, k); /* Convert (C)^k to (scaled_C)^k */ leakage_calc_step = div64_s64(leakage_calc_step, edp_pow(params->temp_scaled, k)); /* leakage_consts_ijk was scaled */ leakage_calc_step = div64_s64(leakage_calc_step, params->ijk_scaled); leakage_mA += leakage_calc_step; } } } /* if specified, set floor for leakage current */ if (params->leakage_min && leakage_mA <= params->leakage_min) leakage_mA = params->leakage_min; /* leakage cannot be negative => leakage model has error */ if (leakage_mA <= 0) { pr_err("VDD_CPU EDP failed: IDDQ too high (%d mA)\n", iddq_mA); r = -EINVAL; goto end; } leakage_mA *= params->leakage_consts_n[n_cores_idx]; /* leakage_const_n was scaled */ leakage_mA = div64_s64(leakage_mA, params->consts_scaled); /* Calculate dynamic current */ dyn_mA = voltage_mV * freq_KHz / 1000; /* Convert mV to V */ dyn_mA = div64_s64(dyn_mA, 1000); dyn_mA *= params->dyn_consts_n[n_cores_idx]; /* dyn_const_n was scaled */ dyn_mA = div64_s64(dyn_mA, params->dyn_scaled); if (power_mW != -1) { leakage_mW = leakage_mA * voltage_mV; dyn_mW = dyn_mA * voltage_mV; if (div64_s64(leakage_mW + dyn_mW, 1000) <= power_mW) goto end; } else if ((leakage_mA + dyn_mA) <= cur_effective) { goto end; } freq_KHz = 0; } end: if (r != 0) return r; return edp_apply_fixed_limits(freq_KHz, params, cur_effective, temp_C, n_cores_idx); }