blob: 1c89dfafbd6a767c85273de024d70aa8d63c454e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <fuchsia/hardware/gpu/clock/c/fidl.h>
#include <fuchsia/hardware/thermal/c/fidl.h>
#include <inttypes.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/watcher.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/port.h>
#include <zircon/syscalls/system.h>
#include <lib/zx/handle.h>
// TODO(braval): Combine thermd & thermd_arm and have a unified
// code for the thermal deamon
static zx_status_t thermal_device_added(int dirfd, int event, const char* name,
void* cookie) {
if (event != WATCH_EVENT_ADD_FILE) {
return ZX_OK;
}
if (!strcmp("000", name)) {
// Device found, terminate watcher
return ZX_ERR_STOP;
} else {
return ZX_OK;
}
}
int main(int argc, char** argv) {
printf("thermd: started\n");
// TODO(braval): This sleep is not needed here but leaving it here
// since the Intel thermd has it. Clean up when both deamons are
// unified
zx_nanosleep(zx_deadline_after(ZX_SEC(3)));
int dirfd = open("/dev/class/thermal", O_DIRECTORY | O_RDONLY);
if (dirfd < 0) {
fprintf(stderr, "ERROR: Failed to open /dev/class/thermal: %d (errno %d)\n",
dirfd, errno);
return -1;
}
zx_status_t st =
fdio_watch_directory(dirfd, thermal_device_added, ZX_TIME_INFINITE, NULL);
if (st != ZX_ERR_STOP) {
fprintf(stderr,
"ERROR: watcher terminating without finding sensors, "
"terminating thermd...\n");
return -1;
}
// first device is the one we are interested
int fd = open("/dev/class/thermal/000", O_RDWR);
if (fd < 0) {
fprintf(stderr, "ERROR: Failed to open sensor: %d (errno %d) \n", fd,
errno);
return -1;
}
// first device is the one we are interested
int fd_gpu = open("/dev/class/gpu-thermal/000", O_RDONLY);
if (fd_gpu < 0) {
fprintf(stderr, "ERROR: Failed to open gpu: %d (errno %d) \n", fd_gpu,
errno);
return -1;
}
zx::handle handle;
st = fdio_get_service_handle(fd, handle.reset_and_get_address());
if (st != ZX_OK) {
fprintf(stderr, "ERROR: Failed to get thermal service: %d\n", st);
return -1;
}
zx::handle gpu_handle;
st = fdio_get_service_handle(fd_gpu, gpu_handle.reset_and_get_address());
if (st != ZX_OK) {
fprintf(stderr, "ERROR: Failed to get gpu service: %d\n", st);
return -1;
}
zx_status_t status2;
fuchsia_hardware_thermal_ThermalDeviceInfo info;
st = fuchsia_hardware_thermal_DeviceGetDeviceInfo(handle.get(), &status2,
&info);
if (st != ZX_OK || status2 != ZX_OK) {
fprintf(stderr, "ERROR: Failed to get thermal info: %d %d\n", st, status2);
return -1;
}
if (info.num_trip_points == 0) {
fprintf(stderr, "Trip points not supported, exiting\n");
return 0;
}
if (!info.active_cooling && !info.passive_cooling) {
fprintf(stderr,
"ERROR: No active or passive cooling present on device, "
"terminating thermd...\n");
return 0;
}
zx_handle_t port = ZX_HANDLE_INVALID;
st = fuchsia_hardware_thermal_DeviceGetStateChangePort(handle.get(), &status2,
&port);
if (st != ZX_OK || status2 != ZX_OK) {
fprintf(stderr, "ERROR: Failed to get event: %d %d\n", st, status2);
return -1;
}
for (;;) {
zx_port_packet_t packet;
st = zx_port_wait(port, ZX_TIME_INFINITE, &packet);
if (st != ZX_OK) {
fprintf(stderr, "ERROR: Failed to wait on port: %d\n", st);
return st;
}
uint32_t trip_idx = (uint32_t)packet.key;
if (trip_idx > info.num_trip_points) {
fprintf(stderr, "ERROR: Invalid trip index: terminating thermd\n");
return -1;
}
if (info.passive_cooling) {
uint32_t big_cluster_opp =
info.trip_point_info[trip_idx].big_cluster_dvfs_opp;
// Set DVFS Opp for Big Cluster.
st = fuchsia_hardware_thermal_DeviceSetDvfsOperatingPoint(
handle.get(), big_cluster_opp,
fuchsia_hardware_thermal_PowerDomain_BIG_CLUSTER_POWER_DOMAIN,
&status2);
if (st != ZX_OK || status2 != ZX_OK) {
fprintf(stderr,
"ERROR: Failed to set DVFS OPP for big cluster: %d %d\n", st,
status2);
return -1;
}
// Check if it's big little.
if (info.big_little) {
// Set the DVFS Opp for Little Cluster.
uint32_t little_cluster_opp =
info.trip_point_info[trip_idx].little_cluster_dvfs_opp;
st = fuchsia_hardware_thermal_DeviceSetDvfsOperatingPoint(
handle.get(), little_cluster_opp,
fuchsia_hardware_thermal_PowerDomain_LITTLE_CLUSTER_POWER_DOMAIN,
&status2);
if (st != ZX_OK || status2 != ZX_OK) {
fprintf(stderr,
"ERROR: Failed to set DVFS OPP for little cluster: %d %d\n",
st, status2);
return -1;
}
}
}
if (info.active_cooling) {
uint32_t fan_level = info.trip_point_info[trip_idx].fan_level;
st = fuchsia_hardware_thermal_DeviceSetFanLevel(handle.get(), fan_level,
&status2);
if (st != ZX_OK || status2 != ZX_OK) {
fprintf(stderr, "ERROR: Failed to set fan level: %d %d\n", st, status2);
}
}
if (info.gpu_throttling) {
int gpu_clk_freq_source =
info.trip_point_info[trip_idx].gpu_clk_freq_source;
if (gpu_clk_freq_source != -1) {
st = fuchsia_hardware_gpu_clock_ClockSetFrequencySource(
gpu_handle.get(), gpu_clk_freq_source, &status2);
if (st != ZX_OK || status2 != ZX_OK) {
fprintf(stderr,
"ERROR: Failed to change gpu clock freq source: %d %d\n", st,
status2);
return -1;
}
}
}
}
close(fd);
printf("thermd terminating: %d\n", st);
return 0;
}