/* * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include "clk.h" #include "clk_fll.h" #include "clk_domain.h" #include "boardobj/boardobjgrp.h" #include "boardobj/boardobjgrp_e32.h" #include "ctrl/ctrlclk.h" #include "ctrl/ctrlvolt.h" static struct clk_domain *construct_clk_domain(struct gk20a *g, void *pargs); static int devinit_get_clocks_table(struct gk20a *g, struct clk_domains *pdomainobjs); static int clk_domain_pmudatainit_super(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata); static struct vbios_clocks_table_1x_hal_clock_entry vbiosclktbl1xhalentry_gp[] = { { clkwhich_gpc2clk, true, 1, }, { clkwhich_xbar2clk, true, 1, }, { clkwhich_mclk, false, 1, }, { clkwhich_sys2clk, true, 1, }, { clkwhich_hub2clk, false, 1, }, { clkwhich_nvdclk, false, 1, }, { clkwhich_pwrclk, false, 1, }, { clkwhich_dispclk, false, 1, }, { clkwhich_pciegenclk, false, 1, } }; /* * Updated from RM devinit_clock.c * GV100 is 0x03 and * GP10x is 0x02 in clocks_hal. */ static struct vbios_clocks_table_1x_hal_clock_entry vbiosclktbl1xhalentry_gv[] = { { clkwhich_gpcclk, true, 2, }, { clkwhich_xbarclk, true, 1, }, { clkwhich_mclk, false, 1, }, { clkwhich_sysclk, true, 1, }, { clkwhich_hubclk, false, 1, }, { clkwhich_nvdclk, true, 1, }, { clkwhich_pwrclk, false, 1, }, { clkwhich_dispclk, false, 1, }, { clkwhich_pciegenclk, false, 1, }, { clkwhich_hostclk, true, 1, } }; static u32 clktranslatehalmumsettoapinumset(u32 clkhaldomains) { u32 clkapidomains = 0; if (clkhaldomains & BIT(clkwhich_gpcclk)) { clkapidomains |= CTRL_CLK_DOMAIN_GPCCLK; } if (clkhaldomains & BIT(clkwhich_xbarclk)) { clkapidomains |= CTRL_CLK_DOMAIN_XBARCLK; } if (clkhaldomains & BIT(clkwhich_sysclk)) { clkapidomains |= CTRL_CLK_DOMAIN_SYSCLK; } if (clkhaldomains & BIT(clkwhich_hubclk)) { clkapidomains |= CTRL_CLK_DOMAIN_HUBCLK; } if (clkhaldomains & BIT(clkwhich_hostclk)) { clkapidomains |= CTRL_CLK_DOMAIN_HOSTCLK; } if (clkhaldomains & BIT(clkwhich_gpc2clk)) { clkapidomains |= CTRL_CLK_DOMAIN_GPC2CLK; } if (clkhaldomains & BIT(clkwhich_xbar2clk)) { clkapidomains |= CTRL_CLK_DOMAIN_XBAR2CLK; } if (clkhaldomains & BIT(clkwhich_sys2clk)) { clkapidomains |= CTRL_CLK_DOMAIN_SYS2CLK; } if (clkhaldomains & BIT(clkwhich_hub2clk)) { clkapidomains |= CTRL_CLK_DOMAIN_HUB2CLK; } if (clkhaldomains & BIT(clkwhich_pwrclk)) { clkapidomains |= CTRL_CLK_DOMAIN_PWRCLK; } if (clkhaldomains & BIT(clkwhich_pciegenclk)) { clkapidomains |= CTRL_CLK_DOMAIN_PCIEGENCLK; } if (clkhaldomains & BIT(clkwhich_mclk)) { clkapidomains |= CTRL_CLK_DOMAIN_MCLK; } if (clkhaldomains & BIT(clkwhich_nvdclk)) { clkapidomains |= CTRL_CLK_DOMAIN_NVDCLK; } if (clkhaldomains & BIT(clkwhich_dispclk)) { clkapidomains |= CTRL_CLK_DOMAIN_DISPCLK; } return clkapidomains; } static int _clk_domains_pmudatainit_3x(struct gk20a *g, struct boardobjgrp *pboardobjgrp, struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) { struct nv_pmu_clk_clk_domain_boardobjgrp_set_header *pset = (struct nv_pmu_clk_clk_domain_boardobjgrp_set_header *) pboardobjgrppmu; struct clk_domains *pdomains = (struct clk_domains *)pboardobjgrp; int status = 0; status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); if (status) { nvgpu_err(g, "error updating pmu boardobjgrp for clk domain 0x%x", status); goto done; } pset->vbios_domains = pdomains->vbios_domains; pset->cntr_sampling_periodms = pdomains->cntr_sampling_periodms; pset->version = CLK_DOMAIN_BOARDOBJGRP_VERSION; pset->b_override_o_v_o_c = false; pset->b_debug_mode = false; pset->b_enforce_vf_monotonicity = pdomains->b_enforce_vf_monotonicity; pset->b_enforce_vf_smoothening = pdomains->b_enforce_vf_smoothening; if (g->ops.clk.split_rail_support) { pset->volt_rails_max = 2; } else { pset->volt_rails_max = 1; } status = boardobjgrpmask_export( &pdomains->master_domains_mask.super, pdomains->master_domains_mask.super.bitcount, &pset->master_domains_mask.super); memcpy(&pset->deltas, &pdomains->deltas, (sizeof(struct ctrl_clk_clk_delta))); done: return status; } static int _clk_domains_pmudata_instget(struct gk20a *g, struct nv_pmu_boardobjgrp *pmuboardobjgrp, struct nv_pmu_boardobj **ppboardobjpmudata, u8 idx) { struct nv_pmu_clk_clk_domain_boardobj_grp_set *pgrp_set = (struct nv_pmu_clk_clk_domain_boardobj_grp_set *) pmuboardobjgrp; nvgpu_log_info(g, " "); /*check whether pmuboardobjgrp has a valid boardobj in index*/ if (((u32)BIT(idx) & pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { return -EINVAL; } *ppboardobjpmudata = (struct nv_pmu_boardobj *) &pgrp_set->objects[idx].data.board_obj; nvgpu_log_info(g, " Done"); return 0; } int clk_domain_sw_setup(struct gk20a *g) { int status; 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; nvgpu_log_info(g, " "); status = boardobjgrpconstruct_e32(g, &g->clk_pmu.clk_domainobjs.super); if (status) { nvgpu_err(g, "error creating boardobjgrp for clk domain, status - 0x%x", status); goto done; } pboardobjgrp = &g->clk_pmu.clk_domainobjs.super.super; pclkdomainobjs = &(g->clk_pmu.clk_domainobjs); BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_DOMAIN); status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, clk, CLK, clk_domain, CLK_DOMAIN); if (status) { nvgpu_err(g, "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", status); goto done; } pboardobjgrp->pmudatainit = _clk_domains_pmudatainit_3x; pboardobjgrp->pmudatainstget = _clk_domains_pmudata_instget; /* Initialize mask to zero.*/ boardobjgrpmask_e32_init(&pclkdomainobjs->prog_domains_mask, NULL); boardobjgrpmask_e32_init(&pclkdomainobjs->master_domains_mask, NULL); pclkdomainobjs->b_enforce_vf_monotonicity = true; pclkdomainobjs->b_enforce_vf_smoothening = true; memset(&pclkdomainobjs->ordered_noise_aware_list, 0, sizeof(pclkdomainobjs->ordered_noise_aware_list)); memset(&pclkdomainobjs->ordered_noise_unaware_list, 0, sizeof(pclkdomainobjs->ordered_noise_unaware_list)); memset(&pclkdomainobjs->deltas, 0, sizeof(struct ctrl_clk_clk_delta)); status = devinit_get_clocks_table(g, pclkdomainobjs); if (status) { goto done; } 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( &pclkdomainobjs->prog_domains_mask.super, i); if (status) { goto done; } } if (pdomain->super.implements(g, &pdomain->super, CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER)) { status = boardobjgrpmask_bitset( &pclkdomainobjs->master_domains_mask.super, i); 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: nvgpu_log_info(g, " done status %x", status); return status; } int clk_domain_pmu_setup(struct gk20a *g) { int status; struct boardobjgrp *pboardobjgrp = NULL; nvgpu_log_info(g, " "); pboardobjgrp = &g->clk_pmu.clk_domainobjs.super.super; if (!pboardobjgrp->bconstructed) { return -EINVAL; } status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); nvgpu_log_info(g, "Done"); return status; } static int devinit_get_clocks_table_35(struct gk20a *g, struct clk_domains *pclkdomainobjs, u8 *clocks_table_ptr) { int status = 0; struct vbios_clocks_table_35_header clocks_table_header = { 0 }; struct vbios_clocks_table_35_entry clocks_table_entry = { 0 }; struct vbios_clocks_table_1x_hal_clock_entry *vbiosclktbl1xhalentry; u8 *clocks_tbl_entry_ptr = NULL; u32 index = 0; struct clk_domain *pclkdomain_dev; union { struct boardobj boardobj; struct clk_domain clk_domain; struct clk_domain_3x v3x; struct clk_domain_3x_fixed v3x_fixed; struct clk_domain_35_prog v35_prog; struct clk_domain_35_master v35_master; struct clk_domain_35_slave v35_slave; } clk_domain_data; nvgpu_log_info(g, " "); memcpy(&clocks_table_header, clocks_table_ptr, VBIOS_CLOCKS_TABLE_35_HEADER_SIZE_09); if (clocks_table_header.header_size < (u8) VBIOS_CLOCKS_TABLE_35_HEADER_SIZE_09) { status = -EINVAL; goto done; } if (clocks_table_header.entry_size < (u8) VBIOS_CLOCKS_TABLE_35_ENTRY_SIZE_11) { status = -EINVAL; goto done; } switch (clocks_table_header.clocks_hal) { case CLK_TABLE_HAL_ENTRY_GP: { vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gp; break; } case CLK_TABLE_HAL_ENTRY_GV: { vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gv; break; } default: { status = -EINVAL; goto done; } } pclkdomainobjs->cntr_sampling_periodms = (u16)clocks_table_header.cntr_sampling_periodms; /* Read table entries*/ clocks_tbl_entry_ptr = clocks_table_ptr + clocks_table_header.header_size; for (index = 0; index < clocks_table_header.entry_count; index++) { memcpy((void*) &clocks_table_entry, (void*) clocks_tbl_entry_ptr, clocks_table_header.entry_size); clk_domain_data.clk_domain.domain = (u8) vbiosclktbl1xhalentry[index].domain; clk_domain_data.clk_domain.api_domain = clktranslatehalmumsettoapinumset( (u32) BIT(clk_domain_data.clk_domain.domain)); clk_domain_data.v3x.b_noise_aware_capable = vbiosclktbl1xhalentry[index].b_noise_aware_capable; switch (BIOS_GET_FIELD(clocks_table_entry.flags0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE)) { case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_FIXED: { clk_domain_data.boardobj.type = CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED; clk_domain_data.v3x_fixed.freq_mhz = (u16)BIOS_GET_FIELD( clocks_table_entry.param1, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_FIXED_FREQUENCY_MHZ); break; } case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_MASTER: { clk_domain_data.boardobj.type = CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER; clk_domain_data.v35_prog.super.clk_prog_idx_first = (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); clk_domain_data.v35_prog.super.clk_prog_idx_last = (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); clk_domain_data.v35_prog.super.noise_unaware_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); if (clk_domain_data.v3x.b_noise_aware_capable) { clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = (bool)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); } else { clk_domain_data.v35_prog.super.noise_aware_ordering_index = CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = false; } clk_domain_data.v35_prog.pre_volt_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_PRE_VOLT_ORDERING_IDX)); clk_domain_data.v35_prog.post_volt_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_POST_VOLT_ORDERING_IDX)); clk_domain_data.v35_prog.super.factory_delta.data.delta_khz = 0; clk_domain_data.v35_prog.super.factory_delta.type = 0; clk_domain_data.v35_prog.super.freq_delta_min_mhz = (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MIN_MHZ)); clk_domain_data.v35_prog.super.freq_delta_max_mhz = (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MAX_MHZ)); clk_domain_data.v35_prog.clk_vf_curve_count = vbiosclktbl1xhalentry[index].clk_vf_curve_count; break; } case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_SLAVE: { clk_domain_data.boardobj.type = CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE; clk_domain_data.v35_prog.super.clk_prog_idx_first = (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); clk_domain_data.v35_prog.super.clk_prog_idx_last = (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); clk_domain_data.v35_prog.super.noise_unaware_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); if (clk_domain_data.v3x.b_noise_aware_capable) { clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = (bool)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); } else { clk_domain_data.v35_prog.super.noise_aware_ordering_index = CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = false; } clk_domain_data.v35_prog.pre_volt_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_PRE_VOLT_ORDERING_IDX)); clk_domain_data.v35_prog.post_volt_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_POST_VOLT_ORDERING_IDX)); clk_domain_data.v35_prog.super.factory_delta.data.delta_khz = 0; clk_domain_data.v35_prog.super.factory_delta.type = 0; clk_domain_data.v35_prog.super.freq_delta_min_mhz = 0; clk_domain_data.v35_prog.super.freq_delta_max_mhz = 0; clk_domain_data.v35_slave.slave.master_idx = (u8)(BIOS_GET_FIELD(clocks_table_entry.param1, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_SLAVE_MASTER_DOMAIN)); break; } default: { nvgpu_err(g, "error reading clock domain entry %d", index); status = -EINVAL; goto done; } } pclkdomain_dev = construct_clk_domain(g, (void *)&clk_domain_data); if (pclkdomain_dev == NULL) { nvgpu_err(g, "unable to construct clock domain boardobj for %d", index); status = -EINVAL; goto done; } status = boardobjgrp_objinsert( &pclkdomainobjs->super.super, (struct boardobj *)(void*) pclkdomain_dev, index); if (status != 0UL) { nvgpu_err(g, "unable to insert clock domain boardobj for %d", index); status = (u32) -EINVAL; goto done; } clocks_tbl_entry_ptr += clocks_table_header.entry_size; } done: nvgpu_log_info(g, " done status %x", status); return status; } static int devinit_get_clocks_table_1x(struct gk20a *g, struct clk_domains *pclkdomainobjs, u8 *clocks_table_ptr) { int status = 0; struct vbios_clocks_table_1x_header clocks_table_header = { 0 }; struct vbios_clocks_table_1x_entry clocks_table_entry = { 0 }; struct vbios_clocks_table_1x_hal_clock_entry *vbiosclktbl1xhalentry; u8 *clocks_tbl_entry_ptr = NULL; u32 index = 0; struct clk_domain *pclkdomain_dev; union { struct boardobj boardobj; struct clk_domain clk_domain; struct clk_domain_3x v3x; struct clk_domain_3x_fixed v3x_fixed; struct clk_domain_3x_prog v3x_prog; struct clk_domain_3x_master v3x_master; struct clk_domain_3x_slave v3x_slave; } clk_domain_data; nvgpu_log_info(g, " "); memcpy(&clocks_table_header, clocks_table_ptr, VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07); if (clocks_table_header.header_size < (u8) VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07) { status = -EINVAL; goto done; } if (clocks_table_header.entry_size < (u8) VBIOS_CLOCKS_TABLE_1X_ENTRY_SIZE_09) { status = -EINVAL; goto done; } switch (clocks_table_header.clocks_hal) { case CLK_TABLE_HAL_ENTRY_GP: { vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gp; break; } case CLK_TABLE_HAL_ENTRY_GV: { vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gv; break; } default: { status = -EINVAL; goto done; } } pclkdomainobjs->cntr_sampling_periodms = (u16)clocks_table_header.cntr_sampling_periodms; /* Read table entries*/ clocks_tbl_entry_ptr = clocks_table_ptr + VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07; for (index = 0; index < clocks_table_header.entry_count; index++) { memcpy((void*) &clocks_table_entry, (void*) clocks_tbl_entry_ptr, clocks_table_header.entry_size); clk_domain_data.clk_domain.domain = (u8) vbiosclktbl1xhalentry[index].domain; clk_domain_data.clk_domain.api_domain = clktranslatehalmumsettoapinumset( BIT(clk_domain_data.clk_domain.domain)); clk_domain_data.v3x.b_noise_aware_capable = vbiosclktbl1xhalentry[index].b_noise_aware_capable; switch (BIOS_GET_FIELD(clocks_table_entry.flags0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE)) { case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_FIXED: { clk_domain_data.boardobj.type = CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED; clk_domain_data.v3x_fixed.freq_mhz = (u16)BIOS_GET_FIELD( clocks_table_entry.param1, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_FIXED_FREQUENCY_MHZ); break; } case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_MASTER: { clk_domain_data.boardobj.type = CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER; clk_domain_data.v3x_prog.clk_prog_idx_first = (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); clk_domain_data.v3x_prog.clk_prog_idx_last = (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); clk_domain_data.v3x_prog.noise_unaware_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); if (clk_domain_data.v3x.b_noise_aware_capable) { clk_domain_data.v3x_prog.noise_aware_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_AWARE_ORDERING_IDX)); clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); } else { clk_domain_data.v3x_prog.noise_aware_ordering_index = CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = false; } clk_domain_data.v3x_prog.factory_delta.data.delta_khz = 0; clk_domain_data.v3x_prog.factory_delta.type = 0; clk_domain_data.v3x_prog.freq_delta_min_mhz = (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MIN_MHZ)); clk_domain_data.v3x_prog.freq_delta_max_mhz = (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MAX_MHZ)); break; } case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_SLAVE: { clk_domain_data.boardobj.type = CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE; clk_domain_data.v3x_prog.clk_prog_idx_first = (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); clk_domain_data.v3x_prog.clk_prog_idx_last = (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); clk_domain_data.v3x_prog.noise_unaware_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); if (clk_domain_data.v3x.b_noise_aware_capable) { clk_domain_data.v3x_prog.noise_aware_ordering_index = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_AWARE_ORDERING_IDX)); clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); } else { clk_domain_data.v3x_prog.noise_aware_ordering_index = CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = false; } clk_domain_data.v3x_prog.factory_delta.data.delta_khz = 0; clk_domain_data.v3x_prog.factory_delta.type = 0; clk_domain_data.v3x_prog.freq_delta_min_mhz = 0; clk_domain_data.v3x_prog.freq_delta_max_mhz = 0; clk_domain_data.v3x_slave.master_idx = (u8)(BIOS_GET_FIELD(clocks_table_entry.param1, NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_SLAVE_MASTER_DOMAIN)); break; } default: { nvgpu_err(g, "error reading clock domain entry %d", index); status = (u32) -EINVAL; goto done; } } pclkdomain_dev = construct_clk_domain(g, (void *)&clk_domain_data); if (pclkdomain_dev == NULL) { nvgpu_err(g, "unable to construct clock domain boardobj for %d", index); status = (u32) -EINVAL; goto done; } status = boardobjgrp_objinsert(&pclkdomainobjs->super.super, (struct boardobj *)(void *)pclkdomain_dev, index); if (status != 0UL) { nvgpu_err(g, "unable to insert clock domain boardobj for %d", index); status = (u32) -EINVAL; goto done; } clocks_tbl_entry_ptr += clocks_table_header.entry_size; } done: nvgpu_log_info(g, " done status %x", status); return status; } static int devinit_get_clocks_table(struct gk20a *g, struct clk_domains *pclkdomainobjs) { int status = 0; u8 *clocks_table_ptr = NULL; struct vbios_clocks_table_1x_header clocks_table_header = { 0 }; nvgpu_log_info(g, " "); clocks_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, g->bios.clock_token, CLOCKS_TABLE); if (clocks_table_ptr == NULL) { status = -EINVAL; goto done; } memcpy(&clocks_table_header, clocks_table_ptr, VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07); if (clocks_table_header.version == 0x35U) { devinit_get_clocks_table_35(g, pclkdomainobjs, clocks_table_ptr); } else { devinit_get_clocks_table_1x(g, pclkdomainobjs, clocks_table_ptr); } done: return status; } static int clkdomainclkproglink_not_supported(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain) { nvgpu_log_info(g, " "); return -EINVAL; } static int clkdomainvfsearch_stub( struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain, u16 *clkmhz, u32 *voltuv, u8 rail) { nvgpu_log_info(g, " "); return -EINVAL; } static u32 clkdomaingetfpoints_stub( struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain, u32 *pfpointscount, u16 *pfreqpointsinmhz, u8 rail) { nvgpu_log_info(g, " "); return -EINVAL; } static int clk_domain_construct_super(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct clk_domain *pdomain; struct clk_domain *ptmpdomain = (struct clk_domain *)pargs; int status = 0; status = boardobj_construct_super(g, ppboardobj, size, pargs); if (status) { return -EINVAL; } pdomain = (struct clk_domain *)*ppboardobj; pdomain->super.pmudatainit = clk_domain_pmudatainit_super; pdomain->clkdomainclkproglink = clkdomainclkproglink_not_supported; pdomain->clkdomainclkvfsearch = clkdomainvfsearch_stub; pdomain->clkdomainclkgetfpoints = clkdomaingetfpoints_stub; pdomain->api_domain = ptmpdomain->api_domain; pdomain->domain = ptmpdomain->domain; pdomain->perf_domain_grp_idx = ptmpdomain->perf_domain_grp_idx; return status; } static int _clk_domain_pmudatainit_3x(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain_3x *pclk_domain_3x; struct nv_pmu_clk_clk_domain_3x_boardobj_set *pset; nvgpu_log_info(g, " "); status = clk_domain_pmudatainit_super(g, board_obj_ptr, ppmudata); if (status != 0) { return status; } pclk_domain_3x = (struct clk_domain_3x *)board_obj_ptr; pset = (struct nv_pmu_clk_clk_domain_3x_boardobj_set *)ppmudata; pset->b_noise_aware_capable = pclk_domain_3x->b_noise_aware_capable; return status; } static int clk_domain_construct_3x(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_3x *pdomain; struct clk_domain_3x *ptmpdomain = (struct clk_domain_3x *)pargs; int status = 0; ptmpobj->type_mask = BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X); status = clk_domain_construct_super(g, ppboardobj, size, pargs); if (status) { return -EINVAL; } pdomain = (struct clk_domain_3x *)*ppboardobj; pdomain->super.super.pmudatainit = _clk_domain_pmudatainit_3x; pdomain->b_noise_aware_capable = ptmpdomain->b_noise_aware_capable; return status; } static int clkdomainclkproglink_3x_prog(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain) { int status = 0; struct clk_domain_3x_prog *p3xprog = (struct clk_domain_3x_prog *)pdomain; struct clk_prog *pprog = NULL; u8 i; nvgpu_log_info(g, " "); for (i = p3xprog->clk_prog_idx_first; i <= p3xprog->clk_prog_idx_last; i++) { pprog = CLK_CLK_PROG_GET(pclk, i); if (pprog == NULL) { status = -EINVAL; } } return status; } static int clkdomaingetslaveclk(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain, u16 *pclkmhz, u16 masterclkmhz) { int status = 0; struct clk_prog *pprog = NULL; struct clk_prog_1x_master *pprog1xmaster = NULL; u8 slaveidx; struct clk_domain_3x_master *p3xmaster; nvgpu_log_info(g, " "); 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; struct clk_prog_1x_master *pprog1xmaster = NULL; u8 i; u8 *pslaveidx = NULL; u8 slaveidx; u16 clkmhz; u32 voltuv; u16 bestclkmhz; u32 bestvoltuv; nvgpu_log_info(g, " "); if ((pclkmhz == NULL) || (pvoltuv == NULL)) { return -EINVAL; } 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 print 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: nvgpu_log_info(g, "done status %x", status); return status; } static u32 clkdomaingetfpoints ( struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain, u32 *pfpointscount, u16 *pfreqpointsinmhz, 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; u32 fpointscount = 0; u32 remainingcount; u32 totalcount; u16 *freqpointsdata; u8 i; nvgpu_log_info(g, " "); if (pfpointscount == NULL) { return -EINVAL; } if ((pfreqpointsinmhz == NULL) && (*pfpointscount != 0)) { return -EINVAL; } if (pdomain->super.implements(g, &pdomain->super, CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { return -EINVAL; } freqpointsdata = pfreqpointsinmhz; totalcount = 0; fpointscount = *pfpointscount; remainingcount = fpointscount; /* 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++) { pprog = CLK_CLK_PROG_GET(pclk, i); pprog1xmaster = (struct clk_prog_1x_master *)pprog; status = pprog1xmaster->getfpoints(g, pclk, pprog1xmaster, &fpointscount, &freqpointsdata, rail); if (status) { *pfpointscount = 0; goto done; } totalcount += fpointscount; if (*pfpointscount) { remainingcount -= fpointscount; fpointscount = remainingcount; } else { fpointscount = 0; } } *pfpointscount = totalcount; done: nvgpu_log_info(g, "done status %x", status); return status; } static int clk_domain_pmudatainit_35_prog(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain_35_prog *pclk_domain_35_prog; struct clk_domain_3x_prog *pclk_domain_3x_prog; struct nv_pmu_clk_clk_domain_35_prog_boardobj_set *pset; struct clk_domains *pdomains = &(g->clk_pmu.clk_domainobjs); nvgpu_log_info(g, " "); status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); if (status != 0UL) { return status; } pclk_domain_35_prog = (struct clk_domain_35_prog *)(void*)board_obj_ptr; pclk_domain_3x_prog = &pclk_domain_35_prog->super; pset = (struct nv_pmu_clk_clk_domain_35_prog_boardobj_set *) (void*) ppmudata; pset->super.clk_prog_idx_first = pclk_domain_3x_prog->clk_prog_idx_first; pset->super.clk_prog_idx_last = pclk_domain_3x_prog->clk_prog_idx_last; pset->super.b_force_noise_unaware_ordering = pclk_domain_3x_prog->b_force_noise_unaware_ordering; pset->super.factory_delta = pclk_domain_3x_prog->factory_delta; pset->super.freq_delta_min_mhz = pclk_domain_3x_prog->freq_delta_min_mhz; pset->super.freq_delta_max_mhz = pclk_domain_3x_prog->freq_delta_max_mhz; memcpy(&pset->super.deltas, &pdomains->deltas, (sizeof(struct ctrl_clk_clk_delta))); pset->pre_volt_ordering_index = pclk_domain_35_prog->pre_volt_ordering_index; pset->post_volt_ordering_index = pclk_domain_35_prog->post_volt_ordering_index; pset->clk_pos = pclk_domain_35_prog->clk_pos; pset->clk_vf_curve_count = pclk_domain_35_prog->clk_vf_curve_count; return status; } static int _clk_domain_pmudatainit_3x_prog(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain_3x_prog *pclk_domain_3x_prog; struct nv_pmu_clk_clk_domain_30_prog_boardobj_set *pset; struct clk_domains *pdomains = &(g->clk_pmu.clk_domainobjs); nvgpu_log_info(g, " "); status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); if (status != 0) { return status; } pclk_domain_3x_prog = (struct clk_domain_3x_prog *)board_obj_ptr; pset = (struct nv_pmu_clk_clk_domain_30_prog_boardobj_set *) ppmudata; pset->super.clk_prog_idx_first = pclk_domain_3x_prog->clk_prog_idx_first; pset->super.clk_prog_idx_last = pclk_domain_3x_prog->clk_prog_idx_last; pset->noise_unaware_ordering_index = pclk_domain_3x_prog->noise_unaware_ordering_index; pset->noise_aware_ordering_index = pclk_domain_3x_prog->noise_aware_ordering_index; pset->super.b_force_noise_unaware_ordering = pclk_domain_3x_prog->b_force_noise_unaware_ordering; pset->super.factory_delta = pclk_domain_3x_prog->factory_delta; pset->super.freq_delta_min_mhz = pclk_domain_3x_prog->freq_delta_min_mhz; pset->super.freq_delta_max_mhz = pclk_domain_3x_prog->freq_delta_max_mhz; memcpy(&pset->super.deltas, &pdomains->deltas, (sizeof(struct ctrl_clk_clk_delta))); return status; } static int clk_domain_construct_35_prog(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_35_prog *pdomain; struct clk_domain_35_prog *ptmpdomain = (struct clk_domain_35_prog *)pargs; int status = 0; ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG); status = clk_domain_construct_3x(g, ppboardobj, size, pargs); if (status != 0UL) { return (u32) -EINVAL; } pdomain = (struct clk_domain_35_prog *)(void*) *ppboardobj; pdomain->super.super.super.super.pmudatainit = clk_domain_pmudatainit_35_prog; pdomain->super.super.super.clkdomainclkproglink = clkdomainclkproglink_3x_prog; pdomain->super.super.super.clkdomainclkvfsearch = clkdomainvfsearch; pdomain->super.super.super.clkdomainclkgetfpoints = clkdomaingetfpoints; pdomain->super.clk_prog_idx_first = ptmpdomain->super.clk_prog_idx_first; pdomain->super.clk_prog_idx_last = ptmpdomain->super.clk_prog_idx_last; pdomain->super.noise_unaware_ordering_index = ptmpdomain->super.noise_unaware_ordering_index; pdomain->super.noise_aware_ordering_index = ptmpdomain->super.noise_aware_ordering_index; pdomain->super.b_force_noise_unaware_ordering = ptmpdomain->super.b_force_noise_unaware_ordering; pdomain->super.factory_delta = ptmpdomain->super.factory_delta; pdomain->super.freq_delta_min_mhz = ptmpdomain->super.freq_delta_min_mhz; pdomain->super.freq_delta_max_mhz = ptmpdomain->super.freq_delta_max_mhz; pdomain->pre_volt_ordering_index = ptmpdomain->pre_volt_ordering_index; pdomain->post_volt_ordering_index = ptmpdomain->post_volt_ordering_index; pdomain->clk_pos = ptmpdomain->clk_pos; pdomain->clk_vf_curve_count = ptmpdomain->clk_vf_curve_count; return status; } static int clk_domain_construct_3x_prog(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_3x_prog *pdomain; struct clk_domain_3x_prog *ptmpdomain = (struct clk_domain_3x_prog *)pargs; int status = 0; ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG); status = clk_domain_construct_3x(g, ppboardobj, size, pargs); if (status) { return -EINVAL; } pdomain = (struct clk_domain_3x_prog *)*ppboardobj; pdomain->super.super.super.pmudatainit = _clk_domain_pmudatainit_3x_prog; pdomain->super.super.clkdomainclkproglink = clkdomainclkproglink_3x_prog; pdomain->super.super.clkdomainclkvfsearch = clkdomainvfsearch; pdomain->super.super.clkdomainclkgetfpoints = clkdomaingetfpoints; 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 = ptmpdomain->noise_unaware_ordering_index; pdomain->noise_aware_ordering_index = ptmpdomain->noise_aware_ordering_index; pdomain->b_force_noise_unaware_ordering = ptmpdomain->b_force_noise_unaware_ordering; pdomain->factory_delta = ptmpdomain->factory_delta; pdomain->freq_delta_min_mhz = ptmpdomain->freq_delta_min_mhz; pdomain->freq_delta_max_mhz = ptmpdomain->freq_delta_max_mhz; return status; } static int _clk_domain_pmudatainit_35_slave(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain_35_slave *pclk_domain_35_slave; struct nv_pmu_clk_clk_domain_35_slave_boardobj_set *pset; nvgpu_log_info(g, " "); status = clk_domain_pmudatainit_35_prog(g, board_obj_ptr, ppmudata); if (status != 0UL) { return status; } pclk_domain_35_slave = (struct clk_domain_35_slave *)(void*)board_obj_ptr; pset = (struct nv_pmu_clk_clk_domain_35_slave_boardobj_set *) (void*) ppmudata; pset->slave.master_idx = pclk_domain_35_slave->slave.master_idx; return status; } static int clk_domain_pmudatainit_3x_slave(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain_3x_slave *pclk_domain_3x_slave; struct nv_pmu_clk_clk_domain_3x_slave_boardobj_set *pset; nvgpu_log_info(g, " "); status = _clk_domain_pmudatainit_3x_prog(g, board_obj_ptr, ppmudata); if (status != 0) { return status; } pclk_domain_3x_slave = (struct clk_domain_3x_slave *)board_obj_ptr; pset = (struct nv_pmu_clk_clk_domain_3x_slave_boardobj_set *) ppmudata; pset->master_idx = pclk_domain_3x_slave->master_idx; return status; } static int clk_domain_construct_35_slave(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_35_slave *pdomain; struct clk_domain_35_slave *ptmpdomain = (struct clk_domain_35_slave *)pargs; int status = 0; if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE) { return (u32) -EINVAL; } ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE); status = clk_domain_construct_35_prog(g, ppboardobj, size, pargs); if (status != 0UL) { return (u32) -EINVAL; } pdomain = (struct clk_domain_35_slave *)(void*)*ppboardobj; pdomain->super.super.super.super.super.pmudatainit = _clk_domain_pmudatainit_35_slave; pdomain->slave.master_idx = ptmpdomain->slave.master_idx; pdomain->slave.clkdomainclkgetslaveclk = clkdomaingetslaveclk; return status; } static int clk_domain_construct_3x_slave(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_3x_slave *pdomain; struct clk_domain_3x_slave *ptmpdomain = (struct clk_domain_3x_slave *)pargs; int status = 0; if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE) { return -EINVAL; } ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE); status = clk_domain_construct_3x_prog(g, ppboardobj, size, pargs); if (status != 0UL) { return -EINVAL; } pdomain = (struct clk_domain_3x_slave *)*ppboardobj; pdomain->super.super.super.super.pmudatainit = clk_domain_pmudatainit_3x_slave; pdomain->master_idx = ptmpdomain->master_idx; pdomain->clkdomainclkgetslaveclk = clkdomaingetslaveclk; return status; } static int clkdomainclkproglink_3x_master(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain) { int 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; u16 freq_max_last_mhz = 0; u8 i; nvgpu_log_info(g, " "); status = clkdomainclkproglink_3x_prog(g, pclk, pdomain); if (status) { goto done; } /* 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++) { 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->vfflatten(g, pclk, pprog1xmaster, BOARDOBJ_GET_IDX(p3xmaster), &freq_max_last_mhz); if (status) { goto done; } } done: nvgpu_log_info(g, "done status %x", status); return status; } static int clk_domain_pmudatainit_35_master(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain_35_master *pclk_domain_35_master; struct nv_pmu_clk_clk_domain_35_master_boardobj_set *pset; nvgpu_log_info(g, " "); status = clk_domain_pmudatainit_35_prog(g, board_obj_ptr, ppmudata); if (status != 0UL) { return status; } pclk_domain_35_master = (struct clk_domain_35_master *) (void*) board_obj_ptr; pset = (struct nv_pmu_clk_clk_domain_35_master_boardobj_set *) (void*) ppmudata; pset->master.slave_idxs_mask = pclk_domain_35_master->master.slave_idxs_mask; return status; } static int _clk_domain_pmudatainit_3x_master(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain_3x_master *pclk_domain_3x_master; struct nv_pmu_clk_clk_domain_3x_master_boardobj_set *pset; nvgpu_log_info(g, " "); status = _clk_domain_pmudatainit_3x_prog(g, board_obj_ptr, ppmudata); if (status != 0) { return status; } pclk_domain_3x_master = (struct clk_domain_3x_master *)board_obj_ptr; pset = (struct nv_pmu_clk_clk_domain_3x_master_boardobj_set *) ppmudata; pset->slave_idxs_mask = pclk_domain_3x_master->slave_idxs_mask; return status; } static int clk_domain_construct_35_master(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_35_master *pdomain; int status = 0; if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER) { return -EINVAL; } ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER); status = clk_domain_construct_35_prog(g, ppboardobj, size, pargs); if (status != 0UL) { return (u32) -EINVAL; } pdomain = (struct clk_domain_35_master *)(void*) *ppboardobj; pdomain->super.super.super.super.super.pmudatainit = clk_domain_pmudatainit_35_master; pdomain->super.super.super.super.clkdomainclkproglink = clkdomainclkproglink_3x_master; pdomain->master.slave_idxs_mask = 0; return status; } static int clk_domain_construct_3x_master(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_3x_master *pdomain; int status = 0; if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER) { return -EINVAL; } ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER); status = clk_domain_construct_3x_prog(g, ppboardobj, size, pargs); if (status) { return -EINVAL; } pdomain = (struct clk_domain_3x_master *)*ppboardobj; pdomain->super.super.super.super.pmudatainit = _clk_domain_pmudatainit_3x_master; pdomain->super.super.super.clkdomainclkproglink = clkdomainclkproglink_3x_master; pdomain->slave_idxs_mask = 0; return status; } static int clkdomainclkproglink_fixed(struct gk20a *g, struct clk_pmupstate *pclk, struct clk_domain *pdomain) { nvgpu_log_info(g, " "); return 0; } static int _clk_domain_pmudatainit_3x_fixed(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain_3x_fixed *pclk_domain_3x_fixed; struct nv_pmu_clk_clk_domain_3x_fixed_boardobj_set *pset; nvgpu_log_info(g, " "); status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); if (status != 0) { return status; } pclk_domain_3x_fixed = (struct clk_domain_3x_fixed *)board_obj_ptr; pset = (struct nv_pmu_clk_clk_domain_3x_fixed_boardobj_set *) ppmudata; pset->freq_mhz = pclk_domain_3x_fixed->freq_mhz; return status; } static int clk_domain_construct_3x_fixed(struct gk20a *g, struct boardobj **ppboardobj, u16 size, void *pargs) { struct boardobj *ptmpobj = (struct boardobj *)pargs; struct clk_domain_3x_fixed *pdomain; struct clk_domain_3x_fixed *ptmpdomain = (struct clk_domain_3x_fixed *)pargs; int status = 0; if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED) { return -EINVAL; } ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED); status = clk_domain_construct_3x(g, ppboardobj, size, pargs); if (status) { return -EINVAL; } pdomain = (struct clk_domain_3x_fixed *)*ppboardobj; pdomain->super.super.super.pmudatainit = _clk_domain_pmudatainit_3x_fixed; pdomain->super.super.clkdomainclkproglink = clkdomainclkproglink_fixed; pdomain->freq_mhz = ptmpdomain->freq_mhz; return status; } static struct clk_domain *construct_clk_domain(struct gk20a *g, void *pargs) { struct boardobj *board_obj_ptr = NULL; u32 status; nvgpu_log_info(g, " %d", BOARDOBJ_GET_TYPE(pargs)); switch (BOARDOBJ_GET_TYPE(pargs)) { case CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED: status = clk_domain_construct_3x_fixed(g, &board_obj_ptr, sizeof(struct clk_domain_3x_fixed), pargs); break; case CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER: status = clk_domain_construct_35_master(g, &board_obj_ptr, sizeof(struct clk_domain_35_master), pargs); break; case CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER: status = clk_domain_construct_3x_master(g, &board_obj_ptr, sizeof(struct clk_domain_3x_master), pargs); break; case CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE: status = clk_domain_construct_35_slave(g, &board_obj_ptr, sizeof(struct clk_domain_35_slave), pargs); break; case CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE: status = clk_domain_construct_3x_slave(g, &board_obj_ptr, sizeof(struct clk_domain_3x_slave), pargs); break; default: return NULL; } if (status) { return NULL; } nvgpu_log_info(g, " Done"); return (struct clk_domain *)board_obj_ptr; } static int clk_domain_pmudatainit_super(struct gk20a *g, struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) { int status = 0; struct clk_domain *pclk_domain; struct nv_pmu_clk_clk_domain_boardobj_set *pset; nvgpu_log_info(g, " "); status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); if (status != 0) { return status; } pclk_domain = (struct clk_domain *)board_obj_ptr; pset = (struct nv_pmu_clk_clk_domain_boardobj_set *)ppmudata; pset->domain = pclk_domain->domain; pset->api_domain = pclk_domain->api_domain; pset->perf_domain_grp_idx = pclk_domain->perf_domain_grp_idx; return status; } int clk_domain_clk_prog_link(struct gk20a *g, struct clk_pmupstate *pclk) { int status = 0; struct clk_domain *pdomain; u8 i; /* Iterate over all CLK_DOMAINs and flatten their VF curves.*/ BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), struct clk_domain *, pdomain, i) { status = pdomain->clkdomainclkproglink(g, pclk, pdomain); if (status) { nvgpu_err(g, "error flattening VF for CLK DOMAIN - 0x%x", pdomain->domain); goto done; } } done: return status; }