Fixes for NVIDIA Tegra124 clocks:

- EMC clock have standard peripheral clock block. Use it.
 - Implement full frequency set method for PLLD2. This PLL
   is used as HDMI pixel clock so we must be able to set it
   to wide range of frequencies, within 5% tolerance allowed
   by HDMI specification. Due to this, full state space search
   (over m, n, p fields) is necessary.

MFC after: 3 weeks
This commit is contained in:
Michal Meloun 2016-12-04 16:04:22 +00:00
parent 3b29466b94
commit 6f1eb3052e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=309538
3 changed files with 90 additions and 19 deletions

View File

@ -189,8 +189,6 @@ PLIST(mux_pll_srcs) = {"osc_div_clk", NULL, "pllP_out0", NULL}; /* FIXME */
PLIST(mux_plle_src1) = {"osc_div_clk", "pllP_out0"};
PLIST(mux_plle_src) = {"pllE_src1", "pllREFE_out"};
PLIST(mux_plld_out0_plld2_out0) = {"pllD_out0", "pllD2_out0"};
PLIST(mux_pllmcp_clkm) = {"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
"pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"};
PLIST(mux_xusb_hs) = {"xusb_ss_div2", "pllU_60"};
PLIST(mux_xusb_ss) = {"pc_xusb_ss", "osc_div_clk"};
@ -240,7 +238,6 @@ static struct clk_mux_def tegra124_mux_clks[] = {
/* Base peripheral clocks. */
MUX(0, "dsia_mux", mux_plld_out0_plld2_out0, PLLD_BASE, 25, 1),
MUX(0, "dsib_mux", mux_plld_out0_plld2_out0, PLLD2_BASE, 25, 1),
MUX(0, "emc_mux", mux_pllmcp_clkm, CLK_SOURCE_EMC, 29, 3),
/* USB. */
MUX(TEGRA124_CLK_XUSB_HS_SRC, "xusb_hs", mux_xusb_hs, CLK_SOURCE_XUSB_SS, 25, 1),

View File

@ -126,6 +126,10 @@ PLIST(mux_m_c_p_a_c2_c3_clkm_c4) =
PLIST(mux_m_c_p_clkm_mud_c2_c3) =
{"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
"pllM_UD", "pllC2_out0", "pllC3_out0"};
PLIST(mux_m_c_p_clkm_mud_c2_c3_cud) =
{"pllM_out0", "pllC_out0", "pllP_out0", "clk_m",
"pllM_UD", "pllC2_out0", "pllC3_out0", "pllC_UD"};
PLIST(mux_m_c2_c_c3_p_N_a) =
{"pllM_out0", "pllC2_out0", "pllC_out0", "pllC3_out0",
"pllP_out0", NULL, "pllA_out0"};
@ -260,7 +264,7 @@ static struct pgate_def pgate_def[] = {
GATE(I2C2, "i2c2", "pc_i2c2", H(22)),
GATE(UARTC, "uartc", "pc_uartc", H(23)),
GATE(MIPI_CAL, "mipi_cal", "clk_m", H(24)),
GATE(EMC, "emc", "emc_mux", H(25)),
GATE(EMC, "emc", "pc_emc_2x", H(25)),
GATE(USB2, "usb2", "clk_m", H(26)),
GATE(USB3, "usb3", "clk_m", H(27)),
GATE(VDE, "vde", "pc_vde", H(29)),
@ -356,7 +360,7 @@ static struct pgate_def pgate_def[] = {
/* GATE(CAM_MCLK, "CAM_MCLK", "clk_m", X(4)), */
/* GATE(CAM_MCLK2, "CAM_MCLK2", "clk_m", X(5)), */
GATE(I2C6, "i2c6", "pc_i2c6", X(6)),
/* GATE(VIM2_CLK, "vim2_clk", clk_m, X(11)), */
GATE(VIM2_CLK, "vim2_clk", "clk_m", X(11)),
/* GATE(EMC_DLL, "emc_dll", "pc_emc_dll", X(14)), */
GATE(HDMI_AUDIO, "hdmi_audio", "pc_hdmi_audio", X(16)),
GATE(CLK72MHZ, "clk72mhz", "pc_clk72mhz", X(17)),
@ -373,17 +377,18 @@ static struct pgate_def pgate_def[] = {
#define DCF_HAVE_ENA 0x0200 /* Block with enable bit */
#define DCF_HAVE_DIV 0x0400 /* Block with divider */
/* Mark block with additional bis / functionality. */
/* Mark block with additional bits / functionality. */
#define DCF_IS_MASK 0x00FF
#define DCF_IS_UART 0x0001
#define DCF_IS_VI 0x0002
#define DCF_IS_HOST1X 0x0003
#define DCF_IS_XUSB_SS 0x0004
#define DCF_IS_EMC_DLL 0x0005
#define FDS_IS_SATA 0x0006
#define DCF_IS_SATA 0x0006
#define DCF_IS_VIC 0x0007
#define DCF_IS_AUDIO 0x0008
#define DCF_IS_SOR0 0x0009
#define DCF_IS_EMC 0x000A
/* Basic pheripheral clock */
#define PER_CLK(_id, cn, pl, r, diw, fiw, f) \
@ -438,7 +443,7 @@ static struct periph_def periph_def[] = {
CLK_8_1(0, "pc_host1x", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HOST1X, DCF_IS_HOST1X),
CLK_8_1(0, "pc_hdmi", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_HDMI, 0),
CLK16_0(0, "pc_i2c2", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_I2C2, 0),
/* EMC 8 */
CLK_8_1(0, "pc_emc_2x", mux_m_c_p_clkm_mud_c2_c3_cud, CLK_SOURCE_EMC, DCF_IS_EMC),
CLK16_1(0, "pc_uartc", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_UARTC, DCF_IS_UART),
CLK_8_1(0, "pc_vi_sensor", mux_m_c2_c_c3_p_N_a, CLK_SOURCE_VI_SENSOR, 0),
CLK_8_1(0, "pc_spi4", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_SPI4, 0),
@ -476,7 +481,7 @@ static struct periph_def periph_def[] = {
/* SYS */
CLK_8_1(0, "pc_sor0", mux_p_m_d_a_c_d2_clkm, CLK_SOURCE_SOR0, DCF_IS_SOR0),
CLK_8_1(0, "pc_sata_oob", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA_OOB, 0),
CLK_8_1(0, "pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, FDS_IS_SATA),
CLK_8_1(0, "pc_sata", mux_p_N_c_N_m_N_clkm, CLK_SOURCE_SATA, DCF_IS_SATA),
CLK_8_1(0, "pc_hda", mux_p_c2_c_c3_m_N_clkm, CLK_SOURCE_HDA, 0),
CLK_8_1(TEGRA124_CLK_XUSB_HOST_SRC,
"pc_xusb_core_host", mux_clkm_p_c2_c_c3_refre, CLK_SOURCE_XUSB_CORE_HOST, 0),

View File

@ -713,6 +713,7 @@ pll_set_std(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags,
return (ERANGE);
*fout = ((fin / m) * n) /p;
return (0);
}
@ -771,7 +772,6 @@ pllc_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
{
uint32_t m, n, p;
p = 2;
m = 1;
n = (*fout * p * m + fin / 2)/ fin;
@ -779,20 +779,89 @@ pllc_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
return (pll_set_std( sc, fin, fout, flags, m, n, p));
}
/*
* PLLD2 is used as source for pixel clock for HDMI.
* We must be able to set it frequency very flexibly and
* precisely (within 5% tolerance limit allowed by HDMI specs).
*
* For this reason, it is necessary to search the full state space.
* Fortunately, thanks to early cycle terminations, performance
* is within acceptable limits.
*/
#define PLLD2_PFD_MIN 12000000 /* 12 MHz */
#define PLLD2_PFD_MAX 38000000 /* 38 MHz */
#define PLLD2_VCO_MIN 600000000 /* 600 MHz */
#define PLLD2_VCO_MAX 1200000000 /* 1.2 GHz */
static int
plld2_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
{
uint32_t m, n, p;
uint32_t best_m, best_n, best_p;
uint64_t vco, pfd;
int64_t err, best_err;
struct mnp_bits *mnp_bits;
struct pdiv_table *tbl;
int p_idx, rv;
p = 2;
m = 1;
n = (*fout * p * m + fin / 2)/ fin;
dprintf("%s: m: %d, n: %d, p: %d\n", __func__, m, n, p);
return (pll_set_std(sc, fin, fout, flags, m, n, p));
mnp_bits = &sc->mnp_bits;
tbl = sc->pdiv_table;
best_err = INT64_MAX;
for (p_idx = 0; tbl[p_idx].divider != 0; p_idx++) {
p = tbl[p_idx].divider;
/* Check constraints */
vco = *fout * p;
if (vco < PLLD2_VCO_MIN)
continue;
if (vco > PLLD2_VCO_MAX)
break;
for (m = 1; m < (1 << mnp_bits->m_width); m++) {
n = (*fout * p * m + fin / 2) / fin;
/* Check constraints */
if (n == 0)
continue;
if (n >= (1 << mnp_bits->n_width))
break;
vco = (fin * n) / m;
if (vco > PLLD2_VCO_MAX || vco < PLLD2_VCO_MIN)
continue;
pfd = fin / m;
if (pfd > PLLD2_PFD_MAX || vco < PLLD2_PFD_MIN)
continue;
/* Constraints passed, save best result */
err = *fout - vco / p;
if (err < 0)
err = -err;
if (err < best_err) {
best_err = err;
best_p = p;
best_m = m;
best_n = n;
}
if (err == 0)
goto done;
}
}
done:
/*
* HDMI specification allows 5% pixel clock tolerance,
* we will by a slightly stricter
*/
if (best_err > ((*fout * 100) / 4))
return (ERANGE);
if (flags & CLK_SET_DRYRUN)
return (0);
rv = pll_set_std(sc, fin, fout, flags, best_m, best_n, best_p);
/* XXXX Panic for rv == ERANGE ? */
return (rv);
}
static int
pllrefe_set_freq(struct pll_sc *sc, uint64_t fin, uint64_t *fout, int flags)
{
@ -883,8 +952,8 @@ tegra124_pll_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout,
struct pll_sc *sc;
sc = clknode_get_softc(clknode);
dprintf("%s: Requested freq: %llu, input freq: %llu\n", __func__,
*fout, fin);
dprintf("%s: %s requested freq: %llu, input freq: %llu\n", __func__,
clknode_get_name(clknode), *fout, fin);
switch (sc->type) {
case PLL_A:
rv = plla_set_freq(sc, fin, fout, flags);