Skip to content

Commit

Permalink
tps65987.c: ensure PD controller is in good state when turning on / off
Browse files Browse the repository at this point in the history
Signed-off-by: Michał Kopeć <[email protected]>
  • Loading branch information
mkopec committed Nov 21, 2023
1 parent be8b79c commit 4971c78
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 27 deletions.
15 changes: 14 additions & 1 deletion src/board/system76/common/include/board/usbpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,23 @@
#ifndef _BOARD_USBPD_H
#define _BOARD_USBPD_H

enum {
// Fully functional, normal operation
USBPD_MODE_APP = 0,
// PD controller is running BIST
USBPD_MODE_BIST,
// PD controller booted in dead battery mode
USBPD_MODE_BOOT,
// Simulated port disconnect by previously issued DISC command
USBPD_MODE_DISC,
// Other values indicate limited functionality
USBPD_MODE_UNKNOWN,
};

void usbpd_init(void);
void usbpd_event(void);
void usbpd_disable_charging(void);
void usbpd_enable_charging(void);
int16_t usbpd_disc(uint8_t timeout);
void usbpd_set_mode(int16_t mode);

#endif // _BOARD_USBPD_H
6 changes: 4 additions & 2 deletions src/board/system76/common/power.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,10 @@ void power_off(void) {
// Commit settings to flash on shutdown
options_save_config();

// Trigger USB-PD disconnect, will be reconnected after TI reset
usbpd_disc(1);
// Cycle the PD controller and dock connection off and on to work around
// dock issues
usbpd_set_mode(USBPD_MODE_DISC);
usbpd_set_mode(USBPD_MODE_APP);

#if HAVE_PCH_PWROK_EC
// De-assert SYS_PWROK
Expand Down
133 changes: 109 additions & 24 deletions src/board/system76/common/usbpd/tps65987.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// USB-PD driver for TPS65987 and the mostly compatible TPS65993 and TPS65994.
// I2C register reference: https://www.ti.com/lit/ug/slvubh2b/slvubh2b.pdf

#include <arch/time.h>
#include <board/battery.h>
#include <board/gpio.h>
#include <board/power.h>
Expand All @@ -12,12 +13,11 @@

#define USBPD_ADDRESS 0x20

#define REG_MODE 0x03
#define REG_CMD1 0x08
#define REG_DATA1 0x09
#define REG_ACTIVE_CONTRACT_PDO 0x34

void usbpd_init(void) {
i2c_reset(&I2C_USBPD, true);
}

enum {
// PDO is empty
USBPD_ERR_PDO_ZERO = 0x1000,
Expand Down Expand Up @@ -94,6 +94,101 @@ static void usbpd_dump(void) {
}
}

#define FOURCC(a, b, c, d) (((((d) << 8) | (c)) << 8 | (b)) << 8 | (a))

// Check the operational state of the PD controller
static int16_t usbpd_get_mode(void) {
int16_t res;
uint32_t mode;
uint8_t reg[5] = { 0 };

DEBUG("USBPD controller mode: ");

res = i2c_get(&I2C_USBPD, USBPD_ADDRESS, REG_MODE, reg, sizeof(reg));
if (res < 0 || reg[0] < 4) {
DEBUG("UNKNOWN (I2C error %d)\n", res);
return USBPD_MODE_UNKNOWN;
}

mode = FOURCC(reg[1], reg[2], reg[3], reg[4]);

switch (mode) {
case FOURCC('A', 'P', 'P', ' '):
DEBUG("APP\n");
return USBPD_MODE_APP;
case FOURCC('B', 'I', 'S', 'T'):
DEBUG("BIST\n");
return USBPD_MODE_BIST;
case FOURCC('B', 'O', 'O', 'T'):
DEBUG("BOOT\n");
return USBPD_MODE_BOOT;
case FOURCC('D', 'I', 'S', 'C'):
DEBUG("DISC\n");
return USBPD_MODE_DISC;
}

DEBUG("UNKNOWN\n");
return USBPD_MODE_UNKNOWN;
}

// Disconnect port for [timeout] seconds
// If timeout is 0, port will remain disconnected indefinitely
static int16_t usbpd_disc(uint8_t timeout) {
int16_t res;
uint8_t cmd[5] = { 4, 'D', 'I', 'S', 'C' };
uint8_t data[2] = { 1, 0 };

data[1] = timeout;

do {
res = i2c_set(&I2C_USBPD, USBPD_ADDRESS, REG_DATA1, data, sizeof(data));
} while (res < 0);

do {
res = i2c_set(&I2C_USBPD, USBPD_ADDRESS, REG_CMD1, cmd, sizeof(cmd));
} while (res < 0);

// TI says this never fails and completes immediately
return 0;
}

// Return to normal operation
// Reboots the PD controller and exits any modal tasks
static int16_t usbpd_gaid(void) {
int16_t res;

uint8_t cmd[5] = { 4, 'G', 'a', 'i', 'd' };

do {
res = i2c_set(&I2C_USBPD, USBPD_ADDRESS, REG_CMD1, cmd, sizeof(cmd));
} while (res < 0);

i2c_reset(&I2C_USBPD, true);

uint32_t deadline = time_get() + 1000; // 1 second
while (usbpd_get_mode() != USBPD_MODE_APP && time_get() < deadline) {};

if (time_get() > deadline) {
DEBUG("USBPD reboot timed out\n");
return -1;
}

return 0;
}

void usbpd_set_mode(int16_t mode) {
switch (mode) {
case USBPD_MODE_APP:
usbpd_gaid();
break;
case USBPD_MODE_DISC:
usbpd_disc(0);
break;
default:
return;
}
}

void usbpd_event(void) {
bool update = false;
int16_t res;
Expand Down Expand Up @@ -165,26 +260,7 @@ static int16_t usbpd_aneg(void) {
int16_t res;

uint8_t cmd[5] = { 4, 'A', 'N', 'e', 'g' };
res = i2c_set(&I2C_USBPD, USBPD_ADDRESS, 0x08, cmd, sizeof(cmd));
if (res < 0) {
return res;
}

//TODO: wait on command completion

return 0;
}

int16_t usbpd_disc(uint8_t timeout) {
int16_t res;

uint8_t cmd[5] = { 4, 'D', 'I', 'S', 'C' };
uint8_t data[2] = { 1, 0 };

data[1] = timeout;

res = i2c_set(&I2C_USBPD, USBPD_ADDRESS, 0x09, data, sizeof(data));
res = i2c_set(&I2C_USBPD, USBPD_ADDRESS, 0x08, cmd, sizeof(cmd));
res = i2c_set(&I2C_USBPD, USBPD_ADDRESS, REG_CMD1, cmd, sizeof(cmd));
if (res < 0) {
return res;
}
Expand Down Expand Up @@ -269,3 +345,12 @@ void usbpd_enable_charging(void) {

DEBUG("OK\n");
}

void usbpd_init(void) {
i2c_reset(&I2C_USBPD, true);

int16_t mode = usbpd_get_mode();

if (mode == USBPD_MODE_DISC || mode == USBPD_MODE_UNKNOWN)
usbpd_set_mode(USBPD_MODE_APP);
}

0 comments on commit 4971c78

Please sign in to comment.