All of lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND,v9] xhci : AMD Promontory USB disable port support
@ 2018-01-16 13:37 Mathias Nyman
  0 siblings, 0 replies; 2+ messages in thread
From: Mathias Nyman @ 2018-01-16 13:37 UTC (permalink / raw)
  To: Joe Lee, linux-usb; +Cc: justin_CY_Chen, YD_Tseng, Eric_kung, joe2_lee

On 11.12.2017 11:42, Joe Lee wrote:
> From: Joe Lee <joe2_lee@asmedia.com.tw>
> 
> For AMD Promontory xHCI host,although you can disable USB ports in
> BIOSsettings,those ports will be enabled anyway after you remove a
> device onthat port and re-plug it in again. It's a known limitation of
> the chip.As a workaround we can clear the PORT_WAKE_BITS.
> 
> Signed-off-by: Joe Lee <joe2_lee@asmedia.com.tw>
> 
> ---
> v9:
>    - Fix multi-line comment style
> v8:
>    - usb_amd_pt_check_port() function return a bool
> v7:
>    - add a empty function for the case when CONFIG_USB_PCI is not
>      defined in pci-quirks.h
> v6:
>    - Fix coding format.
> v5:
>    - Add check disable port setting before set PORT_WAKE_BITS
> v4:
>    - Remove the patch code in case USB_PORT_FEAT_REMOTE_WAKE_MASK
> v3:
>    - Fix some checkpatch.pl

Sorry about the delay.

Patch applies and compiles, and looks functional
I do have a small refactoring suggestion.

Otherwise it looks ready.

> ---
>   drivers/usb/host/pci-quirks.c | 128 ++++++++++++++++++++++++++++++++++++++++++
>   drivers/usb/host/pci-quirks.h |   5 ++
>   drivers/usb/host/xhci-hub.c   |   7 +++
>   drivers/usb/host/xhci-pci.c   |  11 ++++
>   drivers/usb/host/xhci.h       |   2 +-
>   5 files changed, 152 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
> index 6dda362..bf8354e 100644
> --- a/drivers/usb/host/pci-quirks.c
> +++ b/drivers/usb/host/pci-quirks.c
> @@ -65,6 +65,23 @@
>   #define	AX_INDXC		0x30
>   #define	AX_DATAC		0x34
>   
> +#define PT_ADDR_INDX		0xE8
> +#define PT_READ_INDX		0xE4
> +#define PT_SIG_1_ADDR		0xA520
> +#define PT_SIG_2_ADDR		0xA521
> +#define PT_SIG_3_ADDR		0xA522
> +#define PT_SIG_4_ADDR		0xA523
> +#define PT_SIG_1_DATA		0x78
> +#define PT_SIG_2_DATA		0x56
> +#define PT_SIG_3_DATA		0x34
> +#define PT_SIG_4_DATA		0x12
> +#define PT4_P1_REG		0xB521
> +#define PT4_P2_REG		0xB522
> +#define PT2_P1_REG		0xD520
> +#define PT2_P2_REG		0xD521
> +#define PT1_P1_REG		0xD522
> +#define PT1_P2_REG		0xD523
> +
>   #define	NB_PCIE_INDX_ADDR	0xe0
>   #define	NB_PCIE_INDX_DATA	0xe4
>   #define	PCIE_P_CNTL		0x10040
> @@ -511,6 +528,117 @@ void usb_amd_dev_put(void)
>   }
>   EXPORT_SYMBOL_GPL(usb_amd_dev_put);
>   
> +bool usb_amd_pt_check_port(struct device *device, int port)
> +{
> +	unsigned char value;
> +	struct pci_dev *pdev;
> +
> +	pdev = to_pci_dev(device);
> +	pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_1_ADDR);
> +
> +	pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +	if (value != PT_SIG_1_DATA)
> +		return false;
> +
> +	pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_2_ADDR);
> +
> +	pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +	if (value != PT_SIG_2_DATA)
> +		return false;
> +
> +	pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_3_ADDR);
> +
> +	pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +	if (value != PT_SIG_3_DATA)
> +		return false;
> +
> +	pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_4_ADDR);
> +
> +	pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +	if (value != PT_SIG_4_DATA)
> +		return false;
> +

How about changing the code below from here:

