usb: disable tps6598x interrupts

Restore the interrupt masks on chainload or HV guest start. The
interrupt mask is not restored on the USB-C port used by the hypervisor.
This prevents an interrupt storm in the guest when the other USB-C port
is exposed to the guest. Both tps6598x share unfortunately an interrupt
line.

Signed-off-by: Janne Grunau <j@jannau.net>
This commit is contained in:
Janne Grunau 2021-06-12 10:52:23 +02:00 committed by Hector Martin
parent de82209079
commit 98076ef693
7 changed files with 153 additions and 15 deletions

View file

@ -774,6 +774,12 @@ class HV(Reloadable):
self.device_addr_tbl = self.adt.build_addr_lookup()
self.print_tracer = trace.PrintTracer(self, self.device_addr_tbl)
# disable unused USB iodev early so interrupts can be reenabled in hv_init()
for iodev in (IODEV.USB0, IODEV.USB1):
if iodev != self.iodev:
print(f"Disable iodev {iodev!s}")
self.p.iodev_set_usage(iodev, 0)
print("Initializing hypervisor over iodev %s" % self.iodev)
self.p.hv_init()

View file

@ -6,6 +6,7 @@
#include "gxf.h"
#include "pcie.h"
#include "smp.h"
#include "usb.h"
#include "utils.h"
#define HV_TICK_RATE 1000
@ -19,6 +20,8 @@ u64 hv_tick_interval;
void hv_init(void)
{
pcie_shutdown();
// reenable hpm interrupts for the guest for unused iodevs
usb_hpm_restore_irqs(0);
smp_start_secondaries();
hv_wdt_init();

View file

@ -18,6 +18,7 @@
#include "types.h"
#include "uart.h"
#include "uartproxy.h"
#include "usb.h"
#include "utils.h"
#include "xnuboot.h"
@ -82,6 +83,9 @@ int proxy_process(ProxyRequest *request, ProxyReply *reply)
request->args[3], request->args[4]);
break;
case P_VECTOR:
// forcefully restore tps6598x IRQs
usb_hpm_restore_irqs(1);
iodev_console_flush();
next_stage.entry = (generic_func *)request->args[0];
memcpy(next_stage.args, &request->args[1], 4 * sizeof(u64));
return 1;

View file

@ -3,15 +3,22 @@
#include "tps6598x.h"
#include "adt.h"
#include "i2c.h"
#include "iodev.h"
#include "malloc.h"
#include "types.h"
#include "utils.h"
#define TPS_REG_CMD1 0x08
#define TPS_REG_DATA1 0x09
#define TPS_REG_INT_EVENT1 0x14
#define TPS_REG_INT_MASK1 0x16
#define TPS_REG_INT_CLEAR1 0x18
#define TPS_REG_POWER_STATE 0x20
#define TPS_CMD_INVALID 0x21434d44 // !CMD
// 80 ms is not enough
#define TPS_WRITE_DELAY 120
struct tps6598x_dev {
i2c_dev_t *i2c;
u8 addr;
@ -73,6 +80,76 @@ int tps6598x_command(tps6598x_dev_t *dev, const char *cmd, const u8 *data_in, si
return 0;
}
int tps6598x_disable_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state)
{
size_t read;
int written;
static const u8 zeros[CD3218B12_IRQ_WIDTH] = {0x00};
static const u8 ones[CD3218B12_IRQ_WIDTH] = {0xFF};
// store IntEvent 1 to restore it later
read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, state->int_mask1,
sizeof(state->int_mask1));
if (read != CD3218B12_IRQ_WIDTH) {
printf("tps6598x: reading TPS_REG_INT_MASK1 failed\n");
return -1;
}
state->valid = 1;
// mask interrupts and ack all interrupt flags
written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_CLEAR1, ones, sizeof(ones));
if (written != sizeof(zeros)) {
printf("tps6598x: writing TPS_REG_INT_CLEAR1 failed, written: %d\n", written);
return -1;
}
udelay(TPS_WRITE_DELAY);
written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_MASK1, zeros, sizeof(zeros));
if (written != sizeof(ones)) {
printf("tps6598x: writing TPS_REG_INT_MASK1 failed, written: %d\n", written);
return -1;
}
udelay(TPS_WRITE_DELAY);
#ifdef DEBUG
u8 tmp[CD3218B12_IRQ_WIDTH] = {0x00};
read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, tmp, CD3218B12_IRQ_WIDTH);
if (read != CD3218B12_IRQ_WIDTH)
printf("tps6598x: failed verifcation, can't read TPS_REG_INT_MASK1\n");
else {
printf("tps6598x: verify: TPS_REG_INT_MASK1 vs. saved IntMask1\n");
hexdump(tmp, sizeof(tmp));
hexdump(state->int_mask1, sizeof(state->int_mask1));
}
#endif
return 0;
}
int tps6598x_restore_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state)
{
int written;
written = i2c_smbus_write(dev->i2c, dev->addr, TPS_REG_INT_MASK1, state->int_mask1,
sizeof(state->int_mask1));
if (written != sizeof(state->int_mask1)) {
printf("tps6598x: restoring TPS_REG_INT_MASK1 failed\n");
return -1;
}
udelay(TPS_WRITE_DELAY);
#ifdef DEBUG
int read;
u8 tmp[CD3218B12_IRQ_WIDTH];
read = i2c_smbus_read(dev->i2c, dev->addr, TPS_REG_INT_MASK1, tmp, sizeof(tmp));
if (read != sizeof(tmp))
printf("tps6598x: failed verifcation, can't read TPS_REG_INT_MASK1\n");
else {
printf("tps6598x: verify saved IntMask1 vs. TPS_REG_INT_MASK1:\n");
hexdump(state->int_mask1, sizeof(state->int_mask1));
hexdump(tmp, sizeof(tmp));
}
#endif
return 0;
}
int tps6598x_powerup(tps6598x_dev_t *dev)
{
u8 power_state;

View file

@ -15,4 +15,14 @@ int tps6598x_command(tps6598x_dev_t *dev, const char *cmd, const u8 *data_in, si
u8 *data_out, size_t len_out);
int tps6598x_powerup(tps6598x_dev_t *dev);
#define CD3218B12_IRQ_WIDTH 9
typedef struct tps6598x_irq_state {
u8 int_mask1[CD3218B12_IRQ_WIDTH];
bool valid;
} tps6598x_irq_state_t;
int tps6598x_disable_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state);
int tps6598x_restore_irqs(tps6598x_dev_t *dev, tps6598x_irq_state_t *state);
#endif

