2020-03-28 17:18:50 +08:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
2013-01-11 20:10:38 +08:00
|
|
|
/*
|
|
|
|
* usb hub driver head file
|
|
|
|
*
|
|
|
|
* Copyright (C) 1999 Linus Torvalds
|
|
|
|
* Copyright (C) 1999 Johannes Erdfelt
|
|
|
|
* Copyright (C) 1999 Gregory P. Smith
|
|
|
|
* Copyright (C) 2001 Brad Hards (bhards@bigpond.net.au)
|
|
|
|
* Copyright (C) 2012 Intel Corp (tianyu.lan@intel.com)
|
|
|
|
*
|
|
|
|
* move struct usb_hub to this file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/usb.h>
|
|
|
|
#include <linux/usb/ch11.h>
|
|
|
|
#include <linux/usb/hcd.h>
|
|
|
|
#include "usb.h"
|
|
|
|
|
|
|
|
struct usb_hub {
|
|
|
|
struct device *intfdev; /* the "interface" device */
|
|
|
|
struct usb_device *hdev;
|
|
|
|
struct kref kref;
|
|
|
|
struct urb *urb; /* for interrupt polling pipe */
|
|
|
|
|
|
|
|
/* buffer for urb ... with extra space in case of babble */
|
2013-01-25 04:04:13 +08:00
|
|
|
u8 (*buffer)[8];
|
2013-01-11 20:10:38 +08:00
|
|
|
union {
|
|
|
|
struct usb_hub_status hub;
|
|
|
|
struct usb_port_status port;
|
|
|
|
} *status; /* buffer for status reports */
|
|
|
|
struct mutex status_mutex; /* for the status buffer */
|
|
|
|
|
|
|
|
int error; /* last reported error */
|
|
|
|
int nerrors; /* track consecutive errors */
|
|
|
|
|
|
|
|
unsigned long event_bits[1]; /* status change bitmask */
|
|
|
|
unsigned long change_bits[1]; /* ports with logical connect
|
|
|
|
status change */
|
|
|
|
unsigned long removed_bits[1]; /* ports with a "removed"
|
|
|
|
device present */
|
|
|
|
unsigned long wakeup_bits[1]; /* ports that have signaled
|
|
|
|
remote wakeup */
|
2014-05-21 09:08:52 +08:00
|
|
|
unsigned long power_bits[1]; /* ports that are powered */
|
|
|
|
unsigned long child_usage_bits[1]; /* ports powered on for
|
|
|
|
children */
|
2014-05-30 03:58:46 +08:00
|
|
|
unsigned long warm_reset_bits[1]; /* ports requesting warm
|
|
|
|
reset recovery */
|
2013-01-11 20:10:38 +08:00
|
|
|
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
|
|
|
|
#error event_bits[] is too short!
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct usb_hub_descriptor *descriptor; /* class descriptor */
|
|
|
|
struct usb_tt tt; /* Transaction Translator */
|
|
|
|
|
|
|
|
unsigned mA_per_port; /* current for each child */
|
2013-07-12 02:58:04 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
unsigned wakeup_enabled_descendants;
|
|
|
|
#endif
|
2013-01-11 20:10:38 +08:00
|
|
|
|
|
|
|
unsigned limited_power:1;
|
|
|
|
unsigned quiescing:1;
|
|
|
|
unsigned disconnected:1;
|
2014-05-21 09:08:07 +08:00
|
|
|
unsigned in_reset:1;
|
USB: hub: Fix the broken detection of USB3 device in SMSC hub
Renesas R-Car H3ULCB + Kingfisher Infotainment Board is either not able
to detect the USB3.0 mass storage devices or is detecting those as
USB2.0 high speed devices.
The explanation given by Renesas is that, due to a HW issue, the XHCI
driver does not wake up after going to sleep on connecting a USB3.0
device.
In order to mitigate that, disable the auto-suspend feature
specifically for SMSC hubs from hub_probe() function, as a quirk.
Renesas Kingfisher Infotainment Board has two USB3.0 ports (CN2) which
are connected via USB5534B 4-port SuperSpeed/Hi-Speed, low-power,
configurable hub controller.
[1] SanDisk USB 3.0 device detected as USB-2.0 before the patch
[ 74.036390] usb 5-1.1: new high-speed USB device number 4 using xhci-hcd
[ 74.061598] usb 5-1.1: New USB device found, idVendor=0781, idProduct=5581, bcdDevice= 1.00
[ 74.069976] usb 5-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 74.077303] usb 5-1.1: Product: Ultra
[ 74.080980] usb 5-1.1: Manufacturer: SanDisk
[ 74.085263] usb 5-1.1: SerialNumber: 4C530001110208116550
[2] SanDisk USB 3.0 device detected as USB-3.0 after the patch
[ 34.565078] usb 6-1.1: new SuperSpeed Gen 1 USB device number 3 using xhci-hcd
[ 34.588719] usb 6-1.1: New USB device found, idVendor=0781, idProduct=5581, bcdDevice= 1.00
[ 34.597098] usb 6-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 34.604430] usb 6-1.1: Product: Ultra
[ 34.608110] usb 6-1.1: Manufacturer: SanDisk
[ 34.612397] usb 6-1.1: SerialNumber: 4C530001110208116550
Suggested-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Hardik Gajjar <hgajjar@de.adit-jv.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Tested-by: Eugeniu Rosca <erosca@de.adit-jv.com>
Cc: stable <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/1580989763-32291-1-git-send-email-hgajjar@de.adit-jv.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-02-06 19:49:23 +08:00
|
|
|
unsigned quirk_disable_autosuspend:1;
|
2013-01-11 20:10:38 +08:00
|
|
|
|
|
|
|
unsigned quirk_check_port_auto_suspend:1;
|
|
|
|
|
|
|
|
unsigned has_indicators:1;
|
|
|
|
u8 indicator[USB_MAXCHILDREN];
|
|
|
|
struct delayed_work leds;
|
|
|
|
struct delayed_work init_work;
|
2014-09-19 23:32:21 +08:00
|
|
|
struct work_struct events;
|
2019-01-08 23:45:22 +08:00
|
|
|
spinlock_t irq_urb_lock;
|
|
|
|
struct timer_list irq_urb_retry;
|
2013-01-11 20:10:38 +08:00
|
|
|
struct usb_port **ports;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* struct usb port - kernel's representation of a usb port
|
2014-01-04 13:54:41 +08:00
|
|
|
* @child: usb device attached to the port
|
2013-01-11 20:10:38 +08:00
|
|
|
* @dev: generic device interface
|
|
|
|
* @port_owner: port's owner
|
2014-05-21 09:08:28 +08:00
|
|
|
* @peer: related usb2 and usb3 ports (share the same connector)
|
2014-06-18 07:16:32 +08:00
|
|
|
* @req: default pm qos request for hubs without port power control
|
2013-01-11 20:10:38 +08:00
|
|
|
* @connect_type: port's connect type
|
2014-05-21 09:08:40 +08:00
|
|
|
* @location: opaque representation of platform connector location
|
2014-05-21 09:09:26 +08:00
|
|
|
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
|
2013-01-23 04:26:29 +08:00
|
|
|
* @portnum: port index num based one
|
2014-05-21 09:08:57 +08:00
|
|
|
* @is_superspeed cache super-speed status
|
2015-11-14 16:26:33 +08:00
|
|
|
* @usb3_lpm_u1_permit: whether USB3 U1 LPM is permitted.
|
|
|
|
* @usb3_lpm_u2_permit: whether USB3 U2 LPM is permitted.
|
2013-01-11 20:10:38 +08:00
|
|
|
*/
|
|
|
|
struct usb_port {
|
|
|
|
struct usb_device *child;
|
|
|
|
struct device dev;
|
2014-03-10 16:36:40 +08:00
|
|
|
struct usb_dev_state *port_owner;
|
2014-05-21 09:08:28 +08:00
|
|
|
struct usb_port *peer;
|
2014-06-18 07:16:32 +08:00
|
|
|
struct dev_pm_qos_request *req;
|
2013-01-11 20:10:38 +08:00
|
|
|
enum usb_port_connect_type connect_type;
|
2014-05-21 09:08:40 +08:00
|
|
|
usb_port_location_t location;
|
2014-05-21 09:09:26 +08:00
|
|
|
struct mutex status_lock;
|
2018-03-20 18:17:13 +08:00
|
|
|
u32 over_current_count;
|
2013-01-23 04:26:29 +08:00
|
|
|
u8 portnum;
|
2018-05-28 14:32:18 +08:00
|
|
|
u32 quirks;
|
2014-05-21 09:08:57 +08:00
|
|
|
unsigned int is_superspeed:1;
|
2015-11-14 16:26:33 +08:00
|
|
|
unsigned int usb3_lpm_u1_permit:1;
|
|
|
|
unsigned int usb3_lpm_u2_permit:1;
|
2013-01-11 20:10:38 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define to_usb_port(_dev) \
|
|
|
|
container_of(_dev, struct usb_port, dev)
|
|
|
|
|
|
|
|
extern int usb_hub_create_port_device(struct usb_hub *hub,
|
|
|
|
int port1);
|
|
|
|
extern void usb_hub_remove_port_device(struct usb_hub *hub,
|
|
|
|
int port1);
|
2013-06-18 22:28:48 +08:00
|
|
|
extern int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
|
2013-01-23 04:26:29 +08:00
|
|
|
int port1, bool set);
|
2013-01-23 04:26:30 +08:00
|
|
|
extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev);
|
|
|
|
extern int hub_port_debounce(struct usb_hub *hub, int port1,
|
|
|
|
bool must_be_connected);
|
|
|
|
extern int usb_clear_port_feature(struct usb_device *hdev,
|
|
|
|
int port1, int feature);
|
|
|
|
|
2014-05-21 09:08:12 +08:00
|
|
|
static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
|
|
|
|
{
|
|
|
|
__le16 hcs;
|
|
|
|
|
|
|
|
if (!hub)
|
|
|
|
return false;
|
|
|
|
hcs = hub->descriptor->wHubCharacteristics;
|
|
|
|
return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
|
|
|
|
}
|
|
|
|
|
2014-05-21 09:08:57 +08:00
|
|
|
static inline int hub_is_superspeed(struct usb_device *hdev)
|
|
|
|
{
|
|
|
|
return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
|
|
|
|
}
|
|
|
|
|
2015-12-10 15:59:29 +08:00
|
|
|
static inline int hub_is_superspeedplus(struct usb_device *hdev)
|
|
|
|
{
|
|
|
|
return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS &&
|
|
|
|
le16_to_cpu(hdev->descriptor.bcdUSB) >= 0x0310 &&
|
|
|
|
hdev->bos->ssp_cap);
|
|
|
|
}
|
|
|
|
|
2014-05-21 09:08:57 +08:00
|
|
|
static inline unsigned hub_power_on_good_delay(struct usb_hub *hub)
|
|
|
|
{
|
|
|
|
unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;
|
|
|
|
|
|
|
|
/* Wait at least 100 msec for power to become stable */
|
|
|
|
return max(delay, 100U);
|
|
|
|
}
|
|
|
|
|
2013-01-23 04:26:30 +08:00
|
|
|
static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
|
|
|
|
int port1)
|
|
|
|
{
|
|
|
|
return hub_port_debounce(hub, port1, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
|
|
|
|
int port1)
|
|
|
|
{
|
|
|
|
return hub_port_debounce(hub, port1, false);
|
|
|
|
}
|