From 1b1090512020369df18dbe36336ac5a85d2cd693 Mon Sep 17 00:00:00 2001 From: Vijayakumar Date: Wed, 31 Aug 2016 17:40:24 +0530 Subject: gpu: nvgpu: support to parse VF table JIRA DNVGPU-123 function was added to retrieve V for F or F for V for a given clock domain. Clock domain can be master or slave. F or V can be intermediate point between two successive V or F values in VF table. VF table should be cached before calling this function. A F value below Fmin will return Vmin. F > Fmax will return error A V value above Vmax wil return F max. A V value below Vmin will return error. Change-Id: I28b4e8647510c6933e9e1204cfff31d74616e11a Signed-off-by: Vijayakumar Reviewed-on: http://git-master/r/1211234 (cherry-picked from commit 5b83b03f2454fbec8d49a064ed09b09c92d3e9fa) Reviewed-on: http://git-master/r/1235054 Reviewed-by: Thomas Fleury Reviewed-by: Terje Bergstrom Tested-by: Terje Bergstrom --- drivers/gpu/nvgpu/clk/clk.c | 48 +++++++++++ drivers/gpu/nvgpu/clk/clk.h | 8 ++ drivers/gpu/nvgpu/clk/clk_domain.c | 125 +++++++++++++++++++++++++++- drivers/gpu/nvgpu/clk/clk_domain.h | 10 +++ drivers/gpu/nvgpu/clk/clk_prog.c | 153 +++++++++++++++++++++++++++++++++++ drivers/gpu/nvgpu/clk/clk_prog.h | 6 ++ drivers/gpu/nvgpu/clk/clk_vf_point.h | 7 +- drivers/gpu/nvgpu/pstate/pstate.c | 4 + 8 files changed, 356 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/nvgpu/clk/clk.c b/drivers/gpu/nvgpu/clk/clk.c index 0679efc0..34b344c8 100644 --- a/drivers/gpu/nvgpu/clk/clk.c +++ b/drivers/gpu/nvgpu/clk/clk.c @@ -188,3 +188,51 @@ u32 clk_pmu_vf_inject(struct gk20a *g) done: return status; } + +u32 clk_domain_print_vf_table(struct gk20a *g, u32 clkapidomain) +{ + u32 status = -EINVAL; + struct clk_domain *pdomain; + u8 i; + struct clk_pmupstate *pclk = &g->clk_pmu; + u16 clkmhz = 0; + u32 volt = 0; + + BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), + struct clk_domain *, pdomain, i) { + if (pdomain->api_domain == clkapidomain) { + status = pdomain->clkdomainclkvfsearch(g, pclk, + pdomain, &clkmhz, &volt, + CLK_PROG_VFE_ENTRY_LOGIC); + return status; + } + } + return status; +} + +u32 clk_domain_get_f_or_v( + struct gk20a *g, + u32 clkapidomain, + u16 *pclkmhz, + u32 *pvoltuv +) +{ + u32 status = -EINVAL; + struct clk_domain *pdomain; + u8 i; + struct clk_pmupstate *pclk = &g->clk_pmu; + + if ((pclkmhz == NULL) || (pvoltuv == NULL)) + return -EINVAL; + + BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), + struct clk_domain *, pdomain, i) { + if (pdomain->api_domain == clkapidomain) { + status = pdomain->clkdomainclkvfsearch(g, pclk, + pdomain, pclkmhz, pvoltuv, + CLK_PROG_VFE_ENTRY_LOGIC); + return status; + } + } + return status; +} diff --git a/drivers/gpu/nvgpu/clk/clk.h b/drivers/gpu/nvgpu/clk/clk.h index e24aada2..0d12ba7d 100644 --- a/drivers/gpu/nvgpu/clk/clk.h +++ b/drivers/gpu/nvgpu/clk/clk.h @@ -84,5 +84,13 @@ struct vbios_clocks_table_1x_hal_clock_entry { 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 +( + struct gk20a *g, + u32 clkapidomain, + u16 *pclkmhz, + u32 *pvoltuv +); #endif diff --git a/drivers/gpu/nvgpu/clk/clk_domain.c b/drivers/gpu/nvgpu/clk/clk_domain.c index 7371946c..c8da851a 100644 --- a/drivers/gpu/nvgpu/clk/clk_domain.c +++ b/drivers/gpu/nvgpu/clk/clk_domain.c @@ -136,6 +136,8 @@ u32 clk_domain_sw_setup(struct gk20a *g) struct boardobjgrp *pboardobjgrp = NULL; struct clk_domains *pclkdomainobjs; struct clk_domain *pdomain; + struct clk_domain_3x_master *pdomain_master; + struct clk_domain_3x_slave *pdomain_slave; u8 i; gk20a_dbg_info(""); @@ -186,6 +188,7 @@ u32 clk_domain_sw_setup(struct gk20a *g) BOARDOBJGRP_FOR_EACH(&(pclkdomainobjs->super.super), struct clk_domain *, pdomain, i) { + pdomain_master = NULL; if (pdomain->super.implements(g, &pdomain->super, CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG)) { status = boardobjgrpmask_bitset( @@ -201,6 +204,18 @@ u32 clk_domain_sw_setup(struct gk20a *g) if (status) goto done; } + + if (pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { + pdomain_slave = + (struct clk_domain_3x_slave *)pdomain; + pdomain_master = + (struct clk_domain_3x_master *) + (CLK_CLK_DOMAIN_GET((&g->clk_pmu), + pdomain_slave->master_idx)); + pdomain_master->slave_idxs_mask |= BIT(i); + } + } done: @@ -407,6 +422,20 @@ static u32 clkdomainclkproglink_not_supported(struct gk20a *g, return -EINVAL; } +static u32 clkdomainvfsearch_stub( + struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_domain *pdomain, + u16 *clkmhz, + u32 *voltuv, + u8 rail +) +{ + gk20a_dbg_info(""); + return -EINVAL; +} + + static u32 clk_domain_construct_super(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) @@ -429,6 +458,9 @@ static u32 clk_domain_construct_super(struct gk20a *g, pdomain->clkdomainclkproglink = clkdomainclkproglink_not_supported; + pdomain->clkdomainclkvfsearch = + clkdomainvfsearch_stub; + pdomain->api_domain = ptmpdomain->api_domain; pdomain->domain = ptmpdomain->domain; pdomain->perf_domain_grp_idx = @@ -508,6 +540,92 @@ 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 +) +{ + u32 status = 0; + struct clk_domain_3x_master *p3xmaster = + (struct clk_domain_3x_master *)pdomain; + struct clk_prog *pprog = NULL; + struct clk_prog_1x_master *pprog1xmaster = NULL; + u8 i; + u8 *pslaveidx = NULL; + u8 slaveidx; + u16 clkmhz; + u32 voltuv; + u16 bestclkmhz; + u32 bestvoltuv; + + gk20a_dbg_info(""); + if ((*pclkmhz != 0) && (*pvoltuv != 0)) + return -EINVAL; + + bestclkmhz = *pclkmhz; + bestvoltuv = *pvoltuv; + + if (pdomain->super.implements(g, &pdomain->super, + CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { + slaveidx = BOARDOBJ_GET_IDX(pdomain); + pslaveidx = &slaveidx; + p3xmaster = (struct clk_domain_3x_master *) + CLK_CLK_DOMAIN_GET(pclk, + ((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; + i++) { + clkmhz = *pclkmhz; + voltuv = *pvoltuv; + pprog = CLK_CLK_PROG_GET(pclk, i); + + /* MASTER CLK_DOMAINs must point to MASTER CLK_PROGs.*/ + if (!pprog->super.implements(g, &pprog->super, + CTRL_CLK_CLK_PROG_TYPE_1X_MASTER)) { + status = -EINVAL; + goto done; + } + + pprog1xmaster = (struct clk_prog_1x_master *)pprog; + status = pprog1xmaster->vflookup(g, pclk, pprog1xmaster, + pslaveidx, &clkmhz, &voltuv, rail); + /* if look up has found the V or F value matching to other + exit */ + if (status == 0) { + if (*pclkmhz == 0) { + bestclkmhz = clkmhz; + } else { + bestvoltuv = voltuv; + break; + } + } + } + /* clk and volt sent as zero to pring vf table */ + if ((*pclkmhz == 0) && (*pvoltuv == 0)) { + status = 0; + goto done; + } + /* atleast one search found a matching value? */ + if ((bestvoltuv != 0) && (bestclkmhz != 0)) { + *pclkmhz = bestclkmhz; + *pvoltuv = bestvoltuv; + status = 0; + goto done; + } +done: + gk20a_dbg_info("done status %x", status); + return status; +} + static u32 _clk_domain_pmudatainit_3x_prog(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) @@ -568,6 +686,9 @@ static u32 clk_domain_construct_3x_prog(struct gk20a *g, pdomain->super.super.clkdomainclkproglink = clkdomainclkproglink_3x_prog; + pdomain->super.super.clkdomainclkvfsearch = + clkdomainvfsearch; + pdomain->clk_prog_idx_first = ptmpdomain->clk_prog_idx_first; pdomain->clk_prog_idx_last = ptmpdomain->clk_prog_idx_last; pdomain->noise_unaware_ordering_index = @@ -707,8 +828,6 @@ static u32 clk_domain_construct_3x_master(struct gk20a *g, { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_3x_master *pdomain; - struct clk_domain_3x_master *ptmpdomain = - (struct clk_domain_3x_master *)pargs; u32 status = 0; if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER) @@ -726,7 +845,7 @@ static u32 clk_domain_construct_3x_master(struct gk20a *g, pdomain->super.super.super.clkdomainclkproglink = clkdomainclkproglink_3x_master; - pdomain->slave_idxs_mask = ptmpdomain->slave_idxs_mask; + pdomain->slave_idxs_mask = 0; return status; } diff --git a/drivers/gpu/nvgpu/clk/clk_domain.h b/drivers/gpu/nvgpu/clk/clk_domain.h index 2670a066..07976a2a 100644 --- a/drivers/gpu/nvgpu/clk/clk_domain.h +++ b/drivers/gpu/nvgpu/clk/clk_domain.h @@ -26,8 +26,13 @@ struct clk_domain; /*data and function definition to talk to driver*/ u32 clk_domain_sw_setup(struct gk20a *g); 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, + struct clk_domain *pdomain, u16 *clkmhz, + u32 *voltuv, u8 rail); struct clk_domains { struct boardobjgrp_e32 super; u8 n_num_entries; @@ -55,6 +60,7 @@ struct clk_domain { u8 ratio_domain; u8 usage; clkproglink *clkdomainclkproglink; + clkvfsearch *clkdomainclkvfsearch; }; struct clk_domain_3x { @@ -92,4 +98,8 @@ struct clk_domain_3x_slave { u32 clk_domain_clk_prog_link(struct gk20a *g, struct clk_pmupstate *pclk); +#define CLK_CLK_DOMAIN_GET(pclk, idx) \ + ((struct clk_domain *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &pclk->clk_domainobjs.super.super, (u8)(idx))) + #endif diff --git a/drivers/gpu/nvgpu/clk/clk_prog.c b/drivers/gpu/nvgpu/clk/clk_prog.c index 4bf473ac..5e4700a0 100644 --- a/drivers/gpu/nvgpu/clk/clk_prog.c +++ b/drivers/gpu/nvgpu/clk/clk_prog.c @@ -29,6 +29,7 @@ static struct clk_prog *construct_clk_prog(struct gk20a *g, void *pargs); static u32 devinit_get_clk_prog_table(struct gk20a *g, struct clk_progs *pprogobjs); static vf_flatten vfflatten_prog_1x_master; +static vf_lookup vflookup_prog_1x_master; static u32 _clk_progs_pmudatainit(struct gk20a *g, struct boardobjgrp *pboardobjgrp, @@ -603,6 +604,9 @@ static u32 clk_prog_construct_1x_master(struct gk20a *g, pclkprog->vfflatten = vfflatten_prog_1x_master; + pclkprog->vflookup = + vflookup_prog_1x_master; + pclkprog->p_vf_entries = (struct ctrl_clk_clk_prog_1x_master_vf_entry *) kzalloc(vfsize, GFP_KERNEL); @@ -831,3 +835,152 @@ done: gk20a_dbg_info("done status %x", status); return status; } + +static u32 vflookup_prog_1x_master +( + struct gk20a *g, + struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 *slave_clk_domain, + u16 *pclkmhz, + u32 *pvoltuv, + u8 rail +) +{ + u8 j; + struct ctrl_clk_clk_prog_1x_master_vf_entry + *pvfentry; + struct clk_vf_point *pvfpoint; + struct clk_progs *pclkprogobjs; + struct clk_prog_1x_master_ratio *p1xmasterratio; + u16 clkmhz; + u32 voltuv; + u8 slaveentrycount; + u8 i; + struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents; + + if ((*pclkmhz != 0) && (*pvoltuv != 0)) + return -EINVAL; + + pclkprogobjs = &(pclk->clk_progobjs); + + slaveentrycount = pclkprogobjs->slave_entry_count; + + if (pclkprogobjs->vf_entry_count > + CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES) + return -EINVAL; + + if (rail >= pclkprogobjs->vf_entry_count) + return -EINVAL; + + pvfentry = p1xmaster->p_vf_entries; + + pvfentry = (struct ctrl_clk_clk_prog_1x_master_vf_entry *)( + (u8 *)pvfentry + + (sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * + (rail+1))); + + clkmhz = *pclkmhz; + voltuv = *pvoltuv; + + /*if domain is slave domain and freq is input + then derive master clk */ + if ((slave_clk_domain != NULL) && (*pclkmhz != 0)) { + 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; + clkmhz = (clkmhz * 100)/pslaveents->ratio; + } else { + /* only support ratio for now */ + return -EINVAL; + } + } + + /* if both volt and clks are zero simply print*/ + if ((*pvoltuv == 0) && (*pclkmhz == 0)) { + for (j = pvfentry->vf_point_idx_first; + j <= pvfentry->vf_point_idx_last; j++) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); + gk20a_err(dev_from_gk20a(g), "v %x c %x", + clkvfpointvoltageuvget(g, pvfpoint), + clkvfpointfreqmhzget(g, pvfpoint)); + } + return -EINVAL; + } + /* start looking up f for v for v for f */ + /* looking for volt? */ + if (*pvoltuv == 0) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, + pvfentry->vf_point_idx_last); + /* above range? */ + if (clkmhz > clkvfpointfreqmhzget(g, pvfpoint)) + return -EINVAL; + + for (j = pvfentry->vf_point_idx_last; + j >= pvfentry->vf_point_idx_first; j--) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); + if (clkmhz <= clkvfpointfreqmhzget(g, pvfpoint)) + voltuv = clkvfpointvoltageuvget(g, pvfpoint); + else + break; + } + } else { /* looking for clk? */ + + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, + pvfentry->vf_point_idx_first); + /* below range? */ + if (voltuv < clkvfpointvoltageuvget(g, pvfpoint)) + return -EINVAL; + + for (j = pvfentry->vf_point_idx_first; + j <= pvfentry->vf_point_idx_last; j++) { + pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); + if (voltuv >= clkvfpointvoltageuvget(g, pvfpoint)) + clkmhz = clkvfpointfreqmhzget(g, pvfpoint); + else + break; + } + } + + /*if domain is slave domain and freq was looked up + then derive slave clk */ + if ((slave_clk_domain != NULL) && (*pclkmhz == 0)) { + 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; + clkmhz = (clkmhz * pslaveents->ratio)/100; + } else { + /* only support ratio for now */ + return -EINVAL; + } + } + *pclkmhz = clkmhz; + *pvoltuv = voltuv; + if ((clkmhz == 0) || (voltuv == 0)) + return -EINVAL; + return 0; +} diff --git a/drivers/gpu/nvgpu/clk/clk_prog.h b/drivers/gpu/nvgpu/clk/clk_prog.h index 8718bdd6..979d327d 100644 --- a/drivers/gpu/nvgpu/clk/clk_prog.h +++ b/drivers/gpu/nvgpu/clk/clk_prog.h @@ -27,6 +27,11 @@ typedef u32 vf_flatten(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_prog_1x_master *p1xmaster, u8 clk_domain_idx, u16 *pfreqmaxlastmhz); +typedef u32 vf_lookup(struct gk20a *g, struct clk_pmupstate *pclk, + struct clk_prog_1x_master *p1xmaster, + u8 *slave_clk_domain_idx, u16 *pclkmhz, + u32 *pvoltuv, u8 rail); + struct clk_progs { struct boardobjgrp_e255 super; u8 slave_entry_count; @@ -52,6 +57,7 @@ struct clk_prog_1x_master { struct ctrl_clk_clk_delta deltas; union ctrl_clk_clk_prog_1x_master_source_data source_data; vf_flatten *vfflatten; + vf_lookup *vflookup; }; struct clk_prog_1x_master_ratio { diff --git a/drivers/gpu/nvgpu/clk/clk_vf_point.h b/drivers/gpu/nvgpu/clk/clk_vf_point.h index 306bec41..15920066 100644 --- a/drivers/gpu/nvgpu/clk/clk_vf_point.h +++ b/drivers/gpu/nvgpu/clk/clk_vf_point.h @@ -46,8 +46,8 @@ struct clk_vf_point_freq { }; #define CLK_CLK_VF_POINT_GET(pclk, idx) \ - ((struct clk_vf_point)BOARDOBJGRP_OBJ_GET_BY_IDX( \ - &pclk->vfpoints.super.super, (u8)(idx))) + ((struct clk_vf_point *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ + &pclk->clk_vf_pointobjs.super.super, (u8)(idx))) #define clkvfpointpairget(pvfpoint) \ (&((pvfpoint)->pair)) @@ -66,6 +66,9 @@ struct clk_vf_point_freq { CTRL_CLK_VF_PAIR_VOLTAGE_UV_SET(clkvfpointpairget(pvfpoint), \ _voltageuv) +#define clkvfpointvoltageuvget(pgpu, pvfpoint) \ + CTRL_CLK_VF_PAIR_VOLTAGE_UV_GET(clkvfpointpairget(pvfpoint)) \ + struct clk_vf_point *construct_clk_vf_point(struct gk20a *g, void *pargs); #endif diff --git a/drivers/gpu/nvgpu/pstate/pstate.c b/drivers/gpu/nvgpu/pstate/pstate.c index 83f17937..94ff5010 100644 --- a/drivers/gpu/nvgpu/pstate/pstate.c +++ b/drivers/gpu/nvgpu/pstate/pstate.c @@ -96,6 +96,10 @@ int gk20a_init_pstate_pmu_support(struct gk20a *g) return err; err = clk_pmu_vf_inject(g); + if (err) + return err; + + err = clk_vf_point_cache(g); return err; } -- cgit v1.2.2