From de9f4d9f940fb957802f9d50652324ef46675a2c Mon Sep 17 00:00:00 2001 From: P33M Date: Thu, 25 May 2017 16:04:53 +0100 Subject: [PATCH] dwc_otg: make periodic scheduling behave properly for FS buses If the root port is in full-speed mode, transfer times at 12mbit/s would be calculated but matched against high-speed quotas. Reinitialise hcd->frame_usecs[i] on each port enable event so that full-speed bandwidth can be tracked sensibly. Also, don't bother using the FIQ for transfers when in full-speed mode - at the slower bus speed, interrupt frequency is reduced by an order of magnitude. Related issue: https://github.com/raspberrypi/linux/issues/2020 --- drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 15 ++++++++++----- drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 7 +++++-- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 4 ++++ drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 19 ++++++++++++------- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c index a2dc6337836b..38bf5fc792d3 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c @@ -926,8 +926,6 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd) DWC_FREE(dwc_otg_hcd); } -int init_hcd_usecs(dwc_otg_hcd_t *_hcd); - int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) { struct device *dev = dwc_otg_hcd_to_dev(hcd); @@ -1429,6 +1427,7 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) /** * fiq_fsm_transaction_suitable() - Test a QH for compatibility with the FIQ + * @hcd: Pointer to the dwc_otg_hcd struct * @qh: pointer to the endpoint's queue head * * Transaction start/end control flow is grafted onto the existing dwc_otg @@ -1438,8 +1437,14 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) * Returns: 0 for unsuitable, 1 implies the FIQ can be enabled for this transaction. */ -int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh) +int fiq_fsm_transaction_suitable(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) { + /* There is little benefit in using the FIQ to perform transfers if + * the root port is not in high-speed mode. + */ + if (hcd->flags.b.port_speed != DWC_HPRT0_PRTSPD_HIGH_SPEED) + return 0; + if (qh->do_split) { switch (qh->ep_type) { case UE_CONTROL: @@ -2218,7 +2223,7 @@ static void process_periodic_channels(dwc_otg_hcd_t * hcd) continue; } - if (fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { + if (fiq_fsm_enable && fiq_fsm_transaction_suitable(hcd, qh)) { if (qh->do_split) fiq_fsm_queue_split_transaction(hcd, qh); else @@ -2355,7 +2360,7 @@ static void process_non_periodic_channels(dwc_otg_hcd_t * hcd) qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, qh_list_entry); - if(fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { + if(fiq_fsm_enable && fiq_fsm_transaction_suitable(hcd, qh)) { fiq_fsm_queue_split_transaction(hcd, qh); } else { status = queue_transaction(hcd, qh->channel, diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h index 7f7e9eaffd6a..5ed8dccf0395 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h @@ -410,7 +410,8 @@ struct dwc_otg_hcd { unsigned port_suspend_change:1; unsigned port_over_current_change:1; unsigned port_l1_change:1; - unsigned reserved:26; + unsigned port_speed:2; + unsigned reserved:24; } b; } flags; @@ -629,7 +630,7 @@ int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh); void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh); extern int fiq_fsm_queue_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); -extern int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh); +extern int fiq_fsm_transaction_suitable(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num); /** @} */ @@ -823,6 +824,8 @@ static inline uint16_t dwc_micro_frame_num(uint16_t frame) return frame & 0x7; } +extern void init_hcd_usecs(dwc_otg_hcd_t *_hcd); + void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc, dwc_otg_hc_regs_t * hc_regs, dwc_otg_qtd_t * qtd); diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c index a4355afc77b6..c8f52709a7d2 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c @@ -515,6 +515,10 @@ int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd) dwc_otg_host_if_t *host_if = dwc_otg_hcd->core_if->host_if; + dwc_otg_hcd->flags.b.port_speed = hprt0.b.prtspd; + if (microframe_schedule) + init_hcd_usecs(dwc_otg_hcd); + /* Every time when port enables calculate * HFIR.FrInterval */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c index 85a6d431ca54..4b1dd9de99e9 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c @@ -408,13 +408,17 @@ const unsigned short max_uframe_usecs[]={ 100, 100, 100, 100, 100, 100, 30, 0 }; /* * called from dwc_otg_hcd.c:dwc_otg_hcd_init */ -int init_hcd_usecs(dwc_otg_hcd_t *_hcd) +void init_hcd_usecs(dwc_otg_hcd_t *_hcd) { int i; - for (i=0; i<8; i++) { - _hcd->frame_usecs[i] = max_uframe_usecs[i]; + if (_hcd->flags.b.port_speed == DWC_HPRT0_PRTSPD_FULL_SPEED) { + _hcd->frame_usecs[0] = 900; + for (i = 1; i < 8; i++) + _hcd->frame_usecs[i] = 0; + } else { + for (i = 0; i < 8; i++) + _hcd->frame_usecs[i] = max_uframe_usecs[i]; } - return 0; } static int find_single_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) @@ -541,8 +545,9 @@ static int find_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) int ret; ret = -1; - if (_qh->speed == USB_SPEED_HIGH) { - /* if this is a hs transaction we need a full frame */ + if (_qh->speed == USB_SPEED_HIGH || + _hcd->flags.b.port_speed == DWC_HPRT0_PRTSPD_FULL_SPEED) { + /* if this is a hs transaction we need a full frame - or account for FS usecs */ ret = find_single_uframe(_hcd, _qh); } else { /* if this is a fs transaction we may need a sequence of frames */ @@ -627,7 +632,7 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) if (status) { DWC_INFO("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__); - return status; + return -DWC_E_NO_SPACE; } status = check_max_xfer_size(hcd, qh); if (status) { -- 2.30.2