> +	if ((pdev->device == 0x43b9) || (pdev->device == 0x43ba)) {
> +		/* device is AMD_PROMONTORYA_4(0x43b9) or
> +		 * PROMONTORYA_3(0x43ba)
> +		 * disable port setting
> +		 * PT_4_P1_REG[7..1] is USB2.0 port6 to 0
> +		 * PT4_P2_REG[6..0] is USB 13 to port 7
> +		 * 0 : disable ;1 : enable
> +		 */
> +		if (port > 6) {
> +			pci_write_config_word(pdev, PT_ADDR_INDX,
> +						PT4_P2_REG);
> +
> +			pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +			if (value & (1<<(port - 7)))
> +				return false;
> +			else
> +				return true;
> +		} else {
> +			pci_write_config_word(pdev, PT_ADDR_INDX,
> +						PT4_P1_REG);
> +
> +			pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +			if (value & (1<<(port + 1)))
> +				return false;
> +			else
> +				return true;
> +		}
> +	} else if (pdev->device == 0x43bb) {
> +		/* device is AMD_PROMONTORYA_2(0x43bb)
> +		 * disable port setting
> +		 * PT2_P1_REG[7..5] is USB2.0 port2 to 0
> +		 * PT2_P2_REG[5..0] is USB 9 to port3
> +		 * 0 : disable ;1 : enable
> +		 */
> +		if (port > 2) {
> +			pci_write_config_word(pdev, PT_ADDR_INDX, PT2_P2_REG);
> +
> +			pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +			if (value & (1<<(port - 3)))
> +				return false;
> +			else
> +				return true;
> +		} else {
> +			pci_write_config_word(pdev, PT_ADDR_INDX, PT2_P1_REG);
> +
> +			pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +			if (value & (1<<(port + 5)))
> +				return false;
> +			else
> +				return true;
> +		}
> +	} else {
> +		/* device is AMD_PROMONTORYA_1(0x43bc)
> +		 * disable port setting
> +		 * PT1_P1_REG[7..4] is USB2.0 port3 to 0
> +		 * PT1_P2_REG[5..0] is USB 9 to port4
> +		 * 0 : disable ;1 : enable
> +		 */
> +		if (port > 3) {
> +			pci_write_config_word(pdev, PT_ADDR_INDX, PT1_P2_REG);
> +
> +			pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +			if (value & (1<<(port - 4)))
> +				return false;
> +			else
> +				return true;
> +
> +		} else {
> +			pci_write_config_word(pdev, PT_ADDR_INDX, PT1_P1_REG);
> +
> +			pci_read_config_byte(pdev, PT_READ_INDX, &value);
> +			if (value & (1<<(port + 4)))
> +				return false;
> +			else
> +				return true;
> +
> +		}

to here, with something like this instead:

	switch(pdev->device) {
	case 0x43b9:
	case 0x43ba:
		if (port > 6) {
                         reg = PT4_P2_REG;
			port_shift = port - 7;
                 } else {
                         reg = PT4_P1_REG;
                         port_shift = port + 1;
                 }
                 break;
	case 0x43bb:
		if (port > 2) {
                         reg = PT2_P2_REG;
			port_shift = port - 3;
                 } else {
                         reg = PT2_P1_REG;
                         port_shift = port + 4;
                 }
                 break;
         case 0x43bc:
                 if (port > 3) {
                         reg = PT1_P2_REG;
                         port_shift = port - 4;
		} else {
                         reg = PT1_P1_REG;
			port_shift = port + 4;
	        }
                 break;
         default:
                 return false;
         }
         pci_write_config_word(pdev, PT_ADDR_INDX, reg);
	pci_read_config_byte(pdev, PT_READ_INDX, &value);

	return !(value & BIT(port_shift));

-Mathias
---
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 2+ messages in thread

* [RESEND,v9] xhci : AMD Promontory USB disable port support
@ 2017-12-11  9:42 Joe Lee
  0 siblings, 0 replies; 2+ messages in thread
From: Joe Lee @ 2017-12-11  9:42 UTC (permalink / raw)
  To: linux-usb, mathias.nyman; +Cc: justin_CY_Chen, YD_Tseng, Eric_kung, joe2_lee

From: Joe Lee <joe2_lee@asmedia.com.tw>

For AMD Promontory xHCI host,although you can disable USB ports in
BIOSsettings,those ports will be enabled anyway after you remove a
device onthat port and re-plug it in again. It's a known limitation of
the chip.As a workaround we can clear the PORT_WAKE_BITS.

Signed-off-by: Joe Lee <joe2_lee@asmedia.com.tw>
---
v9: 
  - Fix multi-line comment style 
v8: 
  - usb_amd_pt_check_port() function return a bool
v7: 
  - add a empty function for the case when CONFIG_USB_PCI is not
    defined in pci-quirks.h
v6: 
  - Fix coding format.
v5: 
  - Add check disable port setting before set PORT_WAKE_BITS
v4: 
  - Remove the patch code in case USB_PORT_FEAT_REMOTE_WAKE_MASK
v3: 
  - Fix some checkpatch.pl
---
 drivers/usb/host/pci-quirks.c | 128 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/pci-quirks.h |   5 ++
 drivers/usb/host/xhci-hub.c   |   7 +++
 drivers/usb/host/xhci-pci.c   |  11 ++++
 drivers/usb/host/xhci.h       |   2 +-
 5 files changed, 152 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 6dda362..bf8354e 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -65,6 +65,23 @@
 #define	AX_INDXC		0x30
 #define	AX_DATAC		0x34
 
+#define PT_ADDR_INDX		0xE8
+#define PT_READ_INDX		0xE4
+#define PT_SIG_1_ADDR		0xA520
+#define PT_SIG_2_ADDR		0xA521
+#define PT_SIG_3_ADDR		0xA522
+#define PT_SIG_4_ADDR		0xA523
+#define PT_SIG_1_DATA		0x78
+#define PT_SIG_2_DATA		0x56
+#define PT_SIG_3_DATA		0x34
+#define PT_SIG_4_DATA		0x12
+#define PT4_P1_REG		0xB521
+#define PT4_P2_REG		0xB522
+#define PT2_P1_REG		0xD520
+#define PT2_P2_REG		0xD521
+#define PT1_P1_REG		0xD522
+#define PT1_P2_REG		0xD523
+
 #define	NB_PCIE_INDX_ADDR	0xe0
 #define	NB_PCIE_INDX_DATA	0xe4
 #define	PCIE_P_CNTL		0x10040
@@ -511,6 +528,117 @@ void usb_amd_dev_put(void)
 }
 EXPORT_SYMBOL_GPL(usb_amd_dev_put);
 
