diff --git a/include/unicore-mx/common/dwc_otg.ucd b/include/unicore-mx/common/dwc_otg.ucd index 2ce16e35..1c9bb653 100644 --- a/include/unicore-mx/common/dwc_otg.ucd +++ b/include/unicore-mx/common/dwc_otg.ucd @@ -56,6 +56,9 @@ bit SRQ 1 bit SRQSCS 0 reg GOTGINT 0x004 +% Session end detected. The core sets this bit to indicate that the level of the voltage +% on VBUS is no longer valid for a B-Peripheral session when VBUS < 0.8 V //STM32F4 +bit SEDET 2 % OTG AHB configuration register reg GAHBCFG 0x008 @@ -847,6 +850,16 @@ bits XFRSIZ 7 0 % Power and clock gating control and status register reg PCGCCTL 0xE00 +% Stop PHY clock. The application sets this bit to stop the PHY clock when the USB is suspended, the session +% is not valid, or the device is disconnected. +% The application clears this bit when the USB is resumed or a new session starts //STM32F4 +bit STPPCLK 0 +% Gate HCLK. The application sets this bit to gate HCLK to modules other than the AHB Slave and Master +% and wakeup logic when the USB is suspended or the session is not valid. +% The application clears this bit when the USB is resumed or a new session starts. //STM32F4 +bit GATEHCLK 1 +% Indicates that the PHY has been suspended. +bit PHYSUSP 4 % Data FIFO register diff --git a/include/unicore-mx/usbd/usbd.h b/include/unicore-mx/usbd/usbd.h index 53df4d99..c3efa343 100644 --- a/include/unicore-mx/usbd/usbd.h +++ b/include/unicore-mx/usbd/usbd.h @@ -135,6 +135,16 @@ enum usbd_speed { typedef enum usbd_speed usbd_speed; + /** Optional features */ +enum usbd_backend_features{ + USBD_FEATURE_NONE = 0 + , USBD_PHY_EXT = (1 << 0) + , USBD_VBUS_SENSE = (1 << 1) + , USBD_VBUS_EXT = (1 << 2) + //* provide usb-core auto power-up/down on connect/disconnect + , USBD_USE_POWERDOWN = (1 << 3) +}; + struct usbd_backend_config { /** Number of endpoints. (Including control endpoint) */ uint8_t ep_count; @@ -145,13 +155,8 @@ struct usbd_backend_config { /** Device speed */ usbd_speed speed; - /** Optional features */ - enum { - USBD_FEATURE_NONE = 0, - USBD_PHY_EXT = (1 << 0), - USBD_VBUS_SENSE = (1 << 1), - USBD_VBUS_EXT = (1 << 2) - } feature; + /** Optional features see usbd_backend_features*/ + unsigned int feature; }; typedef struct usbd_backend_config usbd_backend_config; @@ -279,7 +284,7 @@ enum usbd_transfer_flags { USBD_FLAG_PACKET_PER_FRAME_MASK = (0x3 << 5) }; -typedef enum usbd_transfer_flags usbd_transfer_flags; +typedef unsigned char usbd_transfer_flags; /** * USB Transfer status @@ -431,6 +436,8 @@ typedef void (*usbd_set_interface_callback)(usbd_device *dev, typedef void (*usbd_setup_callback)(usbd_device *dev, uint8_t addr, const struct usb_setup_data *setup_data); +typedef void (* usbd_session_callback)(usbd_device *dev, bool online); + typedef void (* usbd_generic_callback)(usbd_device *dev); /** @@ -515,6 +522,16 @@ void usbd_register_resume_callback(usbd_device *dev, void usbd_register_sof_callback(usbd_device *dev, usbd_generic_callback callback); +/** + * Register session connect/disconnect callback + * with USBD_VBUS_SENSE feature primary need for cable + * plug detection + * @param[in] dev USB Device + * @param[in] callback callback to be invoked + */ +void usbd_register_session_callback(usbd_device *dev, + usbd_session_callback callback); + /** * Register SET_CONFIGURATION callback \n * stack will invoke @a callback when ever SET_CONFIGURATION is received @@ -643,6 +660,30 @@ uint8_t usbd_get_address(usbd_device *dev); */ usbd_speed usbd_get_speed(usbd_device *dev); + +/** + * Get status of connected cable. + * @param[in] dev USB Device + * @return true - cable connected, and Vbus active + */ +bool usbd_is_vbus(usbd_device *dev); + +/** + * Controls power state of usb-core and PHY + * @param[in] dev USB Device + * @param[in] onoff - true turn on device in action, and power PHY + * false disconnects, disable PHY and stops usb-core + */ +void usbd_enable(usbd_device *dev, bool onoff); + +/** + * Checks power state of usb-core and PHY + * @param[in] dev USB Device + * @param[in] \return true - device in action, and power PHY + * false - disabled PHY and stops usb-core + */ +bool usbd_is_enabled(usbd_device *dev); + /** * Perform a transfer * diff --git a/lib/usbd/backend/dwc_otg_private.h b/lib/usbd/backend/dwc_otg_private.h index e1a1f247..2b181233 100644 --- a/lib/usbd/backend/dwc_otg_private.h +++ b/lib/usbd/backend/dwc_otg_private.h @@ -88,6 +88,7 @@ struct dwc_otg_private_data { #define USBD_DEVICE_EXTRA \ struct dwc_otg_private_data private_data; +//#include "../usbd_private.h" void dwc_otg_init(usbd_device *dev); void dwc_otg_set_address(usbd_device *dev, uint8_t addr); diff --git a/lib/usbd/backend/usbd_dwc_otg.c b/lib/usbd/backend/usbd_dwc_otg.c index ebb42f89..c2b21a3a 100644 --- a/lib/usbd/backend/usbd_dwc_otg.c +++ b/lib/usbd/backend/usbd_dwc_otg.c @@ -979,6 +979,8 @@ static inline void alloc_fifo_for_ep0_only(usbd_device *dev) void dwc_otg_poll(usbd_device *dev) { + if (usbd_is_enabled(dev)){ + if (REBASE(DWC_OTG_GINTSTS) & DWC_OTG_GINTSTS_ENUMDNE) { REBASE(DWC_OTG_DCFG) &= ~DWC_OTG_DCFG_DAD_MASK; disable_all_non_ep0(dev); @@ -1033,14 +1035,31 @@ void dwc_otg_poll(usbd_device *dev) usbd_handle_suspend(dev); } + if (REBASE(DWC_OTG_GINTSTS) & DWC_OTG_GINTSTS_SOF) { + REBASE(DWC_OTG_GINTSTS) = DWC_OTG_GINTSTS_SOF; + usbd_handle_sof(dev); + } + + } //if (usbd_is_enabled(dev)) + else + REBASE(DWC_OTG_GINTSTS) = DWC_OTG_GINTSTS_ENUMDNE | DWC_OTG_GINTSTS_USBRST + | DWC_OTG_GINTSTS_RXFLVL + | DWC_OTG_GINTSTS_IEPINT | DWC_OTG_GINTSTS_OEPINT + | DWC_OTG_GINTSTS_SOF + | DWC_OTG_GINTSTS_USBSUSP | DWC_OTG_GINTSTS_ESUSP; + if (REBASE(DWC_OTG_GINTSTS) & DWC_OTG_GINTSTS_WKUPINT) { REBASE(DWC_OTG_GINTSTS) = DWC_OTG_GINTSTS_WKUPINT; usbd_handle_resume(dev); } - if (REBASE(DWC_OTG_GINTSTS) & DWC_OTG_GINTSTS_SOF) { - REBASE(DWC_OTG_GINTSTS) = DWC_OTG_GINTSTS_SOF; - usbd_handle_sof(dev); + if ( (REBASE(DWC_OTG_GINTSTS) & (DWC_OTG_GINTSTS_SRQINT)) != 0) { + REBASE(DWC_OTG_GINTSTS) = (DWC_OTG_GINTSTS_SRQINT); + usbd_handle_session(dev, true); + } + if ( (REBASE(DWC_OTG_GOTGINT) & (DWC_OTG_GOTGINT_SEDET)) != 0) { + REBASE(DWC_OTG_GOTGINT) = DWC_OTG_GOTGINT_SEDET; + usbd_handle_session(dev, false); } } diff --git a/lib/usbd/backend/usbd_stm32_otg_fs.c b/lib/usbd/backend/usbd_stm32_otg_fs.c index 24eb754f..06e2af8a 100644 --- a/lib/usbd/backend/usbd_stm32_otg_fs.c +++ b/lib/usbd/backend/usbd_stm32_otg_fs.c @@ -28,6 +28,8 @@ #include static usbd_device *init(const usbd_backend_config *config); +static bool otgfs_is_powered(usbd_device *dev); +static usbd_power_status otgfs_power_control(usbd_device *dev, usbd_power_action action); static struct usbd_device _usbd_dev; @@ -51,6 +53,8 @@ const struct usbd_backend usbd_stm32_otg_fs = { .get_speed = dwc_otg_get_speed, .set_address_before_status = true, .base_address = USB_OTG_FS_BASE, + .is_vbus = otgfs_is_powered + , .power_control = otgfs_power_control }; #define REBASE(REG, ...) REG(usbd_stm32_otg_fs.base_address, ##__VA_ARGS__) @@ -74,16 +78,20 @@ static usbd_device *init(const usbd_backend_config *config) _usbd_dev.backend = &usbd_stm32_otg_fs; _usbd_dev.config = config; + //* stm32f4 use FS CID=0x1100, HS CID=0x02000600 + //* stm32f7 0x3000, CID=0x00003100 + const unsigned otg_hs_cid_boundary = 0x3100; + if (config->feature & USBD_VBUS_SENSE) { - if (OTG_FS_CID >= 0x00002000) { /* 2.0 */ + if (OTG_FS_CID >= otg_hs_cid_boundary) { /* 2.0 HS core*/ /* Enable VBUS detection */ OTG_FS_GCCFG |= OTG_GCCFG_VBDEN; - } else { /* 1.x */ + } else { /* 1.x FS core*/ /* Enable VBUS sensing in device mode */ OTG_FS_GCCFG |= OTG_GCCFG_VBUSBSEN; } } else { - if (OTG_FS_CID >= 0x00002000) { /* 2.0 */ + if (OTG_FS_CID >= otg_hs_cid_boundary) { /* 2.0 */ /* Disable VBUS detection. */ OTG_FS_GCCFG &= ~OTG_GCCFG_VBDEN; REBASE(DWC_OTG_GOTGCTL) |= DWC_OTG_GOTGCTL_BVALOEN | @@ -108,3 +116,50 @@ static usbd_device *init(const usbd_backend_config *config) return &_usbd_dev; } + +static +bool otgfs_is_powered(usbd_device *dev){ + uint32_t base = dev->backend->base_address; + return (DWC_OTG_GOTGCTL(base) & DWC_OTG_GOTGCTL_BSVLD) != 0; +} + +static +usbd_power_status otgfs_power_control(usbd_device *dev, usbd_power_action action){ + uint32_t base = dev->backend->base_address; + switch (action){ + case usbd_paActivate:{ + DWC_OTG_PCGCCTL(base) = 0; + OTG_FS_GCCFG |= OTG_GCCFG_PWRDWN; + REBASE(DWC_OTG_GINTMSK) |= DWC_OTG_GINTMSK_ENUMDNEM | DWC_OTG_GINTSTS_USBSUSP + | DWC_OTG_GINTMSK_RXFLVLM | DWC_OTG_GINTMSK_IEPINT ; + break; + } + case usbd_paShutdown: { + /* Wait for AHB idle. */ + uint32_t save_dis = REBASE(DWC_OTG_DCTL) & DWC_OTG_DCTL_SDIS; + //disable device connection before poweroff + REBASE(DWC_OTG_DCTL) |= DWC_OTG_DCTL_SDIS; + while (( (DWC_OTG_GRSTCTL(base) & DWC_OTG_GRSTCTL_AHBIDL) == 0) ); + //* drop ISRs, cause stoped Core cant handle them + REBASE(DWC_OTG_GINTSTS) = ~( DWC_OTG_GINTSTS_USBSUSP + | DWC_OTG_GINTSTS_WKUPINT + | DWC_OTG_GINTSTS_SRQINT + ); + REBASE(DWC_OTG_GINTMSK) &= ~( DWC_OTG_GINTMSK_ENUMDNEM + | DWC_OTG_GINTMSK_RXFLVLM | DWC_OTG_GINTMSK_IEPINT + ); + //poser down PHY + OTG_FS_GCCFG &= ~OTG_GCCFG_PWRDWN; + DWC_OTG_PCGCCTL(base) = DWC_OTG_PCGCCTL_STPPCLK; //| DWC_OTG_PCGCCTL_GATEHCLK ; + //destore connection disable state after PHY poweroff + REBASE(DWC_OTG_DCTL) |= save_dis; + break; + } + } + usbd_power_status res = 0; + if ( (DWC_OTG_PCGCCTL(base) & DWC_OTG_PCGCCTL_PHYSUSP) == 0) + res |= usbd_psPHYOn; + if ((OTG_FS_GCCFG & OTG_GCCFG_PWRDWN) != 0) + res |= usbd_psCoreEnabled; + return res; +} diff --git a/lib/usbd/usbd.c b/lib/usbd/usbd.c index cd382b91..c69e19f0 100644 --- a/lib/usbd/usbd.c +++ b/lib/usbd/usbd.c @@ -128,6 +128,14 @@ void usbd_register_sof_callback(usbd_device *dev, } } +void usbd_register_session_callback(usbd_device *dev, + usbd_session_callback callback) +{ + dev->callback.session = callback; +} + + + /* Functions to be provided by the hardware abstraction layer */ void usbd_poll(usbd_device *dev, uint32_t us) { @@ -145,9 +153,21 @@ void usbd_poll(usbd_device *dev, uint32_t us) void usbd_disconnect(usbd_device *dev, bool disconnect) { + if (!disconnect) { + // power-up on connect + if ( ((dev->config->feature & USBD_USE_POWERDOWN) != 0) + && (dev->backend->power_control) ) + dev->backend->power_control(dev, usbd_paActivate); + } if (dev->backend->disconnect) { dev->backend->disconnect(dev, disconnect); } + if (disconnect) { + // power-down after disconnect + if ( ((dev->config->feature & USBD_USE_POWERDOWN) != 0) + && (dev->backend->power_control) ) + dev->backend->power_control(dev, usbd_paShutdown ); + } } void usbd_ep_prepare(usbd_device *dev, uint8_t addr, usbd_ep_type type, @@ -208,5 +228,28 @@ usbd_speed usbd_get_speed(usbd_device *dev) return dev->backend->get_speed(dev); } +bool usbd_is_vbus(usbd_device *dev){ + if (dev->backend->is_vbus) + return dev->backend->is_vbus(dev); + else + return true; +} + +void usbd_enable(usbd_device *dev, bool onoff){ + if (dev->backend->power_control) + dev->backend->power_control(dev, (onoff)? usbd_paActivate : usbd_paShutdown ); + else + // try emulate enable via disconnect + usbd_disconnect(dev, !onoff); +} + +bool usbd_is_enabled(usbd_device *dev){ + if (dev->backend->power_control) + return dev->backend->power_control(dev, usbd_paCheck ) == usbd_psOn; + else + return true; +} + + /**@}*/ diff --git a/lib/usbd/usbd_private.h b/lib/usbd/usbd_private.h index 6b7ddcfd..d0f99163 100644 --- a/lib/usbd/usbd_private.h +++ b/lib/usbd/usbd_private.h @@ -41,6 +41,7 @@ #ifndef UNICOREMX_USBD_PRIVATE_H #define UNICOREMX_USBD_PRIVATE_H +#include #include /** @@ -109,6 +110,10 @@ struct usbd_device { /** invoked when sof received */ usbd_generic_callback sof; + + //* invoked on Session New / End + //* with USBD_VBUS_SENSE feature detects cable plug + usbd_session_callback session; /** invoked on SETUP packet */ usbd_setup_callback setup; @@ -174,6 +179,22 @@ struct usbd_device { #endif }; +enum _usbd_power_action{ + usbd_paCheck + , usbd_paShutdown + , usbd_paActivate +}; +enum _usbd_power_status{ + usbd_psCoreDisabled = 0 + , usbd_psCoreEnabled = 1 + , usbd_psPHYOff = 0 + , usbd_psPHYOn = 2 + , usbd_psOff = 0 + , usbd_psOn = usbd_psCoreEnabled | usbd_psPHYOn // ~0 +}; +typedef enum _usbd_power_action usbd_power_action; +typedef unsigned usbd_power_status; + /* Functions provided by the hardware abstraction. */ struct usbd_backend { usbd_device * (*init)(const usbd_backend_config *config); @@ -197,6 +218,10 @@ struct usbd_backend { /* Frame number */ uint16_t (*frame_number)(usbd_device *dev); + //* power control + bool (*is_vbus)(usbd_device *dev); + usbd_power_status (*power_control)(usbd_device *dev, usbd_power_action action); + /* * this is to tell usb generic code * that address need to be set before status-stage. @@ -359,6 +384,14 @@ static inline void usbd_handle_sof(usbd_device *dev) } } +static inline +void usbd_handle_session(usbd_device *dev, bool onoff) +{ + if (dev->callback.session != NULL) { + dev->callback.session(dev, onoff); + } +} + /** * Called by backend to pass the setup data when a SETUP packet is recevied * on control endpoint.