blob: 05431cd9752e80ff28714bbe2f3ac3194ccdc9d9 [file] [log] [blame]
#ifdef WINDCHARGER
#define _POSIX_C_SOURCE 199309L /* for clock_gettime */
#define _BSD_SOURCE /* for usleep */
#include <features.h>
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <stacktrace.h>
#include "fileops.h"
#include "pin.h"
#define UNUSED __attribute__((unused))
#define DEVMEM "/dev/mem"
#define GPIO_OUT_FUNCTION0 0xB
#define GPIO_OUT_ENABLE 0x0
#define GPIO_CNTL_PER_REG 4
/* CPU Temperature Monitoring. */
#define SYS_TEMP_DIR "/sys/class/hwmon/hwmon0/device/"
#define SYS_TEMP1 SYS_TEMP_DIR "temp1_input"
struct PinHandle_s {
int unused;
};
#define CHECK(x) do { \
int rv = (x); \
if (rv) { \
fprintf(stderr, "CHECK: %s returned %d\n", #x, rv); \
_exit(99); \
} \
} while (0)
static volatile void* mmap_addr = MAP_FAILED;
static uint32_t mmap_offset;
static size_t mmap_size = 0;
static int mmap_fd = -1;
struct Gpio {
int is_present;
/* Pin number */
unsigned int shift;
/* for offset_direction and offset_data */
unsigned int direction_value; // 0 is output
int old_val;
};
struct platform_info {
const char *name;
off_t mmap_base;
size_t mmap_size;
void (*init)(struct platform_info* p);
unsigned in_offset;
unsigned out_offset;
unsigned set_offset;
unsigned clear_offset;
struct Gpio led_red;
struct Gpio led_blue;
struct Gpio reset_button;
};
struct platform_info platforms[] = {
{
.name = "GFMN100",
.mmap_base = 0x18040000,
.mmap_size = 0x40,
.in_offset = 0x1,
.out_offset = 0x2,
.set_offset = 0x3,
.clear_offset = 0x4,
.led_red = {
.is_present = 1,
.shift = 16,
.direction_value = 0,
.old_val = -1,
},
.led_blue = {
.is_present = 1,
.shift = 11,
.direction_value = 0,
.old_val = -1,
},
.reset_button = {
.is_present = 1,
.shift = 13,
.direction_value = 1,
.old_val = -1,
},
}
};
struct platform_info *platform = NULL;
// Write the given GPIO pin.
static void set_gpio(struct Gpio *g, int level) {
volatile uint32_t* reg = mmap_addr;
if (g->old_val == level) {
return;
}
g->old_val = level;
reg += (level > 0) ? platform->set_offset : platform->clear_offset;
*reg = 1 << g->shift;
}
// Read the given GPIO pin
static int get_gpio(int pin) {
volatile uint32_t* reg = mmap_addr;
uint32_t value;
reg += platform->in_offset;
value = *reg;
return (value >> pin) & 0x1;
}
static int get_temp1(UNUSED PinHandle handle) {
return read_file_long(SYS_TEMP1);
}
// initialize GPIO to input or output
static void set_direction(struct Gpio *g)
{
volatile uint32_t* reg = mmap_addr;
uint32_t data, reg_addr;
reg_addr = GPIO_OUT_ENABLE;
reg += reg_addr;
data = *reg;
data &= ~(1 << g->shift);
data |= 0 << g->shift;
*reg = data;
}
// initialize pin to LED or GPIO etc
static void set_pinmux(struct Gpio *g) {
volatile uint32_t* reg = mmap_addr;
uint32_t data, reg_addr, addr_offset, byte_offset;
addr_offset = (g->shift / GPIO_CNTL_PER_REG) ;
byte_offset = g->shift - addr_offset;
reg_addr = GPIO_OUT_FUNCTION0 + addr_offset;
reg += reg_addr;
data = *reg;
data &= ~(0xFF << (8 * byte_offset));
data |= 0 << (8 * byte_offset);
*reg = data;
}
static void platform_cleanup(void) {
if (mmap_addr != MAP_FAILED) {
if (munmap((void*) mmap_addr, mmap_size) < 0) {
perror("munmap");
}
mmap_addr = MAP_FAILED;
mmap_size = 0;
}
if (mmap_fd >= 0) {
close(mmap_fd);
mmap_fd = -1;
}
}
static int platform_init(struct platform_info* p) {
int page_size;
uint64_t file_start;
platform_cleanup();
mmap_fd = open(DEVMEM, O_RDWR);
if (mmap_fd < 0) {
perror(DEVMEM);
return -1;
}
page_size = getpagesize();
/* map to file at file_start, which has to be page aligned */
file_start = (p->mmap_base / page_size) * page_size;
mmap_offset = p->mmap_base % page_size;
mmap_size = p->mmap_size;
mmap_addr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
mmap_fd, file_start);
if (mmap_addr == MAP_FAILED) {
perror("mmap");
platform_cleanup();
return -1;
}
return 0;
}
static struct platform_info *get_platform_info(const char *platform_name) {
int lim = sizeof(platforms) / sizeof(platforms[0]);
for (int i = 0; i < lim; ++i) {
struct platform_info *p = &platforms[i];
if (0 == strncmp(platform_name, p->name, strlen(p->name))) {
return p;
}
}
fprintf(stderr, "No support for platform %s", platform_name);
exit(1);
}
// read a file containing a single short string.
// Returns a static buffer. Be careful!
static char *read_file(const char *filename) {
static char buf[1024];
int fd = open(filename, O_RDONLY);
if (fd >= 0) {
size_t got = read(fd, buf, sizeof(buf) - 1);
buf[got] = '\0';
close(fd);
return buf;
}
buf[0] = '\0';
return buf;
}
/* API follows */
int has_cpu_temp(void) {
return 1;
}
int has_red_led(void) {
return (platform->led_red.is_present);
}
int has_blue_led(void) {
return (platform->led_blue.is_present);
}
int has_reset_button(void) {
return (platform->reset_button.is_present);
}
int get_red_led(void) {
return get_gpio(platform->led_red.shift);
}
int get_blue_led(void) {
return get_gpio(platform->led_blue.shift);
}
void set_red_led(int level) {
set_gpio(&platform->led_red, level ? 1 : 0);
}
void set_blue_led(int level) {
set_gpio(&platform->led_blue, level ? 1 : 0);
}
static void init_platform(struct platform_info* p) {
if (p->init) {
(*p->init)(p);
}
}
static void initialize_gpios(void) {
init_platform(platform);
set_pinmux(&platform->led_red);
set_pinmux(&platform->led_blue);
set_direction(&platform->led_red);
set_direction(&platform->led_blue);
}
/* standard API follows */
PinHandle PinCreate(void) {
PinHandle handle = (PinHandle) calloc(1, sizeof (*handle));
if (handle == NULL) {
perror("calloc(PinHandle)");
return NULL;
}
platform = get_platform_info(read_file("/etc/platform"));
if (platform_init(platform) < 0) {
fprintf(stderr, "platform_init failed\n");
PinDestroy(handle);
return NULL;
}
initialize_gpios();
return handle;
}
void PinDestroy(PinHandle handle) {
if (handle == NULL)
return;
platform_cleanup();
free(handle);
}
int PinIsPresent(PinHandle handle, PinId id) {
if (handle == NULL) return PIN_ERROR;
switch (id) {
case PIN_LED_RED:
return has_red_led();
case PIN_LED_BLUE:
return has_blue_led();
case PIN_BUTTON_RESET:
return has_reset_button();
case PIN_TEMP_CPU:
return has_cpu_temp();
case PIN_MVOLTS_CPU:
case PIN_FAN_CHASSIS:
case PIN_TEMP_EXTERNAL:
case PIN_NONE:
case PIN_MAX:
default:
return 0;
}
return 0;
}
PinStatus PinValue(PinHandle handle, PinId id, int* valueP) {
if (handle == NULL) return PIN_ERROR;
switch (id) {
case PIN_LED_RED:
*valueP = get_red_led();
break;
case PIN_LED_BLUE:
*valueP = get_blue_led();
break;
case PIN_BUTTON_RESET:
*valueP = !get_gpio(platform->reset_button.shift);
break;
case PIN_TEMP_CPU:
*valueP = get_temp1(handle);
break;
case PIN_MVOLTS_CPU:
case PIN_TEMP_EXTERNAL:
case PIN_NONE:
case PIN_MAX:
default:
*valueP = -1;
return PIN_ERROR;
}
return PIN_OKAY;
}
PinStatus PinSetValue(PinHandle handle, PinId id, int value) {
if (handle == NULL) return PIN_ERROR;
switch (id) {
case PIN_LED_RED:
set_red_led(value);
break;
case PIN_LED_BLUE:
set_blue_led(value);
break;
case PIN_LED_ACTIVITY:
case PIN_LED_STANDBY:
case PIN_FAN_CHASSIS:
case PIN_BUTTON_RESET:
case PIN_MVOLTS_CPU:
case PIN_TEMP_CPU:
case PIN_TEMP_EXTERNAL:
case PIN_NONE:
case PIN_MAX:
return PIN_ERROR;
}
return PIN_OKAY;
}
#endif /* WINDCHARGER */