diff --git a/drivers/gpu/nvgpu/clk/clk.c b/drivers/gpu/nvgpu/clk/clk.c index 7ee4f2839..918cd43c5 100644 --- a/drivers/gpu/nvgpu/clk/clk.c +++ b/drivers/gpu/nvgpu/clk/clk.c @@ -104,7 +104,7 @@ done: return status; } -u32 clk_pmu_vf_inject(struct gk20a *g) +static u32 clk_pmu_vf_inject(struct gk20a *g, struct set_fll_clk *setfllclk) { struct pmu_cmd cmd; struct pmu_msg msg; @@ -115,35 +115,48 @@ u32 clk_pmu_vf_inject(struct gk20a *g) struct clkrpc_pmucmdhandler_params handler = {0}; struct nv_pmu_clk_vf_change_inject *vfchange; + if ((setfllclk->gpc2clkmhz == 0) || (setfllclk->xbar2clkmhz == 0) || + (setfllclk->sys2clkmhz == 0) || (setfllclk->voltuv == 0)) + return -EINVAL; + + if ((setfllclk->target_regime_id_gpc > CTRL_CLK_FLL_REGIME_ID_FR) || + (setfllclk->target_regime_id_sys > CTRL_CLK_FLL_REGIME_ID_FR) || + (setfllclk->target_regime_id_xbar > CTRL_CLK_FLL_REGIME_ID_FR)) + return -EINVAL; + rpccall.function = NV_PMU_CLK_RPC_ID_CLK_VF_CHANGE_INJECT; vfchange = &rpccall.params.clk_vf_change_inject; vfchange->flags = 0; vfchange->clk_list.num_domains = 3; vfchange->clk_list.clk_domains[0].clk_domain = CTRL_CLK_DOMAIN_GPC2CLK; - vfchange->clk_list.clk_domains[0].clk_freq_khz = 2581 * 1000; + vfchange->clk_list.clk_domains[0].clk_freq_khz = + setfllclk->gpc2clkmhz * 1000; vfchange->clk_list.clk_domains[0].clk_flags = 0; vfchange->clk_list.clk_domains[0].current_regime_id = - CTRL_CLK_FLL_REGIME_ID_FFR; + setfllclk->current_regime_id_gpc; vfchange->clk_list.clk_domains[0].target_regime_id = - CTRL_CLK_FLL_REGIME_ID_FR; + setfllclk->target_regime_id_gpc; vfchange->clk_list.clk_domains[1].clk_domain = CTRL_CLK_DOMAIN_XBAR2CLK; - vfchange->clk_list.clk_domains[1].clk_freq_khz = 2505 * 1000; + vfchange->clk_list.clk_domains[1].clk_freq_khz = + setfllclk->xbar2clkmhz * 1000; vfchange->clk_list.clk_domains[1].clk_flags = 0; vfchange->clk_list.clk_domains[1].current_regime_id = - CTRL_CLK_FLL_REGIME_ID_FFR; + setfllclk->current_regime_id_xbar; vfchange->clk_list.clk_domains[1].target_regime_id = - CTRL_CLK_FLL_REGIME_ID_FR; + setfllclk->target_regime_id_xbar; vfchange->clk_list.clk_domains[2].clk_domain = CTRL_CLK_DOMAIN_SYS2CLK; - vfchange->clk_list.clk_domains[2].clk_freq_khz = 2328 * 1000; + vfchange->clk_list.clk_domains[2].clk_freq_khz = + setfllclk->sys2clkmhz * 1000; vfchange->clk_list.clk_domains[2].clk_flags = 0; vfchange->clk_list.clk_domains[2].current_regime_id = - CTRL_CLK_FLL_REGIME_ID_FFR; + setfllclk->current_regime_id_sys; vfchange->clk_list.clk_domains[2].target_regime_id = - CTRL_CLK_FLL_REGIME_ID_FR; + setfllclk->target_regime_id_sys; vfchange->volt_list.num_rails = 1; vfchange->volt_list.rails[0].volt_domain = CTRL_VOLT_DOMAIN_LOGIC; - vfchange->volt_list.rails[0].voltage_uv = 825000; - vfchange->volt_list.rails[0].voltage_min_noise_unaware_uv = 825000; + vfchange->volt_list.rails[0].voltage_uv = setfllclk->voltuv; + vfchange->volt_list.rails[0].voltage_min_noise_unaware_uv = + setfllclk->voltuv; cmd.hdr.unit_id = PMU_UNIT_CLK; cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + @@ -189,6 +202,198 @@ done: return status; } +static u32 find_regime_id(struct gk20a *g, u32 domain, u16 clkmhz) +{ + struct fll_device *pflldev; + u8 j; + struct clk_pmupstate *pclk = &g->clk_pmu; + + BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), + struct fll_device *, pflldev, j) { + if (pflldev->clk_domain == domain) { + if (pflldev->regime_desc.fixed_freq_regime_limit_mhz >= + clkmhz) + return CTRL_CLK_FLL_REGIME_ID_FR; + else + return CTRL_CLK_FLL_REGIME_ID_FFR; + } + } + return CTRL_CLK_FLL_REGIME_ID_INVALID; +} + +static int set_regime_id(struct gk20a *g, u32 domain, u32 regimeid) +{ + struct fll_device *pflldev; + u8 j; + struct clk_pmupstate *pclk = &g->clk_pmu; + + BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), + struct fll_device *, pflldev, j) { + if (pflldev->clk_domain == domain) { + pflldev->regime_desc.regime_id = regimeid; + return 0; + } + } + return -EINVAL; +} + +static int get_regime_id(struct gk20a *g, u32 domain, u32 *regimeid) +{ + struct fll_device *pflldev; + u8 j; + struct clk_pmupstate *pclk = &g->clk_pmu; + + BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), + struct fll_device *, pflldev, j) { + if (pflldev->clk_domain == domain) { + *regimeid = pflldev->regime_desc.regime_id; + return 0; + } + } + return -EINVAL; +} + +int clk_program_fllclks(struct gk20a *g, struct change_fll_clk *fllclk) +{ + int status = -EINVAL; + struct clk_domain *pdomain; + u8 i; + struct clk_pmupstate *pclk = &g->clk_pmu; + u16 clkmhz = 0; + struct clk_domain_3x_master *p3xmaster; + struct clk_domain_3x_slave *p3xslave; + unsigned long slaveidxmask; + struct set_fll_clk setfllclk; + bool foundxbar2clk = false; + bool foundsys2clk = false; + + memset(&setfllclk, 0, sizeof(setfllclk)); + if (fllclk->api_clk_domain != CTRL_CLK_DOMAIN_GPC2CLK) + return -EINVAL; + if (fllclk->voltuv == 0) + return -EINVAL; + if (fllclk->clkmhz == 0) + return -EINVAL; + + mutex_lock(&pclk->changeclkmutex); + + setfllclk.voltuv = fllclk->voltuv; + setfllclk.gpc2clkmhz = fllclk->clkmhz; + + BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), + struct clk_domain *, pdomain, i) { + + if (pdomain->api_domain == fllclk->api_clk_domain) { + + if (!pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER)) { + status = -EINVAL; + goto done; + } + p3xmaster = (struct clk_domain_3x_master *)pdomain; + slaveidxmask = p3xmaster->slave_idxs_mask; + for_each_set_bit(i, &slaveidxmask, 32) { + p3xslave = (struct clk_domain_3x_slave *) + CLK_CLK_DOMAIN_GET(pclk, i); + if ((p3xslave->super.super.super.api_domain != + CTRL_CLK_DOMAIN_XBAR2CLK) && + (p3xslave->super.super.super.api_domain != + CTRL_CLK_DOMAIN_SYS2CLK)) + continue; + clkmhz = 0; + status = p3xslave->clkdomainclkgetslaveclk(g, + pclk, + (struct clk_domain *)p3xslave, + &clkmhz, + fllclk->clkmhz); + if (status) { + status = -EINVAL; + goto done; + } + if (p3xslave->super.super.super.api_domain == + CTRL_CLK_DOMAIN_XBAR2CLK) { + setfllclk.xbar2clkmhz = clkmhz; + foundxbar2clk = true; + } + if (p3xslave->super.super.super.api_domain == + CTRL_CLK_DOMAIN_SYS2CLK) { + setfllclk.sys2clkmhz = clkmhz; + foundsys2clk = true; + } + } + } + } + if (!(foundxbar2clk && foundsys2clk)) { + status = -EINVAL; + goto done; + } + /*set regime ids */ + status = get_regime_id(g, CTRL_CLK_DOMAIN_GPC2CLK, + &setfllclk.current_regime_id_gpc); + if (status) + goto done; + + setfllclk.target_regime_id_gpc = find_regime_id(g, + CTRL_CLK_DOMAIN_GPC2CLK, setfllclk.gpc2clkmhz); + + status = get_regime_id(g, CTRL_CLK_DOMAIN_SYS2CLK, + &setfllclk.current_regime_id_sys); + if (status) + goto done; + + setfllclk.target_regime_id_sys = find_regime_id(g, + CTRL_CLK_DOMAIN_SYS2CLK, setfllclk.sys2clkmhz); + + status = get_regime_id(g, CTRL_CLK_DOMAIN_XBAR2CLK, + &setfllclk.current_regime_id_xbar); + if (status) + goto done; + + setfllclk.target_regime_id_xbar = find_regime_id(g, + CTRL_CLK_DOMAIN_XBAR2CLK, setfllclk.xbar2clkmhz); + + status = clk_pmu_vf_inject(g, &setfllclk); + + if (status) + gk20a_err(dev_from_gk20a(g), + "vf inject to change clk failed"); + + /* save regime ids */ + status = set_regime_id(g, CTRL_CLK_DOMAIN_XBAR2CLK, + setfllclk.target_regime_id_xbar); + if (status) + goto done; + + status = set_regime_id(g, CTRL_CLK_DOMAIN_GPC2CLK, + setfllclk.target_regime_id_gpc); + if (status) + goto done; + + status = set_regime_id(g, CTRL_CLK_DOMAIN_SYS2CLK, + setfllclk.target_regime_id_sys); + if (status) + goto done; +done: + mutex_unlock(&pclk->changeclkmutex); + return status; +} + +int clk_set_boot_fll_clk(struct gk20a *g) +{ + int status; + struct change_fll_clk bootfllclk; + + mutex_init(&g->clk_pmu.changeclkmutex); + + bootfllclk.api_clk_domain = CTRL_CLK_DOMAIN_GPC2CLK; + bootfllclk.clkmhz = 2581; + bootfllclk.voltuv = 825000; + status = clk_program_fllclks(g, &bootfllclk); + if (status) + gk20a_err(dev_from_gk20a(g), "attemp to set boot clk failed"); + return status; +} + u32 clk_domain_print_vf_table(struct gk20a *g, u32 clkapidomain) { u32 status = -EINVAL; diff --git a/drivers/gpu/nvgpu/clk/clk.h b/drivers/gpu/nvgpu/clk/clk.h index 1f25fa4ed..e54af521c 100644 --- a/drivers/gpu/nvgpu/clk/clk.h +++ b/drivers/gpu/nvgpu/clk/clk.h @@ -35,6 +35,7 @@ struct clk_pmupstate { struct clk_progs clk_progobjs; struct clk_vf_points clk_vf_pointobjs; struct clk_mclk_state clk_mclk; + struct mutex changeclkmutex; }; struct clockentry { @@ -44,6 +45,25 @@ struct clockentry { u32 api_clk_domain; }; +struct change_fll_clk { + u32 api_clk_domain; + u16 clkmhz; + u32 voltuv; +}; + +struct set_fll_clk { + u32 voltuv; + u16 gpc2clkmhz; + u32 current_regime_id_gpc; + u32 target_regime_id_gpc; + u16 sys2clkmhz; + u32 current_regime_id_sys; + u32 target_regime_id_sys; + u16 xbar2clkmhz; + u32 current_regime_id_xbar; + u32 target_regime_id_xbar; +}; + #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_MAX_NUMCLKS 9 struct vbios_clock_domain { @@ -82,7 +102,6 @@ struct vbios_clocks_table_1x_hal_clock_entry { #define PERF_CLK_PCIEGENCLK 12 #define PERF_CLK_NUM 13 -u32 clk_pmu_vf_inject(struct gk20a *g); u32 clk_pmu_vin_load(struct gk20a *g); u32 clk_domain_print_vf_table(struct gk20a *g, u32 clkapidomain); u32 clk_domain_get_f_or_v @@ -98,5 +117,6 @@ u32 clk_domain_get_f_points( u32 *fpointscount, u16 *freqpointsinmhz ); - +int clk_set_boot_fll_clk(struct gk20a *g); +int clk_program_fll_clks(struct gk20a *g, struct change_fll_clk *fllclk); #endif diff --git a/drivers/gpu/nvgpu/clk/clk_domain.c b/drivers/gpu/nvgpu/clk/clk_domain.c index f87530dcd..fe3db5d63 100644 --- a/drivers/gpu/nvgpu/clk/clk_domain.c +++ b/drivers/gpu/nvgpu/clk/clk_domain.c @@ -422,14 +422,14 @@ static u32 clkdomainclkproglink_not_supported(struct gk20a *g, return -EINVAL; } -static u32 clkdomainvfsearch_stub( +static int clkdomainvfsearch_stub( struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain, u16 *clkmhz, u32 *voltuv, - u8 rail -) + u8 rail) + { gk20a_dbg_info(""); return -EINVAL; @@ -441,8 +441,7 @@ static u32 clkdomaingetfpoints_stub( struct clk_domain *pdomain, u32 *pfpointscount, u16 *pfreqpointsinmhz, - u8 rail -) + u8 rail) { gk20a_dbg_info(""); return -EINVAL; @@ -556,17 +555,47 @@ static u32 clkdomainclkproglink_3x_prog(struct gk20a *g, return status; } -static u32 clkdomainvfsearch -( - struct gk20a *g, - struct clk_pmupstate *pclk, - struct clk_domain *pdomain, - u16 *pclkmhz, - u32 *pvoltuv, - u8 rail -) +static int clkdomaingetslaveclk(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain, + u16 *pclkmhz, + u16 masterclkmhz) { - u32 status = 0; + int status = 0; + struct clk_prog *pprog = NULL; + struct clk_prog_1x_master *pprog1xmaster = NULL; + u8 slaveidx; + struct clk_domain_3x_master *p3xmaster; + + gk20a_dbg_info(""); + + if (pclkmhz == NULL) + return -EINVAL; + + if (masterclkmhz == 0) + return -EINVAL; + + slaveidx = BOARDOBJ_GET_IDX(pdomain); + p3xmaster = (struct clk_domain_3x_master *) + CLK_CLK_DOMAIN_GET(pclk, + ((struct clk_domain_3x_slave *) + pdomain)->master_idx); + pprog = CLK_CLK_PROG_GET(pclk, p3xmaster->super.clk_prog_idx_first); + pprog1xmaster = (struct clk_prog_1x_master *)pprog; + + status = pprog1xmaster->getslaveclk(g, pclk, pprog1xmaster, + slaveidx, pclkmhz, masterclkmhz); + return status; +} + +static int clkdomainvfsearch(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain, + u16 *pclkmhz, + u32 *pvoltuv, + u8 rail) +{ + int status = 0; struct clk_domain_3x_master *p3xmaster = (struct clk_domain_3x_master *)pdomain; struct clk_prog *pprog = NULL; @@ -580,6 +609,10 @@ static u32 clkdomainvfsearch u32 bestvoltuv; gk20a_dbg_info(""); + + if ((pclkmhz == NULL) || (pvoltuv == NULL)) + return -EINVAL; + if ((*pclkmhz != 0) && (*pvoltuv != 0)) return -EINVAL; @@ -595,7 +628,6 @@ static u32 clkdomainvfsearch ((struct clk_domain_3x_slave *) pdomain)->master_idx); } - /* Iterate over the set of CLK_PROGs pointed at by this domain.*/ for (i = p3xmaster->super.clk_prog_idx_first; i <= p3xmaster->super.clk_prog_idx_last; @@ -625,7 +657,7 @@ static u32 clkdomainvfsearch } } } - /* clk and volt sent as zero to pring vf table */ + /* clk and volt sent as zero to print vf table */ if ((*pclkmhz == 0) && (*pvoltuv == 0)) { status = 0; goto done; @@ -836,6 +868,9 @@ static u32 clk_domain_construct_3x_slave(struct gk20a *g, pdomain->master_idx = ptmpdomain->master_idx; + pdomain->clkdomainclkgetslaveclk = + clkdomaingetslaveclk; + return status; } diff --git a/drivers/gpu/nvgpu/clk/clk_domain.h b/drivers/gpu/nvgpu/clk/clk_domain.h index eeb7c2568..443e1c4c2 100644 --- a/drivers/gpu/nvgpu/clk/clk_domain.h +++ b/drivers/gpu/nvgpu/clk/clk_domain.h @@ -30,10 +30,14 @@ u32 clk_domain_pmu_setup(struct gk20a *g); typedef u32 clkproglink(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain); -typedef u32 clkvfsearch(struct gk20a *g, struct clk_pmupstate *pclk, +typedef int clkvfsearch(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain, u16 *clkmhz, u32 *voltuv, u8 rail); +typedef int clkgetslaveclk(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_domain *pdomain, u16 *clkmhz, + u16 masterclkmhz); + typedef u32 clkgetfpoints(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain, u32 *pfpointscount, u16 *pfreqpointsinmhz, u8 rail); @@ -100,6 +104,7 @@ struct clk_domain_3x_master { struct clk_domain_3x_slave { struct clk_domain_3x_prog super; u8 master_idx; + clkgetslaveclk *clkdomainclkgetslaveclk; }; u32 clk_domain_clk_prog_link(struct gk20a *g, struct clk_pmupstate *pclk); diff --git a/drivers/gpu/nvgpu/clk/clk_prog.c b/drivers/gpu/nvgpu/clk/clk_prog.c index cb9a0e8d3..9fdd8b251 100644 --- a/drivers/gpu/nvgpu/clk/clk_prog.c +++ b/drivers/gpu/nvgpu/clk/clk_prog.c @@ -31,6 +31,7 @@ static u32 devinit_get_clk_prog_table(struct gk20a *g, static vf_flatten vfflatten_prog_1x_master; static vf_lookup vflookup_prog_1x_master; static get_fpoints getfpoints_prog_1x_master; +static get_slaveclk getslaveclk_prog_1x_master; static u32 _clk_progs_pmudatainit(struct gk20a *g, struct boardobjgrp *pboardobjgrp, @@ -611,6 +612,9 @@ static u32 clk_prog_construct_1x_master(struct gk20a *g, pclkprog->getfpoints = getfpoints_prog_1x_master; + pclkprog->getslaveclk = + getslaveclk_prog_1x_master; + pclkprog->p_vf_entries = (struct ctrl_clk_clk_prog_1x_master_vf_entry *) kzalloc(vfsize, GFP_KERNEL); @@ -851,7 +855,7 @@ static u32 vflookup_prog_1x_master u8 rail ) { - u8 j; + int j; struct ctrl_clk_clk_prog_1x_master_vf_entry *pvfentry; struct clk_vf_point *pvfpoint; @@ -860,7 +864,7 @@ static u32 vflookup_prog_1x_master u16 clkmhz; u32 voltuv; u8 slaveentrycount; - u8 i; + int i; struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents; if ((*pclkmhz != 0) && (*pvoltuv != 0)) @@ -1045,3 +1049,50 @@ done: *pfpointscount = fpointscount; return 0; } + +static int getslaveclk_prog_1x_master(struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 slave_clk_domain, + u16 *pclkmhz, + u16 masterclkmhz +) +{ + struct clk_progs *pclkprogobjs; + struct clk_prog_1x_master_ratio *p1xmasterratio; + u8 slaveentrycount; + u8 i; + struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents; + + if (pclkmhz == NULL) + return -EINVAL; + + if (masterclkmhz == 0) + return -EINVAL; + + *pclkmhz = 0; + pclkprogobjs = &(pclk->clk_progobjs); + + slaveentrycount = pclkprogobjs->slave_entry_count; + + if (p1xmaster->super.super.super.implements(g, + &p1xmaster->super.super.super, + CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) { + p1xmasterratio = + (struct clk_prog_1x_master_ratio *)p1xmaster; + pslaveents = p1xmasterratio->p_slave_entries; + for (i = 0; i < slaveentrycount; i++) { + if (pslaveents->clk_dom_idx == + slave_clk_domain) + break; + pslaveents++; + } + if (i == slaveentrycount) + return -EINVAL; + *pclkmhz = (masterclkmhz * pslaveents->ratio)/100; + } else { + /* only support ratio for now */ + return -EINVAL; + } + return 0; +} diff --git a/drivers/gpu/nvgpu/clk/clk_prog.h b/drivers/gpu/nvgpu/clk/clk_prog.h index be92b3fc1..60711b4c3 100644 --- a/drivers/gpu/nvgpu/clk/clk_prog.h +++ b/drivers/gpu/nvgpu/clk/clk_prog.h @@ -32,6 +32,11 @@ typedef u32 vf_lookup(struct gk20a *g, struct clk_pmupstate *pclk, u8 *slave_clk_domain_idx, u16 *pclkmhz, u32 *pvoltuv, u8 rail); +typedef int get_slaveclk(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 slave_clk_domain_idx, u16 *pclkmhz, + u16 masterclkmhz); + typedef u32 get_fpoints(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_prog_1x_master *p1xmaster, u32 *pfpointscount, @@ -65,6 +70,7 @@ struct clk_prog_1x_master { vf_flatten *vfflatten; vf_lookup *vflookup; get_fpoints *getfpoints; + get_slaveclk *getslaveclk; }; struct clk_prog_1x_master_ratio { diff --git a/drivers/gpu/nvgpu/pstate/pstate.c b/drivers/gpu/nvgpu/pstate/pstate.c index 94ff5010f..d6173275c 100644 --- a/drivers/gpu/nvgpu/pstate/pstate.c +++ b/drivers/gpu/nvgpu/pstate/pstate.c @@ -95,11 +95,11 @@ int gk20a_init_pstate_pmu_support(struct gk20a *g) if (err) return err; - err = clk_pmu_vf_inject(g); + err = clk_vf_point_cache(g); if (err) return err; - err = clk_vf_point_cache(g); + err = clk_set_boot_fll_clk(g); return err; }