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, ®); 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"); }
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, ®); 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; }
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; }