#include "xhci.h"
+#ifdef CONFIG_MTK_XHCI
+#include <asm/uaccess.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/xhci/xhci-mtk-scheduler.h>
+#include <linux/xhci/xhci-mtk-power.h>
+#include <linux/xhci/xhci-mtk.h>
+
+#ifdef CONFIG_USBIF_COMPLIANCE
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/kobject.h>
+#include <linux/miscdevice.h>
+
+static struct miscdevice mu3h_uevent_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "usbif_u3h_uevent",
+ .fops = NULL,
+};
+#endif
+#endif
+
#define DRIVER_AUTHOR "Sarah Sharp"
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
module_param(link_quirk, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
+#ifdef CONFIG_USBIF_COMPLIANCE
+int usbif_u3h_send_event(char* event)
+{
+ char udev_event[128];
+ char *envp[] = {udev_event, NULL };
+ int ret ;
+
+ snprintf(udev_event, 128, "USBIF_EVENT=%s",event);
+ printk("usbif_u3h_send_event - sending event - %s in %s\n", udev_event, kobject_get_path(&mu3h_uevent_device.this_device->kobj, GFP_KERNEL));
+ ret = kobject_uevent_env(&mu3h_uevent_device.this_device->kobj, KOBJ_CHANGE, envp);
+ if (ret < 0)
+ printk("usbif_u3h_send_event sending failed with ret = %d, \n", ret);
+
+ return ret;
+}
+#endif
+
/* TODO: copied from ehci-hcd.c - can this be refactored? */
/*
* xhci_handshake - spin reading hc until handshake completes or fails
} else {
xhci_dbg(xhci, "xHCI doesn't need link TRB QUIRK\n");
}
+
retval = xhci_mem_init(xhci, GFP_KERNEL);
xhci_dbg(xhci, "Finished xhci_init\n");
xhci_halt(xhci);
return -ENODEV;
}
+
xhci->shared_hcd->state = HC_STATE_RUNNING;
xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
xhci_ring_cmd_db(xhci);
xhci_dbg(xhci, "Finished xhci_run for USB3 roothub\n");
+
return 0;
}
xhci_dbg(xhci, "%s: compliance mode recovery timer deleted\n",
__func__);
}
-
+#ifndef CONFIG_MTK_XHCI
if (xhci->quirks & XHCI_AMD_PLL_FIX)
usb_amd_dev_put();
-
+#endif
xhci_dbg(xhci, "// Disabling event ring interrupts\n");
temp = xhci_readl(xhci, &xhci->op_regs->status);
xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
u32 drop_flag;
u32 new_add_flags, new_drop_flags, new_slot_info;
int ret;
+#ifdef CONFIG_MTK_XHCI
+ struct sch_ep *sch_ep = NULL;
+ int isTT;
+ int ep_type = 0;
+#endif
ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
if (ret <= 0)
xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
+#ifdef CONFIG_MTK_XHCI
+ slot_ctx = xhci_get_slot_ctx(xhci, xhci->devs[udev->slot_id]->out_ctx);
+ if((slot_ctx->tt_info & 0xff) > 0){
+ isTT = 1;
+ }
+ else{
+ isTT = 0;
+ }
+ if(usb_endpoint_xfer_int(&ep->desc)){
+ ep_type = USB_EP_INT;
+ }
+ else if(usb_endpoint_xfer_isoc(&ep->desc)){
+ ep_type = USB_EP_ISOC;
+ }
+ else if(usb_endpoint_xfer_bulk(&ep->desc)){
+ ep_type = USB_EP_BULK;
+ }
+ sch_ep = mtk_xhci_scheduler_remove_ep(udev->speed, usb_endpoint_dir_in(&ep->desc)
+ , isTT, ep_type, (mtk_u32 *)ep);
+ if(sch_ep != NULL){
+ kfree(sch_ep);
+ }
+ else{
+ xhci_warn(xhci, "[MTK]Doesn't find ep_sch instance when removing endpoint\n");
+ }
+#endif
+
xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n",
(unsigned int) ep->desc.bEndpointAddress,
udev->slot_id,
(unsigned int) new_drop_flags,
(unsigned int) new_add_flags,
(unsigned int) new_slot_info);
+
+ #if defined(CONFIG_MTK_XHCI) && defined(CONFIG_USB_MTK_DUALMODE)
+ mtk_ep_count_dec();
+ #endif
+
return 0;
}
u32 new_add_flags, new_drop_flags, new_slot_info;
struct xhci_virt_device *virt_dev;
int ret = 0;
+#ifdef CONFIG_MTK_XHCI
+ struct xhci_ep_ctx *in_ep_ctx;
+ struct sch_ep *sch_ep;
+ int isTT;
+ int ep_type = 0;
+ int maxp = 0;
+ int burst = 0;
+ int mult = 0;
+ int interval = 0;
+#endif
ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
if (ret <= 0) {
return -ENOMEM;
}
+#ifdef CONFIG_MTK_XHCI
+ in_ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index);
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
+
+ if((slot_ctx->tt_info & 0xff) > 0){
+ isTT = 1;
+ }
+ else{
+ isTT = 0;
+ }
+ if(usb_endpoint_xfer_int(&ep->desc)){
+ ep_type = USB_EP_INT;
+ }
+ else if(usb_endpoint_xfer_isoc(&ep->desc)){
+ ep_type = USB_EP_ISOC;
+ }
+ else if(usb_endpoint_xfer_bulk(&ep->desc)){
+ ep_type = USB_EP_BULK;
+ }
+ if(udev->speed == USB_SPEED_FULL || udev->speed == USB_SPEED_HIGH
+ || udev->speed == USB_SPEED_LOW){
+ maxp = ep->desc.wMaxPacketSize & 0x7FF;
+ burst = ep->desc.wMaxPacketSize >> 11;
+ mult = 0;
+ }
+ else if(udev->speed == USB_SPEED_SUPER){
+ maxp = ep->desc.wMaxPacketSize & 0x7FF;
+ burst = ep->ss_ep_comp.bMaxBurst;
+ mult = ep->ss_ep_comp.bmAttributes & 0x3;
+ }
+ interval = (1 << ((in_ep_ctx->ep_info >> 16) & 0xff));
+ sch_ep = kmalloc(sizeof(struct sch_ep), GFP_KERNEL);
+ if(mtk_xhci_scheduler_add_ep(udev->speed, usb_endpoint_dir_in(&ep->desc),
+ isTT, ep_type, maxp, interval, burst, mult, (mtk_u32 *)ep
+ , (mtk_u32 *)in_ep_ctx, sch_ep) != SCH_SUCCESS){
+ xhci_err(xhci, "[MTK] not enough bandwidth\n");
+ return -ENOSPC;
+ }
+#endif
+
ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
(unsigned int) new_drop_flags,
(unsigned int) new_add_flags,
(unsigned int) new_slot_info);
+
+ #if defined(CONFIG_MTK_XHCI) && defined(CONFIG_USB_MTK_DUALMODE)
+ mtk_ep_count_inc();
+ #endif
+
return 0;
}
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_virt_device *virt_dev;
+#ifndef CONFIG_USB_DEFAULT_PERSIST
struct device *dev = hcd->self.controller;
+#endif
unsigned long flags;
u32 state;
int i, ret;
return 0;
}
+#ifdef CONFIG_MTK_XHCI
+ retval = mtk_xhci_ip_init(hcd, xhci);
+ if(retval)
+ goto error;
+#endif
+
xhci->cap_regs = hcd->regs;
xhci->op_regs = hcd->regs +
HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase));
if (retval)
goto error;
xhci_dbg(xhci, "Called HCD init\n");
+
+ printk("%s(%d): do mtk_xhci_set\n", __func__, __LINE__);
+
return 0;
error:
kfree(xhci);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
+#ifdef CONFIG_USBIF_COMPLIANCE
+#ifndef CONFIG_USB_MTK_DUALMODE
+static int xhci_hcd_driver_init(void)
+{
+ int retval;
+
+ retval = xhci_register_pci();
+ if (retval < 0) {
+ printk(KERN_DEBUG "Problem registering PCI driver.");
+ return retval;
+ }
+
+ #ifdef CONFIG_MTK_XHCI
+ mtk_xhci_ip_init();
+ #endif
+
+ retval = xhci_register_plat();
+ if (retval < 0) {
+ printk(KERN_DEBUG "Problem registering platform driver.");
+ goto unreg_pci;
+ }
+
+ #ifdef CONFIG_MTK_XHCI
+ retval = xhci_attrs_init();
+ if(retval < 0){
+ printk(KERN_DEBUG "Problem creating xhci attributes.");
+ goto unreg_plat;
+ }
+
+ mtk_xhci_wakelock_init();
+ #endif
+
+ /*
+ * Check the compiler generated sizes of structures that must be laid
+ * out in specific ways for hardware access.
+ */
+ BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8);
+ BUILD_BUG_ON(sizeof(struct xhci_slot_ctx) != 8*32/8);
+ BUILD_BUG_ON(sizeof(struct xhci_ep_ctx) != 8*32/8);
+ /* xhci_device_control has eight fields, and also
+ * embeds one xhci_slot_ctx and 31 xhci_ep_ctx
+ */
+ BUILD_BUG_ON(sizeof(struct xhci_stream_ctx) != 4*32/8);
+ BUILD_BUG_ON(sizeof(union xhci_trb) != 4*32/8);
+ BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8);
+ BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 7*32/8);
+ BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
+ /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
+ BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
+ return 0;
+
+#ifdef CONFIG_MTK_XHCI
+unreg_plat:
+ xhci_unregister_plat();
+#endif
+unreg_pci:
+ xhci_unregister_pci();
+ return retval;
+}
+
+static void xhci_hcd_driver_cleanup(void)
+{
+ xhci_unregister_pci();
+ xhci_unregister_plat();
+ xhci_attrs_exit();
+}
+#else
+static int xhci_hcd_driver_init(void)
+{
+ // init in mt_devs.c
+ mtk_xhci_eint_iddig_init();
+ mtk_xhci_switch_init();
+ //mtk_xhci_wakelock_init();
+ return 0;
+}
+
+static void xhci_hcd_driver_cleanup(void)
+{
+ mtk_xhci_eint_iddig_deinit() ;
+}
+
+#endif
+
+static int mu3h_normal_driver_on = 0 ;
+
+static int xhci_mu3h_proc_show(struct seq_file *seq, void *v)
+{
+ seq_printf(seq, "xhci_mu3h_proc_show, mu3h is %d (on:1, off:0)\n", mu3h_normal_driver_on);
+ return 0;
+}
+
+static int xhci_mu3h_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, xhci_mu3h_proc_show, inode->i_private);
+}
+
+static ssize_t xhci_mu3h_proc_write(struct file *file, const char __user *buf, size_t length, loff_t *ppos)
+{
+ int ret ;
+ char msg[32] ;
+ int result;
+
+ if (length >= sizeof(msg)) {
+ printk( "xhci_mu3h_proc_write length error, the error len is %d\n", (unsigned int)length);
+ return -EINVAL;
+ }
+ if (copy_from_user(msg, buf, length))
+ return -EFAULT;
+
+ msg[length] = 0 ;
+
+ printk("xhci_mu3h_proc_write: %s, current driver on/off: %d\n", msg, mu3h_normal_driver_on);
+
+ if ((msg[0] == '1') && (mu3h_normal_driver_on == 0)){
+ xhci_hcd_driver_init() ;
+ mu3h_normal_driver_on = 1 ;
+ printk("registe mu3h driver : m3h xhci driver\n");
+ }else if ((msg[0] == '0') && (mu3h_normal_driver_on == 1)){
+ xhci_hcd_driver_cleanup();
+ mu3h_normal_driver_on = 0 ;
+ printk("unregiste m3h xhci driver.\n");
+ }else{
+ printk("xhci_mu3h_proc_write write faile !\n");
+ }
+ return length;
+}
+
+static const struct file_operations mu3h_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = xhci_mu3h_proc_open,
+ .write = xhci_mu3h_proc_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+
+};
+
+static int __init xhci_hcd_init(void)
+{
+ struct proc_dir_entry *prEntry;
+
+ printk(KERN_DEBUG "xhci_hcd_init");
+
+ // set xhci up at boot up
+ xhci_hcd_driver_init() ;
+ mtk_xhci_wakelock_init();
+ mu3h_normal_driver_on = 1;
+
+ // USBIF
+ prEntry = proc_create("mu3h_driver_init", 0666, NULL, &mu3h_proc_fops);
+ if (prEntry)
+ {
+ printk("create the mu3h init proc OK!\n") ;
+ }else{
+ printk("[ERROR] create the mu3h init proc FAIL\n") ;
+ }
+
+#ifdef CONFIG_MTK_XHCI
+
+ if (!misc_register(&mu3h_uevent_device)){
+ printk("create the mu3h_uevent_device uevent device OK!\n") ;
+
+ }else{
+ printk("[ERROR] create the mu3h_uevent_device uevent device fail\n") ;
+ }
+
+#endif
+
+ return 0 ;
+
+}
+module_init(xhci_hcd_init);
+
+static void __exit xhci_hcd_cleanup(void)
+{
+#ifdef CONFIG_MTK_XHCI
+ misc_deregister(&mu3h_uevent_device);
+#endif
+ printk(KERN_DEBUG "xhci_hcd_cleanup");
+}
+module_exit(xhci_hcd_cleanup);
+
+#else
+#ifndef CONFIG_USB_MTK_DUALMODE
static int __init xhci_hcd_init(void)
{
int retval;
printk(KERN_DEBUG "Problem registering platform driver.");
goto unreg_pci;
}
+
+ #ifdef CONFIG_MTK_XHCI
+ retval = xhci_attrs_init();
+ if(retval < 0){
+ printk(KERN_DEBUG "Problem creating xhci attributes.");
+ goto unreg_plat;
+ }
+
+ mtk_xhci_wakelock_init();
+ #endif
+
/*
* Check the compiler generated sizes of structures that must be laid
* out in specific ways for hardware access.
BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
return 0;
+
+#ifdef CONFIG_MTK_XHCI
+unreg_plat:
+ xhci_unregister_plat();
+#endif
unreg_pci:
xhci_unregister_pci();
return retval;
{
xhci_unregister_pci();
xhci_unregister_plat();
+ xhci_attrs_exit();
}
module_exit(xhci_hcd_cleanup);
+#else
+static int __init xhci_hcd_init(void)
+{
+ mtk_xhci_eint_iddig_init();
+ mtk_xhci_switch_init();
+ mtk_xhci_wakelock_init();
+ return 0;
+}
+module_init(xhci_hcd_init);
+
+static void __exit xhci_hcd_cleanup(void)
+{
+}
+module_exit(xhci_hcd_cleanup);
+
+#endif
+#endif