mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-10 01:34:12 +00:00
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:
parent
de82209079
commit
98076ef693
7 changed files with 153 additions and 15 deletions
|
@ -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()
|
||||
|
||||
|
|
3
src/hv.c
3
src/hv.c
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
67
src/usb.c
67
src/usb.c
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue