Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
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;
};
Пример #5
0
/* 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);
}
Пример #6
0
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;
}
Пример #7
0
/* 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;
}
Пример #10
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) {
		
		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;
}
Пример #11
0
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;
}
Пример #12
0
static s64 depl_ibat_possible(struct depl_driver *drv, s64 ocv, s64 rbat)
{
	return div64_s64(1000 * (ocv - drv->pdata->vsys_min), rbat);
}
Пример #13
0
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;
}
Пример #14
0
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);
}
Пример #15
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;
	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;
}
Пример #16
0
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;
}
Пример #18
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);
}