/* * Copyright (c) 2016, 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 "priv.h" #include "gk20a.h" #define KHZ (1000) #define MHZ (KHZ * 1000) #define MASK(w) ((1 << w) - 1) #define BYPASSCTRL_SYS (SYS_GPCPLL_CFG_BASE + 0x340) #define BYPASSCTRL_SYS_GPCPLL_SHIFT 0 #define BYPASSCTRL_SYS_GPCPLL_WIDTH 1 static u32 pl_to_div(u32 pl) { return pl; } static u32 div_to_pl(u32 div) { return div; } static const struct gk20a_clk_pllg_params gm20b_pllg_params = { .min_vco = 1300000, .max_vco = 2600000, .min_u = 12000, .max_u = 38400, .min_m = 1, .max_m = 255, .min_n = 8, .max_n = 255, .min_pl = 1, .max_pl = 31, }; static struct nvkm_pstate gm20b_pstates[] = { { .base = { .domain[nv_clk_src_gpc] = 76800, .voltage = 0, }, }, { .base = { .domain[nv_clk_src_gpc] = 153600, .voltage = 1, }, }, { .base = { .domain[nv_clk_src_gpc] = 230400, .voltage = 2, }, }, { .base = { .domain[nv_clk_src_gpc] = 307200, .voltage = 3, }, }, { .base = { .domain[nv_clk_src_gpc] = 384000, .voltage = 4, }, }, { .base = { .domain[nv_clk_src_gpc] = 460800, .voltage = 5, }, }, { .base = { .domain[nv_clk_src_gpc] = 537600, .voltage = 6, }, }, { .base = { .domain[nv_clk_src_gpc] = 614400, .voltage = 7, }, }, { .base = { .domain[nv_clk_src_gpc] = 691200, .voltage = 8, }, }, { .base = { .domain[nv_clk_src_gpc] = 768000, .voltage = 9, }, }, { .base = { .domain[nv_clk_src_gpc] = 844800, .voltage = 10, }, }, { .base = { .domain[nv_clk_src_gpc] = 921600, .voltage = 11, }, }, { .base = { .domain[nv_clk_src_gpc] = 998400, .voltage = 12, }, }, }; static int gm20b_clk_init(struct nvkm_clk *base) { struct gk20a_clk *clk = gk20a_clk(base); struct nvkm_subdev *subdev = &clk->base.subdev; struct nvkm_device *device = subdev->device; int ret; /* Set the global bypass control to VCO */ nvkm_mask(device, BYPASSCTRL_SYS, MASK(BYPASSCTRL_SYS_GPCPLL_WIDTH) << BYPASSCTRL_SYS_GPCPLL_SHIFT, 0); /* Start with lowest frequency */ base->func->calc(base, &base->func->pstates[0].base); ret = base->func->prog(&clk->base); if (ret) { nvkm_error(subdev, "cannot initialize clock\n"); return ret; } return 0; } static const struct nvkm_clk_func gm20b_clk_speedo0 = { .init = gm20b_clk_init, .fini = gk20a_clk_fini, .read = gk20a_clk_read, .calc = gk20a_clk_calc, .prog = gk20a_clk_prog, .tidy = gk20a_clk_tidy, .pstates = gm20b_pstates, .nr_pstates = ARRAY_SIZE(gm20b_pstates) - 1, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV }, { nv_clk_src_max }, }, }; int gm20b_clk_new(struct nvkm_device *device, int index, struct nvkm_clk **pclk) { struct gk20a_clk *clk; int ret; clk = kzalloc(sizeof(*clk), GFP_KERNEL); if (!clk) return -ENOMEM; *pclk = &clk->base; ret = _gk20a_clk_ctor(device, index, &gm20b_clk_speedo0, &gm20b_pllg_params, clk); clk->pl_to_div = pl_to_div; clk->div_to_pl = div_to_pl; return ret; }