xhci-tegra: t210: support XHCI "soft retry"
JC Kuo [Tue, 22 Dec 2015 13:33:15 +0000 (21:33 +0800)]
This commit implements a TEGRA210 XHCI specific programming sequence
which needs to be done along with "soft retry".

Verified E2220-ERS.

new added
- wait for U0 when is going to assert clame_en_early, timeout in 300us

Bug 200162414

Change-Id: I9b9e1eeb39d259416c199819808020c48171ab57
Signed-off-by: JC Kuo <jckuo@nvidia.com>
Reviewed-on: http://git-master/r/1169996
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: ChihMin Cheng <ccheng@nvidia.com>
Tested-by: Mark Kuo <mkuo@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>

drivers/usb/host/xhci-tegra.c
drivers/usb/host/xhci-tegra.h

index 8b81f00..e86bb57 100644 (file)
@@ -417,6 +417,13 @@ static inline void must_have_sync_lock(struct tegra_xhci_hcd *tegra)
                && (_pad >= 0); \
                _pad = find_next_enabled_ss_pad(_tegra_xhci_hcd, _pad + 1))
 
+#define for_each_enabled_ss_pad_with_otg(_pad, _tegra_xhci_hcd)                \
+       for (_pad = find_next_enabled_ss_pad_with_otg(_tegra_xhci_hcd, 0);\
+               (_pad < (_tegra_xhci_hcd->soc_config->ss_pad_count))    \
+               && (_pad >= 0); \
+               _pad = find_next_enabled_ss_pad_with_otg(\
+                       _tegra_xhci_hcd, _pad + 1))
+
 #define for_each_enabled_utmi_pad(_pad, _tegra_xhci_hcd)               \
        for (_pad = find_next_enabled_utmi_pad(_tegra_xhci_hcd, 0);     \
                (_pad < (_tegra_xhci_hcd->soc_config->utmi_pad_count))  \
@@ -499,6 +506,20 @@ static inline int find_next_enabled_ss_pad(struct tegra_xhci_hcd *tegra,
        return find_next_enabled_pad(tegra, start, last) - XUSB_SS_INDEX;
 }
 
+static inline int find_next_enabled_ss_pad_with_otg(
+               struct tegra_xhci_hcd *tegra, int curr_pad)
+{
+       int ss_pads = tegra->soc_config->ss_pad_count;
+       int start = XUSB_SS_INDEX + curr_pad;
+       int last = XUSB_SS_INDEX + ss_pads;
+
+       if ((curr_pad < 0) || (curr_pad >= ss_pads))
+               return -1;
+
+       return find_next_enabled_pad_with_otg(tegra, start, last) -
+                                               XUSB_SS_INDEX;
+}
+
 static void tegra_xhci_setup_gpio_for_ss_lane(struct tegra_xhci_hcd *tegra)
 {
        int err = 0;
@@ -2807,6 +2828,51 @@ static int get_wake_sources_for_host_controlled_ports(int enabled_ports)
        return wake_events;
 }
 
+#if defined(CONFIG_ARCH_TEGRA_21x_SOC)
+static struct tegra_rx_ctrl_ops t210_rx_ctrl_ops = {
+       .receiver_detector = t210_receiver_detector,
+       .clamp_en_early = t210_clamp_en_early,
+};
+#endif
+
+static void tegra_xhci_enable_receiver_detector(struct tegra_xhci_hcd *tegra,
+                                               unsigned port)
+{
+       dev_dbg(&tegra->pdev->dev, "%s port %d\n", __func__, port);
+
+       if (tegra->rx_ctrl_ops && tegra->rx_ctrl_ops->receiver_detector)
+               tegra->rx_ctrl_ops->receiver_detector(port, true);
+
+}
+
+static void tegra_xhci_disable_receiver_detector(struct tegra_xhci_hcd *tegra,
+                                               unsigned port)
+{
+       dev_dbg(&tegra->pdev->dev, "%s port %d\n", __func__, port);
+
+       if (tegra->rx_ctrl_ops && tegra->rx_ctrl_ops->receiver_detector)
+               tegra->rx_ctrl_ops->receiver_detector(port, false);
+}
+
+static void tegra_xhci_enable_clamp_en_early(struct tegra_xhci_hcd *tegra,
+                                               unsigned port)
+{
+       dev_dbg(&tegra->pdev->dev, "%s port %d\n", __func__, port);
+
+       if (tegra->rx_ctrl_ops && tegra->rx_ctrl_ops->clamp_en_early)
+               tegra->rx_ctrl_ops->clamp_en_early(port, true);
+
+}
+
+static void tegra_xhci_disable_clamp_en_early(struct tegra_xhci_hcd *tegra,
+                                               unsigned port)
+{
+       dev_dbg(&tegra->pdev->dev, "%s port %d\n", __func__, port);
+
+       if (tegra->rx_ctrl_ops && tegra->rx_ctrl_ops->clamp_en_early)
+               tegra->rx_ctrl_ops->clamp_en_early(port, false);
+}
+
 /* SS ELPG Entry initiated by fw */
 static int tegra_xhci_ss_elpg_entry(struct tegra_xhci_hcd *tegra)
 {
@@ -3580,7 +3646,12 @@ tegra_xhci_process_mbox_message(struct work_struct *work)
        case MBOX_CMD_ENABLE_SS_LFPS_DETECTION:
                ports = tegra->cmd_data;
                for_each_set_bit(port, &ports, BITS_PER_LONG) {
+                       unsigned long flags;
+
+                       spin_lock_irqsave(&xhci->lock, flags);
                        t210_enable_lfps_detector(tegra, port - 1);
+                       tegra_xhci_enable_receiver_detector(tegra, port - 1);
+                       spin_unlock_irqrestore(&xhci->lock, flags);
                }
                sw_resp = CMD_DATA(tegra->cmd_data);
                sw_resp |= CMD_TYPE(MBOX_CMD_ACK);
@@ -4241,11 +4312,13 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req,
 
 static int tegra_xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
 {
-       if (hcd->speed == HCD_USB2) {
-               struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-               struct tegra_xhci_hcd *tegra = hcd_to_tegra_xhci(hcd);
-               int port;
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct tegra_xhci_hcd *tegra = hcd_to_tegra_xhci(hcd);
+       unsigned long flags;
+       int port;
+
 
+       if (hcd->speed == HCD_USB2) {
                for_each_enabled_utmi_pad_with_otg(port, tegra) {
                        u32 portsc = xhci_readl(xhci, xhci->usb2_ports[port]);
                        if (portsc == 0xffffffff)
@@ -4257,6 +4330,27 @@ static int tegra_xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
                }
        }
 
+       if ((hcd->speed != HCD_USB3) || !tegra->rx_ctrl_ops)
+               goto no_rx_control;
+
+       for_each_enabled_ss_pad_with_otg(port, tegra) {
+               u32 portsc = xhci_readl(xhci, xhci->usb3_ports[port]);
+               if (portsc == 0xffffffff)
+                       break;
+
+               spin_lock_irqsave(&xhci->lock, flags);
+               if ((portsc & PORT_PLS_MASK) == XDEV_U0) {
+                       tegra_xhci_disable_receiver_detector(tegra, port);
+               } else {
+                       if ((portsc & PORT_PLS_MASK) == XDEV_RXDETECT)
+                               tegra_xhci_disable_clamp_en_early(tegra, port);
+
+                       tegra_xhci_enable_receiver_detector(tegra, port);
+               }
+               spin_unlock_irqrestore(&xhci->lock, flags);
+       }
+
+no_rx_control:
        return xhci_hub_status_data(hcd, buf);
 }
 
@@ -4272,6 +4366,51 @@ static int tegra_xhci_update_hub_device(struct usb_hcd *hcd,
        return xhci_update_hub_device(hcd, hdev, tt, mem_flags);
 }
 
+static void tegra_xhci_endpoint_soft_retry(struct usb_hcd *hcd,
+               struct usb_host_endpoint *ep, bool on)
+{
+       struct usb_device *udev = (struct usb_device *) ep->hcpriv;
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct tegra_xhci_hcd *tegra = hcd_to_tegra_xhci(hcd);
+       int port = -1;
+       int delay = 0;
+       u32 portsc;
+
+       if (!udev || udev->speed != USB_SPEED_SUPER || !tegra->rx_ctrl_ops)
+               return;
+
+       /* trace back to roothub port */
+       while (udev->parent) {
+               if (udev->parent == udev->bus->root_hub) {
+                       port = udev->portnum - 1;
+                       break;
+               }
+               udev = udev->parent;
+       }
+
+       if (port < 0)
+               return;
+
+       portsc = xhci_readl(xhci, xhci->usb3_ports[port]);
+       dev_dbg(&tegra->pdev->dev, "%s port %d on %d portsc 0x%x\n",
+               __func__, port, on, portsc);
+
+       if (on) {
+               while ((portsc & PORT_PLS_MASK) != XDEV_U0 && delay++ < 6) {
+                       udelay(50);
+                       portsc = xhci_readl(xhci, xhci->usb3_ports[port]);
+               }
+
+               if ((portsc & PORT_PLS_MASK) != XDEV_U0) {
+                       dev_info(&tegra->pdev->dev, "%s port %d doesn't reach U0 in 300us, portsc 0x%x\n",
+                               __func__, port, portsc);
+               }
+               tegra_xhci_disable_receiver_detector(tegra, port);
+               tegra_xhci_enable_clamp_en_early(tegra, port);
+       } else
+               tegra_xhci_disable_clamp_en_early(tegra, port);
+}
+
 static void tegra_xhci_reset_otg_sspi_work(struct work_struct *work)
 {
        struct tegra_xhci_hcd *tegra = container_of(work, struct tegra_xhci_hcd,
@@ -4351,6 +4490,7 @@ static const struct hc_driver tegra_plat_xhci_driver = {
        .add_endpoint =         xhci_add_endpoint,
        .drop_endpoint =        xhci_drop_endpoint,
        .endpoint_reset =       xhci_endpoint_reset,
+       .endpoint_soft_retry =  tegra_xhci_endpoint_soft_retry,
        .check_bandwidth =      xhci_check_bandwidth,
        .reset_bandwidth =      xhci_reset_bandwidth,
        .address_device =       xhci_address_device,
@@ -4371,8 +4511,10 @@ static const struct hc_driver tegra_plat_xhci_driver = {
        .bus_resume =           tegra_xhci_bus_resume,
 #endif
 
+#if !defined(CONFIG_ARCH_TEGRA_21x_SOC)
        .enable_usb3_lpm_timeout =      xhci_enable_usb3_lpm_timeout,
        .disable_usb3_lpm_timeout =     xhci_disable_usb3_lpm_timeout,
+#endif
        .hcd_reinit =   tegra_xhci_hcd_reinit,
 
 #ifdef CONFIG_NV_GAMEPAD_RESET
@@ -5530,6 +5672,11 @@ static int tegra_xhci_probe(struct platform_device *pdev)
                }
        }
 
+#if defined(CONFIG_ARCH_TEGRA_21x_SOC)
+       if (XUSB_IS_T210(tegra))
+               tegra->rx_ctrl_ops = &t210_rx_ctrl_ops;
+#endif
+
        tegra->padregs = soc_config->padctl_offsets;
 
        tegra->base_list[0] = tegra->host_phy_virt_base;
index fd49f1d..bde4025 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * xhci-tegra.h - Nvidia xHCI host controller related data
  *
- * Copyright (c) 2013-2015, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2013-2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -333,6 +333,11 @@ struct tegra_xhci_firmware_log {
        unsigned long flags;
 };
 
+struct tegra_rx_ctrl_ops {
+       void (*receiver_detector)(unsigned port, bool enable);
+       void (*clamp_en_early)(unsigned port, bool enable);
+};
+
 struct tegra_xhci_hcd {
        struct platform_device *pdev;
        struct xhci_hcd *xhci;
@@ -454,6 +459,8 @@ struct tegra_xhci_hcd {
 #endif
        bool init_done;
        bool clock_enable_done;
+
+       struct tegra_rx_ctrl_ops *rx_ctrl_ops;
 };
 
 #define NOT_SUPPORTED  0xFFFFFFFF