diff --git a/ecflash b/ecflash index 9e8f155..67c1a45 160000 --- a/ecflash +++ b/ecflash @@ -1 +1 @@ -Subproject commit 9e8f155bead62bea3d0c16d79c86ef07e1761c0a +Subproject commit 67c1a450b3208ef45816a1b29b85cac92115f3b2 diff --git a/src/board/system76/lemp9/acpi.c b/src/board/system76/lemp9/acpi.c new file mode 100644 index 0000000..aa4f356 --- /dev/null +++ b/src/board/system76/lemp9/acpi.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +static struct Gpio __code ACIN_N = GPIO(B, 0); +static struct Gpio __code LID_SW_N = GPIO(B, 1); + +uint8_t acpi_read(uint8_t addr) { + uint8_t data = 0; + + #define ACPI_8(K, V) \ + case (K): \ + data = (uint8_t)(V); \ + break + + #define ACPI_16(K, V) \ + ACPI_8(K, V); \ + ACPI_8((K) + 1, (V) >> 8) + + #define ACPI_32(K, V) \ + ACPI_16(K, V); \ + ACPI_16((K) + 2, (V) >> 16) + + switch (addr) { + // Lid state and other flags + case 0x03: + if (gpio_get(&LID_SW_N)) { + // Lid is open + data |= 1 << 0; + } + break; + + // Handle AC adapter and battery present + case 0x10: + if (!gpio_get(&ACIN_N)) { + // AC adapter connected + data |= 1 << 0; + } + // BAT0 always connected - TODO + data |= 1 << 2; + break; + + ACPI_16(0x16, battery_design_capacity); + ACPI_16(0x1A, battery_full_capacity); + ACPI_16(0x22, battery_design_voltage); + + // Bypass status test in ACPI - TODO + case 0x26: + data |= 1 << 1; + break; + + ACPI_16(0x2A, battery_current); + ACPI_16(0x2E, battery_remaining_capacity); + ACPI_16(0x32, battery_voltage); + + // Set size of flash (from old firmware) + ACPI_8 (0xE5, 0x80); + } + + DEBUG("acpi_read %02X = %02X\n", addr, data); + return data; +} + + +// If not in debug mode, data is not used, ignore warning +#pragma save +#pragma disable_warning 85 +void acpi_write(uint8_t addr, uint8_t data) { + DEBUG("acpi_write %02X = %02X\n", addr, data); + + switch (addr) { + //TODO + } +} +#pragma restore diff --git a/src/board/system76/lemp9/battery.c b/src/board/system76/lemp9/battery.c new file mode 100644 index 0000000..5a07a2d --- /dev/null +++ b/src/board/system76/lemp9/battery.c @@ -0,0 +1,142 @@ +#include +#include + +int smbus_read(uint8_t address, uint8_t command, uint16_t * data) { + return i2c_get(address, command, (uint8_t *)data, 2); +} + +int smbus_write(uint8_t address, uint8_t command, uint16_t data) { + return i2c_set(address, command, (uint8_t *)&data, 2); +} + +// ChargeOption0 flags +// Low Power Mode Enable +#define SBC_EN_LWPWR ((uint16_t)(1 << 15)) +// Watchdog Timer Adjust +#define SBC_WDTMR_ADJ_175S ((uint16_t)(0b11 << 13)) +// Switching Frequency +#define SBC_PWM_FREQ_800KHZ ((uint16_t)(0b01 << 8)) +// IDCHG Amplifier Gain +#define SBC_IDCHC_GAIN ((uint16_t)(1 << 3)) + +int battery_charger_disable(void) { + int res = 0; + +/* TODO: Grab battery settings + // Set charge option 0 with 175s watchdog + res = smbus_write( + 0x09, + 0x12, + SBC_EN_LWPWR | + SBC_WDTMR_ADJ_175S | + SBC_PWM_FREQ_800KHZ | + SBC_IDCHC_GAIN + ); + + // Disable charge current + res = smbus_write(0x09, 0x14, 0); + if (res < 0) return res; + + // Disable charge voltage + res = smbus_write(0x09, 0x15, 0); + if (res < 0) return res; +*/ + + return 0; +} + +int battery_charger_enable(void) { + int res = 0; + + res = battery_charger_disable(); + if (res < 0) return res; + +/* TODO: Grab battery settings + // Set charge current to ~1.5 A + res = smbus_write(0x09, 0x14, 0x0600); + if (res < 0) return res; + + // Set charge voltage to ~13 V + res = smbus_write(0x09, 0x15, 0x3300); + if (res < 0) return res; + + // Set charge option 0 with watchdog disabled + res = smbus_write( + 0x09, + 0x12, + SBC_EN_LWPWR | + SBC_PWM_FREQ_800KHZ | + SBC_IDCHC_GAIN + ); +*/ + + return 0; +} + +uint16_t battery_temp = 0; +uint16_t battery_voltage = 0; +uint16_t battery_current = 0; +uint16_t battery_charge = 0; +uint16_t battery_remaining_capacity = 0; +uint16_t battery_full_capacity = 0; +uint16_t battery_design_capacity = 0; +uint16_t battery_design_voltage = 0; + +void battery_event(void) { + int res = 0; + + #define command(N, V) { \ + res = smbus_read(0x0B, V, &N); \ + if (res < 0) { \ + N = 0; \ + return; \ + } \ + } + + command(battery_temp, 0x08); + command(battery_voltage, 0x09); + command(battery_current, 0x0A); + command(battery_charge, 0x0D); + command(battery_remaining_capacity, 0x0F); + command(battery_full_capacity, 0x10); + command(battery_design_capacity, 0x18); + command(battery_design_voltage, 0x19); + + #undef command +} + +void battery_debug(void) { + uint16_t data = 0; + int res = 0; + + #define command(N, A, V) { \ + DEBUG(#N ": "); \ + res = smbus_read(A, V, &data); \ + if (res < 0) { \ + DEBUG("ERROR %04X\n", -res); \ + } else { \ + DEBUG("%04X\n", data); \ + } \ + } + + DEBUG("Battery:\n"); + command(Temperature, 0x0B, 0x08); + command(Voltage, 0x0B, 0x09); + command(Current, 0x0B, 0x0A); + command(Charge, 0x0B, 0x0D); + + DEBUG("Charger:\n"); + command(ChargeOption0, 0x09, 0x12); + command(ChargeOption1, 0x09, 0x3B); + command(ChargeOption2, 0x09, 0x38); + command(ChargeOption3, 0x09, 0x37); + command(ChargeCurrent, 0x09, 0x14); + command(ChargeVoltage, 0x09, 0x15); + command(DishargeCurrent, 0x09, 0x39); + command(InputCurrent, 0x09, 0x3F); + command(ProchotOption0, 0x09, 0x3C); + command(ProchotOption1, 0x09, 0x3D); + command(ProchotStatus, 0x09, 0x3A); + + #undef command +} diff --git a/src/board/system76/lemp9/board.mk b/src/board/system76/lemp9/board.mk new file mode 100644 index 0000000..ecd2c32 --- /dev/null +++ b/src/board/system76/lemp9/board.mk @@ -0,0 +1,35 @@ +EC=it8587e + +# Add keymaps to include +INCLUDE+=$(wildcard $(BOARD_DIR)/keymap/*.h) + +# Set log level +# 0 - NONE +# 1 - ERROR +# 2 - WARN +# 3 - INFO +# 4 - DEBUG +# 5 - TRACE +CFLAGS+=-DLEVEL=2 + +# Enable I2C debug on 0x76 +CFLAGS+=-DI2C_DEBUGGER=0x76 + +# Add scratch ROM source +SCRATCH_DIR=$(BOARD_DIR)/scratch +SCRATCH_SRC=$(wildcard $(SCRATCH_DIR)/*.c) +SCRATCH_INCLUDE=$(wildcard $(SCRATCH_DIR)/include/scratch/*.h) $(SCRATCH_DIR)/scratch.mk +SCRATCH_CFLAGS=-I$(SCRATCH_DIR)/include +include $(SCRATCH_DIR)/scratch.mk + +# Include scratch header in main firmware +CFLAGS+=-I$(BUILD)/include +INCLUDE+=$(BUILD)/include/scratch.h + +flash: $(BUILD)/ec.rom + cargo build --manifest-path ecflash/Cargo.toml --example flash --release + sudo ecflash/target/release/examples/flash $< + +isp: $(BUILD)/ec.rom + cargo build --manifest-path ecflash/Cargo.toml --example isp --release + sudo ecflash/target/release/examples/isp $< diff --git a/src/board/system76/lemp9/gctrl.c b/src/board/system76/lemp9/gctrl.c new file mode 100644 index 0000000..d66775f --- /dev/null +++ b/src/board/system76/lemp9/gctrl.c @@ -0,0 +1,7 @@ +#include + +void gctrl_init(void) { + SPCTRL1 = 0x03; + BADRSEL = 0; + RSTS = 0x84; +} diff --git a/src/board/system76/lemp9/gpio.c b/src/board/system76/lemp9/gpio.c new file mode 100644 index 0000000..a1268c5 --- /dev/null +++ b/src/board/system76/lemp9/gpio.c @@ -0,0 +1,232 @@ +#include +#include + +void gpio_init() { + // Enable LPC reset on GPD2 + GCR = 0x04; + + // Set GPIO data + GPDRA = 0; + GPDRB = (1 << 0); + GPDRC = 0; + GPDRD = (1 << 5) | (1 << 4) | (1 << 3); + GPDRE = 0; + GPDRF = 0xC0; // (1 << 7) | (1 << 6) + GPDRG = 0; + GPDRH = 0; + GPDRI = 0; + GPDRJ = 0; + + // Set GPIO control + // EC_SSD_LED# + GPCRA0 = GPIO_IN; + // KBC_BEEP + GPCRA1 = GPIO_ALT; + // CPU_FAN + GPCRA2 = GPIO_ALT; + // PCH_DPWROK_EC + GPCRA3 = GPIO_IN; + // PCH_PWROK_EC + GPCRA4 = GPIO_OUT; + // LED_BAT_CHG + GPCRA5 = GPIO_OUT | GPIO_UP; + // LED_BAT_FULL + GPCRA6 = GPIO_OUT | GPIO_UP; + // LED_PWR + GPCRA7 = GPIO_OUT | GPIO_UP; + // NC + GPCRB0 = GPIO_OUT | GPIO_UP; + // H_PROCHOT_EC + GPCRB1 = GPIO_OUT | GPIO_UP; + // LAN_WAKEUP# + GPCRB2 = GPIO_IN | GPIO_UP; + // SMC_BAT + GPCRB3 = GPIO_ALT; + // SMD_BAT + GPCRB4 = GPIO_ALT; + // GA20 + GPCRB5 = GPIO_OUT | GPIO_UP; + // AC_IN# + GPCRB6 = GPIO_IN | GPIO_UP; + // FP_RST# + GPCRB7 = GPIO_IN; + // ALL_SYS_PWRGD + GPCRC0 = GPIO_IN; + // SMC_VGA_THERM + GPCRC1 = GPIO_IN | GPIO_UP; + // SMD_VGA_THERM + GPCRC2 = GPIO_IN | GPIO_UP; + // KSO16 (Darter) + GPCRC3 = GPIO_IN; + // CNVI_DET# + GPCRC4 = GPIO_OUT | GPIO_UP; + // KSO17 (Darter) + GPCRC5 = GPIO_IN; + // PM_PWROK + GPCRC6 = GPIO_OUT; + // LED_ACIN + GPCRC7 = GPIO_OUT | GPIO_UP; + // PWR_SW# + GPCRD0 = GPIO_IN | GPIO_UP; + // LID_SW# + GPCRD1 = GPIO_IN | GPIO_UP; + // BUF_PLT_RST# + GPCRD2 = GPIO_ALT; + // SMI# + GPCRD3 = GPIO_IN; + // SCI# + GPCRD4 = GPIO_IN; + // PWR_BTN# + GPCRD5 = GPIO_OUT | GPIO_UP; + // CPU_FANSEN + GPCRD6 = GPIO_IN; + // SUSWARN# + GPCRD7 = GPIO_IN; + // SWI# + GPCRE0 = GPIO_OUT | GPIO_UP; + // EC_EN + GPCRE1 = GPIO_OUT | GPIO_UP; + // PCH_SLP_WLAN#_R + GPCRE2 = GPIO_IN; + // VA_EC_EN + GPCRE3 = GPIO_OUT; + // DD_ON + GPCRE4 = GPIO_OUT | GPIO_DOWN; + // EC_RSMRST# + GPCRE5 = GPIO_OUT; + // SB_KBCRST# + GPCRE6 = GPIO_OUT | GPIO_UP; + // AC_PRESENT / PM_PWROK + GPCRE7 = GPIO_OUT | GPIO_UP; + // 80CLK + GPCRF0 = GPIO_IN; + // USB_CHARGE_EN + GPCRF1 = GPIO_OUT | GPIO_UP; + // 3IN1 + GPCRF2 = GPIO_IN | GPIO_UP; + // BT_EN + GPCRF3 = GPIO_OUT | GPIO_UP; + // TP_CLK + GPCRF4 = GPIO_ALT; + // TP_DATA + GPCRF5 = GPIO_ALT; + // H_PECI + GPCRF6 = GPIO_ALT; + // USB_PWR_EN# + GPCRF7 = GPIO_OUT; + // CCD_EN + GPCRG0 = GPIO_OUT | GPIO_UP; + // 3G_EN + GPCRG1 = GPIO_OUT | GPIO_UP; + // VDD3 + GPCRG2 = GPIO_OUT; + // HSPI_CE# + GPCRG3 = GPIO_ALT; + // HSPI_MSI + GPCRG4 = GPIO_ALT; + // HSPI_MSO + GPCRG5 = GPIO_ALT; + // AIRPLAN_LED# + GPCRG6 = GPIO_OUT | GPIO_UP; + // HCPI_SCLK + GPCRG7 = GPIO_ALT; + // EC_CLKRUN# + GPCRH0 = GPIO_ALT; + // SUSC#_PCH + GPCRH1 = GPIO_IN; + // BKL_EN + GPCRH2 = GPIO_OUT | GPIO_UP; + // NC + GPCRH3 = GPIO_OUT | GPIO_UP; + // VR_ON + GPCRH4 = GPIO_IN; + // WLAN_EN + GPCRH5 = GPIO_OUT | GPIO_UP; + // SUSB#_PCH + GPCRH6 = GPIO_IN; + // Unknown + GPCRH7 = GPIO_IN; + // BAT_DET + GPCRI0 = GPIO_ALT; + // BAT_VOLT + GPCRI1 = GPIO_ALT; + // SLP_SUS# + GPCRI2 = GPIO_IN; + // THERM_VOLT + GPCRI3 = GPIO_ALT; + // TOTAL_CUR + GPCRI4 = GPIO_ALT; + // AZ_RST#_EC + GPCRI5 = GPIO_IN; + // LIGHT_KB_DET# + GPCRI6 = GPIO_IN; + // MODEL_ID + GPCRI7 = GPIO_IN; + // SUS_PWR_ACK + GPCRJ0 = GPIO_IN | GPIO_DOWN; + // KBC_MUTE# + GPCRJ1 = GPIO_IN; + // ME_WE + GPCRJ2 = GPIO_OUT; + // SOC_TYPE + GPCRJ3 = GPIO_IN; + // WLAN_PWR_EN + GPCRJ4 = GPIO_OUT | GPIO_UP; + // KBLIGHT_ADJ + GPCRJ5 = GPIO_OUT; + // 3G_PWR_EN + GPCRJ6 = GPIO_OUT | GPIO_UP; + // NC + GPCRJ7 = GPIO_IN; + // LPC_AD0 + GPCRM0 = GPIO_ALT; + // LPC_AD1 + GPCRM1 = GPIO_ALT; + // LPC_AD2 + GPCRM2 = GPIO_ALT; + // LPC_AD3 + GPCRM3 = GPIO_ALT; + // PCLK_KBC + GPCRM4 = GPIO_ALT; + // LPC_FRAME# + GPCRM5 = GPIO_ALT; + // SERIRQ + GPCRM6 = GPIO_ALT; +} + +#if GPIO_DEBUG +void gpio_debug_bank( + char * bank, + uint8_t data, + uint8_t mirror, + uint8_t pot, + volatile uint8_t * control +) { + for(char i = 0; i < 8; i++) { + DEBUG( + "%s%d:\n\tdata %d\n\tmirror %d\n\tpot %d\n\tcontrol %02X\n", + bank, + i, + (data >> i) & 1, + (mirror >> i) & 1, + (pot >> i) & 1, + *(control + i) + ); + } +} + +void gpio_debug(void) { + #define bank(BANK) gpio_debug_bank(#BANK, GPDR ## BANK, GPDMR ## BANK, GPOT ## BANK, &GPCR ## BANK ## 0) + bank(A); + bank(B); + bank(C); + bank(D); + bank(E); + bank(F); + bank(G); + bank(H); + bank(I); + bank(J); + #undef bank +} +#endif diff --git a/src/board/system76/lemp9/include/board/acpi.h b/src/board/system76/lemp9/include/board/acpi.h new file mode 100644 index 0000000..4a6df82 --- /dev/null +++ b/src/board/system76/lemp9/include/board/acpi.h @@ -0,0 +1,9 @@ +#ifndef _BOARD_ACPI_H +#define _BOARD_ACPI_H + +#include + +uint8_t acpi_read(uint8_t addr); +void acpi_write(uint8_t addr, uint8_t data); + +#endif // _BOARD_ACPI_H diff --git a/src/board/system76/lemp9/include/board/battery.h b/src/board/system76/lemp9/include/board/battery.h new file mode 100644 index 0000000..fcb9b2d --- /dev/null +++ b/src/board/system76/lemp9/include/board/battery.h @@ -0,0 +1,20 @@ +#ifndef _BOARD_BATTERY_H +#define _BOARD_BATTERY_H + +#include + +extern uint16_t battery_temp; +extern uint16_t battery_voltage; +extern uint16_t battery_current; +extern uint16_t battery_charge; +extern uint16_t battery_remaining_capacity; +extern uint16_t battery_full_capacity; +extern uint16_t battery_design_capacity; +extern uint16_t battery_design_voltage; + +int battery_charger_disable(void); +int battery_charger_enable(void); +void battery_event(void); +void battery_debug(void); + +#endif // _BOARD_BATTERY_H diff --git a/src/board/system76/lemp9/include/board/cpu.h b/src/board/system76/lemp9/include/board/cpu.h new file mode 100644 index 0000000..1ed4f4d --- /dev/null +++ b/src/board/system76/lemp9/include/board/cpu.h @@ -0,0 +1,6 @@ +#ifndef _BOARD_CPU_H +#define _BOARD_CPU_H + +#define F_CPU 9200000ULL + +#endif // _BOARD_CPU_H diff --git a/src/board/system76/lemp9/include/board/gctrl.h b/src/board/system76/lemp9/include/board/gctrl.h new file mode 100644 index 0000000..80eae12 --- /dev/null +++ b/src/board/system76/lemp9/include/board/gctrl.h @@ -0,0 +1,8 @@ +#ifndef _BOARD_GCTRL_H +#define _BOARD_GCTRL_H + +#include + +void gctrl_init(void); + +#endif // _BOARD_GCTRL_H diff --git a/src/board/system76/lemp9/include/board/gpio.h b/src/board/system76/lemp9/include/board/gpio.h new file mode 100644 index 0000000..c0728d3 --- /dev/null +++ b/src/board/system76/lemp9/include/board/gpio.h @@ -0,0 +1,15 @@ +#ifndef _BOARD_GPIO_H +#define _BOARD_GPIO_H + +#include + +#define GPIO_ALT 0x00 +#define GPIO_IN 0x80 +#define GPIO_OUT 0x40 +#define GPIO_UP 0x04 +#define GPIO_DOWN 0x02 + +void gpio_init(void); +void gpio_debug(void); + +#endif // _BOARD_GPIO_H diff --git a/src/board/system76/lemp9/include/board/kbc.h b/src/board/system76/lemp9/include/board/kbc.h new file mode 100644 index 0000000..17765b8 --- /dev/null +++ b/src/board/system76/lemp9/include/board/kbc.h @@ -0,0 +1,15 @@ +#ifndef _BOARD_KBC_H +#define _BOARD_KBC_H + +#include + +#include + +extern bool kbc_first; +extern bool kbc_second; + +void kbc_init(void); +bool kbc_scancode(struct Kbc * kbc, uint16_t key, bool pressed); +void kbc_event(struct Kbc * kbc); + +#endif // _BOARD_KBC_H diff --git a/src/board/system76/lemp9/include/board/kbscan.h b/src/board/system76/lemp9/include/board/kbscan.h new file mode 100644 index 0000000..f9327ad --- /dev/null +++ b/src/board/system76/lemp9/include/board/kbscan.h @@ -0,0 +1,13 @@ +#ifndef _BOARD_KBSCAN_H +#define _BOARD_KBSCAN_H + +#include + +#include + +extern bool kbscan_enabled; + +void kbscan_init(void); +void kbscan_event(void); + +#endif // _BOARD_KBSCAN_H diff --git a/src/board/system76/lemp9/include/board/keymap.h b/src/board/system76/lemp9/include/board/keymap.h new file mode 100644 index 0000000..11aa2e0 --- /dev/null +++ b/src/board/system76/lemp9/include/board/keymap.h @@ -0,0 +1,207 @@ +#ifndef _BOARD_KEYMAP_H +#define _BOARD_KEYMAP_H + +#include + +// Keymap output pins +#define KM_OUT 16 +// Keymap input pins +#define KM_IN 8 +// Keymap layers (normal, Fn) +#define KM_LAY 2 + +uint16_t keymap(int output, int input, int layer); + +uint16_t keymap_translate(uint16_t key); + +// Key types +#define KT_MASK (0xF000) + +// Normal keys +#define KT_NORMAL (0x0000) + +// Layer selection +#define KT_FN (0x1000) + +// SCI +#define KT_SCI (0x2000) + +#define SCI_BRIGHTNESS_DOWN (0x11) +#define SCI_BRIGHTNESS_UP (0x12) +#define SCI_AIRPLANE_MODE (0x14) +#define SCI_SUSPEND (0x15) + +// See http://www.techtoys.com.hk/Downloads/Download/Microchip/PS2_driver/ScanCode.pdf + +// Should send 0xE0 before scancode bytes +#define K_E0 (0x0100) + +// Hotkeys + +#define K_PLAY_PAUSE (K_E0 | 0x34) +#define K_MUTE (K_E0 | 0x23) +#define K_VOLUME_DOWN (K_E0 | 0x21) +#define K_VOLUME_UP (K_E0 | 0x32) +// Custom scancode +#define K_TOUCHPAD (K_E0 | 0x63) + +// Function keys + +#define K_F1 (0x05) +#define K_F2 (0x06) +#define K_F3 (0x04) +#define K_F4 (0x0C) +#define K_F5 (0x03) +#define K_F6 (0x0B) +#define K_F7 (0x83) +#define K_F8 (0x0A) +#define K_F9 (0x01) +#define K_F10 (0x09) +#define K_F11 (0x78) +#define K_F12 (0x07) + +// Number keys + +#define K_0 (0x45) +#define K_1 (0x16) +#define K_2 (0x1E) +#define K_3 (0x26) +#define K_4 (0x25) +#define K_5 (0x2E) +#define K_6 (0x36) +#define K_7 (0x3D) +#define K_8 (0x3E) +#define K_9 (0x46) + +// Letter keys + +#define K_A (0x1C) +#define K_B (0x32) +#define K_C (0x21) +#define K_D (0x23) +#define K_E (0x24) +#define K_F (0x2B) +#define K_G (0x34) +#define K_H (0x33) +#define K_I (0x43) +#define K_J (0x3B) +#define K_K (0x42) +#define K_L (0x4B) +#define K_M (0x3A) +#define K_N (0x31) +#define K_O (0x44) +#define K_P (0x4D) +#define K_Q (0x15) +#define K_R (0x2D) +#define K_S (0x1B) +#define K_T (0x2C) +#define K_U (0x3C) +#define K_V (0x2A) +#define K_W (0x1D) +#define K_X (0x22) +#define K_Y (0x35) +#define K_Z (0x1A) + +// Special keys + +// Escape key +#define K_ESC (0x76) + +//TODO: Print screen, scroll lock, pause + +// Tick/tilde key +#define K_TICK (0x0E) +// Minus/underline key +#define K_MINUS (0x4E) +// Equals/plus key +#define K_EQUALS (0x55) +// Backspace key +#define K_BKSP (0x66) + +// Tab key +#define K_TAB (0x0D) +// Bracket open key +#define K_BRACE_OPEN (0x54) +// Bracket close key +#define K_BRACE_CLOSE (0x5B) +// Backslash/pipe key +#define K_BACKSLASH (0x5D) + +// Capslock +#define K_CAPS (0x58) +// Semicolon key +#define K_SEMICOLON (0x4C) +// Quote key +#define K_QUOTE (0x52) +// Enter key +#define K_ENTER (0x5A) + +// Left shift +#define K_LEFT_SHIFT (0x12) +// Comma key +#define K_COMMA (0x41) +// Period key +#define K_PERIOD (0x49) +// Slash key +#define K_SLASH (0x4A) +// Right shift +#define K_RIGHT_SHIFT (0x59) + +// Left control key +#define K_LEFT_CTRL (0x14) +// Left super key +#define K_LEFT_SUPER (K_E0 | 0x1F) +// Left alt key +#define K_LEFT_ALT (0x11) +// Space key +#define K_SPACE (0x29) +// Right alt key +#define K_RIGHT_ALT (K_E0 | 0x11) +// Right super key +#define K_RIGHT_SUPER (K_E0 | 0x27) +// Application key +#define K_APP (K_E0 | 0x2F) +// Right control key +#define K_RIGHT_CTRL (K_E0 | 0x14) + +// Arrow keys and related + +// Insert key +#define K_INSERT (K_E0 | 0x70) +// Delete key +#define K_DEL (K_E0 | 0x71) +// Home key +#define K_HOME (K_E0 | 0x6C) +// End key +#define K_END (K_E0 | 0x69) +// Page up key +#define K_PGUP (K_E0 | 0x7D) +// Page down key +#define K_PGDN (K_E0 | 0x7A) + +#define K_UP (K_E0 | 0x75) +#define K_LEFT (K_E0 | 0x6B) +#define K_DOWN (K_E0 | 0x72) +#define K_RIGHT (K_E0 | 0x74) + +// Numpad + +#define K_NUM_LOCK (0x77) +#define K_NUM_SLASH (K_E0 | 0x4A) +#define K_NUM_ASTERISK (0x7C) +#define K_NUM_MINUS (0x7B) +#define K_NUM_PLUS (0x79) +#define K_NUM_PERIOD (0x71) +#define K_NUM_ENTER (K_E0 | 0x5A) +#define K_NUM_0 (0x70) +#define K_NUM_1 (0x69) +#define K_NUM_2 (0x72) +#define K_NUM_3 (0x7A) +#define K_NUM_4 (0x6B) +#define K_NUM_5 (0x73) +#define K_NUM_6 (0x74) +#define K_NUM_7 (0x6C) +#define K_NUM_8 (0x75) +#define K_NUM_9 (0x7D) + +#endif // _BOARD_KEYMAP_H diff --git a/src/board/system76/lemp9/include/board/peci.h b/src/board/system76/lemp9/include/board/peci.h new file mode 100644 index 0000000..a71c04b --- /dev/null +++ b/src/board/system76/lemp9/include/board/peci.h @@ -0,0 +1,14 @@ +#ifndef _BOARD_PECI_H +#define _BOARD_PECI_H + +#include + +extern int16_t peci_offset; +extern int16_t peci_temp; +extern uint8_t peci_duty; +extern uint8_t peci_tcontrol; +extern uint8_t peci_tjmax; + +void peci_event(void); + +#endif // _BOARD_PECI_H diff --git a/src/board/system76/lemp9/include/board/pmc.h b/src/board/system76/lemp9/include/board/pmc.h new file mode 100644 index 0000000..65ea613 --- /dev/null +++ b/src/board/system76/lemp9/include/board/pmc.h @@ -0,0 +1,10 @@ +#ifndef _BOARD_PMC_H +#define _BOARD_PMC_H + +#include + +void pmc_init(void); +bool pmc_sci(struct Pmc * pmc, uint8_t sci); +void pmc_event(struct Pmc * pmc); + +#endif // _BOARD_PMC_H diff --git a/src/board/system76/lemp9/include/board/pnp.h b/src/board/system76/lemp9/include/board/pnp.h new file mode 100644 index 0000000..53dbb68 --- /dev/null +++ b/src/board/system76/lemp9/include/board/pnp.h @@ -0,0 +1,6 @@ +#ifndef _BOARD_PNP_H +#define _BOARD_PNP_H + +void pnp_enable(void); + +#endif // _BOARD_PNP_H diff --git a/src/board/system76/lemp9/include/board/power.h b/src/board/system76/lemp9/include/board/power.h new file mode 100644 index 0000000..504ed64 --- /dev/null +++ b/src/board/system76/lemp9/include/board/power.h @@ -0,0 +1,6 @@ +#ifndef _BOARD_POWER_H +#define _BOARD_POWER_H + +void power_event(void); + +#endif // _BOARD_POWER_H diff --git a/src/board/system76/lemp9/include/board/ps2.h b/src/board/system76/lemp9/include/board/ps2.h new file mode 100644 index 0000000..34ad6ac --- /dev/null +++ b/src/board/system76/lemp9/include/board/ps2.h @@ -0,0 +1,8 @@ +#ifndef _BOARD_PS2_H +#define _BOARD_PS2_H + +#include + +void ps2_init(void); + +#endif // _BOARD_PS2_H diff --git a/src/board/system76/lemp9/include/board/pwm.h b/src/board/system76/lemp9/include/board/pwm.h new file mode 100644 index 0000000..256fde8 --- /dev/null +++ b/src/board/system76/lemp9/include/board/pwm.h @@ -0,0 +1,8 @@ +#ifndef _BOARD_PWM_H +#define _BOARD_PWM_H + +#include + +void pwm_init(void); + +#endif // _BOARD_PWM_H diff --git a/src/board/system76/lemp9/include/board/scratch.h b/src/board/system76/lemp9/include/board/scratch.h new file mode 100644 index 0000000..797b92e --- /dev/null +++ b/src/board/system76/lemp9/include/board/scratch.h @@ -0,0 +1,6 @@ +#ifndef _BOARD_SCRATCH_H +#define _BOARD_SCRATCH_H + +void scratch_trampoline(void); + +#endif // _BOARD_SCRATCH_H diff --git a/src/board/system76/lemp9/include/board/smbus.h b/src/board/system76/lemp9/include/board/smbus.h new file mode 100644 index 0000000..437844a --- /dev/null +++ b/src/board/system76/lemp9/include/board/smbus.h @@ -0,0 +1,8 @@ +#ifndef _BOARD_SMBUS_H +#define _BOARD_SMBUS_H + +#include + +void smbus_init(void); + +#endif // _BOARD_SMBUS_H diff --git a/src/board/system76/lemp9/kbc.c b/src/board/system76/lemp9/kbc.c new file mode 100644 index 0000000..a76b80b --- /dev/null +++ b/src/board/system76/lemp9/kbc.c @@ -0,0 +1,253 @@ +#include +#include +#include +#include +#include + +void kbc_init(void) { + // Disable interrupts + *(KBC.irq) = 0; + *(KBC.control) = 0; +} + +#define KBC_TIMEOUT 10000 + +// System flag +static bool kbc_system = false; +// Enable first port - TODO +bool kbc_first = false; +// Enable second port - TODO +bool kbc_second = false; +// Translate from scancode set 2 to scancode set 1 +// for basically no good reason +static bool kbc_translate = true; + +bool kbc_scancode(struct Kbc * kbc, uint16_t key, bool pressed) { + if (!kbc_first) return true; + if (kbc_translate) { + key = keymap_translate(key); + } + if (!key) return true; + switch (key & 0xFF00) { + case K_E0: + TRACE(" E0\n"); + if (!kbc_keyboard(kbc, 0xE0, KBC_TIMEOUT)) return false; + key &= 0xFF; + // Fall through + case 0x00: + if (!pressed) { + if (kbc_translate) { + key |= 0x80; + } else { + TRACE(" F0\n"); + if (!kbc_keyboard(kbc, 0xF0, KBC_TIMEOUT)) return false; + } + } + TRACE(" %02X\n", key); + if (!kbc_keyboard(kbc, (uint8_t)key, KBC_TIMEOUT)) return false; + break; + } + return true; +} + +enum KbcState { + KBC_STATE_NORMAL, + KBC_STATE_WRITE_CONFIG, + KBC_STATE_SET_LEDS, + KBC_STATE_SCANCODE, + KBC_STATE_WRITE_PORT, + KBC_STATE_FIRST_PORT_OUTPUT, + KBC_STATE_SECOND_PORT_OUTPUT, + KBC_STATE_SECOND_PORT_INPUT, +}; + +void kbc_event(struct Kbc * kbc) { + // TODO: state per KBC (we only have one KBC so low priority) + static enum KbcState state = KBC_STATE_NORMAL; + + uint8_t sts = kbc_status(kbc); + if (sts & KBC_STS_IBF) { + uint8_t data = kbc_read(kbc); + if (sts & KBC_STS_CMD) { + TRACE("kbc cmd: %02X\n", data); + + state = KBC_STATE_NORMAL; + switch (data) { + case 0x20: + TRACE(" read configuration byte\n"); + uint8_t config = *kbc->control & 0x03; + if (kbc_system) { + config |= (1 << 2); + } + if (!kbc_first) { + config |= (1 << 4); + } + if (!kbc_second) { + config |= (1 << 5); + } + if (kbc_translate) { + config |= (1 << 6); + } + kbc_keyboard(kbc, config, KBC_TIMEOUT); + break; + case 0x60: + TRACE(" write configuration byte\n"); + state = KBC_STATE_WRITE_CONFIG; + break; + case 0xA7: + TRACE(" disable second port\n"); + kbc_second = false; + break; + case 0xA8: + TRACE(" enable second port\n"); + kbc_second = true; + break; + case 0xA9: + TRACE(" test second port\n"); + // TODO: communicate with touchpad? + kbc_keyboard(kbc, 0x00, KBC_TIMEOUT); + break; + case 0xAA: + TRACE(" test controller\n"); + // Why not pass the test? + kbc_keyboard(kbc, 0x55, KBC_TIMEOUT); + break; + case 0xAB: + TRACE(" test first port\n"); + // We _ARE_ the keyboard, so everything is good. + kbc_keyboard(kbc, 0x00, KBC_TIMEOUT); + break; + case 0xAD: + TRACE(" disable first port\n"); + kbc_first = false; + break; + case 0xAE: + TRACE(" enable first port\n"); + kbc_first = true; + break; + case 0xD1: + TRACE(" write port byte\n"); + state = KBC_STATE_WRITE_PORT; + break; + case 0xD2: + TRACE(" write first port output\n"); + state = KBC_STATE_FIRST_PORT_OUTPUT; + break; + case 0xD3: + TRACE(" write second port output\n"); + state = KBC_STATE_SECOND_PORT_OUTPUT; + break; + case 0xD4: + TRACE(" write second port input\n"); + state = KBC_STATE_SECOND_PORT_INPUT; + break; + } + } else { + TRACE("kbc data: %02X\n", data); + + switch (state) { + case KBC_STATE_NORMAL: + TRACE(" keyboard command\n"); + switch (data) { + case 0xED: + TRACE(" set leds\n"); + state = KBC_STATE_SET_LEDS; + kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT); + break; + case 0xEE: + TRACE(" echo\n"); + // Hey, this is easy. I like easy commands + kbc_keyboard(kbc, 0xEE, KBC_TIMEOUT); + break; + case 0xF0: + TRACE(" get/set scancode\n"); + state = KBC_STATE_SCANCODE; + kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT); + break; + case 0xF2: + TRACE(" identify keyboard\n"); + if (kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT)) { + if (kbc_keyboard(kbc, 0xAB, KBC_TIMEOUT)) { + kbc_keyboard(kbc, 0x83, KBC_TIMEOUT); + } + } + break; + case 0xF4: + TRACE(" enable scanning\n"); + kbscan_enabled = true; + kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT); + break; + case 0xF5: + TRACE(" disable scanning\n"); + kbscan_enabled = false; + kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT); + break; + case 0xFF: + TRACE(" self test\n"); + if (kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT)) { + // Yep, everything is still good, I promise + kbc_keyboard(kbc, 0xAA, KBC_TIMEOUT); + } + break; + } + break; + case KBC_STATE_WRITE_CONFIG: + TRACE(" write configuration byte\n"); + state = KBC_STATE_NORMAL; + uint8_t control = *kbc->control; + if (data & 1) { + control |= 1; + } else { + control &= ~1; + } + if (data & (1 << 1)) { + control |= (1 << 1); + } else { + control &= ~(1 << 1); + } + kbc_system = (bool)(data & (1 << 2)); + kbc_first = (bool)(!(data & (1 << 4))); + kbc_second = (bool)(!(data & (1 << 5))); + kbc_translate = (bool)(data & (1 << 6)); + *kbc->control = control; + break; + case KBC_STATE_SET_LEDS: + TRACE(" set leds\n"); + state = KBC_STATE_NORMAL; + kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT); + break; + case KBC_STATE_SCANCODE: + TRACE(" get/set scancode\n"); + state = KBC_STATE_NORMAL; + #if LEVEL >= LEVEL_TRACE + switch (data) { + case 0x02: + TRACE(" set scancode set 2\n"); + break; + } + #endif + kbc_keyboard(kbc, 0xFA, KBC_TIMEOUT); + break; + case KBC_STATE_WRITE_PORT: + TRACE(" write port byte\n"); + state = KBC_STATE_NORMAL; + break; + case KBC_STATE_FIRST_PORT_OUTPUT: + TRACE(" write first port output\n"); + state = KBC_STATE_NORMAL; + kbc_keyboard(kbc, data, KBC_TIMEOUT); + break; + case KBC_STATE_SECOND_PORT_OUTPUT: + TRACE(" write second port output\n"); + state = KBC_STATE_NORMAL; + kbc_mouse(kbc, data, KBC_TIMEOUT); + break; + case KBC_STATE_SECOND_PORT_INPUT: + TRACE(" write second port input\n"); + state = KBC_STATE_NORMAL; + ps2_write(&PS2_3, &data, 1); + break; + } + } + } +} diff --git a/src/board/system76/lemp9/kbscan.c b/src/board/system76/lemp9/kbscan.c new file mode 100644 index 0000000..48892fc --- /dev/null +++ b/src/board/system76/lemp9/kbscan.c @@ -0,0 +1,161 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +bool kbscan_enabled = false; + +void kbscan_init(void) { + KSOCTRL = 0x05; + KSICTRLR = 0x04; + + // Set all outputs to GPIO mode, low, and inputs + KSOL = 0; + KSOLGCTRL = 0xFF; + KSOLGOEN = 0; + KSOH1 = 0; + KSOHGCTRL = 0xFF; + KSOHGOEN = 0; + KSOH2 = 0; + + // Make sure timer 2 is stopped + T2CON = 0; +} + +void kbscan_event(void) { + static uint8_t kbscan_layer = 0; + uint8_t layer = kbscan_layer; + static uint8_t kbscan_last[KM_OUT] = { 0 }; + + // If timer 2 is finished + if (TF2) { + // Stop timer 2 running + TR2 = 0; + // Clear timer 2 finished flag + TF2 = 0; + } + + int i; + for (i = 0; i < KM_OUT; i++) { + // Set current line as output + if (i < 8) { + KSOLGOEN = 0; + KSOLGOEN = 1 << i; + } else if (i < 16) { + KSOLGOEN = 0; + KSOHGOEN = 1 << (i - 8); + } else if (i == 16) { + KSOLGOEN = 0; + KSOHGOEN = 0; + } else if (i == 17) { + KSOLGOEN = 0; + KSOHGOEN = 0; + } + + // TODO: figure out optimal delay + delay_ticks(10); + + uint8_t new = ~KSI; + uint8_t last = kbscan_last[i]; + if (new != last) { + int j; + for (j = 0; j < KM_IN; j++) { + bool new_b = new & (1 << j); + bool last_b = last & (1 << j); + if (new_b != last_b) { + // If timer 2 is running + if (TR2) { + // Debounce releases + if (!new_b) { + // Restore bit, so that this release can be handled later + new |= (1 << j); + // Skip processing of release + continue; + } + } else { + // Begin debouncing on press + if (new_b) { + // Run timer 2 for 20 ms + // 65536-(20000 * 69 + 89)/90 = 0xC419 + TH2 = 0xC4; + TL2 = 0x19; + TR2 = 1; + } + } + + uint16_t key = keymap(i, j, kbscan_layer); + DEBUG("KB %d, %d, %d = 0x%04X, %d\n", i, j, kbscan_layer, key, new_b); + if (!key) { + WARN("KB %d, %d, %d missing\n", i, j, kbscan_layer); + } + switch (key & KT_MASK) { + case (KT_FN): + if (new_b) layer = 1; + else layer = 0; + break; + case (KT_SCI): + if (new_b) { + uint8_t sci = (uint8_t)(key & 0xFF); + if (!pmc_sci(&PMC_1, sci)) { + // In the case of ignored SCI, reset bit + new &= ~(1 << j); + } + } + break; + case (KT_NORMAL): + if (kbscan_enabled && key) { + kbc_scancode(&KBC, key, new_b); + } + break; + } + } + } + + kbscan_last[i] = new; + } + } + + if (layer != kbscan_layer) { + //TODO: unpress keys before going to scratch rom + + // Unpress all currently pressed keys + for (i = 0; i < KM_OUT; i++) { + uint8_t new = 0; + uint8_t last = kbscan_last[i]; + if (last) { + int j; + for (j = 0; j < KM_IN; j++) { + bool new_b = new & (1 << j); + bool last_b = last & (1 << j); + if (new_b != last_b) { + uint16_t key = keymap(i, j, kbscan_layer); + DEBUG("KB %d, %d, %d = 0x%04X, %d\n", i, j, kbscan_layer, key, new_b); + switch (key & KT_MASK) { + case (KT_NORMAL): + if (kbscan_enabled && key) { + kbc_scancode(&KBC, key, new_b); + } + break; + } + } + } + } + + kbscan_last[i] = new; + } + + kbscan_layer = layer; + } + + // Reset all lines to inputs + KSOLGOEN = 0; + KSOHGOEN = 0; + + // TODO: figure out optimal delay + delay_ticks(10); +} diff --git a/src/board/system76/lemp9/keymap.c b/src/board/system76/lemp9/keymap.c new file mode 100644 index 0000000..67294a9 --- /dev/null +++ b/src/board/system76/lemp9/keymap.c @@ -0,0 +1,37 @@ +#include +#include + +// Set the desired keymap here +#include "keymap/default.h" + +uint16_t keymap(int output, int input, int layer) { + if (output < KM_OUT && input < KM_IN && layer < KM_LAY) { + return KEYMAP[output][input][layer]; + } else { + return 0; + } +} + +// https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html#ss10.3 +static uint8_t __code lookup[256] = { +0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58, 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59, +0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a, 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b, +0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c, 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d, +0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e, 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f, +0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60, 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61, +0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e, 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76, +0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b, 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f, +0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45, 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54, +0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, +0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, +0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, +0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, +0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, +0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, +0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, +0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +}; + +uint16_t keymap_translate(uint16_t key) { + return (key & 0xFF00) | lookup[(key & 0xFF)]; +} diff --git a/src/board/system76/lemp9/keymap/default.h b/src/board/system76/lemp9/keymap/default.h new file mode 100644 index 0000000..f8cd62a --- /dev/null +++ b/src/board/system76/lemp9/keymap/default.h @@ -0,0 +1,166 @@ +// Default layout - http://www.keyboard-layout-editor.com/#/gists/6aec6d441a039b76ec0895bd6bbda68d + +#define K(V) {V, V} + +uint16_t __code KEYMAP[KM_OUT][KM_IN][KM_LAY] = { + { // 0 + K(0), // 0 + K(0), // 1 + K(0), // 2 + K(0), // 3 + K(0), // 4 + K(0), // 5 + K(K_LEFT_SUPER), // 6 + K(0), // 7 + }, + { // 1 + K(0), // 0 + K(0), // 1 + K(0), // 2 + K(0), // 3 + K(0), // 4 + K(0), // 5 + K(K_LEFT_CTRL), // 6 + K(K_RIGHT_CTRL), // 7 + }, + { // 2 + K(0), // 0 + K(0), // 1 + K(0), // 2 + K(0), // 3 + K(0), // 4 + K(0), // 5 + K(K_LEFT_ALT), // 6 + K(K_RIGHT_ALT), // 7 + }, + { // 3 + K(0), // 0 + K(0), // 1 + K(0), // 2 + K(0), // 3 + K(0), // 4 + K(0), // 5 + K(K_LEFT_SHIFT), // 6 + K(K_RIGHT_SHIFT), // 7 + }, + { // 4 + K(K_X), // 0 + K(K_Z), // 1 + K(K_CAPS), // 2 + K(K_A), // 3 + K(K_TAB), // 4 + K(0), // 5 + K(K_SPACE), // 6 + K(K_Q), // 7 + }, + { // 5 + K(K_V), // 0 + K(K_C), // 1 + K(0), // 2 + K(K_D), // 3 + K(0), // 4 + K(0), // 5 + K(0), // 6 + K(K_E), // 7 + }, + { // 6 + K(KT_FN), // 0 + K(K_B), // 1 + K(K_F), // 2 + K(K_G), // 3 + K(K_R), // 4 + K(K_T), // 5 + K(0), // 6 + K(0), // 7 + }, + { // 7 + K(0), // 0 + K(K_N), // 1 + K(K_H), // 2 + K(K_Y), // 3 + K(K_2), // 4 + K(K_1), // 5 + {K_TICK, K_PLAY_PAUSE}, // 6 + K(K_ESC), // 7 + }, + { // 8 + K(K_I), // 0 + K(K_M), // 1 + K(K_U), // 2 + K(K_5), // 3 + K(K_4), // 4 + K(K_3), // 5 + K(K_F2), // 6 + {K_F1, K_TOUCHPAD}, // 7 + }, + { // 9 + K(K_W), // 0 + K(K_J), // 1 + K(K_K), // 2 + K(0), // 3 + K(K_7), // 4 + K(K_6), // 5 + K(K_F4), // 6 + {K_F3, K_MUTE}, // 7 + }, + { // 10 + K(K_S), // 0 + K(K_COMMA), // 1 + K(K_L), // 2 + K(K_O), // 3 + K(K_9), // 4 + K(K_8), // 5 + {K_F6, K_VOLUME_UP}, // 6 + {K_F5, K_VOLUME_DOWN}, // 7 + }, + { // 11 + K(0), // 0 + K(K_UP), // 1 + K(K_PERIOD), // 2 + K(K_SEMICOLON), // 3 + K(K_P), // 4 + K(K_0), // 5 + {K_F8, KT_SCI | SCI_BRIGHTNESS_DOWN}, // 6 + K(K_F7), // 7 + }, + { // 12 + K(0), // 0 + K(0), // 1 + K(K_SLASH), // 2 + K(K_QUOTE), // 3 + K(K_BRACE_OPEN), // 4 + K(K_MINUS), // 5 + K(K_F10), // 6 + {K_F9, KT_SCI | SCI_BRIGHTNESS_UP}, // 7 + }, + { // 13 + K(0), // 0 + K(0), // 1 + K(0 /* TODO: PrtSc/SysRq */), // 2 + K(K_BRACE_CLOSE), // 3 + K(K_EQUALS), // 4 + K(K_RIGHT), // 5 + K(0 /* TODO: Pause/Break */), // 6 + {K_F11, KT_SCI | SCI_AIRPLANE_MODE}, // 7 + }, + { // 14 + K(0), // 0 + K(K_BACKSLASH), // 1 + K(K_LEFT), // 2 + K(K_BKSP), // 3 + K(K_PGUP), // 4 + K(K_INSERT), // 5 + K(K_HOME), // 6 + K(K_DEL), // 7 + }, + { // 15 + K(K_APP), // 0 + K(0), // 1 + K(K_ENTER), // 2 + K(0), // 3 + K(K_DOWN), // 4 + {K_F12, KT_SCI | SCI_SUSPEND}, // 5 + K(K_END), // 6 + K(K_PGDN), // 7 + }, +}; diff --git a/src/board/system76/lemp9/main.c b/src/board/system76/lemp9/main.c new file mode 100644 index 0000000..a34ae22 --- /dev/null +++ b/src/board/system76/lemp9/main.c @@ -0,0 +1,214 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +uint8_t main_cycle = 0; + +void external_0(void) __interrupt(0) { + TRACE("external_0\n"); +} + +void timer_0(void) __interrupt(1) { + TRACE("timer_0\n"); +} + +void external_1(void) __interrupt(2) { + TRACE("external_1\n"); +} + +void timer_1(void) __interrupt(3) { + TRACE("timer_1\n"); +} + +void serial(void) __interrupt(4) { + TRACE("serial\n"); +} + +void timer_2(void) __interrupt(5) { + TRACE("timer_2\n"); +} + +void init(void) { + gpio_init(); + gctrl_init(); + kbc_init(); + pmc_init(); + kbscan_init(); + pwm_init(); + smbus_init(); + + //TODO: INTC, PECI + + // Allow PECI pin to be used + GCR2 |= (1 << 4); +} + +void ac_adapter() { + static struct Gpio __code ACIN_N = GPIO(B, 0); + static struct Gpio __code LED_ACIN = GPIO(C, 7); + + static bool send_sci = true; + static bool last = true; + + // Check if the adapter line goes low + bool new = gpio_get(&ACIN_N); + // Set ACIN LED + gpio_set(&LED_ACIN, !new); + + // If there has been a change, print + if (new != last) { + DEBUG("Power adapter "); + if (new) { + DEBUG("unplugged\n"); + battery_charger_disable(); + } else { + DEBUG("plugged in\n"); + battery_charger_enable(); + } + battery_debug(); + + // Reset main loop cycle to force reading PECI and battery + main_cycle = 0; + + // Send SCI to update AC and battery information + send_sci = true; + } + + if (send_sci) { + // Send SCI 0x16 for AC detect event + if (pmc_sci(&PMC_1, 0x16)) { + send_sci = false; + } + } + + last = new; +} + +void touchpad_event(struct Ps2 * ps2) { + if (kbc_second) { + *(ps2->control) = 0x07; + } else { + ps2_reset(ps2); + } + + uint8_t status = *(ps2->status); + *(ps2->status) = status; + if (status & (1 << 3)) { + uint8_t data = *(ps2->data); + TRACE("touchpad: %02X\n", data); + kbc_mouse(&KBC, data, 1000); + } +} + +void lid_event(void) { + static struct Gpio __code LID_SW_N = GPIO(B, 1); + + static bool send_sci = true; + static bool last = true; + + // Check if the adapter line goes low + bool new = gpio_get(&LID_SW_N); + // If there has been a change, print + if (new != last) { + DEBUG("Lid "); + if (new) { + DEBUG("open\n"); + + //TODO: send SWI if needed + } else { + DEBUG("closed\n"); + } + + // Send SCI + send_sci = true; + } + + if (send_sci) { + // Send SCI 0x1B for lid event + if (pmc_sci(&PMC_1, 0x1B)) { + send_sci = false; + } + } + + last = new; +} + +void main(void) { + init(); + + INFO("\n"); + + static struct Gpio __code SMI_N = GPIO(D, 4); + static struct Gpio __code SCI_N = GPIO(D, 3); + static struct Gpio __code SWI_N = GPIO(B, 5); + static struct Gpio __code SB_KBCRST_N = GPIO(E, 6); + static struct Gpio __code BT_EN = GPIO(F, 3); + static struct Gpio __code USB_PWR_EN_N = GPIO(E, 3); + static struct Gpio __code CCD_EN = GPIO(D, 1); + static struct Gpio __code PM_CLKRUN_N = GPIO(H, 0); + static struct Gpio __code BKL_EN = GPIO(H, 2); + static struct Gpio __code WLAN_EN = GPIO(G, 1); + static struct Gpio __code WLAN_PWR_EN = GPIO(A, 3); + +#if GPIO_DEBUG + gpio_debug(); +#endif + + // Allow CPU to boot + gpio_set(&SB_KBCRST_N, true); + // Allow backlight to be turned on + gpio_set(&BKL_EN, true); + // Enable camera + gpio_set(&CCD_EN, true); + // Enable wireless + gpio_set(&BT_EN, true); + gpio_set(&WLAN_EN, true); + gpio_set(&WLAN_PWR_EN, true); + // Enable right USB port + gpio_set(&USB_PWR_EN_N, false); + // Assert SMI#, SCI#, and SWI# + gpio_set(&SCI_N, true); + gpio_set(&SMI_N, true); + gpio_set(&SWI_N, true); + + INFO("Hello from System76 EC for %s!\n", xstr(__BOARD__)); + + for(main_cycle = 0; ; main_cycle++) { + // Enables or disables battery charging based on AC adapter + ac_adapter(); + // Handle power states + power_event(); + // Scans keyboard and sends keyboard packets + kbscan_event(); + // Passes through touchpad packets + touchpad_event(&PS2_3); + // Handle lid close/open + lid_event(); + // Checks for keyboard/mouse packets from host + kbc_event(&KBC); + // Only run the following once out of every 256 loops + if (main_cycle == 0) { + // Updates fan status and temps + peci_event(); + // Updates battery status + battery_event(); + } + // Handles ACPI communication + pmc_event(&PMC_1); + } +} diff --git a/src/board/system76/lemp9/peci.c b/src/board/system76/lemp9/peci.c new file mode 100644 index 0000000..6d075ae --- /dev/null +++ b/src/board/system76/lemp9/peci.c @@ -0,0 +1,142 @@ +#include + +#include +#include +#include + +// Tjunction = 100C for i7-8565U (and probably the same for all WHL-U) +#define T_JUNCTION 100 + +int16_t peci_offset = 0; +int16_t peci_temp = 0; +uint8_t peci_duty = 0; +uint8_t peci_tcontrol = 0; +uint8_t peci_tjmax = T_JUNCTION; +static bool peci_config_loaded = false; + +#define PECI_TEMP(X) (((int16_t)(X)) << 6) + +// Read tjmax using index 16 of RdPkgConfig +static void peci_config(void) { + // Wait for completion + while (HOSTAR & 1) {} + // Clear status + HOSTAR = HOSTAR; + + // Enable PECI, clearing data fifo's + HOCTLR = (1 << 5) | (1 << 3); + // Set address to default + HOTRADDR = 0x30; + // Set write length + HOWRLR = 5; + // Set read length + HORDLR = 5; + // Set command + HOCMDR = 0xA1; + // Set Host ID ? + HOWRDR = 0x00; + // Set index + HOWRDR = 16; + // Set parameter + HOWRDR = 0; + HOWRDR = 0; + // Start transaction + HOCTLR |= 1; + + // Wait for completion + while (HOSTAR & 1) {} + + if (HOSTAR & (1 << 1)) { + // Use result if finished successfully + + //TODO: check completion code + uint8_t data = HOWRDR; + + // Throw away reserved byte + data = HOWRDR; + // Tead tcontrol for now + peci_tcontrol = HOWRDR; + // Read tjmax + peci_tjmax = HOWRDR; + // Throw away reserved byte + data = HOWRDR; + + peci_config_loaded = true; + } +} + + +// PECI information can be found here: https://www.intel.com/content/dam/www/public/us/en/documents/design-guides/core-i7-lga-2011-guide.pdf +void peci_event(void) { + // Wait for completion + while (HOSTAR & 1) {} + // Clear status + HOSTAR = HOSTAR; + + + // Enable PECI, clearing data fifo's + HOCTLR = (1 << 5) | (1 << 3); + // Set address to default + HOTRADDR = 0x30; + // Set write length + HOWRLR = 1; + // Set read length + HORDLR = 2; + // Set command + HOCMDR = 1; + // Start transaction + HOCTLR |= 1; + + // Wait for completion + while (HOSTAR & 1) {} + + if (HOSTAR & (1 << 1)) { + // Use result if finished successfully + uint8_t low = HORDDR; + uint8_t high = HORDDR; + peci_offset = ((int16_t)high << 8) | (int16_t)low; + + // TODO: Update max value if possible + // if (!peci_config_loaded) { + // peci_config(); + // } + + // TODO: tjmax + peci_temp = PECI_TEMP(T_JUNCTION) + peci_offset; + + // Set fan based on temp, adapted from + // https://github.com/pop-os/system76-power/blob/master/src/fan.rs#L218 + if (peci_temp >= PECI_TEMP(90)) { + // 90C = 100% + peci_duty = 255; + } else if (peci_temp >= PECI_TEMP(80)) { + // 80C = 50% + peci_duty = 128; + } else if (peci_temp >= PECI_TEMP(75)) { + // 75C = 45% + peci_duty = 115; + } else if (peci_temp >= PECI_TEMP(65)) { + // 65C = 40% + peci_duty = 102; + } else if (peci_temp >= PECI_TEMP(55)) { + // 55C = 35% + peci_duty = 90; + } else if (peci_temp >= PECI_TEMP(45)) { + // 45C = 30% + peci_duty = 77; + } else { + // < 45C = 0% + peci_duty = 0; + } + } else { + // Default to 50% if there is an error + peci_offset = 0; + peci_temp = 0; + peci_duty = 128; + } + + if (peci_duty != DCR2) { + DCR2 = peci_duty; + DEBUG("PECI offset=%d, temp=%d = %d\n", peci_offset, peci_temp, peci_duty); + } +} diff --git a/src/board/system76/lemp9/pmc.c b/src/board/system76/lemp9/pmc.c new file mode 100644 index 0000000..7b15164 --- /dev/null +++ b/src/board/system76/lemp9/pmc.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include + +void pmc_init(void) { + *(PMC_1.control) = 0x41; + *(PMC_2.control) = 0x41; +} + +#define PMC_TIMEOUT 10000 + +enum PmcState { + PMC_STATE_DEFAULT, + PMC_STATE_ACPI_READ, + PMC_STATE_ACPI_WRITE, + PMC_STATE_ACPI_WRITE_ADDR, +}; + +static uint8_t pmc_sci_queue = 0; +static struct Gpio __code SCI_N = GPIO(D, 3); + +bool pmc_sci(struct Pmc * pmc, uint8_t sci) { + bool update = pmc_sci_queue == 0; + // Set SCI queue if possible + if (update) pmc_sci_queue = sci; + // Set SCI pending bit + uint8_t sts = pmc_status(pmc); + pmc_set_status(pmc, sts | (1 << 5)); + // Start SCI interrupt + gpio_set(&SCI_N, false); + *(SCI_N.control) = 0x40; + return update; +} + +void pmc_event(struct Pmc * pmc) { + static enum PmcState state = PMC_STATE_DEFAULT; + static uint8_t state_data[2] = {0, 0}; + + uint8_t sts = pmc_status(pmc); + if (sts & PMC_STS_IBF) { + uint8_t data = pmc_read(pmc); + if (sts & PMC_STS_CMD) { + DEBUG("pmc cmd: %02X\n", data); + + state = PMC_STATE_DEFAULT; + switch (data) { + case 0x80: + state = PMC_STATE_ACPI_READ; + break; + case 0x81: + state = PMC_STATE_ACPI_WRITE; + break; + case 0x82: + DEBUG(" burst enable\n"); + // Set burst bit + pmc_set_status(pmc, sts | (1 << 4)); + // Send acknowledgement byte + pmc_write(pmc, 0x90, PMC_TIMEOUT); + break; + case 0x83: + DEBUG(" burst disable\n"); + // Clear burst bit + pmc_set_status(pmc, sts & ~(1 << 4)); + break; + case 0x84: + DEBUG(" SCI queue\n"); + // Clear SCI pending bit + pmc_set_status(pmc, sts & ~(1 << 5)); + // Send SCI queue + pmc_write(pmc, pmc_sci_queue, PMC_TIMEOUT); + // Stop SCI interrupt + *(SCI_N.control) = 0x80; + gpio_set(&SCI_N, true); + // Clear SCI queue + pmc_sci_queue = 0; + break; + + case 0xDC: + DEBUG(" scratch rom\n"); + pmc_write(pmc, 0x33, PMC_TIMEOUT); + scratch_trampoline(); + break; + } + } else { + DEBUG("pmc data: %02X\n", data); + + switch (state) { + case PMC_STATE_ACPI_READ: + state = PMC_STATE_DEFAULT; + uint8_t value = acpi_read(data); + pmc_write(pmc, value, PMC_TIMEOUT); + break; + case PMC_STATE_ACPI_WRITE: + state = PMC_STATE_ACPI_WRITE_ADDR; + state_data[0] = data; + break; + case PMC_STATE_ACPI_WRITE_ADDR: + state = PMC_STATE_DEFAULT; + acpi_write(state_data[0], data); + break; + default: + state = PMC_STATE_DEFAULT; + break; + } + } + } +} diff --git a/src/board/system76/lemp9/pnp.c b/src/board/system76/lemp9/pnp.c new file mode 100644 index 0000000..c22422f --- /dev/null +++ b/src/board/system76/lemp9/pnp.c @@ -0,0 +1,43 @@ +#include + +#include + +volatile uint8_t __xdata __at(0x1200) IHIOA; +volatile uint8_t __xdata __at(0x1201) IHD; +volatile uint8_t __xdata __at(0x1204) IBMAE; +volatile uint8_t __xdata __at(0x1205) IBCTL; +void e2ci_write(uint8_t port, uint8_t data) { + while (IBCTL & ((1 << 2) | (1 << 1))) {} + IHIOA = port; + IHD = data; + IBMAE = 1; + IBCTL = 1; + while (IBCTL & (1 << 2)) {} + IBMAE = 0; + IBCTL = 0; +} + +void pnp_write(uint8_t reg, uint8_t data) { + e2ci_write(0x2E, reg); + e2ci_write(0x2F, data); +} + +void pnp_enable() { + DEBUG("Enable PNP devices\n"); + + // Enable PMC + pnp_write(0x07, 0x11); + pnp_write(0x30, 0x01); + + // Enable KBC keyboard + pnp_write(0x07, 0x06); + pnp_write(0x30, 0x01); + + // Enable KBC mouse + pnp_write(0x07, 0x05); + pnp_write(0x30, 0x01); + + // Enable SWUC + pnp_write(0x07, 0x04); + pnp_write(0x30, 0x01); +} diff --git a/src/board/system76/lemp9/power.c b/src/board/system76/lemp9/power.c new file mode 100644 index 0000000..fddc7ac --- /dev/null +++ b/src/board/system76/lemp9/power.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include + +// Platform does not currently support Deep Sx +#define DEEP_SX 0 + +extern uint8_t main_cycle; + +static struct Gpio __code PCH_DPWROK_EC = GPIO(A, 7); +static struct Gpio __code PCH_PWROK_EC = GPIO(A, 6); +static struct Gpio __code LED_PWR = GPIO(D, 0); +static struct Gpio __code ALL_SYS_PWRGD = GPIO(C, 0); +static struct Gpio __code PM_PWROK = GPIO(C, 6); +static struct Gpio __code PWR_SW_N = GPIO(B, 3); +static struct Gpio __code BUF_PLT_RST_N = GPIO(D, 2); +static struct Gpio __code PWR_BTN_N = GPIO(D, 5); +static struct Gpio __code SUSWARN_N = GPIO(D, 7); +static struct Gpio __code EC_EN = GPIO(J, 6); +static struct Gpio __code VA_EC_EN = GPIO(J, 4); +static struct Gpio __code DD_ON = GPIO(E, 4); +static struct Gpio __code EC_RSMRST_N = GPIO(E, 5); +static struct Gpio __code AC_PRESENT = GPIO(E, 1); +static struct Gpio __code SUSC_N_PCH = GPIO(H, 1); +static struct Gpio __code VR_ON = GPIO(H, 4); +static struct Gpio __code SUSB_N_PCH = GPIO(H, 6); +static struct Gpio __code SLP_SUS_N = GPIO(J, 3); +static struct Gpio __code SUS_PWR_ACK = GPIO(J, 7); + +// VccRTC stable (55%) to RTCRST# high +#define tPCH01 delay_ms(9) +// VccDSW stable (95%) to RSMRST# high +#define tPCH02 delay_ms(10) +// VccPrimary stable (95%) to RSMRST# high +#define tPCH03 delay_ms(10) +// VccRTC stable (90%) to start of VccDSW voltage ramp +#define tPCH04 delay_ms(9) +// RTCRST# high to DSW_PWROK +#define tPCH05 delay_us(1) +// VccDSW 3.3 stable to VccPrimary 1.05V +#define tPCH06 delay_us(200) +// DSW_PWROK high to RSMRST# high +#define tPCH07 delay_ms(0) +// SLP_S3# de-assertion to PCH_PWROK assertion +#define tPCH08 delay_ms(1) +// SLP_A# high when ASW rails are stable (95%) +#define tPCH09 delay_ms(2, 4, 8, 16) //TODO +// PCH_PWROK low to VCCIO dropping 5% +#define tPCH10 delay_ns(400) +// SLP_SUS# asserting to VccPRIM dropping 5% +#define tPCH11 delay_ns(100) +// RSMRST# asserting to VccPRIM dropping 5% +#define tPCH12 delay_ns(400) +// DSW_PWROK falling to any of VccDSW, VccPRIM dropping 5% +#define tPCH14 delay_ns(400) +// De-assertion of RSMRST# to de-assertion of ESPI_RESET# +#if DEEP_SX + #define tPCH18 delay_us(90) +#else + #define tPCH18 delay_ms(95) +#endif +// DSW_PWROK assertion to SLP_SUS# de-assertion +#define tPCH32 delay_ms(95) +// RSMRST# de-assertion to SUSPWRDNACK valid +#define tPLT01 delay_ms(200) + +// Enable deep sleep well power +void power_on_ds5() { + DEBUG("%02X: power_on_ds5\n", main_cycle); + +#if DEEP_SX + // See Figure 12-18 in Whiskey Lake Platform Design Guide + // | VCCRTC | RTCRST# | VCCDSW_3P3 | DSW_PWROK | + // | tPCH01---------- | | | + // | tPCH04----------------------- | | + // | | tPCH05-------------------------- | + // | | | tPCH02---------------- | + + // tPCH01 and tPCH02 combined make the longest delay + tPCH01; + tPCH02; + + // Deep sleep well is a-ok + gpio_set(PCH_DPWROK_EC, true); + // Wait for deep sleep well to propogate + tPCH32; +#else // DEEP_SX + // See Figure 12-19 in Whiskey Lake Platform Design Guide + // | VCCRTC | RTCRST# | VccPRIM | + // | tPCH01---------- | | + // | tPCH04-------------------- | + + // tPCH04 is the ideal delay + tPCH04; +#endif // DEEP_SX +} + +// Enable S5 power +void power_on_s5() { + DEBUG("%02X: power_on_s5\n", main_cycle); + +#if DEEP_SX + // See Figure 12-18 in Whiskey Lake Platform Design Guide + + // TODO +#else // DEEP_SX + // See Figure 12-19 in Whiskey Lake Platform Design Guide + // TODO - signal timing graph + // See Figure 12-25 in Whiskey Lake Platform Design Guide + // TODO - rail timing graph + + // Enable VCCPRIM_* planes - must be enabled prior to USB power in order to + // avoid leakage + gpio_set(&VA_EC_EN, true); + tPCH06; + + // Enable VDD5 + gpio_set(&DD_ON, true); + + // De-assert SUS_ACK# - TODO is this needed on non-dsx? + gpio_set(&SUS_PWR_ACK, true); + tPCH03; + + // Assert DSW_PWROK + gpio_set(&PCH_DPWROK_EC, true); + + // De-assert RSMRST# + gpio_set(&EC_RSMRST_N, true); + + // Wait for PCH stability + tPCH18; + + // Allow processor to control SUSB# and SUSC# + gpio_set(&EC_EN, true); + + // Wait for SUSPWRDNACK validity + tPLT01; + + // Extra wait - TODO remove + delay_ms(200); +#endif // DEEP_SX +} + +void power_off_s5() { + DEBUG("%02X: power_off_s5\n", main_cycle); + +#if DEEP_SX + // TODO +#else // DEEP_SX + // De-assert SYS_PWROK + gpio_set(&PCH_PWROK_EC, false); + + // De-assert PCH_PWROK + gpio_set(&PM_PWROK, false); + + // Block processor from controlling SUSB# and SUSC# + gpio_set(&EC_EN, false); + + // De-assert RSMRST# + gpio_set(&EC_RSMRST_N, false); + + // Disable VDD5 + gpio_set(&DD_ON, false); + tPCH12; + + // Disable VCCPRIM_* planes + gpio_set(&VA_EC_EN, false); + + // De-assert DSW_PWROK + gpio_set(&PCH_DPWROK_EC, false); + tPCH14; +#endif // DEEP_SX +} + +enum PowerState { + POWER_STATE_DEFAULT, + POWER_STATE_DS5, + POWER_STATE_S5, + POWER_STATE_DS3, + POWER_STATE_S3, + POWER_STATE_S0, +}; + +void power_event(void) { + static enum PowerState state = POWER_STATE_DEFAULT; + + // Always switch to ds5 if EC is running + if (state == POWER_STATE_DEFAULT) { + power_on_ds5(); + state = POWER_STATE_DS5; + } + + // Read power switch state + static bool ps_last = true; + bool ps_new = gpio_get(&PWR_SW_N); + if (!ps_new && ps_last) { + // Ensure press is not spurious + delay_ms(10); + if (gpio_get(&PWR_SW_N) != ps_new) { + DEBUG("%02X: Spurious press\n", main_cycle); + ps_new = ps_last; + } else { + DEBUG("%02X: Power switch press\n", main_cycle); + + // Enable S5 power if necessary, before sending PWR_BTN + if (state == POWER_STATE_DS5) { + power_on_s5(); + state = POWER_STATE_S5; + } + } + } + #if LEVEL >= LEVEL_DEBUG + else if (ps_new && !ps_last) { + DEBUG("%02X: Power switch release\n", main_cycle); + } + #endif + ps_last = ps_new; + + // Send power signal to PCH + gpio_set(&PWR_BTN_N, ps_new); + +#if DEEP_SX + //TODO +#else // DEEP_SX + //TODO: set power state as necessary + + // If system power is good + static bool pg_last = false; + bool pg_new = gpio_get(&ALL_SYS_PWRGD); + if (pg_new && !pg_last) { + DEBUG("%02X: ALL_SYS_PWRGD asserted\n", main_cycle); + + //TODO: tPLT04; + + // Allow H_VR_READY to set PCH_PWROK + gpio_set(&PM_PWROK, true); + + // OEM defined delay from ALL_SYS_PWRGD to SYS_PWROK - TODO + delay_ms(10); + + // Assert SYS_PWROK, system can finally perform PLT_RST# and boot + gpio_set(&PCH_PWROK_EC, true); + } else if(!pg_new && pg_last) { + DEBUG("%02X: ALL_SYS_PWRGD de-asserted\n", main_cycle); + + // De-assert SYS_PWROK + gpio_set(&PCH_PWROK_EC, false); + + // De-assert PCH_PWROK + gpio_set(&PM_PWROK, false); + } + pg_last = pg_new; + + static bool rst_last = false; + bool rst_new = gpio_get(&BUF_PLT_RST_N); + #if LEVEL >= LEVEL_DEBUG + if (!rst_new && rst_last) { + DEBUG("%02X: PLT_RST# asserted\n", main_cycle); + } else + #endif + if(rst_new && !rst_last) { + DEBUG("%02X: PLT_RST# de-asserted\n", main_cycle); + + // LPC was just reset, enable PNP devices + pnp_enable(); + //TODO: reset KBC and touchpad states + } + rst_last = rst_new; + + #if LEVEL >= LEVEL_DEBUG + static bool s3_last = false; + bool s3_new = gpio_get(&SUSB_N_PCH); + if (!s3_new && s3_last) { + DEBUG("%02X: SLP_S3# asserted\n", main_cycle); + } else if(s3_new && !s3_last) { + DEBUG("%02X: SLP_S3# de-asserted\n", main_cycle); + } + s3_last = s3_new; + + static bool s4_last = false; + bool s4_new = gpio_get(&SUSC_N_PCH); + if (!s4_new && s4_last) { + DEBUG("%02X: SLP_S4# asserted\n", main_cycle); + } else if(s4_new && !s4_last) { + DEBUG("%02X: SLP_S4# de-asserted\n", main_cycle); + } + s4_last = s4_new; + + static bool sus_last = false; + bool sus_new = gpio_get(&SLP_SUS_N); + if (!sus_new && sus_last) { + DEBUG("%02X: SLP_SUS# asserted\n", main_cycle); + } else if (sus_new && !sus_last) { + DEBUG("%02X: SLP_SUS# de-asserted\n", main_cycle); + } + sus_last = sus_new; + #endif + + // EC must keep VccPRIM powered if SUSPWRDNACK is de-asserted low or system + // state is S3 + static bool ack_last = false; + bool ack_new = gpio_get(&SUSWARN_N); + if (ack_new && !ack_last) { + DEBUG("%02X: SUSPWRDNACK asserted\n", main_cycle); + + if (gpio_get(&SUSC_N_PCH)) { + DEBUG("%02X: entering S3 state\n", main_cycle); + } else if (state == POWER_STATE_S5) { + power_off_s5(); + state = POWER_STATE_DS5; + } + } + #if LEVEL >= LEVEL_DEBUG + else if (!ack_new && ack_last) { + DEBUG("%02X: SUSPWRDNACK de-asserted\n", main_cycle); + } + #endif + ack_last = ack_new; + +#endif // DEEP_SX +} diff --git a/src/board/system76/lemp9/ps2.c b/src/board/system76/lemp9/ps2.c new file mode 100644 index 0000000..237e061 --- /dev/null +++ b/src/board/system76/lemp9/ps2.c @@ -0,0 +1,7 @@ +#include + +void ps2_init(void) { + ps2_reset(&PS2_1); + ps2_reset(&PS2_2); + ps2_reset(&PS2_3); +} diff --git a/src/board/system76/lemp9/pwm.c b/src/board/system76/lemp9/pwm.c new file mode 100644 index 0000000..6cd7dda --- /dev/null +++ b/src/board/system76/lemp9/pwm.c @@ -0,0 +1,24 @@ +#include + +void pwm_init(void) { + // Set T0CHSEL to TACH0A and T1CHSEL to TACH1A + TSWCTLR = 0; + + // Disable PWM + ZTIER = 0; + + // Set prescalar clock frequency to EC clock + PCFSR = 0b01; + + // Set clock prescaler to 0 + 1 + C0CPRS = 0; + + // Set cycle time to 255 + 1 + CTR0 = 255; + + // Turn off CPU fan (temperature control in peci_event) + DCR2 = 0; + + // Enable PWM + ZTIER = (1 << 1); +} diff --git a/src/board/system76/lemp9/scratch.c b/src/board/system76/lemp9/scratch.c new file mode 100644 index 0000000..ef1b074 --- /dev/null +++ b/src/board/system76/lemp9/scratch.c @@ -0,0 +1,90 @@ +#include +#include + +#include + +// Include scratch ROM, must be less than 2048 bytes, controlled by makefile +uint8_t __code scratch_rom[] = { + #include +}; + +// Scratch RAM 1, 2, 3, and 4 are 2048 bytes total, located at 0x0800 bytes +volatile uint8_t __xdata __at(0x0800) scratch_ram[2048]; + +volatile uint8_t __xdata __at(0x1043) SCAR1L; +volatile uint8_t __xdata __at(0x1044) SCAR1M; +volatile uint8_t __xdata __at(0x1045) SCAR1H; + +volatile uint8_t __xdata __at(0x1046) SCAR2L; +volatile uint8_t __xdata __at(0x1047) SCAR2M; +volatile uint8_t __xdata __at(0x1048) SCAR2H; + +volatile uint8_t __xdata __at(0x1049) SCAR3L; +volatile uint8_t __xdata __at(0x104A) SCAR3M; +volatile uint8_t __xdata __at(0x104B) SCAR3H; + +volatile uint8_t __xdata __at(0x104C) SCAR4L; +volatile uint8_t __xdata __at(0x104D) SCAR4M; +volatile uint8_t __xdata __at(0x104E) SCAR4H; + +// Create new segment located at 0x1000, after scratch ROM mapping +static void scratch_start(void) __naked { + __asm + .area SCRATCH (ABS,CODE) + __endasm; + __asm + .org 0x1000 + __endasm; +} + +// Enter or exit scratch ROM +void scratch_trampoline(void) { + // Uses SCAR1, 2, 3, and 4 which are mapped at 0x0800 in data space and are + // 2048 bytes in size. SCAR0 cannot be used due to __pdata overwriting it. + + if ((SCAR1H == 0x00) || (SCAR2H == 0x00) || (SCAR3H == 0x00) || (SCAR4H == 0x00)) { + // Disable scratch RAM mapping + SCAR1H = 0b11; + SCAR2H = 0b11; + SCAR3H = 0b11; + SCAR4H = 0b11; + } else { + int i; + // Copy scratch ROM + for (i = 0; i < ARRAY_SIZE(scratch_rom) && i < ARRAY_SIZE(scratch_ram); i++) { + scratch_ram[i] = scratch_rom[i]; + } + + // Fill the rest with nop + for (; i < ARRAY_SIZE(scratch_ram); i++) { + scratch_ram[i] = 0x00; + } + + // Set scratch RAM 1 mapping at 0x0000 and enable + SCAR1L = 0x00; + SCAR1M = 0x00; + SCAR1H = 0x00; + // Set scratch RAM 2 mapping at 0x0400 and enable + SCAR2L = 0x00; + SCAR2M = 0x04; + SCAR2H = 0x00; + // Set scratch RAM 3 mapping at 0x0600 and enable + SCAR3L = 0x00; + SCAR3M = 0x06; + SCAR3H = 0x00; + // Set scratch RAM 4 mapping at 0x0700 and enable + SCAR4L = 0x00; + SCAR4M = 0x07; + SCAR4H = 0x00; + } + + // Jump to reset function + __asm__("ljmp 0"); +} + +// Finish segment located at 0x1000 +static void scratch_end(void) __naked { + __asm + .area CSEG (REL,CODE) + __endasm; +} diff --git a/src/board/system76/lemp9/scratch/include/scratch/pmc.h b/src/board/system76/lemp9/scratch/include/scratch/pmc.h new file mode 100644 index 0000000..ca36a16 --- /dev/null +++ b/src/board/system76/lemp9/scratch/include/scratch/pmc.h @@ -0,0 +1,33 @@ +#ifndef _EC_PMC_H +#define _EC_PMC_H + +#include +#include + +struct Pmc { + // Status register + volatile uint8_t * status; + // Data out register + volatile uint8_t * data_out; + // Data in register + volatile uint8_t * data_in; + // Control register + volatile uint8_t * control; +}; + +extern struct Pmc __code PMC_1; + +#define PMC_STS_OBF (1 << 0) +#define PMC_STS_IBF (1 << 1) +#define PMC_STS_CMD (1 << 3) + +uint8_t pmc_status(struct Pmc * pmc); +uint8_t pmc_read(struct Pmc * pmc); +bool pmc_write(struct Pmc * pmc, uint8_t data); + +volatile uint8_t __xdata __at(0x1500) PM1STS; +volatile uint8_t __xdata __at(0x1501) PM1DO; +volatile uint8_t __xdata __at(0x1504) PM1DI; +volatile uint8_t __xdata __at(0x1506) PM1CTL; + +#endif // _EC_PMC_H diff --git a/src/board/system76/lemp9/scratch/main.c b/src/board/system76/lemp9/scratch/main.c new file mode 100644 index 0000000..29eb9ab --- /dev/null +++ b/src/board/system76/lemp9/scratch/main.c @@ -0,0 +1,101 @@ +#include +#include +#include + +#include + +volatile uint8_t __xdata __at(0x103B) ECINDAR0; +volatile uint8_t __xdata __at(0x103C) ECINDAR1; +volatile uint8_t __xdata __at(0x103D) ECINDAR2; +volatile uint8_t __xdata __at(0x103E) ECINDAR3; +volatile uint8_t __xdata __at(0x103F) ECINDDR; + +#define FLASH_SPI 0x00000000 +#define FLASH_EMBEDDED 0x40000000 + +static int flash_transaction(uint32_t offset, uint8_t * data, int length, bool read) { + int i; + for (i = 0; i < length; i++, offset++) { + ECINDAR3 = (uint8_t)(offset >> 24); + ECINDAR2 = (uint8_t)(offset >> 16); + ECINDAR1 = (uint8_t)(offset >> 8); + ECINDAR0 = (uint8_t)(offset); + if (read) { + data[i] = ECINDDR; + } else { + ECINDDR = data[i]; + } + } + return i; +} + +enum PmcState { + PMC_STATE_DEFAULT, + PMC_STATE_WRITE, +}; + +static void pmc_event(struct Pmc * pmc) { + static enum PmcState state = PMC_STATE_DEFAULT; + + uint8_t sts = pmc_status(pmc); + if (sts & PMC_STS_IBF) { + uint8_t data = pmc_read(pmc); + if (sts & PMC_STS_CMD) { + printf_tiny("%x\n", data); + switch (state) { + case PMC_STATE_DEFAULT: + switch (data) { + case 0x01: + // Enable follow + ECINDAR3 = 0x0F; + break; + case 0x02: + // Generate high CE# + data = 0; + flash_transaction(0x7FFFFE00, &data, 1, false); + // Fall through + case 0x03: + state = PMC_STATE_WRITE; + break; + case 0x04: + // Read data + flash_transaction(0x7FFFFD00, &data, 1, true); + pmc_write(pmc, data); + printf_tiny("=%x\n", data); + break; + case 0x05: + // Disable follow + ECINDAR3 = 0x00; + break; + case 0xFC: + // Clear processor caches + __asm__("mov 0xf7, #1"); + __asm__("nop"); + __asm__("mov 0xf7, #1"); + __asm__("nop"); + __asm__("mov 0xf7, #1"); + __asm__("nop"); + __asm__("mov 0xf7, #1"); + __asm__("nop"); + // Exit scratch ROM by going through trampoline + __asm__("ljmp 0x1000"); + break; + } + break; + case PMC_STATE_WRITE: + // Write command or data + flash_transaction(0x7FFFFD00, &data, 1, false); + state = PMC_STATE_DEFAULT; + break; + } + } + } +} + +// Main program while running in scratch ROM +void main(void) { + printf_tiny("SCRATCH\n"); + for (;;) { + pmc_event(&PMC_1); + } +} diff --git a/src/board/system76/lemp9/scratch/pmc.c b/src/board/system76/lemp9/scratch/pmc.c new file mode 100644 index 0000000..7bba10c --- /dev/null +++ b/src/board/system76/lemp9/scratch/pmc.c @@ -0,0 +1,24 @@ +#include + +#define PMC(NUM) { \ + .status = &PM ## NUM ## STS, \ + .data_out = &PM ## NUM ## DO, \ + .data_in = &PM ## NUM ## DI, \ + .control = &PM ## NUM ## CTL, \ +} + +struct Pmc __code PMC_1 = PMC(1); + +uint8_t pmc_status(struct Pmc * pmc) { + return *(pmc->status); +} + +uint8_t pmc_read(struct Pmc * pmc) { + return *(pmc->data_in); +} + +bool pmc_write(struct Pmc * pmc, uint8_t data) { + while (pmc_status(pmc) & PMC_STS_OBF) {} + *(pmc->data_out) = data; + return true; +} diff --git a/src/board/system76/lemp9/scratch/scratch.mk b/src/board/system76/lemp9/scratch/scratch.mk new file mode 100644 index 0000000..ba018b5 --- /dev/null +++ b/src/board/system76/lemp9/scratch/scratch.mk @@ -0,0 +1,40 @@ +# Enable I2C debugging +SCRATCH_SRC+=\ + src/common/i2c.c \ + src/ec/$(EC)/i2c.c +SCRATCH_INCLUDE+=\ + src/common/include/common/*.h \ + src/ec/$(EC)/include/ec/*.h +SCRATCH_CFLAGS+=\ + -Isrc/common/include \ + -Isrc/ec/$(EC)/include \ + -DI2C_DEBUGGER=0x76 + +SCRATCH_BUILD=$(BUILD)/scratch +SCRATCH_OBJ=$(patsubst src/%.c,$(SCRATCH_BUILD)/%.rel,$(SCRATCH_SRC)) +SCRATCH_CC=\ + sdcc \ + -mmcs51 \ + --model-small \ + --code-size 2048 \ + --Werror + +# Convert from binary file to C header +$(BUILD)/include/scratch.h: $(SCRATCH_BUILD)/scratch.rom + @mkdir -p $(@D) + xxd --include < $< > $@ + +# Convert from Intel Hex file to binary file +$(SCRATCH_BUILD)/scratch.rom: $(SCRATCH_BUILD)/scratch.ihx + @mkdir -p $(@D) + makebin -p < $< > $@ + +# Link object files into Intel Hex file +$(SCRATCH_BUILD)/scratch.ihx: $(SCRATCH_OBJ) + @mkdir -p $(@D) + $(SCRATCH_CC) -o $@ $^ + +# Compile C files into object files +$(SCRATCH_OBJ): $(SCRATCH_BUILD)/%.rel: src/%.c $(SCRATCH_INCLUDE) + @mkdir -p $(@D) + $(SCRATCH_CC) $(SCRATCH_CFLAGS) -o $@ -c $< diff --git a/src/board/system76/lemp9/scratch/stdio.c b/src/board/system76/lemp9/scratch/stdio.c new file mode 100644 index 0000000..6537ec8 --- /dev/null +++ b/src/board/system76/lemp9/scratch/stdio.c @@ -0,0 +1,20 @@ +#include + +#ifdef SERIAL_DEBUGGER + #include +#endif + +#ifdef I2C_DEBUGGER + #include +#endif + +int putchar(int c) { + unsigned char byte = (unsigned char)c; +#ifdef SERIAL_DEBUGGER + SBUF = byte; +#endif +#ifdef I2C_DEBUGGER + i2c_send(I2C_DEBUGGER, &byte, 1); +#endif + return (int)byte; +} diff --git a/src/board/system76/lemp9/smbus.c b/src/board/system76/lemp9/smbus.c new file mode 100644 index 0000000..b9dafe2 --- /dev/null +++ b/src/board/system76/lemp9/smbus.c @@ -0,0 +1,24 @@ +#include +#include + +void smbus_init(void) { + // 9.2 MHz * 4.7 us = 43.24 + SMB4P7USL = 43; + // 9.2 MHz * 4.0 us = 36.8 + SMB4P0USL = 37; + // 9.2 MHz * 300 ns = 2.76 + SMB300NS = 3; + // 9.2 MHz * 250 ns = 2.3 + SMB250NS = 2; + // 1.024 KHz * 25 ms = 25.6 + SMB25MS = 26; + // 9.2 MHz * 45.3 us = 416.76 (0x01A1) + SMB45P3USL = 0xA1; + SMB45P3USH = 0x01; + + // Clock set to 50 KHz + // SCLKTSA = 1; + + // Set up for i2c usage + i2c_reset(true); +} diff --git a/src/board/system76/lemp9/stdio.c b/src/board/system76/lemp9/stdio.c new file mode 100644 index 0000000..6537ec8 --- /dev/null +++ b/src/board/system76/lemp9/stdio.c @@ -0,0 +1,20 @@ +#include + +#ifdef SERIAL_DEBUGGER + #include +#endif + +#ifdef I2C_DEBUGGER + #include +#endif + +int putchar(int c) { + unsigned char byte = (unsigned char)c; +#ifdef SERIAL_DEBUGGER + SBUF = byte; +#endif +#ifdef I2C_DEBUGGER + i2c_send(I2C_DEBUGGER, &byte, 1); +#endif + return (int)byte; +}