blob: 0d5701ade21e0f2c6f1eb4bca8434a1a886b12e3 [file] [log] [blame]
#ifdef MINDSPEED
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "fileops.h"
#include "pin.h"
#define DEVMEM "/dev/mem"
/* optimus */
#define REG_PWM_BASE 0x90458000
#define REG_PWM_DIVIDER (REG_PWM_BASE)
#define REG_PWM_HI(p) (REG_PWM_BASE+0x08+0x08*(p))
#define REG_PWM_LO(p) (REG_PWM_HI(p)+0x04)
#define PWM_CLOCK_HZ 250000000 /* 250 MHz */
#define PWM_DIVIDER_ENABLE_MASK (1<<31)
#define PWM_DIVIDER_VALUE_MASK ((1<<8)-1)
#define PWM_TIMER_ENABLE_MASK (1<<31)
#define PWM_TIMER_VALUE_MASK ((1<<20)-1)
#define PWM_DEFAULT_DIVIDER PWM_DIVIDER_VALUE_MASK
#define REG_GPIO_BASE 0x90470000
#define REG_GPIO_OUTPUT (REG_GPIO_BASE+0x00)
#define REG_GPIO_DIRECTION (REG_GPIO_BASE+0x04) /* 1 = output */
#define REG_GPIO_INPUT (REG_GPIO_BASE+0x10)
#define REG_GPIO_SELECT (REG_GPIO_BASE+0x58)
/* manually maintain these */
#define REG_FIRST REG_PWM_BASE
#define REG_LAST REG_GPIO_SELECT
#define REG_LENGTH (REG_LAST + 0x04 - REG_FIRST)
/* index of gpio pins */
#define GPIO_BUTTON 6
#define GPIO_ACTIVITY 12
#define GPIO_RED 13
/* gpio 12 can be pwm 4, 13 can be 5 */
#define PWM_ACTIVITY 4
#define PWM_RED 5
#define PWM_LED_HZ 1000 /* 300-1000 is recommended */
#define PWM_DUTY_OFF_PERCENT 50 /* 0 is full bright, 100 is off */
struct PinHandle_s {
int fd;
volatile unsigned char* addr;
};
#define BIT_IS_SET(data, bit) (((data) & (1u << (bit))) == (1u << (bit)))
#define BIT_SET(data, bit) ((data) | (1u << (bit)))
#define BIT_CLR(data, bit) ((data) & ~(1u << (bit)))
#define SYS_FAN_DIR "/sys/devices/platform/comcerto_i2c.0/i2c-0/0-004c/"
#define SYS_TEMP1 SYS_FAN_DIR "temp1_input"
#define SYS_TEMP2 SYS_FAN_DIR "temp2_input"
#define SYS_FAN SYS_FAN_DIR "pwm1"
#define SYS_RPM SYS_FAN_DIR "fan1_input"
/* helper methods */
// this is for writing to SYS_FAN
// don't use for writing to a regular file since this is not atomic
static void writeIntToFile(char* file, int value) {
FILE* fp = fopen(file, "w");
if (fp == NULL) {
perror(file);
return;
}
fprintf(fp, "%d", value);
fclose(fp);
}
/* optimus methods get sensor data */
static uint32_t getRegister(PinHandle handle, unsigned int reg) {
volatile uint32_t* regaddr = (volatile uint32_t*) (handle->addr + (reg - REG_FIRST));
if (reg < REG_FIRST || reg > REG_LAST) {
fprintf(stderr, "getRegister: register 0x%08x is out of range (0x%08x-0x%08x)\n",
reg, REG_FIRST, REG_LAST);
return 0;
}
return *regaddr;
}
static void setRegister(PinHandle handle, unsigned int reg, uint32_t value) {
volatile uint32_t* regaddr = (volatile uint32_t*) (handle->addr + (reg - REG_FIRST));
if (reg < REG_FIRST || reg > REG_LAST) {
fprintf(stderr, "setRegister: register 0x%08x is out of range (0x%08x-0x%08x)\n",
reg, REG_FIRST, REG_LAST);
return;
}
*regaddr = value;
}
static int getGPIO(PinHandle handle, int gpio) {
uint32_t direction = getRegister(handle, REG_GPIO_DIRECTION);
int reg = BIT_IS_SET(direction, gpio) ? REG_GPIO_OUTPUT : REG_GPIO_INPUT;
uint32_t value = getRegister(handle, reg);
return BIT_IS_SET(value, gpio);
}
static void setGPIO(PinHandle handle, int gpio, int value) {
uint32_t direction = getRegister(handle, REG_GPIO_DIRECTION);
int reg = BIT_IS_SET(direction, gpio) ? REG_GPIO_OUTPUT : REG_GPIO_INPUT;
if (!BIT_IS_SET(direction, gpio)) {
fprintf(stderr, "setGPIO: gpio %d is not an output register, refusing to set\n", gpio);
return;
}
uint32_t val = getRegister(handle, reg);
uint32_t newVal = value ? BIT_SET(val, gpio) : BIT_CLR(val, gpio);
setRegister(handle, reg, newVal);
}
static int getPWMValue(PinHandle handle, int gpio, int pwm) {
uint32_t divider = getRegister(handle, REG_PWM_DIVIDER); /* shared among all PWM */
uint32_t lo = getRegister(handle, REG_PWM_LO(pwm));
uint32_t hi = getRegister(handle, REG_PWM_HI(pwm));
uint32_t hi_enabled = hi & PWM_TIMER_ENABLE_MASK;
hi &= ~PWM_TIMER_ENABLE_MASK;
int is_on = (divider & PWM_DIVIDER_ENABLE_MASK) &&
hi_enabled &&
lo < hi; /* technically true, but maybe not visible */
return is_on;
}
static void setPWMValue(PinHandle handle, int gpio, int pwm, int value) {
static uint32_t warn_divider = 0xffffffff;
uint32_t direction = getRegister(handle, REG_GPIO_DIRECTION);
if (!BIT_IS_SET(direction, gpio)) {
fprintf(stderr, "setPWMValue: gpio %d is not an output register, refusing to set\n", gpio);
return;
}
uint32_t select = getRegister(handle, REG_GPIO_SELECT);
uint32_t mode = (select >> (2*gpio)) & 0x3;
if (mode != 0x1) {
fprintf(stderr, "setPWMValue: setting gpio %d to PWM mode\n", gpio);
select &= ~(0x3 << (2*gpio));
select |= (0x1 << (2*gpio));
setRegister(handle, REG_GPIO_SELECT, select);
}
uint32_t divider = getRegister(handle, REG_PWM_DIVIDER); /* shared among all PWM */
if (! (divider & PWM_DIVIDER_ENABLE_MASK)) { /* not enabled */
fprintf(stderr, "setPWMValue: divider not enabled, enabling\n");
divider = PWM_DIVIDER_ENABLE_MASK | PWM_DEFAULT_DIVIDER;
setRegister(handle, REG_PWM_DIVIDER, divider);
}
divider &= PWM_DIVIDER_VALUE_MASK;
divider++; /* divider reg is 0-based */
uint32_t timer = PWM_CLOCK_HZ / divider / PWM_LED_HZ;
if (timer < 1) {
timer = 1;
if (warn_divider != divider) {
fprintf(stderr, "setPWMValue: PWM_LED_HZ too large, LED will be %d Hz\n",
PWM_CLOCK_HZ/divider/timer);
warn_divider = divider;
}
} else if (timer > PWM_TIMER_VALUE_MASK+1) {
timer = PWM_TIMER_VALUE_MASK+1;
if (warn_divider != divider) {
fprintf(stderr, "setPWMValue: divider too small, LED will be %d Hz\n",
PWM_CLOCK_HZ/divider/timer);
warn_divider = divider;
}
}
/* brighter as duty approaches 0, dimmer as it approaches timer */
uint32_t duty = timer * (value ? PWM_DUTY_OFF_PERCENT : 100) / 100;
if (duty < 1) {
duty = 1;
}
if (duty > timer) {
duty = timer;
}
setRegister(handle, REG_PWM_LO(pwm), duty-1); /* duty reg is 0-based */
setRegister(handle, REG_PWM_HI(pwm), (timer-1) | PWM_TIMER_ENABLE_MASK); /* timer reg is 0-based */
}
static int getFan(PinHandle handle) {
static int rpm_failed = 0;
if (!rpm_failed) {
int val = read_file_long(SYS_RPM);
if (val >= 0) return val;
rpm_failed = 1; // old bootloader doesn't enable tachometer
}
return 0;
}
static void setFan(PinHandle handle, int percent) {
int val = percent * 255 / 100;
if (val < 0) val = 0;
if (val > 255) val = 255;
writeIntToFile(SYS_FAN, val);
}
static int getTemp1(PinHandle handle) {
return read_file_long(SYS_TEMP1);
}
static int getTemp2(PinHandle handle) {
return read_file_long(SYS_TEMP2);
}
/* API implementation */
PinHandle PinCreate(void) {
PinHandle handle = (PinHandle) calloc(1, sizeof (*handle));
if (handle == NULL) {
perror("calloc(PinHandle)");
return NULL;
}
handle->fd = -1;
handle->addr = NULL;
handle->fd = open(DEVMEM, O_RDWR);
if (handle->fd < 0) {
perror(DEVMEM);
PinDestroy(handle);
return NULL;
}
handle->addr = mmap(NULL, REG_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED,
handle->fd, REG_FIRST);
if (handle->addr == MAP_FAILED) {
perror("mmap");
PinDestroy(handle);
return NULL;
}
return handle;
}
void PinDestroy(PinHandle handle) {
if (handle != NULL) {
if (handle->fd > 0) {
close(handle->fd);
handle->fd = -1;
}
if (handle->addr != NULL) {
munmap((void*) handle->addr, REG_LENGTH);
handle->addr = NULL;
}
free(handle);
}
return;
}
int PinIsPresent(PinHandle handle, PinId id) {
switch (id) {
case PIN_LED_RED:
case PIN_LED_ACTIVITY:
case PIN_BUTTON_RESET:
case PIN_TEMP_CPU:
case PIN_TEMP_EXTERNAL:
case PIN_MVOLTS_CPU:
case PIN_FAN_CHASSIS:
return 1;
/* no default here so we can be sure we get all the cases */
case PIN_LED_BLUE:
case PIN_LED_STANDBY:
case PIN_NONE:
case PIN_MAX:
break;
}
return 0;
}
PinStatus PinValue(PinHandle handle, PinId id, int* valueP) {
switch (id) {
case PIN_LED_RED:
*valueP = getPWMValue(handle, GPIO_RED, PWM_RED);
break;
case PIN_LED_ACTIVITY:
*valueP = getPWMValue(handle, GPIO_ACTIVITY, PWM_ACTIVITY);
break;
case PIN_BUTTON_RESET:
*valueP = !getGPIO(handle, GPIO_BUTTON); /* inverted */
break;
case PIN_TEMP_CPU:
/* optimus has temp2 sensor placed close to SOC */
*valueP = getTemp2(handle);
break;
case PIN_TEMP_EXTERNAL:
/* temp1 is the lm96063 internal sensor, which is "external" to the SOC */
*valueP = getTemp1(handle);
break;
case PIN_FAN_CHASSIS:
*valueP = getFan(handle);
break;
case PIN_MVOLTS_CPU:
*valueP = 1000; /* TODO(edjames) */
break;
case PIN_LED_BLUE:
case PIN_LED_STANDBY:
case PIN_NONE:
case PIN_MAX:
*valueP = 0;
return PIN_ERROR;
}
return PIN_OKAY;
}
PinStatus PinSetValue(PinHandle handle, PinId id, int value) {
switch (id) {
case PIN_LED_RED:
setPWMValue(handle, GPIO_RED, PWM_RED, value);
break;
case PIN_LED_ACTIVITY:
setPWMValue(handle, GPIO_ACTIVITY, PWM_ACTIVITY, value);
break;
case PIN_FAN_CHASSIS:
setFan(handle, value);
break;
case PIN_LED_BLUE:
case PIN_LED_STANDBY:
case PIN_BUTTON_RESET:
case PIN_TEMP_CPU:
case PIN_TEMP_EXTERNAL:
case PIN_MVOLTS_CPU:
case PIN_NONE:
case PIN_MAX:
return PIN_ERROR;
}
return PIN_OKAY;
}
#endif /* MINDSPEED */