static bool arizona_is_enabled_fll(struct arizona_fll *fll)
{
	struct arizona *arizona = fll->arizona;
	unsigned int reg;
	int ret;

	ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
	if (ret != 0) {
		arizona_fll_err(fll, "Failed to read current state: %d\n",
				ret);
		return ret;
	}

	return reg & ARIZONA_FLL1_ENA;
}
static void arizona_enable_fll(struct arizona_fll *fll,
			      struct arizona_fll_cfg *ref,
			      struct arizona_fll_cfg *sync)
{
	struct arizona *arizona = fll->arizona;
	int ret;
	bool use_sync = false;

	/*
	 * If we have both REFCLK and SYNCCLK then enable both,
	 * otherwise apply the SYNCCLK settings to REFCLK.
	 */
	if (fll->ref_src >= 0 && fll->ref_freq &&
	    fll->ref_src != fll->sync_src) {
		regmap_update_bits(arizona->regmap, fll->base + 5,
				   ARIZONA_FLL1_OUTDIV_MASK,
				   ref->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);

		arizona_apply_fll(arizona, fll->base, ref, fll->ref_src,
				  false);
		if (fll->sync_src >= 0) {
			arizona_apply_fll(arizona, fll->base + 0x10, sync,
					  fll->sync_src, true);
			use_sync = true;
		}
	} else if (fll->sync_src >= 0) {
		regmap_update_bits(arizona->regmap, fll->base + 5,
				   ARIZONA_FLL1_OUTDIV_MASK,
				   sync->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);

		arizona_apply_fll(arizona, fll->base, sync,
				  fll->sync_src, false);

		regmap_update_bits(arizona->regmap, fll->base + 0x11,
				   ARIZONA_FLL1_SYNC_ENA, 0);
	} else {
		arizona_fll_err(fll, "No clocks provided\n");
		return;
	}

	/*
	 * Increase the bandwidth if we're not using a low frequency
	 * sync source.
	 */
	if (use_sync && fll->sync_freq > 100000)
		regmap_update_bits(arizona->regmap, fll->base + 0x17,
				   ARIZONA_FLL1_SYNC_BW, 0);
	else
		regmap_update_bits(arizona->regmap, fll->base + 0x17,
				   ARIZONA_FLL1_SYNC_BW, ARIZONA_FLL1_SYNC_BW);

	if (!arizona_is_enabled_fll(fll))
		pm_runtime_get(arizona->dev);

	/* Clear any pending completions */
	try_wait_for_completion(&fll->ok);

	regmap_update_bits(arizona->regmap, fll->base + 1,
			   ARIZONA_FLL1_FREERUN, 0);
	regmap_update_bits(arizona->regmap, fll->base + 1,
			   ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
	if (use_sync)
		regmap_update_bits(arizona->regmap, fll->base + 0x11,
				   ARIZONA_FLL1_SYNC_ENA,
				   ARIZONA_FLL1_SYNC_ENA);

	ret = wait_for_completion_timeout(&fll->ok,
					  msecs_to_jiffies(250));
	if (ret == 0)
		arizona_fll_warn(fll, "Timed out waiting for lock\n");
}
Пример #3
0
int arizona_set_fll(struct arizona_fll *fll, int source,
		    unsigned int Fref, unsigned int Fout)
{
	struct arizona *arizona = fll->arizona;
	struct arizona_fll_cfg cfg, sync;
	unsigned int reg, val;
	int syncsrc;
	bool ena;
	int ret;

	ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
	if (ret != 0) {
		arizona_fll_err(fll, "Failed to read current state: %d\n",
				ret);
		return ret;
	}
	ena = reg & ARIZONA_FLL1_ENA;

	if (Fout) {
		/* Do we have a 32kHz reference? */
		regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val);
		switch (val & ARIZONA_CLK_32K_SRC_MASK) {
		case ARIZONA_CLK_SRC_MCLK1:
		case ARIZONA_CLK_SRC_MCLK2:
			syncsrc = val & ARIZONA_CLK_32K_SRC_MASK;
			break;
		default:
			syncsrc = -1;
		}

		if (source == syncsrc)
			syncsrc = -1;

		if (syncsrc >= 0) {
			ret = arizona_calc_fll(fll, &sync, Fref, Fout);
			if (ret != 0)
				return ret;

			ret = arizona_calc_fll(fll, &cfg, 32768, Fout);
			if (ret != 0)
				return ret;
		} else {
			ret = arizona_calc_fll(fll, &cfg, Fref, Fout);
			if (ret != 0)
				return ret;
		}
	} else {
		regmap_update_bits(arizona->regmap, fll->base + 1,
				   ARIZONA_FLL1_ENA, 0);
		regmap_update_bits(arizona->regmap, fll->base + 0x11,
				   ARIZONA_FLL1_SYNC_ENA, 0);

		if (ena)
			pm_runtime_put_autosuspend(arizona->dev);

		return 0;
	}

	regmap_update_bits(arizona->regmap, fll->base + 5,
			   ARIZONA_FLL1_OUTDIV_MASK,
			   cfg.outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);

	if (syncsrc >= 0) {
		arizona_apply_fll(arizona, fll->base, &cfg, syncsrc);
		arizona_apply_fll(arizona, fll->base + 0x10, &sync, source);
	} else {
		arizona_apply_fll(arizona, fll->base, &cfg, source);
	}

	if (!ena)
		pm_runtime_get(arizona->dev);

	/* Clear any pending completions */
	try_wait_for_completion(&fll->ok);