View file

@ -44,6 +44,8 @@ static const struct {
},
};
static tps6598x_irq_state_t tps6598x_irq_state[USB_INSTANCES];
static dart_dev_t *usb_dart_init(const char *path, const char *mapper_path)
{
int dart_path[8];
@ -239,38 +241,46 @@ struct iodev iodev_usb_sec[USB_INSTANCES] = {
},
};
static void hpm_init(void)
static tps6598x_dev_t *hpm_init(i2c_dev_t *i2c, int idx)
{
int i;
const char *hpm_path = usb_drd_paths[idx].hpm_path;
tps6598x_dev_t *tps = tps6598x_init(hpm_path, i2c);
if (!tps) {
printf("usb: tps6598x_init failed for %s.\n", hpm_path);
return NULL;
}
if (tps6598x_powerup(tps) < 0) {
printf("usb: tps6598x_powerup failed for %s.\n", hpm_path);
tps6598x_shutdown(tps);
return NULL;
}
return tps;
}
void usb_init(void)
{
i2c_dev_t *i2c = i2c_init("/arm-io/i2c0");
if (!i2c) {
printf("usb: i2c init failed.\n");
return;
}
for (i = 0; i < USB_INSTANCES; ++i) {
const char *hpm_path = usb_drd_paths[i].hpm_path;
tps6598x_dev_t *tps = tps6598x_init(hpm_path, i2c);
for (int idx = 0; idx < USB_INSTANCES; ++idx) {
tps6598x_dev_t *tps = hpm_init(i2c, idx);
if (!tps) {
printf("usb: tps6598x_init failed for %s.\n", hpm_path);
printf("usb: failed to init hpm%d\n", idx);
continue;
}
if (tps6598x_powerup(tps) < 0) {
printf("usb: tps6598x_powerup failed for %s.\n", hpm_path);
continue;
}
if (tps6598x_disable_irqs(tps, &tps6598x_irq_state[idx]))
printf("usb: unable to disable IRQ masks for hpm%d\n", idx);
tps6598x_shutdown(tps);
}
i2c_shutdown(i2c);
}
void usb_init(void)
{
hpm_init();
for (int idx = 0; idx < 2; ++idx) {
if (usb_phy_bringup(idx) < 0)
@ -278,6 +288,33 @@ void usb_init(void)
}
}
void usb_hpm_restore_irqs(bool force)
{
i2c_dev_t *i2c = i2c_init("/arm-io/i2c0");
if (!i2c) {
printf("usb: i2c init failed.\n");
return;
}
for (int idx = 0; idx < USB_INSTANCES; ++idx) {
if (iodev_usb[idx].usage && !force)
continue;
if (tps6598x_irq_state[idx].valid) {
tps6598x_dev_t *tps = hpm_init(i2c, idx);
if (!tps)
continue;
if (tps6598x_restore_irqs(tps, &tps6598x_irq_state[idx]))
printf("usb: unable to restore IRQ masks for hpm%d\n", idx);
tps6598x_shutdown(tps);
}
}
i2c_shutdown(i2c);
}
void usb_iodev_init(void)
{
for (int i = 0; i < USB_INSTANCES; i++) {

View file

@ -9,6 +9,7 @@
dwc3_dev_t *usb_bringup(u32 idx);
void usb_init(void);
void usb_hpm_restore_irqs(bool force);
void usb_iodev_init(void);
void usb_iodev_shutdown(void);