Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: MIT
nvkm-y += nvkm/subdev/pmu/base.o
nvkm-y += nvkm/subdev/pmu/memx.o
nvkm-y += nvkm/subdev/pmu/gt215.o
nvkm-y += nvkm/subdev/pmu/gf100.o
nvkm-y += nvkm/subdev/pmu/gf119.o
nvkm-y += nvkm/subdev/pmu/gk104.o
nvkm-y += nvkm/subdev/pmu/gk110.o
nvkm-y += nvkm/subdev/pmu/gk208.o
nvkm-y += nvkm/subdev/pmu/gk20a.o
nvkm-y += nvkm/subdev/pmu/gm107.o
nvkm-y += nvkm/subdev/pmu/gm200.o
nvkm-y += nvkm/subdev/pmu/gm20b.o
nvkm-y += nvkm/subdev/pmu/gp102.o
nvkm-y += nvkm/subdev/pmu/gp10b.o
@@ -0,0 +1,167 @@
/*
* 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
*/
#include "priv.h"
#include <core/firmware.h>
#include <subdev/timer.h>
bool
nvkm_pmu_fan_controlled(struct nvkm_device *device)
{
struct nvkm_pmu *pmu = device->pmu;
/* Internal PMU FW does not currently control fans in any way,
* allow SW control of fans instead.
*/
if (pmu && pmu->func->code.size)
return false;
/* Default (board-loaded, or VBIOS PMU/PREOS) PMU FW on Fermi
* and newer automatically control the fan speed, which would
* interfere with SW control.
*/
return (device->chipset >= 0xc0);
}
void
nvkm_pmu_pgob(struct nvkm_pmu *pmu, bool enable)
{
if (pmu && pmu->func->pgob)
pmu->func->pgob(pmu, enable);
}
static void
nvkm_pmu_recv(struct work_struct *work)
{
struct nvkm_pmu *pmu = container_of(work, typeof(*pmu), recv.work);
return pmu->func->recv(pmu);
}
int
nvkm_pmu_send(struct nvkm_pmu *pmu, u32 reply[2],
u32 process, u32 message, u32 data0, u32 data1)
{
if (!pmu || !pmu->func->send)
return -ENODEV;
return pmu->func->send(pmu, reply, process, message, data0, data1);
}
static void
nvkm_pmu_intr(struct nvkm_subdev *subdev)
{
struct nvkm_pmu *pmu = nvkm_pmu(subdev);
if (!pmu->func->intr)
return;
pmu->func->intr(pmu);
}
static int
nvkm_pmu_fini(struct nvkm_subdev *subdev, enum nvkm_suspend_state suspend)
{
struct nvkm_pmu *pmu = nvkm_pmu(subdev);
if (!subdev->use.enabled)
return 0;
if (pmu->func->fini)
pmu->func->fini(pmu);
return 0;
}
static int
nvkm_pmu_init(struct nvkm_subdev *subdev)
{
struct nvkm_pmu *pmu = nvkm_pmu(subdev);
if (!pmu->func->init)
return 0;
return pmu->func->init(pmu);
}
static void *
nvkm_pmu_dtor(struct nvkm_subdev *subdev)
{
struct nvkm_pmu *pmu = nvkm_pmu(subdev);
nvkm_falcon_msgq_del(&pmu->msgq);
nvkm_falcon_cmdq_del(&pmu->lpq);
nvkm_falcon_cmdq_del(&pmu->hpq);
nvkm_falcon_qmgr_del(&pmu->qmgr);
nvkm_falcon_dtor(&pmu->falcon);
mutex_destroy(&pmu->send.mutex);
return nvkm_pmu(subdev);
}
static const struct nvkm_subdev_func
nvkm_pmu = {
.dtor = nvkm_pmu_dtor,
.init = nvkm_pmu_init,
.fini = nvkm_pmu_fini,
.intr = nvkm_pmu_intr,
};
int
nvkm_pmu_ctor(const struct nvkm_pmu_fwif *fwif, struct nvkm_device *device,
enum nvkm_subdev_type type, int inst, struct nvkm_pmu *pmu)
{
int ret;
nvkm_subdev_ctor(&nvkm_pmu, device, type, inst, &pmu->subdev);
mutex_init(&pmu->send.mutex);
INIT_WORK(&pmu->recv.work, nvkm_pmu_recv);
init_waitqueue_head(&pmu->recv.wait);
fwif = nvkm_firmware_load(&pmu->subdev, fwif, "Pmu", pmu);
if (IS_ERR(fwif))
return PTR_ERR(fwif);
pmu->func = fwif->func;
ret = nvkm_falcon_ctor(pmu->func->flcn, &pmu->subdev, pmu->subdev.name,
0x10a000, &pmu->falcon);
if (ret)
return ret;
if ((ret = nvkm_falcon_qmgr_new(&pmu->falcon, &pmu->qmgr)) ||
(ret = nvkm_falcon_cmdq_new(pmu->qmgr, "hpq", &pmu->hpq)) ||
(ret = nvkm_falcon_cmdq_new(pmu->qmgr, "lpq", &pmu->lpq)) ||
(ret = nvkm_falcon_msgq_new(pmu->qmgr, "msgq", &pmu->msgq)))
return ret;
init_completion(&pmu->wpr_ready);
return 0;
}
int
nvkm_pmu_new_(const struct nvkm_pmu_fwif *fwif, struct nvkm_device *device,
enum nvkm_subdev_type type, int inst, struct nvkm_pmu **ppmu)
{
struct nvkm_pmu *pmu;
if (!(pmu = *ppmu = kzalloc_obj(*pmu)))
return -ENOMEM;
return nvkm_pmu_ctor(fwif, device, type, inst, *ppmu);
}
@@ -0,0 +1,94 @@
/*
* Copyright 2014 Martin Peres <martin.peres@free.fr>
*
* 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 folloing 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: Martin Peres
*/
/******************************************************************************
* arith data segment
*****************************************************************************/
#ifdef INCLUDE_PROC
#endif
#ifdef INCLUDE_DATA
#endif
/******************************************************************************
* arith code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
// does a 32x32 -> 64 multiplication
//
// A * B = A_lo * B_lo
// + ( A_hi * B_lo ) << 16
// + ( A_lo * B_hi ) << 16
// + ( A_hi * B_hi ) << 32
//
// $r15 - current
// $r14 - A
// $r13 - B
// $r12 - mul_lo (return)
// $r11 - mul_hi (return)
// $r0 - zero
mulu32_32_64:
push $r1 // A_hi
push $r2 // B_hi
push $r3 // tmp0
push $r4 // tmp1
shr b32 $r1 $r14 16
shr b32 $r2 $r13 16
clear b32 $r12
clear b32 $r11
// A_lo * B_lo
mulu $r12 $r14 $r13
// ( A_hi * B_lo ) << 16
mulu $r3 $r1 $r13 // tmp0 = A_hi * B_lo
mov b32 $r4 $r3
and $r3 0xffff // tmp0 = tmp0_lo
shl b32 $r3 16
shr b32 $r4 16 // tmp1 = tmp0_hi
add b32 $r12 $r3
adc b32 $r11 $r4
// ( A_lo * B_hi ) << 16
mulu $r3 $r14 $r2 // tmp0 = A_lo * B_hi
mov b32 $r4 $r3
and $r3 0xffff // tmp0 = tmp0_lo
shl b32 $r3 16
shr b32 $r4 16 // tmp1 = tmp0_hi
add b32 $r12 $r3
adc b32 $r11 $r4
// ( A_hi * B_hi ) << 32
mulu $r3 $r1 $r2 // tmp0 = A_hi * B_hi
add b32 $r11 $r3
pop $r4
pop $r3
pop $r2
pop $r1
ret
#endif
@@ -0,0 +1,70 @@
/*
* 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
*/
#define NVKM_PPWR_CHIPSET GF100
#define HW_TICKS_PER_US 203 // should be 202.5
//#define NVKM_FALCON_PC24
//#define NVKM_FALCON_UNSHIFTED_IO
//#define NVKM_FALCON_MMIO_UAS
//#define NVKM_FALCON_MMIO_TRAP
#include "macros.fuc"
.section #gf100_pmu_data
#define INCLUDE_PROC
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_PROC
#define INCLUDE_DATA
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_DATA
.align 256
.section #gf100_pmu_code
#define INCLUDE_CODE
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_CODE
.align 256
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,70 @@
/*
* 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
*/
#define NVKM_PPWR_CHIPSET GF119
#define HW_TICKS_PER_US 324
//#define NVKM_FALCON_PC24
#define NVKM_FALCON_UNSHIFTED_IO
//#define NVKM_FALCON_MMIO_UAS
//#define NVKM_FALCON_MMIO_TRAP
#include "macros.fuc"
.section #gf119_pmu_data
#define INCLUDE_PROC
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_PROC
#define INCLUDE_DATA
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_DATA
.align 256
.section #gf119_pmu_code
#define INCLUDE_CODE
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_CODE
.align 256
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,70 @@
/*
* 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
*/
#define NVKM_PPWR_CHIPSET GK208
#define HW_TICKS_PER_US 324
#define NVKM_FALCON_PC24
#define NVKM_FALCON_UNSHIFTED_IO
//#define NVKM_FALCON_MMIO_UAS
//#define NVKM_FALCON_MMIO_TRAP
#include "macros.fuc"
.section #gk208_pmu_data
#define INCLUDE_PROC
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_PROC
#define INCLUDE_DATA
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_DATA
.align 256
.section #gk208_pmu_code
#define INCLUDE_CODE
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_CODE
.align 256
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,70 @@
/*
* 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
*/
#define NVKM_PPWR_CHIPSET GT215
#define HW_TICKS_PER_US 203 // should be 202.5
//#define NVKM_FALCON_PC24
//#define NVKM_FALCON_UNSHIFTED_IO
//#define NVKM_FALCON_MMIO_UAS
//#define NVKM_FALCON_MMIO_TRAP
#include "macros.fuc"
.section #gt215_pmu_data
#define INCLUDE_PROC
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_PROC
#define INCLUDE_DATA
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_DATA
.align 256
.section #gt215_pmu_code
#define INCLUDE_CODE
#include "kernel.fuc"
#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
#include "i2c_.fuc"
#include "test.fuc"
#include "idle.fuc"
#undef INCLUDE_CODE
.align 256
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,150 @@
/*
* 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
*/
#ifdef INCLUDE_PROC
process(PROC_HOST, #host_init, #host_recv)
#endif
/******************************************************************************
* HOST data segment
*****************************************************************************/
#ifdef INCLUDE_DATA
// HOST (R)FIFO packet format
.equ #fifo_process 0x00
.equ #fifo_message 0x04
.equ #fifo_data0 0x08
.equ #fifo_data1 0x0c
// HOST HOST->PWR queue description
.equ #fifo_qlen 4 // log2(size of queue entry in bytes)
.equ #fifo_qnum 3 // log2(max number of entries in queue)
.equ #fifo_qmaskb (1 << #fifo_qnum) // max number of entries in queue
.equ #fifo_qmaskp (#fifo_qmaskb - 1)
.equ #fifo_qmaskf ((#fifo_qmaskb << 1) - 1)
.equ #fifo_qsize (1 << (#fifo_qlen + #fifo_qnum))
fifo_queue: .skip 128 // #fifo_qsize
// HOST PWR->HOST queue description
.equ #rfifo_qlen 4 // log2(size of queue entry in bytes)
.equ #rfifo_qnum 3 // log2(max number of entries in queue)
.equ #rfifo_qmaskb (1 << #rfifo_qnum) // max number of entries in queue
.equ #rfifo_qmaskp (#rfifo_qmaskb - 1)
.equ #rfifo_qmaskf ((#rfifo_qmaskb << 1) - 1)
.equ #rfifo_qsize (1 << (#rfifo_qlen + #rfifo_qnum))
rfifo_queue: .skip 128 // #rfifo_qsize
#endif
/******************************************************************************
* HOST code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
// HOST->PWR comms - dequeue message(s) for process(es) from FIFO
//
// $r15 - current (host)
// $r0 - zero
host_send:
nv_iord($r1, NV_PPWR_FIFO_GET(0))
nv_iord($r2, NV_PPWR_FIFO_PUT(0))
cmp b32 $r1 $r2
bra e #host_send_done
// calculate address of message
and $r14 $r1 #fifo_qmaskp
shl b32 $r14 $r14 #fifo_qlen
add b32 $r14 #fifo_queue
// read message data, and pass to appropriate process
ld b32 $r11 D[$r14 + #fifo_data1]
ld b32 $r12 D[$r14 + #fifo_data0]
ld b32 $r13 D[$r14 + #fifo_message]
ld b32 $r14 D[$r14 + #fifo_process]
call(send)
// increment GET
add b32 $r1 0x1
and $r14 $r1 #fifo_qmaskf
nv_iowr(NV_PPWR_FIFO_GET(0), $r14)
bra #host_send
host_send_done:
ret
// PWR->HOST comms - enqueue message for HOST to RFIFO
//
// $r15 - current (host)
// $r14 - process
// $r13 - message
// $r12 - message data 0
// $r11 - message data 1
// $r0 - zero
host_recv:
// message from intr handler == HOST->PWR comms pending
imm32($r1, PROC_KERN)
cmp b32 $r14 $r1
bra e #host_send
// wait for space in RFIFO
host_recv_wait:
nv_iord($r1, NV_PPWR_RFIFO_GET)
nv_iord($r2, NV_PPWR_RFIFO_PUT)
xor $r1 #rfifo_qmaskb
cmp b32 $r1 $r2
bra e #host_recv_wait
and $r3 $r2 #rfifo_qmaskp
shl b32 $r3 #rfifo_qlen
add b32 $r3 #rfifo_queue
// enqueue message
st b32 D[$r3 + #fifo_data1] $r11
st b32 D[$r3 + #fifo_data0] $r12
st b32 D[$r3 + #fifo_message] $r13
st b32 D[$r3 + #fifo_process] $r14
add b32 $r2 0x1
and $r2 #rfifo_qmaskf
nv_iowr(NV_PPWR_RFIFO_PUT, $r2)
// notify host of pending message
mov $r2 NV_PPWR_INTR_TRIGGER_USER0
nv_iowr(NV_PPWR_INTR_TRIGGER, $r2)
ret
// $r15 - current (host)
// $r0 - zero
host_init:
// store each fifo's base/size in H2D/D2H scratch regs
mov $r1 #fifo_qsize
shl b32 $r1 16
or $r1 #fifo_queue
nv_iowr(NV_PPWR_H2D, $r1);
mov $r1 #rfifo_qsize
shl b32 $r1 16
or $r1 #rfifo_queue
nv_iowr(NV_PPWR_D2H, $r1);
// enable fifo subintr for first fifo
mov $r1 1
nv_iowr(NV_PPWR_FIFO_INTR_EN, $r1)
ret
#endif
@@ -0,0 +1,393 @@
/*
* 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
*/
#define T_TIMEOUT 2200000
#define T_RISEFALL 1000
#define T_HOLD 5000
#ifdef INCLUDE_PROC
process(PROC_I2C_, #i2c_init, #i2c_recv)
#endif
/******************************************************************************
* I2C_ data segment
*****************************************************************************/
#ifdef INCLUDE_DATA
i2c_scl_map:
.b32 NV_PPWR_OUTPUT_I2C_0_SCL
.b32 NV_PPWR_OUTPUT_I2C_1_SCL
.b32 NV_PPWR_OUTPUT_I2C_2_SCL
.b32 NV_PPWR_OUTPUT_I2C_3_SCL
.b32 NV_PPWR_OUTPUT_I2C_4_SCL
.b32 NV_PPWR_OUTPUT_I2C_5_SCL
.b32 NV_PPWR_OUTPUT_I2C_6_SCL
.b32 NV_PPWR_OUTPUT_I2C_7_SCL
.b32 NV_PPWR_OUTPUT_I2C_8_SCL
.b32 NV_PPWR_OUTPUT_I2C_9_SCL
i2c_sda_map:
.b32 NV_PPWR_OUTPUT_I2C_0_SDA
.b32 NV_PPWR_OUTPUT_I2C_1_SDA
.b32 NV_PPWR_OUTPUT_I2C_2_SDA
.b32 NV_PPWR_OUTPUT_I2C_3_SDA
.b32 NV_PPWR_OUTPUT_I2C_4_SDA
.b32 NV_PPWR_OUTPUT_I2C_5_SDA
.b32 NV_PPWR_OUTPUT_I2C_6_SDA
.b32 NV_PPWR_OUTPUT_I2C_7_SDA
.b32 NV_PPWR_OUTPUT_I2C_8_SDA
.b32 NV_PPWR_OUTPUT_I2C_9_SDA
#if NVKM_PPWR_CHIPSET < GF119
i2c_ctrl:
.b32 0x00e138
.b32 0x00e150
.b32 0x00e168
.b32 0x00e180
.b32 0x00e254
.b32 0x00e274
.b32 0x00e764
.b32 0x00e780
.b32 0x00e79c
.b32 0x00e7b8
#endif
#endif
/******************************************************************************
* I2C_ code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
// $r3 - value
// $r2 - sda line
// $r1 - scl line
// $r0 - zero
i2c_drive_scl:
cmp b32 $r3 0
bra e #i2c_drive_scl_lo
nv_iowr(NV_PPWR_OUTPUT_SET, $r1)
ret
i2c_drive_scl_lo:
nv_iowr(NV_PPWR_OUTPUT_CLR, $r1)
ret
i2c_drive_sda:
cmp b32 $r3 0
bra e #i2c_drive_sda_lo
nv_iowr(NV_PPWR_OUTPUT_SET, $r2)
ret
i2c_drive_sda_lo:
nv_iowr(NV_PPWR_OUTPUT_CLR, $r2)
ret
i2c_sense_scl:
bclr $flags $p1
nv_iord($r3, NV_PPWR_INPUT)
and $r3 $r1
bra z #i2c_sense_scl_done
bset $flags $p1
i2c_sense_scl_done:
ret
i2c_sense_sda:
bclr $flags $p1
nv_iord($r3, NV_PPWR_INPUT)
and $r3 $r2
bra z #i2c_sense_sda_done
bset $flags $p1
i2c_sense_sda_done:
ret
#define i2c_drive_scl(v) /*
*/ mov $r3 (v) /*
*/ call(i2c_drive_scl)
#define i2c_drive_sda(v) /*
*/ mov $r3 (v) /*
*/ call(i2c_drive_sda)
#define i2c_sense_scl() /*
*/ call(i2c_sense_scl)
#define i2c_sense_sda() /*
*/ call(i2c_sense_sda)
#define i2c_delay(v) /*
*/ mov $r14 (v) /*
*/ call(nsec)
#define i2c_trace_init() /*
*/ imm32($r6, 0x10000000) /*
*/ sub b32 $r7 $r6 1 /*
*/
#define i2c_trace_down() /*
*/ shr b32 $r6 4 /*
*/ push $r5 /*
*/ shl b32 $r5 $r6 4 /*
*/ sub b32 $r5 $r6 /*
*/ not b32 $r5 /*
*/ and $r7 $r5 /*
*/ pop $r5 /*
*/
#define i2c_trace_exit() /*
*/ shl b32 $r6 4 /*
*/
#define i2c_trace_next() /*
*/ add b32 $r7 $r6 /*
*/
#define i2c_trace_call(func) /*
*/ i2c_trace_next() /*
*/ i2c_trace_down() /*
*/ call(func) /*
*/ i2c_trace_exit() /*
*/
i2c_raise_scl:
push $r4
mov $r4 (T_TIMEOUT / T_RISEFALL)
i2c_drive_scl(1)
i2c_raise_scl_wait:
i2c_delay(T_RISEFALL)
i2c_sense_scl()
bra $p1 #i2c_raise_scl_done
sub b32 $r4 1
bra nz #i2c_raise_scl_wait
i2c_raise_scl_done:
pop $r4
ret
i2c_start:
i2c_sense_scl()
bra not $p1 #i2c_start_rep
i2c_sense_sda()
bra not $p1 #i2c_start_rep
bra #i2c_start_send
i2c_start_rep:
i2c_drive_scl(0)
i2c_drive_sda(1)
i2c_trace_call(i2c_raise_scl)
bra not $p1 #i2c_start_out
i2c_start_send:
i2c_drive_sda(0)
i2c_delay(T_HOLD)
i2c_drive_scl(0)
i2c_delay(T_HOLD)
i2c_start_out:
ret
i2c_stop:
i2c_drive_scl(0)
i2c_drive_sda(0)
i2c_delay(T_RISEFALL)
i2c_drive_scl(1)
i2c_delay(T_HOLD)
i2c_drive_sda(1)
i2c_delay(T_HOLD)
ret
// $r3 - value
// $r2 - sda line
// $r1 - scl line
// $r0 - zero
i2c_bitw:
call(i2c_drive_sda)
i2c_delay(T_RISEFALL)
i2c_trace_call(i2c_raise_scl)
bra not $p1 #i2c_bitw_out
i2c_delay(T_HOLD)
i2c_drive_scl(0)
i2c_delay(T_HOLD)
i2c_bitw_out:
ret
// $r3 - value (out)
// $r2 - sda line
// $r1 - scl line
// $r0 - zero
i2c_bitr:
i2c_drive_sda(1)
i2c_delay(T_RISEFALL)
i2c_trace_call(i2c_raise_scl)
bra not $p1 #i2c_bitr_done
i2c_sense_sda()
i2c_drive_scl(0)
i2c_delay(T_HOLD)
xbit $r3 $flags $p1
bset $flags $p1
i2c_bitr_done:
ret
i2c_get_byte:
mov $r5 0
mov $r4 8
i2c_get_byte_next:
shl b32 $r5 1
i2c_trace_call(i2c_bitr)
bra not $p1 #i2c_get_byte_done
or $r5 $r3
sub b32 $r4 1
bra nz #i2c_get_byte_next
mov $r3 1
i2c_trace_call(i2c_bitw)
i2c_get_byte_done:
ret
i2c_put_byte:
mov $r4 8
i2c_put_byte_next:
sub b32 $r4 1
xbit $r3 $r5 $r4
i2c_trace_call(i2c_bitw)
bra not $p1 #i2c_put_byte_done
cmp b32 $r4 0
bra ne #i2c_put_byte_next
i2c_trace_call(i2c_bitr)
bra not $p1 #i2c_put_byte_done
i2c_trace_next()
cmp b32 $r3 1
bra ne #i2c_put_byte_done
bclr $flags $p1 // nack
i2c_put_byte_done:
ret
i2c_addr:
i2c_trace_call(i2c_start)
bra not $p1 #i2c_addr_done
extr $r3 $r12 I2C__MSG_DATA0_ADDR
shl b32 $r3 1
or $r5 $r3
i2c_trace_call(i2c_put_byte)
i2c_addr_done:
ret
i2c_acquire_addr:
extr $r14 $r12 I2C__MSG_DATA0_PORT
#if NVKM_PPWR_CHIPSET < GF119
shl b32 $r14 2
add b32 $r14 #i2c_ctrl
ld b32 $r14 D[$r14]
#else
shl b32 $r14 5
add b32 $r14 0x00d014
#endif
ret
i2c_acquire:
call(i2c_acquire_addr)
call(rd32)
bset $r13 3
call(wr32)
ret
i2c_release:
call(i2c_acquire_addr)
call(rd32)
bclr $r13 3
call(wr32)
ret
// description
//
// $r15 - current (i2c)
// $r14 - sender process name
// $r13 - message
// $r12 - data0
// $r11 - data1
// $r0 - zero
i2c_recv:
bclr $flags $p1
extr $r1 $r12 I2C__MSG_DATA0_PORT
shl b32 $r1 2
cmp b32 $r1 (#i2c_sda_map - #i2c_scl_map)
bra ge #i2c_recv_done
add b32 $r3 $r1 #i2c_sda_map
ld b32 $r2 D[$r3]
add b32 $r3 $r1 #i2c_scl_map
ld b32 $r1 D[$r3]
bset $flags $p2
push $r13
push $r14
push $r13
i2c_trace_init()
i2c_trace_call(i2c_acquire)
pop $r13
cmp b32 $r13 I2C__MSG_RD08
bra ne #i2c_recv_not_rd08
mov $r5 0
i2c_trace_call(i2c_addr)
bra not $p1 #i2c_recv_done
extr $r5 $r12 I2C__MSG_DATA0_RD08_REG
i2c_trace_call(i2c_put_byte)
bra not $p1 #i2c_recv_done
mov $r5 1
i2c_trace_call(i2c_addr)
bra not $p1 #i2c_recv_done
i2c_trace_call(i2c_get_byte)
bra not $p1 #i2c_recv_done
ins $r11 $r5 I2C__MSG_DATA1_RD08_VAL
i2c_trace_call(i2c_stop)
mov b32 $r11 $r5
clear b32 $r7
bra #i2c_recv_done
i2c_recv_not_rd08:
cmp b32 $r13 I2C__MSG_WR08
bra ne #i2c_recv_not_wr08
mov $r5 0
call(i2c_addr)
bra not $p1 #i2c_recv_done
extr $r5 $r12 I2C__MSG_DATA0_WR08_REG
call(i2c_put_byte)
bra not $p1 #i2c_recv_done
mov $r5 0
call(i2c_addr)
bra not $p1 #i2c_recv_done
extr $r5 $r11 I2C__MSG_DATA1_WR08_VAL
call(i2c_put_byte)
bra not $p1 #i2c_recv_done
call(i2c_stop)
clear b32 $r7
extr $r5 $r12 I2C__MSG_DATA0_WR08_SYNC
bra nz #i2c_recv_done
bclr $flags $p2
bra #i2c_recv_done
i2c_recv_not_wr08:
i2c_recv_done:
extr $r14 $r12 I2C__MSG_DATA0_PORT
call(i2c_release)
pop $r14
pop $r13
bra not $p2 #i2c_recv_exit
mov b32 $r12 $r7
call(send)
i2c_recv_exit:
ret
// description
//
// $r15 - current (i2c)
// $r0 - zero
i2c_init:
ret
#endif
@@ -0,0 +1,84 @@
/*
* 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
*/
#ifdef INCLUDE_PROC
process(PROC_IDLE, #idle, #idle_recv)
#endif
/******************************************************************************
* IDLE data segment
*****************************************************************************/
#ifdef INCLUDE_DATA
#endif
/******************************************************************************
* IDLE code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
// description
//
// $r15 - current (idle)
// $r14 - message
// $r0 - zero
idle_recv:
ret
// description
//
// $r15 - current (idle)
// $r0 - zero
idle:
// set our "no interrupt has occurred during our execution" flag
bset $flags $p0
// count IDLE invocations for debugging purposes
nv_iord($r1, NV_PPWR_DSCRATCH(1))
add b32 $r1 1
nv_iowr(NV_PPWR_DSCRATCH(1), $r1)
// keep looping while there's pending messages for any process
idle_loop:
mov $r1 #proc_list_head
bclr $flags $p2
idle_proc:
// process the process' messages until there's none left
idle_proc_exec:
push $r1
mov b32 $r14 $r1
call(recv)
pop $r1
bra not $p1 #idle_proc_next
bset $flags $p2
bra #idle_proc_exec
// next process!
idle_proc_next:
add b32 $r1 #proc_size
cmp b32 $r1 $r15
bra ne #idle_proc
bra $p2 #idle_loop
// sleep if no interrupts have occurred
sleep $p0
bra #idle
#endif
@@ -0,0 +1,544 @@
/*
* 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
*/
/******************************************************************************
* kernel data segment
*****************************************************************************/
#ifdef INCLUDE_PROC
proc_kern:
process(PROC_KERN, 0, 0)
proc_list_head:
#endif
#ifdef INCLUDE_DATA
proc_list_tail:
time_prev: .b32 0
time_next: .b32 0
#endif
/******************************************************************************
* kernel code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
bra #init
// read nv register
//
// $r15 - current
// $r14 - addr
// $r13 - data (return)
// $r0 - zero
rd32:
nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
imm32($r13, NV_PPWR_MMIO_CTRL_OP_RD | NV_PPWR_MMIO_CTRL_TRIGGER)
nv_iowr(NV_PPWR_MMIO_CTRL, $r13)
rd32_wait:
nv_iord($r13, NV_PPWR_MMIO_CTRL)
and $r13 NV_PPWR_MMIO_CTRL_STATUS
bra nz #rd32_wait
nv_iord($r13, NV_PPWR_MMIO_DATA)
ret
// write nv register
//
// $r15 - current
// $r14 - addr
// $r13 - data
// $r0 - zero
wr32:
nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
nv_iowr(NV_PPWR_MMIO_DATA, $r13)
imm32($r13, NV_PPWR_MMIO_CTRL_OP_WR | NV_PPWR_MMIO_CTRL_MASK_B32_0 | NV_PPWR_MMIO_CTRL_TRIGGER)
#ifdef NVKM_FALCON_MMIO_TRAP
push $r13
mov $r13 NV_PPWR_INTR_TRIGGER_USER1
nv_iowr(NV_PPWR_INTR_TRIGGER, $r13)
wr32_host:
nv_iord($r13, NV_PPWR_INTR)
and $r13 NV_PPWR_INTR_USER1
bra nz #wr32_host
pop $r13
#endif
nv_iowr(NV_PPWR_MMIO_CTRL, $r13)
wr32_wait:
nv_iord($r13, NV_PPWR_MMIO_CTRL)
and $r13 NV_PPWR_MMIO_CTRL_STATUS
bra nz #wr32_wait
ret
// busy-wait for a period of time
//
// $r15 - current
// $r14 - ns
// $r0 - zero
nsec:
push $r9
push $r8
nv_iord($r8, NV_PPWR_TIMER_LOW)
nsec_loop:
nv_iord($r9, NV_PPWR_TIMER_LOW)
sub b32 $r9 $r8
cmp b32 $r9 $r14
bra l #nsec_loop
pop $r8
pop $r9
ret
// busy-wait for a period of time
//
// $r15 - current
// $r14 - addr
// $r13 - mask
// $r12 - data
// $r11 - timeout (ns)
// $r0 - zero
wait:
push $r9
push $r8
nv_iord($r8, NV_PPWR_TIMER_LOW)
wait_loop:
nv_rd32($r10, $r14)
and $r10 $r13
cmp b32 $r10 $r12
bra e #wait_done
nv_iord($r9, NV_PPWR_TIMER_LOW)
sub b32 $r9 $r8
cmp b32 $r9 $r11
bra l #wait_loop
wait_done:
pop $r8
pop $r9
ret
// $r15 - current (kern)
// $r14 - process
// $r8 - NV_PPWR_INTR
intr_watchdog:
// read process' timer status, skip if not enabled
ld b32 $r9 D[$r14 + #proc_time]
cmp b32 $r9 0
bra z #intr_watchdog_next_proc
// subtract last timer's value from process' timer,
// if it's <= 0 then the timer has expired
ld b32 $r10 D[$r0 + #time_prev]
sub b32 $r9 $r10
bra g #intr_watchdog_next_time
mov $r13 KMSG_ALARM
call(send_proc)
clear b32 $r9
bra #intr_watchdog_next_proc
// otherwise, update the next timer's value if this
// process' timer is the soonest
intr_watchdog_next_time:
// ... or if there's no next timer yet
ld b32 $r10 D[$r0 + #time_next]
cmp b32 $r10 0
bra z #intr_watchdog_next_time_set
cmp b32 $r9 $r10
bra g #intr_watchdog_next_proc
intr_watchdog_next_time_set:
st b32 D[$r0 + #time_next] $r9
// update process' timer status, and advance
intr_watchdog_next_proc:
st b32 D[$r14 + #proc_time] $r9
add b32 $r14 #proc_size
cmp b32 $r14 #proc_list_tail
bra ne #intr_watchdog
ret
intr:
push $r0
clear b32 $r0
push $r8
push $r9
push $r10
push $r11
push $r12
push $r13
push $r14
push $r15
mov $r15 #proc_kern
mov $r8 $flags
push $r8
nv_iord($r8, NV_PPWR_DSCRATCH(0))
add b32 $r8 1
nv_iowr(NV_PPWR_DSCRATCH(0), $r8)
nv_iord($r8, NV_PPWR_INTR)
and $r9 $r8 NV_PPWR_INTR_WATCHDOG
bra z #intr_skip_watchdog
st b32 D[$r0 + #time_next] $r0
mov $r14 #proc_list_head
call(intr_watchdog)
ld b32 $r9 D[$r0 + #time_next]
cmp b32 $r9 0
bra z #intr_skip_watchdog
nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9)
st b32 D[$r0 + #time_prev] $r9
intr_skip_watchdog:
and $r9 $r8 NV_PPWR_INTR_SUBINTR
bra z #intr_skip_subintr
nv_iord($r9, NV_PPWR_SUBINTR)
and $r10 $r9 NV_PPWR_SUBINTR_FIFO
bra z #intr_subintr_skip_fifo
nv_iord($r12, NV_PPWR_FIFO_INTR)
push $r12
imm32($r14, PROC_HOST)
mov $r13 KMSG_FIFO
call(send)
pop $r12
nv_iowr(NV_PPWR_FIFO_INTR, $r12)
intr_subintr_skip_fifo:
nv_iowr(NV_PPWR_SUBINTR, $r9)
intr_skip_subintr:
mov $r9 (NV_PPWR_INTR_USER0 | NV_PPWR_INTR_USER1 | NV_PPWR_INTR_PAUSE)
not b32 $r9
and $r8 $r9
nv_iowr(NV_PPWR_INTR_ACK, $r8)
pop $r8
mov $flags $r8
pop $r15
pop $r14
pop $r13
pop $r12
pop $r11
pop $r10
pop $r9
pop $r8
pop $r0
bclr $flags $p0
iret
// calculate the number of ticks in the specified nanoseconds delay
//
// $r15 - current
// $r14 - ns
// $r14 - ticks (return)
// $r0 - zero
ticks_from_ns:
push $r12
push $r11
/* try not losing precision (multiply then divide) */
imm32($r13, HW_TICKS_PER_US)
call(mulu32_32_64)
/* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */
div $r12 $r12 1000
/* check if there wasn't any overflow */
cmpu b32 $r11 0
bra e #ticks_from_ns_quit
/* let's divide then multiply, too bad for the precision! */
div $r14 $r14 1000
imm32($r13, HW_TICKS_PER_US)
call(mulu32_32_64)
/* this cannot overflow as long as HW_TICKS_PER_US < 1000 */
ticks_from_ns_quit:
mov b32 $r14 $r12
pop $r11
pop $r12
ret
// calculate the number of ticks in the specified microsecond delay
//
// $r15 - current
// $r14 - us
// $r14 - ticks (return)
// $r0 - zero
ticks_from_us:
push $r12
push $r11
/* simply multiply $us by HW_TICKS_PER_US */
imm32($r13, HW_TICKS_PER_US)
call(mulu32_32_64)
mov b32 $r14 $r12
/* check if there wasn't any overflow */
cmpu b32 $r11 0
bra e #ticks_from_us_quit
/* Overflow! */
clear b32 $r14
ticks_from_us_quit:
pop $r11
pop $r12
ret
// calculate the number of ticks in the specified microsecond delay
//
// $r15 - current
// $r14 - ticks
// $r14 - us (return)
// $r0 - zero
ticks_to_us:
/* simply divide $ticks by HW_TICKS_PER_US */
imm32($r13, HW_TICKS_PER_US)
div $r14 $r14 $r13
ret
// request the current process be sent a message after a timeout expires
//
// $r15 - current
// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow)
// $r0 - zero
timer:
push $r9
push $r8
// interrupts off to prevent racing with timer isr
bclr $flags ie0
// if current process already has a timer set, bail
ld b32 $r8 D[$r15 + #proc_time]
cmp b32 $r8 0
bra g #timer_done
// halt watchdog timer temporarily
clear b32 $r8
nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
// find out how much time elapsed since the last update
// of the watchdog and add this time to the wanted ticks
nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
ld b32 $r9 D[$r0 + #time_prev]
sub b32 $r9 $r8
add b32 $r14 $r9
st b32 D[$r15 + #proc_time] $r14
// check for a pending interrupt. if there's one already
// pending, we can just bail since the timer isr will
// queue the next soonest right after it's done
nv_iord($r8, NV_PPWR_INTR)
and $r8 NV_PPWR_INTR_WATCHDOG
bra nz #timer_enable
// update the watchdog if this timer should expire first,
// or if there's no timeout already set
nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
cmp b32 $r14 $r0
bra e #timer_reset
cmp b32 $r14 $r8
bra g #timer_enable
timer_reset:
nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
st b32 D[$r0 + #time_prev] $r14
// re-enable the watchdog timer
timer_enable:
mov $r8 1
nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
// interrupts back on
timer_done:
bset $flags ie0
pop $r8
pop $r9
ret
// send message to another process
//
// $r15 - current
// $r14 - process
// $r13 - message
// $r12 - message data 0
// $r11 - message data 1
// $r0 - zero
send_proc:
push $r8
push $r9
// check for space in queue
ld b32 $r8 D[$r14 + #proc_qget]
ld b32 $r9 D[$r14 + #proc_qput]
xor $r8 #proc_qmaskb
cmp b32 $r8 $r9
bra e #send_done
// enqueue message
and $r8 $r9 #proc_qmaskp
shl b32 $r8 $r8 #proc_qlen
add b32 $r8 #proc_queue
add b32 $r8 $r14
ld b32 $r10 D[$r15 + #proc_id]
st b32 D[$r8 + #msg_process] $r10
st b32 D[$r8 + #msg_message] $r13
st b32 D[$r8 + #msg_data0] $r12
st b32 D[$r8 + #msg_data1] $r11
// increment PUT
add b32 $r9 1
and $r9 #proc_qmaskf
st b32 D[$r14 + #proc_qput] $r9
bset $flags $p2
send_done:
pop $r9
pop $r8
ret
// lookup process structure by its name
//
// $r15 - current
// $r14 - process name
// $r0 - zero
//
// $r14 - process
// $p1 - success
find:
push $r8
mov $r8 #proc_list_head
bset $flags $p1
find_loop:
ld b32 $r10 D[$r8 + #proc_id]
cmp b32 $r10 $r14
bra e #find_done
add b32 $r8 #proc_size
cmp b32 $r8 #proc_list_tail
bra ne #find_loop
bclr $flags $p1
find_done:
mov b32 $r14 $r8
pop $r8
ret
// send message to another process
//
// $r15 - current
// $r14 - process id
// $r13 - message
// $r12 - message data 0
// $r11 - message data 1
// $r0 - zero
send:
call(find)
bra $p1 #send_proc
ret
// process single message for a given process
//
// $r15 - current
// $r14 - process
// $r0 - zero
recv:
push $r9
push $r8
ld b32 $r8 D[$r14 + #proc_qget]
ld b32 $r9 D[$r14 + #proc_qput]
bclr $flags $p1
cmp b32 $r8 $r9
bra e #recv_done
// dequeue message
and $r9 $r8 #proc_qmaskp
add b32 $r8 1
and $r8 #proc_qmaskf
st b32 D[$r14 + #proc_qget] $r8
ld b32 $r10 D[$r14 + #proc_recv]
push $r15
mov $r15 $flags
push $r15
mov b32 $r15 $r14
shl b32 $r9 $r9 #proc_qlen
add b32 $r14 $r9
add b32 $r14 #proc_queue
ld b32 $r11 D[$r14 + #msg_data1]
ld b32 $r12 D[$r14 + #msg_data0]
ld b32 $r13 D[$r14 + #msg_message]
ld b32 $r14 D[$r14 + #msg_process]
// process it
call $r10
pop $r15
mov $flags $r15
bset $flags $p1
pop $r15
recv_done:
pop $r8
pop $r9
ret
init:
// setup stack
nv_iord($r1, NV_PPWR_CAPS)
extr $r1 $r1 9:17
shl b32 $r1 8
mov $sp $r1
#ifdef NVKM_FALCON_MMIO_UAS
// somehow allows the magic "access mmio via D[]" stuff that's
// used by the nv_rd32/nv_wr32 macros to work
imm32($r1, 0x10 | NV_PPWR_UAS_CONFIG_ENABLE)
nv_iowrs(NV_PPWR_UAS_CONFIG, $r1)
#endif
// route all interrupts except user0/1 and pause to fuc
imm32($r1, 0xe0)
nv_iowr(NV_PPWR_INTR_ROUTE, $r1)
// enable watchdog and subintr intrs
mov $r1 NV_PPWR_INTR_EN_CLR_MASK
nv_iowr(NV_PPWR_INTR_EN_CLR, $r1)
mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG
or $r1 NV_PPWR_INTR_EN_SET_SUBINTR
nv_iowr(NV_PPWR_INTR_EN_SET, $r1)
// enable interrupts globally
imm32($r1, #intr)
and $r1 0xffff
mov $iv0 $r1
bset $flags ie0
// enable watchdog timer
mov $r1 1
nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1)
// bootstrap processes, idle process will be last, and not return
mov $r15 #proc_list_head
init_proc:
ld b32 $r1 D[$r15 + #proc_init]
cmp b32 $r1 0
bra z #init_proc
call $r1
add b32 $r15 #proc_size
bra #init_proc
#endif
@@ -0,0 +1,272 @@
/*
* 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
*/
#define GT215 0xa3
#define GF100 0xc0
#define GF119 0xd9
#define GK208 0x108
#include "os.h"
// IO addresses
#define NV_PPWR_INTR_TRIGGER 0x0000
#define NV_PPWR_INTR_TRIGGER_USER1 0x00000080
#define NV_PPWR_INTR_TRIGGER_USER0 0x00000040
#define NV_PPWR_INTR_ACK 0x0004
#define NV_PPWR_INTR_ACK_SUBINTR 0x00000800
#define NV_PPWR_INTR_ACK_WATCHDOG 0x00000002
#define NV_PPWR_INTR 0x0008
#define NV_PPWR_INTR_SUBINTR 0x00000800
#define NV_PPWR_INTR_USER1 0x00000080
#define NV_PPWR_INTR_USER0 0x00000040
#define NV_PPWR_INTR_PAUSE 0x00000020
#define NV_PPWR_INTR_WATCHDOG 0x00000002
#define NV_PPWR_INTR_EN_SET 0x0010
#define NV_PPWR_INTR_EN_SET_SUBINTR 0x00000800
#define NV_PPWR_INTR_EN_SET_WATCHDOG 0x00000002
#define NV_PPWR_INTR_EN_CLR 0x0014
#define NV_PPWR_INTR_EN_CLR_MASK /* fuck i hate envyas */ -1
#define NV_PPWR_INTR_ROUTE 0x001c
#define NV_PPWR_TIMER_LOW 0x002c
#define NV_PPWR_WATCHDOG_TIME 0x0034
#define NV_PPWR_WATCHDOG_ENABLE 0x0038
#define NV_PPWR_CAPS 0x0108
#define NV_PPWR_UAS_CONFIG 0x0164
#define NV_PPWR_UAS_CONFIG_ENABLE 0x00010000
#if NVKM_PPWR_CHIPSET >= GK208
#define NV_PPWR_DSCRATCH(i) (4 * (i) + 0x0450)
#endif
#define NV_PPWR_FIFO_PUT(i) (4 * (i) + 0x04a0)
#define NV_PPWR_FIFO_GET(i) (4 * (i) + 0x04b0)
#define NV_PPWR_FIFO_INTR 0x04c0
#define NV_PPWR_FIFO_INTR_EN 0x04c4
#define NV_PPWR_RFIFO_PUT 0x04c8
#define NV_PPWR_RFIFO_GET 0x04cc
#define NV_PPWR_H2D 0x04d0
#define NV_PPWR_D2H 0x04dc
#if NVKM_PPWR_CHIPSET < GK208
#define NV_PPWR_DSCRATCH(i) (4 * (i) + 0x05d0)
#endif
#define NV_PPWR_SUBINTR 0x0688
#define NV_PPWR_SUBINTR_FIFO 0x00000002
#define NV_PPWR_MMIO_ADDR 0x07a0
#define NV_PPWR_MMIO_DATA 0x07a4
#define NV_PPWR_MMIO_CTRL 0x07ac
#define NV_PPWR_MMIO_CTRL_TRIGGER 0x00010000
#define NV_PPWR_MMIO_CTRL_STATUS 0x00007000
#define NV_PPWR_MMIO_CTRL_STATUS_IDLE 0x00000000
#define NV_PPWR_MMIO_CTRL_MASK 0x000000f0
#define NV_PPWR_MMIO_CTRL_MASK_B32_0 0x000000f0
#define NV_PPWR_MMIO_CTRL_OP 0x00000003
#define NV_PPWR_MMIO_CTRL_OP_RD 0x00000001
#define NV_PPWR_MMIO_CTRL_OP_WR 0x00000002
#define NV_PPWR_OUTPUT 0x07c0
#define NV_PPWR_OUTPUT_FB_PAUSE 0x00000004
#if NVKM_PPWR_CHIPSET < GF119
#define NV_PPWR_OUTPUT_I2C_3_SCL 0x00000100
#define NV_PPWR_OUTPUT_I2C_3_SDA 0x00000200
#define NV_PPWR_OUTPUT_I2C_0_SCL 0x00001000
#define NV_PPWR_OUTPUT_I2C_0_SDA 0x00002000
#define NV_PPWR_OUTPUT_I2C_1_SCL 0x00004000
#define NV_PPWR_OUTPUT_I2C_1_SDA 0x00008000
#define NV_PPWR_OUTPUT_I2C_2_SCL 0x00010000
#define NV_PPWR_OUTPUT_I2C_2_SDA 0x00020000
#define NV_PPWR_OUTPUT_I2C_4_SCL 0x00040000
#define NV_PPWR_OUTPUT_I2C_4_SDA 0x00080000
#define NV_PPWR_OUTPUT_I2C_5_SCL 0x00100000
#define NV_PPWR_OUTPUT_I2C_5_SDA 0x00200000
#define NV_PPWR_OUTPUT_I2C_6_SCL 0x00400000
#define NV_PPWR_OUTPUT_I2C_6_SDA 0x00800000
#define NV_PPWR_OUTPUT_I2C_7_SCL 0x01000000
#define NV_PPWR_OUTPUT_I2C_7_SDA 0x02000000
#define NV_PPWR_OUTPUT_I2C_8_SCL 0x04000000
#define NV_PPWR_OUTPUT_I2C_8_SDA 0x08000000
#define NV_PPWR_OUTPUT_I2C_9_SCL 0x10000000
#define NV_PPWR_OUTPUT_I2C_9_SDA 0x20000000
#else
#define NV_PPWR_OUTPUT_I2C_0_SCL 0x00000400
#define NV_PPWR_OUTPUT_I2C_1_SCL 0x00000800
#define NV_PPWR_OUTPUT_I2C_2_SCL 0x00001000
#define NV_PPWR_OUTPUT_I2C_3_SCL 0x00002000
#define NV_PPWR_OUTPUT_I2C_4_SCL 0x00004000
#define NV_PPWR_OUTPUT_I2C_5_SCL 0x00008000
#define NV_PPWR_OUTPUT_I2C_6_SCL 0x00010000
#define NV_PPWR_OUTPUT_I2C_7_SCL 0x00020000
#define NV_PPWR_OUTPUT_I2C_8_SCL 0x00040000
#define NV_PPWR_OUTPUT_I2C_9_SCL 0x00080000
#define NV_PPWR_OUTPUT_I2C_0_SDA 0x00100000
#define NV_PPWR_OUTPUT_I2C_1_SDA 0x00200000
#define NV_PPWR_OUTPUT_I2C_2_SDA 0x00400000
#define NV_PPWR_OUTPUT_I2C_3_SDA 0x00800000
#define NV_PPWR_OUTPUT_I2C_4_SDA 0x01000000
#define NV_PPWR_OUTPUT_I2C_5_SDA 0x02000000
#define NV_PPWR_OUTPUT_I2C_6_SDA 0x04000000
#define NV_PPWR_OUTPUT_I2C_7_SDA 0x08000000
#define NV_PPWR_OUTPUT_I2C_8_SDA 0x10000000
#define NV_PPWR_OUTPUT_I2C_9_SDA 0x20000000
#endif
#define NV_PPWR_INPUT 0x07c4
#define NV_PPWR_OUTPUT_SET 0x07e0
#define NV_PPWR_OUTPUT_SET_FB_PAUSE 0x00000004
#define NV_PPWR_OUTPUT_CLR 0x07e4
#define NV_PPWR_OUTPUT_CLR_FB_PAUSE 0x00000004
// Inter-process message format
.equ #msg_process 0x00 /* send() target, recv() sender */
.equ #msg_message 0x04
.equ #msg_data0 0x08
.equ #msg_data1 0x0c
// Kernel message IDs
#define KMSG_FIFO 0x00000000
#define KMSG_ALARM 0x00000001
// Process message queue description
.equ #proc_qlen 4 // log2(size of queue entry in bytes)
.equ #proc_qnum 2 // log2(max number of entries in queue)
.equ #proc_qmaskb (1 << #proc_qnum) // max number of entries in queue
.equ #proc_qmaskp (#proc_qmaskb - 1)
.equ #proc_qmaskf ((#proc_qmaskb << 1) - 1)
.equ #proc_qsize (1 << (#proc_qlen + #proc_qnum))
// Process table entry
.equ #proc_id 0x00
.equ #proc_init 0x04
.equ #proc_recv 0x08
.equ #proc_time 0x0c
.equ #proc_qput 0x10
.equ #proc_qget 0x14
.equ #proc_queue 0x18
.equ #proc_size (0x18 + #proc_qsize)
#define process(id,init,recv) /*
*/ .b32 id /*
*/ .b32 init /*
*/ .b32 recv /*
*/ .b32 0 /*
*/ .b32 0 /*
*/ .b32 0 /*
*/ .skip 64
#if NVKM_PPWR_CHIPSET < GK208
#define imm32(reg,val) /*
*/ movw reg ((val) & 0x0000ffff) /*
*/ sethi reg ((val) & 0xffff0000)
#else
#define imm32(reg,val) /*
*/ mov reg (val)
#endif
#ifndef NVKM_FALCON_UNSHIFTED_IO
#define nv_iord(reg,ior) /*
*/ mov reg ior /*
*/ shl b32 reg 6 /*
*/ iord reg I[reg + 0x000]
#else
#define nv_iord(reg,ior) /*
*/ mov reg ior /*
*/ iord reg I[reg + 0x000]
#endif
#ifndef NVKM_FALCON_UNSHIFTED_IO
#define nv_iowr(ior,reg) /*
*/ mov $r0 ior /*
*/ shl b32 $r0 6 /*
*/ iowr I[$r0 + 0x000] reg /*
*/ clear b32 $r0
#else
#define nv_iowr(ior,reg) /*
*/ mov $r0 ior /*
*/ iowr I[$r0 + 0x000] reg /*
*/ clear b32 $r0
#endif
#ifndef NVKM_FALCON_UNSHIFTED_IO
#define nv_iowrs(ior,reg) /*
*/ mov $r0 ior /*
*/ shl b32 $r0 6 /*
*/ iowrs I[$r0 + 0x000] reg /*
*/ clear b32 $r0
#else
#define nv_iowrs(ior,reg) /*
*/ mov $r0 ior /*
*/ iowrs I[$r0 + 0x000] reg /*
*/ clear b32 $r0
#endif
#define hash #
#define fn(a) a
#ifndef NVKM_FALCON_PC24
#define call(a) call fn(hash)a
#else
#define call(a) lcall fn(hash)a
#endif
#ifndef NVKM_FALCON_MMIO_UAS
#define nv_rd32(reg,addr) /*
*/ mov b32 $r14 addr /*
*/ call(rd32) /*
*/ mov b32 reg $r13
#else
#define nv_rd32(reg,addr) /*
*/ sethi $r0 0x14000000 /*
*/ or $r0 addr /*
*/ ld b32 reg D[$r0] /*
*/ clear b32 $r0
#endif
#if !defined(NVKM_FALCON_MMIO_UAS) || defined(NVKM_FALCON_MMIO_TRAP)
#define nv_wr32(addr,reg) /*
*/ push addr /*
*/ push reg /*
*/ pop $r13 /*
*/ pop $r14 /*
*/ call(wr32)
#else
#define nv_wr32(addr,reg) /*
*/ sethi $r0 0x14000000 /*
*/ or $r0 addr /*
*/ st b32 D[$r0] reg /*
*/ clear b32 $r0
#endif
#define st(size, addr, reg) /*
*/ imm32($r0, addr) /*
*/ st size D[$r0] reg /*
*/ clear b32 $r0
#define ld(size, reg, addr) /*
*/ imm32($r0, addr) /*
*/ ld size reg D[$r0] /*
*/ clear b32 $r0
// does a 64+64 -> 64 unsigned addition (C = A + B)
#define addu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /*
*/ add b32 reg_a_c_lo b_lo /*
*/ adc b32 reg_a_c_hi b_hi
// does a 64+64 -> 64 substraction (C = A - B)
#define subu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /*
*/ sub b32 reg_a_c_lo b_lo /*
*/ sbb b32 reg_a_c_hi b_hi
@@ -0,0 +1,447 @@
/*
* 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
*/
#ifdef INCLUDE_PROC
process(PROC_MEMX, #memx_init, #memx_recv)
#endif
/******************************************************************************
* MEMX data segment
*****************************************************************************/
#ifdef INCLUDE_DATA
.equ #memx_opcode 0
.equ #memx_header 2
.equ #memx_length 4
.equ #memx_func 8
#define handler(cmd,hdr,len,func) /*
*/ .b16 MEMX_##cmd /*
*/ .b16 hdr /*
*/ .b16 len /*
*/ .b16 0 /*
*/ .b32 func
memx_func_head:
handler(ENTER , 0x0000, 0x0000, #memx_func_enter)
memx_func_next:
handler(LEAVE , 0x0000, 0x0000, #memx_func_leave)
handler(WR32 , 0x0000, 0x0002, #memx_func_wr32)
handler(WAIT , 0x0004, 0x0000, #memx_func_wait)
handler(DELAY , 0x0001, 0x0000, #memx_func_delay)
handler(VBLANK, 0x0001, 0x0000, #memx_func_wait_vblank)
handler(TRAIN , 0x0000, 0x0000, #memx_func_train)
memx_func_tail:
.equ #memx_func_size #memx_func_next - #memx_func_head
.equ #memx_func_num (#memx_func_tail - #memx_func_head) / #memx_func_size
memx_ts_start:
.b32 0
memx_ts_end:
.b32 0
memx_data_head:
.skip 0x0800
memx_data_tail:
memx_train_head:
.skip 0x0100
memx_train_tail:
#endif
/******************************************************************************
* MEMX code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
// description
//
// $r15 - current (memx)
// $r4 - packet length
// $r3 - opcode desciption
// $r0 - zero
memx_func_enter:
#if NVKM_PPWR_CHIPSET == GT215
mov $r8 0x1610
nv_rd32($r7, $r8)
imm32($r6, 0xfffffffc)
and $r7 $r6
mov $r6 0x2
or $r7 $r6
nv_wr32($r8, $r7)
#else
mov $r6 0x001620
imm32($r7, ~0x00000aa2);
nv_rd32($r8, $r6)
and $r8 $r7
nv_wr32($r6, $r8)
imm32($r7, ~0x00000001)
nv_rd32($r8, $r6)
and $r8 $r7
nv_wr32($r6, $r8)
mov $r6 0x0026f0
nv_rd32($r8, $r6)
and $r8 $r7
nv_wr32($r6, $r8)
#endif
mov $r6 NV_PPWR_OUTPUT_SET_FB_PAUSE
nv_iowr(NV_PPWR_OUTPUT_SET, $r6)
memx_func_enter_wait:
nv_iord($r6, NV_PPWR_OUTPUT)
and $r6 NV_PPWR_OUTPUT_FB_PAUSE
bra z #memx_func_enter_wait
nv_iord($r6, NV_PPWR_TIMER_LOW)
st b32 D[$r0 + #memx_ts_start] $r6
ret
// description
//
// $r15 - current (memx)
// $r4 - packet length
// $r3 - opcode desciption
// $r0 - zero
memx_func_leave:
nv_iord($r6, NV_PPWR_TIMER_LOW)
st b32 D[$r0 + #memx_ts_end] $r6
mov $r6 NV_PPWR_OUTPUT_CLR_FB_PAUSE
nv_iowr(NV_PPWR_OUTPUT_CLR, $r6)
memx_func_leave_wait:
nv_iord($r6, NV_PPWR_OUTPUT)
and $r6 NV_PPWR_OUTPUT_FB_PAUSE
bra nz #memx_func_leave_wait
#if NVKM_PPWR_CHIPSET == GT215
mov $r8 0x1610
nv_rd32($r7, $r8)
imm32($r6, 0xffffffcc)
and $r7 $r6
nv_wr32($r8, $r7)
#else
mov $r6 0x0026f0
imm32($r7, 0x00000001)
nv_rd32($r8, $r6)
or $r8 $r7
nv_wr32($r6, $r8)
mov $r6 0x001620
nv_rd32($r8, $r6)
or $r8 $r7
nv_wr32($r6, $r8)
imm32($r7, 0x00000aa2);
nv_rd32($r8, $r6)
or $r8 $r7
nv_wr32($r6, $r8)
#endif
ret
#if NVKM_PPWR_CHIPSET < GF119
// description
//
// $r15 - current (memx)
// $r4 - packet length
// +00: head to wait for vblank on
// $r3 - opcode desciption
// $r0 - zero
memx_func_wait_vblank:
ld b32 $r6 D[$r1 + 0x00]
cmp b32 $r6 0x0
bra z #memx_func_wait_vblank_head0
cmp b32 $r6 0x1
bra z #memx_func_wait_vblank_head1
bra #memx_func_wait_vblank_fini
memx_func_wait_vblank_head1:
mov $r7 0x20
bra #memx_func_wait_vblank_0
memx_func_wait_vblank_head0:
mov $r7 0x8
memx_func_wait_vblank_0:
nv_iord($r6, NV_PPWR_INPUT)
and $r6 $r7
bra nz #memx_func_wait_vblank_0
memx_func_wait_vblank_1:
nv_iord($r6, NV_PPWR_INPUT)
and $r6 $r7
bra z #memx_func_wait_vblank_1
memx_func_wait_vblank_fini:
add b32 $r1 0x4
ret
#else
// XXX: currently no-op
//
// $r15 - current (memx)
// $r4 - packet length
// +00: head to wait for vblank on
// $r3 - opcode desciption
// $r0 - zero
memx_func_wait_vblank:
add b32 $r1 0x4
ret
#endif
// description
//
// $r15 - current (memx)
// $r4 - packet length
// +00*n: addr
// +04*n: data
// $r3 - opcode desciption
// $r0 - zero
memx_func_wr32:
ld b32 $r6 D[$r1 + 0x00]
ld b32 $r5 D[$r1 + 0x04]
add b32 $r1 0x08
nv_wr32($r6, $r5)
sub b32 $r4 0x02
bra nz #memx_func_wr32
ret
// description
//
// $r15 - current (memx)
// $r4 - packet length
// +00: addr
// +04: mask
// +08: data
// +0c: timeout (ns)
// $r3 - opcode desciption
// $r0 - zero
memx_func_wait:
nv_iord($r8, NV_PPWR_TIMER_LOW)
ld b32 $r14 D[$r1 + 0x00]
ld b32 $r13 D[$r1 + 0x04]
ld b32 $r12 D[$r1 + 0x08]
ld b32 $r11 D[$r1 + 0x0c]
add b32 $r1 0x10
call(wait)
ret
// description
//
// $r15 - current (memx)
// $r4 - packet length
// +00: time (ns)
// $r3 - opcode desciption
// $r0 - zero
memx_func_delay:
ld b32 $r14 D[$r1 + 0x00]
add b32 $r1 0x04
call(nsec)
ret
// description
//
// $r15 - current (memx)
// $r4 - packet length
// $r3 - opcode desciption
// $r0 - zero
memx_func_train:
#if NVKM_PPWR_CHIPSET == GT215
// $r5 - outer loop counter
// $r6 - inner loop counter
// $r7 - entry counter (#memx_train_head + $r7)
mov $r5 0x3
mov $r7 0x0
// Read random memory to wake up... things
imm32($r9, 0x700000)
nv_rd32($r8,$r9)
mov $r14 0x2710
call(nsec)
memx_func_train_loop_outer:
mulu $r8 $r5 0x101
sethi $r8 0x02000000
imm32($r9, 0x1111e0)
nv_wr32($r9, $r8)
push $r5
mov $r6 0x0
memx_func_train_loop_inner:
mov $r8 0x1111
mulu $r9 $r6 $r8
shl b32 $r8 $r9 0x10
or $r8 $r9
imm32($r9, 0x100720)
nv_wr32($r9, $r8)
imm32($r9, 0x100080)
nv_rd32($r8, $r9)
or $r8 $r8 0x20
nv_wr32($r9, $r8)
imm32($r9, 0x10053c)
imm32($r8, 0x80003002)
nv_wr32($r9, $r8)
imm32($r14, 0x100560)
imm32($r13, 0x80000000)
add b32 $r12 $r13 0
imm32($r11, 0x001e8480)
call(wait)
// $r5 - inner inner loop counter
// $r9 - result
mov $r5 0
imm32($r9, 0x8300ffff)
memx_func_train_loop_4x:
imm32($r10, 0x100080)
nv_rd32($r8, $r10)
imm32($r11, 0xffffffdf)
and $r8 $r11
nv_wr32($r10, $r8)
imm32($r10, 0x10053c)
imm32($r8, 0x80003002)
nv_wr32($r10, $r8)
imm32($r14, 0x100560)
imm32($r13, 0x80000000)
mov b32 $r12 $r13
imm32($r11, 0x00002710)
call(wait)
nv_rd32($r13, $r14)
and $r9 $r9 $r13
add b32 $r5 1
cmp b16 $r5 0x4
bra l #memx_func_train_loop_4x
add b32 $r10 $r7 #memx_train_head
st b32 D[$r10 + 0] $r9
add b32 $r6 1
add b32 $r7 4
cmp b16 $r6 0x10
bra l #memx_func_train_loop_inner
pop $r5
add b32 $r5 1
cmp b16 $r5 7
bra l #memx_func_train_loop_outer
#endif
ret
// description
//
// $r15 - current (memx)
// $r14 - sender process name
// $r13 - message (exec)
// $r12 - head of script
// $r11 - tail of script
// $r0 - zero
memx_exec:
push $r14
push $r13
mov b32 $r1 $r12
mov b32 $r2 $r11
memx_exec_next:
// fetch the packet header
ld b32 $r3 D[$r1]
add b32 $r1 4
extr $r4 $r3 16:31
extr $r3 $r3 0:15
// execute the opcode handler
sub b32 $r3 1
mulu $r3 #memx_func_size
ld b32 $r5 D[$r3 + #memx_func_head + #memx_func]
call $r5
// keep going, if we haven't reached the end
cmp b32 $r1 $r2
bra l #memx_exec_next
// send completion reply
ld b32 $r11 D[$r0 + #memx_ts_start]
ld b32 $r12 D[$r0 + #memx_ts_end]
sub b32 $r12 $r11
nv_iord($r11, NV_PPWR_INPUT)
pop $r13
pop $r14
call(send)
ret
// description
//
// $r15 - current (memx)
// $r14 - sender process name
// $r13 - message
// $r12 - data0
// $r11 - data1
// $r0 - zero
memx_info:
cmp b16 $r12 0x1
bra e #memx_info_train
memx_info_data:
mov $r12 #memx_data_head
mov $r11 #memx_data_tail - #memx_data_head
bra #memx_info_send
memx_info_train:
mov $r12 #memx_train_head
mov $r11 #memx_train_tail - #memx_train_head
memx_info_send:
call(send)
ret
// description
//
// $r15 - current (memx)
// $r14 - sender process name
// $r13 - message
// $r12 - data0
// $r11 - data1
// $r0 - zero
memx_recv:
cmp b32 $r13 MEMX_MSG_EXEC
bra e #memx_exec
cmp b32 $r13 MEMX_MSG_INFO
bra e #memx_info
ret
// description
//
// $r15 - current (memx)
// $r0 - zero
memx_init:
ret
#endif
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVKM_PWR_OS_H__
#define __NVKM_PWR_OS_H__
/* Process names */
#define PROC_KERN 0x52544e49
#define PROC_IDLE 0x454c4449
#define PROC_HOST 0x54534f48
#define PROC_MEMX 0x584d454d
#define PROC_PERF 0x46524550
#define PROC_I2C_ 0x5f433249
#define PROC_TEST 0x54534554
/* KERN: message identifiers */
#define KMSG_FIFO 0x00000000
#define KMSG_ALARM 0x00000001
/* MEMX: message identifiers */
#define MEMX_MSG_INFO 0
#define MEMX_MSG_EXEC 1
/* MEMX: info types */
#define MEMX_INFO_DATA 0
#define MEMX_INFO_TRAIN 1
/* MEMX: script opcode definitions */
#define MEMX_ENTER 1
#define MEMX_LEAVE 2
#define MEMX_WR32 3
#define MEMX_WAIT 4
#define MEMX_DELAY 5
#define MEMX_VBLANK 6
#define MEMX_TRAIN 7
/* I2C_: message identifiers */
#define I2C__MSG_RD08 0
#define I2C__MSG_WR08 1
#define I2C__MSG_DATA0_PORT 24:31
#define I2C__MSG_DATA0_ADDR 14:23
#define I2C__MSG_DATA0_RD08_PORT I2C__MSG_DATA0_PORT
#define I2C__MSG_DATA0_RD08_ADDR I2C__MSG_DATA0_ADDR
#define I2C__MSG_DATA0_RD08_REG 0:7
#define I2C__MSG_DATA1_RD08_VAL 0:7
#define I2C__MSG_DATA0_WR08_PORT I2C__MSG_DATA0_PORT
#define I2C__MSG_DATA0_WR08_ADDR I2C__MSG_DATA0_ADDR
#define I2C__MSG_DATA0_WR08_SYNC 8:8
#define I2C__MSG_DATA0_WR08_REG 0:7
#define I2C__MSG_DATA1_WR08_VAL 0:7
#endif
@@ -0,0 +1,57 @@
/*
* 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
*/
#ifdef INCLUDE_PROC
process(PROC_PERF, #perf_init, #perf_recv)
#endif
/******************************************************************************
* PERF data segment
*****************************************************************************/
#ifdef INCLUDE_DATA
#endif
/******************************************************************************
* PERF code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
// description
//
// $r15 - current (perf)
// $r14 - sender process name
// $r13 - message
// $r12 - data0
// $r11 - data1
// $r0 - zero
perf_recv:
ret
// description
//
// $r15 - current (perf)
// $r0 - zero
perf_init:
ret
#endif
@@ -0,0 +1,63 @@
/*
* 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
*/
#ifdef INCLUDE_PROC
process(PROC_TEST, #test_init, #test_recv)
#endif
/******************************************************************************
* TEST data segment
*****************************************************************************/
#ifdef INCLUDE_DATA
#endif
/******************************************************************************
* TEST code segment
*****************************************************************************/
#ifdef INCLUDE_CODE
// description
//
// $r15 - current (test)
// $r14 - sender process name
// $r13 - message
// $r12 - data0
// $r11 - data1
// $r0 - zero
test_recv:
nv_iord($r1, NV_PPWR_DSCRATCH(2))
add b32 $r1 1
nv_iowr(NV_PPWR_DSCRATCH(2), $r1)
imm32($r14, 0x134fd900)
call(timer)
ret
// description
//
// $r15 - current (test)
// $r0 - zero
test_init:
mov $r14 0x800
call(timer)
ret
#endif
@@ -0,0 +1,76 @@
/*
* 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
*/
#include "priv.h"
#include "fuc/gf100.fuc3.h"
#include <subdev/mc.h>
void
gf100_pmu_reset(struct nvkm_pmu *pmu)
{
struct nvkm_device *device = pmu->subdev.device;
nvkm_mc_disable(device, NVKM_SUBDEV_PMU, 0);
nvkm_mc_enable(device, NVKM_SUBDEV_PMU, 0);
}
bool
gf100_pmu_enabled(struct nvkm_pmu *pmu)
{
return nvkm_mc_enabled(pmu->subdev.device, NVKM_SUBDEV_PMU, 0);
}
static const struct nvkm_pmu_func
gf100_pmu = {
.flcn = &gt215_pmu_flcn,
.code.data = gf100_pmu_code,
.code.size = sizeof(gf100_pmu_code),
.data.data = gf100_pmu_data,
.data.size = sizeof(gf100_pmu_data),
.enabled = gf100_pmu_enabled,
.reset = gf100_pmu_reset,
.init = gt215_pmu_init,
.fini = gt215_pmu_fini,
.intr = gt215_pmu_intr,
.send = gt215_pmu_send,
.recv = gt215_pmu_recv,
};
int
gf100_pmu_nofw(struct nvkm_pmu *pmu, int ver, const struct nvkm_pmu_fwif *fwif)
{
return 0;
}
static const struct nvkm_pmu_fwif
gf100_pmu_fwif[] = {
{ -1, gf100_pmu_nofw, &gf100_pmu },
{}
};
int
gf100_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gf100_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,54 @@
/*
* 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
*/
#include "priv.h"
#include "fuc/gf119.fuc4.h"
static const struct nvkm_pmu_func
gf119_pmu = {
.flcn = &gt215_pmu_flcn,
.code.data = gf119_pmu_code,
.code.size = sizeof(gf119_pmu_code),
.data.data = gf119_pmu_data,
.data.size = sizeof(gf119_pmu_data),
.enabled = gf100_pmu_enabled,
.reset = gf100_pmu_reset,
.init = gt215_pmu_init,
.fini = gt215_pmu_fini,
.intr = gt215_pmu_intr,
.send = gt215_pmu_send,
.recv = gt215_pmu_recv,
};
static const struct nvkm_pmu_fwif
gf119_pmu_fwif[] = {
{ -1, gf100_pmu_nofw, &gf119_pmu },
{}
};
int
gf119_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gf119_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,134 @@
/*
* 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
*/
#define gf119_pmu_code gk104_pmu_code
#define gf119_pmu_data gk104_pmu_data
#include "priv.h"
#include "fuc/gf119.fuc4.h"
#include <core/option.h>
#include <subdev/fuse.h>
#include <subdev/timer.h>
static void
magic_(struct nvkm_device *device, u32 ctrl, int size)
{
nvkm_wr32(device, 0x00c800, 0x00000000);
nvkm_wr32(device, 0x00c808, 0x00000000);
nvkm_wr32(device, 0x00c800, ctrl);
nvkm_msec(device, 2000,
if (nvkm_rd32(device, 0x00c800) & 0x40000000) {
while (size--)
nvkm_wr32(device, 0x00c804, 0x00000000);
break;
}
);
nvkm_wr32(device, 0x00c800, 0x00000000);
}
static void
magic(struct nvkm_device *device, u32 ctrl)
{
magic_(device, 0x8000a41f | ctrl, 6);
magic_(device, 0x80000421 | ctrl, 1);
}
static void
gk104_pmu_pgob(struct nvkm_pmu *pmu, bool enable)
{
struct nvkm_device *device = pmu->subdev.device;
if (!(nvkm_fuse_read(device->fuse, 0x31c) & 0x00000001))
return;
nvkm_mask(device, 0x000200, 0x00001000, 0x00000000);
nvkm_rd32(device, 0x000200);
nvkm_mask(device, 0x000200, 0x08000000, 0x08000000);
msleep(50);
nvkm_mask(device, 0x10a78c, 0x00000002, 0x00000002);
nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000001);
nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000000);
nvkm_mask(device, 0x020004, 0xc0000000, enable ? 0xc0000000 : 0x40000000);
msleep(50);
nvkm_mask(device, 0x10a78c, 0x00000002, 0x00000000);
nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000001);
nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000000);
nvkm_mask(device, 0x000200, 0x08000000, 0x00000000);
nvkm_mask(device, 0x000200, 0x00001000, 0x00001000);
nvkm_rd32(device, 0x000200);
if (nvkm_boolopt(device->cfgopt, "War00C800_0", true)) {
switch (device->chipset) {
case 0xe4:
magic(device, 0x04000000);
magic(device, 0x06000000);
magic(device, 0x0c000000);
magic(device, 0x0e000000);
break;
case 0xe6:
magic(device, 0x02000000);
magic(device, 0x04000000);
magic(device, 0x0a000000);
break;
case 0xe7:
magic(device, 0x02000000);
break;
default:
break;
}
}
}
static const struct nvkm_pmu_func
gk104_pmu = {
.flcn = &gt215_pmu_flcn,
.code.data = gk104_pmu_code,
.code.size = sizeof(gk104_pmu_code),
.data.data = gk104_pmu_data,
.data.size = sizeof(gk104_pmu_data),
.enabled = gf100_pmu_enabled,
.reset = gf100_pmu_reset,
.init = gt215_pmu_init,
.fini = gt215_pmu_fini,
.intr = gt215_pmu_intr,
.send = gt215_pmu_send,
.recv = gt215_pmu_recv,
.pgob = gk104_pmu_pgob,
};
static const struct nvkm_pmu_fwif
gk104_pmu_fwif[] = {
{ -1, gf100_pmu_nofw, &gk104_pmu },
{}
};
int
gk104_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gk104_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,113 @@
/*
* Copyright 2015 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
*/
#define gf119_pmu_code gk110_pmu_code
#define gf119_pmu_data gk110_pmu_data
#include "priv.h"
#include "fuc/gf119.fuc4.h"
#include <subdev/timer.h>
void
gk110_pmu_pgob(struct nvkm_pmu *pmu, bool enable)
{
struct nvkm_device *device = pmu->subdev.device;
static const struct {
u32 addr;
u32 data;
} magic[] = {
{ 0x020520, 0xfffffffc },
{ 0x020524, 0xfffffffe },
{ 0x020524, 0xfffffffc },
{ 0x020524, 0xfffffff8 },
{ 0x020524, 0xffffffe0 },
{ 0x020530, 0xfffffffe },
{ 0x02052c, 0xfffffffa },
{ 0x02052c, 0xfffffff0 },
{ 0x02052c, 0xffffffc0 },
{ 0x02052c, 0xffffff00 },
{ 0x02052c, 0xfffffc00 },
{ 0x02052c, 0xfffcfc00 },
{ 0x02052c, 0xfff0fc00 },
{ 0x02052c, 0xff80fc00 },
{ 0x020528, 0xfffffffe },
{ 0x020528, 0xfffffffc },
};
int i;
nvkm_mask(device, 0x000200, 0x00001000, 0x00000000);
nvkm_rd32(device, 0x000200);
nvkm_mask(device, 0x000200, 0x08000000, 0x08000000);
msleep(50);
nvkm_mask(device, 0x10a78c, 0x00000002, 0x00000002);
nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000001);
nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000000);
nvkm_mask(device, 0x0206b4, 0x00000000, 0x00000000);
for (i = 0; i < ARRAY_SIZE(magic); i++) {
nvkm_wr32(device, magic[i].addr, magic[i].data);
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, magic[i].addr) & 0x80000000))
break;
);
}
nvkm_mask(device, 0x10a78c, 0x00000002, 0x00000000);
nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000001);
nvkm_mask(device, 0x10a78c, 0x00000001, 0x00000000);
nvkm_mask(device, 0x000200, 0x08000000, 0x00000000);
nvkm_mask(device, 0x000200, 0x00001000, 0x00001000);
nvkm_rd32(device, 0x000200);
}
static const struct nvkm_pmu_func
gk110_pmu = {
.flcn = &gt215_pmu_flcn,
.code.data = gk110_pmu_code,
.code.size = sizeof(gk110_pmu_code),
.data.data = gk110_pmu_data,
.data.size = sizeof(gk110_pmu_data),
.enabled = gf100_pmu_enabled,
.reset = gf100_pmu_reset,
.init = gt215_pmu_init,
.fini = gt215_pmu_fini,
.intr = gt215_pmu_intr,
.send = gt215_pmu_send,
.recv = gt215_pmu_recv,
.pgob = gk110_pmu_pgob,
};
static const struct nvkm_pmu_fwif
gk110_pmu_fwif[] = {
{ -1, gf100_pmu_nofw, &gk110_pmu },
{}
};
int
gk110_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gk110_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,55 @@
/*
* 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
*/
#include "priv.h"
#include "fuc/gk208.fuc5.h"
static const struct nvkm_pmu_func
gk208_pmu = {
.flcn = &gt215_pmu_flcn,
.code.data = gk208_pmu_code,
.code.size = sizeof(gk208_pmu_code),
.data.data = gk208_pmu_data,
.data.size = sizeof(gk208_pmu_data),
.enabled = gf100_pmu_enabled,
.reset = gf100_pmu_reset,
.init = gt215_pmu_init,
.fini = gt215_pmu_fini,
.intr = gt215_pmu_intr,
.send = gt215_pmu_send,
.recv = gt215_pmu_recv,
.pgob = gk110_pmu_pgob,
};
static const struct nvkm_pmu_fwif
gk208_pmu_fwif[] = {
{ -1, gf100_pmu_nofw, &gk208_pmu },
{}
};
int
gk208_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gk208_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,229 @@
/*
* Copyright (c) 2014, 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.
*/
#define gk20a_pmu(p) container_of((p), struct gk20a_pmu, base)
#include "priv.h"
#include <subdev/clk.h>
#include <subdev/timer.h>
#include <subdev/volt.h>
#define BUSY_SLOT 0
#define CLK_SLOT 7
struct gk20a_pmu_dvfs_data {
int p_load_target;
int p_load_max;
int p_smooth;
unsigned int avg_load;
};
struct gk20a_pmu {
struct nvkm_pmu base;
struct nvkm_alarm alarm;
struct gk20a_pmu_dvfs_data *data;
};
struct gk20a_pmu_dvfs_dev_status {
u32 total;
u32 busy;
};
static int
gk20a_pmu_dvfs_target(struct gk20a_pmu *pmu, int *state)
{
struct nvkm_clk *clk = pmu->base.subdev.device->clk;
return nvkm_clk_astate(clk, *state, 0, false);
}
static void
gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu *pmu, int *state)
{
struct nvkm_clk *clk = pmu->base.subdev.device->clk;
*state = clk->pstate;
}
static int
gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu *pmu,
int *state, int load)
{
struct gk20a_pmu_dvfs_data *data = pmu->data;
struct nvkm_clk *clk = pmu->base.subdev.device->clk;
int cur_level, level;
/* For GK20A, the performance level is directly mapped to pstate */
level = cur_level = clk->pstate;
if (load > data->p_load_max) {
level = min(clk->state_nr - 1, level + (clk->state_nr / 3));
} else {
level += ((load - data->p_load_target) * 10 /
data->p_load_target) / 2;
level = max(0, level);
level = min(clk->state_nr - 1, level);
}
nvkm_trace(&pmu->base.subdev, "cur level = %d, new level = %d\n",
cur_level, level);
*state = level;
return (level != cur_level);
}
static void
gk20a_pmu_dvfs_get_dev_status(struct gk20a_pmu *pmu,
struct gk20a_pmu_dvfs_dev_status *status)
{
struct nvkm_falcon *falcon = &pmu->base.falcon;
status->busy = nvkm_falcon_rd32(falcon, 0x508 + (BUSY_SLOT * 0x10));
status->total= nvkm_falcon_rd32(falcon, 0x508 + (CLK_SLOT * 0x10));
}
static void
gk20a_pmu_dvfs_reset_dev_status(struct gk20a_pmu *pmu)
{
struct nvkm_falcon *falcon = &pmu->base.falcon;
nvkm_falcon_wr32(falcon, 0x508 + (BUSY_SLOT * 0x10), 0x80000000);
nvkm_falcon_wr32(falcon, 0x508 + (CLK_SLOT * 0x10), 0x80000000);
}
static void
gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm)
{
struct gk20a_pmu *pmu =
container_of(alarm, struct gk20a_pmu, alarm);
struct gk20a_pmu_dvfs_data *data = pmu->data;
struct gk20a_pmu_dvfs_dev_status status;
struct nvkm_subdev *subdev = &pmu->base.subdev;
struct nvkm_device *device = subdev->device;
struct nvkm_clk *clk = device->clk;
struct nvkm_timer *tmr = device->timer;
struct nvkm_volt *volt = device->volt;
u32 utilization = 0;
int state;
/*
* The PMU is initialized before CLK and VOLT, so we have to make sure the
* CLK and VOLT are ready here.
*/
if (!clk || !volt)
goto resched;
gk20a_pmu_dvfs_get_dev_status(pmu, &status);
if (status.total)
utilization = div_u64((u64)status.busy * 100, status.total);
data->avg_load = (data->p_smooth * data->avg_load) + utilization;
data->avg_load /= data->p_smooth + 1;
nvkm_trace(subdev, "utilization = %d %%, avg_load = %d %%\n",
utilization, data->avg_load);
gk20a_pmu_dvfs_get_cur_state(pmu, &state);
if (gk20a_pmu_dvfs_get_target_state(pmu, &state, data->avg_load)) {
nvkm_trace(subdev, "set new state to %d\n", state);
gk20a_pmu_dvfs_target(pmu, &state);
}
resched:
gk20a_pmu_dvfs_reset_dev_status(pmu);
nvkm_timer_alarm(tmr, 100000000, alarm);
}
static void
gk20a_pmu_fini(struct nvkm_pmu *pmu)
{
struct gk20a_pmu *gpmu = gk20a_pmu(pmu);
nvkm_timer_alarm(pmu->subdev.device->timer, 0, &gpmu->alarm);
nvkm_falcon_put(&pmu->falcon, &pmu->subdev);
}
static int
gk20a_pmu_init(struct nvkm_pmu *pmu)
{
struct gk20a_pmu *gpmu = gk20a_pmu(pmu);
struct nvkm_subdev *subdev = &pmu->subdev;
struct nvkm_device *device = pmu->subdev.device;
struct nvkm_falcon *falcon = &pmu->falcon;
int ret;
ret = nvkm_falcon_get(falcon, subdev);
if (ret) {
nvkm_error(subdev, "cannot acquire %s falcon!\n", falcon->name);
return ret;
}
/* init pwr perf counter */
nvkm_falcon_wr32(falcon, 0x504 + (BUSY_SLOT * 0x10), 0x00200001);
nvkm_falcon_wr32(falcon, 0x50c + (BUSY_SLOT * 0x10), 0x00000002);
nvkm_falcon_wr32(falcon, 0x50c + (CLK_SLOT * 0x10), 0x00000003);
nvkm_timer_alarm(device->timer, 2000000000, &gpmu->alarm);
return 0;
}
static struct gk20a_pmu_dvfs_data
gk20a_dvfs_data= {
.p_load_target = 70,
.p_load_max = 90,
.p_smooth = 1,
};
static const struct nvkm_pmu_func
gk20a_pmu = {
.flcn = &gt215_pmu_flcn,
.init = gk20a_pmu_init,
.fini = gk20a_pmu_fini,
.reset = gf100_pmu_reset,
};
static const struct nvkm_pmu_fwif
gk20a_pmu_fwif[] = {
{ -1, gf100_pmu_nofw, &gk20a_pmu },
{}
};
int
gk20a_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
struct gk20a_pmu *pmu;
int ret;
if (!(pmu = kzalloc_obj(*pmu)))
return -ENOMEM;
*ppmu = &pmu->base;
ret = nvkm_pmu_ctor(gk20a_pmu_fwif, device, type, inst, &pmu->base);
if (ret)
return ret;
pmu->data = &gk20a_dvfs_data;
nvkm_alarm_init(&pmu->alarm, gk20a_pmu_dvfs_work);
return 0;
}
@@ -0,0 +1,56 @@
/*
* 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
*/
#include "priv.h"
#define gk208_pmu_code gm107_pmu_code
#define gk208_pmu_data gm107_pmu_data
#include "fuc/gk208.fuc5.h"
static const struct nvkm_pmu_func
gm107_pmu = {
.flcn = &gt215_pmu_flcn,
.code.data = gm107_pmu_code,
.code.size = sizeof(gm107_pmu_code),
.data.data = gm107_pmu_data,
.data.size = sizeof(gm107_pmu_data),
.enabled = gf100_pmu_enabled,
.reset = gf100_pmu_reset,
.init = gt215_pmu_init,
.fini = gt215_pmu_fini,
.intr = gt215_pmu_intr,
.send = gt215_pmu_send,
.recv = gt215_pmu_recv,
};
static const struct nvkm_pmu_fwif
gm107_pmu_fwif[] = {
{ -1, gf100_pmu_nofw, &gm107_pmu },
{}
};
int
gm107_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gm107_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,85 @@
/*
* Copyright 2016 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 <bskeggs@redhat.com>
*/
#include "priv.h"
static int
gm200_pmu_flcn_bind_stat(struct nvkm_falcon *falcon, bool intr)
{
nvkm_falcon_wr32(falcon, 0x200, 0x0000030e);
return (nvkm_falcon_rd32(falcon, 0x20c) & 0x00007000) >> 12;
}
void
gm200_pmu_flcn_bind_inst(struct nvkm_falcon *falcon, int target, u64 addr)
{
nvkm_falcon_wr32(falcon, 0xe00, 4); /* DMAIDX_UCODE */
nvkm_falcon_wr32(falcon, 0xe04, 0); /* DMAIDX_VIRT */
nvkm_falcon_wr32(falcon, 0xe08, 4); /* DMAIDX_PHYS_VID */
nvkm_falcon_wr32(falcon, 0xe0c, 5); /* DMAIDX_PHYS_SYS_COH */
nvkm_falcon_wr32(falcon, 0xe10, 6); /* DMAIDX_PHYS_SYS_NCOH */
nvkm_falcon_mask(falcon, 0x090, 0x00010000, 0x00010000);
nvkm_falcon_wr32(falcon, 0x480, (1 << 30) | (target << 28) | (addr >> 12));
}
const struct nvkm_falcon_func
gm200_pmu_flcn = {
.disable = gm200_flcn_disable,
.enable = gm200_flcn_enable,
.reset_pmc = true,
.reset_wait_mem_scrubbing = gm200_flcn_reset_wait_mem_scrubbing,
.debug = 0xc08,
.bind_inst = gm200_pmu_flcn_bind_inst,
.bind_stat = gm200_pmu_flcn_bind_stat,
.imem_pio = &gm200_flcn_imem_pio,
.dmem_pio = &gm200_flcn_dmem_pio,
.start = nvkm_falcon_v1_start,
.cmdq = { 0x4a0, 0x4b0, 4 },
.msgq = { 0x4c8, 0x4cc, 0 },
};
static const struct nvkm_pmu_func
gm200_pmu = {
.flcn = &gm200_pmu_flcn,
.reset = gf100_pmu_reset,
};
int
gm200_pmu_nofw(struct nvkm_pmu *pmu, int ver, const struct nvkm_pmu_fwif *fwif)
{
nvkm_warn(&pmu->subdev, "firmware unavailable\n");
return 0;
}
static const struct nvkm_pmu_fwif
gm200_pmu_fwif[] = {
{ -1, gm200_pmu_nofw, &gm200_pmu },
{}
};
int
gm200_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gm200_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,270 @@
/*
* 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 "priv.h"
#include <core/memory.h>
#include <subdev/acr.h>
#include <nvfw/flcn.h>
#include <nvfw/pmu.h>
static int
gm20b_pmu_acr_bootstrap_falcon_cb(void *priv, struct nvfw_falcon_msg *hdr)
{
struct nv_pmu_acr_bootstrap_falcon_msg *msg =
container_of(hdr, typeof(*msg), msg.hdr);
return msg->falcon_id;
}
int
gm20b_pmu_acr_bootstrap_falcon(struct nvkm_falcon *falcon,
enum nvkm_acr_lsf_id id)
{
struct nvkm_pmu *pmu = container_of(falcon, typeof(*pmu), falcon);
struct nv_pmu_acr_bootstrap_falcon_cmd cmd = {
.cmd.hdr.unit_id = NV_PMU_UNIT_ACR,
.cmd.hdr.size = sizeof(cmd),
.cmd.cmd_type = NV_PMU_ACR_CMD_BOOTSTRAP_FALCON,
.flags = NV_PMU_ACR_BOOTSTRAP_FALCON_FLAGS_RESET_YES,
.falcon_id = id,
};
int ret;
ret = nvkm_falcon_cmdq_send(pmu->hpq, &cmd.cmd.hdr,
gm20b_pmu_acr_bootstrap_falcon_cb,
&pmu->subdev, msecs_to_jiffies(1000));
if (ret >= 0) {
if (ret != cmd.falcon_id)
ret = -EIO;
else
ret = 0;
}
return ret;
}
void
gm20b_pmu_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust)
{
struct loader_config hdr;
u64 addr;
nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr));
addr = ((u64)hdr.code_dma_base1 << 40 | hdr.code_dma_base << 8);
hdr.code_dma_base = lower_32_bits((addr + adjust) >> 8);
hdr.code_dma_base1 = upper_32_bits((addr + adjust) >> 8);
addr = ((u64)hdr.data_dma_base1 << 40 | hdr.data_dma_base << 8);
hdr.data_dma_base = lower_32_bits((addr + adjust) >> 8);
hdr.data_dma_base1 = upper_32_bits((addr + adjust) >> 8);
addr = ((u64)hdr.overlay_dma_base1 << 40 | hdr.overlay_dma_base << 8);
hdr.overlay_dma_base = lower_32_bits((addr + adjust) << 8);
hdr.overlay_dma_base1 = upper_32_bits((addr + adjust) << 8);
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
loader_config_dump(&acr->subdev, &hdr);
}
void
gm20b_pmu_acr_bld_write(struct nvkm_acr *acr, u32 bld,
struct nvkm_acr_lsfw *lsfw)
{
const u64 base = lsfw->offset.img + lsfw->app_start_offset;
const u64 code = (base + lsfw->app_resident_code_offset) >> 8;
const u64 data = (base + lsfw->app_resident_data_offset) >> 8;
const struct loader_config hdr = {
.dma_idx = FALCON_DMAIDX_UCODE,
.code_dma_base = lower_32_bits(code),
.code_size_total = lsfw->app_size,
.code_size_to_load = lsfw->app_resident_code_size,
.code_entry_point = lsfw->app_imem_entry,
.data_dma_base = lower_32_bits(data),
.data_size = lsfw->app_resident_data_size,
.overlay_dma_base = lower_32_bits(code),
.argc = 1,
.argv = lsfw->falcon->data.limit - sizeof(struct nv_pmu_args),
.code_dma_base1 = upper_32_bits(code),
.data_dma_base1 = upper_32_bits(data),
.overlay_dma_base1 = upper_32_bits(code),
};
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
}
static const struct nvkm_acr_lsf_func
gm20b_pmu_acr = {
.flags = NVKM_ACR_LSF_DMACTL_REQ_CTX,
.bld_size = sizeof(struct loader_config),
.bld_write = gm20b_pmu_acr_bld_write,
.bld_patch = gm20b_pmu_acr_bld_patch,
.bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_PMU) |
BIT_ULL(NVKM_ACR_LSF_FECS) |
BIT_ULL(NVKM_ACR_LSF_GPCCS),
.bootstrap_falcon = gm20b_pmu_acr_bootstrap_falcon,
};
static int
gm20b_pmu_acr_init_wpr_callback(void *priv, struct nvfw_falcon_msg *hdr)
{
struct nv_pmu_acr_init_wpr_region_msg *msg =
container_of(hdr, typeof(*msg), msg.hdr);
struct nvkm_pmu *pmu = priv;
struct nvkm_subdev *subdev = &pmu->subdev;
if (msg->error_code) {
nvkm_error(subdev, "ACR WPR init failure: %d\n",
msg->error_code);
return -EINVAL;
}
nvkm_debug(subdev, "ACR WPR init complete\n");
complete_all(&pmu->wpr_ready);
return 0;
}
static int
gm20b_pmu_acr_init_wpr(struct nvkm_pmu *pmu)
{
struct nv_pmu_acr_init_wpr_region_cmd cmd = {
.cmd.hdr.unit_id = NV_PMU_UNIT_ACR,
.cmd.hdr.size = sizeof(cmd),
.cmd.cmd_type = NV_PMU_ACR_CMD_INIT_WPR_REGION,
.region_id = 1,
.wpr_offset = 0,
};
return nvkm_falcon_cmdq_send(pmu->hpq, &cmd.cmd.hdr,
gm20b_pmu_acr_init_wpr_callback, pmu, 0);
}
static int
gm20b_pmu_initmsg(struct nvkm_pmu *pmu)
{
struct nv_pmu_init_msg msg;
int ret;
ret = nvkm_falcon_msgq_recv_initmsg(pmu->msgq, &msg, sizeof(msg));
if (ret)
return ret;
if (msg.hdr.unit_id != NV_PMU_UNIT_INIT ||
msg.msg_type != NV_PMU_INIT_MSG_INIT)
return -EINVAL;
nvkm_falcon_cmdq_init(pmu->hpq, msg.queue_info[0].index,
msg.queue_info[0].offset,
msg.queue_info[0].size);
nvkm_falcon_cmdq_init(pmu->lpq, msg.queue_info[1].index,
msg.queue_info[1].offset,
msg.queue_info[1].size);
nvkm_falcon_msgq_init(pmu->msgq, msg.queue_info[4].index,
msg.queue_info[4].offset,
msg.queue_info[4].size);
return gm20b_pmu_acr_init_wpr(pmu);
}
static void
gm20b_pmu_recv(struct nvkm_pmu *pmu)
{
if (!pmu->initmsg_received) {
int ret = pmu->func->initmsg(pmu);
if (ret) {
nvkm_error(&pmu->subdev, "error parsing init message: %d\n", ret);
return;
}
pmu->initmsg_received = true;
}
nvkm_falcon_msgq_recv(pmu->msgq);
}
static void
gm20b_pmu_fini(struct nvkm_pmu *pmu)
{
/*TODO: shutdown RTOS. */
flush_work(&pmu->recv.work);
nvkm_falcon_cmdq_fini(pmu->lpq);
nvkm_falcon_cmdq_fini(pmu->hpq);
reinit_completion(&pmu->wpr_ready);
nvkm_falcon_put(&pmu->falcon, &pmu->subdev);
}
static int
gm20b_pmu_init(struct nvkm_pmu *pmu)
{
struct nvkm_falcon *falcon = &pmu->falcon;
struct nv_pmu_args args = { .secure_mode = true };
u32 addr_args = falcon->data.limit - sizeof(args);
int ret;
ret = nvkm_falcon_get(&pmu->falcon, &pmu->subdev);
if (ret)
return ret;
pmu->initmsg_received = false;
nvkm_falcon_pio_wr(falcon, (u8 *)&args, 0, 0, DMEM, addr_args, sizeof(args), 0, false);
nvkm_falcon_start(falcon);
return 0;
}
const struct nvkm_pmu_func
gm20b_pmu = {
.flcn = &gm200_pmu_flcn,
.init = gm20b_pmu_init,
.fini = gm20b_pmu_fini,
.intr = gt215_pmu_intr,
.recv = gm20b_pmu_recv,
.initmsg = gm20b_pmu_initmsg,
.reset = gf100_pmu_reset,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
MODULE_FIRMWARE("nvidia/gm20b/pmu/desc.bin");
MODULE_FIRMWARE("nvidia/gm20b/pmu/image.bin");
MODULE_FIRMWARE("nvidia/gm20b/pmu/sig.bin");
#endif
int
gm20b_pmu_load(struct nvkm_pmu *pmu, int ver, const struct nvkm_pmu_fwif *fwif)
{
return nvkm_acr_lsfw_load_sig_image_desc(&pmu->subdev, &pmu->falcon,
NVKM_ACR_LSF_PMU, "pmu/",
ver, fwif->acr);
}
static const struct nvkm_pmu_fwif
gm20b_pmu_fwif[] = {
{ 0, gm20b_pmu_load, &gm20b_pmu, &gm20b_pmu_acr },
{ -1, gm200_pmu_nofw, &gm20b_pmu },
{}
};
int
gm20b_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gm20b_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,63 @@
/*
* Copyright 2016 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 <bskeggs@redhat.com>
*/
#include "priv.h"
#include <subdev/gsp.h>
static const struct nvkm_falcon_func
gp102_pmu_flcn = {
.disable = gm200_flcn_disable,
.enable = gm200_flcn_enable,
.reset_eng = gp102_flcn_reset_eng,
.reset_wait_mem_scrubbing = gm200_flcn_reset_wait_mem_scrubbing,
.debug = 0xc08,
.bind_inst = gm200_pmu_flcn_bind_inst,
.bind_stat = gm200_flcn_bind_stat,
.imem_pio = &gm200_flcn_imem_pio,
.dmem_pio = &gm200_flcn_dmem_pio,
.start = nvkm_falcon_v1_start,
.cmdq = { 0x4a0, 0x4b0, 4 },
.msgq = { 0x4c8, 0x4cc, 0 },
};
static const struct nvkm_pmu_func
gp102_pmu = {
.flcn = &gp102_pmu_flcn,
};
static const struct nvkm_pmu_fwif
gp102_pmu_fwif[] = {
{ -1, gm200_pmu_nofw, &gp102_pmu },
{}
};
int
gp102_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
if (nvkm_gsp_rm(device->gsp))
return -ENODEV;
return nvkm_pmu_new_(gp102_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,96 @@
/*
* 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 "priv.h"
#include <subdev/acr.h>
#include <nvfw/flcn.h>
#include <nvfw/pmu.h>
static int
gp10b_pmu_acr_bootstrap_multiple_falcons_cb(void *priv,
struct nvfw_falcon_msg *hdr)
{
struct nv_pmu_acr_bootstrap_multiple_falcons_msg *msg =
container_of(hdr, typeof(*msg), msg.hdr);
return msg->falcon_mask;
}
static int
gp10b_pmu_acr_bootstrap_multiple_falcons(struct nvkm_falcon *falcon, u32 mask)
{
struct nvkm_pmu *pmu = container_of(falcon, typeof(*pmu), falcon);
struct nv_pmu_acr_bootstrap_multiple_falcons_cmd cmd = {
.cmd.hdr.unit_id = NV_PMU_UNIT_ACR,
.cmd.hdr.size = sizeof(cmd),
.cmd.cmd_type = NV_PMU_ACR_CMD_BOOTSTRAP_MULTIPLE_FALCONS,
.flags = NV_PMU_ACR_BOOTSTRAP_MULTIPLE_FALCONS_FLAGS_RESET_YES,
.falcon_mask = mask,
.wpr_lo = 0, /*XXX*/
.wpr_hi = 0, /*XXX*/
};
int ret;
ret = nvkm_falcon_cmdq_send(pmu->hpq, &cmd.cmd.hdr,
gp10b_pmu_acr_bootstrap_multiple_falcons_cb,
&pmu->subdev, msecs_to_jiffies(1000));
if (ret >= 0) {
if (ret != cmd.falcon_mask)
ret = -EIO;
else
ret = 0;
}
return ret;
}
static const struct nvkm_acr_lsf_func
gp10b_pmu_acr = {
.flags = NVKM_ACR_LSF_DMACTL_REQ_CTX,
.bld_size = sizeof(struct loader_config),
.bld_write = gm20b_pmu_acr_bld_write,
.bld_patch = gm20b_pmu_acr_bld_patch,
.bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_PMU) |
BIT_ULL(NVKM_ACR_LSF_FECS) |
BIT_ULL(NVKM_ACR_LSF_GPCCS),
.bootstrap_falcon = gm20b_pmu_acr_bootstrap_falcon,
.bootstrap_multiple_falcons = gp10b_pmu_acr_bootstrap_multiple_falcons,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
MODULE_FIRMWARE("nvidia/gp10b/pmu/desc.bin");
MODULE_FIRMWARE("nvidia/gp10b/pmu/image.bin");
MODULE_FIRMWARE("nvidia/gp10b/pmu/sig.bin");
#endif
static const struct nvkm_pmu_fwif
gp10b_pmu_fwif[] = {
{ 0, gm20b_pmu_load, &gm20b_pmu, &gp10b_pmu_acr },
{ -1, gm200_pmu_nofw, &gm20b_pmu },
{}
};
int
gp10b_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gp10b_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,294 @@
/*
* 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
*/
#include "priv.h"
#include "fuc/gt215.fuc3.h"
#include <subdev/timer.h>
int
gt215_pmu_send(struct nvkm_pmu *pmu, u32 reply[2],
u32 process, u32 message, u32 data0, u32 data1)
{
struct nvkm_subdev *subdev = &pmu->subdev;
struct nvkm_device *device = subdev->device;
u32 addr;
mutex_lock(&pmu->send.mutex);
/* wait for a free slot in the fifo */
addr = nvkm_rd32(device, 0x10a4a0);
if (nvkm_msec(device, 2000,
u32 tmp = nvkm_rd32(device, 0x10a4b0);
if (tmp != (addr ^ 8))
break;
) < 0) {
mutex_unlock(&pmu->send.mutex);
return -EBUSY;
}
/* we currently only support a single process at a time waiting
* on a synchronous reply, take the PMU mutex and tell the
* receive handler what we're waiting for
*/
if (reply) {
pmu->recv.message = message;
pmu->recv.process = process;
}
/* acquire data segment access */
do {
nvkm_wr32(device, 0x10a580, 0x00000001);
} while (nvkm_rd32(device, 0x10a580) != 0x00000001);
/* write the packet */
nvkm_wr32(device, 0x10a1c0, 0x01000000 | (((addr & 0x07) << 4) +
pmu->send.base));
nvkm_wr32(device, 0x10a1c4, process);
nvkm_wr32(device, 0x10a1c4, message);
nvkm_wr32(device, 0x10a1c4, data0);
nvkm_wr32(device, 0x10a1c4, data1);
nvkm_wr32(device, 0x10a4a0, (addr + 1) & 0x0f);
/* release data segment access */
nvkm_wr32(device, 0x10a580, 0x00000000);
/* wait for reply, if requested */
if (reply) {
wait_event(pmu->recv.wait, (pmu->recv.process == 0));
reply[0] = pmu->recv.data[0];
reply[1] = pmu->recv.data[1];
}
mutex_unlock(&pmu->send.mutex);
return 0;
}
void
gt215_pmu_recv(struct nvkm_pmu *pmu)
{
struct nvkm_subdev *subdev = &pmu->subdev;
struct nvkm_device *device = subdev->device;
u32 process, message, data0, data1;
/* nothing to do if GET == PUT */
u32 addr = nvkm_rd32(device, 0x10a4cc);
if (addr == nvkm_rd32(device, 0x10a4c8))
return;
/* acquire data segment access */
do {
nvkm_wr32(device, 0x10a580, 0x00000002);
} while (nvkm_rd32(device, 0x10a580) != 0x00000002);
/* read the packet */
nvkm_wr32(device, 0x10a1c0, 0x02000000 | (((addr & 0x07) << 4) +
pmu->recv.base));
process = nvkm_rd32(device, 0x10a1c4);
message = nvkm_rd32(device, 0x10a1c4);
data0 = nvkm_rd32(device, 0x10a1c4);
data1 = nvkm_rd32(device, 0x10a1c4);
nvkm_wr32(device, 0x10a4cc, (addr + 1) & 0x0f);
/* release data segment access */
nvkm_wr32(device, 0x10a580, 0x00000000);
/* wake process if it's waiting on a synchronous reply */
if (pmu->recv.process) {
if (process == pmu->recv.process &&
message == pmu->recv.message) {
pmu->recv.data[0] = data0;
pmu->recv.data[1] = data1;
pmu->recv.process = 0;
wake_up(&pmu->recv.wait);
return;
}
}
/* right now there's no other expected responses from the engine,
* so assume that any unexpected message is an error.
*/
nvkm_warn(subdev, "%c%c%c%c %08x %08x %08x %08x\n",
(char)((process & 0x000000ff) >> 0),
(char)((process & 0x0000ff00) >> 8),
(char)((process & 0x00ff0000) >> 16),
(char)((process & 0xff000000) >> 24),
process, message, data0, data1);
}
void
gt215_pmu_intr(struct nvkm_pmu *pmu)
{
struct nvkm_subdev *subdev = &pmu->subdev;
struct nvkm_device *device = subdev->device;
u32 disp = nvkm_rd32(device, 0x10a01c);
u32 intr = nvkm_rd32(device, 0x10a008) & disp & ~(disp >> 16);
if (intr & 0x00000020) {
u32 stat = nvkm_rd32(device, 0x10a16c);
if (stat & 0x80000000) {
nvkm_error(subdev, "UAS fault at %06x addr %08x\n",
stat & 0x00ffffff,
nvkm_rd32(device, 0x10a168));
nvkm_wr32(device, 0x10a16c, 0x00000000);
intr &= ~0x00000020;
}
}
if (intr & 0x00000040) {
schedule_work(&pmu->recv.work);
nvkm_wr32(device, 0x10a004, 0x00000040);
intr &= ~0x00000040;
}
if (intr & 0x00000080) {
nvkm_info(subdev, "wr32 %06x %08x\n",
nvkm_rd32(device, 0x10a7a0),
nvkm_rd32(device, 0x10a7a4));
nvkm_wr32(device, 0x10a004, 0x00000080);
intr &= ~0x00000080;
}
if (intr) {
nvkm_error(subdev, "intr %08x\n", intr);
nvkm_wr32(device, 0x10a004, intr);
}
}
void
gt215_pmu_fini(struct nvkm_pmu *pmu)
{
nvkm_wr32(pmu->subdev.device, 0x10a014, 0x00000060);
flush_work(&pmu->recv.work);
}
static void
gt215_pmu_reset(struct nvkm_pmu *pmu)
{
struct nvkm_device *device = pmu->subdev.device;
nvkm_mask(device, 0x022210, 0x00000001, 0x00000000);
nvkm_mask(device, 0x022210, 0x00000001, 0x00000001);
nvkm_rd32(device, 0x022210);
}
static bool
gt215_pmu_enabled(struct nvkm_pmu *pmu)
{
return nvkm_rd32(pmu->subdev.device, 0x022210) & 0x00000001;
}
int
gt215_pmu_init(struct nvkm_pmu *pmu)
{
struct nvkm_device *device = pmu->subdev.device;
int i;
/* Inhibit interrupts, and wait for idle. */
if (pmu->func->enabled(pmu)) {
nvkm_wr32(device, 0x10a014, 0x0000ffff);
nvkm_msec(device, 2000,
if (!nvkm_rd32(device, 0x10a04c))
break;
);
}
pmu->func->reset(pmu);
/* Wait for IMEM/DMEM scrubbing to be complete. */
nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x10a10c) & 0x00000006))
break;
);
/* upload data segment */
nvkm_wr32(device, 0x10a1c0, 0x01000000);
for (i = 0; i < pmu->func->data.size / 4; i++)
nvkm_wr32(device, 0x10a1c4, pmu->func->data.data[i]);
/* upload code segment */
nvkm_wr32(device, 0x10a180, 0x01000000);
for (i = 0; i < pmu->func->code.size / 4; i++) {
if ((i & 0x3f) == 0)
nvkm_wr32(device, 0x10a188, i >> 6);
nvkm_wr32(device, 0x10a184, pmu->func->code.data[i]);
}
/* start it running */
nvkm_wr32(device, 0x10a10c, 0x00000000);
nvkm_wr32(device, 0x10a104, 0x00000000);
nvkm_wr32(device, 0x10a100, 0x00000002);
/* wait for valid host->pmu ring configuration */
if (nvkm_msec(device, 2000,
if (nvkm_rd32(device, 0x10a4d0))
break;
) < 0)
return -EBUSY;
pmu->send.base = nvkm_rd32(device, 0x10a4d0) & 0x0000ffff;
pmu->send.size = nvkm_rd32(device, 0x10a4d0) >> 16;
/* wait for valid pmu->host ring configuration */
if (nvkm_msec(device, 2000,
if (nvkm_rd32(device, 0x10a4dc))
break;
) < 0)
return -EBUSY;
pmu->recv.base = nvkm_rd32(device, 0x10a4dc) & 0x0000ffff;
pmu->recv.size = nvkm_rd32(device, 0x10a4dc) >> 16;
nvkm_wr32(device, 0x10a010, 0x000000e0);
return 0;
}
const struct nvkm_falcon_func
gt215_pmu_flcn = {
};
static const struct nvkm_pmu_func
gt215_pmu = {
.flcn = &gt215_pmu_flcn,
.code.data = gt215_pmu_code,
.code.size = sizeof(gt215_pmu_code),
.data.data = gt215_pmu_data,
.data.size = sizeof(gt215_pmu_data),
.enabled = gt215_pmu_enabled,
.reset = gt215_pmu_reset,
.init = gt215_pmu_init,
.fini = gt215_pmu_fini,
.intr = gt215_pmu_intr,
.send = gt215_pmu_send,
.recv = gt215_pmu_recv,
};
static const struct nvkm_pmu_fwif
gt215_pmu_fwif[] = {
{ -1, gf100_pmu_nofw, &gt215_pmu },
{}
};
int
gt215_pmu_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_pmu **ppmu)
{
return nvkm_pmu_new_(gt215_pmu_fwif, device, type, inst, ppmu);
}
@@ -0,0 +1,204 @@
// SPDX-License-Identifier: MIT
#ifndef __NVKM_PMU_MEMX_H__
#define __NVKM_PMU_MEMX_H__
#include "priv.h"
struct nvkm_memx {
struct nvkm_pmu *pmu;
u32 base;
u32 size;
struct {
u32 mthd;
u32 size;
u32 data[64];
} c;
};
static void
memx_out(struct nvkm_memx *memx)
{
struct nvkm_device *device = memx->pmu->subdev.device;
int i;
if (memx->c.mthd) {
nvkm_wr32(device, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd);
for (i = 0; i < memx->c.size; i++)
nvkm_wr32(device, 0x10a1c4, memx->c.data[i]);
memx->c.mthd = 0;
memx->c.size = 0;
}
}
static void
memx_cmd(struct nvkm_memx *memx, u32 mthd, u32 size, u32 data[])
{
if ((memx->c.size + size >= ARRAY_SIZE(memx->c.data)) ||
(memx->c.mthd && memx->c.mthd != mthd))
memx_out(memx);
memcpy(&memx->c.data[memx->c.size], data, size * sizeof(data[0]));
memx->c.size += size;
memx->c.mthd = mthd;
}
int
nvkm_memx_init(struct nvkm_pmu *pmu, struct nvkm_memx **pmemx)
{
struct nvkm_device *device = pmu->subdev.device;
struct nvkm_memx *memx;
u32 reply[2];
int ret;
ret = nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_INFO,
MEMX_INFO_DATA, 0);
if (ret)
return ret;
memx = *pmemx = kzalloc_obj(*memx);
if (!memx)
return -ENOMEM;
memx->pmu = pmu;
memx->base = reply[0];
memx->size = reply[1];
/* acquire data segment access */
do {
nvkm_wr32(device, 0x10a580, 0x00000003);
} while (nvkm_rd32(device, 0x10a580) != 0x00000003);
nvkm_wr32(device, 0x10a1c0, 0x01000000 | memx->base);
return 0;
}
int
nvkm_memx_fini(struct nvkm_memx **pmemx, bool exec)
{
struct nvkm_memx *memx = *pmemx;
struct nvkm_pmu *pmu = memx->pmu;
struct nvkm_subdev *subdev = &pmu->subdev;
struct nvkm_device *device = subdev->device;
u32 finish, reply[2];
/* flush the cache... */
memx_out(memx);
/* release data segment access */
finish = nvkm_rd32(device, 0x10a1c0) & 0x00ffffff;
nvkm_wr32(device, 0x10a580, 0x00000000);
/* call MEMX process to execute the script, and wait for reply */
if (exec) {
nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_EXEC,
memx->base, finish);
nvkm_debug(subdev, "Exec took %uns, PMU_IN %08x\n",
reply[0], reply[1]);
}
kfree(memx);
return 0;
}
void
nvkm_memx_wr32(struct nvkm_memx *memx, u32 addr, u32 data)
{
nvkm_debug(&memx->pmu->subdev, "R[%06x] = %08x\n", addr, data);
memx_cmd(memx, MEMX_WR32, 2, (u32[]){ addr, data });
}
void
nvkm_memx_wait(struct nvkm_memx *memx,
u32 addr, u32 mask, u32 data, u32 nsec)
{
nvkm_debug(&memx->pmu->subdev, "R[%06x] & %08x == %08x, %d us\n",
addr, mask, data, nsec);
memx_cmd(memx, MEMX_WAIT, 4, (u32[]){ addr, mask, data, nsec });
memx_out(memx); /* fuc can't handle multiple */
}
void
nvkm_memx_nsec(struct nvkm_memx *memx, u32 nsec)
{
nvkm_debug(&memx->pmu->subdev, " DELAY = %d ns\n", nsec);
memx_cmd(memx, MEMX_DELAY, 1, (u32[]){ nsec });
memx_out(memx); /* fuc can't handle multiple */
}
void
nvkm_memx_wait_vblank(struct nvkm_memx *memx)
{
struct nvkm_subdev *subdev = &memx->pmu->subdev;
struct nvkm_device *device = subdev->device;
u32 heads, x, y, px = 0;
int i, head_sync;
if (device->chipset < 0xd0) {
heads = nvkm_rd32(device, 0x610050);
for (i = 0; i < 2; i++) {
/* Heuristic: sync to head with biggest resolution */
if (heads & (2 << (i << 3))) {
x = nvkm_rd32(device, 0x610b40 + (0x540 * i));
y = (x & 0xffff0000) >> 16;
x &= 0x0000ffff;
if ((x * y) > px) {
px = (x * y);
head_sync = i;
}
}
}
}
if (px == 0) {
nvkm_debug(subdev, "WAIT VBLANK !NO ACTIVE HEAD\n");
return;
}
nvkm_debug(subdev, "WAIT VBLANK HEAD%d\n", head_sync);
memx_cmd(memx, MEMX_VBLANK, 1, (u32[]){ head_sync });
memx_out(memx); /* fuc can't handle multiple */
}
void
nvkm_memx_train(struct nvkm_memx *memx)
{
nvkm_debug(&memx->pmu->subdev, " MEM TRAIN\n");
memx_cmd(memx, MEMX_TRAIN, 0, NULL);
}
int
nvkm_memx_train_result(struct nvkm_pmu *pmu, u32 *res, int rsize)
{
struct nvkm_device *device = pmu->subdev.device;
u32 reply[2], base, size, i;
int ret;
ret = nvkm_pmu_send(pmu, reply, PROC_MEMX, MEMX_MSG_INFO,
MEMX_INFO_TRAIN, 0);
if (ret)
return ret;
base = reply[0];
size = reply[1] >> 2;
if (size > rsize)
return -ENOMEM;
/* read the packet */
nvkm_wr32(device, 0x10a1c0, 0x02000000 | base);
for (i = 0; i < size; i++)
res[i] = nvkm_rd32(device, 0x10a1c4);
return 0;
}
void
nvkm_memx_block(struct nvkm_memx *memx)
{
nvkm_debug(&memx->pmu->subdev, " HOST BLOCKED\n");
memx_cmd(memx, MEMX_ENTER, 0, NULL);
}
void
nvkm_memx_unblock(struct nvkm_memx *memx)
{
nvkm_debug(&memx->pmu->subdev, " HOST UNBLOCKED\n");
memx_cmd(memx, MEMX_LEAVE, 0, NULL);
}
#endif
@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVKM_PMU_PRIV_H__
#define __NVKM_PMU_PRIV_H__
#define nvkm_pmu(p) container_of((p), struct nvkm_pmu, subdev)
#include <subdev/pmu.h>
#include <subdev/pmu/fuc/os.h>
enum nvkm_acr_lsf_id;
struct nvkm_acr_lsfw;
struct nvkm_pmu_func {
const struct nvkm_falcon_func *flcn;
struct {
u32 *data;
u32 size;
} code;
struct {
u32 *data;
u32 size;
} data;
bool (*enabled)(struct nvkm_pmu *);
void (*reset)(struct nvkm_pmu *);
int (*init)(struct nvkm_pmu *);
void (*fini)(struct nvkm_pmu *);
void (*intr)(struct nvkm_pmu *);
int (*send)(struct nvkm_pmu *, u32 reply[2], u32 process,
u32 message, u32 data0, u32 data1);
void (*recv)(struct nvkm_pmu *);
int (*initmsg)(struct nvkm_pmu *);
void (*pgob)(struct nvkm_pmu *, bool);
};
extern const struct nvkm_falcon_func gt215_pmu_flcn;
int gt215_pmu_init(struct nvkm_pmu *);
void gt215_pmu_fini(struct nvkm_pmu *);
void gt215_pmu_intr(struct nvkm_pmu *);
void gt215_pmu_recv(struct nvkm_pmu *);
int gt215_pmu_send(struct nvkm_pmu *, u32[2], u32, u32, u32, u32);
bool gf100_pmu_enabled(struct nvkm_pmu *);
void gf100_pmu_reset(struct nvkm_pmu *);
void gp102_pmu_reset(struct nvkm_pmu *pmu);
void gk110_pmu_pgob(struct nvkm_pmu *, bool);
extern const struct nvkm_falcon_func gm200_pmu_flcn;
void gm200_pmu_flcn_bind_inst(struct nvkm_falcon *, int, u64);
extern const struct nvkm_pmu_func gm20b_pmu;
void gm20b_pmu_acr_bld_patch(struct nvkm_acr *, u32, s64);
void gm20b_pmu_acr_bld_write(struct nvkm_acr *, u32, struct nvkm_acr_lsfw *);
int gm20b_pmu_acr_bootstrap_falcon(struct nvkm_falcon *, enum nvkm_acr_lsf_id);
struct nvkm_pmu_fwif {
int version;
int (*load)(struct nvkm_pmu *, int ver, const struct nvkm_pmu_fwif *);
const struct nvkm_pmu_func *func;
const struct nvkm_acr_lsf_func *acr;
};
int gf100_pmu_nofw(struct nvkm_pmu *, int, const struct nvkm_pmu_fwif *);
int gm200_pmu_nofw(struct nvkm_pmu *, int, const struct nvkm_pmu_fwif *);
int gm20b_pmu_load(struct nvkm_pmu *, int, const struct nvkm_pmu_fwif *);
int nvkm_pmu_ctor(const struct nvkm_pmu_fwif *, struct nvkm_device *, enum nvkm_subdev_type, int,
struct nvkm_pmu *);
int nvkm_pmu_new_(const struct nvkm_pmu_fwif *, struct nvkm_device *, enum nvkm_subdev_type, int,
struct nvkm_pmu **);
#endif