From bf965ce978e6dc333eb1890f7d57dbffe9feb03d Mon Sep 17 00:00:00 2001 From: Jonas Blixt Date: Wed, 26 Apr 2023 15:30:44 +0200 Subject: [PATCH] drivers/cdns: Add the cadence USB3 otg driver --- configs/imx8qxmek_defconfig | 16 +- include/drivers/usb/cdns3_udc_core.h | 18 + include/drivers/usb/imx_cdns3_udc.h | 14 + include/drivers/usb/imx_salvo_phy.h | 8 + include/drivers/usb/usbd.h | 8 +- src/board/imx8qxmek/board.c | 40 ++- src/cm/cm_main.c | 2 + src/drivers/usb/Kconfig | 5 + src/drivers/usb/cdns3_udc_core.c | 493 +++++++++++++++++++++++++++ src/drivers/usb/imx_cdns3_udc.c | 109 ++++++ src/drivers/usb/imx_ehci.c | 22 +- src/drivers/usb/imx_salvo_phy.c | 213 ++++++++++++ src/drivers/usb/makefile.mk | 3 + src/drivers/usb/pb_dev_cls.c | 4 +- src/drivers/usb/usbd.c | 63 ++-- 15 files changed, 966 insertions(+), 52 deletions(-) create mode 100644 include/drivers/usb/cdns3_udc_core.h create mode 100644 include/drivers/usb/imx_cdns3_udc.h create mode 100644 include/drivers/usb/imx_salvo_phy.h create mode 100644 src/drivers/usb/cdns3_udc_core.c create mode 100644 src/drivers/usb/imx_cdns3_udc.c create mode 100644 src/drivers/usb/imx_salvo_phy.c diff --git a/configs/imx8qxmek_defconfig b/configs/imx8qxmek_defconfig index d840959d..d94ec2e0 100644 --- a/configs/imx8qxmek_defconfig +++ b/configs/imx8qxmek_defconfig @@ -35,20 +35,18 @@ CONFIG_IMX8X_SIGN_IMAGE=y # Generic options # CONFIG_KEYSTORE="pki/internal_keystore.bpak" -CONFIG_LOGLEVEL=3 +CONFIG_LOGLEVEL=2 CONFIG_ENABLE_WATCHDOG=y CONFIG_PRINT_BOOT_BANNER=y CONFIG_WATCHDOG_TIMEOUT=5 -CONFIG_ENABLE_TIMESTAMPING=y -CONFIG_PRINT_TIMESTAMPS=y -CONFIG_NO_OF_TIMESTAMPS=64 +# CONFIG_ENABLE_TIMESTAMPING is not set CONFIG_DEVICE_UUID=y CONFIG_CRYPTO=y CONFIG_CRYPTO_MAX_HASH_OPS=1 CONFIG_CRYPTO_MAX_DSA_OPS=1 CONFIG_BIO_CORE=y CONFIG_BIO_MAX_DEVS=32 -CONFIG_SELF_TEST=y +# CONFIG_SELF_TEST is not set # end of Generic options # @@ -67,7 +65,7 @@ CONFIG_BOOT_CORE=y CONFIG_BOOT_BPAK_IMAGE_HELPERS=y CONFIG_BOOT_AB_DRIVER=y CONFIG_BOOT_LINUX=y -CONFIG_BOOT_LOAD_CHUNK_KiB=4096 +CONFIG_BOOT_LOAD_CHUNK_kB=4096 # end of Boot # @@ -101,6 +99,7 @@ CONFIG_MMC_CORE=y # CONFIG_MMC_CORE_DEBUG_CMDS is not set # CONFIG_MMC_CORE_DEBUG_IOS is not set # CONFIG_MMC_CORE_HS200_TUNE is not set +# CONFIG_MMC_CORE_OVERRIDE_BOOT_PART_SZ is not set CONFIG_IMX_USDHC=y # CONFIG_IMX_USDHC_XTRA_DEBUG is not set # end of MMC Host drivers @@ -128,9 +127,10 @@ CONFIG_DRIVERS_IMX_LPUART=y # USB # CONFIG_DRIVER_USB_DEVICE=y -CONFIG_DRIVER_IMX_USB2_PHY=y -CONFIG_DRIVER_IMX_EHCI=y +# CONFIG_DRIVER_IMX_USB2_PHY is not set +# CONFIG_DRIVER_IMX_EHCI is not set # CONFIG_DRIVER_IMX_USBDCD is not set +CONFIG_DRIVER_IMX_CDNS3_UDC=y CONFIG_DRIVER_USB_PB_CLS=y # end of USB diff --git a/include/drivers/usb/cdns3_udc_core.h b/include/drivers/usb/cdns3_udc_core.h new file mode 100644 index 00000000..081bfc09 --- /dev/null +++ b/include/drivers/usb/cdns3_udc_core.h @@ -0,0 +1,18 @@ +#ifndef INCLUDE_DRIVERS_USB_CDNS3_UDC_CORE_H +#define INCLUDE_DRIVERS_USB_CDNS3_UDC_CORE_H + +#include +#include + +int cdns3_udc_core_init(void); +int cdns3_udc_core_stop(void); +int cdns3_udc_core_xfer_zlp(usb_ep_t ep); +int cdns3_udc_core_set_address(uint16_t addr); +int cdns3_udc_core_configure_ep(usb_ep_t ep, enum usb_ep_type ep_type, size_t pkt_sz); +int cdns3_udc_core_poll_setup_pkt(struct usb_setup_packet *pkt); +void cdns3_udc_core_xfer_cancel(usb_ep_t ep); +int cdns3_udc_core_xfer_complete(usb_ep_t ep); +int cdns3_udc_core_xfer_start(usb_ep_t ep, void *buf, size_t length); +void cdns3_udc_core_set_base(uintptr_t base_); + +#endif diff --git a/include/drivers/usb/imx_cdns3_udc.h b/include/drivers/usb/imx_cdns3_udc.h new file mode 100644 index 00000000..afc48358 --- /dev/null +++ b/include/drivers/usb/imx_cdns3_udc.h @@ -0,0 +1,14 @@ +#ifndef INCLUDE_DRIVERS_USB_IMX_CDNS3_UDC_H +#define INCLUDE_DRIVERS_USB_IMX_CDNS3_UDC_H + +#include + +struct imx_cdns3_udc_config { + uintptr_t base; + uintptr_t non_core_base; + uintptr_t phy_base; +}; + +int imx_cdns3_udc_init(const struct imx_cdns3_udc_config *cfg); + +#endif diff --git a/include/drivers/usb/imx_salvo_phy.h b/include/drivers/usb/imx_salvo_phy.h new file mode 100644 index 00000000..45216d05 --- /dev/null +++ b/include/drivers/usb/imx_salvo_phy.h @@ -0,0 +1,8 @@ +#ifndef INCLUDE_DRIVERS_USB_IMX_SALVO_PHY_H +#define INCLUDE_DRIVERS_USB_IMX_SALVO_PHY_H + +#include + +int imx_salvo_phy_init(uintptr_t base); + +#endif diff --git a/include/drivers/usb/usbd.h b/include/drivers/usb/usbd.h index 02f65f77..054bc46c 100644 --- a/include/drivers/usb/usbd.h +++ b/include/drivers/usb/usbd.h @@ -171,19 +171,23 @@ struct usbd_hal_ops { int (*init)(void); int (*stop)(void); - int (*xfer_start)(usb_ep_t ep, uintptr_t buf, size_t length); + int (*xfer_start)(usb_ep_t ep, void *buf, size_t length); int (*xfer_complete)(usb_ep_t ep); void (*xfer_cancel)(usb_ep_t ep); int (*poll_setup_pkt)(struct usb_setup_packet *pkt); int (*configure_ep)(usb_ep_t ep, enum usb_ep_type ep_type, size_t pkt_sz); int (*set_address)(uint16_t addr); + int (*ep0_xfer_zlp)(usb_ep_t ep); }; +/* USB Device interface */ +const char *ep_to_str(usb_ep_t ep); int usbd_init_hal_ops(const struct usbd_hal_ops *ops); int usbd_init_cls(const struct usbd_cls_config *cfg); int usbd_init(void); int usbd_connect(void); int usbd_disconnect(void); -int usbd_xfer(usb_ep_t ep, uintptr_t buf, size_t length); +int usbd_xfer(usb_ep_t ep, void *buf, size_t length); + #endif // INCLUDE_DRIVERS_USB_USBD_H diff --git a/src/board/imx8qxmek/board.c b/src/board/imx8qxmek/board.c index 1c2ace54..a9122b18 100644 --- a/src/board/imx8qxmek/board.c +++ b/src/board/imx8qxmek/board.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -478,16 +479,43 @@ int board_init(struct imx8x_platform *plat_ptr) int cm_board_init(void) { int rc; - /* Enable usb stuff */ - sc_pm_set_resource_power_mode(plat->ipc, SC_R_USB_0, SC_PM_PW_MODE_ON); - sc_pm_set_resource_power_mode(plat->ipc, SC_R_USB_0_PHY, SC_PM_PW_MODE_ON); - imx_usb2_phy_init(0x5B100000); + /* Request power domains */ + sc_pm_set_resource_power_mode(plat->ipc, SC_R_USB_2, SC_PM_PW_MODE_ON); + sc_pm_set_resource_power_mode(plat->ipc, SC_R_USB_2_PHY, SC_PM_PW_MODE_ON); - rc = imx_ehci_init(IMX_EHCI_BASE); + /* Request clocks */ + rc = sc_pm_clock_enable(plat->ipc, SC_R_USB_2, SC_PM_CLK_PER, true, false); + + if (rc != SC_ERR_NONE) { + LOG_ERR("USB_2 per clk enable failed!"); + return -PB_ERR; + } + + rc = sc_pm_clock_enable(plat->ipc, SC_R_USB_2, SC_PM_CLK_MISC, true, false); + + if (rc != SC_ERR_NONE) { + LOG_ERR("USB_2 misc clk enable failed!"); + return -PB_ERR; + } + + rc = sc_pm_clock_enable(plat->ipc, SC_R_USB_2, SC_PM_CLK_MST_BUS, true, false); + + if (rc != SC_ERR_NONE) { + LOG_ERR("USB_2 mst bus clk enable failed!"); + return -PB_ERR; + } + + static const struct imx_cdns3_udc_config usb_cfg = { + .base = 0x5B120000, + .non_core_base = 0x5B110000, + .phy_base = 0x5B160000, + }; + + rc = imx_cdns3_udc_init(&usb_cfg); if (rc != PB_OK) { - LOG_ERR("imx_ehci_init failed (%i)", rc); + LOG_ERR("cdns_usb_init failed (%i)", rc); return rc; } diff --git a/src/cm/cm_main.c b/src/cm/cm_main.c index 258234db..23e679ca 100644 --- a/src/cm/cm_main.c +++ b/src/cm/cm_main.c @@ -673,6 +673,7 @@ static int pb_command_parse(void) break; case PB_CMD_DEVICE_READ_CAPS: { + LOG_INFO("Read caps"); struct pb_result_device_caps caps = {0}; caps.stream_no_of_buffers = 2; caps.stream_buffer_size = CONFIG_CM_BUF_SIZE_KiB*1024; @@ -739,6 +740,7 @@ static int pb_command_parse(void) break; case PB_CMD_DEVICE_IDENTIFIER_READ: { + LOG_INFO("Read identifier"); struct pb_result_device_identifier *ident = \ (struct pb_result_device_identifier *) buffer; memset(ident->board_id, 0, sizeof(ident->board_id)); diff --git a/src/drivers/usb/Kconfig b/src/drivers/usb/Kconfig index cbd5420f..5c9e9a2c 100644 --- a/src/drivers/usb/Kconfig +++ b/src/drivers/usb/Kconfig @@ -21,6 +21,11 @@ config DRIVER_IMX_USBDCD depends on DRIVER_USB_DEVICE default n +config DRIVER_IMX_CDNS3_UDC + bool "i.MX CDNS3 UDC Driver" + depends on SOC_FAMILY_IMX + default n + config DRIVER_USB_PB_CLS bool "Punchboot device class" default y diff --git a/src/drivers/usb/cdns3_udc_core.c b/src/drivers/usb/cdns3_udc_core.c new file mode 100644 index 00000000..43aca815 --- /dev/null +++ b/src/drivers/usb/cdns3_udc_core.c @@ -0,0 +1,493 @@ +/** + * Punch BOOT + * + * Copyright (C) 2023 Jonas Blixt + * + * SPDX-License-Identifier: BSD-3-Clause + * + * A minimalistic driver for the cadence USB3 OTG controller. + * + * There is not really any good documentation publicly available for this + * controller and PHY. This drivers is pieced together / reverse engineered + * by looking at the imx8qxp datasheet, uboot and linux implementations. + * + * TODO: + * - EP0 has an in-transfer alignment buffer, but out transfers require + * the buffer argument to be aligned + * - Transfers on other endpoints must supply DMA aligned buffers + * - TRB amount is hard coded. This means that it's possbile to configure + * a too large xfer buffer for command mode, so that there is not enough + * trb's. + * - EP burst size is hardcoded to 64byte (512b endpoint max pkt length) + * - Support for super speed + * - Occationally we get TRBERR irq's. It's not clear why, but re-starting + * the DMA transfer works and there seems to be no dropped packets. + * - A few places that wait for a status bit change should have timeout's + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers and bits */ +#define CDNS_USB_CONF (0x20000) +#define CONF_DMULT BIT(9) +#define CONF_DEV_EN BIT(14) +#define CONF_L1DS BIT(17) +#define CONF_CLK2OFFDS BIT(19) +#define CONF_CLK3OFFDS BIT(22) +#define CONF_U1DS BIT(25) +#define CONF_U2DS BIT(27) + +#define CDNS_USB_STS (0x20004) +#define STS_USB2CONS BIT(20) +#define STS_INRST BIT(10) +#define STS_LST(x) ((x >> 26) & 0x0f) + +#define CDNS_USB_CMD (0x20008) +#define USB_CMD_SET_ADDR BIT(0) +#define USB_CMD_ADDR(x) ((x & 0x7f) << 1) + +#define CDNS_USB_IEN (0x20014) +#define CDNS_USB_ISTS (0x20018) +#define ISTS_CON2I BIT(16) // HS/FS mode connection detected +#define ISTS_U2RESI BIT(18) // USB reset in HS/FS mode ends +#define ISTS_CFGRESI BIT(26) // USB configuration reset detected + +#define CDNS_USB_EP_SEL (0x2001C) +#define EP_SEL_DIR(x) ((x & 1) << 7) +#define EP_SEL(x) (x & 0x0f) + +#define CDNS_USB_EP_TRADDR (0x20020) +#define CDNS_USB_EP_CFG (0x20024) +#define EP_CFG_MAX_PKT_SZ(x) ((x & 0x7ff) << 16) +#define EP_CFG_TYPE(x) ((x & 0x03) << 1) +#define EP_CFG_STREAM_EN BIT(3) +#define EP_CFG_ENABLE BIT(0) + +#define CDNS_USB_EP_CMD (0x20028) +#define EP_CMD_SSTALL BIT(1) +#define EP_CMD_ERDY BIT(3) +#define EP_CMD_REQ_CMPL BIT(5) +#define EP_CMD_DRDY BIT(6) + +#define CDNS_USB_EP_STS_EN (0x20034) +#define EP_STS_EN_SETUP BIT(0) +#define EP_STS_EN_DESCMISEN BIT(4) +#define EP_STS_EN_TRBERREN BIT(7) + +#define CDNS_USB_EP_STS (0x2002C) +#define EP_STS_SETUP BIT(0) +#define EP_STS_IOC BIT(2) +#define EP_STS_DESCMIS BIT(4) +#define EP_STS_TRBERR BIT(7) + +#define CDNS_USB_EP_STS_SID (0x20030) +#define CDNS_USB_DRBL (0x20038) +#define CDNS_USB_EP_IEN (0x2003C) +#define CDNS_USB_EP_ISTS (0x20040) +#define CDNS_USB_PWR (0x20044) +#define CDNS_USB_CONF2 (0x20048) +#define CDNS_USB_CAP1 (0x2004C) +#define CDNS_USB_CAP2 (0x20050) +#define CDNS_USB_CAP3 (0x20054) +#define CDNS_USB_CAP5 (0x2005C) +#define CDNS_USB_CAP6 (0x20060) /* Device controller version */ +#define CDNS_USB_CPKT1 (0x20064) +#define CDNS_USB_CPKT2 (0x20068) +#define CDNS_USB_CPKT3 (0x2006C) +#define CDNS_USB_DBG_LINK1 (0x20104) +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET BIT(25) +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(x) ((x & 0xff) << 8) + +/* Transfer descriptor & bits*/ +struct cdns_trb +{ + uint32_t addr; + uint32_t length; + uint32_t flags; +} __packed; + +#define TRB_MAX_LENGTH (127 * 1024) // (130048) +#define TRB_BURST_LENGTH(x) ((x & 0xFF) << 16) + +#define TRB_FLAG_CYCLE BIT(0) +#define TRB_FLAG_TOGGLE BIT(1) +#define TRB_FLAG_CHAIN BIT(4) +#define TRB_FLAG_IOC BIT(5) +#define TRB_TYPE(x) ((x) << 10) +#define TRB_TYPE_NORMAL (1) +#define TRB_TYPE_LINK (6) + +static uintptr_t base; +static struct usb_setup_packet setup_pkt __aligned(64); // TODO: Define CASHE_WRITEBACK_GRANULE +static struct cdns_trb ep0_in_trb[2] __aligned(64); +static struct cdns_trb ep0_out_trb[2] __aligned(64); +static uint8_t align_buffer[512] __aligned(64); +#define CDNS_TRB_COUNT 128 +static struct cdns_trb trb[CDNS_TRB_COUNT] __aligned(64); // TODO: Hardcoded amount of TRB's + +static void *current_xfer_buf; +static size_t current_xfer_length; +static usb_ep_t current_xfer_ep; + +static int select_ep(usb_ep_t ep) +{ + uint8_t dir = ep % 2; // 0 = Out, 1 = In + uint8_t ep_val = ep / 2; + + if (ep >= USB_EP_END) + return -PB_ERR_PARAM; + + mmio_write_32(base + CDNS_USB_EP_SEL, EP_SEL_DIR(dir) | EP_SEL(ep_val)); + return PB_OK; +} + +static int ep0_xfer_start(void *buf, size_t length, bool in_xfer) +{ + void *buf_p = buf; + struct cdns_trb *trb_p = in_xfer?ep0_in_trb:ep0_out_trb; + + if (length > TRB_MAX_LENGTH) + return -PB_ERR_PARAM; + + if (in_xfer) { + // TODO: Temparary alignment fix + memcpy(align_buffer, buf, length); + buf_p = align_buffer; + } + + if (buf_p) { + arch_clean_cache_range((uintptr_t) buf_p, length); + } + + //LOG_DBG("%s: %p, len=%zu", in_xfer?"IN":"OUT", buf_p, length); + + trb_p->addr = (uint32_t)(uintptr_t) buf_p; + trb_p->length = (uint32_t) length; + trb_p->flags = TRB_FLAG_CYCLE | TRB_FLAG_IOC | TRB_TYPE(TRB_TYPE_NORMAL); + arch_clean_cache_range((uintptr_t) trb_p, sizeof(*trb_p)); + + select_ep(in_xfer?USB_EP0_IN:USB_EP0_OUT); + mmio_write_32(base + CDNS_USB_EP_STS, EP_STS_IOC | EP_STS_TRBERR); + mmio_write_32(base + CDNS_USB_EP_TRADDR, (uint32_t)(uintptr_t) trb_p); + mmio_write_32(base + CDNS_USB_EP_CMD, EP_CMD_DRDY); + + return PB_OK; +} + +int cdns3_udc_core_xfer_start(usb_ep_t ep, void *buf, size_t length) +{ + size_t chunk; + size_t bytes_to_xfer = length; + struct cdns_trb *trb_p = &trb[0]; + uintptr_t buf_p = (uintptr_t) buf; + uint8_t ep_val = ep / 2; + size_t desc_count = 0; + bool in_xfer = !!(ep % 2); + + if (ep >= USB_EP_END) + return -PB_ERR_PARAM; + + LOG_DBG("%s: buf=%p, len=%zu", ep_to_str(ep), buf, length); + + if (ep_val == 0) { + return ep0_xfer_start(buf, length, in_xfer); + } + + if (length > (TRB_MAX_LENGTH * CDNS_TRB_COUNT)) + return -PB_ERR_PARAM; + + // TODO: xfer alignment, what is the minimum alignment? + current_xfer_ep = ep; + current_xfer_buf = buf; + current_xfer_length = length; + + arch_clean_cache_range((uintptr_t) buf, length); + + while (bytes_to_xfer > 0) { + chunk = bytes_to_xfer > TRB_MAX_LENGTH?TRB_MAX_LENGTH:bytes_to_xfer; + + trb_p->addr = (uint32_t) buf_p; + //TODO: Burst size, depends on EP size + // EP sz Burst sz + // ----- -------- + // 1024 128 + // => 512 64 + // < 512 16 + trb_p->length = (uint32_t) chunk | TRB_BURST_LENGTH(64); + trb_p->flags = TRB_FLAG_CYCLE | TRB_TYPE(TRB_TYPE_NORMAL); + + bytes_to_xfer -= chunk; + buf_p += chunk; + + /* Last TRB should generate completion IRQ */ + if (bytes_to_xfer == 0) { + trb_p->flags |= TRB_FLAG_IOC; + } + + LOG_DBG("desc[%lu]: addr=%x, len=%zu, flags=%x", + desc_count, trb_p->addr, chunk, trb_p->flags); + + trb_p++; + trb_p->flags = 0; + desc_count++; + } + + /* Stop gap TRB */ + trb_p->addr = 0; + trb_p->length = 0; + trb_p->flags = 0; + + arch_clean_cache_range((uintptr_t) trb, sizeof(trb[0]) * desc_count); + + select_ep(ep); + mmio_write_32(base + CDNS_USB_EP_STS, EP_STS_IOC | EP_STS_TRBERR); + mmio_write_32(base + CDNS_USB_EP_TRADDR, (uint32_t)(uintptr_t) trb); + mmio_write_32(base + CDNS_USB_EP_CMD, EP_CMD_DRDY); + + return PB_OK; +} + +int cdns3_udc_core_xfer_complete(usb_ep_t ep) +{ + bool in_xfer = !!(ep % 2); + uint32_t ep_sts; + + select_ep(ep); + ep_sts = mmio_read_32(base + CDNS_USB_EP_STS); + + if (ep_sts & EP_STS_TRBERR) { + mmio_write_32(base + CDNS_USB_EP_STS, EP_STS_TRBERR); + + LOG_DBG("%s: TRBERR (0x%x)", ep_to_str(ep), ep_sts); + struct cdns_trb *trb_p = (struct cdns_trb *)(uintptr_t) mmio_read_32(base + CDNS_USB_EP_TRADDR); + arch_invalidate_cache_range((uintptr_t) trb_p, sizeof(struct cdns_trb)); + LOG_DBG(" addr=%x, len=%zu, flags=%x", trb_p->addr, trb_p->length & 0xffff, trb_p->flags); + LOG_DBG(" trb_base=%p, trb_p=0x%x", (void *) trb, trb_p); + + // Re-start DMA + mmio_write_32(base + CDNS_USB_EP_CMD, EP_CMD_DRDY); + } + + if ((ep_sts & EP_STS_IOC)) { + LOG_DBG("%s: Xfer done %x", ep_to_str(ep), ep_sts); + + mmio_write_32(base + CDNS_USB_EP_STS, EP_STS_IOC); + + if (ep == current_xfer_ep) { + if (!in_xfer && current_xfer_buf && current_xfer_length > 0) { + LOG_DBG("Invalidate %p, len=%zu", current_xfer_buf, current_xfer_length); + arch_invalidate_cache_range((uintptr_t) current_xfer_buf, + current_xfer_length); + } + + current_xfer_ep = -1; + current_xfer_buf = NULL; + current_xfer_length = 0; + } + + return PB_OK; + } else { + return -PB_ERR_AGAIN; + } +} + +void cdns3_udc_core_xfer_cancel(usb_ep_t ep) +{ + (void) ep; + + // Not needed in this driver since we have dedicated descriptors + // for ep0 +} + +static void cdns_process_irq(void) +{ + uint32_t ists = mmio_read_32(base + CDNS_USB_ISTS); + + if (ists & ISTS_CFGRESI) { + ep0_xfer_start(&setup_pkt, sizeof(setup_pkt), false); + } + + mmio_write_32(base + CDNS_USB_ISTS, ists); +} + +int cdns3_udc_core_poll_setup_pkt(struct usb_setup_packet *pkt) +{ + bool got_setup_pkt = false; + uint32_t ep_ists; + uint32_t ep_sts; + + ep_ists = mmio_read_32(base + CDNS_USB_EP_ISTS); + mmio_write_32(base + CDNS_USB_EP_ISTS, ep_ists); + if (ep_ists & 1) { /* EP0 Out */ + select_ep(USB_EP0_OUT); + ep_sts = mmio_read_32(base + CDNS_USB_EP_STS); + + if (ep_sts & EP_STS_SETUP) { + arch_invalidate_cache_range((uintptr_t) &setup_pkt, sizeof(setup_pkt)); + memcpy(pkt, &setup_pkt, sizeof(setup_pkt)); + got_setup_pkt = true; + } + + if (ep_sts & EP_STS_DESCMIS) { + LOG_ERR("Desc missing!"); + } + + if (ep_sts & EP_STS_TRBERR) { + LOG_ERR("TRB Error"); + } + + if (ep_sts & EP_STS_IOC) { + /* Queue up a new out setup xfer */ + ep0_xfer_start(&setup_pkt, sizeof(setup_pkt), false); + /* Ack the setup stage */ + mmio_write_32(base + CDNS_USB_EP_CMD, EP_CMD_REQ_CMPL); + } + + mmio_write_32(base + CDNS_USB_EP_STS, ep_sts); + } + + cdns_process_irq(); + + if (got_setup_pkt) + return PB_OK; + else + return -PB_ERR_AGAIN; +} + +int cdns3_udc_core_configure_ep(usb_ep_t ep, enum usb_ep_type ep_type, size_t pkt_sz) +{ + uint32_t ep_num = ep / 2; + LOG_INFO("ep=%u, type=%i, pkt_sz=%zu", ep, ep_type, pkt_sz); + + // TODO's + // o Hard coded bulk EP type + // o Enable both IN/OUT INT + // + select_ep(ep); + mmio_write_32(base + CDNS_USB_EP_CFG, EP_CFG_ENABLE | + EP_CFG_TYPE(2) | + EP_CFG_MAX_PKT_SZ(pkt_sz)); + + mmio_write_32(base + CDNS_USB_EP_STS_EN, EP_STS_EN_DESCMISEN | + EP_STS_EN_TRBERREN); + + /* Enable interrupt for EP0 in/out */ + mmio_write_32(base + CDNS_USB_EP_IEN, (1 << (16 + ep_num)) | (1 << ep_num)); + return PB_OK; +} + +int cdns3_udc_core_set_address(uint16_t addr) +{ + mmio_clrsetbits_32(base + CDNS_USB_CMD, 0xff, + USB_CMD_SET_ADDR | USB_CMD_ADDR(addr)); + + return PB_OK; +} + +int cdns3_udc_core_xfer_zlp(usb_ep_t ep) +{ + select_ep(ep); + /** + * We set ERDY here because there is no data stage in the set address req. + * This means that we exit the setup stage and skip the data stage, + * since there is no data to write to the host. + * + * Setting REQ_CMPL means that we send the ACK when we enter the status stage. + */ + + if (ep == USB_EP0_IN) { + mmio_write_32(base + CDNS_USB_EP_CMD, EP_CMD_ERDY | + EP_CMD_REQ_CMPL); + + // TODO: Timeout? + while(mmio_read_32(base + CDNS_USB_EP_CMD) & EP_CMD_REQ_CMPL); + } else if (ep == USB_EP0_OUT) { + mmio_write_32(base + CDNS_USB_EP_CMD, EP_CMD_ERDY); + } + + return PB_OK; +} + +int cdns3_udc_core_init(void) +{ + uint32_t dev_ctrl_version = 0; + + LOG_INFO("Probing @ 0x%"PRIxPTR"", base); + + dev_ctrl_version = mmio_read_32(base + CDNS_USB_CAP6); + + LOG_INFO("Device controller version: 0x%x", dev_ctrl_version); + + + /* Wait for controller to come out of reset */ + while (!(mmio_read_32(base + CDNS_USB_STS) & BIT(10))) + ; + + /* Configure and enable EP0 */ + // TODO: Hardcoded values for SS + + /* Enable all irq's */ + mmio_write_32(base + CDNS_USB_IEN, 0xffffffff); + + select_ep(USB_EP0_OUT); + // STREAM_EN should probably be set before we can get SS working + mmio_write_32(base + CDNS_USB_EP_CFG, EP_CFG_ENABLE | + EP_CFG_MAX_PKT_SZ(512)); + + mmio_write_32(base + CDNS_USB_EP_STS_EN, EP_STS_EN_SETUP | + EP_STS_EN_DESCMISEN | + EP_STS_EN_TRBERREN); + + select_ep(USB_EP0_IN); + mmio_write_32(base + CDNS_USB_EP_CFG, EP_CFG_ENABLE | + EP_CFG_MAX_PKT_SZ(512)); + + mmio_write_32(base + CDNS_USB_EP_STS_EN, EP_STS_EN_SETUP | + EP_STS_EN_TRBERREN); + + /* Enable interrupt for EP0 in/out */ + mmio_write_32(base + CDNS_USB_EP_IEN, BIT(16) | BIT(0)); + + + mmio_write_32(base + CDNS_USB_CONF, CONF_CLK2OFFDS | + CONF_CLK3OFFDS | + CONF_L1DS); + + mmio_write_32(base + CDNS_USB_CONF, CONF_U1DS | CONF_U2DS); + + /* Configure DMA for multiple transfers. When the DMA has completed the + * current trb or trb chain it will look at the next trb index for a valid + * descriptor and continue transfering data */ + mmio_write_32(base + CDNS_USB_CONF, CONF_DMULT); + mmio_write_32(base + CDNS_USB_CONF, CONF_DEV_EN); + + mmio_write_32(base + CDNS_USB_DBG_LINK1, + DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET | + DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(0x3c)); + + // Maybe we don't need to do this here + ep0_xfer_start(&setup_pkt, sizeof(setup_pkt), false); + + LOG_INFO("usb_sts = 0x%x", mmio_read_32(base + CDNS_USB_STS)); + + return PB_OK; +} + +int cdns3_udc_core_stop(void) +{ + LOG_INFO("Stop"); + return PB_OK; +} + +void cdns3_udc_core_set_base(uintptr_t base_) +{ + base = base_; +} diff --git a/src/drivers/usb/imx_cdns3_udc.c b/src/drivers/usb/imx_cdns3_udc.c new file mode 100644 index 00000000..a66ec72d --- /dev/null +++ b/src/drivers/usb/imx_cdns3_udc.c @@ -0,0 +1,109 @@ +/** + * Punch BOOT + * + * Copyright (C) 2023 Jonas Blixt + * + * SPDX-License-Identifier: BSD-3-Clause + * + * A minimalistic driver for the cadence USB3 OTG controller. This only + * supports the Device role. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB3_CORE_CTRL11 (0x00) +#define USB3_CORE_CTRL12 (0x04) +#define USB3_CORE_INT (0x08) +#define USB3_CORE_STATUS (0x0c) + +/* USB3_CORE_CTRL11 */ +#define SW_RESET_MASK (0x3f << 26) +#define PWR_SW_RESET BIT(31) +#define APB_SW_RESET BIT(30) +#define AXI_SW_RESET BIT(29) +#define RW_SW_RESET BIT(28) +#define PHY_SW_RESET BIT(27) +#define PHYAHB_SW_RESET BIT(26) +#define ALL_SW_RESET (PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \ + RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET) +#define OC_DISABLE BIT(9) +#define MDCTRL_CLK_SEL BIT(7) +#define MODE_STRAP_MASK (0x7) +#define DEV_MODE BIT(2) +#define HOST_MODE BIT(1) +#define OTG_MODE BIT(0) + +/* USB3_INT_REG */ +#define CLK_125_REQ BIT(29) +#define LPM_CLK_REQ BIT(28) +#define DEVU3_WAEKUP_EN BIT(14) +#define OTG_WAKEUP_EN BIT(12) +#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ +#define HOST_INT1_EN BIT(0) /* HOST INT b7:0 */ + +/* USB3_CORE_STATUS */ +#define MDCTRL_CLK_STATUS BIT(15) +#define DEV_POWER_ON_READY BIT(13) +#define HOST_POWER_ON_READY BIT(12) + +static const struct imx_cdns3_udc_config *cfg; + +static int imx_cdns3_udc_start(void) +{ + int rc; + // NOTE: U-boot configures SSPHY_STATUS, but that reg is not documented + // in imx8x datasheet... + + mmio_clrsetbits_32(cfg->non_core_base + USB3_CORE_CTRL11, 0, ALL_SW_RESET); + pb_delay_us(1); + mmio_clrsetbits_32(cfg->non_core_base + USB3_CORE_CTRL11, MODE_STRAP_MASK, DEV_MODE); + mmio_clrsetbits_32(cfg->non_core_base + USB3_CORE_CTRL11, PHYAHB_SW_RESET, 0); + pb_delay_ms(1); + + rc = imx_salvo_phy_init(cfg->phy_base); + + if (rc != PB_OK) + return rc; + + mmio_clrsetbits_32(cfg->non_core_base + USB3_CORE_INT, 0, DEV_INT_EN); + mmio_clrsetbits_32(cfg->non_core_base + USB3_CORE_CTRL11, ALL_SW_RESET, 0); + + LOG_DBG("Waiting for core to come out of reset"); + + while (!(mmio_read_32(cfg->non_core_base + USB3_CORE_STATUS) & DEV_POWER_ON_READY)) + ; + LOG_DBG("Okay, let's go"); + + return cdns3_udc_core_init(); +} + +int imx_cdns3_udc_init(const struct imx_cdns3_udc_config *cfg_) +{ + cfg = cfg_; + + cdns3_udc_core_set_base(cfg->base); + + static const struct usbd_hal_ops ops = { + .init = imx_cdns3_udc_start, + .stop = cdns3_udc_core_stop, + .xfer_start = cdns3_udc_core_xfer_start, + .xfer_complete = cdns3_udc_core_xfer_complete, + .xfer_cancel = cdns3_udc_core_xfer_cancel, + .poll_setup_pkt = cdns3_udc_core_poll_setup_pkt, + .configure_ep = cdns3_udc_core_configure_ep, + .set_address = cdns3_udc_core_set_address, + .ep0_xfer_zlp = cdns3_udc_core_xfer_zlp, + }; + + return usbd_init_hal_ops(&ops); +} diff --git a/src/drivers/usb/imx_ehci.c b/src/drivers/usb/imx_ehci.c index ac1662aa..fbefca5f 100644 --- a/src/drivers/usb/imx_ehci.c +++ b/src/drivers/usb/imx_ehci.c @@ -211,13 +211,14 @@ static int ehci_irq_process(void) * Note: This function will accept buffers of any alignment but for optimal * performace all buffers should be 4k aligned. */ -static int ehci_xfer_start(usb_ep_t ep, uintptr_t bfr, size_t length) +static int ehci_xfer_start(usb_ep_t ep, void *bfr_, size_t length) { struct ehci_queue_head *qh = &dqhs[ep]; struct ehci_transfer_head *dtd = dtds; uint32_t epreg = 0; size_t bytes_to_tx = length; size_t align_length = 0; + uintptr_t bfr = (uintptr_t) bfr_; uintptr_t buf_ptr = bfr; if (ep >= USB_EP_END) @@ -305,7 +306,7 @@ static int ehci_xfer_start(usb_ep_t ep, uintptr_t bfr, size_t length) while (mmio_read_32(ehci_base + EHCI_ENDPTPRIME) & epreg) {}; - xfer_bfr = bfr; + xfer_bfr = (uintptr_t) bfr; xfer_length = length; xfer_head = dtd; xfer_ep = ep; @@ -577,6 +578,22 @@ static int ehci_stop(void) return PB_OK; } +static int ehci_xfer_zlp(usb_ep_t ep) +{ + int rc; + + rc = ehci_xfer_start(ep, 0, 0); + + if (rc != PB_OK) + return rc; + + do { + rc = ehci_xfer_complete(ep); + } while (rc == -PB_ERR_AGAIN); + + return rc; +} + int imx_ehci_init(uintptr_t base) { ehci_base = base; @@ -590,6 +607,7 @@ int imx_ehci_init(uintptr_t base) .poll_setup_pkt = ehci_poll_setup_pkt, .configure_ep = ehci_configure_ep, .set_address = ehci_set_address, + .ep0_xfer_zlp = ehci_xfer_zlp, }; return usbd_init_hal_ops(&ops); diff --git a/src/drivers/usb/imx_salvo_phy.c b/src/drivers/usb/imx_salvo_phy.c new file mode 100644 index 00000000..6b2d5807 --- /dev/null +++ b/src/drivers/usb/imx_salvo_phy.c @@ -0,0 +1,213 @@ +/** + * Punch BOOT + * + * Copyright (C) 2023 Jonas Blixt + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include +#include +#include +#include + +/** + * Register definitions and initial values copied from u-boot, however + * they originate from a cadence manual and NXP originally. Hopefully this is OK + * license wise. + */ + +/* USB3 PHY register definition */ +#define PHY_PMA_CMN_CTRL1 (0xC800 * 4) +#define TB_ADDR_CMN_DIAG_HSCLK_SEL (0x01e0 * 4) +#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR (0x0084 * 4) +#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR (0x0088 * 4) +#define TB_ADDR_CMN_PLL0_INTDIV (0x0094 * 4) +#define TB_ADDR_CMN_PLL0_FRACDIV (0x0095 * 4) +#define TB_ADDR_CMN_PLL0_HIGH_THR (0x0096 * 4) +#define TB_ADDR_CMN_PLL0_SS_CTRL1 (0x0098 * 4) +#define TB_ADDR_CMN_PLL0_SS_CTRL2 (0x0099 * 4) +#define TB_ADDR_CMN_PLL0_DSM_DIAG (0x0097 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_OVRD (0x01c2 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD (0x01c0 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD (0x01c1 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE (0x01C5 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE (0x01C6 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG (0x01C7 * 4) +#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE (0x01c4 * 4) +#define TB_ADDR_CMN_PSM_CLK_CTRL (0x0061 * 4) +#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR (0x40ea * 4) +#define TB_ADDR_XCVR_PSM_RCTRL (0x4001 * 4) +#define TB_ADDR_TX_PSC_A0 (0x4100 * 4) +#define TB_ADDR_TX_PSC_A1 (0x4101 * 4) +#define TB_ADDR_TX_PSC_A2 (0x4102 * 4) +#define TB_ADDR_TX_PSC_A3 (0x4103 * 4) +#define TB_ADDR_TX_DIAG_ECTRL_OVRD (0x41f5 * 4) +#define TB_ADDR_TX_PSC_CAL (0x4106 * 4) +#define TB_ADDR_TX_PSC_RDY (0x4107 * 4) +#define TB_ADDR_RX_PSC_A0 (0x8000 * 4) +#define TB_ADDR_RX_PSC_A1 (0x8001 * 4) +#define TB_ADDR_RX_PSC_A2 (0x8002 * 4) +#define TB_ADDR_RX_PSC_A3 (0x8003 * 4) +#define TB_ADDR_RX_PSC_CAL (0x8006 * 4) +#define TB_ADDR_RX_PSC_RDY (0x8007 * 4) +#define TB_ADDR_TX_TXCC_MGNLS_MULT_000 (0x4058 * 4) +#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 * 4) +#define TB_ADDR_RX_SLC_CU_ITER_TMR (0x80e3 * 4) +#define TB_ADDR_RX_SIGDET_HL_FILT_TMR (0x8090 * 4) +#define TB_ADDR_RX_SAMP_DAC_CTRL (0x8058 * 4) +#define TB_ADDR_RX_DIAG_SIGDET_TUNE (0x81dc * 4) +#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 (0x81df * 4) +#define TB_ADDR_RX_DIAG_BS_TM (0x81f5 * 4) +#define TB_ADDR_RX_DIAG_DFE_CTRL1 (0x81d3 * 4) +#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 (0x81c7 * 4) +#define TB_ADDR_RX_DIAG_ILL_E_TRIM0 (0x81c2 * 4) +#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 (0x81c1 * 4) +#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 (0x81c9 * 4) +#define TB_ADDR_RX_DIAG_RXFE_TM3 (0x81f8 * 4) +#define TB_ADDR_RX_DIAG_RXFE_TM4 (0x81f9 * 4) +#define TB_ADDR_RX_DIAG_LFPSDET_TUNE (0x81dd * 4) +#define TB_ADDR_RX_DIAG_DFE_CTRL3 (0x81d5 * 4) +#define TB_ADDR_RX_DIAG_SC2C_DELAY (0x81e1 * 4) +#define TB_ADDR_RX_REE_VGA_GAIN_NODFE (0x81bf * 4) +#define TB_ADDR_XCVR_PSM_CAL_TMR (0x4002 * 4) +#define TB_ADDR_XCVR_PSM_A0BYP_TMR (0x4004 * 4) +#define TB_ADDR_XCVR_PSM_A0IN_TMR (0x4003 * 4) +#define TB_ADDR_XCVR_PSM_A1IN_TMR (0x4005 * 4) +#define TB_ADDR_XCVR_PSM_A2IN_TMR (0x4006 * 4) +#define TB_ADDR_XCVR_PSM_A3IN_TMR (0x4007 * 4) +#define TB_ADDR_XCVR_PSM_A4IN_TMR (0x4008 * 4) +#define TB_ADDR_XCVR_PSM_A5IN_TMR (0x4009 * 4) +#define TB_ADDR_XCVR_PSM_A0OUT_TMR (0x400a * 4) +#define TB_ADDR_XCVR_PSM_A1OUT_TMR (0x400b * 4) +#define TB_ADDR_XCVR_PSM_A2OUT_TMR (0x400c * 4) +#define TB_ADDR_XCVR_PSM_A3OUT_TMR (0x400d * 4) +#define TB_ADDR_XCVR_PSM_A4OUT_TMR (0x400e * 4) +#define TB_ADDR_XCVR_PSM_A5OUT_TMR (0x400f * 4) +#define TB_ADDR_TX_RCVDET_EN_TMR (0x4122 * 4) +#define TB_ADDR_TX_RCVDET_ST_TMR (0x4123 * 4) +#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR (0x40f2 * 4) +#define TB_ADDR_TX_RCVDETSC_CTRL (0x4124 * 4) + +/* USB2 PHY register definition */ +#define UTMI_REG15 (0x38000 + 0xaf * 4) +#define UTMI_AFE_RX_REG5 (0x38000 + 0x12 * 4) +#define UTMI_AFE_BC_REG4 (0x38000 + 0x29 * 4) + +/* TB_ADDR_TX_RCVDETSC_CTRL */ +#define RXDET_IN_P3_32KHZ BIT(0) +/* + * UTMI_REG15 + * + * Gate how many us for the txvalid signal until analog + * HS/FS transmitters have powered up + */ +#define TXVALID_GATE_THRESHOLD_HS_MASK (BIT(4) | BIT(5)) +/* 0us, txvalid is ready just after HS/FS transmitters have powered up */ +#define TXVALID_GATE_THRESHOLD_HS_0US (BIT(4) | BIT(5)) + +#define SET_B_SESSION_VALID (BIT(6)| BIT(5)) +#define CLR_B_SESSION_VALID (BIT(6)) + +struct cdns_reg_pairs { + uint32_t val; + uint32_t reg_offset; +}; + +static const struct cdns_reg_pairs cdns_nxp_sequence_pair[] = { + {0x0830, PHY_PMA_CMN_CTRL1}, + {0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL}, + {0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR}, + {0x0018, TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR}, + {0x00d0, TB_ADDR_CMN_PLL0_INTDIV}, + {0x4aaa, TB_ADDR_CMN_PLL0_FRACDIV}, + {0x0034, TB_ADDR_CMN_PLL0_HIGH_THR}, + {0x01ee, TB_ADDR_CMN_PLL0_SS_CTRL1}, + {0x7f03, TB_ADDR_CMN_PLL0_SS_CTRL2}, + {0x0020, TB_ADDR_CMN_PLL0_DSM_DIAG}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_OVRD}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD}, + {0x0007, TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE}, + {0x0027, TB_ADDR_CMN_DIAG_PLL0_CP_TUNE}, + {0x0008, TB_ADDR_CMN_DIAG_PLL0_LF_PROG}, + {0x0022, TB_ADDR_CMN_DIAG_PLL0_TEST_MODE}, + {0x000a, TB_ADDR_CMN_PSM_CLK_CTRL}, + {0x0139, TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR}, + {0xbefc, TB_ADDR_XCVR_PSM_RCTRL}, + + {0x7799, TB_ADDR_TX_PSC_A0}, + {0x7798, TB_ADDR_TX_PSC_A1}, + {0x509b, TB_ADDR_TX_PSC_A2}, + {0x0003, TB_ADDR_TX_DIAG_ECTRL_OVRD}, + {0x509b, TB_ADDR_TX_PSC_A3}, + {0x2090, TB_ADDR_TX_PSC_CAL}, + {0x2090, TB_ADDR_TX_PSC_RDY}, + + {0xA6FD, TB_ADDR_RX_PSC_A0}, + {0xA6FD, TB_ADDR_RX_PSC_A1}, + {0xA410, TB_ADDR_RX_PSC_A2}, + {0x2410, TB_ADDR_RX_PSC_A3}, + + {0x23FF, TB_ADDR_RX_PSC_CAL}, + {0x2010, TB_ADDR_RX_PSC_RDY}, + + {0x0020, TB_ADDR_TX_TXCC_MGNLS_MULT_000}, + {0x00ff, TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY}, + {0x0002, TB_ADDR_RX_SLC_CU_ITER_TMR}, + {0x0013, TB_ADDR_RX_SIGDET_HL_FILT_TMR}, + {0x0000, TB_ADDR_RX_SAMP_DAC_CTRL}, + {0x1004, TB_ADDR_RX_DIAG_SIGDET_TUNE}, + {0x4041, TB_ADDR_RX_DIAG_LFPSDET_TUNE2}, + {0x0480, TB_ADDR_RX_DIAG_BS_TM}, + {0x8006, TB_ADDR_RX_DIAG_DFE_CTRL1}, + {0x003f, TB_ADDR_RX_DIAG_ILL_IQE_TRIM4}, + {0x543f, TB_ADDR_RX_DIAG_ILL_E_TRIM0}, + {0x543f, TB_ADDR_RX_DIAG_ILL_IQ_TRIM0}, + {0x0000, TB_ADDR_RX_DIAG_ILL_IQE_TRIM6}, + {0x8000, TB_ADDR_RX_DIAG_RXFE_TM3}, + {0x0003, TB_ADDR_RX_DIAG_RXFE_TM4}, + {0x2408, TB_ADDR_RX_DIAG_LFPSDET_TUNE}, + {0x05ca, TB_ADDR_RX_DIAG_DFE_CTRL3}, + {0x0258, TB_ADDR_RX_DIAG_SC2C_DELAY}, + {0x1fff, TB_ADDR_RX_REE_VGA_GAIN_NODFE}, + + {0x02c6, TB_ADDR_XCVR_PSM_CAL_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A0BYP_TMR}, + {0x02c6, TB_ADDR_XCVR_PSM_A0IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A1IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A2IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A3IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A4IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A5IN_TMR}, + + {0x0002, TB_ADDR_XCVR_PSM_A0OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A1OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A2OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A3OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A4OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A5OUT_TMR}, + /* Change rx detect parameter */ + {0x0960, TB_ADDR_TX_RCVDET_EN_TMR}, + {0x01e0, TB_ADDR_TX_RCVDET_ST_TMR}, + {0x0090, TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR}, +}; + +int imx_salvo_phy_init(uintptr_t base) +{ + LOG_INFO("Init salvo PHY @ 0x%"PRIxPTR, base); + + for (unsigned int i = 0; i < ARRAY_SIZE(cdns_nxp_sequence_pair); i++) { + mmio_write_32(base + cdns_nxp_sequence_pair[i].reg_offset, + cdns_nxp_sequence_pair[i].val); + } + + mmio_clrsetbits_32(base + TB_ADDR_TX_RCVDETSC_CTRL, 0, RXDET_IN_P3_32KHZ); + mmio_clrsetbits_32(base + UTMI_REG15, TXVALID_GATE_THRESHOLD_HS_MASK, + TXVALID_GATE_THRESHOLD_HS_0US); + mmio_write_32(base + UTMI_AFE_RX_REG5, 0x05); + + pb_delay_ms(1); + return PB_OK; +} diff --git a/src/drivers/usb/makefile.mk b/src/drivers/usb/makefile.mk index 587898c9..c2c076c9 100644 --- a/src/drivers/usb/makefile.mk +++ b/src/drivers/usb/makefile.mk @@ -3,3 +3,6 @@ src-$(CONFIG_DRIVER_USB_PB_CLS) += src/drivers/usb/pb_dev_cls.c src-$(CONFIG_DRIVER_IMX_EHCI) += src/drivers/usb/imx_ehci.c src-$(CONFIG_DRIVER_IMX_USBDCD) += src/drivers/usb/imx_usbdcd.c src-$(CONFIG_DRIVER_IMX_USB2_PHY) += src/drivers/usb/imx_usb2_phy.c +src-$(CONFIG_DRIVER_IMX_CDNS3_UDC) += src/drivers/usb/cdns3_udc_core.c +src-$(CONFIG_DRIVER_IMX_CDNS3_UDC) += src/drivers/usb/imx_cdns3_udc.c +src-$(CONFIG_DRIVER_IMX_CDNS3_UDC) += src/drivers/usb/imx_salvo_phy.c diff --git a/src/drivers/usb/pb_dev_cls.c b/src/drivers/usb/pb_dev_cls.c index be9a51d5..aff428a8 100644 --- a/src/drivers/usb/pb_dev_cls.c +++ b/src/drivers/usb/pb_dev_cls.c @@ -147,10 +147,10 @@ int pb_dev_cls_init(void) int pb_dev_cls_write(const void *buf, size_t length) { - return usbd_xfer(USB_EP1_IN, (uintptr_t) buf, length); + return usbd_xfer(USB_EP1_IN, (void *) buf, length); } int pb_dev_cls_read(void *buf, size_t length) { - return usbd_xfer(USB_EP2_OUT, (uintptr_t) buf, length); + return usbd_xfer(USB_EP2_OUT, buf, length); } diff --git a/src/drivers/usb/usbd.c b/src/drivers/usb/usbd.c index a77ba91d..01150f0d 100644 --- a/src/drivers/usb/usbd.c +++ b/src/drivers/usb/usbd.c @@ -28,7 +28,7 @@ static bool enumerated; static int usbd_enumerate(struct usb_setup_packet *setup); -static const char *ep_to_str(usb_ep_t ep) +const char *ep_to_str(usb_ep_t ep) { switch (ep) { case USB_EP0_IN: @@ -79,11 +79,11 @@ static int wait_for_completion(usb_ep_t ep) return rc; } -static int ep0_tx(uintptr_t bfr, size_t length) +static int ep0_tx(const void *bfr, size_t length) { int rc; - rc = hal_ops->xfer_start(USB_EP0_IN, bfr, length); + rc = hal_ops->xfer_start(USB_EP0_IN, (void *) bfr, length); if (rc != PB_OK) return rc; @@ -93,14 +93,12 @@ static int ep0_tx(uintptr_t bfr, size_t length) if (rc != PB_OK) return rc; - rc = hal_ops->xfer_start(USB_EP0_OUT, 0, 0); - - return wait_for_completion(USB_EP0_OUT); + return hal_ops->ep0_xfer_zlp(USB_EP0_OUT); } static int usbd_enumerate(struct usb_setup_packet *setup) { - int rc; + int rc = -PB_ERR; uint16_t length = 0; uint16_t device_status = 0; uint16_t request; @@ -114,26 +112,26 @@ static int usbd_enumerate(struct usb_setup_packet *setup) { if (setup->wValue == 0x0600) { length = MIN(setup->wLength, (uint16_t) sizeof(cls_config->desc->qualifier)); - rc = ep0_tx((uintptr_t) &cls_config->desc->qualifier, length); + rc = ep0_tx(&cls_config->desc->qualifier, length); } else if (setup->wValue == 0x0100) { length = MIN(setup->wLength, (uint16_t) sizeof(cls_config->desc->device)); - rc = ep0_tx((uintptr_t) &cls_config->desc->device, length); + rc = ep0_tx(&cls_config->desc->device, length); } else if (setup->wValue == 0x0200) { length = MIN(setup->wLength, cls_config->desc->config.wTotalLength); - rc = ep0_tx((uintptr_t) &cls_config->desc->config, length); + rc = ep0_tx(&cls_config->desc->config, length); } else if (setup->wValue == 0x0A00) { length = MIN(setup->wLength, (uint16_t) cls_config->desc->interface.bLength); - rc = ep0_tx((uintptr_t) &cls_config->desc->interface, length); + rc = ep0_tx(&cls_config->desc->interface, length); } else if ((setup->wValue & 0xFF00) == 0x0300) { uint8_t str_desc_idx = (uint8_t) (setup->wValue & 0xFF); if (str_desc_idx == 0) { length = MIN(setup->wLength, (uint16_t) sizeof(cls_config->desc->language)); - rc = ep0_tx((uintptr_t) &cls_config->desc->language, length); + rc = ep0_tx(&cls_config->desc->language, length); } else { struct usb_string_descriptor *str_desc = \ cls_config->get_string_descriptor(str_desc_idx); length = MIN(setup->wLength, (uint16_t) str_desc->bLength); - rc = ep0_tx((uintptr_t) str_desc, length); + rc = ep0_tx(str_desc, length); } } else { LOG_ERR("Unhandled descriptor 0x%x", setup->wValue); @@ -143,32 +141,19 @@ static int usbd_enumerate(struct usb_setup_packet *setup) break; case USB_SET_ADDRESS: { - LOG_DBG("Set address 0x%02x", setup->wValue); + //LOG_DBG("Set address 0x%02x", setup->wValue); rc = hal_ops->set_address(setup->wValue); if (rc != PB_OK) break; - rc = hal_ops->xfer_start(USB_EP0_IN, 0, 0); - - if (rc != PB_OK) - break; - - rc = wait_for_completion(USB_EP0_IN); + hal_ops->ep0_xfer_zlp(USB_EP0_IN); } break; case USB_SET_CONFIGURATION: { LOG_DBG("Set configuration"); - rc = hal_ops->xfer_start(USB_EP0_IN, 0, 0); - - if (rc != PB_OK) - break; - - rc = wait_for_completion(USB_EP0_IN); - - if (rc != PB_OK) - break; + hal_ops->ep0_xfer_zlp(USB_EP0_IN); for (int n = 0; n < cls_config->desc->interface.bNumEndpoints; n++) { usb_ep_t ep = (cls_config->desc->eps[n].bEndpointAddress & 0x7f) * 2; @@ -188,6 +173,7 @@ static int usbd_enumerate(struct usb_setup_packet *setup) if (rc != PB_OK) { LOG_ERR("Configuration failed (%i)", rc); + // Here we should stall the endpoint break; } } @@ -198,13 +184,14 @@ static int usbd_enumerate(struct usb_setup_packet *setup) case USB_SET_IDLE: { LOG_DBG("Set idle"); - rc = ep0_tx(0, 0); + hal_ops->ep0_xfer_zlp(USB_EP0_IN); + hal_ops->ep0_xfer_zlp(USB_EP0_OUT); } break; case USB_GET_STATUS: { LOG_DBG("Get status"); - rc = ep0_tx((uintptr_t) &device_status, sizeof(device_status)); + rc = ep0_tx(&device_status, sizeof(device_status)); } break; default: @@ -249,6 +236,9 @@ int usbd_init_cls(const struct usbd_cls_config *cfg) int usbd_init(void) { + if (hal_ops == NULL) + return -PB_ERR_IO; + return hal_ops->init(); } @@ -257,6 +247,9 @@ int usbd_connect(void) int rc; struct usb_setup_packet pkt; + if (hal_ops == NULL) + return -PB_ERR_IO; + /** * 'poll_setup_pkt' returns -PB_ERR_AGAIN when there was no * setup packet to poll. @@ -283,17 +276,23 @@ int usbd_connect(void) int usbd_disconnect(void) { + if (hal_ops == NULL) + return -PB_ERR_IO; + if (hal_ops->stop) return hal_ops->stop(); else return PB_OK; } -int usbd_xfer(usb_ep_t ep, uintptr_t buf, size_t length) +int usbd_xfer(usb_ep_t ep, void *buf, size_t length) { int rc; struct usb_setup_packet pkt; + if (hal_ops == NULL) + return -PB_ERR_IO; + restart_xfer: rc = hal_ops->xfer_start(ep, buf, length);