aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-mtk-sch.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/xhci-mtk-sch.c')
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c149
1 files changed, 85 insertions, 64 deletions
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index 8950d1f10a7f..86c4bc9df3b8 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -25,6 +25,13 @@
*/
#define TT_MICROFRAMES_MAX 9
+/* schedule error type */
+#define ESCH_SS_Y6 1001
+#define ESCH_SS_OVERLAP 1002
+#define ESCH_CS_OVERFLOW 1003
+#define ESCH_BW_OVERFLOW 1004
+#define ESCH_FIXME 1005
+
/* mtk scheduler bitmasks */
#define EP_BPKTS(p) ((p) & 0x7f)
#define EP_BCSCOUNT(p) (((p) & 0x7) << 8)
@@ -32,6 +39,24 @@
#define EP_BOFFSET(p) ((p) & 0x3fff)
#define EP_BREPEAT(p) (((p) & 0x7fff) << 16)
+static char *sch_error_string(int err_num)
+{
+ switch (err_num) {
+ case ESCH_SS_Y6:
+ return "Can't schedule Start-Split in Y6";
+ case ESCH_SS_OVERLAP:
+ return "Can't find a suitable Start-Split location";
+ case ESCH_CS_OVERFLOW:
+ return "The last Complete-Split is greater than 7";
+ case ESCH_BW_OVERFLOW:
+ return "Bandwidth exceeds the maximum limit";
+ case ESCH_FIXME:
+ return "FIXME, to be resolved";
+ default:
+ return "Unknown";
+ }
+}
+
static int is_fs_or_ls(enum usb_device_speed speed)
{
return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW;
@@ -375,7 +400,6 @@ static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
sch_ep->bw_budget_table[j];
}
}
- sch_ep->allocated = used;
}
static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset)
@@ -384,19 +408,20 @@ static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset)
u32 num_esit, tmp;
int base;
int i, j;
+ u8 uframes = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX);
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
+
+ if (sch_ep->ep_type == INT_IN_EP || sch_ep->ep_type == ISOC_IN_EP)
+ offset++;
+
for (i = 0; i < num_esit; i++) {
base = offset + i * sch_ep->esit;
- /*
- * Compared with hs bus, no matter what ep type,
- * the hub will always delay one uframe to send data
- */
- for (j = 0; j < sch_ep->cs_count; j++) {
+ for (j = 0; j < uframes; j++) {
tmp = tt->fs_bus_bw[base + j] + sch_ep->bw_cost_per_microframe;
if (tmp > FS_PAYLOAD_MAX)
- return -ERANGE;
+ return -ESCH_BW_OVERFLOW;
}
}
@@ -406,15 +431,11 @@ static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset)
static int check_sch_tt(struct usb_device *udev,
struct mu3h_sch_ep_info *sch_ep, u32 offset)
{
- struct mu3h_sch_tt *tt = sch_ep->sch_tt;
u32 extra_cs_count;
- u32 fs_budget_start;
u32 start_ss, last_ss;
u32 start_cs, last_cs;
- int i;
start_ss = offset % 8;
- fs_budget_start = (start_ss + 1) % 8;
if (sch_ep->ep_type == ISOC_OUT_EP) {
last_ss = start_ss + sch_ep->cs_count - 1;
@@ -424,11 +445,7 @@ static int check_sch_tt(struct usb_device *udev,
* must never schedule Start-Split in Y6
*/
if (!(start_ss == 7 || last_ss < 6))
- return -ERANGE;
-
- for (i = 0; i < sch_ep->cs_count; i++)
- if (test_bit(offset + i, tt->ss_bit_map))
- return -ERANGE;
+ return -ESCH_SS_Y6;
} else {
u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX);
@@ -438,29 +455,24 @@ static int check_sch_tt(struct usb_device *udev,
* must never schedule Start-Split in Y6
*/
if (start_ss == 6)
- return -ERANGE;
+ return -ESCH_SS_Y6;
/* one uframe for ss + one uframe for idle */
start_cs = (start_ss + 2) % 8;
last_cs = start_cs + cs_count - 1;
if (last_cs > 7)
- return -ERANGE;
+ return -ESCH_CS_OVERFLOW;
if (sch_ep->ep_type == ISOC_IN_EP)
extra_cs_count = (last_cs == 7) ? 1 : 2;
else /* ep_type : INTR IN / INTR OUT */
- extra_cs_count = (fs_budget_start == 6) ? 1 : 2;
+ extra_cs_count = 1;
cs_count += extra_cs_count;
if (cs_count > 7)
cs_count = 7; /* HW limit */
- for (i = 0; i < cs_count + 2; i++) {
- if (test_bit(offset + i, tt->ss_bit_map))
- return -ERANGE;
- }
-
sch_ep->cs_count = cs_count;
/* one for ss, the other for idle */
sch_ep->num_budget_microframes = cs_count + 2;
@@ -482,28 +494,24 @@ static void update_sch_tt(struct usb_device *udev,
struct mu3h_sch_tt *tt = sch_ep->sch_tt;
u32 base, num_esit;
int bw_updated;
- int bits;
int i, j;
+ int offset = sch_ep->offset;
+ u8 uframes = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX);
num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
- bits = (sch_ep->ep_type == ISOC_OUT_EP) ? sch_ep->cs_count : 1;
if (used)
bw_updated = sch_ep->bw_cost_per_microframe;
else
bw_updated = -sch_ep->bw_cost_per_microframe;
- for (i = 0; i < num_esit; i++) {
- base = sch_ep->offset + i * sch_ep->esit;
+ if (sch_ep->ep_type == INT_IN_EP || sch_ep->ep_type == ISOC_IN_EP)
+ offset++;
- for (j = 0; j < bits; j++) {
- if (used)
- set_bit(base + j, tt->ss_bit_map);
- else
- clear_bit(base + j, tt->ss_bit_map);
- }
+ for (i = 0; i < num_esit; i++) {
+ base = offset + i * sch_ep->esit;
- for (j = 0; j < sch_ep->cs_count; j++)
+ for (j = 0; j < uframes; j++)
tt->fs_bus_bw[base + j] += bw_updated;
}
@@ -513,21 +521,48 @@ static void update_sch_tt(struct usb_device *udev,
list_del(&sch_ep->tt_endpoint);
}
+static int load_ep_bw(struct usb_device *udev, struct mu3h_sch_bw_info *sch_bw,
+ struct mu3h_sch_ep_info *sch_ep, bool loaded)
+{
+ if (sch_ep->sch_tt)
+ update_sch_tt(udev, sch_ep, loaded);
+
+ /* update bus bandwidth info */
+ update_bus_bw(sch_bw, sch_ep, loaded);
+ sch_ep->allocated = loaded;
+
+ return 0;
+}
+
+static u32 get_esit_boundary(struct mu3h_sch_ep_info *sch_ep)
+{
+ u32 boundary = sch_ep->esit;
+
+ if (sch_ep->sch_tt) { /* LS/FS with TT */
+ /*
+ * tune for CS, normally esit >= 8 for FS/LS,
+ * not add one for other types to avoid access array
+ * out of boundary
+ */
+ if (sch_ep->ep_type == ISOC_OUT_EP && boundary > 1)
+ boundary--;
+ }
+
+ return boundary;
+}
+
static int check_sch_bw(struct usb_device *udev,
struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
{
u32 offset;
- u32 esit;
u32 min_bw;
u32 min_index;
u32 worst_bw;
u32 bw_boundary;
+ u32 esit_boundary;
u32 min_num_budget;
u32 min_cs_count;
- bool tt_offset_ok = false;
- int ret;
-
- esit = sch_ep->esit;
+ int ret = 0;
/*
* Search through all possible schedule microframes.
@@ -537,16 +572,15 @@ static int check_sch_bw(struct usb_device *udev,
min_index = 0;
min_cs_count = sch_ep->cs_count;
min_num_budget = sch_ep->num_budget_microframes;
- for (offset = 0; offset < esit; offset++) {
- if (is_fs_or_ls(udev->speed)) {
+ esit_boundary = get_esit_boundary(sch_ep);
+ for (offset = 0; offset < sch_ep->esit; offset++) {
+ if (sch_ep->sch_tt) {
ret = check_sch_tt(udev, sch_ep, offset);
if (ret)
continue;
- else
- tt_offset_ok = true;
}
- if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit)
+ if ((offset + sch_ep->num_budget_microframes) > esit_boundary)
break;
worst_bw = get_max_bw(sch_bw, sch_ep, offset);
@@ -569,35 +603,21 @@ static int check_sch_bw(struct usb_device *udev,
/* check bandwidth */
if (min_bw > bw_boundary)
- return -ERANGE;
+ return ret ? ret : -ESCH_BW_OVERFLOW;
sch_ep->offset = min_index;
sch_ep->cs_count = min_cs_count;
sch_ep->num_budget_microframes = min_num_budget;
- if (is_fs_or_ls(udev->speed)) {
- /* all offset for tt is not ok*/
- if (!tt_offset_ok)
- return -ERANGE;
-
- update_sch_tt(udev, sch_ep, 1);
- }
-
- /* update bus bandwidth info */
- update_bus_bw(sch_bw, sch_ep, 1);
-
- return 0;
+ return load_ep_bw(udev, sch_bw, sch_ep, true);
}
static void destroy_sch_ep(struct usb_device *udev,
struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
{
/* only release ep bw check passed by check_sch_bw() */
- if (sch_ep->allocated) {
- update_bus_bw(sch_bw, sch_ep, 0);
- if (sch_ep->sch_tt)
- update_sch_tt(udev, sch_ep, 0);
- }
+ if (sch_ep->allocated)
+ load_ep_bw(udev, sch_bw, sch_ep, false);
if (sch_ep->sch_tt)
drop_tt(udev);
@@ -760,7 +780,8 @@ int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
ret = check_sch_bw(udev, sch_bw, sch_ep);
if (ret) {
- xhci_err(xhci, "Not enough bandwidth!\n");
+ xhci_err(xhci, "Not enough bandwidth! (%s)\n",
+ sch_error_string(-ret));
return -ENOSPC;
}
}