+bool usb_amd_pt_check_port(struct device *device, int port)
+{
+	unsigned char value;
+	struct pci_dev *pdev;
+
+	pdev = to_pci_dev(device);
+	pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_1_ADDR);
+
+	pci_read_config_byte(pdev, PT_READ_INDX, &value);
+	if (value != PT_SIG_1_DATA)
+		return false;
+
+	pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_2_ADDR);
+
+	pci_read_config_byte(pdev, PT_READ_INDX, &value);
+	if (value != PT_SIG_2_DATA)
+		return false;
+
+	pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_3_ADDR);
+
+	pci_read_config_byte(pdev, PT_READ_INDX, &value);
+	if (value != PT_SIG_3_DATA)
+		return false;
+
+	pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_4_ADDR);
+
+	pci_read_config_byte(pdev, PT_READ_INDX, &value);
+	if (value != PT_SIG_4_DATA)
+		return false;
+
+	if ((pdev->device == 0x43b9) || (pdev->device == 0x43ba)) {
+		/* device is AMD_PROMONTORYA_4(0x43b9) or
+		 * PROMONTORYA_3(0x43ba)
+		 * disable port setting
+		 * PT_4_P1_REG[7..1] is USB2.0 port6 to 0
+		 * PT4_P2_REG[6..0] is USB 13 to port 7
+		 * 0 : disable ;1 : enable
+		 */
+		if (port > 6) {
+			pci_write_config_word(pdev, PT_ADDR_INDX,
+						PT4_P2_REG);
+
+			pci_read_config_byte(pdev, PT_READ_INDX, &value);
+			if (value & (1<<(port - 7)))
+				return false;
+			else
+				return true;
+		} else {
+			pci_write_config_word(pdev, PT_ADDR_INDX,
+						PT4_P1_REG);
+
+			pci_read_config_byte(pdev, PT_READ_INDX, &value);
+			if (value & (1<<(port + 1)))
+				return false;
+			else
+				return true;
+		}
+	} else if (pdev->device == 0x43bb) {
+		/* device is AMD_PROMONTORYA_2(0x43bb)
+		 * disable port setting
+		 * PT2_P1_REG[7..5] is USB2.0 port2 to 0
+		 * PT2_P2_REG[5..0] is USB 9 to port3
+		 * 0 : disable ;1 : enable
+		 */
+		if (port > 2) {
+			pci_write_config_word(pdev, PT_ADDR_INDX, PT2_P2_REG);
+
+			pci_read_config_byte(pdev, PT_READ_INDX, &value);
+			if (value & (1<<(port - 3)))
+				return false;
+			else
+				return true;
+		} else {
+			pci_write_config_word(pdev, PT_ADDR_INDX, PT2_P1_REG);
+
+			pci_read_config_byte(pdev, PT_READ_INDX, &value);
+			if (value & (1<<(port + 5)))
+				return false;
+			else
+				return true;
+		}
+	} else {
+		/* device is AMD_PROMONTORYA_1(0x43bc)
+		 * disable port setting
+		 * PT1_P1_REG[7..4] is USB2.0 port3 to 0
+		 * PT1_P2_REG[5..0] is USB 9 to port4
+		 * 0 : disable ;1 : enable
+		 */
+		if (port > 3) {
+			pci_write_config_word(pdev, PT_ADDR_INDX, PT1_P2_REG);
+
+			pci_read_config_byte(pdev, PT_READ_INDX, &value);
+			if (value & (1<<(port - 4)))
+				return false;
+			else
+				return true;
+
+		} else {
+			pci_write_config_word(pdev, PT_ADDR_INDX, PT1_P1_REG);
+
+			pci_read_config_byte(pdev, PT_READ_INDX, &value);
+			if (value & (1<<(port + 4)))
+				return false;
+			else
+				return true;
+
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(usb_amd_pt_check_port);
+
 /*
  * Make sure the controller is completely inactive, unable to
  * generate interrupts or do DMA.
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index b68dcb5..4ca0d9b 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -17,6 +17,7 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
 void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
 void sb800_prefetch(struct device *dev, int on);
 bool usb_xhci_needs_pci_reset(struct pci_dev *pdev);
+bool usb_amd_pt_check_port(struct device *device, int port);
 #else
 struct pci_dev;
 static inline void usb_amd_quirk_pll_disable(void) {}
@@ -25,6 +26,10 @@ static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
 static inline void usb_amd_dev_put(void) {}
 static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
 static inline void sb800_prefetch(struct device *dev, int on) {}
+static inline bool usb_amd_pt_check_port(struct device *device, int port)
+{
+	return false;
+}
 #endif  /* CONFIG_USB_PCI */
 
 #endif  /*  __LINUX_USB_PCI_QUIRKS_H  */
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index a2336de..0df0497 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1528,6 +1528,13 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
 				t2 |= PORT_WKOC_E | PORT_WKCONN_E;
 				t2 &= ~PORT_WKDISC_E;
 			}
+
+			if ((xhci->quirks & XHCI_U2_DISABLE_WAKE) &&
+			    (hcd->speed < HCD_USB3)) {
+				if (usb_amd_pt_check_port(hcd->self.controller,
+							  port_index))
+					t2 &= ~PORT_WAKE_BITS;
+			}
 		} else
 			t2 &= ~PORT_WAKE_BITS;
 
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 76f3929..f056754 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -54,6 +54,10 @@
 #define PCI_DEVICE_ID_INTEL_APL_XHCI			0x5aa8
 #define PCI_DEVICE_ID_INTEL_DNV_XHCI			0x19d0
 
