diff options
Diffstat (limited to 'tools/src/usb.c')
| -rw-r--r-- | tools/src/usb.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/tools/src/usb.c b/tools/src/usb.c new file mode 100644 index 0000000..1f516a1 --- /dev/null +++ b/tools/src/usb.c @@ -0,0 +1,343 @@ +/* SPDX-License-Identifier: MIT */ + +#include "usb.h" +#include "adt.h" +#include "dart.h" +#include "i2c.h" +#include "iodev.h" +#include "malloc.h" +#include "pmgr.h" +#include "tps6598x.h" +#include "types.h" +#include "usb_dwc3.h" +#include "usb_dwc3_regs.h" +#include "utils.h" +#include "vsprintf.h" + +struct usb_drd_regs { + uintptr_t drd_regs; + uintptr_t drd_regs_unk3; + uintptr_t atc; +}; + +#if USB_IODEV_COUNT > 100 +#error "USB_IODEV_COUNT is limited to 100 to prevent overflow in ADT path names" +#endif + +// length of the format string is is used as buffer size +// limits the USB instance numbers to reasonable 2 digits +#define FMT_DART_PATH "/arm-io/dart-usb%u" +#define FMT_DART_MAPPER_PATH "/arm-io/dart-usb%u/mapper-usb%u" +#define FMT_ATC_PATH "/arm-io/atc-phy%u" +#define FMT_DRD_PATH "/arm-io/usb-drd%u" +#define FMT_HPM_PATH "/arm-io/i2c0/hpmBusManager/hpm%u" + +static tps6598x_irq_state_t tps6598x_irq_state[USB_IODEV_COUNT]; +static bool usb_is_initialized = false; + +static dart_dev_t *usb_dart_init(u32 idx) +{ + int mapper_offset; + char path[sizeof(FMT_DART_MAPPER_PATH)]; + + snprintf(path, sizeof(path), FMT_DART_MAPPER_PATH, idx, idx); + mapper_offset = adt_path_offset(adt, path); + if (mapper_offset < 0) { + // Device not present + return NULL; + } + + u32 dart_idx; + if (ADT_GETPROP(adt, mapper_offset, "reg", &dart_idx) < 0) { + printf("usb: Error getting DART %s device index/\n", path); + return NULL; + } + + snprintf(path, sizeof(path), FMT_DART_PATH, idx); + return dart_init_adt(path, 1, dart_idx, false); +} + +static int usb_drd_get_regs(u32 idx, struct usb_drd_regs *regs) +{ + int adt_drd_path[8]; + int adt_drd_offset; + int adt_phy_path[8]; + int adt_phy_offset; + char phy_path[sizeof(FMT_ATC_PATH)]; + char drd_path[sizeof(FMT_DRD_PATH)]; + + snprintf(drd_path, sizeof(drd_path), FMT_DRD_PATH, idx); + adt_drd_offset = adt_path_offset_trace(adt, drd_path, adt_drd_path); + if (adt_drd_offset < 0) { + // Nonexistent device + return -1; + } + + snprintf(phy_path, sizeof(phy_path), FMT_ATC_PATH, idx); + adt_phy_offset = adt_path_offset_trace(adt, phy_path, adt_phy_path); + if (adt_phy_offset < 0) { + printf("usb: Error getting phy node %s\n", phy_path); + return -1; + } + + if (adt_get_reg(adt, adt_phy_path, "reg", 0, ®s->atc, NULL) < 0) { + printf("usb: Error getting reg with index 0 for %s.\n", phy_path); + return -1; + } + if (adt_get_reg(adt, adt_drd_path, "reg", 0, ®s->drd_regs, NULL) < 0) { + printf("usb: Error getting reg with index 0 for %s.\n", drd_path); + return -1; + } + if (adt_get_reg(adt, adt_drd_path, "reg", 3, ®s->drd_regs_unk3, NULL) < 0) { + printf("usb: Error getting reg with index 3 for %s.\n", drd_path); + return -1; + } + + return 0; +} + +int usb_phy_bringup(u32 idx) +{ + char path[24]; + + if (idx >= USB_IODEV_COUNT) + return -1; + + struct usb_drd_regs usb_regs; + if (usb_drd_get_regs(idx, &usb_regs) < 0) + return -1; + + snprintf(path, sizeof(path), FMT_ATC_PATH, idx); + if (pmgr_adt_power_enable(path) < 0) + return -1; + + snprintf(path, sizeof(path), FMT_DART_PATH, idx); + if (pmgr_adt_power_enable(path) < 0) + return -1; + + snprintf(path, sizeof(path), FMT_DRD_PATH, idx); + if (pmgr_adt_power_enable(path) < 0) + return -1; + + write32(usb_regs.atc + 0x08, 0x01c1000f); + write32(usb_regs.atc + 0x04, 0x00000003); + write32(usb_regs.atc + 0x04, 0x00000000); + write32(usb_regs.atc + 0x1c, 0x008c0813); + write32(usb_regs.atc + 0x00, 0x00000002); + + write32(usb_regs.drd_regs_unk3 + 0x0c, 0x00000002); + write32(usb_regs.drd_regs_unk3 + 0x0c, 0x00000022); + write32(usb_regs.drd_regs_unk3 + 0x1c, 0x00000021); + write32(usb_regs.drd_regs_unk3 + 0x20, 0x00009332); + + return 0; +} + +dwc3_dev_t *usb_iodev_bringup(u32 idx) +{ + dart_dev_t *usb_dart = usb_dart_init(idx); + if (!usb_dart) + return NULL; + + struct usb_drd_regs usb_reg; + if (usb_drd_get_regs(idx, &usb_reg) < 0) + return NULL; + + return usb_dwc3_init(usb_reg.drd_regs, usb_dart); +} + +#define USB_IODEV_WRAPPER(name, pipe) \ + static ssize_t usb_##name##_can_read(void *dev) \ + { \ + return usb_dwc3_can_read(dev, pipe); \ + } \ + \ + static bool usb_##name##_can_write(void *dev) \ + { \ + return usb_dwc3_can_write(dev, pipe); \ + } \ + \ + static ssize_t usb_##name##_read(void *dev, void *buf, size_t count) \ + { \ + return usb_dwc3_read(dev, pipe, buf, count); \ + } \ + \ + static ssize_t usb_##name##_write(void *dev, const void *buf, size_t count) \ + { \ + return usb_dwc3_write(dev, pipe, buf, count); \ + } \ + \ + static ssize_t usb_##name##_queue(void *dev, const void *buf, size_t count) \ + { \ + return usb_dwc3_queue(dev, pipe, buf, count); \ + } \ + \ + static void usb_##name##_handle_events(void *dev) \ + { \ + usb_dwc3_handle_events(dev); \ + } \ + \ + static void usb_##name##_flush(void *dev) \ + { \ + usb_dwc3_flush(dev, pipe); \ + } + +USB_IODEV_WRAPPER(0, CDC_ACM_PIPE_0) +USB_IODEV_WRAPPER(1, CDC_ACM_PIPE_1) + +static struct iodev_ops iodev_usb_ops = { + .can_read = usb_0_can_read, + .can_write = usb_0_can_write, + .read = usb_0_read, + .write = usb_0_write, + .queue = usb_0_queue, + .flush = usb_0_flush, + .handle_events = usb_0_handle_events, +}; + +static struct iodev_ops iodev_usb_sec_ops = { + .can_read = usb_1_can_read, + .can_write = usb_1_can_write, + .read = usb_1_read, + .write = usb_1_write, + .queue = usb_1_queue, + .flush = usb_1_flush, + .handle_events = usb_1_handle_events, +}; + +struct iodev iodev_usb_vuart = { + .ops = &iodev_usb_sec_ops, + .usage = 0, + .lock = SPINLOCK_INIT, +}; + +static tps6598x_dev_t *hpm_init(i2c_dev_t *i2c, const char *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) +{ + char hpm_path[sizeof(FMT_HPM_PATH)]; + + if (usb_is_initialized) + return; + + i2c_dev_t *i2c = i2c_init("/arm-io/i2c0"); + if (!i2c) { + printf("usb: i2c init failed.\n"); + return; + } + + for (u32 idx = 0; idx < USB_IODEV_COUNT; ++idx) { + snprintf(hpm_path, sizeof(hpm_path), FMT_HPM_PATH, idx); + if (adt_path_offset(adt, hpm_path) < 0) + continue; // device not present + tps6598x_dev_t *tps = hpm_init(i2c, hpm_path); + if (!tps) { + printf("usb: failed to init hpm%d\n", idx); + 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); + + for (int idx = 0; idx < USB_IODEV_COUNT; ++idx) + usb_phy_bringup(idx); /* Fails on missing devices, just continue */ + + usb_is_initialized = true; +} + +void usb_hpm_restore_irqs(bool force) +{ + char hpm_path[sizeof(FMT_HPM_PATH)]; + + i2c_dev_t *i2c = i2c_init("/arm-io/i2c0"); + if (!i2c) { + printf("usb: i2c init failed.\n"); + return; + } + + for (u32 idx = 0; idx < USB_IODEV_COUNT; ++idx) { + if (iodev_get_usage(IODEV_USB0 + idx) && !force) + continue; + + if (tps6598x_irq_state[idx].valid) { + snprintf(hpm_path, sizeof(hpm_path), FMT_HPM_PATH, idx); + if (adt_path_offset(adt, hpm_path) < 0) + continue; // device not present + tps6598x_dev_t *tps = hpm_init(i2c, hpm_path); + 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_IODEV_COUNT; i++) { + dwc3_dev_t *opaque; + struct iodev *usb_iodev; + + opaque = usb_iodev_bringup(i); + if (!opaque) + continue; + + usb_iodev = memalign(SPINLOCK_ALIGN, sizeof(*usb_iodev)); + if (!usb_iodev) + continue; + + usb_iodev->ops = &iodev_usb_ops; + usb_iodev->opaque = opaque; + usb_iodev->usage = USAGE_CONSOLE | USAGE_UARTPROXY; + spin_init(&usb_iodev->lock); + + iodev_register_device(IODEV_USB0 + i, usb_iodev); + printf("USB%d: initialized at %p\n", i, opaque); + } +} + +void usb_iodev_shutdown(void) +{ + for (int i = 0; i < USB_IODEV_COUNT; i++) { + struct iodev *usb_iodev = iodev_unregister_device(IODEV_USB0 + i); + if (!usb_iodev) + continue; + + printf("USB%d: shutdown\n", i); + usb_dwc3_shutdown(usb_iodev->opaque); + free(usb_iodev); + } +} + +void usb_iodev_vuart_setup(iodev_id_t iodev) +{ + if (iodev < IODEV_USB0 || iodev >= IODEV_USB0 + USB_IODEV_COUNT) + return; + + iodev_usb_vuart.opaque = iodev_get_opaque(iodev); +} |
