You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
5.2 KiB
198 lines
5.2 KiB
/**
|
|
* Copyright 2019 Shawn Anastasio
|
|
* Copyright 2019 Jonathan Currier
|
|
*
|
|
* Copied form src/talos.c
|
|
*
|
|
* This file is part of op-fan-daemon.
|
|
*
|
|
* op-fan-daemon is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* op-fan-daemon is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with op-fan-daemon. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* PWM operations for Blackbird machines
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "fan-daemon.h"
|
|
|
|
static const char fan_controller_path[] =
|
|
"/sys/devices/platform/ahb/ahb:apb/1e786000.pwm-tacho-controller/hwmon/";
|
|
|
|
struct controller {
|
|
char dir_path[REASONABLE_PATH_LEN];
|
|
DIR *dir; // sysfs directory containing pwm control files
|
|
};
|
|
|
|
static struct controller controller;
|
|
|
|
// Maps zones to PWM outputs
|
|
static int8_t zone_map[][5] = {
|
|
[ZONE_CPU0] = {2, -1},
|
|
[ZONE_CPU1] = {-1},
|
|
[ZONE_CHASSIS] = {1, -1}
|
|
};
|
|
|
|
static bool open_controller(const char *path, struct controller *controller_out) {
|
|
bool ret = false;
|
|
char pathbuf[REASONABLE_PATH_LEN];
|
|
|
|
DIR *controller = opendir(path);
|
|
if (!controller) {
|
|
syslog(LOG_ERR, "Unable to open blackbird fan controller base dir!\n");
|
|
goto out;
|
|
}
|
|
|
|
// Walk fs to get to hwmonN directory
|
|
DIR *hwmonN = NULL;
|
|
struct dirent *child;
|
|
while ((child = readdir(controller))) {
|
|
if (child->d_name[0] == '.')
|
|
continue; // Skip hidden, ".", ".."
|
|
|
|
if (!strncmp("hwmon", child->d_name, 5)) {
|
|
snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, child->d_name);
|
|
hwmonN = opendir(pathbuf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hwmonN) {
|
|
syslog(LOG_ERR, "Unable to open blackbird fan controller hwmon directory!\n");
|
|
goto out_controller;
|
|
}
|
|
|
|
// Successfully opened hwmon
|
|
controller_out->dir = hwmonN;
|
|
strncpy(controller_out->dir_path, pathbuf, sizeof(controller_out->dir_path) - 1);
|
|
controller_out->dir_path[sizeof(controller_out->dir_path) - 1] = '\0';
|
|
ret = true;
|
|
|
|
out_controller:
|
|
closedir(controller);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static bool enable_all_pwm(struct controller *controller) {
|
|
char pathbuf[REASONABLE_PATH_LEN + 256 /* d_name */];
|
|
ssize_t n;
|
|
|
|
// Write 1 to all pwmX_enable nodes
|
|
struct dirent *child;
|
|
rewinddir(controller->dir);
|
|
while ((child = readdir(controller->dir))) {
|
|
if (child->d_name[0] == '.')
|
|
continue; // Skip hidden, ".", ".."
|
|
|
|
if (!strstr(child->d_name, "_enable"))
|
|
continue;
|
|
|
|
snprintf(pathbuf, sizeof(pathbuf), "%s/%s", controller->dir_path, child->d_name);
|
|
int fd = open(pathbuf, O_WRONLY);
|
|
if (fd < 0) {
|
|
syslog(LOG_ERR, "Unable to set open pwmX_enable: %m.\n");
|
|
return false;
|
|
}
|
|
|
|
const char manual_mode[] = "1";
|
|
if ((n = write(fd, manual_mode, 1)) < 0) {
|
|
syslog(LOG_ERR, "Unable to set manual PWM mode: %m.\n");
|
|
close(fd);
|
|
return false;
|
|
}
|
|
close(fd);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void set_pwm(struct controller *controller, int8_t pwm, uint8_t speed) {
|
|
char buf[REASONABLE_PATH_LEN + 4 /* pwm */ + 4 /* digit */];
|
|
snprintf(buf, sizeof(buf), "%s/pwm%d", controller->dir_path, pwm);
|
|
|
|
int fd = open(buf, O_WRONLY);
|
|
if (fd < 0)
|
|
goto fail;
|
|
|
|
ssize_t n = snprintf(buf, sizeof(buf), "%d", speed);
|
|
if (write(fd, buf, n) < 0)
|
|
goto fail_open;
|
|
|
|
close(fd);
|
|
|
|
return;
|
|
|
|
fail_open:
|
|
close(fd);
|
|
fail:
|
|
syslog(LOG_ERR, "Unable to set PWM speed: %m.\n");
|
|
}
|
|
|
|
static bool blackbird_probe(void) {
|
|
char buf[256];
|
|
ssize_t n;
|
|
|
|
// Read device-tree to determine if we're on a blackbird
|
|
int fd = open("/proc/device-tree/compatible", O_RDONLY);
|
|
if (fd < 0)
|
|
goto fail;
|
|
|
|
if ((n = read(fd, buf, sizeof(buf) - 1)) < 0)
|
|
goto fail_open;
|
|
close(fd);
|
|
buf[n] = '\0';
|
|
|
|
if (!strstr(buf, "rcs,blackbird-bmc"))
|
|
return false; // Not a blackbird
|
|
|
|
// We're on a blackbird, perform early initalization
|
|
if (!open_controller(fan_controller_path, &controller))
|
|
goto fail_open;
|
|
|
|
if (!enable_all_pwm(&controller))
|
|
goto fail_open;
|
|
|
|
syslog(LOG_INFO, "Successfully autodetected blackbird platform.\n");
|
|
return true;
|
|
|
|
fail_open:
|
|
close(fd);
|
|
fail:
|
|
syslog(LOG_ERR, "blackbird_probe failed: %m.\n");
|
|
return false;
|
|
}
|
|
|
|
static void blackbird_set_zone_speed(enum zone zone, uint8_t speed) {
|
|
int8_t *fans = zone_map[zone];
|
|
assert(fans);
|
|
|
|
for (int8_t *cur = fans; *cur != -1; cur++) {
|
|
set_pwm(&controller, *cur, speed);
|
|
}
|
|
}
|
|
|
|
DECLARE_PWM_OPS(blackbird) = {
|
|
.probe = blackbird_probe,
|
|
.set_zone_speed = blackbird_set_zone_speed
|
|
};
|
|
|
|
|