blob: b1e6294e2a5193d8fb00105cd287b724d9605b7d [file] [log] [blame]
#ifdef BROADCOM
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include "brcm-platform.h"
#define UNUSED __attribute__((unused))
#define DEVMEM "/dev/mem"
static volatile void* mmap_addr = MAP_FAILED;
static size_t mmap_size = 0;
static int mmap_fd = -1;
static void init_gfhd200(UNUSED struct platform_info* p);
static void init_gfhd254(UNUSED struct platform_info* p);
static double get_avs_voltage_7252(struct Voltage* v);
static double get_avs_voltage_74xx(struct Voltage* v);
static double get_avs_temperature_7252(struct Temp* t);
static double get_avs_temperature_74xx(struct Temp* t);
struct platform_info platforms[] = {
{
.name = "GFHD100",
.mmap_base = 0x10400000, // base of many brcm registers
.mmap_size = 0x40000,
.leds = {
.led_red = {
.is_present = 1, // GPIO 17
.offset_direction = 0x94c8, // GIO_AON_IODIR_LO
.offset_data = 0x94c4, // GIO_AON_DATA_LO
.mask = 0x00020000, // 1<<17
.shift = 17,
.off_value = 0,
.on_value = 1,
.direction_value = 0,
.old_val = -1,
},
.led_blue = {
.is_present = 1, // GPIO 12
.offset_direction = 0x94c8, // GIO_AON_IODIR_LO
.offset_data = 0x94c4, // GIO_AON_DATA_LO
.mask = 0x00001000, // 1<<12
.shift = 12,
.off_value = 0,
.on_value = 1,
.direction_value = 0,
.old_val = -1,
},
.led_activity = {
.is_present = 1, // GPIO 13
.offset_direction = 0x94c8, // GIO_AON_IODIR_LO
.offset_data = 0x94c4, // GIO_AON_DATA_LO
.mask = 0x00002000, // 1<<13
.shift = 13,
.off_value = 0,
.on_value = 1,
.direction_value = 0,
.old_val = -1,
},
.led_standby = {
.is_present = 1, // GPIO 10
.offset_direction = 0x94c8, // GIO_AON_IODIR_LO
.offset_data = 0x94c4, // GIO_AON_DATA_LO
.mask = 0x00000400, // 1<<10
.shift = 10,
.off_value = 0,
.on_value = 1,
.direction_value = 0,
.old_val = -1,
},
},
.reset_button = {
.is_present = 1, // GPIO 4
.offset_direction = 0x94c8, // GIO_AON_IODIR_LO
.offset_data = 0x94c4, // GIO_AON_DATA_LO
.mask = 0x00000010, // 1<<4
.shift = 4,
.off_value = 0,
.on_value = 1,
.direction_value = 1,
.old_val = -1,
},
.fan_tick = {
.is_present = 1, // GPIO 98
.offset_direction = 0x6768, // GIO_IODIR_EXT_HI
.offset_data = 0x6764, // GIO_DATA_EXT_HI
.mask = 0x00000100, // 1<<8
.shift = 8,
.off_value = 0,
.on_value = 1,
.direction_value = 1,
.old_val = -1,
},
.fan_control = {
.is_present = 1, // PWM 1
.offset_data = 0x6580, // PWM_CTRL ...
.channel = 0,
.open_drain = 1,
.period = 0xf0,
},
.temp_monitor = {
.is_present = 1, // 7425 AVS_RO_REGISTERS_0
.offset_data = 0x32b00, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
.get_temp = get_avs_temperature_74xx,
},
.voltage_monitor = {
.is_present = 1, // 7425 AVS_RO_REGISTERS_0
.offset_data = 0x32b0c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
.get_voltage = get_avs_voltage_74xx,
},
},
{
.name = "GFMS100",
.mmap_base = 0x10400000, // base of many brcm registers
.mmap_size = 0x40000,
.leds = {
.led_red = {
.is_present = 1, // GPIO 17
.offset_direction = 0x94c8, // GIO_AON_IODIR_LO
.offset_data = 0x94c4, // GIO_AON_DATA_LO 0..17
.mask = 0x00020000, // 1<<17
.shift = 17,
.off_value = 0,
.on_value = 1,
.direction_value = 0,
.old_val = -1,
},
.led_blue = {
.is_present = 0,
},
.led_activity = {
.is_present = 1, // GPIO 13
.offset_direction = 0x94c8, // GIO_AON_IODIR_LO
.offset_data = 0x94c4, // GIO_AON_DATA_LO
.mask = 0x00002000, // 1<<13
.shift = 13,
.off_value = 0,
.on_value = 1,
.direction_value = 0,
.old_val = -1,
},
.led_standby = {
.is_present = 0,
},
},
.reset_button = {
.is_present = 1, // GPIO 4
.offset_direction = 0x94c8, // GIO_AON_IODIR_LO
.offset_data = 0x94c4, // GIO_AON_DATA_LO
.mask = 0x00000010, // 1<<4
.shift = 4,
.off_value = 0,
.on_value = 1,
.direction_value = 1,
.old_val = -1,
},
.fan_tick = {
.is_present = 1, // GPIO 98
.offset_direction = 0x6768, // GIO_IODIR_EXT_HI
.offset_data = 0x6764, // GIO_DATA_EXT_HI
.mask = 0x00000100, // 1<<8
.shift = 8,
.off_value = 0,
.on_value = 1,
.direction_value = 1,
.old_val = -1,
},
.fan_control = {
.is_present = 1, // PWM 1
.offset_data = 0x6580, // PWM_CTRL ...
.channel = 0,
.open_drain = 1,
.period = 0x63,
},
.temp_monitor = {
.is_present = 1, // 7425 AVS_RO_REGISTERS_0
.offset_data = 0x32b00, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
.get_temp = get_avs_temperature_74xx,
},
.voltage_monitor = {
.is_present = 1, // 7425 AVS_RO_REGISTERS_0
.offset_data = 0x32b0c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
.get_voltage = get_avs_voltage_74xx,
},
},
{
.name = "GFHD200",
.init = init_gfhd200,
.mmap_base = 0x10400000, // AON_PIN_CTRL ...
.mmap_size = 0x30000,
.leds = {
.led_red = {
.is_present = 1, // GPIO 5
.pinmux_offset = 0x8500, // PIN_MUX_CTRL_0
.pinmux_mask = 0xf0000000,
.pinmux_value = 0x10000000, // LED_LD1 (segment 1 on led digit1)
.offset_data = 0x9018, // GIO_AON_DATA_LO
.mask = 0x00000002, // 1<<1
.shift = 1,
.off_value =1,
.on_value = 0,
.old_val = -1,
},
.led_blue = {
.is_present = 0,
},
.led_activity = {
.is_present = 1, // GPIO 4
.pinmux_offset = 0x8500, // PIN_MUX_CTRL_0
.pinmux_mask = 0x0f000000,
.pinmux_value = 0x01000000, // LED_LD0 (segment 0 on led digit1)
.offset_data = 0x9018, // GIO_AON_DATA_LO
.mask = 0x00000001, // 1<<0
.shift = 0,
.off_value = 1,
.on_value = 0,
.old_val = -1,
},
.led_standby = {
.is_present = 0,
},
},
.reset_button = {
.is_present = 1, // GPIO 3
.offset_direction = 0x9808, // GIO_AON_IODIR_LO
.offset_data = 0x9804, // GIO_AON_DATA_LO
.mask = 0x00000008, // 1<<3
.shift = 3,
.off_value = 0,
.on_value = 1,
.direction_value = 1,
.old_val = -1,
},
.fan_control = {
.is_present = 0,
},
.temp_monitor = {
.is_present = 1, // 7429 AVS_RO_REGISTERS_0
.offset_data = 0x23300, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
.get_temp = get_avs_temperature_74xx,
},
.voltage_monitor = {
.is_present = 1, // 7429 AVS_RO_REGISTERS_0
.offset_data = 0x2330c, // BCHP_AVS_RO_REGISTERS_0_PVT_1P10V_0_MNTR_STATUS
.get_voltage = get_avs_voltage_74xx,
},
},
{
.name = "GFHD254",
.init = init_gfhd254,
.mmap_base = 0xf0400000, // AON_PIN_CTRL ...
.mmap_size = 0xe0000,
.leds = {
.led_red = {
.is_present = 1, // AON_GPIO_05
.pinmux_offset = 0x10700, // PIN_MUX_CTRL_0
.pinmux_mask = 0x00f00000,
.pinmux_value = 0x00000000, // AON_GPIO_05
.offset_data = 0x17404, // AON_DATA
.mask = 1<<5,
.shift = 5,
.off_value =0,
.on_value = 1,
.old_val = -1,
},
.led_blue = {
.is_present = 0,
},
.led_activity = {
.is_present = 1, // AON_GPIO_04
.pinmux_offset = 0x10700, // PIN_MUX_CTRL_0
.pinmux_mask = 0x000f0000,
.pinmux_value = 0x00000000, // AON_GPIO_04
.offset_data = 0x17404, // LDK_DIGIT1
.mask = 1<<4, // 1<<12
.shift = 4,
.off_value = 0,
.on_value = 1,
.old_val = -1,
},
.led_standby = {
.is_present = 0,
},
.led_brightness = {
.is_present = 1, // GPIO_098
.open_drain = 0,
.offset_data = 0x9000, // PWM_2
.channel = 0,
.old_percent = -1,
.period = 0x65,
},
},
.reset_button = {
.is_present = 1, // GPIO_009
.pinmux_offset = 0x4120, // SUN_TOP_CTRL_PIN_MUX_CTRL_8
.pinmux_mask = 0xf0000000,
.pinmux_value = 0x00000000, // GPIO_009
.offset_direction = 0xa608, // GIO_IODIR_LO
.offset_data = 0xa604, // GIO_DATA_LO
.mask = 0x00000200, // 1<<9
.shift = 9,
.off_value = 0,
.on_value = 1,
.direction_value = 1,
.old_val = -1,
},
.fan_tick = {
.is_present = 1, // GPIO 78
.offset_direction = 0xa648, // GIO_IODIR_EXT_HI
.offset_data = 0xa644, // GIO_DATA_EXT_HI
.mask = 1<<14,
.shift = 14,
.off_value = 0,
.on_value = 1,
.direction_value = 1,
.old_val = -1,
},
.fan_control = {
.is_present = 1, // PWM 3
.offset_data = 0x9000, // PWM_CTRL ...
.channel = 1,
.open_drain = 0,
.period = 0x91,
},
.temp_monitor = {
.is_present = 1, // 7252 AVS_RO_REGISTERS_0
.offset_data = 0xd2200, // BCHP_AVS_RO_REGISTERS_0_PVT_TEMPERATURE_MNTR_STATUS
.get_temp = get_avs_temperature_7252,
},
.voltage_monitor = {
.is_present = 1, // 7252 AVS_RO_REGISTERS_0
.offset_data = 0xd220c, // BCHP_AVS_RO_REGISTERS_0_PVT_1V_0_MNTR_STATUS
.get_voltage = get_avs_voltage_7252,
},
}
};
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);
return NULL;
}
/* set LED/Keypad timings to control LED brightness */
static void init_gfhd200(UNUSED struct platform_info* p) {
volatile uint32_t* reg;
reg = mmap_addr + 0x9034; // LDK_CONTROL
*reg = 0x01; // reset
*reg = 0x18; // ver=1 inv_led=1
reg = mmap_addr + 0x9008; // LDK_PRESCHI, LO (clock divisor)
reg[0] = 0x00;
reg[1] = 0x10; // tick = clock / 0x0010, not sure what clock is
reg = mmap_addr + 0x9010; // LDK_DUTYOFF, ON
reg[0] = 0x40;
reg[1] = 0xc0; // 0x40 off ticks then 0xc0 on ticks == 75% brightness
}
/* set LED/Keypad timings to control LED brightness */
static void init_gfhd254(struct platform_info* p) {
volatile uint32_t* reg;
reg = mmap_addr + 0x17408; // AON_IODIR
reg[0] |= reg[0] & ~(1<<4 | 1<<5); // set gpios to be output
// The fan is connected to PWM3, the register PWM3_CWORD_LSB is set to 1,
// this is the frequency of the PWM, the other pwm register control
// the duty cycle.
reg = mmap_addr + 0x9010; // PWM3_CWORD_MSB
reg[0] = 0x20;
reg[1] = 0x0; // PWM3_CWORD_LSB
// LEDs are connected to PWM2. Setting CWORD_LSB to 0xf to control
// the output freq of the var rate clock.
reg = mmap_addr + 0x9008;
reg[0] = 0x00;
reg[1] = 0x57;
set_pwm(&p->leds.led_brightness, 27);
}
static double get_avs_voltage_7252(struct Voltage* v) {
volatile uint32_t* reg;
uint32_t value, valid, raw_data;
reg = mmap_addr + v->offset_data;
value = *reg;
valid = (value & 0x00000400) >> 10;
raw_data = value & 0x000003ff;
if (!valid) return -1.0;
return ((880.0/1024.0)/(0.7)*raw_data) / 1000;
}
static double get_avs_voltage_74xx(struct Voltage* v) {
volatile uint32_t* reg;
uint32_t value, valid, raw_data;
reg = mmap_addr + v->offset_data;
value = *reg;
// see 7425-PR500-RDS.pdf
valid = (value & 0x00000400) >> 10;
raw_data = value & 0x000003ff;
if (!valid) return -1.0;
return ((990 * raw_data * 8) / (7*1024)) / 1000.0;
}
static double get_avs_temperature_74xx(struct Temp* t) {
volatile uint32_t* reg;
uint32_t value, valid, raw_data;
reg = mmap_addr + t->offset_data;
value = *reg;
// see 7425-PR500-RDS.pdf
valid = (value & 0x00000400) >> 10;
raw_data = value & 0x000003ff;
if (!valid) return -1.0;
return (418000 - (556 * raw_data)) / 1000.0;
}
static double get_avs_temperature_7252(struct Temp* t) {
volatile uint32_t* reg;
uint32_t value, valid, raw_data;
reg = mmap_addr + t->offset_data;
value = *reg;
valid = (value & 0x00000400) >> 10;
raw_data = value & 0x000003ff;
if (!valid) return -1.0;
return 410.04 - (0.48705 * raw_data);
}
/*
platform_init has three steps:
1) Generic platform init. This sets up the mmap.
2) Platform-specific init. Calls the gf* function
corresponding to the passed in platform.
3) GPIO init. Sets up the gpios properly.
*/
int platform_init(struct platform_info* p) {
platform_cleanup();
mmap_fd = open(DEVMEM, O_RDWR);
if (mmap_fd < 0) {
perror(DEVMEM);
return -1;
}
mmap_size = p->mmap_size;
mmap_addr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, mmap_fd, p->mmap_base);
if (mmap_addr == MAP_FAILED) {
perror("mmap");
platform_cleanup();
return -1;
}
if (p->init) {
(*p->init)(p);
}
set_pinmux(&p->leds.led_red);
set_pinmux(&p->leds.led_blue);
set_pinmux(&p->leds.led_activity);
set_pinmux(&p->leds.led_standby);
set_direction(&p->leds.led_red);
set_direction(&p->leds.led_blue);
set_direction(&p->leds.led_activity);
set_direction(&p->leds.led_standby);
set_direction(&p->reset_button);
set_direction(&p->fan_tick);
return 0;
}
void platform_cleanup() {
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;
}
}
void set_gpio(struct Gpio *g, int level) {
volatile uint32_t* reg;
uint32_t value;
if (g->old_val == level) {
// If this is the same value as last time, don't do anything, for two
// reasons:
// 1) If you set the gpio too often, it seems to stay low (the led
// stays off).
// 2) If some process other than us is twiddling a led, this way we
// won't interfere with it.
return;
}
g->old_val = level;
reg = mmap_addr + g->offset_data;
value = *reg;
value &= ~g->mask;
value |= (level ? g->on_value : g->off_value) << g->shift;
*reg = value;
}
int get_gpio(struct Gpio *g) {
volatile uint32_t* reg;
uint32_t value;
reg = mmap_addr + g->offset_data;
value = (*reg & g->mask) >> g->shift;
return (value == g->on_value);
}
/*
Set the PWM duty duty cycle. Percent bounded [0, 100].
The output period of the constant-freq PWM is calculated
by (period_programmed + 1) / Fv, where Fv is the output
of the variable-frequency PWM (in mhz).
Fv is calculated by the following formula:
Fv = (cword) * 2^-16 * 27MHz
cword is the programmed frequency control word.
The fan on lockdown must stay at a constant 23KHz
*/
void set_pwm(struct PwmControl *f, int percent) {
volatile uint32_t* reg;
uint32_t mask0, val0, mask1, val1, on;
if (percent < 0) percent = 0;
if (percent > 100) percent = 100;
if (f->old_percent == percent) return;
f->old_percent = percent;
reg = mmap_addr + f->offset_data;
if (f->channel == 0) {
mask0 = 0xf0; // preserve other channel
val0 = 0x01; // open-drain|start
if (f->open_drain)
val0 |= 0x08;
mask1 = 0x10; // preserve
val1 = 0x01; // constant-freq
on = 6;
} else {
mask0 = 0x0f; // see above
val0 = 0x10;
if (f->open_drain)
val0 |= 0x80;
mask1 = 0x01;
val1 = 0x10;
on = 8;
}
reg[0] = (reg[0] & mask0) | val0;
reg[1] = (reg[1] & mask1) | val1;
reg[on] = (f->period * percent)/100;
reg[on+1] = f->period;
}
void set_direction(struct Gpio *g) {
volatile uint32_t* reg;
uint32_t value;
if (!g->is_present || g->offset_direction == 0)
return;
reg = mmap_addr + g->offset_direction;
value = *reg;
value &= ~g->mask;
value |= g->direction_value << g->shift;
*reg = value;
}
void set_pinmux(struct Gpio *g) {
volatile uint32_t* reg;
uint32_t value;
if (!g->is_present || g->pinmux_offset == 0)
return;
reg = mmap_addr + g->pinmux_offset;
value = *reg;
value &= ~g->pinmux_mask;
value |= g->pinmux_value;
*reg = value;
}
#endif // BROADCOM