diff --git a/nx/include/switch.h b/nx/include/switch.h index 8006f4ed..973473a1 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -51,6 +51,7 @@ extern "C" { #include "switch/services/hwopus.h" #include "switch/services/csrng.h" #include "switch/services/bpc.h" +#include "switch/services/pcv.h" #include "switch/services/psm.h" #include "switch/services/spsm.h" //#include "switch/services/bsd.h" Use switch/runtime/devices/socket.h instead diff --git a/nx/include/switch/services/pcv.h b/nx/include/switch/services/pcv.h new file mode 100644 index 00000000..7b121bf6 --- /dev/null +++ b/nx/include/switch/services/pcv.h @@ -0,0 +1,20 @@ +/** + * @file pcv.h + * @brief PCV service IPC wrapper. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" + +typedef enum { + PcvModule_Cpu = 0, + PcvModule_Gpu = 1, + PcvModule_Emc = 56, +} PcvModule; + +Result pcvInitialize(void); +void pcvExit(void); + +Result pcvGetClockRate(PcvModule module, u32 *out_hz); +Result pcvSetClockRate(PcvModule module, u32 hz); diff --git a/nx/source/services/pcv.c b/nx/source/services/pcv.c new file mode 100644 index 00000000..5c7e075e --- /dev/null +++ b/nx/source/services/pcv.c @@ -0,0 +1,106 @@ +#include "types.h" +#include "result.h" +#include "arm/atomics.h" +#include "kernel/ipc.h" +#include "kernel/detect.h" +#include "services/pcv.h" +#include "services/sm.h" + +static Service g_pcvSrv; +static u64 g_refCnt; + +Result pcvInitialize(void) { + Result rc = 0; + + atomicIncrement64(&g_refCnt); + + if (serviceIsActive(&g_pcvSrv)) + return 0; + + rc = smGetService(&g_pcvSrv, "pcv"); + + if (R_FAILED(rc)) pcvExit(); + + return rc; +} + +void pcvExit(void) { + if (atomicDecrement64(&g_refCnt) == 0) { + serviceClose(&g_pcvSrv); + } +} + +Result pcvSetClockRate(PcvModule module, u32 hz) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 module; + u32 hz; + } *raw; + + raw = serviceIpcPrepareHeader(&g_pcvSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 2; + raw->module = module; + raw->hz = hz; + + Result rc = serviceIpcDispatch(&g_pcvSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_pcvSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result pcvGetClockRate(PcvModule module, u32 *out_hz) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 module; + } *raw; + + raw = serviceIpcPrepareHeader(&g_pcvSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 3; + raw->module = module; + + Result rc = serviceIpcDispatch(&g_pcvSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 hz; + } *resp; + + serviceIpcParse(&g_pcvSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *out_hz = resp->hz; + } + } + + return rc; +}