+#define PCI_DEVICE_ID_AMD_PROMONTORYA_4			0x43b9
+#define PCI_DEVICE_ID_AMD_PROMONTORYA_3			0x43ba
+#define PCI_DEVICE_ID_AMD_PROMONTORYA_2			0x43bb
+#define PCI_DEVICE_ID_AMD_PROMONTORYA_1			0x43bc
 #define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI		0x1142
 
 static const char hcd_name[] = "xhci_hcd";
@@ -137,6 +141,13 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
 	if (pdev->vendor == PCI_VENDOR_ID_AMD)
 		xhci->quirks |= XHCI_TRUST_TX_LENGTH;
 
+	if ((pdev->vendor == PCI_VENDOR_ID_AMD) &&
+		((pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4) ||
+		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_3) ||
+		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_2) ||
+		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_1)))
+		xhci->quirks |= XHCI_U2_DISABLE_WAKE;
+
 	if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
 		xhci->quirks |= XHCI_LPM_SUPPORT;
 		xhci->quirks |= XHCI_INTEL_HOST;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 2b48aa4..7c87189 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1828,7 +1828,7 @@ struct xhci_hcd {
 /* For controller with a broken Port Disable implementation */
 #define XHCI_BROKEN_PORT_PED	(1 << 25)
 #define XHCI_LIMIT_ENDPOINT_INTERVAL_7	(1 << 26)
-/* Reserved. It was XHCI_U2_DISABLE_WAKE */
+#define XHCI_U2_DISABLE_WAKE	(1 << 27)
 #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL	(1 << 28)
 
 	unsigned int		num_active_eps;

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2018-01-16 13:37 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-16 13:37 [RESEND,v9] xhci : AMD Promontory USB disable port support Mathias Nyman
  -- strict thread matches above, loose matches on Subject: below --
2017-12-11  9:42 Joe Lee

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.