static int tegra_tsensor_nvmem_setup()

in tegra/tegra30-tsensor.c [429:507]


static int tegra_tsensor_nvmem_setup(struct tegra_tsensor *ts)
{
	u32 i, ate_ver = 0, cal = 0, t1_25C = 0, t2_90C = 0;
	int err, c1_25C, c2_90C;

	err = tegra_fuse_readl(TEGRA30_FUSE_TEST_PROG_VER, &ate_ver);
	if (err) {
		dev_err_probe(ts->dev, err, "failed to get ATE version\n");
		return err;
	}

	if (ate_ver < 8) {
		dev_info(ts->dev, "unsupported ATE version: %u\n", ate_ver);
		return -ENODEV;
	}

	/*
	 * We have two TSENSOR channels in a two different spots on SoC.
	 * Second channel provides more accurate data on older SoC versions,
	 * use it as a primary channel.
	 */
	if (ate_ver <= 21) {
		dev_info_once(ts->dev,
			      "older ATE version detected, channels remapped\n");
		ts->swap_channels = true;
	}

	err = tegra_fuse_readl(TEGRA30_FUSE_TSENSOR_CALIB, &cal);
	if (err) {
		dev_err(ts->dev, "failed to get calibration data: %d\n", err);
		return err;
	}

	/* get calibrated counter values for 25C/90C thresholds */
	c1_25C = FIELD_GET(TEGRA30_FUSE_TSENSOR_CALIB_LOW, cal);
	c2_90C = FIELD_GET(TEGRA30_FUSE_TSENSOR_CALIB_HIGH, cal);

	/* and calibrated temperatures corresponding to the counter values */
	for (i = 0; i < 7; i++) {
		t1_25C |= tegra_tsensor_fuse_read_spare(14 + i) << i;
		t1_25C |= tegra_tsensor_fuse_read_spare(21 + i) << i;

		t2_90C |= tegra_tsensor_fuse_read_spare(0 + i) << i;
		t2_90C |= tegra_tsensor_fuse_read_spare(7 + i) << i;
	}

	if (c2_90C - c1_25C <= t2_90C - t1_25C) {
		dev_err(ts->dev, "invalid calibration data: %d %d %u %u\n",
			c2_90C, c1_25C, t2_90C, t1_25C);
		return -EINVAL;
	}

	/* all calibration coefficients are premultiplied by 1000000 */

	ts->calib.a = DIV_ROUND_CLOSEST((t2_90C - t1_25C) * 1000000,
					(c2_90C - c1_25C));

	ts->calib.b = t1_25C * 1000000 - ts->calib.a * c1_25C;

	if (tegra_sku_info.revision == TEGRA_REVISION_A01) {
		ts->calib.m =     -2775;
		ts->calib.n =   1338811;
		ts->calib.p =  -7300000;
	} else {
		ts->calib.m =     -3512;
		ts->calib.n =   1528943;
		ts->calib.p = -11100000;
	}

	/* except the coefficient of a reduced quadratic equation */
	ts->calib.r = DIV_ROUND_CLOSEST(ts->calib.n, ts->calib.m * 2);

	dev_info_once(ts->dev,
		      "calibration: %d %d %u %u ATE ver: %u SoC rev: %u\n",
		      c2_90C, c1_25C, t2_90C, t1_25C, ate_ver,
		      tegra_sku_info.revision);

	return 0;
}