1f3867f43SBen Skeggs /* 2f3867f43SBen Skeggs * Copyright 2013 Red Hat Inc. 3f3867f43SBen Skeggs * 4f3867f43SBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a 5f3867f43SBen Skeggs * copy of this software and associated documentation files (the "Software"), 6f3867f43SBen Skeggs * to deal in the Software without restriction, including without limitation 7f3867f43SBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8f3867f43SBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the 9f3867f43SBen Skeggs * Software is furnished to do so, subject to the following conditions: 10f3867f43SBen Skeggs * 11f3867f43SBen Skeggs * The above copyright notice and this permission notice shall be included in 12f3867f43SBen Skeggs * all copies or substantial portions of the Software. 13f3867f43SBen Skeggs * 14f3867f43SBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15f3867f43SBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16f3867f43SBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17f3867f43SBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18f3867f43SBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19f3867f43SBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20f3867f43SBen Skeggs * OTHER DEALINGS IN THE SOFTWARE. 21f3867f43SBen Skeggs * 22f3867f43SBen Skeggs * Authors: Ben Skeggs 23f3867f43SBen Skeggs */ 246625f55cSBen Skeggs #include "priv.h" 256625f55cSBen Skeggs 26f3867f43SBen Skeggs #include <subdev/bios.h> 27f3867f43SBen Skeggs #include <subdev/bios/boost.h> 28f3867f43SBen Skeggs #include <subdev/bios/cstep.h> 29f3867f43SBen Skeggs #include <subdev/bios/perf.h> 304b9ce6e7SKarol Herbst #include <subdev/bios/vpstate.h> 317632b30eSBen Skeggs #include <subdev/fb.h> 327632b30eSBen Skeggs #include <subdev/therm.h> 337632b30eSBen Skeggs #include <subdev/volt.h> 347632b30eSBen Skeggs 357632b30eSBen Skeggs #include <core/option.h> 36f3867f43SBen Skeggs 37f3867f43SBen Skeggs /****************************************************************************** 38f3867f43SBen Skeggs * misc 39f3867f43SBen Skeggs *****************************************************************************/ 40f3867f43SBen Skeggs static u32 417632b30eSBen Skeggs nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust, 42f3867f43SBen Skeggs u8 pstate, u8 domain, u32 input) 43f3867f43SBen Skeggs { 4446484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 45f3867f43SBen Skeggs struct nvbios_boostE boostE; 46f3867f43SBen Skeggs u8 ver, hdr, cnt, len; 4758786017SBen Skeggs u32 data; 48f3867f43SBen Skeggs 49f3867f43SBen Skeggs data = nvbios_boostEm(bios, pstate, &ver, &hdr, &cnt, &len, &boostE); 50f3867f43SBen Skeggs if (data) { 51f3867f43SBen Skeggs struct nvbios_boostS boostS; 52f3867f43SBen Skeggs u8 idx = 0, sver, shdr; 5358786017SBen Skeggs u32 subd; 54f3867f43SBen Skeggs 55f3867f43SBen Skeggs input = max(boostE.min, input); 56f3867f43SBen Skeggs input = min(boostE.max, input); 57f3867f43SBen Skeggs do { 58f3867f43SBen Skeggs sver = ver; 59f3867f43SBen Skeggs shdr = hdr; 60f3867f43SBen Skeggs subd = nvbios_boostSp(bios, idx++, data, &sver, &shdr, 61f3867f43SBen Skeggs cnt, len, &boostS); 62f3867f43SBen Skeggs if (subd && boostS.domain == domain) { 63f3867f43SBen Skeggs if (adjust) 64f3867f43SBen Skeggs input = input * boostS.percent / 100; 65f3867f43SBen Skeggs input = max(boostS.min, input); 66f3867f43SBen Skeggs input = min(boostS.max, input); 67f3867f43SBen Skeggs break; 68f3867f43SBen Skeggs } 69f3867f43SBen Skeggs } while (subd); 70f3867f43SBen Skeggs } 71f3867f43SBen Skeggs 72f3867f43SBen Skeggs return input; 73f3867f43SBen Skeggs } 74f3867f43SBen Skeggs 75f3867f43SBen Skeggs /****************************************************************************** 76f3867f43SBen Skeggs * C-States 77f3867f43SBen Skeggs *****************************************************************************/ 781f7f3d91SKarol Herbst static bool 791f7f3d91SKarol Herbst nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, 801f7f3d91SKarol Herbst u32 max_volt, int temp) 811f7f3d91SKarol Herbst { 824b9ce6e7SKarol Herbst const struct nvkm_domain *domain = clk->domains; 831f7f3d91SKarol Herbst struct nvkm_volt *volt = clk->subdev.device->volt; 841f7f3d91SKarol Herbst int voltage; 851f7f3d91SKarol Herbst 864b9ce6e7SKarol Herbst while (domain && domain->name != nv_clk_src_max) { 874b9ce6e7SKarol Herbst if (domain->flags & NVKM_CLK_DOM_FLAG_VPSTATE) { 884b9ce6e7SKarol Herbst u32 freq = cstate->domain[domain->name]; 894b9ce6e7SKarol Herbst switch (clk->boost_mode) { 904b9ce6e7SKarol Herbst case NVKM_CLK_BOOST_NONE: 914b9ce6e7SKarol Herbst if (clk->base_khz && freq > clk->base_khz) 924b9ce6e7SKarol Herbst return false; 934b9ce6e7SKarol Herbst case NVKM_CLK_BOOST_BIOS: 944b9ce6e7SKarol Herbst if (clk->boost_khz && freq > clk->boost_khz) 954b9ce6e7SKarol Herbst return false; 964b9ce6e7SKarol Herbst } 974b9ce6e7SKarol Herbst } 984b9ce6e7SKarol Herbst domain++; 994b9ce6e7SKarol Herbst } 1004b9ce6e7SKarol Herbst 1011f7f3d91SKarol Herbst if (!volt) 1021f7f3d91SKarol Herbst return true; 1031f7f3d91SKarol Herbst 1041f7f3d91SKarol Herbst voltage = nvkm_volt_map(volt, cstate->voltage, temp); 1051f7f3d91SKarol Herbst if (voltage < 0) 1061f7f3d91SKarol Herbst return false; 1071f7f3d91SKarol Herbst return voltage <= min(max_volt, volt->max_uv); 1081f7f3d91SKarol Herbst } 1091f7f3d91SKarol Herbst 1101f7f3d91SKarol Herbst static struct nvkm_cstate * 1111f7f3d91SKarol Herbst nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, 1121f7f3d91SKarol Herbst struct nvkm_cstate *start) 1131f7f3d91SKarol Herbst { 1141f7f3d91SKarol Herbst struct nvkm_device *device = clk->subdev.device; 1151f7f3d91SKarol Herbst struct nvkm_volt *volt = device->volt; 1161f7f3d91SKarol Herbst struct nvkm_cstate *cstate; 1171f7f3d91SKarol Herbst int max_volt; 1181f7f3d91SKarol Herbst 1191f7f3d91SKarol Herbst if (!pstate || !start) 1201f7f3d91SKarol Herbst return NULL; 1211f7f3d91SKarol Herbst 1221f7f3d91SKarol Herbst if (!volt) 1231f7f3d91SKarol Herbst return start; 1241f7f3d91SKarol Herbst 1251f7f3d91SKarol Herbst max_volt = volt->max_uv; 1261f7f3d91SKarol Herbst if (volt->max0_id != 0xff) 1271f7f3d91SKarol Herbst max_volt = min(max_volt, 1281f7f3d91SKarol Herbst nvkm_volt_map(volt, volt->max0_id, clk->temp)); 1291f7f3d91SKarol Herbst if (volt->max1_id != 0xff) 1301f7f3d91SKarol Herbst max_volt = min(max_volt, 1311f7f3d91SKarol Herbst nvkm_volt_map(volt, volt->max1_id, clk->temp)); 1321f7f3d91SKarol Herbst if (volt->max2_id != 0xff) 1331f7f3d91SKarol Herbst max_volt = min(max_volt, 1341f7f3d91SKarol Herbst nvkm_volt_map(volt, volt->max2_id, clk->temp)); 1351f7f3d91SKarol Herbst 1361f7f3d91SKarol Herbst for (cstate = start; &cstate->head != &pstate->list; 1371f7f3d91SKarol Herbst cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) { 1381f7f3d91SKarol Herbst if (nvkm_cstate_valid(clk, cstate, max_volt, clk->temp)) 1391f7f3d91SKarol Herbst break; 1401f7f3d91SKarol Herbst } 1411f7f3d91SKarol Herbst 1421f7f3d91SKarol Herbst return cstate; 1431f7f3d91SKarol Herbst } 1441f7f3d91SKarol Herbst 1450d6f8100SKarol Herbst static struct nvkm_cstate * 1460d6f8100SKarol Herbst nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) 1470d6f8100SKarol Herbst { 1480d6f8100SKarol Herbst struct nvkm_cstate *cstate; 1490d6f8100SKarol Herbst if (cstatei == NVKM_CLK_CSTATE_HIGHEST) 1500d6f8100SKarol Herbst return list_last_entry(&pstate->list, typeof(*cstate), head); 1510d6f8100SKarol Herbst else { 1520d6f8100SKarol Herbst list_for_each_entry(cstate, &pstate->list, head) { 1530d6f8100SKarol Herbst if (cstate->id == cstatei) 1540d6f8100SKarol Herbst return cstate; 1550d6f8100SKarol Herbst } 1560d6f8100SKarol Herbst } 1570d6f8100SKarol Herbst return NULL; 1580d6f8100SKarol Herbst } 1590d6f8100SKarol Herbst 160f3867f43SBen Skeggs static int 1617632b30eSBen Skeggs nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) 162f3867f43SBen Skeggs { 163b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 164b907649eSBen Skeggs struct nvkm_device *device = subdev->device; 165b907649eSBen Skeggs struct nvkm_therm *therm = device->therm; 166b907649eSBen Skeggs struct nvkm_volt *volt = device->volt; 1677632b30eSBen Skeggs struct nvkm_cstate *cstate; 168f3867f43SBen Skeggs int ret; 169f3867f43SBen Skeggs 170f3867f43SBen Skeggs if (!list_empty(&pstate->list)) { 1710d6f8100SKarol Herbst cstate = nvkm_cstate_get(clk, pstate, cstatei); 1721f7f3d91SKarol Herbst cstate = nvkm_cstate_find_best(clk, pstate, cstate); 173f3867f43SBen Skeggs } else { 174f3867f43SBen Skeggs cstate = &pstate->base; 175f3867f43SBen Skeggs } 176f3867f43SBen Skeggs 177da06b46bSBen Skeggs if (therm) { 178da06b46bSBen Skeggs ret = nvkm_therm_cstate(therm, pstate->fanspeed, +1); 179f3867f43SBen Skeggs if (ret && ret != -ENODEV) { 180b907649eSBen Skeggs nvkm_error(subdev, "failed to raise fan speed: %d\n", ret); 181f3867f43SBen Skeggs return ret; 182f3867f43SBen Skeggs } 183f3867f43SBen Skeggs } 184f3867f43SBen Skeggs 185f3867f43SBen Skeggs if (volt) { 186fa6c4d8eSKarol Herbst ret = nvkm_volt_set_id(volt, cstate->voltage, 1878d08c264SKarol Herbst pstate->base.voltage, clk->temp, +1); 188f3867f43SBen Skeggs if (ret && ret != -ENODEV) { 189b907649eSBen Skeggs nvkm_error(subdev, "failed to raise voltage: %d\n", ret); 190f3867f43SBen Skeggs return ret; 191f3867f43SBen Skeggs } 192f3867f43SBen Skeggs } 193f3867f43SBen Skeggs 1946625f55cSBen Skeggs ret = clk->func->calc(clk, cstate); 195f3867f43SBen Skeggs if (ret == 0) { 1966625f55cSBen Skeggs ret = clk->func->prog(clk); 1976625f55cSBen Skeggs clk->func->tidy(clk); 198f3867f43SBen Skeggs } 199f3867f43SBen Skeggs 200f3867f43SBen Skeggs if (volt) { 201fa6c4d8eSKarol Herbst ret = nvkm_volt_set_id(volt, cstate->voltage, 2028d08c264SKarol Herbst pstate->base.voltage, clk->temp, -1); 203f3867f43SBen Skeggs if (ret && ret != -ENODEV) 204b907649eSBen Skeggs nvkm_error(subdev, "failed to lower voltage: %d\n", ret); 205f3867f43SBen Skeggs } 206f3867f43SBen Skeggs 207da06b46bSBen Skeggs if (therm) { 208da06b46bSBen Skeggs ret = nvkm_therm_cstate(therm, pstate->fanspeed, -1); 209f3867f43SBen Skeggs if (ret && ret != -ENODEV) 210b907649eSBen Skeggs nvkm_error(subdev, "failed to lower fan speed: %d\n", ret); 211f3867f43SBen Skeggs } 212f3867f43SBen Skeggs 2133eca809bSBen Skeggs return ret; 214f3867f43SBen Skeggs } 215f3867f43SBen Skeggs 216f3867f43SBen Skeggs static void 2177632b30eSBen Skeggs nvkm_cstate_del(struct nvkm_cstate *cstate) 218f3867f43SBen Skeggs { 219f3867f43SBen Skeggs list_del(&cstate->head); 220f3867f43SBen Skeggs kfree(cstate); 221f3867f43SBen Skeggs } 222f3867f43SBen Skeggs 223f3867f43SBen Skeggs static int 2247632b30eSBen Skeggs nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) 225f3867f43SBen Skeggs { 22646484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 22717d063dbSKarol Herbst struct nvkm_volt *volt = clk->subdev.device->volt; 2286625f55cSBen Skeggs const struct nvkm_domain *domain = clk->domains; 2297632b30eSBen Skeggs struct nvkm_cstate *cstate = NULL; 230f3867f43SBen Skeggs struct nvbios_cstepX cstepX; 231f3867f43SBen Skeggs u8 ver, hdr; 232f3867f43SBen Skeggs u16 data; 233f3867f43SBen Skeggs 234f3867f43SBen Skeggs data = nvbios_cstepXp(bios, idx, &ver, &hdr, &cstepX); 235f3867f43SBen Skeggs if (!data) 236f3867f43SBen Skeggs return -ENOENT; 237f3867f43SBen Skeggs 23817d063dbSKarol Herbst if (volt && nvkm_volt_map_min(volt, cstepX.voltage) > volt->max_uv) 23917d063dbSKarol Herbst return -EINVAL; 24017d063dbSKarol Herbst 241f3867f43SBen Skeggs cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); 242f3867f43SBen Skeggs if (!cstate) 243f3867f43SBen Skeggs return -ENOMEM; 244f3867f43SBen Skeggs 245f3867f43SBen Skeggs *cstate = pstate->base; 246f3867f43SBen Skeggs cstate->voltage = cstepX.voltage; 247761c8f69SKarol Herbst cstate->id = idx; 248f3867f43SBen Skeggs 249f3867f43SBen Skeggs while (domain && domain->name != nv_clk_src_max) { 250f3867f43SBen Skeggs if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { 2517632b30eSBen Skeggs u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate, 2527632b30eSBen Skeggs domain->bios, cstepX.freq); 253f3867f43SBen Skeggs cstate->domain[domain->name] = freq; 254f3867f43SBen Skeggs } 255f3867f43SBen Skeggs domain++; 256f3867f43SBen Skeggs } 257f3867f43SBen Skeggs 258f3867f43SBen Skeggs list_add(&cstate->head, &pstate->list); 259f3867f43SBen Skeggs return 0; 260f3867f43SBen Skeggs } 261f3867f43SBen Skeggs 262f3867f43SBen Skeggs /****************************************************************************** 263f3867f43SBen Skeggs * P-States 264f3867f43SBen Skeggs *****************************************************************************/ 265f3867f43SBen Skeggs static int 2667632b30eSBen Skeggs nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) 267f3867f43SBen Skeggs { 268b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 269cc90baddSKarol Herbst struct nvkm_fb *fb = subdev->device->fb; 270f68f4c96SKarol Herbst struct nvkm_pci *pci = subdev->device->pci; 2717632b30eSBen Skeggs struct nvkm_pstate *pstate; 272f3867f43SBen Skeggs int ret, idx = 0; 273f3867f43SBen Skeggs 274f3867f43SBen Skeggs list_for_each_entry(pstate, &clk->states, head) { 275f3867f43SBen Skeggs if (idx++ == pstatei) 276f3867f43SBen Skeggs break; 277f3867f43SBen Skeggs } 278f3867f43SBen Skeggs 279b907649eSBen Skeggs nvkm_debug(subdev, "setting performance state %d\n", pstatei); 280f3867f43SBen Skeggs clk->pstate = pstatei; 281f3867f43SBen Skeggs 282f68f4c96SKarol Herbst nvkm_pcie_set_link(pci, pstate->pcie_speed, pstate->pcie_width); 283f68f4c96SKarol Herbst 284cc90baddSKarol Herbst if (fb && fb->ram && fb->ram->func->calc) { 285cc90baddSKarol Herbst struct nvkm_ram *ram = fb->ram; 286f3867f43SBen Skeggs int khz = pstate->base.domain[nv_clk_src_mem]; 287f3867f43SBen Skeggs do { 288d36a99d2SBen Skeggs ret = ram->func->calc(ram, khz); 289f3867f43SBen Skeggs if (ret == 0) 290d36a99d2SBen Skeggs ret = ram->func->prog(ram); 291f3867f43SBen Skeggs } while (ret > 0); 292d36a99d2SBen Skeggs ram->func->tidy(ram); 293f3867f43SBen Skeggs } 294f3867f43SBen Skeggs 2950d6f8100SKarol Herbst return nvkm_cstate_prog(clk, pstate, NVKM_CLK_CSTATE_HIGHEST); 296f3867f43SBen Skeggs } 297f3867f43SBen Skeggs 298f3867f43SBen Skeggs static void 2997632b30eSBen Skeggs nvkm_pstate_work(struct work_struct *work) 300f3867f43SBen Skeggs { 3017632b30eSBen Skeggs struct nvkm_clk *clk = container_of(work, typeof(*clk), work); 302b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 303f3867f43SBen Skeggs int pstate; 304f3867f43SBen Skeggs 305f3867f43SBen Skeggs if (!atomic_xchg(&clk->waiting, 0)) 306f3867f43SBen Skeggs return; 307f3867f43SBen Skeggs clk->pwrsrc = power_supply_is_system_supplied(); 308f3867f43SBen Skeggs 30961a8b84fSKarol Herbst nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d°C D %d\n", 310f3867f43SBen Skeggs clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, 31161a8b84fSKarol Herbst clk->astate, clk->temp, clk->dstate); 312f3867f43SBen Skeggs 313f3867f43SBen Skeggs pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; 314f3867f43SBen Skeggs if (clk->state_nr && pstate != -1) { 315f3867f43SBen Skeggs pstate = (pstate < 0) ? clk->astate : pstate; 31661a8b84fSKarol Herbst pstate = min(pstate, clk->state_nr - 1); 317f3867f43SBen Skeggs pstate = max(pstate, clk->dstate); 318f3867f43SBen Skeggs } else { 319f3867f43SBen Skeggs pstate = clk->pstate = -1; 320f3867f43SBen Skeggs } 321f3867f43SBen Skeggs 322b907649eSBen Skeggs nvkm_trace(subdev, "-> %d\n", pstate); 323f3867f43SBen Skeggs if (pstate != clk->pstate) { 3247632b30eSBen Skeggs int ret = nvkm_pstate_prog(clk, pstate); 325f3867f43SBen Skeggs if (ret) { 326b907649eSBen Skeggs nvkm_error(subdev, "error setting pstate %d: %d\n", 327f3867f43SBen Skeggs pstate, ret); 328f3867f43SBen Skeggs } 329f3867f43SBen Skeggs } 330f3867f43SBen Skeggs 331f3867f43SBen Skeggs wake_up_all(&clk->wait); 332f3867f43SBen Skeggs nvkm_notify_get(&clk->pwrsrc_ntfy); 333f3867f43SBen Skeggs } 334f3867f43SBen Skeggs 335f3867f43SBen Skeggs static int 3367632b30eSBen Skeggs nvkm_pstate_calc(struct nvkm_clk *clk, bool wait) 337f3867f43SBen Skeggs { 338f3867f43SBen Skeggs atomic_set(&clk->waiting, 1); 339f3867f43SBen Skeggs schedule_work(&clk->work); 340f3867f43SBen Skeggs if (wait) 341f3867f43SBen Skeggs wait_event(clk->wait, !atomic_read(&clk->waiting)); 342f3867f43SBen Skeggs return 0; 343f3867f43SBen Skeggs } 344f3867f43SBen Skeggs 345f3867f43SBen Skeggs static void 3467632b30eSBen Skeggs nvkm_pstate_info(struct nvkm_clk *clk, struct nvkm_pstate *pstate) 347f3867f43SBen Skeggs { 3486625f55cSBen Skeggs const struct nvkm_domain *clock = clk->domains - 1; 3497632b30eSBen Skeggs struct nvkm_cstate *cstate; 350b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 351f3867f43SBen Skeggs char info[3][32] = { "", "", "" }; 352f3867f43SBen Skeggs char name[4] = "--"; 353f3867f43SBen Skeggs int i = -1; 354f3867f43SBen Skeggs 355f3867f43SBen Skeggs if (pstate->pstate != 0xff) 356f3867f43SBen Skeggs snprintf(name, sizeof(name), "%02x", pstate->pstate); 357f3867f43SBen Skeggs 358f3867f43SBen Skeggs while ((++clock)->name != nv_clk_src_max) { 359f3867f43SBen Skeggs u32 lo = pstate->base.domain[clock->name]; 360f3867f43SBen Skeggs u32 hi = lo; 361f3867f43SBen Skeggs if (hi == 0) 362f3867f43SBen Skeggs continue; 363f3867f43SBen Skeggs 364b907649eSBen Skeggs nvkm_debug(subdev, "%02x: %10d KHz\n", clock->name, lo); 365f3867f43SBen Skeggs list_for_each_entry(cstate, &pstate->list, head) { 366f3867f43SBen Skeggs u32 freq = cstate->domain[clock->name]; 367f3867f43SBen Skeggs lo = min(lo, freq); 368f3867f43SBen Skeggs hi = max(hi, freq); 369b907649eSBen Skeggs nvkm_debug(subdev, "%10d KHz\n", freq); 370f3867f43SBen Skeggs } 371f3867f43SBen Skeggs 372f3867f43SBen Skeggs if (clock->mname && ++i < ARRAY_SIZE(info)) { 373f3867f43SBen Skeggs lo /= clock->mdiv; 374f3867f43SBen Skeggs hi /= clock->mdiv; 375f3867f43SBen Skeggs if (lo == hi) { 376f3867f43SBen Skeggs snprintf(info[i], sizeof(info[i]), "%s %d MHz", 377f3867f43SBen Skeggs clock->mname, lo); 378f3867f43SBen Skeggs } else { 379f3867f43SBen Skeggs snprintf(info[i], sizeof(info[i]), 380f3867f43SBen Skeggs "%s %d-%d MHz", clock->mname, lo, hi); 381f3867f43SBen Skeggs } 382f3867f43SBen Skeggs } 383f3867f43SBen Skeggs } 384f3867f43SBen Skeggs 385b907649eSBen Skeggs nvkm_debug(subdev, "%s: %s %s %s\n", name, info[0], info[1], info[2]); 386f3867f43SBen Skeggs } 387f3867f43SBen Skeggs 388f3867f43SBen Skeggs static void 3897632b30eSBen Skeggs nvkm_pstate_del(struct nvkm_pstate *pstate) 390f3867f43SBen Skeggs { 3917632b30eSBen Skeggs struct nvkm_cstate *cstate, *temp; 392f3867f43SBen Skeggs 393f3867f43SBen Skeggs list_for_each_entry_safe(cstate, temp, &pstate->list, head) { 3947632b30eSBen Skeggs nvkm_cstate_del(cstate); 395f3867f43SBen Skeggs } 396f3867f43SBen Skeggs 397f3867f43SBen Skeggs list_del(&pstate->head); 398f3867f43SBen Skeggs kfree(pstate); 399f3867f43SBen Skeggs } 400f3867f43SBen Skeggs 401f3867f43SBen Skeggs static int 4027632b30eSBen Skeggs nvkm_pstate_new(struct nvkm_clk *clk, int idx) 403f3867f43SBen Skeggs { 40446484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 4056625f55cSBen Skeggs const struct nvkm_domain *domain = clk->domains - 1; 4067632b30eSBen Skeggs struct nvkm_pstate *pstate; 4077632b30eSBen Skeggs struct nvkm_cstate *cstate; 408f3867f43SBen Skeggs struct nvbios_cstepE cstepE; 409f3867f43SBen Skeggs struct nvbios_perfE perfE; 410f3867f43SBen Skeggs u8 ver, hdr, cnt, len; 411f3867f43SBen Skeggs u16 data; 412f3867f43SBen Skeggs 413f3867f43SBen Skeggs data = nvbios_perfEp(bios, idx, &ver, &hdr, &cnt, &len, &perfE); 414f3867f43SBen Skeggs if (!data) 415f3867f43SBen Skeggs return -EINVAL; 416f3867f43SBen Skeggs if (perfE.pstate == 0xff) 417f3867f43SBen Skeggs return 0; 418f3867f43SBen Skeggs 419f3867f43SBen Skeggs pstate = kzalloc(sizeof(*pstate), GFP_KERNEL); 420f3867f43SBen Skeggs cstate = &pstate->base; 421f3867f43SBen Skeggs if (!pstate) 422f3867f43SBen Skeggs return -ENOMEM; 423f3867f43SBen Skeggs 424f3867f43SBen Skeggs INIT_LIST_HEAD(&pstate->list); 425f3867f43SBen Skeggs 426f3867f43SBen Skeggs pstate->pstate = perfE.pstate; 427f3867f43SBen Skeggs pstate->fanspeed = perfE.fanspeed; 428d3b378c0SKarol Herbst pstate->pcie_speed = perfE.pcie_speed; 429d3b378c0SKarol Herbst pstate->pcie_width = perfE.pcie_width; 430f3867f43SBen Skeggs cstate->voltage = perfE.voltage; 431f3867f43SBen Skeggs cstate->domain[nv_clk_src_core] = perfE.core; 432f3867f43SBen Skeggs cstate->domain[nv_clk_src_shader] = perfE.shader; 433f3867f43SBen Skeggs cstate->domain[nv_clk_src_mem] = perfE.memory; 434f3867f43SBen Skeggs cstate->domain[nv_clk_src_vdec] = perfE.vdec; 435f3867f43SBen Skeggs cstate->domain[nv_clk_src_dom6] = perfE.disp; 436f3867f43SBen Skeggs 437f3867f43SBen Skeggs while (ver >= 0x40 && (++domain)->name != nv_clk_src_max) { 438f3867f43SBen Skeggs struct nvbios_perfS perfS; 439f3867f43SBen Skeggs u8 sver = ver, shdr = hdr; 440f3867f43SBen Skeggs u32 perfSe = nvbios_perfSp(bios, data, domain->bios, 441f3867f43SBen Skeggs &sver, &shdr, cnt, len, &perfS); 442f3867f43SBen Skeggs if (perfSe == 0 || sver != 0x40) 443f3867f43SBen Skeggs continue; 444f3867f43SBen Skeggs 445f3867f43SBen Skeggs if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { 4467632b30eSBen Skeggs perfS.v40.freq = nvkm_clk_adjust(clk, false, 447f3867f43SBen Skeggs pstate->pstate, 448f3867f43SBen Skeggs domain->bios, 449f3867f43SBen Skeggs perfS.v40.freq); 450f3867f43SBen Skeggs } 451f3867f43SBen Skeggs 452f3867f43SBen Skeggs cstate->domain[domain->name] = perfS.v40.freq; 453f3867f43SBen Skeggs } 454f3867f43SBen Skeggs 455f3867f43SBen Skeggs data = nvbios_cstepEm(bios, pstate->pstate, &ver, &hdr, &cstepE); 456f3867f43SBen Skeggs if (data) { 457f3867f43SBen Skeggs int idx = cstepE.index; 458f3867f43SBen Skeggs do { 4597632b30eSBen Skeggs nvkm_cstate_new(clk, idx, pstate); 460f3867f43SBen Skeggs } while(idx--); 461f3867f43SBen Skeggs } 462f3867f43SBen Skeggs 4637632b30eSBen Skeggs nvkm_pstate_info(clk, pstate); 464f3867f43SBen Skeggs list_add_tail(&pstate->head, &clk->states); 465f3867f43SBen Skeggs clk->state_nr++; 466f3867f43SBen Skeggs return 0; 467f3867f43SBen Skeggs } 468f3867f43SBen Skeggs 469f3867f43SBen Skeggs /****************************************************************************** 470f3867f43SBen Skeggs * Adjustment triggers 471f3867f43SBen Skeggs *****************************************************************************/ 472f3867f43SBen Skeggs static int 4737632b30eSBen Skeggs nvkm_clk_ustate_update(struct nvkm_clk *clk, int req) 474f3867f43SBen Skeggs { 4757632b30eSBen Skeggs struct nvkm_pstate *pstate; 476f3867f43SBen Skeggs int i = 0; 477f3867f43SBen Skeggs 478f3867f43SBen Skeggs if (!clk->allow_reclock) 479f3867f43SBen Skeggs return -ENOSYS; 480f3867f43SBen Skeggs 481f3867f43SBen Skeggs if (req != -1 && req != -2) { 482f3867f43SBen Skeggs list_for_each_entry(pstate, &clk->states, head) { 483f3867f43SBen Skeggs if (pstate->pstate == req) 484f3867f43SBen Skeggs break; 485f3867f43SBen Skeggs i++; 486f3867f43SBen Skeggs } 487f3867f43SBen Skeggs 488f3867f43SBen Skeggs if (pstate->pstate != req) 489f3867f43SBen Skeggs return -EINVAL; 490f3867f43SBen Skeggs req = i; 491f3867f43SBen Skeggs } 492f3867f43SBen Skeggs 493f3867f43SBen Skeggs return req + 2; 494f3867f43SBen Skeggs } 495f3867f43SBen Skeggs 496f3867f43SBen Skeggs static int 4977632b30eSBen Skeggs nvkm_clk_nstate(struct nvkm_clk *clk, const char *mode, int arglen) 498f3867f43SBen Skeggs { 499f3867f43SBen Skeggs int ret = 1; 500f3867f43SBen Skeggs 501f3867f43SBen Skeggs if (clk->allow_reclock && !strncasecmpz(mode, "auto", arglen)) 502f3867f43SBen Skeggs return -2; 503f3867f43SBen Skeggs 504f3867f43SBen Skeggs if (strncasecmpz(mode, "disabled", arglen)) { 505f3867f43SBen Skeggs char save = mode[arglen]; 506f3867f43SBen Skeggs long v; 507f3867f43SBen Skeggs 508f3867f43SBen Skeggs ((char *)mode)[arglen] = '\0'; 509f3867f43SBen Skeggs if (!kstrtol(mode, 0, &v)) { 5107632b30eSBen Skeggs ret = nvkm_clk_ustate_update(clk, v); 511f3867f43SBen Skeggs if (ret < 0) 512f3867f43SBen Skeggs ret = 1; 513f3867f43SBen Skeggs } 514f3867f43SBen Skeggs ((char *)mode)[arglen] = save; 515f3867f43SBen Skeggs } 516f3867f43SBen Skeggs 517f3867f43SBen Skeggs return ret - 2; 518f3867f43SBen Skeggs } 519f3867f43SBen Skeggs 520f3867f43SBen Skeggs int 5217632b30eSBen Skeggs nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr) 522f3867f43SBen Skeggs { 5237632b30eSBen Skeggs int ret = nvkm_clk_ustate_update(clk, req); 524f3867f43SBen Skeggs if (ret >= 0) { 525f3867f43SBen Skeggs if (ret -= 2, pwr) clk->ustate_ac = ret; 526f3867f43SBen Skeggs else clk->ustate_dc = ret; 5277632b30eSBen Skeggs return nvkm_pstate_calc(clk, true); 528f3867f43SBen Skeggs } 529f3867f43SBen Skeggs return ret; 530f3867f43SBen Skeggs } 531f3867f43SBen Skeggs 532f3867f43SBen Skeggs int 5337632b30eSBen Skeggs nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait) 534f3867f43SBen Skeggs { 535f3867f43SBen Skeggs if (!rel) clk->astate = req; 536f3867f43SBen Skeggs if ( rel) clk->astate += rel; 537f3867f43SBen Skeggs clk->astate = min(clk->astate, clk->state_nr - 1); 538f3867f43SBen Skeggs clk->astate = max(clk->astate, 0); 5397632b30eSBen Skeggs return nvkm_pstate_calc(clk, wait); 540f3867f43SBen Skeggs } 541f3867f43SBen Skeggs 542f3867f43SBen Skeggs int 54361a8b84fSKarol Herbst nvkm_clk_tstate(struct nvkm_clk *clk, u8 temp) 544f3867f43SBen Skeggs { 54561a8b84fSKarol Herbst if (clk->temp == temp) 54661a8b84fSKarol Herbst return 0; 54761a8b84fSKarol Herbst clk->temp = temp; 54861a8b84fSKarol Herbst return nvkm_pstate_calc(clk, false); 549f3867f43SBen Skeggs } 550f3867f43SBen Skeggs 551f3867f43SBen Skeggs int 5527632b30eSBen Skeggs nvkm_clk_dstate(struct nvkm_clk *clk, int req, int rel) 553f3867f43SBen Skeggs { 554f3867f43SBen Skeggs if (!rel) clk->dstate = req; 555f3867f43SBen Skeggs if ( rel) clk->dstate += rel; 556f3867f43SBen Skeggs clk->dstate = min(clk->dstate, clk->state_nr - 1); 557f3867f43SBen Skeggs clk->dstate = max(clk->dstate, 0); 5587632b30eSBen Skeggs return nvkm_pstate_calc(clk, true); 559f3867f43SBen Skeggs } 560f3867f43SBen Skeggs 561f3867f43SBen Skeggs static int 5627632b30eSBen Skeggs nvkm_clk_pwrsrc(struct nvkm_notify *notify) 563f3867f43SBen Skeggs { 5647632b30eSBen Skeggs struct nvkm_clk *clk = 565f3867f43SBen Skeggs container_of(notify, typeof(*clk), pwrsrc_ntfy); 5667632b30eSBen Skeggs nvkm_pstate_calc(clk, false); 567f3867f43SBen Skeggs return NVKM_NOTIFY_DROP; 568f3867f43SBen Skeggs } 569f3867f43SBen Skeggs 570f3867f43SBen Skeggs /****************************************************************************** 571f3867f43SBen Skeggs * subdev base class implementation 572f3867f43SBen Skeggs *****************************************************************************/ 573f3867f43SBen Skeggs 574f3867f43SBen Skeggs int 5756625f55cSBen Skeggs nvkm_clk_read(struct nvkm_clk *clk, enum nv_clk_src src) 576f3867f43SBen Skeggs { 5776625f55cSBen Skeggs return clk->func->read(clk, src); 578f3867f43SBen Skeggs } 579f3867f43SBen Skeggs 5806625f55cSBen Skeggs static int 5816625f55cSBen Skeggs nvkm_clk_fini(struct nvkm_subdev *subdev, bool suspend) 582f3867f43SBen Skeggs { 5836625f55cSBen Skeggs struct nvkm_clk *clk = nvkm_clk(subdev); 5846625f55cSBen Skeggs nvkm_notify_put(&clk->pwrsrc_ntfy); 5856625f55cSBen Skeggs flush_work(&clk->work); 5866625f55cSBen Skeggs if (clk->func->fini) 5876625f55cSBen Skeggs clk->func->fini(clk); 5886625f55cSBen Skeggs return 0; 5896625f55cSBen Skeggs } 590f3867f43SBen Skeggs 5916625f55cSBen Skeggs static int 5926625f55cSBen Skeggs nvkm_clk_init(struct nvkm_subdev *subdev) 5936625f55cSBen Skeggs { 5946625f55cSBen Skeggs struct nvkm_clk *clk = nvkm_clk(subdev); 5956625f55cSBen Skeggs const struct nvkm_domain *clock = clk->domains; 5966625f55cSBen Skeggs int ret; 597f3867f43SBen Skeggs 598f3867f43SBen Skeggs memset(&clk->bstate, 0x00, sizeof(clk->bstate)); 599f3867f43SBen Skeggs INIT_LIST_HEAD(&clk->bstate.list); 600f3867f43SBen Skeggs clk->bstate.pstate = 0xff; 601f3867f43SBen Skeggs 602f3867f43SBen Skeggs while (clock->name != nv_clk_src_max) { 6036625f55cSBen Skeggs ret = nvkm_clk_read(clk, clock->name); 604f3867f43SBen Skeggs if (ret < 0) { 605b907649eSBen Skeggs nvkm_error(subdev, "%02x freq unknown\n", clock->name); 606f3867f43SBen Skeggs return ret; 607f3867f43SBen Skeggs } 608f3867f43SBen Skeggs clk->bstate.base.domain[clock->name] = ret; 609f3867f43SBen Skeggs clock++; 610f3867f43SBen Skeggs } 611f3867f43SBen Skeggs 6127632b30eSBen Skeggs nvkm_pstate_info(clk, &clk->bstate); 613f3867f43SBen Skeggs 6146625f55cSBen Skeggs if (clk->func->init) 6156625f55cSBen Skeggs return clk->func->init(clk); 6166625f55cSBen Skeggs 617f3867f43SBen Skeggs clk->astate = clk->state_nr - 1; 618f3867f43SBen Skeggs clk->dstate = 0; 619f3867f43SBen Skeggs clk->pstate = -1; 62061a8b84fSKarol Herbst clk->temp = 90; /* reasonable default value */ 6217632b30eSBen Skeggs nvkm_pstate_calc(clk, true); 622f3867f43SBen Skeggs return 0; 623f3867f43SBen Skeggs } 624f3867f43SBen Skeggs 6256625f55cSBen Skeggs static void * 6266625f55cSBen Skeggs nvkm_clk_dtor(struct nvkm_subdev *subdev) 627f3867f43SBen Skeggs { 6286625f55cSBen Skeggs struct nvkm_clk *clk = nvkm_clk(subdev); 6297632b30eSBen Skeggs struct nvkm_pstate *pstate, *temp; 630f3867f43SBen Skeggs 631f3867f43SBen Skeggs nvkm_notify_fini(&clk->pwrsrc_ntfy); 632f3867f43SBen Skeggs 6336625f55cSBen Skeggs /* Early return if the pstates have been provided statically */ 6346625f55cSBen Skeggs if (clk->func->pstates) 6356625f55cSBen Skeggs return clk; 6366625f55cSBen Skeggs 637f3867f43SBen Skeggs list_for_each_entry_safe(pstate, temp, &clk->states, head) { 6387632b30eSBen Skeggs nvkm_pstate_del(pstate); 639f3867f43SBen Skeggs } 640f3867f43SBen Skeggs 6416625f55cSBen Skeggs return clk; 642f3867f43SBen Skeggs } 643f3867f43SBen Skeggs 6446625f55cSBen Skeggs static const struct nvkm_subdev_func 6456625f55cSBen Skeggs nvkm_clk = { 6466625f55cSBen Skeggs .dtor = nvkm_clk_dtor, 6476625f55cSBen Skeggs .init = nvkm_clk_init, 6486625f55cSBen Skeggs .fini = nvkm_clk_fini, 6496625f55cSBen Skeggs }; 6506625f55cSBen Skeggs 651f3867f43SBen Skeggs int 6526625f55cSBen Skeggs nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, 6536625f55cSBen Skeggs int index, bool allow_reclock, struct nvkm_clk *clk) 654f3867f43SBen Skeggs { 6554b9ce6e7SKarol Herbst struct nvkm_subdev *subdev = &clk->subdev; 6564b9ce6e7SKarol Herbst struct nvkm_bios *bios = device->bios; 657f3867f43SBen Skeggs int ret, idx, arglen; 658f3867f43SBen Skeggs const char *mode; 6594b9ce6e7SKarol Herbst struct nvbios_vpstate_header h; 660f3867f43SBen Skeggs 6614b9ce6e7SKarol Herbst nvkm_subdev_ctor(&nvkm_clk, device, index, subdev); 6624b9ce6e7SKarol Herbst 6634b9ce6e7SKarol Herbst if (bios && !nvbios_vpstate_parse(bios, &h)) { 6644b9ce6e7SKarol Herbst struct nvbios_vpstate_entry base, boost; 6654b9ce6e7SKarol Herbst if (!nvbios_vpstate_entry(bios, &h, h.boost_id, &boost)) 6664b9ce6e7SKarol Herbst clk->boost_khz = boost.clock_mhz * 1000; 6674b9ce6e7SKarol Herbst if (!nvbios_vpstate_entry(bios, &h, h.base_id, &base)) 6684b9ce6e7SKarol Herbst clk->base_khz = base.clock_mhz * 1000; 6694b9ce6e7SKarol Herbst } 6704b9ce6e7SKarol Herbst 6716625f55cSBen Skeggs clk->func = func; 672f3867f43SBen Skeggs INIT_LIST_HEAD(&clk->states); 6736625f55cSBen Skeggs clk->domains = func->domains; 674f3867f43SBen Skeggs clk->ustate_ac = -1; 675f3867f43SBen Skeggs clk->ustate_dc = -1; 6766625f55cSBen Skeggs clk->allow_reclock = allow_reclock; 677f3867f43SBen Skeggs 6787632b30eSBen Skeggs INIT_WORK(&clk->work, nvkm_pstate_work); 679f3867f43SBen Skeggs init_waitqueue_head(&clk->wait); 680f3867f43SBen Skeggs atomic_set(&clk->waiting, 0); 681f3867f43SBen Skeggs 682f3867f43SBen Skeggs /* If no pstates are provided, try and fetch them from the BIOS */ 6836625f55cSBen Skeggs if (!func->pstates) { 684f3867f43SBen Skeggs idx = 0; 685f3867f43SBen Skeggs do { 6867632b30eSBen Skeggs ret = nvkm_pstate_new(clk, idx++); 687f3867f43SBen Skeggs } while (ret == 0); 688f3867f43SBen Skeggs } else { 6896625f55cSBen Skeggs for (idx = 0; idx < func->nr_pstates; idx++) 6906625f55cSBen Skeggs list_add_tail(&func->pstates[idx].head, &clk->states); 6916625f55cSBen Skeggs clk->state_nr = func->nr_pstates; 692f3867f43SBen Skeggs } 693f3867f43SBen Skeggs 6947632b30eSBen Skeggs ret = nvkm_notify_init(NULL, &device->event, nvkm_clk_pwrsrc, true, 695f3867f43SBen Skeggs NULL, 0, 0, &clk->pwrsrc_ntfy); 696f3867f43SBen Skeggs if (ret) 697f3867f43SBen Skeggs return ret; 698f3867f43SBen Skeggs 6997632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkMode", &arglen); 700f3867f43SBen Skeggs if (mode) { 7017632b30eSBen Skeggs clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen); 7027632b30eSBen Skeggs clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen); 703f3867f43SBen Skeggs } 704f3867f43SBen Skeggs 7057632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkModeAC", &arglen); 706f3867f43SBen Skeggs if (mode) 7077632b30eSBen Skeggs clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen); 708f3867f43SBen Skeggs 7097632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkModeDC", &arglen); 710f3867f43SBen Skeggs if (mode) 7117632b30eSBen Skeggs clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen); 712f3867f43SBen Skeggs 7134b9ce6e7SKarol Herbst clk->boost_mode = nvkm_longopt(device->cfgopt, "NvBoost", 7144b9ce6e7SKarol Herbst NVKM_CLK_BOOST_NONE); 715f3867f43SBen Skeggs return 0; 716f3867f43SBen Skeggs } 7176625f55cSBen Skeggs 7186625f55cSBen Skeggs int 7196625f55cSBen Skeggs nvkm_clk_new_(const struct nvkm_clk_func *func, struct nvkm_device *device, 7206625f55cSBen Skeggs int index, bool allow_reclock, struct nvkm_clk **pclk) 7216625f55cSBen Skeggs { 7226625f55cSBen Skeggs if (!(*pclk = kzalloc(sizeof(**pclk), GFP_KERNEL))) 7236625f55cSBen Skeggs return -ENOMEM; 7246625f55cSBen Skeggs return nvkm_clk_ctor(func, device, index, allow_reclock, *pclk); 7256625f55cSBen Skeggs } 726