1/* $NetBSD: nouveau_nvkm_subdev_clk_nv40.c,v 1.3 2021/12/18 23:45:39 riastradh Exp $ */ 2 3/* 4 * Copyright 2012 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs 25 */ 26#include <sys/cdefs.h> 27__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_clk_nv40.c,v 1.3 2021/12/18 23:45:39 riastradh Exp $"); 28 29#define nv40_clk(p) container_of((p), struct nv40_clk, base) 30#include "priv.h" 31#include "pll.h" 32 33#include <subdev/bios.h> 34#include <subdev/bios/pll.h> 35 36struct nv40_clk { 37 struct nvkm_clk base; 38 u32 ctrl; 39 u32 npll_ctrl; 40 u32 npll_coef; 41 u32 spll; 42}; 43 44static u32 45read_pll_1(struct nv40_clk *clk, u32 reg) 46{ 47 struct nvkm_device *device = clk->base.subdev.device; 48 u32 ctrl = nvkm_rd32(device, reg + 0x00); 49 int P = (ctrl & 0x00070000) >> 16; 50 int N = (ctrl & 0x0000ff00) >> 8; 51 int M = (ctrl & 0x000000ff) >> 0; 52 u32 ref = 27000, khz = 0; 53 54 if (ctrl & 0x80000000) 55 khz = ref * N / M; 56 57 return khz >> P; 58} 59 60static u32 61read_pll_2(struct nv40_clk *clk, u32 reg) 62{ 63 struct nvkm_device *device = clk->base.subdev.device; 64 u32 ctrl = nvkm_rd32(device, reg + 0x00); 65 u32 coef = nvkm_rd32(device, reg + 0x04); 66 int N2 = (coef & 0xff000000) >> 24; 67 int M2 = (coef & 0x00ff0000) >> 16; 68 int N1 = (coef & 0x0000ff00) >> 8; 69 int M1 = (coef & 0x000000ff) >> 0; 70 int P = (ctrl & 0x00070000) >> 16; 71 u32 ref = 27000, khz = 0; 72 73 if ((ctrl & 0x80000000) && M1) { 74 khz = ref * N1 / M1; 75 if ((ctrl & 0x40000100) == 0x40000000) { 76 if (M2) 77 khz = khz * N2 / M2; 78 else 79 khz = 0; 80 } 81 } 82 83 return khz >> P; 84} 85 86static u32 87read_clk(struct nv40_clk *clk, u32 src) 88{ 89 switch (src) { 90 case 3: 91 return read_pll_2(clk, 0x004000); 92 case 2: 93 return read_pll_1(clk, 0x004008); 94 default: 95 break; 96 } 97 98 return 0; 99} 100 101static int 102nv40_clk_read(struct nvkm_clk *base, enum nv_clk_src src) 103{ 104 struct nv40_clk *clk = nv40_clk(base); 105 struct nvkm_subdev *subdev = &clk->base.subdev; 106 struct nvkm_device *device = subdev->device; 107 u32 mast = nvkm_rd32(device, 0x00c040); 108 109 switch (src) { 110 case nv_clk_src_crystal: 111 return device->crystal; 112 case nv_clk_src_href: 113 return 100000; /*XXX: PCIE/AGP differ*/ 114 case nv_clk_src_core: 115 return read_clk(clk, (mast & 0x00000003) >> 0); 116 case nv_clk_src_shader: 117 return read_clk(clk, (mast & 0x00000030) >> 4); 118 case nv_clk_src_mem: 119 return read_pll_2(clk, 0x4020); 120 default: 121 break; 122 } 123 124 nvkm_debug(subdev, "unknown clock source %d %08x\n", src, mast); 125 return -EINVAL; 126} 127 128static int 129nv40_clk_calc_pll(struct nv40_clk *clk, u32 reg, u32 khz, 130 int *N1, int *M1, int *N2, int *M2, int *log2P) 131{ 132 struct nvkm_subdev *subdev = &clk->base.subdev; 133 struct nvbios_pll pll; 134 int ret; 135 136 ret = nvbios_pll_parse(subdev->device->bios, reg, &pll); 137 if (ret) 138 return ret; 139 140 if (khz < pll.vco1.max_freq) 141 pll.vco2.max_freq = 0; 142 143 ret = nv04_pll_calc(subdev, &pll, khz, N1, M1, N2, M2, log2P); 144 if (ret == 0) 145 return -ERANGE; 146 147 return ret; 148} 149 150static int 151nv40_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) 152{ 153 struct nv40_clk *clk = nv40_clk(base); 154 int gclk = cstate->domain[nv_clk_src_core]; 155 int sclk = cstate->domain[nv_clk_src_shader]; 156 int N1, M1, N2, M2, log2P; 157 int ret; 158 159 /* core/geometric clock */ 160 ret = nv40_clk_calc_pll(clk, 0x004000, gclk, 161 &N1, &M1, &N2, &M2, &log2P); 162 if (ret < 0) 163 return ret; 164 165 if (N2 == M2) { 166 clk->npll_ctrl = 0x80000100 | (log2P << 16); 167 clk->npll_coef = (N1 << 8) | M1; 168 } else { 169 clk->npll_ctrl = 0xc0000000 | (log2P << 16); 170 clk->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1; 171 } 172 173 /* use the second pll for shader/rop clock, if it differs from core */ 174 if (sclk && sclk != gclk) { 175 ret = nv40_clk_calc_pll(clk, 0x004008, sclk, 176 &N1, &M1, NULL, NULL, &log2P); 177 if (ret < 0) 178 return ret; 179 180 clk->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1; 181 clk->ctrl = 0x00000223; 182 } else { 183 clk->spll = 0x00000000; 184 clk->ctrl = 0x00000333; 185 } 186 187 return 0; 188} 189 190static int 191nv40_clk_prog(struct nvkm_clk *base) 192{ 193 struct nv40_clk *clk = nv40_clk(base); 194 struct nvkm_device *device = clk->base.subdev.device; 195 nvkm_mask(device, 0x00c040, 0x00000333, 0x00000000); 196 nvkm_wr32(device, 0x004004, clk->npll_coef); 197 nvkm_mask(device, 0x004000, 0xc0070100, clk->npll_ctrl); 198 nvkm_mask(device, 0x004008, 0xc007ffff, clk->spll); 199 mdelay(5); 200 nvkm_mask(device, 0x00c040, 0x00000333, clk->ctrl); 201 return 0; 202} 203 204static void 205nv40_clk_tidy(struct nvkm_clk *obj) 206{ 207} 208 209static const struct nvkm_clk_func 210nv40_clk = { 211 .read = nv40_clk_read, 212 .calc = nv40_clk_calc, 213 .prog = nv40_clk_prog, 214 .tidy = nv40_clk_tidy, 215 .domains = { 216 { nv_clk_src_crystal, 0xff }, 217 { nv_clk_src_href , 0xff }, 218 { nv_clk_src_core , 0xff, 0, "core", 1000 }, 219 { nv_clk_src_shader , 0xff, 0, "shader", 1000 }, 220 { nv_clk_src_mem , 0xff, 0, "memory", 1000 }, 221 { nv_clk_src_max } 222 } 223}; 224 225int 226nv40_clk_new(struct nvkm_device *device, int index, struct nvkm_clk **pclk) 227{ 228 struct nv40_clk *clk; 229 230 if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL))) 231 return -ENOMEM; 232 clk->base.pll_calc = nv04_clk_pll_calc; 233 clk->base.pll_prog = nv04_clk_pll_prog; 234 *pclk = &clk->base; 235 236 return nvkm_clk_ctor(&nv40_clk, device, index, true, &clk->base); 237} 238