Commit 0cbd4b34 authored by Chunfeng Yun's avatar Chunfeng Yun Committed by Greg Kroah-Hartman

xhci: mediatek: support MTK xHCI host controller

There some vendor quirks for MTK xhci host controller:
1. It defines some extra SW scheduling parameters for HW
  to minimize the scheduling effort for synchronous and
  interrupt endpoints. The parameters are put into reseved
  DWs of slot context and endpoint context.
2. Its IMODI unit for Interrupter Moderation register is
  8 times as much as that defined in xHCI spec.
3. Its TDS in  Normal TRB defines a number of packets that
  remains to be transferred for a TD after processing all
  Max packets in all previous TRBs.
Signed-off-by: default avatarChunfeng Yun <chunfeng.yun@mediatek.com>
Tested-by: default avatarDaniel Thompson <daniel.thompson@linaro.org>
Reviewed-by: default avatarDaniel Thompson <daniel.thompson@linaro.org>
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c74732e3
...@@ -41,6 +41,15 @@ config USB_XHCI_PLATFORM ...@@ -41,6 +41,15 @@ config USB_XHCI_PLATFORM
If unsure, say N. If unsure, say N.
config USB_XHCI_MTK
tristate "xHCI support for Mediatek MT65xx"
select MFD_SYSCON
depends on ARCH_MEDIATEK || COMPILE_TEST
---help---
Say 'Y' to enable the support for the xHCI host controller
found in Mediatek MT65xx SoCs.
If unsure, say N.
config USB_XHCI_MVEBU config USB_XHCI_MVEBU
tristate "xHCI support for Marvell Armada 375/38x" tristate "xHCI support for Marvell Armada 375/38x"
select USB_XHCI_PLATFORM select USB_XHCI_PLATFORM
......
...@@ -13,6 +13,9 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o ...@@ -13,6 +13,9 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o
xhci-hcd-y := xhci.o xhci-mem.o xhci-hcd-y := xhci.o xhci-mem.o
xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
xhci-hcd-y += xhci-trace.o xhci-hcd-y += xhci-trace.o
ifneq ($(CONFIG_USB_XHCI_MTK), )
xhci-hcd-y += xhci-mtk-sch.o
endif
xhci-plat-hcd-y := xhci-plat.o xhci-plat-hcd-y := xhci-plat.o
ifneq ($(CONFIG_USB_XHCI_MVEBU), ) ifneq ($(CONFIG_USB_XHCI_MVEBU), )
...@@ -64,6 +67,7 @@ obj-$(CONFIG_USB_FHCI_HCD) += fhci.o ...@@ -64,6 +67,7 @@ obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
......
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2015 MediaTek Inc.
* Author:
* Zhigang.Wei <zhigang.wei@mediatek.com>
* Chunfeng.Yun <chunfeng.yun@mediatek.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _XHCI_MTK_H_
#define _XHCI_MTK_H_
#include "xhci.h"
/**
* To simplify scheduler algorithm, set a upper limit for ESIT,
* if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT,
* round down to the limit value, that means allocating more
* bandwidth to it.
*/
#define XHCI_MTK_MAX_ESIT 64
/**
* struct mu3h_sch_bw_info: schedule information for bandwidth domain
*
* @bus_bw: array to keep track of bandwidth already used at each uframes
* @bw_ep_list: eps in the bandwidth domain
*
* treat a HS root port as a bandwidth domain, but treat a SS root port as
* two bandwidth domains, one for IN eps and another for OUT eps.
*/
struct mu3h_sch_bw_info {
u32 bus_bw[XHCI_MTK_MAX_ESIT];
struct list_head bw_ep_list;
};
/**
* struct mu3h_sch_ep_info: schedule information for endpoint
*
* @esit: unit is 125us, equal to 2 << Interval field in ep-context
* @num_budget_microframes: number of continuous uframes
* (@repeat==1) scheduled within the interval
* @bw_cost_per_microframe: bandwidth cost per microframe
* @endpoint: linked into bandwidth domain which it belongs to
* @ep: address of usb_host_endpoint struct
* @offset: which uframe of the interval that transfer should be
* scheduled first time within the interval
* @repeat: the time gap between two uframes that transfers are
* scheduled within a interval. in the simple algorithm, only
* assign 0 or 1 to it; 0 means using only one uframe in a
* interval, and 1 means using @num_budget_microframes
* continuous uframes
* @pkts: number of packets to be transferred in the scheduled uframes
* @cs_count: number of CS that host will trigger
* @burst_mode: burst mode for scheduling. 0: normal burst mode,
* distribute the bMaxBurst+1 packets for a single burst
* according to @pkts and @repeat, repeate the burst multiple
* times; 1: distribute the (bMaxBurst+1)*(Mult+1) packets
* according to @pkts and @repeat. normal mode is used by
* default
*/
struct mu3h_sch_ep_info {
u32 esit;
u32 num_budget_microframes;
u32 bw_cost_per_microframe;
struct list_head endpoint;
void *ep;
/*
* mtk xHCI scheduling information put into reserved DWs
* in ep context
*/
u32 offset;
u32 repeat;
u32 pkts;
u32 cs_count;
u32 burst_mode;
};
#define MU3C_U3_PORT_MAX 4
#define MU3C_U2_PORT_MAX 5
/**
* struct mu3c_ippc_regs: MTK ssusb ip port control registers
* @ip_pw_ctr0~3: ip power and clock control registers
* @ip_pw_sts1~2: ip power and clock status registers
* @ip_xhci_cap: ip xHCI capability register
* @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
* @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
* @u2_phy_pll: usb2 phy pll control register
*/
struct mu3c_ippc_regs {
__le32 ip_pw_ctr0;
__le32 ip_pw_ctr1;
__le32 ip_pw_ctr2;
__le32 ip_pw_ctr3;
__le32 ip_pw_sts1;
__le32 ip_pw_sts2;
__le32 reserved0[3];
__le32 ip_xhci_cap;
__le32 reserved1[2];
__le64 u3_ctrl_p[MU3C_U3_PORT_MAX];
__le64 u2_ctrl_p[MU3C_U2_PORT_MAX];
__le32 reserved2;
__le32 u2_phy_pll;
__le32 reserved3[33]; /* 0x80 ~ 0xff */
};
struct xhci_hcd_mtk {
struct device *dev;
struct usb_hcd *hcd;
struct mu3h_sch_bw_info *sch_array;
struct mu3c_ippc_regs __iomem *ippc_regs;
int num_u2_ports;
int num_u3_ports;
struct regulator *vusb33;
struct regulator *vbus;
struct clk *sys_clk; /* sys and mac clock */
struct clk *wk_deb_p0; /* port0's wakeup debounce clock */
struct clk *wk_deb_p1;
struct regmap *pericfg;
struct phy **phys;
int num_phys;
int wakeup_src;
bool lpm_support;
};
static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd)
{
return dev_get_drvdata(hcd->self.controller);
}
#if IS_ENABLED(CONFIG_USB_XHCI_MTK)
int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk);
void xhci_mtk_sch_exit(struct xhci_hcd_mtk *mtk);
int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep);
void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep);
#else
static inline int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd,
struct usb_device *udev, struct usb_host_endpoint *ep)
{
return 0;
}
static inline void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd,
struct usb_device *udev, struct usb_host_endpoint *ep)
{
}
#endif
#endif /* _XHCI_MTK_H_ */
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include "xhci.h" #include "xhci.h"
#include "xhci-trace.h" #include "xhci-trace.h"
#include "xhci-mtk.h"
/* /*
* Returns zero if the TRB isn't in this segment, otherwise it returns the DMA * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
...@@ -3074,17 +3075,22 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred, ...@@ -3074,17 +3075,22 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
{ {
u32 maxp, total_packet_count; u32 maxp, total_packet_count;
if (xhci->hci_version < 0x100) /* MTK xHCI is mostly 0.97 but contains some features from 1.0 */
if (xhci->hci_version < 0x100 && !(xhci->quirks & XHCI_MTK_HOST))
return ((td_total_len - transferred) >> 10); return ((td_total_len - transferred) >> 10);
maxp = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
total_packet_count = DIV_ROUND_UP(td_total_len, maxp);
/* One TRB with a zero-length data packet. */ /* One TRB with a zero-length data packet. */
if (num_trbs_left == 0 || (transferred == 0 && trb_buff_len == 0) || if (num_trbs_left == 0 || (transferred == 0 && trb_buff_len == 0) ||
trb_buff_len == td_total_len) trb_buff_len == td_total_len)
return 0; return 0;
/* for MTK xHCI, TD size doesn't include this TRB */
if (xhci->quirks & XHCI_MTK_HOST)
trb_buff_len = 0;
maxp = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
total_packet_count = DIV_ROUND_UP(td_total_len, maxp);
/* Queueing functions don't count the current TRB into transferred */ /* Queueing functions don't count the current TRB into transferred */
return (total_packet_count - ((transferred + trb_buff_len) / maxp)); return (total_packet_count - ((transferred + trb_buff_len) / maxp));
} }
...@@ -3472,7 +3478,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ...@@ -3472,7 +3478,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
field |= 0x1; field |= 0x1;
/* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */ /* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */
if (xhci->hci_version >= 0x100) { if ((xhci->hci_version >= 0x100) || (xhci->quirks & XHCI_MTK_HOST)) {
if (urb->transfer_buffer_length > 0) { if (urb->transfer_buffer_length > 0) {
if (setup->bRequestType & USB_DIR_IN) if (setup->bRequestType & USB_DIR_IN)
field |= TRB_TX_TYPE(TRB_DATA_IN); field |= TRB_TX_TYPE(TRB_DATA_IN);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "xhci.h" #include "xhci.h"
#include "xhci-trace.h" #include "xhci-trace.h"
#include "xhci-mtk.h"
#define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_AUTHOR "Sarah Sharp"
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
...@@ -634,7 +635,11 @@ int xhci_run(struct usb_hcd *hcd) ...@@ -634,7 +635,11 @@ int xhci_run(struct usb_hcd *hcd)
"// Set the interrupt modulation register"); "// Set the interrupt modulation register");
temp = readl(&xhci->ir_set->irq_control); temp = readl(&xhci->ir_set->irq_control);
temp &= ~ER_IRQ_INTERVAL_MASK; temp &= ~ER_IRQ_INTERVAL_MASK;
temp |= (u32) 160; /*
* the increment interval is 8 times as much as that defined
* in xHCI spec on MTK's controller
*/
temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
writel(temp, &xhci->ir_set->irq_control); writel(temp, &xhci->ir_set->irq_control);
/* Set the HCD state before we enable the irqs */ /* Set the HCD state before we enable the irqs */
...@@ -1698,6 +1703,9 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, ...@@ -1698,6 +1703,9 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
if (xhci->quirks & XHCI_MTK_HOST)
xhci_mtk_drop_ep_quirk(hcd, udev, ep);
xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
(unsigned int) ep->desc.bEndpointAddress, (unsigned int) ep->desc.bEndpointAddress,
udev->slot_id, udev->slot_id,
...@@ -1793,6 +1801,15 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, ...@@ -1793,6 +1801,15 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
return -ENOMEM; return -ENOMEM;
} }
if (xhci->quirks & XHCI_MTK_HOST) {
ret = xhci_mtk_add_ep_quirk(hcd, udev, ep);
if (ret < 0) {
xhci_free_or_cache_endpoint_ring(xhci,
virt_dev, ep_index);
return ret;
}
}
ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs); ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
......
...@@ -1630,6 +1630,7 @@ struct xhci_hcd { ...@@ -1630,6 +1630,7 @@ struct xhci_hcd {
/* For controllers with a broken beyond repair streams implementation */ /* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19) #define XHCI_BROKEN_STREAMS (1 << 19)
#define XHCI_PME_STUCK_QUIRK (1 << 20) #define XHCI_PME_STUCK_QUIRK (1 << 20)
#define XHCI_MTK_HOST (1 << 21)
unsigned int num_active_eps; unsigned int num_active_eps;
unsigned int limit_active_eps; unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */ /* There are two roothubs to keep track of bus suspend info for */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment