/* * Copyright 2013 Red Hat Inc. * * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. * * Authors: Ben Skeggs * Roy Spliet <rspliet@eclipso.eu>
*/ #define gt215_ram(p) container_of((p), struct gt215_ram, base) #include"ram.h" #include"ramfuc.h"
if (nvkm_boolopt(device->cfgopt, "NvMemExec", true) != true) return -ENOSYS;
/* XXX: Multiple partitions? */
result = kmalloc_array(64, sizeof(u32), GFP_KERNEL); if (!result) return -ENOMEM;
train->state = NVA3_TRAIN_EXEC;
/* Clock speeds for training and back */
nvbios_M0205Tp(bios, &ver, &hdr, &cnt, &len, &snr, &ssz, &M0205T); if (M0205T.freq == 0) {
kfree(result); return -ENOENT;
}
clk_current = nvkm_clk_read(clk, nv_clk_src_mem);
ret = gt215_clk_pre(clk, f); if (ret) goto out;
/* First: clock up/down */
ret = ram->base.func->calc(&ram->base, (u32) M0205T.freq * 1000); if (ret) goto out;
/* Do this *after* calc, eliminates write in script */
nvkm_wr32(device, 0x111400, 0x00000000); /* XXX: Magic writes that improve train reliability? */
nvkm_mask(device, 0x100674, 0x0000ffff, 0x00000000);
nvkm_mask(device, 0x1005e4, 0x0000ffff, 0x00000000);
nvkm_mask(device, 0x100b0c, 0x000000ff, 0x00000000);
nvkm_wr32(device, 0x100c04, 0x00000400);
/* Now the training script */
r1700 = ram_rd32(fuc, 0x001700);
/* We support type "5"
* XXX: training pattern table appears to be unused for this routine */ if (!nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)) return -ENOENT;
if (M0205E.type != 5) return 0;
train->state = NVA3_TRAIN_ONCE;
ret = nvkm_ram_get(device, NVKM_RAM_MM_NORMAL, 0x01, 16, 0x8000, true, true, &ram->ltrain.memory); if (ret) return ret;
for (i = 0; i < 0x30; i++) {
nvkm_wr32(device, 0x10f8c0, (i << 8) | i);
nvkm_wr32(device, 0x10f900, pattern[i % 16]);
}
for (i = 0; i < 0x30; i++) {
nvkm_wr32(device, 0x10f8e0, (i << 8) | i);
nvkm_wr32(device, 0x10f920, pattern[i % 16]);
}
/* And upload the pattern */
r001700 = nvkm_rd32(device, 0x1700);
nvkm_wr32(device, 0x1700, addr >> 16); for (i = 0; i < 16; i++)
nvkm_wr32(device, 0x700000 + (i << 2), pattern[i]); for (i = 0; i < 16; i++)
nvkm_wr32(device, 0x700100 + (i << 2), pattern[i]);
nvkm_wr32(device, 0x1700, r001700);
next = &ram->base.target;
next->freq = freq;
ram->base.next = next;
if (ram->ltrain.state == NVA3_TRAIN_ONCE)
gt215_link_train(ram);
/* lookup memory config data relevant to the target frequency */
data = nvbios_rammapEm(bios, freq / 1000, &ver, &hdr, &cnt, &len,
&next->bios); if (!data || ver != 0x10 || hdr < 0x05) {
nvkm_error(subdev, "invalid/missing rammap entry\n"); return -EINVAL;
}
/* locate specific data set for the attached memory */
strap = nvbios_ramcfg_index(subdev); if (strap >= cnt) {
nvkm_error(subdev, "invalid ramcfg strap\n"); return -EINVAL;
}
data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap,
&ver, &hdr, &next->bios); if (!data || ver != 0x10 || hdr < 0x09) {
nvkm_error(subdev, "invalid/missing ramcfg entry\n"); return -EINVAL;
}
/* lookup memory timings, if bios says they're present */ if (next->bios.ramcfg_timing != 0xff) {
data = nvbios_timingEp(bios, next->bios.ramcfg_timing,
&ver, &hdr, &cnt, &len,
&next->bios); if (!data || ver != 0x10 || hdr < 0x17) {
nvkm_error(subdev, "invalid/missing timing entry\n"); return -EINVAL;
}
}
ret = gt215_pll_info(device->clk, 0x12, 0x4000, freq, &mclk); if (ret < 0) {
nvkm_error(subdev, "failed mclk calculation\n"); return ret;
}
gt215_ram_timing_calc(ram, timing);
ret = ram_init(fuc, ram->base.fb); if (ret) return ret;
switch (ram->base.type) { case NVKM_RAM_TYPE_DDR2:
ret = nvkm_sddr2_calc(&ram->base); break; case NVKM_RAM_TYPE_DDR3:
ret = nvkm_sddr3_calc(&ram->base); break; case NVKM_RAM_TYPE_GDDR3:
ret = nvkm_gddr3_calc(&ram->base); break; default:
ret = -ENOSYS; break;
}
if (!next->bios.ramcfg_DLLoff)
r004018 |= 0x00004000;
/* pll2pll requires to switch to a safe clock first */
ctrl = ram_rd32(fuc, 0x004000);
pll2pll = (!(ctrl & 0x00000008)) && mclk.pll;
/* Pre, NVIDIA does this outside the script */ if (next->bios.ramcfg_10_02_10) {
ram_mask(fuc, 0x111104, 0x00000600, 0x00000000);
} else {
ram_mask(fuc, 0x111100, 0x40000000, 0x40000000);
ram_mask(fuc, 0x111104, 0x00000180, 0x00000000);
} /* Always disable this bit during reclock */
ram_mask(fuc, 0x100200, 0x00000800, 0x00000000);
/* If switching from non-pll to pll, lock before disabling FB */ if (mclk.pll && !pll2pll) {
ram_mask(fuc, 0x004128, 0x003f3141, mclk.clk | 0x00000101);
gt215_ram_lock_pll(fuc, &mclk);
}
/* Start with disabling some CRTCs and PFIFO? */
ram_wait_vblank(fuc);
ram_wr32(fuc, 0x611200, 0x3300);
ram_mask(fuc, 0x002504, 0x1, 0x1);
ram_nsec(fuc, 10000);
ram_wait(fuc, 0x002504, 0x10, 0x10, 20000); /* XXX: or longer? */
ram_block(fuc);
ram_nsec(fuc, 2000);
if (!next->bios.ramcfg_10_02_10) { if (ram->base.type == NVKM_RAM_TYPE_GDDR3)
ram_mask(fuc, 0x111100, 0x04020000, 0x00020000); else
ram_mask(fuc, 0x111100, 0x04020000, 0x04020000);
}
/* If we're disabling the DLL, do it now */ switch (next->bios.ramcfg_DLLoff * ram->base.type) { case NVKM_RAM_TYPE_DDR3:
nvkm_sddr3_dll_disable(fuc, ram->base.mr); break; case NVKM_RAM_TYPE_GDDR3:
nvkm_gddr3_dll_disable(fuc, ram->base.mr); break;
}
if (next->bios.timing_10_ODT)
gt215_ram_gpio(fuc, 0x2e, 1);
/* Alter FBVDD/Q, apparently must be done with PLL disabled, thus
* set it to bypass */ if (nvkm_gpio_get(gpio, 0, 0x18, DCB_GPIO_UNUSED) ==
next->bios.ramcfg_FBVDDQ) {
data = ram_rd32(fuc, 0x004000) & 0x9;
if (data == 0x1)
ram_mask(fuc, 0x004000, 0x8, 0x8); if (data & 0x1)
ram_mask(fuc, 0x004000, 0x1, 0x0);
if (data & 0x1)
ram_mask(fuc, 0x004000, 0x1, 0x1);
}
/* Fiddle with clocks */ /* There's 4 scenario's * pll->pll: first switch to a 324MHz clock, set up new PLL, switch * clk->pll: Set up new PLL, switch * pll->clk: Set up clock, switch
* clk->clk: Overwrite ctrl and other bits, switch */
/* Set RAM MR parameters and timings */ for (i = 2; i >= 0; i--) { if (ram_rd32(fuc, mr[i]) != ram->base.mr[i]) {
ram_wr32(fuc, mr[i], ram->base.mr[i]);
ram_nsec(fuc, 1000);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.