	regmap_update_bits(arizona->regmap, fll->base + 1,
			   ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
	if (syncsrc >= 0)
		regmap_update_bits(arizona->regmap, fll->base + 0x11,
				   ARIZONA_FLL1_SYNC_ENA,
				   ARIZONA_FLL1_SYNC_ENA);

	ret = wait_for_completion_timeout(&fll->ok,
					  msecs_to_jiffies(25));
	if (ret == 0)
		arizona_fll_warn(fll, "Timed out waiting for lock\n");

	return 0;
}
static int arizona_calc_fll(struct arizona_fll *fll,
			    struct arizona_fll_cfg *cfg,
			    unsigned int Fref,
			    unsigned int Fout)
{
	unsigned int target, div, gcd_fll;
	int i, ratio;

	arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout);

	/* Fref must be <=13.5MHz */
	div = 1;
	cfg->refdiv = 0;
	while ((Fref / div) > 13500000) {
		div *= 2;
		cfg->refdiv++;

		if (div > 8) {
			arizona_fll_err(fll,
					"Can't scale %dMHz in to <=13.5MHz\n",
					Fref);
			return -EINVAL;
		}
	}

	/* Apply the division for our remaining calculations */
	Fref /= div;

	/* Fvco should be over the targt; don't check the upper bound */
	div = 1;
	while (Fout * div < 90000000 * fll->vco_mult) {
		div++;
		if (div > 7) {
			arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
					Fout);
			return -EINVAL;
		}
	}
	target = Fout * div / fll->vco_mult;
	cfg->outdiv = div;

	arizona_fll_dbg(fll, "Fvco=%dHz\n", target);

	/* Find an appropraite FLL_FRATIO and factor it out of the target */
	for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
		if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
			cfg->fratio_ref = arizona_fratio_ref(fll->arizona, i);
			cfg->fratio_sync = arizona_fratio_sync(fll->arizona, i);
			ratio = fll_fratios[i].ratio;
			break;
		}
	}
	if (i == ARRAY_SIZE(fll_fratios)) {
		arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
				Fref);
		return -EINVAL;
	}

	for (i = 0; i < ARRAY_SIZE(fll_gains); i++) {
		if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) {
			cfg->gain = fll_gains[i].gain;
			break;
		}
	}
	if (i == ARRAY_SIZE(fll_gains)) {
		arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n",
				Fref);
		return -EINVAL;
	}

	cfg->n = target / (ratio * Fref);

	if (target % (ratio * Fref)) {
		gcd_fll = gcd(target, ratio * Fref);
		arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);

		cfg->theta = (target - (cfg->n * ratio * Fref))
			/ gcd_fll;
		cfg->lambda = (ratio * Fref) / gcd_fll;
	} else {
		cfg->theta = 0;
		cfg->lambda = 0;
	}

	/* Round down to 16bit range with cost of accuracy lost.
	 * Denominator must be bigger than numerator so we only
	 * take care of it.
	 */
	while (cfg->lambda >= (1 << 16)) {
		cfg->theta >>= 1;
		cfg->lambda >>= 1;
	}

	arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
			cfg->n, cfg->theta, cfg->lambda);
	arizona_fll_dbg(fll, "FRATIO_REF=%x(%d) FRATIO_SYNC=%x(%d)\n",
			cfg->fratio_ref, cfg->fratio_ref,
			cfg->fratio_sync, cfg->fratio_sync);
	arizona_fll_dbg(fll, "OUTDIV=%x REFCLK_DIV=%x\n",
			cfg->outdiv, cfg->refdiv);
	arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);

	return 0;

}
Пример #5
0
static int arizona_calc_fll(struct arizona_fll *fll,
			    struct arizona_fll_cfg *cfg,
			    unsigned int Fref,
			    unsigned int Fout)
{
	unsigned int target, div, gcd_fll;
	int i, ratio;

	arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout);

	/* Fref must be <=13.5MHz */
	div = 1;
	cfg->refdiv = 0;
	while ((Fref / div) > 13500000) {
		div *= 2;
		cfg->refdiv++;

		if (div > 8) {
			arizona_fll_err(fll,
					"Can't scale %dMHz in to <=13.5MHz\n",
					Fref);
			return -EINVAL;
		}
	}

	/* Apply the division for our remaining calculations */
	Fref /= div;

	/* Fvco should be over the targt; don't check the upper bound */
	div = 1;
	while (Fout * div < 90000000 * fll->vco_mult) {
		div++;
		if (div > 7) {
			arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
					Fout);
			return -EINVAL;
		}
	}
	target = Fout * div / fll->vco_mult;
	cfg->outdiv = div;

	arizona_fll_dbg(fll, "Fvco=%dHz\n", target);

	/* Find an appropraite FLL_FRATIO and factor it out of the target */
	for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
		if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
			cfg->fratio = fll_fratios[i].fratio;
			ratio = fll_fratios[i].ratio;
			break;
		}
	}
	if (i == ARRAY_SIZE(fll_fratios)) {
		arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
				Fref);
		return -EINVAL;
	}

	cfg->n = target / (ratio * Fref);

	if (target % Fref) {
		gcd_fll = gcd(target, ratio * Fref);
		arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);

		cfg->theta = (target - (cfg->n * ratio * Fref))
			/ gcd_fll;
		cfg->lambda = (ratio * Fref) / gcd_fll;
	} else {
		cfg->theta = 0;
		cfg->lambda = 0;
	}

	arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
			cfg->n, cfg->theta, cfg->lambda);
	arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
			cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);

	return 0;

}