summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/typec/Kconfig10
-rw-r--r--drivers/staging/typec/Makefile2
-rw-r--r--drivers/staging/typec/TODO10
-rw-r--r--drivers/staging/typec/fusb302/TODO10
-rw-r--r--drivers/staging/typec/pd.h293
-rw-r--r--drivers/staging/typec/pd_bdo.h31
-rw-r--r--drivers/staging/typec/pd_vdo.h251
-rw-r--r--drivers/staging/typec/tcpci.c4
-rw-r--r--drivers/staging/typec/tcpm.h161
-rw-r--r--drivers/usb/atm/cxacru.c4
-rw-r--r--drivers/usb/atm/speedtch.c11
-rw-r--r--drivers/usb/core/driver.c10
-rw-r--r--drivers/usb/core/hcd.c4
-rw-r--r--drivers/usb/core/hub.c13
-rw-r--r--drivers/usb/core/quirks.c6
-rw-r--r--drivers/usb/core/sysfs.c3
-rw-r--r--drivers/usb/core/usb.h1
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c8
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c4
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c4
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c4
-rw-r--r--drivers/usb/gadget/udc/snps_udc_core.c23
-rw-r--r--drivers/usb/host/Kconfig4
-rw-r--r--drivers/usb/host/Makefile4
-rw-r--r--drivers/usb/host/ehci-atmel.c3
-rw-r--r--drivers/usb/host/max3421-hcd.c87
-rw-r--r--drivers/usb/host/ohci-sa1111.c25
-rw-r--r--drivers/usb/host/xhci-debugfs.c526
-rw-r--r--drivers/usb/host/xhci-debugfs.h137
-rw-r--r--drivers/usb/host/xhci-hub.c5
-rw-r--r--drivers/usb/host/xhci-mem.c9
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c3
-rw-r--r--drivers/usb/host/xhci-mtk.c136
-rw-r--r--drivers/usb/host/xhci-mtk.h5
-rw-r--r--drivers/usb/host/xhci-plat.c11
-rw-r--r--drivers/usb/host/xhci-ring.c21
-rw-r--r--drivers/usb/host/xhci-trace.h5
-rw-r--r--drivers/usb/host/xhci.c88
-rw-r--r--drivers/usb/host/xhci.h20
-rw-r--r--drivers/usb/misc/usbtest.c2
-rw-r--r--drivers/usb/phy/phy-isp1301-omap.c11
-rw-r--r--drivers/usb/phy/phy-mv-usb.c17
-rw-r--r--drivers/usb/storage/option_ms.c4
-rw-r--r--drivers/usb/typec/Kconfig25
-rw-r--r--drivers/usb/typec/Makefile3
-rw-r--r--drivers/usb/typec/fusb302/Kconfig (renamed from drivers/staging/typec/fusb302/Kconfig)0
-rw-r--r--drivers/usb/typec/fusb302/Makefile (renamed from drivers/staging/typec/fusb302/Makefile)0
-rw-r--r--drivers/usb/typec/fusb302/fusb302.c (renamed from drivers/staging/typec/fusb302/fusb302.c)4
-rw-r--r--drivers/usb/typec/fusb302/fusb302_reg.h (renamed from drivers/staging/typec/fusb302/fusb302_reg.h)0
-rw-r--r--drivers/usb/typec/tcpm.c (renamed from drivers/staging/typec/tcpm.c)49
-rw-r--r--drivers/usb/typec/tps6598x.c475
-rw-r--r--drivers/usb/typec/typec_wcove.c594
52 files changed, 1973 insertions, 1167 deletions
diff --git a/drivers/staging/typec/Kconfig b/drivers/staging/typec/Kconfig
index 37a0781b0d0c..5359f556d203 100644
--- a/drivers/staging/typec/Kconfig
+++ b/drivers/staging/typec/Kconfig
@@ -1,13 +1,5 @@
menu "USB Power Delivery and Type-C drivers"
-config TYPEC_TCPM
- tristate "USB Type-C Port Controller Manager"
- depends on USB
- select TYPEC
- help
- The Type-C Port Controller Manager provides a USB PD and USB Type-C
- state machine for use with Type-C Port Controllers.
-
if TYPEC_TCPM
config TYPEC_TCPCI
@@ -17,8 +9,6 @@ config TYPEC_TCPCI
help
Type-C Port Controller driver for TCPCI-compliant controller.
-source "drivers/staging/typec/fusb302/Kconfig"
-
endif
endmenu
diff --git a/drivers/staging/typec/Makefile b/drivers/staging/typec/Makefile
index 30a7e29cbc9e..53d649abcb53 100644
--- a/drivers/staging/typec/Makefile
+++ b/drivers/staging/typec/Makefile
@@ -1,3 +1 @@
-obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
-obj-y += fusb302/
diff --git a/drivers/staging/typec/TODO b/drivers/staging/typec/TODO
index bc1f97a2d1bf..53fe2f726c88 100644
--- a/drivers/staging/typec/TODO
+++ b/drivers/staging/typec/TODO
@@ -1,13 +1,3 @@
-tcpm:
-- Add documentation (at the very least for the API to low level drivers)
-- Split PD code into separate file
-- Check if it makes sense to use tracepoints instead of debugfs for debug logs
-- Implement Alternate Mode handling
-- Address "#if 0" code if not addressed with the above
-- Validate all comments marked with "XXX"; either address or remove comments
-- Add support for USB PD 3.0. While not mandatory, at least fast role swap
- as well as authentication support would be very desirable.
-
tcpci:
- Test with real hardware
diff --git a/drivers/staging/typec/fusb302/TODO b/drivers/staging/typec/fusb302/TODO
deleted file mode 100644
index 19b466eb585d..000000000000
--- a/drivers/staging/typec/fusb302/TODO
+++ /dev/null
@@ -1,10 +0,0 @@
-fusb302:
-- Find a better logging scheme, at least not having the same debugging/logging
- code replicated here and in tcpm
-- Find a non-hacky way to coordinate between PM and I2C access
-- Documentation? The FUSB302 datasheet provides information on the chip to help
- understand the code. But it may still be helpful to have a documentation.
-- We may want to replace the "fcs,max-snk-microvolt", "fcs,max-snk-microamp",
- "fcs,max-snk-microwatt" and "fcs,operating-snk-microwatt" device(tree)
- properties with properties which are part of a generic type-c controller
- devicetree binding.
diff --git a/drivers/staging/typec/pd.h b/drivers/staging/typec/pd.h
deleted file mode 100644
index 30b32ad72acd..000000000000
--- a/drivers/staging/typec/pd.h
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright 2015-2017 Google, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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 __LINUX_USB_PD_H
-#define __LINUX_USB_PD_H
-
-#include <linux/types.h>
-#include <linux/usb/typec.h>
-
-/* USB PD Messages */
-enum pd_ctrl_msg_type {
- /* 0 Reserved */
- PD_CTRL_GOOD_CRC = 1,
- PD_CTRL_GOTO_MIN = 2,
- PD_CTRL_ACCEPT = 3,
- PD_CTRL_REJECT = 4,
- PD_CTRL_PING = 5,
- PD_CTRL_PS_RDY = 6,
- PD_CTRL_GET_SOURCE_CAP = 7,
- PD_CTRL_GET_SINK_CAP = 8,
- PD_CTRL_DR_SWAP = 9,
- PD_CTRL_PR_SWAP = 10,
- PD_CTRL_VCONN_SWAP = 11,
- PD_CTRL_WAIT = 12,
- PD_CTRL_SOFT_RESET = 13,
- /* 14-15 Reserved */
-};
-
-enum pd_data_msg_type {
- /* 0 Reserved */
- PD_DATA_SOURCE_CAP = 1,
- PD_DATA_REQUEST = 2,
- PD_DATA_BIST = 3,
- PD_DATA_SINK_CAP = 4,
- /* 5-14 Reserved */
- PD_DATA_VENDOR_DEF = 15,
-};
-
-#define PD_REV10 0x0
-#define PD_REV20 0x1
-
-#define PD_HEADER_CNT_SHIFT 12
-#define PD_HEADER_CNT_MASK 0x7
-#define PD_HEADER_ID_SHIFT 9
-#define PD_HEADER_ID_MASK 0x7
-#define PD_HEADER_PWR_ROLE BIT(8)
-#define PD_HEADER_REV_SHIFT 6
-#define PD_HEADER_REV_MASK 0x3
-#define PD_HEADER_DATA_ROLE BIT(5)
-#define PD_HEADER_TYPE_SHIFT 0
-#define PD_HEADER_TYPE_MASK 0xf
-
-#define PD_HEADER(type, pwr, data, id, cnt) \
- ((((type) & PD_HEADER_TYPE_MASK) << PD_HEADER_TYPE_SHIFT) | \
- ((pwr) == TYPEC_SOURCE ? PD_HEADER_PWR_ROLE : 0) | \
- ((data) == TYPEC_HOST ? PD_HEADER_DATA_ROLE : 0) | \
- (PD_REV20 << PD_HEADER_REV_SHIFT) | \
- (((id) & PD_HEADER_ID_MASK) << PD_HEADER_ID_SHIFT) | \
- (((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT))
-
-#define PD_HEADER_LE(type, pwr, data, id, cnt) \
- cpu_to_le16(PD_HEADER((type), (pwr), (data), (id), (cnt)))
-
-static inline unsigned int pd_header_cnt(u16 header)
-{
- return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK;
-}
-
-static inline unsigned int pd_header_cnt_le(__le16 header)
-{
- return pd_header_cnt(le16_to_cpu(header));
-}
-
-static inline unsigned int pd_header_type(u16 header)
-{
- return (header >> PD_HEADER_TYPE_SHIFT) & PD_HEADER_TYPE_MASK;
-}
-
-static inline unsigned int pd_header_type_le(__le16 header)
-{
- return pd_header_type(le16_to_cpu(header));
-}
-
-static inline unsigned int pd_header_msgid(u16 header)
-{
- return (header >> PD_HEADER_ID_SHIFT) & PD_HEADER_ID_MASK;
-}
-
-static inline unsigned int pd_header_msgid_le(__le16 header)
-{
- return pd_header_msgid(le16_to_cpu(header));
-}
-
-#define PD_MAX_PAYLOAD 7
-
-struct pd_message {
- __le16 header;
- __le32 payload[PD_MAX_PAYLOAD];
-} __packed;
-
-/* PDO: Power Data Object */
-#define PDO_MAX_OBJECTS 7
-
-enum pd_pdo_type {
- PDO_TYPE_FIXED = 0,
- PDO_TYPE_BATT = 1,
- PDO_TYPE_VAR = 2,
-};
-
-#define PDO_TYPE_SHIFT 30
-#define PDO_TYPE_MASK 0x3
-
-#define PDO_TYPE(t) ((t) << PDO_TYPE_SHIFT)
-
-#define PDO_VOLT_MASK 0x3ff
-#define PDO_CURR_MASK 0x3ff
-#define PDO_PWR_MASK 0x3ff
-
-#define PDO_FIXED_DUAL_ROLE BIT(29) /* Power role swap supported */
-#define PDO_FIXED_SUSPEND BIT(28) /* USB Suspend supported (Source) */
-#define PDO_FIXED_HIGHER_CAP BIT(28) /* Requires more than vSafe5V (Sink) */
-#define PDO_FIXED_EXTPOWER BIT(27) /* Externally powered */
-#define PDO_FIXED_USB_COMM BIT(26) /* USB communications capable */
-#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap supported */
-#define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */
-#define PDO_FIXED_CURR_SHIFT 0 /* 10mA units */
-
-#define PDO_FIXED_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT)
-#define PDO_FIXED_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT)
-
-#define PDO_FIXED(mv, ma, flags) \
- (PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \
- PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma))
-
-#define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */
-#define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */
-#define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */
-
-#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MIN_VOLT_SHIFT)
-#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MAX_VOLT_SHIFT)
-#define PDO_BATT_MAX_POWER(mw) ((((mw) / 250) & PDO_PWR_MASK) << PDO_BATT_MAX_PWR_SHIFT)
-
-#define PDO_BATT(min_mv, max_mv, max_mw) \
- (PDO_TYPE(PDO_TYPE_BATT) | PDO_BATT_MIN_VOLT(min_mv) | \
- PDO_BATT_MAX_VOLT(max_mv) | PDO_BATT_MAX_POWER(max_mw))
-
-#define PDO_VAR_MAX_VOLT_SHIFT 20 /* 50mV units */
-#define PDO_VAR_MIN_VOLT_SHIFT 10 /* 50mV units */
-#define PDO_VAR_MAX_CURR_SHIFT 0 /* 10mA units */
-
-#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MIN_VOLT_SHIFT)
-#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MAX_VOLT_SHIFT)
-#define PDO_VAR_MAX_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_VAR_MAX_CURR_SHIFT)
-
-#define PDO_VAR(min_mv, max_mv, max_ma) \
- (PDO_TYPE(PDO_TYPE_VAR) | PDO_VAR_MIN_VOLT(min_mv) | \
- PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma))
-
-static inline enum pd_pdo_type pdo_type(u32 pdo)
-{
- return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK;
-}
-
-static inline unsigned int pdo_fixed_voltage(u32 pdo)
-{
- return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
-}
-
-static inline unsigned int pdo_min_voltage(u32 pdo)
-{
- return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
-}
-
-static inline unsigned int pdo_max_voltage(u32 pdo)
-{
- return ((pdo >> PDO_VAR_MAX_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
-}
-
-static inline unsigned int pdo_max_current(u32 pdo)
-{
- return ((pdo >> PDO_VAR_MAX_CURR_SHIFT) & PDO_CURR_MASK) * 10;
-}
-
-static inline unsigned int pdo_max_power(u32 pdo)
-{
- return ((pdo >> PDO_BATT_MAX_PWR_SHIFT) & PDO_PWR_MASK) * 250;
-}
-
-/* RDO: Request Data Object */
-#define RDO_OBJ_POS_SHIFT 28
-#define RDO_OBJ_POS_MASK 0x7
-#define RDO_GIVE_BACK BIT(27) /* Supports reduced operating current */
-#define RDO_CAP_MISMATCH BIT(26) /* Not satisfied by source caps */
-#define RDO_USB_COMM BIT(25) /* USB communications capable */
-#define RDO_NO_SUSPEND BIT(24) /* USB Suspend not supported */
-
-#define RDO_PWR_MASK 0x3ff
-#define RDO_CURR_MASK 0x3ff
-
-#define RDO_FIXED_OP_CURR_SHIFT 10
-#define RDO_FIXED_MAX_CURR_SHIFT 0
-
-#define RDO_OBJ(idx) (((idx) & RDO_OBJ_POS_MASK) << RDO_OBJ_POS_SHIFT)
-
-#define PDO_FIXED_OP_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_OP_CURR_SHIFT)
-#define PDO_FIXED_MAX_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_MAX_CURR_SHIFT)
-
-#define RDO_FIXED(idx, op_ma, max_ma, flags) \
- (RDO_OBJ(idx) | (flags) | \
- PDO_FIXED_OP_CURR(op_ma) | PDO_FIXED_MAX_CURR(max_ma))
-
-#define RDO_BATT_OP_PWR_SHIFT 10 /* 250mW units */
-#define RDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */
-
-#define RDO_BATT_OP_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_OP_PWR_SHIFT)
-#define RDO_BATT_MAX_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_MAX_PWR_SHIFT)
-
-#define RDO_BATT(idx, op_mw, max_mw, flags) \
- (RDO_OBJ(idx) | (flags) | \
- RDO_BATT_OP_PWR(op_mw) | RDO_BATT_MAX_PWR(max_mw))
-
-static inline unsigned int rdo_index(u32 rdo)
-{
- return (rdo >> RDO_OBJ_POS_SHIFT) & RDO_OBJ_POS_MASK;
-}
-
-static inline unsigned int rdo_op_current(u32 rdo)
-{
- return ((rdo >> RDO_FIXED_OP_CURR_SHIFT) & RDO_CURR_MASK) * 10;
-}
-
-static inline unsigned int rdo_max_current(u32 rdo)
-{
- return ((rdo >> RDO_FIXED_MAX_CURR_SHIFT) &
- RDO_CURR_MASK) * 10;
-}
-
-static inline unsigned int rdo_op_power(u32 rdo)
-{
- return ((rdo >> RDO_BATT_OP_PWR_SHIFT) & RDO_PWR_MASK) * 250;
-}
-
-static inline unsigned int rdo_max_power(u32 rdo)
-{
- return ((rdo >> RDO_BATT_MAX_PWR_SHIFT) & RDO_PWR_MASK) * 250;
-}
-
-/* USB PD timers and counters */
-#define PD_T_NO_RESPONSE 5000 /* 4.5 - 5.5 seconds */
-#define PD_T_DB_DETECT 10000 /* 10 - 15 seconds */
-#define PD_T_SEND_SOURCE_CAP 150 /* 100 - 200 ms */
-#define PD_T_SENDER_RESPONSE 60 /* 24 - 30 ms, relaxed */
-#define PD_T_SOURCE_ACTIVITY 45
-#define PD_T_SINK_ACTIVITY 135
-#define PD_T_SINK_WAIT_CAP 240
-#define PD_T_PS_TRANSITION 500
-#define PD_T_SRC_TRANSITION 35
-#define PD_T_DRP_SNK 40
-#define PD_T_DRP_SRC 30
-#define PD_T_PS_SOURCE_OFF 920
-#define PD_T_PS_SOURCE_ON 480
-#define PD_T_PS_HARD_RESET 30
-#define PD_T_SRC_RECOVER 760
-#define PD_T_SRC_RECOVER_MAX 1000
-#define PD_T_SRC_TURN_ON 275
-#define PD_T_SAFE_0V 650
-#define PD_T_VCONN_SOURCE_ON 100
-#define PD_T_SINK_REQUEST 100 /* 100 ms minimum */
-#define PD_T_ERROR_RECOVERY 100 /* minimum 25 is insufficient */
-#define PD_T_SRCSWAPSTDBY 625 /* Maximum of 650ms */
-#define PD_T_NEWSRC 250 /* Maximum of 275ms */
-
-#define PD_T_DRP_TRY 100 /* 75 - 150 ms */
-#define PD_T_DRP_TRYWAIT 600 /* 400 - 800 ms */
-
-#define PD_T_CC_DEBOUNCE 200 /* 100 - 200 ms */
-#define PD_T_PD_DEBOUNCE 20 /* 10 - 20 ms */
-
-#define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP)
-#define PD_N_HARD_RESET_COUNT 2
-
-#endif /* __LINUX_USB_PD_H */
diff --git a/drivers/staging/typec/pd_bdo.h b/drivers/staging/typec/pd_bdo.h
deleted file mode 100644
index 90b94d9fea5d..000000000000
--- a/drivers/staging/typec/pd_bdo.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2015-2017 Google, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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 __LINUX_USB_PD_BDO_H
-#define __LINUX_USB_PD_BDO_H
-
-/* BDO : BIST Data Object */
-#define BDO_MODE_RECV (0 << 28)
-#define BDO_MODE_TRANSMIT (1 << 28)
-#define BDO_MODE_COUNTERS (2 << 28)
-#define BDO_MODE_CARRIER0 (3 << 28)
-#define BDO_MODE_CARRIER1 (4 << 28)
-#define BDO_MODE_CARRIER2 (5 << 28)
-#define BDO_MODE_CARRIER3 (6 << 28)
-#define BDO_MODE_EYE (7 << 28)
-#define BDO_MODE_TESTDATA (8 << 28)
-
-#define BDO_MODE_MASK(mode) ((mode) & 0xf0000000)
-
-#endif
diff --git a/drivers/staging/typec/pd_vdo.h b/drivers/staging/typec/pd_vdo.h
deleted file mode 100644
index d92259f8de0a..000000000000
--- a/drivers/staging/typec/pd_vdo.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright 2015-2017 Google, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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 __LINUX_USB_PD_VDO_H
-#define __LINUX_USB_PD_VDO_H
-
-#include "pd.h"
-
-/*
- * VDO : Vendor Defined Message Object
- * VDM object is minimum of VDM header + 6 additional data objects.
- */
-
-#define VDO_MAX_OBJECTS 6
-#define VDO_MAX_SIZE (VDO_MAX_OBJECTS + 1)
-
-/*
- * VDM header
- * ----------
- * <31:16> :: SVID
- * <15> :: VDM type ( 1b == structured, 0b == unstructured )
- * <14:13> :: Structured VDM version (can only be 00 == 1.0 currently)
- * <12:11> :: reserved
- * <10:8> :: object position (1-7 valid ... used for enter/exit mode only)
- * <7:6> :: command type (SVDM only?)
- * <5> :: reserved (SVDM), command type (UVDM)
- * <4:0> :: command
- */
-#define VDO(vid, type, custom) \
- (((vid) << 16) | \
- ((type) << 15) | \
- ((custom) & 0x7FFF))
-
-#define VDO_SVDM_TYPE (1 << 15)
-#define VDO_SVDM_VERS(x) ((x) << 13)
-#define VDO_OPOS(x) ((x) << 8)
-#define VDO_CMDT(x) ((x) << 6)
-#define VDO_OPOS_MASK VDO_OPOS(0x7)
-#define VDO_CMDT_MASK VDO_CMDT(0x3)
-
-#define CMDT_INIT 0
-#define CMDT_RSP_ACK 1
-#define CMDT_RSP_NAK 2
-#define CMDT_RSP_BUSY 3
-
-/* reserved for SVDM ... for Google UVDM */
-#define VDO_SRC_INITIATOR (0 << 5)
-#define VDO_SRC_RESPONDER (1 << 5)
-
-#define CMD_DISCOVER_IDENT 1
-#define CMD_DISCOVER_SVID 2
-#define CMD_DISCOVER_MODES 3
-#define CMD_ENTER_MODE 4
-#define CMD_EXIT_MODE 5
-#define CMD_ATTENTION 6
-
-#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f))
-
-/* ChromeOS specific commands */
-#define VDO_CMD_VERSION VDO_CMD_VENDOR(0)
-#define VDO_CMD_SEND_INFO VDO_CMD_VENDOR(1)
-#define VDO_CMD_READ_INFO VDO_CMD_VENDOR(2)
-#define VDO_CMD_REBOOT VDO_CMD_VENDOR(5)
-#define VDO_CMD_FLASH_ERASE VDO_CMD_VENDOR(6)
-#define VDO_CMD_FLASH_WRITE VDO_CMD_VENDOR(7)
-#define VDO_CMD_ERASE_SIG VDO_CMD_VENDOR(8)
-#define VDO_CMD_PING_ENABLE VDO_CMD_VENDOR(10)
-#define VDO_CMD_CURRENT VDO_CMD_VENDOR(11)
-#define VDO_CMD_FLIP VDO_CMD_VENDOR(12)
-#define VDO_CMD_GET_LOG VDO_CMD_VENDOR(13)
-#define VDO_CMD_CCD_EN VDO_CMD_VENDOR(14)
-
-#define PD_VDO_VID(vdo) ((vdo) >> 16)
-#define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1)
-#define PD_VDO_OPOS(vdo) (((vdo) >> 8) & 0x7)
-#define PD_VDO_CMD(vdo) ((vdo) & 0x1f)
-#define PD_VDO_CMDT(vdo) (((vdo) >> 6) & 0x3)
-
-/*
- * SVDM Identity request -> response
- *
- * Request is simply properly formatted SVDM header
- *
- * Response is 4 data objects:
- * [0] :: SVDM header
- * [1] :: Identitiy header
- * [2] :: Cert Stat VDO
- * [3] :: (Product | Cable) VDO
- * [4] :: AMA VDO
- *
- */
-#define VDO_INDEX_HDR 0
-#define VDO_INDEX_IDH 1
-#define VDO_INDEX_CSTAT 2
-#define VDO_INDEX_CABLE 3
-#define VDO_INDEX_PRODUCT 3
-#define VDO_INDEX_AMA 4
-
-/*
- * SVDM Identity Header
- * --------------------
- * <31> :: data capable as a USB host
- * <30> :: data capable as a USB device
- * <29:27> :: product type
- * <26> :: modal operation supported (1b == yes)
- * <25:16> :: Reserved, Shall be set to zero
- * <15:0> :: USB-IF assigned VID for this cable vendor
- */
-#define IDH_PTYPE_UNDEF 0
-#define IDH_PTYPE_HUB 1
-#define IDH_PTYPE_PERIPH 2
-#define IDH_PTYPE_PCABLE 3
-#define IDH_PTYPE_ACABLE 4
-#define IDH_PTYPE_AMA 5
-
-#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \
- ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \
- | (is_modal) << 26 | ((vid) & 0xffff))
-
-#define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7)
-#define PD_IDH_VID(vdo) ((vdo) & 0xffff)
-#define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26))
-
-/*
- * Cert Stat VDO
- * -------------
- * <31:0> : USB-IF assigned XID for this cable
- */
-#define PD_CSTAT_XID(vdo) (vdo)
-
-/*
- * Product VDO
- * -----------
- * <31:16> : USB Product ID
- * <15:0> : USB bcdDevice
- */
-#define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff))
-#define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff)
-
-/*
- * Cable VDO
- * ---------
- * <31:28> :: Cable HW version
- * <27:24> :: Cable FW version
- * <23:20> :: Reserved, Shall be set to zero
- * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C)
- * <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle)
- * <16:13> :: cable latency (0001 == <10ns(~1m length))
- * <12:11> :: cable termination type (11b == both ends active VCONN req)
- * <10> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
- * <9> :: SSTX2 Directionality support
- * <8> :: SSRX1 Directionality support
- * <7> :: SSRX2 Directionality support
- * <6:5> :: Vbus current handling capability
- * <4> :: Vbus through cable (0b == no, 1b == yes)
- * <3> :: SOP" controller present? (0b == no, 1b == yes)
- * <2:0> :: USB SS Signaling support
- */
-#define CABLE_ATYPE 0
-#define CABLE_BTYPE 1
-#define CABLE_CTYPE 2
-#define CABLE_PLUG 0
-#define CABLE_RECEPTACLE 1
-#define CABLE_CURR_1A5 0
-#define CABLE_CURR_3A 1
-#define CABLE_CURR_5A 2
-#define CABLE_USBSS_U2_ONLY 0
-#define CABLE_USBSS_U31_GEN1 1
-#define CABLE_USBSS_U31_GEN2 2
-#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d, tx2d, rx1d, rx2d, cur,\
- vps, sopp, usbss) \
- (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18 \
- | (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 \
- | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \
- | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \
- | ((usbss) & 0x7))
-
-/*
- * AMA VDO
- * ---------
- * <31:28> :: Cable HW version
- * <27:24> :: Cable FW version
- * <23:12> :: Reserved, Shall be set to zero
- * <11> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
- * <10> :: SSTX2 Directionality support
- * <9> :: SSRX1 Directionality support
- * <8> :: SSRX2 Directionality support
- * <7:5> :: Vconn power
- * <4> :: Vconn power required
- * <3> :: Vbus power required
- * <2:0> :: USB SS Signaling support
- */
-#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \
- (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 \
- | (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8 \
- | ((vcpwr) & 0x7) << 5 | (vcr) << 4 | (vbr) << 3 \
- | ((usbss) & 0x7))
-
-#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1)
-#define PD_VDO_AMA_VBUS_REQ(vdo) (((vdo) >> 3) & 1)
-
-#define AMA_VCONN_PWR_1W 0
-#define AMA_VCONN_PWR_1W5 1
-#define AMA_VCONN_PWR_2W 2
-#define AMA_VCONN_PWR_3W 3
-#define AMA_VCONN_PWR_4W 4
-#define AMA_VCONN_PWR_5W 5
-#define AMA_VCONN_PWR_6W 6
-#define AMA_USBSS_U2_ONLY 0
-#define AMA_USBSS_U31_GEN1 1
-#define AMA_USBSS_U31_GEN2 2
-#define AMA_USBSS_BBONLY 3
-
-/*
- * SVDM Discover SVIDs request -> response
- *
- * Request is properly formatted VDM Header with discover SVIDs command.
- * Response is a set of SVIDs of all all supported SVIDs with all zero's to
- * mark the end of SVIDs. If more than 12 SVIDs are supported command SHOULD be
- * repeated.
- */
-#define VDO_SVID(svid0, svid1) (((svid0) & 0xffff) << 16 | ((svid1) & 0xffff))
-#define PD_VDO_SVID_SVID0(vdo) ((vdo) >> 16)
-#define PD_VDO_SVID_SVID1(vdo) ((vdo) & 0xffff)
-
-/* USB-IF SIDs */
-#define USB_SID_PD 0xff00 /* power delivery */
-#define USB_SID_DISPLAYPORT 0xff01
-#define USB_SID_MHL 0xff02 /* Mobile High-Definition Link */
-
-/* VDM command timeouts (in ms) */
-
-#define PD_T_VDM_UNSTRUCTURED 500
-#define PD_T_VDM_BUSY 100
-#define PD_T_VDM_WAIT_MODE_E 100
-#define PD_T_VDM_SNDR_RSP 30
-#define PD_T_VDM_E_MODE 25
-#define PD_T_VDM_RCVR_RSP 15
-
-#endif /* __LINUX_USB_PD_VDO_H */
diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c
index df72d8b01e73..4636804ea1a4 100644
--- a/drivers/staging/typec/tcpci.c
+++ b/drivers/staging/typec/tcpci.c
@@ -20,11 +20,11 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/regmap.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/tcpm.h>
#include <linux/usb/typec.h>
-#include "pd.h"
#include "tcpci.h"
-#include "tcpm.h"
#define PD_RETRY_COUNT 3
diff --git a/drivers/staging/typec/tcpm.h b/drivers/staging/typec/tcpm.h
deleted file mode 100644
index 7e9a6b7b5cd6..000000000000
--- a/drivers/staging/typec/tcpm.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2015-2017 Google, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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 __LINUX_USB_TCPM_H
-#define __LINUX_USB_TCPM_H
-
-#include <linux/bitops.h>
-#include <linux/usb/typec.h>
-#include "pd.h"
-
-enum typec_cc_status {
- TYPEC_CC_OPEN,
- TYPEC_CC_RA,
- TYPEC_CC_RD,
- TYPEC_CC_RP_DEF,
- TYPEC_CC_RP_1_5,
- TYPEC_CC_RP_3_0,
-};
-
-enum typec_cc_polarity {
- TYPEC_POLARITY_CC1,
- TYPEC_POLARITY_CC2,
-};
-
-/* Time to wait for TCPC to complete transmit */
-#define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */
-#define PD_ROLE_SWAP_TIMEOUT (MSEC_PER_SEC * 10)
-
-enum tcpm_transmit_status {
- TCPC_TX_SUCCESS = 0,
- TCPC_TX_DISCARDED = 1,
- TCPC_TX_FAILED = 2,
-};
-
-enum tcpm_transmit_type {
- TCPC_TX_SOP = 0,
- TCPC_TX_SOP_PRIME = 1,
- TCPC_TX_SOP_PRIME_PRIME = 2,
- TCPC_TX_SOP_DEBUG_PRIME = 3,
- TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4,
- TCPC_TX_HARD_RESET = 5,
- TCPC_TX_CABLE_RESET = 6,
- TCPC_TX_BIST_MODE_2 = 7
-};
-
-struct tcpc_config {
- const u32 *src_pdo;
- unsigned int nr_src_pdo;
-
- const u32 *snk_pdo;
- unsigned int nr_snk_pdo;
-
- const u32 *snk_vdo;
- unsigned int nr_snk_vdo;
-
- unsigned int max_snk_mv;
- unsigned int max_snk_ma;
- unsigned int max_snk_mw;
- unsigned int operating_snk_mw;
-
- enum typec_port_type type;
- enum typec_role default_role;
- bool try_role_hw; /* try.{src,snk} implemented in hardware */
-
- const struct typec_altmode_desc *alt_modes;
-};
-
-enum tcpc_usb_switch {
- TCPC_USB_SWITCH_CONNECT,
- TCPC_USB_SWITCH_DISCONNECT,
- TCPC_USB_SWITCH_RESTORE, /* TODO FIXME */
-};
-
-/* Mux state attributes */
-#define TCPC_MUX_USB_ENABLED BIT(0) /* USB enabled */
-#define TCPC_MUX_DP_ENABLED BIT(1) /* DP enabled */
-#define TCPC_MUX_POLARITY_INVERTED BIT(2) /* Polarity inverted */
-
-/* Mux modes, decoded to attributes */
-enum tcpc_mux_mode {
- TYPEC_MUX_NONE = 0, /* Open switch */
- TYPEC_MUX_USB = TCPC_MUX_USB_ENABLED, /* USB only */
- TYPEC_MUX_DP = TCPC_MUX_DP_ENABLED, /* DP only */
- TYPEC_MUX_DOCK = TCPC_MUX_USB_ENABLED | /* Both USB and DP */
- TCPC_MUX_DP_ENABLED,
-};
-
-struct tcpc_mux_dev {
- int (*set)(struct tcpc_mux_dev *dev, enum tcpc_mux_mode mux_mode,
- enum tcpc_usb_switch usb_config,
- enum typec_cc_polarity polarity);
- bool dfp_only;
- void *priv_data;
-};
-
-struct tcpc_dev {
- const struct tcpc_config *config;
-
- int (*init)(struct tcpc_dev *dev);
- int (*get_vbus)(struct tcpc_dev *dev);
- /*
- * This optional callback gets called by the tcpm core when configured
- * as a snk and cc=Rp-def. This allows the tcpm to provide a fallback
- * current-limit detection method for the cc=Rp-def case. E.g. some
- * tcpcs may include BC1.2 charger detection and use that in this case.
- */
- int (*get_current_limit)(struct tcpc_dev *dev);
- int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc);
- int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1,
- enum typec_cc_status *cc2);
- int (*set_polarity)(struct tcpc_dev *dev,
- enum typec_cc_polarity polarity);
- int (*set_vconn)(struct tcpc_dev *dev, bool on);
- int (*set_vbus)(struct tcpc_dev *dev, bool on, bool charge);
- int (*set_current_limit)(struct tcpc_dev *dev, u32 max_ma, u32 mv);
- int (*set_pd_rx)(struct tcpc_dev *dev, bool on);
- int (*set_roles)(struct tcpc_dev *dev, bool attached,
- enum typec_role role, enum typec_data_role data);
- int (*start_drp_toggling)(struct tcpc_dev *dev,
- enum typec_cc_status cc);
- int (*try_role)(struct tcpc_dev *dev, int role);
- int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type,
- const struct pd_message *msg);
- struct tcpc_mux_dev *mux;
-};
-
-struct tcpm_port;
-
-struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc);
-void tcpm_unregister_port(struct tcpm_port *port);
-
-void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo,
- unsigned int nr_pdo);
-void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
- unsigned int nr_pdo,
- unsigned int max_snk_mv,
- unsigned int max_snk_ma,
- unsigned int max_snk_mw,
- unsigned int operating_snk_mw);
-
-void tcpm_vbus_change(struct tcpm_port *port);
-void tcpm_cc_change(struct tcpm_port *port);
-void tcpm_pd_receive(struct tcpm_port *port,
- const struct pd_message *msg);
-void tcpm_pd_transmit_complete(struct tcpm_port *port,
- enum tcpm_transmit_status status);
-void tcpm_pd_hard_reset(struct tcpm_port *port);
-void tcpm_tcpc_reset(struct tcpm_port *port);
-
-#endif /* __LINUX_USB_TCPM_H */
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 5160a4a966b3..600a670b4feb 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -570,10 +570,8 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
{
struct timer_list timer;
- init_timer(&timer);
+ setup_timer(&timer, cxacru_timeout_kill, (unsigned long)urb);
timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT);
- timer.data = (unsigned long) urb;
- timer.function = cxacru_timeout_kill;
add_timer(&timer);
wait_for_completion(done);
del_timer_sync(&timer);
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
index 3676adb40d89..091db9b281f5 100644
--- a/drivers/usb/atm/speedtch.c
+++ b/drivers/usb/atm/speedtch.c
@@ -874,16 +874,13 @@ static int speedtch_bind(struct usbatm_data *usbatm,
usbatm->flags |= (use_isoc ? UDSL_USE_ISOC : 0);
INIT_WORK(&instance->status_check_work, speedtch_check_status);
- init_timer(&instance->status_check_timer);
-
- instance->status_check_timer.function = speedtch_status_poll;
- instance->status_check_timer.data = (unsigned long)instance;
+ setup_timer(&instance->status_check_timer, speedtch_status_poll,
+ (unsigned long)instance);
instance->last_status = 0xff;
instance->poll_delay = MIN_POLL_DELAY;
- init_timer(&instance->resubmit_timer);
- instance->resubmit_timer.function = speedtch_resubmit_int;
- instance->resubmit_timer.data = (unsigned long)instance;
+ setup_timer(&instance->resubmit_timer, speedtch_resubmit_int,
+ (unsigned long)instance);
instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index eb87a259d55c..353993f983c8 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1461,6 +1461,7 @@ static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
int usb_suspend(struct device *dev, pm_message_t msg)
{
struct usb_device *udev = to_usb_device(dev);
+ int r;
unbind_no_pm_drivers_interfaces(udev);
@@ -1469,7 +1470,14 @@ int usb_suspend(struct device *dev, pm_message_t msg)
* so we may still need to unbind and rebind upon resume
*/
choose_wakeup(udev, msg);
- return usb_suspend_both(udev, msg);
+ r = usb_suspend_both(udev, msg);
+ if (r)
+ return r;
+
+ if (udev->quirks & USB_QUIRK_DISCONNECT_SUSPEND)
+ usb_port_disable(udev);
+
+ return 0;
}
/* The device lock is held by the PM core */
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 75ad6718858c..67aa3d039b9b 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2558,9 +2558,7 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
hcd->self.bus_name = bus_name;
hcd->self.uses_dma = (sysdev->dma_mask != NULL);
- init_timer(&hcd->rh_timer);
- hcd->rh_timer.function = rh_timer_func;
- hcd->rh_timer.data = (unsigned long) hcd;
+ setup_timer(&hcd->rh_timer, rh_timer_func, (unsigned long)hcd);
#ifdef CONFIG_PM
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index e9ce6bb0b22d..196a0a5540ed 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -4183,6 +4183,19 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
return ret;
}
+/*
+ * usb_port_disable - disable a usb device's upstream port
+ * @udev: device to disable
+ * Context: @udev locked, must be able to sleep.
+ *
+ * Disables a USB device that isn't in active use.
+ */
+int usb_port_disable(struct usb_device *udev)
+{
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
+
+ return hub_port_disable(hub, udev->portnum, 0);
+}
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index a6aaf2f193a4..3f08a9be65a8 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -203,6 +203,12 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x10d6, 0x2200), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
+ /* Huawei 4G LTE module */
+ { USB_DEVICE(0x12d1, 0x15bb), .driver_info =
+ USB_QUIRK_DISCONNECT_SUSPEND },
+ { USB_DEVICE(0x12d1, 0x15c3), .driver_info =
+ USB_QUIRK_DISCONNECT_SUSPEND },
+
/* SKYMEDI USB_DRIVE */
{ USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index d930bfda4010..fa5d47da36d2 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -654,7 +654,8 @@ static int add_power_attributes(struct device *dev)
if (udev->usb2_hw_lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb2_hardware_lpm_attr_group);
- if (udev->speed == USB_SPEED_SUPER &&
+ if ((udev->speed == USB_SPEED_SUPER ||
+ udev->speed == USB_SPEED_SUPER_PLUS) &&
udev->lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb3_hardware_lpm_attr_group);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index dc6949248823..f71890a2db4e 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -73,6 +73,7 @@ extern void usb_hub_cleanup(void);
extern int usb_major_init(void);
extern void usb_major_cleanup(void);
extern int usb_device_supports_lpm(struct usb_device *udev);
+extern int usb_port_disable(struct usb_device *udev);
#ifdef CONFIG_PM
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index f04e91ef9e7c..8f0f69783c8b 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -2433,9 +2433,7 @@ static DEVICE_ATTR_RO(urbs);
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
- init_timer(&dum_hcd->timer);
- dum_hcd->timer.function = dummy_timer;
- dum_hcd->timer.data = (unsigned long)dum_hcd;
+ setup_timer(&dum_hcd->timer, dummy_timer, (unsigned long)dum_hcd);
dum_hcd->rh_state = DUMMY_RH_RUNNING;
dum_hcd->stream_en_ep = 0;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
@@ -2464,9 +2462,7 @@ static int dummy_start(struct usb_hcd *hcd)
return dummy_start_ss(dum_hcd);
spin_lock_init(&dum_hcd->dum->lock);
- init_timer(&dum_hcd->timer);
- dum_hcd->timer.function = dummy_timer;
- dum_hcd->timer.data = (unsigned long)dum_hcd;
+ setup_timer(&dum_hcd->timer, dummy_timer, (unsigned long)dum_hcd);
dum_hcd->rh_state = DUMMY_RH_RUNNING;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index 46ce7bc15f2b..3b8dbed7e2e9 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1592,9 +1592,7 @@ static int m66592_probe(struct platform_device *pdev)
m66592->gadget.max_speed = USB_SPEED_HIGH;
m66592->gadget.name = udc_name;
- init_timer(&m66592->timer);
- m66592->timer.function = m66592_timer;
- m66592->timer.data = (unsigned long)m66592;
+ setup_timer(&m66592->timer, m66592_timer, (unsigned long)m66592);
m66592->reg = reg;
ret = request_irq(ires->start, m66592_irq, IRQF_SHARED,
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index a238da906115..974b778e033c 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -2417,9 +2417,7 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
gpio_direction_output(dev->mach->gpio_pullup, 0);
}
- init_timer(&dev->timer);
- dev->timer.function = udc_watchdog;
- dev->timer.data = (unsigned long) dev;
+ setup_timer(&dev->timer, udc_watchdog, (unsigned long)dev);
the_controller = dev;
platform_set_drvdata(pdev, dev);
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index 118ad70f1af0..bb844b94df10 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1877,9 +1877,7 @@ static int r8a66597_probe(struct platform_device *pdev)
r8a66597->gadget.max_speed = USB_SPEED_HIGH;
r8a66597->gadget.name = udc_name;
- init_timer(&r8a66597->timer);
- r8a66597->timer.function = r8a66597_timer;
- r8a66597->timer.data = (unsigned long)r8a66597;
+ setup_timer(&r8a66597->timer, r8a66597_timer, (unsigned long)r8a66597);
r8a66597->reg = reg;
if (r8a66597->pdata->on_chip) {
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 38a165dbf924..2f5e788dd978 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -1733,7 +1733,7 @@ static void udc_soft_reset(struct udc *dev)
}
/* RDE timer callback to set RDE bit */
-static void udc_timer_function(unsigned long v)
+static void udc_timer_function(struct timer_list *unused)
{
u32 tmp;
@@ -1813,7 +1813,7 @@ static void udc_handle_halt_state(struct udc_ep *ep)
}
/* Stall timer callback to poll S bit and set it again after */
-static void udc_pollstall_timer_function(unsigned long v)
+static void udc_pollstall_timer_function(struct timer_list *unused)
{
struct udc_ep *ep;
int halted = 0;
@@ -3067,14 +3067,12 @@ void udc_remove(struct udc *dev)
stop_timer++;
if (timer_pending(&udc_timer))
wait_for_completion(&on_exit);
- if (udc_timer.data)
- del_timer_sync(&udc_timer);
+ del_timer_sync(&udc_timer);
/* remove pollstall timer */
stop_pollstall_timer++;
if (timer_pending(&udc_pollstall_timer))
wait_for_completion(&on_pollstall_exit);
- if (udc_pollstall_timer.data)
- del_timer_sync(&udc_pollstall_timer);
+ del_timer_sync(&udc_pollstall_timer);
udc = NULL;
}
EXPORT_SYMBOL_GPL(udc_remove);
@@ -3164,10 +3162,6 @@ int udc_probe(struct udc *dev)
u32 reg;
int retval;
- /* mark timer as not initialized */
- udc_timer.data = 0;
- udc_pollstall_timer.data = 0;
-
/* device struct setup */
dev->gadget.ops = &udc_ops;
@@ -3207,13 +3201,8 @@ int udc_probe(struct udc *dev)
goto finished;
/* timer init */
- init_timer(&udc_timer);
- udc_timer.function = udc_timer_function;
- udc_timer.data = 1;
- /* timer pollstall init */
- init_timer(&udc_pollstall_timer);
- udc_pollstall_timer.function = udc_pollstall_timer_function;
- udc_pollstall_timer.data = 1;
+ timer_setup(&udc_timer, udc_timer_function, 0);
+ timer_setup(&udc_pollstall_timer, udc_pollstall_timer_function, 0);
/* set SD */
reg = readl(&dev->regs->ctl);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index fa5692dec832..bc09a2e4faeb 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -45,12 +45,12 @@ config USB_XHCI_PLATFORM
If unsure, say N.
config USB_XHCI_MTK
- tristate "xHCI support for Mediatek MT65xx/MT7621"
+ tristate "xHCI support for MediaTek SoCs"
select MFD_SYSCON
depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST
---help---
Say 'Y' to enable the support for the xHCI host controller
- found in Mediatek MT65xx SoCs.
+ found in MediaTek SoCs.
If unsure, say N.
config USB_XHCI_MVEBU
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index cf2691fffcc0..b2a7f058cccb 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -25,6 +25,10 @@ ifneq ($(CONFIG_USB_XHCI_RCAR), )
xhci-plat-hcd-y += xhci-rcar.o
endif
+ifneq ($(CONFIG_DEBUG_FS),)
+ xhci-hcd-y += xhci-debugfs.o
+endif
+
obj-$(CONFIG_USB_WHCI_HCD) += whci/
obj-$(CONFIG_USB_PCI) += pci-quirks.o
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 7440722bfbf0..2a8b9bdc0e57 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -205,7 +205,8 @@ static int __maybe_unused ehci_atmel_drv_resume(struct device *dev)
struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
atmel_start_clock(atmel_ehci);
- return ehci_resume(hcd, false);
+ ehci_resume(hcd, false);
+ return 0;
}
#ifdef CONFIG_OF
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 0ece9a9341e5..928a5aabee02 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -60,6 +60,7 @@
#include <linux/spi/spi.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/of.h>
#include <linux/platform_data/max3421-hcd.h>
@@ -85,6 +86,8 @@
USB_PORT_STAT_C_OVERCURRENT | \
USB_PORT_STAT_C_RESET) << 16)
+#define MAX3421_GPOUT_COUNT 8
+
enum max3421_rh_state {
MAX3421_RH_RESET,
MAX3421_RH_SUSPENDED,
@@ -1672,7 +1675,7 @@ max3421_gpout_set_value(struct usb_hcd *hcd, u8 pin_number, u8 value)
u8 mask, idx;
--pin_number;
- if (pin_number > 7)
+ if (pin_number >= MAX3421_GPOUT_COUNT)
return;
mask = 1u << (pin_number % 4);
@@ -1699,6 +1702,10 @@ max3421_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index,
spin_lock_irqsave(&max3421_hcd->lock, flags);
pdata = spi->dev.platform_data;
+ if (!pdata) {
+ dev_err(&spi->dev, "Device platform data is missing\n");
+ return -EFAULT;
+ }
switch (type_req) {
case ClearHubFeature:
@@ -1832,10 +1839,34 @@ static const struct hc_driver max3421_hcd_desc = {
};
static int
+max3421_of_vbus_en_pin(struct device *dev, struct max3421_hcd_platform_data *pdata)
+{
+ int retval;
+ uint32_t value[2];
+
+ if (!pdata)
+ return -EINVAL;
+
+ retval = of_property_read_u32_array(dev->of_node, "maxim,vbus-en-pin", value, 2);
+ if (retval) {
+ dev_err(dev, "device tree node property 'maxim,vbus-en-pin' is missing\n");
+ return retval;
+ }
+ dev_info(dev, "property 'maxim,vbus-en-pin' value is <%d %d>\n", value[0], value[1]);
+
+ pdata->vbus_gpout = value[0];
+ pdata->vbus_active_level = value[1];
+
+ return 0;
+}
+
+static int
max3421_probe(struct spi_device *spi)
{
+ struct device *dev = &spi->dev;
struct max3421_hcd *max3421_hcd;
struct usb_hcd *hcd = NULL;
+ struct max3421_hcd_platform_data *pdata = NULL;
int retval = -ENOMEM;
if (spi_setup(spi) < 0) {
@@ -1843,6 +1874,42 @@ max3421_probe(struct spi_device *spi)
return -EFAULT;
}
+ if (!spi->irq) {
+ dev_err(dev, "Failed to get SPI IRQ");
+ return -EFAULT;
+ }
+
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
+ pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&spi->dev, "failed to allocate memory for private data\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+ retval = max3421_of_vbus_en_pin(dev, pdata);
+ if (retval)
+ goto error;
+
+ spi->dev.platform_data = pdata;
+ }
+
+ pdata = spi->dev.platform_data;
+ if (!pdata) {
+ dev_err(&spi->dev, "driver configuration data is not provided\n");
+ retval = -EFAULT;
+ goto error;
+ }
+ if (pdata->vbus_active_level > 1) {
+ dev_err(&spi->dev, "vbus active level value %d is out of range (0/1)\n", pdata->vbus_active_level);
+ retval = -EINVAL;
+ goto error;
+ }
+ if (pdata->vbus_gpout < 1 || pdata->vbus_gpout > MAX3421_GPOUT_COUNT) {
+ dev_err(&spi->dev, "vbus gpout value %d is out of range (1..8)\n", pdata->vbus_gpout);
+ retval = -EINVAL;
+ goto error;
+ }
+
hcd = usb_create_hcd(&max3421_hcd_desc, &spi->dev,
dev_name(&spi->dev));
if (!hcd) {
@@ -1885,6 +1952,11 @@ max3421_probe(struct spi_device *spi)
return 0;
error:
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node && pdata) {
+ devm_kfree(&spi->dev, pdata);
+ spi->dev.platform_data = NULL;
+ }
+
if (hcd) {
kfree(max3421_hcd->tx);
kfree(max3421_hcd->rx);
@@ -1923,17 +1995,30 @@ max3421_remove(struct spi_device *spi)
spin_unlock_irqrestore(&max3421_hcd->lock, flags);
+ if (IS_ENABLED(CONFIG_OF) && spi->dev.platform_data) {
+ dev_dbg(&spi->dev, "Freeing platform data structure\n");
+ devm_kfree(&spi->dev, spi->dev.platform_data);
+ spi->dev.platform_data = NULL;
+ }
+
free_irq(spi->irq, hcd);
usb_put_hcd(hcd);
return 0;
}
+static const struct of_device_id max3421_of_match_table[] = {
+ { .compatible = "maxim,max3421", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max3421_of_match_table);
+
static struct spi_driver max3421_driver = {
.probe = max3421_probe,
.remove = max3421_remove,
.driver = {
.name = "max3421-hcd",
+ .of_match_table = of_match_ptr(max3421_of_match_table),
},
};
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index 3a9ea32508df..8758c73215d7 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -42,7 +42,7 @@
#if 0
static void dump_hci_status(struct usb_hcd *hcd, const char *label)
{
- unsigned long status = sa1111_readl(hcd->regs + USB_STATUS);
+ unsigned long status = readl_relaxed(hcd->regs + USB_STATUS);
printk(KERN_DEBUG "%s USB_STATUS = { %s%s%s%s%s}\n", label,
((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
@@ -134,7 +134,7 @@ static int sa1111_start_hc(struct sa1111_dev *dev)
* Configure the power sense and control lines. Place the USB
* host controller in reset.
*/
- sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
+ writel_relaxed(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
dev->mapbase + USB_RESET);
/*
@@ -144,7 +144,7 @@ static int sa1111_start_hc(struct sa1111_dev *dev)
ret = sa1111_enable_device(dev);
if (ret == 0) {
udelay(11);
- sa1111_writel(usb_rst, dev->mapbase + USB_RESET);
+ writel_relaxed(usb_rst, dev->mapbase + USB_RESET);
}
return ret;
@@ -159,8 +159,8 @@ static void sa1111_stop_hc(struct sa1111_dev *dev)
/*
* Put the USB host controller into reset.
*/
- usb_rst = sa1111_readl(dev->mapbase + USB_RESET);
- sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
+ usb_rst = readl_relaxed(dev->mapbase + USB_RESET);
+ writel_relaxed(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
dev->mapbase + USB_RESET);
/*
@@ -178,7 +178,7 @@ static void sa1111_stop_hc(struct sa1111_dev *dev)
static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev)
{
struct usb_hcd *hcd;
- int ret;
+ int ret, irq;
if (usb_disabled())
return -ENODEV;
@@ -196,6 +196,12 @@ static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev)
hcd->rsrc_start = dev->res.start;
hcd->rsrc_len = resource_size(&dev->res);
+ irq = sa1111_get_irq(dev, 1);
+ if (irq <= 0) {
+ ret = irq ? : -ENXIO;
+ goto err1;
+ }
+
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_dbg(&dev->dev, "request_mem_region failed\n");
ret = -EBUSY;
@@ -208,7 +214,7 @@ static int ohci_hcd_sa1111_probe(struct sa1111_dev *dev)
if (ret)
goto err2;
- ret = usb_add_hcd(hcd, dev->irq[1], 0);
+ ret = usb_add_hcd(hcd, irq, 0);
if (ret == 0) {
device_wakeup_enable(hcd->self.controller);
return ret;
@@ -241,8 +247,9 @@ static int ohci_hcd_sa1111_remove(struct sa1111_dev *dev)
return 0;
}
-static void ohci_hcd_sa1111_shutdown(struct sa1111_dev *dev)
+static void ohci_hcd_sa1111_shutdown(struct device *_dev)
{
+ struct sa1111_dev *dev = to_sa1111_device(_dev);
struct usb_hcd *hcd = sa1111_get_drvdata(dev);
if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
@@ -255,9 +262,9 @@ static struct sa1111_driver ohci_hcd_sa1111_driver = {
.drv = {
.name = "sa1111-ohci",
.owner = THIS_MODULE,
+ .shutdown = ohci_hcd_sa1111_shutdown,
},
.devid = SA1111_DEVID_USB,
.probe = ohci_hcd_sa1111_probe,
.remove = ohci_hcd_sa1111_remove,
- .shutdown = ohci_hcd_sa1111_shutdown,
};
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
new file mode 100644
index 000000000000..6772ee90944b
--- /dev/null
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -0,0 +1,526 @@
+/*
+ * xhci-debugfs.c - xHCI debugfs interface
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * Author: Lu Baolu <baolu.lu@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+
+#include "xhci.h"
+#include "xhci-debugfs.h"
+
+static const struct debugfs_reg32 xhci_cap_regs[] = {
+ dump_register(CAPLENGTH),
+ dump_register(HCSPARAMS1),
+ dump_register(HCSPARAMS2),
+ dump_register(HCSPARAMS3),
+ dump_register(HCCPARAMS1),
+ dump_register(DOORBELLOFF),
+ dump_register(RUNTIMEOFF),
+ dump_register(HCCPARAMS2),
+};
+
+static const struct debugfs_reg32 xhci_op_regs[] = {
+ dump_register(USBCMD),
+ dump_register(USBSTS),
+ dump_register(PAGESIZE),
+ dump_register(DNCTRL),
+ dump_register(CRCR),
+ dump_register(DCBAAP_LOW),
+ dump_register(DCBAAP_HIGH),
+ dump_register(CONFIG),
+};
+
+static const struct debugfs_reg32 xhci_runtime_regs[] = {
+ dump_register(MFINDEX),
+ dump_register(IR0_IMAN),
+ dump_register(IR0_IMOD),
+ dump_register(IR0_ERSTSZ),
+ dump_register(IR0_ERSTBA_LOW),
+ dump_register(IR0_ERSTBA_HIGH),
+ dump_register(IR0_ERDP_LOW),
+ dump_register(IR0_ERDP_HIGH),
+};
+
+static const struct debugfs_reg32 xhci_extcap_legsup[] = {
+ dump_register(EXTCAP_USBLEGSUP),
+ dump_register(EXTCAP_USBLEGCTLSTS),
+};
+
+static const struct debugfs_reg32 xhci_extcap_protocol[] = {
+ dump_register(EXTCAP_REVISION),
+ dump_register(EXTCAP_NAME),
+ dump_register(EXTCAP_PORTINFO),
+ dump_register(EXTCAP_PORTTYPE),
+ dump_register(EXTCAP_MANTISSA1),
+ dump_register(EXTCAP_MANTISSA2),
+ dump_register(EXTCAP_MANTISSA3),
+ dump_register(EXTCAP_MANTISSA4),
+ dump_register(EXTCAP_MANTISSA5),
+ dump_register(EXTCAP_MANTISSA6),
+};
+
+static const struct debugfs_reg32 xhci_extcap_dbc[] = {
+ dump_register(EXTCAP_DBC_CAPABILITY),
+ dump_register(EXTCAP_DBC_DOORBELL),
+ dump_register(EXTCAP_DBC_ERSTSIZE),
+ dump_register(EXTCAP_DBC_ERST_LOW),
+ dump_register(EXTCAP_DBC_ERST_HIGH),
+ dump_register(EXTCAP_DBC_ERDP_LOW),
+ dump_register(EXTCAP_DBC_ERDP_HIGH),
+ dump_register(EXTCAP_DBC_CONTROL),
+ dump_register(EXTCAP_DBC_STATUS),
+ dump_register(EXTCAP_DBC_PORTSC),
+ dump_register(EXTCAP_DBC_CONT_LOW),
+ dump_register(EXTCAP_DBC_CONT_HIGH),
+ dump_register(EXTCAP_DBC_DEVINFO1),
+ dump_register(EXTCAP_DBC_DEVINFO2),
+};
+
+static struct dentry *xhci_debugfs_root;
+
+static struct xhci_regset *xhci_debugfs_alloc_regset(struct xhci_hcd *xhci)
+{
+ struct xhci_regset *regset;
+
+ regset = kzalloc(sizeof(*regset), GFP_KERNEL);
+ if (!regset)
+ return NULL;
+
+ /*
+ * The allocation and free of regset are executed in order.
+ * We needn't a lock here.
+ */
+ INIT_LIST_HEAD(&regset->list);
+ list_add_tail(&regset->list, &xhci->regset_list);
+
+ return regset;
+}
+
+static void xhci_debugfs_free_regset(struct xhci_regset *regset)
+{
+ if (!regset)
+ return;
+
+ list_del(&regset->list);
+ kfree(regset);
+}
+
+static void xhci_debugfs_regset(struct xhci_hcd *xhci, u32 base,
+ const struct debugfs_reg32 *regs,
+ size_t nregs, struct dentry *parent,
+ const char *fmt, ...)
+{
+ struct xhci_regset *rgs;
+ va_list args;
+ struct debugfs_regset32 *regset;
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+
+ rgs = xhci_debugfs_alloc_regset(xhci);
+ if (!rgs)
+ return;
+
+ va_start(args, fmt);
+ vsnprintf(rgs->name, sizeof(rgs->name), fmt, args);
+ va_end(args);
+
+ regset = &rgs->regset;
+ regset->regs = regs;
+ regset->nregs = nregs;
+ regset->base = hcd->regs + base;
+
+ debugfs_create_regset32((const char *)rgs->name, 0444, parent, regset);
+}
+
+static void xhci_debugfs_extcap_regset(struct xhci_hcd *xhci, int cap_id,
+ const struct debugfs_reg32 *regs,
+ size_t n, const char *cap_name)
+{
+ u32 offset;
+ int index = 0;
+ size_t psic, nregs = n;
+ void __iomem *base = &xhci->cap_regs->hc_capbase;
+
+ offset = xhci_find_next_ext_cap(base, 0, cap_id);
+ while (offset) {
+ if (cap_id == XHCI_EXT_CAPS_PROTOCOL) {
+ psic = XHCI_EXT_PORT_PSIC(readl(base + offset + 8));
+ nregs = min(4 + psic, n);
+ }
+
+ xhci_debugfs_regset(xhci, offset, regs, nregs,
+ xhci->debugfs_root, "%s:%02d",
+ cap_name, index);
+ offset = xhci_find_next_ext_cap(base, offset, cap_id);
+ index++;
+ }
+}
+
+static int xhci_ring_enqueue_show(struct seq_file *s, void *unused)
+{
+ dma_addr_t dma;
+ struct xhci_ring *ring = s->private;
+
+ dma = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue);
+ seq_printf(s, "%pad\n", &dma);
+
+ return 0;
+}
+
+static int xhci_ring_dequeue_show(struct seq_file *s, void *unused)
+{
+ dma_addr_t dma;
+ struct xhci_ring *ring = s->private;
+
+ dma = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
+ seq_printf(s, "%pad\n", &dma);
+
+ return 0;
+}
+
+static int xhci_ring_cycle_show(struct seq_file *s, void *unused)
+{
+ struct xhci_ring *ring = s->private;
+
+ seq_printf(s, "%d\n", ring->cycle_state);
+
+ return 0;
+}
+
+static void xhci_ring_dump_segment(struct seq_file *s,
+ struct xhci_segment *seg)
+{
+ int i;
+ dma_addr_t dma;
+ union xhci_trb *trb;
+
+ for (i = 0; i < TRBS_PER_SEGMENT; i++) {
+ trb = &seg->trbs[i];
+ dma = seg->dma + i * sizeof(*trb);
+ seq_printf(s, "%pad: %s\n", &dma,
+ xhci_decode_trb(trb->generic.field[0],
+ trb->generic.field[1],
+ trb->generic.field[2],
+ trb->generic.field[3]));
+ }
+}
+
+static int xhci_ring_trb_show(struct seq_file *s, void *unused)
+{
+ int i;
+ struct xhci_ring *ring = s->private;
+ struct xhci_segment *seg = ring->first_seg;
+
+ for (i = 0; i < ring->num_segs; i++) {
+ xhci_ring_dump_segment(s, seg);
+ seg = seg->next;
+ }
+
+ return 0;
+}
+
+static struct xhci_file_map ring_files[] = {
+ {"enqueue", xhci_ring_enqueue_show, },
+ {"dequeue", xhci_ring_dequeue_show, },
+ {"cycle", xhci_ring_cycle_show, },
+ {"trbs", xhci_ring_trb_show, },
+};
+
+static int xhci_ring_open(struct inode *inode, struct file *file)
+{
+ int i;
+ struct xhci_file_map *f_map;
+ const char *file_name = file_dentry(file)->d_iname;
+
+ for (i = 0; i < ARRAY_SIZE(ring_files); i++) {
+ f_map = &ring_files[i];
+
+ if (strcmp(f_map->name, file_name) == 0)
+ break;
+ }
+
+ return single_open(file, f_map->show, inode->i_private);
+}
+
+static const struct file_operations xhci_ring_fops = {
+ .open = xhci_ring_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int xhci_slot_context_show(struct seq_file *s, void *unused)
+{
+ struct xhci_hcd *xhci;
+ struct xhci_slot_ctx *slot_ctx;
+ struct xhci_slot_priv *priv = s->private;
+ struct xhci_virt_device *dev = priv->dev;
+
+ xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus));
+ slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
+ seq_printf(s, "%pad: %s\n", &dev->out_ctx->dma,
+ xhci_decode_slot_context(slot_ctx->dev_info,
+ slot_ctx->dev_info2,
+ slot_ctx->tt_info,
+ slot_ctx->dev_state));
+
+ return 0;
+}
+
+static int xhci_endpoint_context_show(struct seq_file *s, void *unused)
+{
+ int dci;
+ dma_addr_t dma;
+ struct xhci_hcd *xhci;
+ struct xhci_ep_ctx *ep_ctx;
+ struct xhci_slot_priv *priv = s->private;
+ struct xhci_virt_device *dev = priv->dev;
+
+ xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus));
+
+ for (dci = 1; dci < 32; dci++) {
+ ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, dci);
+ dma = dev->out_ctx->dma + dci * CTX_SIZE(xhci->hcc_params);
+ seq_printf(s, "%pad: %s\n", &dma,
+ xhci_decode_ep_context(ep_ctx->ep_info,
+ ep_ctx->ep_info2,
+ ep_ctx->deq,
+ ep_ctx->tx_info));
+ }
+
+ return 0;
+}
+
+static int xhci_device_name_show(struct seq_file *s, void *unused)
+{
+ struct xhci_slot_priv *priv = s->private;
+ struct xhci_virt_device *dev = priv->dev;
+
+ seq_printf(s, "%s\n", dev_name(&dev->udev->dev));
+
+ return 0;
+}
+
+static struct xhci_file_map context_files[] = {
+ {"name", xhci_device_name_show, },
+ {"slot-context", xhci_slot_context_show, },
+ {"ep-context", xhci_endpoint_context_show, },
+};
+
+static int xhci_context_open(struct inode *inode, struct file *file)
+{
+ int i;
+ struct xhci_file_map *f_map;
+ const char *file_name = file_dentry(file)->d_iname;
+
+ for (i = 0; i < ARRAY_SIZE(context_files); i++) {
+ f_map = &context_files[i];
+
+ if (strcmp(f_map->name, file_name) == 0)
+ break;
+ }
+
+ return single_open(file, f_map->show, inode->i_private);
+}
+
+static const struct file_operations xhci_context_fops = {
+ .open = xhci_context_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void xhci_debugfs_create_files(struct xhci_hcd *xhci,
+ struct xhci_file_map *files,
+ size_t nentries, void *data,
+ struct dentry *parent,
+ const struct file_operations *fops)
+{
+ int i;
+
+ for (i = 0; i < nentries; i++)
+ debugfs_create_file(files[i].name, 0444, parent, data, fops);
+}
+
+static struct dentry *xhci_debugfs_create_ring_dir(struct xhci_hcd *xhci,
+ struct xhci_ring *ring,
+ const char *name,
+ struct dentry *parent)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir(name, parent);
+ xhci_debugfs_create_files(xhci, ring_files, ARRAY_SIZE(ring_files),
+ ring, dir, &xhci_ring_fops);
+
+ return dir;
+}
+
+static void xhci_debugfs_create_context_files(struct xhci_hcd *xhci,
+ struct dentry *parent,
+ int slot_id)
+{
+ struct xhci_virt_device *dev = xhci->devs[slot_id];
+
+ xhci_debugfs_create_files(xhci, context_files,
+ ARRAY_SIZE(context_files),
+ dev->debugfs_private,
+ parent, &xhci_context_fops);
+}
+
+void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci,
+ struct xhci_virt_device *dev,
+ int ep_index)
+{
+ struct xhci_ep_priv *epriv;
+ struct xhci_slot_priv *spriv = dev->debugfs_private;
+
+ if (spriv->eps[ep_index])
+ return;
+
+ epriv = kzalloc(sizeof(*epriv), GFP_KERNEL);
+ if (!epriv)
+ return;
+
+ snprintf(epriv->name, sizeof(epriv->name), "ep%02d", ep_index);
+ epriv->root = xhci_debugfs_create_ring_dir(xhci,
+ dev->eps[ep_index].new_ring,
+ epriv->name,
+ spriv->root);
+ spriv->eps[ep_index] = epriv;
+}
+
+void xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci,
+ struct xhci_virt_device *dev,
+ int ep_index)
+{
+ struct xhci_ep_priv *epriv;
+ struct xhci_slot_priv *spriv = dev->debugfs_private;
+
+ if (!spriv || !spriv->eps[ep_index])
+ return;
+
+ epriv = spriv->eps[ep_index];
+ debugfs_remove_recursive(epriv->root);
+ spriv->eps[ep_index] = NULL;
+ kfree(epriv);
+}
+
+void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id)
+{
+ struct xhci_slot_priv *priv;
+ struct xhci_virt_device *dev = xhci->devs[slot_id];
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return;
+
+ snprintf(priv->name, sizeof(priv->name), "%02d", slot_id);
+ priv->root = debugfs_create_dir(priv->name, xhci->debugfs_slots);
+ priv->dev = dev;
+ dev->debugfs_private = priv;
+
+ xhci_debugfs_create_ring_dir(xhci, dev->eps[0].ring,
+ "ep00", priv->root);
+
+ xhci_debugfs_create_context_files(xhci, priv->root, slot_id);
+}
+
+void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id)
+{
+ int i;
+ struct xhci_slot_priv *priv;
+ struct xhci_virt_device *dev = xhci->devs[slot_id];
+
+ if (!dev || !dev->debugfs_private)
+ return;
+
+ priv = dev->debugfs_private;
+
+ debugfs_remove_recursive(priv->root);
+
+ for (i = 0; i < 31; i++)
+ kfree(priv->eps[i]);
+
+ kfree(priv);
+ dev->debugfs_private = NULL;
+}
+
+void xhci_debugfs_init(struct xhci_hcd *xhci)
+{
+ struct device *dev = xhci_to_hcd(xhci)->self.controller;
+
+ xhci->debugfs_root = debugfs_create_dir(dev_name(dev),
+ xhci_debugfs_root);
+
+ INIT_LIST_HEAD(&xhci->regset_list);
+
+ xhci_debugfs_regset(xhci,
+ 0,
+ xhci_cap_regs, ARRAY_SIZE(xhci_cap_regs),
+ xhci->debugfs_root, "reg-cap");
+
+ xhci_debugfs_regset(xhci,
+ HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)),
+ xhci_op_regs, ARRAY_SIZE(xhci_op_regs),
+ xhci->debugfs_root, "reg-op");
+
+ xhci_debugfs_regset(xhci,
+ readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK,
+ xhci_runtime_regs, ARRAY_SIZE(xhci_runtime_regs),
+ xhci->debugfs_root, "reg-runtime");
+
+ xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_LEGACY,
+ xhci_extcap_legsup,
+ ARRAY_SIZE(xhci_extcap_legsup),
+ "reg-ext-legsup");
+
+ xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_PROTOCOL,
+ xhci_extcap_protocol,
+ ARRAY_SIZE(xhci_extcap_protocol),
+ "reg-ext-protocol");
+
+ xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_DEBUG,
+ xhci_extcap_dbc,
+ ARRAY_SIZE(xhci_extcap_dbc),
+ "reg-ext-dbc");
+
+ xhci_debugfs_create_ring_dir(xhci, xhci->cmd_ring,
+ "command-ring",
+ xhci->debugfs_root);
+
+ xhci_debugfs_create_ring_dir(xhci, xhci->event_ring,
+ "event-ring",
+ xhci->debugfs_root);
+
+ xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root);
+}
+
+void xhci_debugfs_exit(struct xhci_hcd *xhci)
+{
+ struct xhci_regset *rgs, *tmp;
+
+ debugfs_remove_recursive(xhci->debugfs_root);
+ xhci->debugfs_root = NULL;
+ xhci->debugfs_slots = NULL;
+
+ list_for_each_entry_safe(rgs, tmp, &xhci->regset_list, list)
+ xhci_debugfs_free_regset(rgs);
+}
+
+void __init xhci_debugfs_create_root(void)
+{
+ xhci_debugfs_root = debugfs_create_dir("xhci", usb_debug_root);
+}
+
+void __exit xhci_debugfs_remove_root(void)
+{
+ debugfs_remove_recursive(xhci_debugfs_root);
+ xhci_debugfs_root = NULL;
+}
diff --git a/drivers/usb/host/xhci-debugfs.h b/drivers/usb/host/xhci-debugfs.h
new file mode 100644
index 000000000000..3adc9976f180
--- /dev/null
+++ b/drivers/usb/host/xhci-debugfs.h
@@ -0,0 +1,137 @@
+/*
+ * xhci-debugfs.h - xHCI debugfs interface
+ *
+ * Copyright (C) 2017 Intel Corporation
+ *
+ * Author: Lu Baolu <baolu.lu@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_XHCI_DEBUGFS_H
+#define __LINUX_XHCI_DEBUGFS_H
+
+#include <linux/debugfs.h>
+
+#define DEBUGFS_NAMELEN 32
+
+#define REG_CAPLENGTH 0x00
+#define REG_HCSPARAMS1 0x04
+#define REG_HCSPARAMS2 0x08
+#define REG_HCSPARAMS3 0x0c
+#define REG_HCCPARAMS1 0x10
+#define REG_DOORBELLOFF 0x14
+#define REG_RUNTIMEOFF 0x18
+#define REG_HCCPARAMS2 0x1c
+
+#define REG_USBCMD 0x00
+#define REG_USBSTS 0x04
+#define REG_PAGESIZE 0x08
+#define REG_DNCTRL 0x14
+#define REG_CRCR 0x18
+#define REG_DCBAAP_LOW 0x30
+#define REG_DCBAAP_HIGH 0x34
+#define REG_CONFIG 0x38
+
+#define REG_MFINDEX 0x00
+#define REG_IR0_IMAN 0x20
+#define REG_IR0_IMOD 0x24
+#define REG_IR0_ERSTSZ 0x28
+#define REG_IR0_ERSTBA_LOW 0x30
+#define REG_IR0_ERSTBA_HIGH 0x34
+#define REG_IR0_ERDP_LOW 0x38
+#define REG_IR0_ERDP_HIGH 0x3c
+
+#define REG_EXTCAP_USBLEGSUP 0x00
+#define REG_EXTCAP_USBLEGCTLSTS 0x04
+
+#define REG_EXTCAP_REVISION 0x00
+#define REG_EXTCAP_NAME 0x04
+#define REG_EXTCAP_PORTINFO 0x08
+#define REG_EXTCAP_PORTTYPE 0x0c
+#define REG_EXTCAP_MANTISSA1 0x10
+#define REG_EXTCAP_MANTISSA2 0x14
+#define REG_EXTCAP_MANTISSA3 0x18
+#define REG_EXTCAP_MANTISSA4 0x1c
+#define REG_EXTCAP_MANTISSA5 0x20
+#define REG_EXTCAP_MANTISSA6 0x24
+
+#define REG_EXTCAP_DBC_CAPABILITY 0x00
+#define REG_EXTCAP_DBC_DOORBELL 0x04
+#define REG_EXTCAP_DBC_ERSTSIZE 0x08
+#define REG_EXTCAP_DBC_ERST_LOW 0x10
+#define REG_EXTCAP_DBC_ERST_HIGH 0x14
+#define REG_EXTCAP_DBC_ERDP_LOW 0x18
+#define REG_EXTCAP_DBC_ERDP_HIGH 0x1c
+#define REG_EXTCAP_DBC_CONTROL 0x20
+#define REG_EXTCAP_DBC_STATUS 0x24
+#define REG_EXTCAP_DBC_PORTSC 0x28
+#define REG_EXTCAP_DBC_CONT_LOW 0x30
+#define REG_EXTCAP_DBC_CONT_HIGH 0x34
+#define REG_EXTCAP_DBC_DEVINFO1 0x38
+#define REG_EXTCAP_DBC_DEVINFO2 0x3c
+
+#define dump_register(nm) \
+{ \
+ .name = __stringify(nm), \
+ .offset = REG_ ##nm, \
+}
+
+struct xhci_regset {
+ char name[DEBUGFS_NAMELEN];
+ struct debugfs_regset32 regset;
+ size_t nregs;
+ struct dentry *parent;
+ struct list_head list;
+};
+
+struct xhci_file_map {
+ const char *name;
+ int (*show)(struct seq_file *s, void *unused);
+};
+
+struct xhci_ep_priv {
+ char name[DEBUGFS_NAMELEN];
+ struct dentry *root;
+};
+
+struct xhci_slot_priv {
+ char name[DEBUGFS_NAMELEN];
+ struct dentry *root;
+ struct xhci_ep_priv *eps[31];
+ struct xhci_virt_device *dev;
+};
+
+#ifdef CONFIG_DEBUG_FS
+void xhci_debugfs_init(struct xhci_hcd *xhci);
+void xhci_debugfs_exit(struct xhci_hcd *xhci);
+void __init xhci_debugfs_create_root(void);
+void __exit xhci_debugfs_remove_root(void);
+void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id);
+void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id);
+void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int ep_index);
+void xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int ep_index);
+#else
+static inline void xhci_debugfs_init(struct xhci_hcd *xhci) { }
+static inline void xhci_debugfs_exit(struct xhci_hcd *xhci) { }
+static inline void __init xhci_debugfs_create_root(void) { }
+static inline void __exit xhci_debugfs_remove_root(void) { }
+static inline void xhci_debugfs_create_slot(struct xhci_hcd *x, int s) { }
+static inline void xhci_debugfs_remove_slot(struct xhci_hcd *x, int s) { }
+static inline void
+xhci_debugfs_create_endpoint(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int ep_index) { }
+static inline void
+xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci,
+ struct xhci_virt_device *virt_dev,
+ int ep_index) { }
+#endif /* CONFIG_DEBUG_FS */
+
+#endif /* __LINUX_XHCI_DEBUGFS_H */
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index a2336deb5e36..9762333d8d7f 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -634,7 +634,10 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
xhci_dbg(xhci, "Disable all slots\n");
spin_unlock_irqrestore(&xhci->lock, *flags);
for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
- retval = xhci_disable_slot(xhci, NULL, i);
+ if (!xhci->devs[i])
+ continue;
+
+ retval = xhci_disable_slot(xhci, i);
if (retval)
xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n",
i, retval);
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 2a82c927ded2..795219a397d3 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -28,6 +28,7 @@
#include "xhci.h"
#include "xhci-trace.h"
+#include "xhci-debugfs.h"
/*
* Allocates a generic ring segment from the ring pool, sets the dma address,
@@ -465,8 +466,6 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
return 0;
}
-#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
-
static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
int type, gfp_t flags)
{
@@ -801,8 +800,8 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
static void xhci_init_endpoint_timer(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep)
{
- setup_timer(&ep->stop_cmd_timer, xhci_stop_endpoint_command_watchdog,
- (unsigned long)ep);
+ timer_setup(&ep->stop_cmd_timer, xhci_stop_endpoint_command_watchdog,
+ 0);
ep->xhci = xhci;
}
@@ -961,6 +960,7 @@ void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
}
}
/* we are now at a leaf device */
+ xhci_debugfs_remove_slot(xhci, slot_id);
xhci_free_virt_device(xhci, slot_id);
}
@@ -1496,7 +1496,6 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
ep_ctx->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) |
EP_AVG_TRB_LENGTH(avg_trb_len));
- /* FIXME Debug endpoint context */
return 0;
}
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
index 6e7ddf6cafae..bfc51bc902b8 100644
--- a/drivers/usb/host/xhci-mtk-sch.c
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -287,12 +287,13 @@ static bool need_bw_sch(struct usb_host_endpoint *ep,
int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)
{
+ struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd);
struct mu3h_sch_bw_info *sch_array;
int num_usb_bus;
int i;
/* ss IN and OUT are separated */
- num_usb_bus = mtk->num_u3_ports * 2 + mtk->num_u2_ports;
+ num_usb_bus = xhci->num_usb3_ports * 2 + xhci->num_usb2_ports;
sch_array = kcalloc(num_usb_bus, sizeof(*sch_array), GFP_KERNEL);
if (sch_array == NULL)
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 8fb60657ed4f..e5caabe7eebe 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -43,6 +43,7 @@
/* ip_pw_sts1 register */
#define STS1_IP_SLEEP_STS BIT(30)
+#define STS1_U3_MAC_RST BIT(16)
#define STS1_XHCI_RST BIT(11)
#define STS1_SYS125_RST BIT(10)
#define STS1_REF_RST BIT(8)
@@ -91,6 +92,7 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
{
struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
u32 value, check_val;
+ int u3_ports_disabed = 0;
int ret;
int i;
@@ -102,8 +104,13 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
value &= ~CTRL1_IP_HOST_PDN;
writel(value, &ippc->ip_pw_ctr1);
- /* power on and enable all u3 ports */
+ /* power on and enable u3 ports except skipped ones */
for (i = 0; i < mtk->num_u3_ports; i++) {
+ if ((0x1 << i) & mtk->u3p_dis_msk) {
+ u3_ports_disabed++;
+ continue;
+ }
+
value = readl(&ippc->u3_ctrl_p[i]);
value &= ~(CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS);
value |= CTRL_U3_PORT_HOST_SEL;
@@ -125,6 +132,9 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
check_val = STS1_SYSPLL_STABLE | STS1_REF_RST |
STS1_SYS125_RST | STS1_XHCI_RST;
+ if (mtk->num_u3_ports > u3_ports_disabed)
+ check_val |= STS1_U3_MAC_RST;
+
ret = readl_poll_timeout(&ippc->ip_pw_sts1, value,
(check_val == (value & check_val)), 100, 20000);
if (ret) {
@@ -145,8 +155,11 @@ static int xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk)
if (!mtk->has_ippc)
return 0;
- /* power down all u3 ports */
+ /* power down u3 ports except skipped ones */
for (i = 0; i < mtk->num_u3_ports; i++) {
+ if ((0x1 << i) & mtk->u3p_dis_msk)
+ continue;
+
value = readl(&ippc->u3_ctrl_p[i]);
value |= CTRL_U3_PORT_PDN;
writel(value, &ippc->u3_ctrl_p[i]);
@@ -208,6 +221,44 @@ static int xhci_mtk_ssusb_config(struct xhci_hcd_mtk *mtk)
return xhci_mtk_host_enable(mtk);
}
+/* ignore the error if the clock does not exist */
+static struct clk *optional_clk_get(struct device *dev, const char *id)
+{
+ struct clk *opt_clk;
+
+ opt_clk = devm_clk_get(dev, id);
+ /* ignore error number except EPROBE_DEFER */
+ if (IS_ERR(opt_clk) && (PTR_ERR(opt_clk) != -EPROBE_DEFER))
+ opt_clk = NULL;
+
+ return opt_clk;
+}
+
+static int xhci_mtk_clks_get(struct xhci_hcd_mtk *mtk)
+{
+ struct device *dev = mtk->dev;
+
+ mtk->sys_clk = devm_clk_get(dev, "sys_ck");
+ if (IS_ERR(mtk->sys_clk)) {
+ dev_err(dev, "fail to get sys_ck\n");
+ return PTR_ERR(mtk->sys_clk);
+ }
+
+ mtk->ref_clk = optional_clk_get(dev, "ref_ck");
+ if (IS_ERR(mtk->ref_clk))
+ return PTR_ERR(mtk->ref_clk);
+
+ mtk->mcu_clk = optional_clk_get(dev, "mcu_ck");
+ if (IS_ERR(mtk->mcu_clk))
+ return PTR_ERR(mtk->mcu_clk);
+
+ mtk->dma_clk = optional_clk_get(dev, "dma_ck");
+ if (IS_ERR(mtk->dma_clk))
+ return PTR_ERR(mtk->dma_clk);
+
+ return 0;
+}
+
static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk)
{
int ret;
@@ -224,37 +275,34 @@ static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk)
goto sys_clk_err;
}
- if (mtk->wakeup_src) {
- ret = clk_prepare_enable(mtk->wk_deb_p0);
- if (ret) {
- dev_err(mtk->dev, "failed to enable wk_deb_p0\n");
- goto usb_p0_err;
- }
+ ret = clk_prepare_enable(mtk->mcu_clk);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable mcu_clk\n");
+ goto mcu_clk_err;
+ }
- ret = clk_prepare_enable(mtk->wk_deb_p1);
- if (ret) {
- dev_err(mtk->dev, "failed to enable wk_deb_p1\n");
- goto usb_p1_err;
- }
+ ret = clk_prepare_enable(mtk->dma_clk);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable dma_clk\n");
+ goto dma_clk_err;
}
+
return 0;
-usb_p1_err:
- clk_disable_unprepare(mtk->wk_deb_p0);
-usb_p0_err:
+dma_clk_err:
+ clk_disable_unprepare(mtk->mcu_clk);
+mcu_clk_err:
clk_disable_unprepare(mtk->sys_clk);
sys_clk_err:
clk_disable_unprepare(mtk->ref_clk);
ref_clk_err:
- return -EINVAL;
+ return ret;
}
static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk)
{
- if (mtk->wakeup_src) {
- clk_disable_unprepare(mtk->wk_deb_p1);
- clk_disable_unprepare(mtk->wk_deb_p0);
- }
+ clk_disable_unprepare(mtk->dma_clk);
+ clk_disable_unprepare(mtk->mcu_clk);
clk_disable_unprepare(mtk->sys_clk);
clk_disable_unprepare(mtk->ref_clk);
}
@@ -358,18 +406,6 @@ static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk,
if (!mtk->wakeup_src)
return 0;
- mtk->wk_deb_p0 = devm_clk_get(dev, "wakeup_deb_p0");
- if (IS_ERR(mtk->wk_deb_p0)) {
- dev_err(dev, "fail to get wakeup_deb_p0\n");
- return PTR_ERR(mtk->wk_deb_p0);
- }
-
- mtk->wk_deb_p1 = devm_clk_get(dev, "wakeup_deb_p1");
- if (IS_ERR(mtk->wk_deb_p1)) {
- dev_err(dev, "fail to get wakeup_deb_p1\n");
- return PTR_ERR(mtk->wk_deb_p1);
- }
-
mtk->pericfg = syscon_regmap_lookup_by_phandle(dn,
"mediatek,syscon-wakeup");
if (IS_ERR(mtk->pericfg)) {
@@ -492,7 +528,6 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
/* called during probe() after chip reset completes */
static int xhci_mtk_setup(struct usb_hcd *hcd)
{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
int ret;
@@ -507,8 +542,6 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
return ret;
if (usb_hcd_is_primary_hcd(hcd)) {
- mtk->num_u3_ports = xhci->num_usb3_ports;
- mtk->num_u2_ports = xhci->num_usb2_ports;
ret = xhci_mtk_sch_init(mtk);
if (ret)
return ret;
@@ -552,26 +585,14 @@ static int xhci_mtk_probe(struct platform_device *pdev)
return PTR_ERR(mtk->vusb33);
}
- mtk->sys_clk = devm_clk_get(dev, "sys_ck");
- if (IS_ERR(mtk->sys_clk)) {
- dev_err(dev, "fail to get sys_ck\n");
- return PTR_ERR(mtk->sys_clk);
- }
-
- /*
- * reference clock is usually a "fixed-clock", make it optional
- * for backward compatibility and ignore the error if it does
- * not exist.
- */
- mtk->ref_clk = devm_clk_get(dev, "ref_ck");
- if (IS_ERR(mtk->ref_clk)) {
- if (PTR_ERR(mtk->ref_clk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
- mtk->ref_clk = NULL;
- }
+ ret = xhci_mtk_clks_get(mtk);
+ if (ret)
+ return ret;
mtk->lpm_support = of_property_read_bool(node, "usb3-lpm-capable");
+ /* optional property, ignore the error if it does not exist */
+ of_property_read_u32(node, "mediatek,u3p-dis-msk",
+ &mtk->u3p_dis_msk);
ret = usb_wakeup_of_property_parse(mtk, node);
if (ret)
@@ -606,15 +627,10 @@ static int xhci_mtk_probe(struct platform_device *pdev)
}
/* Initialize dma_mask and coherent_dma_mask to 32-bits */
- ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret)
goto disable_clk;
- if (!dev->dma_mask)
- dev->dma_mask = &dev->coherent_dma_mask;
- else
- dma_set_mask(dev, DMA_BIT_MASK(32));
-
hcd = usb_create_hcd(driver, dev, dev_name(dev));
if (!hcd) {
ret = -ENOMEM;
diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
index 3aa5e1d25064..45ff5c67efb5 100644
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -121,12 +121,13 @@ struct xhci_hcd_mtk {
bool has_ippc;
int num_u2_ports;
int num_u3_ports;
+ int u3p_dis_msk;
struct regulator *vusb33;
struct regulator *vbus;
struct clk *sys_clk; /* sys and mac clock */
struct clk *ref_clk;
- struct clk *wk_deb_p0; /* port0's wakeup debounce clock */
- struct clk *wk_deb_p1;
+ struct clk *mcu_clk;
+ struct clk *dma_clk;
struct regmap *pericfg;
struct phy **phys;
int num_phys;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 1cb6eaef4ae1..1969e56a8d8d 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/usb/phy.h>
#include <linux/slab.h>
@@ -152,7 +153,7 @@ MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
static int xhci_plat_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
+ const struct xhci_plat_priv *priv_match;
const struct hc_driver *driver;
struct device *sysdev;
struct xhci_hcd *xhci;
@@ -242,9 +243,8 @@ static int xhci_plat_probe(struct platform_device *pdev)
}
xhci = hcd_to_xhci(hcd);
- match = of_match_node(usb_xhci_of_match, pdev->dev.of_node);
- if (match) {
- const struct xhci_plat_priv *priv_match = match->data;
+ priv_match = of_device_get_match_data(&pdev->dev);
+ if (priv_match) {
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
/* Just copy data for now */
@@ -263,6 +263,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto disable_clk;
}
+ if (device_property_read_bool(sysdev, "usb2-lpm-disable"))
+ xhci->quirks |= XHCI_HW_LPM_DISABLE;
+
if (device_property_read_bool(sysdev, "usb3-lpm-capable"))
xhci->quirks |= XHCI_LPM_SUPPORT;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 82c746e2d85c..521d19e82494 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -171,13 +171,13 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
if (ring->type == TYPE_EVENT) {
if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) {
ring->dequeue++;
- return;
+ goto out;
}
if (last_trb_on_ring(ring, ring->deq_seg, ring->dequeue))
ring->cycle_state ^= 1;
ring->deq_seg = ring->deq_seg->next;
ring->dequeue = ring->deq_seg->trbs;
- return;
+ goto out;
}
/* All other rings have link trbs */
@@ -190,6 +190,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
ring->dequeue = ring->deq_seg->trbs;
}
+out:
trace_xhci_inc_deq(ring);
return;
@@ -946,15 +947,12 @@ void xhci_hc_died(struct xhci_hcd *xhci)
* Instead we use a combination of that flag and checking if a new timer is
* pending.
*/
-void xhci_stop_endpoint_command_watchdog(unsigned long arg)
+void xhci_stop_endpoint_command_watchdog(struct timer_list *t)
{
- struct xhci_hcd *xhci;
- struct xhci_virt_ep *ep;
+ struct xhci_virt_ep *ep = from_timer(ep, t, stop_cmd_timer);
+ struct xhci_hcd *xhci = ep->xhci;
unsigned long flags;
- ep = (struct xhci_virt_ep *) arg;
- xhci = ep->xhci;
-
spin_lock_irqsave(&xhci->lock, flags);
/* bail out if cmd completed but raced with stop ep watchdog timer.*/
@@ -1680,9 +1678,14 @@ static void handle_port_status(struct xhci_hcd *xhci,
bus_state->resume_done[faked_port_index] = jiffies +
msecs_to_jiffies(USB_RESUME_TIMEOUT);
set_bit(faked_port_index, &bus_state->resuming_ports);
+ /* Do the rest in GetPortStatus after resume time delay.
+ * Avoid polling roothub status before that so that a
+ * usb device auto-resume latency around ~40ms.
+ */
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
mod_timer(&hcd->rh_timer,
bus_state->resume_done[faked_port_index]);
- /* Do the rest in GetPortStatus */
+ bogus_port_status = true;
}
}
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index f20753b99624..754dfb0e1a02 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -388,6 +388,11 @@ DEFINE_EVENT(xhci_log_slot_ctx, xhci_handle_cmd_set_deq,
TP_ARGS(ctx)
);
+DEFINE_EVENT(xhci_log_slot_ctx, xhci_configure_endpoint,
+ TP_PROTO(struct xhci_slot_ctx *ctx),
+ TP_ARGS(ctx)
+);
+
DECLARE_EVENT_CLASS(xhci_log_ring,
TP_PROTO(struct xhci_ring *ring),
TP_ARGS(ring),
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 51535ba2bcd4..ee077a21dfda 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -32,6 +32,7 @@
#include "xhci.h"
#include "xhci-trace.h"
#include "xhci-mtk.h"
+#include "xhci-debugfs.h"
#define DRIVER_AUTHOR "Sarah Sharp"
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
@@ -632,6 +633,9 @@ int xhci_run(struct usb_hcd *hcd)
}
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Finished xhci_run for USB2 roothub");
+
+ xhci_debugfs_init(xhci);
+
return 0;
}
EXPORT_SYMBOL_GPL(xhci_run);
@@ -660,6 +664,8 @@ static void xhci_stop(struct usb_hcd *hcd)
return;
}
+ xhci_debugfs_exit(xhci);
+
spin_lock_irq(&xhci->lock);
xhci->xhc_state |= XHCI_STATE_HALTED;
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
@@ -1600,6 +1606,8 @@ static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag);
new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+ xhci_debugfs_remove_endpoint(xhci, xhci->devs[udev->slot_id], ep_index);
+
xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
if (xhci->quirks & XHCI_MTK_HOST)
@@ -1723,6 +1731,8 @@ static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
/* Store the usb_device pointer for later use */
ep->hcpriv = udev;
+ xhci_debugfs_create_endpoint(xhci, virt_dev, ep_index);
+
xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
(unsigned int) ep->desc.bEndpointAddress,
udev->slot_id,
@@ -2555,6 +2565,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
unsigned long flags;
struct xhci_input_control_ctx *ctrl_ctx;
struct xhci_virt_device *virt_dev;
+ struct xhci_slot_ctx *slot_ctx;
if (!command)
return -EINVAL;
@@ -2593,6 +2604,9 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
return -ENOMEM;
}
+ slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx);
+ trace_xhci_configure_endpoint(slot_ctx);
+
if (!ctx_change)
ret = xhci_queue_configure_endpoint(xhci, command,
command->in_ctx->dma,
@@ -2773,6 +2787,7 @@ static void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
/* Free any rings allocated for added endpoints */
for (i = 0; i < 31; i++) {
if (virt_dev->eps[i].new_ring) {
+ xhci_debugfs_remove_endpoint(xhci, virt_dev, i);
xhci_ring_free(xhci, virt_dev->eps[i].new_ring);
virt_dev->eps[i].new_ring = NULL;
}
@@ -3488,6 +3503,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
}
if (ep->ring) {
+ xhci_debugfs_remove_endpoint(xhci, virt_dev, i);
xhci_free_endpoint_ring(xhci, virt_dev, i);
last_freed_endpoint = i;
}
@@ -3520,11 +3536,8 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
struct xhci_virt_device *virt_dev;
struct xhci_slot_ctx *slot_ctx;
int i, ret;
- struct xhci_command *command;
- command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
- if (!command)
- return;
+ xhci_debugfs_remove_slot(xhci, udev->slot_id);
#ifndef CONFIG_USB_DEFAULT_PERSIST
/*
@@ -3540,10 +3553,8 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
/* If the host is halted due to driver unload, we still need to free the
* device.
*/
- if (ret <= 0 && ret != -ENODEV) {
- kfree(command);
+ if (ret <= 0 && ret != -ENODEV)
return;
- }
virt_dev = xhci->devs[udev->slot_id];
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
@@ -3555,26 +3566,19 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
}
- xhci_disable_slot(xhci, command, udev->slot_id);
- /*
- * Event command completion handler will free any data structures
- * associated with the slot. XXX Can free sleep?
- */
+ ret = xhci_disable_slot(xhci, udev->slot_id);
+ if (ret)
+ xhci_free_virt_device(xhci, udev->slot_id);
}
-int xhci_disable_slot(struct xhci_hcd *xhci, struct xhci_command *command,
- u32 slot_id)
+int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
{
+ struct xhci_command *command;
unsigned long flags;
u32 state;
int ret = 0;
- struct xhci_virt_device *virt_dev;
- virt_dev = xhci->devs[slot_id];
- if (!virt_dev)
- return -EINVAL;
- if (!command)
- command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
+ command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
if (!command)
return -ENOMEM;
@@ -3583,17 +3587,16 @@ int xhci_disable_slot(struct xhci_hcd *xhci, struct xhci_command *command,
state = readl(&xhci->op_regs->status);
if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) ||
(xhci->xhc_state & XHCI_STATE_HALTED)) {
- xhci_free_virt_device(xhci, slot_id);
spin_unlock_irqrestore(&xhci->lock, flags);
kfree(command);
- return ret;
+ return -ENODEV;
}
ret = xhci_queue_slot_control(xhci, command, TRB_DISABLE_SLOT,
slot_id);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
+ kfree(command);
return ret;
}
xhci_ring_cmd_db(xhci);
@@ -3641,13 +3644,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
if (!command)
return 0;
- /* xhci->slot_id and xhci->addr_dev are not thread-safe */
- mutex_lock(&xhci->mutex);
spin_lock_irqsave(&xhci->lock, flags);
ret = xhci_queue_slot_control(xhci, command, TRB_ENABLE_SLOT, 0);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
- mutex_unlock(&xhci->mutex);
xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
xhci_free_command(xhci, command);
return 0;
@@ -3657,7 +3657,6 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
wait_for_completion(command->completion);
slot_id = command->slot_id;
- mutex_unlock(&xhci->mutex);
if (!slot_id || command->status != COMP_SUCCESS) {
xhci_err(xhci, "Error while assigning device slot ID\n");
@@ -3668,6 +3667,8 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
return 0;
}
+ xhci_free_command(xhci, command);
+
if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) {
spin_lock_irqsave(&xhci->lock, flags);
ret = xhci_reserve_host_control_ep_resources(xhci);
@@ -3694,6 +3695,8 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
udev->slot_id = slot_id;
+ xhci_debugfs_create_slot(xhci, slot_id);
+
#ifndef CONFIG_USB_DEFAULT_PERSIST
/*
* If resetting upon resume, we can't put the controller into runtime
@@ -3703,18 +3706,16 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
pm_runtime_get_noresume(hcd->self.controller);
#endif
-
- xhci_free_command(xhci, command);
/* Is this a LS or FS device under a HS hub? */
/* Hub or peripherial? */
return 1;
disable_slot:
- /* Disable slot, if we can do it without mem alloc */
- kfree(command->completion);
- command->completion = NULL;
- command->status = 0;
- return xhci_disable_slot(xhci, command, udev->slot_id);
+ ret = xhci_disable_slot(xhci, udev->slot_id);
+ if (ret)
+ xhci_free_virt_device(xhci, udev->slot_id);
+
+ return 0;
}
/*
@@ -3838,8 +3839,14 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
break;
case COMP_USB_TRANSACTION_ERROR:
dev_warn(&udev->dev, "Device not responding to setup %s.\n", act);
- ret = -EPROTO;
- break;
+
+ mutex_unlock(&xhci->mutex);
+ ret = xhci_disable_slot(xhci, udev->slot_id);
+ if (!ret)
+ xhci_alloc_dev(hcd, udev);
+ kfree(command->completion);
+ kfree(command);
+ return -EPROTO;
case COMP_INCOMPATIBLE_DEVICE_ERROR:
dev_warn(&udev->dev,
"ERROR: Incompatible device for setup %s command\n", act);
@@ -4088,7 +4095,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
enable ? "enable" : "disable", port_num + 1);
- if (enable) {
+ if (enable && !(xhci->quirks & XHCI_HW_LPM_DISABLE)) {
/* Host supports BESL timeout instead of HIRD */
if (udev->usb2_hw_lpm_besl_capable) {
/* if device doesn't have a preferred BESL value use a
@@ -5005,6 +5012,8 @@ static int __init xhci_hcd_init(void)
if (usb_disabled())
return -ENODEV;
+ xhci_debugfs_create_root();
+
return 0;
}
@@ -5012,7 +5021,10 @@ static int __init xhci_hcd_init(void)
* If an init function is provided, an exit function must also be provided
* to allow module unload.
*/
-static void __exit xhci_hcd_fini(void) { }
+static void __exit xhci_hcd_fini(void)
+{
+ xhci_debugfs_remove_root();
+}
module_init(xhci_hcd_init);
module_exit(xhci_hcd_fini);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 2b48aa4f6b76..86df906aec46 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -131,6 +131,8 @@ struct xhci_cap_regs {
/* Extended Capabilities pointer from PCI base - section 5.3.6 */
#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p)
+#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
+
/* db_off bitmask - bits 0:1 reserved */
#define DBOFF_MASK (~0x3)
@@ -1007,6 +1009,8 @@ struct xhci_virt_device {
struct xhci_tt_bw_info *tt_info;
/* The current max exit latency for the enabled USB3 link states. */
u16 current_mel;
+ /* Used for the debugfs interfaces. */
+ void *debugfs_private;
};
/*
@@ -1830,6 +1834,7 @@ struct xhci_hcd {
#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
/* Reserved. It was XHCI_U2_DISABLE_WAKE */
#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28)
+#define XHCI_HW_LPM_DISABLE (1 << 29)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -1859,6 +1864,10 @@ struct xhci_hcd {
/* Compliance Mode Timer Triggered every 2 seconds */
#define COMP_MODE_RCVRY_MSECS 2000
+ struct dentry *debugfs_root;
+ struct dentry *debugfs_slots;
+ struct list_head regset_list;
+
/* platform-specific data -- must come last */
unsigned long priv[0] __aligned(sizeof(s64));
};
@@ -2012,8 +2021,7 @@ int xhci_run(struct usb_hcd *hcd);
int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
void xhci_init_driver(struct hc_driver *drv,
const struct xhci_driver_overrides *over);
-int xhci_disable_slot(struct xhci_hcd *xhci,
- struct xhci_command *command, u32 slot_id);
+int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
@@ -2068,7 +2076,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
struct xhci_dequeue_state *deq_state);
void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
unsigned int stream_id, struct xhci_td *td);
-void xhci_stop_endpoint_command_watchdog(unsigned long arg);
+void xhci_stop_endpoint_command_watchdog(struct timer_list *t);
void xhci_handle_command_timeout(struct work_struct *work);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
@@ -2107,6 +2115,7 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container
struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
unsigned int stream_id);
+
static inline struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
struct urb *urb)
{
@@ -2442,11 +2451,12 @@ static inline const char *xhci_decode_portsc(u32 portsc)
static char str[256];
int ret;
- ret = sprintf(str, "%s %s %s Link:%s ",
+ ret = sprintf(str, "%s %s %s Link:%s PortSpeed:%d ",
portsc & PORT_POWER ? "Powered" : "Powered-off",
portsc & PORT_CONNECT ? "Connected" : "Not-connected",
portsc & PORT_PE ? "Enabled" : "Disabled",
- xhci_portsc_link_state_string(portsc));
+ xhci_portsc_link_state_string(portsc),
+ DEV_PORT_SPEED(portsc));
if (portsc & PORT_OC)
ret += sprintf(str + ret, "OverCurrent ");
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index b3fc602b2e24..3639e00a51a0 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -1909,7 +1909,7 @@ static struct urb *iso_alloc_urb(
if (bytes < 0 || !desc)
return NULL;
- maxp = 0x7ff & usb_endpoint_maxp(desc);
+ maxp = usb_endpoint_maxp(desc);
maxp *= usb_endpoint_maxp_mult(desc);
packets = DIV_ROUND_UP(bytes, maxp);
diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c
index c6052c814bcc..00bcc6c6db17 100644
--- a/drivers/usb/phy/phy-isp1301-omap.c
+++ b/drivers/usb/phy/phy-isp1301-omap.c
@@ -1183,9 +1183,11 @@ static irqreturn_t isp1301_irq(int irq, void *isp)
return IRQ_HANDLED;
}
-static void isp1301_timer(unsigned long _isp)
+static void isp1301_timer(struct timer_list *t)
{
- isp1301_defer_work((void *)_isp, WORK_TIMER);
+ struct isp1301 *isp = from_timer(isp, t, timer);
+
+ isp1301_defer_work(isp, WORK_TIMER);
}
/*-------------------------------------------------------------------------*/
@@ -1222,7 +1224,6 @@ static int isp1301_remove(struct i2c_client *i2c)
if (machine_is_omap_h2())
gpio_free(2);
- isp->timer.data = 0;
set_bit(WORK_STOP, &isp->todo);
del_timer_sync(&isp->timer);
flush_work(&isp->work);
@@ -1507,9 +1508,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
}
INIT_WORK(&isp->work, isp1301_work);
- init_timer(&isp->timer);
- isp->timer.function = isp1301_timer;
- isp->timer.data = (unsigned long) isp;
+ timer_setup(&isp->timer, isp1301_timer, 0);
i2c_set_clientdata(i2c, isp);
isp->client = i2c;
diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c
index 0e315694adc9..fc9ed047d25d 100644
--- a/drivers/usb/phy/phy-mv-usb.c
+++ b/drivers/usb/phy/phy-mv-usb.c
@@ -87,9 +87,10 @@ static void mv_otg_run_state_machine(struct mv_otg *mvotg,
queue_delayed_work(mvotg->qwork, &mvotg->work, delay);
}
-static void mv_otg_timer_await_bcon(unsigned long data)
+static void mv_otg_timer_await_bcon(struct timer_list *t)
{
- struct mv_otg *mvotg = (struct mv_otg *) data;
+ struct mv_otg *mvotg = from_timer(mvotg, t,
+ otg_ctrl.timer[A_WAIT_BCON_TIMER]);
mvotg->otg_ctrl.a_wait_bcon_timeout = 1;
@@ -117,8 +118,7 @@ static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
}
static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
- unsigned long interval,
- void (*callback) (unsigned long))
+ unsigned long interval)
{
struct timer_list *timer;
@@ -131,9 +131,6 @@ static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
return -EBUSY;
}
- init_timer(timer);
- timer->data = (unsigned long) mvotg;
- timer->function = callback;
timer->expires = jiffies + interval;
add_timer(timer);
@@ -459,8 +456,7 @@ run:
if (old_state != OTG_STATE_A_HOST)
mv_otg_start_host(mvotg, 1);
mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
- T_A_WAIT_BCON,
- mv_otg_timer_await_bcon);
+ T_A_WAIT_BCON);
/*
* Now, we directly enter A_HOST. So set b_conn = 1
* here. In fact, it need host driver to notify us.
@@ -722,7 +718,8 @@ static int mv_otg_probe(struct platform_device *pdev)
otg->set_vbus = mv_otg_set_vbus;
for (i = 0; i < OTG_TIMER_NUM; i++)
- init_timer(&mvotg->otg_ctrl.timer[i]);
+ timer_setup(&mvotg->otg_ctrl.timer[i],
+ mv_otg_timer_await_bcon, 0);
r = platform_get_resource_byname(mvotg->pdev,
IORESOURCE_MEM, "phyregs");
diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c
index 57282f12317b..4a73cd4783ae 100644
--- a/drivers/usb/storage/option_ms.c
+++ b/drivers/usb/storage/option_ms.c
@@ -41,7 +41,7 @@ MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default),"
static int option_rezero(struct us_data *us)
{
- const unsigned char rezero_msg[] = {
+ static const unsigned char rezero_msg[] = {
0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12,
0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -87,7 +87,7 @@ out:
static int option_inquiry(struct us_data *us)
{
- const unsigned char inquiry_msg[] = {
+ static const unsigned char inquiry_msg[] = {
0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12,
0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index bc1b7745f1d4..465d7da849c3 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -4,6 +4,18 @@ menu "USB Power Delivery and Type-C drivers"
config TYPEC
tristate
+config TYPEC_TCPM
+ tristate "USB Type-C Port Controller Manager"
+ depends on USB
+ select TYPEC
+ help
+ The Type-C Port Controller Manager provides a USB PD and USB Type-C
+ state machine for use with Type-C Port Controllers.
+
+if TYPEC_TCPM
+
+source "drivers/usb/typec/fusb302/Kconfig"
+
config TYPEC_WCOVE
tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
depends on ACPI
@@ -19,6 +31,19 @@ config TYPEC_WCOVE
To compile this driver as module, choose M here: the module will be
called typec_wcove
+endif
+
source "drivers/usb/typec/ucsi/Kconfig"
+config TYPEC_TPS6598X
+ tristate "TI TPS6598x USB Power Delivery controller driver"
+ depends on I2C
+ select TYPEC
+ help
+ Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power
+ Delivery controller.
+
+ If you choose to build this driver as a dynamically linked module, the
+ module will be called tps6598x.ko.
+
endmenu
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index bc214f15f1b5..6be5e161ac2b 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,3 +1,6 @@
obj-$(CONFIG_TYPEC) += typec.o
+obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
+obj-y += fusb302/
obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
obj-$(CONFIG_TYPEC_UCSI) += ucsi/
+obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
diff --git a/drivers/staging/typec/fusb302/Kconfig b/drivers/usb/typec/fusb302/Kconfig
index 48a4f2fcee03..48a4f2fcee03 100644
--- a/drivers/staging/typec/fusb302/Kconfig
+++ b/drivers/usb/typec/fusb302/Kconfig
diff --git a/drivers/staging/typec/fusb302/Makefile b/drivers/usb/typec/fusb302/Makefile
index 207efa5fbab8..207efa5fbab8 100644
--- a/drivers/staging/typec/fusb302/Makefile
+++ b/drivers/usb/typec/fusb302/Makefile
diff --git a/drivers/staging/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c
index fc6a3cf74eb3..e790b67d4953 100644
--- a/drivers/staging/typec/fusb302/fusb302.c
+++ b/drivers/usb/typec/fusb302/fusb302.c
@@ -37,11 +37,11 @@
#include <linux/string.h>
#include <linux/types.h>
#include <linux/usb/typec.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/pd.h>
#include <linux/workqueue.h>
#include "fusb302_reg.h"
-#include "../tcpm.h"
-#include "../pd.h"
/*
* When the device is SNK, BC_LVL interrupt is used to monitor cc pins
diff --git a/drivers/staging/typec/fusb302/fusb302_reg.h b/drivers/usb/typec/fusb302/fusb302_reg.h
index 0682e63de773..0682e63de773 100644
--- a/drivers/staging/typec/fusb302/fusb302_reg.h
+++ b/drivers/usb/typec/fusb302/fusb302_reg.h
diff --git a/drivers/staging/typec/tcpm.c b/drivers/usb/typec/tcpm.c
index 8af62e74d54c..2b735f3e5765 100644
--- a/drivers/staging/typec/tcpm.c
+++ b/drivers/usb/typec/tcpm.c
@@ -26,14 +26,13 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/pd_bdo.h>
+#include <linux/usb/pd_vdo.h>
+#include <linux/usb/tcpm.h>
#include <linux/usb/typec.h>
#include <linux/workqueue.h>
-#include "pd.h"
-#include "pd_vdo.h"
-#include "pd_bdo.h"
-#include "tcpm.h"
-
#define FOREACH_STATE(S) \
S(INVALID_STATE), \
S(DRP_TOGGLING), \
@@ -908,27 +907,6 @@ static void svdm_consume_identity(struct tcpm_port *port, const __le32 *payload,
memset(&port->mode_data, 0, sizeof(port->mode_data));
-#if 0 /* Not really a match */
- switch (PD_IDH_PTYPE(vdo)) {
- case IDH_PTYPE_UNDEF:
- port->partner.type = TYPEC_PARTNER_NONE; /* no longer exists */
- break;
- case IDH_PTYPE_HUB:
- break;
- case IDH_PTYPE_PERIPH:
- break;
- case IDH_PTYPE_PCABLE:
- break;
- case IDH_PTYPE_ACABLE:
- break;
- case IDH_PTYPE_AMA:
- port->partner.type = TYPEC_PARTNER_ALTMODE;
- break;
- default:
- break;
- }
-#endif
-
port->partner_ident.id_header = vdo;
port->partner_ident.cert_stat = le32_to_cpu(payload[VDO_INDEX_CSTAT]);
port->partner_ident.product = product;
@@ -1008,7 +986,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
}
port->partner_altmode[pmdata->altmodes] =
typec_partner_register_altmode(port->partner, paltmode);
- if (port->partner_altmode[pmdata->altmodes] == NULL) {
+ if (!port->partner_altmode[pmdata->altmodes]) {
tcpm_log(port,
"Failed to register alternate modes for SVID 0x%04x",
paltmode->svid);
@@ -1103,11 +1081,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
response[0] = VDO(svid, 1, CMD_DISCOVER_MODES);
rlen = 1;
} else {
-#if 0
- response[0] = pd_dfp_enter_mode(port, 0, 0);
- if (response[0])
- rlen = 1;
-#endif
+ /* enter alternate mode if/when implemented */
}
break;
case CMD_ENTER_MODE:
@@ -1145,10 +1119,6 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
if (PD_VDO_SVDM(p0))
rlen = tcpm_pd_svdm(port, payload, cnt, response);
-#if 0
- else
- rlen = tcpm_pd_custom_vdm(port, cnt, payload, response);
-#endif
if (rlen > 0) {
tcpm_queue_vdm(port, response[0], &response[1], rlen - 1);
@@ -2442,7 +2412,6 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SNK_STARTUP, 0);
break;
case SNK_STARTUP:
- /* XXX: callback into infrastructure */
opmode = tcpm_get_pwr_opmode(port->polarity ?
port->cc2 : port->cc1);
typec_set_pwr_opmode(port->typec_port, opmode);
@@ -3589,11 +3558,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
port->partner_desc.identity = &port->partner_ident;
port->port_type = tcpc->config->type;
- /*
- * TODO:
- * - alt_modes, set_alt_mode
- * - {debug,audio}_accessory
- */
port->typec_port = typec_register_port(port->dev, &port->typec_caps);
if (!port->typec_port) {
@@ -3638,6 +3602,7 @@ void tcpm_unregister_port(struct tcpm_port *port)
{
int i;
+ tcpm_reset_port(port);
for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++)
typec_unregister_altmode(port->port_altmode[i]);
typec_unregister_port(port->typec_port);
diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c
new file mode 100644
index 000000000000..8893f7937560
--- /dev/null
+++ b/drivers/usb/typec/tps6598x.c
@@ -0,0 +1,475 @@
+/*
+ * Driver for TI TPS6598x USB Power Delivery controller family
+ *
+ * Copyright (C) 2017, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/usb/typec.h>
+
+/* Register offsets */
+#define TPS_REG_CMD1 0x08
+#define TPS_REG_DATA1 0x09
+#define TPS_REG_INT_EVENT1 0x14
+#define TPS_REG_INT_EVENT2 0x15
+#define TPS_REG_INT_MASK1 0x16
+#define TPS_REG_INT_MASK2 0x17
+#define TPS_REG_INT_CLEAR1 0x18
+#define TPS_REG_INT_CLEAR2 0x19
+#define TPS_REG_STATUS 0x1a
+#define TPS_REG_SYSTEM_CONF 0x28
+#define TPS_REG_CTRL_CONF 0x29
+#define TPS_REG_POWER_STATUS 0x3f
+#define TPS_REG_RX_IDENTITY_SOP 0x48
+
+/* TPS_REG_INT_* bits */
+#define TPS_REG_INT_PLUG_EVENT BIT(3)
+
+/* TPS_REG_STATUS bits */
+#define TPS_STATUS_PLUG_PRESENT BIT(0)
+#define TPS_STATUS_ORIENTATION BIT(4)
+#define TPS_STATUS_PORTROLE(s) (!!((s) & BIT(5)))
+#define TPS_STATUS_DATAROLE(s) (!!((s) & BIT(6)))
+#define TPS_STATUS_VCONN(s) (!!((s) & BIT(7)))
+
+/* TPS_REG_SYSTEM_CONF bits */
+#define TPS_SYSCONF_PORTINFO(c) ((c) & 3)
+
+enum {
+ TPS_PORTINFO_SINK,
+ TPS_PORTINFO_SINK_ACCESSORY,
+ TPS_PORTINFO_DRP_UFP,
+ TPS_PORTINFO_DRP_UFP_DRD,
+ TPS_PORTINFO_DRP_DFP,
+ TPS_PORTINFO_DRP_DFP_DRD,
+ TPS_PORTINFO_SOURCE,
+};
+
+/* TPS_REG_POWER_STATUS bits */
+#define TPS_POWER_STATUS_SOURCESINK BIT(1)
+#define TPS_POWER_STATUS_PWROPMODE(p) (((p) & GENMASK(3, 2)) >> 2)
+
+/* TPS_REG_RX_IDENTITY_SOP */
+struct tps6598x_rx_identity_reg {
+ u8 status;
+ struct usb_pd_identity identity;
+ u32 vdo[3];
+} __packed;
+
+/* Standard Task return codes */
+#define TPS_TASK_TIMEOUT 1
+#define TPS_TASK_REJECTED 3
+
+/* Unrecognized commands will be replaced with "!CMD" */
+#define INVALID_CMD(_cmd_) (_cmd_ == 0x444d4321)
+
+struct tps6598x {
+ struct device *dev;
+ struct regmap *regmap;
+ struct mutex lock; /* device lock */
+
+ struct typec_port *port;
+ struct typec_partner *partner;
+ struct usb_pd_identity partner_identity;
+ struct typec_capability typec_cap;
+};
+
+static inline int tps6598x_read16(struct tps6598x *tps, u8 reg, u16 *val)
+{
+ return regmap_raw_read(tps->regmap, reg, val, sizeof(u16));
+}
+
+static inline int tps6598x_read32(struct tps6598x *tps, u8 reg, u32 *val)
+{
+ return regmap_raw_read(tps->regmap, reg, val, sizeof(u32));
+}
+
+static inline int tps6598x_read64(struct tps6598x *tps, u8 reg, u64 *val)
+{
+ return regmap_raw_read(tps->regmap, reg, val, sizeof(u64));
+}
+
+static inline int tps6598x_write16(struct tps6598x *tps, u8 reg, u16 val)
+{
+ return regmap_raw_write(tps->regmap, reg, &val, sizeof(u16));
+}
+
+static inline int tps6598x_write32(struct tps6598x *tps, u8 reg, u32 val)
+{
+ return regmap_raw_write(tps->regmap, reg, &val, sizeof(u32));
+}
+
+static inline int tps6598x_write64(struct tps6598x *tps, u8 reg, u64 val)
+{
+ return regmap_raw_write(tps->regmap, reg, &val, sizeof(u64));
+}
+
+static inline int
+tps6598x_write_4cc(struct tps6598x *tps, u8 reg, const char *val)
+{
+ return regmap_raw_write(tps->regmap, reg, &val, sizeof(u32));
+}
+
+static int tps6598x_read_partner_identity(struct tps6598x *tps)
+{
+ struct tps6598x_rx_identity_reg id;
+ int ret;
+
+ ret = regmap_raw_read(tps->regmap, TPS_REG_RX_IDENTITY_SOP,
+ &id, sizeof(id));
+ if (ret)
+ return ret;
+
+ tps->partner_identity = id.identity;
+
+ return 0;
+}
+
+static int tps6598x_connect(struct tps6598x *tps, u32 status)
+{
+ struct typec_partner_desc desc;
+ enum typec_pwr_opmode mode;
+ u16 pwr_status;
+ int ret;
+
+ if (tps->partner)
+ return 0;
+
+ ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status);
+ if (ret < 0)
+ return ret;
+
+ mode = TPS_POWER_STATUS_PWROPMODE(pwr_status);
+
+ desc.usb_pd = mode == TYPEC_PWR_MODE_PD;
+ desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
+ desc.identity = NULL;
+
+ if (desc.usb_pd) {
+ ret = tps6598x_read_partner_identity(tps);
+ if (ret)
+ return ret;
+ desc.identity = &tps->partner_identity;
+ }
+
+ tps->partner = typec_register_partner(tps->port, &desc);
+ if (!tps->partner)
+ return -ENODEV;
+
+ typec_set_pwr_opmode(tps->port, mode);
+ typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status));
+ typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status));
+ typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status));
+
+ if (desc.identity)
+ typec_partner_set_identity(tps->partner);
+
+ return 0;
+}
+
+static void tps6598x_disconnect(struct tps6598x *tps, u32 status)
+{
+ typec_unregister_partner(tps->partner);
+ tps->partner = NULL;
+ typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB);
+ typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status));
+ typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status));
+ typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status));
+}
+
+static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd,
+ size_t in_len, u8 *in_data,
+ size_t out_len, u8 *out_data)
+{
+ unsigned long timeout;
+ u32 val;
+ int ret;
+
+ ret = tps6598x_read32(tps, TPS_REG_CMD1, &val);
+ if (ret)
+ return ret;
+ if (val && !INVALID_CMD(val))
+ return -EBUSY;
+
+ if (in_len) {
+ ret = regmap_raw_write(tps->regmap, TPS_REG_DATA1,
+ in_data, in_len);
+ if (ret)
+ return ret;
+ }
+
+ ret = tps6598x_write_4cc(tps, TPS_REG_CMD1, cmd);
+ if (ret < 0)
+ return ret;
+
+ /* XXX: Using 1s for now, but it may not be enough for every command. */
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ do {
+ ret = tps6598x_read32(tps, TPS_REG_CMD1, &val);
+ if (ret)
+ return ret;
+ if (INVALID_CMD(val))
+ return -EINVAL;
+
+ if (time_is_before_jiffies(timeout))
+ return -ETIMEDOUT;
+ } while (val);
+
+ if (out_len) {
+ ret = regmap_raw_read(tps->regmap, TPS_REG_DATA1,
+ out_data, out_len);
+ if (ret)
+ return ret;
+ val = out_data[0];
+ } else {
+ ret = regmap_read(tps->regmap, TPS_REG_DATA1, &val);
+ if (ret)
+ return ret;
+ }
+
+ switch (val) {
+ case TPS_TASK_TIMEOUT:
+ return -ETIMEDOUT;
+ case TPS_TASK_REJECTED:
+ return -EPERM;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int
+tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role)
+{
+ struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap);
+ const char *cmd = (role == TYPEC_DEVICE) ? "SWUF" : "SWDF";
+ u32 status;
+ int ret;
+
+ mutex_lock(&tps->lock);
+
+ ret = tps6598x_exec_cmd(tps, cmd, 0, NULL, 0, NULL);
+ if (ret)
+ goto out_unlock;
+
+ ret = tps6598x_read32(tps, TPS_REG_STATUS, &status);
+ if (ret)
+ goto out_unlock;
+
+ if (role != TPS_STATUS_DATAROLE(status)) {
+ ret = -EPROTO;
+ goto out_unlock;
+ }
+
+ typec_set_data_role(tps->port, role);
+
+out_unlock:
+ mutex_unlock(&tps->lock);
+
+ return ret;
+}
+
+static int
+tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role)
+{
+ struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap);
+ const char *cmd = (role == TYPEC_SINK) ? "SWSk" : "SWSr";
+ u32 status;
+ int ret;
+
+ mutex_lock(&tps->lock);
+
+ ret = tps6598x_exec_cmd(tps, cmd, 0, NULL, 0, NULL);
+ if (ret)
+ goto out_unlock;
+
+ ret = tps6598x_read32(tps, TPS_REG_STATUS, &status);
+ if (ret)
+ goto out_unlock;
+
+ if (role != TPS_STATUS_PORTROLE(status)) {
+ ret = -EPROTO;
+ goto out_unlock;
+ }
+
+ typec_set_pwr_role(tps->port, role);
+
+out_unlock:
+ mutex_unlock(&tps->lock);
+
+ return ret;
+}
+
+static irqreturn_t tps6598x_interrupt(int irq, void *data)
+{
+ struct tps6598x *tps = data;
+ u64 event1;
+ u64 event2;
+ u32 status;
+ int ret;
+
+ mutex_lock(&tps->lock);
+
+ ret = tps6598x_read64(tps, TPS_REG_INT_EVENT1, &event1);
+ ret |= tps6598x_read64(tps, TPS_REG_INT_EVENT2, &event2);
+ if (ret) {
+ dev_err(tps->dev, "%s: failed to read events\n", __func__);
+ goto err_unlock;
+ }
+
+ ret = tps6598x_read32(tps, TPS_REG_STATUS, &status);
+ if (ret) {
+ dev_err(tps->dev, "%s: failed to read status\n", __func__);
+ goto err_clear_ints;
+ }
+
+ /* Handle plug insert or removal */
+ if ((event1 | event2) & TPS_REG_INT_PLUG_EVENT) {
+ if (status & TPS_STATUS_PLUG_PRESENT) {
+ ret = tps6598x_connect(tps, status);
+ if (ret)
+ dev_err(tps->dev,
+ "failed to register partner\n");
+ } else {
+ tps6598x_disconnect(tps, status);
+ }
+ }
+
+err_clear_ints:
+ tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event1);
+ tps6598x_write64(tps, TPS_REG_INT_CLEAR2, event2);
+
+err_unlock:
+ mutex_unlock(&tps->lock);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_config tps6598x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x7F,
+};
+
+static int tps6598x_probe(struct i2c_client *client)
+{
+ struct tps6598x *tps;
+ u32 status;
+ u32 conf;
+ u32 vid;
+ int ret;
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ mutex_init(&tps->lock);
+ tps->dev = &client->dev;
+
+ tps->regmap = devm_regmap_init_i2c(client, &tps6598x_regmap_config);
+ if (IS_ERR(tps->regmap))
+ return PTR_ERR(tps->regmap);
+
+ ret = tps6598x_read32(tps, 0, &vid);
+ if (ret < 0)
+ return ret;
+ if (!vid)
+ return -ENODEV;
+
+ ret = tps6598x_read32(tps, TPS_REG_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ ret = tps6598x_read32(tps, TPS_REG_SYSTEM_CONF, &conf);
+ if (ret < 0)
+ return ret;
+
+ switch (TPS_SYSCONF_PORTINFO(conf)) {
+ case TPS_PORTINFO_SINK_ACCESSORY:
+ case TPS_PORTINFO_SINK:
+ tps->typec_cap.type = TYPEC_PORT_UFP;
+ break;
+ case TPS_PORTINFO_DRP_UFP_DRD:
+ case TPS_PORTINFO_DRP_DFP_DRD:
+ tps->typec_cap.dr_set = tps6598x_dr_set;
+ case TPS_PORTINFO_DRP_UFP:
+ case TPS_PORTINFO_DRP_DFP:
+ tps->typec_cap.pr_set = tps6598x_pr_set;
+ tps->typec_cap.type = TYPEC_PORT_DRP;
+ break;
+ case TPS_PORTINFO_SOURCE:
+ tps->typec_cap.type = TYPEC_PORT_DFP;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ tps->typec_cap.revision = USB_TYPEC_REV_1_2;
+ tps->typec_cap.pd_revision = 0x200;
+ tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
+
+ tps->port = typec_register_port(&client->dev, &tps->typec_cap);
+ if (!tps->port)
+ return -ENODEV;
+
+ if (status & TPS_STATUS_PLUG_PRESENT) {
+ ret = tps6598x_connect(tps, status);
+ if (ret)
+ dev_err(&client->dev, "failed to register partner\n");
+ }
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ tps6598x_interrupt,
+ IRQF_SHARED | IRQF_ONESHOT,
+ dev_name(&client->dev), tps);
+ if (ret) {
+ tps6598x_disconnect(tps, 0);
+ typec_unregister_port(tps->port);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, tps);
+
+ return 0;
+}
+
+static int tps6598x_remove(struct i2c_client *client)
+{
+ struct tps6598x *tps = i2c_get_clientdata(client);
+
+ tps6598x_disconnect(tps, 0);
+ typec_unregister_port(tps->port);
+
+ return 0;
+}
+
+static const struct acpi_device_id tps6598x_acpi_match[] = {
+ { "INT3515", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, tps6598x_acpi_match);
+
+static struct i2c_driver tps6598x_i2c_driver = {
+ .driver = {
+ .name = "tps6598x",
+ .acpi_match_table = tps6598x_acpi_match,
+ },
+ .probe_new = tps6598x_probe,
+ .remove = tps6598x_remove,
+};
+module_i2c_driver(tps6598x_i2c_driver);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI TPS6598x USB Power Delivery Controller Driver");
diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c
index e9c4e784a9cb..4ce30a967962 100644
--- a/drivers/usb/typec/typec_wcove.c
+++ b/drivers/usb/typec/typec_wcove.c
@@ -11,14 +11,13 @@
#include <linux/acpi.h>
#include <linux/module.h>
+#include <linux/usb/tcpm.h>
#include <linux/interrupt.h>
-#include <linux/usb/typec.h>
#include <linux/platform_device.h>
#include <linux/mfd/intel_soc_pmic.h>
/* Register offsets */
#define WCOVE_CHGRIRQ0 0x4e09
-#define WCOVE_PHYCTRL 0x5e07
#define USBC_CONTROL1 0x7001
#define USBC_CONTROL2 0x7002
@@ -28,22 +27,57 @@
#define USBC_STATUS1 0x7007
#define USBC_STATUS2 0x7008
#define USBC_STATUS3 0x7009
+#define USBC_CC1 0x700a
+#define USBC_CC2 0x700b
+#define USBC_CC1_STATUS 0x700c
+#define USBC_CC2_STATUS 0x700d
#define USBC_IRQ1 0x7015
#define USBC_IRQ2 0x7016
#define USBC_IRQMASK1 0x7017
#define USBC_IRQMASK2 0x7018
+#define USBC_PDCFG2 0x701a
+#define USBC_PDCFG3 0x701b
+#define USBC_PDSTATUS 0x701c
+#define USBC_RXSTATUS 0x701d
+#define USBC_RXINFO 0x701e
+#define USBC_TXCMD 0x701f
+#define USBC_TXINFO 0x7020
+#define USBC_RX_DATA 0x7028
+#define USBC_TX_DATA 0x7047
/* Register bits */
-#define USBC_CONTROL1_MODE_DRP(r) (((r) & ~0x7) | 4)
+#define USBC_CONTROL1_MODE_MASK 0x3
+#define USBC_CONTROL1_MODE_SNK 0
+#define USBC_CONTROL1_MODE_SNKACC 1
+#define USBC_CONTROL1_MODE_SRC 2
+#define USBC_CONTROL1_MODE_SRCACC 3
+#define USBC_CONTROL1_MODE_DRP 4
+#define USBC_CONTROL1_MODE_DRPACC 5
+#define USBC_CONTROL1_MODE_TEST 7
+#define USBC_CONTROL1_CURSRC_MASK 0xc
+#define USBC_CONTROL1_CURSRC_UA_0 (0 << 3)
+#define USBC_CONTROL1_CURSRC_UA_80 (1 << 3)
+#define USBC_CONTROL1_CURSRC_UA_180 (2 << 3)
+#define USBC_CONTROL1_CURSRC_UA_330 (3 << 3)
+#define USBC_CONTROL1_DRPTOGGLE_RANDOM 0xe0
#define USBC_CONTROL2_UNATT_SNK BIT(0)
#define USBC_CONTROL2_UNATT_SRC BIT(1)
#define USBC_CONTROL2_DIS_ST BIT(2)
+#define USBC_CONTROL3_DET_DIS BIT(0)
#define USBC_CONTROL3_PD_DIS BIT(1)
+#define USBC_CONTROL3_RESETPHY BIT(2)
+#define USBC_CC_CTRL_PU_EN BIT(0)
#define USBC_CC_CTRL_VCONN_EN BIT(1)
+#define USBC_CC_CTRL_TX_EN BIT(2)
+#define USBC_CC_CTRL_PD_EN BIT(3)
+#define USBC_CC_CTRL_CDET_EN BIT(4)
+#define USBC_CC_CTRL_RDET_EN BIT(5)
+#define USBC_CC_CTRL_ADC_EN BIT(6)
+#define USBC_CC_CTRL_VBUSOK BIT(7)
#define USBC_STATUS1_DET_ONGOING BIT(6)
#define USBC_STATUS1_RSLT(r) ((r) & 0xf)
@@ -61,6 +95,15 @@
#define USBC_STATUS2_VBUS_REQ BIT(5)
+#define UCSC_CC_STATUS_SNK_RP BIT(0)
+#define UCSC_CC_STATUS_PWRDEFSNK BIT(1)
+#define UCSC_CC_STATUS_PWR_1P5A_SNK BIT(2)
+#define UCSC_CC_STATUS_PWR_3A_SNK BIT(3)
+#define UCSC_CC_STATUS_SRC_RP BIT(4)
+#define UCSC_CC_STATUS_RX(r) (((r) >> 5) & 0x3)
+#define USBC_CC_STATUS_RD 1
+#define USBC_CC_STATUS_RA 2
+
#define USBC_IRQ1_ADCDONE1 BIT(2)
#define USBC_IRQ1_OVERTEMP BIT(1)
#define USBC_IRQ1_SHORT BIT(0)
@@ -79,15 +122,44 @@
USBC_IRQ2_RX_HR | USBC_IRQ2_RX_CR | \
USBC_IRQ2_TX_SUCCESS | USBC_IRQ2_TX_FAIL)
+#define USBC_PDCFG2_SOP BIT(0)
+#define USBC_PDCFG2_SOP_P BIT(1)
+#define USBC_PDCFG2_SOP_PP BIT(2)
+#define USBC_PDCFG2_SOP_P_DEBUG BIT(3)
+#define USBC_PDCFG2_SOP_PP_DEBUG BIT(4)
+
+#define USBC_PDCFG3_DATAROLE_SHIFT 1
+#define USBC_PDCFG3_SOP_SHIFT 2
+
+#define USBC_RXSTATUS_RXCLEAR BIT(0)
+#define USBC_RXSTATUS_RXDATA BIT(7)
+
+#define USBC_RXINFO_RXBYTES(i) (((i) >> 3) & 0x1f)
+
+#define USBC_TXCMD_BUF_RDY BIT(0)
+#define USBC_TXCMD_START BIT(1)
+#define USBC_TXCMD_NOP (0 << 5)
+#define USBC_TXCMD_MSG (1 << 5)
+#define USBC_TXCMD_CR (2 << 5)
+#define USBC_TXCMD_HR (3 << 5)
+#define USBC_TXCMD_BIST (4 << 5)
+
+#define USBC_TXINFO_RETRIES(d) (d << 3)
+
struct wcove_typec {
struct mutex lock; /* device lock */
struct device *dev;
struct regmap *regmap;
- struct typec_port *port;
- struct typec_capability cap;
- struct typec_partner *partner;
+ guid_t guid;
+
+ bool vbus;
+
+ struct tcpc_dev tcpc;
+ struct tcpm_port *tcpm;
};
+#define tcpc_to_wcove(_tcpc_) container_of(_tcpc_, struct wcove_typec, tcpc)
+
enum wcove_typec_func {
WCOVE_FUNC_DRIVE_VBUS = 1,
WCOVE_FUNC_ORIENTATION,
@@ -105,8 +177,7 @@ enum wcove_typec_role {
WCOVE_ROLE_DEVICE,
};
-static guid_t guid = GUID_INIT(0x482383f0, 0x2876, 0x4e49,
- 0x86, 0x85, 0xdb, 0x66, 0x21, 0x1a, 0xf0, 0x37);
+#define WCOVE_DSM_UUID "482383f0-2876-4e49-8685-db66211af037"
static int wcove_typec_func(struct wcove_typec *wcove,
enum wcove_typec_func func, int param)
@@ -118,7 +189,7 @@ static int wcove_typec_func(struct wcove_typec *wcove,
tmp.type = ACPI_TYPE_INTEGER;
tmp.integer.value = param;
- obj = acpi_evaluate_dsm(ACPI_HANDLE(wcove->dev), &guid, 1, func,
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(wcove->dev), &wcove->guid, 1, func,
&argv4);
if (!obj) {
dev_err(wcove->dev, "%s: failed to evaluate _DSM\n", __func__);
@@ -129,158 +200,349 @@ static int wcove_typec_func(struct wcove_typec *wcove,
return 0;
}
-static irqreturn_t wcove_typec_irq(int irq, void *data)
+static int wcove_init(struct tcpc_dev *tcpc)
{
- enum typec_role role = TYPEC_SINK;
- struct typec_partner_desc partner;
- struct wcove_typec *wcove = data;
- unsigned int cc1_ctrl;
- unsigned int cc2_ctrl;
- unsigned int cc_irq1;
- unsigned int cc_irq2;
- unsigned int status1;
- unsigned int status2;
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
int ret;
- mutex_lock(&wcove->lock);
-
- ret = regmap_read(wcove->regmap, USBC_IRQ1, &cc_irq1);
+ /* Unmask everything */
+ ret = regmap_write(wcove->regmap, USBC_IRQMASK1, 0);
if (ret)
- goto err;
+ return ret;
- ret = regmap_read(wcove->regmap, USBC_IRQ2, &cc_irq2);
- if (ret)
- goto err;
+ return regmap_write(wcove->regmap, USBC_IRQMASK2, 0);
+}
- ret = regmap_read(wcove->regmap, USBC_STATUS1, &status1);
- if (ret)
- goto err;
+static int wcove_get_vbus(struct tcpc_dev *tcpc)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
+ unsigned int cc1ctrl;
+ int ret;
- ret = regmap_read(wcove->regmap, USBC_STATUS2, &status2);
+ ret = regmap_read(wcove->regmap, USBC_CC1_CTRL, &cc1ctrl);
if (ret)
- goto err;
+ return ret;
- ret = regmap_read(wcove->regmap, USBC_CC1_CTRL, &cc1_ctrl);
- if (ret)
- goto err;
+ wcove->vbus = !!(cc1ctrl & USBC_CC_CTRL_VBUSOK);
- ret = regmap_read(wcove->regmap, USBC_CC2_CTRL, &cc2_ctrl);
- if (ret)
- goto err;
+ return wcove->vbus;
+}
- if (cc_irq1) {
- if (cc_irq1 & USBC_IRQ1_OVERTEMP)
- dev_err(wcove->dev, "VCONN Switch Over Temperature!\n");
- if (cc_irq1 & USBC_IRQ1_SHORT)
- dev_err(wcove->dev, "VCONN Switch Short Circuit!\n");
- ret = regmap_write(wcove->regmap, USBC_IRQ1, cc_irq1);
- if (ret)
- goto err;
- }
+static int wcove_set_vbus(struct tcpc_dev *tcpc, bool on, bool sink)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
- if (cc_irq2) {
- ret = regmap_write(wcove->regmap, USBC_IRQ2, cc_irq2);
- if (ret)
- goto err;
- /*
- * Ignoring any PD communication interrupts until the PD support
- * is available
- */
- if (cc_irq2 & ~USBC_IRQ2_CC_CHANGE) {
- dev_WARN(wcove->dev, "USB PD handling missing\n");
- goto err;
+ return wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VBUS, on);
+}
+
+static int wcove_set_vconn(struct tcpc_dev *tcpc, bool on)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
+
+ return wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, on);
+}
+
+static enum typec_cc_status wcove_to_typec_cc(unsigned int cc)
+{
+ if (cc & UCSC_CC_STATUS_SNK_RP) {
+ if (cc & UCSC_CC_STATUS_PWRDEFSNK)
+ return TYPEC_CC_RP_DEF;
+ else if (cc & UCSC_CC_STATUS_PWR_1P5A_SNK)
+ return TYPEC_CC_RP_1_5;
+ else if (cc & UCSC_CC_STATUS_PWR_3A_SNK)
+ return TYPEC_CC_RP_3_0;
+ } else {
+ switch (UCSC_CC_STATUS_RX(cc)) {
+ case USBC_CC_STATUS_RD:
+ return TYPEC_CC_RD;
+ case USBC_CC_STATUS_RA:
+ return TYPEC_CC_RA;
+ default:
+ break;
}
}
+ return TYPEC_CC_OPEN;
+}
- if (status1 & USBC_STATUS1_DET_ONGOING)
- goto out;
+static int wcove_get_cc(struct tcpc_dev *tcpc, enum typec_cc_status *cc1,
+ enum typec_cc_status *cc2)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
+ unsigned int cc1_status;
+ unsigned int cc2_status;
+ int ret;
- if (USBC_STATUS1_RSLT(status1) == USBC_RSLT_NOTHING) {
- if (wcove->partner) {
- typec_unregister_partner(wcove->partner);
- wcove->partner = NULL;
- }
+ ret = regmap_read(wcove->regmap, USBC_CC1_STATUS, &cc1_status);
+ if (ret)
+ return ret;
- wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION,
- WCOVE_ORIENTATION_NORMAL);
+ ret = regmap_read(wcove->regmap, USBC_CC2_STATUS, &cc2_status);
+ if (ret)
+ return ret;
- /* This makes sure the device controller is disconnected */
- wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_HOST);
+ *cc1 = wcove_to_typec_cc(cc1_status);
+ *cc2 = wcove_to_typec_cc(cc2_status);
- /* Port to default role */
- typec_set_data_role(wcove->port, TYPEC_DEVICE);
- typec_set_pwr_role(wcove->port, TYPEC_SINK);
- typec_set_pwr_opmode(wcove->port, TYPEC_PWR_MODE_USB);
+ return 0;
+}
- goto out;
- }
+static int wcove_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
+{
+ /* XXX: Relying on the HW FSM to configure things correctly for now */
+ return 0;
+}
- if (wcove->partner)
- goto out;
+static int wcove_set_polarity(struct tcpc_dev *tcpc, enum typec_cc_polarity pol)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
- switch (USBC_STATUS1_ORIENT(status1)) {
- case USBC_ORIENT_NORMAL:
- wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION,
- WCOVE_ORIENTATION_NORMAL);
- break;
- case USBC_ORIENT_REVERSE:
- wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION,
- WCOVE_ORIENTATION_REVERSE);
- default:
- break;
+ return wcove_typec_func(wcove, WCOVE_FUNC_ORIENTATION, pol);
+}
+
+static int wcove_set_current_limit(struct tcpc_dev *tcpc, u32 max_ma, u32 mv)
+{
+ return 0;
+}
+
+static int wcove_set_roles(struct tcpc_dev *tcpc, bool attached,
+ enum typec_role role, enum typec_data_role data)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
+ unsigned int val;
+ int ret;
+
+ ret = wcove_typec_func(wcove, WCOVE_FUNC_ROLE, data == TYPEC_HOST ?
+ WCOVE_ROLE_HOST : WCOVE_ROLE_DEVICE);
+ if (ret)
+ return ret;
+
+ val = role;
+ val |= data << USBC_PDCFG3_DATAROLE_SHIFT;
+ val |= PD_REV20 << USBC_PDCFG3_SOP_SHIFT;
+
+ return regmap_write(wcove->regmap, USBC_PDCFG3, val);
+}
+
+static int wcove_set_pd_rx(struct tcpc_dev *tcpc, bool on)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
+
+ return regmap_write(wcove->regmap, USBC_PDCFG2,
+ on ? USBC_PDCFG2_SOP : 0);
+}
+
+static int wcove_pd_transmit(struct tcpc_dev *tcpc,
+ enum tcpm_transmit_type type,
+ const struct pd_message *msg)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
+ unsigned int info = 0;
+ unsigned int cmd;
+ int ret;
+
+ ret = regmap_read(wcove->regmap, USBC_TXCMD, &cmd);
+ if (ret)
+ return ret;
+
+ if (!(cmd & USBC_TXCMD_BUF_RDY)) {
+ dev_warn(wcove->dev, "%s: Last transmission still ongoing!",
+ __func__);
+ return -EBUSY;
}
- memset(&partner, 0, sizeof(partner));
+ if (msg) {
+ const u8 *data = (void *)msg;
+ int i;
+
+ for (i = 0; i < pd_header_cnt(msg->header) * 4 + 2; i++) {
+ ret = regmap_write(wcove->regmap, USBC_TX_DATA + i,
+ data[i]);
+ if (ret)
+ return ret;
+ }
+ }
- switch (USBC_STATUS1_RSLT(status1)) {
- case USBC_RSLT_SRC_DEFAULT:
- typec_set_pwr_opmode(wcove->port, TYPEC_PWR_MODE_USB);
+ switch (type) {
+ case TCPC_TX_SOP:
+ case TCPC_TX_SOP_PRIME:
+ case TCPC_TX_SOP_PRIME_PRIME:
+ case TCPC_TX_SOP_DEBUG_PRIME:
+ case TCPC_TX_SOP_DEBUG_PRIME_PRIME:
+ info = type + 1;
+ cmd = USBC_TXCMD_MSG;
break;
- case USBC_RSLT_SRC_1_5A:
- typec_set_pwr_opmode(wcove->port, TYPEC_PWR_MODE_1_5A);
+ case TCPC_TX_HARD_RESET:
+ cmd = USBC_TXCMD_HR;
break;
- case USBC_RSLT_SRC_3_0A:
- typec_set_pwr_opmode(wcove->port, TYPEC_PWR_MODE_3_0A);
+ case TCPC_TX_CABLE_RESET:
+ cmd = USBC_TXCMD_CR;
break;
- case USBC_RSLT_SNK:
- role = TYPEC_SOURCE;
+ case TCPC_TX_BIST_MODE_2:
+ cmd = USBC_TXCMD_BIST;
break;
- case USBC_RSLT_DEBUG_ACC:
- partner.accessory = TYPEC_ACCESSORY_DEBUG;
+ default:
+ return -EINVAL;
+ }
+
+ /* NOTE Setting maximum number of retries (7) */
+ ret = regmap_write(wcove->regmap, USBC_TXINFO,
+ info | USBC_TXINFO_RETRIES(7));
+ if (ret)
+ return ret;
+
+ return regmap_write(wcove->regmap, USBC_TXCMD, cmd | USBC_TXCMD_START);
+}
+
+static int wcove_start_drp_toggling(struct tcpc_dev *tcpc,
+ enum typec_cc_status cc)
+{
+ struct wcove_typec *wcove = tcpc_to_wcove(tcpc);
+ unsigned int usbc_ctrl;
+
+ usbc_ctrl = USBC_CONTROL1_MODE_DRP | USBC_CONTROL1_DRPTOGGLE_RANDOM;
+
+ switch (cc) {
+ case TYPEC_CC_RP_1_5:
+ usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_180;
break;
- case USBC_RSLT_AUDIO_ACC:
- partner.accessory = TYPEC_ACCESSORY_AUDIO;
+ case TYPEC_CC_RP_3_0:
+ usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_330;
break;
default:
- dev_WARN(wcove->dev, "%s Undefined result\n", __func__);
- goto err;
+ usbc_ctrl |= USBC_CONTROL1_CURSRC_UA_80;
+ break;
}
- if (role == TYPEC_SINK) {
- wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_DEVICE);
- typec_set_data_role(wcove->port, TYPEC_DEVICE);
- typec_set_pwr_role(wcove->port, TYPEC_SINK);
- } else {
- wcove_typec_func(wcove, WCOVE_FUNC_ROLE, WCOVE_ROLE_HOST);
- typec_set_pwr_role(wcove->port, TYPEC_SOURCE);
- typec_set_data_role(wcove->port, TYPEC_HOST);
+ return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl);
+}
+
+static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg)
+{
+ unsigned int info;
+ int ret;
+ int i;
+
+ ret = regmap_read(wcove->regmap, USBC_RXINFO, &info);
+ if (ret)
+ return ret;
+
+ /* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */
+
+ for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) {
+ ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i);
+ if (ret)
+ return ret;
}
- wcove->partner = typec_register_partner(wcove->port, &partner);
- if (!wcove->partner)
- dev_err(wcove->dev, "failed register partner\n");
-out:
- /* If either CC pins is requesting VCONN, we turn it on */
- if ((cc1_ctrl & USBC_CC_CTRL_VCONN_EN) ||
- (cc2_ctrl & USBC_CC_CTRL_VCONN_EN))
- wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, true);
- else
+ return regmap_write(wcove->regmap, USBC_RXSTATUS,
+ USBC_RXSTATUS_RXCLEAR);
+}
+
+static irqreturn_t wcove_typec_irq(int irq, void *data)
+{
+ struct wcove_typec *wcove = data;
+ unsigned int usbc_irq1 = 0;
+ unsigned int usbc_irq2 = 0;
+ unsigned int cc1ctrl;
+ int ret;
+
+ mutex_lock(&wcove->lock);
+
+ /* Read.. */
+ ret = regmap_read(wcove->regmap, USBC_IRQ1, &usbc_irq1);
+ if (ret)
+ goto err;
+
+ ret = regmap_read(wcove->regmap, USBC_IRQ2, &usbc_irq2);
+ if (ret)
+ goto err;
+
+ ret = regmap_read(wcove->regmap, USBC_CC1_CTRL, &cc1ctrl);
+ if (ret)
+ goto err;
+
+ if (!wcove->tcpm)
+ goto err;
+
+ /* ..check.. */
+ if (usbc_irq1 & USBC_IRQ1_OVERTEMP) {
+ dev_err(wcove->dev, "VCONN Switch Over Temperature!\n");
+ wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, false);
+ /* REVISIT: Report an error? */
+ }
+
+ if (usbc_irq1 & USBC_IRQ1_SHORT) {
+ dev_err(wcove->dev, "VCONN Switch Short Circuit!\n");
wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VCONN, false);
+ /* REVISIT: Report an error? */
+ }
+
+ if (wcove->vbus != !!(cc1ctrl & USBC_CC_CTRL_VBUSOK))
+ tcpm_vbus_change(wcove->tcpm);
+
+ /* REVISIT: See if tcpm code can be made to consider Type-C HW FSMs */
+ if (usbc_irq2 & USBC_IRQ2_CC_CHANGE)
+ tcpm_cc_change(wcove->tcpm);
+
+ if (usbc_irq2 & USBC_IRQ2_RX_PD) {
+ unsigned int status;
+
+ /*
+ * FIXME: Need to check if TX is ongoing and report
+ * TX_DIREGARDED if needed?
+ */
+
+ ret = regmap_read(wcove->regmap, USBC_RXSTATUS, &status);
+ if (ret)
+ goto err;
+
+ /* Flush all buffers */
+ while (status & USBC_RXSTATUS_RXDATA) {
+ struct pd_message msg;
+
+ ret = wcove_read_rx_buffer(wcove, &msg);
+ if (ret) {
+ dev_err(wcove->dev, "%s: RX read failed\n",
+ __func__);
+ goto err;
+ }
+
+ tcpm_pd_receive(wcove->tcpm, &msg);
+
+ ret = regmap_read(wcove->regmap, USBC_RXSTATUS,
+ &status);
+ if (ret)
+ goto err;
+ }
+ }
+
+ if (usbc_irq2 & USBC_IRQ2_RX_HR)
+ tcpm_pd_hard_reset(wcove->tcpm);
+
+ /* REVISIT: if (usbc_irq2 & USBC_IRQ2_RX_CR) */
+
+ if (usbc_irq2 & USBC_IRQ2_TX_SUCCESS)
+ tcpm_pd_transmit_complete(wcove->tcpm, TCPC_TX_SUCCESS);
+
+ if (usbc_irq2 & USBC_IRQ2_TX_FAIL)
+ tcpm_pd_transmit_complete(wcove->tcpm, TCPC_TX_FAILED);
- /* Relying on the FSM to know when we need to drive VBUS. */
- wcove_typec_func(wcove, WCOVE_FUNC_DRIVE_VBUS,
- !!(status2 & USBC_STATUS2_VBUS_REQ));
err:
+ /* ..and clear. */
+ if (usbc_irq1) {
+ ret = regmap_write(wcove->regmap, USBC_IRQ1, usbc_irq1);
+ if (ret)
+ dev_WARN(wcove->dev, "%s failed to clear IRQ1\n",
+ __func__);
+ }
+
+ if (usbc_irq2) {
+ ret = regmap_write(wcove->regmap, USBC_IRQ2, usbc_irq2);
+ if (ret)
+ dev_WARN(wcove->dev, "%s failed to clear IRQ2\n",
+ __func__);
+ }
+
/* REVISIT: Clear WhiskeyCove CHGR Type-C interrupt */
regmap_write(wcove->regmap, WCOVE_CHGRIRQ0, BIT(5));
@@ -288,11 +550,41 @@ err:
return IRQ_HANDLED;
}
+/*
+ * The following power levels should be safe to use with Joule board.
+ */
+static const u32 src_pdo[] = {
+ PDO_FIXED(5000, 1500, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |
+ PDO_FIXED_USB_COMM),
+};
+
+static const u32 snk_pdo[] = {
+ PDO_FIXED(12000, 3000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |
+ PDO_FIXED_USB_COMM),
+ PDO_BATT(4750, 12000, 15000),
+ PDO_VAR(4750, 12000, 3000),
+};
+
+static struct tcpc_config wcove_typec_config = {
+ .src_pdo = src_pdo,
+ .nr_src_pdo = ARRAY_SIZE(src_pdo),
+ .snk_pdo = snk_pdo,
+ .nr_snk_pdo = ARRAY_SIZE(snk_pdo),
+
+ .max_snk_mv = 12000,
+ .max_snk_ma = 3000,
+ .max_snk_mw = 36000,
+ .operating_snk_mw = 15000,
+
+ .type = TYPEC_PORT_DRP,
+ .default_role = TYPEC_SINK,
+};
+
static int wcove_typec_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
struct wcove_typec *wcove;
- unsigned int val;
+ int irq;
int ret;
wcove = devm_kzalloc(&pdev->dev, sizeof(*wcove), GFP_KERNEL);
@@ -303,43 +595,47 @@ static int wcove_typec_probe(struct platform_device *pdev)
wcove->dev = &pdev->dev;
wcove->regmap = pmic->regmap;
- ret = regmap_irq_get_virq(pmic->irq_chip_data_chgr,
+ irq = regmap_irq_get_virq(pmic->irq_chip_data_chgr,
platform_get_irq(pdev, 0));
- if (ret < 0)
- return ret;
+ if (irq < 0)
+ return irq;
- ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,
- wcove_typec_irq, IRQF_ONESHOT,
- "wcove_typec", wcove);
+ ret = guid_parse(WCOVE_DSM_UUID, &wcove->guid);
if (ret)
return ret;
- if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), &guid, 0, 0x1f)) {
+ if (!acpi_check_dsm(ACPI_HANDLE(&pdev->dev), &wcove->guid, 0, 0x1f)) {
dev_err(&pdev->dev, "Missing _DSM functions\n");
return -ENODEV;
}
- wcove->cap.type = TYPEC_PORT_DRP;
- wcove->cap.revision = USB_TYPEC_REV_1_1;
- wcove->cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
+ wcove->tcpc.init = wcove_init;
+ wcove->tcpc.get_vbus = wcove_get_vbus;
+ wcove->tcpc.set_vbus = wcove_set_vbus;
+ wcove->tcpc.set_cc = wcove_set_cc;
+ wcove->tcpc.get_cc = wcove_get_cc;
+ wcove->tcpc.set_polarity = wcove_set_polarity;
+ wcove->tcpc.set_vconn = wcove_set_vconn;
+ wcove->tcpc.set_current_limit = wcove_set_current_limit;
+ wcove->tcpc.start_drp_toggling = wcove_start_drp_toggling;
- /* Make sure the PD PHY is disabled until USB PD is available */
- regmap_read(wcove->regmap, USBC_CONTROL3, &val);
- regmap_write(wcove->regmap, USBC_CONTROL3, val | USBC_CONTROL3_PD_DIS);
+ wcove->tcpc.set_pd_rx = wcove_set_pd_rx;
+ wcove->tcpc.set_roles = wcove_set_roles;
+ wcove->tcpc.pd_transmit = wcove_pd_transmit;
- /* DRP mode without accessory support */
- regmap_read(wcove->regmap, USBC_CONTROL1, &val);
- regmap_write(wcove->regmap, USBC_CONTROL1, USBC_CONTROL1_MODE_DRP(val));
+ wcove->tcpc.config = &wcove_typec_config;
- wcove->port = typec_register_port(&pdev->dev, &wcove->cap);
- if (!wcove->port)
- return -ENODEV;
+ wcove->tcpm = tcpm_register_port(wcove->dev, &wcove->tcpc);
+ if (IS_ERR(wcove->tcpm))
+ return PTR_ERR(wcove->tcpm);
- /* Unmask everything */
- regmap_read(wcove->regmap, USBC_IRQMASK1, &val);
- regmap_write(wcove->regmap, USBC_IRQMASK1, val & ~USBC_IRQMASK1_ALL);
- regmap_read(wcove->regmap, USBC_IRQMASK2, &val);
- regmap_write(wcove->regmap, USBC_IRQMASK2, val & ~USBC_IRQMASK2_ALL);
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ wcove_typec_irq, IRQF_ONESHOT,
+ "wcove_typec", wcove);
+ if (ret) {
+ tcpm_unregister_port(wcove->tcpm);
+ return ret;
+ }
platform_set_drvdata(pdev, wcove);
return 0;
@@ -356,8 +652,8 @@ static int wcove_typec_remove(struct platform_device *pdev)
regmap_read(wcove->regmap, USBC_IRQMASK2, &val);
regmap_write(wcove->regmap, USBC_IRQMASK2, val | USBC_IRQMASK2_ALL);
- typec_unregister_partner(wcove->partner);
- typec_unregister_port(wcove->port);
+ tcpm_unregister_port(wcove->tcpm);
+
return 0;
}