Update Linux to v5.10.109
Sourced from [1]
[1] https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.109.tar.xz
Change-Id: I19bca9fc6762d4e63bcf3e4cba88bbe560d9c76c
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 275568a..26475b4 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -30,7 +30,7 @@
bool "USB support"
depends on HAS_IOMEM
default y
- ---help---
+ help
This option adds core support for Universal Serial Bus (USB).
You will also need drivers from the following menu to make use of it.
@@ -47,7 +47,7 @@
select GENERIC_ALLOCATOR
select USB_COMMON
select NLS # for UTF-8 strings
- ---help---
+ help
Universal Serial Bus (USB) is a specification for a serial bus
subsystem which offers higher speeds and more features than the
traditional PC serial port. The bus supplies power to peripherals
@@ -83,7 +83,7 @@
bool "PCI based USB host interface"
depends on PCI
default y
- ---help---
+ help
Many embedded system SOCs (e.g. freescale T2080) have both
PCI and USB modules with the USB module directly controlled by
registers and having no relationship to the PCI module.
@@ -133,7 +133,7 @@
tristate "USS720 parport driver"
depends on PARPORT
select PARPORT_NOT_PC
- ---help---
+ help
This driver is for USB parallel port adapters that use the Lucent
Technologies USS-720 chip. These cables are plugged into your USB
port and provide USB compatibility to peripherals designed with
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 5d41f85..e62a770 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -230,12 +230,12 @@
static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%u\n", value);
+ return sprintf(buf, "%u\n", value);
}
static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", value);
+ return sprintf(buf, "%d\n", value);
}
static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
@@ -255,8 +255,8 @@
static char *str[] = { "no", "yes" };
if (unlikely(value >= ARRAY_SIZE(str)))
- return snprintf(buf, PAGE_SIZE, "%u\n", value);
- return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+ return sprintf(buf, "%u\n", value);
+ return sprintf(buf, "%s\n", str[value]);
}
static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf)
@@ -264,8 +264,8 @@
static char *str[] = { NULL, "not connected", "connected", "lost" };
if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))
- return snprintf(buf, PAGE_SIZE, "%u\n", value);
- return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+ return sprintf(buf, "%u\n", value);
+ return sprintf(buf, "%s\n", str[value]);
}
static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf)
@@ -275,8 +275,8 @@
"waiting", "initialising"
};
if (unlikely(value >= ARRAY_SIZE(str)))
- return snprintf(buf, PAGE_SIZE, "%u\n", value);
- return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+ return sprintf(buf, "%u\n", value);
+ return sprintf(buf, "%s\n", str[value]);
}
static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
@@ -288,8 +288,8 @@
"ITU-T G.992.2 (G.LITE)"
};
if (unlikely(value >= ARRAY_SIZE(str)))
- return snprintf(buf, PAGE_SIZE, "%u\n", value);
- return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+ return sprintf(buf, "%u\n", value);
+ return sprintf(buf, "%s\n", str[value]);
}
/*
@@ -309,8 +309,7 @@
if (instance == NULL || instance->usbatm->atm_dev == NULL)
return -ENODEV;
- return snprintf(buf, PAGE_SIZE, "%pM\n",
- instance->usbatm->atm_dev->esi);
+ return sprintf(buf, "%pM\n", instance->usbatm->atm_dev->esi);
}
static ssize_t adsl_state_show(struct device *dev,
@@ -326,8 +325,8 @@
value = instance->card_info[CXINF_LINE_STARTABLE];
if (unlikely(value >= ARRAY_SIZE(str)))
- return snprintf(buf, PAGE_SIZE, "%u\n", value);
- return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+ return sprintf(buf, "%u\n", value);
+ return sprintf(buf, "%s\n", str[value]);
}
static ssize_t adsl_state_store(struct device *dev,
@@ -408,7 +407,7 @@
case CXPOLL_STOPPING:
/* abort stop request */
instance->poll_state = CXPOLL_POLLING;
- /* fall through */
+ fallthrough;
case CXPOLL_POLLING:
case CXPOLL_SHUTDOWN:
/* don't start polling */
@@ -802,7 +801,7 @@
case CXPOLL_STOPPING:
/* abort stop request */
instance->poll_state = CXPOLL_POLLING;
- /* fall through */
+ fallthrough;
case CXPOLL_POLLING:
case CXPOLL_SHUTDOWN:
/* don't start polling */
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 635cf04..7862998 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -350,7 +350,7 @@
u8 string_header[E4_L1_STRING_HEADER];
u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER];
struct block_index page_header[E4_NO_SWAPPAGE_HEADERS];
- u8 code[0];
+ u8 code[];
} __packed;
/* structures describing a block within a DSP page */
@@ -570,7 +570,7 @@
#define LOAD_INTERNAL 0xA0
#define F8051_USBCS 0x7f92
-/**
+/*
* uea_send_modem_cmd - Send a command for pre-firmware devices.
*/
static int uea_send_modem_cmd(struct usb_device *usb,
@@ -672,7 +672,7 @@
uea_leaves(usb);
}
-/**
+/*
* uea_load_firmware - Load usb firmware for pre-firmware devices.
*/
static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index dbea284..56fe30d 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -511,9 +511,10 @@
** receive **
**************/
-static void usbatm_rx_process(unsigned long data)
+static void usbatm_rx_process(struct tasklet_struct *t)
{
- struct usbatm_data *instance = (struct usbatm_data *)data;
+ struct usbatm_data *instance = from_tasklet(instance, t,
+ rx_channel.tasklet);
struct urb *urb;
while ((urb = usbatm_pop_urb(&instance->rx_channel))) {
@@ -564,9 +565,10 @@
** send **
***********/
-static void usbatm_tx_process(unsigned long data)
+static void usbatm_tx_process(struct tasklet_struct *t)
{
- struct usbatm_data *instance = (struct usbatm_data *)data;
+ struct usbatm_data *instance = from_tasklet(instance, t,
+ tx_channel.tasklet);
struct sk_buff *skb = instance->current_skb;
struct urb *urb = NULL;
const unsigned int buf_size = instance->tx_channel.buf_size;
@@ -1069,8 +1071,8 @@
usbatm_init_channel(&instance->rx_channel);
usbatm_init_channel(&instance->tx_channel);
- tasklet_init(&instance->rx_channel.tasklet, usbatm_rx_process, (unsigned long)instance);
- tasklet_init(&instance->tx_channel.tasklet, usbatm_tx_process, (unsigned long)instance);
+ tasklet_setup(&instance->rx_channel.tasklet, usbatm_rx_process);
+ tasklet_setup(&instance->tx_channel.tasklet, usbatm_tx_process);
instance->rx_channel.stride = ATM_CELL_SIZE + driver->rx_padding;
instance->tx_channel.stride = ATM_CELL_SIZE + driver->tx_padding;
instance->rx_channel.usbatm = instance->tx_channel.usbatm = instance;
@@ -1275,7 +1277,7 @@
static int __init usbatm_usb_init(void)
{
- if (sizeof(struct usbatm_control) > FIELD_SIZEOF(struct sk_buff, cb)) {
+ if (sizeof(struct usbatm_control) > sizeof_field(struct sk_buff, cb)) {
printk(KERN_ERR "%s unusable with this kernel!\n", usbatm_driver_name);
return -EIO;
}
diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h
index d3bdc4c..d96658e 100644
--- a/drivers/usb/atm/usbatm.h
+++ b/drivers/usb/atm/usbatm.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/******************************************************************************
* usbatm.h - Generic USB xDSL driver core
*
@@ -164,7 +164,7 @@
unsigned char *cell_buf; /* holds partial rx cell */
unsigned int buf_usage;
- struct urb *urbs[0];
+ struct urb *urbs[];
};
static inline void *to_usbatm_driver_data(struct usb_interface *intf)
diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c
index c39eee1..39f2376 100644
--- a/drivers/usb/c67x00/c67x00-hcd.c
+++ b/drivers/usb/c67x00/c67x00-hcd.c
@@ -228,7 +228,7 @@
* Main part of host controller driver
*/
-/**
+/*
* c67x00_hcd_irq
*
* This function is called from the interrupt handler in c67x00-drv.c
@@ -260,7 +260,7 @@
}
}
-/**
+/*
* c67x00_hcd_start: Host controller start hook
*/
static int c67x00_hcd_start(struct usb_hcd *hcd)
@@ -272,7 +272,7 @@
return 0;
}
-/**
+/*
* c67x00_hcd_stop: Host controller stop hook
*/
static void c67x00_hcd_stop(struct usb_hcd *hcd)
diff --git a/drivers/usb/c67x00/c67x00-hcd.h b/drivers/usb/c67x00/c67x00-hcd.h
index 3b181d4..6b6b04a 100644
--- a/drivers/usb/c67x00/c67x00-hcd.h
+++ b/drivers/usb/c67x00/c67x00-hcd.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* c67x00-hcd.h: Cypress C67X00 USB HCD
*
diff --git a/drivers/usb/c67x00/c67x00-ll-hpi.c b/drivers/usb/c67x00/c67x00-ll-hpi.c
index e1fe360..7a214a3 100644
--- a/drivers/usb/c67x00/c67x00-ll-hpi.c
+++ b/drivers/usb/c67x00/c67x00-ll-hpi.c
@@ -262,7 +262,7 @@
return hpi_read_word(sie->dev, USB_CTL_REG(sie->sie_num));
}
-/**
+/*
* c67x00_ll_usb_clear_status - clear the USB status bits
*/
void c67x00_ll_usb_clear_status(struct c67x00_sie *sie, u16 bits)
@@ -395,7 +395,7 @@
/* -------------------------------------------------------------------------- */
-/**
+/*
* c67x00_ll_write_mem_le16 - write into c67x00 memory
* Only data is little endian, addr has cpu endianess.
*/
@@ -434,7 +434,7 @@
}
}
-/**
+/*
* c67x00_ll_read_mem_le16 - read from c67x00 memory
* Only data is little endian, addr has cpu endianess.
*/
diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c
index 9865750..e65f1a0 100644
--- a/drivers/usb/c67x00/c67x00-sched.c
+++ b/drivers/usb/c67x00/c67x00-sched.c
@@ -23,7 +23,7 @@
/* -------------------------------------------------------------------------- */
-/**
+/*
* struct c67x00_ep_data: Host endpoint data structure
*/
struct c67x00_ep_data {
@@ -34,7 +34,7 @@
u16 next_frame; /* For int/isoc transactions */
};
-/**
+/*
* struct c67x00_td
*
* Hardware parts are little endiannes, SW in CPU endianess.
@@ -130,7 +130,7 @@
/* -------------------------------------------------------------------------- */
-/**
+/*
* dbg_td - Dump the contents of the TD
*/
static void dbg_td(struct c67x00_hcd *c67x00, struct c67x00_td *td, char *msg)
@@ -161,7 +161,7 @@
return c67x00_ll_husb_get_frame(c67x00->sie) & HOST_FRAME_MASK;
}
-/**
+/*
* frame_add
* Software wraparound for framenumbers.
*/
@@ -170,7 +170,7 @@
return (a + b) & HOST_FRAME_MASK;
}
-/**
+/*
* frame_after - is frame a after frame b
*/
static inline int frame_after(u16 a, u16 b)
@@ -179,7 +179,7 @@
(HOST_FRAME_MASK / 2);
}
-/**
+/*
* frame_after_eq - is frame a after or equal to frame b
*/
static inline int frame_after_eq(u16 a, u16 b)
@@ -190,7 +190,7 @@
/* -------------------------------------------------------------------------- */
-/**
+/*
* c67x00_release_urb - remove link from all tds to this urb
* Disconnects the urb from it's tds, so that it can be given back.
* pre: urb->hcpriv != NULL
@@ -557,7 +557,7 @@
/* -------------------------------------------------------------------------- */
-/**
+/*
* td_addr and buf_addr must be word aligned
*/
static int c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb,
@@ -685,7 +685,7 @@
return 0;
}
-/**
+/*
* return 0 in case more bandwidth is available, else errorcode
*/
static int c67x00_add_ctrl_urb(struct c67x00_hcd *c67x00, struct urb *urb)
@@ -710,7 +710,8 @@
if (ret)
return ret;
break;
- } /* else fallthrough */
+ }
+ fallthrough;
case STATUS_STAGE:
pid = !usb_pipeout(urb->pipe) ? USB_PID_OUT : USB_PID_IN;
ret = c67x00_create_td(c67x00, urb, NULL, 0, pid, 1,
@@ -822,7 +823,7 @@
/* -------------------------------------------------------------------------- */
-/**
+/*
* Get TD from C67X00
*/
static inline void
@@ -970,7 +971,7 @@
/* -------------------------------------------------------------------------- */
-/**
+/*
* c67x00_check_td_list - handle tds which have been processed by the c67x00
* pre: current_td == 0
*/
@@ -1045,7 +1046,7 @@
return !c67x00_ll_husb_get_current_td(c67x00->sie);
}
-/**
+/*
* Send td to C67X00
*/
static void c67x00_send_td(struct c67x00_hcd *c67x00, struct c67x00_td *td)
@@ -1081,7 +1082,7 @@
/* -------------------------------------------------------------------------- */
-/**
+/*
* c67x00_do_work - Schedulers state machine
*/
static void c67x00_do_work(struct c67x00_hcd *c67x00)
@@ -1122,9 +1123,9 @@
/* -------------------------------------------------------------------------- */
-static void c67x00_sched_tasklet(unsigned long __c67x00)
+static void c67x00_sched_tasklet(struct tasklet_struct *t)
{
- struct c67x00_hcd *c67x00 = (struct c67x00_hcd *)__c67x00;
+ struct c67x00_hcd *c67x00 = from_tasklet(c67x00, t, tasklet);
c67x00_do_work(c67x00);
}
@@ -1135,8 +1136,7 @@
int c67x00_sched_start_scheduler(struct c67x00_hcd *c67x00)
{
- tasklet_init(&c67x00->tasklet, c67x00_sched_tasklet,
- (unsigned long)c67x00);
+ tasklet_setup(&c67x00->tasklet, c67x00_sched_tasklet);
return 0;
}
diff --git a/drivers/usb/c67x00/c67x00.h b/drivers/usb/c67x00/c67x00.h
index 7ce1092..a4456d0 100644
--- a/drivers/usb/c67x00/c67x00.h
+++ b/drivers/usb/c67x00/c67x00.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* c67x00.h: Cypress C67X00 USB register and field definitions
*
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
index d033161..84716d2 100644
--- a/drivers/usb/cdns3/Kconfig
+++ b/drivers/usb/cdns3/Kconfig
@@ -43,4 +43,24 @@
If you choose to build this driver as module it will
be dynamically linked and module will be called cdns3-pci.ko
+config USB_CDNS3_TI
+ tristate "Cadence USB3 support on TI platforms"
+ depends on ARCH_K3 || COMPILE_TEST
+ default USB_CDNS3
+ help
+ Say 'Y' or 'M' here if you are building for Texas Instruments
+ platforms that contain Cadence USB3 controller core.
+
+ e.g. J721e.
+
+config USB_CDNS3_IMX
+ tristate "Cadence USB3 support on NXP i.MX platforms"
+ depends on ARCH_MXC || COMPILE_TEST
+ default USB_CDNS3
+ help
+ Say 'Y' or 'M' here if you are building for NXP i.MX
+ platforms that contain Cadence USB3 controller core.
+
+ For example, imx8qm and imx8qxp.
+
endif
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index a703547..d47e341 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -14,3 +14,5 @@
cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
+obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o
+obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o
diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c
new file mode 100644
index 0000000..7e728aa
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-imx.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * cdns3-imx.c - NXP i.MX specific Glue layer for Cadence USB Controller
+ *
+ * Copyright (C) 2019 NXP
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/iopoll.h>
+#include <linux/pm_runtime.h>
+#include "core.h"
+
+#define USB3_CORE_CTRL1 0x00
+#define USB3_CORE_CTRL2 0x04
+#define USB3_INT_REG 0x08
+#define USB3_CORE_STATUS 0x0c
+#define XHCI_DEBUG_LINK_ST 0x10
+#define XHCI_DEBUG_BUS 0x14
+#define USB3_SSPHY_CTRL1 0x40
+#define USB3_SSPHY_CTRL2 0x44
+#define USB3_SSPHY_STATUS 0x4c
+#define USB2_PHY_CTRL1 0x50
+#define USB2_PHY_CTRL2 0x54
+#define USB2_PHY_STATUS 0x5c
+
+/* Register bits definition */
+
+/* USB3_CORE_CTRL1 */
+#define SW_RESET_MASK GENMASK(31, 26)
+#define PWR_SW_RESET BIT(31)
+#define APB_SW_RESET BIT(30)
+#define AXI_SW_RESET BIT(29)
+#define RW_SW_RESET BIT(28)
+#define PHY_SW_RESET BIT(27)
+#define PHYAHB_SW_RESET BIT(26)
+#define ALL_SW_RESET (PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \
+ RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET)
+#define OC_DISABLE BIT(9)
+#define MDCTRL_CLK_SEL BIT(7)
+#define MODE_STRAP_MASK (0x7)
+#define DEV_MODE (1 << 2)
+#define HOST_MODE (1 << 1)
+#define OTG_MODE (1 << 0)
+
+/* USB3_INT_REG */
+#define CLK_125_REQ BIT(29)
+#define LPM_CLK_REQ BIT(28)
+#define DEVU3_WAEKUP_EN BIT(14)
+#define OTG_WAKEUP_EN BIT(12)
+#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
+#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
+
+/* USB3_CORE_STATUS */
+#define MDCTRL_CLK_STATUS BIT(15)
+#define DEV_POWER_ON_READY BIT(13)
+#define HOST_POWER_ON_READY BIT(12)
+
+/* USB3_SSPHY_STATUS */
+#define CLK_VALID_MASK (0x3f << 26)
+#define CLK_VALID_COMPARE_BITS (0xf << 28)
+#define PHY_REFCLK_REQ (1 << 0)
+
+/* OTG registers definition */
+#define OTGSTS 0x4
+/* OTGSTS */
+#define OTG_NRDY BIT(11)
+
+/* xHCI registers definition */
+#define XECP_PM_PMCSR 0x8018
+#define XECP_AUX_CTRL_REG1 0x8120
+
+/* Register bits definition */
+/* XECP_AUX_CTRL_REG1 */
+#define CFG_RXDET_P3_EN BIT(15)
+
+/* XECP_PM_PMCSR */
+#define PS_MASK GENMASK(1, 0)
+#define PS_D0 0
+#define PS_D1 1
+
+struct cdns_imx {
+ struct device *dev;
+ void __iomem *noncore;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct platform_device *cdns3_pdev;
+};
+
+static inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset)
+{
+ return readl(data->noncore + offset);
+}
+
+static inline void cdns_imx_writel(struct cdns_imx *data, u32 offset, u32 value)
+{
+ writel(value, data->noncore + offset);
+}
+
+static const struct clk_bulk_data imx_cdns3_core_clks[] = {
+ { .id = "usb3_lpm_clk" },
+ { .id = "usb3_bus_clk" },
+ { .id = "usb3_aclk" },
+ { .id = "usb3_ipg_clk" },
+ { .id = "usb3_core_pclk" },
+};
+
+static int cdns_imx_noncore_init(struct cdns_imx *data)
+{
+ u32 value;
+ int ret;
+ struct device *dev = data->dev;
+
+ cdns_imx_writel(data, USB3_SSPHY_STATUS, CLK_VALID_MASK);
+ udelay(1);
+ ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value,
+ (value & CLK_VALID_COMPARE_BITS) == CLK_VALID_COMPARE_BITS,
+ 10, 100000);
+ if (ret) {
+ dev_err(dev, "wait clkvld timeout\n");
+ return ret;
+ }
+
+ value = cdns_imx_readl(data, USB3_CORE_CTRL1);
+ value |= ALL_SW_RESET;
+ cdns_imx_writel(data, USB3_CORE_CTRL1, value);
+ udelay(1);
+
+ value = cdns_imx_readl(data, USB3_CORE_CTRL1);
+ value = (value & ~MODE_STRAP_MASK) | OTG_MODE | OC_DISABLE;
+ cdns_imx_writel(data, USB3_CORE_CTRL1, value);
+
+ value = cdns_imx_readl(data, USB3_INT_REG);
+ value |= HOST_INT1_EN | DEV_INT_EN;
+ cdns_imx_writel(data, USB3_INT_REG, value);
+
+ value = cdns_imx_readl(data, USB3_CORE_CTRL1);
+ value &= ~ALL_SW_RESET;
+ cdns_imx_writel(data, USB3_CORE_CTRL1, value);
+ return ret;
+}
+
+static int cdns_imx_platform_suspend(struct device *dev,
+ bool suspend, bool wakeup);
+static struct cdns3_platform_data cdns_imx_pdata = {
+ .platform_suspend = cdns_imx_platform_suspend,
+};
+
+static const struct of_dev_auxdata cdns_imx_auxdata[] = {
+ {
+ .compatible = "cdns,usb3",
+ .platform_data = &cdns_imx_pdata,
+ },
+ {},
+};
+
+static int cdns_imx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct cdns_imx *data;
+ int ret;
+
+ if (!node)
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+ data->dev = dev;
+ data->noncore = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->noncore)) {
+ dev_err(dev, "can't map IOMEM resource\n");
+ return PTR_ERR(data->noncore);
+ }
+
+ data->num_clks = ARRAY_SIZE(imx_cdns3_core_clks);
+ data->clks = devm_kmemdup(dev, imx_cdns3_core_clks,
+ sizeof(imx_cdns3_core_clks), GFP_KERNEL);
+ if (!data->clks)
+ return -ENOMEM;
+
+ ret = devm_clk_bulk_get(dev, data->num_clks, data->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(data->num_clks, data->clks);
+ if (ret)
+ return ret;
+
+ ret = cdns_imx_noncore_init(data);
+ if (ret)
+ goto err;
+
+ ret = of_platform_populate(node, NULL, cdns_imx_auxdata, dev);
+ if (ret) {
+ dev_err(dev, "failed to create children: %d\n", ret);
+ goto err;
+ }
+
+ device_set_wakeup_capable(dev, true);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_forbid(dev);
+
+ return ret;
+err:
+ clk_bulk_disable_unprepare(data->num_clks, data->clks);
+ return ret;
+}
+
+static int cdns_imx_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ of_platform_depopulate(dev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static void cdns3_set_wakeup(struct cdns_imx *data, bool enable)
+{
+ u32 value;
+
+ value = cdns_imx_readl(data, USB3_INT_REG);
+ if (enable)
+ value |= OTG_WAKEUP_EN | DEVU3_WAEKUP_EN;
+ else
+ value &= ~(OTG_WAKEUP_EN | DEVU3_WAEKUP_EN);
+
+ cdns_imx_writel(data, USB3_INT_REG, value);
+}
+
+static int cdns_imx_platform_suspend(struct device *dev,
+ bool suspend, bool wakeup)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ struct device *parent = dev->parent;
+ struct cdns_imx *data = dev_get_drvdata(parent);
+ void __iomem *otg_regs = (void __iomem *)(cdns->otg_regs);
+ void __iomem *xhci_regs = cdns->xhci_regs;
+ u32 value;
+ int ret = 0;
+
+ if (cdns->role != USB_ROLE_HOST)
+ return 0;
+
+ if (suspend) {
+ /* SW request low power when all usb ports allow to it ??? */
+ value = readl(xhci_regs + XECP_PM_PMCSR);
+ value &= ~PS_MASK;
+ value |= PS_D1;
+ writel(value, xhci_regs + XECP_PM_PMCSR);
+
+ /* mdctrl_clk_sel */
+ value = cdns_imx_readl(data, USB3_CORE_CTRL1);
+ value |= MDCTRL_CLK_SEL;
+ cdns_imx_writel(data, USB3_CORE_CTRL1, value);
+
+ /* wait for mdctrl_clk_status */
+ value = cdns_imx_readl(data, USB3_CORE_STATUS);
+ ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value,
+ (value & MDCTRL_CLK_STATUS) == MDCTRL_CLK_STATUS,
+ 10, 100000);
+ if (ret)
+ dev_warn(parent, "wait mdctrl_clk_status timeout\n");
+
+ /* wait lpm_clk_req to be 0 */
+ value = cdns_imx_readl(data, USB3_INT_REG);
+ ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value,
+ (value & LPM_CLK_REQ) != LPM_CLK_REQ,
+ 10, 100000);
+ if (ret)
+ dev_warn(parent, "wait lpm_clk_req timeout\n");
+
+ /* wait phy_refclk_req to be 0 */
+ value = cdns_imx_readl(data, USB3_SSPHY_STATUS);
+ ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value,
+ (value & PHY_REFCLK_REQ) != PHY_REFCLK_REQ,
+ 10, 100000);
+ if (ret)
+ dev_warn(parent, "wait phy_refclk_req timeout\n");
+
+ cdns3_set_wakeup(data, wakeup);
+ } else {
+ cdns3_set_wakeup(data, false);
+
+ /* SW request D0 */
+ value = readl(xhci_regs + XECP_PM_PMCSR);
+ value &= ~PS_MASK;
+ value |= PS_D0;
+ writel(value, xhci_regs + XECP_PM_PMCSR);
+
+ /* clr CFG_RXDET_P3_EN */
+ value = readl(xhci_regs + XECP_AUX_CTRL_REG1);
+ value &= ~CFG_RXDET_P3_EN;
+ writel(value, xhci_regs + XECP_AUX_CTRL_REG1);
+
+ /* clear mdctrl_clk_sel */
+ value = cdns_imx_readl(data, USB3_CORE_CTRL1);
+ value &= ~MDCTRL_CLK_SEL;
+ cdns_imx_writel(data, USB3_CORE_CTRL1, value);
+
+ /* wait CLK_125_REQ to be 1 */
+ value = cdns_imx_readl(data, USB3_INT_REG);
+ ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value,
+ (value & CLK_125_REQ) == CLK_125_REQ,
+ 10, 100000);
+ if (ret)
+ dev_warn(parent, "wait CLK_125_REQ timeout\n");
+
+ /* wait for mdctrl_clk_status is cleared */
+ value = cdns_imx_readl(data, USB3_CORE_STATUS);
+ ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value,
+ (value & MDCTRL_CLK_STATUS) != MDCTRL_CLK_STATUS,
+ 10, 100000);
+ if (ret)
+ dev_warn(parent, "wait mdctrl_clk_status cleared timeout\n");
+
+ /* Wait until OTG_NRDY is 0 */
+ value = readl(otg_regs + OTGSTS);
+ ret = readl_poll_timeout(otg_regs + OTGSTS, value,
+ (value & OTG_NRDY) != OTG_NRDY,
+ 10, 100000);
+ if (ret)
+ dev_warn(parent, "wait OTG ready timeout\n");
+ }
+
+ return ret;
+
+}
+
+static int cdns_imx_resume(struct device *dev)
+{
+ struct cdns_imx *data = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(data->num_clks, data->clks);
+}
+
+static int cdns_imx_suspend(struct device *dev)
+{
+ struct cdns_imx *data = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(data->num_clks, data->clks);
+
+ return 0;
+}
+#else
+static int cdns_imx_platform_suspend(struct device *dev,
+ bool suspend, bool wakeup)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops cdns_imx_pm_ops = {
+ SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL)
+};
+
+static const struct of_device_id cdns_imx_of_match[] = {
+ { .compatible = "fsl,imx8qm-usb3", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cdns_imx_of_match);
+
+static struct platform_driver cdns_imx_driver = {
+ .probe = cdns_imx_probe,
+ .remove = cdns_imx_remove,
+ .driver = {
+ .name = "cdns3-imx",
+ .of_match_table = cdns_imx_of_match,
+ .pm = &cdns_imx_pm_ops,
+ },
+};
+module_platform_driver(cdns_imx_driver);
+
+MODULE_ALIAS("platform:cdns3-imx");
+MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USB3 i.MX Glue Layer");
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
index b0a29ef..deeea61 100644
--- a/drivers/usb/cdns3/cdns3-pci-wrap.c
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -201,4 +201,4 @@
MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
+MODULE_DESCRIPTION("Cadence USBSS PCI wrapper");
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
new file mode 100644
index 0000000..90e2466
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * cdns3-ti.c - TI specific Glue layer for Cadence USB Controller
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+
+/* USB Wrapper register offsets */
+#define USBSS_PID 0x0
+#define USBSS_W1 0x4
+#define USBSS_STATIC_CONFIG 0x8
+#define USBSS_PHY_TEST 0xc
+#define USBSS_DEBUG_CTRL 0x10
+#define USBSS_DEBUG_INFO 0x14
+#define USBSS_DEBUG_LINK_STATE 0x18
+#define USBSS_DEVICE_CTRL 0x1c
+
+/* Wrapper 1 register bits */
+#define USBSS_W1_PWRUP_RST BIT(0)
+#define USBSS_W1_OVERCURRENT_SEL BIT(8)
+#define USBSS_W1_MODESTRAP_SEL BIT(9)
+#define USBSS_W1_OVERCURRENT BIT(16)
+#define USBSS_W1_MODESTRAP_MASK GENMASK(18, 17)
+#define USBSS_W1_MODESTRAP_SHIFT 17
+#define USBSS_W1_USB2_ONLY BIT(19)
+
+/* Static config register bits */
+#define USBSS1_STATIC_PLL_REF_SEL_MASK GENMASK(8, 5)
+#define USBSS1_STATIC_PLL_REF_SEL_SHIFT 5
+#define USBSS1_STATIC_LOOPBACK_MODE_MASK GENMASK(4, 3)
+#define USBSS1_STATIC_LOOPBACK_MODE_SHIFT 3
+#define USBSS1_STATIC_VBUS_SEL_MASK GENMASK(2, 1)
+#define USBSS1_STATIC_VBUS_SEL_SHIFT 1
+#define USBSS1_STATIC_LANE_REVERSE BIT(0)
+
+/* Modestrap modes */
+enum modestrap_mode { USBSS_MODESTRAP_MODE_NONE,
+ USBSS_MODESTRAP_MODE_HOST,
+ USBSS_MODESTRAP_MODE_PERIPHERAL};
+
+struct cdns_ti {
+ struct device *dev;
+ void __iomem *usbss;
+ unsigned usb2_only:1;
+ unsigned vbus_divider:1;
+ struct clk *usb2_refclk;
+ struct clk *lpm_clk;
+};
+
+static const int cdns_ti_rate_table[] = { /* in KHZ */
+ 9600,
+ 10000,
+ 12000,
+ 19200,
+ 20000,
+ 24000,
+ 25000,
+ 26000,
+ 38400,
+ 40000,
+ 58000,
+ 50000,
+ 52000,
+};
+
+static inline u32 cdns_ti_readl(struct cdns_ti *data, u32 offset)
+{
+ return readl(data->usbss + offset);
+}
+
+static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value)
+{
+ writel(value, data->usbss + offset);
+}
+
+static int cdns_ti_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct cdns_ti *data;
+ int error;
+ u32 reg;
+ int rate_code, i;
+ unsigned long rate;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+
+ data->dev = dev;
+
+ data->usbss = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->usbss)) {
+ dev_err(dev, "can't map IOMEM resource\n");
+ return PTR_ERR(data->usbss);
+ }
+
+ data->usb2_refclk = devm_clk_get(dev, "ref");
+ if (IS_ERR(data->usb2_refclk)) {
+ dev_err(dev, "can't get usb2_refclk\n");
+ return PTR_ERR(data->usb2_refclk);
+ }
+
+ data->lpm_clk = devm_clk_get(dev, "lpm");
+ if (IS_ERR(data->lpm_clk)) {
+ dev_err(dev, "can't get lpm_clk\n");
+ return PTR_ERR(data->lpm_clk);
+ }
+
+ rate = clk_get_rate(data->usb2_refclk);
+ rate /= 1000; /* To KHz */
+ for (i = 0; i < ARRAY_SIZE(cdns_ti_rate_table); i++) {
+ if (cdns_ti_rate_table[i] == rate)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(cdns_ti_rate_table)) {
+ dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
+ return -EINVAL;
+ }
+
+ rate_code = i;
+
+ pm_runtime_enable(dev);
+ error = pm_runtime_get_sync(dev);
+ if (error < 0) {
+ dev_err(dev, "pm_runtime_get_sync failed: %d\n", error);
+ goto err;
+ }
+
+ /* assert RESET */
+ reg = cdns_ti_readl(data, USBSS_W1);
+ reg &= ~USBSS_W1_PWRUP_RST;
+ cdns_ti_writel(data, USBSS_W1, reg);
+
+ /* set static config */
+ reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
+ reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
+ reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
+
+ reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
+ data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+ if (data->vbus_divider)
+ reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
+
+ cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
+ reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
+
+ /* set USB2_ONLY mode if requested */
+ reg = cdns_ti_readl(data, USBSS_W1);
+ data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
+ if (data->usb2_only)
+ reg |= USBSS_W1_USB2_ONLY;
+
+ /* set default modestrap */
+ reg |= USBSS_W1_MODESTRAP_SEL;
+ reg &= ~USBSS_W1_MODESTRAP_MASK;
+ reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
+ cdns_ti_writel(data, USBSS_W1, reg);
+
+ /* de-assert RESET */
+ reg |= USBSS_W1_PWRUP_RST;
+ cdns_ti_writel(data, USBSS_W1, reg);
+
+ error = of_platform_populate(node, NULL, NULL, dev);
+ if (error) {
+ dev_err(dev, "failed to create children: %d\n", error);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pm_runtime_put_sync(data->dev);
+ pm_runtime_disable(data->dev);
+
+ return error;
+}
+
+static int cdns_ti_remove_core(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static int cdns_ti_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ device_for_each_child(dev, NULL, cdns_ti_remove_core);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id cdns_ti_of_match[] = {
+ { .compatible = "ti,j721e-usb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cdns_ti_of_match);
+
+static struct platform_driver cdns_ti_driver = {
+ .probe = cdns_ti_probe,
+ .remove = cdns_ti_remove,
+ .driver = {
+ .name = "cdns3-ti",
+ .of_match_table = cdns_ti_of_match,
+ },
+};
+
+module_platform_driver(cdns_ti_driver);
+
+MODULE_ALIAS("platform:cdns3-ti");
+MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USB3 TI Glue Layer");
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index c2123ef..6eeb7ed 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -27,13 +27,6 @@
static int cdns3_idle_init(struct cdns3 *cdns);
-static inline
-struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
-{
- WARN_ON(!cdns->roles[cdns->role]);
- return cdns->roles[cdns->role];
-}
-
static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
{
int ret;
@@ -82,8 +75,6 @@
cdns3_drd_exit(cdns);
}
-static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
-
/**
* cdns3_core_init_role - initialize role of operation
* @cdns: Pointer to cdns3 structure
@@ -95,7 +86,7 @@
struct device *dev = cdns->dev;
enum usb_dr_mode best_dr_mode;
enum usb_dr_mode dr_mode;
- int ret = 0;
+ int ret;
dr_mode = usb_get_dr_mode(dev);
cdns->role = USB_ROLE_NONE;
@@ -186,25 +177,31 @@
goto err;
}
- return ret;
+ return 0;
err:
cdns3_exit_roles(cdns);
return ret;
}
/**
- * cdsn3_hw_role_state_machine - role switch state machine based on hw events.
+ * cdns3_hw_role_state_machine - role switch state machine based on hw events.
* @cdns: Pointer to controller structure.
*
* Returns next role to be entered based on hw events.
*/
-static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
+static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns)
{
- enum usb_role role;
+ enum usb_role role = USB_ROLE_NONE;
int id, vbus;
- if (cdns->dr_mode != USB_DR_MODE_OTG)
- goto not_otg;
+ if (cdns->dr_mode != USB_DR_MODE_OTG) {
+ if (cdns3_is_host(cdns))
+ role = USB_ROLE_HOST;
+ if (cdns3_is_device(cdns))
+ role = USB_ROLE_DEVICE;
+
+ return role;
+ }
id = cdns3_get_id(cdns);
vbus = cdns3_get_vbus(cdns);
@@ -241,14 +238,6 @@
dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
return role;
-
-not_otg:
- if (cdns3_is_host(cdns))
- role = USB_ROLE_HOST;
- if (cdns3_is_device(cdns))
- role = USB_ROLE_DEVICE;
-
- return role;
}
static int cdns3_idle_role_start(struct cdns3 *cdns)
@@ -284,21 +273,21 @@
/**
* cdns3_hw_role_switch - switch roles based on HW state
- * @cdns3: controller
+ * @cdns: controller
*/
int cdns3_hw_role_switch(struct cdns3 *cdns)
{
enum usb_role real_role, current_role;
int ret = 0;
- /* Do nothing if role based on syfs. */
- if (cdns->role_override)
+ /* Depends on role switch class */
+ if (cdns->role_sw)
return 0;
pm_runtime_get_sync(cdns->dev);
current_role = cdns->role;
- real_role = cdsn3_hw_role_state_machine(cdns);
+ real_role = cdns3_hw_role_state_machine(cdns);
/* Do nothing if nothing changed */
if (current_role == real_role)
@@ -326,13 +315,13 @@
/**
* cdsn3_role_get - get current role of controller.
*
- * @dev: Pointer to device structure
+ * @sw: pointer to USB role switch structure
*
* Returns role
*/
-static enum usb_role cdns3_role_get(struct device *dev)
+static enum usb_role cdns3_role_get(struct usb_role_switch *sw)
{
- struct cdns3 *cdns = dev_get_drvdata(dev);
+ struct cdns3 *cdns = usb_role_switch_get_drvdata(sw);
return cdns->role;
}
@@ -340,52 +329,19 @@
/**
* cdns3_role_set - set current role of controller.
*
- * @dev: pointer to device object
- * @role - the previous role
+ * @sw: pointer to USB role switch structure
+ * @role: the previous role
* Handles below events:
* - Role switch for dual-role devices
* - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
*/
-static int cdns3_role_set(struct device *dev, enum usb_role role)
+static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
{
- struct cdns3 *cdns = dev_get_drvdata(dev);
+ struct cdns3 *cdns = usb_role_switch_get_drvdata(sw);
int ret = 0;
pm_runtime_get_sync(cdns->dev);
- /*
- * FIXME: switch role framework should be extended to meet
- * requirements. Driver assumes that role can be controlled
- * by SW or HW. Temporary workaround is to use USB_ROLE_NONE to
- * switch from SW to HW control.
- *
- * For dr_mode == USB_DR_MODE_OTG:
- * if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver
- * sets role_override flag and forces that role.
- * if user sets USB_ROLE_NONE, driver clears role_override and lets
- * HW state machine take over.
- *
- * For dr_mode != USB_DR_MODE_OTG:
- * Assumptions:
- * 1. Restricted user control between NONE and dr_mode.
- * 2. Driver doesn't need to rely on role_override flag.
- * 3. Driver needs to ensure that HW state machine is never called
- * if dr_mode != USB_DR_MODE_OTG.
- */
- if (role == USB_ROLE_NONE)
- cdns->role_override = 0;
- else
- cdns->role_override = 1;
-
- /*
- * HW state might have changed so driver need to trigger
- * HW state machine if dr_mode == USB_DR_MODE_OTG.
- */
- if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) {
- cdns3_hw_role_switch(cdns);
- goto pm_put;
- }
-
if (cdns->role == role)
goto pm_put;
@@ -395,7 +351,6 @@
case USB_ROLE_HOST:
break;
default:
- ret = -EPERM;
goto pm_put;
}
}
@@ -406,28 +361,63 @@
case USB_ROLE_DEVICE:
break;
default:
- ret = -EPERM;
goto pm_put;
}
}
cdns3_role_stop(cdns);
ret = cdns3_role_start(cdns, role);
- if (ret) {
+ if (ret)
dev_err(cdns->dev, "set role %d has failed\n", role);
- ret = -EPERM;
- }
pm_put:
pm_runtime_put_sync(cdns->dev);
return ret;
}
-static const struct usb_role_switch_desc cdns3_switch_desc = {
- .set = cdns3_role_set,
- .get = cdns3_role_get,
- .allow_userspace_control = true,
-};
+static int set_phy_power_on(struct cdns3 *cdns)
+{
+ int ret;
+
+ ret = phy_power_on(cdns->usb2_phy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(cdns->usb3_phy);
+ if (ret)
+ phy_power_off(cdns->usb2_phy);
+
+ return ret;
+}
+
+static void set_phy_power_off(struct cdns3 *cdns)
+{
+ phy_power_off(cdns->usb3_phy);
+ phy_power_off(cdns->usb2_phy);
+}
+
+/**
+ * cdns3_wakeup_irq - interrupt handler for wakeup events
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_wakeup_irq(int irq, void *data)
+{
+ struct cdns3 *cdns = data;
+
+ if (cdns->in_lpm) {
+ disable_irq_nosync(irq);
+ cdns->wakeup_pending = true;
+ if ((cdns->role == USB_ROLE_HOST) && cdns->host_dev)
+ pm_request_resume(&cdns->host_dev->dev);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
/**
* cdns3_probe - probe for cdns3 core device
@@ -446,7 +436,7 @@
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "error setting dma mask: %d\n", ret);
- return -ENODEV;
+ return ret;
}
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
@@ -454,6 +444,7 @@
return -ENOMEM;
cdns->dev = dev;
+ cdns->pdata = dev_get_platdata(dev);
platform_set_drvdata(pdev, cdns);
@@ -480,8 +471,7 @@
if (cdns->dev_irq < 0)
dev_err(dev, "couldn't get peripheral irq\n");
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dev");
- regs = devm_ioremap_resource(dev, res);
+ regs = devm_platform_ioremap_resource_byname(pdev, "dev");
if (IS_ERR(regs))
return PTR_ERR(regs);
cdns->dev_regs = regs;
@@ -501,8 +491,21 @@
return -ENXIO;
}
+ cdns->phyrst_a_enable = device_property_read_bool(dev, "cdns,phyrst-a-enable");
+
cdns->otg_res = *res;
+ cdns->wakeup_irq = platform_get_irq_byname_optional(pdev, "wakeup");
+ if (cdns->wakeup_irq == -EPROBE_DEFER)
+ return cdns->wakeup_irq;
+ else if (cdns->wakeup_irq == 0)
+ return -EINVAL;
+
+ if (cdns->wakeup_irq < 0) {
+ dev_dbg(dev, "couldn't get wakeup irq\n");
+ cdns->wakeup_irq = 0x0;
+ }
+
mutex_init(&cdns->mutex);
cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
@@ -521,32 +524,53 @@
if (ret)
goto err1;
- ret = phy_power_on(cdns->usb2_phy);
+ ret = set_phy_power_on(cdns);
if (ret)
goto err2;
- ret = phy_power_on(cdns->usb3_phy);
- if (ret)
- goto err3;
+ if (device_property_read_bool(dev, "usb-role-switch")) {
+ struct usb_role_switch_desc sw_desc = { };
- cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc);
- if (IS_ERR(cdns->role_sw)) {
- ret = PTR_ERR(cdns->role_sw);
- dev_warn(dev, "Unable to register Role Switch\n");
- goto err4;
+ sw_desc.set = cdns3_role_set;
+ sw_desc.get = cdns3_role_get;
+ sw_desc.allow_userspace_control = true;
+ sw_desc.driver_data = cdns;
+ sw_desc.fwnode = dev->fwnode;
+
+ cdns->role_sw = usb_role_switch_register(dev, &sw_desc);
+ if (IS_ERR(cdns->role_sw)) {
+ ret = PTR_ERR(cdns->role_sw);
+ dev_warn(dev, "Unable to register Role Switch\n");
+ goto err3;
+ }
+ }
+
+ if (cdns->wakeup_irq) {
+ ret = devm_request_irq(cdns->dev, cdns->wakeup_irq,
+ cdns3_wakeup_irq,
+ IRQF_SHARED,
+ dev_name(cdns->dev), cdns);
+
+ if (ret) {
+ dev_err(cdns->dev, "couldn't register wakeup irq handler\n");
+ goto err4;
+ }
}
ret = cdns3_drd_init(cdns);
if (ret)
- goto err5;
+ goto err4;
ret = cdns3_core_init_role(cdns);
if (ret)
- goto err5;
+ goto err4;
+ spin_lock_init(&cdns->lock);
device_set_wakeup_capable(dev, true);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+ if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)))
+ pm_runtime_forbid(dev);
/*
* The controller needs less time between bus and controller suspend,
@@ -559,14 +583,12 @@
dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
return 0;
-err5:
- cdns3_drd_exit(cdns);
- usb_role_switch_unregister(cdns->role_sw);
err4:
- phy_power_off(cdns->usb3_phy);
-
+ cdns3_drd_exit(cdns);
+ if (cdns->role_sw)
+ usb_role_switch_unregister(cdns->role_sw);
err3:
- phy_power_off(cdns->usb2_phy);
+ set_phy_power_off(cdns);
err2:
phy_exit(cdns->usb3_phy);
err1:
@@ -590,13 +612,89 @@
pm_runtime_put_noidle(&pdev->dev);
cdns3_exit_roles(cdns);
usb_role_switch_unregister(cdns->role_sw);
- phy_power_off(cdns->usb2_phy);
- phy_power_off(cdns->usb3_phy);
+ set_phy_power_off(cdns);
phy_exit(cdns->usb2_phy);
phy_exit(cdns->usb3_phy);
return 0;
}
+#ifdef CONFIG_PM
+
+static int cdns3_set_platform_suspend(struct device *dev,
+ bool suspend, bool wakeup)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (cdns->pdata && cdns->pdata->platform_suspend)
+ ret = cdns->pdata->platform_suspend(dev, suspend, wakeup);
+
+ return ret;
+}
+
+static int cdns3_controller_suspend(struct device *dev, pm_message_t msg)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ bool wakeup;
+ unsigned long flags;
+
+ if (cdns->in_lpm)
+ return 0;
+
+ if (PMSG_IS_AUTO(msg))
+ wakeup = true;
+ else
+ wakeup = device_may_wakeup(dev);
+
+ cdns3_set_platform_suspend(cdns->dev, true, wakeup);
+ set_phy_power_off(cdns);
+ spin_lock_irqsave(&cdns->lock, flags);
+ cdns->in_lpm = true;
+ spin_unlock_irqrestore(&cdns->lock, flags);
+ dev_dbg(cdns->dev, "%s ends\n", __func__);
+
+ return 0;
+}
+
+static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ int ret;
+ unsigned long flags;
+
+ if (!cdns->in_lpm)
+ return 0;
+
+ ret = set_phy_power_on(cdns);
+ if (ret)
+ return ret;
+
+ cdns3_set_platform_suspend(cdns->dev, false, false);
+
+ spin_lock_irqsave(&cdns->lock, flags);
+ if (cdns->roles[cdns->role]->resume && !PMSG_IS_AUTO(msg))
+ cdns->roles[cdns->role]->resume(cdns, false);
+
+ cdns->in_lpm = false;
+ spin_unlock_irqrestore(&cdns->lock, flags);
+ if (cdns->wakeup_pending) {
+ cdns->wakeup_pending = false;
+ enable_irq(cdns->wakeup_irq);
+ }
+ dev_dbg(cdns->dev, "%s ends\n", __func__);
+
+ return ret;
+}
+
+static int cdns3_runtime_suspend(struct device *dev)
+{
+ return cdns3_controller_suspend(dev, PMSG_AUTO_SUSPEND);
+}
+
+static int cdns3_runtime_resume(struct device *dev)
+{
+ return cdns3_controller_resume(dev, PMSG_AUTO_RESUME);
+}
#ifdef CONFIG_PM_SLEEP
static int cdns3_suspend(struct device *dev)
@@ -604,45 +702,38 @@
struct cdns3 *cdns = dev_get_drvdata(dev);
unsigned long flags;
- if (cdns->role == USB_ROLE_HOST)
- return 0;
-
if (pm_runtime_status_suspended(dev))
pm_runtime_resume(dev);
if (cdns->roles[cdns->role]->suspend) {
- spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
+ spin_lock_irqsave(&cdns->lock, flags);
cdns->roles[cdns->role]->suspend(cdns, false);
- spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
+ spin_unlock_irqrestore(&cdns->lock, flags);
}
- return 0;
+ return cdns3_controller_suspend(dev, PMSG_SUSPEND);
}
static int cdns3_resume(struct device *dev)
{
- struct cdns3 *cdns = dev_get_drvdata(dev);
- unsigned long flags;
+ int ret;
- if (cdns->role == USB_ROLE_HOST)
- return 0;
-
- if (cdns->roles[cdns->role]->resume) {
- spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
- cdns->roles[cdns->role]->resume(cdns, false);
- spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
- }
+ ret = cdns3_controller_resume(dev, PMSG_RESUME);
+ if (ret)
+ return ret;
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
- return 0;
+ return ret;
}
-#endif
+#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM */
static const struct dev_pm_ops cdns3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
+ SET_RUNTIME_PM_OPS(cdns3_runtime_suspend, cdns3_runtime_resume, NULL)
};
#ifdef CONFIG_OF
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 969eb94..3176f92 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -38,6 +38,14 @@
};
#define CDNS3_XHCI_RESOURCES_NUM 2
+
+struct cdns3_platform_data {
+ int (*platform_suspend)(struct device *dev,
+ bool suspend, bool wakeup);
+ unsigned long quirks;
+#define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0)
+};
+
/**
* struct cdns3 - Representation of Cadence USB3 DRD controller.
* @dev: pointer to Cadence device struct
@@ -50,6 +58,7 @@
* @otg_regs: pointer to base of otg registers
* @otg_irq: irq number for otg controller
* @dev_irq: irq number for device controller
+ * @wakeup_irq: irq number for wakeup event, it is optional
* @roles: array of supported roles for this controller
* @role: current role
* @host_dev: the child host device pointer for cdns3 core
@@ -62,7 +71,11 @@
* This field based on firmware setting, kernel configuration
* and hardware configuration.
* @role_sw: pointer to role switch object.
- * @role_override: set 1 if role rely on SW.
+ * @in_lpm: indicate the controller is in low power mode
+ * @wakeup_pending: wakeup interrupt pending
+ * @pdata: platform data from glue layer
+ * @lock: spinlock structure
+ * @xhci_plat_data: xhci private data structure pointer
*/
struct cdns3 {
struct device *dev;
@@ -77,9 +90,11 @@
#define CDNS3_CONTROLLER_V0 0
#define CDNS3_CONTROLLER_V1 1
u32 version;
+ bool phyrst_a_enable;
int otg_irq;
int dev_irq;
+ int wakeup_irq;
struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
enum usb_role role;
struct platform_device *host_dev;
@@ -90,7 +105,11 @@
struct mutex mutex;
enum usb_dr_mode dr_mode;
struct usb_role_switch *role_sw;
- int role_override;
+ bool in_lpm;
+ bool wakeup_pending;
+ struct cdns3_platform_data *pdata;
+ spinlock_t lock;
+ struct xhci_plat_priv *xhci_plat_data;
};
int cdns3_hw_role_switch(struct cdns3 *cdns);
diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
index 2c9afbf..a5c6a29 100644
--- a/drivers/usb/cdns3/debug.h
+++ b/drivers/usb/cdns3/debug.h
@@ -140,7 +140,7 @@
trb_per_sector = TRBS_PER_SEGMENT;
if (trb_per_sector > TRBS_PER_SEGMENT) {
- sprintf(str + ret, "\t\tTo big transfer ring %d\n",
+ sprintf(str + ret, "\t\tTransfer ring %d too big\n",
trb_per_sector);
return str;
}
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
index 16ad485..38ccd29 100644
--- a/drivers/usb/cdns3/drd.c
+++ b/drivers/usb/cdns3/drd.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
#include "gadget.h"
#include "drd.h"
@@ -29,7 +30,6 @@
*/
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
{
- int ret = 0;
u32 reg;
switch (mode) {
@@ -43,6 +43,18 @@
reg = readl(&cdns->otg_v1_regs->override);
reg |= OVERRIDE_IDPULLUP;
writel(reg, &cdns->otg_v1_regs->override);
+
+ /*
+ * Enable work around feature built into the
+ * controller to address issue with RX Sensitivity
+ * est (EL_17) for USB2 PHY. The issue only occures
+ * for 0x0002450D controller version.
+ */
+ if (cdns->phyrst_a_enable) {
+ reg = readl(&cdns->otg_v1_regs->phyrst_cfg);
+ reg |= PHYRST_CFG_PHYRST_A_ENABLE;
+ writel(reg, &cdns->otg_v1_regs->phyrst_cfg);
+ }
} else {
reg = readl(&cdns->otg_v0_regs->ctrl1);
reg |= OVERRIDE_IDPULLUP_V0;
@@ -61,7 +73,7 @@
return -EINVAL;
}
- return ret;
+ return 0;
}
int cdns3_get_id(struct cdns3 *cdns)
@@ -84,25 +96,25 @@
return vbus;
}
-int cdns3_is_host(struct cdns3 *cdns)
+bool cdns3_is_host(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_HOST)
- return 1;
- else if (!cdns3_get_id(cdns))
- return 1;
+ return true;
+ else if (cdns3_get_id(cdns) == CDNS3_ID_HOST)
+ return true;
- return 0;
+ return false;
}
-int cdns3_is_device(struct cdns3 *cdns)
+bool cdns3_is_device(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
- return 1;
+ return true;
else if (cdns->dr_mode == USB_DR_MODE_OTG)
- if (cdns3_get_id(cdns))
- return 1;
+ if (cdns3_get_id(cdns) == CDNS3_ID_PERIPHERAL)
+ return true;
- return 0;
+ return false;
}
/**
@@ -125,83 +137,99 @@
}
/**
- * cdns3_drd_switch_host - start/stop host
- * @cdns: Pointer to controller context structure
- * @on: 1 for start, 0 for stop
+ * cdns3_drd_host_on - start host.
+ * @cdns: Pointer to controller context structure.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+int cdns3_drd_host_on(struct cdns3 *cdns)
+{
+ u32 val;
+ int ret;
+
+ /* Enable host mode. */
+ writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS,
+ &cdns->otg_regs->cmd);
+
+ dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
+ ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
+ val & OTGSTS_XHCI_READY, 1, 100000);
+
+ if (ret)
+ dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
+
+ phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST);
+ return ret;
+}
+
+/**
+ * cdns3_drd_host_off - stop host.
+ * @cdns: Pointer to controller context structure.
+ */
+void cdns3_drd_host_off(struct cdns3 *cdns)
+{
+ u32 val;
+
+ writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+ OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+ &cdns->otg_regs->cmd);
+
+ /* Waiting till H_IDLE state.*/
+ readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
+ !(val & OTGSTATE_HOST_STATE_MASK),
+ 1, 2000000);
+ phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
+}
+
+/**
+ * cdns3_drd_gadget_on - start gadget.
+ * @cdns: Pointer to controller context structure.
*
* Returns 0 on success otherwise negative errno
*/
-int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
+int cdns3_drd_gadget_on(struct cdns3 *cdns)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
/* switch OTG core */
- if (on) {
- writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
+ writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
- dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
- ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
- val & OTGSTS_XHCI_READY,
- 1, 100000);
- if (ret) {
- dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
- return ret;
- }
- } else {
- writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
- OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
- &cdns->otg_regs->cmd);
- /* Waiting till H_IDLE state.*/
- readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
- !(val & OTGSTATE_HOST_STATE_MASK),
- 1, 2000000);
+ dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
+
+ ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
+ val & OTGSTS_DEV_READY,
+ 1, 100000);
+ if (ret) {
+ dev_err(cdns->dev, "timeout waiting for dev_ready\n");
+ return ret;
}
+ phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
return 0;
}
/**
- * cdns3_drd_switch_gadget - start/stop gadget
- * @cdns: Pointer to controller context structure
- * @on: 1 for start, 0 for stop
- *
- * Returns 0 on success otherwise negative errno
+ * cdns3_drd_gadget_off - stop gadget.
+ * @cdns: Pointer to controller context structure.
*/
-int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
+void cdns3_drd_gadget_off(struct cdns3 *cdns)
{
- int ret, val;
- u32 reg = OTGCMD_OTG_DIS;
+ u32 val;
- /* switch OTG core */
- if (on) {
- writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
-
- dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
-
- ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
- val & OTGSTS_DEV_READY,
- 1, 100000);
- if (ret) {
- dev_err(cdns->dev, "timeout waiting for dev_ready\n");
- return ret;
- }
- } else {
- /*
- * driver should wait at least 10us after disabling Device
- * before turning-off Device (DEV_BUS_DROP)
- */
- usleep_range(20, 30);
- writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
- OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
- &cdns->otg_regs->cmd);
- /* Waiting till DEV_IDLE state.*/
- readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
- !(val & OTGSTATE_DEV_STATE_MASK),
- 1, 2000000);
- }
-
- return 0;
+ /*
+ * Driver should wait at least 10us after disabling Device
+ * before turning-off Device (DEV_BUS_DROP).
+ */
+ usleep_range(20, 30);
+ writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+ OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+ &cdns->otg_regs->cmd);
+ /* Waiting till DEV_IDLE state.*/
+ readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
+ !(val & OTGSTATE_DEV_STATE_MASK),
+ 1, 2000000);
+ phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
}
/**
@@ -212,7 +240,7 @@
*/
static int cdns3_init_otg_mode(struct cdns3 *cdns)
{
- int ret = 0;
+ int ret;
cdns3_otg_disable_irq(cdns);
/* clear all interrupts */
@@ -223,7 +251,8 @@
return ret;
cdns3_otg_enable_irq(cdns);
- return ret;
+
+ return 0;
}
/**
@@ -234,7 +263,7 @@
*/
int cdns3_drd_update_mode(struct cdns3 *cdns)
{
- int ret = 0;
+ int ret;
switch (cdns->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
@@ -279,12 +308,15 @@
u32 reg;
if (cdns->dr_mode != USB_DR_MODE_OTG)
+ return IRQ_NONE;
+
+ if (cdns->in_lpm)
return ret;
reg = readl(&cdns->otg_regs->ivect);
if (!reg)
- return ret;
+ return IRQ_NONE;
if (reg & OTGIEN_ID_CHANGE_INT) {
dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
@@ -307,8 +339,8 @@
int cdns3_drd_init(struct cdns3 *cdns)
{
void __iomem *regs;
- int ret = 0;
u32 state;
+ int ret;
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
if (IS_ERR(regs))
@@ -329,7 +361,7 @@
cdns->otg_v1_regs = NULL;
cdns->otg_regs = regs;
writel(1, &cdns->otg_v0_regs->simulate);
- dev_info(cdns->dev, "DRD version v0 (%08x)\n",
+ dev_dbg(cdns->dev, "DRD version v0 (%08x)\n",
readl(&cdns->otg_v0_regs->version));
} else {
cdns->otg_v0_regs = NULL;
@@ -337,7 +369,7 @@
cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
cdns->version = CDNS3_CONTROLLER_V1;
writel(1, &cdns->otg_v1_regs->simulate);
- dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
+ dev_dbg(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
readl(&cdns->otg_v1_regs->did),
readl(&cdns->otg_v1_regs->rid));
}
@@ -359,19 +391,18 @@
cdns3_drd_thread_irq,
IRQF_SHARED,
dev_name(cdns->dev), cdns);
-
if (ret) {
dev_err(cdns->dev, "couldn't get otg_irq\n");
return ret;
}
state = readl(&cdns->otg_regs->sts);
- if (OTGSTS_OTG_NRDY(state) != 0) {
+ if (OTGSTS_OTG_NRDY(state)) {
dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
return -ENODEV;
}
- return ret;
+ return 0;
}
int cdns3_drd_exit(struct cdns3 *cdns)
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
index 04e01c4..f1ccae2 100644
--- a/drivers/usb/cdns3/drd.h
+++ b/drivers/usb/cdns3/drd.h
@@ -31,7 +31,7 @@
__le32 simulate;
__le32 override;
__le32 susp_ctrl;
- __le32 reserved4;
+ __le32 phyrst_cfg;
__le32 anasts;
__le32 adp_ramp_time;
__le32 ctrl1;
@@ -153,15 +153,23 @@
/* Only for CDNS3_CONTROLLER_V0 version */
#define OVERRIDE_IDPULLUP_V0 BIT(24)
-int cdns3_is_host(struct cdns3 *cdns);
-int cdns3_is_device(struct cdns3 *cdns);
+/* PHYRST_CFG - bitmasks */
+#define PHYRST_CFG_PHYRST_A_ENABLE BIT(0)
+
+#define CDNS3_ID_PERIPHERAL 1
+#define CDNS3_ID_HOST 0
+
+bool cdns3_is_host(struct cdns3 *cdns);
+bool cdns3_is_device(struct cdns3 *cdns);
int cdns3_get_id(struct cdns3 *cdns);
int cdns3_get_vbus(struct cdns3 *cdns);
int cdns3_drd_init(struct cdns3 *cdns);
int cdns3_drd_exit(struct cdns3 *cdns);
int cdns3_drd_update_mode(struct cdns3 *cdns);
-int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
-int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
+int cdns3_drd_gadget_on(struct cdns3 *cdns);
+void cdns3_drd_gadget_off(struct cdns3 *cdns);
+int cdns3_drd_host_on(struct cdns3 *cdns);
+void cdns3_drd_host_off(struct cdns3 *cdns);
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
#endif /* __LINUX_CDNS3_DRD */
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
index 390779e..30d3516 100644
--- a/drivers/usb/cdns3/ep0.c
+++ b/drivers/usb/cdns3/ep0.c
@@ -29,6 +29,7 @@
* @length: data length
* @erdy: set it to 1 when ERDY packet should be sent -
* exit from flow control state
+ * @zlp: add zero length packet
*/
static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
dma_addr_t dma_addr,
@@ -122,8 +123,6 @@
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
&priv_dev->regs->ep_cmd);
-
- cdns3_allow_enable_l1(priv_dev, 1);
}
/**
@@ -138,49 +137,36 @@
struct usb_ctrlrequest *ctrl_req)
{
enum usb_device_state device_state = priv_dev->gadget.state;
- struct cdns3_endpoint *priv_ep;
u32 config = le16_to_cpu(ctrl_req->wValue);
int result = 0;
- int i;
switch (device_state) {
case USB_STATE_ADDRESS:
- /* Configure non-control EPs */
- for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
- priv_ep = priv_dev->eps[i];
- if (!priv_ep)
- continue;
-
- if (priv_ep->flags & EP_CLAIMED)
- cdns3_ep_config(priv_ep);
- }
-
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
- if (result)
- return result;
+ if (result || !config)
+ goto reset_config;
- if (config) {
- cdns3_set_hw_configuration(priv_dev);
- } else {
- cdns3_hw_reset_eps_config(priv_dev);
- usb_gadget_set_state(&priv_dev->gadget,
- USB_STATE_ADDRESS);
- }
break;
case USB_STATE_CONFIGURED:
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+ if (!config && !result)
+ goto reset_config;
- if (!config && !result) {
- cdns3_hw_reset_eps_config(priv_dev);
- usb_gadget_set_state(&priv_dev->gadget,
- USB_STATE_ADDRESS);
- }
break;
default:
- result = -EINVAL;
+ return -EINVAL;
}
+ return 0;
+
+reset_config:
+ if (result != USB_GADGET_DELAYED_STATUS)
+ cdns3_hw_reset_eps_config(priv_dev);
+
+ usb_gadget_set_state(&priv_dev->gadget,
+ USB_STATE_ADDRESS);
+
return result;
}
@@ -227,7 +213,7 @@
/**
* cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
* @priv_dev: extended gadget object
- * @ctrl_req: pointer to received setup packet
+ * @ctrl: pointer to received setup packet
*
* Returns 0 if success, error code on error
*/
@@ -329,17 +315,10 @@
tmode >>= 8;
switch (tmode) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- cdns3_ep0_complete_setup(priv_dev, 0, 1);
- /**
- * Little delay to give the controller some time
- * for sending status stage.
- * This time should be less then 3ms.
- */
- mdelay(1);
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));
@@ -408,7 +387,7 @@
* Handling of GET/SET_FEATURE standard USB request
*
* @priv_dev: extended gadget object
- * @ctrl_req: pointer to received setup packet
+ * @ctrl: pointer to received setup packet
* @set: must be set to 1 for SET_FEATURE request
*
* Returns 0 if success, error code on error
@@ -617,7 +596,7 @@
{
u32 ep_sts_reg;
- cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
+ cdns3_select_ep(priv_dev, USB_DIR_OUT);
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
@@ -646,7 +625,6 @@
if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
priv_dev->wait_for_setup = 0;
- cdns3_allow_enable_l1(priv_dev, 0);
cdns3_ep0_setup_phase(priv_dev);
} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
priv_dev->ep0_data_dir = dir;
@@ -661,6 +639,9 @@
/**
* cdns3_gadget_ep0_enable
+ * @ep: pointer to endpoint zero object
+ * @desc: pointer to usb endpoint descriptor
+ *
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active
*/
@@ -672,6 +653,8 @@
/**
* cdns3_gadget_ep0_disable
+ * @ep: pointer to endpoint zero object
+ *
* Function shouldn't be called by gadget driver,
* endpoint 0 is allways active
*/
@@ -708,9 +691,9 @@
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
unsigned long flags;
- int erdy_sent = 0;
int ret = 0;
u8 zlp = 0;
+ int i;
spin_lock_irqsave(&priv_dev->lock, flags);
trace_cdns3_ep0_queue(priv_dev, request);
@@ -723,15 +706,27 @@
/* send STATUS stage. Should be called only for SET_CONFIGURATION */
if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
+ u32 val;
+
cdns3_select_ep(priv_dev, 0x00);
- erdy_sent = !priv_dev->hw_configured_flag;
+ /*
+ * Configure all non-control EPs which are not enabled by class driver
+ */
+ for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+ priv_ep = priv_dev->eps[i];
+ if (priv_ep && priv_ep->flags & EP_CLAIMED &&
+ !(priv_ep->flags & EP_ENABLED))
+ cdns3_ep_config(priv_ep, 0);
+ }
+
cdns3_set_hw_configuration(priv_dev);
-
- if (!erdy_sent)
- cdns3_ep0_complete_setup(priv_dev, 0, 1);
-
- cdns3_allow_enable_l1(priv_dev, 1);
+ cdns3_ep0_complete_setup(priv_dev, 0, 1);
+ /* wait until configuration set */
+ ret = readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val,
+ val & USB_STS_CFGSTS_MASK, 1, 100);
+ if (ret == -ETIMEDOUT)
+ dev_warn(priv_dev->dev, "timeout for waiting configuration set\n");
request->actual = 0;
priv_dev->status_completion_no_call = true;
@@ -745,7 +740,7 @@
* ep0_queue is back.
*/
queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
- return 0;
+ return ret;
}
if (!list_empty(&priv_ep->pending_req_list)) {
@@ -817,6 +812,7 @@
struct cdns3_usb_regs __iomem *regs;
struct cdns3_endpoint *priv_ep;
u32 max_packet_size = 64;
+ u32 ep_cfg;
regs = priv_dev->regs;
@@ -848,8 +844,10 @@
BIT(0) | BIT(16));
}
- writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
- ®s->ep_cfg);
+ ep_cfg = EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size);
+
+ if (!(priv_ep->flags & EP_CONFIGURED))
+ writel(ep_cfg, ®s->ep_cfg);
writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
®s->ep_sts_en);
@@ -857,8 +855,10 @@
/* init ep in */
cdns3_select_ep(priv_dev, USB_DIR_IN);
- writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
- ®s->ep_cfg);
+ if (!(priv_ep->flags & EP_CONFIGURED))
+ writel(ep_cfg, ®s->ep_cfg);
+
+ priv_ep->flags |= EP_CONFIGURED;
writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en);
@@ -868,7 +868,7 @@
/**
* cdns3_init_ep0 Initializes software endpoint 0 of gadget
* @priv_dev: extended gadget object
- * @ep_priv: extended endpoint object
+ * @priv_ep: extended endpoint object
*
* Returns 0 on success else error code.
*/
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index c3bf54c..e111622 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -71,6 +71,23 @@
struct usb_request *request,
gfp_t gfp_flags);
+static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+ struct usb_request *request);
+
+static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep,
+ struct usb_request *request);
+
+/**
+ * cdns3_clear_register_bit - clear bit in given register.
+ * @ptr: address of device controller register to be read and changed
+ * @mask: bits requested to clar
+ */
+static void cdns3_clear_register_bit(void __iomem *ptr, u32 mask)
+{
+ mask = readl(ptr) & ~mask;
+ writel(mask, ptr);
+}
+
/**
* cdns3_set_register_bit - set bit in given register.
* @ptr: address of device controller register to be read and changed
@@ -120,7 +137,7 @@
*
* Returns buffer or NULL if no buffers in list
*/
-struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list)
+static struct cdns3_aligned_buf *cdns3_next_align_buf(struct list_head *list)
{
return list_first_entry_or_null(list, struct cdns3_aligned_buf, list);
}
@@ -131,7 +148,7 @@
*
* Returns request or NULL if no requests in list
*/
-struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
+static struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
{
return list_first_entry_or_null(list, struct cdns3_request, list);
}
@@ -150,6 +167,21 @@
writel(ep, &priv_dev->regs->ep_sel);
}
+/**
+ * cdns3_get_tdl - gets current tdl for selected endpoint.
+ * @priv_dev: extended gadget object
+ *
+ * Before calling this function the appropriate endpoint must
+ * be selected by means of cdns3_select_ep function.
+ */
+static int cdns3_get_tdl(struct cdns3_device *priv_dev)
+{
+ if (priv_dev->dev_ver < DEV_VER_V3)
+ return EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd));
+ else
+ return readl(&priv_dev->regs->ep_tdl);
+}
+
dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
struct cdns3_trb *trb)
{
@@ -158,7 +190,7 @@
return priv_ep->trb_pool_dma + offset;
}
-int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
+static int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
{
switch (priv_ep->type) {
case USB_ENDPOINT_XFER_ISOC:
@@ -166,7 +198,22 @@
case USB_ENDPOINT_XFER_CONTROL:
return TRB_CTRL_RING_SIZE;
default:
- return TRB_RING_SIZE;
+ if (priv_ep->use_streams)
+ return TRB_STREAM_RING_SIZE;
+ else
+ return TRB_RING_SIZE;
+ }
+}
+
+static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+ if (priv_ep->trb_pool) {
+ dma_free_coherent(priv_dev->sysdev,
+ cdns3_ring_size(priv_ep),
+ priv_ep->trb_pool, priv_ep->trb_pool_dma);
+ priv_ep->trb_pool = NULL;
}
}
@@ -180,8 +227,12 @@
{
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
int ring_size = cdns3_ring_size(priv_ep);
+ int num_trbs = ring_size / TRB_SIZE;
struct cdns3_trb *link_trb;
+ if (priv_ep->trb_pool && priv_ep->alloc_ring_size < ring_size)
+ cdns3_free_trb_pool(priv_ep);
+
if (!priv_ep->trb_pool) {
priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev,
ring_size,
@@ -189,32 +240,31 @@
GFP_DMA32 | GFP_ATOMIC);
if (!priv_ep->trb_pool)
return -ENOMEM;
+
+ priv_ep->alloc_ring_size = ring_size;
}
memset(priv_ep->trb_pool, 0, ring_size);
+ priv_ep->num_trbs = num_trbs;
+
if (!priv_ep->num)
return 0;
- priv_ep->num_trbs = ring_size / TRB_SIZE;
- /* Initialize the last TRB as Link TRB. */
+ /* Initialize the last TRB as Link TRB */
link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
- link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
- link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE;
- return 0;
-}
-
-static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
-{
- struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
-
- if (priv_ep->trb_pool) {
- dma_free_coherent(priv_dev->sysdev,
- cdns3_ring_size(priv_ep),
- priv_ep->trb_pool, priv_ep->trb_pool_dma);
- priv_ep->trb_pool = NULL;
+ if (priv_ep->use_streams) {
+ /*
+ * For stream capable endpoints driver use single correct TRB.
+ * The last trb has zeroed cycle bit
+ */
+ link_trb->control = 0;
+ } else {
+ link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma));
+ link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE);
}
+ return 0;
}
/**
@@ -246,6 +296,8 @@
*/
void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
{
+ int i;
+
writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
cdns3_allow_enable_l1(priv_dev, 0);
@@ -253,6 +305,11 @@
priv_dev->onchip_used_size = 0;
priv_dev->out_mem_is_allocated = 0;
priv_dev->wait_for_setup = 0;
+ priv_dev->using_streams = 0;
+
+ for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
+ if (priv_dev->eps[i])
+ priv_dev->eps[i]->flags &= ~EP_CONFIGURED;
}
/**
@@ -295,7 +352,7 @@
cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
}
-void cdns3_move_deq_to_next_trb(struct cdns3_request *priv_req)
+static void cdns3_move_deq_to_next_trb(struct cdns3_request *priv_req)
{
struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
int current_trb = priv_req->start_trb;
@@ -356,17 +413,43 @@
{
struct usb_request *request;
int ret = 0;
+ u8 pending_empty = list_empty(&priv_ep->pending_req_list);
+
+ /*
+ * If the last pending transfer is INTERNAL
+ * OR streams are enabled for this endpoint
+ * do NOT start new transfer till the last one is pending
+ */
+ if (!pending_empty) {
+ struct cdns3_request *priv_req;
+
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+ priv_req = to_cdns3_request(request);
+ if ((priv_req->flags & REQUEST_INTERNAL) ||
+ (priv_ep->flags & EP_TDLCHK_EN) ||
+ priv_ep->use_streams) {
+ dev_dbg(priv_dev->dev, "Blocking external request\n");
+ return ret;
+ }
+ }
while (!list_empty(&priv_ep->deferred_req_list)) {
request = cdns3_next_request(&priv_ep->deferred_req_list);
- ret = cdns3_ep_run_transfer(priv_ep, request);
+ if (!priv_ep->use_streams) {
+ ret = cdns3_ep_run_transfer(priv_ep, request);
+ } else {
+ priv_ep->stream_sg_idx = 0;
+ ret = cdns3_ep_run_stream_transfer(priv_ep, request);
+ }
if (ret)
return ret;
list_del(&request->list);
list_add_tail(&request->list,
&priv_ep->pending_req_list);
+ if (request->stream_id != 0 || (priv_ep->flags & EP_TDLCHK_EN))
+ break;
}
priv_ep->flags &= ~EP_RING_FULL;
@@ -379,12 +462,42 @@
* buffer for unblocking on-chip FIFO buffer. This flag will be cleared
* if before first DESCMISS interrupt the DMA will be armed.
*/
-#define cdns3_wa2_enable_detection(priv_dev, ep_priv, reg) do { \
+#define cdns3_wa2_enable_detection(priv_dev, priv_ep, reg) do { \
if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) { \
priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET; \
(reg) |= EP_STS_EN_DESCMISEN; \
} } while (0)
+static void __cdns3_descmiss_copy_data(struct usb_request *request,
+ struct usb_request *descmiss_req)
+{
+ int length = request->actual + descmiss_req->actual;
+ struct scatterlist *s = request->sg;
+
+ if (!s) {
+ if (length <= request->length) {
+ memcpy(&((u8 *)request->buf)[request->actual],
+ descmiss_req->buf,
+ descmiss_req->actual);
+ request->actual = length;
+ } else {
+ /* It should never occures */
+ request->status = -ENOMEM;
+ }
+ } else {
+ if (length <= sg_dma_len(s)) {
+ void *p = phys_to_virt(sg_dma_address(s));
+
+ memcpy(&((u8 *)p)[request->actual],
+ descmiss_req->buf,
+ descmiss_req->actual);
+ request->actual = length;
+ } else {
+ request->status = -ENOMEM;
+ }
+ }
+}
+
/**
* cdns3_wa2_descmiss_copy_data copy data from internal requests to
* request queued by class driver.
@@ -399,7 +512,6 @@
while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
int chunk_end;
- int length;
descmiss_priv_req =
cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
@@ -410,22 +522,9 @@
break;
chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
- length = request->actual + descmiss_req->actual;
-
request->status = descmiss_req->status;
-
- if (length <= request->length) {
- memcpy(&((u8 *)request->buf)[request->actual],
- descmiss_req->buf,
- descmiss_req->actual);
- request->actual = length;
- } else {
- /* It should never occures */
- request->status = -ENOMEM;
- }
-
+ __cdns3_descmiss_copy_data(request, descmiss_req);
list_del_init(&descmiss_priv_req->list);
-
kfree(descmiss_req->buf);
cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
--priv_ep->wa2_counter;
@@ -435,9 +534,9 @@
}
}
-struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep,
- struct cdns3_request *priv_req)
+static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req)
{
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
priv_req->flags & REQUEST_INTERNAL) {
@@ -450,10 +549,17 @@
if (!req)
return NULL;
+ /* unmap the gadget request before copying data */
+ usb_gadget_unmap_request_by_dev(priv_dev->sysdev, req,
+ priv_ep->dir);
+
cdns3_wa2_descmiss_copy_data(priv_ep, req);
if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
req->length != req->actual) {
/* wait for next part of transfer */
+ /* re-map the gadget request buffer*/
+ usb_gadget_map_request_by_dev(priv_dev->sysdev, req,
+ usb_endpoint_dir_in(priv_ep->endpoint.desc));
return NULL;
}
@@ -468,9 +574,9 @@
return &priv_req->request;
}
-int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep,
- struct cdns3_request *priv_req)
+static int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep,
+ struct cdns3_request *priv_req)
{
int deferred = 0;
@@ -561,7 +667,7 @@
/**
* cdns3_wa2_descmissing_packet - handles descriptor missing event.
- * @priv_dev: extended gadget object
+ * @priv_ep: extended gadget object
*
* This function is used only for WA2. For more information see Work around 2
* description.
@@ -570,6 +676,13 @@
{
struct cdns3_request *priv_req;
struct usb_request *request;
+ u8 pending_empty = list_empty(&priv_ep->pending_req_list);
+
+ /* check for pending transfer */
+ if (!pending_empty) {
+ trace_cdns3_wa2(priv_ep, "Ignoring Descriptor missing IRQ\n");
+ return;
+ }
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
@@ -578,8 +691,10 @@
trace_cdns3_wa2(priv_ep, "Description Missing detected\n");
- if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS)
+ if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS) {
+ trace_cdns3_wa2(priv_ep, "WA2 overflow\n");
cdns3_wa2_remove_old_request(priv_ep);
+ }
request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
GFP_ATOMIC);
@@ -621,6 +736,78 @@
"Failed: No sufficient memory for DESCMIS\n");
}
+static void cdns3_wa2_reset_tdl(struct cdns3_device *priv_dev)
+{
+ u16 tdl = EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd));
+
+ if (tdl) {
+ u16 reset_val = EP_CMD_TDL_MAX + 1 - tdl;
+
+ writel(EP_CMD_TDL_SET(reset_val) | EP_CMD_STDL,
+ &priv_dev->regs->ep_cmd);
+ }
+}
+
+static void cdns3_wa2_check_outq_status(struct cdns3_device *priv_dev)
+{
+ u32 ep_sts_reg;
+
+ /* select EP0-out */
+ cdns3_select_ep(priv_dev, 0);
+
+ ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+
+ if (EP_STS_OUTQ_VAL(ep_sts_reg)) {
+ u32 outq_ep_num = EP_STS_OUTQ_NO(ep_sts_reg);
+ struct cdns3_endpoint *outq_ep = priv_dev->eps[outq_ep_num];
+
+ if ((outq_ep->flags & EP_ENABLED) && !(outq_ep->use_streams) &&
+ outq_ep->type != USB_ENDPOINT_XFER_ISOC && outq_ep_num) {
+ u8 pending_empty = list_empty(&outq_ep->pending_req_list);
+
+ if ((outq_ep->flags & EP_QUIRK_EXTRA_BUF_DET) ||
+ (outq_ep->flags & EP_QUIRK_EXTRA_BUF_EN) ||
+ !pending_empty) {
+ } else {
+ u32 ep_sts_en_reg;
+ u32 ep_cmd_reg;
+
+ cdns3_select_ep(priv_dev, outq_ep->num |
+ outq_ep->dir);
+ ep_sts_en_reg = readl(&priv_dev->regs->ep_sts_en);
+ ep_cmd_reg = readl(&priv_dev->regs->ep_cmd);
+
+ outq_ep->flags |= EP_TDLCHK_EN;
+ cdns3_set_register_bit(&priv_dev->regs->ep_cfg,
+ EP_CFG_TDL_CHK);
+
+ cdns3_wa2_enable_detection(priv_dev, outq_ep,
+ ep_sts_en_reg);
+ writel(ep_sts_en_reg,
+ &priv_dev->regs->ep_sts_en);
+ /* reset tdl value to zero */
+ cdns3_wa2_reset_tdl(priv_dev);
+ /*
+ * Memory barrier - Reset tdl before ringing the
+ * doorbell.
+ */
+ wmb();
+ if (EP_CMD_DRDY & ep_cmd_reg) {
+ trace_cdns3_wa2(outq_ep, "Enabling WA2 skipping doorbell\n");
+
+ } else {
+ trace_cdns3_wa2(outq_ep, "Enabling WA2 ringing doorbell\n");
+ /*
+ * ring doorbell to generate DESCMIS irq
+ */
+ writel(EP_CMD_DRDY,
+ &priv_dev->regs->ep_cmd);
+ }
+ }
+ }
+ }
+}
+
/**
* cdns3_gadget_giveback - call struct usb_request's ->complete callback
* @priv_ep: The endpoint to whom the request belongs to
@@ -652,6 +839,8 @@
request->length);
priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
+ /* All TRBs have finished, clear the counter */
+ priv_req->finished_trb = 0;
trace_cdns3_gadget_giveback(priv_req);
if (priv_dev->dev_ver < DEV_VER_V2) {
@@ -672,7 +861,7 @@
cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
}
-void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
+static void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
{
/* Work around for stale data address in TRB*/
if (priv_ep->wa1_set) {
@@ -682,10 +871,10 @@
priv_ep->wa1_trb_index = 0xFFFF;
if (priv_ep->wa1_cycle_bit) {
priv_ep->wa1_trb->control =
- priv_ep->wa1_trb->control | 0x1;
+ priv_ep->wa1_trb->control | cpu_to_le32(0x1);
} else {
priv_ep->wa1_trb->control =
- priv_ep->wa1_trb->control & ~0x1;
+ priv_ep->wa1_trb->control & cpu_to_le32(~0x1);
}
}
}
@@ -807,30 +996,153 @@
cdns3_wa1_restore_cycle_bit(priv_ep);
}
-/**
- * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
- * @priv_ep: endpoint object
- *
- * Returns zero on success or negative value on failure
- */
-int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
- struct usb_request *request)
+static int cdns3_ep_run_stream_transfer(struct cdns3_endpoint *priv_ep,
+ struct usb_request *request)
{
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
struct cdns3_request *priv_req;
struct cdns3_trb *trb;
dma_addr_t trb_dma;
+ int address;
+ u32 control;
+ u32 length;
+ u32 tdl;
+ unsigned int sg_idx = priv_ep->stream_sg_idx;
+
+ priv_req = to_cdns3_request(request);
+ address = priv_ep->endpoint.desc->bEndpointAddress;
+
+ priv_ep->flags |= EP_PENDING_REQUEST;
+
+ /* must allocate buffer aligned to 8 */
+ if (priv_req->flags & REQUEST_UNALIGNED)
+ trb_dma = priv_req->aligned_buf->dma;
+ else
+ trb_dma = request->dma;
+
+ /* For stream capable endpoints driver use only single TD. */
+ trb = priv_ep->trb_pool + priv_ep->enqueue;
+ priv_req->start_trb = priv_ep->enqueue;
+ priv_req->end_trb = priv_req->start_trb;
+ priv_req->trb = trb;
+
+ cdns3_select_ep(priv_ep->cdns3_dev, address);
+
+ control = TRB_TYPE(TRB_NORMAL) | TRB_CYCLE |
+ TRB_STREAM_ID(priv_req->request.stream_id) | TRB_ISP;
+
+ if (!request->num_sgs) {
+ trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
+ length = request->length;
+ } else {
+ trb->buffer = cpu_to_le32(TRB_BUFFER(request->sg[sg_idx].dma_address));
+ length = request->sg[sg_idx].length;
+ }
+
+ tdl = DIV_ROUND_UP(length, priv_ep->endpoint.maxpacket);
+
+ trb->length = cpu_to_le32(TRB_BURST_LEN(16) | TRB_LEN(length));
+
+ /*
+ * For DEV_VER_V2 controller version we have enabled
+ * USB_CONF2_EN_TDL_TRB in DMULT configuration.
+ * This enables TDL calculation based on TRB, hence setting TDL in TRB.
+ */
+ if (priv_dev->dev_ver >= DEV_VER_V2) {
+ if (priv_dev->gadget.speed == USB_SPEED_SUPER)
+ trb->length |= cpu_to_le32(TRB_TDL_SS_SIZE(tdl));
+ }
+ priv_req->flags |= REQUEST_PENDING;
+
+ trb->control = cpu_to_le32(control);
+
+ trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
+
+ /*
+ * Memory barrier - Cycle Bit must be set before trb->length and
+ * trb->buffer fields.
+ */
+ wmb();
+
+ /* always first element */
+ writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
+ &priv_dev->regs->ep_traddr);
+
+ if (!(priv_ep->flags & EP_STALLED)) {
+ trace_cdns3_ring(priv_ep);
+ /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
+ writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
+
+ priv_ep->prime_flag = false;
+
+ /*
+ * Controller version DEV_VER_V2 tdl calculation
+ * is based on TRB
+ */
+
+ if (priv_dev->dev_ver < DEV_VER_V2)
+ writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL,
+ &priv_dev->regs->ep_cmd);
+ else if (priv_dev->dev_ver > DEV_VER_V2)
+ writel(tdl, &priv_dev->regs->ep_tdl);
+
+ priv_ep->last_stream_id = priv_req->request.stream_id;
+ writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+ writel(EP_CMD_ERDY_SID(priv_req->request.stream_id) |
+ EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
+
+ trace_cdns3_doorbell_epx(priv_ep->name,
+ readl(&priv_dev->regs->ep_traddr));
+ }
+
+ /* WORKAROUND for transition to L0 */
+ __cdns3_gadget_wakeup(priv_dev);
+
+ return 0;
+}
+
+static void cdns3_rearm_drdy_if_needed(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+ if (priv_dev->dev_ver < DEV_VER_V3)
+ return;
+
+ if (readl(&priv_dev->regs->ep_sts) & EP_STS_TRBERR) {
+ writel(EP_STS_TRBERR, &priv_dev->regs->ep_sts);
+ writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+ }
+}
+
+/**
+ * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
+ * @priv_ep: endpoint object
+ * @request: request object
+ *
+ * Returns zero on success or negative value on failure
+ */
+static int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+ struct usb_request *request)
+{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+ struct cdns3_request *priv_req;
+ struct cdns3_trb *trb;
+ struct cdns3_trb *link_trb = NULL;
+ dma_addr_t trb_dma;
u32 togle_pcs = 1;
int sg_iter = 0;
int num_trb;
int address;
u32 control;
int pcs;
+ u16 total_tdl = 0;
+ struct scatterlist *s = NULL;
+ bool sg_supported = !!(request->num_mapped_sgs);
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
num_trb = priv_ep->interval;
else
- num_trb = request->num_sgs ? request->num_sgs : 1;
+ num_trb = sg_supported ? request->num_mapped_sgs : 1;
if (num_trb > priv_ep->free_trbs) {
priv_ep->flags |= EP_RING_FULL;
@@ -856,7 +1168,6 @@
/* prepare ring */
if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) {
- struct cdns3_trb *link_trb;
int doorbell, dma_index;
u32 ch_bit = 0;
@@ -883,41 +1194,49 @@
TRBS_PER_SEGMENT > 2)
ch_bit = TRB_CHAIN;
- link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
- TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit;
+ link_trb->control = cpu_to_le32(((priv_ep->pcs) ? TRB_CYCLE : 0) |
+ TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit);
}
if (priv_dev->dev_ver <= DEV_VER_V2)
togle_pcs = cdns3_wa1_update_guard(priv_ep, trb);
+ if (sg_supported)
+ s = request->sg;
+
/* set incorrect Cycle Bit for first trb*/
control = priv_ep->pcs ? 0 : TRB_CYCLE;
+ trb->length = 0;
+ if (priv_dev->dev_ver >= DEV_VER_V2) {
+ u16 td_size;
+
+ td_size = DIV_ROUND_UP(request->length,
+ priv_ep->endpoint.maxpacket);
+ if (priv_dev->gadget.speed == USB_SPEED_SUPER)
+ trb->length = TRB_TDL_SS_SIZE(td_size);
+ else
+ control |= TRB_TDL_HS_SIZE(td_size);
+ }
do {
u32 length;
- u16 td_size = 0;
/* fill TRB */
control |= TRB_TYPE(TRB_NORMAL);
- trb->buffer = TRB_BUFFER(request->num_sgs == 0
- ? trb_dma : request->sg[sg_iter].dma_address);
-
- if (likely(!request->num_sgs))
+ if (sg_supported) {
+ trb->buffer = cpu_to_le32(TRB_BUFFER(sg_dma_address(s)));
+ length = sg_dma_len(s);
+ } else {
+ trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
length = request->length;
- else
- length = request->sg[sg_iter].length;
+ }
- if (likely(priv_dev->dev_ver >= DEV_VER_V2))
- td_size = DIV_ROUND_UP(length,
+ if (priv_ep->flags & EP_TDLCHK_EN)
+ total_tdl += DIV_ROUND_UP(length,
priv_ep->endpoint.maxpacket);
- trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) |
- TRB_LEN(length);
- if (priv_dev->gadget.speed == USB_SPEED_SUPER)
- trb->length |= TRB_TDL_SS_SIZE(td_size);
- else
- control |= TRB_TDL_HS_SIZE(td_size);
-
+ trb->length |= cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) |
+ TRB_LEN(length));
pcs = priv_ep->pcs ? TRB_CYCLE : 0;
/*
@@ -936,23 +1255,51 @@
}
if (sg_iter)
- trb->control = control;
+ trb->control = cpu_to_le32(control);
else
- priv_req->trb->control = control;
+ priv_req->trb->control = cpu_to_le32(control);
+
+ if (sg_supported) {
+ trb->control |= TRB_ISP;
+ /* Don't set chain bit for last TRB */
+ if (sg_iter < num_trb - 1)
+ trb->control |= TRB_CHAIN;
+
+ s = sg_next(s);
+ }
control = 0;
++sg_iter;
priv_req->end_trb = priv_ep->enqueue;
cdns3_ep_inc_enq(priv_ep);
trb = priv_ep->trb_pool + priv_ep->enqueue;
+ trb->length = 0;
} while (sg_iter < num_trb);
trb = priv_req->trb;
priv_req->flags |= REQUEST_PENDING;
+ priv_req->num_of_trb = num_trb;
if (sg_iter == 1)
- trb->control |= TRB_IOC | TRB_ISP;
+ trb->control |= cpu_to_le32(TRB_IOC | TRB_ISP);
+
+ if (priv_dev->dev_ver < DEV_VER_V2 &&
+ (priv_ep->flags & EP_TDLCHK_EN)) {
+ u16 tdl = total_tdl;
+ u16 old_tdl = EP_CMD_TDL_GET(readl(&priv_dev->regs->ep_cmd));
+
+ if (tdl > EP_CMD_TDL_MAX) {
+ tdl = EP_CMD_TDL_MAX;
+ priv_ep->pending_tdl = total_tdl - EP_CMD_TDL_MAX;
+ }
+
+ if (old_tdl < tdl) {
+ tdl -= old_tdl;
+ writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL,
+ &priv_dev->regs->ep_cmd);
+ }
+ }
/*
* Memory barrier - cycle bit must be set before other filds in trb.
@@ -961,12 +1308,27 @@
/* give the TD to the consumer*/
if (togle_pcs)
- trb->control = trb->control ^ 1;
+ trb->control = trb->control ^ cpu_to_le32(1);
if (priv_dev->dev_ver <= DEV_VER_V2)
cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep);
- trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
+ if (num_trb > 1) {
+ int i = 0;
+
+ while (i < num_trb) {
+ trace_cdns3_prepare_trb(priv_ep, trb + i);
+ if (trb + i == link_trb) {
+ trb = priv_ep->trb_pool;
+ num_trb = num_trb - i;
+ i = 0;
+ } else {
+ i++;
+ }
+ }
+ } else {
+ trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
+ }
/*
* Memory barrier - Cycle Bit must be set before trb->length and
@@ -1003,6 +1365,7 @@
/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+ cdns3_rearm_drdy_if_needed(priv_ep);
trace_cdns3_doorbell_epx(priv_ep->name,
readl(&priv_dev->regs->ep_traddr));
}
@@ -1017,21 +1380,15 @@
{
struct cdns3_endpoint *priv_ep;
struct usb_ep *ep;
- int val;
if (priv_dev->hw_configured_flag)
return;
writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
- writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
cdns3_set_register_bit(&priv_dev->regs->usb_conf,
USB_CONF_U1EN | USB_CONF_U2EN);
- /* wait until configuration set */
- readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val,
- val & USB_STS_CFGSTS_MASK, 1, 100);
-
priv_dev->hw_configured_flag = 1;
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
@@ -1040,10 +1397,12 @@
cdns3_start_all_request(priv_dev, priv_ep);
}
}
+
+ cdns3_allow_enable_l1(priv_dev, 1);
}
/**
- * cdns3_request_handled - check whether request has been handled by DMA
+ * cdns3_trb_handled - check whether trb has been handled by DMA
*
* @priv_ep: extended endpoint object.
* @priv_req: request object for checking
@@ -1060,36 +1419,32 @@
* ET = priv_req->end_trb - index of last TRB in transfer ring
* CI = current_index - index of processed TRB by DMA.
*
- * As first step, function checks if cycle bit for priv_req->start_trb is
- * correct.
+ * As first step, we check if the TRB between the ST and ET.
+ * Then, we check if cycle bit for index priv_ep->dequeue
+ * is correct.
*
* some rules:
- * 1. priv_ep->dequeue never exceed current_index.
+ * 1. priv_ep->dequeue never equals to current_index.
* 2 priv_ep->enqueue never exceed priv_ep->dequeue
* 3. exception: priv_ep->enqueue == priv_ep->dequeue
* and priv_ep->free_trbs is zero.
* This case indicate that TR is full.
*
- * Then We can split recognition into two parts:
+ * At below two cases, the request have been handled.
* Case 1 - priv_ep->dequeue < current_index
* SR ... EQ ... DQ ... CI ... ER
* SR ... DQ ... CI ... EQ ... ER
*
- * Request has been handled by DMA if ST and ET is between DQ and CI.
- *
* Case 2 - priv_ep->dequeue > current_index
- * This situation take place when CI go through the LINK TRB at the end of
+ * This situation takes place when CI go through the LINK TRB at the end of
* transfer ring.
* SR ... CI ... EQ ... DQ ... ER
- *
- * Request has been handled by DMA if ET is less then CI or
- * ET is greater or equal DQ.
*/
-static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
+static bool cdns3_trb_handled(struct cdns3_endpoint *priv_ep,
struct cdns3_request *priv_req)
{
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
- struct cdns3_trb *trb = priv_req->trb;
+ struct cdns3_trb *trb;
int current_index = 0;
int handled = 0;
int doorbell;
@@ -1097,9 +1452,27 @@
current_index = cdns3_get_dma_pos(priv_dev, priv_ep);
doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
- trb = &priv_ep->trb_pool[priv_req->start_trb];
+ /* current trb doesn't belong to this request */
+ if (priv_req->start_trb < priv_req->end_trb) {
+ if (priv_ep->dequeue > priv_req->end_trb)
+ goto finish;
- if ((trb->control & TRB_CYCLE) != priv_ep->ccs)
+ if (priv_ep->dequeue < priv_req->start_trb)
+ goto finish;
+ }
+
+ if ((priv_req->start_trb > priv_req->end_trb) &&
+ (priv_ep->dequeue > priv_req->end_trb) &&
+ (priv_ep->dequeue < priv_req->start_trb))
+ goto finish;
+
+ if ((priv_req->start_trb == priv_req->end_trb) &&
+ (priv_ep->dequeue != priv_req->end_trb))
+ goto finish;
+
+ trb = &priv_ep->trb_pool[priv_ep->dequeue];
+
+ if ((le32_to_cpu(trb->control) & TRB_CYCLE) != priv_ep->ccs)
goto finish;
if (doorbell == 1 && current_index == priv_ep->dequeue)
@@ -1119,12 +1492,8 @@
!priv_ep->dequeue)
goto finish;
- if (priv_req->end_trb >= priv_ep->dequeue &&
- priv_req->end_trb < current_index)
- handled = 1;
+ handled = 1;
} else if (priv_ep->dequeue > current_index) {
- if (priv_req->end_trb < current_index ||
- priv_req->end_trb >= priv_ep->dequeue)
handled = 1;
}
@@ -1140,6 +1509,8 @@
struct cdns3_request *priv_req;
struct usb_request *request;
struct cdns3_trb *trb;
+ bool request_handled = false;
+ bool transfer_end = false;
while (!list_empty(&priv_ep->pending_req_list)) {
request = cdns3_next_request(&priv_ep->pending_req_list);
@@ -1148,34 +1519,73 @@
trb = priv_ep->trb_pool + priv_ep->dequeue;
/* Request was dequeued and TRB was changed to TRB_LINK. */
- if (TRB_FIELD_TO_TYPE(trb->control) == TRB_LINK) {
+ if (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK) {
trace_cdns3_complete_trb(priv_ep, trb);
cdns3_move_deq_to_next_trb(priv_req);
}
- /* Re-select endpoint. It could be changed by other CPU during
- * handling usb_gadget_giveback_request.
- */
- cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
+ if (!request->stream_id) {
+ /* Re-select endpoint. It could be changed by other CPU
+ * during handling usb_gadget_giveback_request.
+ */
+ cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
- if (!cdns3_request_handled(priv_ep, priv_req))
- goto prepare_next_td;
+ while (cdns3_trb_handled(priv_ep, priv_req)) {
+ priv_req->finished_trb++;
+ if (priv_req->finished_trb >= priv_req->num_of_trb)
+ request_handled = true;
- trb = priv_ep->trb_pool + priv_ep->dequeue;
- trace_cdns3_complete_trb(priv_ep, trb);
+ trb = priv_ep->trb_pool + priv_ep->dequeue;
+ trace_cdns3_complete_trb(priv_ep, trb);
- if (trb != priv_req->trb)
- dev_warn(priv_dev->dev,
- "request_trb=0x%p, queue_trb=0x%p\n",
- priv_req->trb, trb);
+ if (!transfer_end)
+ request->actual +=
+ TRB_LEN(le32_to_cpu(trb->length));
- request->actual = TRB_LEN(le32_to_cpu(trb->length));
- cdns3_move_deq_to_next_trb(priv_req);
- cdns3_gadget_giveback(priv_ep, priv_req, 0);
+ if (priv_req->num_of_trb > 1 &&
+ le32_to_cpu(trb->control) & TRB_SMM)
+ transfer_end = true;
- if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
- TRBS_PER_SEGMENT == 2)
+ cdns3_ep_inc_deq(priv_ep);
+ }
+
+ if (request_handled) {
+ cdns3_gadget_giveback(priv_ep, priv_req, 0);
+ request_handled = false;
+ transfer_end = false;
+ } else {
+ goto prepare_next_td;
+ }
+
+ if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
+ TRBS_PER_SEGMENT == 2)
+ break;
+ } else {
+ /* Re-select endpoint. It could be changed by other CPU
+ * during handling usb_gadget_giveback_request.
+ */
+ cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
+
+ trb = priv_ep->trb_pool;
+ trace_cdns3_complete_trb(priv_ep, trb);
+
+ if (trb != priv_req->trb)
+ dev_warn(priv_dev->dev,
+ "request_trb=0x%p, queue_trb=0x%p\n",
+ priv_req->trb, trb);
+
+ request->actual += TRB_LEN(le32_to_cpu(trb->length));
+
+ if (!request->num_sgs ||
+ (request->num_sgs == (priv_ep->stream_sg_idx + 1))) {
+ priv_ep->stream_sg_idx = 0;
+ cdns3_gadget_giveback(priv_ep, priv_req, 0);
+ } else {
+ priv_ep->stream_sg_idx++;
+ cdns3_ep_run_stream_transfer(priv_ep, request);
+ }
break;
+ }
}
priv_ep->flags &= ~EP_PENDING_REQUEST;
@@ -1205,6 +1615,21 @@
}
}
+static void cdns3_reprogram_tdl(struct cdns3_endpoint *priv_ep)
+{
+ u16 tdl = priv_ep->pending_tdl;
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+ if (tdl > EP_CMD_TDL_MAX) {
+ tdl = EP_CMD_TDL_MAX;
+ priv_ep->pending_tdl -= EP_CMD_TDL_MAX;
+ } else {
+ priv_ep->pending_tdl = 0;
+ }
+
+ writel(EP_CMD_TDL_SET(tdl) | EP_CMD_STDL, &priv_dev->regs->ep_cmd);
+}
+
/**
* cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
* @priv_ep: endpoint object
@@ -1215,6 +1640,9 @@
{
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
u32 ep_sts_reg;
+ struct usb_request *deferred_request;
+ struct usb_request *pending_request;
+ u32 tdl = 0;
cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
@@ -1223,6 +1651,36 @@
ep_sts_reg = readl(&priv_dev->regs->ep_sts);
writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+ if ((ep_sts_reg & EP_STS_PRIME) && priv_ep->use_streams) {
+ bool dbusy = !!(ep_sts_reg & EP_STS_DBUSY);
+
+ tdl = cdns3_get_tdl(priv_dev);
+
+ /*
+ * Continue the previous transfer:
+ * There is some racing between ERDY and PRIME. The device send
+ * ERDY and almost in the same time Host send PRIME. It cause
+ * that host ignore the ERDY packet and driver has to send it
+ * again.
+ */
+ if (tdl && (dbusy || !EP_STS_BUFFEMPTY(ep_sts_reg) ||
+ EP_STS_HOSTPP(ep_sts_reg))) {
+ writel(EP_CMD_ERDY |
+ EP_CMD_ERDY_SID(priv_ep->last_stream_id),
+ &priv_dev->regs->ep_cmd);
+ ep_sts_reg &= ~(EP_STS_MD_EXIT | EP_STS_IOC);
+ } else {
+ priv_ep->prime_flag = true;
+
+ pending_request = cdns3_next_request(&priv_ep->pending_req_list);
+ deferred_request = cdns3_next_request(&priv_ep->deferred_req_list);
+
+ if (deferred_request && !pending_request) {
+ cdns3_start_all_request(priv_dev, priv_ep);
+ }
+ }
+ }
+
if (ep_sts_reg & EP_STS_TRBERR) {
if (priv_ep->flags & EP_STALL_PENDING &&
!(ep_sts_reg & EP_STS_DESCMIS &&
@@ -1259,7 +1717,8 @@
}
}
- if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+ if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP) ||
+ (ep_sts_reg & EP_STS_IOT)) {
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
if (ep_sts_reg & EP_STS_ISP)
priv_ep->flags |= EP_QUIRK_END_TRANSFER;
@@ -1267,6 +1726,29 @@
priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
}
+ if (!priv_ep->use_streams) {
+ if ((ep_sts_reg & EP_STS_IOC) ||
+ (ep_sts_reg & EP_STS_ISP)) {
+ cdns3_transfer_completed(priv_dev, priv_ep);
+ } else if ((priv_ep->flags & EP_TDLCHK_EN) &
+ priv_ep->pending_tdl) {
+ /* handle IOT with pending tdl */
+ cdns3_reprogram_tdl(priv_ep);
+ }
+ } else if (priv_ep->dir == USB_DIR_OUT) {
+ priv_ep->ep_sts_pending |= ep_sts_reg;
+ } else if (ep_sts_reg & EP_STS_IOT) {
+ cdns3_transfer_completed(priv_dev, priv_ep);
+ }
+ }
+
+ /*
+ * MD_EXIT interrupt sets when stream capable endpoint exits
+ * from MOVE DATA state of Bulk IN/OUT stream protocol state machine
+ */
+ if (priv_ep->dir == USB_DIR_OUT && (ep_sts_reg & EP_STS_MD_EXIT) &&
+ (priv_ep->ep_sts_pending & EP_STS_IOT) && priv_ep->use_streams) {
+ priv_ep->ep_sts_pending = 0;
cdns3_transfer_completed(priv_dev, priv_ep);
}
@@ -1274,7 +1756,7 @@
* WA2: this condition should only be meet when
* priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
* priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
- * In other cases this interrupt will be disabled/
+ * In other cases this interrupt will be disabled.
*/
if (ep_sts_reg & EP_STS_DESCMIS && priv_dev->dev_ver < DEV_VER_V2 &&
!(priv_ep->flags & EP_STALLED))
@@ -1285,11 +1767,8 @@
static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev)
{
- if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) {
- spin_unlock(&priv_dev->lock);
+ if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect)
priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
- spin_lock(&priv_dev->lock);
- }
}
/**
@@ -1300,6 +1779,7 @@
*/
static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
u32 usb_ists)
+__must_hold(&priv_dev->lock)
{
int speed = 0;
@@ -1324,7 +1804,9 @@
/* Disconnection detected */
if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
+ spin_unlock(&priv_dev->lock);
cdns3_disconnect_gadget(priv_dev);
+ spin_lock(&priv_dev->lock);
priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
cdns3_hw_reset_eps_config(priv_dev);
@@ -1376,9 +1858,13 @@
static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
{
struct cdns3_device *priv_dev = data;
+ struct cdns3 *cdns = dev_get_drvdata(priv_dev->dev);
irqreturn_t ret = IRQ_NONE;
u32 reg;
+ if (cdns->in_lpm)
+ return ret;
+
/* check USB device interrupt */
reg = readl(&priv_dev->regs->usb_ists);
if (reg) {
@@ -1419,8 +1905,8 @@
struct cdns3_device *priv_dev = data;
irqreturn_t ret = IRQ_NONE;
unsigned long flags;
- int bit;
- u32 reg;
+ unsigned int bit;
+ unsigned long reg;
spin_lock_irqsave(&priv_dev->lock, flags);
@@ -1451,12 +1937,15 @@
if (!reg)
goto irqend;
- for_each_set_bit(bit, (unsigned long *)®,
+ for_each_set_bit(bit, ®,
sizeof(u32) * BITS_PER_BYTE) {
cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
ret = IRQ_HANDLED;
}
+ if (priv_dev->dev_ver < DEV_VER_V2 && priv_dev->using_streams)
+ cdns3_wa2_check_outq_status(priv_dev);
+
irqend:
writel(~0, &priv_dev->regs->ep_ien);
spin_unlock_irqrestore(&priv_dev->lock, flags);
@@ -1511,8 +2000,8 @@
return 0;
}
-void cdns3_configure_dmult(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep)
+static void cdns3_configure_dmult(struct cdns3_device *priv_dev,
+ struct cdns3_endpoint *priv_ep)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
@@ -1548,8 +2037,9 @@
/**
* cdns3_ep_config Configure hardware endpoint
* @priv_ep: extended endpoint object
+ * @enable: set EP_CFG_ENABLE bit in ep_cfg register.
*/
-void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
+int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
{
bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
@@ -1608,7 +2098,7 @@
break;
default:
/* all other speed are not supported */
- return;
+ return -EINVAL;
}
if (max_packet_size == 1024)
@@ -1618,11 +2108,33 @@
else
priv_ep->trb_burst_size = 16;
- ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1,
- !!priv_ep->dir);
- if (ret) {
- dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
- return;
+ /* onchip buffer is only allocated before configuration */
+ if (!priv_dev->hw_configured_flag) {
+ ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1,
+ !!priv_ep->dir);
+ if (ret) {
+ dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
+ return ret;
+ }
+ }
+
+ if (enable)
+ ep_cfg |= EP_CFG_ENABLE;
+
+ if (priv_ep->use_streams && priv_dev->gadget.speed >= USB_SPEED_SUPER) {
+ if (priv_dev->dev_ver >= DEV_VER_V3) {
+ u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0));
+
+ /*
+ * Stream capable endpoints are handled by using ep_tdl
+ * register. Other endpoints use TDL from TRB feature.
+ */
+ cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb,
+ mask);
+ }
+
+ /* Enable Stream Bit TDL chk and SID chk */
+ ep_cfg |= EP_CFG_STREAM_EN | EP_CFG_TDL_CHK | EP_CFG_SID_CHK;
}
ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
@@ -1632,9 +2144,12 @@
cdns3_select_ep(priv_dev, bEndpointAddress);
writel(ep_cfg, &priv_dev->regs->ep_cfg);
+ priv_ep->flags |= EP_CONFIGURED;
dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
priv_ep->name, ep_cfg);
+
+ return 0;
}
/* Find correct direction for HW endpoint according to description */
@@ -1770,15 +2285,17 @@
{
struct cdns3_endpoint *priv_ep;
struct cdns3_device *priv_dev;
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
u32 reg = EP_STS_EN_TRBERREN;
u32 bEndpointAddress;
unsigned long flags;
int enable = 1;
- int ret;
+ int ret = 0;
int val;
priv_ep = ep_to_cdns3_ep(ep);
priv_dev = priv_ep->cdns3_dev;
+ comp_desc = priv_ep->endpoint.comp_desc;
if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
@@ -1809,8 +2326,41 @@
goto exit;
}
- ret = cdns3_allocate_trb_pool(priv_ep);
+ bEndpointAddress = priv_ep->num | priv_ep->dir;
+ cdns3_select_ep(priv_dev, bEndpointAddress);
+ /*
+ * For some versions of controller at some point during ISO OUT traffic
+ * DMA reads Transfer Ring for the EP which has never got doorbell.
+ * This issue was detected only on simulation, but to avoid this issue
+ * driver add protection against it. To fix it driver enable ISO OUT
+ * endpoint before setting DRBL. This special treatment of ISO OUT
+ * endpoints are recommended by controller specification.
+ */
+ if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir)
+ enable = 0;
+
+ if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
+ /*
+ * Enable stream support (SS mode) related interrupts
+ * in EP_STS_EN Register
+ */
+ if (priv_dev->gadget.speed >= USB_SPEED_SUPER) {
+ reg |= EP_STS_EN_IOTEN | EP_STS_EN_PRIMEEEN |
+ EP_STS_EN_SIDERREN | EP_STS_EN_MD_EXITEN |
+ EP_STS_EN_STREAMREN;
+ priv_ep->use_streams = true;
+ ret = cdns3_ep_config(priv_ep, enable);
+ priv_dev->using_streams |= true;
+ }
+ } else {
+ ret = cdns3_ep_config(priv_ep, enable);
+ }
+
+ if (ret)
+ goto exit;
+
+ ret = cdns3_allocate_trb_pool(priv_ep);
if (ret)
goto exit;
@@ -1840,20 +2390,6 @@
writel(reg, &priv_dev->regs->ep_sts_en);
- /*
- * For some versions of controller at some point during ISO OUT traffic
- * DMA reads Transfer Ring for the EP which has never got doorbell.
- * This issue was detected only on simulation, but to avoid this issue
- * driver add protection against it. To fix it driver enable ISO OUT
- * endpoint before setting DRBL. This special treatment of ISO OUT
- * endpoints are recommended by controller specification.
- */
- if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir)
- enable = 0;
-
- if (enable)
- cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
-
ep->desc = desc;
priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING |
EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN);
@@ -1955,6 +2491,7 @@
ep->desc = NULL;
priv_ep->flags &= ~EP_ENABLED;
+ priv_ep->use_streams = false;
spin_unlock_irqrestore(&priv_dev->lock, flags);
@@ -2003,13 +2540,21 @@
list_add_tail(&request->list, &priv_ep->deferred_req_list);
/*
+ * For stream capable endpoint if prime irq flag is set then only start
+ * request.
* If hardware endpoint configuration has not been set yet then
* just queue request in deferred list. Transfer will be started in
* cdns3_set_hw_configuration.
*/
- if (priv_dev->hw_configured_flag && !(priv_ep->flags & EP_STALLED) &&
- !(priv_ep->flags & EP_STALL_PENDING))
- cdns3_start_all_request(priv_dev, priv_ep);
+ if (!request->stream_id) {
+ if (priv_dev->hw_configured_flag &&
+ !(priv_ep->flags & EP_STALLED) &&
+ !(priv_ep->flags & EP_STALL_PENDING))
+ cdns3_start_all_request(priv_dev, priv_ep);
+ } else {
+ if (priv_dev->hw_configured_flag && priv_ep->prime_flag)
+ cdns3_start_all_request(priv_dev, priv_ep);
+ }
return 0;
}
@@ -2104,10 +2649,10 @@
/* Update ring only if removed request is on pending_req_list list */
if (req_on_hw_ring && link_trb) {
- link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma +
- ((priv_req->end_trb + 1) * TRB_SIZE));
- link_trb->control = (link_trb->control & TRB_CYCLE) |
- TRB_TYPE(TRB_LINK) | TRB_CHAIN;
+ link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma +
+ ((priv_req->end_trb + 1) * TRB_SIZE)));
+ link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) |
+ TRB_TYPE(TRB_LINK) | TRB_CHAIN);
if (priv_ep->wa1_trb == priv_req->trb)
cdns3_wa1_restore_cycle_bit(priv_ep);
@@ -2123,7 +2668,7 @@
/**
* __cdns3_gadget_ep_set_halt Sets stall on selected endpoint
* Should be called after acquiring spin_lock and selecting ep
- * @ep: endpoint object to set stall on.
+ * @priv_ep: endpoint object to set stall on.
*/
void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep)
{
@@ -2144,7 +2689,7 @@
/**
* __cdns3_gadget_ep_clear_halt Clears stall on selected endpoint
* Should be called after acquiring spin_lock and selecting ep
- * @ep: endpoint object to clear stall on
+ * @priv_ep: endpoint object to clear stall on
*/
int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
{
@@ -2162,7 +2707,7 @@
priv_req = to_cdns3_request(request);
trb = priv_req->trb;
if (trb)
- trb->control = trb->control ^ TRB_CYCLE;
+ trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
}
writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
@@ -2177,7 +2722,8 @@
if (request) {
if (trb)
- trb->control = trb->control ^ TRB_CYCLE;
+ trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
+
cdns3_rearm_transfer(priv_ep, 1);
}
@@ -2287,10 +2833,13 @@
{
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
- if (is_on)
+ if (is_on) {
writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
- else
+ } else {
+ writel(~0, &priv_dev->regs->ep_ists);
+ writel(~0, &priv_dev->regs->usb_ists);
writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+ }
return 0;
}
@@ -2331,6 +2880,8 @@
/* enable generic interrupt*/
writel(USB_IEN_INIT, ®s->usb_ien);
writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, ®s->usb_conf);
+ /* keep Fast Access bit */
+ writel(PUSB_PWR_FST_REG_ACCESS, &priv_dev->regs->usb_pwr);
cdns3_configure_dmult(priv_dev, NULL);
}
@@ -2369,7 +2920,7 @@
dev_err(priv_dev->dev,
"invalid maximum_speed parameter %d\n",
max_speed);
- /* fall through */
+ fallthrough;
case USB_SPEED_UNKNOWN:
/* default to superspeed */
max_speed = USB_SPEED_SUPER;
@@ -2393,7 +2944,6 @@
struct cdns3_endpoint *priv_ep;
u32 bEndpointAddress;
struct usb_ep *ep;
- int ret = 0;
int val;
priv_dev->gadget_driver = NULL;
@@ -2415,9 +2965,10 @@
/* disable interrupt for device */
writel(0, &priv_dev->regs->usb_ien);
+ writel(0, &priv_dev->regs->usb_pwr);
writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
- return ret;
+ return 0;
}
static const struct usb_gadget_ops cdns3_gadget_ops = {
@@ -2446,7 +2997,7 @@
/**
* cdns3_init_eps Initializes software endpoints of gadget
- * @cdns3: extended gadget object
+ * @priv_dev: extended gadget object
*
* Returns 0 on success, error code elsewhere
*/
@@ -2521,7 +3072,7 @@
priv_ep->flags = 0;
- dev_info(priv_dev->dev, "Initialized %s support: %s %s\n",
+ dev_dbg(priv_dev->dev, "Initialized %s support: %s %s\n",
priv_ep->name,
priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
priv_ep->endpoint.caps.type_iso ? "ISO" : "");
@@ -2537,6 +3088,14 @@
return -ENOMEM;
}
+static void cdns3_gadget_release(struct device *dev)
+{
+ struct cdns3_device *priv_dev = container_of(dev,
+ struct cdns3_device, gadget.dev);
+
+ kfree(priv_dev);
+}
+
void cdns3_gadget_exit(struct cdns3 *cdns)
{
struct cdns3_device *priv_dev;
@@ -2547,7 +3106,7 @@
pm_runtime_mark_last_busy(cdns->dev);
pm_runtime_put_autosuspend(cdns->dev);
- usb_del_gadget_udc(&priv_dev->gadget);
+ usb_del_gadget(&priv_dev->gadget);
devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev);
cdns3_free_all_eps(priv_dev);
@@ -2568,9 +3127,9 @@
priv_dev->setup_dma);
kfree(priv_dev->zlp_buf);
- kfree(priv_dev);
+ usb_put_gadget(&priv_dev->gadget);
cdns->gadget_dev = NULL;
- cdns3_drd_switch_gadget(cdns, 0);
+ cdns3_drd_gadget_off(cdns);
}
static int cdns3_gadget_start(struct cdns3 *cdns)
@@ -2583,6 +3142,8 @@
if (!priv_dev)
return -ENOMEM;
+ usb_initialize_gadget(cdns->dev, &priv_dev->gadget,
+ cdns3_gadget_release);
cdns->gadget_dev = priv_dev;
priv_dev->sysdev = cdns->dev;
priv_dev->dev = cdns->dev;
@@ -2611,7 +3172,7 @@
default:
dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
max_speed);
- /* fall through */
+ fallthrough;
case USB_SPEED_UNKNOWN:
/* default to superspeed */
max_speed = USB_SPEED_SUPER;
@@ -2623,8 +3184,8 @@
priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
priv_dev->gadget.ops = &cdns3_gadget_ops;
priv_dev->gadget.name = "usb-ss-gadget";
- priv_dev->gadget.sg_supported = 1;
priv_dev->gadget.quirk_avoids_skb_reserve = 1;
+ priv_dev->gadget.irq = cdns->dev_irq;
spin_lock_init(&priv_dev->lock);
INIT_WORK(&priv_dev->pending_status_wq,
@@ -2661,6 +3222,8 @@
readl(&priv_dev->regs->usb_cap2));
priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver);
+ if (priv_dev->dev_ver >= DEV_VER_V2)
+ priv_dev->gadget.sg_supported = 1;
priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
if (!priv_dev->zlp_buf) {
@@ -2669,10 +3232,9 @@
}
/* add USB gadget device */
- ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
+ ret = usb_add_gadget(&priv_dev->gadget);
if (ret < 0) {
- dev_err(priv_dev->dev,
- "Failed to register USB device controller\n");
+ dev_err(priv_dev->dev, "Failed to add gadget\n");
goto err4;
}
@@ -2685,6 +3247,7 @@
err2:
cdns3_free_all_eps(priv_dev);
err1:
+ usb_put_gadget(&priv_dev->gadget);
cdns->gadget_dev = NULL;
return ret;
}
@@ -2700,7 +3263,7 @@
return ret;
}
- cdns3_drd_switch_gadget(cdns, 1);
+ cdns3_drd_gadget_on(cdns);
pm_runtime_get_sync(cdns->dev);
ret = cdns3_gadget_start(cdns);
@@ -2729,10 +3292,13 @@
}
static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
+__must_hold(&cdns->lock)
{
struct cdns3_device *priv_dev = cdns->gadget_dev;
+ spin_unlock(&cdns->lock);
cdns3_disconnect_gadget(priv_dev);
+ spin_lock(&cdns->lock);
priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
@@ -2759,7 +3325,7 @@
/**
* cdns3_gadget_init - initialize device structure
*
- * cdns: cdns3 instance
+ * @cdns: cdns3 instance
*
* This function initializes the gadget.
*/
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
index ec5c054..21fa461 100644
--- a/drivers/usb/cdns3/gadget.h
+++ b/drivers/usb/cdns3/gadget.h
@@ -599,6 +599,7 @@
#define EP_CMD_TDL_MASK GENMASK(15, 9)
#define EP_CMD_TDL_SET(p) (((p) << 9) & EP_CMD_TDL_MASK)
#define EP_CMD_TDL_GET(p) (((p) & EP_CMD_TDL_MASK) >> 9)
+#define EP_CMD_TDL_MAX (EP_CMD_TDL_MASK >> 9)
/* ERDY Stream ID value (used in SS mode). */
#define EP_CMD_ERDY_SID_MASK GENMASK(31, 16)
@@ -965,14 +966,22 @@
/*
* USBSS-DEV DMA interface.
*/
-#define TRBS_PER_SEGMENT 40
+#define TRBS_PER_SEGMENT 600
#define ISO_MAX_INTERVAL 10
+#define MAX_TRB_LENGTH BIT(16)
+
#if TRBS_PER_SEGMENT < 2
#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2."
#endif
+#define TRBS_PER_STREAM_SEGMENT 2
+
+#if TRBS_PER_STREAM_SEGMENT < 2
+#error "Incorrect TRBS_PER_STREAMS_SEGMENT. Minimal Transfer Ring size is 2."
+#endif
+
/*
*Only for ISOC endpoints - maximum number of TRBs is calculated as
* pow(2, bInterval-1) * number of usb requests. It is limitation made by
@@ -1000,6 +1009,7 @@
#define TRB_SIZE (sizeof(struct cdns3_trb))
#define TRB_RING_SIZE (TRB_SIZE * TRBS_PER_SEGMENT)
+#define TRB_STREAM_RING_SIZE (TRB_SIZE * TRBS_PER_STREAM_SEGMENT)
#define TRB_ISO_RING_SIZE (TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
#define TRB_CTRL_RING_SIZE (TRB_SIZE * 2)
@@ -1020,6 +1030,11 @@
* When set to '1', the device will toggle its interpretation of the Cycle bit
*/
#define TRB_TOGGLE BIT(1)
+/*
+ * The controller will set it if OUTSMM (OUT size mismatch) is detected,
+ * this bit is for normal TRB
+ */
+#define TRB_SMM BIT(1)
/*
* Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was
@@ -1078,7 +1093,7 @@
#define CDNS3_ENDPOINTS_MAX_COUNT 32
#define CDNS3_EP_ZLP_BUF_SIZE 1024
-#define CDNS3_EP_BUF_SIZE 2 /* KB */
+#define CDNS3_EP_BUF_SIZE 4 /* KB */
#define CDNS3_EP_ISO_HS_MULT 3
#define CDNS3_EP_ISO_SS_BURST 3
#define CDNS3_MAX_NUM_DESCMISS_BUF 32
@@ -1109,6 +1124,7 @@
* @interval: interval between packets used for ISOC endpoint.
* @free_trbs: number of free TRBs in transfer ring
* @num_trbs: number of all TRBs in transfer ring
+ * @alloc_ring_size: size of the allocated TRB ring
* @pcs: producer cycle state
* @ccs: consumer cycle state
* @enqueue: enqueue index in transfer ring
@@ -1142,6 +1158,8 @@
#define EP_QUIRK_END_TRANSFER BIT(11)
#define EP_QUIRK_EXTRA_BUF_DET BIT(12)
#define EP_QUIRK_EXTRA_BUF_EN BIT(13)
+#define EP_TDLCHK_EN BIT(15)
+#define EP_CONFIGURED BIT(16)
u32 flags;
struct cdns3_request *descmis_req;
@@ -1153,6 +1171,7 @@
int free_trbs;
int num_trbs;
+ int alloc_ring_size;
u8 pcs;
u8 ccs;
int enqueue;
@@ -1163,6 +1182,14 @@
struct cdns3_trb *wa1_trb;
unsigned int wa1_trb_index;
unsigned int wa1_cycle_bit:1;
+
+ /* Stream related */
+ unsigned int use_streams:1;
+ unsigned int prime_flag:1;
+ u32 ep_sts_pending;
+ u16 last_stream_id;
+ u16 pending_tdl;
+ unsigned int stream_sg_idx;
};
/**
@@ -1178,7 +1205,7 @@
void *buf;
dma_addr_t dma;
u32 size;
- int in_use:1;
+ unsigned in_use:1;
struct list_head list;
};
@@ -1194,6 +1221,8 @@
* this endpoint
* @flags: flag specifying special usage of request
* @list: used by internally allocated request to add to wa2_descmiss_req_list.
+ * @finished_trb: number of trb has already finished per request
+ * @num_of_trb: how many trbs in this request
*/
struct cdns3_request {
struct usb_request request;
@@ -1209,6 +1238,8 @@
#define REQUEST_UNALIGNED BIT(4)
u32 flags;
struct list_head list;
+ int finished_trb;
+ int num_of_trb;
};
#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
@@ -1287,9 +1318,10 @@
unsigned u2_allowed:1;
unsigned is_selfpowered:1;
unsigned setup_pending:1;
- int hw_configured_flag:1;
- int wake_up_flag:1;
+ unsigned hw_configured_flag:1;
+ unsigned wake_up_flag:1;
unsigned status_completion_no_call:1;
+ unsigned using_streams:1;
int out_mem_is_allocated;
struct work_struct pending_status_wq;
@@ -1310,8 +1342,6 @@
void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
struct usb_request *cdns3_next_request(struct list_head *list);
-int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
- struct usb_request *request);
void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm);
int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
u8 cdns3_ep_addr_to_index(u8 ep_addr);
@@ -1331,7 +1361,7 @@
int cdns3_init_ep0(struct cdns3_device *priv_dev,
struct cdns3_endpoint *priv_ep);
void cdns3_ep0_config(struct cdns3_device *priv_dev);
-void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
+int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable);
void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev);
diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
index ae11810..2604171 100644
--- a/drivers/usb/cdns3/host-export.h
+++ b/drivers/usb/cdns3/host-export.h
@@ -9,9 +9,11 @@
#ifndef __LINUX_CDNS3_HOST_EXPORT
#define __LINUX_CDNS3_HOST_EXPORT
+struct usb_hcd;
#ifdef CONFIG_USB_CDNS3_HOST
int cdns3_host_init(struct cdns3 *cdns);
+int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd);
#else
@@ -21,6 +23,10 @@
}
static inline void cdns3_host_exit(struct cdns3 *cdns) { }
+static inline int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
+{
+ return 0;
+}
#endif /* CONFIG_USB_CDNS3_HOST */
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
index ad788bf..1029777 100644
--- a/drivers/usb/cdns3/host.c
+++ b/drivers/usb/cdns3/host.c
@@ -13,13 +13,28 @@
#include "core.h"
#include "drd.h"
#include "host-export.h"
+#include <linux/usb/hcd.h>
+#include "../host/xhci.h"
+#include "../host/xhci-plat.h"
+
+#define XECP_PORT_CAP_REG 0x8000
+#define XECP_AUX_CTRL_REG1 0x8120
+
+#define CFG_RXDET_P3_EN BIT(15)
+#define LPM_2_STB_SWITCH_EN BIT(25)
+
+static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
+ .quirks = XHCI_SKIP_PHY_INIT,
+ .suspend_quirk = xhci_cdns3_suspend_quirk,
+};
static int __cdns3_host_init(struct cdns3 *cdns)
{
struct platform_device *xhci;
int ret;
+ struct usb_hcd *hcd;
- cdns3_drd_switch_host(cdns, 1);
+ cdns3_drd_host_on(cdns);
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
@@ -37,23 +52,73 @@
goto err1;
}
- ret = platform_device_add(xhci);
- if (ret) {
- dev_err(cdns->dev, "failed to register xHCI device\n");
+ cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci,
+ sizeof(struct xhci_plat_priv), GFP_KERNEL);
+ if (!cdns->xhci_plat_data) {
+ ret = -ENOMEM;
goto err1;
}
+ if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))
+ cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
+
+ ret = platform_device_add_data(xhci, cdns->xhci_plat_data,
+ sizeof(struct xhci_plat_priv));
+ if (ret)
+ goto free_memory;
+
+ ret = platform_device_add(xhci);
+ if (ret) {
+ dev_err(cdns->dev, "failed to register xHCI device\n");
+ goto free_memory;
+ }
+
+ /* Glue needs to access xHCI region register for Power management */
+ hcd = platform_get_drvdata(xhci);
+ if (hcd)
+ cdns->xhci_regs = hcd->regs;
+
return 0;
+
+free_memory:
+ kfree(cdns->xhci_plat_data);
err1:
platform_device_put(xhci);
return ret;
}
+int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ u32 value;
+
+ if (pm_runtime_status_suspended(hcd->self.controller))
+ return 0;
+
+ /* set usbcmd.EU3S */
+ value = readl(&xhci->op_regs->command);
+ value |= CMD_PM_INDEX;
+ writel(value, &xhci->op_regs->command);
+
+ if (hcd->regs) {
+ value = readl(hcd->regs + XECP_AUX_CTRL_REG1);
+ value |= CFG_RXDET_P3_EN;
+ writel(value, hcd->regs + XECP_AUX_CTRL_REG1);
+
+ value = readl(hcd->regs + XECP_PORT_CAP_REG);
+ value |= LPM_2_STB_SWITCH_EN;
+ writel(value, hcd->regs + XECP_PORT_CAP_REG);
+ }
+
+ return 0;
+}
+
static void cdns3_host_exit(struct cdns3 *cdns)
{
+ kfree(cdns->xhci_plat_data);
platform_device_unregister(cdns->host_dev);
cdns->host_dev = NULL;
- cdns3_drd_switch_host(cdns, 0);
+ cdns3_drd_host_off(cdns);
}
int cdns3_host_init(struct cdns3 *cdns)
diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
index f848245..0a2a326 100644
--- a/drivers/usb/cdns3/trace.h
+++ b/drivers/usb/cdns3/trace.h
@@ -122,18 +122,24 @@
__string(ep_name, priv_ep->name)
__field(u32, ep_sts)
__field(u32, ep_traddr)
+ __field(u32, ep_last_sid)
+ __field(u32, use_streams)
__dynamic_array(char, str, CDNS3_MSG_MAX)
),
TP_fast_assign(
__assign_str(ep_name, priv_ep->name);
__entry->ep_sts = readl(&priv_dev->regs->ep_sts);
__entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
+ __entry->ep_last_sid = priv_ep->last_stream_id;
+ __entry->use_streams = priv_ep->use_streams;
),
- TP_printk("%s, ep_traddr: %08x",
+ TP_printk("%s, ep_traddr: %08x ep_last_sid: %08x use_streams: %d",
cdns3_decode_epx_irq(__get_str(str),
__get_str(ep_name),
__entry->ep_sts),
- __entry->ep_traddr)
+ __entry->ep_traddr,
+ __entry->ep_last_sid,
+ __entry->use_streams)
);
DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
@@ -210,6 +216,7 @@
__field(int, end_trb)
__field(struct cdns3_trb *, start_trb_addr)
__field(int, flags)
+ __field(unsigned int, stream_id)
),
TP_fast_assign(
__assign_str(name, req->priv_ep->name);
@@ -225,9 +232,10 @@
__entry->end_trb = req->end_trb;
__entry->start_trb_addr = req->trb;
__entry->flags = req->flags;
+ __entry->stream_id = req->request.stream_id;
),
TP_printk("%s: req: %p, req buff %p, length: %u/%u %s%s%s, status: %d,"
- " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
+ " trb: [start:%d, end:%d: virt addr %pa], flags:%x SID: %u",
__get_str(name), __entry->req, __entry->buf, __entry->actual,
__entry->length,
__entry->zero ? "Z" : "z",
@@ -237,7 +245,8 @@
__entry->start_trb,
__entry->end_trb,
__entry->start_trb_addr,
- __entry->flags
+ __entry->flags,
+ __entry->stream_id
)
);
@@ -281,6 +290,39 @@
__entry->length)
);
+DECLARE_EVENT_CLASS(cdns3_stream_split_transfer_len,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req),
+ TP_STRUCT__entry(
+ __string(name, req->priv_ep->name)
+ __field(struct cdns3_request *, req)
+ __field(unsigned int, length)
+ __field(unsigned int, actual)
+ __field(unsigned int, stream_id)
+ ),
+ TP_fast_assign(
+ __assign_str(name, req->priv_ep->name);
+ __entry->req = req;
+ __entry->actual = req->request.length;
+ __entry->length = req->request.actual;
+ __entry->stream_id = req->request.stream_id;
+ ),
+ TP_printk("%s: req: %p,request length: %u actual length: %u SID: %u",
+ __get_str(name), __entry->req, __entry->length,
+ __entry->actual, __entry->stream_id)
+);
+
+DEFINE_EVENT(cdns3_stream_split_transfer_len, cdns3_stream_transfer_split,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_stream_split_transfer_len,
+ cdns3_stream_transfer_split_next_part,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
TP_PROTO(struct cdns3_request *priv_req),
TP_ARGS(priv_req),
@@ -319,6 +361,34 @@
TP_ARGS(req)
);
+DECLARE_EVENT_CLASS(cdns3_log_map_request,
+ TP_PROTO(struct cdns3_request *priv_req),
+ TP_ARGS(priv_req),
+ TP_STRUCT__entry(
+ __string(name, priv_req->priv_ep->name)
+ __field(struct usb_request *, req)
+ __field(void *, buf)
+ __field(dma_addr_t, dma)
+ ),
+ TP_fast_assign(
+ __assign_str(name, priv_req->priv_ep->name);
+ __entry->req = &priv_req->request;
+ __entry->buf = priv_req->request.buf;
+ __entry->dma = priv_req->request.dma;
+ ),
+ TP_printk("%s: req: %p, req buf %p, dma %p",
+ __get_str(name), __entry->req, __entry->buf, &__entry->dma
+ )
+);
+DEFINE_EVENT(cdns3_log_map_request, cdns3_map_request,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+DEFINE_EVENT(cdns3_log_map_request, cdns3_mapped_request,
+ TP_PROTO(struct cdns3_request *req),
+ TP_ARGS(req)
+);
+
DECLARE_EVENT_CLASS(cdns3_log_trb,
TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
TP_ARGS(priv_ep, trb),
@@ -329,6 +399,7 @@
__field(u32, length)
__field(u32, control)
__field(u32, type)
+ __field(unsigned int, last_stream_id)
),
TP_fast_assign(
__assign_str(name, priv_ep->name);
@@ -337,8 +408,9 @@
__entry->length = le32_to_cpu(trb->length);
__entry->control = le32_to_cpu(trb->control);
__entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
+ __entry->last_stream_id = priv_ep->last_stream_id;
),
- TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s)",
+ TP_printk("%s: trb %p, dma buf: 0x%08x, size: %ld, burst: %d ctrl: 0x%08x (%s%s%s%s%s%s%s) SID:%lu LAST_SID:%u",
__get_str(name), __entry->trb, __entry->buffer,
TRB_LEN(__entry->length),
(u8)TRB_BURST_LEN_GET(__entry->length),
@@ -349,7 +421,9 @@
__entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
__entry->control & TRB_CHAIN ? "CHAIN, " : "",
__entry->control & TRB_IOC ? "IOC, " : "",
- TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
+ TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK",
+ TRB_FIELD_TO_STREAMID(__entry->control),
+ __entry->last_stream_id
)
);
@@ -398,6 +472,7 @@
__field(unsigned int, maxpacket)
__field(unsigned int, maxpacket_limit)
__field(unsigned int, max_streams)
+ __field(unsigned int, use_streams)
__field(unsigned int, maxburst)
__field(unsigned int, flags)
__field(unsigned int, dir)
@@ -409,16 +484,18 @@
__entry->maxpacket = priv_ep->endpoint.maxpacket;
__entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
__entry->max_streams = priv_ep->endpoint.max_streams;
+ __entry->use_streams = priv_ep->use_streams;
__entry->maxburst = priv_ep->endpoint.maxburst;
__entry->flags = priv_ep->flags;
__entry->dir = priv_ep->dir;
__entry->enqueue = priv_ep->enqueue;
__entry->dequeue = priv_ep->dequeue;
),
- TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
- "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
+ TP_printk("%s: mps: %d/%d. streams: %d, stream enable: %d, burst: %d, "
+ "enq idx: %d, deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
__get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->max_streams,
+ __entry->use_streams,
__entry->maxburst, __entry->enqueue,
__entry->dequeue,
__entry->flags & EP_ENABLED ? "EN | " : "",
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index ae850b3..8bafcfc 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -7,6 +7,7 @@
select RESET_CONTROLLER
select USB_ULPI_BUS
select USB_ROLE_SWITCH
+ select USB_TEGRA_PHY if ARCH_TEGRA
help
Say Y here if your system has a dual role high speed USB
controller based on ChipIdea silicon IP. It supports:
@@ -17,17 +18,6 @@
if USB_CHIPIDEA
-config USB_CHIPIDEA_OF
- tristate
- depends on OF
- default USB_CHIPIDEA
-
-config USB_CHIPIDEA_PCI
- tristate
- depends on USB_PCI
- depends on NOP_USB_XCEIV
- default USB_CHIPIDEA
-
config USB_CHIPIDEA_UDC
bool "ChipIdea device controller"
depends on USB_GADGET
@@ -42,4 +32,30 @@
help
Say Y here to enable host controller functionality of the
ChipIdea driver.
+
+config USB_CHIPIDEA_PCI
+ tristate "Enable PCI glue driver" if EMBEDDED
+ depends on USB_PCI
+ depends on NOP_USB_XCEIV
+ default USB_CHIPIDEA
+
+config USB_CHIPIDEA_MSM
+ tristate "Enable MSM hsusb glue driver" if EMBEDDED
+ default USB_CHIPIDEA
+
+config USB_CHIPIDEA_IMX
+ tristate "Enable i.MX USB glue driver" if EMBEDDED
+ depends on OF
+ default USB_CHIPIDEA
+
+config USB_CHIPIDEA_GENERIC
+ tristate "Enable generic USB2 glue driver" if EMBEDDED
+ default USB_CHIPIDEA
+
+config USB_CHIPIDEA_TEGRA
+ tristate "Enable Tegra UDC glue driver" if EMBEDDED
+ depends on OF
+ depends on USB_CHIPIDEA_UDC
+ default USB_CHIPIDEA
+
endif
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 12df94f..fae779a 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -8,11 +8,8 @@
# Glue/Bridge layers go here
-obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_usb2.o
-obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o
-obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o
-
-obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
-
-obj-$(CONFIG_USB_CHIPIDEA_OF) += usbmisc_imx.o ci_hdrc_imx.o
-obj-$(CONFIG_USB_CHIPIDEA_OF) += ci_hdrc_tegra.o
+obj-$(CONFIG_USB_CHIPIDEA_GENERIC) += ci_hdrc_usb2.o
+obj-$(CONFIG_USB_CHIPIDEA_MSM) += ci_hdrc_msm.o
+obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
+obj-$(CONFIG_USB_CHIPIDEA_IMX) += ci_hdrc_imx.o usbmisc_imx.o
+obj-$(CONFIG_USB_CHIPIDEA_TEGRA) += ci_hdrc_tegra.o
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
index 98da995..b1540ce 100644
--- a/drivers/usb/chipidea/bits.h
+++ b/drivers/usb/chipidea/bits.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* bits.h - register bits of the ChipIdea USB IP core
*
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 6911aef..0697eb9 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* ci.h - common structures, functions, and macros of the ChipIdea driver
*
@@ -25,6 +25,7 @@
#define TD_PAGE_COUNT 5
#define CI_HDRC_PAGE_SIZE 4096ul /* page size for TD's */
#define ENDPT_MAX 32
+#define CI_MAX_BUF_SIZE (TD_PAGE_COUNT * CI_HDRC_PAGE_SIZE)
/******************************************************************************
* REGISTERS
@@ -302,6 +303,16 @@
return USB_ROLE_NONE;
}
+static inline enum ci_role usb_role_to_ci_role(enum usb_role role)
+{
+ if (role == USB_ROLE_HOST)
+ return CI_ROLE_HOST;
+ else if (role == USB_ROLE_DEVICE)
+ return CI_ROLE_GADGET;
+ else
+ return CI_ROLE_END;
+}
+
/**
* hw_read_id_reg: reads from a identification register
* @ci: the controller
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 85561b3..f798455 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -170,6 +170,11 @@
if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
data->ulpi = 1;
+ of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control",
+ &data->emp_curr_control);
+ of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust",
+ &data->dc_vol_level_adjust);
+
return data;
}
@@ -276,21 +281,33 @@
struct device *dev = ci->dev->parent;
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
+ struct imx_usbmisc_data *mdata = data->usbmisc_data;
switch (event) {
case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
- ret = pinctrl_select_state(data->pinctrl,
- data->pinctrl_hsic_active);
- if (ret)
- dev_err(dev, "hsic_active select failed, err=%d\n",
- ret);
+ if (data->pinctrl) {
+ ret = pinctrl_select_state(data->pinctrl,
+ data->pinctrl_hsic_active);
+ if (ret)
+ dev_err(dev,
+ "hsic_active select failed, err=%d\n",
+ ret);
+ }
break;
case CI_HDRC_IMX_HSIC_SUSPEND_EVENT:
- ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data);
+ ret = imx_usbmisc_hsic_set_connect(mdata);
if (ret)
dev_err(dev,
"hsic_set_connect failed, err=%d\n", ret);
break;
+ case CI_HDRC_CONTROLLER_VBUS_EVENT:
+ if (ci->vbus_active)
+ ret = imx_usbmisc_charger_detection(mdata, true);
+ else
+ ret = imx_usbmisc_charger_detection(mdata, false);
+ if (ci->usb_phy)
+ schedule_work(&ci->usb_phy->chg_work);
+ break;
default:
break;
}
@@ -311,7 +328,6 @@
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
- struct pinctrl_state *pinctrl_hsic_idle;
of_id = of_match_device(ci_hdrc_imx_dt_ids, dev);
if (!of_id)
@@ -335,12 +351,42 @@
pdata.flags |= CI_HDRC_IMX_IS_HSIC;
data->usbmisc_data->hsic = 1;
data->pinctrl = devm_pinctrl_get(dev);
- if (IS_ERR(data->pinctrl)) {
- dev_err(dev, "pinctrl get failed, err=%ld\n",
+ if (PTR_ERR(data->pinctrl) == -ENODEV)
+ data->pinctrl = NULL;
+ else if (IS_ERR(data->pinctrl)) {
+ if (PTR_ERR(data->pinctrl) != -EPROBE_DEFER)
+ dev_err(dev, "pinctrl get failed, err=%ld\n",
PTR_ERR(data->pinctrl));
return PTR_ERR(data->pinctrl);
}
+ data->hsic_pad_regulator =
+ devm_regulator_get_optional(dev, "hsic");
+ if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
+ /* no pad regualator is needed */
+ data->hsic_pad_regulator = NULL;
+ } else if (IS_ERR(data->hsic_pad_regulator)) {
+ if (PTR_ERR(data->hsic_pad_regulator) != -EPROBE_DEFER)
+ dev_err(dev,
+ "Get HSIC pad regulator error: %ld\n",
+ PTR_ERR(data->hsic_pad_regulator));
+ return PTR_ERR(data->hsic_pad_regulator);
+ }
+
+ if (data->hsic_pad_regulator) {
+ ret = regulator_enable(data->hsic_pad_regulator);
+ if (ret) {
+ dev_err(dev,
+ "Failed to enable HSIC pad regulator\n");
+ return ret;
+ }
+ }
+ }
+
+ /* HSIC pinctrl handling */
+ if (data->pinctrl) {
+ struct pinctrl_state *pinctrl_hsic_idle;
+
pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle");
if (IS_ERR(pinctrl_hsic_idle)) {
dev_err(dev,
@@ -363,32 +409,10 @@
PTR_ERR(data->pinctrl_hsic_active));
return PTR_ERR(data->pinctrl_hsic_active);
}
-
- data->hsic_pad_regulator = devm_regulator_get(dev, "hsic");
- if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
- /* no pad regualator is needed */
- data->hsic_pad_regulator = NULL;
- } else if (IS_ERR(data->hsic_pad_regulator)) {
- dev_err(dev, "Get HSIC pad regulator error: %ld\n",
- PTR_ERR(data->hsic_pad_regulator));
- return PTR_ERR(data->hsic_pad_regulator);
- }
-
- if (data->hsic_pad_regulator) {
- ret = regulator_enable(data->hsic_pad_regulator);
- if (ret) {
- dev_err(dev,
- "Failed to enable HSIC pad regulator\n");
- return ret;
- }
- }
}
if (pdata.flags & CI_HDRC_PMQOS)
- pm_qos_add_request(&data->pm_qos_req,
- PM_QOS_CPU_DMA_LATENCY, 0);
+ cpu_latency_qos_add_request(&data->pm_qos_req, 0);
ret = imx_get_clks(dev);
if (ret)
@@ -401,14 +425,21 @@
data->phy = devm_usb_get_phy_by_phandle(dev, "fsl,usbphy", 0);
if (IS_ERR(data->phy)) {
ret = PTR_ERR(data->phy);
- /* Return -EINVAL if no usbphy is available */
- if (ret == -ENODEV)
- data->phy = NULL;
- else
+ if (ret != -ENODEV)
goto err_clk;
+ data->phy = devm_usb_get_phy_by_phandle(dev, "phys", 0);
+ if (IS_ERR(data->phy)) {
+ ret = PTR_ERR(data->phy);
+ if (ret == -ENODEV)
+ data->phy = NULL;
+ else
+ goto err_clk;
+ }
}
pdata.usb_phy = data->phy;
+ if (data->usbmisc_data)
+ data->usbmisc_data->usb_phy = data->phy;
if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
@@ -438,6 +469,20 @@
goto err_clk;
}
+ if (data->usbmisc_data) {
+ if (!IS_ERR(pdata.id_extcon.edev) ||
+ of_property_read_bool(np, "usb-role-switch"))
+ data->usbmisc_data->ext_id = 1;
+
+ if (!IS_ERR(pdata.vbus_extcon.edev) ||
+ of_property_read_bool(np, "usb-role-switch"))
+ data->usbmisc_data->ext_vbus = 1;
+
+ /* usbmisc needs to know dr mode to choose wakeup setting */
+ data->usbmisc_data->available_role =
+ ci_hdrc_query_available_role(data->ci_pdev);
+ }
+
ret = imx_usbmisc_init_post(data->usbmisc_data);
if (ret) {
dev_err(dev, "usbmisc post failed, ret=%d\n", ret);
@@ -462,7 +507,7 @@
/* don't overwrite original ret (cf. EPROBE_DEFER) */
regulator_disable(data->hsic_pad_regulator);
if (pdata.flags & CI_HDRC_PMQOS)
- pm_qos_remove_request(&data->pm_qos_req);
+ cpu_latency_qos_remove_request(&data->pm_qos_req);
data->ci_pdev = NULL;
return ret;
}
@@ -483,7 +528,7 @@
if (data->ci_pdev) {
imx_disable_unprepare_clks(&pdev->dev);
if (data->plat_data->flags & CI_HDRC_PMQOS)
- pm_qos_remove_request(&data->pm_qos_req);
+ cpu_latency_qos_remove_request(&data->pm_qos_req);
if (data->hsic_pad_regulator)
regulator_disable(data->hsic_pad_regulator);
}
@@ -511,7 +556,7 @@
imx_disable_unprepare_clks(dev);
if (data->plat_data->flags & CI_HDRC_PMQOS)
- pm_qos_remove_request(&data->pm_qos_req);
+ cpu_latency_qos_remove_request(&data->pm_qos_req);
data->in_lpm = true;
@@ -531,8 +576,7 @@
}
if (data->plat_data->flags & CI_HDRC_PMQOS)
- pm_qos_add_request(&data->pm_qos_req,
- PM_QOS_CPU_DMA_LATENCY, 0);
+ cpu_latency_qos_add_request(&data->pm_qos_req, 0);
ret = imx_prepare_enable_clks(dev);
if (ret)
@@ -580,7 +624,12 @@
}
}
- return imx_controller_suspend(dev);
+ ret = imx_controller_suspend(dev);
+ if (ret)
+ return ret;
+
+ pinctrl_pm_select_sleep_state(dev);
+ return ret;
}
static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
@@ -588,6 +637,7 @@
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
+ pinctrl_pm_select_default_state(dev);
ret = imx_controller_resume(dev);
if (!ret && data->supports_runtime_pm) {
pm_runtime_disable(dev);
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h
index c842e03..999c653 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.h
+++ b/drivers/usb/chipidea/ci_hdrc_imx.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2012 Freescale Semiconductor, Inc.
*/
@@ -22,6 +22,12 @@
unsigned int evdo:1; /* set external vbus divider option */
unsigned int ulpi:1; /* connected to an ULPI phy */
unsigned int hsic:1; /* HSIC controlller */
+ unsigned int ext_id:1; /* ID from exteranl event */
+ unsigned int ext_vbus:1; /* Vbus from exteranl event */
+ struct usb_phy *usb_phy;
+ enum usb_dr_mode available_role; /* runtime usb dr mode */
+ int emp_curr_control;
+ int dc_vol_level_adjust;
};
int imx_usbmisc_init(struct imx_usbmisc_data *data);
@@ -29,5 +35,6 @@
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled);
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on);
+int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect);
#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
diff --git a/drivers/usb/chipidea/ci_hdrc_pci.c b/drivers/usb/chipidea/ci_hdrc_pci.c
index 49a6154..d63479e 100644
--- a/drivers/usb/chipidea/ci_hdrc_pci.c
+++ b/drivers/usb/chipidea/ci_hdrc_pci.c
@@ -120,7 +120,7 @@
usb_phy_generic_unregister(ci->phy);
}
-/**
+/*
* PCI device table
* PCI device structure
*
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index 1202535..7455df0 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -24,35 +24,23 @@
unsigned long flags;
};
-static const struct tegra_udc_soc_info tegra20_udc_soc_info = {
- .flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
-};
-
-static const struct tegra_udc_soc_info tegra30_udc_soc_info = {
- .flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
-};
-
-static const struct tegra_udc_soc_info tegra114_udc_soc_info = {
- .flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
-};
-
-static const struct tegra_udc_soc_info tegra124_udc_soc_info = {
+static const struct tegra_udc_soc_info tegra_udc_soc_info = {
.flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
};
static const struct of_device_id tegra_udc_of_match[] = {
{
.compatible = "nvidia,tegra20-udc",
- .data = &tegra20_udc_soc_info,
+ .data = &tegra_udc_soc_info,
}, {
.compatible = "nvidia,tegra30-udc",
- .data = &tegra30_udc_soc_info,
+ .data = &tegra_udc_soc_info,
}, {
.compatible = "nvidia,tegra114-udc",
- .data = &tegra114_udc_soc_info,
+ .data = &tegra_udc_soc_info,
}, {
.compatible = "nvidia,tegra124-udc",
- .data = &tegra124_udc_soc_info,
+ .data = &tegra_udc_soc_info,
}, {
/* sentinel */
}
@@ -95,13 +83,6 @@
return err;
}
- /*
- * Tegra's USB PHY driver doesn't implement optional phy_init()
- * hook, so we have to power on UDC controller before ChipIdea
- * driver initialization kicks in.
- */
- usb_phy_set_suspend(udc->phy, 0);
-
/* setup and register ChipIdea HDRC device */
udc->data.name = "tegra-udc";
udc->data.flags = soc->flags;
@@ -121,7 +102,6 @@
return 0;
fail_power_off:
- usb_phy_set_suspend(udc->phy, 1);
clk_disable_unprepare(udc->clk);
return err;
}
@@ -131,7 +111,6 @@
struct tegra_udc *udc = platform_get_drvdata(pdev);
ci_hdrc_remove_device(udc->dev);
- usb_phy_set_suspend(udc->phy, 1);
clk_disable_unprepare(udc->clk);
return 0;
diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c
index c044fba..89e1d82 100644
--- a/drivers/usb/chipidea/ci_hdrc_usb2.c
+++ b/drivers/usb/chipidea/ci_hdrc_usb2.c
@@ -28,13 +28,19 @@
.flags = CI_HDRC_DISABLE_STREAMING,
};
-static struct ci_hdrc_platform_data ci_zynq_pdata = {
+static const struct ci_hdrc_platform_data ci_zynq_pdata = {
.capoffset = DEF_CAPOFFSET,
};
+static const struct ci_hdrc_platform_data ci_zevio_pdata = {
+ .capoffset = DEF_CAPOFFSET,
+ .flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
+};
+
static const struct of_device_id ci_hdrc_usb2_of_match[] = {
- { .compatible = "chipidea,usb2"},
- { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata},
+ { .compatible = "chipidea,usb2" },
+ { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata },
+ { .compatible = "lsi,zevio-usb", .data = &ci_zevio_pdata },
{ }
};
MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match);
@@ -64,13 +70,14 @@
if (!priv)
return -ENOMEM;
- priv->clk = devm_clk_get(dev, NULL);
- if (!IS_ERR(priv->clk)) {
- ret = clk_prepare_enable(priv->clk);
- if (ret) {
- dev_err(dev, "failed to enable the clock: %d\n", ret);
- return ret;
- }
+ priv->clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable the clock: %d\n", ret);
+ return ret;
}
ci_pdata->name = dev_name(dev);
@@ -94,8 +101,7 @@
return 0;
clk_err:
- if (!IS_ERR(priv->clk))
- clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk);
return ret;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c
deleted file mode 100644
index e1634da..0000000
--- a/drivers/usb/chipidea/ci_hdrc_zevio.c
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
- *
- * Based off drivers/usb/chipidea/ci_hdrc_msm.c
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/usb/gadget.h>
-#include <linux/usb/chipidea.h>
-
-#include "ci.h"
-
-static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
- .name = "ci_hdrc_zevio",
- .flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
- .capoffset = DEF_CAPOFFSET,
-};
-
-static int ci_hdrc_zevio_probe(struct platform_device *pdev)
-{
- struct platform_device *ci_pdev;
-
- dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n");
-
- ci_pdev = ci_hdrc_add_device(&pdev->dev,
- pdev->resource, pdev->num_resources,
- &ci_hdrc_zevio_platdata);
-
- if (IS_ERR(ci_pdev)) {
- dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
- return PTR_ERR(ci_pdev);
- }
-
- platform_set_drvdata(pdev, ci_pdev);
-
- return 0;
-}
-
-static int ci_hdrc_zevio_remove(struct platform_device *pdev)
-{
- struct platform_device *ci_pdev = platform_get_drvdata(pdev);
-
- ci_hdrc_remove_device(ci_pdev);
-
- return 0;
-}
-
-static const struct of_device_id ci_hdrc_zevio_dt_ids[] = {
- { .compatible = "lsi,zevio-usb", },
- { /* sentinel */ }
-};
-
-static struct platform_driver ci_hdrc_zevio_driver = {
- .probe = ci_hdrc_zevio_probe,
- .remove = ci_hdrc_zevio_remove,
- .driver = {
- .name = "zevio_usb",
- .of_match_table = ci_hdrc_zevio_dt_ids,
- },
-};
-
-MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids);
-module_platform_driver(ci_hdrc_zevio_driver);
-
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index b7da2a2..127b1a6 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -3,42 +3,16 @@
* core.c - ChipIdea USB IP core family device controller
*
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ * Copyright (C) 2020 NXP
*
* Author: David Lopo
- */
-
-/*
- * Description: ChipIdea USB IP core family device controller
+ * Peter Chen <peter.chen@nxp.com>
*
- * This driver is composed of several blocks:
- * - HW: hardware interface
- * - DBG: debug facilities (optional)
- * - UTIL: utilities
- * - ISR: interrupts handling
- * - ENDPT: endpoint operations (Gadget API)
- * - GADGET: gadget operations (Gadget API)
- * - BUS: bus glue code, bus abstraction layer
- *
- * Compile Options
- * - STALL_IN: non-empty bulk-in pipes cannot be halted
- * if defined mass storage compliance succeeds but with warnings
- * => case 4: Hi > Dn
- * => case 5: Hi > Di
- * => case 8: Hi <> Do
- * if undefined usbtest 13 fails
- * - TRACE: enable function tracing (depends on DEBUG)
- *
- * Main Features
- * - Chapter 9 & Mass Storage Compliance with Gadget File Storage
- * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
- * - Normal & LPM support
- *
- * USBTEST Report
- * - OK: 0-12, 13 (STALL_IN defined) & 14
- * - Not Supported: 15 & 16 (ISO)
- *
- * TODO List
- * - Suspend & Remote Wakeup
+ * Main Features:
+ * - Four transfers are supported, usbtest is passed
+ * - USB Certification for gadget: CH9 and Mass Storage are passed
+ * - Low power mode
+ * - USB wakeup
*/
#include <linux/delay.h>
#include <linux/device.h>
@@ -181,6 +155,7 @@
/**
* hw_port_test_set: writes port test mode (execute without interruption)
+ * @ci: the controller
* @mode: new value
*
* This function returns an error code
@@ -272,7 +247,7 @@
ci->rev = ci_get_revision(ci);
dev_dbg(ci->dev,
- "ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n",
+ "revision: %d, lpm: %d; cap: %px op: %px\n",
ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
/* setup lock mode ? */
@@ -534,7 +509,7 @@
return 0;
}
-static irqreturn_t ci_irq(int irq, void *data)
+static irqreturn_t ci_irq_handler(int irq, void *data)
{
struct ci_hdrc *ci = data;
irqreturn_t ret = IRQ_NONE;
@@ -587,6 +562,15 @@
return ret;
}
+static void ci_irq(struct ci_hdrc *ci)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ ci_irq_handler(ci->irq, ci);
+ local_irq_restore(flags);
+}
+
static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
void *ptr)
{
@@ -596,13 +580,13 @@
cbl->connected = event;
cbl->changed = true;
- ci_irq(ci->irq, ci);
+ ci_irq(ci);
return NOTIFY_DONE;
}
-static enum usb_role ci_usb_role_switch_get(struct device *dev)
+static enum usb_role ci_usb_role_switch_get(struct usb_role_switch *sw)
{
- struct ci_hdrc *ci = dev_get_drvdata(dev);
+ struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
enum usb_role role;
unsigned long flags;
@@ -613,14 +597,17 @@
return role;
}
-static int ci_usb_role_switch_set(struct device *dev, enum usb_role role)
+static int ci_usb_role_switch_set(struct usb_role_switch *sw,
+ enum usb_role role)
{
- struct ci_hdrc *ci = dev_get_drvdata(dev);
+ struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
struct ci_hdrc_cable *cable = NULL;
enum usb_role current_role = ci_role_to_usb_role(ci);
+ enum ci_role ci_role = usb_role_to_ci_role(role);
unsigned long flags;
- if (current_role == role)
+ if ((ci_role != CI_ROLE_END && !ci->roles[ci_role]) ||
+ (current_role == role))
return 0;
pm_runtime_get_sync(ci->dev);
@@ -634,7 +621,7 @@
if (cable) {
cable->changed = true;
cable->connected = false;
- ci_irq(ci->irq, ci);
+ ci_irq(ci);
spin_unlock_irqrestore(&ci->lock, flags);
if (ci->wq && role != USB_ROLE_NONE)
flush_workqueue(ci->wq);
@@ -652,7 +639,7 @@
if (cable) {
cable->changed = true;
cable->connected = true;
- ci_irq(ci->irq, ci);
+ ci_irq(ci);
}
spin_unlock_irqrestore(&ci->lock, flags);
pm_runtime_put_sync(ci->dev);
@@ -663,6 +650,7 @@
static struct usb_role_switch_desc ci_role_switch = {
.set = ci_usb_role_switch_set,
.get = ci_usb_role_switch_get,
+ .allow_userspace_control = true,
};
static int ci_get_platdata(struct device *dev,
@@ -683,7 +671,7 @@
if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) {
/* Get the vbus regulator */
- platdata->reg_vbus = devm_regulator_get(dev, "vbus");
+ platdata->reg_vbus = devm_regulator_get_optional(dev, "vbus");
if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
@@ -899,6 +887,33 @@
}
EXPORT_SYMBOL_GPL(ci_hdrc_remove_device);
+/**
+ * ci_hdrc_query_available_role: get runtime available operation mode
+ *
+ * The glue layer can get current operation mode (host/peripheral/otg)
+ * This function should be called after ci core device has created.
+ *
+ * @pdev: the platform device of ci core.
+ *
+ * Return runtime usb_dr_mode.
+ */
+enum usb_dr_mode ci_hdrc_query_available_role(struct platform_device *pdev)
+{
+ struct ci_hdrc *ci = platform_get_drvdata(pdev);
+
+ if (!ci)
+ return USB_DR_MODE_UNKNOWN;
+ if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])
+ return USB_DR_MODE_OTG;
+ else if (ci->roles[CI_ROLE_HOST])
+ return USB_DR_MODE_HOST;
+ else if (ci->roles[CI_ROLE_GADGET])
+ return USB_DR_MODE_PERIPHERAL;
+ else
+ return USB_DR_MODE_UNKNOWN;
+}
+EXPORT_SYMBOL_GPL(ci_hdrc_query_available_role);
+
static inline void ci_role_destroy(struct ci_hdrc *ci)
{
ci_hdrc_gadget_destroy(ci);
@@ -1116,6 +1131,7 @@
}
if (ci_role_switch.fwnode) {
+ ci_role_switch.driver_data = ci;
ci->role_switch = usb_role_switch_register(dev,
&ci_role_switch);
if (IS_ERR(ci->role_switch)) {
@@ -1145,8 +1161,11 @@
if (!ci_otg_is_fsm_mode(ci)) {
/* only update vbus status for peripheral */
- if (ci->role == CI_ROLE_GADGET)
+ if (ci->role == CI_ROLE_GADGET) {
+ /* Pull down DP for possible charger detection */
+ hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
ci_handle_vbus_change(ci);
+ }
ret = ci_role_start(ci, ci->role);
if (ret) {
@@ -1156,7 +1175,7 @@
}
}
- ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
+ ret = devm_request_irq(dev, ci->irq, ci_irq_handler, IRQF_SHARED,
ci->platdata->name, ci);
if (ret)
goto stop;
@@ -1277,11 +1296,11 @@
if (!IS_ERR(cable_id->edev) && ci->is_otg &&
(otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS))
- ci_irq(ci->irq, ci);
+ ci_irq(ci);
if (!IS_ERR(cable_vbus->edev) && ci->is_otg &&
(otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS))
- ci_irq(ci->irq, ci);
+ ci_irq(ci);
}
static int ci_controller_resume(struct device *dev)
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index fcc91a3..da5d18c 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -18,7 +18,7 @@
#include "bits.h"
#include "otg.h"
-/**
+/*
* ci_device_show: prints information about device capabilities and status
*/
static int ci_device_show(struct seq_file *s, void *data)
@@ -47,7 +47,7 @@
}
DEFINE_SHOW_ATTRIBUTE(ci_device);
-/**
+/*
* ci_port_test_show: reads port test mode
*/
static int ci_port_test_show(struct seq_file *s, void *data)
@@ -67,7 +67,7 @@
return 0;
}
-/**
+/*
* ci_port_test_write: writes port test mode
*/
static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf,
@@ -115,7 +115,7 @@
.release = single_release,
};
-/**
+/*
* ci_qheads_show: DMA contents of all queue heads
*/
static int ci_qheads_show(struct seq_file *s, void *data)
@@ -147,7 +147,7 @@
}
DEFINE_SHOW_ATTRIBUTE(ci_qheads);
-/**
+/*
* ci_requests_show: DMA contents of all requests currently queued (all endpts)
*/
static int ci_requests_show(struct seq_file *s, void *data)
@@ -342,7 +342,7 @@
*/
void dbg_create_files(struct ci_hdrc *ci)
{
- ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL);
+ ci->debugfs = debugfs_create_dir(dev_name(ci->dev), usb_debug_root);
debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
&ci_device_fops);
diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h
index 70112cf..2625aa0 100644
--- a/drivers/usb/chipidea/host.h
+++ b/drivers/usb/chipidea/host.h
@@ -20,7 +20,7 @@
}
-static void ci_hdrc_host_driver_init(void)
+static inline void ci_hdrc_host_driver_init(void)
{
}
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index fbfb02e..d3aada3 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -23,6 +23,7 @@
/**
* hw_read_otgsc returns otgsc register bits value.
+ * @ci: the controller
* @mask: bitfield mask
*/
u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
@@ -75,6 +76,7 @@
/**
* hw_write_otgsc updates target bits of OTGSC register.
+ * @ci: the controller
* @mask: bitfield mask
* @data: to be written
*/
@@ -170,6 +172,13 @@
dev_dbg(ci->dev, "switching from %s to %s\n",
ci_role(ci)->name, ci->roles[role]->name);
+ if (ci->vbus_active && ci->role == CI_ROLE_GADGET)
+ /*
+ * vbus disconnect event is lost due to role
+ * switch occurs during system suspend.
+ */
+ usb_gadget_vbus_disconnect(&ci->gadget);
+
ci_role_stop(ci);
if (role == CI_ROLE_GADGET &&
@@ -222,7 +231,7 @@
/**
* ci_hdrc_otg_init - initialize otg struct
- * ci: the controller
+ * @ci: the controller
*/
int ci_hdrc_otg_init(struct ci_hdrc *ci)
{
@@ -241,7 +250,7 @@
/**
* ci_hdrc_otg_destroy - destroy otg struct
- * ci: the controller
+ * @ci: the controller
*/
void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
{
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
index 4f8b817..5e7a6e5 100644
--- a/drivers/usb/chipidea/otg.h
+++ b/drivers/usb/chipidea/otg.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2013-2014 Freescale Semiconductor, Inc.
*
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
index 2b49d29..1f5c5ae 100644
--- a/drivers/usb/chipidea/otg_fsm.h
+++ b/drivers/usb/chipidea/otg_fsm.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
*
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 8f18e7b..5f35cdd 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -72,6 +72,7 @@
/**
* hw_device_state: enables/disables interrupts (execute without interruption)
+ * @ci: the controller
* @dma: 0 => disable, !0 => enable and set dma engine
*
* This function returns an error code
@@ -91,6 +92,7 @@
/**
* hw_ep_flush: flush endpoint fifo (execute without interruption)
+ * @ci: the controller
* @num: endpoint number
* @dir: endpoint direction
*
@@ -112,6 +114,7 @@
/**
* hw_ep_disable: disables endpoint (execute without interruption)
+ * @ci: the controller
* @num: endpoint number
* @dir: endpoint direction
*
@@ -126,6 +129,7 @@
/**
* hw_ep_enable: enables endpoint (execute without interruption)
+ * @ci: the controller
* @num: endpoint number
* @dir: endpoint direction
* @type: endpoint type
@@ -161,6 +165,7 @@
/**
* hw_ep_get_halt: return endpoint halt status
+ * @ci: the controller
* @num: endpoint number
* @dir: endpoint direction
*
@@ -175,6 +180,7 @@
/**
* hw_ep_prime: primes endpoint (execute without interruption)
+ * @ci: the controller
* @num: endpoint number
* @dir: endpoint direction
* @is_ctrl: true if control endpoint
@@ -205,6 +211,7 @@
/**
* hw_ep_set_halt: configures ep halt & resets data toggle after clear (execute
* without interruption)
+ * @ci: the controller
* @num: endpoint number
* @dir: endpoint direction
* @value: true => stall, false => unstall
@@ -231,6 +238,7 @@
/**
* hw_is_port_high_speed: test if port is high speed
+ * @ci: the controller
*
* This function returns true if high speed port
*/
@@ -243,6 +251,7 @@
/**
* hw_test_and_clear_complete: test & clear complete status (execute without
* interruption)
+ * @ci: the controller
* @n: endpoint number
*
* This function returns complete status
@@ -256,6 +265,7 @@
/**
* hw_test_and_clear_intr_active: test & clear active interrupts (execute
* without interruption)
+ * @ci: the controller
*
* This function returns active interrutps
*/
@@ -270,6 +280,7 @@
/**
* hw_test_and_clear_setup_guard: test & clear setup guard (execute without
* interruption)
+ * @ci: the controller
*
* This function returns guard value
*/
@@ -281,6 +292,7 @@
/**
* hw_test_and_set_setup_guard: test & set setup guard (execute without
* interruption)
+ * @ci: the controller
*
* This function returns guard value
*/
@@ -291,6 +303,7 @@
/**
* hw_usb_set_address: configures USB address (execute without interruption)
+ * @ci: the controller
* @value: new USB address
*
* This function explicitly sets the address, without the "USBADRA" (advance)
@@ -305,6 +318,7 @@
/**
* hw_usb_reset: restart device after a bus reset (execute without
* interruption)
+ * @ci: the controller
*
* This function returns an error code
*/
@@ -338,7 +352,7 @@
*****************************************************************************/
static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
- unsigned length)
+ unsigned int length, struct scatterlist *s)
{
int i;
u32 temp;
@@ -366,7 +380,13 @@
node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO));
}
- temp = (u32) (hwreq->req.dma + hwreq->req.actual);
+ if (s) {
+ temp = (u32) (sg_dma_address(s) + hwreq->req.actual);
+ node->td_remaining_size = CI_MAX_BUF_SIZE - length;
+ } else {
+ temp = (u32) (hwreq->req.dma + hwreq->req.actual);
+ }
+
if (length) {
node->ptr->page[0] = cpu_to_le32(temp);
for (i = 1; i < TD_PAGE_COUNT; i++) {
@@ -400,6 +420,123 @@
return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
}
+static int prepare_td_for_non_sg(struct ci_hw_ep *hwep,
+ struct ci_hw_req *hwreq)
+{
+ unsigned int rest = hwreq->req.length;
+ int pages = TD_PAGE_COUNT;
+ int ret = 0;
+
+ if (rest == 0) {
+ ret = add_td_to_list(hwep, hwreq, 0, NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * The first buffer could be not page aligned.
+ * In that case we have to span into one extra td.
+ */
+ if (hwreq->req.dma % PAGE_SIZE)
+ pages--;
+
+ while (rest > 0) {
+ unsigned int count = min(hwreq->req.length - hwreq->req.actual,
+ (unsigned int)(pages * CI_HDRC_PAGE_SIZE));
+
+ ret = add_td_to_list(hwep, hwreq, count, NULL);
+ if (ret < 0)
+ return ret;
+
+ rest -= count;
+ }
+
+ if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
+ && (hwreq->req.length % hwep->ep.maxpacket == 0)) {
+ ret = add_td_to_list(hwep, hwreq, 0, NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int prepare_td_per_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
+ struct scatterlist *s)
+{
+ unsigned int rest = sg_dma_len(s);
+ int ret = 0;
+
+ hwreq->req.actual = 0;
+ while (rest > 0) {
+ unsigned int count = min_t(unsigned int, rest,
+ CI_MAX_BUF_SIZE);
+
+ ret = add_td_to_list(hwep, hwreq, count, s);
+ if (ret < 0)
+ return ret;
+
+ rest -= count;
+ }
+
+ return ret;
+}
+
+static void ci_add_buffer_entry(struct td_node *node, struct scatterlist *s)
+{
+ int empty_td_slot_index = (CI_MAX_BUF_SIZE - node->td_remaining_size)
+ / CI_HDRC_PAGE_SIZE;
+ int i;
+ u32 token;
+
+ token = le32_to_cpu(node->ptr->token) + (sg_dma_len(s) << __ffs(TD_TOTAL_BYTES));
+ node->ptr->token = cpu_to_le32(token);
+
+ for (i = empty_td_slot_index; i < TD_PAGE_COUNT; i++) {
+ u32 page = (u32) sg_dma_address(s) +
+ (i - empty_td_slot_index) * CI_HDRC_PAGE_SIZE;
+
+ page &= ~TD_RESERVED_MASK;
+ node->ptr->page[i] = cpu_to_le32(page);
+ }
+}
+
+static int prepare_td_for_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
+{
+ struct usb_request *req = &hwreq->req;
+ struct scatterlist *s = req->sg;
+ int ret = 0, i = 0;
+ struct td_node *node = NULL;
+
+ if (!s || req->zero || req->length == 0) {
+ dev_err(hwep->ci->dev, "not supported operation for sg\n");
+ return -EINVAL;
+ }
+
+ while (i++ < req->num_mapped_sgs) {
+ if (sg_dma_address(s) % PAGE_SIZE) {
+ dev_err(hwep->ci->dev, "not page aligned sg buffer\n");
+ return -EINVAL;
+ }
+
+ if (node && (node->td_remaining_size >= sg_dma_len(s))) {
+ ci_add_buffer_entry(node, s);
+ node->td_remaining_size -= sg_dma_len(s);
+ } else {
+ ret = prepare_td_per_sg(hwep, hwreq, s);
+ if (ret)
+ return ret;
+
+ node = list_entry(hwreq->tds.prev,
+ struct td_node, td);
+ }
+
+ s = sg_next(s);
+ }
+
+ return ret;
+}
+
/**
* _hardware_enqueue: configures a request at hardware level
* @hwep: endpoint
@@ -411,8 +548,6 @@
{
struct ci_hdrc *ci = hwep->ci;
int ret = 0;
- unsigned rest = hwreq->req.length;
- int pages = TD_PAGE_COUNT;
struct td_node *firstnode, *lastnode;
/* don't queue twice */
@@ -426,35 +561,13 @@
if (ret)
return ret;
- /*
- * The first buffer could be not page aligned.
- * In that case we have to span into one extra td.
- */
- if (hwreq->req.dma % PAGE_SIZE)
- pages--;
+ if (hwreq->req.num_mapped_sgs)
+ ret = prepare_td_for_sg(hwep, hwreq);
+ else
+ ret = prepare_td_for_non_sg(hwep, hwreq);
- if (rest == 0) {
- ret = add_td_to_list(hwep, hwreq, 0);
- if (ret < 0)
- goto done;
- }
-
- while (rest > 0) {
- unsigned count = min(hwreq->req.length - hwreq->req.actual,
- (unsigned)(pages * CI_HDRC_PAGE_SIZE));
- ret = add_td_to_list(hwep, hwreq, count);
- if (ret < 0)
- goto done;
-
- rest -= count;
- }
-
- if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
- && (hwreq->req.length % hwep->ep.maxpacket == 0)) {
- ret = add_td_to_list(hwep, hwreq, 0);
- if (ret < 0)
- goto done;
- }
+ if (ret)
+ return ret;
firstnode = list_first_entry(&hwreq->tds, struct td_node, td);
@@ -512,7 +625,7 @@
return ret;
}
-/*
+/**
* free_pending_td: remove a pending request for the endpoint
* @hwep: endpoint
*/
@@ -538,8 +651,8 @@
/**
* _hardware_dequeue: handles a request at hardware level
- * @gadget: gadget
- * @hwep: endpoint
+ * @hwep: endpoint
+ * @hwreq: request
*
* This function returns an error code
*/
@@ -1117,11 +1230,11 @@
case USB_DEVICE_TEST_MODE:
tmode = le16_to_cpu(req.wIndex) >> 8;
switch (tmode) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
ci->test_mode = tmode;
err = isr_setup_status_phase(
ci);
@@ -1218,7 +1331,7 @@
/******************************************************************************
* ENDPT block
*****************************************************************************/
-/**
+/*
* ep_enable: configure endpoint, making it usable
*
* Check usb_ep_enable() at "usb_gadget.h" for details
@@ -1286,7 +1399,7 @@
return retval;
}
-/**
+/*
* ep_disable: endpoint is no longer usable
*
* Check usb_ep_disable() at "usb_gadget.h" for details
@@ -1326,7 +1439,7 @@
return retval;
}
-/**
+/*
* ep_alloc_request: allocate a request object to use with this endpoint
*
* Check usb_ep_alloc_request() at "usb_gadget.h" for details
@@ -1347,7 +1460,7 @@
return (hwreq == NULL) ? NULL : &hwreq->req;
}
-/**
+/*
* ep_free_request: frees a request object
*
* Check usb_ep_free_request() at "usb_gadget.h" for details
@@ -1380,7 +1493,7 @@
spin_unlock_irqrestore(hwep->lock, flags);
}
-/**
+/*
* ep_queue: queues (submits) an I/O request to an endpoint
*
* Check usb_ep_queue()* at usb_gadget.h" for details
@@ -1405,7 +1518,7 @@
return retval;
}
-/**
+/*
* ep_dequeue: dequeues (cancels, unlinks) an I/O request from an endpoint
*
* Check usb_ep_dequeue() at "usb_gadget.h" for details
@@ -1449,7 +1562,7 @@
return 0;
}
-/**
+/*
* ep_set_halt: sets the endpoint halt feature
*
* Check usb_ep_set_halt() at "usb_gadget.h" for details
@@ -1459,7 +1572,7 @@
return _ep_set_halt(ep, value, true);
}
-/**
+/*
* ep_set_wedge: sets the halt feature and ignores clear requests
*
* Check usb_ep_set_wedge() at "usb_gadget.h" for details
@@ -1479,7 +1592,7 @@
return usb_ep_set_halt(ep);
}
-/**
+/*
* ep_fifo_flush: flushes contents of a fifo
*
* Check usb_ep_fifo_flush() at "usb_gadget.h" for details
@@ -1505,7 +1618,7 @@
spin_unlock_irqrestore(hwep->lock, flags);
}
-/**
+/*
* Endpoint-specific part of the API to the USB controller hardware
* Check "usb_gadget.h" for details
*/
@@ -1524,44 +1637,61 @@
/******************************************************************************
* GADGET block
*****************************************************************************/
+/*
+ * ci_hdrc_gadget_connect: caller makes sure gadget driver is binded
+ */
+static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active)
+{
+ struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
+
+ if (is_active) {
+ pm_runtime_get_sync(ci->dev);
+ hw_device_reset(ci);
+ spin_lock_irq(&ci->lock);
+ if (ci->driver) {
+ hw_device_state(ci, ci->ep0out->qh.dma);
+ usb_gadget_set_state(_gadget, USB_STATE_POWERED);
+ spin_unlock_irq(&ci->lock);
+ usb_udc_vbus_handler(_gadget, true);
+ } else {
+ spin_unlock_irq(&ci->lock);
+ }
+ } else {
+ usb_udc_vbus_handler(_gadget, false);
+ if (ci->driver)
+ ci->driver->disconnect(&ci->gadget);
+ hw_device_state(ci, 0);
+ if (ci->platdata->notify_event)
+ ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_STOPPED_EVENT);
+ _gadget_stop_activity(&ci->gadget);
+ pm_runtime_put_sync(ci->dev);
+ usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED);
+ }
+}
+
static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
{
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
unsigned long flags;
- int gadget_ready = 0;
+ int ret = 0;
spin_lock_irqsave(&ci->lock, flags);
ci->vbus_active = is_active;
- if (ci->driver)
- gadget_ready = 1;
spin_unlock_irqrestore(&ci->lock, flags);
if (ci->usb_phy)
usb_phy_set_charger_state(ci->usb_phy, is_active ?
USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
- if (gadget_ready) {
- if (is_active) {
- pm_runtime_get_sync(&_gadget->dev);
- hw_device_reset(ci);
- hw_device_state(ci, ci->ep0out->qh.dma);
- usb_gadget_set_state(_gadget, USB_STATE_POWERED);
- usb_udc_vbus_handler(_gadget, true);
- } else {
- usb_udc_vbus_handler(_gadget, false);
- if (ci->driver)
- ci->driver->disconnect(&ci->gadget);
- hw_device_state(ci, 0);
- if (ci->platdata->notify_event)
- ci->platdata->notify_event(ci,
- CI_HDRC_CONTROLLER_STOPPED_EVENT);
- _gadget_stop_activity(&ci->gadget);
- pm_runtime_put_sync(&_gadget->dev);
- usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED);
- }
- }
+ if (ci->platdata->notify_event)
+ ret = ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_VBUS_EVENT);
- return 0;
+ if (ci->driver)
+ ci_hdrc_gadget_connect(_gadget, is_active);
+
+ return ret;
}
static int ci_udc_wakeup(struct usb_gadget *_gadget)
@@ -1612,7 +1742,7 @@
}
/* Change Data+ pullup status
- * this func is used by usb_gadget_connect/disconnet
+ * this func is used by usb_gadget_connect/disconnect
*/
static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
{
@@ -1625,12 +1755,12 @@
if (ci_otg_is_fsm_mode(ci) || ci->role == CI_ROLE_HOST)
return 0;
- pm_runtime_get_sync(&ci->gadget.dev);
+ pm_runtime_get_sync(ci->dev);
if (is_on)
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
else
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
- pm_runtime_put_sync(&ci->gadget.dev);
+ pm_runtime_put_sync(ci->dev);
return 0;
}
@@ -1657,7 +1787,7 @@
return NULL;
}
-/**
+/*
* Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o)
* Check "usb_gadget.h" for details
@@ -1785,18 +1915,10 @@
return retval;
}
- pm_runtime_get_sync(&ci->gadget.dev);
- if (ci->vbus_active) {
- hw_device_reset(ci);
- } else {
+ if (ci->vbus_active)
+ ci_hdrc_gadget_connect(gadget, 1);
+ else
usb_udc_vbus_handler(&ci->gadget, false);
- pm_runtime_put_sync(&ci->gadget.dev);
- return retval;
- }
-
- retval = hw_device_state(ci, ci->ep0out->qh.dma);
- if (retval)
- pm_runtime_put_sync(&ci->gadget.dev);
return retval;
}
@@ -1817,7 +1939,7 @@
mutex_unlock(&ci->fsm.lock);
}
-/**
+/*
* ci_udc_stop: unregister a gadget driver
*/
static int ci_udc_stop(struct usb_gadget *gadget)
@@ -1826,6 +1948,7 @@
unsigned long flags;
spin_lock_irqsave(&ci->lock, flags);
+ ci->driver = NULL;
if (ci->vbus_active) {
hw_device_state(ci, 0);
@@ -1835,10 +1958,9 @@
CI_HDRC_CONTROLLER_STOPPED_EVENT);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
- pm_runtime_put(&ci->gadget.dev);
+ pm_runtime_put(ci->dev);
}
- ci->driver = NULL;
spin_unlock_irqrestore(&ci->lock, flags);
ci_udc_stop_for_otg_fsm(ci);
@@ -1848,7 +1970,7 @@
/******************************************************************************
* BUS block
*****************************************************************************/
-/**
+/*
* udc_irq: ci interrupt handler
*
* This function returns IRQ_HANDLED if the IRQ has been handled
@@ -1932,6 +2054,8 @@
ci->gadget.max_speed = USB_SPEED_HIGH;
ci->gadget.name = ci->platdata->name;
ci->gadget.otg_caps = otg_caps;
+ ci->gadget.sg_supported = 1;
+ ci->gadget.irq = ci->irq;
if (ci->platdata->flags & CI_HDRC_REQUIRES_ALIGNED_DMA)
ci->gadget.quirk_avoids_skb_reserve = 1;
@@ -1967,9 +2091,6 @@
if (retval)
goto destroy_eps;
- pm_runtime_no_callbacks(&ci->gadget.dev);
- pm_runtime_enable(&ci->gadget.dev);
-
return retval;
destroy_eps:
@@ -1981,7 +2102,7 @@
return retval;
}
-/**
+/*
* ci_hdrc_gadget_destroy: parent remove must call this to remove UDC
*
* No interrupts active, the IRQ has been released
@@ -2031,7 +2152,7 @@
/**
* ci_hdrc_gadget_init - initialize device related bits
- * ci: the controller
+ * @ci: the controller
*
* This function initializes the gadget, if the device is "device capable".
*/
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
index e023735..5193df1 100644
--- a/drivers/usb/chipidea/udc.h
+++ b/drivers/usb/chipidea/udc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* udc.h - ChipIdea UDC structures
*
@@ -61,16 +61,14 @@
struct list_head td;
dma_addr_t dma;
struct ci_hw_td *ptr;
+ int td_remaining_size;
};
/**
* struct ci_hw_req - usb request representation
* @req: request structure for gadget drivers
* @queue: link to QH list
- * @ptr: transfer descriptor for this request
- * @dma: dma address for the transfer descriptor
- * @zptr: transfer descriptor for the zero packet
- * @zdma: dma address of the zero packet's transfer descriptor
+ * @tds: link to TD list
*/
struct ci_hw_req {
struct usb_request req;
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 078c1fd..425b291 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/usb/otg.h>
#include "ci_hdrc_imx.h"
@@ -99,6 +100,42 @@
#define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3)
+#define MX7D_USBNC_AUTO_RESUME BIT(2)
+/* The default DM/DP value is pull-down */
+#define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6)
+#define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1)
+#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6))
+#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8)
+#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12)
+#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13)
+#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14)
+#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15)
+#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \
+ BIT(14) | BIT(15))
+
+#define MX7D_USB_OTG_PHY_CFG1 0x30
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
+#define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0 BIT(16)
+
+#define MX7D_USB_OTG_PHY_CFG2 0x34
+
+#define MX7D_USB_OTG_PHY_STATUS 0x3c
+#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
+#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
+#define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3)
+#define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
+
+#define MX7D_USB_OTG_PHY_CFG1 0x30
+#define TXPREEMPAMPTUNE0_BIT 28
+#define TXPREEMPAMPTUNE0_MASK (3 << 28)
+#define TXVREFTUNE0_BIT 20
+#define TXVREFTUNE0_MASK (0xf << 20)
+
+#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
+ MX6_BM_ID_WAKEUP)
struct usbmisc_ops {
/* It's called once when probe a usb device */
@@ -111,6 +148,8 @@
int (*hsic_set_connect)(struct imx_usbmisc_data *data);
/* It's called during suspend/resume */
int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled);
+ /* usb charger detection */
+ int (*charger_detection)(struct imx_usbmisc_data *data);
};
struct imx_usbmisc {
@@ -330,14 +369,25 @@
return 0;
}
+static u32 usbmisc_wakeup_setting(struct imx_usbmisc_data *data)
+{
+ u32 wakeup_setting = MX6_USB_OTG_WAKEUP_BITS;
+
+ if (data->ext_id || data->available_role != USB_DR_MODE_OTG)
+ wakeup_setting &= ~MX6_BM_ID_WAKEUP;
+
+ if (data->ext_vbus || data->available_role == USB_DR_MODE_HOST)
+ wakeup_setting &= ~MX6_BM_VBUS_WAKEUP;
+
+ return wakeup_setting;
+}
+
static int usbmisc_imx6q_set_wakeup
(struct imx_usbmisc_data *data, bool enabled)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
- u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
- MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
int ret = 0;
if (data->index > 3)
@@ -346,11 +396,12 @@
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + data->index * 4);
if (enabled) {
- val |= wakeup_setting;
+ val &= ~MX6_USB_OTG_WAKEUP_BITS;
+ val |= usbmisc_wakeup_setting(data);
} else {
if (val & MX6_BM_WAKEUP_INTR)
pr_debug("wakeup int at ci_hdrc.%d\n", data->index);
- val &= ~wakeup_setting;
+ val &= ~MX6_USB_OTG_WAKEUP_BITS;
}
writel(val, usbmisc->base + data->index * 4);
spin_unlock_irqrestore(&usbmisc->lock, flags);
@@ -547,17 +598,17 @@
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
- u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
- MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base);
if (enabled) {
- writel(val | wakeup_setting, usbmisc->base);
+ val &= ~MX6_USB_OTG_WAKEUP_BITS;
+ val |= usbmisc_wakeup_setting(data);
+ writel(val, usbmisc->base);
} else {
if (val & MX6_BM_WAKEUP_INTR)
dev_dbg(data->dev, "wakeup int\n");
- writel(val & ~wakeup_setting, usbmisc->base);
+ writel(val & ~MX6_USB_OTG_WAKEUP_BITS, usbmisc->base);
}
spin_unlock_irqrestore(&usbmisc->lock, flags);
@@ -594,10 +645,290 @@
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
- reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
- reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
- writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
- usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ /* SoC non-burst setting */
+ reg = readl(usbmisc->base);
+ writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
+
+ if (!data->hsic) {
+ reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
+ writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID
+ | MX7D_USBNC_AUTO_RESUME,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ /* PHY tuning for signal quality */
+ reg = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
+ if (data->emp_curr_control && data->emp_curr_control <=
+ (TXPREEMPAMPTUNE0_MASK >> TXPREEMPAMPTUNE0_BIT)) {
+ reg &= ~TXPREEMPAMPTUNE0_MASK;
+ reg |= (data->emp_curr_control << TXPREEMPAMPTUNE0_BIT);
+ }
+
+ if (data->dc_vol_level_adjust && data->dc_vol_level_adjust <=
+ (TXVREFTUNE0_MASK >> TXVREFTUNE0_BIT)) {
+ reg &= ~TXVREFTUNE0_MASK;
+ reg |= (data->dc_vol_level_adjust << TXVREFTUNE0_BIT);
+ }
+
+ writel(reg, usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
+ }
+
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ usbmisc_imx7d_set_wakeup(data, false);
+
+ return 0;
+}
+
+static int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ struct usb_phy *usb_phy = data->usb_phy;
+ int val;
+ unsigned long flags;
+
+ /* Clear VDATSRCENB0 to disable VDP_SRC and IDM_SNK required by BC 1.2 spec */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0;
+ writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ /* TVDMSRC_DIS */
+ msleep(20);
+
+ /* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL,
+ usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ /* TVDMSRC_ON */
+ msleep(40);
+
+ /*
+ * Per BC 1.2, check voltage of D+:
+ * DCP: if greater than VDAT_REF;
+ * CDP: if less than VDAT_REF.
+ */
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+ if (val & MX7D_USB_OTG_PHY_STATUS_CHRGDET) {
+ dev_dbg(data->dev, "It is a dedicate charging port\n");
+ usb_phy->chg_type = DCP_TYPE;
+ } else {
+ dev_dbg(data->dev, "It is a charging downstream port\n");
+ usb_phy->chg_type = CDP_TYPE;
+ }
+
+ return 0;
+}
+
+static void imx7_disable_charger_detector(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ val &= ~(MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL);
+ writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+
+ /* Set OPMODE to be 2'b00 and disable its override */
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
+ writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
+
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ writel(val & ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+}
+
+static int imx7d_charger_data_contact_detect(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 val;
+ int i, data_pin_contact_count = 0;
+
+ /* Enable Data Contact Detect (DCD) per the USB BC 1.2 */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
+ usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ for (i = 0; i < 100; i = i + 1) {
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+ if (!(val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0)) {
+ if (data_pin_contact_count++ > 5)
+ /* Data pin makes contact */
+ break;
+ usleep_range(5000, 10000);
+ } else {
+ data_pin_contact_count = 0;
+ usleep_range(5000, 6000);
+ }
+ }
+
+ /* Disable DCD after finished data contact check */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ writel(val & ~MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
+ usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ if (i == 100) {
+ dev_err(data->dev,
+ "VBUS is coming from a dedicated power supply.\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ struct usb_phy *usb_phy = data->usb_phy;
+ unsigned long flags;
+ u32 val;
+
+ /* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL;
+ writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0,
+ usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ /* TVDPSRC_ON */
+ msleep(40);
+
+ /* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+ if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET)) {
+ dev_dbg(data->dev, "It is a standard downstream port\n");
+ usb_phy->chg_type = SDP_TYPE;
+ }
+
+ return 0;
+}
+
+/*
+ * Whole charger detection process:
+ * 1. OPMODE override to be non-driving
+ * 2. Data contact check
+ * 3. Primary detection
+ * 4. Secondary detection
+ * 5. Disable charger detection
+ */
+static int imx7d_charger_detection(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ struct usb_phy *usb_phy = data->usb_phy;
+ unsigned long flags;
+ u32 val;
+ int ret;
+
+ /* Check if vbus is valid */
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+ if (!(val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD)) {
+ dev_err(data->dev, "vbus is error\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Keep OPMODE to be non-driving mode during the whole
+ * charger detection process.
+ */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
+ val |= MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING;
+ writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
+
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ writel(val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ ret = imx7d_charger_data_contact_detect(data);
+ if (ret)
+ return ret;
+
+ ret = imx7d_charger_primary_detection(data);
+ if (!ret && usb_phy->chg_type != SDP_TYPE)
+ ret = imx7d_charger_secondary_detection(data);
+
+ imx7_disable_charger_detector(data);
+
+ return ret;
+}
+
+static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 reg;
+
+ if (data->index >= 1)
+ return -EINVAL;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ reg = readl(usbmisc->base);
+ if (data->disable_oc) {
+ reg |= MX6_BM_OVER_CUR_DIS;
+ } else {
+ reg &= ~MX6_BM_OVER_CUR_DIS;
+
+ /*
+ * If the polarity is not configured keep it as setup by the
+ * bootloader.
+ */
+ if (data->oc_pol_configured && data->oc_pol_active_low)
+ reg |= MX6_BM_OVER_CUR_POLARITY;
+ else if (data->oc_pol_configured)
+ reg &= ~MX6_BM_OVER_CUR_POLARITY;
+ }
+ /* If the polarity is not set keep it as setup by the bootlader */
+ if (data->pwr_pol == 1)
+ reg |= MX6_BM_PWR_POLARITY;
+
+ writel(reg, usbmisc->base);
+
+ /* SoC non-burst setting */
+ reg = readl(usbmisc->base);
+ writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
+
+ if (data->hsic) {
+ reg = readl(usbmisc->base);
+ writel(reg | MX6_BM_UTMI_ON_CLOCK, usbmisc->base);
+
+ reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
+ reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON;
+ writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
+
+ /*
+ * For non-HSIC controller, the autoresume is enabled
+ * at MXS PHY driver (usbphy_ctrl bit18).
+ */
+ reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ writel(reg | MX7D_USBNC_AUTO_RESUME,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ } else {
+ reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
+ writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ }
spin_unlock_irqrestore(&usbmisc->lock, flags);
@@ -644,6 +975,14 @@
static const struct usbmisc_ops imx7d_usbmisc_ops = {
.init = usbmisc_imx7d_init,
.set_wakeup = usbmisc_imx7d_set_wakeup,
+ .charger_detection = imx7d_charger_detection,
+};
+
+static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
+ .init = usbmisc_imx7ulp_init,
+ .set_wakeup = usbmisc_imx7d_set_wakeup,
+ .hsic_set_connect = usbmisc_imx6_hsic_set_connect,
+ .hsic_set_clk = usbmisc_imx6_hsic_set_clk,
};
static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data)
@@ -722,6 +1061,39 @@
return usbmisc->ops->hsic_set_clk(data, on);
}
EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
+
+int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect)
+{
+ struct imx_usbmisc *usbmisc;
+ struct usb_phy *usb_phy;
+ int ret = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ usbmisc = dev_get_drvdata(data->dev);
+ usb_phy = data->usb_phy;
+ if (!usbmisc->ops->charger_detection)
+ return -ENOTSUPP;
+
+ if (connect) {
+ ret = usbmisc->ops->charger_detection(data);
+ if (ret) {
+ dev_err(data->dev,
+ "Error occurs during detection: %d\n",
+ ret);
+ usb_phy->chg_state = USB_CHARGER_ABSENT;
+ } else {
+ usb_phy->chg_state = USB_CHARGER_PRESENT;
+ }
+ } else {
+ usb_phy->chg_state = USB_CHARGER_ABSENT;
+ usb_phy->chg_type = UNKNOWN_TYPE;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection);
+
static const struct of_device_id usbmisc_imx_dt_ids[] = {
{
.compatible = "fsl,imx25-usbmisc",
@@ -765,7 +1137,7 @@
},
{
.compatible = "fsl,imx7ulp-usbmisc",
- .data = &imx7d_usbmisc_ops,
+ .data = &imx7ulp_usbmisc_ops,
},
{ /* sentinel */ }
};
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
index f8a7989..d3f5162 100644
--- a/drivers/usb/class/Kconfig
+++ b/drivers/usb/class/Kconfig
@@ -7,7 +7,7 @@
config USB_ACM
tristate "USB Modem (CDC ACM) support"
depends on TTY
- ---help---
+ help
This driver supports USB modems and ISDN adapters which support the
Communication Device Class Abstract Control Model interface.
Please read <file:Documentation/usb/acm.rst> for details.
@@ -30,7 +30,7 @@
config USB_WDM
tristate "USB Wireless Device Management support"
- ---help---
+ help
This driver supports the WMC Device Management functionality
of cell phones compliant to the CDC WMC specification. You can use
AT commands over this device.
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index c0604c6..7950d5b 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -185,7 +185,7 @@
for (;;) {
wb = &acm->wb[wbn];
if (!wb->use) {
- wb->use = 1;
+ wb->use = true;
wb->len = 0;
return wbn;
}
@@ -203,7 +203,8 @@
n = ACM_NW;
spin_lock_irqsave(&acm->write_lock, flags);
for (i = 0; i < ACM_NW; i++)
- n -= acm->wb[i].use;
+ if(acm->wb[i].use)
+ n--;
spin_unlock_irqrestore(&acm->write_lock, flags);
return n;
}
@@ -213,7 +214,7 @@
*/
static void acm_write_done(struct acm *acm, struct acm_wb *wb)
{
- wb->use = 0;
+ wb->use = false;
acm->transmitting--;
usb_autopm_put_interface_async(acm->control);
}
@@ -339,6 +340,9 @@
acm->iocount.overrun++;
spin_unlock_irqrestore(&acm->read_lock, flags);
+ if (newctrl & ACM_CTRL_BRK)
+ tty_flip_buffer_push(&acm->port);
+
if (difference)
wake_up_all(&acm->wioctl);
@@ -474,11 +478,16 @@
static void acm_process_read_urb(struct acm *acm, struct urb *urb)
{
+ unsigned long flags;
+
if (!urb->actual_length)
return;
+ spin_lock_irqsave(&acm->read_lock, flags);
tty_insert_flip_string(&acm->port, urb->transfer_buffer,
urb->actual_length);
+ spin_unlock_irqrestore(&acm->read_lock, flags);
+
tty_flip_buffer_push(&acm->port);
}
@@ -725,7 +734,8 @@
{
struct acm *acm = container_of(port, struct acm, port);
- acm_release_minor(acm);
+ if (acm->minor != ACM_MINOR_INVALID)
+ acm_release_minor(acm);
usb_put_intf(acm->control);
kfree(acm->country_codes);
kfree(acm);
@@ -754,7 +764,7 @@
if (!urb)
break;
wb = urb->context;
- wb->use = 0;
+ wb->use = false;
usb_autopm_put_interface_async(acm->control);
}
@@ -806,7 +816,7 @@
wb = &acm->wb[wbn];
if (!acm->dev) {
- wb->use = 0;
+ wb->use = false;
spin_unlock_irqrestore(&acm->write_lock, flags);
return -ENODEV;
}
@@ -818,7 +828,7 @@
stat = usb_autopm_get_interface_async(acm->control);
if (stat) {
- wb->use = 0;
+ wb->use = false;
spin_unlock_irqrestore(&acm->write_lock, flags);
return stat;
}
@@ -1200,9 +1210,6 @@
return -EINVAL;
}
- if (!intf->cur_altsetting)
- return -EINVAL;
-
if (!buflen) {
if (intf->cur_altsetting->endpoint &&
intf->cur_altsetting->endpoint->extralen &&
@@ -1225,26 +1232,19 @@
call_intf_num = cmgmd->bDataInterface;
if (!union_header) {
- if (call_intf_num > 0) {
+ if (intf->cur_altsetting->desc.bNumEndpoints == 3) {
+ dev_dbg(&intf->dev, "No union descriptor, assuming single interface\n");
+ combined_interfaces = 1;
+ control_interface = data_interface = intf;
+ goto look_for_collapsed_interface;
+ } else if (call_intf_num > 0) {
dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n");
- /* quirks for Droids MuIn LCD */
- if (quirks & NO_DATA_INTERFACE) {
- data_interface = usb_ifnum_to_if(usb_dev, 0);
- } else {
- data_intf_num = call_intf_num;
- data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
- }
+ data_intf_num = call_intf_num;
+ data_interface = usb_ifnum_to_if(usb_dev, data_intf_num);
control_interface = intf;
} else {
- if (intf->cur_altsetting->desc.bNumEndpoints != 3) {
- dev_dbg(&intf->dev,"No union descriptor, giving up\n");
- return -ENODEV;
- } else {
- dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n");
- combined_interfaces = 1;
- control_interface = data_interface = intf;
- goto look_for_collapsed_interface;
- }
+ dev_dbg(&intf->dev, "No union descriptor, giving up\n");
+ return -ENODEV;
}
} else {
int class = -1;
@@ -1268,8 +1268,6 @@
dev_dbg(&intf->dev, "no interfaces\n");
return -ENODEV;
}
- if (!data_interface->cur_altsetting || !control_interface->cur_altsetting)
- return -ENODEV;
if (data_intf_num != call_intf_num)
dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
@@ -1296,10 +1294,8 @@
skip_normal_probe:
/*workaround for switched interfaces */
- if (data_interface->cur_altsetting->desc.bInterfaceClass
- != CDC_DATA_INTERFACE_TYPE) {
- if (control_interface->cur_altsetting->desc.bInterfaceClass
- == CDC_DATA_INTERFACE_TYPE) {
+ if (data_interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA) {
+ if (control_interface->cur_altsetting->desc.bInterfaceClass == USB_CLASS_CDC_DATA) {
dev_dbg(&intf->dev,
"Your device has switched interfaces.\n");
swap(control_interface, data_interface);
@@ -1356,8 +1352,10 @@
usb_get_intf(acm->control); /* undone in destruct() */
minor = acm_alloc_minor(acm);
- if (minor < 0)
+ if (minor < 0) {
+ acm->minor = ACM_MINOR_INVALID;
goto alloc_fail1;
+ }
acm->minor = minor;
acm->dev = usb_dev;
@@ -1909,11 +1907,6 @@
/* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
- /* Support for Droids MuIn LCD */
- { USB_DEVICE(0x04d8, 0x000b),
- .driver_info = NO_DATA_INTERFACE,
- },
-
#if IS_ENABLED(CONFIG_INPUT_IMS_PCU)
{ USB_DEVICE(0x04d8, 0x0082), /* Application mode */
.driver_info = IGNORE_DEVICE,
@@ -1921,6 +1914,12 @@
{ USB_DEVICE(0x04d8, 0x0083), /* Bootloader mode */
.driver_info = IGNORE_DEVICE,
},
+#endif
+
+#if IS_ENABLED(CONFIG_IR_TOY)
+ { USB_DEVICE(0x04d8, 0xfd08),
+ .driver_info = IGNORE_DEVICE,
+ },
{ USB_DEVICE(0x04d8, 0xf58b),
.driver_info = IGNORE_DEVICE,
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index b95ff76..3aa7f0a 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -22,6 +22,8 @@
#define ACM_TTY_MAJOR 166
#define ACM_TTY_MINORS 256
+#define ACM_MINOR_INVALID ACM_TTY_MINORS
+
/*
* Requests.
*/
@@ -64,12 +66,12 @@
#define ACM_NR 16
struct acm_wb {
- unsigned char *buf;
+ u8 *buf;
dma_addr_t dmah;
- int len;
- int use;
+ unsigned int len;
struct urb *urb;
struct acm *instance;
+ bool use;
};
struct acm_rb {
@@ -130,15 +132,12 @@
unsigned long quirks;
};
-#define CDC_DATA_INTERFACE_TYPE 0x0a
-
/* constants describing various quirks and errors */
#define NO_UNION_NORMAL BIT(0)
#define SINGLE_RX_URB BIT(1)
#define NO_CAP_LINE BIT(2)
-#define NO_DATA_INTERFACE BIT(4)
-#define IGNORE_DEVICE BIT(5)
-#define QUIRK_CONTROL_LINE_STATE BIT(6)
-#define CLEAR_HALT_CONDITIONS BIT(7)
-#define SEND_ZERO_PACKET BIT(8)
-#define DISABLE_ECHO BIT(9)
+#define IGNORE_DEVICE BIT(3)
+#define QUIRK_CONTROL_LINE_STATE BIT(4)
+#define CLEAR_HALT_CONDITIONS BIT(5)
+#define SEND_ZERO_PACKET BIT(6)
+#define DISABLE_ECHO BIT(7)
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index de7bb8e..d1e4a73 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -794,7 +794,7 @@
.release = wdm_release,
.poll = wdm_poll,
.unlocked_ioctl = wdm_ioctl,
- .compat_ioctl = wdm_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.llseek = noop_llseek,
};
@@ -1000,7 +1000,8 @@
* @intf: usb interface the subdriver will associate with
* @ep: interrupt endpoint to monitor for notifications
* @bufsize: maximum message size to support for read/write
- *
+ * @manage_power: call-back invoked during open and release to
+ * manage the device's power
* Create WDM usb class character device and associate it with intf
* without binding, allowing another driver to manage the interface.
*
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 3922a6f..49f59d5 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0+
-/**
+/*
* drivers/usb/class/usbtmc.c - USB Test & Measurement class driver
*
* Copyright (C) 2007 Stefan Kopp, Gechingen, Germany
@@ -1889,6 +1889,7 @@
struct usbtmc_ctrlrequest request;
u8 *buffer = NULL;
int rv;
+ unsigned int is_in, pipe;
unsigned long res;
res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest));
@@ -1898,12 +1899,14 @@
if (request.req.wLength > USBTMC_BUFSIZE)
return -EMSGSIZE;
+ is_in = request.req.bRequestType & USB_DIR_IN;
+
if (request.req.wLength) {
buffer = kmalloc(request.req.wLength, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
- if ((request.req.bRequestType & USB_DIR_IN) == 0) {
+ if (!is_in) {
/* Send control data to device */
res = copy_from_user(buffer, request.data,
request.req.wLength);
@@ -1914,8 +1917,12 @@
}
}
+ if (is_in)
+ pipe = usb_rcvctrlpipe(data->usb_dev, 0);
+ else
+ pipe = usb_sndctrlpipe(data->usb_dev, 0);
rv = usb_control_msg(data->usb_dev,
- usb_rcvctrlpipe(data->usb_dev, 0),
+ pipe,
request.req.bRequest,
request.req.bRequestType,
request.req.wValue,
@@ -1927,7 +1934,7 @@
goto exit;
}
- if (rv && (request.req.bRequestType & USB_DIR_IN)) {
+ if (rv && is_in) {
/* Read control data from device */
res = copy_to_user(request.data, buffer, rv);
if (res)
@@ -2217,9 +2224,7 @@
.release = usbtmc_release,
.flush = usbtmc_flush,
.unlocked_ioctl = usbtmc_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = usbtmc_ioctl,
-#endif
+ .compat_ioctl = compat_ptr_ioctl,
.fasync = usbtmc_fasync,
.poll = usbtmc_poll,
.llseek = default_llseek,
@@ -2284,7 +2289,7 @@
case -EOVERFLOW:
dev_err(dev, "overflow with length %d, actual length is %d\n",
data->iin_wMaxPacketSize, urb->actual_length);
- /* fall through */
+ fallthrough;
default:
/* urb terminated, clean up */
dev_dbg(dev, "urb terminated, status: %d\n", status);
diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig
index d611477..b856622 100644
--- a/drivers/usb/common/Kconfig
+++ b/drivers/usb/common/Kconfig
@@ -6,8 +6,7 @@
config USB_LED_TRIG
bool "USB LED Triggers"
- depends on LEDS_CLASS && LEDS_TRIGGERS
- select USB_COMMON
+ depends on LEDS_CLASS && USB_COMMON && LEDS_TRIGGERS
help
This option adds LED triggers for USB host and/or gadget activity.
@@ -40,6 +39,7 @@
tristate "USB GPIO Based Connection Detection Driver"
depends on GPIOLIB
select USB_ROLE_SWITCH
+ select POWER_SUPPLY
help
The driver supports USB role switch between host and device via GPIO
based USB cable detection, used typically if an input GPIO is used
diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index 92a986a..ba849c7 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* Common USB debugging functions
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -53,15 +53,15 @@
static const char *usb_decode_test_mode(u16 wIndex)
{
switch (wIndex) {
- case TEST_J:
+ case USB_TEST_J:
return ": TEST_J";
- case TEST_K:
+ case USB_TEST_K:
return ": TEST_K";
- case TEST_SE0_NAK:
+ case USB_TEST_SE0_NAK:
return ": TEST_SE0_NAK";
- case TEST_PACKET:
+ case USB_TEST_PACKET:
return ": TEST_PACKET";
- case TEST_FORCE_EN:
+ case USB_TEST_FORCE_ENABLE:
return ": TEST_FORCE_EN";
default:
return ": UNKNOWN";
@@ -207,7 +207,7 @@
snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue);
}
-/**
+/*
* usb_decode_ctrl - returns a string representation of ctrl request
*/
const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 9a2ab67..3c705f1 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* ulpi.c - USB ULPI PHY bus
*
* Copyright (C) 2015 Intel Corporation
@@ -39,8 +39,11 @@
struct ulpi *ulpi = to_ulpi_dev(dev);
const struct ulpi_device_id *id;
- /* Some ULPI devices don't have a vendor id so rely on OF match */
- if (ulpi->id.vendor == 0)
+ /*
+ * Some ULPI devices don't have a vendor id
+ * or provide an id_table so rely on OF match.
+ */
+ if (ulpi->id.vendor == 0 || !drv->id_table)
return of_driver_match_device(dev, driver);
for (id = drv->id_table; id->vendor; id++)
@@ -129,6 +132,7 @@
static void ulpi_dev_release(struct device *dev)
{
+ of_node_put(dev->of_node);
kfree(to_ulpi_dev(dev));
}
@@ -143,6 +147,7 @@
/**
* ulpi_register_driver - register a driver with the ULPI bus
* @drv: driver being registered
+ * @module: ends up being THIS_MODULE
*
* Registers a driver with the ULPI bus.
*/
@@ -245,12 +250,16 @@
return ret;
ret = ulpi_read_id(ulpi);
- if (ret)
+ if (ret) {
+ of_node_put(ulpi->dev.of_node);
return ret;
+ }
ret = device_register(&ulpi->dev);
- if (ret)
+ if (ret) {
+ put_device(&ulpi->dev);
return ret;
+ }
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
ulpi->id.vendor, ulpi->id.product);
@@ -290,14 +299,13 @@
/**
* ulpi_unregister_interface - unregister ULPI interface
- * @intrf: struct ulpi_interface
+ * @ulpi: struct ulpi_interface
*
* Unregisters a ULPI device and it's interface that was created with
* ulpi_create_interface().
*/
void ulpi_unregister_interface(struct ulpi *ulpi)
{
- of_node_put(ulpi->dev.of_node);
device_unregister(&ulpi->dev);
}
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c
index ed204cb..c9545a4 100644
--- a/drivers/usb/common/usb-conn-gpio.c
+++ b/drivers/usb/common/usb-conn-gpio.c
@@ -17,6 +17,7 @@
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/role.h>
@@ -38,9 +39,12 @@
struct gpio_desc *vbus_gpiod;
int id_irq;
int vbus_irq;
+
+ struct power_supply_desc desc;
+ struct power_supply *charger;
};
-/**
+/*
* "DEVICE" = VBUS and "HOST" = !ID, so we have:
* Both "DEVICE" and "HOST" can't be set as active at the same time
* so if "HOST" is active (i.e. ID is 0) we keep "DEVICE" inactive
@@ -87,14 +91,14 @@
return;
}
- if (info->last_role == USB_ROLE_HOST)
+ if (info->last_role == USB_ROLE_HOST && info->vbus)
regulator_disable(info->vbus);
ret = usb_role_switch_set_role(info->role_sw, role);
if (ret)
dev_err(info->dev, "failed to set role: %d\n", ret);
- if (role == USB_ROLE_HOST) {
+ if (role == USB_ROLE_HOST && info->vbus) {
ret = regulator_enable(info->vbus);
if (ret)
dev_err(info->dev, "enable vbus regulator failed\n");
@@ -102,8 +106,11 @@
info->last_role = role;
- dev_dbg(info->dev, "vbus regulator is %s\n",
- regulator_is_enabled(info->vbus) ? "enabled" : "disabled");
+ if (info->vbus)
+ dev_dbg(info->dev, "vbus regulator is %s\n",
+ regulator_is_enabled(info->vbus) ? "enabled" : "disabled");
+
+ power_supply_changed(info->charger);
}
static void usb_conn_queue_dwork(struct usb_conn_info *info,
@@ -121,10 +128,54 @@
return IRQ_HANDLED;
}
+static enum power_supply_property usb_charger_properties[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int usb_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct usb_conn_info *info = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = info->last_role == USB_ROLE_DEVICE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int usb_conn_psy_register(struct usb_conn_info *info)
+{
+ struct device *dev = info->dev;
+ struct power_supply_desc *desc = &info->desc;
+ struct power_supply_config cfg = {
+ .of_node = dev->of_node,
+ };
+
+ desc->name = "usb-charger";
+ desc->properties = usb_charger_properties;
+ desc->num_properties = ARRAY_SIZE(usb_charger_properties);
+ desc->get_property = usb_charger_get_property;
+ desc->type = POWER_SUPPLY_TYPE_USB;
+ cfg.drv_data = info;
+
+ info->charger = devm_power_supply_register(dev, desc, &cfg);
+ if (IS_ERR(info->charger))
+ dev_err(dev, "Unable to register charger\n");
+
+ return PTR_ERR_OR_ZERO(info->charger);
+}
+
static int usb_conn_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct usb_conn_info *info;
+ bool need_vbus = true;
int ret = 0;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
@@ -154,10 +205,26 @@
INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
- info->vbus = devm_regulator_get(dev, "vbus");
+ /*
+ * If the USB connector is a child of a USB port and that port already provides the VBUS
+ * supply, there's no need for the USB connector to provide it again.
+ */
+ if (dev->parent && dev->parent->of_node) {
+ if (of_find_property(dev->parent->of_node, "vbus-supply", NULL))
+ need_vbus = false;
+ }
+
+ if (!need_vbus) {
+ info->vbus = devm_regulator_get_optional(dev, "vbus");
+ if (PTR_ERR(info->vbus) == -ENODEV)
+ info->vbus = NULL;
+ } else {
+ info->vbus = devm_regulator_get(dev, "vbus");
+ }
+
if (IS_ERR(info->vbus)) {
if (PTR_ERR(info->vbus) != -EPROBE_DEFER)
- dev_err(dev, "failed to get vbus\n");
+ dev_err(dev, "failed to get vbus: %ld\n", PTR_ERR(info->vbus));
return PTR_ERR(info->vbus);
}
@@ -169,6 +236,10 @@
return PTR_ERR(info->role_sw);
}
+ ret = usb_conn_psy_register(info);
+ if (ret)
+ goto put_role_sw;
+
if (info->id_gpiod) {
info->id_irq = gpiod_to_irq(info->id_gpiod);
if (info->id_irq < 0) {
@@ -221,7 +292,7 @@
cancel_delayed_work_sync(&info->dw_det);
- if (info->last_role == USB_ROLE_HOST)
+ if (info->last_role == USB_ROLE_HOST && info->vbus)
regulator_disable(info->vbus);
usb_role_switch_put(info->role_sw);
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index ecaacc8..351ede4 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -32,6 +32,20 @@
If you have any questions about this, say Y here, only say N
if you know exactly what you are doing.
+config USB_FEW_INIT_RETRIES
+ bool "Limit USB device initialization to only a few retries"
+ help
+ When a new USB device is detected, the kernel tries very hard
+ to initialize and enumerate it, with lots of nested retry loops.
+ This almost always works, but when it fails it can take a long time.
+ This option tells the kernel to make only a few retry attempts,
+ so that the total time required for a failed initialization is
+ no more than 30 seconds (as required by the USB OTG spec).
+
+ Say N here unless you require new-device enumeration failure to
+ occur within 30 seconds (as might be needed in an embedded
+ application).
+
config USB_DYNAMIC_MINORS
bool "Dynamic USB minor allocation"
help
@@ -55,18 +69,18 @@
Select this only if your board has Mini-AB/Micro-AB
connector.
-config USB_OTG_WHITELIST
+config USB_OTG_PRODUCTLIST
bool "Rely on OTG and EH Targeted Peripherals List"
depends on USB
help
- If you say Y here, the "otg_whitelist.h" file will be used as a
- product whitelist, so USB peripherals not listed there will be
+ If you say Y here, the "otg_productlist.h" file will be used as a
+ product list, so USB peripherals not listed there will be
rejected during enumeration. This behavior is required by the
USB OTG and EH specification for all devices not on your product's
"Targeted Peripherals List". "Embedded Hosts" are likewise
allowed to support only a limited number of peripherals.
-config USB_OTG_BLACKLIST_HUB
+config USB_OTG_DISABLE_EXTERNAL_HUB
bool "Disable external hubs"
depends on USB_OTG || EXPERT
help
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index c68217b..39f1eca 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -298,10 +298,10 @@
goto skip_to_next_endpoint_or_interface_descriptor;
}
- /* Ignore blacklisted endpoints */
- if (udev->quirks & USB_QUIRK_ENDPOINT_BLACKLIST) {
- if (usb_endpoint_is_blacklisted(udev, ifp, d)) {
- dev_warn(ddev, "config %d interface %d altsetting %d has a blacklisted endpoint with address 0x%X, skipping\n",
+ /* Ignore some endpoints */
+ if (udev->quirks & USB_QUIRK_ENDPOINT_IGNORE) {
+ if (usb_endpoint_is_ignored(udev, ifp, d)) {
+ dev_warn(ddev, "config %d interface %d altsetting %d has an ignored endpoint with address 0x%X, skipping\n",
cfgno, inum, asnum,
d->bEndpointAddress);
goto skip_to_next_endpoint_or_interface_descriptor;
@@ -322,7 +322,7 @@
j = 255;
if (usb_endpoint_xfer_int(d)) {
i = 1;
- switch (to_usb_device(ddev)->speed) {
+ switch (udev->speed) {
case USB_SPEED_SUPER_PLUS:
case USB_SPEED_SUPER:
case USB_SPEED_HIGH:
@@ -343,8 +343,7 @@
/*
* This quirk fixes bIntervals reported in ms.
*/
- if (to_usb_device(ddev)->quirks &
- USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL) {
+ if (udev->quirks & USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL) {
n = clamp(fls(d->bInterval) + 3, i, j);
i = j = n;
}
@@ -352,8 +351,7 @@
* This quirk fixes bIntervals reported in
* linear microframes.
*/
- if (to_usb_device(ddev)->quirks &
- USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) {
+ if (udev->quirks & USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) {
n = clamp(fls(d->bInterval), i, j);
i = j = n;
}
@@ -370,7 +368,7 @@
} else if (usb_endpoint_xfer_isoc(d)) {
i = 1;
j = 16;
- switch (to_usb_device(ddev)->speed) {
+ switch (udev->speed) {
case USB_SPEED_HIGH:
n = 7; /* 8 ms = 2^(7-1) uframes */
break;
@@ -392,8 +390,7 @@
* explicitly forbidden by the USB spec. In an attempt to make
* them usable, we will try treating them as Interrupt endpoints.
*/
- if (to_usb_device(ddev)->speed == USB_SPEED_LOW &&
- usb_endpoint_xfer_bulk(d)) {
+ if (udev->speed == USB_SPEED_LOW && usb_endpoint_xfer_bulk(d)) {
dev_warn(ddev, "config %d interface %d altsetting %d "
"endpoint 0x%X is Bulk; changing to Interrupt\n",
cfgno, inum, asnum, d->bEndpointAddress);
@@ -409,7 +406,7 @@
* the USB-2 spec requires such endpoints to have wMaxPacketSize = 0
* (see the end of section 5.6.3), so don't warn about them.
*/
- maxp = usb_endpoint_maxp(&endpoint->desc);
+ maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize);
if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) {
dev_warn(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",
cfgno, inum, asnum, d->bEndpointAddress);
@@ -417,7 +414,7 @@
/* Find the highest legal maxpacket size for this endpoint */
i = 0; /* additional transactions per microframe */
- switch (to_usb_device(ddev)->speed) {
+ switch (udev->speed) {
case USB_SPEED_LOW:
maxpacket_maxes = low_speed_maxpacket_maxes;
break;
@@ -425,12 +422,12 @@
maxpacket_maxes = full_speed_maxpacket_maxes;
break;
case USB_SPEED_HIGH:
- /* Bits 12..11 are allowed only for HS periodic endpoints */
+ /* Multiple-transactions bits are allowed only for HS periodic endpoints */
if (usb_endpoint_xfer_int(d) || usb_endpoint_xfer_isoc(d)) {
- i = maxp & (BIT(12) | BIT(11));
+ i = maxp & USB_EP_MAXP_MULT_MASK;
maxp &= ~i;
}
- /* fallthrough */
+ fallthrough;
default:
maxpacket_maxes = high_speed_maxpacket_maxes;
break;
@@ -453,8 +450,7 @@
* maxpacket sizes other than 512. High speed HCDs may not
* be able to handle that particular bug, so let's warn...
*/
- if (to_usb_device(ddev)->speed == USB_SPEED_HIGH
- && usb_endpoint_xfer_bulk(d)) {
+ if (udev->speed == USB_SPEED_HIGH && usb_endpoint_xfer_bulk(d)) {
if (maxp != 512)
dev_warn(ddev, "config %d interface %d altsetting %d "
"bulk endpoint 0x%X has invalid maxpacket %d\n",
@@ -463,7 +459,7 @@
}
/* Parse a possible SuperSpeed endpoint companion descriptor */
- if (to_usb_device(ddev)->speed >= USB_SPEED_SUPER)
+ if (udev->speed >= USB_SPEED_SUPER)
usb_parse_ss_endpoint_companion(ddev, cfgno,
inum, asnum, endpoint, buffer, size);
@@ -866,10 +862,10 @@
{
struct device *ddev = &dev->dev;
int ncfg = dev->descriptor.bNumConfigurations;
- int result = -ENOMEM;
unsigned int cfgno, length;
unsigned char *bigbuffer;
struct usb_config_descriptor *desc;
+ int result;
if (ncfg > USB_MAXCONFIG) {
dev_warn(ddev, "too many configurations: %d, "
@@ -885,16 +881,16 @@
length = ncfg * sizeof(struct usb_host_config);
dev->config = kzalloc(length, GFP_KERNEL);
if (!dev->config)
- goto err2;
+ return -ENOMEM;
length = ncfg * sizeof(char *);
dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
if (!dev->rawdescriptors)
- goto err2;
+ return -ENOMEM;
desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
if (!desc)
- goto err2;
+ return -ENOMEM;
for (cfgno = 0; cfgno < ncfg; cfgno++) {
/* We grab just the first descriptor so we know how long
@@ -956,9 +952,7 @@
err:
kfree(desc);
dev->descriptor.bNumConfigurations = cfgno;
-err2:
- if (result == -ENOMEM)
- dev_err(ddev, "out of memory\n");
+
return result;
}
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 44f28a1..1ef2de6 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -39,7 +39,6 @@
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/gfp.h>
-#include <linux/poll.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h>
@@ -97,22 +96,6 @@
/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
"E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
-/*
- * Wait for an connect/disconnect event to happen. We initialize
- * the event counter with an odd number, and each event will increment
- * the event counter by two, so it will always _stay_ odd. That means
- * that it will never be zero, so "event 0" will never match a current
- * event, and thus 'poll' will always trigger as readable for the first
- * time it gets called.
- */
-static struct device_connect_event {
- atomic_t count;
- wait_queue_head_t wait;
-} device_event = {
- .count = ATOMIC_INIT(1),
- .wait = __WAIT_QUEUE_HEAD_INITIALIZER(device_event.wait)
-};
-
struct class_info {
int class;
char *class_name;
@@ -133,6 +116,10 @@
{USB_CLASS_CSCID, "scard"},
{USB_CLASS_CONTENT_SEC, "c-sec"},
{USB_CLASS_VIDEO, "video"},
+ {USB_CLASS_PERSONAL_HEALTHCARE, "perhc"},
+ {USB_CLASS_AUDIO_VIDEO, "av"},
+ {USB_CLASS_BILLBOARD, "blbrd"},
+ {USB_CLASS_USB_TYPE_C_BRIDGE, "bridg"},
{USB_CLASS_WIRELESS_CONTROLLER, "wlcon"},
{USB_CLASS_MISC, "misc"},
{USB_CLASS_APP_SPEC, "app."},
@@ -142,12 +129,6 @@
/*****************************************************************/
-void usbfs_conn_disc_event(void)
-{
- atomic_add(2, &device_event.count);
- wake_up(&device_event.wait);
-}
-
static const char *class_decode(const int class)
{
int ix;
@@ -598,8 +579,6 @@
return -EINVAL;
if (nbytes <= 0)
return 0;
- if (!access_ok(buf, nbytes))
- return -EFAULT;
mutex_lock(&usb_bus_idr_lock);
/* print devices for all busses */
@@ -621,25 +600,7 @@
return total_written;
}
-/* Kernel lock for "lastev" protection */
-static __poll_t usb_device_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- unsigned int event_count;
-
- poll_wait(file, &device_event.wait, wait);
-
- event_count = atomic_read(&device_event.count);
- if (file->f_version != event_count) {
- file->f_version = event_count;
- return EPOLLIN | EPOLLRDNORM;
- }
-
- return 0;
-}
-
const struct file_operations usbfs_devices_fops = {
.llseek = no_seek_end_llseek,
.read = usb_device_read,
- .poll = usb_device_poll,
};
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index d037deb..73b60f0 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -583,7 +583,7 @@
/* Now carefully unlink all the marked pending URBs */
rescan:
- list_for_each_entry(as, &ps->async_pending, asynclist) {
+ list_for_each_entry_reverse(as, &ps->async_pending, asynclist) {
if (as->bulk_status == AS_UNLINK) {
as->bulk_status = 0; /* Only once */
urb = as->urb;
@@ -645,7 +645,7 @@
spin_lock_irqsave(&ps->lock, flags);
while (!list_empty(list)) {
- as = list_entry(list->next, struct async, asynclist);
+ as = list_last_entry(list, struct async, asynclist);
list_del_init(&as->asynclist);
urb = as->urb;
usb_get_urb(urb);
@@ -1102,22 +1102,20 @@
return 0;
}
-static int proc_control(struct usb_dev_state *ps, void __user *arg)
+static int do_proc_control(struct usb_dev_state *ps,
+ struct usbdevfs_ctrltransfer *ctrl)
{
struct usb_device *dev = ps->dev;
- struct usbdevfs_ctrltransfer ctrl;
unsigned int tmo;
unsigned char *tbuf;
unsigned wLength;
int i, pipe, ret;
- if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
- return -EFAULT;
- ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest,
- ctrl.wIndex);
+ ret = check_ctrlrecip(ps, ctrl->bRequestType, ctrl->bRequest,
+ ctrl->wIndex);
if (ret)
return ret;
- wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */
+ wLength = ctrl->wLength; /* To suppress 64k PAGE_SIZE warning */
if (wLength > PAGE_SIZE)
return -EINVAL;
ret = usbfs_increase_memory_usage(PAGE_SIZE + sizeof(struct urb) +
@@ -1129,57 +1127,52 @@
ret = -ENOMEM;
goto done;
}
- tmo = ctrl.timeout;
+ tmo = ctrl->timeout;
snoop(&dev->dev, "control urb: bRequestType=%02x "
"bRequest=%02x wValue=%04x "
"wIndex=%04x wLength=%04x\n",
- ctrl.bRequestType, ctrl.bRequest, ctrl.wValue,
- ctrl.wIndex, ctrl.wLength);
- if (ctrl.bRequestType & 0x80) {
- if (ctrl.wLength && !access_ok(ctrl.data,
- ctrl.wLength)) {
- ret = -EINVAL;
- goto done;
- }
+ ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
+ ctrl->wIndex, ctrl->wLength);
+ if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength) {
pipe = usb_rcvctrlpipe(dev, 0);
- snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT, NULL, 0);
+ snoop_urb(dev, NULL, pipe, ctrl->wLength, tmo, SUBMIT, NULL, 0);
usb_unlock_device(dev);
- i = usb_control_msg(dev, pipe, ctrl.bRequest,
- ctrl.bRequestType, ctrl.wValue, ctrl.wIndex,
- tbuf, ctrl.wLength, tmo);
+ i = usb_control_msg(dev, pipe, ctrl->bRequest,
+ ctrl->bRequestType, ctrl->wValue, ctrl->wIndex,
+ tbuf, ctrl->wLength, tmo);
usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE,
tbuf, max(i, 0));
- if ((i > 0) && ctrl.wLength) {
- if (copy_to_user(ctrl.data, tbuf, i)) {
+ if ((i > 0) && ctrl->wLength) {
+ if (copy_to_user(ctrl->data, tbuf, i)) {
ret = -EFAULT;
goto done;
}
}
} else {
- if (ctrl.wLength) {
- if (copy_from_user(tbuf, ctrl.data, ctrl.wLength)) {
+ if (ctrl->wLength) {
+ if (copy_from_user(tbuf, ctrl->data, ctrl->wLength)) {
ret = -EFAULT;
goto done;
}
}
pipe = usb_sndctrlpipe(dev, 0);
- snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT,
- tbuf, ctrl.wLength);
+ snoop_urb(dev, NULL, pipe, ctrl->wLength, tmo, SUBMIT,
+ tbuf, ctrl->wLength);
usb_unlock_device(dev);
- i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest,
- ctrl.bRequestType, ctrl.wValue, ctrl.wIndex,
- tbuf, ctrl.wLength, tmo);
+ i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl->bRequest,
+ ctrl->bRequestType, ctrl->wValue, ctrl->wIndex,
+ tbuf, ctrl->wLength, tmo);
usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, NULL, 0);
}
if (i < 0 && i != -EPIPE) {
dev_printk(KERN_DEBUG, &dev->dev, "usbfs: USBDEVFS_CONTROL "
"failed cmd %s rqt %u rq %u len %u ret %d\n",
- current->comm, ctrl.bRequestType, ctrl.bRequest,
- ctrl.wLength, i);
+ current->comm, ctrl->bRequestType, ctrl->bRequest,
+ ctrl->wLength, i);
}
ret = i;
done:
@@ -1189,30 +1182,37 @@
return ret;
}
-static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
+static int proc_control(struct usb_dev_state *ps, void __user *arg)
+{
+ struct usbdevfs_ctrltransfer ctrl;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+ return do_proc_control(ps, &ctrl);
+}
+
+static int do_proc_bulk(struct usb_dev_state *ps,
+ struct usbdevfs_bulktransfer *bulk)
{
struct usb_device *dev = ps->dev;
- struct usbdevfs_bulktransfer bulk;
unsigned int tmo, len1, pipe;
int len2;
unsigned char *tbuf;
int i, ret;
- if (copy_from_user(&bulk, arg, sizeof(bulk)))
- return -EFAULT;
- ret = findintfep(ps->dev, bulk.ep);
+ ret = findintfep(ps->dev, bulk->ep);
if (ret < 0)
return ret;
ret = checkintf(ps, ret);
if (ret)
return ret;
- if (bulk.ep & USB_DIR_IN)
- pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f);
+ if (bulk->ep & USB_DIR_IN)
+ pipe = usb_rcvbulkpipe(dev, bulk->ep & 0x7f);
else
- pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f);
- if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
+ pipe = usb_sndbulkpipe(dev, bulk->ep & 0x7f);
+ if (!usb_maxpacket(dev, pipe, !(bulk->ep & USB_DIR_IN)))
return -EINVAL;
- len1 = bulk.len;
+ len1 = bulk->len;
if (len1 >= (INT_MAX - sizeof(struct urb)))
return -EINVAL;
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
@@ -1228,12 +1228,8 @@
ret = -ENOMEM;
goto done;
}
- tmo = bulk.timeout;
- if (bulk.ep & 0x80) {
- if (len1 && !access_ok(bulk.data, len1)) {
- ret = -EINVAL;
- goto done;
- }
+ tmo = bulk->timeout;
+ if (bulk->ep & 0x80) {
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0);
usb_unlock_device(dev);
@@ -1242,14 +1238,14 @@
snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, tbuf, len2);
if (!i && len2) {
- if (copy_to_user(bulk.data, tbuf, len2)) {
+ if (copy_to_user(bulk->data, tbuf, len2)) {
ret = -EFAULT;
goto done;
}
}
} else {
if (len1) {
- if (copy_from_user(tbuf, bulk.data, len1)) {
+ if (copy_from_user(tbuf, bulk->data, len1)) {
ret = -EFAULT;
goto done;
}
@@ -1268,6 +1264,15 @@
return ret;
}
+static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
+{
+ struct usbdevfs_bulktransfer bulk;
+
+ if (copy_from_user(&bulk, arg, sizeof(bulk)))
+ return -EFAULT;
+ return do_proc_bulk(ps, &bulk);
+}
+
static void check_reset_of_active_ep(struct usb_device *udev,
unsigned int epnum, char *ioctl_name)
{
@@ -1577,10 +1582,10 @@
uurb->buffer_length = le16_to_cpu(dr->wLength);
uurb->buffer += 8;
if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
- is_in = 1;
+ is_in = true;
uurb->endpoint |= USB_DIR_IN;
} else {
- is_in = 0;
+ is_in = false;
uurb->endpoint &= ~USB_DIR_IN;
}
if (is_in)
@@ -2027,33 +2032,31 @@
static int proc_control_compat(struct usb_dev_state *ps,
struct usbdevfs_ctrltransfer32 __user *p32)
{
- struct usbdevfs_ctrltransfer __user *p;
- __u32 udata;
- p = compat_alloc_user_space(sizeof(*p));
- if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
- get_user(udata, &p32->data) ||
- put_user(compat_ptr(udata), &p->data))
+ struct usbdevfs_ctrltransfer ctrl;
+ u32 udata;
+
+ if (copy_from_user(&ctrl, p32, sizeof(*p32) - sizeof(compat_caddr_t)) ||
+ get_user(udata, &p32->data))
return -EFAULT;
- return proc_control(ps, p);
+ ctrl.data = compat_ptr(udata);
+ return do_proc_control(ps, &ctrl);
}
static int proc_bulk_compat(struct usb_dev_state *ps,
struct usbdevfs_bulktransfer32 __user *p32)
{
- struct usbdevfs_bulktransfer __user *p;
- compat_uint_t n;
+ struct usbdevfs_bulktransfer bulk;
compat_caddr_t addr;
- p = compat_alloc_user_space(sizeof(*p));
-
- if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
- get_user(n, &p32->len) || put_user(n, &p->len) ||
- get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
- get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
+ if (get_user(bulk.ep, &p32->ep) ||
+ get_user(bulk.len, &p32->len) ||
+ get_user(bulk.timeout, &p32->timeout) ||
+ get_user(addr, &p32->data))
return -EFAULT;
-
- return proc_bulk(ps, p);
+ bulk.data = compat_ptr(addr);
+ return do_proc_bulk(ps, &bulk);
}
+
static int proc_disconnectsignal_compat(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_disconnectsignal32 ds;
@@ -2712,18 +2715,6 @@
return ret;
}
-#ifdef CONFIG_COMPAT
-static long usbdev_compat_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret;
-
- ret = usbdev_do_ioctl(file, cmd, compat_ptr(arg));
-
- return ret;
-}
-#endif
-
/* No kernel lock - fine */
static __poll_t usbdev_poll(struct file *file,
struct poll_table_struct *wait)
@@ -2747,9 +2738,7 @@
.read = usbdev_read,
.poll = usbdev_poll,
.unlocked_ioctl = usbdev_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = usbdev_compat_ioctl,
-#endif
+ .compat_ioctl = compat_ptr_ioctl,
.mmap = usbdev_mmap,
.open = usbdev_open,
.release = usbdev_release,
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 2b27d23..4dfa44d 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -261,9 +261,41 @@
*/
if (!udriver->supports_autosuspend)
error = usb_autoresume_device(udev);
+ if (error)
+ return error;
- if (!error)
- error = udriver->probe(udev);
+ if (udriver->generic_subclass)
+ error = usb_generic_driver_probe(udev);
+ if (error)
+ return error;
+
+ /* Probe the USB device with the driver in hand, but only
+ * defer to a generic driver in case the current USB
+ * device driver has an id_table or a match function; i.e.,
+ * when the device driver was explicitly matched against
+ * a device.
+ *
+ * If the device driver does not have either of these,
+ * then we assume that it can bind to any device and is
+ * not truly a more specialized/non-generic driver, so a
+ * return value of -ENODEV should not force the device
+ * to be handled by the generic USB driver, as there
+ * can still be another, more specialized, device driver.
+ *
+ * This accommodates the usbip driver.
+ *
+ * TODO: What if, in the future, there are multiple
+ * specialized USB device drivers for a particular device?
+ * In such cases, there is a need to try all matching
+ * specialised device drivers prior to setting the
+ * use_generic_driver bit.
+ */
+ error = udriver->probe(udev);
+ if (error == -ENODEV && udriver != &usb_generic_driver &&
+ (udriver->id_table || udriver->match)) {
+ udev->use_generic_driver = 1;
+ return -EPROBE_DEFER;
+ }
return error;
}
@@ -273,7 +305,10 @@
struct usb_device *udev = to_usb_device(dev);
struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
- udriver->disconnect(udev);
+ if (udriver->disconnect)
+ udriver->disconnect(udev);
+ if (udriver->generic_subclass)
+ usb_generic_driver_disconnect(udev);
if (!udriver->supports_autosuspend)
usb_autosuspend_device(udev);
return 0;
@@ -790,17 +825,58 @@
}
EXPORT_SYMBOL_GPL(usb_match_id);
+const struct usb_device_id *usb_device_match_id(struct usb_device *udev,
+ const struct usb_device_id *id)
+{
+ if (!id)
+ return NULL;
+
+ for (; id->idVendor || id->idProduct ; id++) {
+ if (usb_match_device(udev, id))
+ return id;
+ }
+
+ return NULL;
+}
+
+bool usb_driver_applicable(struct usb_device *udev,
+ struct usb_device_driver *udrv)
+{
+ if (udrv->id_table && udrv->match)
+ return usb_device_match_id(udev, udrv->id_table) != NULL &&
+ udrv->match(udev);
+
+ if (udrv->id_table)
+ return usb_device_match_id(udev, udrv->id_table) != NULL;
+
+ if (udrv->match)
+ return udrv->match(udev);
+
+ return false;
+}
+
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
+ struct usb_device *udev;
+ struct usb_device_driver *udrv;
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
return 0;
- /* TODO: Add real matching code */
- return 1;
+ udev = to_usb_device(dev);
+ udrv = to_usb_device_driver(drv);
+
+ /* If the device driver under consideration does not have a
+ * id_table or a match function, then let the driver's probe
+ * function decide.
+ */
+ if (!udrv->id_table && !udrv->match)
+ return 1;
+
+ return usb_driver_applicable(udev, udrv);
} else if (is_usb_interface(dev)) {
struct usb_interface *intf;
@@ -867,6 +943,27 @@
return 0;
}
+static int __usb_bus_reprobe_drivers(struct device *dev, void *data)
+{
+ struct usb_device_driver *new_udriver = data;
+ struct usb_device *udev;
+ int ret;
+
+ /* Don't reprobe if current driver isn't usb_generic_driver */
+ if (dev->driver != &usb_generic_driver.drvwrap.driver)
+ return 0;
+
+ udev = to_usb_device(dev);
+ if (!usb_driver_applicable(udev, new_udriver))
+ return 0;
+
+ ret = device_reprobe(dev);
+ if (ret && ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to reprobe device (error %d)\n", ret);
+
+ return 0;
+}
+
/**
* usb_register_device_driver - register a USB device (not interface) driver
* @new_udriver: USB operations for the device driver
@@ -896,13 +993,19 @@
retval = driver_register(&new_udriver->drvwrap.driver);
- if (!retval)
+ if (!retval) {
pr_info("%s: registered new device driver %s\n",
usbcore_name, new_udriver->name);
- else
- printk(KERN_ERR "%s: error %d registering device "
- " driver %s\n",
+ /*
+ * Check whether any device could be better served with
+ * this new driver
+ */
+ bus_for_each_dev(&usb_bus_type, NULL, new_udriver,
+ __usb_bus_reprobe_drivers);
+ } else {
+ pr_err("%s: error %d registering device driver %s\n",
usbcore_name, retval, new_udriver->name);
+ }
return retval;
}
@@ -976,9 +1079,8 @@
out_newid:
driver_unregister(&new_driver->drvwrap.driver);
- printk(KERN_ERR "%s: error %d registering interface "
- " driver %s\n",
- usbcore_name, retval, new_driver->name);
+ pr_err("%s: error %d registering interface driver %s\n",
+ usbcore_name, retval, new_driver->name);
goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
@@ -1149,7 +1251,10 @@
udev->do_remote_wakeup = 0;
udriver = &usb_generic_driver;
}
- status = udriver->suspend(udev, msg);
+ if (udriver->suspend)
+ status = udriver->suspend(udev, msg);
+ if (status == 0 && udriver->generic_subclass)
+ status = usb_generic_driver_suspend(udev, msg);
done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
@@ -1181,7 +1286,10 @@
udev->reset_resume = 1;
udriver = to_usb_device_driver(udev->dev.driver);
- status = udriver->resume(udev, msg);
+ if (udriver->generic_subclass)
+ status = usb_generic_driver_resume(udev, msg);
+ if (status == 0 && udriver->resume)
+ status = udriver->resume(udev, msg);
done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 38f8b3e..26f9fb9 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -195,7 +195,35 @@
}
EXPORT_SYMBOL_GPL(usb_choose_configuration);
-static int generic_probe(struct usb_device *udev)
+static int __check_for_non_generic_match(struct device_driver *drv, void *data)
+{
+ struct usb_device *udev = data;
+ struct usb_device_driver *udrv;
+
+ if (!is_usb_device_driver(drv))
+ return 0;
+ udrv = to_usb_device_driver(drv);
+ if (udrv == &usb_generic_driver)
+ return 0;
+ return usb_driver_applicable(udev, udrv);
+}
+
+static bool usb_generic_driver_match(struct usb_device *udev)
+{
+ if (udev->use_generic_driver)
+ return true;
+
+ /*
+ * If any other driver wants the device, leave the device to this other
+ * driver.
+ */
+ if (bus_for_each_drv(&usb_bus_type, NULL, udev, __check_for_non_generic_match))
+ return false;
+
+ return true;
+}
+
+int usb_generic_driver_probe(struct usb_device *udev)
{
int err, c;
@@ -222,7 +250,7 @@
return 0;
}
-static void generic_disconnect(struct usb_device *udev)
+void usb_generic_driver_disconnect(struct usb_device *udev)
{
usb_notify_remove_device(udev);
@@ -234,7 +262,7 @@
#ifdef CONFIG_PM
-static int generic_suspend(struct usb_device *udev, pm_message_t msg)
+int usb_generic_driver_suspend(struct usb_device *udev, pm_message_t msg)
{
int rc;
@@ -262,7 +290,7 @@
return rc;
}
-static int generic_resume(struct usb_device *udev, pm_message_t msg)
+int usb_generic_driver_resume(struct usb_device *udev, pm_message_t msg)
{
int rc;
@@ -285,11 +313,12 @@
struct usb_device_driver usb_generic_driver = {
.name = "usb",
- .probe = generic_probe,
- .disconnect = generic_disconnect,
+ .match = usb_generic_driver_match,
+ .probe = usb_generic_driver_probe,
+ .disconnect = usb_generic_driver_disconnect,
#ifdef CONFIG_PM
- .suspend = generic_suspend,
- .resume = generic_resume,
+ .suspend = usb_generic_driver_suspend,
+ .resume = usb_generic_driver_resume,
#endif
.supports_autosuspend = 1,
};
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 9e26b01..ec0d6c5 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -159,6 +159,7 @@
* usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed
* @id: pci hotplug id connecting controller to HCD framework
+ * @driver: USB HC driver handle
* Context: !in_interrupt()
*
* Allocates basic PCI resources for this USB host controller, and
@@ -169,9 +170,9 @@
*
* Return: 0 if successful.
*/
-int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
+ const struct hc_driver *driver)
{
- struct hc_driver *driver;
struct usb_hcd *hcd;
int retval;
int hcd_irq = 0;
@@ -181,7 +182,7 @@
if (!id)
return -EINVAL;
- driver = (struct hc_driver *)id->driver_data;
+
if (!driver)
return -EINVAL;
@@ -193,20 +194,21 @@
* make sure irq setup is not touched for xhci in generic hcd code
*/
if ((driver->flags & HCD_MASK) < HCD_USB3) {
- if (!dev->irq) {
+ retval = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY | PCI_IRQ_MSI);
+ if (retval < 0) {
dev_err(&dev->dev,
"Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
pci_name(dev));
retval = -ENODEV;
goto disable_pci;
}
- hcd_irq = dev->irq;
+ hcd_irq = pci_irq_vector(dev, 0);
}
hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
if (!hcd) {
retval = -ENOMEM;
- goto disable_pci;
+ goto free_irq_vectors;
}
hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
@@ -222,7 +224,7 @@
retval = -EBUSY;
goto put_hcd;
}
- hcd->regs = devm_ioremap_nocache(&dev->dev, hcd->rsrc_start,
+ hcd->regs = devm_ioremap(&dev->dev, hcd->rsrc_start,
hcd->rsrc_len);
if (hcd->regs == NULL) {
dev_dbg(&dev->dev, "error mapping memory\n");
@@ -234,7 +236,7 @@
/* UHCI */
int region;
- for (region = 0; region < PCI_ROM_RESOURCE; region++) {
+ for (region = 0; region < PCI_STD_NUM_BARS; region++) {
if (!(pci_resource_flags(dev, region) &
IORESOURCE_IO))
continue;
@@ -285,6 +287,9 @@
put_hcd:
usb_put_hcd(hcd);
+free_irq_vectors:
+ if ((driver->flags & HCD_MASK) < HCD_USB3)
+ pci_free_irq_vectors(dev);
disable_pci:
pci_disable_device(dev);
dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval);
@@ -310,11 +315,14 @@
void usb_hcd_pci_remove(struct pci_dev *dev)
{
struct usb_hcd *hcd;
+ int hcd_driver_flags;
hcd = pci_get_drvdata(dev);
if (!hcd)
return;
+ hcd_driver_flags = hcd->driver->flags;
+
if (pci_dev_run_wake(dev))
pm_runtime_get_noresume(&dev->dev);
@@ -342,6 +350,8 @@
up_read(&companions_rwsem);
}
usb_put_hcd(hcd);
+ if ((hcd_driver_flags & HCD_MASK) < HCD_USB3)
+ pci_free_irq_vectors(dev);
pci_disable_device(dev);
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
@@ -453,7 +463,7 @@
* synchronized here.
*/
if (!hcd->msix_enabled)
- synchronize_irq(pci_dev->irq);
+ synchronize_irq(pci_irq_vector(pci_dev, 0));
/* Downstream ports from this root hub should already be quiesced, so
* there will be no DMA activity. Now we can shut down the upstream
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index d0f4560..ddd1d3e 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/genalloc.h>
#include <linux/io.h>
+#include <linux/kcov.h>
#include <linux/phy/phy.h>
#include <linux/usb.h>
@@ -563,7 +564,7 @@
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
tbuf[0] = 1;
len = 1;
- /* FALLTHROUGH */
+ fallthrough;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
@@ -632,7 +633,7 @@
case DeviceRequest | USB_REQ_GET_INTERFACE:
tbuf[0] = 0;
len = 1;
- /* FALLTHROUGH */
+ fallthrough;
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
@@ -650,7 +651,7 @@
tbuf[0] = 0;
tbuf[1] = 0;
len = 2;
- /* FALLTHROUGH */
+ fallthrough;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
case EndpointOutRequest | USB_REQ_SET_FEATURE:
dev_dbg (hcd->self.controller, "no endpoint features yet\n");
@@ -753,6 +754,7 @@
{
struct urb *urb;
int length;
+ int status;
unsigned long flags;
char buffer[6]; /* Any root hubs with > 31 ports? */
@@ -770,11 +772,17 @@
if (urb) {
clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
hcd->status_urb = NULL;
+ if (urb->transfer_buffer_length >= length) {
+ status = 0;
+ } else {
+ status = -EOVERFLOW;
+ length = urb->transfer_buffer_length;
+ }
urb->actual_length = length;
memcpy(urb->transfer_buffer, buffer, length);
usb_hcd_unlink_urb_from_ep(hcd, urb);
- usb_hcd_giveback_urb(hcd, urb, 0);
+ usb_hcd_giveback_urb(hcd, urb, status);
} else {
length = 0;
set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
@@ -1420,10 +1428,7 @@
return ret;
urb->transfer_flags |= URB_SETUP_MAP_LOCAL;
} else if (hcd_uses_dma(hcd)) {
- if (is_vmalloc_addr(urb->setup_packet)) {
- WARN_ONCE(1, "setup packet is not dma capable\n");
- return -EAGAIN;
- } else if (object_is_on_stack(urb->setup_packet)) {
+ if (object_is_on_stack(urb->setup_packet)) {
WARN_ONCE(1, "setup packet is on stack\n");
return -EAGAIN;
}
@@ -1488,9 +1493,6 @@
ret = -EAGAIN;
else
urb->transfer_flags |= URB_DMA_MAP_PAGE;
- } else if (is_vmalloc_addr(urb->transfer_buffer)) {
- WARN_ONCE(1, "transfer buffer not dma capable\n");
- ret = -EAGAIN;
} else if (object_is_on_stack(urb->transfer_buffer)) {
WARN_ONCE(1, "transfer buffer is on stack\n");
ret = -EAGAIN;
@@ -1560,6 +1562,13 @@
urb->hcpriv = NULL;
INIT_LIST_HEAD(&urb->urb_list);
atomic_dec(&urb->use_count);
+ /*
+ * Order the write of urb->use_count above before the read
+ * of urb->reject below. Pairs with the memory barriers in
+ * usb_kill_urb() and usb_poison_urb().
+ */
+ smp_mb__after_atomic();
+
atomic_dec(&urb->dev->urbnum);
if (atomic_read(&urb->reject))
wake_up(&usb_kill_urb_queue);
@@ -1651,18 +1660,34 @@
/* pass ownership to the completion handler */
urb->status = status;
+ /*
+ * This function can be called in task context inside another remote
+ * coverage collection section, but KCOV doesn't support that kind of
+ * recursion yet. Only collect coverage in softirq context for now.
+ */
+ if (in_serving_softirq())
+ kcov_remote_start_usb((u64)urb->dev->bus->busnum);
urb->complete(urb);
+ if (in_serving_softirq())
+ kcov_remote_stop();
usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);
+ /*
+ * Order the write of urb->use_count above before the read
+ * of urb->reject below. Pairs with the memory barriers in
+ * usb_kill_urb() and usb_poison_urb().
+ */
+ smp_mb__after_atomic();
+
if (unlikely(atomic_read(&urb->reject)))
wake_up(&usb_kill_urb_queue);
usb_put_urb(urb);
}
-static void usb_giveback_urb_bh(unsigned long param)
+static void usb_giveback_urb_bh(struct tasklet_struct *t)
{
- struct giveback_urb_bh *bh = (struct giveback_urb_bh *)param;
+ struct giveback_urb_bh *bh = from_tasklet(bh, t, bh);
struct list_head local_list;
spin_lock_irq(&bh->lock);
@@ -2406,7 +2431,7 @@
spin_lock_init(&bh->lock);
INIT_LIST_HEAD(&bh->head);
- tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh);
+ tasklet_setup(&bh->bh, usb_giveback_urb_bh);
}
struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
@@ -2729,7 +2754,7 @@
case HCD_USB32:
rhdev->rx_lanes = 2;
rhdev->tx_lanes = 2;
- /* fall through */
+ fallthrough;
case HCD_USB31:
rhdev->speed = USB_SPEED_SUPER_PLUS;
break;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 303e8b3..18ee391 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -18,6 +18,7 @@
#include <linux/sched/mm.h>
#include <linux/list.h>
#include <linux/slab.h>
+#include <linux/kcov.h>
#include <linux/ioctl.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
@@ -34,7 +35,7 @@
#include <asm/byteorder.h>
#include "hub.h"
-#include "otg_whitelist.h"
+#include "otg_productlist.h"
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
#define USB_VENDOR_SMSC 0x0424
@@ -95,7 +96,7 @@
MODULE_PARM_DESC(old_scheme_first,
"start with the old device initialization scheme");
-static bool use_both_schemes = 1;
+static bool use_both_schemes = true;
module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(use_both_schemes,
"try the other device initialization scheme if the "
@@ -732,7 +733,7 @@
if ((++hub->nerrors < 10) || hub->error)
goto resubmit;
hub->error = status;
- /* FALL THROUGH */
+ fallthrough;
/* let hub_wq handle things */
case 0: /* we got data: port status changed */
@@ -1108,7 +1109,10 @@
} else {
hub_power_on(hub, true);
}
- }
+ /* Give some time on remote wakeup to let links to transit to U0 */
+ } else if (hub_is_superspeed(hub->hdev))
+ msleep(20);
+
init2:
/*
@@ -1223,7 +1227,7 @@
*/
if (portchange || (hub_is_superspeed(hub->hdev) &&
port_resumed))
- set_bit(port1, hub->change_bits);
+ set_bit(port1, hub->event_bits);
} else if (udev->persist_enabled) {
#ifdef CONFIG_PM
@@ -1839,7 +1843,7 @@
return -E2BIG;
}
-#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
+#ifdef CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB
if (hdev->parent) {
dev_warn(&intf->dev, "ignoring external hub\n");
return -ENODEV;
@@ -2408,7 +2412,7 @@
if (err < 0)
return err;
- if (IS_ENABLED(CONFIG_USB_OTG_WHITELIST) && hcd->tpl_support &&
+ if (IS_ENABLED(CONFIG_USB_OTG_PRODUCTLIST) && hcd->tpl_support &&
!is_targeted(udev)) {
/* Maybe it can talk to us, though we can't talk to it.
* (Includes HNP test device.)
@@ -2710,11 +2714,20 @@
}
+#ifdef CONFIG_USB_FEW_INIT_RETRIES
+#define PORT_RESET_TRIES 2
+#define SET_ADDRESS_TRIES 1
+#define GET_DESCRIPTOR_TRIES 1
+#define GET_MAXPACKET0_TRIES 1
+#define PORT_INIT_TRIES 4
+
+#else
#define PORT_RESET_TRIES 5
#define SET_ADDRESS_TRIES 2
#define GET_DESCRIPTOR_TRIES 2
-#define SET_CONFIG_TRIES (2 * (use_both_schemes + 1))
-#define USE_NEW_SCHEME(i, scheme) ((i) / 2 == (int)(scheme))
+#define GET_MAXPACKET0_TRIES 3
+#define PORT_INIT_TRIES 4
+#endif /* CONFIG_USB_FEW_INIT_RETRIES */
#define HUB_ROOT_RESET_TIME 60 /* times are in msec */
#define HUB_SHORT_RESET_TIME 10
@@ -2722,23 +2735,31 @@
#define HUB_LONG_RESET_TIME 200
#define HUB_RESET_TIMEOUT 800
-/*
- * "New scheme" enumeration causes an extra state transition to be
- * exposed to an xhci host and causes USB3 devices to receive control
- * commands in the default state. This has been seen to cause
- * enumeration failures, so disable this enumeration scheme for USB3
- * devices.
- */
static bool use_new_scheme(struct usb_device *udev, int retry,
struct usb_port *port_dev)
{
int old_scheme_first_port =
- port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME;
+ (port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME) ||
+ old_scheme_first;
+ /*
+ * "New scheme" enumeration causes an extra state transition to be
+ * exposed to an xhci host and causes USB3 devices to receive control
+ * commands in the default state. This has been seen to cause
+ * enumeration failures, so disable this enumeration scheme for USB3
+ * devices.
+ */
if (udev->speed >= USB_SPEED_SUPER)
return false;
- return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first);
+ /*
+ * If use_both_schemes is set, use the first scheme (whichever
+ * it is) for the larger half of the retries, then use the other
+ * scheme. Otherwise, use the first scheme for all the retries.
+ */
+ if (use_both_schemes && retry >= (PORT_INIT_TRIES + 1) / 2)
+ return old_scheme_first_port; /* Second half */
+ return !old_scheme_first_port; /* First half or all */
}
/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
@@ -4594,6 +4615,7 @@
const char *speed;
int devnum = udev->devnum;
const char *driver_name;
+ bool do_new_scheme;
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
@@ -4609,8 +4631,6 @@
if (oldspeed == USB_SPEED_LOW)
delay = HUB_LONG_RESET_TIME;
- mutex_lock(hcd->address0_mutex);
-
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
retval = hub_port_reset(hub, port1, udev, delay, false);
@@ -4706,14 +4726,13 @@
* first 8 bytes of the device descriptor to get the ep0 maxpacket
* value.
*/
- for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
- bool did_new_scheme = false;
+ do_new_scheme = use_new_scheme(udev, retry_counter, port_dev);
- if (use_new_scheme(udev, retry_counter, port_dev)) {
+ for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
+ if (do_new_scheme) {
struct usb_device_descriptor *buf;
int r = 0;
- did_new_scheme = true;
retval = hub_enable_device(udev);
if (retval < 0) {
dev_err(&udev->dev,
@@ -4733,7 +4752,8 @@
* 255 is for WUSB devices, we actually need to use
* 512 (WUSB1.0[4.8.1]).
*/
- for (operations = 0; operations < 3; ++operations) {
+ for (operations = 0; operations < GET_MAXPACKET0_TRIES;
+ ++operations) {
buf->bMaxPacketSize0 = 0;
r = usb_control_msg(udev, usb_rcvaddr0pipe(),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
@@ -4747,7 +4767,7 @@
r = 0;
break;
}
- /* FALL THROUGH */
+ fallthrough;
default:
if (r == 0)
r = -EPROTO;
@@ -4822,11 +4842,7 @@
* - read ep0 maxpacket even for high and low speed,
*/
msleep(10);
- /* use_new_scheme() checks the speed which may have
- * changed since the initial look so we cache the result
- * in did_new_scheme
- */
- if (did_new_scheme)
+ if (do_new_scheme)
break;
}
@@ -4925,7 +4941,6 @@
hub_port_disable(hub, port1, 0);
update_devnum(udev, devnum); /* for disconnect processing */
}
- mutex_unlock(hcd->address0_mutex);
return retval;
}
@@ -5004,6 +5019,91 @@
return remaining;
}
+
+static int descriptors_changed(struct usb_device *udev,
+ struct usb_device_descriptor *old_device_descriptor,
+ struct usb_host_bos *old_bos)
+{
+ int changed = 0;
+ unsigned index;
+ unsigned serial_len = 0;
+ unsigned len;
+ unsigned old_length;
+ int length;
+ char *buf;
+
+ if (memcmp(&udev->descriptor, old_device_descriptor,
+ sizeof(*old_device_descriptor)) != 0)
+ return 1;
+
+ if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
+ return 1;
+ if (udev->bos) {
+ len = le16_to_cpu(udev->bos->desc->wTotalLength);
+ if (len != le16_to_cpu(old_bos->desc->wTotalLength))
+ return 1;
+ if (memcmp(udev->bos->desc, old_bos->desc, len))
+ return 1;
+ }
+
+ /* Since the idVendor, idProduct, and bcdDevice values in the
+ * device descriptor haven't changed, we will assume the
+ * Manufacturer and Product strings haven't changed either.
+ * But the SerialNumber string could be different (e.g., a
+ * different flash card of the same brand).
+ */
+ if (udev->serial)
+ serial_len = strlen(udev->serial) + 1;
+
+ len = serial_len;
+ for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
+ old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
+ len = max(len, old_length);
+ }
+
+ buf = kmalloc(len, GFP_NOIO);
+ if (!buf)
+ /* assume the worst */
+ return 1;
+
+ for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
+ old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
+ length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,
+ old_length);
+ if (length != old_length) {
+ dev_dbg(&udev->dev, "config index %d, error %d\n",
+ index, length);
+ changed = 1;
+ break;
+ }
+ if (memcmp(buf, udev->rawdescriptors[index], old_length)
+ != 0) {
+ dev_dbg(&udev->dev, "config index %d changed (#%d)\n",
+ index,
+ ((struct usb_config_descriptor *) buf)->
+ bConfigurationValue);
+ changed = 1;
+ break;
+ }
+ }
+
+ if (!changed && serial_len) {
+ length = usb_string(udev, udev->descriptor.iSerialNumber,
+ buf, serial_len);
+ if (length + 1 != serial_len) {
+ dev_dbg(&udev->dev, "serial string error %d\n",
+ length);
+ changed = 1;
+ } else if (memcmp(buf, udev->serial, length) != 0) {
+ dev_dbg(&udev->dev, "serial string changed\n");
+ changed = 1;
+ }
+ }
+
+ kfree(buf);
+ return changed;
+}
+
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
u16 portchange)
{
@@ -5015,6 +5115,7 @@
struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *udev = port_dev->child;
static int unreliable_port = -1;
+ bool retry_locked;
/* Disconnect any existing devices under this port */
if (udev) {
@@ -5070,8 +5171,11 @@
unit_load = 100;
status = 0;
- for (i = 0; i < SET_CONFIG_TRIES; i++) {
+ for (i = 0; i < PORT_INIT_TRIES; i++) {
+ usb_lock_port(port_dev);
+ mutex_lock(hcd->address0_mutex);
+ retry_locked = true;
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
*/
@@ -5079,6 +5183,8 @@
if (!udev) {
dev_err(&port_dev->dev,
"couldn't allocate usb_device\n");
+ mutex_unlock(hcd->address0_mutex);
+ usb_unlock_port(port_dev);
goto done;
}
@@ -5100,12 +5206,14 @@
}
/* reset (non-USB 3.0 devices) and get descriptor */
- usb_lock_port(port_dev);
status = hub_port_init(hub, udev, port1, i);
- usb_unlock_port(port_dev);
if (status < 0)
goto loop;
+ mutex_unlock(hcd->address0_mutex);
+ usb_unlock_port(port_dev);
+ retry_locked = false;
+
if (udev->quirks & USB_QUIRK_DELAY_INIT)
msleep(2000);
@@ -5198,12 +5306,16 @@
usb_ep0_reinit(udev);
release_devnum(udev);
hub_free_dev(udev);
+ if (retry_locked) {
+ mutex_unlock(hcd->address0_mutex);
+ usb_unlock_port(port_dev);
+ }
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
/* When halfway through our retry count, power-cycle the port */
- if (i == (SET_CONFIG_TRIES / 2) - 1) {
+ if (i == (PORT_INIT_TRIES - 1) / 2) {
dev_info(&port_dev->dev, "attempt power cycle\n");
usb_hub_set_port_power(hdev, hub, port1, false);
msleep(2 * hub_power_on_good_delay(hub));
@@ -5241,7 +5353,9 @@
{
struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *udev = port_dev->child;
+ struct usb_device_descriptor descriptor;
int status = -ENODEV;
+ int retval;
dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
portchange, portspeed(hub, portstatus));
@@ -5262,7 +5376,30 @@
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
udev->state != USB_STATE_NOTATTACHED) {
if (portstatus & USB_PORT_STAT_ENABLE) {
- status = 0; /* Nothing to do */
+ /*
+ * USB-3 connections are initialized automatically by
+ * the hostcontroller hardware. Therefore check for
+ * changed device descriptors before resuscitating the
+ * device.
+ */
+ descriptor = udev->descriptor;
+ retval = usb_get_device_descriptor(udev,
+ sizeof(udev->descriptor));
+ if (retval < 0) {
+ dev_dbg(&udev->dev,
+ "can't read device descriptor %d\n",
+ retval);
+ } else {
+ if (descriptors_changed(udev, &descriptor,
+ udev->bos)) {
+ dev_dbg(&udev->dev,
+ "device descriptor has changed\n");
+ /* for disconnect() calls */
+ udev->descriptor = descriptor;
+ } else {
+ status = 0; /* Nothing to do */
+ }
+ }
#ifdef CONFIG_PM
} else if (udev->state == USB_STATE_SUSPENDED &&
udev->persist_enabled) {
@@ -5448,6 +5585,8 @@
hub_dev = hub->intfdev;
intf = to_usb_interface(hub_dev);
+ kcov_remote_start_usb((u64)hdev->bus->busnum);
+
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
hdev->state, hdev->maxchild,
/* NOTE: expects max 15 ports... */
@@ -5554,6 +5693,8 @@
/* Balance the stuff in kick_hub_wq() and allow autosuspend */
usb_autopm_put_interface(intf);
kref_put(&hub->kref, hub_release);
+
+ kcov_remote_stop();
}
static const struct usb_device_id hub_id_table[] = {
@@ -5636,90 +5777,6 @@
usb_deregister(&hub_driver);
} /* usb_hub_cleanup() */
-static int descriptors_changed(struct usb_device *udev,
- struct usb_device_descriptor *old_device_descriptor,
- struct usb_host_bos *old_bos)
-{
- int changed = 0;
- unsigned index;
- unsigned serial_len = 0;
- unsigned len;
- unsigned old_length;
- int length;
- char *buf;
-
- if (memcmp(&udev->descriptor, old_device_descriptor,
- sizeof(*old_device_descriptor)) != 0)
- return 1;
-
- if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
- return 1;
- if (udev->bos) {
- len = le16_to_cpu(udev->bos->desc->wTotalLength);
- if (len != le16_to_cpu(old_bos->desc->wTotalLength))
- return 1;
- if (memcmp(udev->bos->desc, old_bos->desc, len))
- return 1;
- }
-
- /* Since the idVendor, idProduct, and bcdDevice values in the
- * device descriptor haven't changed, we will assume the
- * Manufacturer and Product strings haven't changed either.
- * But the SerialNumber string could be different (e.g., a
- * different flash card of the same brand).
- */
- if (udev->serial)
- serial_len = strlen(udev->serial) + 1;
-
- len = serial_len;
- for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
- old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
- len = max(len, old_length);
- }
-
- buf = kmalloc(len, GFP_NOIO);
- if (!buf)
- /* assume the worst */
- return 1;
-
- for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
- old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
- length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,
- old_length);
- if (length != old_length) {
- dev_dbg(&udev->dev, "config index %d, error %d\n",
- index, length);
- changed = 1;
- break;
- }
- if (memcmp(buf, udev->rawdescriptors[index], old_length)
- != 0) {
- dev_dbg(&udev->dev, "config index %d changed (#%d)\n",
- index,
- ((struct usb_config_descriptor *) buf)->
- bConfigurationValue);
- changed = 1;
- break;
- }
- }
-
- if (!changed && serial_len) {
- length = usb_string(udev, udev->descriptor.iSerialNumber,
- buf, serial_len);
- if (length + 1 != serial_len) {
- dev_dbg(&udev->dev, "serial string error %d\n",
- length);
- changed = 1;
- } else if (memcmp(buf, udev->serial, length) != 0) {
- dev_dbg(&udev->dev, "serial string changed\n");
- changed = 1;
- }
- }
-
- kfree(buf);
- return changed;
-}
-
/**
* usb_reset_and_verify_device - perform a USB port reset to reinitialize a device
* @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
@@ -5794,7 +5851,9 @@
bos = udev->bos;
udev->bos = NULL;
- for (i = 0; i < SET_CONFIG_TRIES; ++i) {
+ mutex_lock(hcd->address0_mutex);
+
+ for (i = 0; i < PORT_INIT_TRIES; ++i) {
/* ep0 maxpacket size may change; let the HCD know about it.
* Other endpoints will be handled by re-enumeration. */
@@ -5803,6 +5862,7 @@
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
break;
}
+ mutex_unlock(hcd->address0_mutex);
if (ret < 0)
goto re_enumerate;
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index a8f23f8..22ea1f4 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* usb hub driver head file
*
diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c
index c12ac56..ba371a2 100644
--- a/drivers/usb/core/ledtrig-usbport.c
+++ b/drivers/usb/core/ledtrig-usbport.c
@@ -34,7 +34,7 @@
* Helpers
***************************************/
-/**
+/*
* usbport_trig_usb_dev_observed - Check if dev is connected to observed port
*/
static bool usbport_trig_usb_dev_observed(struct usbport_trig_data *usbport_data,
@@ -64,7 +64,7 @@
return 0;
}
-/**
+/*
* usbport_trig_update_count - Recalculate amount of connected matching devices
*/
static void usbport_trig_update_count(struct usbport_trig_data *usbport_data)
@@ -123,7 +123,7 @@
* Adding & removing ports
***************************************/
-/**
+/*
* usbport_trig_port_observed - Check if port should be observed
*/
static bool usbport_trig_port_observed(struct usbport_trig_data *usbport_data,
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 7ca9087..dba2bac 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -5,6 +5,7 @@
* Released under the GPLv2 only.
*/
+#include <linux/acpi.h>
#include <linux/pci.h> /* for scatterlist macros */
#include <linux/usb.h>
#include <linux/module.h>
@@ -162,6 +163,143 @@
EXPORT_SYMBOL_GPL(usb_control_msg);
/**
+ * usb_control_msg_send - Builds a control "send" message, sends it off and waits for completion
+ * @dev: pointer to the usb device to send the message to
+ * @endpoint: endpoint to send the message to
+ * @request: USB message request value
+ * @requesttype: USB message request type value
+ * @value: USB message value
+ * @index: USB message index value
+ * @driver_data: pointer to the data to send
+ * @size: length in bytes of the data to send
+ * @timeout: time in msecs to wait for the message to complete before timing
+ * out (if 0 the wait is forever)
+ * @memflags: the flags for memory allocation for buffers
+ *
+ * Context: !in_interrupt ()
+ *
+ * This function sends a control message to a specified endpoint that is not
+ * expected to fill in a response (i.e. a "send message") and waits for the
+ * message to complete, or timeout.
+ *
+ * Do not use this function from within an interrupt context. If you need
+ * an asynchronous message, or need to send a message from within interrupt
+ * context, use usb_submit_urb(). If a thread in your driver uses this call,
+ * make sure your disconnect() method can wait for it to complete. Since you
+ * don't have a handle on the URB used, you can't cancel the request.
+ *
+ * The data pointer can be made to a reference on the stack, or anywhere else,
+ * as it will not be modified at all. This does not have the restriction that
+ * usb_control_msg() has where the data pointer must be to dynamically allocated
+ * memory (i.e. memory that can be successfully DMAed to a device).
+ *
+ * Return: If successful, 0 is returned, Otherwise, a negative error number.
+ */
+int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request,
+ __u8 requesttype, __u16 value, __u16 index,
+ const void *driver_data, __u16 size, int timeout,
+ gfp_t memflags)
+{
+ unsigned int pipe = usb_sndctrlpipe(dev, endpoint);
+ int ret;
+ u8 *data = NULL;
+
+ if (usb_pipe_type_check(dev, pipe))
+ return -EINVAL;
+
+ if (size) {
+ data = kmemdup(driver_data, size, memflags);
+ if (!data)
+ return -ENOMEM;
+ }
+
+ ret = usb_control_msg(dev, pipe, request, requesttype, value, index,
+ data, size, timeout);
+ kfree(data);
+
+ if (ret < 0)
+ return ret;
+ if (ret == size)
+ return 0;
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(usb_control_msg_send);
+
+/**
+ * usb_control_msg_recv - Builds a control "receive" message, sends it off and waits for completion
+ * @dev: pointer to the usb device to send the message to
+ * @endpoint: endpoint to send the message to
+ * @request: USB message request value
+ * @requesttype: USB message request type value
+ * @value: USB message value
+ * @index: USB message index value
+ * @driver_data: pointer to the data to be filled in by the message
+ * @size: length in bytes of the data to be received
+ * @timeout: time in msecs to wait for the message to complete before timing
+ * out (if 0 the wait is forever)
+ * @memflags: the flags for memory allocation for buffers
+ *
+ * Context: !in_interrupt ()
+ *
+ * This function sends a control message to a specified endpoint that is
+ * expected to fill in a response (i.e. a "receive message") and waits for the
+ * message to complete, or timeout.
+ *
+ * Do not use this function from within an interrupt context. If you need
+ * an asynchronous message, or need to send a message from within interrupt
+ * context, use usb_submit_urb(). If a thread in your driver uses this call,
+ * make sure your disconnect() method can wait for it to complete. Since you
+ * don't have a handle on the URB used, you can't cancel the request.
+ *
+ * The data pointer can be made to a reference on the stack, or anywhere else
+ * that can be successfully written to. This function does not have the
+ * restriction that usb_control_msg() has where the data pointer must be to
+ * dynamically allocated memory (i.e. memory that can be successfully DMAed to a
+ * device).
+ *
+ * The "whole" message must be properly received from the device in order for
+ * this function to be successful. If a device returns less than the expected
+ * amount of data, then the function will fail. Do not use this for messages
+ * where a variable amount of data might be returned.
+ *
+ * Return: If successful, 0 is returned, Otherwise, a negative error number.
+ */
+int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request,
+ __u8 requesttype, __u16 value, __u16 index,
+ void *driver_data, __u16 size, int timeout,
+ gfp_t memflags)
+{
+ unsigned int pipe = usb_rcvctrlpipe(dev, endpoint);
+ int ret;
+ u8 *data;
+
+ if (!size || !driver_data || usb_pipe_type_check(dev, pipe))
+ return -EINVAL;
+
+ data = kmalloc(size, memflags);
+ if (!data)
+ return -ENOMEM;
+
+ ret = usb_control_msg(dev, pipe, request, requesttype, value, index,
+ data, size, timeout);
+
+ if (ret < 0)
+ goto exit;
+
+ if (ret == size) {
+ memcpy(driver_data, data, size);
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+exit:
+ kfree(data);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_control_msg_recv);
+
+/**
* usb_interrupt_msg - Builds an interrupt urb, sends it off and waits for completion
* @usb_dev: pointer to the usb device to send the message to
* @pipe: endpoint "pipe" to send the message to
@@ -953,11 +1091,12 @@
if (dev->speed < USB_SPEED_SUPER)
return 0;
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ return usb_control_msg_send(dev, 0,
USB_REQ_SET_ISOCH_DELAY,
USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
dev->hub_delay, 0, NULL, 0,
- USB_CTRL_SET_TIMEOUT);
+ USB_CTRL_SET_TIMEOUT,
+ GFP_NOIO);
}
/**
@@ -1075,13 +1214,13 @@
* (like some ibmcam model 1 units) seem to expect hosts to make
* this request for iso endpoints, which can't halt!
*/
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
- USB_ENDPOINT_HALT, endp, NULL, 0,
- USB_CTRL_SET_TIMEOUT);
+ result = usb_control_msg_send(dev, 0,
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
+ USB_ENDPOINT_HALT, endp, NULL, 0,
+ USB_CTRL_SET_TIMEOUT, GFP_NOIO);
/* don't un-halt or force to DATA0 except on success */
- if (result < 0)
+ if (result)
return result;
/* NOTE: seems like Microsoft and Apple don't bother verifying
@@ -1443,9 +1582,11 @@
if (dev->quirks & USB_QUIRK_NO_SET_INTF)
ret = -EPIPE;
else
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
- alternate, interface, NULL, 0, 5000);
+ ret = usb_control_msg_send(dev, 0,
+ USB_REQ_SET_INTERFACE,
+ USB_RECIP_INTERFACE, alternate,
+ interface, NULL, 0, 5000,
+ GFP_NOIO);
/* 9.4.10 says devices don't need this and are free to STALL the
* request if the interface only has one alternate setting.
@@ -1455,7 +1596,7 @@
"manual set_interface for iface %d, alt %d\n",
interface, alternate);
manual = 1;
- } else if (ret < 0) {
+ } else if (ret) {
/* Re-instate the old alt setting */
usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
usb_enable_lpm(dev);
@@ -1579,11 +1720,11 @@
mutex_unlock(hcd->bandwidth_mutex);
return retval;
}
- retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_SET_CONFIGURATION, 0,
- config->desc.bConfigurationValue, 0,
- NULL, 0, USB_CTRL_SET_TIMEOUT);
- if (retval < 0) {
+ retval = usb_control_msg_send(dev, 0, USB_REQ_SET_CONFIGURATION, 0,
+ config->desc.bConfigurationValue, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT,
+ GFP_NOIO);
+ if (retval) {
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
@@ -1947,16 +2088,11 @@
intf->dev.of_node = usb_of_get_interface_node(dev,
configuration, ifnum);
}
+ ACPI_COMPANION_SET(&intf->dev, ACPI_COMPANION(&dev->dev));
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
intf->dev.groups = usb_interface_groups;
- /*
- * Please refer to usb_alloc_dev() to see why we set
- * dma_mask and dma_pfn_offset.
- */
- intf->dev.dma_mask = dev->dev.dma_mask;
- intf->dev.dma_pfn_offset = dev->dev.dma_pfn_offset;
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
intf->minor = -1;
device_initialize(&intf->dev);
@@ -1967,10 +2103,10 @@
}
kfree(new_interfaces);
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
- NULL, 0, USB_CTRL_SET_TIMEOUT);
- if (ret < 0 && cp) {
+ ret = usb_control_msg_send(dev, 0, USB_REQ_SET_CONFIGURATION, 0,
+ configuration, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT, GFP_NOIO);
+ if (ret && cp) {
/*
* All the old state is gone, so what else can we do?
* The device is probably useless now anyway.
diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c
index 651708d..617e925 100644
--- a/drivers/usb/core/of.c
+++ b/drivers/usb/core/of.c
@@ -45,7 +45,7 @@
*
* Determine whether a USB device has a so called combined node which is
* shared with its sole interface. This is the case if and only if the device
- * has a node and its decriptors report the following:
+ * has a node and its descriptors report the following:
*
* 1) bDeviceClass is 0 or 9, and
* 2) bNumConfigurations is 1, and
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_productlist.h
similarity index 88%
rename from drivers/usb/core/otg_whitelist.h
rename to drivers/usb/core/otg_productlist.h
index 2ae9015..db67df2 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_productlist.h
@@ -1,18 +1,14 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * drivers/usb/core/otg_whitelist.h
- *
- * Copyright (C) 2004 Texas Instruments
- */
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (C) 2004 Texas Instruments */
/*
- * This OTG and Embedded Host Whitelist is "Targeted Peripheral List".
+ * This OTG and Embedded Host list is "Targeted Peripheral List".
* It should mostly use of USB_DEVICE() or USB_DEVICE_VER() entries..
*
* YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING!
*/
-static struct usb_device_id whitelist_table[] = {
+static struct usb_device_id productlist_table[] = {
/* hubs are optional in OTG, but very handy ... */
{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
@@ -44,7 +40,7 @@
static int is_targeted(struct usb_device *dev)
{
- struct usb_device_id *id = whitelist_table;
+ struct usb_device_id *id = productlist_table;
/* HNP test device is _never_ targeted (see OTG spec 6.6.6) */
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a &&
@@ -59,7 +55,7 @@
/* NOTE: can't use usb_match_id() since interface caches
* aren't set up yet. this is cut/paste from that code.
*/
- for (id = whitelist_table; id->match_flags; id++) {
+ for (id = productlist_table; id->match_flags; id++) {
if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
continue;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index d97544f..baf80e2 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -353,7 +353,7 @@
/* Guillemot Hercules DJ Console audio card (BZ 208357) */
{ USB_DEVICE(0x06f8, 0xb000), .driver_info =
- USB_QUIRK_ENDPOINT_BLACKLIST },
+ USB_QUIRK_ENDPOINT_IGNORE },
/* Midiman M-Audio Keystation 88es */
{ USB_DEVICE(0x0763, 0x0192), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -375,11 +375,11 @@
/* Sound Devices USBPre2 */
{ USB_DEVICE(0x0926, 0x0202), .driver_info =
- USB_QUIRK_ENDPOINT_BLACKLIST },
+ USB_QUIRK_ENDPOINT_IGNORE },
/* Sound Devices MixPre-D */
{ USB_DEVICE(0x0926, 0x0208), .driver_info =
- USB_QUIRK_ENDPOINT_BLACKLIST },
+ USB_QUIRK_ENDPOINT_IGNORE },
/* Keytouch QWERTY Panel keyboard */
{ USB_DEVICE(0x0926, 0x3333), .driver_info =
@@ -435,6 +435,12 @@
{ USB_DEVICE(0x1532, 0x0116), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Lenovo USB-C to Ethernet Adapter RTL8153-04 */
+ { USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* Lenovo Powered USB-C Travel Hub (4X90S92381, RTL8153 GigE) */
+ { USB_DEVICE(0x17ef, 0x721e), .driver_info = USB_QUIRK_NO_LPM },
+
/* Lenovo ThinkCenter A630Z TI024Gen3 usb-audio */
{ USB_DEVICE(0x17ef, 0xa012), .driver_info =
USB_QUIRK_DISCONNECT_SUSPEND },
@@ -533,12 +539,12 @@
};
/*
- * Entries for blacklisted endpoints that should be ignored when parsing
- * configuration descriptors.
+ * Entries for endpoints that should be ignored when parsing configuration
+ * descriptors.
*
- * Matched for devices with USB_QUIRK_ENDPOINT_BLACKLIST.
+ * Matched for devices with USB_QUIRK_ENDPOINT_IGNORE.
*/
-static const struct usb_device_id usb_endpoint_blacklist[] = {
+static const struct usb_device_id usb_endpoint_ignore[] = {
{ USB_DEVICE_INTERFACE_NUMBER(0x06f8, 0xb000, 5), .driver_info = 0x01 },
{ USB_DEVICE_INTERFACE_NUMBER(0x06f8, 0xb000, 5), .driver_info = 0x81 },
{ USB_DEVICE_INTERFACE_NUMBER(0x0926, 0x0202, 1), .driver_info = 0x85 },
@@ -546,14 +552,14 @@
{ }
};
-bool usb_endpoint_is_blacklisted(struct usb_device *udev,
- struct usb_host_interface *intf,
- struct usb_endpoint_descriptor *epd)
+bool usb_endpoint_is_ignored(struct usb_device *udev,
+ struct usb_host_interface *intf,
+ struct usb_endpoint_descriptor *epd)
{
const struct usb_device_id *id;
unsigned int address;
- for (id = usb_endpoint_blacklist; id->match_flags; ++id) {
+ for (id = usb_endpoint_ignore; id->match_flags; ++id) {
if (!usb_match_device(udev, id))
continue;
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 2f594c8..8d13419 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -849,7 +849,7 @@
static umode_t dev_string_attrs_are_visible(struct kobject *kobj,
struct attribute *a, int n)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct usb_device *udev = to_usb_device(dev);
if (a == &dev_attr_manufacturer.attr) {
@@ -883,7 +883,7 @@
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct usb_device *udev = to_usb_device(dev);
size_t nleft = count;
size_t srclen, n;
@@ -1238,7 +1238,7 @@
static umode_t intf_assoc_attrs_are_visible(struct kobject *kobj,
struct attribute *a, int n)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct usb_interface *intf = to_usb_interface(dev);
if (intf->intf_assoc == NULL)
@@ -1267,8 +1267,10 @@
if (!alt->string && !(udev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
alt->string = usb_cache_string(udev, alt->desc.iInterface);
- if (alt->string && device_create_file(&intf->dev, &dev_attr_interface))
- ; /* We don't actually care if the function fails. */
+ if (alt->string && device_create_file(&intf->dev, &dev_attr_interface)) {
+ /* This is not a serious error */
+ dev_dbg(&intf->dev, "interface string descriptor file not created\n");
+ }
intf->sysfs_files_created = 1;
}
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 31ca5ab..9c28502 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -192,6 +192,28 @@
};
/**
+ * usb_pipe_type_check - sanity check of a specific pipe for a usb device
+ * @dev: struct usb_device to be checked
+ * @pipe: pipe to check
+ *
+ * This performs a light-weight sanity check for the endpoint in the
+ * given usb device. It returns 0 if the pipe is valid for the specific usb
+ * device, otherwise a negative error code.
+ */
+int usb_pipe_type_check(struct usb_device *dev, unsigned int pipe)
+{
+ const struct usb_host_endpoint *ep;
+
+ ep = usb_pipe_endpoint(dev, pipe);
+ if (!ep)
+ return -EINVAL;
+ if (usb_pipetype(pipe) != pipetypes[usb_endpoint_type(&ep->desc)])
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_pipe_type_check);
+
+/**
* usb_urb_ep_type_check - sanity check of endpoint in the given urb
* @urb: urb to be checked
*
@@ -201,14 +223,7 @@
*/
int usb_urb_ep_type_check(const struct urb *urb)
{
- const struct usb_host_endpoint *ep;
-
- ep = usb_pipe_endpoint(urb->dev, urb->pipe);
- if (!ep)
- return -EINVAL;
- if (usb_pipetype(urb->pipe) != pipetypes[usb_endpoint_type(&ep->desc)])
- return -EINVAL;
- return 0;
+ return usb_pipe_type_check(urb->dev, urb->pipe);
}
EXPORT_SYMBOL_GPL(usb_urb_ep_type_check);
@@ -474,7 +489,7 @@
*/
/* Check that the pipe's type matches the endpoint's type */
- if (usb_urb_ep_type_check(urb))
+ if (usb_pipe_type_check(urb->dev, urb->pipe))
dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
usb_pipetype(urb->pipe), pipetypes[xfertype]);
@@ -486,7 +501,7 @@
case USB_ENDPOINT_XFER_INT:
if (is_out)
allowed |= URB_ZERO_PACKET;
- /* FALLTHROUGH */
+ fallthrough;
default: /* all non-iso endpoints */
if (!is_out)
allowed |= URB_SHORT_NOT_OK;
@@ -519,7 +534,7 @@
if ((urb->interval < 6)
&& (xfertype == USB_ENDPOINT_XFER_INT))
return -EINVAL;
- /* fall through */
+ fallthrough;
default:
if (urb->interval <= 0)
return -EINVAL;
@@ -691,6 +706,12 @@
if (!(urb && urb->dev && urb->ep))
return;
atomic_inc(&urb->reject);
+ /*
+ * Order the write of urb->reject above before the read
+ * of urb->use_count below. Pairs with the barriers in
+ * __usb_hcd_giveback_urb() and usb_hcd_submit_urb().
+ */
+ smp_mb__after_atomic();
usb_hcd_unlink_urb(urb, -ENOENT);
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
@@ -732,6 +753,12 @@
if (!urb)
return;
atomic_inc(&urb->reject);
+ /*
+ * Order the write of urb->reject above before the read
+ * of urb->use_count below. Pairs with the barriers in
+ * __usb_hcd_giveback_urb() and usb_hcd_submit_urb().
+ */
+ smp_mb__after_atomic();
if (!urb->dev || !urb->ep)
return;
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 9043d72..50b2fc7 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -86,7 +86,7 @@
{
enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *upc;
+ union acpi_object *upc = NULL;
acpi_status status;
/*
@@ -98,11 +98,12 @@
* no connectable, the port would be not used.
*/
status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
- upc = buffer.pointer;
- if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
- || upc->package.count != 4) {
+ if (ACPI_FAILURE(status))
goto out;
- }
+
+ upc = buffer.pointer;
+ if (!upc || (upc->type != ACPI_TYPE_PACKAGE) || upc->package.count != 4)
+ goto out;
if (upc->package.elements[0].integer.value)
if (pld->user_visible)
@@ -186,7 +187,7 @@
handle = adev->handle;
status = acpi_get_physical_device_location(handle, &pld);
- if (!ACPI_FAILURE(status) && pld) {
+ if (ACPI_SUCCESS(status) && pld) {
port_dev->location = USB_ACPI_LOCATION_VALID
| pld->group_token << 8 | pld->group_position;
port_dev->connect_type = usb_acpi_get_connect_type(handle, pld);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index f16c26d..db4de53 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -19,9 +19,8 @@
* just a collection of helper routines that implement the
* generic USB things that the real drivers can use..
*
- * Think of this as a "USB library" rather than anything else.
- * It should be considered a slave, with no callbacks. Callbacks
- * are evil.
+ * Think of this as a "USB library" rather than anything else,
+ * with no callbacks. Callbacks are evil.
*/
#include <linux/module.h>
@@ -600,18 +599,6 @@
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.groups = usb_device_groups;
- /*
- * Fake a dma_mask/offset for the USB device:
- * We cannot really use the dma-mapping API (dma_alloc_* and
- * dma_map_*) for USB devices but instead need to use
- * usb_alloc_coherent and pass data in 'urb's, but some subsystems
- * manually look into the mask/offset pair to determine whether
- * they need bounce buffers.
- * Note: calling dma_set_mask() on a USB device would set the
- * mask for the entire HCD, so don't do that.
- */
- dev->dev.dma_mask = bus->sysdev->dma_mask;
- dev->dev.dma_pfn_offset = bus->sysdev->dma_pfn_offset;
set_dev_node(&dev->dev, dev_to_node(bus->sysdev));
dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
@@ -761,6 +748,38 @@
}
EXPORT_SYMBOL_GPL(usb_put_intf);
+/**
+ * usb_intf_get_dma_device - acquire a reference on the usb interface's DMA endpoint
+ * @intf: the usb interface
+ *
+ * While a USB device cannot perform DMA operations by itself, many USB
+ * controllers can. A call to usb_intf_get_dma_device() returns the DMA endpoint
+ * for the given USB interface, if any. The returned device structure must be
+ * released with put_device().
+ *
+ * See also usb_get_dma_device().
+ *
+ * Returns: A reference to the usb interface's DMA endpoint; or NULL if none
+ * exists.
+ */
+struct device *usb_intf_get_dma_device(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct device *dmadev;
+
+ if (!udev->bus)
+ return NULL;
+
+ dmadev = get_device(udev->bus->sysdev);
+ if (!dmadev || !dmadev->dma_mask) {
+ put_device(dmadev);
+ return NULL;
+ }
+
+ return dmadev;
+}
+EXPORT_SYMBOL_GPL(usb_intf_get_dma_device);
+
/* USB device locking
*
* USB devices and interfaces are locked using the semaphore in their
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 3ad0ee5..82538da 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Released under the GPLv2 only.
*/
@@ -37,7 +37,7 @@
extern void usb_detect_quirks(struct usb_device *udev);
extern void usb_detect_interface_quirks(struct usb_device *udev);
extern void usb_release_quirk_list(void);
-extern bool usb_endpoint_is_blacklisted(struct usb_device *udev,
+extern bool usb_endpoint_is_ignored(struct usb_device *udev,
struct usb_host_interface *intf,
struct usb_endpoint_descriptor *epd);
extern int usb_remove_device(struct usb_device *udev);
@@ -50,6 +50,12 @@
extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev);
+extern int usb_generic_driver_probe(struct usb_device *udev);
+extern void usb_generic_driver_disconnect(struct usb_device *udev);
+extern int usb_generic_driver_suspend(struct usb_device *udev,
+ pm_message_t msg);
+extern int usb_generic_driver_resume(struct usb_device *udev,
+ pm_message_t msg);
static inline unsigned usb_get_max_power(struct usb_device *udev,
struct usb_host_config *c)
@@ -66,6 +72,10 @@
const struct usb_device_id *id);
extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id);
+extern const struct usb_device_id *usb_device_match_id(struct usb_device *udev,
+ const struct usb_device_id *id);
+extern bool usb_driver_applicable(struct usb_device *udev,
+ struct usb_device_driver *udrv);
extern void usb_forced_unbind_intf(struct usb_interface *intf);
extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
@@ -183,7 +193,6 @@
extern struct usb_driver usbfs_driver;
extern const struct file_operations usbfs_devices_fops;
extern const struct file_operations usbdev_file_operations;
-extern void usbfs_conn_disc_event(void);
extern int usb_devio_init(void);
extern void usb_devio_cleanup(void);
diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig
index 16e1aa3..c131719 100644
--- a/drivers/usb/dwc2/Kconfig
+++ b/drivers/usb/dwc2/Kconfig
@@ -5,6 +5,7 @@
depends on HAS_DMA
depends on USB || USB_GADGET
depends on HAS_IOMEM
+ select USB_ROLE_SWITCH
help
Say Y here if your system has a Dual Role Hi-Speed USB
controller based on the DesignWare HSOTG IP Core.
diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile
index 440320c..2bcd694 100644
--- a/drivers/usb/dwc2/Makefile
+++ b/drivers/usb/dwc2/Makefile
@@ -3,7 +3,7 @@
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC2) += dwc2.o
-dwc2-y := core.o core_intr.o platform.o
+dwc2-y := core.o core_intr.o platform.o drd.o
dwc2-y += params.o
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 0939804..15911ac 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -524,10 +524,25 @@
greset |= GRSTCTL_CSFTRST;
dwc2_writel(hsotg, greset, GRSTCTL);
- if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 10000)) {
- dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
- __func__);
- return -EBUSY;
+ if ((hsotg->hw_params.snpsid & DWC2_CORE_REV_MASK) <
+ (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
+ if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL,
+ GRSTCTL_CSFTRST, 10000)) {
+ dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST\n",
+ __func__);
+ return -EBUSY;
+ }
+ } else {
+ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL,
+ GRSTCTL_CSFTRST_DONE, 10000)) {
+ dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST_DONE\n",
+ __func__);
+ return -EBUSY;
+ }
+ greset = dwc2_readl(hsotg, GRSTCTL);
+ greset &= ~GRSTCTL_CSFTRST;
+ greset |= GRSTCTL_CSFTRST_DONE;
+ dwc2_writel(hsotg, greset, GRSTCTL);
}
/* Wait for AHB master IDLE state */
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 9bb2efc..03d16a0 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* core.h - DesignWare HS OTG Controller common declarations
*
@@ -135,7 +135,7 @@
* @target_frame: Targeted frame num to setup next ISOC transfer
* @frame_overrun: Indicates SOF number overrun in DSTS
*
- * This is the driver's state for each registered enpoint, allowing it
+ * This is the driver's state for each registered endpoint, allowing it
* to keep track of transactions that need doing. Each endpoint has a
* lock to protect the state, to try and avoid using an overall lock
* for the host controller as much as possible.
@@ -413,6 +413,10 @@
* register.
* 0 - Deactivate the transceiver (default)
* 1 - Activate the transceiver
+ * @activate_stm_id_vb_detection: Activate external ID pin and Vbus level
+ * detection using GGPIO register.
+ * 0 - Deactivate the external level detection (default)
+ * 1 - Activate the external level detection
* @g_dma: Enables gadget dma usage (default: autodetect).
* @g_dma_desc: Enables gadget descriptor DMA (default: autodetect).
* @g_rx_fifo_size: The periodic rx fifo size for the device, in
@@ -483,6 +487,7 @@
bool service_interval;
u8 hird_threshold;
bool activate_stm_fs_transceiver;
+ bool activate_stm_id_vb_detection;
bool ipg_isoc_en;
u16 max_packet_count;
u32 max_transfer_size;
@@ -857,6 +862,7 @@
* - USB_DR_MODE_PERIPHERAL
* - USB_DR_MODE_HOST
* - USB_DR_MODE_OTG
+ * @role_sw: usb_role_switch handle
* @hcd_enabled: Host mode sub-driver initialization indicator.
* @gadget_enabled: Peripheral mode sub-driver initialization indicator.
* @ll_hw_enabled: Status of low-level hardware resources.
@@ -876,6 +882,8 @@
* removed once all SoCs support usb transceiver.
* @supplies: Definition of USB power supplies
* @vbus_supply: Regulator supplying vbus.
+ * @usb33d: Optional 3.3v regulator used on some stm32 devices to
+ * supply ID and VBUS detection hardware.
* @lock: Spinlock that protects all the driver data structures
* @priv: Stores a pointer to the struct usb_hcd
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
@@ -1031,7 +1039,7 @@
* @fifo_mem: Total internal RAM for FIFOs (bytes)
* @fifo_map: Each bit intend for concrete fifo. If that bit is set,
* then that fifo is used
- * @gadget: Represents a usb slave device
+ * @gadget: Represents a usb gadget device
* @connected: Used in slave mode. True if device connected with host
* @eps_in: The IN endpoints being supplied to the gadget framework
* @eps_out: The OUT endpoints being supplied to the gadget framework
@@ -1049,6 +1057,7 @@
struct dwc2_core_params params;
enum usb_otg_state op_state;
enum usb_dr_mode dr_mode;
+ struct usb_role_switch *role_sw;
unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1;
unsigned int ll_hw_enabled:1;
@@ -1063,6 +1072,7 @@
struct dwc2_hsotg_plat *plat;
struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
struct regulator *vbus_supply;
+ struct regulator *usb33d;
spinlock_t lock;
void *priv;
@@ -1097,8 +1107,10 @@
#define DWC2_CORE_REV_3_00a 0x4f54300a
#define DWC2_CORE_REV_3_10a 0x4f54310a
#define DWC2_CORE_REV_4_00a 0x4f54400a
+#define DWC2_CORE_REV_4_20a 0x4f54420a
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
+#define DWC2_CORE_REV_MASK 0x0000ffff
/* DWC OTG HW Core ID */
#define DWC2_OTG_ID 0x4f540000
@@ -1303,6 +1315,8 @@
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
+int dwc2_check_core_version(struct dwc2_hsotg *hsotg);
+
/*
* Common core Functions.
* The following functions support managing the DWC_otg controller in either
@@ -1366,6 +1380,11 @@
return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
}
+int dwc2_drd_init(struct dwc2_hsotg *hsotg);
+void dwc2_drd_suspend(struct dwc2_hsotg *hsotg);
+void dwc2_drd_resume(struct dwc2_hsotg *hsotg);
+void dwc2_drd_exit(struct dwc2_hsotg *hsotg);
+
/*
* Dump core registers and SPRAM
*/
@@ -1382,10 +1401,12 @@
int dwc2_gadget_init(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset);
+void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
+#define dwc2_is_device_enabled(hsotg) (hsotg->enabled)
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
@@ -1407,12 +1428,14 @@
{ return 0; }
static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset) {}
+static inline void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
int testmode)
{ return 0; }
#define dwc2_is_device_connected(hsotg) (0)
+#define dwc2_is_device_enabled(hsotg) (0)
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 44d7458..e3f429f 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -288,14 +288,9 @@
/*
* Need to schedule a work, as there are possible DELAY function calls.
- * Release lock before scheduling workq as it holds spinlock during
- * scheduling.
*/
- if (hsotg->wq_otg) {
- spin_unlock(&hsotg->lock);
+ if (hsotg->wq_otg)
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
- spin_lock(&hsotg->lock);
- }
}
/**
diff --git a/drivers/usb/dwc2/debug.h b/drivers/usb/dwc2/debug.h
index a8c565b..47252c5 100644
--- a/drivers/usb/dwc2/debug.h
+++ b/drivers/usb/dwc2/debug.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* debug.h - Designware USB2 DRD controller debug header
*
diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c
index 7f62f4c..aaafd46 100644
--- a/drivers/usb/dwc2/debugfs.c
+++ b/drivers/usb/dwc2/debugfs.c
@@ -37,15 +37,15 @@
return -EFAULT;
if (!strncmp(buf, "test_j", 6))
- testmode = TEST_J;
+ testmode = USB_TEST_J;
else if (!strncmp(buf, "test_k", 6))
- testmode = TEST_K;
+ testmode = USB_TEST_K;
else if (!strncmp(buf, "test_se0_nak", 12))
- testmode = TEST_SE0_NAK;
+ testmode = USB_TEST_SE0_NAK;
else if (!strncmp(buf, "test_packet", 11))
- testmode = TEST_PACKET;
+ testmode = USB_TEST_PACKET;
else if (!strncmp(buf, "test_force_enable", 17))
- testmode = TEST_FORCE_EN;
+ testmode = USB_TEST_FORCE_ENABLE;
else
testmode = 0;
@@ -78,19 +78,19 @@
case 0:
seq_puts(s, "no test\n");
break;
- case TEST_J:
+ case USB_TEST_J:
seq_puts(s, "test_j\n");
break;
- case TEST_K:
+ case USB_TEST_K:
seq_puts(s, "test_k\n");
break;
- case TEST_SE0_NAK:
+ case USB_TEST_SE0_NAK:
seq_puts(s, "test_se0_nak\n");
break;
- case TEST_PACKET:
+ case USB_TEST_PACKET:
seq_puts(s, "test_packet\n");
break;
- case TEST_FORCE_EN:
+ case USB_TEST_FORCE_ENABLE:
seq_puts(s, "test_force_enable\n");
break;
default:
@@ -183,6 +183,7 @@
static int fifo_show(struct seq_file *seq, void *v)
{
struct dwc2_hsotg *hsotg = seq->private;
+ int fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
u32 val;
int idx;
@@ -196,7 +197,7 @@
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
- for (idx = 1; idx < hsotg->num_of_eps; idx++) {
+ for (idx = 1; idx <= fifo_count; idx++) {
val = dwc2_readl(hsotg, DPTXFSIZN(idx));
seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
@@ -770,7 +771,7 @@
int ret;
struct dentry *root;
- root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
+ root = debugfs_create_dir(dev_name(hsotg->dev), usb_debug_root);
hsotg->debug_root = root;
debugfs_create_file("params", 0444, root, hsotg, ¶ms_fops);
diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c
new file mode 100644
index 0000000..36f2c38
--- /dev/null
+++ b/drivers/usb/dwc2/drd.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drd.c - DesignWare USB2 DRD Controller Dual-role support
+ *
+ * Copyright (C) 2020 STMicroelectronics
+ *
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/usb/role.h>
+#include "core.h"
+
+static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
+{
+ unsigned long flags;
+ u32 gotgctl;
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+
+ gotgctl = dwc2_readl(hsotg, GOTGCTL);
+ gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN;
+ gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
+ gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
+ dwc2_writel(hsotg, gotgctl, GOTGCTL);
+
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST));
+}
+
+static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
+{
+ u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
+
+ /* Check if A-Session is already in the right state */
+ if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
+ (!valid && !(gotgctl & GOTGCTL_ASESVLD)))
+ return -EALREADY;
+
+ gotgctl &= ~GOTGCTL_BVALOVAL;
+ if (valid)
+ gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
+ else
+ gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
+ dwc2_writel(hsotg, gotgctl, GOTGCTL);
+
+ return 0;
+}
+
+static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
+{
+ u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
+
+ /* Check if B-Session is already in the right state */
+ if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
+ (!valid && !(gotgctl & GOTGCTL_BSESVLD)))
+ return -EALREADY;
+
+ gotgctl &= ~GOTGCTL_AVALOVAL;
+ if (valid)
+ gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
+ else
+ gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL);
+ dwc2_writel(hsotg, gotgctl, GOTGCTL);
+
+ return 0;
+}
+
+static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
+{
+ struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
+ unsigned long flags;
+ int already = 0;
+
+ /* Skip session not in line with dr_mode */
+ if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
+ (role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
+ return -EINVAL;
+
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+ IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+ /* Skip session if core is in test mode */
+ if (role == USB_ROLE_NONE && hsotg->test_mode) {
+ dev_dbg(hsotg->dev, "Core is in test mode\n");
+ return -EBUSY;
+ }
+#endif
+
+ /*
+ * In case of USB_DR_MODE_PERIPHERAL, clock is disabled at the end of
+ * the probe and enabled on udc_start.
+ * If role-switch set is called before the udc_start, we need to enable
+ * the clock to read/write GOTGCTL and GUSBCFG registers to override
+ * mode and sessions. It is the case if cable is plugged at boot.
+ */
+ if (!hsotg->ll_hw_enabled && hsotg->clk) {
+ int ret = clk_prepare_enable(hsotg->clk);
+
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+
+ if (role == USB_ROLE_HOST) {
+ already = dwc2_ovr_avalid(hsotg, true);
+ } else if (role == USB_ROLE_DEVICE) {
+ already = dwc2_ovr_bvalid(hsotg, true);
+ if (dwc2_is_device_enabled(hsotg)) {
+ /* This clear DCTL.SFTDISCON bit */
+ dwc2_hsotg_core_connect(hsotg);
+ }
+ } else {
+ if (dwc2_is_device_mode(hsotg)) {
+ if (!dwc2_ovr_bvalid(hsotg, false))
+ /* This set DCTL.SFTDISCON bit */
+ dwc2_hsotg_core_disconnect(hsotg);
+ } else {
+ dwc2_ovr_avalid(hsotg, false);
+ }
+ }
+
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ if (!already && hsotg->dr_mode == USB_DR_MODE_OTG)
+ /* This will raise a Connector ID Status Change Interrupt */
+ dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
+
+ if (!hsotg->ll_hw_enabled && hsotg->clk)
+ clk_disable_unprepare(hsotg->clk);
+
+ dev_dbg(hsotg->dev, "%s-session valid\n",
+ role == USB_ROLE_NONE ? "No" :
+ role == USB_ROLE_HOST ? "A" : "B");
+
+ return 0;
+}
+
+int dwc2_drd_init(struct dwc2_hsotg *hsotg)
+{
+ struct usb_role_switch_desc role_sw_desc = {0};
+ struct usb_role_switch *role_sw;
+ int ret;
+
+ if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
+ return 0;
+
+ role_sw_desc.driver_data = hsotg;
+ role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
+ role_sw_desc.set = dwc2_drd_role_sw_set;
+ role_sw_desc.allow_userspace_control = true;
+
+ role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc);
+ if (IS_ERR(role_sw)) {
+ ret = PTR_ERR(role_sw);
+ dev_err(hsotg->dev,
+ "failed to register role switch: %d\n", ret);
+ return ret;
+ }
+
+ hsotg->role_sw = role_sw;
+
+ /* Enable override and initialize values */
+ dwc2_ovr_init(hsotg);
+
+ return 0;
+}
+
+void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
+{
+ u32 gintsts, gintmsk;
+
+ if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
+ gintmsk &= ~GINTSTS_CONIDSTSCHNG;
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
+ gintsts = dwc2_readl(hsotg, GINTSTS);
+ dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
+ }
+}
+
+void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
+{
+ u32 gintsts, gintmsk;
+
+ if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
+ gintsts = dwc2_readl(hsotg, GINTSTS);
+ dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
+ gintmsk = dwc2_readl(hsotg, GINTMSK);
+ gintmsk |= GINTSTS_CONIDSTSCHNG;
+ dwc2_writel(hsotg, gintmsk, GINTMSK);
+ }
+}
+
+void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
+{
+ if (hsotg->role_sw)
+ usb_role_switch_unregister(hsotg->role_sw);
+}
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 66dfcdb..ec54971 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -115,10 +115,16 @@
*/
static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
{
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
+ u16 limit = DSTS_SOFFN_LIMIT;
+
+ if (hsotg->gadget.speed != USB_SPEED_HIGH)
+ limit >>= 3;
+
hs_ep->target_frame += hs_ep->interval;
- if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) {
+ if (hs_ep->target_frame > limit) {
hs_ep->frame_overrun = true;
- hs_ep->target_frame &= DSTS_SOFFN_LIMIT;
+ hs_ep->target_frame &= limit;
} else {
hs_ep->frame_overrun = false;
}
@@ -136,10 +142,16 @@
*/
static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep)
{
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
+ u16 limit = DSTS_SOFFN_LIMIT;
+
+ if (hsotg->gadget.speed != USB_SPEED_HIGH)
+ limit >>= 3;
+
if (hs_ep->target_frame)
hs_ep->target_frame -= 1;
else
- hs_ep->target_frame = DSTS_SOFFN_LIMIT;
+ hs_ep->target_frame = limit;
}
/**
@@ -260,6 +272,7 @@
gintsts2 = dwc2_readl(hsotg, GINTSTS2);
gintmsk2 = dwc2_readl(hsotg, GINTMSK2);
+ gintsts2 &= gintmsk2;
if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) {
dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__);
@@ -900,11 +913,10 @@
struct dwc2_dma_desc *desc;
struct dwc2_hsotg *hsotg = hs_ep->parent;
u32 index;
- u32 maxsize = 0;
u32 mask = 0;
u8 pid = 0;
- maxsize = dwc2_gadget_get_desc_params(hs_ep, &mask);
+ dwc2_gadget_get_desc_params(hs_ep, &mask);
index = hs_ep->next_desc;
desc = &hs_ep->desc_list[index];
@@ -1018,6 +1030,12 @@
dwc2_writel(hsotg, ctrl, depctl);
}
+static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep);
+static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
+ struct dwc2_hsotg_ep *hs_ep,
+ struct dwc2_hsotg_req *hs_req,
+ int result);
+
/**
* dwc2_hsotg_start_req - start a USB request from an endpoint's queue
* @hsotg: The controller state.
@@ -1170,14 +1188,21 @@
}
}
- if (hs_ep->isochronous && hs_ep->interval == 1) {
- hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
- dwc2_gadget_incr_frame_num(hs_ep);
-
- if (hs_ep->target_frame & 0x1)
- ctrl |= DXEPCTL_SETODDFR;
- else
- ctrl |= DXEPCTL_SETEVENFR;
+ if (hs_ep->isochronous) {
+ if (!dwc2_gadget_target_frame_elapsed(hs_ep)) {
+ if (hs_ep->interval == 1) {
+ if (hs_ep->target_frame & 0x1)
+ ctrl |= DXEPCTL_SETODDFR;
+ else
+ ctrl |= DXEPCTL_SETEVENFR;
+ }
+ ctrl |= DXEPCTL_CNAK;
+ } else {
+ hs_req->req.frame_number = hs_ep->target_frame;
+ hs_req->req.actual = 0;
+ dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA);
+ return;
+ }
}
ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
@@ -1325,12 +1350,16 @@
u32 target_frame = hs_ep->target_frame;
u32 current_frame = hsotg->frame_number;
bool frame_overrun = hs_ep->frame_overrun;
+ u16 limit = DSTS_SOFFN_LIMIT;
+
+ if (hsotg->gadget.speed != USB_SPEED_HIGH)
+ limit >>= 3;
if (!frame_overrun && current_frame >= target_frame)
return true;
if (frame_overrun && current_frame >= target_frame &&
- ((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2))
+ ((current_frame - target_frame) < limit / 2))
return true;
return false;
@@ -1568,11 +1597,11 @@
dctl &= ~DCTL_TSTCTL_MASK;
switch (testmode) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
dctl |= testmode << DCTL_TSTCTL_SHIFT;
break;
default:
@@ -1653,7 +1682,8 @@
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
- status = 1 << USB_DEVICE_SELF_POWERED;
+ status = hsotg->gadget.is_selfpowered <<
+ USB_DEVICE_SELF_POWERED;
status |= hsotg->remote_wakeup_allowed <<
USB_DEVICE_REMOTE_WAKEUP;
reply = cpu_to_le16(status);
@@ -1712,11 +1742,9 @@
*/
static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)
{
- u32 mask;
struct dwc2_hsotg *hsotg = hs_ep->parent;
int dir_in = hs_ep->dir_in;
struct dwc2_hsotg_req *hs_req;
- u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK;
if (!list_empty(&hs_ep->queue)) {
hs_req = get_ep_head(hs_ep);
@@ -1732,9 +1760,6 @@
} else {
dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n",
__func__);
- mask = dwc2_readl(hsotg, epmsk_reg);
- mask |= DOEPMSK_OUTTKNEPDISMSK;
- dwc2_writel(hsotg, mask, epmsk_reg);
}
}
@@ -2304,19 +2329,6 @@
dwc2_hsotg_program_zlp(hsotg, hsotg->eps_out[0]);
}
-static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg,
- u32 epctl_reg)
-{
- u32 ctrl;
-
- ctrl = dwc2_readl(hsotg, epctl_reg);
- if (ctrl & DXEPCTL_EOFRNUM)
- ctrl |= DXEPCTL_SETEVENFR;
- else
- ctrl |= DXEPCTL_SETODDFR;
- dwc2_writel(hsotg, ctrl, epctl_reg);
-}
-
/*
* dwc2_gadget_get_xfersize_ddma - get transferred bytes amount from desc
* @hs_ep - The endpoint on which transfer went
@@ -2437,20 +2449,11 @@
dwc2_hsotg_ep0_zlp(hsotg, true);
}
- /*
- * Slave mode OUT transfers do not go through XferComplete so
- * adjust the ISOC parity here.
- */
- if (!using_dma(hsotg)) {
- if (hs_ep->isochronous && hs_ep->interval == 1)
- dwc2_hsotg_change_ep_iso_parity(hsotg, DOEPCTL(epnum));
- else if (hs_ep->isochronous && hs_ep->interval > 1)
- dwc2_gadget_incr_frame_num(hs_ep);
- }
-
/* Set actual frame number for completed transfers */
- if (!using_desc_dma(hsotg) && hs_ep->isochronous)
- req->frame_number = hsotg->frame_number;
+ if (!using_desc_dma(hsotg) && hs_ep->isochronous) {
+ req->frame_number = hs_ep->target_frame;
+ dwc2_gadget_incr_frame_num(hs_ep);
+ }
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
}
@@ -2764,6 +2767,12 @@
return;
}
+ /* Set actual frame number for completed transfers */
+ if (!using_desc_dma(hsotg) && hs_ep->isochronous) {
+ hs_req->req.frame_number = hs_ep->target_frame;
+ dwc2_gadget_incr_frame_num(hs_ep);
+ }
+
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
}
@@ -2824,23 +2833,18 @@
dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
- if (hs_ep->isochronous) {
- dwc2_hsotg_complete_in(hsotg, hs_ep);
- return;
- }
-
if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) {
int dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_CGNPINNAK;
dwc2_writel(hsotg, dctl, DCTL);
}
- return;
- }
+ } else {
- if (dctl & DCTL_GOUTNAKSTS) {
- dctl |= DCTL_CGOUTNAK;
- dwc2_writel(hsotg, dctl, DCTL);
+ if (dctl & DCTL_GOUTNAKSTS) {
+ dctl |= DCTL_CGOUTNAK;
+ dwc2_writel(hsotg, dctl, DCTL);
+ }
}
if (!hs_ep->isochronous)
@@ -2854,15 +2858,16 @@
do {
hs_req = get_ep_head(hs_ep);
- if (hs_req)
+ if (hs_req) {
+ hs_req->req.frame_number = hs_ep->target_frame;
+ hs_req->req.actual = 0;
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req,
-ENODATA);
+ }
dwc2_gadget_incr_frame_num(hs_ep);
/* Update current frame number value. */
hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
} while (dwc2_gadget_target_frame_elapsed(hs_ep));
-
- dwc2_gadget_start_next_request(hs_ep);
}
/**
@@ -2879,8 +2884,8 @@
static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
{
struct dwc2_hsotg *hsotg = ep->parent;
+ struct dwc2_hsotg_req *hs_req;
int dir_in = ep->dir_in;
- u32 doepmsk;
if (dir_in || !ep->isochronous)
return;
@@ -2894,28 +2899,42 @@
return;
}
- if (ep->interval > 1 &&
- ep->target_frame == TARGET_FRAME_INITIAL) {
+ if (ep->target_frame == TARGET_FRAME_INITIAL) {
u32 ctrl;
ep->target_frame = hsotg->frame_number;
- dwc2_gadget_incr_frame_num(ep);
+ if (ep->interval > 1) {
+ ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index));
+ if (ep->target_frame & 0x1)
+ ctrl |= DXEPCTL_SETODDFR;
+ else
+ ctrl |= DXEPCTL_SETEVENFR;
- ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index));
- if (ep->target_frame & 0x1)
- ctrl |= DXEPCTL_SETODDFR;
- else
- ctrl |= DXEPCTL_SETEVENFR;
-
- dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index));
+ dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index));
+ }
}
- dwc2_gadget_start_next_request(ep);
- doepmsk = dwc2_readl(hsotg, DOEPMSK);
- doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK;
- dwc2_writel(hsotg, doepmsk, DOEPMSK);
+ while (dwc2_gadget_target_frame_elapsed(ep)) {
+ hs_req = get_ep_head(ep);
+ if (hs_req) {
+ hs_req->req.frame_number = ep->target_frame;
+ hs_req->req.actual = 0;
+ dwc2_hsotg_complete_request(hsotg, ep, hs_req, -ENODATA);
+ }
+
+ dwc2_gadget_incr_frame_num(ep);
+ /* Update current frame number value. */
+ hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
+ }
+
+ if (!ep->req)
+ dwc2_gadget_start_next_request(ep);
+
}
+static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
+ struct dwc2_hsotg_ep *hs_ep);
+
/**
* dwc2_gadget_handle_nak - handle NAK interrupt
* @hs_ep: The endpoint on which interrupt is asserted.
@@ -2933,7 +2952,9 @@
static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg *hsotg = hs_ep->parent;
+ struct dwc2_hsotg_req *hs_req;
int dir_in = hs_ep->dir_in;
+ u32 ctrl;
if (!dir_in || !hs_ep->isochronous)
return;
@@ -2975,13 +2996,32 @@
dwc2_writel(hsotg, ctrl, DIEPCTL(hs_ep->index));
}
-
- dwc2_hsotg_complete_request(hsotg, hs_ep,
- get_ep_head(hs_ep), 0);
}
- if (!using_desc_dma(hsotg))
+ if (using_desc_dma(hsotg))
+ return;
+
+ ctrl = dwc2_readl(hsotg, DIEPCTL(hs_ep->index));
+ if (ctrl & DXEPCTL_EPENA)
+ dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep);
+ else
+ dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
+
+ while (dwc2_gadget_target_frame_elapsed(hs_ep)) {
+ hs_req = get_ep_head(hs_ep);
+ if (hs_req) {
+ hs_req->req.frame_number = hs_ep->target_frame;
+ hs_req->req.actual = 0;
+ dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA);
+ }
+
dwc2_gadget_incr_frame_num(hs_ep);
+ /* Update current frame number value. */
+ hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
+ }
+
+ if (!hs_ep->req)
+ dwc2_gadget_start_next_request(hs_ep);
}
/**
@@ -3000,10 +3040,8 @@
u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
u32 ints;
- u32 ctrl;
ints = dwc2_gadget_read_ep_interrupts(hsotg, idx, dir_in);
- ctrl = dwc2_readl(hsotg, epctl_reg);
/* Clear endpoint interrupts */
dwc2_writel(hsotg, ints, epint_reg);
@@ -3039,21 +3077,15 @@
/* In DDMA handle isochronous requests separately */
if (using_desc_dma(hsotg) && hs_ep->isochronous) {
- /* XferCompl set along with BNA */
- if (!(ints & DXEPINT_BNAINTR))
- dwc2_gadget_complete_isoc_request_ddma(hs_ep);
+ dwc2_gadget_complete_isoc_request_ddma(hs_ep);
} else if (dir_in) {
/*
* We get OutDone from the FIFO, so we only
* need to look at completing IN requests here
* if operating slave mode
*/
- if (hs_ep->isochronous && hs_ep->interval > 1)
- dwc2_gadget_incr_frame_num(hs_ep);
-
- dwc2_hsotg_complete_in(hsotg, hs_ep);
- if (ints & DXEPINT_NAKINTRPT)
- ints &= ~DXEPINT_NAKINTRPT;
+ if (!hs_ep->isochronous || !(ints & DXEPINT_NAKINTRPT))
+ dwc2_hsotg_complete_in(hsotg, hs_ep);
if (idx == 0 && !hs_ep->req)
dwc2_hsotg_enqueue_setup(hsotg);
@@ -3062,10 +3094,8 @@
* We're using DMA, we need to fire an OutDone here
* as we ignore the RXFIFO.
*/
- if (hs_ep->isochronous && hs_ep->interval > 1)
- dwc2_gadget_incr_frame_num(hs_ep);
-
- dwc2_hsotg_handle_outdone(hsotg, idx);
+ if (!hs_ep->isochronous || !(ints & DXEPINT_OUTTKNEPDIS))
+ dwc2_hsotg_handle_outdone(hsotg, idx);
}
}
@@ -3554,7 +3584,7 @@
dwc2_readl(hsotg, DOEPCTL0));
}
-static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
+void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
{
/* set the soft-disconnect bit */
dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
@@ -3813,15 +3843,26 @@
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_out[idx];
/* Proceed only unmasked ISOC EPs */
- if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous)
+ if (BIT(idx) & ~daintmsk)
continue;
epctrl = dwc2_readl(hsotg, DOEPCTL(idx));
- if (epctrl & DXEPCTL_EPENA) {
+ //ISOC Ep's only
+ if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) {
epctrl |= DXEPCTL_SNAK;
epctrl |= DXEPCTL_EPDIS;
dwc2_writel(hsotg, epctrl, DOEPCTL(idx));
+ continue;
+ }
+
+ //Non-ISOC EP's
+ if (hs_ep->halted) {
+ if (!(epctrl & DXEPCTL_EPENA))
+ epctrl |= DXEPCTL_EPENA;
+ epctrl |= DXEPCTL_EPDIS;
+ epctrl |= DXEPCTL_STALL;
+ dwc2_writel(hsotg, epctrl, DOEPCTL(idx));
}
}
@@ -3892,9 +3933,27 @@
__func__);
}
} else {
+ /* Mask GINTSTS_GOUTNAKEFF interrupt */
+ dwc2_hsotg_disable_gsint(hsotg, GINTSTS_GOUTNAKEFF);
+
if (!(dwc2_readl(hsotg, GINTSTS) & GINTSTS_GOUTNAKEFF))
dwc2_set_bit(hsotg, DCTL, DCTL_SGOUTNAK);
+ if (!using_dma(hsotg)) {
+ /* Wait for GINTSTS_RXFLVL interrupt */
+ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
+ GINTSTS_RXFLVL, 100)) {
+ dev_warn(hsotg->dev, "%s: timeout GINTSTS.RXFLVL\n",
+ __func__);
+ } else {
+ /*
+ * Pop GLOBAL OUT NAK status packet from RxFIFO
+ * to assert GOUTNAKEFF interrupt
+ */
+ dwc2_readl(hsotg, GRXSTSP);
+ }
+ }
+
/* Wait for global nak to take effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
GINTSTS_GOUTNAKEFF, 100))
@@ -4055,6 +4114,7 @@
mask |= DIEPMSK_NAKMSK;
dwc2_writel(hsotg, mask, DIEPMSK);
} else {
+ epctrl |= DXEPCTL_SNAK;
mask = dwc2_readl(hsotg, DOEPMSK);
mask |= DOEPMSK_OUTTKNEPDISMSK;
dwc2_writel(hsotg, mask, DOEPMSK);
@@ -4340,19 +4400,23 @@
epctl = dwc2_readl(hs, epreg);
if (value) {
- epctl |= DXEPCTL_STALL;
+ /* Unmask GOUTNAKEFF interrupt */
+ dwc2_hsotg_en_gsint(hs, GINTSTS_GOUTNAKEFF);
+
+ if (!(dwc2_readl(hs, GINTSTS) & GINTSTS_GOUTNAKEFF))
+ dwc2_set_bit(hs, DCTL, DCTL_SGOUTNAK);
+ // STALL bit will be set in GOUTNAKEFF interrupt handler
} else {
epctl &= ~DXEPCTL_STALL;
xfertype = epctl & DXEPCTL_EPTYPE_MASK;
if (xfertype == DXEPCTL_EPTYPE_BULK ||
xfertype == DXEPCTL_EPTYPE_INTERRUPT)
epctl |= DXEPCTL_SETD0PID;
+ dwc2_writel(hs, epctl, epreg);
}
- dwc2_writel(hs, epctl, epreg);
}
hs_ep->halted = value;
-
return 0;
}
@@ -4539,6 +4603,26 @@
}
/**
+ * dwc2_hsotg_set_selfpowered - set if device is self/bus powered
+ * @gadget: The usb gadget state
+ * @is_selfpowered: Whether the device is self-powered
+ *
+ * Set if the device is self or bus powered.
+ */
+static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget,
+ int is_selfpowered)
+{
+ struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+ gadget->is_selfpowered = !!is_selfpowered;
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ return 0;
+}
+
+/**
* dwc2_hsotg_pullup - connect/disconnect the USB PHY
* @gadget: The usb gadget state
* @is_on: Current state of the USB PHY
@@ -4629,6 +4713,7 @@
static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
.get_frame = dwc2_hsotg_gadget_getframe,
+ .set_selfpowered = dwc2_hsotg_set_selfpowered,
.udc_start = dwc2_hsotg_udc_start,
.udc_stop = dwc2_hsotg_udc_stop,
.pullup = dwc2_hsotg_pullup,
@@ -4947,7 +5032,7 @@
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
spin_unlock_irqrestore(&hsotg->lock, flags);
- for (ep = 0; ep < hsotg->num_of_eps; ep++) {
+ for (ep = 1; ep < hsotg->num_of_eps; ep++) {
if (hsotg->eps_in[ep])
dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
if (hsotg->eps_out[ep])
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index f29fbad..30919f7 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -2825,7 +2825,7 @@
list_move_tail(&chan->split_order_list_entry,
&hsotg->split_order);
- if (hsotg->params.host_dma) {
+ if (hsotg->params.host_dma && chan->qh) {
if (hsotg->params.dma_desc_enable) {
if (!chan->xfer_started ||
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
@@ -3629,7 +3629,7 @@
"SetPortFeature - USB_PORT_FEAT_SUSPEND\n");
if (windex != hsotg->otg_port)
goto error;
- if (hsotg->params.power_down == 2)
+ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION)
dwc2_enter_hibernation(hsotg, 1);
else
dwc2_port_suspend(hsotg, windex);
@@ -3647,7 +3647,7 @@
break;
case USB_PORT_FEAT_RESET:
- if (hsotg->params.power_down == 2 &&
+ if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION &&
hsotg->hibernated)
dwc2_exit_hibernation(hsotg, 0, 1, 1);
hprt0 = dwc2_read_hprt0(hsotg);
@@ -5074,6 +5074,10 @@
hcd->has_tt = 1;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ retval = -EINVAL;
+ goto error1;
+ }
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index 8ca6d12..ea02ee6 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* hcd.h - DesignWare HS OTG Controller host-mode declarations
*
@@ -199,7 +199,7 @@
u32 flags;
u16 interval;
struct dwc2_hcd_pipe_info pipe_info;
- struct dwc2_hcd_iso_packet_desc iso_descs[0];
+ struct dwc2_hcd_iso_packet_desc iso_descs[];
};
/* Phases for control transfers */
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 68bbac6..94af71e 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -59,7 +59,7 @@
#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
/* If we get a NAK, wait this long before retrying */
-#define DWC2_RETRY_WAIT_DELAY 1*1E6L
+#define DWC2_RETRY_WAIT_DELAY (1 * NSEC_PER_MSEC)
/**
* dwc2_periodic_channel_available() - Checks that a channel is available for a
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 510e87e..c3d6dde 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* hw.h - DesignWare HS OTG Controller hardware definitions
*
@@ -54,6 +54,12 @@
#define GOTGCTL_HSTSETHNPEN BIT(10)
#define GOTGCTL_HNPREQ BIT(9)
#define GOTGCTL_HSTNEGSCS BIT(8)
+#define GOTGCTL_BVALOVAL BIT(7)
+#define GOTGCTL_BVALOEN BIT(6)
+#define GOTGCTL_AVALOVAL BIT(5)
+#define GOTGCTL_AVALOEN BIT(4)
+#define GOTGCTL_VBVALOVAL BIT(3)
+#define GOTGCTL_VBVALOEN BIT(2)
#define GOTGCTL_SESREQ BIT(1)
#define GOTGCTL_SESREQSCS BIT(0)
@@ -120,6 +126,7 @@
#define GRSTCTL HSOTG_REG(0x010)
#define GRSTCTL_AHBIDLE BIT(31)
#define GRSTCTL_DMAREQ BIT(30)
+#define GRSTCTL_CSFTRST_DONE BIT(29)
#define GRSTCTL_TXFNUM_MASK (0x1f << 6)
#define GRSTCTL_TXFNUM_SHIFT 6
#define GRSTCTL_TXFNUM_LIMIT 0x1f
@@ -227,6 +234,8 @@
#define GPVNDCTL HSOTG_REG(0x0034)
#define GGPIO HSOTG_REG(0x0038)
#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16)
+#define GGPIO_STM32_OTG_GCCFG_VBDEN BIT(21)
+#define GGPIO_STM32_OTG_GCCFG_IDEN BIT(22)
#define GUID HSOTG_REG(0x003c)
#define GSNPSID HSOTG_REG(0x0040)
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 6d38126..267543c 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -68,14 +68,14 @@
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
p->change_speed_quirk = true;
- p->power_down = false;
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
}
static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
- p->power_down = 0;
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
p->phy_utmi_width = 8;
}
@@ -89,7 +89,7 @@
p->host_perio_tx_fifo_size = 256;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
- p->power_down = 0;
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
}
static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
@@ -163,6 +163,35 @@
p->host_perio_tx_fifo_size = 256;
}
+static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+ p->speed = DWC2_SPEED_PARAM_FULL;
+ p->host_rx_fifo_size = 128;
+ p->host_nperio_tx_fifo_size = 96;
+ p->host_perio_tx_fifo_size = 96;
+ p->max_packet_count = 256;
+ p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
+ p->i2c_enable = false;
+ p->activate_stm_fs_transceiver = true;
+ p->activate_stm_id_vb_detection = true;
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
+static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+ p->activate_stm_id_vb_detection = !device_property_read_bool(hsotg->dev, "usb-role-switch");
+ p->host_rx_fifo_size = 440;
+ p->host_nperio_tx_fifo_size = 256;
+ p->host_perio_tx_fifo_size = 256;
+ p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
{ .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params },
@@ -181,11 +210,16 @@
{ .compatible = "amlogic,meson-g12a-usb",
.data = dwc2_set_amlogic_g12a_params },
{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
+ { .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params },
{ .compatible = "st,stm32f4x9-fsotg",
.data = dwc2_set_stm32f4x9_fsotg_params },
{ .compatible = "st,stm32f4x9-hsotg" },
{ .compatible = "st,stm32f7-hsotg",
.data = dwc2_set_stm32f7_hsotg_params },
+ { .compatible = "st,stm32mp15-fsotg",
+ .data = dwc2_set_stm32mp15_fsotg_params },
+ { .compatible = "st,stm32mp15-hsotg",
+ .data = dwc2_set_stm32mp15_hsotg_params },
{},
};
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
@@ -286,11 +320,11 @@
int val;
if (hsotg->hw_params.hibernation)
- val = 2;
+ val = DWC2_POWER_DOWN_PARAM_HIBERNATION;
else if (hsotg->hw_params.power_optimized)
- val = 1;
+ val = DWC2_POWER_DOWN_PARAM_PARTIAL;
else
- val = 0;
+ val = DWC2_POWER_DOWN_PARAM_NONE;
hsotg->params.power_down = val;
}
@@ -749,25 +783,6 @@
u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
u32 grxfsiz;
- /*
- * Attempt to ensure this device is really a DWC_otg Controller.
- * Read and verify the GSNPSID register contents. The value should be
- * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
- */
-
- hw->snpsid = dwc2_readl(hsotg, GSNPSID);
- if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
- (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
- (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
- dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
- hw->snpsid);
- return -ENODEV;
- }
-
- dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
- hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
- hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
-
hwcfg1 = dwc2_readl(hsotg, GHWCFG1);
hwcfg2 = dwc2_readl(hsotg, GHWCFG2);
hwcfg3 = dwc2_readl(hsotg, GHWCFG3);
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 34bb612..49d333f 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -121,6 +121,13 @@
return 0;
}
+static void __dwc2_disable_regulators(void *data)
+{
+ struct dwc2_hsotg *hsotg = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
+}
+
static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
{
struct platform_device *pdev = to_platform_device(hsotg->dev);
@@ -131,6 +138,11 @@
if (ret)
return ret;
+ ret = devm_add_action_or_reset(&pdev->dev,
+ __dwc2_disable_regulators, hsotg);
+ if (ret)
+ return ret;
+
if (hsotg->clk) {
ret = clk_prepare_enable(hsotg->clk);
if (ret)
@@ -186,10 +198,7 @@
if (hsotg->clk)
clk_disable_unprepare(hsotg->clk);
- ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
- hsotg->supplies);
-
- return ret;
+ return 0;
}
/**
@@ -285,7 +294,9 @@
ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
if (ret) {
- dev_err(hsotg->dev, "failed to request supplies: %d\n", ret);
+ if (ret != -EPROBE_DEFER)
+ dev_err(hsotg->dev, "failed to request supplies: %d\n",
+ ret);
return ret;
}
return 0;
@@ -312,6 +323,11 @@
if (hsotg->gadget_enabled)
dwc2_hsotg_remove(hsotg);
+ dwc2_drd_exit(hsotg);
+
+ if (hsotg->params.activate_stm_id_vb_detection)
+ regulator_disable(hsotg->usb33d);
+
if (hsotg->ll_hw_enabled)
dwc2_lowlevel_hw_disable(hsotg);
@@ -359,6 +375,37 @@
}
/**
+ * Check core version
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ */
+int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+
+ /*
+ * Attempt to ensure this device is really a DWC_otg Controller.
+ * Read and verify the GSNPSID register contents. The value should be
+ * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
+ */
+
+ hw->snpsid = dwc2_readl(hsotg, GSNPSID);
+ if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
+ (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
+ (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
+ dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
+ hw->snpsid);
+ return -ENODEV;
+ }
+
+ dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
+ hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
+ hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
+ return 0;
+}
+
+/**
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
* driver
*
@@ -393,8 +440,7 @@
return retval;
}
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- hsotg->regs = devm_ioremap_resource(&dev->dev, res);
+ hsotg->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res);
if (IS_ERR(hsotg->regs))
return PTR_ERR(hsotg->regs);
@@ -442,6 +488,14 @@
"snps,need-phy-for-wake");
/*
+ * Before performing any core related operations
+ * check core version.
+ */
+ retval = dwc2_check_core_version(hsotg);
+ if (retval)
+ goto error;
+
+ /*
* Reset before dwc2_get_hwparams() then it could get power-on real
* reset value form registers.
*/
@@ -465,10 +519,45 @@
if (retval)
goto error;
+ if (hsotg->params.activate_stm_id_vb_detection) {
+ u32 ggpio;
+
+ hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d");
+ if (IS_ERR(hsotg->usb33d)) {
+ retval = PTR_ERR(hsotg->usb33d);
+ if (retval != -EPROBE_DEFER)
+ dev_err(hsotg->dev,
+ "failed to request usb33d supply: %d\n",
+ retval);
+ goto error;
+ }
+ retval = regulator_enable(hsotg->usb33d);
+ if (retval) {
+ dev_err(hsotg->dev,
+ "failed to enable usb33d supply: %d\n", retval);
+ goto error;
+ }
+
+ ggpio = dwc2_readl(hsotg, GGPIO);
+ ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
+ ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
+ dwc2_writel(hsotg, ggpio, GGPIO);
+
+ /* ID/VBUS detection startup time */
+ usleep_range(5000, 7000);
+ }
+
+ retval = dwc2_drd_init(hsotg);
+ if (retval) {
+ if (retval != -EPROBE_DEFER)
+ dev_err(hsotg->dev, "failed to initialize dual-role\n");
+ goto error_init;
+ }
+
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg);
if (retval)
- goto error;
+ goto error_drd;
hsotg->gadget_enabled = 1;
}
@@ -494,7 +583,7 @@
if (retval) {
if (hsotg->gadget_enabled)
dwc2_hsotg_remove(hsotg);
- goto error;
+ goto error_drd;
}
hsotg->hcd_enabled = 1;
}
@@ -516,12 +605,25 @@
if (retval) {
hsotg->gadget.udc = NULL;
dwc2_hsotg_remove(hsotg);
- goto error;
+ goto error_debugfs;
}
}
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
return 0;
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
+ IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+error_debugfs:
+ dwc2_debugfs_exit(hsotg);
+ if (hsotg->hcd_enabled)
+ dwc2_hcd_remove(hsotg);
+#endif
+error_drd:
+ dwc2_drd_exit(hsotg);
+
+error_init:
+ if (hsotg->params.activate_stm_id_vb_detection)
+ regulator_disable(hsotg->usb33d);
error:
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL)
dwc2_lowlevel_hw_disable(hsotg);
@@ -537,6 +639,39 @@
if (is_device_mode)
dwc2_hsotg_suspend(dwc2);
+ dwc2_drd_suspend(dwc2);
+
+ if (dwc2->params.activate_stm_id_vb_detection) {
+ unsigned long flags;
+ u32 ggpio, gotgctl;
+
+ /*
+ * Need to force the mode to the current mode to avoid Mode
+ * Mismatch Interrupt when ID detection will be disabled.
+ */
+ dwc2_force_mode(dwc2, !is_device_mode);
+
+ spin_lock_irqsave(&dwc2->lock, flags);
+ gotgctl = dwc2_readl(dwc2, GOTGCTL);
+ /* bypass debounce filter, enable overrides */
+ gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
+ gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN;
+ /* Force A / B session if needed */
+ if (gotgctl & GOTGCTL_ASESVLD)
+ gotgctl |= GOTGCTL_AVALOVAL;
+ if (gotgctl & GOTGCTL_BSESVLD)
+ gotgctl |= GOTGCTL_BVALOVAL;
+ dwc2_writel(dwc2, gotgctl, GOTGCTL);
+ spin_unlock_irqrestore(&dwc2->lock, flags);
+
+ ggpio = dwc2_readl(dwc2, GGPIO);
+ ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN;
+ ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN;
+ dwc2_writel(dwc2, ggpio, GGPIO);
+
+ regulator_disable(dwc2->usb33d);
+ }
+
if (dwc2->ll_hw_enabled &&
(is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
ret = __dwc2_lowlevel_hw_disable(dwc2);
@@ -558,6 +693,36 @@
}
dwc2->phy_off_for_suspend = false;
+ if (dwc2->params.activate_stm_id_vb_detection) {
+ unsigned long flags;
+ u32 ggpio, gotgctl;
+
+ ret = regulator_enable(dwc2->usb33d);
+ if (ret)
+ return ret;
+
+ ggpio = dwc2_readl(dwc2, GGPIO);
+ ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
+ ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
+ dwc2_writel(dwc2, ggpio, GGPIO);
+
+ /* ID/VBUS detection startup time */
+ usleep_range(5000, 7000);
+
+ spin_lock_irqsave(&dwc2->lock, flags);
+ gotgctl = dwc2_readl(dwc2, GOTGCTL);
+ gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS;
+ gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN |
+ GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL);
+ dwc2_writel(dwc2, gotgctl, GOTGCTL);
+ spin_unlock_irqrestore(&dwc2->lock, flags);
+ }
+
+ /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
+ dwc2_force_dr_mode(dwc2);
+
+ dwc2_drd_resume(dwc2);
+
if (dwc2_is_device_mode(dwc2))
ret = dwc2_hsotg_resume(dwc2);
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 556a876..7a23045 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -4,6 +4,7 @@
tristate "DesignWare USB3 DRD Core Support"
depends on (USB || USB_GADGET) && HAS_DMA
select USB_XHCI_PLATFORM if USB_XHCI_HCD
+ select USB_ROLE_SWITCH if USB_DWC3_DUAL_ROLE
help
Say Y or M here if your system has a Dual Role SuperSpeed
USB controller based on the DesignWare USB3 IP Core.
@@ -97,24 +98,24 @@
Say 'Y' or 'M' here if you have one such device
config USB_DWC3_MESON_G12A
- tristate "Amlogic Meson G12A Platforms"
- depends on OF && COMMON_CLK
- depends on ARCH_MESON || COMPILE_TEST
- default USB_DWC3
- select USB_ROLE_SWITCH
+ tristate "Amlogic Meson G12A Platforms"
+ depends on OF && COMMON_CLK
+ depends on ARCH_MESON || COMPILE_TEST
+ default USB_DWC3
+ select USB_ROLE_SWITCH
select REGMAP_MMIO
- help
- Support USB2/3 functionality in Amlogic G12A platforms.
- Say 'Y' or 'M' if you have one such device.
+ help
+ Support USB2/3 functionality in Amlogic G12A platforms.
+ Say 'Y' or 'M' if you have one such device.
config USB_DWC3_OF_SIMPLE
- tristate "Generic OF Simple Glue Layer"
- depends on OF && COMMON_CLK
- default USB_DWC3
- help
- Support USB2/3 functionality in simple SoC integrations.
- Currently supports Xilinx and Qualcomm DWC USB3 IP.
- Say 'Y' or 'M' if you have one such device.
+ tristate "Generic OF Simple Glue Layer"
+ depends on OF && COMMON_CLK
+ default USB_DWC3
+ help
+ Support USB2/3 functionality in simple SoC integrations.
+ Currently supports Xilinx and Qualcomm DWC USB3 IP.
+ Say 'Y' or 'M' if you have one such device.
config USB_DWC3_ST
tristate "STMicroelectronics Platforms"
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 2c68ec6..1580d51 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* core.c - DesignWare USB3 DRD Controller Core file
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -85,7 +85,9 @@
* specified or set to OTG, then set the mode to peripheral.
*/
if (mode == USB_DR_MODE_OTG &&
- dwc->revision >= DWC3_REVISION_330A)
+ (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
+ !device_property_read_bool(dwc->dev, "usb-role-switch")) &&
+ !DWC3_VER_IS_PRIOR(DWC3, 330A))
mode = USB_DR_MODE_PERIPHERAL;
}
@@ -112,6 +114,8 @@
dwc->current_dr_role = mode;
}
+static int dwc3_core_soft_reset(struct dwc3 *dwc);
+
static void __dwc3_set_mode(struct work_struct *work)
{
struct dwc3 *dwc = work_to_dwc(work);
@@ -119,20 +123,21 @@
int ret;
u32 reg;
- if (dwc->dr_mode != USB_DR_MODE_OTG)
- return;
+ mutex_lock(&dwc->mutex);
+
+ pm_runtime_get_sync(dwc->dev);
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
dwc3_otg_update(dwc, 0);
if (!dwc->desired_dr_role)
- return;
+ goto out;
if (dwc->desired_dr_role == dwc->current_dr_role)
- return;
+ goto out;
if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
- return;
+ goto out;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_HOST:
@@ -153,6 +158,25 @@
break;
}
+ /* For DRD host or device mode only */
+ if (dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG) {
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg |= DWC3_GCTL_CORESOFTRESET;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ /*
+ * Wait for internal clocks to synchronized. DWC_usb31 and
+ * DWC_usb32 may need at least 50ms (less for DWC_usb3). To
+ * keep it consistent across different IPs, let's wait up to
+ * 100ms before clearing GCTL.CORESOFTRESET.
+ */
+ msleep(100);
+
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_CORESOFTRESET;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ }
+
spin_lock_irqsave(&dwc->lock, flags);
dwc3_set_prtcap(dwc, dwc->desired_dr_role);
@@ -177,6 +201,8 @@
}
break;
case DWC3_GCTL_PRTCAP_DEVICE:
+ dwc3_core_soft_reset(dwc);
+
dwc3_event_buffers_setup(dwc);
if (dwc->usb2_phy)
@@ -196,12 +222,19 @@
break;
}
+out:
+ pm_runtime_mark_last_busy(dwc->dev);
+ pm_runtime_put_autosuspend(dwc->dev);
+ mutex_unlock(&dwc->mutex);
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
unsigned long flags;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
+
spin_lock_irqsave(&dwc->lock, flags);
dwc->desired_dr_role = mode;
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -231,19 +264,6 @@
{
u32 reg;
int retries = 1000;
- int ret;
-
- usb_phy_init(dwc->usb2_phy);
- usb_phy_init(dwc->usb3_phy);
- ret = phy_init(dwc->usb2_generic_phy);
- if (ret < 0)
- return ret;
-
- ret = phy_init(dwc->usb3_generic_phy);
- if (ret < 0) {
- phy_exit(dwc->usb2_generic_phy);
- return ret;
- }
/*
* We're resetting only the device side because, if we're in host mode,
@@ -263,7 +283,7 @@
* take a little more than 50ms. Set the polling rate at 20ms
* for 10 times instead.
*/
- if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
+ if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
retries = 10;
do {
@@ -271,16 +291,12 @@
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
- if (dwc3_is_usb31(dwc) &&
- dwc->revision >= DWC3_USB31_REVISION_190A)
+ if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
msleep(20);
else
udelay(1);
} while (--retries);
- phy_exit(dwc->usb3_generic_phy);
- phy_exit(dwc->usb2_generic_phy);
-
return -ETIMEDOUT;
done:
@@ -289,18 +305,12 @@
* is cleared, we must wait at least 50ms before accessing the PHY
* domain (synchronization delay).
*/
- if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
+ if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A))
msleep(50);
return 0;
}
-static const struct clk_bulk_data dwc3_core_clks[] = {
- { .id = "ref" },
- { .id = "bus_early" },
- { .id = "suspend" },
-};
-
/*
* dwc3_frame_length_adjustment - Adjusts frame length if required
* @dwc3: Pointer to our controller context structure
@@ -310,7 +320,7 @@
u32 reg;
u32 dft;
- if (dwc->revision < DWC3_REVISION_250A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return;
if (dwc->fladj == 0)
@@ -572,8 +582,11 @@
*/
static int dwc3_phy_setup(struct dwc3 *dwc)
{
+ unsigned int hw_mode;
u32 reg;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
/*
@@ -588,9 +601,17 @@
* will be '0' when the core is reset. Application needs to set it
* to '1' after the core initialization is completed.
*/
- if (dwc->revision > DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ /*
+ * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be cleared after
+ * power-on reset, and it can be set after core initialization, which is
+ * after device soft-reset during initialization.
+ */
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD)
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+
if (dwc->u2ss_inp3_quirk)
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
@@ -641,9 +662,8 @@
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
break;
}
- /* FALLTHROUGH */
+ fallthrough;
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
- /* FALLTHROUGH */
default:
break;
}
@@ -671,9 +691,17 @@
* be '0' when the core is reset. Application needs to set it to
* '1' after the core initialization is completed.
*/
- if (dwc->revision > DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ /*
+ * For DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared after
+ * power-on reset, and it can be set after core initialization, which is
+ * after device soft-reset during initialization.
+ */
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+
if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@@ -712,15 +740,13 @@
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+ dwc->ip = DWC3_GSNPS_ID(reg);
/* This should read as U3 followed by revision number */
- if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
- /* Detected DWC_usb3 IP */
+ if (DWC3_IP_IS(DWC3)) {
dwc->revision = reg;
- } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
- /* Detected DWC_usb31 IP */
+ } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
- dwc->revision |= DWC3_REVISION_IS_DWC31;
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
} else {
return false;
@@ -753,8 +779,7 @@
*/
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
dwc->dr_mode == USB_DR_MODE_OTG) &&
- (dwc->revision >= DWC3_REVISION_210A &&
- dwc->revision <= DWC3_REVISION_250A))
+ DWC3_VER_IS_WITHIN(DWC3, 210A, 250A))
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
else
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
@@ -797,7 +822,7 @@
* and falls back to high-speed mode which causes
* the device to enter a Connect/Disconnect loop
*/
- if (dwc->revision < DWC3_REVISION_190A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 190A))
reg |= DWC3_GCTL_U2RSTECN;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -908,22 +933,18 @@
*/
static int dwc3_core_init(struct dwc3 *dwc)
{
+ unsigned int hw_mode;
u32 reg;
int ret;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+
/*
* Write Linux Version Code to our GUID register so it's easy to figure
* out which kernel version a bug was found.
*/
dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
- /* Handle USB2.0-only core configuration */
- if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
- if (dwc->maximum_speed == USB_SPEED_SUPER)
- dwc->maximum_speed = USB_SPEED_HIGH;
- }
-
ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
@@ -942,9 +963,36 @@
dwc->phys_ready = true;
}
+ usb_phy_init(dwc->usb2_phy);
+ usb_phy_init(dwc->usb3_phy);
+ ret = phy_init(dwc->usb2_generic_phy);
+ if (ret < 0)
+ goto err0a;
+
+ ret = phy_init(dwc->usb3_generic_phy);
+ if (ret < 0) {
+ phy_exit(dwc->usb2_generic_phy);
+ goto err0a;
+ }
+
ret = dwc3_core_soft_reset(dwc);
if (ret)
- goto err0a;
+ goto err1;
+
+ if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
+ !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
+ if (!dwc->dis_u3_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ }
+
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+ }
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
@@ -979,20 +1027,20 @@
* the DWC_usb3 controller. It is NOT available in the
* DWC_usb31 controller.
*/
- if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
+ if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
- if (dwc->revision >= DWC3_REVISION_250A) {
+ if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
/*
* Enable hardware control of sending remote wakeup
* in HS when the device is in the L1 state.
*/
- if (dwc->revision >= DWC3_REVISION_290A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
if (dwc->dis_tx_ipgap_linecheck_quirk)
@@ -1024,7 +1072,7 @@
* Must config both number of packets and max burst settings to enable
* RX and/or TX threshold.
*/
- if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
+ if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) {
u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
u8 rx_maxburst = dwc->rx_max_burst_prd;
u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
@@ -1271,6 +1319,8 @@
"snps,usb3_lpm_capable");
dwc->usb2_lpm_disable = device_property_read_bool(dev,
"snps,usb2-lpm-disable");
+ dwc->usb2_gadget_lpm_disable = device_property_read_bool(dev,
+ "snps,usb2-gadget-lpm-disable");
device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
&rx_thr_num_pkt_prd);
device_property_read_u8(dev, "snps,rx-max-burst-prd",
@@ -1349,15 +1399,16 @@
/* check whether the core supports IMOD */
bool dwc3_has_imod(struct dwc3 *dwc)
{
- return ((dwc3_is_usb3(dwc) &&
- dwc->revision >= DWC3_REVISION_300A) ||
- (dwc3_is_usb31(dwc) &&
- dwc->revision >= DWC3_USB31_REVISION_120A));
+ return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) ||
+ DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) ||
+ DWC3_IP_IS(DWC32);
}
static void dwc3_check_params(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
+ unsigned int hwparam_gen =
+ DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3);
/* Check for proper value of imod_interval */
if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
@@ -1373,7 +1424,7 @@
* affected version.
*/
if (!dwc->imod_interval &&
- (dwc->revision == DWC3_REVISION_300A))
+ DWC3_VER_IS(DWC3, 300A))
dwc->imod_interval = 1;
/* Check the maximum_speed parameter */
@@ -1381,25 +1432,40 @@
case USB_SPEED_LOW:
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
+ break;
case USB_SPEED_SUPER:
+ if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS)
+ dev_warn(dev, "UDC doesn't support Gen 1\n");
+ break;
case USB_SPEED_SUPER_PLUS:
+ if ((DWC3_IP_IS(DWC32) &&
+ hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) ||
+ (!DWC3_IP_IS(DWC32) &&
+ hwparam_gen != DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ dev_warn(dev, "UDC doesn't support SSP\n");
break;
default:
dev_err(dev, "invalid maximum_speed parameter %d\n",
dwc->maximum_speed);
- /* fall through */
+ fallthrough;
case USB_SPEED_UNKNOWN:
- /* default to superspeed */
- dwc->maximum_speed = USB_SPEED_SUPER;
-
- /*
- * default to superspeed plus if we are capable.
- */
- if (dwc3_is_usb31(dwc) &&
- (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
- DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
+ switch (hwparam_gen) {
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
-
+ break;
+ case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
+ if (DWC3_IP_IS(DWC32))
+ dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
+ else
+ dwc->maximum_speed = USB_SPEED_SUPER;
+ break;
+ case DWC3_GHWPARAMS3_SSPHY_IFC_DIS:
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ break;
+ default:
+ dwc->maximum_speed = USB_SPEED_SUPER;
+ break;
+ }
break;
}
}
@@ -1418,11 +1484,6 @@
if (!dwc)
return -ENOMEM;
- dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks),
- GFP_KERNEL);
- if (!dwc->clks)
- return -ENOMEM;
-
dwc->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1453,22 +1514,23 @@
dwc3_get_properties(dwc);
- dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
+ dwc->reset = devm_reset_control_array_get(dev, true, true);
if (IS_ERR(dwc->reset))
return PTR_ERR(dwc->reset);
if (dev->of_node) {
- dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
-
- ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+ ret = devm_clk_bulk_get_all(dev, &dwc->clks);
if (ret == -EPROBE_DEFER)
return ret;
/*
* Clocks are optional, but new DT platforms should support all
* clocks as required by the DT-binding.
*/
- if (ret)
+ if (ret < 0)
dwc->num_clks = 0;
+ else
+ dwc->num_clks = ret;
+
}
ret = reset_control_deassert(dwc->reset);
@@ -1489,6 +1551,7 @@
dwc3_cache_hwparams(dwc);
spin_lock_init(&dwc->lock);
+ mutex_init(&dwc->mutex);
pm_runtime_set_active(dev);
pm_runtime_use_autosuspend(dev);
@@ -1626,6 +1689,8 @@
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_DEVICE:
+ if (pm_runtime_suspended(dwc->dev))
+ break;
spin_lock_irqsave(&dwc->lock, flags);
dwc3_gadget_suspend(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index da296f8..79e1b82 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* core.h - DesignWare USB3 DRD Core Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/bitops.h>
@@ -25,6 +26,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h>
@@ -68,6 +70,7 @@
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
+#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16)
/* DWC3 registers memory space boundries */
#define DWC3_XHCI_REGS_START 0x0
@@ -308,8 +311,8 @@
/* Global TX Fifo Size Register */
#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
-#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
-#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
+#define DWC31_GTXFIFOSIZ_TXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */
+#define DWC3_GTXFIFOSIZ_TXFDEP(n) ((n) & 0xffff)
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
/* Global RX Fifo Size Register */
@@ -366,6 +369,9 @@
#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
+/* DWC_usb32 only */
+#define DWC3_GHWPARAMS6_MDWIDTH(n) ((n) & (0x3 << 8))
+
/* Global HWPARAMS7 Register */
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
@@ -495,6 +501,7 @@
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
+#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
@@ -633,7 +640,7 @@
struct dwc3_event_buffer {
void *buf;
void *cache;
- unsigned length;
+ unsigned int length;
unsigned int lpos;
unsigned int count;
unsigned int flags;
@@ -693,7 +700,7 @@
struct dwc3 *dwc;
u32 saved_state;
- unsigned flags;
+ unsigned int flags;
#define DWC3_EP_ENABLED BIT(0)
#define DWC3_EP_STALL BIT(1)
#define DWC3_EP_WEDGE BIT(2)
@@ -701,6 +708,10 @@
#define DWC3_EP_END_TRANSFER_PENDING BIT(4)
#define DWC3_EP_PENDING_REQUEST BIT(5)
#define DWC3_EP_DELAY_START BIT(6)
+#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
+#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
+#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
+#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
/* This last one is specific to EP0 */
@@ -889,9 +900,9 @@
struct scatterlist *sg;
struct scatterlist *start_sg;
- unsigned num_pending_sgs;
+ unsigned int num_pending_sgs;
unsigned int num_queued_sgs;
- unsigned remaining;
+ unsigned int remaining;
unsigned int status;
#define DWC3_REQUEST_STATUS_QUEUED 0
@@ -904,11 +915,11 @@
struct dwc3_trb *trb;
dma_addr_t trb_dma;
- unsigned num_trbs;
+ unsigned int num_trbs;
- unsigned needs_extra_trb:1;
- unsigned direction:1;
- unsigned mapped:1;
+ unsigned int needs_extra_trb:1;
+ unsigned int direction:1;
+ unsigned int mapped:1;
};
/*
@@ -932,6 +943,7 @@
* @scratch_addr: dma address of scratchbuf
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
+ * @mutex: for mode switching
* @dev: pointer to our struct device
* @sysdev: pointer to the DMA-capable device
* @xhci: pointer to our xHCI child
@@ -954,7 +966,8 @@
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
- * @revision: revision register contents
+ * @ip: controller's ID
+ * @revision: controller's version of an IP
* @version_type: VERSIONTYPE register contents, a sub release of a revision
* @dr_mode: requested mode of operation
* @current_dr_role: current role of operation when in dual-role mode
@@ -964,6 +977,9 @@
* @hsphy_mode: UTMI phy mode, one of following:
* - USBPHY_INTERFACE_MODE_UTMI
* - USBPHY_INTERFACE_MODE_UTMIW
+ * @role_sw: usb_role_switch handle
+ * @role_switch_default_mode: default operation mode of controller while
+ * usb role is USB_ROLE_NONE.
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -1002,8 +1018,8 @@
* @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
* there's now way for software to detect this in runtime.
* @is_utmi_l1_suspend: the core asserts output signal
- * 0 - utmi_sleep_n
- * 1 - utmi_l1_suspend_n
+ * 0 - utmi_sleep_n
+ * 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board
* @pending_events: true when we have pending IRQs to be handled
* @pullups_connected: true when Run/Stop bit is set
@@ -1012,7 +1028,8 @@
* @dis_start_transfer_quirk: set if start_transfer failure SW workaround is
* not needed for DWC_usb31 version 1.70a-ea06 and below
* @usb3_lpm_capable: set if hadrware supports Link Power Management
- * @usb2_lpm_disable: set to disable usb2 lpm
+ * @usb2_lpm_disable: set to disable usb2 lpm for host
+ * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget
* @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
@@ -1039,14 +1056,14 @@
* instances in park mode.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
- * 0 - -6dB de-emphasis
- * 1 - -3.5dB de-emphasis
- * 2 - No de-emphasis
- * 3 - Reserved
+ * 0 - -6dB de-emphasis
+ * 1 - -3.5dB de-emphasis
+ * 2 - No de-emphasis
+ * 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
* @dis_split_quirk: set to disable split boundary.
* @imod_interval: set the interrupt moderation interval in 250ns
- * increments or 0 to disable.
+ * increments or 0 to disable.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1063,6 +1080,9 @@
/* device lock */
spinlock_t lock;
+ /* mode switching lock */
+ struct mutex mutex;
+
struct device *dev;
struct device *sysdev;
@@ -1072,7 +1092,7 @@
struct dwc3_event_buffer *ev_buf;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
- struct usb_gadget gadget;
+ struct usb_gadget *gadget;
struct usb_gadget_driver *gadget_driver;
struct clk_bulk_data *clks;
@@ -1100,6 +1120,8 @@
struct extcon_dev *edev;
struct notifier_block edev_nb;
enum usb_phy_interface hsphy_mode;
+ struct usb_role_switch *role_sw;
+ enum usb_dr_mode role_switch_default_mode;
u32 fladj;
u32 irq_gadget;
@@ -1111,15 +1133,15 @@
u32 u1u2;
u32 maximum_speed;
- /*
- * All 3.1 IP version constants are greater than the 3.0 IP
- * version constants. This works for most version checks in
- * dwc3. However, in the future, this may not apply as
- * features may be developed on newer versions of the 3.0 IP
- * that are not in the 3.1 IP.
- */
+ u32 ip;
+
+#define DWC3_IP 0x5533
+#define DWC31_IP 0x3331
+#define DWC32_IP 0x3332
+
u32 revision;
+#define DWC3_REVISION_ANY 0x0
#define DWC3_REVISION_173A 0x5533173a
#define DWC3_REVISION_175A 0x5533175a
#define DWC3_REVISION_180A 0x5533180a
@@ -1144,20 +1166,20 @@
#define DWC3_REVISION_310A 0x5533310a
#define DWC3_REVISION_330A 0x5533330a
-/*
- * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
- * just so dwc31 revisions are always larger than dwc3.
- */
-#define DWC3_REVISION_IS_DWC31 0x80000000
-#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31)
+#define DWC31_REVISION_ANY 0x0
+#define DWC31_REVISION_110A 0x3131302a
+#define DWC31_REVISION_120A 0x3132302a
+#define DWC31_REVISION_160A 0x3136302a
+#define DWC31_REVISION_170A 0x3137302a
+#define DWC31_REVISION_180A 0x3138302a
+#define DWC31_REVISION_190A 0x3139302a
+
+#define DWC32_REVISION_ANY 0x0
+#define DWC32_REVISION_100A 0x3130302a
u32 version_type;
+#define DWC31_VERSIONTYPE_ANY 0x0
#define DWC31_VERSIONTYPE_EA01 0x65613031
#define DWC31_VERSIONTYPE_EA02 0x65613032
#define DWC31_VERSIONTYPE_EA03 0x65613033
@@ -1211,6 +1233,7 @@
unsigned dis_start_transfer_quirk:1;
unsigned usb3_lpm_capable:1;
unsigned usb2_lpm_disable:1;
+ unsigned usb2_gadget_lpm_disable:1;
unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1;
@@ -1262,7 +1285,7 @@
#define DWC3_DEPEVT_EPCMDCMPLT 0x07
/**
- * struct dwc3_event_depvt - Device Endpoint Events
+ * struct dwc3_event_depevt - Device Endpoint Events
* @one_bit: indicates this is an endpoint event (not used)
* @endpoint_number: number of the endpoint
* @endpoint_event: The event we have:
@@ -1301,6 +1324,10 @@
#define DEPEVT_STREAMEVT_FOUND 1
#define DEPEVT_STREAMEVT_NOTFOUND 2
+/* Stream event parameter */
+#define DEPEVT_STREAM_PRIME 0xfffe
+#define DEPEVT_STREAM_NOSTREAM 0x0
+
/* Control-only Status */
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
@@ -1403,17 +1430,26 @@
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
-/* check whether we are on the DWC_usb3 core */
-static inline bool dwc3_is_usb3(struct dwc3 *dwc)
-{
- return !(dwc->revision & DWC3_REVISION_IS_DWC31);
-}
+#define DWC3_IP_IS(_ip) \
+ (dwc->ip == _ip##_IP)
-/* check whether we are on the DWC_usb31 core */
-static inline bool dwc3_is_usb31(struct dwc3 *dwc)
-{
- return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
-}
+#define DWC3_VER_IS(_ip, _ver) \
+ (DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver)
+
+#define DWC3_VER_IS_PRIOR(_ip, _ver) \
+ (DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver)
+
+#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \
+ (DWC3_IP_IS(_ip) && \
+ dwc->revision >= _ip##_REVISION_##_from && \
+ (!(_ip##_REVISION_##_to) || \
+ dwc->revision <= _ip##_REVISION_##_to))
+
+#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \
+ (DWC3_VER_IS(_ip, _ver) && \
+ dwc->version_type >= _ip##_VERSIONTYPE_##_from && \
+ (!(_ip##_VERSIONTYPE_##_to) || \
+ dwc->version_type <= _ip##_VERSIONTYPE_##_to))
bool dwc3_has_imod(struct dwc3 *dwc);
@@ -1436,9 +1472,10 @@
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
-int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params);
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
+ u32 param);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
@@ -1452,7 +1489,7 @@
enum dwc3_link_state state)
{ return 0; }
-static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params)
{ return 0; }
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index e105ddf..74d9c2c 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/**
* debug.h - DesignWare USB3 DRD Controller Debug Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -68,6 +68,8 @@
return "All FIFO Flush";
case DWC3_DGCMD_SET_ENDPOINT_NRDY:
return "Set Endpoint NRDY";
+ case DWC3_DGCMD_SET_ENDPOINT_PRIME:
+ return "Set Endpoint Prime";
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
return "Run SoC Bus Loopback Test";
default:
@@ -112,7 +114,7 @@
case DWC3_LINK_STATE_RESUME:
return "Resume";
default:
- return "UNKNOWN link state\n";
+ return "UNKNOWN link state";
}
}
@@ -141,7 +143,7 @@
case DWC3_LINK_STATE_RESUME:
return "Resume";
default:
- return "UNKNOWN link state\n";
+ return "UNKNOWN link state";
}
}
@@ -369,7 +371,9 @@
static inline const char *dwc3_decode_event(char *str, size_t size, u32 event,
u32 ep0state)
{
- const union dwc3_event evt = (union dwc3_event) event;
+ union dwc3_event evt;
+
+ memcpy(&evt, &event, sizeof(event));
if (evt.type.is_devspec)
return dwc3_gadget_event_string(str, size, &evt.devt);
@@ -410,8 +414,8 @@
#ifdef CONFIG_DEBUG_FS
extern void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep);
-extern void dwc3_debugfs_init(struct dwc3 *);
-extern void dwc3_debugfs_exit(struct dwc3 *);
+extern void dwc3_debugfs_init(struct dwc3 *d);
+extern void dwc3_debugfs_exit(struct dwc3 *d);
#else
static inline void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep)
{ }
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 9fb519b..3ebe3e6 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -2,7 +2,7 @@
/**
* debugfs.c - DesignWare USB3 DRD Controller DebugFS file
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -397,13 +397,13 @@
switch (DWC3_GCTL_PRTCAP(reg)) {
case DWC3_GCTL_PRTCAP_HOST:
- seq_printf(s, "host\n");
+ seq_puts(s, "host\n");
break;
case DWC3_GCTL_PRTCAP_DEVICE:
- seq_printf(s, "device\n");
+ seq_puts(s, "device\n");
break;
case DWC3_GCTL_PRTCAP_OTG:
- seq_printf(s, "otg\n");
+ seq_puts(s, "otg\n");
break;
default:
seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg));
@@ -428,6 +428,9 @@
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return count;
+
if (!strncmp(buf, "host", 4))
mode = DWC3_GCTL_PRTCAP_HOST;
@@ -464,22 +467,22 @@
switch (reg) {
case 0:
- seq_printf(s, "no test\n");
+ seq_puts(s, "no test\n");
break;
- case TEST_J:
- seq_printf(s, "test_j\n");
+ case USB_TEST_J:
+ seq_puts(s, "test_j\n");
break;
- case TEST_K:
- seq_printf(s, "test_k\n");
+ case USB_TEST_K:
+ seq_puts(s, "test_k\n");
break;
- case TEST_SE0_NAK:
- seq_printf(s, "test_se0_nak\n");
+ case USB_TEST_SE0_NAK:
+ seq_puts(s, "test_se0_nak\n");
break;
- case TEST_PACKET:
- seq_printf(s, "test_packet\n");
+ case USB_TEST_PACKET:
+ seq_puts(s, "test_packet\n");
break;
- case TEST_FORCE_EN:
- seq_printf(s, "test_force_enable\n");
+ case USB_TEST_FORCE_ENABLE:
+ seq_puts(s, "test_force_enable\n");
break;
default:
seq_printf(s, "UNKNOWN %d\n", reg);
@@ -506,15 +509,15 @@
return -EFAULT;
if (!strncmp(buf, "test_j", 6))
- testmode = TEST_J;
+ testmode = USB_TEST_J;
else if (!strncmp(buf, "test_k", 6))
- testmode = TEST_K;
+ testmode = USB_TEST_K;
else if (!strncmp(buf, "test_se0_nak", 12))
- testmode = TEST_SE0_NAK;
+ testmode = USB_TEST_SE0_NAK;
else if (!strncmp(buf, "test_packet", 11))
- testmode = TEST_PACKET;
+ testmode = USB_TEST_PACKET;
else if (!strncmp(buf, "test_force_enable", 17))
- testmode = TEST_FORCE_EN;
+ testmode = USB_TEST_FORCE_ENABLE;
else
testmode = 0;
@@ -635,13 +638,18 @@
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
+ int mdwidth;
u32 val;
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
/* Convert to bytes */
- val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+ val *= mdwidth;
val >>= 3;
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -654,13 +662,18 @@
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
+ int mdwidth;
u32 val;
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
/* Convert to bytes */
- val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+ val *= mdwidth;
val >>= 3;
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -750,27 +763,26 @@
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
- if (!(dep->flags & DWC3_EP_ENABLED) ||
- !dep->endpoint.desc) {
- seq_printf(s, "--\n");
+ if (!(dep->flags & DWC3_EP_ENABLED) || !dep->endpoint.desc) {
+ seq_puts(s, "--\n");
goto out;
}
switch (usb_endpoint_type(dep->endpoint.desc)) {
case USB_ENDPOINT_XFER_CONTROL:
- seq_printf(s, "control\n");
+ seq_puts(s, "control\n");
break;
case USB_ENDPOINT_XFER_ISOC:
- seq_printf(s, "isochronous\n");
+ seq_puts(s, "isochronous\n");
break;
case USB_ENDPOINT_XFER_BULK:
- seq_printf(s, "bulk\n");
+ seq_puts(s, "bulk\n");
break;
case USB_ENDPOINT_XFER_INT:
- seq_printf(s, "interrupt\n");
+ seq_puts(s, "interrupt\n");
break;
default:
- seq_printf(s, "--\n");
+ seq_puts(s, "--\n");
}
out:
@@ -788,11 +800,11 @@
spin_lock_irqsave(&dwc->lock, flags);
if (dep->number <= 1) {
- seq_printf(s, "--\n");
+ seq_puts(s, "--\n");
goto out;
}
- seq_printf(s, "buffer_addr,size,type,ioc,isp_imi,csp,chn,lst,hwo\n");
+ seq_puts(s, "buffer_addr,size,type,ioc,isp_imi,csp,chn,lst,hwo\n");
for (i = 0; i < DWC3_TRB_NUM; i++) {
struct dwc3_trb *trb = &dep->trb_pool[i];
@@ -874,7 +886,7 @@
const struct file_operations *fops = dwc3_ep_file_map[i].fops;
const char *name = dwc3_ep_file_map[i].name;
- debugfs_create_file(name, S_IRUGO, parent, dep, fops);
+ debugfs_create_file(name, 0444, parent, dep, fops);
}
}
@@ -900,24 +912,21 @@
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
- root = debugfs_create_dir(dev_name(dwc->dev), NULL);
+ root = debugfs_create_dir(dev_name(dwc->dev), usb_debug_root);
dwc->root = root;
- debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
+ debugfs_create_regset32("regdump", 0444, root, dwc->regset);
+ debugfs_create_file("lsp_dump", 0644, root, dwc, &dwc3_lsp_fops);
- debugfs_create_file("lsp_dump", S_IRUGO | S_IWUSR, root, dwc,
- &dwc3_lsp_fops);
-
- if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
- debugfs_create_file("mode", S_IRUGO | S_IWUSR, root, dwc,
+ if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE))
+ debugfs_create_file("mode", 0644, root, dwc,
&dwc3_mode_fops);
- }
if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) ||
IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
- debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, dwc,
- &dwc3_testmode_fops);
- debugfs_create_file("link_state", S_IRUGO | S_IWUSR, root, dwc,
+ debugfs_create_file("testmode", 0644, root, dwc,
+ &dwc3_testmode_fops);
+ debugfs_create_file("link_state", 0644, root, dwc,
&dwc3_link_state_fops);
}
}
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index c946d64..3e1c1aa 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* drd.c - DesignWare USB3 DRD Controller Dual-role support
*
- * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Roger Quadros <rogerq@ti.com>
*/
@@ -56,7 +56,7 @@
spin_lock(&dwc->lock);
if (dwc->otg_restart_host) {
dwc3_otg_host_init(dwc);
- dwc->otg_restart_host = 0;
+ dwc->otg_restart_host = false;
}
spin_unlock(&dwc->lock);
@@ -82,7 +82,7 @@
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
!(reg & DWC3_OEVT_DEVICEMODE))
- dwc->otg_restart_host = 1;
+ dwc->otg_restart_host = true;
dwc3_writel(dwc->regs, DWC3_OEVT, reg);
ret = IRQ_WAKE_THREAD;
}
@@ -476,6 +476,94 @@
return edev;
}
+#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
+#define ROLE_SWITCH 1
+static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
+ enum usb_role role)
+{
+ struct dwc3 *dwc = usb_role_switch_get_drvdata(sw);
+ u32 mode;
+
+ switch (role) {
+ case USB_ROLE_HOST:
+ mode = DWC3_GCTL_PRTCAP_HOST;
+ break;
+ case USB_ROLE_DEVICE:
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ break;
+ default:
+ if (dwc->role_switch_default_mode == USB_DR_MODE_HOST)
+ mode = DWC3_GCTL_PRTCAP_HOST;
+ else
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ break;
+ }
+
+ dwc3_set_mode(dwc, mode);
+ return 0;
+}
+
+static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
+{
+ struct dwc3 *dwc = usb_role_switch_get_drvdata(sw);
+ unsigned long flags;
+ enum usb_role role;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_HOST:
+ role = USB_ROLE_HOST;
+ break;
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ role = USB_ROLE_DEVICE;
+ break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ role = dwc->current_otg_role;
+ break;
+ default:
+ if (dwc->role_switch_default_mode == USB_DR_MODE_HOST)
+ role = USB_ROLE_HOST;
+ else
+ role = USB_ROLE_DEVICE;
+ break;
+ }
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return role;
+}
+
+static int dwc3_setup_role_switch(struct dwc3 *dwc)
+{
+ struct usb_role_switch_desc dwc3_role_switch = {NULL};
+ const char *str;
+ u32 mode;
+ int ret;
+
+ ret = device_property_read_string(dwc->dev, "role-switch-default-mode",
+ &str);
+ if (ret >= 0 && !strncmp(str, "host", strlen("host"))) {
+ dwc->role_switch_default_mode = USB_DR_MODE_HOST;
+ mode = DWC3_GCTL_PRTCAP_HOST;
+ } else {
+ dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ }
+
+ dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
+ dwc3_role_switch.set = dwc3_usb_role_switch_set;
+ dwc3_role_switch.get = dwc3_usb_role_switch_get;
+ dwc3_role_switch.driver_data = dwc;
+ dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch);
+ if (IS_ERR(dwc->role_sw))
+ return PTR_ERR(dwc->role_sw);
+
+ dwc3_set_mode(dwc, mode);
+ return 0;
+}
+#else
+#define ROLE_SWITCH 0
+#define dwc3_setup_role_switch(x) 0
+#endif
+
int dwc3_drd_init(struct dwc3 *dwc)
{
int ret, irq;
@@ -484,7 +572,12 @@
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
- if (dwc->edev) {
+ if (ROLE_SWITCH &&
+ device_property_read_bool(dwc->dev, "usb-role-switch")) {
+ ret = dwc3_setup_role_switch(dwc);
+ if (ret < 0)
+ return ret;
+ } else if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
@@ -531,6 +624,9 @@
{
unsigned long flags;
+ if (dwc->role_sw)
+ usb_role_switch_unregister(dwc->role_sw);
+
if (dwc->edev)
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
@@ -557,6 +653,6 @@
break;
}
- if (!dwc->edev)
+ if (dwc->otg_irq)
free_irq(dwc->otg_irq, dwc);
}
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index c1e9ea6..90bb022 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/**
- * dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer
+ * dwc3-exynos.c - Samsung Exynos DWC3 Specific Glue layer
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com
@@ -255,4 +255,4 @@
MODULE_AUTHOR("Anton Tikhomirov <av.tikhomirov@samsung.com>");
MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("DesignWare USB3 EXYNOS Glue Layer");
+MODULE_DESCRIPTION("DesignWare USB3 Exynos Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-haps.c b/drivers/usb/dwc3/dwc3-haps.c
index 3cecbf1..55b4a90 100644
--- a/drivers/usb/dwc3/dwc3-haps.c
+++ b/drivers/usb/dwc3/dwc3-haps.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* dwc3-haps.c - Synopsys HAPS PCI Specific glue layer
*
* Copyright (C) 2018 Synopsys, Inc.
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
index 1e14a6f..9a99253 100644
--- a/drivers/usb/dwc3/dwc3-keystone.c
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -2,7 +2,7 @@
/**
* dwc3-keystone.c - Keystone Specific Glue layer
*
- * Copyright (C) 2010-2013 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2013 Texas Instruments Incorporated - https://www.ti.com
*
* Author: WingMan Kwok <w-kwok2@ti.com>
*/
@@ -14,6 +14,7 @@
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
/* USBSS register offsets */
@@ -34,6 +35,7 @@
struct dwc3_keystone {
struct device *dev;
void __iomem *usbss;
+ struct phy *usb3_phy;
};
static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
@@ -95,8 +97,38 @@
if (IS_ERR(kdwc->usbss))
return PTR_ERR(kdwc->usbss);
- pm_runtime_enable(kdwc->dev);
+ /* PSC dependency on AM65 needs SERDES0 to be powered before USB0 */
+ kdwc->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
+ if (IS_ERR(kdwc->usb3_phy)) {
+ error = PTR_ERR(kdwc->usb3_phy);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "couldn't get usb3 phy: %d\n", error);
+ return error;
+ }
+
+ phy_pm_runtime_get_sync(kdwc->usb3_phy);
+
+ error = phy_reset(kdwc->usb3_phy);
+ if (error < 0) {
+ dev_err(dev, "usb3 phy reset failed: %d\n", error);
+ return error;
+ }
+
+ error = phy_init(kdwc->usb3_phy);
+ if (error < 0) {
+ dev_err(dev, "usb3 phy init failed: %d\n", error);
+ return error;
+ }
+
+ error = phy_power_on(kdwc->usb3_phy);
+ if (error < 0) {
+ dev_err(dev, "usb3 phy power on failed: %d\n", error);
+ phy_exit(kdwc->usb3_phy);
+ return error;
+ }
+
+ pm_runtime_enable(kdwc->dev);
error = pm_runtime_get_sync(kdwc->dev);
if (error < 0) {
dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n",
@@ -138,6 +170,9 @@
err_irq:
pm_runtime_put_sync(kdwc->dev);
pm_runtime_disable(kdwc->dev);
+ phy_power_off(kdwc->usb3_phy);
+ phy_exit(kdwc->usb3_phy);
+ phy_pm_runtime_put_sync(kdwc->usb3_phy);
return error;
}
@@ -163,6 +198,10 @@
pm_runtime_put_sync(kdwc->dev);
pm_runtime_disable(kdwc->dev);
+ phy_power_off(kdwc->usb3_phy);
+ phy_exit(kdwc->usb3_phy);
+ phy_pm_runtime_put_sync(kdwc->usb3_phy);
+
platform_set_drvdata(pdev, NULL);
return 0;
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
index 8a3ec1a..d0f9b7c 100644
--- a/drivers/usb/dwc3/dwc3-meson-g12a.c
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -30,7 +30,7 @@
#include <linux/usb/role.h>
#include <linux/regulator/consumer.h>
-/* USB2 Ports Control Registers */
+/* USB2 Ports Control Registers, offsets are per-port */
#define U2P_REG_SIZE 0x20
@@ -50,14 +50,16 @@
/* USB Glue Control Registers */
-#define USB_R0 0x80
+#define G12A_GLUE_OFFSET 0x80
+
+#define USB_R0 0x00
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
#define USB_R0_U2D_ACT BIT(31)
-#define USB_R1 0x84
+#define USB_R1 0x04
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
#define USB_R1_U3H_PME_ENABLE BIT(1)
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2)
@@ -69,23 +71,23 @@
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
-#define USB_R2 0x88
+#define USB_R2 0x08
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
-#define USB_R3 0x8c
+#define USB_R3 0x0c
#define USB_R3_P30_SSC_ENABLE BIT(0)
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
#define USB_R3_P30_REF_SSP_EN BIT(13)
-#define USB_R4 0x90
+#define USB_R4 0x10
#define USB_R4_P21_PORT_RESET_0 BIT(0)
#define USB_R4_P21_SLEEP_M0 BIT(1)
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
#define USB_R4_P21_ONLY BIT(4)
-#define USB_R5 0x94
+#define USB_R5 0x14
#define USB_R5_ID_DIG_SYNC BIT(0)
#define USB_R5_ID_DIG_REG BIT(1)
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
@@ -96,21 +98,165 @@
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
-enum {
- USB2_HOST_PHY = 0,
- USB2_OTG_PHY,
- USB3_HOST_PHY,
- PHY_COUNT,
+#define PHY_COUNT 3
+#define USB2_OTG_PHY 1
+
+static struct clk_bulk_data meson_gxl_clocks[] = {
+ { .id = "usb_ctrl" },
+ { .id = "ddr" },
};
-static const char *phy_names[PHY_COUNT] = {
+static struct clk_bulk_data meson_g12a_clocks[] = {
+ { .id = NULL },
+};
+
+static struct clk_bulk_data meson_a1_clocks[] = {
+ { .id = "usb_ctrl" },
+ { .id = "usb_bus" },
+ { .id = "xtal_usb_ctrl" },
+};
+
+static const char * const meson_gxm_phy_names[] = {
+ "usb2-phy0", "usb2-phy1", "usb2-phy2",
+};
+
+static const char * const meson_g12a_phy_names[] = {
"usb2-phy0", "usb2-phy1", "usb3-phy0",
};
+/*
+ * Amlogic A1 has a single physical PHY, in slot 1, but still has the
+ * two U2 PHY controls register blocks like G12A.
+ * AXG has the similar scheme, thus needs the same tweak.
+ * Handling the first PHY on slot 1 would need a large amount of code
+ * changes, and the current management is generic enough to handle it
+ * correctly when only the "usb2-phy1" phy is specified on-par with the
+ * DT bindings.
+ */
+static const char * const meson_a1_phy_names[] = {
+ "usb2-phy0", "usb2-phy1"
+};
+
+struct dwc3_meson_g12a;
+
+struct dwc3_meson_g12a_drvdata {
+ bool otg_switch_supported;
+ bool otg_phy_host_port_disable;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ const char * const *phy_names;
+ int num_phys;
+ int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
+ int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+ int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+ int (*usb_init)(struct dwc3_meson_g12a *priv);
+ int (*usb_post_init)(struct dwc3_meson_g12a *priv);
+};
+
+static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base);
+static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base);
+
+static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode);
+
+static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode);
+static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode);
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv);
+static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv);
+
+static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
+
+/*
+ * For GXL and GXM SoCs:
+ * USB Phy muxing between the DWC2 Device controller and the DWC3 Host
+ * controller is buggy when switching from Device to Host when USB port
+ * is unpopulated, it causes the DWC3 to hard crash.
+ * When populated (including OTG switching with ID pin), the switch works
+ * like a charm like on the G12A platforms.
+ * In order to still switch from Host to Device on an USB Type-A port,
+ * an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host
+ * controller from the port, but when used the DWC3 controller must be
+ * reset to recover usage of the port.
+ */
+
+static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
+ .otg_switch_supported = true,
+ .otg_phy_host_port_disable = true,
+ .clks = meson_gxl_clocks,
+ .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_a1_phy_names,
+ .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+ .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+ .usb_init = dwc3_meson_gxl_usb_init,
+ .usb_post_init = dwc3_meson_gxl_usb_post_init,
+};
+
+static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
+ .otg_switch_supported = true,
+ .otg_phy_host_port_disable = true,
+ .clks = meson_gxl_clocks,
+ .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_gxm_phy_names,
+ .num_phys = ARRAY_SIZE(meson_gxm_phy_names),
+ .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+ .usb_init = dwc3_meson_gxl_usb_init,
+ .usb_post_init = dwc3_meson_gxl_usb_post_init,
+};
+
+static struct dwc3_meson_g12a_drvdata axg_drvdata = {
+ .otg_switch_supported = true,
+ .clks = meson_gxl_clocks,
+ .num_clks = ARRAY_SIZE(meson_gxl_clocks),
+ .phy_names = meson_a1_phy_names,
+ .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+ .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+ .usb_init = dwc3_meson_g12a_usb_init,
+ .usb_post_init = dwc3_meson_gxl_usb_post_init,
+};
+
+static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
+ .otg_switch_supported = true,
+ .clks = meson_g12a_clocks,
+ .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+ .phy_names = meson_g12a_phy_names,
+ .num_phys = ARRAY_SIZE(meson_g12a_phy_names),
+ .setup_regmaps = dwc3_meson_g12a_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_g12a_set_phy_mode,
+ .usb_init = dwc3_meson_g12a_usb_init,
+};
+
+static struct dwc3_meson_g12a_drvdata a1_drvdata = {
+ .otg_switch_supported = false,
+ .clks = meson_a1_clocks,
+ .num_clks = ARRAY_SIZE(meson_a1_clocks),
+ .phy_names = meson_a1_phy_names,
+ .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+ .setup_regmaps = dwc3_meson_g12a_setup_regmaps,
+ .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
+ .set_phy_mode = dwc3_meson_g12a_set_phy_mode,
+ .usb_init = dwc3_meson_g12a_usb_init,
+};
+
struct dwc3_meson_g12a {
struct device *dev;
- struct regmap *regmap;
- struct clk *clk;
+ struct regmap *u2p_regmap[PHY_COUNT];
+ struct regmap *usb_glue_regmap;
struct reset_control *reset;
struct phy *phys[PHY_COUNT];
enum usb_dr_mode otg_mode;
@@ -120,51 +266,81 @@
struct regulator *vbus;
struct usb_role_switch_desc switch_desc;
struct usb_role_switch *role_switch;
+ const struct dwc3_meson_g12a_drvdata *drvdata;
};
-static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
- int i, enum phy_mode mode)
+static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode)
+{
+ return phy_set_mode(priv->phys[i], mode);
+}
+
+static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode)
+{
+ /* On GXL PHY must be started in device mode for DWC2 init */
+ return priv->drvdata->set_phy_mode(priv, i,
+ (i == USB2_OTG_PHY) ? PHY_MODE_USB_DEVICE
+ : PHY_MODE_USB_HOST);
+}
+
+static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
+ int i, enum phy_mode mode)
{
if (mode == PHY_MODE_USB_HOST)
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_HOST_DEVICE,
U2P_R0_HOST_DEVICE);
else
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_HOST_DEVICE, 0);
+
+ return 0;
}
-static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
+static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+ enum phy_mode mode)
{
- int i;
+ int ret;
- if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
- priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
- else
- priv->otg_phy_mode = PHY_MODE_USB_HOST;
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_POWER_ON_RESET,
+ U2P_R0_POWER_ON_RESET);
- for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
+ if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
+ U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+
+ ret = priv->drvdata->set_phy_mode(priv, i, mode);
+ } else
+ ret = priv->drvdata->set_phy_mode(priv, i,
+ PHY_MODE_USB_HOST);
+
+ if (ret)
+ return ret;
+
+ regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+ U2P_R0_POWER_ON_RESET, 0);
+
+ return 0;
+}
+
+static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
+{
+ int i, ret;
+
+ for (i = 0; i < priv->drvdata->num_phys; ++i) {
if (!priv->phys[i])
continue;
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_POWER_ON_RESET,
- U2P_R0_POWER_ON_RESET);
+ if (!strstr(priv->drvdata->phy_names[i], "usb2"))
+ continue;
- if (i == USB2_OTG_PHY) {
- regmap_update_bits(priv->regmap,
- U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
- U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
-
- dwc3_meson_g12a_usb2_set_mode(priv, i,
- priv->otg_phy_mode);
- } else
- dwc3_meson_g12a_usb2_set_mode(priv, i,
- PHY_MODE_USB_HOST);
-
- regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
- U2P_R0_POWER_ON_RESET, 0);
+ ret = priv->drvdata->usb2_init_phy(priv, i, mode);
+ if (ret)
+ return ret;
}
return 0;
@@ -172,7 +348,7 @@
static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
{
- regmap_update_bits(priv->regmap, USB_R3,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R3,
USB_R3_P30_SSC_RANGE_MASK |
USB_R3_P30_REF_SSP_EN,
USB_R3_P30_SSC_ENABLE |
@@ -180,61 +356,77 @@
USB_R3_P30_REF_SSP_EN);
udelay(2);
- regmap_update_bits(priv->regmap, USB_R2,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R2,
USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
- regmap_update_bits(priv->regmap, USB_R2,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R2,
USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
udelay(2);
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_P30_PCS_TX_SWING_FULL_MASK,
FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
}
-static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
+static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
{
- if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
- regmap_update_bits(priv->regmap, USB_R0,
+ if (mode == PHY_MODE_USB_DEVICE) {
+ if (priv->otg_mode != USB_DR_MODE_OTG &&
+ priv->drvdata->otg_phy_host_port_disable)
+ /* Isolate the OTG PHY port from the Host Controller */
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
+ USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
+ FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
+ BIT(USB2_OTG_PHY)));
+
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_ACT, USB_R0_U2D_ACT);
- regmap_update_bits(priv->regmap, USB_R0,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
- regmap_update_bits(priv->regmap, USB_R4,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R4,
USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
} else {
- regmap_update_bits(priv->regmap, USB_R0,
+ if (priv->otg_mode != USB_DR_MODE_OTG &&
+ priv->drvdata->otg_phy_host_port_disable) {
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
+ USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0);
+ msleep(500);
+ }
+ regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_ACT, 0);
- regmap_update_bits(priv->regmap, USB_R4,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R4,
USB_R4_P21_SLEEP_M0, 0);
}
}
-static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv,
+ enum phy_mode mode)
{
int ret;
- ret = dwc3_meson_g12a_usb2_init(priv);
+ ret = dwc3_meson_g12a_usb2_init(priv, mode);
if (ret)
return ret;
- regmap_update_bits(priv->regmap, USB_R1,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_EN_0,
USB_R5_ID_DIG_EN_0);
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_EN_1,
USB_R5_ID_DIG_EN_1);
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_TH_MASK,
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
@@ -242,12 +434,13 @@
if (priv->usb3_ports)
dwc3_meson_g12a_usb3_init(priv);
- dwc3_meson_g12a_usb_otg_apply_mode(priv);
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
return 0;
}
-static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
+static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = {
+ .name = "usb-glue",
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
@@ -256,17 +449,19 @@
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
{
+ const char *phy_name;
int i;
- for (i = 0 ; i < PHY_COUNT ; ++i) {
- priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
+ for (i = 0 ; i < priv->drvdata->num_phys ; ++i) {
+ phy_name = priv->drvdata->phy_names[i];
+ priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name);
if (!priv->phys[i])
continue;
if (IS_ERR(priv->phys[i]))
return PTR_ERR(priv->phys[i]);
- if (i == USB3_HOST_PHY)
+ if (strstr(phy_name, "usb3"))
priv->usb3_ports++;
else
priv->usb2_ports++;
@@ -282,7 +477,7 @@
{
u32 reg;
- regmap_read(priv->regmap, USB_R5, ®);
+ regmap_read(priv->usb_glue_regmap, USB_R5, ®);
if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
return PHY_MODE_USB_DEVICE;
@@ -295,7 +490,7 @@
{
int ret;
- if (!priv->phys[USB2_OTG_PHY])
+ if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY])
return -EINVAL;
if (mode == PHY_MODE_USB_HOST)
@@ -314,16 +509,19 @@
priv->otg_phy_mode = mode;
- dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
+ ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode);
+ if (ret)
+ return ret;
- dwc3_meson_g12a_usb_otg_apply_mode(priv);
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
return 0;
}
-static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role)
+static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
+ enum usb_role role)
{
- struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
+ struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw);
enum phy_mode mode;
if (role == USB_ROLE_NONE)
@@ -335,12 +533,15 @@
if (mode == priv->otg_phy_mode)
return 0;
+ if (priv->drvdata->otg_phy_host_port_disable)
+ dev_warn_once(priv->dev, "Broken manual OTG switch\n");
+
return dwc3_meson_g12a_otg_mode_set(priv, mode);
}
-static enum usb_role dwc3_meson_g12a_role_get(struct device *dev)
+static enum usb_role dwc3_meson_g12a_role_get(struct usb_role_switch *sw)
{
- struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
+ struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw);
return priv->otg_phy_mode == PHY_MODE_USB_HOST ?
USB_ROLE_HOST : USB_ROLE_DEVICE;
@@ -357,7 +558,8 @@
dev_warn(priv->dev, "Failed to switch OTG mode\n");
}
- regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
+ USB_R5_ID_DIG_IRQ, 0);
return IRQ_HANDLED;
}
@@ -380,80 +582,24 @@
return &pdev->dev;
}
-static int dwc3_meson_g12a_probe(struct platform_device *pdev)
+static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
+ struct dwc3_meson_g12a *priv)
{
- struct dwc3_meson_g12a *priv;
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- void __iomem *base;
enum phy_mode otg_id;
- int ret, i, irq;
+ int ret, irq;
+ struct device *dev = &pdev->dev;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- priv->regmap = devm_regmap_init_mmio(dev, base,
- &phy_meson_g12a_usb3_regmap_conf);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
- priv->vbus = devm_regulator_get_optional(dev, "vbus");
- if (IS_ERR(priv->vbus)) {
- if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
- return PTR_ERR(priv->vbus);
- priv->vbus = NULL;
- }
-
- priv->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(priv->clk))
- return PTR_ERR(priv->clk);
-
- ret = clk_prepare_enable(priv->clk);
- if (ret)
- return ret;
-
- devm_add_action_or_reset(dev,
- (void(*)(void *))clk_disable_unprepare,
- priv->clk);
-
- platform_set_drvdata(pdev, priv);
- priv->dev = dev;
-
- priv->reset = devm_reset_control_get(dev, NULL);
- if (IS_ERR(priv->reset)) {
- ret = PTR_ERR(priv->reset);
- dev_err(dev, "failed to get device reset, err=%d\n", ret);
- return ret;
- }
-
- ret = reset_control_reset(priv->reset);
- if (ret)
- return ret;
-
- ret = dwc3_meson_g12a_get_phys(priv);
- if (ret)
- return ret;
-
- if (priv->vbus) {
- ret = regulator_enable(priv->vbus);
- if (ret)
- return ret;
- }
-
- /* Get dr_mode */
- priv->otg_mode = usb_get_dr_mode(dev);
+ if (!priv->drvdata->otg_switch_supported)
+ return 0;
if (priv->otg_mode == USB_DR_MODE_OTG) {
/* Ack irq before registering */
- regmap_update_bits(priv->regmap, USB_R5,
+ regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_IRQ, 0);
irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
dwc3_meson_g12a_irq_thread,
IRQF_ONESHOT, pdev->name, priv);
@@ -461,28 +607,6 @@
return ret;
}
- dwc3_meson_g12a_usb_init(priv);
-
- /* Init PHYs */
- for (i = 0 ; i < PHY_COUNT ; ++i) {
- ret = phy_init(priv->phys[i]);
- if (ret)
- return ret;
- }
-
- /* Set PHY Power */
- for (i = 0 ; i < PHY_COUNT ; ++i) {
- ret = phy_power_on(priv->phys[i]);
- if (ret)
- goto err_phys_exit;
- }
-
- ret = of_platform_populate(np, NULL, NULL, dev);
- if (ret) {
- clk_disable_unprepare(priv->clk);
- goto err_phys_power;
- }
-
/* Setup OTG mode corresponding to the ID pin */
if (priv->otg_mode == USB_DR_MODE_OTG) {
otg_id = dwc3_meson_g12a_get_id(priv);
@@ -499,11 +623,190 @@
priv->switch_desc.allow_userspace_control = true;
priv->switch_desc.set = dwc3_meson_g12a_role_set;
priv->switch_desc.get = dwc3_meson_g12a_role_get;
+ priv->switch_desc.driver_data = priv;
priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
if (IS_ERR(priv->role_switch))
dev_warn(dev, "Unable to register Role Switch\n");
+ return 0;
+}
+
+static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base)
+{
+ /* GXL controls the PHY mode in the PHY registers unlike G12A */
+ priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
+ &phy_meson_g12a_usb_glue_regmap_conf);
+ return PTR_ERR_OR_ZERO(priv->usb_glue_regmap);
+}
+
+static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
+ void __iomem *base)
+{
+ int i;
+
+ priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev,
+ base + G12A_GLUE_OFFSET,
+ &phy_meson_g12a_usb_glue_regmap_conf);
+ if (IS_ERR(priv->usb_glue_regmap))
+ return PTR_ERR(priv->usb_glue_regmap);
+
+ /* Create a regmap for each USB2 PHY control register set */
+ for (i = 0; i < priv->drvdata->num_phys; i++) {
+ struct regmap_config u2p_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = U2P_R1,
+ };
+
+ if (!strstr(priv->drvdata->phy_names[i], "usb2"))
+ continue;
+
+ u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
+ "u2p-%d", i);
+ if (!u2p_regmap_config.name)
+ return -ENOMEM;
+
+ priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev,
+ base + (i * U2P_REG_SIZE),
+ &u2p_regmap_config);
+ if (IS_ERR(priv->u2p_regmap[i]))
+ return PTR_ERR(priv->u2p_regmap[i]);
+ }
+
+ return 0;
+}
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+{
+ return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode);
+}
+
+static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv)
+{
+ return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE);
+}
+
+static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv)
+{
+ int ret;
+
+ ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY,
+ priv->otg_phy_mode);
+ if (ret)
+ return ret;
+
+ dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode);
+
+ return 0;
+}
+
+static int dwc3_meson_g12a_probe(struct platform_device *pdev)
+{
+ struct dwc3_meson_g12a *priv;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ void __iomem *base;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->drvdata = of_device_get_match_data(&pdev->dev);
+ priv->dev = dev;
+
+ priv->vbus = devm_regulator_get_optional(dev, "vbus");
+ if (IS_ERR(priv->vbus)) {
+ if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
+ return PTR_ERR(priv->vbus);
+ priv->vbus = NULL;
+ }
+
+ ret = devm_clk_bulk_get(dev,
+ priv->drvdata->num_clks,
+ priv->drvdata->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(priv->drvdata->num_clks,
+ priv->drvdata->clks);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->reset = devm_reset_control_get_shared(dev, NULL);
+ if (IS_ERR(priv->reset)) {
+ ret = PTR_ERR(priv->reset);
+ dev_err(dev, "failed to get device reset, err=%d\n", ret);
+ goto err_disable_clks;
+ }
+
+ ret = reset_control_reset(priv->reset);
+ if (ret)
+ goto err_disable_clks;
+
+ ret = dwc3_meson_g12a_get_phys(priv);
+ if (ret)
+ goto err_disable_clks;
+
+ ret = priv->drvdata->setup_regmaps(priv, base);
+ if (ret)
+ goto err_disable_clks;
+
+ if (priv->vbus) {
+ ret = regulator_enable(priv->vbus);
+ if (ret)
+ goto err_disable_clks;
+ }
+
+ /* Get dr_mode */
+ priv->otg_mode = usb_get_dr_mode(dev);
+
+ if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
+ priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
+ else
+ priv->otg_phy_mode = PHY_MODE_USB_HOST;
+
+ ret = priv->drvdata->usb_init(priv);
+ if (ret)
+ goto err_disable_regulator;
+
+ /* Init PHYs */
+ for (i = 0 ; i < PHY_COUNT ; ++i) {
+ ret = phy_init(priv->phys[i]);
+ if (ret)
+ goto err_disable_regulator;
+ }
+
+ /* Set PHY Power */
+ for (i = 0 ; i < PHY_COUNT ; ++i) {
+ ret = phy_power_on(priv->phys[i]);
+ if (ret)
+ goto err_phys_exit;
+ }
+
+ if (priv->drvdata->usb_post_init) {
+ ret = priv->drvdata->usb_post_init(priv);
+ if (ret)
+ goto err_phys_power;
+ }
+
+ ret = of_platform_populate(np, NULL, NULL, dev);
+ if (ret)
+ goto err_phys_power;
+
+ ret = dwc3_meson_g12a_otg_init(pdev, priv);
+ if (ret)
+ goto err_phys_power;
+
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
@@ -518,6 +821,14 @@
for (i = 0 ; i < PHY_COUNT ; ++i)
phy_exit(priv->phys[i]);
+err_disable_regulator:
+ if (priv->vbus)
+ regulator_disable(priv->vbus);
+
+err_disable_clks:
+ clk_bulk_disable_unprepare(priv->drvdata->num_clks,
+ priv->drvdata->clks);
+
return ret;
}
@@ -527,7 +838,8 @@
struct device *dev = &pdev->dev;
int i;
- usb_role_switch_unregister(priv->role_switch);
+ if (priv->drvdata->otg_switch_supported)
+ usb_role_switch_unregister(priv->role_switch);
of_platform_depopulate(dev);
@@ -540,6 +852,9 @@
pm_runtime_put_noidle(dev);
pm_runtime_set_suspended(dev);
+ clk_bulk_disable_unprepare(priv->drvdata->num_clks,
+ priv->drvdata->clks);
+
return 0;
}
@@ -547,7 +862,8 @@
{
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
- clk_disable(priv->clk);
+ clk_bulk_disable_unprepare(priv->drvdata->num_clks,
+ priv->drvdata->clks);
return 0;
}
@@ -556,7 +872,8 @@
{
struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
- return clk_enable(priv->clk);
+ return clk_bulk_prepare_enable(priv->drvdata->num_clks,
+ priv->drvdata->clks);
}
static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
@@ -587,7 +904,9 @@
reset_control_deassert(priv->reset);
- dwc3_meson_g12a_usb_init(priv);
+ ret = priv->drvdata->usb_init(priv);
+ if (ret)
+ return ret;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -603,8 +922,8 @@
return ret;
}
- if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
- ret = regulator_enable(priv->vbus);
+ if (priv->vbus && priv->otg_phy_mode == PHY_MODE_USB_HOST) {
+ ret = regulator_enable(priv->vbus);
if (ret)
return ret;
}
@@ -619,7 +938,26 @@
};
static const struct of_device_id dwc3_meson_g12a_match[] = {
- { .compatible = "amlogic,meson-g12a-usb-ctrl" },
+ {
+ .compatible = "amlogic,meson-gxl-usb-ctrl",
+ .data = &gxl_drvdata,
+ },
+ {
+ .compatible = "amlogic,meson-gxm-usb-ctrl",
+ .data = &gxm_drvdata,
+ },
+ {
+ .compatible = "amlogic,meson-axg-usb-ctrl",
+ .data = &axg_drvdata,
+ },
+ {
+ .compatible = "amlogic,meson-g12a-usb-ctrl",
+ .data = &g12a_drvdata,
+ },
+ {
+ .compatible = "amlogic,meson-a1-usb-ctrl",
+ .data = &a1_drvdata,
+ },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index d055e00..e62ecd2 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* dwc3-of-simple.c - OF glue layer for simple integrations
*
- * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com
*
* Author: Felipe Balbi <balbi@ti.com>
*
@@ -27,7 +27,6 @@
struct clk_bulk_data *clks;
int num_clocks;
struct reset_control *resets;
- bool pulse_resets;
bool need_reset;
};
@@ -38,7 +37,6 @@
struct device_node *np = dev->of_node;
int ret;
- bool shared_resets = false;
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
@@ -54,13 +52,7 @@
if (of_device_is_compatible(np, "rockchip,rk3399-dwc3"))
simple->need_reset = true;
- if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
- of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
- shared_resets = true;
- simple->pulse_resets = true;
- }
-
- simple->resets = of_reset_control_array_get(np, shared_resets, true,
+ simple->resets = of_reset_control_array_get(np, false, true,
true);
if (IS_ERR(simple->resets)) {
ret = PTR_ERR(simple->resets);
@@ -68,15 +60,9 @@
return ret;
}
- if (simple->pulse_resets) {
- ret = reset_control_reset(simple->resets);
- if (ret)
- goto err_resetc_put;
- } else {
- ret = reset_control_deassert(simple->resets);
- if (ret)
- goto err_resetc_put;
- }
+ ret = reset_control_deassert(simple->resets);
+ if (ret)
+ goto err_resetc_put;
ret = clk_bulk_get_all(simple->dev, &simple->clks);
if (ret < 0)
@@ -102,37 +88,46 @@
clk_bulk_put_all(simple->num_clocks, simple->clks);
err_resetc_assert:
- if (!simple->pulse_resets)
- reset_control_assert(simple->resets);
+ reset_control_assert(simple->resets);
err_resetc_put:
reset_control_put(simple->resets);
return ret;
}
-static int dwc3_of_simple_remove(struct platform_device *pdev)
+static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple)
{
- struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
- struct device *dev = &pdev->dev;
-
- of_platform_depopulate(dev);
+ of_platform_depopulate(simple->dev);
clk_bulk_disable_unprepare(simple->num_clocks, simple->clks);
clk_bulk_put_all(simple->num_clocks, simple->clks);
simple->num_clocks = 0;
- if (!simple->pulse_resets)
- reset_control_assert(simple->resets);
+ reset_control_assert(simple->resets);
reset_control_put(simple->resets);
- pm_runtime_disable(dev);
- pm_runtime_put_noidle(dev);
- pm_runtime_set_suspended(dev);
+ pm_runtime_disable(simple->dev);
+ pm_runtime_put_noidle(simple->dev);
+ pm_runtime_set_suspended(simple->dev);
+}
+
+static int dwc3_of_simple_remove(struct platform_device *pdev)
+{
+ struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
+
+ __dwc3_of_simple_teardown(simple);
return 0;
}
+static void dwc3_of_simple_shutdown(struct platform_device *pdev)
+{
+ struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
+
+ __dwc3_of_simple_teardown(simple);
+}
+
static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device *dev)
{
struct dwc3_of_simple *simple = dev_get_drvdata(dev);
@@ -180,10 +175,9 @@
{ .compatible = "xlnx,zynqmp-dwc3" },
{ .compatible = "cavium,octeon-7130-usb-uctl" },
{ .compatible = "sprd,sc9860-dwc3" },
- { .compatible = "amlogic,meson-axg-dwc3" },
- { .compatible = "amlogic,meson-gxl-dwc3" },
{ .compatible = "allwinner,sun50i-h6-dwc3" },
{ .compatible = "hisilicon,hi3670-dwc3" },
+ { .compatible = "intel,keembay-dwc3" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
@@ -191,6 +185,7 @@
static struct platform_driver dwc3_of_simple_driver = {
.probe = dwc3_of_simple_probe,
.remove = dwc3_of_simple_remove,
+ .shutdown = dwc3_of_simple_shutdown,
.driver = {
.name = "dwc3-of-simple",
.of_match_table = of_dwc3_simple_match,
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index e8acad4..e196673 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* dwc3-omap.c - OMAP Specific Glue layer
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -462,8 +462,6 @@
int ret;
int irq;
- u32 reg;
-
void __iomem *base;
if (!node) {
@@ -508,9 +506,6 @@
dwc3_omap_map_offset(omap);
dwc3_omap_set_utmi_mode(omap);
- /* check the DMA Status */
- reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
-
ret = dwc3_omap_extcon_register(omap);
if (ret < 0)
goto err1;
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 5a7c152..98df8d5 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -2,7 +2,7 @@
/**
* dwc3-pci.c - PCI Specific glue layer
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -81,8 +81,8 @@
static struct gpiod_lookup_table platform_bytcr_gpios = {
.dev_id = "0000:00:16.0",
.table = {
- GPIO_LOOKUP("INT33FC:00", 54, "reset", GPIO_ACTIVE_HIGH),
- GPIO_LOOKUP("INT33FC:02", 14, "cs", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("INT33FC:00", 54, "cs", GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP("INT33FC:02", 14, "reset", GPIO_ACTIVE_HIGH),
{}
},
};
@@ -118,6 +118,9 @@
static const struct property_entry dwc3_pci_mrfld_properties[] = {
PROPERTY_ENTRY_STRING("dr_mode", "otg"),
PROPERTY_ENTRY_STRING("linux,extcon-name", "mrfld_bcove_pwrsrc"),
+ PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
+ PROPERTY_ENTRY_BOOL("snps,usb2-gadget-lpm-disable"),
PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
{}
};
@@ -138,7 +141,6 @@
PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"),
PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
- PROPERTY_ENTRY_BOOL("snps,usb2-gadget-lpm-disable"),
PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
{}
};
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index 7874b97..504f8af 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -9,10 +9,11 @@
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/irq.h>
-#include <linux/clk-provider.h>
+#include <linux/of_clk.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/extcon.h>
+#include <linux/interconnect.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
@@ -43,6 +44,14 @@
#define SDM845_QSCRATCH_SIZE 0x400
#define SDM845_DWC3_CORE_SIZE 0xcd00
+/* Interconnect path bandwidths in MBps */
+#define USB_MEMORY_AVG_HS_BW MBps_to_icc(240)
+#define USB_MEMORY_PEAK_HS_BW MBps_to_icc(700)
+#define USB_MEMORY_AVG_SS_BW MBps_to_icc(1000)
+#define USB_MEMORY_PEAK_SS_BW MBps_to_icc(2500)
+#define APPS_USB_AVG_BW 0
+#define APPS_USB_PEAK_BW MBps_to_icc(40)
+
struct dwc3_acpi_pdata {
u32 qscratch_base_offset;
u32 qscratch_base_size;
@@ -51,12 +60,14 @@
int dp_hs_phy_irq_index;
int dm_hs_phy_irq_index;
int ss_phy_irq_index;
+ bool is_urs;
};
struct dwc3_qcom {
struct device *dev;
void __iomem *qscratch_base;
struct platform_device *dwc3;
+ struct platform_device *urs_usb;
struct clk **clks;
int num_clocks;
struct reset_control *resets;
@@ -76,6 +87,8 @@
enum usb_dr_mode mode;
bool is_suspended;
bool pm_suspended;
+ struct icc_path *icc_path_ddr;
+ struct icc_path *icc_path_apps;
};
static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val)
@@ -190,6 +203,99 @@
return 0;
}
+static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom)
+{
+ int ret;
+
+ ret = icc_enable(qcom->icc_path_ddr);
+ if (ret)
+ return ret;
+
+ ret = icc_enable(qcom->icc_path_apps);
+ if (ret)
+ icc_disable(qcom->icc_path_ddr);
+
+ return ret;
+}
+
+static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom)
+{
+ int ret;
+
+ ret = icc_disable(qcom->icc_path_ddr);
+ if (ret)
+ return ret;
+
+ ret = icc_disable(qcom->icc_path_apps);
+ if (ret)
+ icc_enable(qcom->icc_path_ddr);
+
+ return ret;
+}
+
+/**
+ * dwc3_qcom_interconnect_init() - Get interconnect path handles
+ * and set bandwidhth.
+ * @qcom: Pointer to the concerned usb core.
+ *
+ */
+static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom)
+{
+ struct device *dev = qcom->dev;
+ int ret;
+
+ if (has_acpi_companion(dev))
+ return 0;
+
+ qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr");
+ if (IS_ERR(qcom->icc_path_ddr)) {
+ dev_err(dev, "failed to get usb-ddr path: %ld\n",
+ PTR_ERR(qcom->icc_path_ddr));
+ return PTR_ERR(qcom->icc_path_ddr);
+ }
+
+ qcom->icc_path_apps = of_icc_get(dev, "apps-usb");
+ if (IS_ERR(qcom->icc_path_apps)) {
+ dev_err(dev, "failed to get apps-usb path: %ld\n",
+ PTR_ERR(qcom->icc_path_apps));
+ return PTR_ERR(qcom->icc_path_apps);
+ }
+
+ if (usb_get_maximum_speed(&qcom->dwc3->dev) >= USB_SPEED_SUPER ||
+ usb_get_maximum_speed(&qcom->dwc3->dev) == USB_SPEED_UNKNOWN)
+ ret = icc_set_bw(qcom->icc_path_ddr,
+ USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW);
+ else
+ ret = icc_set_bw(qcom->icc_path_ddr,
+ USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW);
+
+ if (ret) {
+ dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret);
+ return ret;
+ }
+
+ ret = icc_set_bw(qcom->icc_path_apps,
+ APPS_USB_AVG_BW, APPS_USB_PEAK_BW);
+ if (ret) {
+ dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_qcom_interconnect_exit() - Release interconnect path handles
+ * @qcom: Pointer to the concerned usb core.
+ *
+ * This function is used to release interconnect path handle.
+ */
+static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom)
+{
+ icc_put(qcom->icc_path_ddr);
+ icc_put(qcom->icc_path_apps);
+}
+
static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom)
{
if (qcom->hs_phy_irq) {
@@ -239,7 +345,7 @@
static int dwc3_qcom_suspend(struct dwc3_qcom *qcom)
{
u32 val;
- int i;
+ int i, ret;
if (qcom->is_suspended)
return 0;
@@ -251,6 +357,10 @@
for (i = qcom->num_clocks - 1; i >= 0; i--)
clk_disable_unprepare(qcom->clks[i]);
+ ret = dwc3_qcom_interconnect_disable(qcom);
+ if (ret)
+ dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret);
+
if (device_may_wakeup(qcom->dev))
dwc3_qcom_enable_interrupts(qcom);
@@ -279,6 +389,10 @@
}
}
+ ret = dwc3_qcom_interconnect_enable(qcom);
+ if (ret)
+ dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret);
+
/* Clear existing events from PHY related to L2 in/out */
dwc3_qcom_setbits(qcom->qscratch_base, PWR_EVNT_IRQ_STAT_REG,
PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK);
@@ -323,13 +437,15 @@
static int dwc3_qcom_get_irq(struct platform_device *pdev,
const char *name, int num)
{
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ struct platform_device *pdev_irq = qcom->urs_usb ? qcom->urs_usb : pdev;
struct device_node *np = pdev->dev.of_node;
int ret;
if (np)
- ret = platform_get_irq_byname(pdev, name);
+ ret = platform_get_irq_byname(pdev_irq, name);
else
- ret = platform_get_irq(pdev, num);
+ ret = platform_get_irq(pdev_irq, num);
return ret;
}
@@ -338,7 +454,9 @@
{
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
const struct dwc3_acpi_pdata *pdata = qcom->acpi_pdata;
- int irq, ret;
+ int irq;
+ int ret;
+
irq = dwc3_qcom_get_irq(pdev, "hs_phy_irq",
pdata ? pdata->hs_phy_irq_index : -1);
if (irq > 0) {
@@ -457,9 +575,11 @@
static int dwc3_qcom_acpi_register_core(struct platform_device *pdev)
{
- struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
struct resource *res, *child_res = NULL;
+ struct platform_device *pdev_irq = qcom->urs_usb ? qcom->urs_usb :
+ pdev;
int irq;
int ret;
@@ -489,7 +609,11 @@
child_res[0].end = child_res[0].start +
qcom->acpi_pdata->dwc3_core_base_size;
- irq = platform_get_irq(pdev, 0);
+ irq = platform_get_irq(pdev_irq, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto out;
+ }
child_res[1].flags = IORESOURCE_IRQ;
child_res[1].start = child_res[1].end = irq;
@@ -517,7 +641,7 @@
static int dwc3_qcom_of_register_core(struct platform_device *pdev)
{
- struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
+ struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node, *dwc3_np;
struct device *dev = &pdev->dev;
int ret;
@@ -546,15 +670,32 @@
return ret;
}
-static const struct dwc3_acpi_pdata sdm845_acpi_pdata = {
- .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET,
- .qscratch_base_size = SDM845_QSCRATCH_SIZE,
- .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE,
- .hs_phy_irq_index = 1,
- .dp_hs_phy_irq_index = 4,
- .dm_hs_phy_irq_index = 3,
- .ss_phy_irq_index = 2
-};
+static struct platform_device *
+dwc3_qcom_create_urs_usb_platdev(struct device *dev)
+{
+ struct fwnode_handle *fwh;
+ struct acpi_device *adev;
+ char name[8];
+ int ret;
+ int id;
+
+ /* Figure out device id */
+ ret = sscanf(fwnode_get_name(dev->fwnode), "URS%d", &id);
+ if (!ret)
+ return NULL;
+
+ /* Find the child using name */
+ snprintf(name, sizeof(name), "USB%d", id);
+ fwh = fwnode_get_named_child_node(dev->fwnode, name);
+ if (!fwh)
+ return NULL;
+
+ adev = to_acpi_device_node(fwh);
+ if (!adev)
+ return NULL;
+
+ return acpi_create_platform_device(adev, NULL);
+}
static int dwc3_qcom_probe(struct platform_device *pdev)
{
@@ -620,6 +761,17 @@
qcom->acpi_pdata->qscratch_base_offset;
parent_res->end = parent_res->start +
qcom->acpi_pdata->qscratch_base_size;
+
+ if (qcom->acpi_pdata->is_urs) {
+ qcom->urs_usb = dwc3_qcom_create_urs_usb_platdev(dev);
+ if (IS_ERR_OR_NULL(qcom->urs_usb)) {
+ dev_err(dev, "failed to create URS USB platdev\n");
+ if (!qcom->urs_usb)
+ return -ENODEV;
+ else
+ return PTR_ERR(qcom->urs_usb);
+ }
+ }
}
qcom->qscratch_base = devm_ioremap_resource(dev, parent_res);
@@ -654,6 +806,10 @@
goto depopulate;
}
+ ret = dwc3_qcom_interconnect_init(qcom);
+ if (ret)
+ goto depopulate;
+
qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
/* enable vbus override for device mode */
@@ -663,7 +819,7 @@
/* register extcon to override sw_vbus on Vbus change later */
ret = dwc3_qcom_register_extcon(qcom);
if (ret)
- goto depopulate;
+ goto interconnect_exit;
device_init_wakeup(&pdev->dev, 1);
qcom->is_suspended = false;
@@ -673,6 +829,8 @@
return 0;
+interconnect_exit:
+ dwc3_qcom_interconnect_exit(qcom);
depopulate:
if (np)
of_platform_depopulate(&pdev->dev);
@@ -703,6 +861,7 @@
}
qcom->num_clocks = 0;
+ dwc3_qcom_interconnect_exit(qcom);
reset_control_assert(qcom->resets);
pm_runtime_allow(dev);
@@ -764,11 +923,37 @@
};
MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match);
+#ifdef CONFIG_ACPI
+static const struct dwc3_acpi_pdata sdm845_acpi_pdata = {
+ .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET,
+ .qscratch_base_size = SDM845_QSCRATCH_SIZE,
+ .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE,
+ .hs_phy_irq_index = 1,
+ .dp_hs_phy_irq_index = 4,
+ .dm_hs_phy_irq_index = 3,
+ .ss_phy_irq_index = 2
+};
+
+static const struct dwc3_acpi_pdata sdm845_acpi_urs_pdata = {
+ .qscratch_base_offset = SDM845_QSCRATCH_BASE_OFFSET,
+ .qscratch_base_size = SDM845_QSCRATCH_SIZE,
+ .dwc3_core_base_size = SDM845_DWC3_CORE_SIZE,
+ .hs_phy_irq_index = 1,
+ .dp_hs_phy_irq_index = 4,
+ .dm_hs_phy_irq_index = 3,
+ .ss_phy_irq_index = 2,
+ .is_urs = true,
+};
+
static const struct acpi_device_id dwc3_qcom_acpi_match[] = {
{ "QCOM2430", (unsigned long)&sdm845_acpi_pdata },
+ { "QCOM0304", (unsigned long)&sdm845_acpi_urs_pdata },
+ { "QCOM0497", (unsigned long)&sdm845_acpi_urs_pdata },
+ { "QCOM04A6", (unsigned long)&sdm845_acpi_pdata },
{ },
};
MODULE_DEVICE_TABLE(acpi, dwc3_qcom_acpi_match);
+#endif
static struct platform_driver dwc3_qcom_driver = {
.probe = dwc3_qcom_probe,
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index c682420..e733be8 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -206,8 +206,8 @@
if (!dwc3_data)
return -ENOMEM;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg-glue");
- dwc3_data->glue_base = devm_ioremap_resource(dev, res);
+ dwc3_data->glue_base =
+ devm_platform_ioremap_resource_byname(pdev, "reg-glue");
if (IS_ERR(dwc3_data->glue_base))
return PTR_ERR(dwc3_data->glue_base);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 4f28122..3cd2942 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -2,7 +2,7 @@
/*
* ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -105,7 +105,7 @@
* IRQ we were waiting for is long gone.
*/
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
- unsigned direction;
+ unsigned int direction;
direction = !!(dep->flags & DWC3_EP0_DIR_IN);
@@ -127,11 +127,11 @@
* handle it here.
*/
if (dwc->delayed_status) {
- unsigned direction;
+ unsigned int direction;
direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_CONFIGURED);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_CONFIGURED);
if (dwc->ep0state == EP0_STATUS_PHASE)
__dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
@@ -172,7 +172,7 @@
* XferNotReady(STATUS).
*/
if (dwc->three_stage_setup) {
- unsigned direction;
+ unsigned int direction;
direction = dwc->ep0_expect_in;
dwc->ep0state = EP0_DATA_PHASE;
@@ -328,7 +328,7 @@
/*
* LTM will be set once we know how to set this in HW.
*/
- usb_status |= dwc->gadget.is_selfpowered;
+ usb_status |= dwc->gadget->is_selfpowered;
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
@@ -428,11 +428,11 @@
return -EINVAL;
switch (wIndex >> 8) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
dwc->test_mode_nr = wIndex >> 8;
dwc->test_mode = true;
break;
@@ -453,7 +453,7 @@
wValue = le16_to_cpu(ctrl->wValue);
wIndex = le16_to_cpu(ctrl->wIndex);
- state = dwc->gadget.state;
+ state = dwc->gadget->state;
switch (wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
@@ -567,7 +567,7 @@
static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ enum usb_device_state state = dwc->gadget->state;
u32 addr;
u32 reg;
@@ -588,9 +588,9 @@
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
if (addr)
- usb_gadget_set_state(&dwc->gadget, USB_STATE_ADDRESS);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
else
- usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_DEFAULT);
return 0;
}
@@ -600,14 +600,14 @@
int ret;
spin_unlock(&dwc->lock);
- ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl);
+ ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
spin_lock(&dwc->lock);
return ret;
}
static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
- enum usb_device_state state = dwc->gadget.state;
+ enum usb_device_state state = dwc->gadget->state;
u32 cfg;
int ret;
u32 reg;
@@ -630,7 +630,7 @@
* to change the state on the next usb_ep_queue()
*/
if (ret == 0)
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_CONFIGURED);
/*
@@ -649,7 +649,7 @@
case USB_STATE_CONFIGURED:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
if (!cfg && !ret)
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_ADDRESS);
break;
default:
@@ -705,7 +705,7 @@
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
{
struct dwc3_ep *dep;
- enum usb_device_state state = dwc->gadget.state;
+ enum usb_device_state state = dwc->gadget->state;
u16 wLength;
if (state == USB_STATE_DEFAULT)
@@ -749,7 +749,7 @@
if (wIndex || wLength)
return -EINVAL;
- dwc->gadget.isoch_delay = wValue;
+ dwc->gadget->isoch_delay = wValue;
return 0;
}
@@ -1129,7 +1129,7 @@
*/
if (!list_empty(&dep->pending_list)) {
dwc->delayed_status = false;
- usb_gadget_set_state(&dwc->gadget,
+ usb_gadget_set_state(dwc->gadget,
USB_STATE_CONFIGURED);
dwc3_ep0_do_control_status(dwc, event);
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 39a9ad1..b68fe48 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2,7 +2,7 @@
/*
* gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -46,18 +46,18 @@
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
switch (mode) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
reg |= mode << 1;
break;
default:
return -EINVAL;
}
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
return 0;
}
@@ -95,7 +95,7 @@
* Wait until device controller is ready. Only applies to 1.94a and
* later RTL.
*/
- if (dwc->revision >= DWC3_REVISION_194A) {
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
while (--retries) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
if (reg & DWC3_DSTS_DCNRD)
@@ -111,6 +111,9 @@
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ /* set no action before sending new link state change */
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
/* set requested state */
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
@@ -119,7 +122,7 @@
* The following code is racy when called from dwc3_gadget_wakeup,
* and is not needed, at least on newer versions
*/
- if (dwc->revision >= DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
return 0;
/* wait for a change in DSTS */
@@ -224,7 +227,8 @@
* Caller should take care of locking. Issue @cmd with a given @param to @dwc
* and wait for its completion.
*/
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
+ u32 param)
{
u32 timeout = 500;
int status = 0;
@@ -265,7 +269,7 @@
* Caller should handle locking. This function will issue @cmd with given
* @params to @dep and wait for its completion.
*/
-int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
+int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params)
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
@@ -287,7 +291,7 @@
*
* DWC_usb3 3.30a and DWC_usb31 1.90a programming guide section 3.2.2
*/
- if (dwc->gadget.speed <= USB_SPEED_HIGH) {
+ if (dwc->gadget->speed <= USB_SPEED_HIGH) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
@@ -306,13 +310,24 @@
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
int link_state;
+ /*
+ * Initiate remote wakeup if the link state is in U3 when
+ * operating in SS/SSP or L1/L2 when operating in HS/FS. If the
+ * link state is in U1/U2, no remote wakeup is needed. The Start
+ * Transfer command will initiate the link recovery.
+ */
link_state = dwc3_gadget_get_link_state(dwc);
- if (link_state == DWC3_LINK_STATE_U1 ||
- link_state == DWC3_LINK_STATE_U2 ||
- link_state == DWC3_LINK_STATE_U3) {
+ switch (link_state) {
+ case DWC3_LINK_STATE_U2:
+ if (dwc->gadget->speed >= USB_SPEED_SUPER)
+ break;
+
+ fallthrough;
+ case DWC3_LINK_STATE_U3:
ret = __dwc3_gadget_wakeup(dwc);
dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
ret);
+ break;
}
}
@@ -352,6 +367,8 @@
ret = 0;
break;
case DEPEVT_TRANSFER_NO_RESOURCE:
+ dev_WARN(dwc->dev, "No resource for %s\n",
+ dep->name);
ret = -EINVAL;
break;
case DEPEVT_TRANSFER_BUS_EXPIRY:
@@ -383,9 +400,12 @@
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
- if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
- dep->flags |= DWC3_EP_TRANSFER_STARTED;
- dwc3_gadget_ep_get_transfer_index(dep);
+ if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
+ if (ret == 0)
+ dep->flags |= DWC3_EP_TRANSFER_STARTED;
+
+ if (ret != -ETIMEDOUT)
+ dwc3_gadget_ep_get_transfer_index(dep);
}
if (saved_config) {
@@ -411,8 +431,9 @@
* IN transfers due to a mishandled error condition. Synopsys
* STAR 9000614252.
*/
- if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
- (dwc->gadget.speed >= USB_SPEED_SUPER))
+ if (dep->direction &&
+ !DWC3_VER_IS_PRIOR(DWC3, 260A) &&
+ (dwc->gadget->speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN;
memset(¶ms, 0, sizeof(params));
@@ -552,8 +573,9 @@
| DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
/* Burst size is only needed in SuperSpeed mode */
- if (dwc->gadget.speed >= USB_SPEED_SUPER) {
+ if (dwc->gadget->speed >= USB_SPEED_SUPER) {
u32 burst = dep->endpoint.maxburst;
+
params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1);
}
@@ -569,6 +591,7 @@
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
+ | DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_STREAM_EVENT_EN;
dep->stream_capable = true;
}
@@ -595,15 +618,17 @@
u8 bInterval_m1;
/*
- * Valid range for DEPCFG.bInterval_m1 is from 0 to 13, and it
- * must be set to 0 when the controller operates in full-speed.
+ * Valid range for DEPCFG.bInterval_m1 is from 0 to 13.
+ *
+ * NOTE: The programming guide incorrectly stated bInterval_m1
+ * must be set to 0 when operating in fullspeed. Internally the
+ * controller does not have this limitation. See DWC_usb3x
+ * programming guide section 3.2.2.1.
*/
bInterval_m1 = min_t(u8, desc->bInterval - 1, 13);
- if (dwc->gadget.speed == USB_SPEED_FULL)
- bInterval_m1 = 0;
if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
- dwc->gadget.speed == USB_SPEED_FULL)
+ dwc->gadget->speed == USB_SPEED_FULL)
dep->interval = desc->bInterval;
else
dep->interval = 1 << (desc->bInterval - 1);
@@ -614,6 +639,9 @@
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
}
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt);
+
/**
* __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
@@ -674,7 +702,7 @@
* Issue StartTransfer here with no-op TRB so we can always rely on No
* Response Update Transfer command.
*/
- if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
+ if (usb_endpoint_xfer_bulk(desc) ||
usb_endpoint_xfer_int(desc)) {
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
@@ -693,6 +721,29 @@
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
if (ret < 0)
return ret;
+
+ if (dep->stream_capable) {
+ /*
+ * For streams, at start, there maybe a race where the
+ * host primes the endpoint before the function driver
+ * queues a request to initiate a stream. In that case,
+ * the controller will not see the prime to generate the
+ * ERDY and start stream. To workaround this, issue a
+ * no-op TRB as normal, but end it immediately. As a
+ * result, when the function driver queues the request,
+ * the next START_TRANSFER command will cause the
+ * controller to generate an ERDY to initiate the
+ * stream.
+ */
+ dwc3_stop_active_transfer(dep, true, true);
+
+ /*
+ * All stream eps will reinitiate stream on NoStream
+ * rejection until we can determine that the host can
+ * prime after the first transfer.
+ */
+ dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
+ }
}
out:
@@ -701,8 +752,6 @@
return 0;
}
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
- bool interrupt);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
@@ -922,11 +971,13 @@
}
static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
- dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
- unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
+ dma_addr_t dma, unsigned int length, unsigned int chain,
+ unsigned int node, unsigned int stream_id,
+ unsigned int short_not_ok, unsigned int no_interrupt,
+ unsigned int is_last, bool must_interrupt)
{
struct dwc3 *dwc = dep->dwc;
- struct usb_gadget *gadget = &dwc->gadget;
+ struct usb_gadget *gadget = dwc->gadget;
enum usb_device_speed speed = gadget->speed;
trb->size = DWC3_TRB_SIZE_LENGTH(length);
@@ -1010,16 +1061,30 @@
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
}
- if ((!no_interrupt && !chain) ||
- (dwc3_calc_trbs_left(dep) == 1))
+ if ((!no_interrupt && !chain) || must_interrupt)
trb->ctrl |= DWC3_TRB_CTRL_IOC;
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
+ else if (dep->stream_capable && is_last)
+ trb->ctrl |= DWC3_TRB_CTRL_LST;
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
+ /*
+ * As per data book 4.2.3.2TRB Control Bit Rules section
+ *
+ * The controller autonomously checks the HWO field of a TRB to determine if the
+ * entire TRB is valid. Therefore, software must ensure that the rest of the TRB
+ * is valid before setting the HWO field to '1'. In most systems, this means that
+ * software must update the fourth DWORD of a TRB last.
+ *
+ * However there is a possibility of CPU re-ordering here which can cause
+ * controller to observe the HWO bit set prematurely.
+ * Add a write memory barrier to prevent CPU re-ordering.
+ */
+ wmb();
trb->ctrl |= DWC3_TRB_CTRL_HWO;
dwc3_ep_inc_enq(dep);
@@ -1034,18 +1099,24 @@
* @trb_length: buffer size of the TRB
* @chain: should this TRB be chained to the next?
* @node: only for isochronous endpoints. First TRB needs different type.
+ * @use_bounce_buffer: set to use bounce buffer
+ * @must_interrupt: set to interrupt on TRB completion
*/
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, unsigned int trb_length,
- unsigned chain, unsigned node)
+ unsigned int chain, unsigned int node, bool use_bounce_buffer,
+ bool must_interrupt)
{
struct dwc3_trb *trb;
dma_addr_t dma;
- unsigned stream_id = req->request.stream_id;
- unsigned short_not_ok = req->request.short_not_ok;
- unsigned no_interrupt = req->request.no_interrupt;
+ unsigned int stream_id = req->request.stream_id;
+ unsigned int short_not_ok = req->request.short_not_ok;
+ unsigned int no_interrupt = req->request.no_interrupt;
+ unsigned int is_last = req->request.is_last;
- if (req->request.num_sgs > 0)
+ if (use_bounce_buffer)
+ dma = dep->dwc->bounce_addr;
+ else if (req->request.num_sgs > 0)
dma = sg_dma_address(req->start_sg);
else
dma = req->request.dma;
@@ -1061,20 +1132,73 @@
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
- stream_id, short_not_ok, no_interrupt);
+ stream_id, short_not_ok, no_interrupt, is_last,
+ must_interrupt);
}
-static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
+static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req)
+{
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = req->request.length % maxp;
+
+ if ((req->request.length && req->request.zero && !rem &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc)) ||
+ (!req->direction && rem))
+ return true;
+
+ return false;
+}
+
+/**
+ * dwc3_prepare_last_sg - prepare TRBs for the last SG entry
+ * @dep: The endpoint that the request belongs to
+ * @req: The request to prepare
+ * @entry_length: The last SG entry size
+ * @node: Indicates whether this is not the first entry (for isoc only)
+ *
+ * Return the number of TRBs prepared.
+ */
+static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
+ struct dwc3_request *req, unsigned int entry_length,
+ unsigned int node)
+{
+ unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
+ unsigned int rem = req->request.length % maxp;
+ unsigned int num_trbs = 1;
+
+ if (dwc3_needs_extra_trb(dep, req))
+ num_trbs++;
+
+ if (dwc3_calc_trbs_left(dep) < num_trbs)
+ return 0;
+
+ req->needs_extra_trb = num_trbs > 1;
+
+ /* Prepare a normal TRB */
+ if (req->direction || req->request.length)
+ dwc3_prepare_one_trb(dep, req, entry_length,
+ req->needs_extra_trb, node, false, false);
+
+ /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */
+ if ((!req->direction && !req->request.length) || req->needs_extra_trb)
+ dwc3_prepare_one_trb(dep, req,
+ req->direction ? 0 : maxp - rem,
+ false, 1, true, false);
+
+ return num_trbs;
+}
+
+static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep,
struct dwc3_request *req)
{
struct scatterlist *sg = req->start_sg;
struct scatterlist *s;
int i;
unsigned int length = req->request.length;
- unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
- unsigned int rem = length % maxp;
unsigned int remaining = req->request.num_mapped_sgs
- req->num_queued_sgs;
+ unsigned int num_trbs = req->num_trbs;
+ bool needs_extra_trb = dwc3_needs_extra_trb(dep, req);
/*
* If we resume preparing the request, then get the remaining length of
@@ -1084,8 +1208,10 @@
length -= sg_dma_len(s);
for_each_sg(sg, s, remaining, i) {
+ unsigned int num_trbs_left = dwc3_calc_trbs_left(dep);
unsigned int trb_length;
- unsigned chain = true;
+ bool must_interrupt = false;
+ bool last_sg = false;
trb_length = min_t(unsigned int, length, sg_dma_len(s));
@@ -1099,56 +1225,28 @@
* mapped sg.
*/
if ((i == remaining - 1) || !length)
- chain = false;
+ last_sg = true;
- if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
+ if (!num_trbs_left)
+ break;
- req->needs_extra_trb = true;
-
- /* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, trb_length, true, i);
-
- /* Now prepare one extra TRB to align transfer size */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr,
- maxp - rem, false, 1,
- req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt);
- } else if (req->request.zero && req->request.length &&
- !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- !rem && !chain) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
-
- req->needs_extra_trb = true;
-
- /* Prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, trb_length, true, i);
-
- /* Prepare one extra TRB to handle ZLP */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
- !req->direction, 1,
- req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt);
-
- /* Prepare one more TRB to handle MPS alignment */
- if (!req->direction) {
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
- false, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt);
- }
+ if (last_sg) {
+ if (!dwc3_prepare_last_sg(dep, req, trb_length, i))
+ break;
} else {
- dwc3_prepare_one_trb(dep, req, trb_length, chain, i);
+ /*
+ * Look ahead to check if we have enough TRBs for the
+ * next SG entry. If not, set interrupt on this TRB to
+ * resume preparing the next SG entry when more TRBs are
+ * free.
+ */
+ if (num_trbs_left == 1 || (needs_extra_trb &&
+ num_trbs_left <= 2 &&
+ sg_dma_len(sg_next(s)) >= length))
+ must_interrupt = true;
+
+ dwc3_prepare_one_trb(dep, req, trb_length, 1, i, false,
+ must_interrupt);
}
/*
@@ -1158,7 +1256,7 @@
* we have free trbs we can continue queuing from where we
* previously stopped
*/
- if (chain)
+ if (!last_sg)
req->start_sg = sg_next(s);
req->num_queued_sgs++;
@@ -1174,65 +1272,17 @@
break;
}
- if (!dwc3_calc_trbs_left(dep))
+ if (must_interrupt)
break;
}
+
+ return req->num_trbs - num_trbs;
}
-static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
+static int dwc3_prepare_trbs_linear(struct dwc3_ep *dep,
struct dwc3_request *req)
{
- unsigned int length = req->request.length;
- unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
- unsigned int rem = length % maxp;
-
- if ((!length || rem) && usb_endpoint_dir_out(dep->endpoint.desc)) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
-
- req->needs_extra_trb = true;
-
- /* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, length, true, 0);
-
- /* Now prepare one extra TRB to align transfer size */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
- false, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt);
- } else if (req->request.zero && req->request.length &&
- !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (IS_ALIGNED(req->request.length, maxp))) {
- struct dwc3 *dwc = dep->dwc;
- struct dwc3_trb *trb;
-
- req->needs_extra_trb = true;
-
- /* prepare normal TRB */
- dwc3_prepare_one_trb(dep, req, length, true, 0);
-
- /* Prepare one extra TRB to handle ZLP */
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
- !req->direction, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt);
-
- /* Prepare one more TRB to handle MPS alignment for OUT */
- if (!req->direction) {
- trb = &dep->trb_pool[dep->trb_enqueue];
- req->num_trbs++;
- __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
- false, 1, req->request.stream_id,
- req->request.short_not_ok,
- req->request.no_interrupt);
- }
- } else {
- dwc3_prepare_one_trb(dep, req, length, false, 0);
- }
+ return dwc3_prepare_last_sg(dep, req, req->request.length, 0);
}
/*
@@ -1242,10 +1292,13 @@
* The function goes through the requests list and sets up TRBs for the
* transfers. The function returns once there are no more TRBs available or
* it runs out of requests.
+ *
+ * Returns the number of TRBs prepared or negative errno.
*/
-static void dwc3_prepare_trbs(struct dwc3_ep *dep)
+static int dwc3_prepare_trbs(struct dwc3_ep *dep)
{
struct dwc3_request *req, *n;
+ int ret = 0;
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
@@ -1260,35 +1313,58 @@
* break things.
*/
list_for_each_entry(req, &dep->started_list, list) {
- if (req->num_pending_sgs > 0)
- dwc3_prepare_one_trb_sg(dep, req);
+ if (req->num_pending_sgs > 0) {
+ ret = dwc3_prepare_trbs_sg(dep, req);
+ if (!ret || req->num_pending_sgs)
+ return ret;
+ }
if (!dwc3_calc_trbs_left(dep))
- return;
+ return ret;
+
+ /*
+ * Don't prepare beyond a transfer. In DWC_usb32, its transfer
+ * burst capability may try to read and use TRBs beyond the
+ * active transfer instead of stopping.
+ */
+ if (dep->stream_capable && req->request.is_last)
+ return ret;
}
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
struct dwc3 *dwc = dep->dwc;
- int ret;
ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
dep->direction);
if (ret)
- return;
+ return ret;
req->sg = req->request.sg;
req->start_sg = req->sg;
req->num_queued_sgs = 0;
req->num_pending_sgs = req->request.num_mapped_sgs;
- if (req->num_pending_sgs > 0)
- dwc3_prepare_one_trb_sg(dep, req);
- else
- dwc3_prepare_one_trb_linear(dep, req);
+ if (req->num_pending_sgs > 0) {
+ ret = dwc3_prepare_trbs_sg(dep, req);
+ if (req->num_pending_sgs)
+ return ret;
+ } else {
+ ret = dwc3_prepare_trbs_linear(dep, req);
+ }
- if (!dwc3_calc_trbs_left(dep))
- return;
+ if (!ret || !dwc3_calc_trbs_left(dep))
+ return ret;
+
+ /*
+ * Don't prepare beyond a transfer. In DWC_usb32, its transfer
+ * burst capability may try to read and use TRBs beyond the
+ * active transfer instead of stopping.
+ */
+ if (dep->stream_capable && req->request.is_last)
+ return ret;
}
+
+ return ret;
}
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);
@@ -1301,12 +1377,24 @@
int ret;
u32 cmd;
- if (!dwc3_calc_trbs_left(dep))
- return 0;
+ /*
+ * Note that it's normal to have no new TRBs prepared (i.e. ret == 0).
+ * This happens when we need to stop and restart a transfer such as in
+ * the case of reinitiating a stream or retrying an isoc transfer.
+ */
+ ret = dwc3_prepare_trbs(dep);
+ if (ret < 0)
+ return ret;
starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);
- dwc3_prepare_trbs(dep);
+ /*
+ * If there's no new TRB prepared and we don't need to restart a
+ * transfer, there's no need to update the transfer.
+ */
+ if (!ret && !starting)
+ return ret;
+
req = next_request(&dep->started_list);
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
@@ -1349,6 +1437,9 @@
return ret;
}
+ if (dep->stream_capable && req->request.is_last)
+ dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE;
+
return 0;
}
@@ -1417,7 +1508,7 @@
* Check if we can start isoc transfer on the next interval or
* 4 uframes in the future with BIT[15:14] as dep->combo_num
*/
- test_frame_number = dep->frame_number & 0x3fff;
+ test_frame_number = dep->frame_number & DWC3_FRNUMBER_MASK;
test_frame_number |= dep->combo_num << 14;
test_frame_number += max_t(u32, 4, dep->interval);
@@ -1464,7 +1555,7 @@
else if (test0 && test1)
dep->combo_num = 0;
- dep->frame_number &= 0x3fff;
+ dep->frame_number &= DWC3_FRNUMBER_MASK;
dep->frame_number |= dep->combo_num << 14;
dep->frame_number += max_t(u32, 4, dep->interval);
@@ -1477,25 +1568,45 @@
static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
{
+ const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
int ret;
int i;
- if (list_empty(&dep->pending_list)) {
+ if (list_empty(&dep->pending_list) &&
+ list_empty(&dep->started_list)) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
return -EAGAIN;
}
- if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
- (dwc->revision <= DWC3_USB31_REVISION_160A ||
- (dwc->revision == DWC3_USB31_REVISION_170A &&
- dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
- dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
-
- if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
+ if (!dwc->dis_start_transfer_quirk &&
+ (DWC3_VER_IS_PRIOR(DWC31, 170A) ||
+ DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
+ if (dwc->gadget->speed <= USB_SPEED_HIGH && dep->direction)
return dwc3_gadget_start_isoc_quirk(dep);
}
+ if (desc->bInterval <= 14 &&
+ dwc->gadget->speed >= USB_SPEED_HIGH) {
+ u32 frame = __dwc3_gadget_get_frame(dwc);
+ bool rollover = frame <
+ (dep->frame_number & DWC3_FRNUMBER_MASK);
+
+ /*
+ * frame_number is set from XferNotReady and may be already
+ * out of date. DSTS only provides the lower 14 bit of the
+ * current frame number. So add the upper two bits of
+ * frame_number and handle a possible rollover.
+ * This will provide the correct frame_number unless more than
+ * rollover has happened since XferNotReady.
+ */
+
+ dep->frame_number = (dep->frame_number & ~DWC3_FRNUMBER_MASK) |
+ frame;
+ if (rollover)
+ dep->frame_number += BIT(14);
+ }
+
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
@@ -1504,6 +1615,27 @@
break;
}
+ /*
+ * After a number of unsuccessful start attempts due to bus-expiry
+ * status, issue END_TRANSFER command and retry on the next XferNotReady
+ * event.
+ */
+ if (ret == -EAGAIN) {
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER |
+ DWC3_DEPCMD_CMDIOC |
+ DWC3_DEPCMD_PARAM(dep->resource_index);
+
+ dep->resource_index = 0;
+ memset(¶ms, 0, sizeof(params));
+
+ ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
+ if (!ret)
+ dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+ }
+
return ret;
}
@@ -1536,6 +1668,9 @@
list_add_tail(&req->list, &dep->pending_list);
req->status = DWC3_REQUEST_STATUS_QUEUED;
+ if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)
+ return 0;
+
/*
* Start the transfer only after the END_TRANSFER is completed
* and endpoint STALL is cleared.
@@ -1561,9 +1696,8 @@
return 0;
if ((dep->flags & DWC3_EP_PENDING_REQUEST)) {
- if (!(dep->flags & DWC3_EP_TRANSFER_STARTED)) {
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
return __dwc3_gadget_start_isoc(dep);
- }
}
}
@@ -1646,39 +1780,42 @@
spin_lock_irqsave(&dwc->lock, flags);
- list_for_each_entry(r, &dep->pending_list, list) {
+ list_for_each_entry(r, &dep->cancelled_list, list) {
if (r == req)
- break;
+ goto out;
}
- if (r != req) {
- list_for_each_entry(r, &dep->started_list, list) {
- if (r == req)
- break;
- }
+ list_for_each_entry(r, &dep->pending_list, list) {
if (r == req) {
+ dwc3_gadget_giveback(dep, req, -ECONNRESET);
+ goto out;
+ }
+ }
+
+ list_for_each_entry(r, &dep->started_list, list) {
+ if (r == req) {
+ struct dwc3_request *t;
+
/* wait until it is processed */
dwc3_stop_active_transfer(dep, true, true);
- if (!r->trb)
- goto out0;
+ /*
+ * Remove any started request if the transfer is
+ * cancelled.
+ */
+ list_for_each_entry_safe(r, t, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(r);
- dwc3_gadget_move_cancelled_request(req);
- if (dep->flags & DWC3_EP_TRANSFER_STARTED)
- goto out0;
- else
- goto out1;
+ dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
+
+ goto out;
}
- dev_err(dwc->dev, "request %pK was not queued to %s\n",
- request, ep->name);
- ret = -EINVAL;
- goto out0;
}
-out1:
- dwc3_gadget_giveback(dep, req, -ECONNRESET);
-
-out0:
+ dev_err(dwc->dev, "request %pK was not queued to %s\n",
+ request, ep->name);
+ ret = -EINVAL;
+out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1702,8 +1839,8 @@
if (value) {
struct dwc3_trb *trb;
- unsigned transfer_in_flight;
- unsigned started;
+ unsigned int transfer_in_flight;
+ unsigned int started;
if (dep->number > 1)
trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
@@ -1879,7 +2016,7 @@
}
/* Recent versions do this automatically */
- if (dwc->revision < DWC3_REVISION_194A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
/* write zeroes to Link Change Request */
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
@@ -1956,12 +2093,12 @@
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
- if (dwc->revision <= DWC3_REVISION_187A) {
+ if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
reg &= ~DWC3_DCTL_TRGTULST_MASK;
reg |= DWC3_DCTL_TRGTULST_RX_DET;
}
- if (dwc->revision >= DWC3_REVISION_194A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
reg &= ~DWC3_DCTL_KEEP_CONNECT;
reg |= DWC3_DCTL_RUN_STOP;
@@ -1978,7 +2115,7 @@
dwc->pullups_connected = false;
}
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
do {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
@@ -2101,11 +2238,11 @@
DWC3_DEVTEN_USBRSTEN |
DWC3_DEVTEN_DISCONNEVTEN);
- if (dwc->revision < DWC3_REVISION_250A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A))
reg |= DWC3_DEVTEN_ULSTCNGEN;
/* On 2.30a and above this bit enables U3/L2-L1 Suspend Events */
- if (dwc->revision >= DWC3_REVISION_230A)
+ if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
reg |= DWC3_DEVTEN_EOPFEN;
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
@@ -2150,6 +2287,8 @@
ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
nump = min_t(u32, nump, 16);
@@ -2186,10 +2325,10 @@
* bursts of data without going through any sort of endpoint throttling.
*/
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
- if (dwc3_is_usb31(dwc))
- reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
- else
+ if (DWC3_IP_IS(DWC3))
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+ else
+ reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
@@ -2249,7 +2388,7 @@
spin_lock_irqsave(&dwc->lock, flags);
if (dwc->gadget_driver) {
dev_err(dwc->dev, "%s is already bound to %s\n",
- dwc->gadget.name,
+ dwc->gadget->name,
dwc->gadget_driver->driver.name);
ret = -EBUSY;
goto err1;
@@ -2352,7 +2491,7 @@
* STAR#9000525659: Clock Domain Crossing on DCTL in
* USB 2.0 Mode
*/
- if (dwc->revision < DWC3_REVISION_220A &&
+ if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
!dwc->dis_metastability_quirk) {
reg |= DWC3_DCFG_SUPERSPEED;
} else {
@@ -2370,18 +2509,18 @@
reg |= DWC3_DCFG_SUPERSPEED;
break;
case USB_SPEED_SUPER_PLUS:
- if (dwc3_is_usb31(dwc))
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- else
+ if (DWC3_IP_IS(DWC3))
reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
break;
default:
dev_err(dwc->dev, "invalid speed (%d)\n", speed);
- if (dwc->revision & DWC3_REVISION_IS_DWC31)
- reg |= DWC3_DCFG_SUPERSPEED_PLUS;
- else
+ if (DWC3_IP_IS(DWC3))
reg |= DWC3_DCFG_SUPERSPEED;
+ else
+ reg |= DWC3_DCFG_SUPERSPEED_PLUS;
}
}
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
@@ -2410,7 +2549,7 @@
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!dep->direction)
- dwc->gadget.ep0 = &dep->endpoint;
+ dwc->gadget->ep0 = &dep->endpoint;
dep->endpoint.caps.type_control = true;
@@ -2424,14 +2563,17 @@
int size;
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
- if (dwc3_is_usb31(dwc))
- size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+ if (DWC3_IP_IS(DWC3))
+ size = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
- size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+ size = DWC31_GTXFIFOSIZ_TXFDEP(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
size *= mdwidth;
@@ -2450,10 +2592,10 @@
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
- dep->endpoint.max_streams = 15;
+ dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ &dwc->gadget->ep_list);
dep->endpoint.caps.type_iso = true;
dep->endpoint.caps.type_bulk = true;
dep->endpoint.caps.type_int = true;
@@ -2468,16 +2610,18 @@
int size;
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+ if (DWC3_IP_IS(DWC32))
+ mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
/* MDWIDTH is represented in bits, convert to bytes */
mdwidth /= 8;
/* All OUT endpoints share a single RxFIFO space */
size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
- if (dwc3_is_usb31(dwc))
- size = DWC31_GRXFIFOSIZ_RXFDEP(size);
- else
+ if (DWC3_IP_IS(DWC3))
size = DWC3_GRXFIFOSIZ_RXFDEP(size);
+ else
+ size = DWC31_GRXFIFOSIZ_RXFDEP(size);
/* FIFO depth is in MDWDITH bytes */
size *= mdwidth;
@@ -2497,10 +2641,10 @@
size /= 3;
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
- dep->endpoint.max_streams = 15;
+ dep->endpoint.max_streams = 16;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
- &dwc->gadget.ep_list);
+ &dwc->gadget->ep_list);
dep->endpoint.caps.type_iso = true;
dep->endpoint.caps.type_bulk = true;
dep->endpoint.caps.type_int = true;
@@ -2563,7 +2707,7 @@
{
u8 epnum;
- INIT_LIST_HEAD(&dwc->gadget.ep_list);
+ INIT_LIST_HEAD(&dwc->gadget->ep_list);
for (epnum = 0; epnum < total; epnum++) {
int ret;
@@ -2644,12 +2788,12 @@
}
/*
- * If we're dealing with unaligned size OUT transfer, we will be left
- * with one TRB pending in the ring. We need to manually clear HWO bit
- * from that TRB.
+ * We use bounce buffer for requests that needs extra TRB or OUT ZLP. If
+ * this TRB points to the bounce buffer address, it's a MPS alignment
+ * TRB. Don't add it to req->remaining calculation.
*/
-
- if (req->needs_extra_trb && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) {
+ if (trb->bpl == lower_32_bits(dep->dwc->bounce_addr) &&
+ trb->bph == upper_32_bits(dep->dwc->bounce_addr)) {
trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
return 1;
}
@@ -2730,17 +2874,8 @@
goto out;
if (req->needs_extra_trb) {
- unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
-
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
-
- /* Reclaim MPS padding TRB for ZLP */
- if (!req->direction && req->request.zero && req->request.length &&
- !usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (IS_ALIGNED(req->request.length, maxp)))
- ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status);
-
req->needs_extra_trb = false;
}
@@ -2790,37 +2925,34 @@
dep->frame_number = event->parameters;
}
-static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
- const struct dwc3_event_depevt *event)
+static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event, int status)
{
struct dwc3 *dwc = dep->dwc;
- unsigned status = 0;
- bool stop = false;
+ bool no_started_trb = true;
- dwc3_gadget_endpoint_frame_from_event(dep, event);
-
- if (event->status & DEPEVT_STATUS_BUSERR)
- status = -ECONNRESET;
-
- if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
- status = -EXDEV;
-
- if (list_empty(&dep->started_list))
- stop = true;
- }
+ if (!dep->endpoint.desc)
+ return no_started_trb;
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
- if (stop)
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
+ goto out;
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ list_empty(&dep->started_list) &&
+ (list_empty(&dep->pending_list) || status == -EXDEV))
dwc3_stop_active_transfer(dep, true, true);
else if (dwc3_gadget_ep_should_continue(dep))
- __dwc3_gadget_kick_transfer(dep);
+ if (__dwc3_gadget_kick_transfer(dep) == 0)
+ no_started_trb = false;
+out:
/*
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
* See dwc3_gadget_linksts_change_interrupt() for 1st half.
*/
- if (dwc->revision < DWC3_REVISION_183A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
u32 reg;
int i;
@@ -2831,7 +2963,7 @@
continue;
if (!list_empty(&dep->started_list))
- return;
+ return no_started_trb;
}
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -2840,21 +2972,177 @@
dwc->u1u2 = 0;
}
+
+ return no_started_trb;
+}
+
+static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ int status = 0;
+
+ if (!dep->endpoint.desc)
+ return;
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+ if (event->status & DEPEVT_STATUS_BUSERR)
+ status = -ECONNRESET;
+
+ if (event->status & DEPEVT_STATUS_MISSED_ISOC)
+ status = -EXDEV;
+
+ dwc3_gadget_endpoint_trbs_complete(dep, event, status);
+}
+
+static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ int status = 0;
+
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+
+ if (event->status & DEPEVT_STATUS_BUSERR)
+ status = -ECONNRESET;
+
+ if (dwc3_gadget_endpoint_trbs_complete(dep, event, status))
+ dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
}
static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+ /*
+ * The XferNotReady event is generated only once before the endpoint
+ * starts. It will be generated again when END_TRANSFER command is
+ * issued. For some controller versions, the XferNotReady event may be
+ * generated while the END_TRANSFER command is still in process. Ignore
+ * it and wait for the next XferNotReady event after the command is
+ * completed.
+ */
+ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
+ return;
+
(void) __dwc3_gadget_start_isoc(dep);
}
+static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ u8 cmd = DEPEVT_PARAMETER_CMD(event->parameters);
+
+ if (cmd != DWC3_DEPCMD_ENDTRANSFER)
+ return;
+
+ /*
+ * The END_TRANSFER command will cause the controller to generate a
+ * NoStream Event, and it's not due to the host DP NoStream rejection.
+ * Ignore the next NoStream event.
+ */
+ if (dep->stream_capable)
+ dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
+
+ dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
+ dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+ dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
+ if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) {
+ struct dwc3 *dwc = dep->dwc;
+
+ dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL;
+ if (dwc3_send_clear_stall_ep_cmd(dep)) {
+ struct usb_ep *ep0 = &dwc->eps[0]->endpoint;
+
+ dev_err(dwc->dev, "failed to clear STALL on %s\n", dep->name);
+ if (dwc->delayed_status)
+ __dwc3_gadget_ep0_set_halt(ep0, 1);
+ return;
+ }
+
+ dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+ if (dwc->delayed_status)
+ dwc3_ep0_send_delayed_status(dwc);
+ }
+
+ if ((dep->flags & DWC3_EP_DELAY_START) &&
+ !usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ __dwc3_gadget_kick_transfer(dep);
+
+ dep->flags &= ~DWC3_EP_DELAY_START;
+}
+
+static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
+ const struct dwc3_event_depevt *event)
+{
+ struct dwc3 *dwc = dep->dwc;
+
+ if (event->status == DEPEVT_STREAMEVT_FOUND) {
+ dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
+ goto out;
+ }
+
+ /* Note: NoStream rejection event param value is 0 and not 0xFFFF */
+ switch (event->parameters) {
+ case DEPEVT_STREAM_PRIME:
+ /*
+ * If the host can properly transition the endpoint state from
+ * idle to prime after a NoStream rejection, there's no need to
+ * force restarting the endpoint to reinitiate the stream. To
+ * simplify the check, assume the host follows the USB spec if
+ * it primed the endpoint more than once.
+ */
+ if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
+ if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
+ dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
+ else
+ dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
+ }
+
+ break;
+ case DEPEVT_STREAM_NOSTREAM:
+ if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
+ !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
+ !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))
+ break;
+
+ /*
+ * If the host rejects a stream due to no active stream, by the
+ * USB and xHCI spec, the endpoint will be put back to idle
+ * state. When the host is ready (buffer added/updated), it will
+ * prime the endpoint to inform the usb device controller. This
+ * triggers the device controller to issue ERDY to restart the
+ * stream. However, some hosts don't follow this and keep the
+ * endpoint in the idle state. No prime will come despite host
+ * streams are updated, and the device controller will not be
+ * triggered to generate ERDY to move the next stream data. To
+ * workaround this and maintain compatibility with various
+ * hosts, force to reinitate the stream until the host is ready
+ * instead of waiting for the host to prime the endpoint.
+ */
+ if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
+ unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
+
+ dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
+ } else {
+ dep->flags |= DWC3_EP_DELAY_START;
+ dwc3_stop_active_transfer(dep, true, true);
+ return;
+ }
+ break;
+ }
+
+out:
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
+}
+
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
struct dwc3_ep *dep;
u8 epnum = event->endpoint_number;
- u8 cmd;
dep = dwc->eps[epnum];
@@ -2880,41 +3168,14 @@
dwc3_gadget_endpoint_transfer_not_ready(dep, event);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- cmd = DEPEVT_PARAMETER_CMD(event->parameters);
-
- if (cmd == DWC3_DEPCMD_ENDTRANSFER) {
- dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
- dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
- dwc3_gadget_ep_cleanup_cancelled_requests(dep);
-
- if (dep->flags & DWC3_EP_PENDING_CLEAR_STALL) {
- struct dwc3 *dwc = dep->dwc;
-
- dep->flags &= ~DWC3_EP_PENDING_CLEAR_STALL;
- if (dwc3_send_clear_stall_ep_cmd(dep)) {
- struct usb_ep *ep0 = &dwc->eps[0]->endpoint;
-
- dev_err(dwc->dev, "failed to clear STALL on %s\n",
- dep->name);
- if (dwc->delayed_status)
- __dwc3_gadget_ep0_set_halt(ep0, 1);
- return;
- }
-
- dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
- if (dwc->delayed_status)
- dwc3_ep0_send_delayed_status(dwc);
- }
-
- if ((dep->flags & DWC3_EP_DELAY_START) &&
- !usb_endpoint_xfer_isoc(dep->endpoint.desc))
- __dwc3_gadget_kick_transfer(dep);
-
- dep->flags &= ~DWC3_EP_DELAY_START;
- }
+ dwc3_gadget_endpoint_command_complete(dep, event);
+ break;
+ case DWC3_DEPEVT_XFERCOMPLETE:
+ dwc3_gadget_endpoint_transfer_complete(dep, event);
break;
case DWC3_DEPEVT_STREAMEVT:
- case DWC3_DEPEVT_XFERCOMPLETE:
+ dwc3_gadget_endpoint_stream_event(dep, event);
+ break;
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
}
@@ -2924,7 +3185,7 @@
{
if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->disconnect(&dwc->gadget);
+ dwc->gadget_driver->disconnect(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -2933,7 +3194,7 @@
{
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->suspend(&dwc->gadget);
+ dwc->gadget_driver->suspend(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -2942,7 +3203,7 @@
{
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -2952,9 +3213,9 @@
if (!dwc->gadget_driver)
return;
- if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
+ if (dwc->gadget->speed != USB_SPEED_UNKNOWN) {
spin_unlock(&dwc->lock);
- usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
+ usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
}
}
@@ -2962,7 +3223,6 @@
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt)
{
- struct dwc3 *dwc = dep->dwc;
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
int ret;
@@ -2978,16 +3238,13 @@
* much trouble synchronizing between us and gadget driver.
*
* We have discussed this with the IP Provider and it was
- * suggested to giveback all requests here, but give HW some
- * extra time to synchronize with the interconnect. We're using
- * an arbitrary 100us delay for that.
+ * suggested to giveback all requests here.
*
* Note also that a similar handling was tested by Synopsys
* (thanks a lot Paul) and nothing bad has come out of it.
- * In short, what we're doing is:
- *
- * - Issue EndTransfer WITH CMDIOC bit set
- * - Wait 100us
+ * In short, what we're doing is issuing EndTransfer with
+ * CMDIOC bit set and delay kicking transfer until the
+ * EndTransfer command had completed.
*
* As of IP version 3.10a of the DWC_usb3 IP, the controller
* supports a mode to work around the above limitation. The
@@ -2996,8 +3253,7 @@
* by writing GUCTL2[14]. This polling is already done in the
* dwc3_send_gadget_ep_cmd() function so if the mode is
* enabled, the EndTransfer command will have completed upon
- * returning from this function and we don't need to delay for
- * 100us.
+ * returning from this function.
*
* This mode is NOT available on the DWC_usb31 IP.
*/
@@ -3015,9 +3271,6 @@
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
else
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
-
- if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
- udelay(100);
}
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
@@ -3046,18 +3299,18 @@
{
int reg;
+ dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET);
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
-
reg &= ~DWC3_DCTL_INITU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
dwc3_disconnect_gadget(dwc);
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
+ dwc->gadget->speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
- usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
dwc->connected = false;
}
@@ -3101,7 +3354,7 @@
* STAR#9000466709: RTL: Device : Disconnect event not
* generated if setup packet pending in FIFO
*/
- if (dwc->revision < DWC3_REVISION_188A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 188A)) {
if (dwc->setup_packet_pending)
dwc3_gadget_disconnect_interrupt(dwc);
}
@@ -3118,7 +3371,7 @@
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->test_mode = false;
dwc3_clear_stall_all_ep(dwc);
@@ -3151,8 +3404,8 @@
switch (speed) {
case DWC3_DSTS_SUPERSPEED_PLUS:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget.ep0->maxpacket = 512;
- dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
+ dwc->gadget->ep0->maxpacket = 512;
+ dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
break;
case DWC3_DSTS_SUPERSPEED:
/*
@@ -3168,35 +3421,36 @@
* STAR#9000483510: RTL: SS : USB3 reset event may
* not be generated always when the link enters poll
*/
- if (dwc->revision < DWC3_REVISION_190A)
+ if (DWC3_VER_IS_PRIOR(DWC3, 190A))
dwc3_gadget_reset_interrupt(dwc);
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
- dwc->gadget.ep0->maxpacket = 512;
- dwc->gadget.speed = USB_SPEED_SUPER;
+ dwc->gadget->ep0->maxpacket = 512;
+ dwc->gadget->speed = USB_SPEED_SUPER;
break;
case DWC3_DSTS_HIGHSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_HIGH;
+ dwc->gadget->ep0->maxpacket = 64;
+ dwc->gadget->speed = USB_SPEED_HIGH;
break;
case DWC3_DSTS_FULLSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
- dwc->gadget.ep0->maxpacket = 64;
- dwc->gadget.speed = USB_SPEED_FULL;
+ dwc->gadget->ep0->maxpacket = 64;
+ dwc->gadget->speed = USB_SPEED_FULL;
break;
case DWC3_DSTS_LOWSPEED:
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
- dwc->gadget.ep0->maxpacket = 8;
- dwc->gadget.speed = USB_SPEED_LOW;
+ dwc->gadget->ep0->maxpacket = 8;
+ dwc->gadget->speed = USB_SPEED_LOW;
break;
}
- dwc->eps[1]->endpoint.maxpacket = dwc->gadget.ep0->maxpacket;
+ dwc->eps[1]->endpoint.maxpacket = dwc->gadget->ep0->maxpacket;
/* Enable USB2 LPM Capability */
- if ((dwc->revision > DWC3_REVISION_194A) &&
+ if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) &&
+ !dwc->usb2_gadget_lpm_disable &&
(speed != DWC3_DSTS_SUPERSPEED) &&
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
@@ -3215,18 +3469,23 @@
* BESL value in the LPM token is less than or equal to LPM
* NYET threshold.
*/
- WARN_ONCE(dwc->revision < DWC3_REVISION_240A
- && dwc->has_lpm_erratum,
+ WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum,
"LPM Erratum not available on dwc3 revisions < 2.40a\n");
- if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
+ if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A))
reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
} else {
+ if (dwc->usb2_gadget_lpm_disable) {
+ reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg &= ~DWC3_DCFG_LPM_CAP;
+ dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ }
+
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
}
dep = dwc->eps[0];
@@ -3261,7 +3520,7 @@
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
- dwc->gadget_driver->resume(&dwc->gadget);
+ dwc->gadget_driver->resume(dwc->gadget);
spin_lock(&dwc->lock);
}
}
@@ -3290,7 +3549,7 @@
* operational mode
*/
pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
- if ((dwc->revision < DWC3_REVISION_250A) &&
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A) &&
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
(next == DWC3_LINK_STATE_RESUME)) {
@@ -3316,7 +3575,7 @@
* STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
* core send LGO_Ux entering U0
*/
- if (dwc->revision < DWC3_REVISION_183A) {
+ if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
if (next == DWC3_LINK_STATE_U0) {
u32 u1u2;
u32 reg;
@@ -3335,7 +3594,7 @@
reg &= ~u1u2;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_gadget_dctl_write_safe(dwc, reg);
break;
default:
/* do nothing */
@@ -3427,12 +3686,12 @@
break;
case DWC3_DEVICE_EVENT_EOPF:
/* It changed to be suspend event for version 2.30a and above */
- if (dwc->revision >= DWC3_REVISION_230A) {
+ if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
/*
* Ignore suspend event until the gadget enters into
* USB_STATE_CONFIGURED state.
*/
- if (dwc->gadget.state >= USB_STATE_CONFIGURED)
+ if (dwc->gadget->state >= USB_STATE_CONFIGURED)
dwc3_gadget_suspend_interrupt(dwc,
event->event_info);
}
@@ -3516,9 +3775,11 @@
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
+ local_bh_disable();
spin_lock_irqsave(&dwc->lock, flags);
ret = dwc3_process_event_buf(evt);
spin_unlock_irqrestore(&dwc->lock, flags);
+ local_bh_enable();
return ret;
}
@@ -3607,6 +3868,13 @@
return irq;
}
+static void dwc_gadget_release(struct device *dev)
+{
+ struct usb_gadget *gadget = container_of(dev, struct usb_gadget, dev);
+
+ kfree(gadget);
+}
+
/**
* dwc3_gadget_init - initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -3617,6 +3885,7 @@
{
int ret;
int irq;
+ struct device *dev;
irq = dwc3_gadget_get_irq(dwc);
if (irq < 0) {
@@ -3649,12 +3918,21 @@
}
init_completion(&dwc->ep0_in_setup);
+ dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL);
+ if (!dwc->gadget) {
+ ret = -ENOMEM;
+ goto err3;
+ }
- dwc->gadget.ops = &dwc3_gadget_ops;
- dwc->gadget.speed = USB_SPEED_UNKNOWN;
- dwc->gadget.sg_supported = true;
- dwc->gadget.name = "dwc3-gadget";
- dwc->gadget.lpm_capable = true;
+
+ usb_initialize_gadget(dwc->dev, dwc->gadget, dwc_gadget_release);
+ dev = &dwc->gadget->dev;
+ dev->platform_data = dwc;
+ dwc->gadget->ops = &dwc3_gadget_ops;
+ dwc->gadget->speed = USB_SPEED_UNKNOWN;
+ dwc->gadget->sg_supported = true;
+ dwc->gadget->name = "dwc3-gadget";
+ dwc->gadget->lpm_capable = !dwc->usb2_gadget_lpm_disable;
/*
* FIXME We might be setting max_speed to <SUPER, however versions
@@ -3672,12 +3950,12 @@
* is less than super speed because we don't have means, yet, to tell
* composite.c that we are USB 2.0 + LPM ECN.
*/
- if (dwc->revision < DWC3_REVISION_220A &&
+ if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
!dwc->dis_metastability_quirk)
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);
- dwc->gadget.max_speed = dwc->maximum_speed;
+ dwc->gadget->max_speed = dwc->maximum_speed;
/*
* REVISIT: Here we should clear all pending IRQs to be
@@ -3686,21 +3964,23 @@
ret = dwc3_gadget_init_endpoints(dwc, dwc->num_eps);
if (ret)
- goto err3;
-
- ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
- if (ret) {
- dev_err(dwc->dev, "failed to register udc\n");
goto err4;
+
+ ret = usb_add_gadget(dwc->gadget);
+ if (ret) {
+ dev_err(dwc->dev, "failed to add gadget\n");
+ goto err5;
}
- dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed);
+ dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
return 0;
-err4:
+err5:
dwc3_gadget_free_endpoints(dwc);
-
+err4:
+ usb_put_gadget(dwc->gadget);
+ dwc->gadget = NULL;
err3:
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
@@ -3720,8 +4000,12 @@
void dwc3_gadget_exit(struct dwc3 *dwc)
{
- usb_del_gadget_udc(&dwc->gadget);
+ if (!dwc->gadget)
+ return;
+
+ usb_del_gadget(dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
+ usb_put_gadget(dwc->gadget);
dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
dwc->bounce_addr);
kfree(dwc->setup_buf);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index f207e59..0cd2819 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* gadget.h - DesignWare USB3 DRD Gadget Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
@@ -17,7 +17,7 @@
struct dwc3;
#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint))
-#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
+#define gadget_to_dwc(g) (dev_get_platdata(&g->dev))
/* DEPCFG parameter 1 */
#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
@@ -54,6 +54,8 @@
/* U2 Device exit Latency */
#define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */
+/* Frame/Microframe Number Mask */
+#define DWC3_FRNUMBER_MASK 0x3fff
/* -------------------------------------------------------------------------- */
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
@@ -128,4 +130,18 @@
dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
+/**
+ * dwc3_gadget_dctl_write_safe - write to DCTL safe from link state change
+ * @dwc: pointer to our context structure
+ * @value: value to write to DCTL
+ *
+ * Use this function when doing read-modify-write to DCTL. It will not
+ * send link state change request.
+ */
+static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value)
+{
+ value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
+ dwc3_writel(dwc->regs, DWC3_DCTL, value);
+}
+
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index fa25287..e195176 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* host.c - DesignWare USB3 DRD Controller Host Glue
*
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
*/
+#include <linux/acpi.h>
#include <linux/platform_device.h>
#include "core.h"
@@ -75,6 +76,7 @@
}
xhci->dev.parent = dwc->dev;
+ ACPI_COMPANION_SET(&xhci->dev, ACPI_COMPANION(dwc->dev));
dwc->xhci = xhci;
@@ -102,7 +104,7 @@
*
* This following flag tells XHCI to do just that.
*/
- if (dwc->revision <= DWC3_REVISION_300A)
+ if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
if (prop_idx) {
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 70acdf9..76b73b1 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/**
* io.h - DesignWare USB3 DRD IO Header
*
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com
*
* Authors: Felipe Balbi <balbi@ti.com>,
* Sebastian Andrzej Siewior <bigeasy@linutronix.de>
diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c
index f8886f3..1b45a97 100644
--- a/drivers/usb/dwc3/trace.c
+++ b/drivers/usb/dwc3/trace.c
@@ -2,7 +2,7 @@
/**
* trace.c - DesignWare USB3 DRD Controller Trace Support
*
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com
*
* Author: Felipe Balbi <balbi@ti.com>
*/
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index 9edff17..97f4f11 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -1,8 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/**
* trace.h - DesignWare USB3 DRD Controller Trace Support
*
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com
*
* Author: Felipe Balbi <balbi@ti.com>
*/
@@ -104,8 +104,8 @@
TP_STRUCT__entry(
__string(name, req->dep->name)
__field(struct dwc3_request *, req)
- __field(unsigned, actual)
- __field(unsigned, length)
+ __field(unsigned int, actual)
+ __field(unsigned int, length)
__field(int, status)
__field(int, zero)
__field(int, short_not_ok)
@@ -227,6 +227,8 @@
__field(u32, size)
__field(u32, ctrl)
__field(u32, type)
+ __field(u32, enqueue)
+ __field(u32, dequeue)
),
TP_fast_assign(
__assign_str(name, dep->name);
@@ -236,11 +238,15 @@
__entry->size = trb->size;
__entry->ctrl = trb->ctrl;
__entry->type = usb_endpoint_type(dep->endpoint.desc);
+ __entry->enqueue = dep->trb_enqueue;
+ __entry->dequeue = dep->trb_dequeue;
),
- TP_printk("%s: trb %p buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
- __get_str(name), __entry->trb, __entry->bph, __entry->bpl,
+ TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
+ __get_str(name), __entry->trb, __entry->enqueue,
+ __entry->dequeue, __entry->bph, __entry->bpl,
({char *s;
int pcm = ((__entry->size >> 24) & 3) + 1;
+
switch (__entry->type) {
case USB_ENDPOINT_XFER_INT:
case USB_ENDPOINT_XFER_ISOC:
@@ -286,12 +292,12 @@
TP_ARGS(dep),
TP_STRUCT__entry(
__string(name, dep->name)
- __field(unsigned, maxpacket)
- __field(unsigned, maxpacket_limit)
- __field(unsigned, max_streams)
- __field(unsigned, maxburst)
- __field(unsigned, flags)
- __field(unsigned, direction)
+ __field(unsigned int, maxpacket)
+ __field(unsigned int, maxpacket_limit)
+ __field(unsigned int, max_streams)
+ __field(unsigned int, maxburst)
+ __field(unsigned int, flags)
+ __field(unsigned int, direction)
__field(u8, trb_enqueue)
__field(u8, trb_dequeue)
),
diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c
index ffe3440..f23f4c9 100644
--- a/drivers/usb/dwc3/ulpi.c
+++ b/drivers/usb/dwc3/ulpi.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* ulpi.c - DesignWare USB3 Controller's ULPI PHY interface
*
* Copyright (C) 2015 Intel Corporation
@@ -24,7 +24,7 @@
static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read)
{
unsigned long ns = 5L * DWC3_ULPI_BASE_DELAY;
- unsigned int count = 1000;
+ unsigned int count = 10000;
u32 reg;
if (addr >= ULPI_EXT_VENDOR_SPECIFIC)
@@ -33,6 +33,10 @@
if (read)
ns += DWC3_ULPI_BASE_DELAY;
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ if (reg & DWC3_GUSB2PHYCFG_SUSPHY)
+ usleep_range(1000, 1200);
+
while (count--) {
ndelay(ns);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
@@ -50,12 +54,6 @@
u32 reg;
int ret;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
- }
-
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
@@ -73,12 +71,6 @@
struct dwc3 *dwc = dev_get_drvdata(dev);
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
- }
-
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
reg |= DWC3_GUSB2PHYACC_WRITE | val;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index ea0d531..45b42d8 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -15,6 +15,7 @@
#include <linux/console.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/iopoll.h>
#include <linux/pci_regs.h>
#include <linux/pci_ids.h>
#include <linux/usb/ch9.h>
@@ -161,17 +162,11 @@
static int dbgp_wait_until_complete(void)
{
u32 ctrl;
- int loop = DBGP_TIMEOUT;
+ int ret;
- do {
- ctrl = readl(&ehci_debug->control);
- /* Stop when the transaction is finished */
- if (ctrl & DBGP_DONE)
- break;
- udelay(1);
- } while (--loop > 0);
-
- if (!loop)
+ ret = readl_poll_timeout_atomic(&ehci_debug->control, ctrl,
+ (ctrl & DBGP_DONE), 1, DBGP_TIMEOUT);
+ if (ret)
return -DBGP_TIMEOUT;
/*
@@ -912,7 +907,7 @@
static void early_dbgp_write(struct console *con, const char *str, u32 n)
{
- int chunk, ret;
+ int chunk;
char buf[DBGP_MAX_PACKET];
int use_cr = 0;
u32 cmd, ctrl;
@@ -951,8 +946,8 @@
buf[chunk] = *str;
}
if (chunk > 0) {
- ret = dbgp_bulk_write(USB_DEBUG_DEVNUM,
- dbgp_endpoint_out, buf, chunk);
+ dbgp_bulk_write(USB_DEBUG_DEVNUM,
+ dbgp_endpoint_out, buf, chunk);
}
}
if (unlikely(reset_run)) {
@@ -1058,7 +1053,8 @@
kgdbdbgp_wait_time = simple_strtoul(ptr, &ptr, 10);
}
kgdb_register_io_module(&kgdbdbgp_io_ops);
- kgdbdbgp_io_ops.is_console = early_dbgp_console.index != -1;
+ if (early_dbgp_console.index != -1)
+ kgdbdbgp_io_ops.cons = &early_dbgp_console;
return 0;
}
diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c
index 5a462a1..6c04341 100644
--- a/drivers/usb/early/xhci-dbc.c
+++ b/drivers/usb/early/xhci-dbc.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* xhci-dbc.c - xHCI debug capability early driver
*
* Copyright (C) 2016 Intel Corporation
@@ -18,10 +18,10 @@
#include <asm/fixmap.h>
#include <linux/bcd.h>
#include <linux/export.h>
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kthread.h>
+#include <linux/usb/xhci-dbgp.h>
#include "../host/xhci.h"
#include "xhci-dbc.h"
@@ -135,6 +135,7 @@
{
u32 result;
+ /* Can not use readl_poll_timeout_atomic() for early boot things */
do {
result = readl(ptr);
result &= mask;
@@ -971,7 +972,7 @@
goto free_and_quit;
}
- base = ioremap_nocache(xdbc.xhci_start, xdbc.xhci_length);
+ base = ioremap(xdbc.xhci_start, xdbc.xhci_length);
if (!base) {
xdbc_trace("failed to remap the io address\n");
ret = -ENOMEM;
diff --git a/drivers/usb/early/xhci-dbc.h b/drivers/usb/early/xhci-dbc.h
index 6e2b726..8b4d71d 100644
--- a/drivers/usb/early/xhci-dbc.h
+++ b/drivers/usb/early/xhci-dbc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* xhci-dbc.h - xHCI debug capability early driver
*
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 09ba3af..2d15257 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -19,8 +19,8 @@
select USB_COMMON
select NLS
help
- USB is a master/slave protocol, organized with one master
- host (such as a PC) controlling up to 127 peripheral devices.
+ USB is a host/device protocol, organized with one host (such as a
+ PC) controlling up to 127 peripheral devices.
The USB hardware is asymmetric, which makes it easier to set up:
you can't connect a "to-the-host" connector to a peripheral.
@@ -485,34 +485,6 @@
Both protocols can work on USB2.0 and USB3.0.
UAS utilizes the USB 3.0 feature called streams support.
-choice
- tristate "USB Gadget precomposed configurations"
- default USB_ETH
- optional
- help
- A Linux "Gadget Driver" talks to the USB Peripheral Controller
- driver through the abstract "gadget" API. Some other operating
- systems call these "client" drivers, of which "class drivers"
- are a subset (implementing a USB device class specification).
- A gadget driver implements one or more USB functions using
- the peripheral hardware.
-
- Gadget drivers are hardware-neutral, or "platform independent",
- except that they sometimes must understand quirks or limitations
- of the particular controllers they work with. For example, when
- a controller doesn't support alternate configurations or provide
- enough of the right types of endpoints, the gadget driver might
- not be able work with that controller, or might need to implement
- a less common variant of a device class protocol.
-
- The available choices each represent a single precomposed USB
- gadget configuration. In the device model, each option contains
- both the device instantiation as a child for a USB gadget
- controller, and the relevant drivers for each function declared
- by the device.
-
source "drivers/usb/gadget/legacy/Kconfig"
-endchoice
-
endif # USB_GADGET
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 6bd3fdb..a980799 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -72,17 +72,17 @@
descriptors = f->ssp_descriptors;
if (descriptors)
break;
- /* FALLTHROUGH */
+ fallthrough;
case USB_SPEED_SUPER:
descriptors = f->ss_descriptors;
if (descriptors)
break;
- /* FALLTHROUGH */
+ fallthrough;
case USB_SPEED_HIGH:
descriptors = f->hs_descriptors;
if (descriptors)
break;
- /* FALLTHROUGH */
+ fallthrough;
default:
descriptors = f->fs_descriptors;
}
@@ -170,20 +170,20 @@
want_comp_desc = 1;
break;
}
- /* fall through */
+ fallthrough;
case USB_SPEED_SUPER:
if (gadget_is_superspeed(g)) {
speed_desc = f->ss_descriptors;
want_comp_desc = 1;
break;
}
- /* fall through */
+ fallthrough;
case USB_SPEED_HIGH:
if (gadget_is_dualspeed(g)) {
speed_desc = f->hs_descriptors;
break;
}
- /* fall through */
+ fallthrough;
default:
speed_desc = f->fs_descriptors;
}
@@ -237,7 +237,7 @@
case USB_ENDPOINT_XFER_ISOC:
/* mult: bits 1:0 of bmAttributes */
_ep->mult = (comp_desc->bmAttributes & 0x3) + 1;
- /* fall through */
+ fallthrough;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
_ep->maxburst = comp_desc->bMaxBurst + 1;
@@ -846,9 +846,9 @@
result = 0;
}
- INFO(cdev, "%s config #%d: %s\n",
- usb_speed_string(gadget->speed),
- number, c ? c->label : "unconfigured");
+ DBG(cdev, "%s config #%d: %s\n",
+ usb_speed_string(gadget->speed),
+ number, c ? c->label : "unconfigured");
if (!c)
goto done;
@@ -1255,7 +1255,7 @@
EXPORT_SYMBOL_GPL(usb_string_id);
/**
- * usb_string_ids() - allocate unused string IDs in batch
+ * usb_string_ids_tab() - allocate unused string IDs in batch
* @cdev: the device whose string descriptor IDs are being allocated
* @str: an array of usb_string objects to assign numbers to
* Context: single threaded during gadget setup
@@ -1648,6 +1648,18 @@
struct usb_function *f = NULL;
u8 endp;
+ if (w_length > USB_COMP_EP0_BUFSIZ) {
+ if (ctrl->bRequestType & USB_DIR_IN) {
+ /* Cast away the const, we are going to overwrite on purpose. */
+ __le16 *temp = (__le16 *)&ctrl->wLength;
+
+ *temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ);
+ w_length = USB_COMP_EP0_BUFSIZ;
+ } else {
+ goto done;
+ }
+ }
+
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion
* when we delegate to it.
@@ -1707,7 +1719,7 @@
if (!gadget_is_dualspeed(gadget) ||
gadget->speed >= USB_SPEED_SUPER)
break;
- /* FALLTHROUGH */
+ fallthrough;
case USB_DT_CONFIG:
value = config_desc(cdev, w_value);
if (value >= 0)
@@ -1932,6 +1944,9 @@
if (w_index != 0x5 || (w_value >> 8))
break;
interface = w_value & 0xFF;
+ if (interface >= MAX_CONFIG_INTERFACES ||
+ !os_desc_cfg->interface[interface])
+ break;
buf[6] = w_index;
count = count_ext_prop(os_desc_cfg,
interface);
@@ -2161,7 +2176,7 @@
if (!cdev->req)
return -ENOMEM;
- cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
+ cdev->req->buf = kzalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
if (!cdev->req->buf)
goto fail;
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 3d4710c..9b7fa53 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -13,8 +13,6 @@
int check_user_usb_string(const char *name,
struct usb_gadget_strings *stringtab_dev)
{
- unsigned primary_lang;
- unsigned sub_lang;
u16 num;
int ret;
@@ -22,17 +20,7 @@
if (ret)
return ret;
- primary_lang = num & 0x3ff;
- sub_lang = num >> 10;
-
- /* simple sanity check for valid langid */
- switch (primary_lang) {
- case 0:
- case 0x62 ... 0xfe:
- case 0x100 ... 0x3ff:
- return -EINVAL;
- }
- if (!sub_lang)
+ if (!usb_validate_langid(num))
return -EINVAL;
stringtab_dev->language = num;
@@ -309,6 +297,47 @@
return ret;
}
+static ssize_t gadget_dev_desc_max_speed_show(struct config_item *item,
+ char *page)
+{
+ enum usb_device_speed speed = to_gadget_info(item)->composite.max_speed;
+
+ return sprintf(page, "%s\n", usb_speed_string(speed));
+}
+
+static ssize_t gadget_dev_desc_max_speed_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct gadget_info *gi = to_gadget_info(item);
+
+ mutex_lock(&gi->lock);
+
+ /* Prevent changing of max_speed after the driver is binded */
+ if (gi->composite.gadget_driver.udc_name)
+ goto err;
+
+ if (strncmp(page, "super-speed-plus", 16) == 0)
+ gi->composite.max_speed = USB_SPEED_SUPER_PLUS;
+ else if (strncmp(page, "super-speed", 11) == 0)
+ gi->composite.max_speed = USB_SPEED_SUPER;
+ else if (strncmp(page, "high-speed", 10) == 0)
+ gi->composite.max_speed = USB_SPEED_HIGH;
+ else if (strncmp(page, "full-speed", 10) == 0)
+ gi->composite.max_speed = USB_SPEED_FULL;
+ else if (strncmp(page, "low-speed", 9) == 0)
+ gi->composite.max_speed = USB_SPEED_LOW;
+ else
+ goto err;
+
+ gi->composite.gadget_driver.max_speed = gi->composite.max_speed;
+
+ mutex_unlock(&gi->lock);
+ return len;
+err:
+ mutex_unlock(&gi->lock);
+ return -EINVAL;
+}
+
CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass);
CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass);
CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol);
@@ -318,6 +347,7 @@
CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice);
CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB);
CONFIGFS_ATTR(gadget_dev_desc_, UDC);
+CONFIGFS_ATTR(gadget_dev_desc_, max_speed);
static struct configfs_attribute *gadget_root_attrs[] = {
&gadget_dev_desc_attr_bDeviceClass,
@@ -329,6 +359,7 @@
&gadget_dev_desc_attr_bcdDevice,
&gadget_dev_desc_attr_bcdUSB,
&gadget_dev_desc_attr_UDC,
+ &gadget_dev_desc_attr_max_speed,
NULL,
};
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index add0f7e..349945e 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -425,9 +425,11 @@
/* we know alt == 0, so this is an activation or a reset */
if (intf == acm->ctrl_id) {
- dev_vdbg(&cdev->gadget->dev,
- "reset acm control interface %d\n", intf);
- usb_ep_disable(acm->notify);
+ if (acm->notify->enabled) {
+ dev_vdbg(&cdev->gadget->dev,
+ "reset acm control interface %d\n", intf);
+ usb_ep_disable(acm->notify);
+ }
if (!acm->notify->desc)
if (config_ep_by_speed(cdev->gadget, f, acm->notify))
@@ -723,6 +725,20 @@
kfree(acm);
}
+static void acm_resume(struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+
+ gserial_resume(&acm->port);
+}
+
+static void acm_suspend(struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+
+ gserial_suspend(&acm->port);
+}
+
static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
{
struct f_serial_opts *opts;
@@ -750,6 +766,8 @@
acm->port_num = opts->port_num;
acm->port.func.unbind = acm_unbind;
acm->port.func.free_func = acm_free_func;
+ acm->port.func.resume = acm_resume;
+ acm->port.func.suspend = acm_suspend;
return &acm->port.func;
}
@@ -771,6 +789,24 @@
.release = acm_attr_release,
};
+#ifdef CONFIG_U_SERIAL_CONSOLE
+
+static ssize_t f_acm_console_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ return gserial_set_console(to_f_serial_opts(item)->port_num,
+ page, count);
+}
+
+static ssize_t f_acm_console_show(struct config_item *item, char *page)
+{
+ return gserial_get_console(to_f_serial_opts(item)->port_num, page);
+}
+
+CONFIGFS_ATTR(f_acm_, console);
+
+#endif /* CONFIG_U_SERIAL_CONSOLE */
+
static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
{
return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
@@ -779,6 +815,9 @@
CONFIGFS_ATTR_RO(f_acm_, port_num);
static struct configfs_attribute *acm_attrs[] = {
+#ifdef CONFIG_U_SERIAL_CONSOLE
+ &f_acm_attr_console,
+#endif
&f_acm_attr_port_num,
NULL,
};
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 95ef8bc..5d38f29 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -296,8 +296,6 @@
goto fail;
eem->port.out_ep = ep;
- status = -ENOMEM;
-
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 94ccf43..bb0d928 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -32,7 +32,7 @@
#include <linux/usb/functionfs.h>
#include <linux/aio.h>
-#include <linux/mmu_context.h>
+#include <linux/kthread.h>
#include <linux/poll.h>
#include <linux/eventfd.h>
@@ -614,7 +614,7 @@
file->private_data = ffs;
ffs_data_opened(ffs);
- return 0;
+ return stream_open(inode, file);
}
static int ffs_ep0_release(struct inode *inode, struct file *file)
@@ -824,13 +824,9 @@
bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
if (io_data->read && ret > 0) {
- mm_segment_t oldfs = get_fs();
-
- set_fs(USER_DS);
- use_mm(io_data->mm);
+ kthread_use_mm(io_data->mm);
ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
- unuse_mm(io_data->mm);
- set_fs(oldfs);
+ kthread_unuse_mm(io_data->mm);
}
io_data->kiocb->ki_complete(io_data->kiocb, ret, ret);
@@ -1156,7 +1152,7 @@
file->private_data = epfile;
ffs_data_opened(epfile->ffs);
- return 0;
+ return stream_open(inode, file);
}
static int ffs_aio_cancel(struct kiocb *kiocb)
@@ -1359,14 +1355,6 @@
return ret;
}
-#ifdef CONFIG_COMPAT
-static long ffs_epfile_compat_ioctl(struct file *file, unsigned code,
- unsigned long value)
-{
- return ffs_epfile_ioctl(file, code, value);
-}
-#endif
-
static const struct file_operations ffs_epfile_operations = {
.llseek = no_llseek,
@@ -1375,9 +1363,7 @@
.read_iter = ffs_epfile_read_iter,
.release = ffs_epfile_release,
.unlocked_ioctl = ffs_epfile_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = ffs_epfile_compat_ioctl,
-#endif
+ .compat_ioctl = compat_ptr_ioctl,
};
@@ -1503,7 +1489,7 @@
Opt_gid,
};
-static const struct fs_parameter_spec ffs_fs_param_specs[] = {
+static const struct fs_parameter_spec ffs_fs_fs_parameters[] = {
fsparam_bool ("no_disconnect", Opt_no_disconnect),
fsparam_u32 ("rmode", Opt_rmode),
fsparam_u32 ("fmode", Opt_fmode),
@@ -1513,11 +1499,6 @@
{}
};
-static const struct fs_parameter_description ffs_fs_fs_parameters = {
- .name = "kAFS",
- .specs = ffs_fs_param_specs,
-};
-
static int ffs_fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
struct ffs_sb_fill_data *data = fc->fs_private;
@@ -1526,7 +1507,7 @@
ENTER();
- opt = fs_parse(fc, &ffs_fs_fs_parameters, param, &result);
+ opt = fs_parse(fc, ffs_fs_fs_parameters, param, &result);
if (opt < 0)
return opt;
@@ -1654,7 +1635,7 @@
.owner = THIS_MODULE,
.name = "functionfs",
.init_fs_context = ffs_fs_init_fs_context,
- .parameters = &ffs_fs_fs_parameters,
+ .parameters = ffs_fs_fs_parameters,
.kill_sb = ffs_fs_kill_sb,
};
MODULE_ALIAS_FS("functionfs");
@@ -1719,7 +1700,7 @@
ffs_data_clear(ffs);
ffs_release_dev(ffs->private_data);
BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
- waitqueue_active(&ffs->ep0req_completion.wait) ||
+ swait_active(&ffs->ep0req_completion.wait) ||
waitqueue_active(&ffs->wait));
destroy_workqueue(ffs->io_completion_wq);
kfree(ffs->dev_name);
@@ -1729,16 +1710,24 @@
static void ffs_data_closed(struct ffs_data *ffs)
{
+ struct ffs_epfile *epfiles;
+ unsigned long flags;
+
ENTER();
if (atomic_dec_and_test(&ffs->opened)) {
if (ffs->no_disconnect) {
ffs->state = FFS_DEACTIVATED;
- if (ffs->epfiles) {
- ffs_epfiles_destroy(ffs->epfiles,
- ffs->eps_count);
- ffs->epfiles = NULL;
- }
+ spin_lock_irqsave(&ffs->eps_lock, flags);
+ epfiles = ffs->epfiles;
+ ffs->epfiles = NULL;
+ spin_unlock_irqrestore(&ffs->eps_lock,
+ flags);
+
+ if (epfiles)
+ ffs_epfiles_destroy(epfiles,
+ ffs->eps_count);
+
if (ffs->setup_state == FFS_SETUP_PENDING)
__ffs_ep0_stall(ffs);
} else {
@@ -1785,17 +1774,34 @@
static void ffs_data_clear(struct ffs_data *ffs)
{
+ struct ffs_epfile *epfiles;
+ unsigned long flags;
+
ENTER();
ffs_closed(ffs);
BUG_ON(ffs->gadget);
- if (ffs->epfiles)
- ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+ spin_lock_irqsave(&ffs->eps_lock, flags);
+ epfiles = ffs->epfiles;
+ ffs->epfiles = NULL;
+ spin_unlock_irqrestore(&ffs->eps_lock, flags);
- if (ffs->ffs_eventfd)
+ /*
+ * potential race possible between ffs_func_eps_disable
+ * & ffs_epfile_release therefore maintaining a local
+ * copy of epfile will save us from use-after-free.
+ */
+ if (epfiles) {
+ ffs_epfiles_destroy(epfiles, ffs->eps_count);
+ ffs->epfiles = NULL;
+ }
+
+ if (ffs->ffs_eventfd) {
eventfd_ctx_put(ffs->ffs_eventfd);
+ ffs->ffs_eventfd = NULL;
+ }
kfree(ffs->raw_descs_data);
kfree(ffs->raw_strings);
@@ -1808,7 +1814,6 @@
ffs_data_clear(ffs);
- ffs->epfiles = NULL;
ffs->raw_descs_data = NULL;
ffs->raw_descs = NULL;
ffs->raw_strings = NULL;
@@ -1937,12 +1942,15 @@
static void ffs_func_eps_disable(struct ffs_function *func)
{
- struct ffs_ep *ep = func->eps;
- struct ffs_epfile *epfile = func->ffs->epfiles;
- unsigned count = func->ffs->eps_count;
+ struct ffs_ep *ep;
+ struct ffs_epfile *epfile;
+ unsigned short count;
unsigned long flags;
spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ count = func->ffs->eps_count;
+ epfile = func->ffs->epfiles;
+ ep = func->eps;
while (count--) {
/* pending requests get nuked */
if (likely(ep->ep))
@@ -1960,14 +1968,18 @@
static int ffs_func_eps_enable(struct ffs_function *func)
{
- struct ffs_data *ffs = func->ffs;
- struct ffs_ep *ep = func->eps;
- struct ffs_epfile *epfile = ffs->epfiles;
- unsigned count = ffs->eps_count;
+ struct ffs_data *ffs;
+ struct ffs_ep *ep;
+ struct ffs_epfile *epfile;
+ unsigned short count;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ ffs = func->ffs;
+ ep = func->eps;
+ epfile = ffs->epfiles;
+ count = ffs->eps_count;
while(count--) {
ep->ep->driver_data = ep;
@@ -2371,7 +2383,7 @@
return _len - len;
}
-/**
+/*
* Validate contents of the buffer from userspace related to OS descriptors.
*/
static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
@@ -2523,7 +2535,7 @@
os_descs_count = get_unaligned_le32(data);
data += 4;
len -= 4;
- };
+ }
/* Read descriptors */
raw_descs = data;
@@ -2746,7 +2758,7 @@
switch (type) {
case FUNCTIONFS_RESUME:
rem_type2 = FUNCTIONFS_SUSPEND;
- /* FALL THROUGH */
+ fallthrough;
case FUNCTIONFS_SUSPEND:
case FUNCTIONFS_SETUP:
rem_type1 = type;
@@ -3532,7 +3544,7 @@
static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
{
- if (strlen(name) >= FIELD_SIZEOF(struct ffs_dev, name))
+ if (strlen(name) >= sizeof_field(struct ffs_dev, name))
return -ENAMETOOLONG;
return ffs_name_dev(to_f_fs_opts(fi)->dev, name);
}
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index e4d7141..6742271 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -253,9 +253,6 @@
if (!count)
return 0;
- if (!access_ok(buffer, count))
- return -EFAULT;
-
spin_lock_irqsave(&hidg->read_spinlock, flags);
#define READ_COND (!list_empty(&hidg->completed_out_req))
@@ -340,9 +337,6 @@
unsigned long flags;
ssize_t status = -ENOMEM;
- if (!access_ok(buffer, count))
- return -EFAULT;
-
spin_lock_irqsave(&hidg->write_spinlock, flags);
if (!hidg->req) {
@@ -498,7 +492,7 @@
break;
default:
ERROR(cdev, "Set report failed %d\n", req->status);
- /* FALLTHROUGH */
+ fallthrough;
case -ECONNABORTED: /* hardware forced ep reset */
case -ECONNRESET: /* request dequeued */
case -ESHUTDOWN: /* disconnect from host */
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 7c96c46..950c943 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -216,6 +216,7 @@
#include <linux/freezer.h>
#include <linux/module.h>
#include <linux/uaccess.h>
+#include <asm/unaligned.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -2038,7 +2039,6 @@
case RELEASE:
case RESERVE:
case SEND_DIAGNOSTIC:
- /* Fall through */
default:
unknown_cmnd:
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 0e083a5..8fff995 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -698,9 +698,9 @@
f_midi_drop_out_substreams(midi);
}
-static void f_midi_in_tasklet(unsigned long data)
+static void f_midi_in_tasklet(struct tasklet_struct *t)
{
- struct f_midi *midi = (struct f_midi *) data;
+ struct f_midi *midi = from_tasklet(midi, t, tasklet);
f_midi_transmit(midi);
}
@@ -875,7 +875,7 @@
int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
midi->gadget = cdev->gadget;
- tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
+ tasklet_setup(&midi->tasklet, f_midi_in_tasklet);
status = f_midi_register_card(midi);
if (status < 0)
goto fail_register;
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 9b19d69..8551272 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -378,7 +378,7 @@
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
- /* .bMaxBurst = 0, */
+ .bMaxBurst = 15,
/* .bmAttributes = 0, */
};
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index 55b7f57..ab26d84 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -432,7 +432,7 @@
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = obex_free_inst;
- ret = gserial_alloc_line(&opts->port_num);
+ ret = gserial_alloc_line_no_console(&opts->port_num);
if (ret) {
kfree(opts);
return ERR_PTR(ret);
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 8b72b19..0b468f5 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -48,7 +48,7 @@
struct usb_ep *in_ep, *out_ep;
struct usb_request *in_req;
- struct usb_request *out_reqv[0];
+ struct usb_request *out_reqv[];
};
static int phonet_rxq_size = 17;
@@ -212,7 +212,7 @@
case -ESHUTDOWN: /* disconnected */
case -ECONNRESET: /* disabled */
dev->stats.tx_aborted_errors++;
- /* fall through */
+ fallthrough;
default:
dev->stats.tx_errors++;
}
@@ -360,7 +360,7 @@
/* Do resubmit in these cases: */
case -EOVERFLOW: /* request buffer overflow */
dev->stats.rx_over_errors++;
- /* fall through */
+ fallthrough;
default:
dev->stats.rx_errors++;
break;
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 2a1868b..236ecc9 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -293,7 +293,7 @@
/* data overrun */
case -EOVERFLOW:
- /* FALLTHROUGH */
+ fallthrough;
default:
DBG(dev, "rx status %d\n", status);
@@ -312,7 +312,7 @@
switch (req->status) {
default:
VDBG(dev, "tx err %d\n", req->status);
- /* FALLTHROUGH */
+ fallthrough;
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
break;
@@ -346,6 +346,11 @@
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->interface < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENODEV;
+ }
+
if (!dev->printer_cdev_open) {
dev->printer_cdev_open = 1;
fd->private_data = dev;
@@ -440,6 +445,12 @@
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->interface < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return -ENODEV;
+ }
+
/* We will use this flag later to check if a printer reset happened
* after we turn interrupts back on.
*/
@@ -571,6 +582,12 @@
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->interface < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return -ENODEV;
+ }
+
/* Check if a printer reset happens while we have interrupts on */
dev->reset_printer = 0;
@@ -677,6 +694,13 @@
inode_lock(inode);
spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->interface < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ inode_unlock(inode);
+ return -ENODEV;
+ }
+
tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
spin_unlock_irqrestore(&dev->lock, flags);
@@ -699,6 +723,13 @@
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->interface < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ mutex_unlock(&dev->lock_printer_io);
+ return EPOLLERR | EPOLLHUP;
+ }
+
setup_rx_reqs(dev);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
@@ -732,6 +763,11 @@
spin_lock_irqsave(&dev->lock, flags);
+ if (dev->interface < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENODEV;
+ }
+
switch (code) {
case GADGET_GET_PRINTER_STATUS:
status = (int)dev->printer_status;
@@ -929,7 +965,7 @@
if (!w_value && !w_length &&
!(USB_DIR_IN & ctrl->bRequestType))
break;
- /* fall through */
+ fallthrough;
default:
return false;
}
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index f20c55d..ee95e8f 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -428,7 +428,7 @@
DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
ep->name, status,
req->actual, req->length);
- /* FALLTHROUGH */
+ fallthrough;
case 0:
if (ep != rndis->notify)
break;
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 6db973d..1ed8ff0 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -266,6 +266,24 @@
.release = serial_attr_release,
};
+#ifdef CONFIG_U_SERIAL_CONSOLE
+
+static ssize_t f_serial_console_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ return gserial_set_console(to_f_serial_opts(item)->port_num,
+ page, count);
+}
+
+static ssize_t f_serial_console_show(struct config_item *item, char *page)
+{
+ return gserial_get_console(to_f_serial_opts(item)->port_num, page);
+}
+
+CONFIGFS_ATTR(f_serial_, console);
+
+#endif /* CONFIG_U_SERIAL_CONSOLE */
+
static ssize_t f_serial_port_num_show(struct config_item *item, char *page)
{
return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
@@ -274,6 +292,9 @@
CONFIGFS_ATTR_RO(f_serial_, port_num);
static struct configfs_attribute *acm_attrs[] = {
+#ifdef CONFIG_U_SERIAL_CONSOLE
+ &f_serial_attr_console,
+#endif
&f_serial_attr_port_num,
NULL,
};
@@ -327,6 +348,20 @@
usb_free_all_descriptors(f);
}
+static void gser_resume(struct usb_function *f)
+{
+ struct f_gser *gser = func_to_gser(f);
+
+ gserial_resume(&gser->port);
+}
+
+static void gser_suspend(struct usb_function *f)
+{
+ struct f_gser *gser = func_to_gser(f);
+
+ gserial_suspend(&gser->port);
+}
+
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
{
struct f_gser *gser;
@@ -348,6 +383,8 @@
gser->port.func.set_alt = gser_set_alt;
gser->port.func.disable = gser_disable;
gser->port.func.free_func = gser_free;
+ gser->port.func.resume = gser_resume;
+ gser->port.func.suspend = gser_suspend;
return &gser->port.func;
}
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index 282737e..2c65a9b 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -583,6 +583,7 @@
if (is_iso) {
switch (speed) {
+ case USB_SPEED_SUPER_PLUS:
case USB_SPEED_SUPER:
size = ss->isoc_maxpacket *
(ss->isoc_mult + 1) *
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 41a10bc..5a2e9ce 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -392,12 +392,12 @@
fu->flags = USBG_IS_BOT;
- config_ep_by_speed(gadget, f, fu->ep_in);
+ config_ep_by_speed_and_alt(gadget, f, fu->ep_in, USB_G_ALT_INT_BBB);
ret = usb_ep_enable(fu->ep_in);
if (ret)
goto err_b_in;
- config_ep_by_speed(gadget, f, fu->ep_out);
+ config_ep_by_speed_and_alt(gadget, f, fu->ep_out, USB_G_ALT_INT_BBB);
ret = usb_ep_enable(fu->ep_out);
if (ret)
goto err_b_out;
@@ -531,6 +531,7 @@
stream->req_in->sg = se_cmd->t_data_sg;
}
+ stream->req_in->is_last = 1;
stream->req_in->complete = uasp_status_data_cmpl;
stream->req_in->length = se_cmd->data_length;
stream->req_in->context = cmd;
@@ -554,6 +555,7 @@
*/
iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
iu->status = se_cmd->scsi_status;
+ stream->req_status->is_last = 1;
stream->req_status->context = cmd;
stream->req_status->length = se_cmd->scsi_sense_length + 16;
stream->req_status->buf = iu;
@@ -847,24 +849,24 @@
fu->flags = USBG_IS_UAS;
- if (gadget->speed == USB_SPEED_SUPER)
+ if (gadget->speed >= USB_SPEED_SUPER)
fu->flags |= USBG_USE_STREAMS;
- config_ep_by_speed(gadget, f, fu->ep_in);
+ config_ep_by_speed_and_alt(gadget, f, fu->ep_in, USB_G_ALT_INT_UAS);
ret = usb_ep_enable(fu->ep_in);
if (ret)
goto err_b_in;
- config_ep_by_speed(gadget, f, fu->ep_out);
+ config_ep_by_speed_and_alt(gadget, f, fu->ep_out, USB_G_ALT_INT_UAS);
ret = usb_ep_enable(fu->ep_out);
if (ret)
goto err_b_out;
- config_ep_by_speed(gadget, f, fu->ep_cmd);
+ config_ep_by_speed_and_alt(gadget, f, fu->ep_cmd, USB_G_ALT_INT_UAS);
ret = usb_ep_enable(fu->ep_cmd);
if (ret)
goto err_cmd;
- config_ep_by_speed(gadget, f, fu->ep_status);
+ config_ep_by_speed_and_alt(gadget, f, fu->ep_status, USB_G_ALT_INT_UAS);
ret = usb_ep_enable(fu->ep_status);
if (ret)
goto err_status;
@@ -992,6 +994,7 @@
req->sg = se_cmd->t_data_sg;
}
+ req->is_last = 1;
req->complete = usbg_data_write_cmpl;
req->length = se_cmd->data_length;
req->context = cmd;
@@ -1050,7 +1053,8 @@
transport_init_se_cmd(se_cmd,
tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
- cmd->prio_attr, cmd->sense_iu.sense);
+ cmd->prio_attr, cmd->sense_iu.sense,
+ cmd->unpacked_lun);
goto out;
}
@@ -1147,7 +1151,7 @@
default:
pr_debug_once("Unsupported prio_attr: %02x.\n",
cmd_iu->prio_attr);
- /* fall through */
+ fallthrough;
case UAS_SIMPLE_TAG:
cmd->prio_attr = TCM_SIMPLE_TAG;
break;
@@ -1180,7 +1184,8 @@
transport_init_se_cmd(se_cmd,
tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
- cmd->prio_attr, cmd->sense_iu.sense);
+ cmd->prio_attr, cmd->sense_iu.sense,
+ cmd->unpacked_lun);
goto out;
}
@@ -2095,6 +2100,16 @@
usb_composite_setup_continue(fu->function.config->cdev);
}
+static int tcm_get_alt(struct usb_function *f, unsigned intf)
+{
+ if (intf == bot_intf_desc.bInterfaceNumber)
+ return USB_G_ALT_INT_BBB;
+ if (intf == uasp_intf_desc.bInterfaceNumber)
+ return USB_G_ALT_INT_UAS;
+
+ return -EOPNOTSUPP;
+}
+
static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_uas *fu = to_f_uas(f);
@@ -2302,6 +2317,7 @@
fu->function.bind = tcm_bind;
fu->function.unbind = tcm_unbind;
fu->function.set_alt = tcm_set_alt;
+ fu->function.get_alt = tcm_get_alt;
fu->function.setup = tcm_setup;
fu->function.disable = tcm_disable;
fu->function.free_func = tcm_free;
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index 06ee6e9..e2d7f69 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -754,8 +754,6 @@
audio->out_ep = ep;
audio->out_ep->desc = &as_out_ep_desc;
- status = -ENOMEM;
-
/* copy descriptors, and track endpoint copies */
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
NULL);
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index dd960ce..11cc605 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -176,7 +176,7 @@
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
/* .bTerminalID = DYNAMIC */
- .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED),
+ .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE),
.bAssocTerminal = 0,
/* .bCSourceID = DYNAMIC */
.iChannelNames = 0,
@@ -204,7 +204,7 @@
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
/* .bTerminalID = DYNAMIC */
- .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED),
+ .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER),
.bAssocTerminal = 0,
/* .bSourceID = DYNAMIC */
/* .bCSourceID = DYNAMIC */
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index c03b67a..f48a00e 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -428,7 +428,7 @@
video_set_drvdata(&uvc->vdev, uvc);
- ret = video_register_device(&uvc->vdev, VFL_TYPE_GRABBER, -1);
+ ret = video_register_device(&uvc->vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0)
return ret;
@@ -745,20 +745,20 @@
/* Initialise video. */
ret = uvcg_video_init(&uvc->video, uvc);
if (ret < 0)
- goto error;
+ goto v4l2_error;
/* Register a V4L2 device. */
ret = uvc_register_video(uvc);
if (ret < 0) {
uvcg_err(f, "failed to register video device\n");
- goto error;
+ goto v4l2_error;
}
return 0;
-error:
+v4l2_error:
v4l2_device_unregister(&uvc->v4l2_dev);
-
+error:
if (uvc->control_req)
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
kfree(uvc->control_buf);
diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index a81a177..1db972d 100644
--- a/drivers/usb/gadget/function/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* f_uvc.h -- USB Video Class Gadget driver
*
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 04c142c..4150de9 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -72,7 +72,7 @@
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-static const struct file_operations rndis_proc_fops;
+static const struct proc_ops rndis_proc_ops;
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
@@ -637,14 +637,18 @@
rndis_set_cmplt_type *resp;
rndis_resp_t *r;
+ BufLength = le32_to_cpu(buf->InformationBufferLength);
+ BufOffset = le32_to_cpu(buf->InformationBufferOffset);
+ if ((BufLength > RNDIS_MAX_TOTAL_SIZE) ||
+ (BufOffset > RNDIS_MAX_TOTAL_SIZE) ||
+ (BufOffset + 8 >= RNDIS_MAX_TOTAL_SIZE))
+ return -EINVAL;
+
r = rndis_add_response(params, sizeof(rndis_set_cmplt_type));
if (!r)
return -ENOMEM;
resp = (rndis_set_cmplt_type *)r->buf;
- BufLength = le32_to_cpu(buf->InformationBufferLength);
- BufOffset = le32_to_cpu(buf->InformationBufferOffset);
-
#ifdef VERBOSE_DEBUG
pr_debug("%s: Length: %d\n", __func__, BufLength);
pr_debug("%s: Offset: %d\n", __func__, BufOffset);
@@ -902,7 +906,7 @@
sprintf(name, NAME_TEMPLATE, i);
proc_entry = proc_create_data(name, 0660, NULL,
- &rndis_proc_fops, params);
+ &rndis_proc_ops, params);
if (!proc_entry) {
kfree(params);
rndis_put_nr(i);
@@ -919,6 +923,7 @@
params->resp_avail = resp_avail;
params->v = v;
INIT_LIST_HEAD(¶ms->resp_queue);
+ spin_lock_init(¶ms->resp_lock);
pr_debug("%s: configNr = %d\n", __func__, i);
return params;
@@ -1012,12 +1017,14 @@
{
rndis_resp_t *r, *n;
+ spin_lock(¶ms->resp_lock);
list_for_each_entry_safe(r, n, ¶ms->resp_queue, list) {
if (r->buf == buf) {
list_del(&r->list);
kfree(r);
}
}
+ spin_unlock(¶ms->resp_lock);
}
EXPORT_SYMBOL_GPL(rndis_free_response);
@@ -1027,14 +1034,17 @@
if (!length) return NULL;
+ spin_lock(¶ms->resp_lock);
list_for_each_entry_safe(r, n, ¶ms->resp_queue, list) {
if (!r->send) {
r->send = 1;
*length = r->length;
+ spin_unlock(¶ms->resp_lock);
return r->buf;
}
}
+ spin_unlock(¶ms->resp_lock);
return NULL;
}
EXPORT_SYMBOL_GPL(rndis_get_next_response);
@@ -1051,7 +1061,9 @@
r->length = length;
r->send = 0;
+ spin_lock(¶ms->resp_lock);
list_add_tail(&r->list, ¶ms->resp_queue);
+ spin_unlock(¶ms->resp_lock);
return r;
}
@@ -1164,13 +1176,12 @@
return single_open(file, rndis_proc_show, PDE_DATA(inode));
}
-static const struct file_operations rndis_proc_fops = {
- .owner = THIS_MODULE,
- .open = rndis_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = rndis_proc_write,
+static const struct proc_ops rndis_proc_ops = {
+ .proc_open = rndis_proc_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+ .proc_write = rndis_proc_write,
};
#define NAME_TEMPLATE "driver/rndis-%03d"
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
index c7e3a70..6206b8b 100644
--- a/drivers/usb/gadget/function/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* RNDIS Definitions for Remote NDIS
*
@@ -174,6 +174,7 @@
void (*resp_avail)(void *v);
void *v;
struct list_head resp_queue;
+ spinlock_t resp_lock;
} rndis_params;
/* RNDIS Message parser and other useless functions */
diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index e5e3a25..bdeb1e2 100644
--- a/drivers/usb/gadget/function/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
@@ -172,11 +172,6 @@
DATA_DIR_NONE
};
-static inline u32 get_unaligned_be24(u8 *buf)
-{
- return 0xffffff & (u32) get_unaligned_be32(buf - 1);
-}
-
static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
{
return container_of(dev, struct fsg_lun, dev);
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 4e01ba0..95605b1 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -244,18 +244,6 @@
return bytes_to_frames(substream->runtime, prm->hw_ptr);
}
-static int uac_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int uac_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int uac_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_uac_chip *uac = snd_pcm_substream_chip(substream);
@@ -331,9 +319,6 @@
static const struct snd_pcm_ops uac_pcm_ops = {
.open = uac_pcm_open,
.close = uac_pcm_null,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = uac_pcm_hw_params,
- .hw_free = uac_pcm_hw_free,
.trigger = uac_pcm_trigger,
.pointer = uac_pcm_pointer,
.prepare = uac_pcm_null,
@@ -386,7 +371,7 @@
ep = audio_dev->out_ep;
prm = &uac->c_prm;
config_ep_by_speed(gadget, &audio_dev->func, ep);
- req_len = prm->max_psize;
+ req_len = ep->maxpacket;
prm->ep_enabled = true;
usb_ep_enable(ep);
@@ -404,7 +389,7 @@
req->context = &prm->ureq[i];
req->length = req_len;
req->complete = u_audio_iso_complete;
- req->buf = prm->rbuf + i * prm->max_psize;
+ req->buf = prm->rbuf + i * ep->maxpacket;
}
if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
@@ -432,7 +417,7 @@
struct usb_ep *ep;
struct uac_rtd_params *prm;
struct uac_params *params = &audio_dev->params;
- unsigned int factor, rate;
+ unsigned int factor;
const struct usb_endpoint_descriptor *ep_desc;
int req_len, i;
@@ -451,13 +436,15 @@
/* pre-compute some values for iso_complete() */
uac->p_framesize = params->p_ssize *
num_channels(params->p_chmask);
- rate = params->p_srate * uac->p_framesize;
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
- uac->p_pktsize = min_t(unsigned int, rate / uac->p_interval,
- prm->max_psize);
+ uac->p_pktsize = min_t(unsigned int,
+ uac->p_framesize *
+ (params->p_srate / uac->p_interval),
+ ep->maxpacket);
- if (uac->p_pktsize < prm->max_psize)
- uac->p_pktsize_residue = rate % uac->p_interval;
+ if (uac->p_pktsize < ep->maxpacket)
+ uac->p_pktsize_residue = uac->p_framesize *
+ (params->p_srate % uac->p_interval);
else
uac->p_pktsize_residue = 0;
@@ -480,7 +467,7 @@
req->context = &prm->ureq[i];
req->length = req_len;
req->complete = u_audio_iso_complete;
- req->buf = prm->rbuf + i * prm->max_psize;
+ req->buf = prm->rbuf + i * ep->maxpacket;
}
if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))
@@ -594,8 +581,8 @@
strlcpy(card->shortname, card_name, sizeof(card->shortname));
sprintf(card->longname, "%s %i", card_name, card->dev->id);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 0, BUFF_SIZE_MAX);
err = snd_card_register(card);
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 81d3d4e..5ea6b86 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_audio.h -- interface to USB gadget "ALSA sound card" utilities
*
diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h
index 098ece5..77cfb89 100644
--- a/drivers/usb/gadget/function/u_ecm.h
+++ b/drivers/usb/gadget/function/u_ecm.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ecm.h
*
diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h
index 921386a..3bd85df 100644
--- a/drivers/usb/gadget/function/u_eem.h
+++ b/drivers/usb/gadget/function/u_eem.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_eem.h
*
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 57da62e..a40be8b 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -322,7 +322,7 @@
/* data overrun */
case -EOVERFLOW:
dev->net->stats.rx_over_errors++;
- /* FALLTHROUGH */
+ fallthrough;
default:
dev->net->stats.rx_errors++;
@@ -445,7 +445,7 @@
default:
dev->net->stats.tx_errors++;
VDBG(dev, "tx err %d\n", req->status);
- /* FALLTHROUGH */
+ fallthrough;
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
dev_kfree_skb_any(skb);
@@ -732,7 +732,7 @@
.name = "gadget",
};
-/**
+/*
* gether_setup_name - initialize one ethernet-over-usb link
* @g: gadget to associated with these links
* @ethaddr: NULL, or a buffer in which the ethernet address of the
@@ -860,19 +860,23 @@
{
struct eth_dev *dev;
struct usb_gadget *g;
- struct sockaddr sa;
int status;
if (!net->dev.parent)
return -EINVAL;
dev = netdev_priv(net);
g = dev->gadget;
+
+ memcpy(net->dev_addr, dev->dev_mac, ETH_ALEN);
+ net->addr_assign_type = NET_ADDR_RANDOM;
+
status = register_netdev(net);
if (status < 0) {
dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
return status;
} else {
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
+ INFO(dev, "MAC %pM\n", dev->dev_mac);
/* two kinds of host-initiated state changes:
* - iff DATA transfer is active, carrier is "on"
@@ -880,15 +884,6 @@
*/
netif_carrier_off(net);
}
- sa.sa_family = net->type;
- memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN);
- rtnl_lock();
- status = dev_set_mac_address(net, &sa, NULL);
- rtnl_unlock();
- if (status)
- pr_warn("cannot set self ethernet address: %d\n", status);
- else
- INFO(dev, "MAC %pM\n", dev->dev_mac);
return status;
}
@@ -1014,7 +1009,7 @@
}
EXPORT_SYMBOL_GPL(gether_get_ifname);
-/**
+/*
* gether_cleanup - remove Ethernet-over-USB device
* Context: may sleep
*
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 332307d..10dd640 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_ether.h -- interface to USB gadget "ethernet link" utilities
*
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index 5b1d477..f982e18 100644
--- a/drivers/usb/gadget/function/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ether_configfs.h
*
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index f9b0cf6..f102ec2 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_fs.h
*
diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h
index ce4f076..2f7a373 100644
--- a/drivers/usb/gadget/function/u_gether.h
+++ b/drivers/usb/gadget/function/u_gether.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_gether.h
*
diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h
index 1594bfa..84e6da3 100644
--- a/drivers/usb/gadget/function/u_hid.h
+++ b/drivers/usb/gadget/function/u_hid.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_hid.h
*
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
index 29bf006..f6e14af 100644
--- a/drivers/usb/gadget/function/u_midi.h
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_midi.h
*
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index 70da320..5408854 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ncm.h
*
diff --git a/drivers/usb/gadget/function/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h
index 12fb613..c53233b 100644
--- a/drivers/usb/gadget/function/u_phonet.h
+++ b/drivers/usb/gadget/function/u_phonet.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_phonet.h - interface to Phonet
*
diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h
index 7879776..318205f 100644
--- a/drivers/usb/gadget/function/u_printer.h
+++ b/drivers/usb/gadget/function/u_printer.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_printer.h
*
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index 1e148b7..a8c409b 100644
--- a/drivers/usb/gadget/function/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_rndis.h
*
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 038c445..2caccbb 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -82,14 +82,13 @@
#define GS_CONSOLE_BUF_SIZE 8192
/* console info */
-struct gscons_info {
- struct gs_port *port;
- struct task_struct *console_thread;
- struct kfifo con_buf;
- /* protect the buf and busy flag */
- spinlock_t con_lock;
- int req_busy;
- struct usb_request *console_req;
+struct gs_console {
+ struct console console;
+ struct work_struct work;
+ spinlock_t lock;
+ struct usb_request *req;
+ struct kfifo buf;
+ size_t missed;
};
/*
@@ -101,8 +100,10 @@
spinlock_t port_lock; /* guard port_* access */
struct gserial *port_usb;
+#ifdef CONFIG_U_SERIAL_CONSOLE
+ struct gs_console *console;
+#endif
- bool openclose; /* open/close in progress */
u8 port_num;
struct list_head read_pool;
@@ -119,6 +120,8 @@
wait_queue_head_t drain_wait; /* wait while writes drain */
bool write_busy;
wait_queue_head_t close_wait;
+ bool suspended; /* port suspended */
+ bool start_delayed; /* delay start when suspended */
/* REVISIT this state ... */
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@@ -383,7 +386,7 @@
/* presumably a transient fault */
pr_warn("ttyGS%d: unexpected RX status %d\n",
port->port_num, req->status);
- /* FALLTHROUGH */
+ fallthrough;
case 0:
/* normal completion */
break;
@@ -469,7 +472,7 @@
/* presumably a transient fault */
pr_warn("%s: unexpected %s status %d\n",
__func__, ep->name, req->status);
- /* FALL THROUGH */
+ fallthrough;
case 0:
/* normal completion */
gs_start_tx(port);
@@ -524,7 +527,7 @@
/**
* gs_start_io - start USB I/O streams
- * @dev: encapsulates endpoints to use
+ * @port: port to use
* Context: holding port_lock; port_tty and port_usb are non-null
*
* We only start I/O when something is connected to both sides of
@@ -588,109 +591,79 @@
{
int port_num = tty->index;
struct gs_port *port;
- int status;
+ int status = 0;
- do {
- mutex_lock(&ports[port_num].lock);
- port = ports[port_num].port;
- if (!port)
- status = -ENODEV;
- else {
- spin_lock_irq(&port->port_lock);
+ mutex_lock(&ports[port_num].lock);
+ port = ports[port_num].port;
+ if (!port) {
+ status = -ENODEV;
+ goto out;
+ }
- /* already open? Great. */
- if (port->port.count) {
- status = 0;
- port->port.count++;
-
- /* currently opening/closing? wait ... */
- } else if (port->openclose) {
- status = -EBUSY;
-
- /* ... else we do the work */
- } else {
- status = -EAGAIN;
- port->openclose = true;
- }
- spin_unlock_irq(&port->port_lock);
- }
- mutex_unlock(&ports[port_num].lock);
-
- switch (status) {
- default:
- /* fully handled */
- return status;
- case -EAGAIN:
- /* must do the work */
- break;
- case -EBUSY:
- /* wait for EAGAIN task to finish */
- msleep(1);
- /* REVISIT could have a waitchannel here, if
- * concurrent open performance is important
- */
- break;
- }
- } while (status != -EAGAIN);
-
- /* Do the "real open" */
spin_lock_irq(&port->port_lock);
/* allocate circular buffer on first open */
if (!kfifo_initialized(&port->port_write_buf)) {
spin_unlock_irq(&port->port_lock);
+
+ /*
+ * portmaster's mutex still protects from simultaneous open(),
+ * and close() can't happen, yet.
+ */
+
status = kfifo_alloc(&port->port_write_buf,
WRITE_BUF_SIZE, GFP_KERNEL);
- spin_lock_irq(&port->port_lock);
-
if (status) {
pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
- port->port_num, tty, file);
- port->openclose = false;
- goto exit_unlock_port;
+ port_num, tty, file);
+ goto out;
}
+
+ spin_lock_irq(&port->port_lock);
}
- /* REVISIT if REMOVED (ports[].port NULL), abort the open
- * to let rmmod work faster (but this way isn't wrong).
- */
-
- /* REVISIT maybe wait for "carrier detect" */
+ /* already open? Great. */
+ if (port->port.count++)
+ goto exit_unlock_port;
tty->driver_data = port;
port->port.tty = tty;
- port->port.count = 1;
- port->openclose = false;
-
/* if connected, start the I/O stream */
if (port->port_usb) {
- struct gserial *gser = port->port_usb;
+ /* if port is suspended, wait resume to start I/0 stream */
+ if (!port->suspended) {
+ struct gserial *gser = port->port_usb;
- pr_debug("gs_open: start ttyGS%d\n", port->port_num);
- gs_start_io(port);
+ pr_debug("gs_open: start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
- if (gser->connect)
- gser->connect(gser);
+ if (gser->connect)
+ gser->connect(gser);
+ } else {
+ pr_debug("delay start of ttyGS%d\n", port->port_num);
+ port->start_delayed = true;
+ }
}
pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
- status = 0;
-
exit_unlock_port:
spin_unlock_irq(&port->port_lock);
+out:
+ mutex_unlock(&ports[port_num].lock);
return status;
}
-static int gs_writes_finished(struct gs_port *p)
+static int gs_close_flush_done(struct gs_port *p)
{
int cond;
- /* return true on disconnect or empty buffer */
+ /* return true on disconnect or empty buffer or if raced with open() */
spin_lock_irq(&p->port_lock);
- cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf);
+ cond = p->port_usb == NULL || !kfifo_len(&p->port_write_buf) ||
+ p->port.count > 1;
spin_unlock_irq(&p->port_lock);
return cond;
@@ -704,6 +677,7 @@
spin_lock_irq(&port->port_lock);
if (port->port.count != 1) {
+raced_with_open:
if (port->port.count == 0)
WARN_ON(1);
else
@@ -713,14 +687,8 @@
pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
- /* mark port as closing but in use; we can drop port lock
- * and sleep if necessary
- */
- port->openclose = true;
- port->port.count = 0;
-
gser = port->port_usb;
- if (gser && gser->disconnect)
+ if (gser && !port->suspended && gser->disconnect)
gser->disconnect(gser);
/* wait for circular write buffer to drain, disconnect, or at
@@ -729,9 +697,13 @@
if (kfifo_len(&port->port_write_buf) > 0 && gser) {
spin_unlock_irq(&port->port_lock);
wait_event_interruptible_timeout(port->drain_wait,
- gs_writes_finished(port),
+ gs_close_flush_done(port),
GS_CLOSE_TIMEOUT * HZ);
spin_lock_irq(&port->port_lock);
+
+ if (port->port.count != 1)
+ goto raced_with_open;
+
gser = port->port_usb;
}
@@ -744,10 +716,10 @@
else
kfifo_reset(&port->port_write_buf);
+ port->start_delayed = false;
+ port->port.count = 0;
port->port.tty = NULL;
- port->openclose = false;
-
pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
port->port_num, tty, file);
@@ -891,50 +863,23 @@
#ifdef CONFIG_U_SERIAL_CONSOLE
-static struct gscons_info gscons_info;
-static struct console gserial_cons;
-
-static struct usb_request *gs_request_new(struct usb_ep *ep)
+static void gs_console_complete_out(struct usb_ep *ep, struct usb_request *req)
{
- struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC);
- if (!req)
- return NULL;
-
- req->buf = kmalloc(ep->maxpacket, GFP_ATOMIC);
- if (!req->buf) {
- usb_ep_free_request(ep, req);
- return NULL;
- }
-
- return req;
-}
-
-static void gs_request_free(struct usb_request *req, struct usb_ep *ep)
-{
- if (!req)
- return;
-
- kfree(req->buf);
- usb_ep_free_request(ep, req);
-}
-
-static void gs_complete_out(struct usb_ep *ep, struct usb_request *req)
-{
- struct gscons_info *info = &gscons_info;
+ struct gs_console *cons = req->context;
switch (req->status) {
default:
pr_warn("%s: unexpected %s status %d\n",
__func__, ep->name, req->status);
- /* fall through */
+ fallthrough;
case 0:
/* normal completion */
- spin_lock(&info->con_lock);
- info->req_busy = 0;
- spin_unlock(&info->con_lock);
-
- wake_up_process(info->console_thread);
+ spin_lock(&cons->lock);
+ req->length = 0;
+ schedule_work(&cons->work);
+ spin_unlock(&cons->lock);
break;
+ case -ECONNRESET:
case -ESHUTDOWN:
/* disconnect */
pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
@@ -942,190 +887,250 @@
}
}
-static int gs_console_connect(int port_num)
+static void __gs_console_push(struct gs_console *cons)
{
- struct gscons_info *info = &gscons_info;
- struct gs_port *port;
+ struct usb_request *req = cons->req;
struct usb_ep *ep;
+ size_t size;
- if (port_num != gserial_cons.index) {
- pr_err("%s: port num [%d] is not support console\n",
- __func__, port_num);
- return -ENXIO;
+ if (!req)
+ return; /* disconnected */
+
+ if (req->length)
+ return; /* busy */
+
+ ep = cons->console.data;
+ size = kfifo_out(&cons->buf, req->buf, ep->maxpacket);
+ if (!size)
+ return;
+
+ if (cons->missed && ep->maxpacket >= 64) {
+ char buf[64];
+ size_t len;
+
+ len = sprintf(buf, "\n[missed %zu bytes]\n", cons->missed);
+ kfifo_in(&cons->buf, buf, len);
+ cons->missed = 0;
}
- port = ports[port_num].port;
- ep = port->port_usb->in;
- if (!info->console_req) {
- info->console_req = gs_request_new(ep);
- if (!info->console_req)
- return -ENOMEM;
- info->console_req->complete = gs_complete_out;
- }
-
- info->port = port;
- spin_lock(&info->con_lock);
- info->req_busy = 0;
- spin_unlock(&info->con_lock);
- pr_vdebug("port[%d] console connect!\n", port_num);
- return 0;
+ req->length = size;
+ if (usb_ep_queue(ep, req, GFP_ATOMIC))
+ req->length = 0;
}
-static void gs_console_disconnect(struct usb_ep *ep)
+static void gs_console_work(struct work_struct *work)
{
- struct gscons_info *info = &gscons_info;
- struct usb_request *req = info->console_req;
+ struct gs_console *cons = container_of(work, struct gs_console, work);
- gs_request_free(req, ep);
- info->console_req = NULL;
-}
+ spin_lock_irq(&cons->lock);
-static int gs_console_thread(void *data)
-{
- struct gscons_info *info = &gscons_info;
- struct gs_port *port;
- struct usb_request *req;
- struct usb_ep *ep;
- int xfer, ret, count, size;
+ __gs_console_push(cons);
- do {
- port = info->port;
- set_current_state(TASK_INTERRUPTIBLE);
- if (!port || !port->port_usb
- || !port->port_usb->in || !info->console_req)
- goto sched;
-
- req = info->console_req;
- ep = port->port_usb->in;
-
- spin_lock_irq(&info->con_lock);
- count = kfifo_len(&info->con_buf);
- size = ep->maxpacket;
-
- if (count > 0 && !info->req_busy) {
- set_current_state(TASK_RUNNING);
- if (count < size)
- size = count;
-
- xfer = kfifo_out(&info->con_buf, req->buf, size);
- req->length = xfer;
-
- spin_unlock(&info->con_lock);
- ret = usb_ep_queue(ep, req, GFP_ATOMIC);
- spin_lock(&info->con_lock);
- if (ret < 0)
- info->req_busy = 0;
- else
- info->req_busy = 1;
-
- spin_unlock_irq(&info->con_lock);
- } else {
- spin_unlock_irq(&info->con_lock);
-sched:
- if (kthread_should_stop()) {
- set_current_state(TASK_RUNNING);
- break;
- }
- schedule();
- }
- } while (1);
-
- return 0;
-}
-
-static int gs_console_setup(struct console *co, char *options)
-{
- struct gscons_info *info = &gscons_info;
- int status;
-
- info->port = NULL;
- info->console_req = NULL;
- info->req_busy = 0;
- spin_lock_init(&info->con_lock);
-
- status = kfifo_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL);
- if (status) {
- pr_err("%s: allocate console buffer failed\n", __func__);
- return status;
- }
-
- info->console_thread = kthread_create(gs_console_thread,
- co, "gs_console");
- if (IS_ERR(info->console_thread)) {
- pr_err("%s: cannot create console thread\n", __func__);
- kfifo_free(&info->con_buf);
- return PTR_ERR(info->console_thread);
- }
- wake_up_process(info->console_thread);
-
- return 0;
+ spin_unlock_irq(&cons->lock);
}
static void gs_console_write(struct console *co,
const char *buf, unsigned count)
{
- struct gscons_info *info = &gscons_info;
+ struct gs_console *cons = container_of(co, struct gs_console, console);
unsigned long flags;
+ size_t n;
- spin_lock_irqsave(&info->con_lock, flags);
- kfifo_in(&info->con_buf, buf, count);
- spin_unlock_irqrestore(&info->con_lock, flags);
+ spin_lock_irqsave(&cons->lock, flags);
- wake_up_process(info->console_thread);
+ n = kfifo_in(&cons->buf, buf, count);
+ if (n < count)
+ cons->missed += count - n;
+
+ if (cons->req && !cons->req->length)
+ schedule_work(&cons->work);
+
+ spin_unlock_irqrestore(&cons->lock, flags);
}
static struct tty_driver *gs_console_device(struct console *co, int *index)
{
- struct tty_driver **p = (struct tty_driver **)co->data;
-
- if (!*p)
- return NULL;
-
*index = co->index;
- return *p;
+ return gs_tty_driver;
}
-static struct console gserial_cons = {
- .name = "ttyGS",
- .write = gs_console_write,
- .device = gs_console_device,
- .setup = gs_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &gs_tty_driver,
-};
-
-static void gserial_console_init(void)
+static int gs_console_connect(struct gs_port *port)
{
- register_console(&gserial_cons);
+ struct gs_console *cons = port->console;
+ struct usb_request *req;
+ struct usb_ep *ep;
+
+ if (!cons)
+ return 0;
+
+ ep = port->port_usb->in;
+ req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+ if (!req)
+ return -ENOMEM;
+ req->complete = gs_console_complete_out;
+ req->context = cons;
+ req->length = 0;
+
+ spin_lock(&cons->lock);
+ cons->req = req;
+ cons->console.data = ep;
+ spin_unlock(&cons->lock);
+
+ pr_debug("ttyGS%d: console connected!\n", port->port_num);
+
+ schedule_work(&cons->work);
+
+ return 0;
}
-static void gserial_console_exit(void)
+static void gs_console_disconnect(struct gs_port *port)
{
- struct gscons_info *info = &gscons_info;
+ struct gs_console *cons = port->console;
+ struct usb_request *req;
+ struct usb_ep *ep;
- unregister_console(&gserial_cons);
- if (!IS_ERR_OR_NULL(info->console_thread))
- kthread_stop(info->console_thread);
- kfifo_free(&info->con_buf);
+ if (!cons)
+ return;
+
+ spin_lock(&cons->lock);
+
+ req = cons->req;
+ ep = cons->console.data;
+ cons->req = NULL;
+
+ spin_unlock(&cons->lock);
+
+ if (!req)
+ return;
+
+ usb_ep_dequeue(ep, req);
+ gs_free_req(ep, req);
}
+static int gs_console_init(struct gs_port *port)
+{
+ struct gs_console *cons;
+ int err;
+
+ if (port->console)
+ return 0;
+
+ cons = kzalloc(sizeof(*port->console), GFP_KERNEL);
+ if (!cons)
+ return -ENOMEM;
+
+ strcpy(cons->console.name, "ttyGS");
+ cons->console.write = gs_console_write;
+ cons->console.device = gs_console_device;
+ cons->console.flags = CON_PRINTBUFFER;
+ cons->console.index = port->port_num;
+
+ INIT_WORK(&cons->work, gs_console_work);
+ spin_lock_init(&cons->lock);
+
+ err = kfifo_alloc(&cons->buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL);
+ if (err) {
+ pr_err("ttyGS%d: allocate console buffer failed\n", port->port_num);
+ kfree(cons);
+ return err;
+ }
+
+ port->console = cons;
+ register_console(&cons->console);
+
+ spin_lock_irq(&port->port_lock);
+ if (port->port_usb)
+ gs_console_connect(port);
+ spin_unlock_irq(&port->port_lock);
+
+ return 0;
+}
+
+static void gs_console_exit(struct gs_port *port)
+{
+ struct gs_console *cons = port->console;
+
+ if (!cons)
+ return;
+
+ unregister_console(&cons->console);
+
+ spin_lock_irq(&port->port_lock);
+ if (cons->req)
+ gs_console_disconnect(port);
+ spin_unlock_irq(&port->port_lock);
+
+ cancel_work_sync(&cons->work);
+ kfifo_free(&cons->buf);
+ kfree(cons);
+ port->console = NULL;
+}
+
+ssize_t gserial_set_console(unsigned char port_num, const char *page, size_t count)
+{
+ struct gs_port *port;
+ bool enable;
+ int ret;
+
+ ret = strtobool(page, &enable);
+ if (ret)
+ return ret;
+
+ mutex_lock(&ports[port_num].lock);
+ port = ports[port_num].port;
+
+ if (WARN_ON(port == NULL)) {
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (enable)
+ ret = gs_console_init(port);
+ else
+ gs_console_exit(port);
+out:
+ mutex_unlock(&ports[port_num].lock);
+
+ return ret < 0 ? ret : count;
+}
+EXPORT_SYMBOL_GPL(gserial_set_console);
+
+ssize_t gserial_get_console(unsigned char port_num, char *page)
+{
+ struct gs_port *port;
+ ssize_t ret;
+
+ mutex_lock(&ports[port_num].lock);
+ port = ports[port_num].port;
+
+ if (WARN_ON(port == NULL))
+ ret = -ENXIO;
+ else
+ ret = sprintf(page, "%u\n", !!port->console);
+
+ mutex_unlock(&ports[port_num].lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gserial_get_console);
+
#else
-static int gs_console_connect(int port_num)
+static int gs_console_connect(struct gs_port *port)
{
return 0;
}
-static void gs_console_disconnect(struct usb_ep *ep)
+static void gs_console_disconnect(struct gs_port *port)
{
}
-static void gserial_console_init(void)
+static int gs_console_init(struct gs_port *port)
{
+ return -ENOSYS;
}
-static void gserial_console_exit(void)
+static void gs_console_exit(struct gs_port *port)
{
}
@@ -1174,8 +1179,9 @@
int cond;
spin_lock_irq(&port->port_lock);
- cond = (port->port.count == 0) && !port->openclose;
+ cond = port->port.count == 0;
spin_unlock_irq(&port->port_lock);
+
return cond;
}
@@ -1199,18 +1205,19 @@
return;
}
port = ports[port_num].port;
+ gs_console_exit(port);
ports[port_num].port = NULL;
mutex_unlock(&ports[port_num].lock);
gserial_free_port(port);
tty_unregister_device(gs_tty_driver, port_num);
- gserial_console_exit();
}
EXPORT_SYMBOL_GPL(gserial_free_line);
-int gserial_alloc_line(unsigned char *line_num)
+int gserial_alloc_line_no_console(unsigned char *line_num)
{
struct usb_cdc_line_coding coding;
+ struct gs_port *port;
struct device *tty_dev;
int ret;
int port_num;
@@ -1233,26 +1240,35 @@
/* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
- tty_dev = tty_port_register_device(&ports[port_num].port->port,
+ port = ports[port_num].port;
+ tty_dev = tty_port_register_device(&port->port,
gs_tty_driver, port_num, NULL);
if (IS_ERR(tty_dev)) {
- struct gs_port *port;
pr_err("%s: failed to register tty for port %d, err %ld\n",
__func__, port_num, PTR_ERR(tty_dev));
ret = PTR_ERR(tty_dev);
mutex_lock(&ports[port_num].lock);
- port = ports[port_num].port;
ports[port_num].port = NULL;
mutex_unlock(&ports[port_num].lock);
gserial_free_port(port);
goto err;
}
*line_num = port_num;
- gserial_console_init();
err:
return ret;
}
+EXPORT_SYMBOL_GPL(gserial_alloc_line_no_console);
+
+int gserial_alloc_line(unsigned char *line_num)
+{
+ int ret = gserial_alloc_line_no_console(line_num);
+
+ if (!ret && !*line_num)
+ gs_console_init(ports[*line_num].port);
+
+ return ret;
+}
EXPORT_SYMBOL_GPL(gserial_alloc_line);
/**
@@ -1331,7 +1347,7 @@
gser->disconnect(gser);
}
- status = gs_console_connect(port_num);
+ status = gs_console_connect(port);
spin_unlock_irqrestore(&port->port_lock, flags);
return status;
@@ -1363,16 +1379,19 @@
/* tell the TTY glue not to do I/O here any more */
spin_lock_irqsave(&port->port_lock, flags);
+ gs_console_disconnect(port);
+
/* REVISIT as above: how best to track this? */
port->port_line_coding = gser->port_line_coding;
port->port_usb = NULL;
gser->ioport = NULL;
- if (port->port.count > 0 || port->openclose) {
+ if (port->port.count > 0) {
wake_up_interruptible(&port->drain_wait);
if (port->port.tty)
tty_hangup(port->port.tty);
}
+ port->suspended = false;
spin_unlock_irqrestore(&port->port_lock, flags);
/* disable endpoints, aborting down any active I/O */
@@ -1381,7 +1400,7 @@
/* finally, free any unused/unusable I/O buffers */
spin_lock_irqsave(&port->port_lock, flags);
- if (port->port.count == 0 && !port->openclose)
+ if (port->port.count == 0)
kfifo_free(&port->port_write_buf);
gs_free_requests(gser->out, &port->read_pool, NULL);
gs_free_requests(gser->out, &port->read_queue, NULL);
@@ -1390,11 +1409,42 @@
port->read_allocated = port->read_started =
port->write_allocated = port->write_started = 0;
- gs_console_disconnect(gser->in);
spin_unlock_irqrestore(&port->port_lock, flags);
}
EXPORT_SYMBOL_GPL(gserial_disconnect);
+void gserial_suspend(struct gserial *gser)
+{
+ struct gs_port *port = gser->ioport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->suspended = true;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gserial_suspend);
+
+void gserial_resume(struct gserial *gser)
+{
+ struct gs_port *port = gser->ioport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->suspended = false;
+ if (!port->start_delayed) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ pr_debug("delayed start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+ if (gser->connect)
+ gser->connect(gser);
+ port->start_delayed = false;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gserial_resume);
+
static int userial_init(void)
{
unsigned i;
diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index 9acaac1..102a732 100644
--- a/drivers/usb/gadget/function/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_serial.h - interface to USB gadget "serial port"/TTY utilities
*
@@ -12,7 +12,7 @@
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
-#define MAX_U_SERIAL_PORTS 4
+#define MAX_U_SERIAL_PORTS 8
struct f_serial_opts {
struct usb_function_instance func_inst;
@@ -54,12 +54,22 @@
void gs_free_req(struct usb_ep *, struct usb_request *req);
/* management of individual TTY ports */
+int gserial_alloc_line_no_console(unsigned char *port_line);
int gserial_alloc_line(unsigned char *port_line);
void gserial_free_line(unsigned char port_line);
+#ifdef CONFIG_U_SERIAL_CONSOLE
+
+ssize_t gserial_set_console(unsigned char port_num, const char *page, size_t count);
+ssize_t gserial_get_console(unsigned char port_num, char *page);
+
+#endif /* CONFIG_U_SERIAL_CONSOLE */
+
/* connect/disconnect is handled by individual functions */
int gserial_connect(struct gserial *, u8 port_num);
void gserial_disconnect(struct gserial *);
+void gserial_suspend(struct gserial *p);
+void gserial_resume(struct gserial *p);
/* functions are bound to configurations by a config or gadget driver */
int gser_bind_config(struct usb_configuration *c, u8 port_num);
diff --git a/drivers/usb/gadget/function/u_tcm.h b/drivers/usb/gadget/function/u_tcm.h
index 3f7ccec..2cd15d9 100644
--- a/drivers/usb/gadget/function/u_tcm.h
+++ b/drivers/usb/gadget/function/u_tcm.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_tcm.h
*
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 6f1a9d7..39c0e29 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uac1.h - Utility definitions for UAC1 function
*
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.c b/drivers/usb/gadget/function/u_uac1_legacy.c
index 5393e5c..60ae8b2 100644
--- a/drivers/usb/gadget/function/u_uac1_legacy.c
+++ b/drivers/usb/gadget/function/u_uac1_legacy.c
@@ -23,7 +23,7 @@
/*-------------------------------------------------------------------------*/
-/**
+/*
* Some ALSA internal helper functions
*/
static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
@@ -86,7 +86,7 @@
}
/*-------------------------------------------------------------------------*/
-/**
+/*
* Set default hardware params
*/
static int playback_default_hw_params(struct gaudio_snd_dev *snd)
@@ -146,7 +146,7 @@
return 0;
}
-/**
+/*
* Playback audio buffer data by ALSA PCM device
*/
size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
@@ -189,7 +189,7 @@
return card->playback.rate;
}
-/**
+/*
* Open ALSA PCM and control device files
* Initial the PCM or control device
*/
@@ -250,7 +250,7 @@
return 0;
}
-/**
+/*
* Close ALSA PCM and control device files
*/
static int gaudio_close_snd_dev(struct gaudio *gau)
@@ -275,7 +275,7 @@
return 0;
}
-/**
+/*
* gaudio_setup - setup ALSA interface and preparing for USB transfer
*
* This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
@@ -294,7 +294,7 @@
}
-/**
+/*
* gaudio_cleanup - remove ALSA device interface
*
* This is called to free all resources allocated by @gaudio_setup().
diff --git a/drivers/usb/gadget/function/u_uac1_legacy.h b/drivers/usb/gadget/function/u_uac1_legacy.h
index 5c1bdf4..b5df9bc 100644
--- a/drivers/usb/gadget/function/u_uac1_legacy.h
+++ b/drivers/usb/gadget/function/u_uac1_legacy.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
*
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index 8204879..b503571 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uac2.h
*
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index 16da49a..9a01a7d 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uvc.h
*
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 1473d25..893aaa7 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* uvc_gadget.h -- USB Video Class Gadget driver
*
@@ -77,6 +77,8 @@
struct uvc_device *uvc;
struct usb_ep *ep;
+ struct work_struct pump;
+
/* Frame parameters */
u8 bpp;
u32 fcc;
@@ -115,6 +117,7 @@
enum uvc_state state;
struct usb_function func;
struct uvc_video video;
+ bool func_connected;
/* Descriptors */
struct {
@@ -145,6 +148,7 @@
struct uvc_file_handle {
struct v4l2_fh vfh;
struct uvc_video *device;
+ bool is_uvc_app_handle;
};
#define to_uvc_file_handle(handle) \
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
index 341391d..7e1d7ca 100644
--- a/drivers/usb/gadget/function/uvc_configfs.h
+++ b/drivers/usb/gadget/function/uvc_configfs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_configfs.h
*
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index 495f0ec..197c26f 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -169,7 +169,9 @@
if (ret < 0)
return ret;
- return uvcg_video_pump(video);
+ schedule_work(&video->pump);
+
+ return ret;
}
static int
@@ -225,17 +227,55 @@
uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
+ struct uvc_device *uvc = video_get_drvdata(fh->vdev);
+ struct uvc_file_handle *handle = to_uvc_file_handle(fh);
+ int ret;
+
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL;
- return v4l2_event_subscribe(fh, sub, 2, NULL);
+ if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
+ return -EBUSY;
+
+ ret = v4l2_event_subscribe(fh, sub, 2, NULL);
+ if (ret < 0)
+ return ret;
+
+ if (sub->type == UVC_EVENT_SETUP) {
+ uvc->func_connected = true;
+ handle->is_uvc_app_handle = true;
+ uvc_function_connect(uvc);
+ }
+
+ return 0;
+}
+
+static void uvc_v4l2_disable(struct uvc_device *uvc)
+{
+ uvc->func_connected = false;
+ uvc_function_disconnect(uvc);
+ uvcg_video_enable(&uvc->video, 0);
+ uvcg_free_buffers(&uvc->video.queue);
}
static int
uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
- return v4l2_event_unsubscribe(fh, sub);
+ struct uvc_device *uvc = video_get_drvdata(fh->vdev);
+ struct uvc_file_handle *handle = to_uvc_file_handle(fh);
+ int ret;
+
+ ret = v4l2_event_unsubscribe(fh, sub);
+ if (ret < 0)
+ return ret;
+
+ if (sub->type == UVC_EVENT_SETUP && handle->is_uvc_app_handle) {
+ uvc_v4l2_disable(uvc);
+ handle->is_uvc_app_handle = false;
+ }
+
+ return 0;
}
static long
@@ -290,7 +330,6 @@
handle->device = &uvc->video;
file->private_data = &handle->vfh;
- uvc_function_connect(uvc);
return 0;
}
@@ -302,11 +341,9 @@
struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
struct uvc_video *video = handle->device;
- uvc_function_disconnect(uvc);
-
mutex_lock(&video->mutex);
- uvcg_video_enable(video, 0);
- uvcg_free_buffers(&video->queue);
+ if (handle->is_uvc_app_handle)
+ uvc_v4l2_disable(uvc);
mutex_unlock(&video->mutex);
file->private_data = NULL;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h
index 452d710..1576005 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.h
+++ b/drivers/usb/gadget/function/uvc_v4l2.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_v4l2.h -- USB Video Class Gadget driver
*
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 5c042f3..633e23d 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -142,44 +142,12 @@
return ret;
}
-/*
- * I somehow feel that synchronisation won't be easy to achieve here. We have
- * three events that control USB requests submission:
- *
- * - USB request completion: the completion handler will resubmit the request
- * if a video buffer is available.
- *
- * - USB interface setting selection: in response to a SET_INTERFACE request,
- * the handler will start streaming if a video buffer is available and if
- * video is not currently streaming.
- *
- * - V4L2 buffer queueing: the driver will start streaming if video is not
- * currently streaming.
- *
- * Race conditions between those 3 events might lead to deadlocks or other
- * nasty side effects.
- *
- * The "video currently streaming" condition can't be detected by the irqqueue
- * being empty, as a request can still be in flight. A separate "queue paused"
- * flag is thus needed.
- *
- * The paused flag will be set when we try to retrieve the irqqueue head if the
- * queue is empty, and cleared when we queue a buffer.
- *
- * The USB request completion handler will get the buffer at the irqqueue head
- * under protection of the queue spinlock. If the queue is empty, the streaming
- * paused flag will be set. Right after releasing the spinlock a userspace
- * application can queue a buffer. The flag will then cleared, and the ioctl
- * handler will restart the video stream.
- */
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_video *video = req->context;
struct uvc_video_queue *queue = &video->queue;
- struct uvc_buffer *buf;
unsigned long flags;
- int ret;
switch (req->status) {
case 0:
@@ -188,39 +156,20 @@
case -ESHUTDOWN: /* disconnect from host. */
uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
uvcg_queue_cancel(queue, 1);
- goto requeue;
+ break;
default:
uvcg_info(&video->uvc->func,
"VS request completed with status %d.\n",
req->status);
uvcg_queue_cancel(queue, 0);
- goto requeue;
}
- spin_lock_irqsave(&video->queue.irqlock, flags);
- buf = uvcg_queue_head(&video->queue);
- if (buf == NULL) {
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
- goto requeue;
- }
-
- video->encode(req, video, buf);
-
- ret = uvcg_video_ep_queue(video, req);
- spin_unlock_irqrestore(&video->queue.irqlock, flags);
-
- if (ret < 0) {
- uvcg_queue_cancel(queue, 0);
- goto requeue;
- }
-
- return;
-
-requeue:
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
+
+ schedule_work(&video->pump);
}
static int
@@ -294,18 +243,15 @@
* This function fills the available USB requests (listed in req_free) with
* video data from the queued buffers.
*/
-int uvcg_video_pump(struct uvc_video *video)
+static void uvcg_video_pump(struct work_struct *work)
{
+ struct uvc_video *video = container_of(work, struct uvc_video, pump);
struct uvc_video_queue *queue = &video->queue;
struct usb_request *req;
struct uvc_buffer *buf;
unsigned long flags;
int ret;
- /* FIXME TODO Race between uvcg_video_pump and requests completion
- * handler ???
- */
-
while (1) {
/* Retrieve the first available USB request, protected by the
* request lock.
@@ -313,7 +259,7 @@
spin_lock_irqsave(&video->req_lock, flags);
if (list_empty(&video->req_free)) {
spin_unlock_irqrestore(&video->req_lock, flags);
- return 0;
+ return;
}
req = list_first_entry(&video->req_free, struct usb_request,
list);
@@ -345,7 +291,7 @@
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
- return 0;
+ return;
}
/*
@@ -363,6 +309,9 @@
}
if (!enable) {
+ cancel_work_sync(&video->pump);
+ uvcg_queue_cancel(&video->queue, 0);
+
for (i = 0; i < UVC_NUM_REQUESTS; ++i)
if (video->req[i])
usb_ep_dequeue(video->ep, video->req[i]);
@@ -384,7 +333,9 @@
} else
video->encode = uvc_video_encode_isoc;
- return uvcg_video_pump(video);
+ schedule_work(&video->pump);
+
+ return ret;
}
/*
@@ -394,6 +345,7 @@
{
INIT_LIST_HEAD(&video->req_free);
spin_lock_init(&video->req_lock);
+ INIT_WORK(&video->pump, uvcg_video_pump);
video->uvc = uvc;
video->fcc = V4L2_PIX_FMT_YUYV;
diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h
index dff1210..03adeef 100644
--- a/drivers/usb/gadget/function/uvc_video.h
+++ b/drivers/usb/gadget/function/uvc_video.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_video.h -- USB Video Class Gadget driver
*
@@ -14,8 +14,6 @@
struct uvc_video;
-int uvcg_video_pump(struct uvc_video *video);
-
int uvcg_video_enable(struct uvc_video *video, int enable);
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index 69ff7f8..f02c38b 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -13,6 +13,28 @@
# With help from a special transceiver and a "Mini-AB" jack, systems with
# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
#
+# A Linux "Gadget Driver" talks to the USB Peripheral Controller
+# driver through the abstract "gadget" API. Some other operating
+# systems call these "client" drivers, of which "class drivers"
+# are a subset (implementing a USB device class specification).
+# A gadget driver implements one or more USB functions using
+# the peripheral hardware.
+#
+# Gadget drivers are hardware-neutral, or "platform independent",
+# except that they sometimes must understand quirks or limitations
+# of the particular controllers they work with. For example, when
+# a controller doesn't support alternate configurations or provide
+# enough of the right types of endpoints, the gadget driver might
+# not be able work with that controller, or might need to implement
+# a less common variant of a device class protocol.
+#
+# The available choices each represent a single precomposed USB
+# gadget configuration. In the device model, each option contains
+# both the device instantiation as a child for a USB gadget
+# controller, and the relevant drivers for each function declared
+# by the device.
+
+menu "USB Gadget precomposed configurations"
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
@@ -149,21 +171,21 @@
is given in comments found in that info file.
config USB_ETH_EEM
- bool "Ethernet Emulation Model (EEM) support"
- depends on USB_ETH
+ bool "Ethernet Emulation Model (EEM) support"
+ depends on USB_ETH
select USB_LIBCOMPOSITE
select USB_F_EEM
- help
- CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
- and therefore can be supported by more hardware. Technically ECM and
- EEM are designed for different applications. The ECM model extends
- the network interface to the target (e.g. a USB cable modem), and the
- EEM model is for mobile devices to communicate with hosts using
- ethernet over USB. For Linux gadgets, however, the interface with
- the host is the same (a usbX device), so the differences are minimal.
+ help
+ CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
+ and therefore can be supported by more hardware. Technically ECM and
+ EEM are designed for different applications. The ECM model extends
+ the network interface to the target (e.g. a USB cable modem), and the
+ EEM model is for mobile devices to communicate with hosts using
+ ethernet over USB. For Linux gadgets, however, the interface with
+ the host is the same (a usbX device), so the differences are minimal.
- If you say "y" here, the Ethernet gadget driver will use the EEM
- protocol rather than ECM. If unsure, say "n".
+ If you say "y" here, the Ethernet gadget driver will use the EEM
+ protocol rather than ECM. If unsure, say "n".
config USB_G_NCM
tristate "Network Control Model (NCM) support"
@@ -489,3 +511,16 @@
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_webcam".
+
+config USB_RAW_GADGET
+ tristate "USB Raw Gadget"
+ help
+ USB Raw Gadget is a kernel module that provides a userspace interface
+ for the USB Gadget subsystem. Essentially it allows to emulate USB
+ devices from userspace. See Documentation/usb/raw-gadget.rst for
+ details.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "raw_gadget".
+
+endmenu
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
index abd0c3e..4d864bf 100644
--- a/drivers/usb/gadget/legacy/Makefile
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -43,3 +43,4 @@
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
+obj-$(CONFIG_USB_RAW_GADGET) += raw_gadget.o
diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c
index 6680dcf..e8033e5 100644
--- a/drivers/usb/gadget/legacy/acm_ms.c
+++ b/drivers/usb/gadget/legacy/acm_ms.c
@@ -105,7 +105,6 @@
*/
static int acm_ms_do_config(struct usb_configuration *c)
{
- struct fsg_opts *opts;
int status;
if (gadget_is_otg(c->cdev->gadget)) {
@@ -113,8 +112,6 @@
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- opts = fsg_opts_from_func_inst(fi_msg);
-
f_acm = usb_get_function(f_acm_inst);
if (IS_ERR(f_acm))
return PTR_ERR(f_acm);
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index e1d566c..6bcbad3 100644
--- a/drivers/usb/gadget/legacy/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -137,7 +137,7 @@
goto fail_1;
}
- req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL);
+ req->buf = kzalloc(DBGP_REQ_LEN, GFP_KERNEL);
if (!req->buf) {
err = -ENOMEM;
stp = 2;
@@ -345,6 +345,19 @@
void *data = NULL;
u16 len = 0;
+ if (length > DBGP_REQ_LEN) {
+ if (ctrl->bRequestType & USB_DIR_IN) {
+ /* Cast away the const, we are going to overwrite on purpose. */
+ __le16 *temp = (__le16 *)&ctrl->wLength;
+
+ *temp = cpu_to_le16(DBGP_REQ_LEN);
+ length = DBGP_REQ_LEN;
+ } else {
+ return err;
+ }
+ }
+
+
if (request == USB_REQ_GET_DESCRIPTOR) {
switch (value>>8) {
case USB_DT_DEVICE:
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index 9eea2d1..265c392 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -174,7 +174,7 @@
}
static struct usb_composite_driver midi_driver = {
- .name = (char *) longname,
+ .name = longname,
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
index 5b27d28..3912cc8 100644
--- a/drivers/usb/gadget/legacy/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -99,8 +99,10 @@
list_for_each_entry(e, &hidg_func_list, node) {
e->f = usb_get_function(e->fi);
- if (IS_ERR(e->f))
+ if (IS_ERR(e->f)) {
+ status = PTR_ERR(e->f);
goto put;
+ }
status = usb_add_function(c, e->f);
if (status < 0) {
usb_put_function(e->f);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index cabcbb4..454860d 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -21,7 +21,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
-#include <linux/mmu_context.h>
+#include <linux/kthread.h>
#include <linux/aio.h>
#include <linux/uio.h>
#include <linux/refcount.h>
@@ -110,6 +110,8 @@
/* enough for the whole queue: most events invalidate others */
#define N_EVENT 5
+#define RBUF_SIZE 256
+
struct dev_data {
spinlock_t lock;
refcount_t count;
@@ -144,7 +146,7 @@
struct dentry *dentry;
/* except this scratch i/o buffer for ep0 */
- u8 rbuf [256];
+ u8 rbuf[RBUF_SIZE];
};
static inline void get_dev (struct dev_data *data)
@@ -312,7 +314,7 @@
case STATE_EP_READY: /* not configured yet */
if (is_write)
return 0;
- // FALLTHRU
+ fallthrough;
case STATE_EP_UNBOUND: /* clean disconnect */
break;
// case STATE_EP_DISABLED: /* "can't happen" */
@@ -344,7 +346,7 @@
spin_unlock_irq (&epdata->dev->lock);
if (likely (value == 0)) {
- value = wait_event_interruptible (done.wait, done.done);
+ value = wait_for_completion_interruptible(&done);
if (value != 0) {
spin_lock_irq (&epdata->dev->lock);
if (likely (epdata->ep != NULL)) {
@@ -353,7 +355,7 @@
usb_ep_dequeue (epdata->ep, epdata->req);
spin_unlock_irq (&epdata->dev->lock);
- wait_event (done.wait, done.done);
+ wait_for_completion(&done);
if (epdata->status == -ECONNRESET)
epdata->status = -EINTR;
} else {
@@ -462,9 +464,9 @@
struct kiocb *iocb = priv->iocb;
size_t ret;
- use_mm(mm);
+ kthread_use_mm(mm);
ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
- unuse_mm(mm);
+ kthread_unuse_mm(mm);
if (!ret)
ret = -EFAULT;
@@ -1084,7 +1086,7 @@
case GADGETFS_DISCONNECT:
if (dev->state == STATE_DEV_SETUP)
dev->setup_abort = 1;
- // FALL THROUGH
+ fallthrough;
case GADGETFS_CONNECT:
dev->ev_next = 0;
break;
@@ -1333,6 +1335,18 @@
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ if (w_length > RBUF_SIZE) {
+ if (ctrl->bRequestType & USB_DIR_IN) {
+ /* Cast away the const, we are going to overwrite on purpose. */
+ __le16 *temp = (__le16 *)&ctrl->wLength;
+
+ *temp = cpu_to_le16(RBUF_SIZE);
+ w_length = RBUF_SIZE;
+ } else {
+ return value;
+ }
+ }
+
spin_lock (&dev->lock);
dev->setup_abort = 0;
if (dev->state == STATE_DEV_UNCONNECTED) {
@@ -1381,7 +1395,6 @@
make_qualifier (dev);
break;
case USB_DT_OTHER_SPEED_CONFIG:
- // FALLTHROUGH
case USB_DT_CONFIG:
value = config_buf (dev,
w_value >> 8,
@@ -1718,7 +1731,7 @@
case STATE_DEV_UNCONNECTED:
next_event (dev, GADGETFS_SUSPEND);
ep0_readable (dev);
- /* FALLTHROUGH */
+ fallthrough;
default:
break;
}
@@ -1735,7 +1748,7 @@
.suspend = gadgetfs_suspend,
.driver = {
- .name = (char *) shortname,
+ .name = shortname,
},
};
@@ -1815,8 +1828,9 @@
spin_lock_irq (&dev->lock);
value = -EINVAL;
if (dev->buf) {
+ spin_unlock_irq(&dev->lock);
kfree(kbuf);
- goto fail;
+ return value;
}
dev->buf = kbuf;
@@ -1863,8 +1877,8 @@
value = usb_gadget_probe_driver(&gadgetfs_driver);
if (value != 0) {
- kfree (dev->buf);
- dev->buf = NULL;
+ spin_lock_irq(&dev->lock);
+ goto fail;
} else {
/* at this point "good" hardware has for the first time
* let the USB the host see us. alternatively, if users
@@ -1881,6 +1895,9 @@
return value;
fail:
+ dev->config = NULL;
+ dev->hs_config = NULL;
+ dev->dev = NULL;
spin_unlock_irq (&dev->lock);
pr_debug ("%s: %s fail %zd, %p\n", shortname, __func__, value, dev);
kfree (dev->buf);
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index fd5595a..9ed22c5 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -105,7 +105,6 @@
static int msg_do_config(struct usb_configuration *c)
{
- struct fsg_opts *opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@@ -113,8 +112,6 @@
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- opts = fsg_opts_from_func_inst(fi_msg);
-
f_msg = usb_get_function(fi_msg);
if (IS_ERR(f_msg))
return PTR_ERR(f_msg);
@@ -232,18 +229,8 @@
.unbind = msg_unbind,
};
+module_usb_composite_driver(msg_driver);
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Michal Nazarewicz");
MODULE_LICENSE("GPL");
-
-static int __init msg_init(void)
-{
- return usb_composite_probe(&msg_driver);
-}
-module_init(msg_init);
-
-static void __exit msg_cleanup(void)
-{
- usb_composite_unregister(&msg_driver);
-}
-module_exit(msg_cleanup);
diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
index 978c1a3..2e15f9a 100644
--- a/drivers/usb/gadget/legacy/nokia.c
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -62,7 +62,6 @@
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static char manufacturer_nokia[] = "Nokia";
-static const char product_nokia[] = NOKIA_LONG_NAME;
static const char description_nokia[] = "PC-Suite Configuration";
static struct usb_string strings_dev[] = {
diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c
index 57858f0..2cd3895 100644
--- a/drivers/usb/gadget/legacy/printer.c
+++ b/drivers/usb/gadget/legacy/printer.c
@@ -21,7 +21,6 @@
#define DRIVER_VERSION "2015 FEB 17"
static const char shortname [] = "printer";
-static const char driver_desc [] = DRIVER_DESC;
#include "u_printer.h"
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
new file mode 100644
index 0000000..33efa69
--- /dev/null
+++ b/drivers/usb/gadget/legacy/raw_gadget.c
@@ -0,0 +1,1283 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Raw Gadget driver.
+ * See Documentation/usb/raw-gadget.rst for more details.
+ *
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ */
+
+#include <linux/compiler.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/kref.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/gadget.h>
+
+#include <uapi/linux/usb/raw_gadget.h>
+
+#define DRIVER_DESC "USB Raw Gadget"
+#define DRIVER_NAME "raw-gadget"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Andrey Konovalov");
+MODULE_LICENSE("GPL");
+
+/*----------------------------------------------------------------------*/
+
+#define RAW_EVENT_QUEUE_SIZE 16
+
+struct raw_event_queue {
+ /* See the comment in raw_event_queue_fetch() for locking details. */
+ spinlock_t lock;
+ struct semaphore sema;
+ struct usb_raw_event *events[RAW_EVENT_QUEUE_SIZE];
+ int size;
+};
+
+static void raw_event_queue_init(struct raw_event_queue *queue)
+{
+ spin_lock_init(&queue->lock);
+ sema_init(&queue->sema, 0);
+ queue->size = 0;
+}
+
+static int raw_event_queue_add(struct raw_event_queue *queue,
+ enum usb_raw_event_type type, size_t length, const void *data)
+{
+ unsigned long flags;
+ struct usb_raw_event *event;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) {
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return -ENOMEM;
+ }
+ event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
+ if (!event) {
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return -ENOMEM;
+ }
+ event->type = type;
+ event->length = length;
+ if (event->length)
+ memcpy(&event->data[0], data, length);
+ queue->events[queue->size] = event;
+ queue->size++;
+ up(&queue->sema);
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return 0;
+}
+
+static struct usb_raw_event *raw_event_queue_fetch(
+ struct raw_event_queue *queue)
+{
+ int ret;
+ unsigned long flags;
+ struct usb_raw_event *event;
+
+ /*
+ * This function can be called concurrently. We first check that
+ * there's at least one event queued by decrementing the semaphore,
+ * and then take the lock to protect queue struct fields.
+ */
+ ret = down_interruptible(&queue->sema);
+ if (ret)
+ return ERR_PTR(ret);
+ spin_lock_irqsave(&queue->lock, flags);
+ /*
+ * queue->size must have the same value as queue->sema counter (before
+ * the down_interruptible() call above), so this check is a fail-safe.
+ */
+ if (WARN_ON(!queue->size)) {
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return ERR_PTR(-ENODEV);
+ }
+ event = queue->events[0];
+ queue->size--;
+ memmove(&queue->events[0], &queue->events[1],
+ queue->size * sizeof(queue->events[0]));
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return event;
+}
+
+static void raw_event_queue_destroy(struct raw_event_queue *queue)
+{
+ int i;
+
+ for (i = 0; i < queue->size; i++)
+ kfree(queue->events[i]);
+ queue->size = 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+struct raw_dev;
+
+enum ep_state {
+ STATE_EP_DISABLED,
+ STATE_EP_ENABLED,
+};
+
+struct raw_ep {
+ struct raw_dev *dev;
+ enum ep_state state;
+ struct usb_ep *ep;
+ u8 addr;
+ struct usb_request *req;
+ bool urb_queued;
+ bool disabling;
+ ssize_t status;
+};
+
+enum dev_state {
+ STATE_DEV_INVALID = 0,
+ STATE_DEV_OPENED,
+ STATE_DEV_INITIALIZED,
+ STATE_DEV_RUNNING,
+ STATE_DEV_CLOSED,
+ STATE_DEV_FAILED
+};
+
+struct raw_dev {
+ struct kref count;
+ spinlock_t lock;
+
+ const char *udc_name;
+ struct usb_gadget_driver driver;
+
+ /* Reference to misc device: */
+ struct device *dev;
+
+ /* Protected by lock: */
+ enum dev_state state;
+ bool gadget_registered;
+ struct usb_gadget *gadget;
+ struct usb_request *req;
+ bool ep0_in_pending;
+ bool ep0_out_pending;
+ bool ep0_urb_queued;
+ ssize_t ep0_status;
+ struct raw_ep eps[USB_RAW_EPS_NUM_MAX];
+ int eps_num;
+
+ struct completion ep0_done;
+ struct raw_event_queue queue;
+};
+
+static struct raw_dev *dev_new(void)
+{
+ struct raw_dev *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+ /* Matches kref_put() in raw_release(). */
+ kref_init(&dev->count);
+ spin_lock_init(&dev->lock);
+ init_completion(&dev->ep0_done);
+ raw_event_queue_init(&dev->queue);
+ return dev;
+}
+
+static void dev_free(struct kref *kref)
+{
+ struct raw_dev *dev = container_of(kref, struct raw_dev, count);
+ int i;
+
+ kfree(dev->udc_name);
+ kfree(dev->driver.udc_name);
+ if (dev->req) {
+ if (dev->ep0_urb_queued)
+ usb_ep_dequeue(dev->gadget->ep0, dev->req);
+ usb_ep_free_request(dev->gadget->ep0, dev->req);
+ }
+ raw_event_queue_destroy(&dev->queue);
+ for (i = 0; i < dev->eps_num; i++) {
+ if (dev->eps[i].state == STATE_EP_DISABLED)
+ continue;
+ usb_ep_disable(dev->eps[i].ep);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ }
+ kfree(dev);
+}
+
+/*----------------------------------------------------------------------*/
+
+static int raw_queue_event(struct raw_dev *dev,
+ enum usb_raw_event_type type, size_t length, const void *data)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ ret = raw_event_queue_add(&dev->queue, type, length, data);
+ if (ret < 0) {
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ return ret;
+}
+
+static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct raw_dev *dev = req->context;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (req->status)
+ dev->ep0_status = req->status;
+ else
+ dev->ep0_status = req->actual;
+ if (dev->ep0_in_pending)
+ dev->ep0_in_pending = false;
+ else
+ dev->ep0_out_pending = false;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ complete(&dev->ep0_done);
+}
+
+static u8 get_ep_addr(const char *name)
+{
+ /* If the endpoint has fixed function (named as e.g. "ep12out-bulk"),
+ * parse the endpoint address from its name. We deliberately use
+ * deprecated simple_strtoul() function here, as the number isn't
+ * followed by '\0' nor '\n'.
+ */
+ if (isdigit(name[2]))
+ return simple_strtoul(&name[2], NULL, 10);
+ /* Otherwise the endpoint is configurable (named as e.g. "ep-a"). */
+ return USB_RAW_EP_ADDR_ANY;
+}
+
+static int gadget_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ int ret = 0, i = 0;
+ struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
+ struct usb_request *req;
+ struct usb_ep *ep;
+ unsigned long flags;
+
+ if (strcmp(gadget->name, dev->udc_name) != 0)
+ return -ENODEV;
+
+ set_gadget_data(gadget, dev);
+ req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!req) {
+ dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
+ set_gadget_data(gadget, NULL);
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->req = req;
+ dev->req->context = dev;
+ dev->req->complete = gadget_ep0_complete;
+ dev->gadget = gadget;
+ gadget_for_each_ep(ep, dev->gadget) {
+ dev->eps[i].ep = ep;
+ dev->eps[i].addr = get_ep_addr(ep->name);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ i++;
+ }
+ dev->eps_num = i;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* Matches kref_put() in gadget_unbind(). */
+ kref_get(&dev->count);
+
+ ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
+ if (ret < 0)
+ dev_err(&gadget->dev, "failed to queue event\n");
+
+ return ret;
+}
+
+static void gadget_unbind(struct usb_gadget *gadget)
+{
+ struct raw_dev *dev = get_gadget_data(gadget);
+
+ set_gadget_data(gadget, NULL);
+ /* Matches kref_get() in gadget_bind(). */
+ kref_put(&dev->count, dev_free);
+}
+
+static int gadget_setup(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+{
+ int ret = 0;
+ struct raw_dev *dev = get_gadget_data(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_err(&gadget->dev, "ignoring, device is not running\n");
+ ret = -ENODEV;
+ goto out_unlock;
+ }
+ if (dev->ep0_in_pending || dev->ep0_out_pending) {
+ dev_dbg(&gadget->dev, "stalling, request already pending\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength)
+ dev->ep0_in_pending = true;
+ else
+ dev->ep0_out_pending = true;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl);
+ if (ret < 0)
+ dev_err(&gadget->dev, "failed to queue event\n");
+ goto out;
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+out:
+ return ret;
+}
+
+/* These are currently unused but present in case UDC driver requires them. */
+static void gadget_disconnect(struct usb_gadget *gadget) { }
+static void gadget_suspend(struct usb_gadget *gadget) { }
+static void gadget_resume(struct usb_gadget *gadget) { }
+static void gadget_reset(struct usb_gadget *gadget) { }
+
+/*----------------------------------------------------------------------*/
+
+static struct miscdevice raw_misc_device;
+
+static int raw_open(struct inode *inode, struct file *fd)
+{
+ struct raw_dev *dev;
+
+ /* Nonblocking I/O is not supported yet. */
+ if (fd->f_flags & O_NONBLOCK)
+ return -EINVAL;
+
+ dev = dev_new();
+ if (!dev)
+ return -ENOMEM;
+ fd->private_data = dev;
+ dev->state = STATE_DEV_OPENED;
+ dev->dev = raw_misc_device.this_device;
+ return 0;
+}
+
+static int raw_release(struct inode *inode, struct file *fd)
+{
+ int ret = 0;
+ struct raw_dev *dev = fd->private_data;
+ unsigned long flags;
+ bool unregister = false;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_CLOSED;
+ if (!dev->gadget) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ goto out_put;
+ }
+ if (dev->gadget_registered)
+ unregister = true;
+ dev->gadget_registered = false;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (unregister) {
+ ret = usb_gadget_unregister_driver(&dev->driver);
+ if (ret != 0)
+ dev_err(dev->dev,
+ "usb_gadget_unregister_driver() failed with %d\n",
+ ret);
+ /* Matches kref_get() in raw_ioctl_run(). */
+ kref_put(&dev->count, dev_free);
+ }
+
+out_put:
+ /* Matches dev_new() in raw_open(). */
+ kref_put(&dev->count, dev_free);
+ return ret;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ struct usb_raw_init arg;
+ char *udc_driver_name;
+ char *udc_device_name;
+ unsigned long flags;
+
+ if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
+ return -EFAULT;
+
+ switch (arg.speed) {
+ case USB_SPEED_UNKNOWN:
+ arg.speed = USB_SPEED_HIGH;
+ break;
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
+ if (!udc_driver_name)
+ return -ENOMEM;
+ ret = strscpy(udc_driver_name, &arg.driver_name[0],
+ UDC_NAME_LENGTH_MAX);
+ if (ret < 0) {
+ kfree(udc_driver_name);
+ return ret;
+ }
+ ret = 0;
+
+ udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
+ if (!udc_device_name) {
+ kfree(udc_driver_name);
+ return -ENOMEM;
+ }
+ ret = strscpy(udc_device_name, &arg.device_name[0],
+ UDC_NAME_LENGTH_MAX);
+ if (ret < 0) {
+ kfree(udc_driver_name);
+ kfree(udc_device_name);
+ return ret;
+ }
+ ret = 0;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_OPENED) {
+ dev_dbg(dev->dev, "fail, device is not opened\n");
+ kfree(udc_driver_name);
+ kfree(udc_device_name);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ dev->udc_name = udc_driver_name;
+
+ dev->driver.function = DRIVER_DESC;
+ dev->driver.max_speed = arg.speed;
+ dev->driver.setup = gadget_setup;
+ dev->driver.disconnect = gadget_disconnect;
+ dev->driver.bind = gadget_bind;
+ dev->driver.unbind = gadget_unbind;
+ dev->driver.suspend = gadget_suspend;
+ dev->driver.resume = gadget_resume;
+ dev->driver.reset = gadget_reset;
+ dev->driver.driver.name = DRIVER_NAME;
+ dev->driver.udc_name = udc_device_name;
+ dev->driver.match_existing_only = 1;
+
+ dev->state = STATE_DEV_INITIALIZED;
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (value)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_INITIALIZED) {
+ dev_dbg(dev->dev, "fail, device is not initialized\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ ret = usb_gadget_probe_driver(&dev->driver);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (ret) {
+ dev_err(dev->dev,
+ "fail, usb_gadget_probe_driver returned %d\n", ret);
+ dev->state = STATE_DEV_FAILED;
+ goto out_unlock;
+ }
+ dev->gadget_registered = true;
+ dev->state = STATE_DEV_RUNNING;
+ /* Matches kref_put() in raw_release(). */
+ kref_get(&dev->count);
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
+{
+ struct usb_raw_event arg;
+ unsigned long flags;
+ struct usb_raw_event *event;
+ uint32_t length;
+
+ if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
+ return -EFAULT;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EINVAL;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ event = raw_event_queue_fetch(&dev->queue);
+ if (PTR_ERR(event) == -EINTR) {
+ dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+ return -EINTR;
+ }
+ if (IS_ERR(event)) {
+ dev_err(&dev->gadget->dev, "failed to fetch event\n");
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENODEV;
+ }
+ length = min(arg.length, event->length);
+ if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
+ kfree(event);
+ return -EFAULT;
+ }
+
+ kfree(event);
+ return 0;
+}
+
+static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
+ bool get_from_user)
+{
+ void *data;
+
+ if (copy_from_user(io, ptr, sizeof(*io)))
+ return ERR_PTR(-EFAULT);
+ if (io->ep >= USB_RAW_EPS_NUM_MAX)
+ return ERR_PTR(-EINVAL);
+ if (!usb_raw_io_flags_valid(io->flags))
+ return ERR_PTR(-EINVAL);
+ if (io->length > PAGE_SIZE)
+ return ERR_PTR(-EINVAL);
+ if (get_from_user)
+ data = memdup_user(ptr + sizeof(*io), io->length);
+ else {
+ data = kmalloc(io->length, GFP_KERNEL);
+ if (!data)
+ data = ERR_PTR(-ENOMEM);
+ }
+ return data;
+}
+
+static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
+ void *data, bool in)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (dev->ep0_urb_queued) {
+ dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if ((in && !dev->ep0_in_pending) ||
+ (!in && !dev->ep0_out_pending)) {
+ dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (WARN_ON(in && dev->ep0_out_pending)) {
+ ret = -ENODEV;
+ dev->state = STATE_DEV_FAILED;
+ goto out_done;
+ }
+ if (WARN_ON(!in && dev->ep0_in_pending)) {
+ ret = -ENODEV;
+ dev->state = STATE_DEV_FAILED;
+ goto out_done;
+ }
+
+ dev->req->buf = data;
+ dev->req->length = io->length;
+ dev->req->zero = usb_raw_io_flags_zero(io->flags);
+ dev->ep0_urb_queued = true;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
+ if (ret) {
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_queue returned %d\n", ret);
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ goto out_done;
+ }
+
+ ret = wait_for_completion_interruptible(&dev->ep0_done);
+ if (ret) {
+ dev_dbg(&dev->gadget->dev, "wait interrupted\n");
+ usb_ep_dequeue(dev->gadget->ep0, dev->req);
+ wait_for_completion(&dev->ep0_done);
+ spin_lock_irqsave(&dev->lock, flags);
+ goto out_done;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ ret = dev->ep0_status;
+
+out_done:
+ dev->ep0_urb_queued = false;
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ void *data;
+ struct usb_raw_ep_io io;
+
+ data = raw_alloc_io_data(&io, (void __user *)value, true);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ ret = raw_process_ep0_io(dev, &io, data, true);
+ kfree(data);
+ return ret;
+}
+
+static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ void *data;
+ struct usb_raw_ep_io io;
+ unsigned int length;
+
+ data = raw_alloc_io_data(&io, (void __user *)value, false);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ ret = raw_process_ep0_io(dev, &io, data, false);
+ if (ret < 0)
+ goto free;
+
+ length = min(io.length, (unsigned int)ret);
+ if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
+ ret = -EFAULT;
+ else
+ ret = length;
+free:
+ kfree(data);
+ return ret;
+}
+
+static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (value)
+ return -EINVAL;
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (dev->ep0_urb_queued) {
+ dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (!dev->ep0_in_pending && !dev->ep0_out_pending) {
+ dev_dbg(&dev->gadget->dev, "fail, no request pending\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ ret = usb_ep_set_halt(dev->gadget->ep0);
+ if (ret < 0)
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_set_halt returned %d\n", ret);
+
+ if (dev->ep0_in_pending)
+ dev->ep0_in_pending = false;
+ else
+ dev->ep0_out_pending = false;
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0, i;
+ unsigned long flags;
+ struct usb_endpoint_descriptor *desc;
+ struct raw_ep *ep;
+
+ desc = memdup_user((void __user *)value, sizeof(*desc));
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ /*
+ * Endpoints with a maxpacket length of 0 can cause crashes in UDC
+ * drivers.
+ */
+ if (usb_endpoint_maxp(desc) == 0) {
+ dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
+ kfree(desc);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_free;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_free;
+ }
+
+ for (i = 0; i < dev->eps_num; i++) {
+ ep = &dev->eps[i];
+ if (ep->state != STATE_EP_DISABLED)
+ continue;
+ if (ep->addr != usb_endpoint_num(desc) &&
+ ep->addr != USB_RAW_EP_ADDR_ANY)
+ continue;
+ if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
+ continue;
+ ep->ep->desc = desc;
+ ret = usb_ep_enable(ep->ep);
+ if (ret < 0) {
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_enable returned %d\n", ret);
+ goto out_free;
+ }
+ ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
+ if (!ep->req) {
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_alloc_request failed\n");
+ usb_ep_disable(ep->ep);
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ ep->state = STATE_EP_ENABLED;
+ ep->ep->driver_data = ep;
+ ret = i;
+ goto out_unlock;
+ }
+
+ dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n");
+ ret = -EBUSY;
+
+out_free:
+ kfree(desc);
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0, i = value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (i < 0 || i >= dev->eps_num) {
+ dev_dbg(dev->dev, "fail, invalid endpoint\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (dev->eps[i].state == STATE_EP_DISABLED) {
+ dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (dev->eps[i].disabling) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, disable already in progress\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (dev->eps[i].urb_queued) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, waiting for urb completion\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ dev->eps[i].disabling = true;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_ep_disable(dev->eps[i].ep);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+ kfree(dev->eps[i].ep->desc);
+ dev->eps[i].state = STATE_EP_DISABLED;
+ dev->eps[i].disabling = false;
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
+ unsigned long value, bool set, bool halt)
+{
+ int ret = 0, i = value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (i < 0 || i >= dev->eps_num) {
+ dev_dbg(dev->dev, "fail, invalid endpoint\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (dev->eps[i].state == STATE_EP_DISABLED) {
+ dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (dev->eps[i].disabling) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, disable is in progress\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (dev->eps[i].urb_queued) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, waiting for urb completion\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, can't halt/wedge ISO endpoint\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (set && halt) {
+ ret = usb_ep_set_halt(dev->eps[i].ep);
+ if (ret < 0)
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_set_halt returned %d\n", ret);
+ } else if (!set && halt) {
+ ret = usb_ep_clear_halt(dev->eps[i].ep);
+ if (ret < 0)
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_clear_halt returned %d\n", ret);
+ } else if (set && !halt) {
+ ret = usb_ep_set_wedge(dev->eps[i].ep);
+ if (ret < 0)
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_set_wedge returned %d\n", ret);
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data;
+ struct raw_dev *dev = r_ep->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (req->status)
+ r_ep->status = req->status;
+ else
+ r_ep->status = req->actual;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ complete((struct completion *)req->context);
+}
+
+static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
+ void *data, bool in)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct raw_ep *ep;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (io->ep >= dev->eps_num) {
+ dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ ep = &dev->eps[io->ep];
+ if (ep->state != STATE_EP_ENABLED) {
+ dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (ep->disabling) {
+ dev_dbg(&dev->gadget->dev,
+ "fail, endpoint is already being disabled\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (ep->urb_queued) {
+ dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ if (in != usb_endpoint_dir_in(ep->ep->desc)) {
+ dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ ep->dev = dev;
+ ep->req->context = &done;
+ ep->req->complete = gadget_ep_complete;
+ ep->req->buf = data;
+ ep->req->length = io->length;
+ ep->req->zero = usb_raw_io_flags_zero(io->flags);
+ ep->urb_queued = true;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
+ if (ret) {
+ dev_err(&dev->gadget->dev,
+ "fail, usb_ep_queue returned %d\n", ret);
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->state = STATE_DEV_FAILED;
+ goto out_done;
+ }
+
+ ret = wait_for_completion_interruptible(&done);
+ if (ret) {
+ dev_dbg(&dev->gadget->dev, "wait interrupted\n");
+ usb_ep_dequeue(ep->ep, ep->req);
+ wait_for_completion(&done);
+ spin_lock_irqsave(&dev->lock, flags);
+ goto out_done;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ ret = ep->status;
+
+out_done:
+ ep->urb_queued = false;
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ char *data;
+ struct usb_raw_ep_io io;
+
+ data = raw_alloc_io_data(&io, (void __user *)value, true);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ ret = raw_process_ep_io(dev, &io, data, true);
+ kfree(data);
+ return ret;
+}
+
+static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ char *data;
+ struct usb_raw_ep_io io;
+ unsigned int length;
+
+ data = raw_alloc_io_data(&io, (void __user *)value, false);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ ret = raw_process_ep_io(dev, &io, data, false);
+ if (ret < 0)
+ goto free;
+
+ length = min(io.length, (unsigned int)ret);
+ if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
+ ret = -EFAULT;
+ else
+ ret = length;
+free:
+ kfree(data);
+ return ret;
+}
+
+static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (value)
+ return -EINVAL;
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ usb_gadget_vbus_draw(dev->gadget, 2 * value);
+
+out_unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static void fill_ep_caps(struct usb_ep_caps *caps,
+ struct usb_raw_ep_caps *raw_caps)
+{
+ raw_caps->type_control = caps->type_control;
+ raw_caps->type_iso = caps->type_iso;
+ raw_caps->type_bulk = caps->type_bulk;
+ raw_caps->type_int = caps->type_int;
+ raw_caps->dir_in = caps->dir_in;
+ raw_caps->dir_out = caps->dir_out;
+}
+
+static void fill_ep_limits(struct usb_ep *ep, struct usb_raw_ep_limits *limits)
+{
+ limits->maxpacket_limit = ep->maxpacket_limit;
+ limits->max_streams = ep->max_streams;
+}
+
+static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
+{
+ int ret = 0, i;
+ unsigned long flags;
+ struct usb_raw_eps_info *info;
+ struct raw_ep *ep;
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->state != STATE_DEV_RUNNING) {
+ dev_dbg(dev->dev, "fail, device is not running\n");
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ goto out_free;
+ }
+ if (!dev->gadget) {
+ dev_dbg(dev->dev, "fail, gadget is not bound\n");
+ ret = -EBUSY;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ goto out_free;
+ }
+
+ memset(info, 0, sizeof(*info));
+ for (i = 0; i < dev->eps_num; i++) {
+ ep = &dev->eps[i];
+ strscpy(&info->eps[i].name[0], ep->ep->name,
+ USB_RAW_EP_NAME_MAX);
+ info->eps[i].addr = ep->addr;
+ fill_ep_caps(&ep->ep->caps, &info->eps[i].caps);
+ fill_ep_limits(ep->ep, &info->eps[i].limits);
+ }
+ ret = dev->eps_num;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (copy_to_user((void __user *)value, info, sizeof(*info)))
+ ret = -EFAULT;
+
+out_free:
+ kfree(info);
+out:
+ return ret;
+}
+
+static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
+{
+ struct raw_dev *dev = fd->private_data;
+ int ret = 0;
+
+ if (!dev)
+ return -EBUSY;
+
+ switch (cmd) {
+ case USB_RAW_IOCTL_INIT:
+ ret = raw_ioctl_init(dev, value);
+ break;
+ case USB_RAW_IOCTL_RUN:
+ ret = raw_ioctl_run(dev, value);
+ break;
+ case USB_RAW_IOCTL_EVENT_FETCH:
+ ret = raw_ioctl_event_fetch(dev, value);
+ break;
+ case USB_RAW_IOCTL_EP0_WRITE:
+ ret = raw_ioctl_ep0_write(dev, value);
+ break;
+ case USB_RAW_IOCTL_EP0_READ:
+ ret = raw_ioctl_ep0_read(dev, value);
+ break;
+ case USB_RAW_IOCTL_EP_ENABLE:
+ ret = raw_ioctl_ep_enable(dev, value);
+ break;
+ case USB_RAW_IOCTL_EP_DISABLE:
+ ret = raw_ioctl_ep_disable(dev, value);
+ break;
+ case USB_RAW_IOCTL_EP_WRITE:
+ ret = raw_ioctl_ep_write(dev, value);
+ break;
+ case USB_RAW_IOCTL_EP_READ:
+ ret = raw_ioctl_ep_read(dev, value);
+ break;
+ case USB_RAW_IOCTL_CONFIGURE:
+ ret = raw_ioctl_configure(dev, value);
+ break;
+ case USB_RAW_IOCTL_VBUS_DRAW:
+ ret = raw_ioctl_vbus_draw(dev, value);
+ break;
+ case USB_RAW_IOCTL_EPS_INFO:
+ ret = raw_ioctl_eps_info(dev, value);
+ break;
+ case USB_RAW_IOCTL_EP0_STALL:
+ ret = raw_ioctl_ep0_stall(dev, value);
+ break;
+ case USB_RAW_IOCTL_EP_SET_HALT:
+ ret = raw_ioctl_ep_set_clear_halt_wedge(
+ dev, value, true, true);
+ break;
+ case USB_RAW_IOCTL_EP_CLEAR_HALT:
+ ret = raw_ioctl_ep_set_clear_halt_wedge(
+ dev, value, false, true);
+ break;
+ case USB_RAW_IOCTL_EP_SET_WEDGE:
+ ret = raw_ioctl_ep_set_clear_halt_wedge(
+ dev, value, true, false);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------*/
+
+static const struct file_operations raw_fops = {
+ .open = raw_open,
+ .unlocked_ioctl = raw_ioctl,
+ .compat_ioctl = raw_ioctl,
+ .release = raw_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice raw_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DRIVER_NAME,
+ .fops = &raw_fops,
+};
+
+module_misc_device(raw_misc_device);
diff --git a/drivers/usb/gadget/legacy/serial.c b/drivers/usb/gadget/legacy/serial.c
index de30d76..da44f89 100644
--- a/drivers/usb/gadget/legacy/serial.c
+++ b/drivers/usb/gadget/legacy/serial.c
@@ -97,6 +97,36 @@
module_param(n_ports, uint, 0);
MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
+static bool enable = true;
+
+static int switch_gserial_enable(bool do_enable);
+
+static int enable_set(const char *s, const struct kernel_param *kp)
+{
+ bool do_enable;
+ int ret;
+
+ if (!s) /* called for no-arg enable == default */
+ return 0;
+
+ ret = strtobool(s, &do_enable);
+ if (ret || enable == do_enable)
+ return ret;
+
+ ret = switch_gserial_enable(do_enable);
+ if (!ret)
+ enable = do_enable;
+
+ return ret;
+}
+
+static const struct kernel_param_ops enable_ops = {
+ .set = enable_set,
+ .get = param_get_bool,
+};
+
+module_param_cb(enable, &enable_ops, &enable, 0644);
+
/*-------------------------------------------------------------------------*/
static struct usb_configuration serial_config_driver = {
@@ -240,6 +270,19 @@
.unbind = gs_unbind,
};
+static int switch_gserial_enable(bool do_enable)
+{
+ if (!serial_config_driver.label)
+ /* init() was not called, yet */
+ return 0;
+
+ if (do_enable)
+ return usb_composite_probe(&gserial_driver);
+
+ usb_composite_unregister(&gserial_driver);
+ return 0;
+}
+
static int __init init(void)
{
/* We *could* export two configs; that'd be much cleaner...
@@ -266,12 +309,16 @@
}
strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
+ if (!enable)
+ return 0;
+
return usb_composite_probe(&gserial_driver);
}
module_init(init);
static void __exit cleanup(void)
{
- usb_composite_unregister(&gserial_driver);
+ if (enable)
+ usb_composite_unregister(&gserial_driver);
}
module_exit(cleanup);
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 6e84b44..23312a0 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -11,8 +11,8 @@
* can write a hardware-agnostic gadget driver running inside a USB device.
* Some hardware details are visible, but don't affect most of the driver.
*
- * Use it with the Linux host/master side "usbtest" driver to get a basic
- * functional test of your device-side usb stack, or with "usb-skeleton".
+ * Use it with the Linux host side "usbtest" driver to get a basic functional
+ * test of your device-side usb stack, or with "usb-skeleton".
*
* It supports two similar configurations. One sinks whatever the usb host
* writes, and in return sources zeroes. The other loops whatever the host
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index d354036..933e80d 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -120,10 +120,10 @@
dynamically linked module called "fotg210_udc".
config USB_GR_UDC
- tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
- depends on HAS_DMA
- help
- Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
+ tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
+ depends on HAS_DMA
+ help
+ Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
VHDL IP core library.
config USB_OMAP
@@ -330,6 +330,7 @@
config USB_FSL_QE
tristate "Freescale QE/CPM USB Device Controller"
depends on FSL_SOC && (QUICC_ENGINE || CPM)
+ depends on !64BIT || BROKEN
help
Some of Freescale PowerPC processors have a Full Speed
QE/CPM2 USB controller, which support device mode with 4
@@ -441,6 +442,27 @@
dynamically linked module called "udc-xilinx" and force all
gadget drivers to also be dynamically linked.
+config USB_MAX3420_UDC
+ tristate "MAX3420 (USB-over-SPI) support"
+ depends on SPI
+ help
+ The Maxim MAX3420 chip supports USB2.0 full-speed peripheral mode.
+ The MAX3420 is run by SPI interface, and hence the dependency.
+
+ To compile this driver as a module, choose M here: the module will
+ be called max3420_udc
+
+config USB_TEGRA_XUDC
+ tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller"
+ depends on ARCH_TEGRA || COMPILE_TEST
+ depends on PHY_TEGRA_XUSB
+ help
+ Enables NVIDIA Tegra USB 3.0 device mode controller driver.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "tegra_xudc" and force all
+ gadget drivers to also be dynamically linked.
+
source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig"
#
@@ -453,7 +475,7 @@
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
- side is the master; the gadget side is the slave. Gadget drivers
+ side is the controller; the gadget side is the device. Gadget drivers
can be high, full, or low speed; and they have access to endpoints
like those from NET2280, PXA2xx, or SA1100 hardware.
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index 897f648..f5a7ce2 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -24,6 +24,7 @@
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
fsl_usb2_udc-y := fsl_udc_core.o
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
+obj-$(CONFIG_USB_TEGRA_XUDC) += tegra-xudc.o
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
obj-$(CONFIG_USB_RENESAS_USB3) += renesas_usb3.o
@@ -41,3 +42,4 @@
obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub/
obj-$(CONFIG_USB_BDC_UDC) += bdc/
+obj-$(CONFIG_USB_MAX3420_UDC) += max3420_udc.o
diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index dfdef6a..3296f3f 100644
--- a/drivers/usb/gadget/udc/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
@@ -2,7 +2,7 @@
/*
* amd5536.h -- header for AMD 5536 UDC high/full speed USB device controller
*
- * Copyright (C) 2007 AMD (http://www.amd.com)
+ * Copyright (C) 2007 AMD (https://www.amd.com)
* Author: Thomas Dahlmann
*/
@@ -440,7 +440,7 @@
/* endpoint data descriptor pointer */
u32 desptr;
- /* reserverd */
+ /* reserved */
u32 reserved;
/* write/read confirmation */
diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c
index 3622840..c80f9bd 100644
--- a/drivers/usb/gadget/udc/amd5536udc_pci.c
+++ b/drivers/usb/gadget/udc/amd5536udc_pci.c
@@ -2,7 +2,7 @@
/*
* amd5536udc_pci.c -- AMD 5536 UDC high/full speed USB device controller
*
- * Copyright (C) 2005-2007 AMD (http://www.amd.com)
+ * Copyright (C) 2005-2007 AMD (https://www.amd.com)
* Author: Thomas Dahlmann
*/
@@ -49,7 +49,6 @@
static struct udc *udc;
/* description */
-static const char mod_desc[] = UDC_MOD_DESCRIPTION;
static const char name[] = "amd5536udc-pci";
/* Reset all pci context */
@@ -116,7 +115,7 @@
goto err_memreg;
}
- dev->virt_addr = ioremap_nocache(resource, len);
+ dev->virt_addr = ioremap(resource, len);
if (!dev->virt_addr) {
dev_dbg(&pdev->dev, "start address cannot be mapped\n");
retval = -EFAULT;
@@ -202,7 +201,7 @@
/* PCI functions */
static struct pci_driver udc_pci_driver = {
- .name = (char *) name,
+ .name = name,
.id_table = pci_id,
.probe = udc_pci_probe,
.remove = udc_pci_remove,
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig
index 83ba8a2..605500b 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig
+++ b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig
@@ -4,5 +4,5 @@
depends on ARCH_ASPEED || COMPILE_TEST
depends on USB_LIBCOMPOSITE
help
- USB peripheral controller for the Aspeed AST2500 family
- SoCs supporting the "vHub" functionality and USB2.0
+ USB peripheral controller for the Aspeed AST2400, AST2500 and
+ AST2600 family SoCs supporting the "vHub" functionality and USB2.0
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
index c1bfbfd..d11d3d1 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -100,7 +100,7 @@
{
struct ast_vhub *vhub = data;
irqreturn_t iret = IRQ_NONE;
- u32 istat;
+ u32 i, istat;
/* Stale interrupt while tearing down */
if (!vhub->ep0_bufs)
@@ -122,10 +122,10 @@
/* Handle generic EPs first */
if (istat & VHUB_IRQ_EP_POOL_ACK_STALL) {
- u32 i, ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
+ u32 ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
writel(ep_acks, vhub->regs + AST_VHUB_EP_ACK_ISR);
- for (i = 0; ep_acks && i < AST_VHUB_NUM_GEN_EPs; i++) {
+ for (i = 0; ep_acks && i < vhub->max_epns; i++) {
u32 mask = VHUB_EP_IRQ(i);
if (ep_acks & mask) {
ast_vhub_epn_ack_irq(&vhub->epns[i]);
@@ -135,21 +135,11 @@
}
/* Handle device interrupts */
- if (istat & (VHUB_IRQ_DEVICE1 |
- VHUB_IRQ_DEVICE2 |
- VHUB_IRQ_DEVICE3 |
- VHUB_IRQ_DEVICE4 |
- VHUB_IRQ_DEVICE5)) {
- if (istat & VHUB_IRQ_DEVICE1)
- ast_vhub_dev_irq(&vhub->ports[0].dev);
- if (istat & VHUB_IRQ_DEVICE2)
- ast_vhub_dev_irq(&vhub->ports[1].dev);
- if (istat & VHUB_IRQ_DEVICE3)
- ast_vhub_dev_irq(&vhub->ports[2].dev);
- if (istat & VHUB_IRQ_DEVICE4)
- ast_vhub_dev_irq(&vhub->ports[3].dev);
- if (istat & VHUB_IRQ_DEVICE5)
- ast_vhub_dev_irq(&vhub->ports[4].dev);
+ if (istat & vhub->port_irq_mask) {
+ for (i = 0; i < vhub->max_ports; i++) {
+ if (istat & VHUB_DEV_IRQ(i))
+ ast_vhub_dev_irq(&vhub->ports[i].dev);
+ }
}
/* Handle top-level vHub EP0 interrupts */
@@ -183,7 +173,7 @@
void ast_vhub_init_hw(struct ast_vhub *vhub)
{
- u32 ctrl;
+ u32 ctrl, port_mask, epn_mask;
UDCDBG(vhub,"(Re)Starting HW ...\n");
@@ -223,15 +213,20 @@
}
/* Reset all devices */
- writel(VHUB_SW_RESET_ALL, vhub->regs + AST_VHUB_SW_RESET);
+ port_mask = GENMASK(vhub->max_ports, 1);
+ writel(VHUB_SW_RESET_ROOT_HUB |
+ VHUB_SW_RESET_DMA_CONTROLLER |
+ VHUB_SW_RESET_EP_POOL |
+ port_mask, vhub->regs + AST_VHUB_SW_RESET);
udelay(1);
writel(0, vhub->regs + AST_VHUB_SW_RESET);
/* Disable and cleanup EP ACK/NACK interrupts */
+ epn_mask = GENMASK(vhub->max_epns - 1, 0);
writel(0, vhub->regs + AST_VHUB_EP_ACK_IER);
writel(0, vhub->regs + AST_VHUB_EP_NACK_IER);
- writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_ACK_ISR);
- writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_NACK_ISR);
+ writel(epn_mask, vhub->regs + AST_VHUB_EP_ACK_ISR);
+ writel(epn_mask, vhub->regs + AST_VHUB_EP_NACK_ISR);
/* Default settings for EP0, enable HW hub EP1 */
writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
@@ -274,7 +269,7 @@
return 0;
/* Remove devices */
- for (i = 0; i < AST_VHUB_NUM_PORTS; i++)
+ for (i = 0; i < vhub->max_ports; i++)
ast_vhub_del_dev(&vhub->ports[i].dev);
spin_lock_irqsave(&vhub->lock, flags);
@@ -296,7 +291,7 @@
if (vhub->ep0_bufs)
dma_free_coherent(&pdev->dev,
AST_VHUB_EP0_MAX_PACKET *
- (AST_VHUB_NUM_PORTS + 1),
+ (vhub->max_ports + 1),
vhub->ep0_bufs,
vhub->ep0_bufs_dma);
vhub->ep0_bufs = NULL;
@@ -310,13 +305,36 @@
struct ast_vhub *vhub;
struct resource *res;
int i, rc = 0;
+ const struct device_node *np = pdev->dev.of_node;
vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL);
if (!vhub)
return -ENOMEM;
+ rc = of_property_read_u32(np, "aspeed,vhub-downstream-ports",
+ &vhub->max_ports);
+ if (rc < 0)
+ vhub->max_ports = AST_VHUB_NUM_PORTS;
+
+ vhub->ports = devm_kcalloc(&pdev->dev, vhub->max_ports,
+ sizeof(*vhub->ports), GFP_KERNEL);
+ if (!vhub->ports)
+ return -ENOMEM;
+
+ rc = of_property_read_u32(np, "aspeed,vhub-generic-endpoints",
+ &vhub->max_epns);
+ if (rc < 0)
+ vhub->max_epns = AST_VHUB_NUM_GEN_EPs;
+
+ vhub->epns = devm_kcalloc(&pdev->dev, vhub->max_epns,
+ sizeof(*vhub->epns), GFP_KERNEL);
+ if (!vhub->epns)
+ return -ENOMEM;
+
spin_lock_init(&vhub->lock);
vhub->pdev = pdev;
+ vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
+ VHUB_IRQ_DEV1_BIT);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vhub->regs = devm_ioremap_resource(&pdev->dev, res);
@@ -367,7 +385,7 @@
*/
vhub->ep0_bufs = dma_alloc_coherent(&pdev->dev,
AST_VHUB_EP0_MAX_PACKET *
- (AST_VHUB_NUM_PORTS + 1),
+ (vhub->max_ports + 1),
&vhub->ep0_bufs_dma, GFP_KERNEL);
if (!vhub->ep0_bufs) {
dev_err(&pdev->dev, "Failed to allocate EP0 DMA buffers\n");
@@ -381,13 +399,15 @@
ast_vhub_init_ep0(vhub, &vhub->ep0, NULL);
/* Init devices */
- for (i = 0; i < AST_VHUB_NUM_PORTS && rc == 0; i++)
+ for (i = 0; i < vhub->max_ports && rc == 0; i++)
rc = ast_vhub_init_dev(vhub, i);
if (rc)
goto err;
/* Init hub emulation */
- ast_vhub_init_hub(vhub);
+ rc = ast_vhub_init_hub(vhub);
+ if (rc)
+ goto err;
/* Initialize HW */
ast_vhub_init_hw(vhub);
@@ -408,6 +428,9 @@
{
.compatible = "aspeed,ast2500-usb-vhub",
},
+ {
+ .compatible = "aspeed,ast2600-usb-vhub",
+ },
{ }
};
MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
index 4008e7a..d268306 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -77,7 +77,7 @@
writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
/* Clear stall on all EPs */
- for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+ for (i = 0; i < d->max_epns; i++) {
struct ast_vhub_ep *ep = d->epns[i];
if (ep && (ep->epn.stalled || ep->epn.wedged)) {
@@ -137,7 +137,7 @@
is_set ? "SET" : "CLEAR", ep_num, wValue);
if (ep_num == 0)
return std_req_complete;
- if (ep_num >= AST_VHUB_NUM_GEN_EPs || !d->epns[ep_num - 1])
+ if (ep_num >= d->max_epns || !d->epns[ep_num - 1])
return std_req_stall;
if (wValue != USB_ENDPOINT_HALT)
return std_req_driver;
@@ -181,7 +181,7 @@
DDBG(d, "GET_STATUS(ep%d)\n", ep_num);
- if (ep_num >= AST_VHUB_NUM_GEN_EPs)
+ if (ep_num >= d->max_epns)
return std_req_stall;
if (ep_num != 0) {
ep = d->epns[ep_num - 1];
@@ -299,7 +299,7 @@
{
unsigned int i;
- for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+ for (i = 0; i < d->max_epns; i++) {
if (!d->epns[i])
continue;
ast_vhub_nuke(d->epns[i], -ESHUTDOWN);
@@ -416,10 +416,10 @@
* that will allow the generic code to use our
* assigned address.
*/
- for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
+ for (i = 0; i < d->max_epns; i++)
if (d->epns[i] == NULL)
break;
- if (i >= AST_VHUB_NUM_GEN_EPs)
+ if (i >= d->max_epns)
return NULL;
addr = i + 1;
@@ -526,6 +526,7 @@
usb_del_gadget_udc(&d->gadget);
device_unregister(d->port_dev);
+ kfree(d->epns);
}
static void ast_vhub_dev_release(struct device *dev)
@@ -547,13 +548,24 @@
ast_vhub_init_ep0(vhub, &d->ep0, d);
/*
+ * A USB device can have up to 30 endpoints besides control
+ * endpoint 0.
+ */
+ d->max_epns = min_t(u32, vhub->max_epns, 30);
+ d->epns = kcalloc(d->max_epns, sizeof(*d->epns), GFP_KERNEL);
+ if (!d->epns)
+ return -ENOMEM;
+
+ /*
* The UDC core really needs us to have separate and uniquely
* named "parent" devices for each port so we create a sub device
* here for that purpose
*/
d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
- if (!d->port_dev)
- return -ENOMEM;
+ if (!d->port_dev) {
+ rc = -ENOMEM;
+ goto fail_alloc;
+ }
device_initialize(d->port_dev);
d->port_dev->release = ast_vhub_dev_release;
d->port_dev->parent = parent;
@@ -584,6 +596,8 @@
device_del(d->port_dev);
fail_add:
put_device(d->port_dev);
+ fail_alloc:
+ kfree(d->epns);
return rc;
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
index 2cd406e..cb164c6 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c
@@ -803,10 +803,10 @@
/* Find a free one (no device) */
spin_lock_irqsave(&vhub->lock, flags);
- for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
+ for (i = 0; i < vhub->max_epns; i++)
if (vhub->epns[i].dev == NULL)
break;
- if (i >= AST_VHUB_NUM_GEN_EPs) {
+ if (i >= vhub->max_epns) {
spin_unlock_irqrestore(&vhub->lock, flags);
return NULL;
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
index 19b3517..bfd8e77 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
@@ -50,6 +50,7 @@
#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
enum {
+ AST_VHUB_STR_INDEX_MAX = 4,
AST_VHUB_STR_MANUF = 3,
AST_VHUB_STR_PRODUCT = 2,
AST_VHUB_STR_SERIAL = 1,
@@ -72,13 +73,6 @@
.bNumConfigurations = 1,
};
-/* Patches to the above when forcing USB1 mode */
-static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
-{
- desc->bcdUSB = cpu_to_le16(0x0100);
- desc->bDeviceProtocol = 0;
-}
-
/*
* Configuration descriptor: same comments as above
* regarding handling USB1 mode.
@@ -93,11 +87,7 @@
USB_DT_INTERFACE_SIZE + \
USB_DT_ENDPOINT_SIZE)
-static const struct ast_vhub_full_cdesc {
- struct usb_config_descriptor cfg;
- struct usb_interface_descriptor intf;
- struct usb_endpoint_descriptor ep;
-} __attribute__ ((packed)) ast_vhub_conf_desc = {
+static const struct ast_vhub_full_cdesc ast_vhub_conf_desc = {
.cfg = {
.bLength = USB_DT_CONFIG_SIZE,
.bDescriptorType = USB_DT_CONFIG,
@@ -266,6 +256,7 @@
u8 desc_type, u16 len)
{
size_t dsize;
+ struct ast_vhub *vhub = ep->vhub;
EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type);
@@ -281,20 +272,20 @@
switch(desc_type) {
case USB_DT_DEVICE:
dsize = USB_DT_DEVICE_SIZE;
- memcpy(ep->buf, &ast_vhub_dev_desc, dsize);
- BUILD_BUG_ON(dsize > sizeof(ast_vhub_dev_desc));
+ memcpy(ep->buf, &vhub->vhub_dev_desc, dsize);
+ BUILD_BUG_ON(dsize > sizeof(vhub->vhub_dev_desc));
BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET);
break;
case USB_DT_CONFIG:
dsize = AST_VHUB_CONF_DESC_SIZE;
- memcpy(ep->buf, &ast_vhub_conf_desc, dsize);
- BUILD_BUG_ON(dsize > sizeof(ast_vhub_conf_desc));
+ memcpy(ep->buf, &vhub->vhub_conf_desc, dsize);
+ BUILD_BUG_ON(dsize > sizeof(vhub->vhub_conf_desc));
BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
break;
case USB_DT_HUB:
dsize = AST_VHUB_HUB_DESC_SIZE;
- memcpy(ep->buf, &ast_vhub_hub_desc, dsize);
- BUILD_BUG_ON(dsize > sizeof(ast_vhub_hub_desc));
+ memcpy(ep->buf, &vhub->vhub_hub_desc, dsize);
+ BUILD_BUG_ON(dsize > sizeof(vhub->vhub_hub_desc));
BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
break;
default:
@@ -305,30 +296,81 @@
if (len > dsize)
len = dsize;
- /* Patch it if forcing USB1 */
- if (desc_type == USB_DT_DEVICE && ep->vhub->force_usb1)
- ast_vhub_patch_dev_desc_usb1(ep->buf);
-
/* Shoot it from the EP buffer */
return ast_vhub_reply(ep, NULL, len);
}
+static struct usb_gadget_strings*
+ast_vhub_str_of_container(struct usb_gadget_string_container *container)
+{
+ return (struct usb_gadget_strings *)container->stash;
+}
+
+static int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
+ size_t size)
+{
+ int rc, hdr_len, nlangs, max_langs;
+ struct usb_gadget_strings *lang_str;
+ struct usb_gadget_string_container *container;
+ struct usb_string_descriptor *sdesc = buf;
+
+ nlangs = 0;
+ hdr_len = sizeof(struct usb_descriptor_header);
+ max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
+ list_for_each_entry(container, &vhub->vhub_str_desc, list) {
+ if (nlangs >= max_langs)
+ break;
+
+ lang_str = ast_vhub_str_of_container(container);
+ sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
+ }
+
+ rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
+ sdesc->bLength = rc;
+ sdesc->bDescriptorType = USB_DT_STRING;
+
+ return rc;
+}
+
+static struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
+ u16 lang_id)
+{
+ struct usb_gadget_strings *lang_str;
+ struct usb_gadget_string_container *container;
+
+ list_for_each_entry(container, &vhub->vhub_str_desc, list) {
+ lang_str = ast_vhub_str_of_container(container);
+ if (lang_str->language == lang_id)
+ return lang_str;
+ }
+
+ return NULL;
+}
+
static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
u8 string_id, u16 lang_id,
u16 len)
{
- int rc = usb_gadget_get_string (&ast_vhub_strings, string_id, ep->buf);
+ int rc;
+ u8 buf[256];
+ struct ast_vhub *vhub = ep->vhub;
+ struct usb_gadget_strings *lang_str;
- /*
- * This should never happen unless we put too big strings in
- * the array above
- */
- BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
+ if (string_id == 0) {
+ rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
+ } else {
+ lang_str = ast_vhub_lookup_string(vhub, lang_id);
+ if (!lang_str)
+ return std_req_stall;
- if (rc < 0)
+ rc = usb_gadget_get_string(lang_str, string_id, buf);
+ }
+
+ if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
return std_req_stall;
/* Shoot it from the EP buffer */
+ memcpy(ep->buf, buf, rc);
return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
}
@@ -504,7 +546,7 @@
* we let the normal host wake path deal with it later.
*/
spin_lock_irqsave(&vhub->lock, flags);
- for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+ for (i = 0; i < vhub->max_ports; i++) {
struct ast_vhub_port *p = &vhub->ports[i];
if (!(p->status & USB_PORT_STAT_SUSPEND))
@@ -587,7 +629,7 @@
struct ast_vhub *vhub = ep->vhub;
struct ast_vhub_port *p;
- if (port == 0 || port > AST_VHUB_NUM_PORTS)
+ if (port == 0 || port > vhub->max_ports)
return std_req_stall;
port--;
p = &vhub->ports[port];
@@ -630,7 +672,7 @@
struct ast_vhub *vhub = ep->vhub;
struct ast_vhub_port *p;
- if (port == 0 || port > AST_VHUB_NUM_PORTS)
+ if (port == 0 || port > vhub->max_ports)
return std_req_stall;
port--;
p = &vhub->ports[port];
@@ -676,7 +718,7 @@
struct ast_vhub *vhub = ep->vhub;
u16 stat, chg;
- if (port == 0 || port > AST_VHUB_NUM_PORTS)
+ if (port == 0 || port > vhub->max_ports)
return std_req_stall;
port--;
@@ -757,7 +799,7 @@
* Forward to unsuspended ports without changing
* their connection status.
*/
- for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+ for (i = 0; i < vhub->max_ports; i++) {
struct ast_vhub_port *p = &vhub->ports[i];
if (!(p->status & USB_PORT_STAT_SUSPEND))
@@ -780,7 +822,7 @@
* Forward to unsuspended ports without changing
* their connection status.
*/
- for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+ for (i = 0; i < vhub->max_ports; i++) {
struct ast_vhub_port *p = &vhub->ports[i];
if (!(p->status & USB_PORT_STAT_SUSPEND))
@@ -814,7 +856,7 @@
* Clear all port status, disable gadgets and "suspend"
* them. They will be woken up by a port reset.
*/
- for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+ for (i = 0; i < vhub->max_ports; i++) {
struct ast_vhub_port *p = &vhub->ports[i];
/* Only keep the connected flag */
@@ -834,9 +876,175 @@
writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
}
-void ast_vhub_init_hub(struct ast_vhub *vhub)
+static void ast_vhub_of_parse_dev_desc(struct ast_vhub *vhub,
+ const struct device_node *vhub_np)
+{
+ u16 id;
+ u32 data;
+
+ if (!of_property_read_u32(vhub_np, "vhub-vendor-id", &data)) {
+ id = (u16)data;
+ vhub->vhub_dev_desc.idVendor = cpu_to_le16(id);
+ }
+ if (!of_property_read_u32(vhub_np, "vhub-product-id", &data)) {
+ id = (u16)data;
+ vhub->vhub_dev_desc.idProduct = cpu_to_le16(id);
+ }
+ if (!of_property_read_u32(vhub_np, "vhub-device-revision", &data)) {
+ id = (u16)data;
+ vhub->vhub_dev_desc.bcdDevice = cpu_to_le16(id);
+ }
+}
+
+static void ast_vhub_fixup_usb1_dev_desc(struct ast_vhub *vhub)
+{
+ vhub->vhub_dev_desc.bcdUSB = cpu_to_le16(0x0100);
+ vhub->vhub_dev_desc.bDeviceProtocol = 0;
+}
+
+static struct usb_gadget_string_container*
+ast_vhub_str_container_alloc(struct ast_vhub *vhub)
+{
+ unsigned int size;
+ struct usb_string *str_array;
+ struct usb_gadget_strings *lang_str;
+ struct usb_gadget_string_container *container;
+
+ size = sizeof(*container);
+ size += sizeof(struct usb_gadget_strings);
+ size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
+ container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
+ if (!container)
+ return ERR_PTR(-ENOMEM);
+
+ lang_str = ast_vhub_str_of_container(container);
+ str_array = (struct usb_string *)(lang_str + 1);
+ lang_str->strings = str_array;
+ return container;
+}
+
+static void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
+ const struct usb_gadget_strings *src)
+{
+ struct usb_string *src_array = src->strings;
+ struct usb_string *dest_array = dest->strings;
+
+ dest->language = src->language;
+ if (src_array && dest_array) {
+ do {
+ *dest_array = *src_array;
+ dest_array++;
+ src_array++;
+ } while (src_array->s);
+ }
+}
+
+static int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
+ const struct usb_gadget_strings *src_str)
+{
+ struct usb_gadget_strings *dest_str;
+ struct usb_gadget_string_container *container;
+
+ container = ast_vhub_str_container_alloc(vhub);
+ if (IS_ERR(container))
+ return PTR_ERR(container);
+
+ dest_str = ast_vhub_str_of_container(container);
+ ast_vhub_str_deep_copy(dest_str, src_str);
+ list_add_tail(&container->list, &vhub->vhub_str_desc);
+
+ return 0;
+}
+
+static const struct {
+ const char *name;
+ u8 id;
+} str_id_map[] = {
+ {"manufacturer", AST_VHUB_STR_MANUF},
+ {"product", AST_VHUB_STR_PRODUCT},
+ {"serial-number", AST_VHUB_STR_SERIAL},
+ {},
+};
+
+static int ast_vhub_of_parse_str_desc(struct ast_vhub *vhub,
+ const struct device_node *desc_np)
+{
+ u32 langid;
+ int ret = 0;
+ int i, offset;
+ const char *str;
+ struct device_node *child;
+ struct usb_string str_array[AST_VHUB_STR_INDEX_MAX];
+ struct usb_gadget_strings lang_str = {
+ .strings = (struct usb_string *)str_array,
+ };
+
+ for_each_child_of_node(desc_np, child) {
+ if (of_property_read_u32(child, "reg", &langid))
+ continue; /* no language identifier specified */
+
+ if (!usb_validate_langid(langid))
+ continue; /* invalid language identifier */
+
+ lang_str.language = langid;
+ for (i = offset = 0; str_id_map[i].name; i++) {
+ str = of_get_property(child, str_id_map[i].name, NULL);
+ if (str) {
+ str_array[offset].s = str;
+ str_array[offset].id = str_id_map[i].id;
+ offset++;
+ }
+ }
+ str_array[offset].id = 0;
+ str_array[offset].s = NULL;
+
+ ret = ast_vhub_str_alloc_add(vhub, &lang_str);
+ if (ret) {
+ of_node_put(child);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int ast_vhub_init_desc(struct ast_vhub *vhub)
+{
+ int ret;
+ struct device_node *desc_np;
+ const struct device_node *vhub_np = vhub->pdev->dev.of_node;
+
+ /* Initialize vhub Device Descriptor. */
+ memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
+ sizeof(vhub->vhub_dev_desc));
+ ast_vhub_of_parse_dev_desc(vhub, vhub_np);
+ if (vhub->force_usb1)
+ ast_vhub_fixup_usb1_dev_desc(vhub);
+
+ /* Initialize vhub Configuration Descriptor. */
+ memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
+ sizeof(vhub->vhub_conf_desc));
+
+ /* Initialize vhub Hub Descriptor. */
+ memcpy(&vhub->vhub_hub_desc, &ast_vhub_hub_desc,
+ sizeof(vhub->vhub_hub_desc));
+ vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
+
+ /* Initialize vhub String Descriptors. */
+ INIT_LIST_HEAD(&vhub->vhub_str_desc);
+ desc_np = of_get_child_by_name(vhub_np, "vhub-strings");
+ if (desc_np)
+ ret = ast_vhub_of_parse_str_desc(vhub, desc_np);
+ else
+ ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);
+
+ return ret;
+}
+
+int ast_vhub_init_hub(struct ast_vhub *vhub)
{
vhub->speed = USB_SPEED_UNKNOWN;
INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
-}
+ return ast_vhub_init_desc(vhub);
+}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
index 761919e..87a5dea 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -2,6 +2,9 @@
#ifndef __ASPEED_VHUB_H
#define __ASPEED_VHUB_H
+#include <linux/usb.h>
+#include <linux/usb/ch11.h>
+
/*****************************
* *
* VHUB register definitions *
@@ -48,14 +51,11 @@
#define VHUB_CTRL_UPSTREAM_CONNECT (1 << 0)
/* IER & ISR */
+#define VHUB_IRQ_DEV1_BIT 9
#define VHUB_IRQ_USB_CMD_DEADLOCK (1 << 18)
#define VHUB_IRQ_EP_POOL_NAK (1 << 17)
#define VHUB_IRQ_EP_POOL_ACK_STALL (1 << 16)
-#define VHUB_IRQ_DEVICE5 (1 << 13)
-#define VHUB_IRQ_DEVICE4 (1 << 12)
-#define VHUB_IRQ_DEVICE3 (1 << 11)
-#define VHUB_IRQ_DEVICE2 (1 << 10)
-#define VHUB_IRQ_DEVICE1 (1 << 9)
+#define VHUB_IRQ_DEVICE1 (1 << (VHUB_IRQ_DEV1_BIT))
#define VHUB_IRQ_BUS_RESUME (1 << 8)
#define VHUB_IRQ_BUS_SUSPEND (1 << 7)
#define VHUB_IRQ_BUS_RESET (1 << 6)
@@ -67,6 +67,9 @@
#define VHUB_IRQ_HUB_EP0_SETUP (1 << 0)
#define VHUB_IRQ_ACK_ALL 0x1ff
+/* Downstream device IRQ mask. */
+#define VHUB_DEV_IRQ(n) (VHUB_IRQ_DEVICE1 << (n))
+
/* SW reset reg */
#define VHUB_SW_RESET_EP_POOL (1 << 9)
#define VHUB_SW_RESET_DMA_CONTROLLER (1 << 8)
@@ -76,17 +79,9 @@
#define VHUB_SW_RESET_DEVICE2 (1 << 2)
#define VHUB_SW_RESET_DEVICE1 (1 << 1)
#define VHUB_SW_RESET_ROOT_HUB (1 << 0)
-#define VHUB_SW_RESET_ALL (VHUB_SW_RESET_EP_POOL | \
- VHUB_SW_RESET_DMA_CONTROLLER | \
- VHUB_SW_RESET_DEVICE5 | \
- VHUB_SW_RESET_DEVICE4 | \
- VHUB_SW_RESET_DEVICE3 | \
- VHUB_SW_RESET_DEVICE2 | \
- VHUB_SW_RESET_DEVICE1 | \
- VHUB_SW_RESET_ROOT_HUB)
+
/* EP ACK/NACK IRQ masks */
#define VHUB_EP_IRQ(n) (1 << (n))
-#define VHUB_EP_IRQ_ALL 0x7fff /* 15 EPs */
/* USB status reg */
#define VHUB_USBSTS_HISPEED (1 << 27)
@@ -210,6 +205,11 @@
* *
****************************************/
+/*
+ * AST_VHUB_NUM_GEN_EPs and AST_VHUB_NUM_PORTS are kept to avoid breaking
+ * existing AST2400/AST2500 platforms. AST2600 and future vhub revisions
+ * should define number of downstream ports and endpoints in device tree.
+ */
#define AST_VHUB_NUM_GEN_EPs 15 /* Generic non-0 EPs */
#define AST_VHUB_NUM_PORTS 5 /* vHub ports */
#define AST_VHUB_EP0_MAX_PACKET 64 /* EP0's max packet size */
@@ -312,7 +312,7 @@
/* Registers */
void __iomem *regs;
- /* Index in global pool (0..14) */
+ /* Index in global pool (zero-based) */
unsigned int g_idx;
/* DMA Descriptors */
@@ -342,7 +342,7 @@
struct ast_vhub *vhub;
void __iomem *regs;
- /* Device index (0...4) and name string */
+ /* Device index (zero-based) and name string */
unsigned int index;
const char *name;
@@ -358,7 +358,8 @@
/* Endpoint structures */
struct ast_vhub_ep ep0;
- struct ast_vhub_ep *epns[AST_VHUB_NUM_GEN_EPs];
+ struct ast_vhub_ep **epns;
+ u32 max_epns;
};
#define to_ast_dev(__g) container_of(__g, struct ast_vhub_dev, gadget)
@@ -373,6 +374,12 @@
struct ast_vhub_dev dev;
};
+struct ast_vhub_full_cdesc {
+ struct usb_config_descriptor cfg;
+ struct usb_interface_descriptor intf;
+ struct usb_endpoint_descriptor ep;
+} __packed;
+
/* Global vhub structure */
struct ast_vhub {
struct platform_device *pdev;
@@ -393,10 +400,13 @@
bool ep1_stalled : 1;
/* Per-port info */
- struct ast_vhub_port ports[AST_VHUB_NUM_PORTS];
+ struct ast_vhub_port *ports;
+ u32 max_ports;
+ u32 port_irq_mask;
/* Generic EP data structures */
- struct ast_vhub_ep epns[AST_VHUB_NUM_GEN_EPs];
+ struct ast_vhub_ep *epns;
+ u32 max_epns;
/* Upstream bus is suspended ? */
bool suspended : 1;
@@ -409,6 +419,12 @@
/* Upstream bus speed captured at bus reset */
unsigned int speed;
+
+ /* Standard USB Descriptors of the vhub. */
+ struct usb_device_descriptor vhub_dev_desc;
+ struct ast_vhub_full_cdesc vhub_conf_desc;
+ struct usb_hub_descriptor vhub_hub_desc;
+ struct list_head vhub_str_desc;
};
/* Standard request handlers result codes */
@@ -518,7 +534,7 @@
__VA_ARGS__)
/* hub.c */
-void ast_vhub_init_hub(struct ast_vhub *vhub);
+int ast_vhub_init_hub(struct ast_vhub *vhub);
enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
struct usb_ctrlrequest *crq);
enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index d7714c9..d9ad9ad 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -1808,7 +1808,6 @@
struct device *dev = &pdev->dev;
struct at91_udc *udc;
int retval;
- struct resource *res;
struct at91_ep *ep;
int i;
@@ -1839,8 +1838,7 @@
ep->is_pingpong = 1;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- udc->udp_baseaddr = devm_ioremap_resource(dev, res);
+ udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(udc->udp_baseaddr))
return PTR_ERR(udc->udp_baseaddr);
@@ -2025,7 +2023,7 @@
.suspend = at91udc_suspend,
.resume = at91udc_resume,
.driver = {
- .name = (char *) driver_name,
+ .name = driver_name,
.of_match_table = at91_udc_dt_ids,
},
};
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index bebe814..2b893bc 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/ctype.h>
+#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/delay.h>
@@ -184,7 +185,7 @@
return 0;
}
-const struct file_operations queue_dbg_fops = {
+static const struct file_operations queue_dbg_fops = {
.owner = THIS_MODULE,
.open = queue_dbg_open,
.llseek = no_llseek,
@@ -192,7 +193,7 @@
.release = queue_dbg_release,
};
-const struct file_operations regs_dbg_fops = {
+static const struct file_operations regs_dbg_fops = {
.owner = THIS_MODULE,
.open = regs_dbg_open,
.llseek = generic_file_llseek,
@@ -226,7 +227,7 @@
struct dentry *root;
struct resource *regs_resource;
- root = debugfs_create_dir(udc->gadget.name, NULL);
+ root = debugfs_create_dir(udc->gadget.name, usb_debug_root);
udc->debugfs_root = root;
regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM,
@@ -327,7 +328,7 @@
switch (fifo_mode) {
default:
fifo_mode = 0;
- /* fall through */
+ fallthrough;
case 0:
udc->fifo_cfg = NULL;
n = 0;
@@ -675,13 +676,7 @@
if (!ep->ep.desc) {
spin_unlock_irqrestore(&udc->lock, flags);
- /* REVISIT because this driver disables endpoints in
- * reset_all_endpoints() before calling disconnect(),
- * most gadget drivers would trigger this non-error ...
- */
- if (udc->gadget.speed != USB_SPEED_UNKNOWN)
- DBG(DBG_ERR, "ep_disable: %s not enabled\n",
- ep->ep.name);
+ DBG(DBG_ERR, "ep_disable: %s not enabled\n", ep->ep.name);
return -EINVAL;
}
ep->ep.desc = NULL;
@@ -1033,6 +1028,7 @@
return 0;
}
+static int atmel_usba_pullup(struct usb_gadget *gadget, int is_on);
static int atmel_usba_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
static int atmel_usba_stop(struct usb_gadget *gadget);
@@ -1060,16 +1056,19 @@
switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_CONTROL:
+ ep->nr_banks = 1;
break;
case USB_ENDPOINT_XFER_ISOC:
ep->fifo_size = 1024;
- ep->nr_banks = 2;
+ if (ep->udc->ep_prealloc)
+ ep->nr_banks = 2;
break;
case USB_ENDPOINT_XFER_BULK:
ep->fifo_size = 512;
- ep->nr_banks = 1;
+ if (ep->udc->ep_prealloc)
+ ep->nr_banks = 1;
break;
case USB_ENDPOINT_XFER_INT:
@@ -1079,7 +1078,8 @@
else
ep->fifo_size =
roundup_pow_of_two(le16_to_cpu(desc->wMaxPacketSize));
- ep->nr_banks = 1;
+ if (ep->udc->ep_prealloc)
+ ep->nr_banks = 1;
break;
}
@@ -1095,8 +1095,6 @@
USBA_BF(EPT_SIZE, fls(ep->fifo_size - 1) - 3);
ep->ept_cfg |= USBA_BF(BK_NUMBER, ep->nr_banks);
-
- ep->udc->configured_ep++;
}
return _ep;
@@ -1106,6 +1104,7 @@
.get_frame = usba_udc_get_frame,
.wakeup = usba_udc_wakeup,
.set_selfpowered = usba_udc_set_selfpowered,
+ .pullup = atmel_usba_pullup,
.udc_start = atmel_usba_start,
.udc_stop = atmel_usba_stop,
.match_ep = atmel_usba_match_ep,
@@ -1121,7 +1120,7 @@
.bInterval = 1,
};
-static struct usb_gadget usba_gadget_template = {
+static const struct usb_gadget usba_gadget_template = {
.ops = &usba_udc_ops,
.max_speed = USB_SPEED_HIGH,
.name = "atmel_usba_udc",
@@ -1789,7 +1788,7 @@
if (status & USBA_END_OF_RESET) {
struct usba_ep *ep0, *ep;
- int i, n;
+ int i;
usba_writel(udc, INT_CLR,
USBA_END_OF_RESET|USBA_END_OF_RESUME
@@ -1837,13 +1836,14 @@
"ODD: EP0 configuration is invalid!\n");
/* Preallocate other endpoints */
- n = fifo_mode ? udc->num_ep : udc->configured_ep;
- for (i = 1; i < n; i++) {
+ for (i = 1; i < udc->num_ep; i++) {
ep = &udc->usba_ep[i];
- usba_ep_writel(ep, CFG, ep->ept_cfg);
- if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
- dev_err(&udc->pdev->dev,
- "ODD: EP%d configuration is invalid!\n", i);
+ if (ep->ep.claimed) {
+ usba_ep_writel(ep, CFG, ep->ept_cfg);
+ if (!(usba_ep_readl(ep, CFG) & USBA_EPT_MAPPED))
+ dev_err(&udc->pdev->dev,
+ "ODD: EP%d configuration is invalid!\n", i);
+ }
}
}
@@ -1962,6 +1962,24 @@
return IRQ_HANDLED;
}
+static int atmel_usba_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
+ unsigned long flags;
+ u32 ctrl;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ ctrl = usba_readl(udc, CTRL);
+ if (is_on)
+ ctrl &= ~USBA_DETACH;
+ else
+ ctrl |= USBA_DETACH;
+ usba_writel(udc, CTRL, ctrl);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
static int atmel_usba_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
@@ -2010,9 +2028,6 @@
if (udc->vbus_pin)
disable_irq(gpiod_to_irq(udc->vbus_pin));
- if (fifo_mode == 0)
- udc->configured_ep = 1;
-
udc->suspended = false;
usba_stop(udc);
@@ -2042,37 +2057,106 @@
.pulse_bias = at91sam9g45_pulse_bias,
};
+static const struct usba_ep_config ep_config_sam9[] __initconst = {
+ { .nr_banks = 1 }, /* ep 0 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
+ { .nr_banks = 3, .can_dma = 1 }, /* ep 3 */
+ { .nr_banks = 3, .can_dma = 1 }, /* ep 4 */
+ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
+ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
+};
+
+static const struct usba_ep_config ep_config_sama5[] __initconst = {
+ { .nr_banks = 1 }, /* ep 0 */
+ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
+ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 3 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 4 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
+ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 7 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 8 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 9 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 10 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 11 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 12 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 13 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 14 */
+ { .nr_banks = 2, .can_isoc = 1 }, /* ep 15 */
+};
+
+static const struct usba_udc_config udc_at91sam9rl_cfg = {
+ .errata = &at91sam9rl_errata,
+ .config = ep_config_sam9,
+ .num_ep = ARRAY_SIZE(ep_config_sam9),
+ .ep_prealloc = true,
+};
+
+static const struct usba_udc_config udc_at91sam9g45_cfg = {
+ .errata = &at91sam9g45_errata,
+ .config = ep_config_sam9,
+ .num_ep = ARRAY_SIZE(ep_config_sam9),
+ .ep_prealloc = true,
+};
+
+static const struct usba_udc_config udc_sama5d3_cfg = {
+ .config = ep_config_sama5,
+ .num_ep = ARRAY_SIZE(ep_config_sama5),
+ .ep_prealloc = true,
+};
+
+static const struct usba_udc_config udc_sam9x60_cfg = {
+ .num_ep = ARRAY_SIZE(ep_config_sam9),
+ .config = ep_config_sam9,
+ .ep_prealloc = false,
+};
+
static const struct of_device_id atmel_udc_dt_ids[] = {
- { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
- { .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
- { .compatible = "atmel,sama5d3-udc" },
+ { .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg },
+ { .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg },
+ { .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg },
+ { .compatible = "microchip,sam9x60-udc", .data = &udc_sam9x60_cfg },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
+static const struct of_device_id atmel_pmc_dt_ids[] = {
+ { .compatible = "atmel,at91sam9g45-pmc" },
+ { .compatible = "atmel,at91sam9rl-pmc" },
+ { .compatible = "atmel,at91sam9x5-pmc" },
+ { /* sentinel */ }
+};
+
static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
struct usba_udc *udc)
{
- u32 val;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct device_node *pp;
int i, ret;
struct usba_ep *eps, *ep;
+ const struct usba_udc_config *udc_config;
match = of_match_node(atmel_udc_dt_ids, np);
if (!match)
return ERR_PTR(-EINVAL);
- udc->errata = match->data;
- udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
- if (IS_ERR(udc->pmc))
- udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
- if (IS_ERR(udc->pmc))
- udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc");
- if (udc->errata && IS_ERR(udc->pmc))
- return ERR_CAST(udc->pmc);
+ udc_config = match->data;
+ udc->ep_prealloc = udc_config->ep_prealloc;
+ udc->errata = udc_config->errata;
+ if (udc->errata) {
+ pp = of_find_matching_node_and_match(NULL, atmel_pmc_dt_ids,
+ NULL);
+ if (!pp)
+ return ERR_PTR(-ENODEV);
+
+ udc->pmc = syscon_node_to_regmap(pp);
+ of_node_put(pp);
+ if (IS_ERR(udc->pmc))
+ return ERR_CAST(udc->pmc);
+ }
udc->num_ep = 0;
@@ -2080,10 +2164,7 @@
GPIOD_IN);
if (fifo_mode == 0) {
- pp = NULL;
- while ((pp = of_get_next_child(np, pp)))
- udc->num_ep++;
- udc->configured_ep = 1;
+ udc->num_ep = udc_config->num_ep;
} else {
udc->num_ep = usba_config_fifo_table(udc);
}
@@ -2097,54 +2178,39 @@
INIT_LIST_HEAD(&eps[0].ep.ep_list);
- pp = NULL;
i = 0;
- while ((pp = of_get_next_child(np, pp)) && i < udc->num_ep) {
+ while (i < udc->num_ep) {
+ const struct usba_ep_config *ep_cfg = &udc_config->config[i];
+
ep = &eps[i];
- ret = of_property_read_u32(pp, "reg", &val);
- if (ret) {
- dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret);
- goto err;
- }
- ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : val;
+ ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : i;
- ret = of_property_read_u32(pp, "atmel,fifo-size", &val);
- if (ret) {
- dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
- goto err;
- }
+ /* Only the first EP is 64 bytes */
+ if (ep->index == 0)
+ ep->fifo_size = 64;
+ else
+ ep->fifo_size = 1024;
+
if (fifo_mode) {
- if (val < udc->fifo_cfg[i].fifo_size) {
+ if (ep->fifo_size < udc->fifo_cfg[i].fifo_size)
dev_warn(&pdev->dev,
- "Using max fifo-size value from DT\n");
- ep->fifo_size = val;
- } else {
+ "Using default max fifo-size value\n");
+ else
ep->fifo_size = udc->fifo_cfg[i].fifo_size;
- }
- } else {
- ep->fifo_size = val;
}
- ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
- if (ret) {
- dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
- goto err;
- }
+ ep->nr_banks = ep_cfg->nr_banks;
if (fifo_mode) {
- if (val < udc->fifo_cfg[i].nr_banks) {
+ if (ep->nr_banks < udc->fifo_cfg[i].nr_banks)
dev_warn(&pdev->dev,
- "Using max nb-banks value from DT\n");
- ep->nr_banks = val;
- } else {
+ "Using default max nb-banks value\n");
+ else
ep->nr_banks = udc->fifo_cfg[i].nr_banks;
- }
- } else {
- ep->nr_banks = val;
}
- ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
- ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
+ ep->can_dma = ep_cfg->can_dma;
+ ep->can_isoc = ep_cfg->can_isoc;
sprintf(ep->name, "ep%d", ep->index);
ep->ep.name = ep->name;
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index a0225e4..620472f 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -290,6 +290,12 @@
#endif
};
+struct usba_ep_config {
+ u8 nr_banks;
+ unsigned int can_dma:1;
+ unsigned int can_isoc:1;
+};
+
struct usba_request {
struct usb_request req;
struct list_head queue;
@@ -307,6 +313,13 @@
void (*pulse_bias)(struct usba_udc *udc);
};
+struct usba_udc_config {
+ const struct usba_udc_errata *errata;
+ const struct usba_ep_config *config;
+ const int num_ep;
+ const bool ep_prealloc;
+};
+
struct usba_udc {
/* Protect hw registers from concurrent modifications */
spinlock_t lock;
@@ -324,7 +337,6 @@
int irq;
struct gpio_desc *vbus_pin;
int num_ep;
- int configured_ep;
struct usba_fifo_cfg *fifo_cfg;
struct clk *pclk;
struct clk *hclk;
@@ -332,6 +344,7 @@
bool bias_pulse_needed;
bool clocked;
bool suspended;
+ bool ep_prealloc;
u16 devstatus;
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index 97b1646..9cd4a70 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -26,6 +26,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/timer.h>
+#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/workqueue.h>
@@ -266,8 +267,8 @@
* @pd: Platform data (board/port info).
* @usbd_clk: Clock descriptor for the USB device block.
* @usbh_clk: Clock descriptor for the USB host block.
- * @gadget: USB slave device.
- * @driver: Driver for USB slave devices.
+ * @gadget: USB device.
+ * @driver: Driver for USB device.
* @usbd_regs: Base address of the USBD/USB20D block.
* @iudma_regs: Base address of the USBD's associated IUDMA block.
* @bep: Array of endpoints, including ep0.
@@ -1744,7 +1745,7 @@
/**
* bcm63xx_udc_get_frame - Read current SOF frame number from the HW.
- * @gadget: USB slave device.
+ * @gadget: USB device.
*/
static int bcm63xx_udc_get_frame(struct usb_gadget *gadget)
{
@@ -1756,7 +1757,7 @@
/**
* bcm63xx_udc_pullup - Enable/disable pullup on D+ line.
- * @gadget: USB slave device.
+ * @gadget: USB device.
* @is_on: 0 to disable pullup, 1 to enable.
*
* See notes in bcm63xx_select_pullup().
@@ -1805,8 +1806,8 @@
/**
* bcm63xx_udc_start - Start the controller.
- * @gadget: USB slave device.
- * @driver: Driver for USB slave devices.
+ * @gadget: USB device.
+ * @driver: Driver for USB device.
*/
static int bcm63xx_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
@@ -1842,8 +1843,8 @@
/**
* bcm63xx_udc_stop - Shut down the controller.
- * @gadget: USB slave device.
- * @driver: Driver for USB slave devices.
+ * @gadget: USB device.
+ * @driver: Driver for USB device.
*/
static int bcm63xx_udc_stop(struct usb_gadget *gadget)
{
@@ -2248,7 +2249,7 @@
if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS))
return;
- root = debugfs_create_dir(udc->gadget.name, NULL);
+ root = debugfs_create_dir(udc->gadget.name, usb_debug_root);
udc->debugfs_root = root;
debugfs_create_file("usbd", 0400, root, udc, &bcm63xx_usbd_dbg_fops);
@@ -2282,7 +2283,6 @@
struct device *dev = &pdev->dev;
struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev);
struct bcm63xx_udc *udc;
- struct resource *res;
int rc = -ENOMEM, i, irq;
udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
@@ -2298,13 +2298,11 @@
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- udc->usbd_regs = devm_ioremap_resource(dev, res);
+ udc->usbd_regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(udc->usbd_regs))
return PTR_ERR(udc->usbd_regs);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- udc->iudma_regs = devm_ioremap_resource(dev, res);
+ udc->iudma_regs = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(udc->iudma_regs))
return PTR_ERR(udc->iudma_regs);
diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h
index 6e1e881..ac75e25 100644
--- a/drivers/usb/gadget/udc/bdc/bdc.h
+++ b/drivers/usb/gadget/udc/bdc/bdc.h
@@ -44,7 +44,7 @@
#define NUM_SR_ENTRIES 64
/* Num of bds per table */
-#define NUM_BDS_PER_TABLE 32
+#define NUM_BDS_PER_TABLE 64
/* Num of tables in bd list for control,bulk and Int ep */
#define NUM_TABLES 2
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index 845aead..fa1a390 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -12,6 +12,7 @@
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/list.h>
@@ -29,24 +30,19 @@
#include "bdc_dbg.h"
/* Poll till controller status is not OIP */
-static int poll_oip(struct bdc *bdc, int usec)
+static int poll_oip(struct bdc *bdc, u32 usec)
{
u32 status;
- /* Poll till STS!= OIP */
- while (usec) {
- status = bdc_readl(bdc->regs, BDC_BDCSC);
- if (BDC_CSTS(status) != BDC_OIP) {
- dev_dbg(bdc->dev,
- "poll_oip complete status=%d",
- BDC_CSTS(status));
- return 0;
- }
- udelay(10);
- usec -= 10;
- }
- dev_err(bdc->dev, "Err: operation timedout BDCSC: 0x%08x\n", status);
+ int ret;
- return -ETIMEDOUT;
+ ret = readl_poll_timeout(bdc->regs + BDC_BDCSC, status,
+ (BDC_CSTS(status) != BDC_OIP), 10, usec);
+ if (ret)
+ dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status);
+ else
+ dev_dbg(bdc->dev, "%s complete status=%d", __func__, BDC_CSTS(status));
+
+ return ret;
}
/* Stop the BDC controller */
@@ -292,9 +288,13 @@
/* Initialize SRR to 0 */
memset(bdc->srr.sr_bds, 0,
NUM_SR_ENTRIES * sizeof(struct bdc_bd));
- /* clear ep flags to avoid post disconnect stops/deconfigs */
- for (i = 1; i < bdc->num_eps; ++i)
- bdc->bdc_ep_array[i]->flags = 0;
+ /*
+ * clear ep flags to avoid post disconnect stops/deconfigs but
+ * not during S2 exit
+ */
+ if (!bdc->gadget.speed)
+ for (i = 1; i < bdc->num_eps; ++i)
+ bdc->bdc_ep_array[i]->flags = 0;
} else {
/* One time initiaization only */
/* Enable status report function pointers */
@@ -484,40 +484,22 @@
static int bdc_probe(struct platform_device *pdev)
{
struct bdc *bdc;
- struct resource *res;
- int ret = -ENOMEM;
+ int ret;
int irq;
u32 temp;
struct device *dev = &pdev->dev;
- struct clk *clk;
int phy_num;
dev_dbg(dev, "%s()\n", __func__);
- clk = devm_clk_get(dev, "sw_usbd");
- if (IS_ERR(clk)) {
- dev_info(dev, "Clock not found in Device Tree\n");
- clk = NULL;
- }
-
- ret = clk_prepare_enable(clk);
- if (ret) {
- dev_err(dev, "could not enable clock\n");
- return ret;
- }
-
bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL);
if (!bdc)
return -ENOMEM;
- bdc->clk = clk;
+ bdc->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(bdc->regs))
+ return PTR_ERR(bdc->regs);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- bdc->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(bdc->regs)) {
- dev_err(dev, "ioremap error\n");
- return -ENOMEM;
- }
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
@@ -550,10 +532,20 @@
}
}
+ bdc->clk = devm_clk_get_optional(dev, "sw_usbd");
+ if (IS_ERR(bdc->clk))
+ return PTR_ERR(bdc->clk);
+
+ ret = clk_prepare_enable(bdc->clk);
+ if (ret) {
+ dev_err(dev, "could not enable clock\n");
+ return ret;
+ }
+
ret = bdc_phy_init(bdc);
if (ret) {
dev_err(bdc->dev, "BDC phy init failure:%d\n", ret);
- return ret;
+ goto disable_clk;
}
temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
@@ -586,6 +578,8 @@
bdc_hw_exit(bdc);
phycleanup:
bdc_phy_exit(bdc);
+disable_clk:
+ clk_disable_unprepare(bdc->clk);
return ret;
}
@@ -641,7 +635,7 @@
bdc_resume);
static const struct of_device_id bdc_of_match[] = {
- { .compatible = "brcm,bdc-v0.16" },
+ { .compatible = "brcm,bdc-udc-v2" },
{ .compatible = "brcm,bdc" },
{ /* sentinel */ }
};
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index 9ddc0b4..fafdc9f 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -929,11 +929,11 @@
usb2_pm &= ~BDC_PTC_MASK;
dev_dbg(bdc->dev, "%s\n", __func__);
switch (bdc->test_mode) {
- case TEST_J:
- case TEST_K:
- case TEST_SE0_NAK:
- case TEST_PACKET:
- case TEST_FORCE_EN:
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET:
+ case USB_TEST_FORCE_ENABLE:
usb2_pm |= bdc->test_mode << 28;
break;
default:
diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c
index 7bfd58c..248426a 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_udc.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
@@ -195,7 +195,7 @@
break;
case BDC_LINK_STATE_U0:
if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) {
- bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED;
+ bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED;
if (bdc->gadget.speed == USB_SPEED_SUPER) {
bdc_function_wake_fh(bdc, 0);
bdc->devstatus |= FUNC_WAKE_ISSUED;
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index e41f67c..3a3b5a0 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* udc.c - Core UDC Framework
*
* Copyright (C) 2010 Texas Instruments
@@ -23,11 +23,11 @@
/**
* struct usb_udc - describes one usb device controller
- * @driver - the gadget driver pointer. For use by the class code
- * @dev - the child device to the actual controller
- * @gadget - the gadget. For use by the class code
- * @list - for use by the udc class driver
- * @vbus - for udcs who care about vbus status, this value is real vbus status;
+ * @driver: the gadget driver pointer. For use by the class code
+ * @dev: the child device to the actual controller
+ * @gadget: the gadget. For use by the class code
+ * @list: for use by the udc class driver
+ * @vbus: for udcs who care about vbus status, this value is real vbus status;
* for udcs who do not care about vbus status, this value is always true
*
* This represents the internal data structure which is used by the UDC-class
@@ -85,7 +85,7 @@
* for interrupt transfers as well as bulk, but it likely couldn't be used
* for iso transfers or for endpoint 14. some endpoints are fully
* configurable, with more generic names like "ep-a". (remember that for
- * USB, "in" means "towards the USB master".)
+ * USB, "in" means "towards the USB host".)
*
* This routine must be called in process context.
*
@@ -715,6 +715,9 @@
goto out;
}
+ if (!gadget->connected)
+ goto out;
+
if (gadget->deactivated) {
/*
* If gadget is deactivated we only save new state.
@@ -891,6 +894,9 @@
/**
* usb_gadget_giveback_request - give the request back to the gadget layer
+ * @ep: the endpoint to be used with with the request
+ * @req: the request being given back
+ *
* Context: in_interrupt()
*
* This is called by device controller drivers in order to return the
@@ -1084,8 +1090,7 @@
/**
* usb_gadget_udc_stop - tells usb device controller we don't need it anymore
- * @gadget: The device we want to stop activity
- * @driver: The driver to unbind from @gadget
+ * @udc: The UDC to be stopped
*
* This call is issued by the UDC Class driver after calling
* gadget driver's unbind() method.
@@ -1162,21 +1167,18 @@
}
/**
- * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
+ * usb_initialize_gadget - initialize a gadget and its embedded struct device
* @parent: the parent device to this udc. Usually the controller driver's
* device.
- * @gadget: the gadget to be added to the list.
+ * @gadget: the gadget to be initialized.
* @release: a gadget release function.
*
* Returns zero on success, negative errno otherwise.
* Calls the gadget release function in the latter case.
*/
-int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
+void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
- struct usb_udc *udc;
- int ret = -ENOMEM;
-
dev_set_name(&gadget->dev, "gadget");
INIT_WORK(&gadget->work, usb_gadget_state_work);
gadget->dev.parent = parent;
@@ -1187,17 +1189,32 @@
gadget->dev.release = usb_udc_nop_release;
device_initialize(&gadget->dev);
+}
+EXPORT_SYMBOL_GPL(usb_initialize_gadget);
+
+/**
+ * usb_add_gadget - adds a new gadget to the udc class driver list
+ * @gadget: the gadget to be added to the list.
+ *
+ * Returns zero on success, negative errno otherwise.
+ * Does not do a final usb_put_gadget() if an error occurs.
+ */
+int usb_add_gadget(struct usb_gadget *gadget)
+{
+ struct usb_udc *udc;
+ int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc)
- goto err_put_gadget;
+ goto error;
device_initialize(&udc->dev);
udc->dev.release = usb_udc_release;
udc->dev.class = udc_class;
udc->dev.groups = usb_udc_attr_groups;
- udc->dev.parent = parent;
- ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
+ udc->dev.parent = gadget->dev.parent;
+ ret = dev_set_name(&udc->dev, "%s",
+ kobject_name(&gadget->dev.parent->kobj));
if (ret)
goto err_put_udc;
@@ -1228,6 +1245,7 @@
return 0;
err_del_udc:
+ flush_work(&gadget->work);
device_del(&udc->dev);
err_unlist_udc:
@@ -1239,8 +1257,30 @@
err_put_udc:
put_device(&udc->dev);
- err_put_gadget:
- put_device(&gadget->dev);
+ error:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget);
+
+/**
+ * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
+ * @parent: the parent device to this udc. Usually the controller driver's
+ * device.
+ * @gadget: the gadget to be added to the list.
+ * @release: a gadget release function.
+ *
+ * Returns zero on success, negative errno otherwise.
+ * Calls the gadget release function in the latter case.
+ */
+int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
+ void (*release)(struct device *dev))
+{
+ int ret;
+
+ usb_initialize_gadget(parent, gadget, release);
+ ret = usb_add_gadget(gadget);
+ if (ret)
+ usb_put_gadget(gadget);
return ret;
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release);
@@ -1303,18 +1343,18 @@
usb_gadget_udc_stop(udc);
udc->driver = NULL;
- udc->dev.driver = NULL;
udc->gadget->dev.driver = NULL;
}
/**
- * usb_del_gadget_udc - deletes @udc from udc_list
+ * usb_del_gadget - deletes @udc from udc_list
* @gadget: the gadget to be removed.
*
- * This, will call usb_gadget_unregister_driver() if
+ * This will call usb_gadget_unregister_driver() if
* the @udc is still busy.
+ * It will not do a final usb_put_gadget().
*/
-void usb_del_gadget_udc(struct usb_gadget *gadget)
+void usb_del_gadget(struct usb_gadget *gadget)
{
struct usb_udc *udc = gadget->udc;
@@ -1337,8 +1377,20 @@
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
flush_work(&gadget->work);
device_unregister(&udc->dev);
- device_unregister(&gadget->dev);
- memset(&gadget->dev, 0x00, sizeof(gadget->dev));
+ device_del(&gadget->dev);
+}
+EXPORT_SYMBOL_GPL(usb_del_gadget);
+
+/**
+ * usb_del_gadget_udc - deletes @udc from udc_list
+ * @gadget: the gadget to be removed.
+ *
+ * Calls usb_del_gadget() and does a final usb_put_gadget().
+ */
+void usb_del_gadget_udc(struct usb_gadget *gadget)
+{
+ usb_del_gadget(gadget);
+ usb_put_gadget(gadget);
}
EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
@@ -1352,7 +1404,6 @@
driver->function);
udc->driver = driver;
- udc->dev.driver = &driver->driver;
udc->gadget->dev.driver = &driver->driver;
usb_gadget_udc_set_speed(udc, driver->max_speed);
@@ -1374,7 +1425,6 @@
dev_err(&udc->dev, "failed to start %s: %d\n",
udc->driver->function, ret);
udc->driver = NULL;
- udc->dev.driver = NULL;
udc->gadget->dev.driver = NULL;
return ret;
}
@@ -1416,6 +1466,8 @@
}
mutex_unlock(&udc_lock);
+ if (ret)
+ pr_warn("udc-core: couldn't find an available UDC or it's busy\n");
return ret;
found:
ret = udc_bind_to_driver(udc, driver);
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 58261ec..92d01dd 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -14,7 +14,7 @@
* Linux-USB host controller driver. USB traffic is simulated; there's
* no need for USB hardware. Use this with two other drivers:
*
- * - Gadget driver, responding to requests (slave);
+ * - Gadget driver, responding to requests (device);
* - Host-side device driver, as already familiar in Linux.
*
* Having this all in one kernel can help some stages of development,
@@ -187,31 +187,31 @@
USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
/* and now some generic EPs so we have enough in multi config */
- EP_INFO("ep3out",
+ EP_INFO("ep-aout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep4in",
+ EP_INFO("ep-bin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep5out",
+ EP_INFO("ep-cout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep6out",
+ EP_INFO("ep-dout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep7in",
+ EP_INFO("ep-ein",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep8out",
+ EP_INFO("ep-fout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep9in",
+ EP_INFO("ep-gin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep10out",
+ EP_INFO("ep-hout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep11out",
+ EP_INFO("ep-iout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep12in",
+ EP_INFO("ep-jin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep13out",
+ EP_INFO("ep-kout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
- EP_INFO("ep14in",
+ EP_INFO("ep-lin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
- EP_INFO("ep15out",
+ EP_INFO("ep-mout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
#undef EP_INFO
@@ -261,7 +261,7 @@
spinlock_t lock;
/*
- * SLAVE/GADGET side support
+ * DEVICE/GADGET side support
*/
struct dummy_ep ep[DUMMY_ENDPOINTS];
int address;
@@ -276,7 +276,7 @@
unsigned pullup:1;
/*
- * MASTER/HOST side support
+ * HOST side support
*/
struct dummy_hcd *hs_hcd;
struct dummy_hcd *ss_hcd;
@@ -323,7 +323,7 @@
/*-------------------------------------------------------------------------*/
-/* SLAVE/GADGET SIDE UTILITY ROUTINES */
+/* DEVICE/GADGET SIDE UTILITY ROUTINES */
/* called with spinlock held */
static void nuke(struct dummy *dum, struct dummy_ep *ep)
@@ -427,6 +427,7 @@
/* caller must hold lock */
static void set_link_state(struct dummy_hcd *dum_hcd)
+ __must_hold(&dum->lock)
{
struct dummy *dum = dum_hcd->dum;
unsigned int power_bit;
@@ -485,7 +486,7 @@
/*-------------------------------------------------------------------------*/
-/* SLAVE/GADGET SIDE DRIVER
+/* DEVICE/GADGET SIDE DRIVER
*
* This only tracks gadget state. All the work is done when the host
* side tries some (emulated) i/o operation. Real device controller
@@ -566,12 +567,12 @@
if (max <= 1024)
break;
/* save a return statement */
- /* fall through */
+ fallthrough;
case USB_SPEED_FULL:
if (max <= 64)
break;
/* save a return statement */
- /* fall through */
+ fallthrough;
default:
if (max <= 8)
break;
@@ -589,7 +590,7 @@
if (max <= 1024)
break;
/* save a return statement */
- /* fall through */
+ fallthrough;
case USB_SPEED_FULL:
if (max <= 1023)
break;
@@ -971,7 +972,7 @@
* hardware can be built with discrete components, so the gadget API doesn't
* require that assumption.
*
- * For this emulator, it might be convenient to create a usb slave device
+ * For this emulator, it might be convenient to create a usb device
* for each driver that registers: just add to a big root hub.
*/
@@ -995,7 +996,7 @@
}
/*
- * SLAVE side init ... the layer above hardware, which
+ * DEVICE side init ... the layer above hardware, which
* can't enumerate without help from the driver we're binding.
*/
@@ -1141,7 +1142,7 @@
.suspend = dummy_udc_suspend,
.resume = dummy_udc_resume,
.driver = {
- .name = (char *) gadget_name,
+ .name = gadget_name,
},
};
@@ -1157,7 +1158,7 @@
return index;
}
-/* MASTER/HOST SIDE DRIVER
+/* HOST SIDE DRIVER
*
* this uses the hcd framework to hook up to host side drivers.
* its root hub will only have one device, otherwise it acts like
@@ -1587,7 +1588,7 @@
/**
* handle_control_request() - handles all control transfers
- * @dum: pointer to dummy (the_controller)
+ * @dum_hcd: pointer to dummy (the_controller)
* @urb: the urb request to handle
* @setup: pointer to the setup data for a USB device control
* request
@@ -1949,7 +1950,7 @@
* this almost certainly polls too fast.
*/
limit = max(limit, periodic_bytes(dum, ep));
- /* FALLTHROUGH */
+ fallthrough;
default:
treat_control_like_bulk:
@@ -2120,9 +2121,21 @@
dum_hcd->port_status &= ~USB_PORT_STAT_POWER;
set_link_state(dum_hcd);
break;
- default:
+ case USB_PORT_FEAT_ENABLE:
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ /* Not allowed for USB-3 */
+ if (hcd->speed == HCD_USB3)
+ goto error;
+ fallthrough;
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_RESET:
dum_hcd->port_status &= ~(1 << wValue);
set_link_state(dum_hcd);
+ break;
+ default:
+ /* Disallow INDICATOR and C_OVER_CURRENT */
+ goto error;
}
break;
case GetHubDescriptor:
@@ -2258,19 +2271,22 @@
"supported for USB 2.0 roothub\n");
goto error;
}
- /* FALLS THROUGH */
+ fallthrough;
case USB_PORT_FEAT_RESET:
+ if (!(dum_hcd->port_status & USB_PORT_STAT_CONNECTION))
+ break;
/* if it's already enabled, disable */
if (hcd->speed == HCD_USB3) {
- dum_hcd->port_status = 0;
dum_hcd->port_status =
(USB_SS_PORT_STAT_POWER |
USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_RESET);
- } else
+ } else {
dum_hcd->port_status &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
+ dum_hcd->port_status |= USB_PORT_STAT_RESET;
+ }
/*
* We want to reset device status. All but the
* Self powered feature
@@ -2282,19 +2298,19 @@
* interval? Is it still 50msec as for HS?
*/
dum_hcd->re_timeout = jiffies + msecs_to_jiffies(50);
- /* FALLS THROUGH */
- default:
- if (hcd->speed == HCD_USB3) {
- if ((dum_hcd->port_status &
- USB_SS_PORT_STAT_POWER) != 0) {
- dum_hcd->port_status |= (1 << wValue);
- }
- } else
- if ((dum_hcd->port_status &
- USB_PORT_STAT_POWER) != 0) {
- dum_hcd->port_status |= (1 << wValue);
- }
set_link_state(dum_hcd);
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_RESET:
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ /* Not allowed for USB-3, and ignored for USB-2 */
+ if (hcd->speed == HCD_USB3)
+ goto error;
+ break;
+ default:
+ /* Disallow TEST, INDICATOR, and C_OVER_CURRENT */
+ goto error;
}
break;
case GetPortErrorCount:
@@ -2457,8 +2473,8 @@
struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd);
/*
- * MASTER side init ... we emulate a root hub that'll only ever
- * talk to one device (the slave side). Also appears in sysfs,
+ * HOST side init ... we emulate a root hub that'll only ever
+ * talk to one device (the gadget side). Also appears in sysfs,
* just like more familiar pci-based HCDs.
*/
if (!usb_hcd_is_primary_hcd(hcd))
@@ -2727,7 +2743,7 @@
.suspend = dummy_hcd_suspend,
.resume = dummy_hcd_resume,
.driver = {
- .name = (char *) driver_name,
+ .name = driver_name,
},
};
diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
index c313d07..75bf446 100644
--- a/drivers/usb/gadget/udc/fotg210-udc.c
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
@@ -1209,7 +1209,7 @@
static struct platform_driver fotg210_driver = {
.driver = {
- .name = (char *)udc_name,
+ .name = udc_name,
},
.probe = fotg210_udc_probe,
.remove = fotg210_udc_remove,
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index 2707be6..fa66449 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -923,9 +923,9 @@
return 0;
}
-static void ep_rx_tasklet(unsigned long data)
+static void ep_rx_tasklet(struct tasklet_struct *t)
{
- struct qe_udc *udc = (struct qe_udc *)data;
+ struct qe_udc *udc = from_tasklet(udc, t, rx_tasklet);
struct qe_ep *ep;
struct qe_frame *pframe;
struct qe_bd __iomem *bd;
@@ -2553,8 +2553,7 @@
DMA_TO_DEVICE);
}
- tasklet_init(&udc->rx_tasklet, ep_rx_tasklet,
- (unsigned long)udc);
+ tasklet_setup(&udc->rx_tasklet, ep_rx_tasklet);
/* request irq and disable DR */
udc->usb_irq = irq_of_parse_and_map(np, 0);
if (!udc->usb_irq) {
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h
index 2c537a9..53ca0ff 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.h
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.h
@@ -333,8 +333,8 @@
u32 resume_state; /* USB state to resume*/
u32 usb_state; /* USB current state */
u32 usb_next_state; /* USB next state */
- u32 ep0_state; /* Enpoint zero state */
- u32 ep0_dir; /* Enpoint zero direction: can be
+ u32 ep0_state; /* Endpoint zero state */
+ u32 ep0_dir; /* Endpoint zero direction: can be
USB_DIR_IN or USB_DIR_OUT*/
u32 usb_sof_count; /* SOF count */
u32 errors; /* USB ERRORs count */
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 9a05863..ad6ff9c 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -53,7 +53,6 @@
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
static const char driver_name[] = "fsl-usb2-udc";
-static const char driver_desc[] = DRIVER_DESC;
static struct usb_dr_device __iomem *dr_regs;
@@ -251,7 +250,7 @@
break;
case FSL_USB2_PHY_UTMI_WIDE:
portctrl |= PORTSCX_PTW_16BIT;
- /* fall through */
+ fallthrough;
case FSL_USB2_PHY_UTMI:
case FSL_USB2_PHY_UTMI_DUAL:
if (udc->pdata->have_sysif_regs) {
@@ -1052,10 +1051,11 @@
u32 bitmask;
struct ep_queue_head *qh;
- ep = container_of(_ep, struct fsl_ep, ep);
- if (!_ep || (!ep->ep.desc && ep_index(ep) != 0))
+ if (!_ep || !_ep->desc || !(_ep->desc->bEndpointAddress&0xF))
return -ENODEV;
+ ep = container_of(_ep, struct fsl_ep, ep);
+
udc = (struct fsl_udc *)ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
@@ -1208,7 +1208,7 @@
}
/* Change Data+ pullup status
- * this func is used by usb_gadget_connect/disconnet
+ * this func is used by usb_gadget_connect/disconnect
*/
static int fsl_pullup(struct usb_gadget *gadget, int is_on)
{
@@ -1595,14 +1595,13 @@
struct fsl_req *curr_req)
{
struct ep_td_struct *curr_td;
- int td_complete, actual, remaining_length, j, tmp;
+ int actual, remaining_length, j, tmp;
int status = 0;
int errors = 0;
struct ep_queue_head *curr_qh = &udc->ep_qh[pipe];
int direction = pipe % 2;
curr_td = curr_req->head;
- td_complete = 0;
actual = curr_req->req.length;
for (j = 0; j < curr_req->dtd_count; j++) {
@@ -1647,11 +1646,9 @@
status = -EPROTO;
break;
} else {
- td_complete++;
break;
}
} else {
- td_complete++;
VDBG("dTD transmitted successful");
}
@@ -2064,7 +2061,7 @@
"Sleep Enable: %d SOF Received Enable: %d "
"Reset Enable: %d\n"
"System Error Enable: %d "
- "Port Change Dectected Enable: %d\n"
+ "Port Change Detected Enable: %d\n"
"USB Error Intr Enable: %d USB Intr Enable: %d\n\n",
(tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
(tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,
@@ -2442,11 +2439,12 @@
/* DEN is bidirectional ep number, max_ep doubles the number */
udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
- udc_controller->irq = platform_get_irq(pdev, 0);
- if (!udc_controller->irq) {
- ret = -ENODEV;
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0) {
+ ret = ret ? : -ENODEV;
goto err_iounmap;
}
+ udc_controller->irq = ret;
ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
driver_name, udc_controller);
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index 00e3f66..9af8b41 100644
--- a/drivers/usb/gadget/udc/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -1507,7 +1507,7 @@
static struct platform_driver fusb300_driver = {
.remove = fusb300_remove,
.driver = {
- .name = (char *) udc_name,
+ .name = udc_name,
},
};
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index b706ad3..3e1267d 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -125,11 +125,14 @@
max = get_unaligned_le16(&desc->wMaxPacketSize);
switch (max) {
case 64:
- mode++; /* fall through */
+ mode++;
+ fallthrough;
case 32:
- mode++; /* fall through */
+ mode++;
+ fallthrough;
case 16:
- mode++; /* fall through */
+ mode++;
+ fallthrough;
case 8:
mode <<= 3;
break;
@@ -1783,7 +1786,7 @@
}
dev->got_region = 1;
- base = ioremap_nocache(resource, len);
+ base = ioremap(resource, len);
if (base == NULL) {
DBG(dev, "can't map memory\n");
retval = -EFAULT;
@@ -1844,7 +1847,7 @@
MODULE_DEVICE_TABLE (pci, pci_ids);
static struct pci_driver goku_pci_driver = {
- .name = (char *) driver_name,
+ .name = driver_name,
.id_table = pci_ids,
.probe = goku_probe,
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index da73a06..f8f3aa5 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -8,7 +8,7 @@
* GRLIB VHDL IP core library.
*
* Full documentation of the GRUSBDC core can be found here:
- * http://www.gaisler.com/products/grlib/grip.pdf
+ * https://www.gaisler.com/products/grlib/grip.pdf
*
* Contributors:
* - Andreas Larsson <andreas@gaisler.com>
@@ -29,6 +29,7 @@
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/device.h>
+#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/dma-mapping.h>
@@ -47,7 +48,6 @@
#define DRIVER_DESC "Aeroflex Gaisler GRUSBDC USB Peripheral Controller"
static const char driver_name[] = DRIVER_NAME;
-static const char driver_desc[] = DRIVER_DESC;
#define gr_read32(x) (ioread32be((x)))
#define gr_write32(x, v) (iowrite32be((v), (x)))
@@ -208,7 +208,7 @@
{
const char *name = "gr_udc_state";
- dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL);
+ dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), usb_debug_root);
debugfs_create_file(name, 0444, dev->dfs_root, dev, &gr_dfs_fops);
}
@@ -912,9 +912,9 @@
return gr_ep0_respond_empty(dev);
case USB_DEVICE_TEST_MODE:
- /* The hardware does not support TEST_FORCE_EN */
+ /* The hardware does not support USB_TEST_FORCE_ENABLE */
test = index >> 8;
- if (test >= TEST_J && test <= TEST_PACKET) {
+ if (test >= USB_TEST_J && test <= USB_TEST_PACKET) {
dev->test_mode = test;
return gr_ep0_respond(dev, NULL, 0,
gr_ep0_testmode_complete);
@@ -2121,7 +2121,6 @@
static int gr_probe(struct platform_device *pdev)
{
struct gr_udc *dev;
- struct resource *res;
struct gr_regs __iomem *regs;
int retval;
u32 status;
@@ -2131,8 +2130,7 @@
return -ENOMEM;
dev->dev = &pdev->dev;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- regs = devm_ioremap_resource(dev->dev, res);
+ regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
diff --git a/drivers/usb/gadget/udc/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h
index 417ad2a..ac5b3f6 100644
--- a/drivers/usb/gadget/udc/gr_udc.h
+++ b/drivers/usb/gadget/udc/gr_udc.h
@@ -8,7 +8,7 @@
* GRLIB VHDL IP core library.
*
* Full documentation of the GRUSBDC core can be found here:
- * http://www.gaisler.com/products/grlib/grip.pdf
+ * https://www.gaisler.com/products/grlib/grip.pdf
*
* Contributors:
* - Andreas Larsson <andreas@gaisler.com>
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index 6d2f1f9..3f1c62a 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -495,7 +495,7 @@
}
}
-static int proc_udc_show(struct seq_file *s, void *unused)
+static int udc_show(struct seq_file *s, void *unused)
{
struct lpc32xx_udc *udc = s->private;
struct lpc32xx_ep *ep;
@@ -524,22 +524,11 @@
return 0;
}
-static int proc_udc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, proc_udc_show, PDE_DATA(inode));
-}
-
-static const struct file_operations proc_ops = {
- .owner = THIS_MODULE,
- .open = proc_udc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(udc);
static void create_debug_file(struct lpc32xx_udc *udc)
{
- udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &proc_ops);
+ udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &udc_fops);
}
static void remove_debug_file(struct lpc32xx_udc *udc)
@@ -1926,7 +1915,7 @@
};
/* Send a ZLP on a non-0 IN EP */
-void udc_send_in_zlp(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
+static void udc_send_in_zlp(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
{
/* Clear EP status */
udc_clearep_getsts(udc, ep->hwep_num);
@@ -1940,7 +1929,7 @@
* This function will only be called when a delayed ZLP needs to be sent out
* after a DMA transfer has filled both buffers.
*/
-void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
+static void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep)
{
u32 epstatus;
struct lpc32xx_request *req;
@@ -2986,7 +2975,7 @@
/* Enable or disable USB remote wakeup */
}
-struct lpc32xx_usbd_cfg lpc32xx_usbddata = {
+static struct lpc32xx_usbd_cfg lpc32xx_usbddata = {
.vbus_drv_pol = 0,
.conn_chgb = &lpc32xx_usbd_conn_chg,
.susp_chgb = &lpc32xx_usbd_susp_chg,
@@ -3001,7 +2990,6 @@
struct device *dev = &pdev->dev;
struct lpc32xx_udc *udc;
int retval, i;
- struct resource *res;
dma_addr_t dma_handle;
struct device_node *isp1301_node;
@@ -3049,9 +3037,6 @@
* IORESOURCE_IRQ, USB device interrupt number
* IORESOURCE_IRQ, USB transceiver interrupt number
*/
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENXIO;
spin_lock_init(&udc->lock);
@@ -3062,7 +3047,7 @@
return udc->udp_irq[i];
}
- udc->udp_baseaddr = devm_ioremap_resource(dev, res);
+ udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(udc->udp_baseaddr)) {
dev_err(udc->dev, "IO map failure\n");
return PTR_ERR(udc->udp_baseaddr);
@@ -3272,7 +3257,7 @@
.suspend = lpc32xx_udc_suspend,
.resume = lpc32xx_udc_resume,
.driver = {
- .name = (char *) driver_name,
+ .name = driver_name,
.of_match_table = of_match_ptr(lpc32xx_udc_of_match),
},
};
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index ea59b56..931e636 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1691,7 +1691,7 @@
static struct platform_driver m66592_driver = {
.remove = m66592_remove,
.driver = {
- .name = (char *) udc_name,
+ .name = udc_name,
},
};
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
new file mode 100644
index 0000000..91c9e90
--- /dev/null
+++ b/drivers/usb/gadget/udc/max3420_udc.c
@@ -0,0 +1,1336 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MAX3420 Device Controller driver for USB.
+ *
+ * Author: Jaswinder Singh Brar <jaswinder.singh@linaro.org>
+ * (C) Copyright 2019-2020 Linaro Ltd
+ *
+ * Based on:
+ * o MAX3420E datasheet
+ * https://datasheets.maximintegrated.com/en/ds/MAX3420E.pdf
+ * o MAX342{0,1}E Programming Guides
+ * https://pdfserv.maximintegrated.com/en/an/AN3598.pdf
+ * https://pdfserv.maximintegrated.com/en/an/AN3785.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/prefetch.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+
+#define MAX3420_MAX_EPS 4
+#define MAX3420_EP_MAX_PACKET 64 /* Same for all Endpoints */
+#define MAX3420_EPNAME_SIZE 16 /* Buffer size for endpoint name */
+
+#define MAX3420_ACKSTAT BIT(0)
+
+#define MAX3420_SPI_DIR_RD 0 /* read register from MAX3420 */
+#define MAX3420_SPI_DIR_WR 1 /* write register to MAX3420 */
+
+/* SPI commands: */
+#define MAX3420_SPI_DIR_SHIFT 1
+#define MAX3420_SPI_REG_SHIFT 3
+
+#define MAX3420_REG_EP0FIFO 0
+#define MAX3420_REG_EP1FIFO 1
+#define MAX3420_REG_EP2FIFO 2
+#define MAX3420_REG_EP3FIFO 3
+#define MAX3420_REG_SUDFIFO 4
+#define MAX3420_REG_EP0BC 5
+#define MAX3420_REG_EP1BC 6
+#define MAX3420_REG_EP2BC 7
+#define MAX3420_REG_EP3BC 8
+
+#define MAX3420_REG_EPSTALLS 9
+ #define ACKSTAT BIT(6)
+ #define STLSTAT BIT(5)
+ #define STLEP3IN BIT(4)
+ #define STLEP2IN BIT(3)
+ #define STLEP1OUT BIT(2)
+ #define STLEP0OUT BIT(1)
+ #define STLEP0IN BIT(0)
+
+#define MAX3420_REG_CLRTOGS 10
+ #define EP3DISAB BIT(7)
+ #define EP2DISAB BIT(6)
+ #define EP1DISAB BIT(5)
+ #define CTGEP3IN BIT(4)
+ #define CTGEP2IN BIT(3)
+ #define CTGEP1OUT BIT(2)
+
+#define MAX3420_REG_EPIRQ 11
+#define MAX3420_REG_EPIEN 12
+ #define SUDAVIRQ BIT(5)
+ #define IN3BAVIRQ BIT(4)
+ #define IN2BAVIRQ BIT(3)
+ #define OUT1DAVIRQ BIT(2)
+ #define OUT0DAVIRQ BIT(1)
+ #define IN0BAVIRQ BIT(0)
+
+#define MAX3420_REG_USBIRQ 13
+#define MAX3420_REG_USBIEN 14
+ #define OSCOKIRQ BIT(0)
+ #define RWUDNIRQ BIT(1)
+ #define BUSACTIRQ BIT(2)
+ #define URESIRQ BIT(3)
+ #define SUSPIRQ BIT(4)
+ #define NOVBUSIRQ BIT(5)
+ #define VBUSIRQ BIT(6)
+ #define URESDNIRQ BIT(7)
+
+#define MAX3420_REG_USBCTL 15
+ #define HOSCSTEN BIT(7)
+ #define VBGATE BIT(6)
+ #define CHIPRES BIT(5)
+ #define PWRDOWN BIT(4)
+ #define CONNECT BIT(3)
+ #define SIGRWU BIT(2)
+
+#define MAX3420_REG_CPUCTL 16
+ #define IE BIT(0)
+
+#define MAX3420_REG_PINCTL 17
+ #define EP3INAK BIT(7)
+ #define EP2INAK BIT(6)
+ #define EP0INAK BIT(5)
+ #define FDUPSPI BIT(4)
+ #define INTLEVEL BIT(3)
+ #define POSINT BIT(2)
+ #define GPXB BIT(1)
+ #define GPXA BIT(0)
+
+#define MAX3420_REG_REVISION 18
+
+#define MAX3420_REG_FNADDR 19
+ #define FNADDR_MASK 0x7f
+
+#define MAX3420_REG_IOPINS 20
+#define MAX3420_REG_IOPINS2 21
+#define MAX3420_REG_GPINIRQ 22
+#define MAX3420_REG_GPINIEN 23
+#define MAX3420_REG_GPINPOL 24
+#define MAX3420_REG_HIRQ 25
+#define MAX3420_REG_HIEN 26
+#define MAX3420_REG_MODE 27
+#define MAX3420_REG_PERADDR 28
+#define MAX3420_REG_HCTL 29
+#define MAX3420_REG_HXFR 30
+#define MAX3420_REG_HRSL 31
+
+#define ENABLE_IRQ BIT(0)
+#define IOPIN_UPDATE BIT(1)
+#define REMOTE_WAKEUP BIT(2)
+#define CONNECT_HOST GENMASK(4, 3)
+#define HCONNECT (1 << 3)
+#define HDISCONNECT (3 << 3)
+#define UDC_START GENMASK(6, 5)
+#define START (1 << 5)
+#define STOP (3 << 5)
+#define ENABLE_EP GENMASK(8, 7)
+#define ENABLE (1 << 7)
+#define DISABLE (3 << 7)
+#define STALL_EP GENMASK(10, 9)
+#define STALL (1 << 9)
+#define UNSTALL (3 << 9)
+
+#define MAX3420_CMD(c) FIELD_PREP(GENMASK(7, 3), c)
+#define MAX3420_SPI_CMD_RD(c) (MAX3420_CMD(c) | (0 << 1))
+#define MAX3420_SPI_CMD_WR(c) (MAX3420_CMD(c) | (1 << 1))
+
+struct max3420_req {
+ struct usb_request usb_req;
+ struct list_head queue;
+ struct max3420_ep *ep;
+};
+
+struct max3420_ep {
+ struct usb_ep ep_usb;
+ struct max3420_udc *udc;
+ struct list_head queue;
+ char name[MAX3420_EPNAME_SIZE];
+ unsigned int maxpacket;
+ spinlock_t lock;
+ int halted;
+ u32 todo;
+ int id;
+};
+
+struct max3420_udc {
+ struct usb_gadget gadget;
+ struct max3420_ep ep[MAX3420_MAX_EPS];
+ struct usb_gadget_driver *driver;
+ struct task_struct *thread_task;
+ int remote_wkp, is_selfpowered;
+ bool vbus_active, softconnect;
+ struct usb_ctrlrequest setup;
+ struct mutex spi_bus_mutex;
+ struct max3420_req ep0req;
+ struct spi_device *spi;
+ struct device *dev;
+ spinlock_t lock;
+ bool suspended;
+ u8 ep0buf[64];
+ u32 todo;
+};
+
+#define to_max3420_req(r) container_of((r), struct max3420_req, usb_req)
+#define to_max3420_ep(e) container_of((e), struct max3420_ep, ep_usb)
+#define to_udc(g) container_of((g), struct max3420_udc, gadget)
+
+#define DRIVER_DESC "MAX3420 USB Device-Mode Driver"
+static const char driver_name[] = "max3420-udc";
+
+/* Control endpoint configuration.*/
+static const struct usb_endpoint_descriptor ep0_desc = {
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = cpu_to_le16(MAX3420_EP_MAX_PACKET),
+};
+
+static void spi_ack_ctrl(struct max3420_udc *udc)
+{
+ struct spi_device *spi = udc->spi;
+ struct spi_transfer transfer;
+ struct spi_message msg;
+ u8 txdata[1];
+
+ memset(&transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ txdata[0] = MAX3420_ACKSTAT;
+ transfer.tx_buf = txdata;
+ transfer.len = 1;
+
+ spi_message_add_tail(&transfer, &msg);
+ spi_sync(spi, &msg);
+}
+
+static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int actstat)
+{
+ struct spi_device *spi = udc->spi;
+ struct spi_transfer transfer;
+ struct spi_message msg;
+ u8 txdata[2], rxdata[2];
+
+ memset(&transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ txdata[0] = MAX3420_SPI_CMD_RD(reg) | (actstat ? MAX3420_ACKSTAT : 0);
+ transfer.tx_buf = txdata;
+ transfer.rx_buf = rxdata;
+ transfer.len = 2;
+
+ spi_message_add_tail(&transfer, &msg);
+ spi_sync(spi, &msg);
+
+ return rxdata[1];
+}
+
+static u8 spi_rd8(struct max3420_udc *udc, u8 reg)
+{
+ return spi_rd8_ack(udc, reg, 0);
+}
+
+static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int actstat)
+{
+ struct spi_device *spi = udc->spi;
+ struct spi_transfer transfer;
+ struct spi_message msg;
+ u8 txdata[2];
+
+ memset(&transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ txdata[0] = MAX3420_SPI_CMD_WR(reg) | (actstat ? MAX3420_ACKSTAT : 0);
+ txdata[1] = val;
+
+ transfer.tx_buf = txdata;
+ transfer.len = 2;
+
+ spi_message_add_tail(&transfer, &msg);
+ spi_sync(spi, &msg);
+}
+
+static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val)
+{
+ spi_wr8_ack(udc, reg, val, 0);
+}
+
+static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len)
+{
+ struct spi_device *spi = udc->spi;
+ struct spi_transfer transfer;
+ struct spi_message msg;
+ u8 local_buf[MAX3420_EP_MAX_PACKET + 1] = {};
+
+ memset(&transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ local_buf[0] = MAX3420_SPI_CMD_RD(reg);
+ transfer.tx_buf = &local_buf[0];
+ transfer.rx_buf = &local_buf[0];
+ transfer.len = len + 1;
+
+ spi_message_add_tail(&transfer, &msg);
+ spi_sync(spi, &msg);
+
+ memcpy(buf, &local_buf[1], len);
+}
+
+static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len)
+{
+ struct spi_device *spi = udc->spi;
+ struct spi_transfer transfer;
+ struct spi_message msg;
+ u8 local_buf[MAX3420_EP_MAX_PACKET + 1] = {};
+
+ memset(&transfer, 0, sizeof(transfer));
+
+ spi_message_init(&msg);
+
+ local_buf[0] = MAX3420_SPI_CMD_WR(reg);
+ memcpy(&local_buf[1], buf, len);
+
+ transfer.tx_buf = local_buf;
+ transfer.len = len + 1;
+
+ spi_message_add_tail(&transfer, &msg);
+ spi_sync(spi, &msg);
+}
+
+static int spi_max3420_enable(struct max3420_ep *ep)
+{
+ struct max3420_udc *udc = ep->udc;
+ unsigned long flags;
+ u8 epdis, epien;
+ int todo;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ todo = ep->todo & ENABLE_EP;
+ ep->todo &= ~ENABLE_EP;
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ if (!todo || ep->id == 0)
+ return false;
+
+ epien = spi_rd8(udc, MAX3420_REG_EPIEN);
+ epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS);
+
+ if (todo == ENABLE) {
+ epdis &= ~BIT(ep->id + 4);
+ epien |= BIT(ep->id + 1);
+ } else {
+ epdis |= BIT(ep->id + 4);
+ epien &= ~BIT(ep->id + 1);
+ }
+
+ spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis);
+ spi_wr8(udc, MAX3420_REG_EPIEN, epien);
+
+ return true;
+}
+
+static int spi_max3420_stall(struct max3420_ep *ep)
+{
+ struct max3420_udc *udc = ep->udc;
+ unsigned long flags;
+ u8 epstalls;
+ int todo;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ todo = ep->todo & STALL_EP;
+ ep->todo &= ~STALL_EP;
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ if (!todo || ep->id == 0)
+ return false;
+
+ epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS);
+ if (todo == STALL) {
+ ep->halted = 1;
+ epstalls |= BIT(ep->id + 1);
+ } else {
+ u8 clrtogs;
+
+ ep->halted = 0;
+ epstalls &= ~BIT(ep->id + 1);
+ clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS);
+ clrtogs |= BIT(ep->id + 1);
+ spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs);
+ }
+ spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | ACKSTAT);
+
+ return true;
+}
+
+static int spi_max3420_rwkup(struct max3420_udc *udc)
+{
+ unsigned long flags;
+ int wake_remote;
+ u8 usbctl;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ wake_remote = udc->todo & REMOTE_WAKEUP;
+ udc->todo &= ~REMOTE_WAKEUP;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (!wake_remote || !udc->suspended)
+ return false;
+
+ /* Set Remote-WkUp Signal*/
+ usbctl = spi_rd8(udc, MAX3420_REG_USBCTL);
+ usbctl |= SIGRWU;
+ spi_wr8(udc, MAX3420_REG_USBCTL, usbctl);
+
+ msleep_interruptible(5);
+
+ /* Clear Remote-WkUp Signal*/
+ usbctl = spi_rd8(udc, MAX3420_REG_USBCTL);
+ usbctl &= ~SIGRWU;
+ spi_wr8(udc, MAX3420_REG_USBCTL, usbctl);
+
+ udc->suspended = false;
+
+ return true;
+}
+
+static void max3420_nuke(struct max3420_ep *ep, int status);
+static void __max3420_stop(struct max3420_udc *udc)
+{
+ u8 val;
+ int i;
+
+ /* clear all pending requests */
+ for (i = 1; i < MAX3420_MAX_EPS; i++)
+ max3420_nuke(&udc->ep[i], -ECONNRESET);
+
+ /* Disable IRQ to CPU */
+ spi_wr8(udc, MAX3420_REG_CPUCTL, 0);
+
+ val = spi_rd8(udc, MAX3420_REG_USBCTL);
+ val |= PWRDOWN;
+ if (udc->is_selfpowered)
+ val &= ~HOSCSTEN;
+ else
+ val |= HOSCSTEN;
+ spi_wr8(udc, MAX3420_REG_USBCTL, val);
+}
+
+static void __max3420_start(struct max3420_udc *udc)
+{
+ u8 val;
+
+ /* Need this delay if bus-powered,
+ * but even for self-powered it helps stability
+ */
+ msleep_interruptible(250);
+
+ /* configure SPI */
+ spi_wr8(udc, MAX3420_REG_PINCTL, FDUPSPI);
+
+ /* Chip Reset */
+ spi_wr8(udc, MAX3420_REG_USBCTL, CHIPRES);
+ msleep_interruptible(5);
+ spi_wr8(udc, MAX3420_REG_USBCTL, 0);
+
+ /* Poll for OSC to stabilize */
+ while (1) {
+ val = spi_rd8(udc, MAX3420_REG_USBIRQ);
+ if (val & OSCOKIRQ)
+ break;
+ cond_resched();
+ }
+
+ /* Enable PULL-UP only when Vbus detected */
+ val = spi_rd8(udc, MAX3420_REG_USBCTL);
+ val |= VBGATE | CONNECT;
+ spi_wr8(udc, MAX3420_REG_USBCTL, val);
+
+ val = URESDNIRQ | URESIRQ;
+ if (udc->is_selfpowered)
+ val |= NOVBUSIRQ;
+ spi_wr8(udc, MAX3420_REG_USBIEN, val);
+
+ /* Enable only EP0 interrupts */
+ val = IN0BAVIRQ | OUT0DAVIRQ | SUDAVIRQ;
+ spi_wr8(udc, MAX3420_REG_EPIEN, val);
+
+ /* Enable IRQ to CPU */
+ spi_wr8(udc, MAX3420_REG_CPUCTL, IE);
+}
+
+static int max3420_start(struct max3420_udc *udc)
+{
+ unsigned long flags;
+ int todo;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ todo = udc->todo & UDC_START;
+ udc->todo &= ~UDC_START;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (!todo)
+ return false;
+
+ if (udc->vbus_active && udc->softconnect)
+ __max3420_start(udc);
+ else
+ __max3420_stop(udc);
+
+ return true;
+}
+
+static irqreturn_t max3420_vbus_handler(int irq, void *dev_id)
+{
+ struct max3420_udc *udc = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ /* its a vbus change interrupt */
+ udc->vbus_active = !udc->vbus_active;
+ udc->todo |= UDC_START;
+ usb_udc_vbus_handler(&udc->gadget, udc->vbus_active);
+ usb_gadget_set_state(&udc->gadget, udc->vbus_active
+ ? USB_STATE_POWERED : USB_STATE_NOTATTACHED);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (udc->thread_task &&
+ udc->thread_task->state != TASK_RUNNING)
+ wake_up_process(udc->thread_task);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max3420_irq_handler(int irq, void *dev_id)
+{
+ struct max3420_udc *udc = dev_id;
+ struct spi_device *spi = udc->spi;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if ((udc->todo & ENABLE_IRQ) == 0) {
+ disable_irq_nosync(spi->irq);
+ udc->todo |= ENABLE_IRQ;
+ }
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (udc->thread_task &&
+ udc->thread_task->state != TASK_RUNNING)
+ wake_up_process(udc->thread_task);
+
+ return IRQ_HANDLED;
+}
+
+static void max3420_getstatus(struct max3420_udc *udc)
+{
+ struct max3420_ep *ep;
+ u16 status = 0;
+
+ switch (udc->setup.bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ /* Get device status */
+ status = udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED;
+ status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP);
+ break;
+ case USB_RECIP_INTERFACE:
+ if (udc->driver->setup(&udc->gadget, &udc->setup) < 0)
+ goto stall;
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK];
+ if (udc->setup.wIndex & USB_DIR_IN) {
+ if (!ep->ep_usb.caps.dir_in)
+ goto stall;
+ } else {
+ if (!ep->ep_usb.caps.dir_out)
+ goto stall;
+ }
+ if (ep->halted)
+ status = 1 << USB_ENDPOINT_HALT;
+ break;
+ default:
+ goto stall;
+ }
+
+ status = cpu_to_le16(status);
+ spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2);
+ spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1);
+ return;
+stall:
+ dev_err(udc->dev, "Can't respond to getstatus request\n");
+ spi_wr8(udc, MAX3420_REG_EPSTALLS, STLEP0IN | STLEP0OUT | STLSTAT);
+}
+
+static void max3420_set_clear_feature(struct max3420_udc *udc)
+{
+ struct max3420_ep *ep;
+ int set = udc->setup.bRequest == USB_REQ_SET_FEATURE;
+ unsigned long flags;
+ int id;
+
+ switch (udc->setup.bRequestType) {
+ case USB_RECIP_DEVICE:
+ if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP)
+ break;
+
+ if (udc->setup.bRequest == USB_REQ_SET_FEATURE)
+ udc->remote_wkp = 1;
+ else
+ udc->remote_wkp = 0;
+
+ return spi_ack_ctrl(udc);
+
+ case USB_RECIP_ENDPOINT:
+ if (udc->setup.wValue != USB_ENDPOINT_HALT)
+ break;
+
+ id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ ep = &udc->ep[id];
+
+ spin_lock_irqsave(&ep->lock, flags);
+ ep->todo &= ~STALL_EP;
+ if (set)
+ ep->todo |= STALL;
+ else
+ ep->todo |= UNSTALL;
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ spi_max3420_stall(ep);
+ return;
+ default:
+ break;
+ }
+
+ dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n");
+ spi_wr8(udc, MAX3420_REG_EPSTALLS, STLEP0IN | STLEP0OUT | STLSTAT);
+}
+
+static void max3420_handle_setup(struct max3420_udc *udc)
+{
+ struct usb_ctrlrequest setup;
+
+ spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8);
+
+ udc->setup = setup;
+ udc->setup.wValue = cpu_to_le16(setup.wValue);
+ udc->setup.wIndex = cpu_to_le16(setup.wIndex);
+ udc->setup.wLength = cpu_to_le16(setup.wLength);
+
+ switch (udc->setup.bRequest) {
+ case USB_REQ_GET_STATUS:
+ /* Data+Status phase form udc */
+ if ((udc->setup.bRequestType &
+ (USB_DIR_IN | USB_TYPE_MASK)) !=
+ (USB_DIR_IN | USB_TYPE_STANDARD)) {
+ break;
+ }
+ return max3420_getstatus(udc);
+ case USB_REQ_SET_ADDRESS:
+ /* Status phase from udc */
+ if (udc->setup.bRequestType != (USB_DIR_OUT |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE)) {
+ break;
+ }
+ spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1);
+ dev_dbg(udc->dev, "Assigned Address=%d\n", udc->setup.wValue);
+ return;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* Requests with no data phase, status phase from udc */
+ if ((udc->setup.bRequestType & USB_TYPE_MASK)
+ != USB_TYPE_STANDARD)
+ break;
+ return max3420_set_clear_feature(udc);
+ default:
+ break;
+ }
+
+ if (udc->driver->setup(&udc->gadget, &setup) < 0) {
+ /* Stall EP0 */
+ spi_wr8(udc, MAX3420_REG_EPSTALLS,
+ STLEP0IN | STLEP0OUT | STLSTAT);
+ }
+}
+
+static void max3420_req_done(struct max3420_req *req, int status)
+{
+ struct max3420_ep *ep = req->ep;
+ struct max3420_udc *udc = ep->udc;
+
+ if (req->usb_req.status == -EINPROGRESS)
+ req->usb_req.status = status;
+ else
+ status = req->usb_req.status;
+
+ if (status && status != -ESHUTDOWN)
+ dev_err(udc->dev, "%s done %p, status %d\n",
+ ep->ep_usb.name, req, status);
+
+ if (req->usb_req.complete)
+ req->usb_req.complete(&ep->ep_usb, &req->usb_req);
+}
+
+static int max3420_do_data(struct max3420_udc *udc, int ep_id, int in)
+{
+ struct max3420_ep *ep = &udc->ep[ep_id];
+ struct max3420_req *req;
+ int done, length, psz;
+ void *buf;
+
+ if (list_empty(&ep->queue))
+ return false;
+
+ req = list_first_entry(&ep->queue, struct max3420_req, queue);
+ buf = req->usb_req.buf + req->usb_req.actual;
+
+ psz = ep->ep_usb.maxpacket;
+ length = req->usb_req.length - req->usb_req.actual;
+ length = min(length, psz);
+
+ if (length == 0) {
+ done = 1;
+ goto xfer_done;
+ }
+
+ done = 0;
+ if (in) {
+ prefetch(buf);
+ spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length);
+ spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length);
+ if (length < psz)
+ done = 1;
+ } else {
+ psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id);
+ length = min(length, psz);
+ prefetchw(buf);
+ spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length);
+ if (length < ep->ep_usb.maxpacket)
+ done = 1;
+ }
+
+ req->usb_req.actual += length;
+
+ if (req->usb_req.actual == req->usb_req.length)
+ done = 1;
+
+xfer_done:
+ if (done) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ list_del_init(&req->queue);
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ if (ep_id == 0)
+ spi_ack_ctrl(udc);
+
+ max3420_req_done(req, 0);
+ }
+
+ return true;
+}
+
+static int max3420_handle_irqs(struct max3420_udc *udc)
+{
+ u8 epien, epirq, usbirq, usbien, reg[4];
+ bool ret = false;
+
+ spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4);
+ epirq = reg[0];
+ epien = reg[1];
+ usbirq = reg[2];
+ usbien = reg[3];
+
+ usbirq &= usbien;
+ epirq &= epien;
+
+ if (epirq & SUDAVIRQ) {
+ spi_wr8(udc, MAX3420_REG_EPIRQ, SUDAVIRQ);
+ max3420_handle_setup(udc);
+ return true;
+ }
+
+ if (usbirq & VBUSIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, VBUSIRQ);
+ dev_dbg(udc->dev, "Cable plugged in\n");
+ return true;
+ }
+
+ if (usbirq & NOVBUSIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, NOVBUSIRQ);
+ dev_dbg(udc->dev, "Cable pulled out\n");
+ return true;
+ }
+
+ if (usbirq & URESIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, URESIRQ);
+ dev_dbg(udc->dev, "USB Reset - Start\n");
+ return true;
+ }
+
+ if (usbirq & URESDNIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, URESDNIRQ);
+ dev_dbg(udc->dev, "USB Reset - END\n");
+ spi_wr8(udc, MAX3420_REG_USBIEN, URESDNIRQ | URESIRQ);
+ spi_wr8(udc, MAX3420_REG_EPIEN, SUDAVIRQ | IN0BAVIRQ
+ | OUT0DAVIRQ);
+ return true;
+ }
+
+ if (usbirq & SUSPIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, SUSPIRQ);
+ dev_dbg(udc->dev, "USB Suspend - Enter\n");
+ udc->suspended = true;
+ return true;
+ }
+
+ if (usbirq & BUSACTIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, BUSACTIRQ);
+ dev_dbg(udc->dev, "USB Suspend - Exit\n");
+ udc->suspended = false;
+ return true;
+ }
+
+ if (usbirq & RWUDNIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, RWUDNIRQ);
+ dev_dbg(udc->dev, "Asked Host to wakeup\n");
+ return true;
+ }
+
+ if (usbirq & OSCOKIRQ) {
+ spi_wr8(udc, MAX3420_REG_USBIRQ, OSCOKIRQ);
+ dev_dbg(udc->dev, "Osc stabilized, start work\n");
+ return true;
+ }
+
+ if (epirq & OUT0DAVIRQ && max3420_do_data(udc, 0, 0)) {
+ spi_wr8_ack(udc, MAX3420_REG_EPIRQ, OUT0DAVIRQ, 1);
+ ret = true;
+ }
+
+ if (epirq & IN0BAVIRQ && max3420_do_data(udc, 0, 1))
+ ret = true;
+
+ if (epirq & OUT1DAVIRQ && max3420_do_data(udc, 1, 0)) {
+ spi_wr8_ack(udc, MAX3420_REG_EPIRQ, OUT1DAVIRQ, 1);
+ ret = true;
+ }
+
+ if (epirq & IN2BAVIRQ && max3420_do_data(udc, 2, 1))
+ ret = true;
+
+ if (epirq & IN3BAVIRQ && max3420_do_data(udc, 3, 1))
+ ret = true;
+
+ return ret;
+}
+
+static int max3420_thread(void *dev_id)
+{
+ struct max3420_udc *udc = dev_id;
+ struct spi_device *spi = udc->spi;
+ int i, loop_again = 1;
+ unsigned long flags;
+
+ while (!kthread_should_stop()) {
+ if (!loop_again) {
+ ktime_t kt = ns_to_ktime(1000 * 1000 * 250); /* 250ms */
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (udc->todo & ENABLE_IRQ) {
+ enable_irq(spi->irq);
+ udc->todo &= ~ENABLE_IRQ;
+ }
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ schedule_hrtimeout(&kt, HRTIMER_MODE_REL);
+ }
+ loop_again = 0;
+
+ mutex_lock(&udc->spi_bus_mutex);
+
+ /* If bus-vbus_active and disconnected */
+ if (!udc->vbus_active || !udc->softconnect)
+ goto loop;
+
+ if (max3420_start(udc)) {
+ loop_again = 1;
+ goto loop;
+ }
+
+ if (max3420_handle_irqs(udc)) {
+ loop_again = 1;
+ goto loop;
+ }
+
+ if (spi_max3420_rwkup(udc)) {
+ loop_again = 1;
+ goto loop;
+ }
+
+ max3420_do_data(udc, 0, 1); /* get done with the EP0 ZLP */
+
+ for (i = 1; i < MAX3420_MAX_EPS; i++) {
+ struct max3420_ep *ep = &udc->ep[i];
+
+ if (spi_max3420_enable(ep))
+ loop_again = 1;
+ if (spi_max3420_stall(ep))
+ loop_again = 1;
+ }
+loop:
+ mutex_unlock(&udc->spi_bus_mutex);
+ }
+
+ set_current_state(TASK_RUNNING);
+ dev_info(udc->dev, "SPI thread exiting\n");
+ return 0;
+}
+
+static int max3420_ep_set_halt(struct usb_ep *_ep, int stall)
+{
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+ struct max3420_udc *udc = ep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+
+ ep->todo &= ~STALL_EP;
+ if (stall)
+ ep->todo |= STALL;
+ else
+ ep->todo |= UNSTALL;
+
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ wake_up_process(udc->thread_task);
+
+ dev_dbg(udc->dev, "%sStall %s\n", stall ? "" : "Un", ep->name);
+ return 0;
+}
+
+static int __max3420_ep_enable(struct max3420_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ unsigned int maxp = usb_endpoint_maxp(desc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ ep->ep_usb.desc = desc;
+ ep->ep_usb.maxpacket = maxp;
+
+ ep->todo &= ~ENABLE_EP;
+ ep->todo |= ENABLE;
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ return 0;
+}
+
+static int max3420_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+ struct max3420_udc *udc = ep->udc;
+
+ __max3420_ep_enable(ep, desc);
+
+ wake_up_process(udc->thread_task);
+
+ return 0;
+}
+
+static void max3420_nuke(struct max3420_ep *ep, int status)
+{
+ struct max3420_req *req, *r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+
+ list_for_each_entry_safe(req, r, &ep->queue, queue) {
+ list_del_init(&req->queue);
+
+ spin_unlock_irqrestore(&ep->lock, flags);
+ max3420_req_done(req, status);
+ spin_lock_irqsave(&ep->lock, flags);
+ }
+
+ spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+static void __max3420_ep_disable(struct max3420_ep *ep)
+{
+ struct max3420_udc *udc = ep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+
+ ep->ep_usb.desc = NULL;
+
+ ep->todo &= ~ENABLE_EP;
+ ep->todo |= DISABLE;
+
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ dev_dbg(udc->dev, "Disabled %s\n", ep->name);
+}
+
+static int max3420_ep_disable(struct usb_ep *_ep)
+{
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+ struct max3420_udc *udc = ep->udc;
+
+ max3420_nuke(ep, -ESHUTDOWN);
+
+ __max3420_ep_disable(ep);
+
+ wake_up_process(udc->thread_task);
+
+ return 0;
+}
+
+static struct usb_request *max3420_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+ struct max3420_req *req;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return NULL;
+
+ req->ep = ep;
+
+ return &req->usb_req;
+}
+
+static void max3420_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ kfree(to_max3420_req(_req));
+}
+
+static int max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t ignored)
+{
+ struct max3420_req *req = to_max3420_req(_req);
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+ struct max3420_udc *udc = ep->udc;
+ unsigned long flags;
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ spin_lock_irqsave(&ep->lock, flags);
+ list_add_tail(&req->queue, &ep->queue);
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ wake_up_process(udc->thread_task);
+ return 0;
+}
+
+static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct max3420_req *t, *req = to_max3420_req(_req);
+ struct max3420_ep *ep = to_max3420_ep(_ep);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ep->lock, flags);
+
+ /* Pluck the descriptor from queue */
+ list_for_each_entry(t, &ep->queue, queue)
+ if (t == req) {
+ list_del_init(&req->queue);
+ break;
+ }
+
+ spin_unlock_irqrestore(&ep->lock, flags);
+
+ if (t == req)
+ max3420_req_done(req, -ECONNRESET);
+
+ return 0;
+}
+
+static const struct usb_ep_ops max3420_ep_ops = {
+ .enable = max3420_ep_enable,
+ .disable = max3420_ep_disable,
+ .alloc_request = max3420_alloc_request,
+ .free_request = max3420_free_request,
+ .queue = max3420_ep_queue,
+ .dequeue = max3420_ep_dequeue,
+ .set_halt = max3420_ep_set_halt,
+};
+
+static int max3420_wakeup(struct usb_gadget *gadget)
+{
+ struct max3420_udc *udc = to_udc(gadget);
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* Only if wakeup allowed by host */
+ if (udc->remote_wkp) {
+ udc->todo |= REMOTE_WAKEUP;
+ ret = 0;
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (udc->thread_task &&
+ udc->thread_task->state != TASK_RUNNING)
+ wake_up_process(udc->thread_task);
+ return ret;
+}
+
+static int max3420_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct max3420_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ /* hook up the driver */
+ driver->driver.bus = NULL;
+ udc->driver = driver;
+ udc->gadget.speed = USB_SPEED_FULL;
+
+ udc->gadget.is_selfpowered = udc->is_selfpowered;
+ udc->remote_wkp = 0;
+ udc->softconnect = true;
+ udc->todo |= UDC_START;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (udc->thread_task &&
+ udc->thread_task->state != TASK_RUNNING)
+ wake_up_process(udc->thread_task);
+
+ return 0;
+}
+
+static int max3420_udc_stop(struct usb_gadget *gadget)
+{
+ struct max3420_udc *udc = to_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->is_selfpowered = udc->gadget.is_selfpowered;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->driver = NULL;
+ udc->softconnect = false;
+ udc->todo |= UDC_START;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (udc->thread_task &&
+ udc->thread_task->state != TASK_RUNNING)
+ wake_up_process(udc->thread_task);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops max3420_udc_ops = {
+ .udc_start = max3420_udc_start,
+ .udc_stop = max3420_udc_stop,
+ .wakeup = max3420_wakeup,
+};
+
+static void max3420_eps_init(struct max3420_udc *udc)
+{
+ int idx;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+ for (idx = 0; idx < MAX3420_MAX_EPS; idx++) {
+ struct max3420_ep *ep = &udc->ep[idx];
+
+ spin_lock_init(&ep->lock);
+ INIT_LIST_HEAD(&ep->queue);
+
+ ep->udc = udc;
+ ep->id = idx;
+ ep->halted = 0;
+ ep->maxpacket = 0;
+ ep->ep_usb.name = ep->name;
+ ep->ep_usb.ops = &max3420_ep_ops;
+ usb_ep_set_maxpacket_limit(&ep->ep_usb, MAX3420_EP_MAX_PACKET);
+
+ if (idx == 0) { /* For EP0 */
+ ep->ep_usb.desc = &ep0_desc;
+ ep->ep_usb.maxpacket = usb_endpoint_maxp(&ep0_desc);
+ ep->ep_usb.caps.type_control = true;
+ ep->ep_usb.caps.dir_in = true;
+ ep->ep_usb.caps.dir_out = true;
+ snprintf(ep->name, MAX3420_EPNAME_SIZE, "ep0");
+ continue;
+ }
+
+ if (idx == 1) { /* EP1 is OUT */
+ ep->ep_usb.caps.dir_in = false;
+ ep->ep_usb.caps.dir_out = true;
+ snprintf(ep->name, MAX3420_EPNAME_SIZE, "ep1-bulk-out");
+ } else { /* EP2 & EP3 are IN */
+ ep->ep_usb.caps.dir_in = true;
+ ep->ep_usb.caps.dir_out = false;
+ snprintf(ep->name, MAX3420_EPNAME_SIZE,
+ "ep%d-bulk-in", idx);
+ }
+ ep->ep_usb.caps.type_iso = false;
+ ep->ep_usb.caps.type_int = false;
+ ep->ep_usb.caps.type_bulk = true;
+
+ list_add_tail(&ep->ep_usb.ep_list,
+ &udc->gadget.ep_list);
+ }
+}
+
+static int max3420_probe(struct spi_device *spi)
+{
+ struct max3420_udc *udc;
+ int err, irq;
+ u8 reg[8];
+
+ if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
+ dev_err(&spi->dev, "UDC needs full duplex to work\n");
+ return -EINVAL;
+ }
+
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+
+ err = spi_setup(spi);
+ if (err) {
+ dev_err(&spi->dev, "Unable to setup SPI bus\n");
+ return -EFAULT;
+ }
+
+ udc = devm_kzalloc(&spi->dev, sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
+
+ udc->spi = spi;
+
+ udc->remote_wkp = 0;
+
+ /* Setup gadget structure */
+ udc->gadget.ops = &max3420_udc_ops;
+ udc->gadget.max_speed = USB_SPEED_FULL;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->gadget.ep0 = &udc->ep[0].ep_usb;
+ udc->gadget.name = driver_name;
+
+ spin_lock_init(&udc->lock);
+ mutex_init(&udc->spi_bus_mutex);
+
+ udc->ep0req.ep = &udc->ep[0];
+ udc->ep0req.usb_req.buf = udc->ep0buf;
+ INIT_LIST_HEAD(&udc->ep0req.queue);
+
+ /* setup Endpoints */
+ max3420_eps_init(udc);
+
+ /* configure SPI */
+ spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8);
+ spi_wr8(udc, MAX3420_REG_PINCTL, FDUPSPI);
+
+ err = usb_add_gadget_udc(&spi->dev, &udc->gadget);
+ if (err)
+ return err;
+
+ udc->dev = &udc->gadget.dev;
+
+ spi_set_drvdata(spi, udc);
+
+ irq = of_irq_get_byname(spi->dev.of_node, "udc");
+ err = devm_request_irq(&spi->dev, irq, max3420_irq_handler, 0,
+ "max3420", udc);
+ if (err < 0)
+ goto del_gadget;
+
+ udc->thread_task = kthread_create(max3420_thread, udc,
+ "max3420-thread");
+ if (IS_ERR(udc->thread_task)) {
+ err = PTR_ERR(udc->thread_task);
+ goto del_gadget;
+ }
+
+ irq = of_irq_get_byname(spi->dev.of_node, "vbus");
+ if (irq <= 0) { /* no vbus irq implies self-powered design */
+ udc->is_selfpowered = 1;
+ udc->vbus_active = true;
+ udc->todo |= UDC_START;
+ usb_udc_vbus_handler(&udc->gadget, udc->vbus_active);
+ usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED);
+ max3420_start(udc);
+ } else {
+ udc->is_selfpowered = 0;
+ /* Detect current vbus status */
+ spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8);
+ if (reg[7] != 0xff)
+ udc->vbus_active = true;
+
+ err = devm_request_irq(&spi->dev, irq,
+ max3420_vbus_handler, 0, "vbus", udc);
+ if (err < 0)
+ goto del_gadget;
+ }
+
+ return 0;
+
+del_gadget:
+ usb_del_gadget_udc(&udc->gadget);
+ return err;
+}
+
+static int max3420_remove(struct spi_device *spi)
+{
+ struct max3420_udc *udc = spi_get_drvdata(spi);
+ unsigned long flags;
+
+ usb_del_gadget_udc(&udc->gadget);
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ kthread_stop(udc->thread_task);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static const struct of_device_id max3420_udc_of_match[] = {
+ { .compatible = "maxim,max3420-udc"},
+ { .compatible = "maxim,max3421-udc"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, max3420_udc_of_match);
+
+static struct spi_driver max3420_driver = {
+ .driver = {
+ .name = "max3420-udc",
+ .of_match_table = of_match_ptr(max3420_udc_of_match),
+ },
+ .probe = max3420_probe,
+ .remove = max3420_remove,
+};
+
+module_spi_driver(max3420_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Jassi Brar <jaswinder.singh@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h
index 982625b..66b84f7 100644
--- a/drivers/usb/gadget/udc/mv_u3d.h
+++ b/drivers/usb/gadget/udc/mv_u3d.h
@@ -138,7 +138,7 @@
u32 doorbell; /* doorbell register */
};
-/* control enpoint enable registers */
+/* control endpoint enable registers */
struct epxcr {
u32 epxoutcr0; /* ep out control 0 register */
u32 epxoutcr1; /* ep out control 1 register */
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index bdba3f4..0db97fe 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -32,7 +32,6 @@
#define DRIVER_DESC "Marvell PXA USB3.0 Device Controller driver"
static const char driver_name[] = "mv_u3d";
-static const char driver_desc[] = DRIVER_DESC;
static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status);
static void mv_u3d_stop_activity(struct mv_u3d *u3d,
@@ -1548,7 +1547,7 @@
delegate = true;
/* delegate USB standard requests to the gadget driver */
- if (delegate == true) {
+ if (delegate) {
/* USB requests handled by gadget */
if (setup->wLength) {
/* DATA phase from gadget, STATUS phase from u3d */
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 80a1b52..0fb4ef4 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -53,7 +53,6 @@
static DECLARE_COMPLETION(release_done);
static const char driver_name[] = "mv_udc";
-static const char driver_desc[] = DRIVER_DESC;
static void nuke(struct mv_ep *ep, int status);
static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver);
@@ -1502,7 +1501,7 @@
static void mv_udc_testmode(struct mv_udc *udc, u16 index)
{
- if (index <= TEST_FORCE_EN) {
+ if (index <= USB_TEST_FORCE_ENABLE) {
udc->test_mode = index;
if (udc_prime_status(udc, EP_DIR_IN, 0, true))
ep0_stall(udc);
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 5980540..23a7356 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -9,7 +9,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
-#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -54,7 +53,7 @@
*
* If use_dma is disabled, pio will be used instead.
*/
-static bool use_dma = 0;
+static bool use_dma = false;
module_param(use_dma, bool, 0644);
/*
@@ -1688,7 +1687,7 @@
net2272_write(dev, USBTEST, mode);
/* load test packet */
- if (mode == TEST_PACKET) {
+ if (mode == USB_TEST_PACKET) {
/* switch to 8 bit mode */
net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) &
~(1 << DATA_WIDTH));
@@ -2196,7 +2195,8 @@
static void
net2272_gadget_release(struct device *_dev)
{
- struct net2272 *dev = dev_get_drvdata(_dev);
+ struct net2272 *dev = container_of(_dev, struct net2272, gadget.dev);
+
kfree(dev);
}
@@ -2205,7 +2205,8 @@
static void
net2272_remove(struct net2272 *dev)
{
- usb_del_gadget_udc(&dev->gadget);
+ if (dev->added)
+ usb_del_gadget(&dev->gadget);
free_irq(dev->irq, dev);
iounmap(dev->base_addr);
device_remove_file(dev->dev, &dev_attr_registers);
@@ -2235,6 +2236,7 @@
/* the "gadget" abstracts/virtualizes the controller */
ret->gadget.name = driver_name;
+ usb_initialize_gadget(dev, &ret->gadget, net2272_gadget_release);
return ret;
}
@@ -2273,10 +2275,10 @@
if (ret)
goto err_irq;
- ret = usb_add_gadget_udc_release(dev->dev, &dev->gadget,
- net2272_gadget_release);
+ ret = usb_add_gadget(&dev->gadget);
if (ret)
goto err_add_udc;
+ dev->added = 1;
return 0;
@@ -2323,7 +2325,7 @@
goto err;
}
- mem_mapped_addr[i] = ioremap_nocache(resource, len);
+ mem_mapped_addr[i] = ioremap(resource, len);
if (mem_mapped_addr[i] == NULL) {
release_mem_region(resource, len);
dev_dbg(dev->dev, "can't map memory\n");
@@ -2370,6 +2372,8 @@
err:
while (--i >= 0) {
+ if (i == 1)
+ continue; /* BAR1 unused */
iounmap(mem_mapped_addr[i]);
release_mem_region(pci_resource_start(pdev, i),
pci_resource_len(pdev, i));
@@ -2401,7 +2405,7 @@
goto err;
}
- mem_mapped_addr[i] = ioremap_nocache(resource, len);
+ mem_mapped_addr[i] = ioremap(resource, len);
if (mem_mapped_addr[i] == NULL) {
release_mem_region(resource, len);
dev_dbg(dev->dev, "can't map memory\n");
@@ -2449,7 +2453,7 @@
if (pci_enable_device(pdev) < 0) {
ret = -ENODEV;
- goto err_free;
+ goto err_put;
}
pci_set_master(pdev);
@@ -2472,8 +2476,8 @@
err_pci:
pci_disable_device(pdev);
- err_free:
- kfree(dev);
+ err_put:
+ usb_put_gadget(&dev->gadget);
return ret;
}
@@ -2534,7 +2538,7 @@
pci_disable_device(pdev);
- kfree(dev);
+ usb_put_gadget(&dev->gadget);
}
/* Table of matching PCI IDs */
@@ -2625,7 +2629,7 @@
ret = -EBUSY;
goto err;
}
- dev->base_addr = ioremap_nocache(base, len);
+ dev->base_addr = ioremap(base, len);
if (!dev->base_addr) {
dev_dbg(dev->dev, "can't map memory\n");
ret = -EFAULT;
@@ -2647,7 +2651,7 @@
err_req:
release_mem_region(base, len);
err:
- kfree(dev);
+ usb_put_gadget(&dev->gadget);
return ret;
}
@@ -2662,7 +2666,7 @@
release_mem_region(pdev->resource[0].start,
resource_size(&pdev->resource[0]));
- kfree(dev);
+ usb_put_gadget(&dev->gadget);
return 0;
}
diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h
index 8e64462..c669308 100644
--- a/drivers/usb/gadget/udc/net2272.h
+++ b/drivers/usb/gadget/udc/net2272.h
@@ -105,11 +105,6 @@
#define USBTEST 0x32
#define TEST_MODE_SELECT 0
#define NORMAL_OPERATION 0
-#define TEST_J 1
-#define TEST_K 2
-#define TEST_SE0_NAK 3
-#define TEST_PACKET 4
-#define TEST_FORCE_ENABLE 5
#define XCVRDIAG 0x33
#define FORCE_FULL_SPEED 2
#define FORCE_HIGH_SPEED 3
@@ -446,6 +441,7 @@
unsigned protocol_stall:1,
softconnect:1,
wakeup:1,
+ added:1,
dma_eot_polarity:1,
dma_dack_polarity:1,
dma_dreq_polarity:1,
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index 7c616d7..fc9f99f 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -52,6 +52,7 @@
#include <linux/usb/gadget.h>
#include <linux/prefetch.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
@@ -360,18 +361,16 @@
static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
{
u32 result;
+ int ret;
- do {
- result = readl(ptr);
- if (result == ~(u32)0) /* "device unplugged" */
- return -ENODEV;
- result &= mask;
- if (result == done)
- return 0;
- udelay(1);
- usec--;
- } while (usec > 0);
- return -ETIMEDOUT;
+ ret = readl_poll_timeout_atomic(ptr, result,
+ ((result & mask) == done ||
+ result == U32_MAX),
+ 1, usec);
+ if (result == U32_MAX) /* device unplugged */
+ return -ENODEV;
+
+ return ret;
}
static const struct usb_ep_ops net2280_ep_ops;
@@ -2861,6 +2860,8 @@
static void handle_stat0_irqs_superspeed(struct net2280 *dev,
struct net2280_ep *ep, struct usb_ctrlrequest r)
{
+ struct net2280_ep *e;
+ u16 status;
int tmp = 0;
#define w_value le16_to_cpu(r.wValue)
@@ -2868,9 +2869,6 @@
#define w_length le16_to_cpu(r.wLength)
switch (r.bRequest) {
- struct net2280_ep *e;
- u16 status;
-
case USB_REQ_SET_CONFIGURATION:
dev->addressed_state = !w_value;
goto usb3_delegate;
@@ -3562,7 +3560,7 @@
static void gadget_release(struct device *_dev)
{
- struct net2280 *dev = dev_get_drvdata(_dev);
+ struct net2280 *dev = container_of(_dev, struct net2280, gadget.dev);
kfree(dev);
}
@@ -3573,7 +3571,8 @@
{
struct net2280 *dev = pci_get_drvdata(pdev);
- usb_del_gadget_udc(&dev->gadget);
+ if (dev->added)
+ usb_del_gadget(&dev->gadget);
BUG_ON(dev->driver);
@@ -3604,6 +3603,7 @@
device_remove_file(&pdev->dev, &dev_attr_registers);
ep_info(dev, "unbind\n");
+ usb_put_gadget(&dev->gadget);
}
/* wrap this driver around the specified device, but
@@ -3625,6 +3625,7 @@
}
pci_set_drvdata(pdev, dev);
+ usb_initialize_gadget(&pdev->dev, &dev->gadget, gadget_release);
spin_lock_init(&dev->lock);
dev->quirks = id->driver_data;
dev->pdev = pdev;
@@ -3659,7 +3660,7 @@
* 8051 code into the chip, e.g. to turn on PCI PM.
*/
- base = ioremap_nocache(resource, len);
+ base = ioremap(resource, len);
if (base == NULL) {
ep_dbg(dev, "can't map memory\n");
retval = -EFAULT;
@@ -3775,10 +3776,10 @@
if (retval)
goto done;
- retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
- gadget_release);
+ retval = usb_add_gadget(&dev->gadget);
if (retval)
goto done;
+ dev->added = 1;
return 0;
done:
@@ -3859,7 +3860,7 @@
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver net2280_pci_driver = {
- .name = (char *) driver_name,
+ .name = driver_name,
.id_table = pci_ids,
.probe = net2280_probe,
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
index 85d3ca1..7da3dc1 100644
--- a/drivers/usb/gadget/udc/net2280.h
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -156,6 +156,7 @@
softconnect : 1,
got_irq : 1,
region:1,
+ added:1,
u1_enable:1,
u2_enable:1,
ltm_enable:1,
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index f36f073..494da00 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2576,7 +2576,7 @@
case USB_ENDPOINT_XFER_INT:
ep->ep.caps.type_int = true;
break;
- };
+ }
if (addr & USB_DIR_IN)
ep->ep.caps.dir_in = true;
@@ -2757,7 +2757,7 @@
/* NOTE: "knows" the order of the resources! */
if (!request_mem_region(pdev->resource[0].start,
- pdev->resource[0].end - pdev->resource[0].start + 1,
+ resource_size(&pdev->resource[0]),
driver_name)) {
DBG("request_mem_region failed\n");
return -EBUSY;
@@ -2831,7 +2831,7 @@
type = "integrated";
break;
}
- /* FALL THROUGH */
+ fallthrough;
case 3:
case 11:
case 16:
@@ -2848,7 +2848,7 @@
case 14: /* transceiverless */
if (cpu_is_omap1710())
goto bad_on_1710;
- /* FALL THROUGH */
+ fallthrough;
case 13:
case 15:
type = "no";
@@ -2934,7 +2934,7 @@
}
release_mem_region(pdev->resource[0].start,
- pdev->resource[0].end - pdev->resource[0].start + 1);
+ resource_size(&pdev->resource[0]));
return status;
}
@@ -2950,7 +2950,7 @@
wait_for_completion(&done);
release_mem_region(pdev->resource[0].start,
- pdev->resource[0].end - pdev->resource[0].start + 1);
+ resource_size(&pdev->resource[0]));
return 0;
}
@@ -3001,7 +3001,7 @@
.suspend = omap_udc_suspend,
.resume = omap_udc_resume,
.driver = {
- .name = (char *) driver_name,
+ .name = driver_name,
},
};
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index da8aeec..fd3656d 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -7,17 +7,16 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/gpio.h>
#include <linux/irq.h>
-/* GPIO port for VBUS detecting */
-static int vbus_gpio_port = -1; /* GPIO port number (-1:Not used) */
-
#define PCH_VBUS_PERIOD 3000 /* VBUS polling period (msec) */
#define PCH_VBUS_INTERVAL 10 /* VBUS polling interval (msec) */
@@ -229,8 +228,7 @@
* for control data
* @status: Status
* @reserved: Reserved
- * @data12: First setup word
- * @data34: Second setup word
+ * @request: Control Request
*/
struct pch_udc_stp_dma_desc {
u32 status;
@@ -302,13 +300,13 @@
/**
* struct pch_vbus_gpio_data - Structure holding GPIO informaton
* for detecting VBUS
- * @port: gpio port number
+ * @port: gpio descriptor for the VBUS GPIO
* @intr: gpio interrupt number
- * @irq_work_fall Structure for WorkQueue
- * @irq_work_rise Structure for WorkQueue
+ * @irq_work_fall: Structure for WorkQueue
+ * @irq_work_rise: Structure for WorkQueue
*/
struct pch_vbus_gpio_data {
- int port;
+ struct gpio_desc *port;
int intr;
struct work_struct irq_work_fall;
struct work_struct irq_work_rise;
@@ -475,7 +473,7 @@
* pch_udc_write_csr() - Write the command and status registers.
* @dev: Reference to pch_udc_dev structure
* @val: value to be written to CSR register
- * @addr: address of CSR register
+ * @ep: end-point number
*/
static void pch_udc_write_csr(struct pch_udc_dev *dev, unsigned long val,
unsigned int ep)
@@ -490,7 +488,7 @@
/**
* pch_udc_read_csr() - Read the command and status registers.
* @dev: Reference to pch_udc_dev structure
- * @addr: address of CSR register
+ * @ep: end-point number
*
* Return codes: content of CSR register
*/
@@ -660,6 +658,7 @@
* pch_udc_ep_set_bufsz() - Set the maximum packet size for the endpoint
* @ep: Reference to structure of type pch_udc_ep_regs
* @buf_size: The buffer word size
+ * @ep_in: EP is IN
*/
static void pch_udc_ep_set_bufsz(struct pch_udc_ep *ep,
u32 buf_size, u32 ep_in)
@@ -972,7 +971,8 @@
/**
* pch_udc_ep_enable() - This api enables endpoint
- * @regs: Reference to structure pch_udc_ep_regs
+ * @ep: reference to structure of type pch_udc_ep_regs
+ * @cfg: current configuration information
* @desc: endpoint descriptor
*/
static void pch_udc_ep_enable(struct pch_udc_ep *ep,
@@ -1008,7 +1008,7 @@
/**
* pch_udc_ep_disable() - This api disables endpoint
- * @regs: Reference to structure pch_udc_ep_regs
+ * @ep: reference to structure of type pch_udc_ep_regs
*/
static void pch_udc_ep_disable(struct pch_udc_ep *ep)
{
@@ -1028,7 +1028,7 @@
/**
* pch_udc_wait_ep_stall() - Wait EP stall.
- * @dev: Reference to pch_udc_dev structure
+ * @ep: reference to structure of type pch_udc_ep_regs
*/
static void pch_udc_wait_ep_stall(struct pch_udc_ep *ep)
{
@@ -1262,7 +1262,7 @@
int vbus = 0;
if (dev->vbus_gpio.port)
- vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0;
+ vbus = gpiod_get_value(dev->vbus_gpio.port) ? 1 : 0;
else
vbus = -1;
@@ -1340,7 +1340,7 @@
/**
* pch_vbus_gpio_irq() - IRQ handler for GPIO interrupt for changing VBUS
* @irq: Interrupt request number
- * @dev: Reference to the device structure
+ * @data: Reference to the device structure
*
* Return codes:
* 0: Success
@@ -1361,45 +1361,75 @@
return IRQ_HANDLED;
}
+static struct gpiod_lookup_table minnowboard_udc_gpios = {
+ .dev_id = "0000:02:02.4",
+ .table = {
+ GPIO_LOOKUP("sch_gpio.33158", 12, NULL, GPIO_ACTIVE_HIGH),
+ {}
+ },
+};
+
+static const struct dmi_system_id pch_udc_gpio_dmi_table[] = {
+ {
+ .ident = "MinnowBoard",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "MinnowBoard"),
+ },
+ .driver_data = &minnowboard_udc_gpios,
+ },
+ { }
+};
+
+static void pch_vbus_gpio_remove_table(void *table)
+{
+ gpiod_remove_lookup_table(table);
+}
+
+static int pch_vbus_gpio_add_table(struct pch_udc_dev *dev)
+{
+ struct device *d = &dev->pdev->dev;
+ const struct dmi_system_id *dmi;
+
+ dmi = dmi_first_match(pch_udc_gpio_dmi_table);
+ if (!dmi)
+ return 0;
+
+ gpiod_add_lookup_table(dmi->driver_data);
+ return devm_add_action_or_reset(d, pch_vbus_gpio_remove_table, dmi->driver_data);
+}
+
/**
* pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS.
- * @dev: Reference to the driver structure
- * @vbus_gpio Number of GPIO port to detect gpio
+ * @dev: Reference to the driver structure
*
* Return codes:
* 0: Success
* -EINVAL: GPIO port is invalid or can't be initialized.
*/
-static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port)
+static int pch_vbus_gpio_init(struct pch_udc_dev *dev)
{
+ struct device *d = &dev->pdev->dev;
int err;
int irq_num = 0;
+ struct gpio_desc *gpiod;
- dev->vbus_gpio.port = 0;
+ dev->vbus_gpio.port = NULL;
dev->vbus_gpio.intr = 0;
- if (vbus_gpio_port <= -1)
- return -EINVAL;
+ err = pch_vbus_gpio_add_table(dev);
+ if (err)
+ return err;
- err = gpio_is_valid(vbus_gpio_port);
- if (!err) {
- pr_err("%s: gpio port %d is invalid\n",
- __func__, vbus_gpio_port);
- return -EINVAL;
- }
+ /* Retrieve the GPIO line from the USB gadget device */
+ gpiod = devm_gpiod_get_optional(d, NULL, GPIOD_IN);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+ gpiod_set_consumer_name(gpiod, "pch_vbus");
- err = gpio_request(vbus_gpio_port, "pch_vbus");
- if (err) {
- pr_err("%s: can't request gpio port %d, err: %d\n",
- __func__, vbus_gpio_port, err);
- return -EINVAL;
- }
-
- dev->vbus_gpio.port = vbus_gpio_port;
- gpio_direction_input(vbus_gpio_port);
+ dev->vbus_gpio.port = gpiod;
INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall);
- irq_num = gpio_to_irq(vbus_gpio_port);
+ irq_num = gpiod_to_irq(gpiod);
if (irq_num > 0) {
irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH);
err = request_irq(irq_num, pch_vbus_gpio_irq, 0,
@@ -1425,9 +1455,6 @@
{
if (dev->vbus_gpio.intr)
free_irq(dev->vbus_gpio.intr, dev);
-
- if (dev->vbus_gpio.port)
- gpio_free(dev->vbus_gpio.port);
}
/**
@@ -1508,8 +1535,8 @@
/**
* pch_udc_free_dma_chain() - This function frees the DMA chain created
* for the request
- * @dev Reference to the driver structure
- * @req Reference to the request to be freed
+ * @dev: Reference to the driver structure
+ * @req: Reference to the request to be freed
*
* Return codes:
* 0: Success
@@ -1716,7 +1743,7 @@
/**
* pch_udc_pcd_ep_disable() - This API disables endpoint and is called
* from gadget driver
- * @usbep Reference to the USB endpoint structure
+ * @usbep: Reference to the USB endpoint structure
*
* Return codes:
* 0: Success
@@ -2005,7 +2032,6 @@
* pch_udc_pcd_set_wedge() - This function Sets or clear the endpoint
* halt feature
* @usbep: Reference to the USB endpoint structure
- * @halt: Specifies whether to set or clear the feature
*
* Return codes:
* 0: Success
@@ -2767,7 +2793,7 @@
/**
* pch_udc_isr() - This function handles interrupts from the PCH USB Device
* @irq: Interrupt request number
- * @dev: Reference to the device structure
+ * @pdev: Reference to the device structure
*/
static irqreturn_t pch_udc_isr(int irq, void *pdev)
{
@@ -2905,19 +2931,25 @@
* @dev: Reference to the driver structure
*
* Return codes:
- * 0: Success
+ * 0: Success
+ * -%ERRNO: All kind of errors when retrieving VBUS GPIO
*/
static int pch_udc_pcd_init(struct pch_udc_dev *dev)
{
+ int ret;
+
pch_udc_init(dev);
pch_udc_pcd_reinit(dev);
- pch_vbus_gpio_init(dev, vbus_gpio_port);
- return 0;
+
+ ret = pch_vbus_gpio_init(dev);
+ if (ret)
+ pch_udc_exit(dev);
+ return ret;
}
/**
* init_dma_pools() - create dma pools during initialization
- * @pdev: reference to struct pci_dev
+ * @dev: reference to struct pci_dev
*/
static int init_dma_pools(struct pch_udc_dev *dev)
{
@@ -3098,6 +3130,7 @@
if (retval)
return retval;
+ dev->pdev = pdev;
pci_set_drvdata(pdev, dev);
/* Determine BAR based on PCI ID */
@@ -3114,8 +3147,9 @@
dev->base_addr = pcim_iomap_table(pdev)[bar];
/* initialize the hardware */
- if (pch_udc_pcd_init(dev))
- return -ENODEV;
+ retval = pch_udc_pcd_init(dev);
+ if (retval)
+ return retval;
pci_enable_msi(pdev);
@@ -3132,7 +3166,6 @@
/* device struct setup */
spin_lock_init(&dev->lock);
- dev->pdev = pdev;
dev->gadget.ops = &pch_udc_ops;
retval = init_dma_pools(dev);
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index d4be535..10324a7 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -2321,7 +2321,6 @@
struct pxa25x_udc *dev = &memory;
int retval, irq;
u32 chiprev;
- struct resource *res;
pr_info("%s: version %s\n", driver_name, DRIVER_VERSION);
@@ -2341,12 +2340,12 @@
case PXA250_A0:
case PXA250_A1:
/* A0/A1 "not released"; ep 13, 15 unusable */
- /* fall through */
+ fallthrough;
case PXA250_B2: case PXA210_B2:
case PXA250_B1: case PXA210_B1:
case PXA250_B0: case PXA210_B0:
/* OUT-DMA is broken ... */
- /* fall through */
+ fallthrough;
case PXA250_C0: case PXA210_C0:
break;
#elif defined(CONFIG_ARCH_IXP4XX)
@@ -2367,8 +2366,7 @@
if (irq < 0)
return -ENODEV;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dev->regs = devm_ioremap_resource(&pdev->dev, res);
+ dev->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(dev->regs))
return PTR_ERR(dev->regs);
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 0142332..cfaeca4 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -207,7 +207,7 @@
{
struct dentry *root;
- root = debugfs_create_dir(udc->gadget.name, NULL);
+ root = debugfs_create_dir(udc->gadget.name, usb_debug_root);
udc->debugfs_root = root;
debugfs_create_file("udcstate", 0400, root, udc, &state_dbg_fops);
@@ -386,7 +386,7 @@
/**
* ep_write_UDCCSR - set bits in UDCCSR
- * @udc: udc device
+ * @ep: udc endpoint
* @mask: bits to set in UDCCR
*
* Sets bits in UDCCSR (UDCCSR0 and UDCCSR*).
@@ -472,7 +472,7 @@
/**
* set_ep0state - Set ep0 automata state
- * @dev: udc device
+ * @udc: udc device
* @state: state
*/
static void set_ep0state(struct pxa_udc *udc, int state)
@@ -498,7 +498,6 @@
/**
* inc_ep_stats_reqs - Update ep stats counts
* @ep: physical endpoint
- * @req: usb request
* @is_in: ep direction (USB_DIR_IN or 0)
*
*/
@@ -1473,7 +1472,6 @@
* Context: any
*
* The UDC should be enabled if :
-
* - the pullup resistor is connected
* - and a gadget driver is bound
* - and vbus is sensed (or no vbus sense is available)
@@ -1688,7 +1686,7 @@
/**
* udc_enable - Enables the udc device
- * @dev: udc device
+ * @udc: udc device
*
* Enables the udc device : enables clocks, udc interrupts, control endpoint
* interrupts, sets usb as UDC client and setups endpoints.
@@ -1732,8 +1730,8 @@
/**
* pxa27x_start - Register gadget driver
+ * @g: gadget
* @driver: gadget driver
- * @bind: bind function
*
* When a driver is successfully registered, it will receive control requests
* including set_configuration(), which enables non-control requests. Then
@@ -1775,7 +1773,6 @@
/**
* stop_activity - Stops udc endpoints
* @udc: udc device
- * @driver: gadget driver
*
* Disables all udc endpoints (even control endpoint), report disconnect to
* the gadget user.
@@ -1792,7 +1789,7 @@
/**
* pxa27x_udc_stop - Unregister the gadget driver
- * @driver: gadget driver
+ * @g: gadget
*
* Returns 0 if no error, -ENODEV, -EINVAL otherwise
*/
@@ -2349,14 +2346,13 @@
/**
* pxa_udc_probe - probes the udc device
- * @_dev: platform device
+ * @pdev: platform device
*
* Perform basic init : allocates udc clock, creates sysfs files, requests
* irq.
*/
static int pxa_udc_probe(struct platform_device *pdev)
{
- struct resource *regs;
struct pxa_udc *udc = &memory;
int retval = 0, gpio;
struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev);
@@ -2378,8 +2374,7 @@
udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS);
}
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- udc->regs = devm_ioremap_resource(&pdev->dev, regs);
+ udc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(udc->regs))
return PTR_ERR(udc->regs);
udc->irq = platform_get_irq(pdev, 0);
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index a766476..38e4d6b 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1250,7 +1250,7 @@
do {
tmp = r8a66597_read(r8a66597, INTSTS0) & CTSQ;
udelay(1);
- } while (tmp != CS_IDST || timeout-- > 0);
+ } while (tmp != CS_IDST && timeout-- > 0);
if (tmp == CS_IDST)
r8a66597_bset(r8a66597,
@@ -1827,10 +1827,8 @@
static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
struct platform_device *pdev)
{
- struct resource *res;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac");
- r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res);
+ r8a66597->sudmac_reg =
+ devm_platform_ioremap_resource_byname(pdev, "sudmac");
return PTR_ERR_OR_ZERO(r8a66597->sudmac_reg);
}
@@ -1838,7 +1836,7 @@
{
struct device *dev = &pdev->dev;
char clk_name[8];
- struct resource *res, *ires;
+ struct resource *ires;
int irq;
void __iomem *reg = NULL;
struct r8a66597 *r8a66597 = NULL;
@@ -1846,8 +1844,7 @@
int i;
unsigned long irq_trigger;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- reg = devm_ioremap_resource(&pdev->dev, res);
+ reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg))
return PTR_ERR(reg);
@@ -1971,7 +1968,7 @@
static struct platform_driver r8a66597_driver = {
.remove = r8a66597_remove,
.driver = {
- .name = (char *) udc_name,
+ .name = udc_name,
},
};
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index b6653bc..601829a 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -775,6 +775,18 @@
usb3_transition_to_default_state(usb3, false);
}
+static void usb3_irq_epc_int_1_suspend(struct renesas_usb3 *usb3)
+{
+ usb3_disable_irq_1(usb3, USB_INT_1_B2_SPND);
+
+ if (usb3->gadget.speed != USB_SPEED_UNKNOWN &&
+ usb3->gadget.state != USB_STATE_NOTATTACHED) {
+ if (usb3->driver && usb3->driver->suspend)
+ usb3->driver->suspend(&usb3->gadget);
+ usb_gadget_set_state(&usb3->gadget, USB_STATE_SUSPENDED);
+ }
+}
+
static void usb3_irq_epc_int_1_disable(struct renesas_usb3 *usb3)
{
usb3_stop_usb3_connection(usb3);
@@ -860,6 +872,9 @@
if (int_sta_1 & USB_INT_1_B2_RSUM)
usb3_irq_epc_int_1_resume(usb3);
+ if (int_sta_1 & USB_INT_1_B2_SPND)
+ usb3_irq_epc_int_1_suspend(usb3);
+
if (int_sta_1 & USB_INT_1_SPEED)
usb3_irq_epc_int_1_speed(usb3);
@@ -2341,14 +2356,14 @@
.set_selfpowered = renesas_usb3_set_selfpowered,
};
-static enum usb_role renesas_usb3_role_switch_get(struct device *dev)
+static enum usb_role renesas_usb3_role_switch_get(struct usb_role_switch *sw)
{
- struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+ struct renesas_usb3 *usb3 = usb_role_switch_get_drvdata(sw);
enum usb_role cur_role;
- pm_runtime_get_sync(dev);
+ pm_runtime_get_sync(usb3_to_dev(usb3));
cur_role = usb3_is_host(usb3) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
- pm_runtime_put(dev);
+ pm_runtime_put(usb3_to_dev(usb3));
return cur_role;
}
@@ -2358,11 +2373,13 @@
{
struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
struct device *host = usb3->host_dev;
- enum usb_role cur_role = renesas_usb3_role_switch_get(dev);
+ enum usb_role cur_role = renesas_usb3_role_switch_get(usb3->role_sw);
switch (role) {
case USB_ROLE_NONE:
usb3->connection_state = USB_ROLE_NONE;
+ if (cur_role == USB_ROLE_HOST)
+ device_release_driver(host);
if (usb3->driver)
usb3_disconnect(usb3);
usb3_vbus_out(usb3, false);
@@ -2410,7 +2427,7 @@
{
struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
struct device *host = usb3->host_dev;
- enum usb_role cur_role = renesas_usb3_role_switch_get(dev);
+ enum usb_role cur_role = renesas_usb3_role_switch_get(usb3->role_sw);
if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) {
device_release_driver(host);
@@ -2424,19 +2441,19 @@
}
}
-static int renesas_usb3_role_switch_set(struct device *dev,
+static int renesas_usb3_role_switch_set(struct usb_role_switch *sw,
enum usb_role role)
{
- struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+ struct renesas_usb3 *usb3 = usb_role_switch_get_drvdata(sw);
- pm_runtime_get_sync(dev);
+ pm_runtime_get_sync(usb3_to_dev(usb3));
if (usb3->role_sw_by_connector)
- handle_ext_role_switch_states(dev, role);
+ handle_ext_role_switch_states(usb3_to_dev(usb3), role);
else
- handle_role_switch_states(dev, role);
+ handle_role_switch_states(usb3_to_dev(usb3), role);
- pm_runtime_put(dev);
+ pm_runtime_put(usb3_to_dev(usb3));
return 0;
}
@@ -2537,7 +2554,7 @@
static void renesas_usb3_debugfs_init(struct renesas_usb3 *usb3,
struct device *dev)
{
- usb3->dentry = debugfs_create_dir(dev_name(dev), NULL);
+ usb3->dentry = debugfs_create_dir(dev_name(dev), usb_debug_root);
debugfs_create_file("b_device", 0644, usb3->dentry, usb3,
&renesas_usb3_b_device_fops);
@@ -2731,7 +2748,6 @@
static int renesas_usb3_probe(struct platform_device *pdev)
{
struct renesas_usb3 *usb3;
- struct resource *res;
int irq, ret;
const struct renesas_usb3_priv *priv;
const struct soc_device_attribute *attr;
@@ -2750,8 +2766,7 @@
if (!usb3)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- usb3->reg = devm_ioremap_resource(&pdev->dev, res);
+ usb3->reg = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(usb3->reg))
return PTR_ERR(usb3->reg);
@@ -2816,6 +2831,8 @@
renesas_usb3_role_switch_desc.fwnode = dev_fwnode(&pdev->dev);
}
+ renesas_usb3_role_switch_desc.driver_data = usb3;
+
INIT_WORK(&usb3->role_work, renesas_usb3_role_work);
usb3->role_sw = usb_role_switch_register(&pdev->dev,
&renesas_usb3_role_switch_desc);
@@ -2891,7 +2908,7 @@
.probe = renesas_usb3_probe,
.remove = renesas_usb3_remove,
.driver = {
- .name = (char *)udc_name,
+ .name = udc_name,
.pm = &renesas_usb3_pm_ops,
.of_match_table = of_match_ptr(usb3_of_match),
},
diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index 858993c..7bd5182 100644
--- a/drivers/usb/gadget/udc/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
@@ -30,8 +30,6 @@
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
-#include <mach/regs-s3c2443-clock.h>
-
#define S3C_HSUDC_REG(x) (x)
/* Non-Indexed Registers */
@@ -186,53 +184,6 @@
writel(readl(ptr) | val, ptr);
}
-static void s3c_hsudc_init_phy(void)
-{
- u32 cfg;
-
- cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY;
- writel(cfg, S3C2443_PWRCFG);
-
- cfg = readl(S3C2443_URSTCON);
- cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
- writel(cfg, S3C2443_URSTCON);
- mdelay(1);
-
- cfg = readl(S3C2443_URSTCON);
- cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
- writel(cfg, S3C2443_URSTCON);
-
- cfg = readl(S3C2443_PHYCTRL);
- cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT);
- cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL);
- writel(cfg, S3C2443_PHYCTRL);
-
- cfg = readl(S3C2443_PHYPWR);
- cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN |
- S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK |
- S3C2443_PHYPWR_ANALOG_PD);
- cfg |= S3C2443_PHYPWR_COMMON_ON;
- writel(cfg, S3C2443_PHYPWR);
-
- cfg = readl(S3C2443_UCLKCON);
- cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN |
- S3C2443_UCLKCON_TCLKEN);
- writel(cfg, S3C2443_UCLKCON);
-}
-
-static void s3c_hsudc_uninit_phy(void)
-{
- u32 cfg;
-
- cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY;
- writel(cfg, S3C2443_PWRCFG);
-
- writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR);
-
- cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN;
- writel(cfg, S3C2443_UCLKCON);
-}
-
/**
* s3c_hsudc_complete_request - Complete a transfer request.
* @hsep: Endpoint to which the request belongs.
@@ -1188,7 +1139,8 @@
pm_runtime_get_sync(hsudc->dev);
- s3c_hsudc_init_phy();
+ if (hsudc->pd->phy_init)
+ hsudc->pd->phy_init();
if (hsudc->pd->gpio_init)
hsudc->pd->gpio_init();
@@ -1210,7 +1162,8 @@
spin_lock_irqsave(&hsudc->lock, flags);
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
- s3c_hsudc_uninit_phy();
+ if (hsudc->pd->phy_uninit)
+ hsudc->pd->phy_uninit();
pm_runtime_put(hsudc->dev);
@@ -1263,7 +1216,6 @@
static int s3c_hsudc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
struct s3c_hsudc *hsudc;
struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev);
int ret, i;
@@ -1286,13 +1238,12 @@
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
hsudc->supplies);
if (ret != 0) {
- dev_err(dev, "failed to request supplies: %d\n", ret);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to request supplies: %d\n", ret);
goto err_supplies;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- hsudc->regs = devm_ioremap_resource(&pdev->dev, res);
+ hsudc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hsudc->regs)) {
ret = PTR_ERR(hsudc->regs);
goto err_res;
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index 5dcc069..82c4f3f 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -36,15 +36,11 @@
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/unaligned.h>
-#include <mach/irqs.h>
-#include <mach/hardware.h>
-
-#include <plat/regs-udc.h>
#include <linux/platform_data/usb-s3c2410_udc.h>
-
#include "s3c2410_udc.h"
+#include "s3c2410_udc_regs.h"
#define DRIVER_DESC "S3C2410 USB Device Controller Gadget"
#define DRIVER_AUTHOR "Herbert Pötzl <herbert@13thfloor.at>, " \
@@ -57,8 +53,7 @@
static struct clk *udc_clock;
static struct clk *usb_bus_clock;
static void __iomem *base_addr;
-static u64 rsrc_start;
-static u64 rsrc_len;
+static int irq_usbd;
static struct dentry *s3c2410_udc_debugfs_root;
static inline u32 udc_read(u32 reg)
@@ -308,7 +303,7 @@
switch (idx) {
default:
idx = 0;
- /* fall through */
+ fallthrough;
case 0:
fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
break;
@@ -413,7 +408,7 @@
switch (idx) {
default:
idx = 0;
- /* fall through */
+ fallthrough;
case 0:
fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
break;
@@ -835,8 +830,6 @@
}
}
-#include <mach/regs-irq.h>
-
/*
* s3c2410_udc_irq - interrupt handler
*/
@@ -977,7 +970,7 @@
}
}
- dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);
+ dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", irq_usbd);
/* Restore old index */
udc_write(idx, S3C2410_UDC_INDEX_REG);
@@ -1270,7 +1263,6 @@
static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
- struct s3c2410_udc *udc;
int retval = -EINVAL;
unsigned long flags;
struct s3c2410_request *req = NULL;
@@ -1283,8 +1275,6 @@
if (!_ep || !_req)
return retval;
- udc = to_s3c2410_udc(ep->gadget);
-
local_irq_save(flags);
list_for_each_entry(req, &ep->queue, queue) {
@@ -1760,7 +1750,8 @@
udc_clock = clk_get(NULL, "usb-device");
if (IS_ERR(udc_clock)) {
dev_err(dev, "failed to get udc clock source\n");
- return PTR_ERR(udc_clock);
+ retval = PTR_ERR(udc_clock);
+ goto err_usb_bus_clk;
}
clk_prepare_enable(udc_clock);
@@ -1780,16 +1771,10 @@
spin_lock_init(&udc->lock);
udc_info = dev_get_platdata(&pdev->dev);
- rsrc_start = S3C2410_PA_USBDEV;
- rsrc_len = S3C24XX_SZ_USBDEV;
-
- if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
- return -EBUSY;
-
- base_addr = ioremap(rsrc_start, rsrc_len);
- if (!base_addr) {
- retval = -ENOMEM;
- goto err_mem;
+ base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base_addr)) {
+ retval = PTR_ERR(base_addr);
+ goto err_udc_clk;
}
the_controller = udc;
@@ -1798,17 +1783,23 @@
s3c2410_udc_disable(udc);
s3c2410_udc_reinit(udc);
+ irq_usbd = platform_get_irq(pdev, 0);
+ if (irq_usbd < 0) {
+ retval = irq_usbd;
+ goto err_udc_clk;
+ }
+
/* irq setup after old hardware state is cleaned up */
- retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
+ retval = request_irq(irq_usbd, s3c2410_udc_irq,
0, gadget_name, udc);
if (retval != 0) {
- dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
+ dev_err(dev, "cannot get irq %i, err %d\n", irq_usbd, retval);
retval = -EBUSY;
- goto err_map;
+ goto err_udc_clk;
}
- dev_dbg(dev, "got irq %i\n", IRQ_USBD);
+ dev_dbg(dev, "got irq %i\n", irq_usbd);
if (udc_info && udc_info->vbus_pin > 0) {
retval = gpio_request(udc_info->vbus_pin, "udc vbus");
@@ -1875,11 +1866,15 @@
if (udc_info && udc_info->vbus_pin > 0)
gpio_free(udc_info->vbus_pin);
err_int:
- free_irq(IRQ_USBD, udc);
-err_map:
- iounmap(base_addr);
-err_mem:
- release_mem_region(rsrc_start, rsrc_len);
+ free_irq(irq_usbd, udc);
+err_udc_clk:
+ clk_disable_unprepare(udc_clock);
+ clk_put(udc_clock);
+ udc_clock = NULL;
+err_usb_bus_clk:
+ clk_disable_unprepare(usb_bus_clock);
+ clk_put(usb_bus_clock);
+ usb_bus_clock = NULL;
return retval;
}
@@ -1909,10 +1904,7 @@
free_irq(irq, udc);
}
- free_irq(IRQ_USBD, udc);
-
- iounmap(base_addr);
- release_mem_region(rsrc_start, rsrc_len);
+ free_irq(irq_usbd, udc);
if (!IS_ERR(udc_clock) && udc_clock != NULL) {
clk_disable_unprepare(udc_clock);
@@ -1974,7 +1966,8 @@
dprintk(DEBUG_NORMAL, "%s\n", gadget_name);
- s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
+ s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name,
+ usb_debug_root);
retval = platform_driver_register(&udc_driver_24x0);
if (retval)
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h
index bdcaa8d..68bdf3e 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.h
+++ b/drivers/usb/gadget/udc/s3c2410_udc.h
@@ -90,6 +90,7 @@
unsigned req_pending : 1;
u8 vbus;
struct dentry *regs_info;
+ int irq;
};
#define to_s3c2410(g) (container_of((g), struct s3c2410_udc, gadget))
diff --git a/drivers/usb/gadget/udc/s3c2410_udc_regs.h b/drivers/usb/gadget/udc/s3c2410_udc_regs.h
new file mode 100644
index 0000000..d8d2eea
--- /dev/null
+++ b/drivers/usb/gadget/udc/s3c2410_udc_regs.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2004 Herbert Poetzl <herbert@13thfloor.at>
+ */
+
+#ifndef __ASM_ARCH_REGS_UDC_H
+#define __ASM_ARCH_REGS_UDC_H
+
+#define S3C2410_USBDREG(x) (x)
+
+#define S3C2410_UDC_FUNC_ADDR_REG S3C2410_USBDREG(0x0140)
+#define S3C2410_UDC_PWR_REG S3C2410_USBDREG(0x0144)
+#define S3C2410_UDC_EP_INT_REG S3C2410_USBDREG(0x0148)
+
+#define S3C2410_UDC_USB_INT_REG S3C2410_USBDREG(0x0158)
+#define S3C2410_UDC_EP_INT_EN_REG S3C2410_USBDREG(0x015c)
+
+#define S3C2410_UDC_USB_INT_EN_REG S3C2410_USBDREG(0x016c)
+
+#define S3C2410_UDC_FRAME_NUM1_REG S3C2410_USBDREG(0x0170)
+#define S3C2410_UDC_FRAME_NUM2_REG S3C2410_USBDREG(0x0174)
+
+#define S3C2410_UDC_EP0_FIFO_REG S3C2410_USBDREG(0x01c0)
+#define S3C2410_UDC_EP1_FIFO_REG S3C2410_USBDREG(0x01c4)
+#define S3C2410_UDC_EP2_FIFO_REG S3C2410_USBDREG(0x01c8)
+#define S3C2410_UDC_EP3_FIFO_REG S3C2410_USBDREG(0x01cc)
+#define S3C2410_UDC_EP4_FIFO_REG S3C2410_USBDREG(0x01d0)
+
+#define S3C2410_UDC_EP1_DMA_CON S3C2410_USBDREG(0x0200)
+#define S3C2410_UDC_EP1_DMA_UNIT S3C2410_USBDREG(0x0204)
+#define S3C2410_UDC_EP1_DMA_FIFO S3C2410_USBDREG(0x0208)
+#define S3C2410_UDC_EP1_DMA_TTC_L S3C2410_USBDREG(0x020c)
+#define S3C2410_UDC_EP1_DMA_TTC_M S3C2410_USBDREG(0x0210)
+#define S3C2410_UDC_EP1_DMA_TTC_H S3C2410_USBDREG(0x0214)
+
+#define S3C2410_UDC_EP2_DMA_CON S3C2410_USBDREG(0x0218)
+#define S3C2410_UDC_EP2_DMA_UNIT S3C2410_USBDREG(0x021c)
+#define S3C2410_UDC_EP2_DMA_FIFO S3C2410_USBDREG(0x0220)
+#define S3C2410_UDC_EP2_DMA_TTC_L S3C2410_USBDREG(0x0224)
+#define S3C2410_UDC_EP2_DMA_TTC_M S3C2410_USBDREG(0x0228)
+#define S3C2410_UDC_EP2_DMA_TTC_H S3C2410_USBDREG(0x022c)
+
+#define S3C2410_UDC_EP3_DMA_CON S3C2410_USBDREG(0x0240)
+#define S3C2410_UDC_EP3_DMA_UNIT S3C2410_USBDREG(0x0244)
+#define S3C2410_UDC_EP3_DMA_FIFO S3C2410_USBDREG(0x0248)
+#define S3C2410_UDC_EP3_DMA_TTC_L S3C2410_USBDREG(0x024c)
+#define S3C2410_UDC_EP3_DMA_TTC_M S3C2410_USBDREG(0x0250)
+#define S3C2410_UDC_EP3_DMA_TTC_H S3C2410_USBDREG(0x0254)
+
+#define S3C2410_UDC_EP4_DMA_CON S3C2410_USBDREG(0x0258)
+#define S3C2410_UDC_EP4_DMA_UNIT S3C2410_USBDREG(0x025c)
+#define S3C2410_UDC_EP4_DMA_FIFO S3C2410_USBDREG(0x0260)
+#define S3C2410_UDC_EP4_DMA_TTC_L S3C2410_USBDREG(0x0264)
+#define S3C2410_UDC_EP4_DMA_TTC_M S3C2410_USBDREG(0x0268)
+#define S3C2410_UDC_EP4_DMA_TTC_H S3C2410_USBDREG(0x026c)
+
+#define S3C2410_UDC_INDEX_REG S3C2410_USBDREG(0x0178)
+
+/* indexed registers */
+
+#define S3C2410_UDC_MAXP_REG S3C2410_USBDREG(0x0180)
+
+#define S3C2410_UDC_EP0_CSR_REG S3C2410_USBDREG(0x0184)
+
+#define S3C2410_UDC_IN_CSR1_REG S3C2410_USBDREG(0x0184)
+#define S3C2410_UDC_IN_CSR2_REG S3C2410_USBDREG(0x0188)
+
+#define S3C2410_UDC_OUT_CSR1_REG S3C2410_USBDREG(0x0190)
+#define S3C2410_UDC_OUT_CSR2_REG S3C2410_USBDREG(0x0194)
+#define S3C2410_UDC_OUT_FIFO_CNT1_REG S3C2410_USBDREG(0x0198)
+#define S3C2410_UDC_OUT_FIFO_CNT2_REG S3C2410_USBDREG(0x019c)
+
+#define S3C2410_UDC_FUNCADDR_UPDATE (1 << 7)
+
+#define S3C2410_UDC_PWR_ISOUP (1 << 7) /* R/W */
+#define S3C2410_UDC_PWR_RESET (1 << 3) /* R */
+#define S3C2410_UDC_PWR_RESUME (1 << 2) /* R/W */
+#define S3C2410_UDC_PWR_SUSPEND (1 << 1) /* R */
+#define S3C2410_UDC_PWR_ENSUSPEND (1 << 0) /* R/W */
+
+#define S3C2410_UDC_PWR_DEFAULT (0x00)
+
+#define S3C2410_UDC_INT_EP4 (1 << 4) /* R/W (clear only) */
+#define S3C2410_UDC_INT_EP3 (1 << 3) /* R/W (clear only) */
+#define S3C2410_UDC_INT_EP2 (1 << 2) /* R/W (clear only) */
+#define S3C2410_UDC_INT_EP1 (1 << 1) /* R/W (clear only) */
+#define S3C2410_UDC_INT_EP0 (1 << 0) /* R/W (clear only) */
+
+#define S3C2410_UDC_USBINT_RESET (1 << 2) /* R/W (clear only) */
+#define S3C2410_UDC_USBINT_RESUME (1 << 1) /* R/W (clear only) */
+#define S3C2410_UDC_USBINT_SUSPEND (1 << 0) /* R/W (clear only) */
+
+#define S3C2410_UDC_INTE_EP4 (1 << 4) /* R/W */
+#define S3C2410_UDC_INTE_EP3 (1 << 3) /* R/W */
+#define S3C2410_UDC_INTE_EP2 (1 << 2) /* R/W */
+#define S3C2410_UDC_INTE_EP1 (1 << 1) /* R/W */
+#define S3C2410_UDC_INTE_EP0 (1 << 0) /* R/W */
+
+#define S3C2410_UDC_USBINTE_RESET (1 << 2) /* R/W */
+#define S3C2410_UDC_USBINTE_SUSPEND (1 << 0) /* R/W */
+
+#define S3C2410_UDC_INDEX_EP0 (0x00)
+#define S3C2410_UDC_INDEX_EP1 (0x01)
+#define S3C2410_UDC_INDEX_EP2 (0x02)
+#define S3C2410_UDC_INDEX_EP3 (0x03)
+#define S3C2410_UDC_INDEX_EP4 (0x04)
+
+#define S3C2410_UDC_ICSR1_CLRDT (1 << 6) /* R/W */
+#define S3C2410_UDC_ICSR1_SENTSTL (1 << 5) /* R/W (clear only) */
+#define S3C2410_UDC_ICSR1_SENDSTL (1 << 4) /* R/W */
+#define S3C2410_UDC_ICSR1_FFLUSH (1 << 3) /* W (set only) */
+#define S3C2410_UDC_ICSR1_UNDRUN (1 << 2) /* R/W (clear only) */
+#define S3C2410_UDC_ICSR1_PKTRDY (1 << 0) /* R/W (set only) */
+
+#define S3C2410_UDC_ICSR2_AUTOSET (1 << 7) /* R/W */
+#define S3C2410_UDC_ICSR2_ISO (1 << 6) /* R/W */
+#define S3C2410_UDC_ICSR2_MODEIN (1 << 5) /* R/W */
+#define S3C2410_UDC_ICSR2_DMAIEN (1 << 4) /* R/W */
+
+#define S3C2410_UDC_OCSR1_CLRDT (1 << 7) /* R/W */
+#define S3C2410_UDC_OCSR1_SENTSTL (1 << 6) /* R/W (clear only) */
+#define S3C2410_UDC_OCSR1_SENDSTL (1 << 5) /* R/W */
+#define S3C2410_UDC_OCSR1_FFLUSH (1 << 4) /* R/W */
+#define S3C2410_UDC_OCSR1_DERROR (1 << 3) /* R */
+#define S3C2410_UDC_OCSR1_OVRRUN (1 << 2) /* R/W (clear only) */
+#define S3C2410_UDC_OCSR1_PKTRDY (1 << 0) /* R/W (clear only) */
+
+#define S3C2410_UDC_OCSR2_AUTOCLR (1 << 7) /* R/W */
+#define S3C2410_UDC_OCSR2_ISO (1 << 6) /* R/W */
+#define S3C2410_UDC_OCSR2_DMAIEN (1 << 5) /* R/W */
+
+#define S3C2410_UDC_EP0_CSR_OPKRDY (1 << 0)
+#define S3C2410_UDC_EP0_CSR_IPKRDY (1 << 1)
+#define S3C2410_UDC_EP0_CSR_SENTSTL (1 << 2)
+#define S3C2410_UDC_EP0_CSR_DE (1 << 3)
+#define S3C2410_UDC_EP0_CSR_SE (1 << 4)
+#define S3C2410_UDC_EP0_CSR_SENDSTL (1 << 5)
+#define S3C2410_UDC_EP0_CSR_SOPKTRDY (1 << 6)
+#define S3C2410_UDC_EP0_CSR_SSE (1 << 7)
+
+#define S3C2410_UDC_MAXP_8 (1 << 0)
+#define S3C2410_UDC_MAXP_16 (1 << 1)
+#define S3C2410_UDC_MAXP_32 (1 << 2)
+#define S3C2410_UDC_MAXP_64 (1 << 3)
+
+#endif
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 3fcded3..6c726d2 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -2,7 +2,7 @@
/*
* amd5536.c -- AMD 5536 UDC high/full speed USB device controller
*
- * Copyright (C) 2005-2007 AMD (http://www.amd.com)
+ * Copyright (C) 2005-2007 AMD (https://www.amd.com)
* Author: Thomas Dahlmann
*/
@@ -96,9 +96,7 @@
static DECLARE_COMPLETION(on_pollstall_exit);
/* tasklet for usb disconnect */
-static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,
- (unsigned long) &udc);
-
+static DECLARE_TASKLET_OLD(disconnect_tasklet, udc_tasklet_disconnect);
/* endpoint names used for print */
static const char ep0_string[] = "ep0in";
@@ -1661,7 +1659,7 @@
/* Tasklet for disconnect to be outside of interrupt context */
static void udc_tasklet_disconnect(unsigned long par)
{
- struct udc *dev = (struct udc *)(*((struct udc **) par));
+ struct udc *dev = udc;
u32 tmp;
DBG(dev, "Tasklet disconnect\n");
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
new file mode 100644
index 0000000..57ee72f
--- /dev/null
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -0,0 +1,4050 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NVIDIA Tegra XUSB device mode controller
+ *
+ * Copyright (c) 2013-2019, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2015, Google Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/tegra/xusb.h>
+#include <linux/pm_domain.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
+#include <linux/usb/phy.h>
+#include <linux/workqueue.h>
+
+/* XUSB_DEV registers */
+#define SPARAM 0x000
+#define SPARAM_ERSTMAX_MASK GENMASK(20, 16)
+#define SPARAM_ERSTMAX(x) (((x) << 16) & SPARAM_ERSTMAX_MASK)
+#define DB 0x004
+#define DB_TARGET_MASK GENMASK(15, 8)
+#define DB_TARGET(x) (((x) << 8) & DB_TARGET_MASK)
+#define DB_STREAMID_MASK GENMASK(31, 16)
+#define DB_STREAMID(x) (((x) << 16) & DB_STREAMID_MASK)
+#define ERSTSZ 0x008
+#define ERSTSZ_ERSTXSZ_SHIFT(x) ((x) * 16)
+#define ERSTSZ_ERSTXSZ_MASK GENMASK(15, 0)
+#define ERSTXBALO(x) (0x010 + 8 * (x))
+#define ERSTXBAHI(x) (0x014 + 8 * (x))
+#define ERDPLO 0x020
+#define ERDPLO_EHB BIT(3)
+#define ERDPHI 0x024
+#define EREPLO 0x028
+#define EREPLO_ECS BIT(0)
+#define EREPLO_SEGI BIT(1)
+#define EREPHI 0x02c
+#define CTRL 0x030
+#define CTRL_RUN BIT(0)
+#define CTRL_LSE BIT(1)
+#define CTRL_IE BIT(4)
+#define CTRL_SMI_EVT BIT(5)
+#define CTRL_SMI_DSE BIT(6)
+#define CTRL_EWE BIT(7)
+#define CTRL_DEVADDR_MASK GENMASK(30, 24)
+#define CTRL_DEVADDR(x) (((x) << 24) & CTRL_DEVADDR_MASK)
+#define CTRL_ENABLE BIT(31)
+#define ST 0x034
+#define ST_RC BIT(0)
+#define ST_IP BIT(4)
+#define RT_IMOD 0x038
+#define RT_IMOD_IMODI_MASK GENMASK(15, 0)
+#define RT_IMOD_IMODI(x) ((x) & RT_IMOD_IMODI_MASK)
+#define RT_IMOD_IMODC_MASK GENMASK(31, 16)
+#define RT_IMOD_IMODC(x) (((x) << 16) & RT_IMOD_IMODC_MASK)
+#define PORTSC 0x03c
+#define PORTSC_CCS BIT(0)
+#define PORTSC_PED BIT(1)
+#define PORTSC_PR BIT(4)
+#define PORTSC_PLS_SHIFT 5
+#define PORTSC_PLS_MASK GENMASK(8, 5)
+#define PORTSC_PLS_U0 0x0
+#define PORTSC_PLS_U2 0x2
+#define PORTSC_PLS_U3 0x3
+#define PORTSC_PLS_DISABLED 0x4
+#define PORTSC_PLS_RXDETECT 0x5
+#define PORTSC_PLS_INACTIVE 0x6
+#define PORTSC_PLS_RESUME 0xf
+#define PORTSC_PLS(x) (((x) << PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK)
+#define PORTSC_PS_SHIFT 10
+#define PORTSC_PS_MASK GENMASK(13, 10)
+#define PORTSC_PS_UNDEFINED 0x0
+#define PORTSC_PS_FS 0x1
+#define PORTSC_PS_LS 0x2
+#define PORTSC_PS_HS 0x3
+#define PORTSC_PS_SS 0x4
+#define PORTSC_LWS BIT(16)
+#define PORTSC_CSC BIT(17)
+#define PORTSC_WRC BIT(19)
+#define PORTSC_PRC BIT(21)
+#define PORTSC_PLC BIT(22)
+#define PORTSC_CEC BIT(23)
+#define PORTSC_WPR BIT(30)
+#define PORTSC_CHANGE_MASK (PORTSC_CSC | PORTSC_WRC | PORTSC_PRC | \
+ PORTSC_PLC | PORTSC_CEC)
+#define ECPLO 0x040
+#define ECPHI 0x044
+#define MFINDEX 0x048
+#define MFINDEX_FRAME_SHIFT 3
+#define MFINDEX_FRAME_MASK GENMASK(13, 3)
+#define PORTPM 0x04c
+#define PORTPM_L1S_MASK GENMASK(1, 0)
+#define PORTPM_L1S_DROP 0x0
+#define PORTPM_L1S_ACCEPT 0x1
+#define PORTPM_L1S_NYET 0x2
+#define PORTPM_L1S_STALL 0x3
+#define PORTPM_L1S(x) ((x) & PORTPM_L1S_MASK)
+#define PORTPM_RWE BIT(3)
+#define PORTPM_U2TIMEOUT_MASK GENMASK(15, 8)
+#define PORTPM_U1TIMEOUT_MASK GENMASK(23, 16)
+#define PORTPM_FLA BIT(24)
+#define PORTPM_VBA BIT(25)
+#define PORTPM_WOC BIT(26)
+#define PORTPM_WOD BIT(27)
+#define PORTPM_U1E BIT(28)
+#define PORTPM_U2E BIT(29)
+#define PORTPM_FRWE BIT(30)
+#define PORTPM_PNG_CYA BIT(31)
+#define EP_HALT 0x050
+#define EP_PAUSE 0x054
+#define EP_RELOAD 0x058
+#define EP_STCHG 0x05c
+#define DEVNOTIF_LO 0x064
+#define DEVNOTIF_LO_TRIG BIT(0)
+#define DEVNOTIF_LO_TYPE_MASK GENMASK(7, 4)
+#define DEVNOTIF_LO_TYPE(x) (((x) << 4) & DEVNOTIF_LO_TYPE_MASK)
+#define DEVNOTIF_LO_TYPE_FUNCTION_WAKE 0x1
+#define DEVNOTIF_HI 0x068
+#define PORTHALT 0x06c
+#define PORTHALT_HALT_LTSSM BIT(0)
+#define PORTHALT_HALT_REJECT BIT(1)
+#define PORTHALT_STCHG_REQ BIT(20)
+#define PORTHALT_STCHG_INTR_EN BIT(24)
+#define PORT_TM 0x070
+#define EP_THREAD_ACTIVE 0x074
+#define EP_STOPPED 0x078
+#define HSFSPI_COUNT0 0x100
+#define HSFSPI_COUNT13 0x134
+#define HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK GENMASK(29, 0)
+#define HSFSPI_COUNT13_U2_RESUME_K_DURATION(x) ((x) & \
+ HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK)
+#define BLCG 0x840
+#define SSPX_CORE_CNT0 0x610
+#define SSPX_CORE_CNT0_PING_TBURST_MASK GENMASK(7, 0)
+#define SSPX_CORE_CNT0_PING_TBURST(x) ((x) & SSPX_CORE_CNT0_PING_TBURST_MASK)
+#define SSPX_CORE_CNT30 0x688
+#define SSPX_CORE_CNT30_LMPITP_TIMER_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT30_LMPITP_TIMER(x) ((x) & \
+ SSPX_CORE_CNT30_LMPITP_TIMER_MASK)
+#define SSPX_CORE_CNT32 0x690
+#define SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK GENMASK(7, 0)
+#define SSPX_CORE_CNT32_POLL_TBURST_MAX(x) ((x) & \
+ SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK)
+#define SSPX_CORE_CNT56 0x6fc
+#define SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(x) ((x) & \
+ SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK)
+#define SSPX_CORE_CNT57 0x700
+#define SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(x) ((x) & \
+ SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK)
+#define SSPX_CORE_CNT65 0x720
+#define SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(x) ((x) & \
+ SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK)
+#define SSPX_CORE_CNT66 0x724
+#define SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(x) ((x) & \
+ SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK)
+#define SSPX_CORE_CNT67 0x728
+#define SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(x) ((x) & \
+ SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK)
+#define SSPX_CORE_CNT72 0x73c
+#define SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK GENMASK(19, 0)
+#define SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(x) ((x) & \
+ SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK)
+#define SSPX_CORE_PADCTL4 0x750
+#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK GENMASK(19, 0)
+#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(x) ((x) & \
+ SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK)
+#define BLCG_DFPCI BIT(0)
+#define BLCG_UFPCI BIT(1)
+#define BLCG_FE BIT(2)
+#define BLCG_COREPLL_PWRDN BIT(8)
+#define BLCG_IOPLL_0_PWRDN BIT(9)
+#define BLCG_IOPLL_1_PWRDN BIT(10)
+#define BLCG_IOPLL_2_PWRDN BIT(11)
+#define BLCG_ALL 0x1ff
+#define CFG_DEV_SSPI_XFER 0x858
+#define CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK GENMASK(31, 0)
+#define CFG_DEV_SSPI_XFER_ACKTIMEOUT(x) ((x) & \
+ CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK)
+#define CFG_DEV_FE 0x85c
+#define CFG_DEV_FE_PORTREGSEL_MASK GENMASK(1, 0)
+#define CFG_DEV_FE_PORTREGSEL_SS_PI 1
+#define CFG_DEV_FE_PORTREGSEL_HSFS_PI 2
+#define CFG_DEV_FE_PORTREGSEL(x) ((x) & CFG_DEV_FE_PORTREGSEL_MASK)
+#define CFG_DEV_FE_INFINITE_SS_RETRY BIT(29)
+
+/* FPCI registers */
+#define XUSB_DEV_CFG_1 0x004
+#define XUSB_DEV_CFG_1_IO_SPACE_EN BIT(0)
+#define XUSB_DEV_CFG_1_MEMORY_SPACE_EN BIT(1)
+#define XUSB_DEV_CFG_1_BUS_MASTER_EN BIT(2)
+#define XUSB_DEV_CFG_4 0x010
+#define XUSB_DEV_CFG_4_BASE_ADDR_MASK GENMASK(31, 15)
+#define XUSB_DEV_CFG_5 0x014
+
+/* IPFS registers */
+#define XUSB_DEV_CONFIGURATION_0 0x180
+#define XUSB_DEV_CONFIGURATION_0_EN_FPCI BIT(0)
+#define XUSB_DEV_INTR_MASK_0 0x188
+#define XUSB_DEV_INTR_MASK_0_IP_INT_MASK BIT(16)
+
+struct tegra_xudc_ep_context {
+ __le32 info0;
+ __le32 info1;
+ __le32 deq_lo;
+ __le32 deq_hi;
+ __le32 tx_info;
+ __le32 rsvd[11];
+};
+
+#define EP_STATE_DISABLED 0
+#define EP_STATE_RUNNING 1
+#define EP_STATE_HALTED 2
+#define EP_STATE_STOPPED 3
+#define EP_STATE_ERROR 4
+
+#define EP_TYPE_INVALID 0
+#define EP_TYPE_ISOCH_OUT 1
+#define EP_TYPE_BULK_OUT 2
+#define EP_TYPE_INTERRUPT_OUT 3
+#define EP_TYPE_CONTROL 4
+#define EP_TYPE_ISCOH_IN 5
+#define EP_TYPE_BULK_IN 6
+#define EP_TYPE_INTERRUPT_IN 7
+
+#define BUILD_EP_CONTEXT_RW(name, member, shift, mask) \
+static inline u32 ep_ctx_read_##name(struct tegra_xudc_ep_context *ctx) \
+{ \
+ return (le32_to_cpu(ctx->member) >> (shift)) & (mask); \
+} \
+static inline void \
+ep_ctx_write_##name(struct tegra_xudc_ep_context *ctx, u32 val) \
+{ \
+ u32 tmp; \
+ \
+ tmp = le32_to_cpu(ctx->member) & ~((mask) << (shift)); \
+ tmp |= (val & (mask)) << (shift); \
+ ctx->member = cpu_to_le32(tmp); \
+}
+
+BUILD_EP_CONTEXT_RW(state, info0, 0, 0x7)
+BUILD_EP_CONTEXT_RW(mult, info0, 8, 0x3)
+BUILD_EP_CONTEXT_RW(max_pstreams, info0, 10, 0x1f)
+BUILD_EP_CONTEXT_RW(lsa, info0, 15, 0x1)
+BUILD_EP_CONTEXT_RW(interval, info0, 16, 0xff)
+BUILD_EP_CONTEXT_RW(cerr, info1, 1, 0x3)
+BUILD_EP_CONTEXT_RW(type, info1, 3, 0x7)
+BUILD_EP_CONTEXT_RW(hid, info1, 7, 0x1)
+BUILD_EP_CONTEXT_RW(max_burst_size, info1, 8, 0xff)
+BUILD_EP_CONTEXT_RW(max_packet_size, info1, 16, 0xffff)
+BUILD_EP_CONTEXT_RW(dcs, deq_lo, 0, 0x1)
+BUILD_EP_CONTEXT_RW(deq_lo, deq_lo, 4, 0xfffffff)
+BUILD_EP_CONTEXT_RW(deq_hi, deq_hi, 0, 0xffffffff)
+BUILD_EP_CONTEXT_RW(avg_trb_len, tx_info, 0, 0xffff)
+BUILD_EP_CONTEXT_RW(max_esit_payload, tx_info, 16, 0xffff)
+BUILD_EP_CONTEXT_RW(edtla, rsvd[0], 0, 0xffffff)
+BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 24, 0xff)
+BUILD_EP_CONTEXT_RW(partial_td, rsvd[0], 25, 0x1)
+BUILD_EP_CONTEXT_RW(cerrcnt, rsvd[1], 18, 0x3)
+BUILD_EP_CONTEXT_RW(data_offset, rsvd[2], 0, 0x1ffff)
+BUILD_EP_CONTEXT_RW(numtrbs, rsvd[2], 22, 0x1f)
+BUILD_EP_CONTEXT_RW(devaddr, rsvd[6], 0, 0x7f)
+
+static inline u64 ep_ctx_read_deq_ptr(struct tegra_xudc_ep_context *ctx)
+{
+ return ((u64)ep_ctx_read_deq_hi(ctx) << 32) |
+ (ep_ctx_read_deq_lo(ctx) << 4);
+}
+
+static inline void
+ep_ctx_write_deq_ptr(struct tegra_xudc_ep_context *ctx, u64 addr)
+{
+ ep_ctx_write_deq_lo(ctx, lower_32_bits(addr) >> 4);
+ ep_ctx_write_deq_hi(ctx, upper_32_bits(addr));
+}
+
+struct tegra_xudc_trb {
+ __le32 data_lo;
+ __le32 data_hi;
+ __le32 status;
+ __le32 control;
+};
+
+#define TRB_TYPE_RSVD 0
+#define TRB_TYPE_NORMAL 1
+#define TRB_TYPE_SETUP_STAGE 2
+#define TRB_TYPE_DATA_STAGE 3
+#define TRB_TYPE_STATUS_STAGE 4
+#define TRB_TYPE_ISOCH 5
+#define TRB_TYPE_LINK 6
+#define TRB_TYPE_TRANSFER_EVENT 32
+#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34
+#define TRB_TYPE_STREAM 48
+#define TRB_TYPE_SETUP_PACKET_EVENT 63
+
+#define TRB_CMPL_CODE_INVALID 0
+#define TRB_CMPL_CODE_SUCCESS 1
+#define TRB_CMPL_CODE_DATA_BUFFER_ERR 2
+#define TRB_CMPL_CODE_BABBLE_DETECTED_ERR 3
+#define TRB_CMPL_CODE_USB_TRANS_ERR 4
+#define TRB_CMPL_CODE_TRB_ERR 5
+#define TRB_CMPL_CODE_STALL 6
+#define TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR 10
+#define TRB_CMPL_CODE_SHORT_PACKET 13
+#define TRB_CMPL_CODE_RING_UNDERRUN 14
+#define TRB_CMPL_CODE_RING_OVERRUN 15
+#define TRB_CMPL_CODE_EVENT_RING_FULL_ERR 21
+#define TRB_CMPL_CODE_STOPPED 26
+#define TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN 31
+#define TRB_CMPL_CODE_STREAM_NUMP_ERROR 219
+#define TRB_CMPL_CODE_PRIME_PIPE_RECEIVED 220
+#define TRB_CMPL_CODE_HOST_REJECTED 221
+#define TRB_CMPL_CODE_CTRL_DIR_ERR 222
+#define TRB_CMPL_CODE_CTRL_SEQNUM_ERR 223
+
+#define BUILD_TRB_RW(name, member, shift, mask) \
+static inline u32 trb_read_##name(struct tegra_xudc_trb *trb) \
+{ \
+ return (le32_to_cpu(trb->member) >> (shift)) & (mask); \
+} \
+static inline void \
+trb_write_##name(struct tegra_xudc_trb *trb, u32 val) \
+{ \
+ u32 tmp; \
+ \
+ tmp = le32_to_cpu(trb->member) & ~((mask) << (shift)); \
+ tmp |= (val & (mask)) << (shift); \
+ trb->member = cpu_to_le32(tmp); \
+}
+
+BUILD_TRB_RW(data_lo, data_lo, 0, 0xffffffff)
+BUILD_TRB_RW(data_hi, data_hi, 0, 0xffffffff)
+BUILD_TRB_RW(seq_num, status, 0, 0xffff)
+BUILD_TRB_RW(transfer_len, status, 0, 0xffffff)
+BUILD_TRB_RW(td_size, status, 17, 0x1f)
+BUILD_TRB_RW(cmpl_code, status, 24, 0xff)
+BUILD_TRB_RW(cycle, control, 0, 0x1)
+BUILD_TRB_RW(toggle_cycle, control, 1, 0x1)
+BUILD_TRB_RW(isp, control, 2, 0x1)
+BUILD_TRB_RW(chain, control, 4, 0x1)
+BUILD_TRB_RW(ioc, control, 5, 0x1)
+BUILD_TRB_RW(type, control, 10, 0x3f)
+BUILD_TRB_RW(stream_id, control, 16, 0xffff)
+BUILD_TRB_RW(endpoint_id, control, 16, 0x1f)
+BUILD_TRB_RW(tlbpc, control, 16, 0xf)
+BUILD_TRB_RW(data_stage_dir, control, 16, 0x1)
+BUILD_TRB_RW(frame_id, control, 20, 0x7ff)
+BUILD_TRB_RW(sia, control, 31, 0x1)
+
+static inline u64 trb_read_data_ptr(struct tegra_xudc_trb *trb)
+{
+ return ((u64)trb_read_data_hi(trb) << 32) |
+ trb_read_data_lo(trb);
+}
+
+static inline void trb_write_data_ptr(struct tegra_xudc_trb *trb, u64 addr)
+{
+ trb_write_data_lo(trb, lower_32_bits(addr));
+ trb_write_data_hi(trb, upper_32_bits(addr));
+}
+
+struct tegra_xudc_request {
+ struct usb_request usb_req;
+
+ size_t buf_queued;
+ unsigned int trbs_queued;
+ unsigned int trbs_needed;
+ bool need_zlp;
+
+ struct tegra_xudc_trb *first_trb;
+ struct tegra_xudc_trb *last_trb;
+
+ struct list_head list;
+};
+
+struct tegra_xudc_ep {
+ struct tegra_xudc *xudc;
+ struct usb_ep usb_ep;
+ unsigned int index;
+ char name[8];
+
+ struct tegra_xudc_ep_context *context;
+
+#define XUDC_TRANSFER_RING_SIZE 64
+ struct tegra_xudc_trb *transfer_ring;
+ dma_addr_t transfer_ring_phys;
+
+ unsigned int enq_ptr;
+ unsigned int deq_ptr;
+ bool pcs;
+ bool ring_full;
+ bool stream_rejected;
+
+ struct list_head queue;
+ const struct usb_endpoint_descriptor *desc;
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+};
+
+struct tegra_xudc_sel_timing {
+ __u8 u1sel;
+ __u8 u1pel;
+ __le16 u2sel;
+ __le16 u2pel;
+};
+
+enum tegra_xudc_setup_state {
+ WAIT_FOR_SETUP,
+ DATA_STAGE_XFER,
+ DATA_STAGE_RECV,
+ STATUS_STAGE_XFER,
+ STATUS_STAGE_RECV,
+};
+
+struct tegra_xudc_setup_packet {
+ struct usb_ctrlrequest ctrl_req;
+ unsigned int seq_num;
+};
+
+struct tegra_xudc_save_regs {
+ u32 ctrl;
+ u32 portpm;
+};
+
+struct tegra_xudc {
+ struct device *dev;
+ const struct tegra_xudc_soc *soc;
+ struct tegra_xusb_padctl *padctl;
+
+ spinlock_t lock;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+#define XUDC_NR_EVENT_RINGS 2
+#define XUDC_EVENT_RING_SIZE 4096
+ struct tegra_xudc_trb *event_ring[XUDC_NR_EVENT_RINGS];
+ dma_addr_t event_ring_phys[XUDC_NR_EVENT_RINGS];
+ unsigned int event_ring_index;
+ unsigned int event_ring_deq_ptr;
+ bool ccs;
+
+#define XUDC_NR_EPS 32
+ struct tegra_xudc_ep ep[XUDC_NR_EPS];
+ struct tegra_xudc_ep_context *ep_context;
+ dma_addr_t ep_context_phys;
+
+ struct device *genpd_dev_device;
+ struct device *genpd_dev_ss;
+ struct device_link *genpd_dl_device;
+ struct device_link *genpd_dl_ss;
+
+ struct dma_pool *transfer_ring_pool;
+
+ bool queued_setup_packet;
+ struct tegra_xudc_setup_packet setup_packet;
+ enum tegra_xudc_setup_state setup_state;
+ u16 setup_seq_num;
+
+ u16 dev_addr;
+ u16 isoch_delay;
+ struct tegra_xudc_sel_timing sel_timing;
+ u8 test_mode_pattern;
+ u16 status_buf;
+ struct tegra_xudc_request *ep0_req;
+
+ bool pullup;
+
+ unsigned int nr_enabled_eps;
+ unsigned int nr_isoch_eps;
+
+ unsigned int device_state;
+ unsigned int resume_state;
+
+ int irq;
+
+ void __iomem *base;
+ resource_size_t phys_base;
+ void __iomem *ipfs;
+ void __iomem *fpci;
+
+ struct regulator_bulk_data *supplies;
+
+ struct clk_bulk_data *clks;
+
+ bool device_mode;
+ struct work_struct usb_role_sw_work;
+
+ struct phy **usb3_phy;
+ struct phy *curr_usb3_phy;
+ struct phy **utmi_phy;
+ struct phy *curr_utmi_phy;
+
+ struct tegra_xudc_save_regs saved_regs;
+ bool suspended;
+ bool powergated;
+
+ struct usb_phy **usbphy;
+ struct usb_phy *curr_usbphy;
+ struct notifier_block vbus_nb;
+
+ struct completion disconnect_complete;
+
+ bool selfpowered;
+
+#define TOGGLE_VBUS_WAIT_MS 100
+ struct delayed_work plc_reset_work;
+ bool wait_csc;
+
+ struct delayed_work port_reset_war_work;
+ bool wait_for_sec_prc;
+};
+
+#define XUDC_TRB_MAX_BUFFER_SIZE 65536
+#define XUDC_MAX_ISOCH_EPS 4
+#define XUDC_INTERRUPT_MODERATION_US 0
+
+static struct usb_endpoint_descriptor tegra_xudc_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = cpu_to_le16(64),
+};
+
+struct tegra_xudc_soc {
+ const char * const *supply_names;
+ unsigned int num_supplies;
+ const char * const *clock_names;
+ unsigned int num_clks;
+ unsigned int num_phys;
+ bool u1_enable;
+ bool u2_enable;
+ bool lpm_enable;
+ bool invalid_seq_num;
+ bool pls_quirk;
+ bool port_reset_quirk;
+ bool port_speed_quirk;
+ bool has_ipfs;
+};
+
+static inline u32 fpci_readl(struct tegra_xudc *xudc, unsigned int offset)
+{
+ return readl(xudc->fpci + offset);
+}
+
+static inline void fpci_writel(struct tegra_xudc *xudc, u32 val,
+ unsigned int offset)
+{
+ writel(val, xudc->fpci + offset);
+}
+
+static inline u32 ipfs_readl(struct tegra_xudc *xudc, unsigned int offset)
+{
+ return readl(xudc->ipfs + offset);
+}
+
+static inline void ipfs_writel(struct tegra_xudc *xudc, u32 val,
+ unsigned int offset)
+{
+ writel(val, xudc->ipfs + offset);
+}
+
+static inline u32 xudc_readl(struct tegra_xudc *xudc, unsigned int offset)
+{
+ return readl(xudc->base + offset);
+}
+
+static inline void xudc_writel(struct tegra_xudc *xudc, u32 val,
+ unsigned int offset)
+{
+ writel(val, xudc->base + offset);
+}
+
+static inline int xudc_readl_poll(struct tegra_xudc *xudc,
+ unsigned int offset, u32 mask, u32 val)
+{
+ u32 regval;
+
+ return readl_poll_timeout_atomic(xudc->base + offset, regval,
+ (regval & mask) == val, 1, 100);
+}
+
+static inline struct tegra_xudc *to_xudc(struct usb_gadget *gadget)
+{
+ return container_of(gadget, struct tegra_xudc, gadget);
+}
+
+static inline struct tegra_xudc_ep *to_xudc_ep(struct usb_ep *ep)
+{
+ return container_of(ep, struct tegra_xudc_ep, usb_ep);
+}
+
+static inline struct tegra_xudc_request *to_xudc_req(struct usb_request *req)
+{
+ return container_of(req, struct tegra_xudc_request, usb_req);
+}
+
+static inline void dump_trb(struct tegra_xudc *xudc, const char *type,
+ struct tegra_xudc_trb *trb)
+{
+ dev_dbg(xudc->dev,
+ "%s: %p, lo = %#x, hi = %#x, status = %#x, control = %#x\n",
+ type, trb, trb->data_lo, trb->data_hi, trb->status,
+ trb->control);
+}
+
+static void tegra_xudc_limit_port_speed(struct tegra_xudc *xudc)
+{
+ u32 val;
+
+ /* limit port speed to gen 1 */
+ val = xudc_readl(xudc, SSPX_CORE_CNT56);
+ val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK);
+ val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x260);
+ xudc_writel(xudc, val, SSPX_CORE_CNT56);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT57);
+ val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK);
+ val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x6D6);
+ xudc_writel(xudc, val, SSPX_CORE_CNT57);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT65);
+ val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0x4B0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT66);
+ val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x4B0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT67);
+ val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x4B0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT67);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT72);
+ val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK);
+ val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x10);
+ xudc_writel(xudc, val, SSPX_CORE_CNT72);
+}
+
+static void tegra_xudc_restore_port_speed(struct tegra_xudc *xudc)
+{
+ u32 val;
+
+ /* restore port speed to gen2 */
+ val = xudc_readl(xudc, SSPX_CORE_CNT56);
+ val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK);
+ val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x438);
+ xudc_writel(xudc, val, SSPX_CORE_CNT56);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT57);
+ val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK);
+ val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x528);
+ xudc_writel(xudc, val, SSPX_CORE_CNT57);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT65);
+ val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0xE10);
+ xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT66);
+ val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x348);
+ xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT67);
+ val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK);
+ val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x5a0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT67);
+
+ val = xudc_readl(xudc, SSPX_CORE_CNT72);
+ val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK);
+ val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x1c21);
+ xudc_writel(xudc, val, SSPX_CORE_CNT72);
+}
+
+static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
+{
+ int err;
+
+ pm_runtime_get_sync(xudc->dev);
+
+ err = phy_power_on(xudc->curr_utmi_phy);
+ if (err < 0)
+ dev_err(xudc->dev, "UTMI power on failed: %d\n", err);
+
+ err = phy_power_on(xudc->curr_usb3_phy);
+ if (err < 0)
+ dev_err(xudc->dev, "USB3 PHY power on failed: %d\n", err);
+
+ dev_dbg(xudc->dev, "device mode on\n");
+
+ phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
+ USB_ROLE_DEVICE);
+}
+
+static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
+{
+ bool connected = false;
+ u32 pls, val;
+ int err;
+
+ dev_dbg(xudc->dev, "device mode off\n");
+
+ connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS);
+
+ reinit_completion(&xudc->disconnect_complete);
+
+ if (xudc->soc->port_speed_quirk)
+ tegra_xudc_restore_port_speed(xudc);
+
+ phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
+
+ pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
+ PORTSC_PLS_SHIFT;
+
+ /* Direct link to U0 if disconnected in RESUME or U2. */
+ if (xudc->soc->pls_quirk && xudc->gadget.speed == USB_SPEED_SUPER &&
+ (pls == PORTSC_PLS_RESUME || pls == PORTSC_PLS_U2)) {
+ val = xudc_readl(xudc, PORTPM);
+ val |= PORTPM_FRWE;
+ xudc_writel(xudc, val, PORTPM);
+
+ val = xudc_readl(xudc, PORTSC);
+ val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK);
+ val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0);
+ xudc_writel(xudc, val, PORTSC);
+ }
+
+ /* Wait for disconnect event. */
+ if (connected)
+ wait_for_completion(&xudc->disconnect_complete);
+
+ /* Make sure interrupt handler has completed before powergating. */
+ synchronize_irq(xudc->irq);
+
+ err = phy_power_off(xudc->curr_utmi_phy);
+ if (err < 0)
+ dev_err(xudc->dev, "UTMI PHY power off failed: %d\n", err);
+
+ err = phy_power_off(xudc->curr_usb3_phy);
+ if (err < 0)
+ dev_err(xudc->dev, "USB3 PHY power off failed: %d\n", err);
+
+ pm_runtime_put(xudc->dev);
+}
+
+static void tegra_xudc_usb_role_sw_work(struct work_struct *work)
+{
+ struct tegra_xudc *xudc = container_of(work, struct tegra_xudc,
+ usb_role_sw_work);
+
+ if (xudc->device_mode)
+ tegra_xudc_device_mode_on(xudc);
+ else
+ tegra_xudc_device_mode_off(xudc);
+}
+
+static int tegra_xudc_get_phy_index(struct tegra_xudc *xudc,
+ struct usb_phy *usbphy)
+{
+ unsigned int i;
+
+ for (i = 0; i < xudc->soc->num_phys; i++) {
+ if (xudc->usbphy[i] && usbphy == xudc->usbphy[i])
+ return i;
+ }
+
+ dev_info(xudc->dev, "phy index could not be found for shared USB PHY");
+ return -1;
+}
+
+static int tegra_xudc_vbus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc,
+ vbus_nb);
+ struct usb_phy *usbphy = (struct usb_phy *)data;
+ int phy_index;
+
+ dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event);
+
+ if ((xudc->device_mode && usbphy->last_event == USB_EVENT_VBUS) ||
+ (!xudc->device_mode && usbphy->last_event != USB_EVENT_VBUS)) {
+ dev_dbg(xudc->dev, "Same role(%d) received. Ignore",
+ xudc->device_mode);
+ return NOTIFY_OK;
+ }
+
+ xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true :
+ false;
+
+ phy_index = tegra_xudc_get_phy_index(xudc, usbphy);
+ dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__,
+ phy_index);
+
+ if (!xudc->suspended && phy_index != -1) {
+ xudc->curr_utmi_phy = xudc->utmi_phy[phy_index];
+ xudc->curr_usb3_phy = xudc->usb3_phy[phy_index];
+ xudc->curr_usbphy = usbphy;
+ schedule_work(&xudc->usb_role_sw_work);
+ }
+
+ return NOTIFY_OK;
+}
+
+static void tegra_xudc_plc_reset_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct tegra_xudc *xudc = container_of(dwork, struct tegra_xudc,
+ plc_reset_work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+
+ if (xudc->wait_csc) {
+ u32 pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
+ PORTSC_PLS_SHIFT;
+
+ if (pls == PORTSC_PLS_INACTIVE) {
+ dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n");
+ phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
+ USB_ROLE_NONE);
+ phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
+ USB_ROLE_DEVICE);
+
+ xudc->wait_csc = false;
+ }
+ }
+
+ spin_unlock_irqrestore(&xudc->lock, flags);
+}
+
+static void tegra_xudc_port_reset_war_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct tegra_xudc *xudc =
+ container_of(dwork, struct tegra_xudc, port_reset_war_work);
+ unsigned long flags;
+ u32 pls;
+ int ret;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+
+ if (xudc->device_mode && xudc->wait_for_sec_prc) {
+ pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
+ PORTSC_PLS_SHIFT;
+ dev_dbg(xudc->dev, "pls = %x\n", pls);
+
+ if (pls == PORTSC_PLS_DISABLED) {
+ dev_dbg(xudc->dev, "toggle vbus\n");
+ /* PRC doesn't complete in 100ms, toggle the vbus */
+ ret = tegra_phy_xusb_utmi_port_reset(
+ xudc->curr_utmi_phy);
+ if (ret == 1)
+ xudc->wait_for_sec_prc = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&xudc->lock, flags);
+}
+
+static dma_addr_t trb_virt_to_phys(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_trb *trb)
+{
+ unsigned int index;
+
+ index = trb - ep->transfer_ring;
+
+ if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE))
+ return 0;
+
+ return (ep->transfer_ring_phys + index * sizeof(*trb));
+}
+
+static struct tegra_xudc_trb *trb_phys_to_virt(struct tegra_xudc_ep *ep,
+ dma_addr_t addr)
+{
+ struct tegra_xudc_trb *trb;
+ unsigned int index;
+
+ index = (addr - ep->transfer_ring_phys) / sizeof(*trb);
+
+ if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE))
+ return NULL;
+
+ trb = &ep->transfer_ring[index];
+
+ return trb;
+}
+
+static void ep_reload(struct tegra_xudc *xudc, unsigned int ep)
+{
+ xudc_writel(xudc, BIT(ep), EP_RELOAD);
+ xudc_readl_poll(xudc, EP_RELOAD, BIT(ep), 0);
+}
+
+static void ep_pause(struct tegra_xudc *xudc, unsigned int ep)
+{
+ u32 val;
+
+ val = xudc_readl(xudc, EP_PAUSE);
+ if (val & BIT(ep))
+ return;
+ val |= BIT(ep);
+
+ xudc_writel(xudc, val, EP_PAUSE);
+
+ xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep));
+
+ xudc_writel(xudc, BIT(ep), EP_STCHG);
+}
+
+static void ep_unpause(struct tegra_xudc *xudc, unsigned int ep)
+{
+ u32 val;
+
+ val = xudc_readl(xudc, EP_PAUSE);
+ if (!(val & BIT(ep)))
+ return;
+ val &= ~BIT(ep);
+
+ xudc_writel(xudc, val, EP_PAUSE);
+
+ xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep));
+
+ xudc_writel(xudc, BIT(ep), EP_STCHG);
+}
+
+static void ep_unpause_all(struct tegra_xudc *xudc)
+{
+ u32 val;
+
+ val = xudc_readl(xudc, EP_PAUSE);
+
+ xudc_writel(xudc, 0, EP_PAUSE);
+
+ xudc_readl_poll(xudc, EP_STCHG, val, val);
+
+ xudc_writel(xudc, val, EP_STCHG);
+}
+
+static void ep_halt(struct tegra_xudc *xudc, unsigned int ep)
+{
+ u32 val;
+
+ val = xudc_readl(xudc, EP_HALT);
+ if (val & BIT(ep))
+ return;
+ val |= BIT(ep);
+ xudc_writel(xudc, val, EP_HALT);
+
+ xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep));
+
+ xudc_writel(xudc, BIT(ep), EP_STCHG);
+}
+
+static void ep_unhalt(struct tegra_xudc *xudc, unsigned int ep)
+{
+ u32 val;
+
+ val = xudc_readl(xudc, EP_HALT);
+ if (!(val & BIT(ep)))
+ return;
+ val &= ~BIT(ep);
+ xudc_writel(xudc, val, EP_HALT);
+
+ xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep));
+
+ xudc_writel(xudc, BIT(ep), EP_STCHG);
+}
+
+static void ep_unhalt_all(struct tegra_xudc *xudc)
+{
+ u32 val;
+
+ val = xudc_readl(xudc, EP_HALT);
+ if (!val)
+ return;
+ xudc_writel(xudc, 0, EP_HALT);
+
+ xudc_readl_poll(xudc, EP_STCHG, val, val);
+
+ xudc_writel(xudc, val, EP_STCHG);
+}
+
+static void ep_wait_for_stopped(struct tegra_xudc *xudc, unsigned int ep)
+{
+ xudc_readl_poll(xudc, EP_STOPPED, BIT(ep), BIT(ep));
+ xudc_writel(xudc, BIT(ep), EP_STOPPED);
+}
+
+static void ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep)
+{
+ xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0);
+}
+
+static void tegra_xudc_req_done(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_request *req, int status)
+{
+ struct tegra_xudc *xudc = ep->xudc;
+
+ dev_dbg(xudc->dev, "completing request %p on EP %u with status %d\n",
+ req, ep->index, status);
+
+ if (likely(req->usb_req.status == -EINPROGRESS))
+ req->usb_req.status = status;
+
+ list_del_init(&req->list);
+
+ if (usb_endpoint_xfer_control(ep->desc)) {
+ usb_gadget_unmap_request(&xudc->gadget, &req->usb_req,
+ (xudc->setup_state ==
+ DATA_STAGE_XFER));
+ } else {
+ usb_gadget_unmap_request(&xudc->gadget, &req->usb_req,
+ usb_endpoint_dir_in(ep->desc));
+ }
+
+ spin_unlock(&xudc->lock);
+ usb_gadget_giveback_request(&ep->usb_ep, &req->usb_req);
+ spin_lock(&xudc->lock);
+}
+
+static void tegra_xudc_ep_nuke(struct tegra_xudc_ep *ep, int status)
+{
+ struct tegra_xudc_request *req;
+
+ while (!list_empty(&ep->queue)) {
+ req = list_first_entry(&ep->queue, struct tegra_xudc_request,
+ list);
+ tegra_xudc_req_done(ep, req, status);
+ }
+}
+
+static unsigned int ep_available_trbs(struct tegra_xudc_ep *ep)
+{
+ if (ep->ring_full)
+ return 0;
+
+ if (ep->deq_ptr > ep->enq_ptr)
+ return ep->deq_ptr - ep->enq_ptr - 1;
+
+ return XUDC_TRANSFER_RING_SIZE - (ep->enq_ptr - ep->deq_ptr) - 2;
+}
+
+static void tegra_xudc_queue_one_trb(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_request *req,
+ struct tegra_xudc_trb *trb,
+ bool ioc)
+{
+ struct tegra_xudc *xudc = ep->xudc;
+ dma_addr_t buf_addr;
+ size_t len;
+
+ len = min_t(size_t, XUDC_TRB_MAX_BUFFER_SIZE, req->usb_req.length -
+ req->buf_queued);
+ if (len > 0)
+ buf_addr = req->usb_req.dma + req->buf_queued;
+ else
+ buf_addr = 0;
+
+ trb_write_data_ptr(trb, buf_addr);
+
+ trb_write_transfer_len(trb, len);
+ trb_write_td_size(trb, req->trbs_needed - req->trbs_queued - 1);
+
+ if (req->trbs_queued == req->trbs_needed - 1 ||
+ (req->need_zlp && req->trbs_queued == req->trbs_needed - 2))
+ trb_write_chain(trb, 0);
+ else
+ trb_write_chain(trb, 1);
+
+ trb_write_ioc(trb, ioc);
+
+ if (usb_endpoint_dir_out(ep->desc) ||
+ (usb_endpoint_xfer_control(ep->desc) &&
+ (xudc->setup_state == DATA_STAGE_RECV)))
+ trb_write_isp(trb, 1);
+ else
+ trb_write_isp(trb, 0);
+
+ if (usb_endpoint_xfer_control(ep->desc)) {
+ if (xudc->setup_state == DATA_STAGE_XFER ||
+ xudc->setup_state == DATA_STAGE_RECV)
+ trb_write_type(trb, TRB_TYPE_DATA_STAGE);
+ else
+ trb_write_type(trb, TRB_TYPE_STATUS_STAGE);
+
+ if (xudc->setup_state == DATA_STAGE_XFER ||
+ xudc->setup_state == STATUS_STAGE_XFER)
+ trb_write_data_stage_dir(trb, 1);
+ else
+ trb_write_data_stage_dir(trb, 0);
+ } else if (usb_endpoint_xfer_isoc(ep->desc)) {
+ trb_write_type(trb, TRB_TYPE_ISOCH);
+ trb_write_sia(trb, 1);
+ trb_write_frame_id(trb, 0);
+ trb_write_tlbpc(trb, 0);
+ } else if (usb_ss_max_streams(ep->comp_desc)) {
+ trb_write_type(trb, TRB_TYPE_STREAM);
+ trb_write_stream_id(trb, req->usb_req.stream_id);
+ } else {
+ trb_write_type(trb, TRB_TYPE_NORMAL);
+ trb_write_stream_id(trb, 0);
+ }
+
+ trb_write_cycle(trb, ep->pcs);
+
+ req->trbs_queued++;
+ req->buf_queued += len;
+
+ dump_trb(xudc, "TRANSFER", trb);
+}
+
+static unsigned int tegra_xudc_queue_trbs(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_request *req)
+{
+ unsigned int i, count, available;
+ bool wait_td = false;
+
+ available = ep_available_trbs(ep);
+ count = req->trbs_needed - req->trbs_queued;
+ if (available < count) {
+ count = available;
+ ep->ring_full = true;
+ }
+
+ /*
+ * To generate zero-length packet on USB bus, SW needs schedule a
+ * standalone zero-length TD. According to HW's behavior, SW needs
+ * to schedule TDs in different ways for different endpoint types.
+ *
+ * For control endpoint:
+ * - Data stage TD (IOC = 1, CH = 0)
+ * - Ring doorbell and wait transfer event
+ * - Data stage TD for ZLP (IOC = 1, CH = 0)
+ * - Ring doorbell
+ *
+ * For bulk and interrupt endpoints:
+ * - Normal transfer TD (IOC = 0, CH = 0)
+ * - Normal transfer TD for ZLP (IOC = 1, CH = 0)
+ * - Ring doorbell
+ */
+
+ if (req->need_zlp && usb_endpoint_xfer_control(ep->desc) && count > 1)
+ wait_td = true;
+
+ if (!req->first_trb)
+ req->first_trb = &ep->transfer_ring[ep->enq_ptr];
+
+ for (i = 0; i < count; i++) {
+ struct tegra_xudc_trb *trb = &ep->transfer_ring[ep->enq_ptr];
+ bool ioc = false;
+
+ if ((i == count - 1) || (wait_td && i == count - 2))
+ ioc = true;
+
+ tegra_xudc_queue_one_trb(ep, req, trb, ioc);
+ req->last_trb = trb;
+
+ ep->enq_ptr++;
+ if (ep->enq_ptr == XUDC_TRANSFER_RING_SIZE - 1) {
+ trb = &ep->transfer_ring[ep->enq_ptr];
+ trb_write_cycle(trb, ep->pcs);
+ ep->pcs = !ep->pcs;
+ ep->enq_ptr = 0;
+ }
+
+ if (ioc)
+ break;
+ }
+
+ return count;
+}
+
+static void tegra_xudc_ep_ring_doorbell(struct tegra_xudc_ep *ep)
+{
+ struct tegra_xudc *xudc = ep->xudc;
+ u32 val;
+
+ if (list_empty(&ep->queue))
+ return;
+
+ val = DB_TARGET(ep->index);
+ if (usb_endpoint_xfer_control(ep->desc)) {
+ val |= DB_STREAMID(xudc->setup_seq_num);
+ } else if (usb_ss_max_streams(ep->comp_desc) > 0) {
+ struct tegra_xudc_request *req;
+
+ /* Don't ring doorbell if the stream has been rejected. */
+ if (ep->stream_rejected)
+ return;
+
+ req = list_first_entry(&ep->queue, struct tegra_xudc_request,
+ list);
+ val |= DB_STREAMID(req->usb_req.stream_id);
+ }
+
+ dev_dbg(xudc->dev, "ring doorbell: %#x\n", val);
+ xudc_writel(xudc, val, DB);
+}
+
+static void tegra_xudc_ep_kick_queue(struct tegra_xudc_ep *ep)
+{
+ struct tegra_xudc_request *req;
+ bool trbs_queued = false;
+
+ list_for_each_entry(req, &ep->queue, list) {
+ if (ep->ring_full)
+ break;
+
+ if (tegra_xudc_queue_trbs(ep, req) > 0)
+ trbs_queued = true;
+ }
+
+ if (trbs_queued)
+ tegra_xudc_ep_ring_doorbell(ep);
+}
+
+static int
+__tegra_xudc_ep_queue(struct tegra_xudc_ep *ep, struct tegra_xudc_request *req)
+{
+ struct tegra_xudc *xudc = ep->xudc;
+ int err;
+
+ if (usb_endpoint_xfer_control(ep->desc) && !list_empty(&ep->queue)) {
+ dev_err(xudc->dev, "control EP has pending transfers\n");
+ return -EINVAL;
+ }
+
+ if (usb_endpoint_xfer_control(ep->desc)) {
+ err = usb_gadget_map_request(&xudc->gadget, &req->usb_req,
+ (xudc->setup_state ==
+ DATA_STAGE_XFER));
+ } else {
+ err = usb_gadget_map_request(&xudc->gadget, &req->usb_req,
+ usb_endpoint_dir_in(ep->desc));
+ }
+
+ if (err < 0) {
+ dev_err(xudc->dev, "failed to map request: %d\n", err);
+ return err;
+ }
+
+ req->first_trb = NULL;
+ req->last_trb = NULL;
+ req->buf_queued = 0;
+ req->trbs_queued = 0;
+ req->need_zlp = false;
+ req->trbs_needed = DIV_ROUND_UP(req->usb_req.length,
+ XUDC_TRB_MAX_BUFFER_SIZE);
+ if (req->usb_req.length == 0)
+ req->trbs_needed++;
+
+ if (!usb_endpoint_xfer_isoc(ep->desc) &&
+ req->usb_req.zero && req->usb_req.length &&
+ ((req->usb_req.length % ep->usb_ep.maxpacket) == 0)) {
+ req->trbs_needed++;
+ req->need_zlp = true;
+ }
+
+ req->usb_req.status = -EINPROGRESS;
+ req->usb_req.actual = 0;
+
+ list_add_tail(&req->list, &ep->queue);
+
+ tegra_xudc_ep_kick_queue(ep);
+
+ return 0;
+}
+
+static int
+tegra_xudc_ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req,
+ gfp_t gfp)
+{
+ struct tegra_xudc_request *req;
+ struct tegra_xudc_ep *ep;
+ struct tegra_xudc *xudc;
+ unsigned long flags;
+ int ret;
+
+ if (!usb_ep || !usb_req)
+ return -EINVAL;
+
+ ep = to_xudc_ep(usb_ep);
+ req = to_xudc_req(usb_req);
+ xudc = ep->xudc;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ if (xudc->powergated || !ep->desc) {
+ ret = -ESHUTDOWN;
+ goto unlock;
+ }
+
+ ret = __tegra_xudc_ep_queue(ep, req);
+unlock:
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ return ret;
+}
+
+static void squeeze_transfer_ring(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_request *req)
+{
+ struct tegra_xudc_trb *trb = req->first_trb;
+ bool pcs_enq = trb_read_cycle(trb);
+ bool pcs;
+
+ /*
+ * Clear out all the TRBs part of or after the cancelled request,
+ * and must correct trb cycle bit to the last un-enqueued state.
+ */
+ while (trb != &ep->transfer_ring[ep->enq_ptr]) {
+ pcs = trb_read_cycle(trb);
+ memset(trb, 0, sizeof(*trb));
+ trb_write_cycle(trb, !pcs);
+ trb++;
+
+ if (trb_read_type(trb) == TRB_TYPE_LINK)
+ trb = ep->transfer_ring;
+ }
+
+ /* Requests will be re-queued at the start of the cancelled request. */
+ ep->enq_ptr = req->first_trb - ep->transfer_ring;
+ /*
+ * Retrieve the correct cycle bit state from the first trb of
+ * the cancelled request.
+ */
+ ep->pcs = pcs_enq;
+ ep->ring_full = false;
+ list_for_each_entry_continue(req, &ep->queue, list) {
+ req->usb_req.status = -EINPROGRESS;
+ req->usb_req.actual = 0;
+
+ req->first_trb = NULL;
+ req->last_trb = NULL;
+ req->buf_queued = 0;
+ req->trbs_queued = 0;
+ }
+}
+
+/*
+ * Determine if the given TRB is in the range [first trb, last trb] for the
+ * given request.
+ */
+static bool trb_in_request(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_request *req,
+ struct tegra_xudc_trb *trb)
+{
+ dev_dbg(ep->xudc->dev, "%s: request %p -> %p; trb %p\n", __func__,
+ req->first_trb, req->last_trb, trb);
+
+ if (trb >= req->first_trb && (trb <= req->last_trb ||
+ req->last_trb < req->first_trb))
+ return true;
+
+ if (trb < req->first_trb && trb <= req->last_trb &&
+ req->last_trb < req->first_trb)
+ return true;
+
+ return false;
+}
+
+/*
+ * Determine if the given TRB is in the range [EP enqueue pointer, first TRB)
+ * for the given endpoint and request.
+ */
+static bool trb_before_request(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_request *req,
+ struct tegra_xudc_trb *trb)
+{
+ struct tegra_xudc_trb *enq_trb = &ep->transfer_ring[ep->enq_ptr];
+
+ dev_dbg(ep->xudc->dev, "%s: request %p -> %p; enq ptr: %p; trb %p\n",
+ __func__, req->first_trb, req->last_trb, enq_trb, trb);
+
+ if (trb < req->first_trb && (enq_trb <= trb ||
+ req->first_trb < enq_trb))
+ return true;
+
+ if (trb > req->first_trb && req->first_trb < enq_trb && enq_trb <= trb)
+ return true;
+
+ return false;
+}
+
+static int
+__tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_request *req)
+{
+ struct tegra_xudc *xudc = ep->xudc;
+ struct tegra_xudc_request *r;
+ struct tegra_xudc_trb *deq_trb;
+ bool busy, kick_queue = false;
+ int ret = 0;
+
+ /* Make sure the request is actually queued to this endpoint. */
+ list_for_each_entry(r, &ep->queue, list) {
+ if (r == req)
+ break;
+ }
+
+ if (r != req)
+ return -EINVAL;
+
+ /* Request hasn't been queued in the transfer ring yet. */
+ if (!req->trbs_queued) {
+ tegra_xudc_req_done(ep, req, -ECONNRESET);
+ return 0;
+ }
+
+ /* Halt DMA for this endpiont. */
+ if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) {
+ ep_pause(xudc, ep->index);
+ ep_wait_for_inactive(xudc, ep->index);
+ }
+
+ deq_trb = trb_phys_to_virt(ep, ep_ctx_read_deq_ptr(ep->context));
+ /* Is the hardware processing the TRB at the dequeue pointer? */
+ busy = (trb_read_cycle(deq_trb) == ep_ctx_read_dcs(ep->context));
+
+ if (trb_in_request(ep, req, deq_trb) && busy) {
+ /*
+ * Request has been partially completed or it hasn't
+ * started processing yet.
+ */
+ dma_addr_t deq_ptr;
+
+ squeeze_transfer_ring(ep, req);
+
+ req->usb_req.actual = ep_ctx_read_edtla(ep->context);
+ tegra_xudc_req_done(ep, req, -ECONNRESET);
+ kick_queue = true;
+
+ /* EDTLA is > 0: request has been partially completed */
+ if (req->usb_req.actual > 0) {
+ /*
+ * Abort the pending transfer and update the dequeue
+ * pointer
+ */
+ ep_ctx_write_edtla(ep->context, 0);
+ ep_ctx_write_partial_td(ep->context, 0);
+ ep_ctx_write_data_offset(ep->context, 0);
+
+ deq_ptr = trb_virt_to_phys(ep,
+ &ep->transfer_ring[ep->enq_ptr]);
+
+ if (dma_mapping_error(xudc->dev, deq_ptr)) {
+ ret = -EINVAL;
+ } else {
+ ep_ctx_write_deq_ptr(ep->context, deq_ptr);
+ ep_ctx_write_dcs(ep->context, ep->pcs);
+ ep_reload(xudc, ep->index);
+ }
+ }
+ } else if (trb_before_request(ep, req, deq_trb) && busy) {
+ /* Request hasn't started processing yet. */
+ squeeze_transfer_ring(ep, req);
+
+ tegra_xudc_req_done(ep, req, -ECONNRESET);
+ kick_queue = true;
+ } else {
+ /*
+ * Request has completed, but we haven't processed the
+ * completion event yet.
+ */
+ tegra_xudc_req_done(ep, req, -ECONNRESET);
+ ret = -EINVAL;
+ }
+
+ /* Resume the endpoint. */
+ ep_unpause(xudc, ep->index);
+
+ if (kick_queue)
+ tegra_xudc_ep_kick_queue(ep);
+
+ return ret;
+}
+
+static int
+tegra_xudc_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req)
+{
+ struct tegra_xudc_request *req;
+ struct tegra_xudc_ep *ep;
+ struct tegra_xudc *xudc;
+ unsigned long flags;
+ int ret;
+
+ if (!usb_ep || !usb_req)
+ return -EINVAL;
+
+ ep = to_xudc_ep(usb_ep);
+ req = to_xudc_req(usb_req);
+ xudc = ep->xudc;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+
+ if (xudc->powergated || !ep->desc) {
+ ret = -ESHUTDOWN;
+ goto unlock;
+ }
+
+ ret = __tegra_xudc_ep_dequeue(ep, req);
+unlock:
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ return ret;
+}
+
+static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt)
+{
+ struct tegra_xudc *xudc = ep->xudc;
+
+ if (!ep->desc)
+ return -EINVAL;
+
+ if (usb_endpoint_xfer_isoc(ep->desc)) {
+ dev_err(xudc->dev, "can't halt isochronous EP\n");
+ return -ENOTSUPP;
+ }
+
+ if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) {
+ dev_dbg(xudc->dev, "EP %u already %s\n", ep->index,
+ halt ? "halted" : "not halted");
+ return 0;
+ }
+
+ if (halt) {
+ ep_halt(xudc, ep->index);
+ } else {
+ ep_ctx_write_state(ep->context, EP_STATE_DISABLED);
+
+ ep_reload(xudc, ep->index);
+
+ ep_ctx_write_state(ep->context, EP_STATE_RUNNING);
+ ep_ctx_write_seq_num(ep->context, 0);
+
+ ep_reload(xudc, ep->index);
+ ep_unpause(xudc, ep->index);
+ ep_unhalt(xudc, ep->index);
+
+ tegra_xudc_ep_ring_doorbell(ep);
+ }
+
+ return 0;
+}
+
+static int tegra_xudc_ep_set_halt(struct usb_ep *usb_ep, int value)
+{
+ struct tegra_xudc_ep *ep;
+ struct tegra_xudc *xudc;
+ unsigned long flags;
+ int ret;
+
+ if (!usb_ep)
+ return -EINVAL;
+
+ ep = to_xudc_ep(usb_ep);
+ xudc = ep->xudc;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ if (xudc->powergated) {
+ ret = -ESHUTDOWN;
+ goto unlock;
+ }
+
+ if (value && usb_endpoint_dir_in(ep->desc) &&
+ !list_empty(&ep->queue)) {
+ dev_err(xudc->dev, "can't halt EP with requests pending\n");
+ ret = -EAGAIN;
+ goto unlock;
+ }
+
+ ret = __tegra_xudc_ep_set_halt(ep, value);
+unlock:
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ return ret;
+}
+
+static void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep)
+{
+ const struct usb_endpoint_descriptor *desc = ep->desc;
+ const struct usb_ss_ep_comp_descriptor *comp_desc = ep->comp_desc;
+ struct tegra_xudc *xudc = ep->xudc;
+ u16 maxpacket, maxburst = 0, esit = 0;
+ u32 val;
+
+ maxpacket = usb_endpoint_maxp(desc);
+ if (xudc->gadget.speed == USB_SPEED_SUPER) {
+ if (!usb_endpoint_xfer_control(desc))
+ maxburst = comp_desc->bMaxBurst;
+
+ if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc))
+ esit = le16_to_cpu(comp_desc->wBytesPerInterval);
+ } else if ((xudc->gadget.speed < USB_SPEED_SUPER) &&
+ (usb_endpoint_xfer_int(desc) ||
+ usb_endpoint_xfer_isoc(desc))) {
+ if (xudc->gadget.speed == USB_SPEED_HIGH) {
+ maxburst = usb_endpoint_maxp_mult(desc) - 1;
+ if (maxburst == 0x3) {
+ dev_warn(xudc->dev,
+ "invalid endpoint maxburst\n");
+ maxburst = 0x2;
+ }
+ }
+ esit = maxpacket * (maxburst + 1);
+ }
+
+ memset(ep->context, 0, sizeof(*ep->context));
+
+ ep_ctx_write_state(ep->context, EP_STATE_RUNNING);
+ ep_ctx_write_interval(ep->context, desc->bInterval);
+ if (xudc->gadget.speed == USB_SPEED_SUPER) {
+ if (usb_endpoint_xfer_isoc(desc)) {
+ ep_ctx_write_mult(ep->context,
+ comp_desc->bmAttributes & 0x3);
+ }
+
+ if (usb_endpoint_xfer_bulk(desc)) {
+ ep_ctx_write_max_pstreams(ep->context,
+ comp_desc->bmAttributes &
+ 0x1f);
+ ep_ctx_write_lsa(ep->context, 1);
+ }
+ }
+
+ if (!usb_endpoint_xfer_control(desc) && usb_endpoint_dir_out(desc))
+ val = usb_endpoint_type(desc);
+ else
+ val = usb_endpoint_type(desc) + EP_TYPE_CONTROL;
+
+ ep_ctx_write_type(ep->context, val);
+ ep_ctx_write_cerr(ep->context, 0x3);
+ ep_ctx_write_max_packet_size(ep->context, maxpacket);
+ ep_ctx_write_max_burst_size(ep->context, maxburst);
+
+ ep_ctx_write_deq_ptr(ep->context, ep->transfer_ring_phys);
+ ep_ctx_write_dcs(ep->context, ep->pcs);
+
+ /* Select a reasonable average TRB length based on endpoint type. */
+ switch (usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ val = 8;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ val = 1024;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_ISOC:
+ default:
+ val = 3072;
+ break;
+ }
+
+ ep_ctx_write_avg_trb_len(ep->context, val);
+ ep_ctx_write_max_esit_payload(ep->context, esit);
+
+ ep_ctx_write_cerrcnt(ep->context, 0x3);
+}
+
+static void setup_link_trb(struct tegra_xudc_ep *ep,
+ struct tegra_xudc_trb *trb)
+{
+ trb_write_data_ptr(trb, ep->transfer_ring_phys);
+ trb_write_type(trb, TRB_TYPE_LINK);
+ trb_write_toggle_cycle(trb, 1);
+}
+
+static int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep)
+{
+ struct tegra_xudc *xudc = ep->xudc;
+
+ if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) {
+ dev_err(xudc->dev, "endpoint %u already disabled\n",
+ ep->index);
+ return -EINVAL;
+ }
+
+ ep_ctx_write_state(ep->context, EP_STATE_DISABLED);
+
+ ep_reload(xudc, ep->index);
+
+ tegra_xudc_ep_nuke(ep, -ESHUTDOWN);
+
+ xudc->nr_enabled_eps--;
+ if (usb_endpoint_xfer_isoc(ep->desc))
+ xudc->nr_isoch_eps--;
+
+ ep->desc = NULL;
+ ep->comp_desc = NULL;
+
+ memset(ep->context, 0, sizeof(*ep->context));
+
+ ep_unpause(xudc, ep->index);
+ ep_unhalt(xudc, ep->index);
+ if (xudc_readl(xudc, EP_STOPPED) & BIT(ep->index))
+ xudc_writel(xudc, BIT(ep->index), EP_STOPPED);
+
+ /*
+ * If this is the last endpoint disabled in a de-configure request,
+ * switch back to address state.
+ */
+ if ((xudc->device_state == USB_STATE_CONFIGURED) &&
+ (xudc->nr_enabled_eps == 1)) {
+ u32 val;
+
+ xudc->device_state = USB_STATE_ADDRESS;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+
+ val = xudc_readl(xudc, CTRL);
+ val &= ~CTRL_RUN;
+ xudc_writel(xudc, val, CTRL);
+ }
+
+ dev_info(xudc->dev, "ep %u disabled\n", ep->index);
+
+ return 0;
+}
+
+static int tegra_xudc_ep_disable(struct usb_ep *usb_ep)
+{
+ struct tegra_xudc_ep *ep;
+ struct tegra_xudc *xudc;
+ unsigned long flags;
+ int ret;
+
+ if (!usb_ep)
+ return -EINVAL;
+
+ ep = to_xudc_ep(usb_ep);
+ xudc = ep->xudc;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ if (xudc->powergated) {
+ ret = -ESHUTDOWN;
+ goto unlock;
+ }
+
+ ret = __tegra_xudc_ep_disable(ep);
+unlock:
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ return ret;
+}
+
+static int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct tegra_xudc *xudc = ep->xudc;
+ unsigned int i;
+ u32 val;
+
+ if (xudc->gadget.speed == USB_SPEED_SUPER &&
+ !usb_endpoint_xfer_control(desc) && !ep->usb_ep.comp_desc)
+ return -EINVAL;
+
+ /* Disable the EP if it is not disabled */
+ if (ep_ctx_read_state(ep->context) != EP_STATE_DISABLED)
+ __tegra_xudc_ep_disable(ep);
+
+ ep->desc = desc;
+ ep->comp_desc = ep->usb_ep.comp_desc;
+
+ if (usb_endpoint_xfer_isoc(desc)) {
+ if (xudc->nr_isoch_eps > XUDC_MAX_ISOCH_EPS) {
+ dev_err(xudc->dev, "too many isochronous endpoints\n");
+ return -EBUSY;
+ }
+ xudc->nr_isoch_eps++;
+ }
+
+ memset(ep->transfer_ring, 0, XUDC_TRANSFER_RING_SIZE *
+ sizeof(*ep->transfer_ring));
+ setup_link_trb(ep, &ep->transfer_ring[XUDC_TRANSFER_RING_SIZE - 1]);
+
+ ep->enq_ptr = 0;
+ ep->deq_ptr = 0;
+ ep->pcs = true;
+ ep->ring_full = false;
+ xudc->nr_enabled_eps++;
+
+ tegra_xudc_ep_context_setup(ep);
+
+ /*
+ * No need to reload and un-halt EP0. This will be done automatically
+ * once a valid SETUP packet is received.
+ */
+ if (usb_endpoint_xfer_control(desc))
+ goto out;
+
+ /*
+ * Transition to configured state once the first non-control
+ * endpoint is enabled.
+ */
+ if (xudc->device_state == USB_STATE_ADDRESS) {
+ val = xudc_readl(xudc, CTRL);
+ val |= CTRL_RUN;
+ xudc_writel(xudc, val, CTRL);
+
+ xudc->device_state = USB_STATE_CONFIGURED;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+ }
+
+ if (usb_endpoint_xfer_isoc(desc)) {
+ /*
+ * Pause all bulk endpoints when enabling an isoch endpoint
+ * to ensure the isoch endpoint is allocated enough bandwidth.
+ */
+ for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) {
+ if (xudc->ep[i].desc &&
+ usb_endpoint_xfer_bulk(xudc->ep[i].desc))
+ ep_pause(xudc, i);
+ }
+ }
+
+ ep_reload(xudc, ep->index);
+ ep_unpause(xudc, ep->index);
+ ep_unhalt(xudc, ep->index);
+
+ if (usb_endpoint_xfer_isoc(desc)) {
+ for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) {
+ if (xudc->ep[i].desc &&
+ usb_endpoint_xfer_bulk(xudc->ep[i].desc))
+ ep_unpause(xudc, i);
+ }
+ }
+
+out:
+ dev_info(xudc->dev, "EP %u (type: %s, dir: %s) enabled\n", ep->index,
+ usb_ep_type_string(usb_endpoint_type(ep->desc)),
+ usb_endpoint_dir_in(ep->desc) ? "in" : "out");
+
+ return 0;
+}
+
+static int tegra_xudc_ep_enable(struct usb_ep *usb_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct tegra_xudc_ep *ep;
+ struct tegra_xudc *xudc;
+ unsigned long flags;
+ int ret;
+
+ if (!usb_ep || !desc || (desc->bDescriptorType != USB_DT_ENDPOINT))
+ return -EINVAL;
+
+ ep = to_xudc_ep(usb_ep);
+ xudc = ep->xudc;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ if (xudc->powergated) {
+ ret = -ESHUTDOWN;
+ goto unlock;
+ }
+
+ ret = __tegra_xudc_ep_enable(ep, desc);
+unlock:
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ return ret;
+}
+
+static struct usb_request *
+tegra_xudc_ep_alloc_request(struct usb_ep *usb_ep, gfp_t gfp)
+{
+ struct tegra_xudc_request *req;
+
+ req = kzalloc(sizeof(*req), gfp);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->list);
+
+ return &req->usb_req;
+}
+
+static void tegra_xudc_ep_free_request(struct usb_ep *usb_ep,
+ struct usb_request *usb_req)
+{
+ struct tegra_xudc_request *req = to_xudc_req(usb_req);
+
+ kfree(req);
+}
+
+static struct usb_ep_ops tegra_xudc_ep_ops = {
+ .enable = tegra_xudc_ep_enable,
+ .disable = tegra_xudc_ep_disable,
+ .alloc_request = tegra_xudc_ep_alloc_request,
+ .free_request = tegra_xudc_ep_free_request,
+ .queue = tegra_xudc_ep_queue,
+ .dequeue = tegra_xudc_ep_dequeue,
+ .set_halt = tegra_xudc_ep_set_halt,
+};
+
+static int tegra_xudc_ep0_enable(struct usb_ep *usb_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ return -EBUSY;
+}
+
+static int tegra_xudc_ep0_disable(struct usb_ep *usb_ep)
+{
+ return -EBUSY;
+}
+
+static struct usb_ep_ops tegra_xudc_ep0_ops = {
+ .enable = tegra_xudc_ep0_enable,
+ .disable = tegra_xudc_ep0_disable,
+ .alloc_request = tegra_xudc_ep_alloc_request,
+ .free_request = tegra_xudc_ep_free_request,
+ .queue = tegra_xudc_ep_queue,
+ .dequeue = tegra_xudc_ep_dequeue,
+ .set_halt = tegra_xudc_ep_set_halt,
+};
+
+static int tegra_xudc_gadget_get_frame(struct usb_gadget *gadget)
+{
+ struct tegra_xudc *xudc = to_xudc(gadget);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ if (xudc->powergated) {
+ ret = -ESHUTDOWN;
+ goto unlock;
+ }
+
+ ret = (xudc_readl(xudc, MFINDEX) & MFINDEX_FRAME_MASK) >>
+ MFINDEX_FRAME_SHIFT;
+unlock:
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ return ret;
+}
+
+static void tegra_xudc_resume_device_state(struct tegra_xudc *xudc)
+{
+ unsigned int i;
+ u32 val;
+
+ ep_unpause_all(xudc);
+
+ /* Direct link to U0. */
+ val = xudc_readl(xudc, PORTSC);
+ if (((val & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT) != PORTSC_PLS_U0) {
+ val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK);
+ val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0);
+ xudc_writel(xudc, val, PORTSC);
+ }
+
+ if (xudc->device_state == USB_STATE_SUSPENDED) {
+ xudc->device_state = xudc->resume_state;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+ xudc->resume_state = 0;
+ }
+
+ /*
+ * Doorbells may be dropped if they are sent too soon (< ~200ns)
+ * after unpausing the endpoint. Wait for 500ns just to be safe.
+ */
+ ndelay(500);
+ for (i = 0; i < ARRAY_SIZE(xudc->ep); i++)
+ tegra_xudc_ep_ring_doorbell(&xudc->ep[i]);
+}
+
+static int tegra_xudc_gadget_wakeup(struct usb_gadget *gadget)
+{
+ struct tegra_xudc *xudc = to_xudc(gadget);
+ unsigned long flags;
+ int ret = 0;
+ u32 val;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+
+ if (xudc->powergated) {
+ ret = -ESHUTDOWN;
+ goto unlock;
+ }
+ val = xudc_readl(xudc, PORTPM);
+ dev_dbg(xudc->dev, "%s: PORTPM=%#x, speed=%x\n", __func__,
+ val, gadget->speed);
+
+ if (((xudc->gadget.speed <= USB_SPEED_HIGH) &&
+ (val & PORTPM_RWE)) ||
+ ((xudc->gadget.speed == USB_SPEED_SUPER) &&
+ (val & PORTPM_FRWE))) {
+ tegra_xudc_resume_device_state(xudc);
+
+ /* Send Device Notification packet. */
+ if (xudc->gadget.speed == USB_SPEED_SUPER) {
+ val = DEVNOTIF_LO_TYPE(DEVNOTIF_LO_TYPE_FUNCTION_WAKE)
+ | DEVNOTIF_LO_TRIG;
+ xudc_writel(xudc, 0, DEVNOTIF_HI);
+ xudc_writel(xudc, val, DEVNOTIF_LO);
+ }
+ }
+
+unlock:
+ dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret);
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ return ret;
+}
+
+static int tegra_xudc_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct tegra_xudc *xudc = to_xudc(gadget);
+ unsigned long flags;
+ u32 val;
+
+ pm_runtime_get_sync(xudc->dev);
+
+ spin_lock_irqsave(&xudc->lock, flags);
+
+ if (is_on != xudc->pullup) {
+ val = xudc_readl(xudc, CTRL);
+ if (is_on)
+ val |= CTRL_ENABLE;
+ else
+ val &= ~CTRL_ENABLE;
+ xudc_writel(xudc, val, CTRL);
+ }
+
+ xudc->pullup = is_on;
+ dev_dbg(xudc->dev, "%s: pullup:%d", __func__, is_on);
+
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ pm_runtime_put(xudc->dev);
+
+ return 0;
+}
+
+static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct tegra_xudc *xudc = to_xudc(gadget);
+ unsigned long flags;
+ u32 val;
+ int ret;
+ unsigned int i;
+
+ if (!driver)
+ return -EINVAL;
+
+ pm_runtime_get_sync(xudc->dev);
+
+ spin_lock_irqsave(&xudc->lock, flags);
+
+ if (xudc->driver) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ xudc->setup_state = WAIT_FOR_SETUP;
+ xudc->device_state = USB_STATE_DEFAULT;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+
+ ret = __tegra_xudc_ep_enable(&xudc->ep[0], &tegra_xudc_ep0_desc);
+ if (ret < 0)
+ goto unlock;
+
+ val = xudc_readl(xudc, CTRL);
+ val |= CTRL_IE | CTRL_LSE;
+ xudc_writel(xudc, val, CTRL);
+
+ val = xudc_readl(xudc, PORTHALT);
+ val |= PORTHALT_STCHG_INTR_EN;
+ xudc_writel(xudc, val, PORTHALT);
+
+ if (xudc->pullup) {
+ val = xudc_readl(xudc, CTRL);
+ val |= CTRL_ENABLE;
+ xudc_writel(xudc, val, CTRL);
+ }
+
+ for (i = 0; i < xudc->soc->num_phys; i++)
+ if (xudc->usbphy[i])
+ otg_set_peripheral(xudc->usbphy[i]->otg, gadget);
+
+ xudc->driver = driver;
+unlock:
+ dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret);
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ pm_runtime_put(xudc->dev);
+
+ return ret;
+}
+
+static int tegra_xudc_gadget_stop(struct usb_gadget *gadget)
+{
+ struct tegra_xudc *xudc = to_xudc(gadget);
+ unsigned long flags;
+ u32 val;
+ unsigned int i;
+
+ pm_runtime_get_sync(xudc->dev);
+
+ spin_lock_irqsave(&xudc->lock, flags);
+
+ for (i = 0; i < xudc->soc->num_phys; i++)
+ if (xudc->usbphy[i])
+ otg_set_peripheral(xudc->usbphy[i]->otg, NULL);
+
+ val = xudc_readl(xudc, CTRL);
+ val &= ~(CTRL_IE | CTRL_ENABLE);
+ xudc_writel(xudc, val, CTRL);
+
+ __tegra_xudc_ep_disable(&xudc->ep[0]);
+
+ xudc->driver = NULL;
+ dev_dbg(xudc->dev, "Gadget stopped");
+
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ pm_runtime_put(xudc->dev);
+
+ return 0;
+}
+
+static int tegra_xudc_gadget_vbus_draw(struct usb_gadget *gadget,
+ unsigned int m_a)
+{
+ int ret = 0;
+ struct tegra_xudc *xudc = to_xudc(gadget);
+
+ dev_dbg(xudc->dev, "%s: %u mA\n", __func__, m_a);
+
+ if (xudc->curr_usbphy->chg_type == SDP_TYPE)
+ ret = usb_phy_set_power(xudc->curr_usbphy, m_a);
+
+ return ret;
+}
+
+static int tegra_xudc_set_selfpowered(struct usb_gadget *gadget, int is_on)
+{
+ struct tegra_xudc *xudc = to_xudc(gadget);
+
+ dev_dbg(xudc->dev, "%s: %d\n", __func__, is_on);
+ xudc->selfpowered = !!is_on;
+
+ return 0;
+}
+
+static struct usb_gadget_ops tegra_xudc_gadget_ops = {
+ .get_frame = tegra_xudc_gadget_get_frame,
+ .wakeup = tegra_xudc_gadget_wakeup,
+ .pullup = tegra_xudc_gadget_pullup,
+ .udc_start = tegra_xudc_gadget_start,
+ .udc_stop = tegra_xudc_gadget_stop,
+ .vbus_draw = tegra_xudc_gadget_vbus_draw,
+ .set_selfpowered = tegra_xudc_set_selfpowered,
+};
+
+static void no_op_complete(struct usb_ep *ep, struct usb_request *req)
+{
+}
+
+static int
+tegra_xudc_ep0_queue_status(struct tegra_xudc *xudc,
+ void (*cmpl)(struct usb_ep *, struct usb_request *))
+{
+ xudc->ep0_req->usb_req.buf = NULL;
+ xudc->ep0_req->usb_req.dma = 0;
+ xudc->ep0_req->usb_req.length = 0;
+ xudc->ep0_req->usb_req.complete = cmpl;
+ xudc->ep0_req->usb_req.context = xudc;
+
+ return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req);
+}
+
+static int
+tegra_xudc_ep0_queue_data(struct tegra_xudc *xudc, void *buf, size_t len,
+ void (*cmpl)(struct usb_ep *, struct usb_request *))
+{
+ xudc->ep0_req->usb_req.buf = buf;
+ xudc->ep0_req->usb_req.length = len;
+ xudc->ep0_req->usb_req.complete = cmpl;
+ xudc->ep0_req->usb_req.context = xudc;
+
+ return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req);
+}
+
+static void tegra_xudc_ep0_req_done(struct tegra_xudc *xudc)
+{
+ switch (xudc->setup_state) {
+ case DATA_STAGE_XFER:
+ xudc->setup_state = STATUS_STAGE_RECV;
+ tegra_xudc_ep0_queue_status(xudc, no_op_complete);
+ break;
+ case DATA_STAGE_RECV:
+ xudc->setup_state = STATUS_STAGE_XFER;
+ tegra_xudc_ep0_queue_status(xudc, no_op_complete);
+ break;
+ default:
+ xudc->setup_state = WAIT_FOR_SETUP;
+ break;
+ }
+}
+
+static int tegra_xudc_ep0_delegate_req(struct tegra_xudc *xudc,
+ struct usb_ctrlrequest *ctrl)
+{
+ int ret;
+
+ spin_unlock(&xudc->lock);
+ ret = xudc->driver->setup(&xudc->gadget, ctrl);
+ spin_lock(&xudc->lock);
+
+ return ret;
+}
+
+static void set_feature_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct tegra_xudc *xudc = req->context;
+
+ if (xudc->test_mode_pattern) {
+ xudc_writel(xudc, xudc->test_mode_pattern, PORT_TM);
+ xudc->test_mode_pattern = 0;
+ }
+}
+
+static int tegra_xudc_ep0_set_feature(struct tegra_xudc *xudc,
+ struct usb_ctrlrequest *ctrl)
+{
+ bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+ u32 feature = le16_to_cpu(ctrl->wValue);
+ u32 index = le16_to_cpu(ctrl->wIndex);
+ u32 val, ep;
+ int ret;
+
+ if (le16_to_cpu(ctrl->wLength) != 0)
+ return -EINVAL;
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ switch (feature) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ if ((xudc->gadget.speed == USB_SPEED_SUPER) ||
+ (xudc->device_state == USB_STATE_DEFAULT))
+ return -EINVAL;
+
+ val = xudc_readl(xudc, PORTPM);
+ if (set)
+ val |= PORTPM_RWE;
+ else
+ val &= ~PORTPM_RWE;
+
+ xudc_writel(xudc, val, PORTPM);
+ break;
+ case USB_DEVICE_U1_ENABLE:
+ case USB_DEVICE_U2_ENABLE:
+ if ((xudc->device_state != USB_STATE_CONFIGURED) ||
+ (xudc->gadget.speed != USB_SPEED_SUPER))
+ return -EINVAL;
+
+ val = xudc_readl(xudc, PORTPM);
+ if ((feature == USB_DEVICE_U1_ENABLE) &&
+ xudc->soc->u1_enable) {
+ if (set)
+ val |= PORTPM_U1E;
+ else
+ val &= ~PORTPM_U1E;
+ }
+
+ if ((feature == USB_DEVICE_U2_ENABLE) &&
+ xudc->soc->u2_enable) {
+ if (set)
+ val |= PORTPM_U2E;
+ else
+ val &= ~PORTPM_U2E;
+ }
+
+ xudc_writel(xudc, val, PORTPM);
+ break;
+ case USB_DEVICE_TEST_MODE:
+ if (xudc->gadget.speed != USB_SPEED_HIGH)
+ return -EINVAL;
+
+ if (!set)
+ return -EINVAL;
+
+ xudc->test_mode_pattern = index >> 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ break;
+ case USB_RECIP_INTERFACE:
+ if (xudc->device_state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+
+ switch (feature) {
+ case USB_INTRF_FUNC_SUSPEND:
+ if (set) {
+ val = xudc_readl(xudc, PORTPM);
+
+ if (index & USB_INTRF_FUNC_SUSPEND_RW)
+ val |= PORTPM_FRWE;
+ else
+ val &= ~PORTPM_FRWE;
+
+ xudc_writel(xudc, val, PORTPM);
+ }
+
+ return tegra_xudc_ep0_delegate_req(xudc, ctrl);
+ default:
+ return -EINVAL;
+ }
+
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 +
+ ((index & USB_DIR_IN) ? 1 : 0);
+
+ if ((xudc->device_state == USB_STATE_DEFAULT) ||
+ ((xudc->device_state == USB_STATE_ADDRESS) &&
+ (index != 0)))
+ return -EINVAL;
+
+ ret = __tegra_xudc_ep_set_halt(&xudc->ep[ep], set);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return tegra_xudc_ep0_queue_status(xudc, set_feature_complete);
+}
+
+static int tegra_xudc_ep0_get_status(struct tegra_xudc *xudc,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct tegra_xudc_ep_context *ep_ctx;
+ u32 val, ep, index = le16_to_cpu(ctrl->wIndex);
+ u16 status = 0;
+
+ if (!(ctrl->bRequestType & USB_DIR_IN))
+ return -EINVAL;
+
+ if ((le16_to_cpu(ctrl->wValue) != 0) ||
+ (le16_to_cpu(ctrl->wLength) != 2))
+ return -EINVAL;
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ val = xudc_readl(xudc, PORTPM);
+
+ if (xudc->selfpowered)
+ status |= BIT(USB_DEVICE_SELF_POWERED);
+
+ if ((xudc->gadget.speed < USB_SPEED_SUPER) &&
+ (val & PORTPM_RWE))
+ status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
+
+ if (xudc->gadget.speed == USB_SPEED_SUPER) {
+ if (val & PORTPM_U1E)
+ status |= BIT(USB_DEV_STAT_U1_ENABLED);
+ if (val & PORTPM_U2E)
+ status |= BIT(USB_DEV_STAT_U2_ENABLED);
+ }
+ break;
+ case USB_RECIP_INTERFACE:
+ if (xudc->gadget.speed == USB_SPEED_SUPER) {
+ status |= USB_INTRF_STAT_FUNC_RW_CAP;
+ val = xudc_readl(xudc, PORTPM);
+ if (val & PORTPM_FRWE)
+ status |= USB_INTRF_STAT_FUNC_RW;
+ }
+ break;
+ case USB_RECIP_ENDPOINT:
+ ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 +
+ ((index & USB_DIR_IN) ? 1 : 0);
+ ep_ctx = &xudc->ep_context[ep];
+
+ if ((xudc->device_state != USB_STATE_CONFIGURED) &&
+ ((xudc->device_state != USB_STATE_ADDRESS) || (ep != 0)))
+ return -EINVAL;
+
+ if (ep_ctx_read_state(ep_ctx) == EP_STATE_DISABLED)
+ return -EINVAL;
+
+ if (xudc_readl(xudc, EP_HALT) & BIT(ep))
+ status |= BIT(USB_ENDPOINT_HALT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ xudc->status_buf = cpu_to_le16(status);
+ return tegra_xudc_ep0_queue_data(xudc, &xudc->status_buf,
+ sizeof(xudc->status_buf),
+ no_op_complete);
+}
+
+static void set_sel_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ /* Nothing to do with SEL values */
+}
+
+static int tegra_xudc_ep0_set_sel(struct tegra_xudc *xudc,
+ struct usb_ctrlrequest *ctrl)
+{
+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE |
+ USB_TYPE_STANDARD))
+ return -EINVAL;
+
+ if (xudc->device_state == USB_STATE_DEFAULT)
+ return -EINVAL;
+
+ if ((le16_to_cpu(ctrl->wIndex) != 0) ||
+ (le16_to_cpu(ctrl->wValue) != 0) ||
+ (le16_to_cpu(ctrl->wLength) != 6))
+ return -EINVAL;
+
+ return tegra_xudc_ep0_queue_data(xudc, &xudc->sel_timing,
+ sizeof(xudc->sel_timing),
+ set_sel_complete);
+}
+
+static void set_isoch_delay_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ /* Nothing to do with isoch delay */
+}
+
+static int tegra_xudc_ep0_set_isoch_delay(struct tegra_xudc *xudc,
+ struct usb_ctrlrequest *ctrl)
+{
+ u32 delay = le16_to_cpu(ctrl->wValue);
+
+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE |
+ USB_TYPE_STANDARD))
+ return -EINVAL;
+
+ if ((delay > 65535) || (le16_to_cpu(ctrl->wIndex) != 0) ||
+ (le16_to_cpu(ctrl->wLength) != 0))
+ return -EINVAL;
+
+ xudc->isoch_delay = delay;
+
+ return tegra_xudc_ep0_queue_status(xudc, set_isoch_delay_complete);
+}
+
+static void set_address_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct tegra_xudc *xudc = req->context;
+
+ if ((xudc->device_state == USB_STATE_DEFAULT) &&
+ (xudc->dev_addr != 0)) {
+ xudc->device_state = USB_STATE_ADDRESS;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+ } else if ((xudc->device_state == USB_STATE_ADDRESS) &&
+ (xudc->dev_addr == 0)) {
+ xudc->device_state = USB_STATE_DEFAULT;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+ }
+}
+
+static int tegra_xudc_ep0_set_address(struct tegra_xudc *xudc,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct tegra_xudc_ep *ep0 = &xudc->ep[0];
+ u32 val, addr = le16_to_cpu(ctrl->wValue);
+
+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE |
+ USB_TYPE_STANDARD))
+ return -EINVAL;
+
+ if ((addr > 127) || (le16_to_cpu(ctrl->wIndex) != 0) ||
+ (le16_to_cpu(ctrl->wLength) != 0))
+ return -EINVAL;
+
+ if (xudc->device_state == USB_STATE_CONFIGURED)
+ return -EINVAL;
+
+ dev_dbg(xudc->dev, "set address: %u\n", addr);
+
+ xudc->dev_addr = addr;
+ val = xudc_readl(xudc, CTRL);
+ val &= ~(CTRL_DEVADDR_MASK);
+ val |= CTRL_DEVADDR(addr);
+ xudc_writel(xudc, val, CTRL);
+
+ ep_ctx_write_devaddr(ep0->context, addr);
+
+ return tegra_xudc_ep0_queue_status(xudc, set_address_complete);
+}
+
+static int tegra_xudc_ep0_standard_req(struct tegra_xudc *xudc,
+ struct usb_ctrlrequest *ctrl)
+{
+ int ret;
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_STATUS:
+ dev_dbg(xudc->dev, "USB_REQ_GET_STATUS\n");
+ ret = tegra_xudc_ep0_get_status(xudc, ctrl);
+ break;
+ case USB_REQ_SET_ADDRESS:
+ dev_dbg(xudc->dev, "USB_REQ_SET_ADDRESS\n");
+ ret = tegra_xudc_ep0_set_address(xudc, ctrl);
+ break;
+ case USB_REQ_SET_SEL:
+ dev_dbg(xudc->dev, "USB_REQ_SET_SEL\n");
+ ret = tegra_xudc_ep0_set_sel(xudc, ctrl);
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ dev_dbg(xudc->dev, "USB_REQ_SET_ISOCH_DELAY\n");
+ ret = tegra_xudc_ep0_set_isoch_delay(xudc, ctrl);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ dev_dbg(xudc->dev, "USB_REQ_CLEAR/SET_FEATURE\n");
+ ret = tegra_xudc_ep0_set_feature(xudc, ctrl);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ dev_dbg(xudc->dev, "USB_REQ_SET_CONFIGURATION\n");
+ /*
+ * In theory we need to clear RUN bit before status stage of
+ * deconfig request sent, but this seems to be causing problems.
+ * Clear RUN once all endpoints are disabled instead.
+ */
+ fallthrough;
+ default:
+ ret = tegra_xudc_ep0_delegate_req(xudc, ctrl);
+ break;
+ }
+
+ return ret;
+}
+
+static void tegra_xudc_handle_ep0_setup_packet(struct tegra_xudc *xudc,
+ struct usb_ctrlrequest *ctrl,
+ u16 seq_num)
+{
+ int ret;
+
+ xudc->setup_seq_num = seq_num;
+
+ /* Ensure EP0 is unhalted. */
+ ep_unhalt(xudc, 0);
+
+ /*
+ * On Tegra210, setup packets with sequence numbers 0xfffe or 0xffff
+ * are invalid. Halt EP0 until we get a valid packet.
+ */
+ if (xudc->soc->invalid_seq_num &&
+ (seq_num == 0xfffe || seq_num == 0xffff)) {
+ dev_warn(xudc->dev, "invalid sequence number detected\n");
+ ep_halt(xudc, 0);
+ return;
+ }
+
+ if (ctrl->wLength)
+ xudc->setup_state = (ctrl->bRequestType & USB_DIR_IN) ?
+ DATA_STAGE_XFER : DATA_STAGE_RECV;
+ else
+ xudc->setup_state = STATUS_STAGE_XFER;
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ ret = tegra_xudc_ep0_standard_req(xudc, ctrl);
+ else
+ ret = tegra_xudc_ep0_delegate_req(xudc, ctrl);
+
+ if (ret < 0) {
+ dev_warn(xudc->dev, "setup request failed: %d\n", ret);
+ xudc->setup_state = WAIT_FOR_SETUP;
+ ep_halt(xudc, 0);
+ }
+}
+
+static void tegra_xudc_handle_ep0_event(struct tegra_xudc *xudc,
+ struct tegra_xudc_trb *event)
+{
+ struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)event;
+ u16 seq_num = trb_read_seq_num(event);
+
+ if (xudc->setup_state != WAIT_FOR_SETUP) {
+ /*
+ * The controller is in the process of handling another
+ * setup request. Queue subsequent requests and handle
+ * the last one once the controller reports a sequence
+ * number error.
+ */
+ memcpy(&xudc->setup_packet.ctrl_req, ctrl, sizeof(*ctrl));
+ xudc->setup_packet.seq_num = seq_num;
+ xudc->queued_setup_packet = true;
+ } else {
+ tegra_xudc_handle_ep0_setup_packet(xudc, ctrl, seq_num);
+ }
+}
+
+static struct tegra_xudc_request *
+trb_to_request(struct tegra_xudc_ep *ep, struct tegra_xudc_trb *trb)
+{
+ struct tegra_xudc_request *req;
+
+ list_for_each_entry(req, &ep->queue, list) {
+ if (!req->trbs_queued)
+ break;
+
+ if (trb_in_request(ep, req, trb))
+ return req;
+ }
+
+ return NULL;
+}
+
+static void tegra_xudc_handle_transfer_completion(struct tegra_xudc *xudc,
+ struct tegra_xudc_ep *ep,
+ struct tegra_xudc_trb *event)
+{
+ struct tegra_xudc_request *req;
+ struct tegra_xudc_trb *trb;
+ bool short_packet;
+
+ short_packet = (trb_read_cmpl_code(event) ==
+ TRB_CMPL_CODE_SHORT_PACKET);
+
+ trb = trb_phys_to_virt(ep, trb_read_data_ptr(event));
+ req = trb_to_request(ep, trb);
+
+ /*
+ * TDs are complete on short packet or when the completed TRB is the
+ * last TRB in the TD (the CHAIN bit is unset).
+ */
+ if (req && (short_packet || (!trb_read_chain(trb) &&
+ (req->trbs_needed == req->trbs_queued)))) {
+ struct tegra_xudc_trb *last = req->last_trb;
+ unsigned int residual;
+
+ residual = trb_read_transfer_len(event);
+ req->usb_req.actual = req->usb_req.length - residual;
+
+ dev_dbg(xudc->dev, "bytes transferred %u / %u\n",
+ req->usb_req.actual, req->usb_req.length);
+
+ tegra_xudc_req_done(ep, req, 0);
+
+ if (ep->desc && usb_endpoint_xfer_control(ep->desc))
+ tegra_xudc_ep0_req_done(xudc);
+
+ /*
+ * Advance the dequeue pointer past the end of the current TD
+ * on short packet completion.
+ */
+ if (short_packet) {
+ ep->deq_ptr = (last - ep->transfer_ring) + 1;
+ if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1)
+ ep->deq_ptr = 0;
+ }
+ } else if (!req) {
+ dev_warn(xudc->dev, "transfer event on dequeued request\n");
+ }
+
+ if (ep->desc)
+ tegra_xudc_ep_kick_queue(ep);
+}
+
+static void tegra_xudc_handle_transfer_event(struct tegra_xudc *xudc,
+ struct tegra_xudc_trb *event)
+{
+ unsigned int ep_index = trb_read_endpoint_id(event);
+ struct tegra_xudc_ep *ep = &xudc->ep[ep_index];
+ struct tegra_xudc_trb *trb;
+ u16 comp_code;
+
+ if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) {
+ dev_warn(xudc->dev, "transfer event on disabled EP %u\n",
+ ep_index);
+ return;
+ }
+
+ /* Update transfer ring dequeue pointer. */
+ trb = trb_phys_to_virt(ep, trb_read_data_ptr(event));
+ comp_code = trb_read_cmpl_code(event);
+ if (comp_code != TRB_CMPL_CODE_BABBLE_DETECTED_ERR) {
+ ep->deq_ptr = (trb - ep->transfer_ring) + 1;
+
+ if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1)
+ ep->deq_ptr = 0;
+ ep->ring_full = false;
+ }
+
+ switch (comp_code) {
+ case TRB_CMPL_CODE_SUCCESS:
+ case TRB_CMPL_CODE_SHORT_PACKET:
+ tegra_xudc_handle_transfer_completion(xudc, ep, event);
+ break;
+ case TRB_CMPL_CODE_HOST_REJECTED:
+ dev_info(xudc->dev, "stream rejected on EP %u\n", ep_index);
+
+ ep->stream_rejected = true;
+ break;
+ case TRB_CMPL_CODE_PRIME_PIPE_RECEIVED:
+ dev_info(xudc->dev, "prime pipe received on EP %u\n", ep_index);
+
+ if (ep->stream_rejected) {
+ ep->stream_rejected = false;
+ /*
+ * An EP is stopped when a stream is rejected. Wait
+ * for the EP to report that it is stopped and then
+ * un-stop it.
+ */
+ ep_wait_for_stopped(xudc, ep_index);
+ }
+ tegra_xudc_ep_ring_doorbell(ep);
+ break;
+ case TRB_CMPL_CODE_BABBLE_DETECTED_ERR:
+ /*
+ * Wait for the EP to be stopped so the controller stops
+ * processing doorbells.
+ */
+ ep_wait_for_stopped(xudc, ep_index);
+ ep->enq_ptr = ep->deq_ptr;
+ tegra_xudc_ep_nuke(ep, -EIO);
+ fallthrough;
+ case TRB_CMPL_CODE_STREAM_NUMP_ERROR:
+ case TRB_CMPL_CODE_CTRL_DIR_ERR:
+ case TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR:
+ case TRB_CMPL_CODE_RING_UNDERRUN:
+ case TRB_CMPL_CODE_RING_OVERRUN:
+ case TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN:
+ case TRB_CMPL_CODE_USB_TRANS_ERR:
+ case TRB_CMPL_CODE_TRB_ERR:
+ dev_err(xudc->dev, "completion error %#x on EP %u\n",
+ comp_code, ep_index);
+
+ ep_halt(xudc, ep_index);
+ break;
+ case TRB_CMPL_CODE_CTRL_SEQNUM_ERR:
+ dev_info(xudc->dev, "sequence number error\n");
+
+ /*
+ * Kill any queued control request and skip to the last
+ * setup packet we received.
+ */
+ tegra_xudc_ep_nuke(ep, -EINVAL);
+ xudc->setup_state = WAIT_FOR_SETUP;
+ if (!xudc->queued_setup_packet)
+ break;
+
+ tegra_xudc_handle_ep0_setup_packet(xudc,
+ &xudc->setup_packet.ctrl_req,
+ xudc->setup_packet.seq_num);
+ xudc->queued_setup_packet = false;
+ break;
+ case TRB_CMPL_CODE_STOPPED:
+ dev_dbg(xudc->dev, "stop completion code on EP %u\n",
+ ep_index);
+
+ /* Disconnected. */
+ tegra_xudc_ep_nuke(ep, -ECONNREFUSED);
+ break;
+ default:
+ dev_dbg(xudc->dev, "completion event %#x on EP %u\n",
+ comp_code, ep_index);
+ break;
+ }
+}
+
+static void tegra_xudc_reset(struct tegra_xudc *xudc)
+{
+ struct tegra_xudc_ep *ep0 = &xudc->ep[0];
+ dma_addr_t deq_ptr;
+ unsigned int i;
+
+ xudc->setup_state = WAIT_FOR_SETUP;
+ xudc->device_state = USB_STATE_DEFAULT;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+
+ ep_unpause_all(xudc);
+
+ for (i = 0; i < ARRAY_SIZE(xudc->ep); i++)
+ tegra_xudc_ep_nuke(&xudc->ep[i], -ESHUTDOWN);
+
+ /*
+ * Reset sequence number and dequeue pointer to flush the transfer
+ * ring.
+ */
+ ep0->deq_ptr = ep0->enq_ptr;
+ ep0->ring_full = false;
+
+ xudc->setup_seq_num = 0;
+ xudc->queued_setup_packet = false;
+
+ ep_ctx_write_seq_num(ep0->context, xudc->setup_seq_num);
+
+ deq_ptr = trb_virt_to_phys(ep0, &ep0->transfer_ring[ep0->deq_ptr]);
+
+ if (!dma_mapping_error(xudc->dev, deq_ptr)) {
+ ep_ctx_write_deq_ptr(ep0->context, deq_ptr);
+ ep_ctx_write_dcs(ep0->context, ep0->pcs);
+ }
+
+ ep_unhalt_all(xudc);
+ ep_reload(xudc, 0);
+ ep_unpause(xudc, 0);
+}
+
+static void tegra_xudc_port_connect(struct tegra_xudc *xudc)
+{
+ struct tegra_xudc_ep *ep0 = &xudc->ep[0];
+ u16 maxpacket;
+ u32 val;
+
+ val = (xudc_readl(xudc, PORTSC) & PORTSC_PS_MASK) >> PORTSC_PS_SHIFT;
+ switch (val) {
+ case PORTSC_PS_LS:
+ xudc->gadget.speed = USB_SPEED_LOW;
+ break;
+ case PORTSC_PS_FS:
+ xudc->gadget.speed = USB_SPEED_FULL;
+ break;
+ case PORTSC_PS_HS:
+ xudc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case PORTSC_PS_SS:
+ xudc->gadget.speed = USB_SPEED_SUPER;
+ break;
+ default:
+ xudc->gadget.speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+
+ xudc->device_state = USB_STATE_DEFAULT;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+
+ xudc->setup_state = WAIT_FOR_SETUP;
+
+ if (xudc->gadget.speed == USB_SPEED_SUPER)
+ maxpacket = 512;
+ else
+ maxpacket = 64;
+
+ ep_ctx_write_max_packet_size(ep0->context, maxpacket);
+ tegra_xudc_ep0_desc.wMaxPacketSize = cpu_to_le16(maxpacket);
+ usb_ep_set_maxpacket_limit(&ep0->usb_ep, maxpacket);
+
+ if (!xudc->soc->u1_enable) {
+ val = xudc_readl(xudc, PORTPM);
+ val &= ~(PORTPM_U1TIMEOUT_MASK);
+ xudc_writel(xudc, val, PORTPM);
+ }
+
+ if (!xudc->soc->u2_enable) {
+ val = xudc_readl(xudc, PORTPM);
+ val &= ~(PORTPM_U2TIMEOUT_MASK);
+ xudc_writel(xudc, val, PORTPM);
+ }
+
+ if (xudc->gadget.speed <= USB_SPEED_HIGH) {
+ val = xudc_readl(xudc, PORTPM);
+ val &= ~(PORTPM_L1S_MASK);
+ if (xudc->soc->lpm_enable)
+ val |= PORTPM_L1S(PORTPM_L1S_ACCEPT);
+ else
+ val |= PORTPM_L1S(PORTPM_L1S_NYET);
+ xudc_writel(xudc, val, PORTPM);
+ }
+
+ val = xudc_readl(xudc, ST);
+ if (val & ST_RC)
+ xudc_writel(xudc, ST_RC, ST);
+}
+
+static void tegra_xudc_port_disconnect(struct tegra_xudc *xudc)
+{
+ tegra_xudc_reset(xudc);
+
+ if (xudc->driver && xudc->driver->disconnect) {
+ spin_unlock(&xudc->lock);
+ xudc->driver->disconnect(&xudc->gadget);
+ spin_lock(&xudc->lock);
+ }
+
+ xudc->device_state = USB_STATE_NOTATTACHED;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+
+ complete(&xudc->disconnect_complete);
+}
+
+static void tegra_xudc_port_reset(struct tegra_xudc *xudc)
+{
+ tegra_xudc_reset(xudc);
+
+ if (xudc->driver) {
+ spin_unlock(&xudc->lock);
+ usb_gadget_udc_reset(&xudc->gadget, xudc->driver);
+ spin_lock(&xudc->lock);
+ }
+
+ tegra_xudc_port_connect(xudc);
+}
+
+static void tegra_xudc_port_suspend(struct tegra_xudc *xudc)
+{
+ dev_dbg(xudc->dev, "port suspend\n");
+
+ xudc->resume_state = xudc->device_state;
+ xudc->device_state = USB_STATE_SUSPENDED;
+ usb_gadget_set_state(&xudc->gadget, xudc->device_state);
+
+ if (xudc->driver->suspend) {
+ spin_unlock(&xudc->lock);
+ xudc->driver->suspend(&xudc->gadget);
+ spin_lock(&xudc->lock);
+ }
+}
+
+static void tegra_xudc_port_resume(struct tegra_xudc *xudc)
+{
+ dev_dbg(xudc->dev, "port resume\n");
+
+ tegra_xudc_resume_device_state(xudc);
+
+ if (xudc->driver->resume) {
+ spin_unlock(&xudc->lock);
+ xudc->driver->resume(&xudc->gadget);
+ spin_lock(&xudc->lock);
+ }
+}
+
+static inline void clear_port_change(struct tegra_xudc *xudc, u32 flag)
+{
+ u32 val;
+
+ val = xudc_readl(xudc, PORTSC);
+ val &= ~PORTSC_CHANGE_MASK;
+ val |= flag;
+ xudc_writel(xudc, val, PORTSC);
+}
+
+static void __tegra_xudc_handle_port_status(struct tegra_xudc *xudc)
+{
+ u32 portsc, porthalt;
+
+ porthalt = xudc_readl(xudc, PORTHALT);
+ if ((porthalt & PORTHALT_STCHG_REQ) &&
+ (porthalt & PORTHALT_HALT_LTSSM)) {
+ dev_dbg(xudc->dev, "STCHG_REQ, PORTHALT = %#x\n", porthalt);
+ porthalt &= ~PORTHALT_HALT_LTSSM;
+ xudc_writel(xudc, porthalt, PORTHALT);
+ }
+
+ portsc = xudc_readl(xudc, PORTSC);
+ if ((portsc & PORTSC_PRC) && (portsc & PORTSC_PR)) {
+ dev_dbg(xudc->dev, "PRC, PR, PORTSC = %#x\n", portsc);
+ clear_port_change(xudc, PORTSC_PRC | PORTSC_PED);
+#define TOGGLE_VBUS_WAIT_MS 100
+ if (xudc->soc->port_reset_quirk) {
+ schedule_delayed_work(&xudc->port_reset_war_work,
+ msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS));
+ xudc->wait_for_sec_prc = 1;
+ }
+ }
+
+ if ((portsc & PORTSC_PRC) && !(portsc & PORTSC_PR)) {
+ dev_dbg(xudc->dev, "PRC, Not PR, PORTSC = %#x\n", portsc);
+ clear_port_change(xudc, PORTSC_PRC | PORTSC_PED);
+ tegra_xudc_port_reset(xudc);
+ cancel_delayed_work(&xudc->port_reset_war_work);
+ xudc->wait_for_sec_prc = 0;
+ }
+
+ portsc = xudc_readl(xudc, PORTSC);
+ if (portsc & PORTSC_WRC) {
+ dev_dbg(xudc->dev, "WRC, PORTSC = %#x\n", portsc);
+ clear_port_change(xudc, PORTSC_WRC | PORTSC_PED);
+ if (!(xudc_readl(xudc, PORTSC) & PORTSC_WPR))
+ tegra_xudc_port_reset(xudc);
+ }
+
+ portsc = xudc_readl(xudc, PORTSC);
+ if (portsc & PORTSC_CSC) {
+ dev_dbg(xudc->dev, "CSC, PORTSC = %#x\n", portsc);
+ clear_port_change(xudc, PORTSC_CSC);
+
+ if (portsc & PORTSC_CCS)
+ tegra_xudc_port_connect(xudc);
+ else
+ tegra_xudc_port_disconnect(xudc);
+
+ if (xudc->wait_csc) {
+ cancel_delayed_work(&xudc->plc_reset_work);
+ xudc->wait_csc = false;
+ }
+ }
+
+ portsc = xudc_readl(xudc, PORTSC);
+ if (portsc & PORTSC_PLC) {
+ u32 pls = (portsc & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT;
+
+ dev_dbg(xudc->dev, "PLC, PORTSC = %#x\n", portsc);
+ clear_port_change(xudc, PORTSC_PLC);
+ switch (pls) {
+ case PORTSC_PLS_U3:
+ tegra_xudc_port_suspend(xudc);
+ break;
+ case PORTSC_PLS_U0:
+ if (xudc->gadget.speed < USB_SPEED_SUPER)
+ tegra_xudc_port_resume(xudc);
+ break;
+ case PORTSC_PLS_RESUME:
+ if (xudc->gadget.speed == USB_SPEED_SUPER)
+ tegra_xudc_port_resume(xudc);
+ break;
+ case PORTSC_PLS_INACTIVE:
+ schedule_delayed_work(&xudc->plc_reset_work,
+ msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS));
+ xudc->wait_csc = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (portsc & PORTSC_CEC) {
+ dev_warn(xudc->dev, "CEC, PORTSC = %#x\n", portsc);
+ clear_port_change(xudc, PORTSC_CEC);
+ }
+
+ dev_dbg(xudc->dev, "PORTSC = %#x\n", xudc_readl(xudc, PORTSC));
+}
+
+static void tegra_xudc_handle_port_status(struct tegra_xudc *xudc)
+{
+ while ((xudc_readl(xudc, PORTSC) & PORTSC_CHANGE_MASK) ||
+ (xudc_readl(xudc, PORTHALT) & PORTHALT_STCHG_REQ))
+ __tegra_xudc_handle_port_status(xudc);
+}
+
+static void tegra_xudc_handle_event(struct tegra_xudc *xudc,
+ struct tegra_xudc_trb *event)
+{
+ u32 type = trb_read_type(event);
+
+ dump_trb(xudc, "EVENT", event);
+
+ switch (type) {
+ case TRB_TYPE_PORT_STATUS_CHANGE_EVENT:
+ tegra_xudc_handle_port_status(xudc);
+ break;
+ case TRB_TYPE_TRANSFER_EVENT:
+ tegra_xudc_handle_transfer_event(xudc, event);
+ break;
+ case TRB_TYPE_SETUP_PACKET_EVENT:
+ tegra_xudc_handle_ep0_event(xudc, event);
+ break;
+ default:
+ dev_info(xudc->dev, "Unrecognized TRB type = %#x\n", type);
+ break;
+ }
+}
+
+static void tegra_xudc_process_event_ring(struct tegra_xudc *xudc)
+{
+ struct tegra_xudc_trb *event;
+ dma_addr_t erdp;
+
+ while (true) {
+ event = xudc->event_ring[xudc->event_ring_index] +
+ xudc->event_ring_deq_ptr;
+
+ if (trb_read_cycle(event) != xudc->ccs)
+ break;
+
+ tegra_xudc_handle_event(xudc, event);
+
+ xudc->event_ring_deq_ptr++;
+ if (xudc->event_ring_deq_ptr == XUDC_EVENT_RING_SIZE) {
+ xudc->event_ring_deq_ptr = 0;
+ xudc->event_ring_index++;
+ }
+
+ if (xudc->event_ring_index == XUDC_NR_EVENT_RINGS) {
+ xudc->event_ring_index = 0;
+ xudc->ccs = !xudc->ccs;
+ }
+ }
+
+ erdp = xudc->event_ring_phys[xudc->event_ring_index] +
+ xudc->event_ring_deq_ptr * sizeof(*event);
+
+ xudc_writel(xudc, upper_32_bits(erdp), ERDPHI);
+ xudc_writel(xudc, lower_32_bits(erdp) | ERDPLO_EHB, ERDPLO);
+}
+
+static irqreturn_t tegra_xudc_irq(int irq, void *data)
+{
+ struct tegra_xudc *xudc = data;
+ unsigned long flags;
+ u32 val;
+
+ val = xudc_readl(xudc, ST);
+ if (!(val & ST_IP))
+ return IRQ_NONE;
+ xudc_writel(xudc, ST_IP, ST);
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ tegra_xudc_process_event_ring(xudc);
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int tegra_xudc_alloc_ep(struct tegra_xudc *xudc, unsigned int index)
+{
+ struct tegra_xudc_ep *ep = &xudc->ep[index];
+
+ ep->xudc = xudc;
+ ep->index = index;
+ ep->context = &xudc->ep_context[index];
+ INIT_LIST_HEAD(&ep->queue);
+
+ /*
+ * EP1 would be the input endpoint corresponding to EP0, but since
+ * EP0 is bi-directional, EP1 is unused.
+ */
+ if (index == 1)
+ return 0;
+
+ ep->transfer_ring = dma_pool_alloc(xudc->transfer_ring_pool,
+ GFP_KERNEL,
+ &ep->transfer_ring_phys);
+ if (!ep->transfer_ring)
+ return -ENOMEM;
+
+ if (index) {
+ snprintf(ep->name, sizeof(ep->name), "ep%u%s", index / 2,
+ (index % 2 == 0) ? "out" : "in");
+ ep->usb_ep.name = ep->name;
+ usb_ep_set_maxpacket_limit(&ep->usb_ep, 1024);
+ ep->usb_ep.max_streams = 16;
+ ep->usb_ep.ops = &tegra_xudc_ep_ops;
+ ep->usb_ep.caps.type_bulk = true;
+ ep->usb_ep.caps.type_int = true;
+ if (index & 1)
+ ep->usb_ep.caps.dir_in = true;
+ else
+ ep->usb_ep.caps.dir_out = true;
+ list_add_tail(&ep->usb_ep.ep_list, &xudc->gadget.ep_list);
+ } else {
+ strscpy(ep->name, "ep0", 3);
+ ep->usb_ep.name = ep->name;
+ usb_ep_set_maxpacket_limit(&ep->usb_ep, 512);
+ ep->usb_ep.ops = &tegra_xudc_ep0_ops;
+ ep->usb_ep.caps.type_control = true;
+ ep->usb_ep.caps.dir_in = true;
+ ep->usb_ep.caps.dir_out = true;
+ }
+
+ return 0;
+}
+
+static void tegra_xudc_free_ep(struct tegra_xudc *xudc, unsigned int index)
+{
+ struct tegra_xudc_ep *ep = &xudc->ep[index];
+
+ /*
+ * EP1 would be the input endpoint corresponding to EP0, but since
+ * EP0 is bi-directional, EP1 is unused.
+ */
+ if (index == 1)
+ return;
+
+ dma_pool_free(xudc->transfer_ring_pool, ep->transfer_ring,
+ ep->transfer_ring_phys);
+}
+
+static int tegra_xudc_alloc_eps(struct tegra_xudc *xudc)
+{
+ struct usb_request *req;
+ unsigned int i;
+ int err;
+
+ xudc->ep_context =
+ dma_alloc_coherent(xudc->dev, XUDC_NR_EPS *
+ sizeof(*xudc->ep_context),
+ &xudc->ep_context_phys, GFP_KERNEL);
+ if (!xudc->ep_context)
+ return -ENOMEM;
+
+ xudc->transfer_ring_pool =
+ dmam_pool_create(dev_name(xudc->dev), xudc->dev,
+ XUDC_TRANSFER_RING_SIZE *
+ sizeof(struct tegra_xudc_trb),
+ sizeof(struct tegra_xudc_trb), 0);
+ if (!xudc->transfer_ring_pool) {
+ err = -ENOMEM;
+ goto free_ep_context;
+ }
+
+ INIT_LIST_HEAD(&xudc->gadget.ep_list);
+ for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) {
+ err = tegra_xudc_alloc_ep(xudc, i);
+ if (err < 0)
+ goto free_eps;
+ }
+
+ req = tegra_xudc_ep_alloc_request(&xudc->ep[0].usb_ep, GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ goto free_eps;
+ }
+ xudc->ep0_req = to_xudc_req(req);
+
+ return 0;
+
+free_eps:
+ for (; i > 0; i--)
+ tegra_xudc_free_ep(xudc, i - 1);
+free_ep_context:
+ dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context),
+ xudc->ep_context, xudc->ep_context_phys);
+ return err;
+}
+
+static void tegra_xudc_init_eps(struct tegra_xudc *xudc)
+{
+ xudc_writel(xudc, lower_32_bits(xudc->ep_context_phys), ECPLO);
+ xudc_writel(xudc, upper_32_bits(xudc->ep_context_phys), ECPHI);
+}
+
+static void tegra_xudc_free_eps(struct tegra_xudc *xudc)
+{
+ unsigned int i;
+
+ tegra_xudc_ep_free_request(&xudc->ep[0].usb_ep,
+ &xudc->ep0_req->usb_req);
+
+ for (i = 0; i < ARRAY_SIZE(xudc->ep); i++)
+ tegra_xudc_free_ep(xudc, i);
+
+ dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context),
+ xudc->ep_context, xudc->ep_context_phys);
+}
+
+static int tegra_xudc_alloc_event_ring(struct tegra_xudc *xudc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
+ xudc->event_ring[i] =
+ dma_alloc_coherent(xudc->dev, XUDC_EVENT_RING_SIZE *
+ sizeof(*xudc->event_ring[i]),
+ &xudc->event_ring_phys[i],
+ GFP_KERNEL);
+ if (!xudc->event_ring[i])
+ goto free_dma;
+ }
+
+ return 0;
+
+free_dma:
+ for (; i > 0; i--) {
+ dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE *
+ sizeof(*xudc->event_ring[i - 1]),
+ xudc->event_ring[i - 1],
+ xudc->event_ring_phys[i - 1]);
+ }
+ return -ENOMEM;
+}
+
+static void tegra_xudc_init_event_ring(struct tegra_xudc *xudc)
+{
+ unsigned int i;
+ u32 val;
+
+ val = xudc_readl(xudc, SPARAM);
+ val &= ~(SPARAM_ERSTMAX_MASK);
+ val |= SPARAM_ERSTMAX(XUDC_NR_EVENT_RINGS);
+ xudc_writel(xudc, val, SPARAM);
+
+ for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
+ memset(xudc->event_ring[i], 0, XUDC_EVENT_RING_SIZE *
+ sizeof(*xudc->event_ring[i]));
+
+ val = xudc_readl(xudc, ERSTSZ);
+ val &= ~(ERSTSZ_ERSTXSZ_MASK << ERSTSZ_ERSTXSZ_SHIFT(i));
+ val |= XUDC_EVENT_RING_SIZE << ERSTSZ_ERSTXSZ_SHIFT(i);
+ xudc_writel(xudc, val, ERSTSZ);
+
+ xudc_writel(xudc, lower_32_bits(xudc->event_ring_phys[i]),
+ ERSTXBALO(i));
+ xudc_writel(xudc, upper_32_bits(xudc->event_ring_phys[i]),
+ ERSTXBAHI(i));
+ }
+
+ val = lower_32_bits(xudc->event_ring_phys[0]);
+ xudc_writel(xudc, val, ERDPLO);
+ val |= EREPLO_ECS;
+ xudc_writel(xudc, val, EREPLO);
+
+ val = upper_32_bits(xudc->event_ring_phys[0]);
+ xudc_writel(xudc, val, ERDPHI);
+ xudc_writel(xudc, val, EREPHI);
+
+ xudc->ccs = true;
+ xudc->event_ring_index = 0;
+ xudc->event_ring_deq_ptr = 0;
+}
+
+static void tegra_xudc_free_event_ring(struct tegra_xudc *xudc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) {
+ dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE *
+ sizeof(*xudc->event_ring[i]),
+ xudc->event_ring[i],
+ xudc->event_ring_phys[i]);
+ }
+}
+
+static void tegra_xudc_fpci_ipfs_init(struct tegra_xudc *xudc)
+{
+ u32 val;
+
+ if (xudc->soc->has_ipfs) {
+ val = ipfs_readl(xudc, XUSB_DEV_CONFIGURATION_0);
+ val |= XUSB_DEV_CONFIGURATION_0_EN_FPCI;
+ ipfs_writel(xudc, val, XUSB_DEV_CONFIGURATION_0);
+ usleep_range(10, 15);
+ }
+
+ /* Enable bus master */
+ val = XUSB_DEV_CFG_1_IO_SPACE_EN | XUSB_DEV_CFG_1_MEMORY_SPACE_EN |
+ XUSB_DEV_CFG_1_BUS_MASTER_EN;
+ fpci_writel(xudc, val, XUSB_DEV_CFG_1);
+
+ /* Program BAR0 space */
+ val = fpci_readl(xudc, XUSB_DEV_CFG_4);
+ val &= ~(XUSB_DEV_CFG_4_BASE_ADDR_MASK);
+ val |= xudc->phys_base & (XUSB_DEV_CFG_4_BASE_ADDR_MASK);
+
+ fpci_writel(xudc, val, XUSB_DEV_CFG_4);
+ fpci_writel(xudc, upper_32_bits(xudc->phys_base), XUSB_DEV_CFG_5);
+
+ usleep_range(100, 200);
+
+ if (xudc->soc->has_ipfs) {
+ /* Enable interrupt assertion */
+ val = ipfs_readl(xudc, XUSB_DEV_INTR_MASK_0);
+ val |= XUSB_DEV_INTR_MASK_0_IP_INT_MASK;
+ ipfs_writel(xudc, val, XUSB_DEV_INTR_MASK_0);
+ }
+}
+
+static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
+{
+ u32 val, imod;
+
+ if (xudc->soc->has_ipfs) {
+ val = xudc_readl(xudc, BLCG);
+ val |= BLCG_ALL;
+ val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE |
+ BLCG_COREPLL_PWRDN);
+ val |= BLCG_IOPLL_0_PWRDN;
+ val |= BLCG_IOPLL_1_PWRDN;
+ val |= BLCG_IOPLL_2_PWRDN;
+
+ xudc_writel(xudc, val, BLCG);
+ }
+
+ if (xudc->soc->port_speed_quirk)
+ tegra_xudc_limit_port_speed(xudc);
+
+ /* Set a reasonable U3 exit timer value. */
+ val = xudc_readl(xudc, SSPX_CORE_PADCTL4);
+ val &= ~(SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK);
+ val |= SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(0x5dc0);
+ xudc_writel(xudc, val, SSPX_CORE_PADCTL4);
+
+ /* Default ping LFPS tBurst is too large. */
+ val = xudc_readl(xudc, SSPX_CORE_CNT0);
+ val &= ~(SSPX_CORE_CNT0_PING_TBURST_MASK);
+ val |= SSPX_CORE_CNT0_PING_TBURST(0xa);
+ xudc_writel(xudc, val, SSPX_CORE_CNT0);
+
+ /* Default tPortConfiguration timeout is too small. */
+ val = xudc_readl(xudc, SSPX_CORE_CNT30);
+ val &= ~(SSPX_CORE_CNT30_LMPITP_TIMER_MASK);
+ val |= SSPX_CORE_CNT30_LMPITP_TIMER(0x978);
+ xudc_writel(xudc, val, SSPX_CORE_CNT30);
+
+ if (xudc->soc->lpm_enable) {
+ /* Set L1 resume duration to 95 us. */
+ val = xudc_readl(xudc, HSFSPI_COUNT13);
+ val &= ~(HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK);
+ val |= HSFSPI_COUNT13_U2_RESUME_K_DURATION(0x2c88);
+ xudc_writel(xudc, val, HSFSPI_COUNT13);
+ }
+
+ /*
+ * Compliacne suite appears to be violating polling LFPS tBurst max
+ * of 1.4us. Send 1.45us instead.
+ */
+ val = xudc_readl(xudc, SSPX_CORE_CNT32);
+ val &= ~(SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK);
+ val |= SSPX_CORE_CNT32_POLL_TBURST_MAX(0xb0);
+ xudc_writel(xudc, val, SSPX_CORE_CNT32);
+
+ /* Direct HS/FS port instance to RxDetect. */
+ val = xudc_readl(xudc, CFG_DEV_FE);
+ val &= ~(CFG_DEV_FE_PORTREGSEL_MASK);
+ val |= CFG_DEV_FE_PORTREGSEL(CFG_DEV_FE_PORTREGSEL_HSFS_PI);
+ xudc_writel(xudc, val, CFG_DEV_FE);
+
+ val = xudc_readl(xudc, PORTSC);
+ val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK);
+ val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT);
+ xudc_writel(xudc, val, PORTSC);
+
+ /* Direct SS port instance to RxDetect. */
+ val = xudc_readl(xudc, CFG_DEV_FE);
+ val &= ~(CFG_DEV_FE_PORTREGSEL_MASK);
+ val |= CFG_DEV_FE_PORTREGSEL_SS_PI & CFG_DEV_FE_PORTREGSEL_MASK;
+ xudc_writel(xudc, val, CFG_DEV_FE);
+
+ val = xudc_readl(xudc, PORTSC);
+ val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK);
+ val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT);
+ xudc_writel(xudc, val, PORTSC);
+
+ /* Restore port instance. */
+ val = xudc_readl(xudc, CFG_DEV_FE);
+ val &= ~(CFG_DEV_FE_PORTREGSEL_MASK);
+ xudc_writel(xudc, val, CFG_DEV_FE);
+
+ /*
+ * Enable INFINITE_SS_RETRY to prevent device from entering
+ * Disabled.Error when attached to buggy SuperSpeed hubs.
+ */
+ val = xudc_readl(xudc, CFG_DEV_FE);
+ val |= CFG_DEV_FE_INFINITE_SS_RETRY;
+ xudc_writel(xudc, val, CFG_DEV_FE);
+
+ /* Set interrupt moderation. */
+ imod = XUDC_INTERRUPT_MODERATION_US * 4;
+ val = xudc_readl(xudc, RT_IMOD);
+ val &= ~((RT_IMOD_IMODI_MASK) | (RT_IMOD_IMODC_MASK));
+ val |= (RT_IMOD_IMODI(imod) | RT_IMOD_IMODC(imod));
+ xudc_writel(xudc, val, RT_IMOD);
+
+ /* increase SSPI transaction timeout from 32us to 512us */
+ val = xudc_readl(xudc, CFG_DEV_SSPI_XFER);
+ val &= ~(CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK);
+ val |= CFG_DEV_SSPI_XFER_ACKTIMEOUT(0xf000);
+ xudc_writel(xudc, val, CFG_DEV_SSPI_XFER);
+}
+
+static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
+{
+ int err = 0, usb3;
+ unsigned int i;
+
+ xudc->utmi_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
+ sizeof(*xudc->utmi_phy), GFP_KERNEL);
+ if (!xudc->utmi_phy)
+ return -ENOMEM;
+
+ xudc->usb3_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
+ sizeof(*xudc->usb3_phy), GFP_KERNEL);
+ if (!xudc->usb3_phy)
+ return -ENOMEM;
+
+ xudc->usbphy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
+ sizeof(*xudc->usbphy), GFP_KERNEL);
+ if (!xudc->usbphy)
+ return -ENOMEM;
+
+ xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;
+
+ for (i = 0; i < xudc->soc->num_phys; i++) {
+ char phy_name[] = "usb.-.";
+
+ /* Get USB2 phy */
+ snprintf(phy_name, sizeof(phy_name), "usb2-%d", i);
+ xudc->utmi_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
+ if (IS_ERR(xudc->utmi_phy[i])) {
+ err = PTR_ERR(xudc->utmi_phy[i]);
+ if (err != -EPROBE_DEFER)
+ dev_err(xudc->dev, "failed to get usb2-%d PHY: %d\n",
+ i, err);
+
+ goto clean_up;
+ } else if (xudc->utmi_phy[i]) {
+ /* Get usb-phy, if utmi phy is available */
+ xudc->usbphy[i] = devm_usb_get_phy_by_node(xudc->dev,
+ xudc->utmi_phy[i]->dev.of_node,
+ &xudc->vbus_nb);
+ if (IS_ERR(xudc->usbphy[i])) {
+ err = PTR_ERR(xudc->usbphy[i]);
+ dev_err(xudc->dev, "failed to get usbphy-%d: %d\n",
+ i, err);
+ goto clean_up;
+ }
+ } else if (!xudc->utmi_phy[i]) {
+ /* if utmi phy is not available, ignore USB3 phy get */
+ continue;
+ }
+
+ /* Get USB3 phy */
+ usb3 = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i);
+ if (usb3 < 0)
+ continue;
+
+ snprintf(phy_name, sizeof(phy_name), "usb3-%d", usb3);
+ xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
+ if (IS_ERR(xudc->usb3_phy[i])) {
+ err = PTR_ERR(xudc->usb3_phy[i]);
+ if (err != -EPROBE_DEFER)
+ dev_err(xudc->dev, "failed to get usb3-%d PHY: %d\n",
+ usb3, err);
+
+ goto clean_up;
+ } else if (xudc->usb3_phy[i])
+ dev_dbg(xudc->dev, "usb3-%d PHY registered", usb3);
+ }
+
+ return err;
+
+clean_up:
+ for (i = 0; i < xudc->soc->num_phys; i++) {
+ xudc->usb3_phy[i] = NULL;
+ xudc->utmi_phy[i] = NULL;
+ xudc->usbphy[i] = NULL;
+ }
+
+ return err;
+}
+
+static void tegra_xudc_phy_exit(struct tegra_xudc *xudc)
+{
+ unsigned int i;
+
+ for (i = 0; i < xudc->soc->num_phys; i++) {
+ phy_exit(xudc->usb3_phy[i]);
+ phy_exit(xudc->utmi_phy[i]);
+ }
+}
+
+static int tegra_xudc_phy_init(struct tegra_xudc *xudc)
+{
+ int err;
+ unsigned int i;
+
+ for (i = 0; i < xudc->soc->num_phys; i++) {
+ err = phy_init(xudc->utmi_phy[i]);
+ if (err < 0) {
+ dev_err(xudc->dev, "UTMI PHY #%u initialization failed: %d\n", i, err);
+ goto exit_phy;
+ }
+
+ err = phy_init(xudc->usb3_phy[i]);
+ if (err < 0) {
+ dev_err(xudc->dev, "USB3 PHY #%u initialization failed: %d\n", i, err);
+ goto exit_phy;
+ }
+ }
+ return 0;
+
+exit_phy:
+ tegra_xudc_phy_exit(xudc);
+ return err;
+}
+
+static const char * const tegra210_xudc_supply_names[] = {
+ "hvdd-usb",
+ "avddio-usb",
+};
+
+static const char * const tegra210_xudc_clock_names[] = {
+ "dev",
+ "ss",
+ "ss_src",
+ "hs_src",
+ "fs_src",
+};
+
+static const char * const tegra186_xudc_clock_names[] = {
+ "dev",
+ "ss",
+ "ss_src",
+ "fs_src",
+};
+
+static struct tegra_xudc_soc tegra210_xudc_soc_data = {
+ .supply_names = tegra210_xudc_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names),
+ .clock_names = tegra210_xudc_clock_names,
+ .num_clks = ARRAY_SIZE(tegra210_xudc_clock_names),
+ .num_phys = 4,
+ .u1_enable = false,
+ .u2_enable = true,
+ .lpm_enable = false,
+ .invalid_seq_num = true,
+ .pls_quirk = true,
+ .port_reset_quirk = true,
+ .port_speed_quirk = false,
+ .has_ipfs = true,
+};
+
+static struct tegra_xudc_soc tegra186_xudc_soc_data = {
+ .clock_names = tegra186_xudc_clock_names,
+ .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
+ .num_phys = 4,
+ .u1_enable = true,
+ .u2_enable = true,
+ .lpm_enable = false,
+ .invalid_seq_num = false,
+ .pls_quirk = false,
+ .port_reset_quirk = false,
+ .port_speed_quirk = false,
+ .has_ipfs = false,
+};
+
+static struct tegra_xudc_soc tegra194_xudc_soc_data = {
+ .clock_names = tegra186_xudc_clock_names,
+ .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
+ .num_phys = 4,
+ .u1_enable = true,
+ .u2_enable = true,
+ .lpm_enable = true,
+ .invalid_seq_num = false,
+ .pls_quirk = false,
+ .port_reset_quirk = false,
+ .port_speed_quirk = true,
+ .has_ipfs = false,
+};
+
+static const struct of_device_id tegra_xudc_of_match[] = {
+ {
+ .compatible = "nvidia,tegra210-xudc",
+ .data = &tegra210_xudc_soc_data
+ },
+ {
+ .compatible = "nvidia,tegra186-xudc",
+ .data = &tegra186_xudc_soc_data
+ },
+ {
+ .compatible = "nvidia,tegra194-xudc",
+ .data = &tegra194_xudc_soc_data
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tegra_xudc_of_match);
+
+static void tegra_xudc_powerdomain_remove(struct tegra_xudc *xudc)
+{
+ if (xudc->genpd_dl_ss)
+ device_link_del(xudc->genpd_dl_ss);
+ if (xudc->genpd_dl_device)
+ device_link_del(xudc->genpd_dl_device);
+ if (xudc->genpd_dev_ss)
+ dev_pm_domain_detach(xudc->genpd_dev_ss, true);
+ if (xudc->genpd_dev_device)
+ dev_pm_domain_detach(xudc->genpd_dev_device, true);
+}
+
+static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc)
+{
+ struct device *dev = xudc->dev;
+ int err;
+
+ xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, "dev");
+ if (IS_ERR(xudc->genpd_dev_device)) {
+ err = PTR_ERR(xudc->genpd_dev_device);
+ dev_err(dev, "failed to get device power domain: %d\n", err);
+ return err;
+ }
+
+ xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss");
+ if (IS_ERR(xudc->genpd_dev_ss)) {
+ err = PTR_ERR(xudc->genpd_dev_ss);
+ dev_err(dev, "failed to get SuperSpeed power domain: %d\n", err);
+ return err;
+ }
+
+ xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device,
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (!xudc->genpd_dl_device) {
+ dev_err(dev, "failed to add USB device link\n");
+ return -ENODEV;
+ }
+
+ xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss,
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (!xudc->genpd_dl_ss) {
+ dev_err(dev, "failed to add SuperSpeed device link\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int tegra_xudc_probe(struct platform_device *pdev)
+{
+ struct tegra_xudc *xudc;
+ struct resource *res;
+ unsigned int i;
+ int err;
+
+ xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_KERNEL);
+ if (!xudc)
+ return -ENOMEM;
+
+ xudc->dev = &pdev->dev;
+ platform_set_drvdata(pdev, xudc);
+
+ xudc->soc = of_device_get_match_data(&pdev->dev);
+ if (!xudc->soc)
+ return -ENODEV;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
+ xudc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(xudc->base))
+ return PTR_ERR(xudc->base);
+ xudc->phys_base = res->start;
+
+ xudc->fpci = devm_platform_ioremap_resource_byname(pdev, "fpci");
+ if (IS_ERR(xudc->fpci))
+ return PTR_ERR(xudc->fpci);
+
+ if (xudc->soc->has_ipfs) {
+ xudc->ipfs = devm_platform_ioremap_resource_byname(pdev, "ipfs");
+ if (IS_ERR(xudc->ipfs))
+ return PTR_ERR(xudc->ipfs);
+ }
+
+ xudc->irq = platform_get_irq(pdev, 0);
+ if (xudc->irq < 0)
+ return xudc->irq;
+
+ err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0,
+ dev_name(&pdev->dev), xudc);
+ if (err < 0) {
+ dev_err(xudc->dev, "failed to claim IRQ#%u: %d\n", xudc->irq,
+ err);
+ return err;
+ }
+
+ xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, sizeof(*xudc->clks),
+ GFP_KERNEL);
+ if (!xudc->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < xudc->soc->num_clks; i++)
+ xudc->clks[i].id = xudc->soc->clock_names[i];
+
+ err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, xudc->clks);
+ if (err) {
+ if (err != -EPROBE_DEFER)
+ dev_err(xudc->dev, "failed to request clocks: %d\n", err);
+
+ return err;
+ }
+
+ xudc->supplies = devm_kcalloc(&pdev->dev, xudc->soc->num_supplies,
+ sizeof(*xudc->supplies), GFP_KERNEL);
+ if (!xudc->supplies)
+ return -ENOMEM;
+
+ for (i = 0; i < xudc->soc->num_supplies; i++)
+ xudc->supplies[i].supply = xudc->soc->supply_names[i];
+
+ err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies,
+ xudc->supplies);
+ if (err) {
+ if (err != -EPROBE_DEFER)
+ dev_err(xudc->dev, "failed to request regulators: %d\n", err);
+
+ return err;
+ }
+
+ xudc->padctl = tegra_xusb_padctl_get(&pdev->dev);
+ if (IS_ERR(xudc->padctl))
+ return PTR_ERR(xudc->padctl);
+
+ err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies);
+ if (err) {
+ dev_err(xudc->dev, "failed to enable regulators: %d\n", err);
+ goto put_padctl;
+ }
+
+ err = tegra_xudc_phy_get(xudc);
+ if (err)
+ goto disable_regulator;
+
+ err = tegra_xudc_powerdomain_init(xudc);
+ if (err)
+ goto put_powerdomains;
+
+ err = tegra_xudc_phy_init(xudc);
+ if (err)
+ goto put_powerdomains;
+
+ err = tegra_xudc_alloc_event_ring(xudc);
+ if (err)
+ goto disable_phy;
+
+ err = tegra_xudc_alloc_eps(xudc);
+ if (err)
+ goto free_event_ring;
+
+ spin_lock_init(&xudc->lock);
+
+ init_completion(&xudc->disconnect_complete);
+
+ INIT_WORK(&xudc->usb_role_sw_work, tegra_xudc_usb_role_sw_work);
+
+ INIT_DELAYED_WORK(&xudc->plc_reset_work, tegra_xudc_plc_reset_work);
+
+ INIT_DELAYED_WORK(&xudc->port_reset_war_work,
+ tegra_xudc_port_reset_war_work);
+
+ pm_runtime_enable(&pdev->dev);
+
+ xudc->gadget.ops = &tegra_xudc_gadget_ops;
+ xudc->gadget.ep0 = &xudc->ep[0].usb_ep;
+ xudc->gadget.name = "tegra-xudc";
+ xudc->gadget.max_speed = USB_SPEED_SUPER;
+
+ err = usb_add_gadget_udc(&pdev->dev, &xudc->gadget);
+ if (err) {
+ dev_err(&pdev->dev, "failed to add USB gadget: %d\n", err);
+ goto free_eps;
+ }
+
+ return 0;
+
+free_eps:
+ pm_runtime_disable(&pdev->dev);
+ tegra_xudc_free_eps(xudc);
+free_event_ring:
+ tegra_xudc_free_event_ring(xudc);
+disable_phy:
+ tegra_xudc_phy_exit(xudc);
+put_powerdomains:
+ tegra_xudc_powerdomain_remove(xudc);
+disable_regulator:
+ regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
+put_padctl:
+ tegra_xusb_padctl_put(xudc->padctl);
+
+ return err;
+}
+
+static int tegra_xudc_remove(struct platform_device *pdev)
+{
+ struct tegra_xudc *xudc = platform_get_drvdata(pdev);
+ unsigned int i;
+
+ pm_runtime_get_sync(xudc->dev);
+
+ cancel_delayed_work_sync(&xudc->plc_reset_work);
+ cancel_work_sync(&xudc->usb_role_sw_work);
+
+ usb_del_gadget_udc(&xudc->gadget);
+
+ tegra_xudc_free_eps(xudc);
+ tegra_xudc_free_event_ring(xudc);
+
+ tegra_xudc_powerdomain_remove(xudc);
+
+ regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
+
+ for (i = 0; i < xudc->soc->num_phys; i++) {
+ phy_power_off(xudc->utmi_phy[i]);
+ phy_power_off(xudc->usb3_phy[i]);
+ }
+
+ tegra_xudc_phy_exit(xudc);
+
+ pm_runtime_disable(xudc->dev);
+ pm_runtime_put(xudc->dev);
+
+ tegra_xusb_padctl_put(xudc->padctl);
+
+ return 0;
+}
+
+static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
+{
+ unsigned long flags;
+
+ dev_dbg(xudc->dev, "entering ELPG\n");
+
+ spin_lock_irqsave(&xudc->lock, flags);
+
+ xudc->powergated = true;
+ xudc->saved_regs.ctrl = xudc_readl(xudc, CTRL);
+ xudc->saved_regs.portpm = xudc_readl(xudc, PORTPM);
+ xudc_writel(xudc, 0, CTRL);
+
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks);
+
+ regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
+
+ dev_dbg(xudc->dev, "entering ELPG done\n");
+ return 0;
+}
+
+static int __maybe_unused tegra_xudc_unpowergate(struct tegra_xudc *xudc)
+{
+ unsigned long flags;
+ int err;
+
+ dev_dbg(xudc->dev, "exiting ELPG\n");
+
+ err = regulator_bulk_enable(xudc->soc->num_supplies,
+ xudc->supplies);
+ if (err < 0)
+ return err;
+
+ err = clk_bulk_prepare_enable(xudc->soc->num_clks, xudc->clks);
+ if (err < 0)
+ return err;
+
+ tegra_xudc_fpci_ipfs_init(xudc);
+
+ tegra_xudc_device_params_init(xudc);
+
+ tegra_xudc_init_event_ring(xudc);
+
+ tegra_xudc_init_eps(xudc);
+
+ xudc_writel(xudc, xudc->saved_regs.portpm, PORTPM);
+ xudc_writel(xudc, xudc->saved_regs.ctrl, CTRL);
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ xudc->powergated = false;
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ dev_dbg(xudc->dev, "exiting ELPG done\n");
+ return 0;
+}
+
+static int __maybe_unused tegra_xudc_suspend(struct device *dev)
+{
+ struct tegra_xudc *xudc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ xudc->suspended = true;
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ flush_work(&xudc->usb_role_sw_work);
+
+ if (!pm_runtime_status_suspended(dev)) {
+ /* Forcibly disconnect before powergating. */
+ tegra_xudc_device_mode_off(xudc);
+ tegra_xudc_powergate(xudc);
+ }
+
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+static int __maybe_unused tegra_xudc_resume(struct device *dev)
+{
+ struct tegra_xudc *xudc = dev_get_drvdata(dev);
+ unsigned long flags;
+ int err;
+
+ err = tegra_xudc_unpowergate(xudc);
+ if (err < 0)
+ return err;
+
+ spin_lock_irqsave(&xudc->lock, flags);
+ xudc->suspended = false;
+ spin_unlock_irqrestore(&xudc->lock, flags);
+
+ schedule_work(&xudc->usb_role_sw_work);
+
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int __maybe_unused tegra_xudc_runtime_suspend(struct device *dev)
+{
+ struct tegra_xudc *xudc = dev_get_drvdata(dev);
+
+ return tegra_xudc_powergate(xudc);
+}
+
+static int __maybe_unused tegra_xudc_runtime_resume(struct device *dev)
+{
+ struct tegra_xudc *xudc = dev_get_drvdata(dev);
+
+ return tegra_xudc_unpowergate(xudc);
+}
+
+static const struct dev_pm_ops tegra_xudc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_xudc_suspend, tegra_xudc_resume)
+ SET_RUNTIME_PM_OPS(tegra_xudc_runtime_suspend,
+ tegra_xudc_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra_xudc_driver = {
+ .probe = tegra_xudc_probe,
+ .remove = tegra_xudc_remove,
+ .driver = {
+ .name = "tegra-xudc",
+ .pm = &tegra_xudc_pm_ops,
+ .of_match_table = tegra_xudc_of_match,
+ },
+};
+module_platform_driver(tegra_xudc_driver);
+
+MODULE_DESCRIPTION("NVIDIA Tegra XUSB Device Controller");
+MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>");
+MODULE_AUTHOR("Hui Fu <hfu@nvidia.com>");
+MODULE_AUTHOR("Nagarjuna Kristam <nkristam@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index 29d8e5f..096f56a 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -1399,7 +1399,6 @@
/**
* xudc_stop - stops the device.
* @gadget: pointer to the usb gadget structure
- * @driver: pointer to usb gadget driver structure
*
* Return: zero always
*/
@@ -1613,6 +1612,8 @@
break;
case USB_RECIP_ENDPOINT:
epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ if (epnum >= XUSB_MAX_ENDPOINTS)
+ goto stall;
target_ep = &udc->ep[epnum];
epcfgreg = udc->read_fn(udc->addr + target_ep->offset);
halt = epcfgreg & XUSB_EP_CFG_STALL_MASK;
@@ -1680,6 +1681,10 @@
case USB_RECIP_ENDPOINT:
if (!udc->setup.wValue) {
endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ if (endpoint >= XUSB_MAX_ENDPOINTS) {
+ xudc_ep0_stall(udc);
+ return;
+ }
target_ep = &udc->ep[endpoint];
outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK;
outinbit = outinbit >> 7;
@@ -1733,6 +1738,7 @@
* Process setup packet and delegate to gadget layer.
*/
static void xudc_handle_setup(struct xusb_udc *udc)
+ __must_hold(&udc->lock)
{
struct xusb_ep *ep0 = &udc->ep[0];
struct usb_ctrlrequest setup;
@@ -2097,9 +2103,9 @@
/* Check for IP endianness */
udc->write_fn = xudc_write32_be;
udc->read_fn = xudc_read32_be;
- udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, TEST_J);
+ udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, USB_TEST_J);
if ((udc->read_fn(udc->addr + XUSB_TESTMODE_OFFSET))
- != TEST_J) {
+ != USB_TEST_J) {
udc->write_fn = xudc_write32;
udc->read_fn = xudc_read32;
}
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 33f77e5..75f6f99 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -65,3 +65,27 @@
return buf [0];
}
EXPORT_SYMBOL_GPL(usb_gadget_get_string);
+
+/**
+ * usb_validate_langid - validate usb language identifiers
+ * @langid: usb language identifier
+ *
+ * Returns true for valid language identifier, otherwise false.
+ */
+bool usb_validate_langid(u16 langid)
+{
+ u16 primary_lang = langid & 0x3ff; /* bit [9:0] */
+ u16 sub_lang = langid >> 10; /* bit [15:10] */
+
+ switch (primary_lang) {
+ case 0:
+ case 0x62 ... 0xfe:
+ case 0x100 ... 0x3ff:
+ return false;
+ }
+ if (!sub_lang)
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(usb_validate_langid);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 79b2e79..ab12c4b 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -20,7 +20,7 @@
config USB_XHCI_HCD
tristate "xHCI HCD (USB 3.0) support"
depends on HAS_DMA && HAS_IOMEM
- ---help---
+ help
The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0
"SuperSpeed" host controller hardware.
@@ -31,21 +31,30 @@
config USB_XHCI_DBGCAP
bool "xHCI support for debug capability"
depends on TTY
- ---help---
+ help
Say 'Y' to enable the support for the xHCI debug capability. Make
sure that your xHCI host supports the extended debug capability and
you want a TTY serial device based on the xHCI debug capability
before enabling this option. If unsure, say 'N'.
config USB_XHCI_PCI
- tristate
- depends on USB_PCI
- default y
+ tristate
+ depends on USB_PCI
+ depends on USB_XHCI_PCI_RENESAS || !USB_XHCI_PCI_RENESAS
+ default y
+
+config USB_XHCI_PCI_RENESAS
+ tristate "Support for additional Renesas xHCI controller with firmware"
+ help
+ Say 'Y' to enable the support for the Renesas xHCI controller with
+ firmware. Make sure you have the firwmare for the device and
+ installed on your system for this device to work.
+ If unsure, say 'N'.
config USB_XHCI_PLATFORM
tristate "Generic xHCI driver for a platform device"
select USB_XHCI_RCAR if ARCH_RENESAS
- ---help---
+ help
Adds an xHCI host driver for a generic platform device, which
provides a memory space and an irq.
It is also a prerequisite for platform specific drivers that
@@ -64,7 +73,7 @@
tristate "xHCI support for MediaTek SoCs"
select MFD_SYSCON
depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST
- ---help---
+ help
Say 'Y' to enable the support for the xHCI host controller
found in MediaTek SoCs.
If unsure, say N.
@@ -74,7 +83,7 @@
select USB_XHCI_PLATFORM
depends on HAS_IOMEM
depends on ARCH_MVEBU || COMPILE_TEST
- ---help---
+ help
Say 'Y' to enable the support for the xHCI host controller
found in Marvell Armada 375/38x/37xx ARM SOCs.
@@ -82,7 +91,7 @@
tristate "xHCI support for Renesas R-Car SoCs"
depends on USB_XHCI_PLATFORM
depends on ARCH_RENESAS || COMPILE_TEST
- ---help---
+ help
Say 'Y' to enable the support for the xHCI host controller
found in Renesas R-Car ARM SoCs.
@@ -91,16 +100,36 @@
depends on PHY_TEGRA_XUSB
depends on RESET_CONTROLLER
select FW_LOADER
- ---help---
+ help
Say 'Y' to enable the support for the xHCI host controller
found in NVIDIA Tegra124 and later SoCs.
endif # USB_XHCI_HCD
+config USB_EHCI_BRCMSTB
+ tristate
+
+config USB_BRCMSTB
+ tristate "Broadcom STB USB support"
+ depends on (ARCH_BRCMSTB && PHY_BRCM_USB) || COMPILE_TEST
+ select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
+ select USB_EHCI_BRCMSTB if USB_EHCI_HCD
+ select USB_XHCI_PLATFORM if USB_XHCI_HCD
+ help
+ Enables support for XHCI, EHCI and OHCI host controllers
+ found in Broadcom STB SoC's.
+
+ To compile these drivers as modules, choose M here: the
+ modules will be called ohci-platform.ko, ehci-brcm.ko and
+ xhci-plat-hcd.ko
+
+ Disabling this will keep the controllers and corresponding
+ PHYs powered down.
+
config USB_EHCI_HCD
tristate "EHCI HCD (USB 2.0) support"
depends on HAS_DMA && HAS_IOMEM
- ---help---
+ help
The Enhanced Host Controller Interface (EHCI) is standard for USB 2.0
"high speed" (480 Mbit/sec, 60 Mbyte/sec) host controller hardware.
If your USB host controller supports USB 2.0, you will likely want to
@@ -122,7 +151,7 @@
config USB_EHCI_ROOT_HUB_TT
bool "Root Hub Transaction Translators"
depends on USB_EHCI_HCD
- ---help---
+ help
Some EHCI chips have vendor-specific extensions to integrate
transaction translators, so that no OHCI or UHCI companion
controller is needed. It's safe to say "y" even if your
@@ -135,7 +164,7 @@
bool "Improved Transaction Translator scheduling"
depends on USB_EHCI_HCD
default y
- ---help---
+ help
This changes the periodic scheduling code to fill more of the low
and full speed bandwidth available from the Transaction Translator
(TT) in USB 2.0 hubs. Without this, only one transfer will be
@@ -163,7 +192,7 @@
depends on MSP_HAS_USB
select USB_EHCI_BIG_ENDIAN_DESC
select USB_EHCI_BIG_ENDIAN_MMIO
- ---help---
+ help
Enables support for the onchip USB controller on the PMC_MSP7100 Family SoC's.
If unsure, say N.
@@ -172,7 +201,7 @@
depends on (PPC32 || MICROBLAZE)
select USB_EHCI_BIG_ENDIAN_DESC
select USB_EHCI_BIG_ENDIAN_MMIO
- ---help---
+ help
Xilinx xps USB host controller core is EHCI compliant and has
transaction translator built-in. It can be configured to either
support both high speed and full speed devices, or high speed
@@ -181,14 +210,14 @@
config USB_EHCI_FSL
tristate "Support for Freescale on-chip EHCI USB controller"
select USB_EHCI_ROOT_HUB_TT
- ---help---
+ help
Variation of ARC USB block used in some Freescale chips.
config USB_EHCI_MXC
tristate "Support for Freescale i.MX on-chip EHCI USB controller"
- depends on ARCH_MXC
+ depends on ARCH_MXC || COMPILE_TEST
select USB_EHCI_ROOT_HUB_TT
- ---help---
+ help
Variation of ARC USB block used in some Freescale chips.
config USB_EHCI_HCD_NPCM7XX
@@ -204,15 +233,15 @@
depends on ARCH_OMAP
depends on NOP_USB_XCEIV
default y
- ---help---
+ help
Enables support for the on-chip EHCI controller on
OMAP3 and later chips.
config USB_EHCI_HCD_ORION
tristate "Support for Marvell EBU on-chip EHCI USB controller"
- depends on USB_EHCI_HCD && (PLAT_ORION || ARCH_MVEBU)
- default y
- ---help---
+ depends on USB_EHCI_HCD && (PLAT_ORION || ARCH_MVEBU || COMPILE_TEST)
+ default y if (PLAT_ORION || ARCH_MVEBU)
+ help
Enables support for the on-chip EHCI controller on Marvell's
embedded ARM SoCs, including Orion, Kirkwood, Dove, Armada XP,
Armada 370. This is different from the EHCI implementation
@@ -220,16 +249,16 @@
Marvell PXA/MMP USB controller" for those.
config USB_EHCI_HCD_SPEAR
- tristate "Support for ST SPEAr on-chip EHCI USB controller"
- depends on USB_EHCI_HCD && PLAT_SPEAR
- default y
- ---help---
- Enables support for the on-chip EHCI controller on
- ST SPEAr chips.
+ tristate "Support for ST SPEAr on-chip EHCI USB controller"
+ depends on USB_EHCI_HCD && (PLAT_SPEAR || COMPILE_TEST)
+ default y if PLAT_SPEAR
+ help
+ Enables support for the on-chip EHCI controller on
+ ST SPEAr chips.
config USB_EHCI_HCD_STI
tristate "Support for ST STiHxxx on-chip EHCI USB controller"
- depends on ARCH_STI && OF
+ depends on (ARCH_STI || COMPILE_TEST) && OF
select GENERIC_PHY
select USB_EHCI_HCD_PLATFORM
help
@@ -237,48 +266,48 @@
STMicroelectronics consumer electronics SoC's.
config USB_EHCI_HCD_AT91
- tristate "Support for Atmel on-chip EHCI USB controller"
- depends on USB_EHCI_HCD && ARCH_AT91
- default y
- ---help---
- Enables support for the on-chip EHCI controller on
- Atmel chips.
+ tristate "Support for Atmel on-chip EHCI USB controller"
+ depends on USB_EHCI_HCD && (ARCH_AT91 || COMPILE_TEST)
+ default y if ARCH_AT91
+ help
+ Enables support for the on-chip EHCI controller on
+ Atmel chips.
config USB_EHCI_TEGRA
- tristate "NVIDIA Tegra HCD support"
- depends on ARCH_TEGRA
- select USB_EHCI_ROOT_HUB_TT
- select USB_TEGRA_PHY
- help
- This driver enables support for the internal USB Host Controllers
- found in NVIDIA Tegra SoCs. The controllers are EHCI compliant.
+ tristate "NVIDIA Tegra HCD support"
+ depends on ARCH_TEGRA
+ select USB_EHCI_ROOT_HUB_TT
+ select USB_TEGRA_PHY
+ help
+ This driver enables support for the internal USB Host Controllers
+ found in NVIDIA Tegra SoCs. The controllers are EHCI compliant.
config USB_EHCI_HCD_PPC_OF
bool "EHCI support for PPC USB controller on OF platform bus"
depends on PPC
default y
- ---help---
+ help
Enables support for the USB controller present on the PowerPC
OpenFirmware platform bus.
config USB_EHCI_SH
bool "EHCI support for SuperH USB controller"
- depends on SUPERH
- ---help---
+ depends on SUPERH || COMPILE_TEST
+ help
Enables support for the on-chip EHCI controller on the SuperH.
If you use the PCI EHCI controller, this option is not necessary.
config USB_EHCI_EXYNOS
- tristate "EHCI support for Samsung S5P/EXYNOS SoC Series"
- depends on ARCH_S5PV210 || ARCH_EXYNOS
- help
- Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
+ tristate "EHCI support for Samsung S5P/Exynos SoC Series"
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
+ help
+ Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
config USB_EHCI_MV
tristate "EHCI support for Marvell PXA/MMP USB controller"
- depends on (ARCH_PXA || ARCH_MMP)
+ depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST
select USB_EHCI_ROOT_HUB_TT
- ---help---
+ help
Enables support for Marvell (including PXA and MMP series) on-chip
USB SPH and OTG controller. SPH is a single port host, and it can
only be EHCI host. OTG is controller that can switch to host mode.
@@ -289,9 +318,9 @@
config USB_CNS3XXX_EHCI
bool "Cavium CNS3XXX EHCI Module (DEPRECATED)"
- depends on ARCH_CNS3XXX
+ depends on ARCH_CNS3XXX || COMPILE_TEST
select USB_EHCI_HCD_PLATFORM
- ---help---
+ help
This option is deprecated now and the driver was removed, use
USB_EHCI_HCD_PLATFORM instead.
@@ -301,7 +330,7 @@
config USB_EHCI_HCD_PLATFORM
tristate "Generic EHCI driver for a platform device"
- ---help---
+ help
Adds an EHCI host driver for a generic platform device, which
provides a memory space and an irq.
@@ -326,7 +355,7 @@
config USB_OXU210HP_HCD
tristate "OXU210HP HCD support"
depends on HAS_IOMEM
- ---help---
+ help
The OXU210HP is an USB host/OTG/device controller. Enable this
option if your board has this chip. If unsure, say N.
@@ -339,7 +368,7 @@
config USB_ISP116X_HCD
tristate "ISP116X HCD support"
depends on HAS_IOMEM
- ---help---
+ help
The ISP1160 and ISP1161 chips are USB host controllers. Enable this
option if your board has this chip. If unsure, say N.
@@ -352,7 +381,7 @@
tristate "ISP1362 HCD support"
depends on HAS_IOMEM
depends on COMPILE_TEST # nothing uses this
- ---help---
+ help
Supports the Philips ISP1362 chip as a host controller
This driver does not support isochronous transfers.
@@ -363,7 +392,7 @@
config USB_FOTG210_HCD
tristate "FOTG210 HCD support"
depends on USB && HAS_DMA && HAS_IOMEM
- ---help---
+ help
Faraday FOTG210 is an OTG controller which can be configured as
an USB2.0 host. It is designed to meet USB2.0 EHCI specification
with minor modification.
@@ -374,7 +403,7 @@
config USB_MAX3421_HCD
tristate "MAX3421 HCD (USB-over-SPI) support"
depends on USB && SPI
- ---help---
+ help
The Maxim MAX3421E chip supports standard USB 2.0-compliant
full-speed devices either in host or peripheral mode. This
driver supports the host-mode of the MAX3421E only.
@@ -385,7 +414,7 @@
config USB_OHCI_HCD
tristate "OHCI HCD (USB 1.1) support"
depends on HAS_DMA && HAS_IOMEM
- ---help---
+ help
The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's
UHCI specification. If your USB host controller follows the OHCI spec,
@@ -405,20 +434,20 @@
depends on ARCH_OMAP1
depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
default y
- ---help---
+ help
Enables support for the OHCI controller on OMAP1/2 chips.
config USB_OHCI_HCD_SPEAR
- tristate "Support for ST SPEAr on-chip OHCI USB controller"
- depends on USB_OHCI_HCD && PLAT_SPEAR
- default y
- ---help---
- Enables support for the on-chip OHCI controller on
- ST SPEAr chips.
+ tristate "Support for ST SPEAr on-chip OHCI USB controller"
+ depends on USB_OHCI_HCD && (PLAT_SPEAR || COMPILE_TEST)
+ default y if PLAT_SPEAR
+ help
+ Enables support for the on-chip OHCI controller on
+ ST SPEAr chips.
config USB_OHCI_HCD_STI
tristate "Support for ST STiHxxx on-chip OHCI USB controller"
- depends on ARCH_STI && OF
+ depends on (ARCH_STI || COMPILE_TEST) && OF
select GENERIC_PHY
select USB_OHCI_HCD_PLATFORM
help
@@ -426,12 +455,12 @@
STMicroelectronics consumer electronics SoC's.
config USB_OHCI_HCD_S3C2410
- tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series"
- depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX)
- default y
- ---help---
- Enables support for the on-chip OHCI controller on
- S3C24xx/S3C64xx chips.
+ tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series"
+ depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX || COMPILE_TEST)
+ default y if (ARCH_S3C24XX || ARCH_S3C64XX)
+ help
+ Enables support for the on-chip OHCI controller on
+ S3C24xx/S3C64xx chips.
config USB_OHCI_HCD_LPC32XX
tristate "Support for LPC on-chip OHCI USB controller"
@@ -439,31 +468,31 @@
depends on ARCH_LPC32XX || COMPILE_TEST
depends on USB_ISP1301
default y
- ---help---
- Enables support for the on-chip OHCI controller on
- NXP chips.
+ help
+ Enables support for the on-chip OHCI controller on
+ NXP chips.
config USB_OHCI_HCD_PXA27X
tristate "Support for PXA27X/PXA3XX on-chip OHCI USB controller"
depends on USB_OHCI_HCD && (PXA27x || PXA3xx)
default y
- ---help---
+ help
Enables support for the on-chip OHCI controller on
PXA27x/PXA3xx chips.
config USB_OHCI_HCD_AT91
tristate "Support for Atmel on-chip OHCI USB controller"
- depends on USB_OHCI_HCD && ARCH_AT91 && OF
- default y
- ---help---
- Enables support for the on-chip OHCI controller on
- Atmel chips.
+ depends on USB_OHCI_HCD && (ARCH_AT91 || COMPILE_TEST) && OF
+ default y if ARCH_AT91
+ help
+ Enables support for the on-chip OHCI controller on
+ Atmel chips.
config USB_OHCI_HCD_OMAP3
tristate "OHCI support for OMAP3 and later chips"
- depends on (ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5)
+ depends on ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST
select USB_OHCI_HCD_PLATFORM
- default y
+ default y if ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5
help
This option is deprecated now and the driver was removed, use
USB_OHCI_HCD_PLATFORM instead.
@@ -473,10 +502,10 @@
config USB_OHCI_HCD_DAVINCI
tristate "OHCI support for TI DaVinci DA8xx"
- depends on ARCH_DAVINCI_DA8XX
+ depends on ARCH_DAVINCI_DA8XX || COMPILE_TEST
depends on USB_OHCI_HCD
select PHY_DA8XX_USB
- default y
+ default y if ARCH_DAVINCI_DA8XX
help
Enables support for the DaVinci DA8xx integrated OHCI
controller. This driver cannot currently be a loadable
@@ -487,7 +516,7 @@
depends on PPC
select USB_OHCI_BIG_ENDIAN_DESC
select USB_OHCI_BIG_ENDIAN_MMIO
- ---help---
+ help
Enables support for big-endian USB controllers present on the
OpenFirmware platform bus.
@@ -495,7 +524,7 @@
bool "OHCI support for OF platform bus (little endian)"
depends on PPC
select USB_OHCI_LITTLE_ENDIAN
- ---help---
+ help
Enables support for little-endian USB controllers present on the
OpenFirmware platform bus.
@@ -509,7 +538,7 @@
depends on USB_PCI
default y
select USB_OHCI_LITTLE_ENDIAN
- ---help---
+ help
Enables support for PCI-bus plug-in USB controller cards.
If unsure, say Y.
@@ -518,7 +547,7 @@
depends on (SSB = y || SSB = USB_OHCI_HCD)
select USB_HCD_SSB
select USB_OHCI_HCD_PLATFORM
- ---help---
+ help
This option is deprecated now and the driver was removed, use
USB_HCD_SSB and USB_OHCI_HCD_PLATFORM instead.
@@ -532,9 +561,9 @@
config USB_OHCI_SH
bool "OHCI support for SuperH USB controller (DEPRECATED)"
- depends on SUPERH
+ depends on SUPERH || COMPILE_TEST
select USB_OHCI_HCD_PLATFORM
- ---help---
+ help
This option is deprecated now and the driver was removed, use
USB_OHCI_HCD_PLATFORM instead.
@@ -542,16 +571,16 @@
If you use the PCI OHCI controller, this option is not necessary.
config USB_OHCI_EXYNOS
- tristate "OHCI support for Samsung S5P/EXYNOS SoC Series"
- depends on ARCH_S5PV210 || ARCH_EXYNOS
+ tristate "OHCI support for Samsung S5P/Exynos SoC Series"
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
help
- Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
+ Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
config USB_CNS3XXX_OHCI
bool "Cavium CNS3XXX OHCI Module (DEPRECATED)"
- depends on ARCH_CNS3XXX
+ depends on ARCH_CNS3XXX || COMPILE_TEST
select USB_OHCI_HCD_PLATFORM
- ---help---
+ help
This option is deprecated now and the driver was removed, use
USB_OHCI_HCD_PLATFORM instead.
@@ -560,7 +589,7 @@
config USB_OHCI_HCD_PLATFORM
tristate "Generic OHCI driver for a platform device"
- ---help---
+ help
Adds an OHCI host driver for a generic platform device, which
provides a memory space and an irq.
@@ -586,7 +615,7 @@
config USB_UHCI_HCD
tristate "UHCI HCD (most Intel and VIA) support"
depends on USB_PCI || USB_UHCI_SUPPORT_NON_PCI_HC
- ---help---
+ help
The Universal Host Controller Interface is a standard by Intel for
accessing the USB hardware in the PC (which is also called the USB
host controller). If your USB host controller conforms to this
@@ -609,8 +638,8 @@
default y if (ARCH_VT8500 || ARCH_ASPEED)
config USB_UHCI_ASPEED
- bool
- default y if ARCH_ASPEED
+ bool
+ default y if ARCH_ASPEED
config USB_FHCI_HCD
tristate "Freescale QE USB Host Controller support"
@@ -713,14 +742,14 @@
module will be called renesas-usbhs.
config USB_IMX21_HCD
- tristate "i.MX21 HCD support"
- depends on ARM && ARCH_MXC
- help
- This driver enables support for the on-chip USB host in the
- i.MX21 processor.
+ tristate "i.MX21 HCD support"
+ depends on ARM && ARCH_MXC
+ help
+ This driver enables support for the on-chip USB host in the
+ i.MX21 processor.
- To compile this driver as a module, choose M here: the
- module will be called "imx21-hcd".
+ To compile this driver as a module, choose M here: the
+ module will be called "imx21-hcd".
config USB_HCD_BCMA
tristate "BCMA usb host driver"
@@ -748,7 +777,7 @@
config USB_HCD_TEST_MODE
bool "HCD test mode support"
- ---help---
+ help
Say 'Y' to enable additional software test modes that may be
supported by the host controller drivers.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index b191361..bc73133 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -49,6 +49,7 @@
obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
+obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
@@ -71,6 +72,7 @@
obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
+obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o
obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o
diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c
index 2400a82..2df52f7 100644
--- a/drivers/usb/host/bcma-hcd.c
+++ b/drivers/usb/host/bcma-hcd.c
@@ -168,7 +168,7 @@
}
}
-/**
+/*
* bcma_hcd_usb20_old_arm_init - Initialize old USB 2.0 controller on ARM
*
* Old USB 2.0 core is identified as BCMA_CORE_USB20_HOST and was introduced
@@ -261,7 +261,7 @@
usleep_range(1000, 2000);
}
-/**
+/*
* bcma_hcd_usb20_ns_init - Initialize Northstar USB 2.0 controller
*/
static int bcma_hcd_usb20_ns_init(struct bcma_hcd_device *bcma_hcd)
@@ -495,15 +495,4 @@
.suspend = bcma_hcd_suspend,
.resume = bcma_hcd_resume,
};
-
-static int __init bcma_hcd_init(void)
-{
- return bcma_driver_register(&bcma_hcd_driver);
-}
-module_init(bcma_hcd_init);
-
-static void __exit bcma_hcd_exit(void)
-{
- bcma_driver_unregister(&bcma_hcd_driver);
-}
-module_exit(bcma_hcd_exit);
+module_bcma_driver(bcma_hcd_driver);
diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c
new file mode 100644
index 0000000..3e0ebe8
--- /dev/null
+++ b/drivers/usb/host/ehci-brcm.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2020, Broadcom */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/iopoll.h>
+
+#include "ehci.h"
+
+#define hcd_to_ehci_priv(h) ((struct brcm_priv *)hcd_to_ehci(h)->priv)
+
+struct brcm_priv {
+ struct clk *clk;
+};
+
+/*
+ * ehci_brcm_wait_for_sof
+ * Wait for start of next microframe, then wait extra delay microseconds
+ */
+static inline void ehci_brcm_wait_for_sof(struct ehci_hcd *ehci, u32 delay)
+{
+ u32 frame_idx = ehci_readl(ehci, &ehci->regs->frame_index);
+ u32 val;
+ int res;
+
+ /* Wait for next microframe (every 125 usecs) */
+ res = readl_relaxed_poll_timeout(&ehci->regs->frame_index, val,
+ val != frame_idx, 1, 130);
+ if (res)
+ ehci_err(ehci, "Error waiting for SOF\n");
+ udelay(delay);
+}
+
+/*
+ * ehci_brcm_hub_control
+ * The EHCI controller has a bug where it can violate the SOF
+ * interval between the first two SOF's transmitted after resume
+ * if the resume occurs near the end of the microframe. This causees
+ * the controller to detect babble on the suspended port and
+ * will eventually cause the controller to reset the port.
+ * The fix is to Intercept the echi-hcd request to complete RESUME and
+ * align it to the start of the next microframe.
+ * See SWLINUX-1909 for more details
+ */
+static int ehci_brcm_hub_control(
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int ports = HCS_N_PORTS(ehci->hcs_params);
+ u32 __iomem *status_reg;
+ unsigned long flags;
+ int retval, irq_disabled = 0;
+
+ status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
+
+ /*
+ * RESUME is cleared when GetPortStatus() is called 20ms after start
+ * of RESUME
+ */
+ if ((typeReq == GetPortStatus) &&
+ (wIndex && wIndex <= ports) &&
+ ehci->reset_done[wIndex-1] &&
+ time_after_eq(jiffies, ehci->reset_done[wIndex-1]) &&
+ (ehci_readl(ehci, status_reg) & PORT_RESUME)) {
+
+ /*
+ * to make sure we are not interrupted until RESUME bit
+ * is cleared, disable interrupts on current CPU
+ */
+ ehci_dbg(ehci, "SOF alignment workaround\n");
+ irq_disabled = 1;
+ local_irq_save(flags);
+ ehci_brcm_wait_for_sof(ehci, 5);
+ }
+ retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+ if (irq_disabled)
+ local_irq_restore(flags);
+ return retval;
+}
+
+static int ehci_brcm_reset(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int len;
+
+ ehci->big_endian_mmio = 1;
+
+ ehci->caps = (void __iomem *)hcd->regs;
+ len = HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+ ehci->regs = (void __iomem *)(hcd->regs + len);
+
+ /* This fixes the lockup during reboot due to prior interrupts */
+ ehci_writel(ehci, CMD_RESET, &ehci->regs->command);
+ mdelay(10);
+
+ /*
+ * SWLINUX-1705: Avoid OUT packet underflows during high memory
+ * bus usage
+ * port_status[0x0f] = Broadcom-proprietary USB_EHCI_INSNREG00 @ 0x90
+ */
+ ehci_writel(ehci, 0x00800040, &ehci->regs->port_status[0x10]);
+ ehci_writel(ehci, 0x00000001, &ehci->regs->port_status[0x12]);
+
+ return ehci_setup(hcd);
+}
+
+static struct hc_driver __read_mostly ehci_brcm_hc_driver;
+
+static const struct ehci_driver_overrides brcm_overrides __initconst = {
+ .reset = ehci_brcm_reset,
+ .extra_priv_size = sizeof(struct brcm_priv),
+};
+
+static int ehci_brcm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res_mem;
+ struct brcm_priv *priv;
+ struct usb_hcd *hcd;
+ int irq;
+ int err;
+
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq ? irq : -EINVAL;
+
+ /* Hook the hub control routine to work around a bug */
+ ehci_brcm_hc_driver.hub_control = ehci_brcm_hub_control;
+
+ /* initialize hcd */
+ hcd = usb_create_hcd(&ehci_brcm_hc_driver, dev, dev_name(dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, hcd);
+ priv = hcd_to_ehci_priv(hcd);
+
+ priv->clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ err = PTR_ERR(priv->clk);
+ goto err_hcd;
+ }
+
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ goto err_hcd;
+
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res_mem);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto err_clk;
+ }
+ hcd->rsrc_start = res_mem->start;
+ hcd->rsrc_len = resource_size(res_mem);
+ err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (err)
+ goto err_clk;
+
+ device_wakeup_enable(hcd->self.controller);
+ device_enable_async_suspend(hcd->self.controller);
+
+ return 0;
+
+err_clk:
+ clk_disable_unprepare(priv->clk);
+err_hcd:
+ usb_put_hcd(hcd);
+
+ return err;
+}
+
+static int ehci_brcm_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
+
+ usb_remove_hcd(hcd);
+ clk_disable_unprepare(priv->clk);
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+static int __maybe_unused ehci_brcm_suspend(struct device *dev)
+{
+ int ret;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
+ bool do_wakeup = device_may_wakeup(dev);
+
+ ret = ehci_suspend(hcd, do_wakeup);
+ if (ret)
+ return ret;
+ clk_disable_unprepare(priv->clk);
+ return 0;
+}
+
+static int __maybe_unused ehci_brcm_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
+ int err;
+
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ return err;
+ /*
+ * SWLINUX-1705: Avoid OUT packet underflows during high memory
+ * bus usage
+ * port_status[0x0f] = Broadcom-proprietary USB_EHCI_INSNREG00
+ * @ 0x90
+ */
+ ehci_writel(ehci, 0x00800040, &ehci->regs->port_status[0x10]);
+ ehci_writel(ehci, 0x00000001, &ehci->regs->port_status[0x12]);
+
+ ehci_resume(hcd, false);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ehci_brcm_pm_ops, ehci_brcm_suspend,
+ ehci_brcm_resume);
+
+static const struct of_device_id brcm_ehci_of_match[] = {
+ { .compatible = "brcm,ehci-brcm-v2", },
+ { .compatible = "brcm,bcm7445-ehci", },
+ {}
+};
+
+static struct platform_driver ehci_brcm_driver = {
+ .probe = ehci_brcm_probe,
+ .remove = ehci_brcm_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ehci-brcm",
+ .pm = &ehci_brcm_pm_ops,
+ .of_match_table = brcm_ehci_of_match,
+ }
+};
+
+static int __init ehci_brcm_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ ehci_init_driver(&ehci_brcm_hc_driver, &brcm_overrides);
+ return platform_driver_register(&ehci_brcm_driver);
+}
+module_init(ehci_brcm_init);
+
+static void __exit ehci_brcm_exit(void)
+{
+ platform_driver_unregister(&ehci_brcm_driver);
+}
+module_exit(ehci_brcm_exit);
+
+MODULE_ALIAS("platform:ehci-brcm");
+MODULE_DESCRIPTION("EHCI Broadcom STB driver");
+MODULE_AUTHOR("Al Cooper");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 7619cfb..0b7f1ed 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -823,7 +823,7 @@
break;
case 0: /* illegal reserved capability */
cap = 0;
- /* FALLTHROUGH */
+ fallthrough;
default: /* unknown */
break;
}
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index 84d59a6..1a9b757 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * SAMSUNG EXYNOS USB HOST EHCI Controller
+ * Samsung Exynos USB HOST EHCI Controller
*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Author: Jingoo Han <jg1.han@samsung.com>
@@ -21,7 +21,7 @@
#include "ehci.h"
-#define DRIVER_DESC "EHCI EXYNOS driver"
+#define DRIVER_DESC "EHCI Exynos driver"
#define EHCI_INSNREG00(base) (base + 0x90)
#define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25)
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 9e9c232..1e8b59a 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -234,7 +234,7 @@
break;
case FSL_USB2_PHY_UTMI_WIDE:
portsc |= PORT_PTS_PTW;
- /* fall through */
+ fallthrough;
case FSL_USB2_PHY_UTMI:
/* Presence of this node "has_fsl_erratum_a006918"
* in device-tree is used to stop USB controller
@@ -244,7 +244,7 @@
dev_warn(dev, "USB PHY clock invalid\n");
return -EINVAL;
}
- /* fall through */
+ fallthrough;
case FSL_USB2_PHY_UTMI_DUAL:
/* PHY_CLK_VALID bit is de-featured from all controller
* versions below 2.4 and is to be checked only for
@@ -683,7 +683,7 @@
/**
* fsl_ehci_drv_remove - shutdown processing for FSL-based HCDs
- * @dev: USB Host Controller being removed
+ * @pdev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_fsl_probe().
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
index 9d18c6e..c95341d 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2005-2010,2012 Freescale Semiconductor, Inc.
* Copyright (c) 2005 MontaVista Software
*/
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 5c6ce1e..8aff19f 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -634,7 +634,16 @@
/* Wait until HC become operational */
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
msleep(5);
- rc = ehci_handshake(ehci, &ehci->regs->status, STS_HALT, 0, 100 * 1000);
+
+ /* For Aspeed, STS_HALT also depends on ASS/PSS status.
+ * Check CMD_RUN instead.
+ */
+ if (ehci->is_aspeed)
+ rc = ehci_handshake(ehci, &ehci->regs->command, CMD_RUN,
+ 1, 100 * 1000);
+ else
+ rc = ehci_handshake(ehci, &ehci->regs->status, STS_HALT,
+ 0, 100 * 1000);
up_write(&ehci_cf_port_reset_rwsem);
@@ -703,7 +712,8 @@
static irqreturn_t ehci_irq (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
- u32 status, masked_status, pcd_status = 0, cmd;
+ u32 status, current_status, masked_status, pcd_status = 0;
+ u32 cmd;
int bh;
unsigned long flags;
@@ -715,19 +725,22 @@
*/
spin_lock_irqsave(&ehci->lock, flags);
- status = ehci_readl(ehci, &ehci->regs->status);
+ status = 0;
+ current_status = ehci_readl(ehci, &ehci->regs->status);
+restart:
/* e.g. cardbus physical eject */
- if (status == ~(u32) 0) {
+ if (current_status == ~(u32) 0) {
ehci_dbg (ehci, "device removed\n");
goto dead;
}
+ status |= current_status;
/*
* We don't use STS_FLR, but some controllers don't like it to
* remain on, so mask it out along with the other status bits.
*/
- masked_status = status & (INTR_MASK | STS_FLR);
+ masked_status = current_status & (INTR_MASK | STS_FLR);
/* Shared IRQ? */
if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) {
@@ -737,6 +750,12 @@
/* clear (just) interrupts */
ehci_writel(ehci, masked_status, &ehci->regs->status);
+
+ /* For edge interrupts, don't race with an interrupt bit being raised */
+ current_status = ehci_readl(ehci, &ehci->regs->status);
+ if (current_status & INTR_MASK)
+ goto restart;
+
cmd = ehci_readl(ehci, &ehci->regs->command);
bh = 0;
@@ -995,7 +1014,7 @@
start_unlink_async(ehci, qh);
else
start_unlink_intr(ehci, qh);
- /* FALL THROUGH */
+ fallthrough;
case QH_STATE_COMPLETING: /* already in unlinking */
case QH_STATE_UNLINK: /* wait for hw to finish? */
case QH_STATE_UNLINK_WAIT:
@@ -1012,7 +1031,7 @@
qh_destroy(ehci, qh);
break;
}
- /* fall through */
+ fallthrough;
default:
/* caller was supposed to have unlinked any requests;
* that's not our job. just leak this memory.
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index b0e0f8e..8fd2724 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -11,6 +11,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/usb/otg.h>
+#include <linux/usb/of.h>
#include <linux/platform_data/mv_usb.h>
#include <linux/io.h>
@@ -66,6 +67,8 @@
{
struct device *dev = hcd->self.controller;
struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 status;
int retval;
if (ehci_mv == NULL) {
@@ -79,6 +82,14 @@
if (retval)
dev_err(dev, "ehci_setup failed %d\n", retval);
+ if (of_usb_get_phy_mode(dev->of_node) == USBPHY_INTERFACE_MODE_HSIC) {
+ status = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ status |= PORT_TEST_FORCE;
+ ehci_writel(ehci, status, &ehci->regs->port_status[0]);
+ status &= ~PORT_TEST_FORCE;
+ ehci_writel(ehci, status, &ehci->regs->port_status[0]);
+ }
+
return retval;
}
@@ -96,13 +107,14 @@
struct ehci_hcd *ehci;
struct ehci_hcd_mv *ehci_mv;
struct resource *r;
- int retval = -ENODEV;
+ int retval;
u32 offset;
+ u32 status;
if (usb_disabled())
return -ENODEV;
- hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, "mv ehci");
+ hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd)
return -ENOMEM;
@@ -115,7 +127,7 @@
ehci_mv->set_vbus = pdata->set_vbus;
}
- ehci_mv->phy = devm_phy_get(&pdev->dev, "usb");
+ ehci_mv->phy = devm_phy_optional_get(&pdev->dev, "usb");
if (IS_ERR(ehci_mv->phy)) {
retval = PTR_ERR(ehci_mv->phy);
if (retval != -EPROBE_DEFER)
@@ -130,8 +142,6 @@
goto err_put_hcd;
}
-
-
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ehci_mv->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(ehci_mv->base)) {
@@ -161,7 +171,7 @@
hcd->irq = retval;
ehci = hcd_to_ehci(hcd);
- ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
+ ehci->caps = (struct ehci_caps __iomem *) ehci_mv->cap_regs;
if (ehci_mv->mode == MV_USB_MODE_OTG) {
ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
@@ -199,6 +209,14 @@
device_wakeup_enable(hcd->self.controller);
}
+ if (of_usb_get_phy_mode(pdev->dev.of_node) == USBPHY_INTERFACE_MODE_HSIC) {
+ status = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ /* These "reserved" bits actually enable HSIC mode. */
+ status |= BIT(25);
+ status &= ~GENMASK(31, 30);
+ ehci_writel(ehci, status, &ehci->regs->port_status[0]);
+ }
+
dev_info(&pdev->dev,
"successful find EHCI device with regs 0x%p irq %d"
" working in %s mode\n", hcd->regs, hcd->irq,
@@ -243,10 +261,8 @@
MODULE_ALIAS("mv-ehci");
static const struct platform_device_id ehci_id_table[] = {
- {"pxa-u2oehci", PXA_U2OEHCI},
- {"pxa-sph", PXA_SPH},
- {"mmp3-hsic", MMP3_HSIC},
- {"mmp3-fsic", MMP3_FSIC},
+ {"pxa-u2oehci", 0},
+ {"pxa-sph", 0},
{},
};
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
index 7f65c86..dc26763 100644
--- a/drivers/usb/host/ehci-mxc.c
+++ b/drivers/usb/host/ehci-mxc.c
@@ -36,12 +36,12 @@
static int ehci_mxc_drv_probe(struct platform_device *pdev)
{
- struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ struct mxc_usbh_platform_data *pdata = dev_get_platdata(dev);
struct usb_hcd *hcd;
struct resource *res;
int irq, ret;
struct ehci_mxc_priv *priv;
- struct device *dev = &pdev->dev;
struct ehci_hcd *ehci;
if (!pdata) {
@@ -58,7 +58,7 @@
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs);
goto err_alloc;
@@ -71,14 +71,14 @@
priv = (struct ehci_mxc_priv *) ehci->priv;
/* enable clocks */
- priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
+ priv->usbclk = devm_clk_get(dev, "ipg");
if (IS_ERR(priv->usbclk)) {
ret = PTR_ERR(priv->usbclk);
goto err_alloc;
}
clk_prepare_enable(priv->usbclk);
- priv->ahbclk = devm_clk_get(&pdev->dev, "ahb");
+ priv->ahbclk = devm_clk_get(dev, "ahb");
if (IS_ERR(priv->ahbclk)) {
ret = PTR_ERR(priv->ahbclk);
goto err_clk_ahb;
@@ -86,13 +86,12 @@
clk_prepare_enable(priv->ahbclk);
/* "dr" device has its own clock on i.MX51 */
- priv->phyclk = devm_clk_get(&pdev->dev, "phy");
+ priv->phyclk = devm_clk_get(dev, "phy");
if (IS_ERR(priv->phyclk))
priv->phyclk = NULL;
if (priv->phyclk)
clk_prepare_enable(priv->phyclk);
-
/* call platform specific init function */
if (pdata->init) {
ret = pdata->init(pdev);
diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c
index adaf8fb..6b5a7a8 100644
--- a/drivers/usb/host/ehci-npcm7xx.c
+++ b/drivers/usb/host/ehci-npcm7xx.c
@@ -37,8 +37,7 @@
static struct hc_driver __read_mostly ehci_npcm7xx_hc_driver;
-#ifdef CONFIG_PM_SLEEP
-static int ehci_npcm7xx_drv_suspend(struct device *dev)
+static int __maybe_unused ehci_npcm7xx_drv_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
bool do_wakeup = device_may_wakeup(dev);
@@ -46,14 +45,13 @@
return ehci_suspend(hcd, do_wakeup);
}
-static int ehci_npcm7xx_drv_resume(struct device *dev)
+static int __maybe_unused ehci_npcm7xx_drv_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
ehci_resume(hcd, false);
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(ehci_npcm7xx_pm_ops, ehci_npcm7xx_drv_suspend,
ehci_npcm7xx_drv_resume);
@@ -183,7 +181,7 @@
.driver = {
.name = "npcm7xx-ehci",
.bus = &platform_bus_type,
- .pm = &ehci_npcm7xx_pm_ops,
+ .pm = pm_ptr(&ehci_npcm7xx_pm_ops),
.of_match_table = npcm7xx_ehci_id_table,
}
};
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 03122dc..7f4a03e 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -24,7 +24,6 @@
#include <linux/slab.h>
#include <linux/usb/ulpi.h>
#include <linux/pm_runtime.h>
-#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
@@ -78,6 +77,7 @@
/**
* ehci_hcd_omap_probe - initialize TI-based HCDs
+ * @pdev: Pointer to this platform device's information
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 774ccaa..e87cf3a 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -124,8 +124,7 @@
case 0x005b: /* CK804 */
case 0x00d8: /* CK8 */
case 0x00e8: /* CK8S */
- if (pci_set_consistent_dma_mask(pdev,
- DMA_BIT_MASK(31)) < 0)
+ if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(31)) < 0)
ehci_warn(ehci, "can't enable NVidia "
"workaround for >2GB RAM\n");
break;
@@ -370,23 +369,21 @@
{
if (is_bypassed_id(pdev))
return -ENODEV;
- return usb_hcd_pci_probe(pdev, id);
+ return usb_hcd_pci_probe(pdev, id, &ehci_pci_hc_driver);
}
static void ehci_pci_remove(struct pci_dev *pdev)
{
pci_clear_mwi(pdev);
- usb_hcd_pci_remove(pdev);
+ usb_hcd_pci_remove(pdev);
}
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids [] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0),
- .driver_data = (unsigned long) &ehci_pci_hc_driver,
}, {
PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_HOST),
- .driver_data = (unsigned long) &ehci_pci_hc_driver,
},
{ /* end: all zeroes */ }
};
@@ -394,7 +391,7 @@
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ehci_pci_driver = {
- .name = (char *) hcd_name,
+ .name = hcd_name,
.id_table = pci_ids,
.probe = ehci_pci_probe,
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index e4fc3f6..2dcfc67 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -42,6 +42,9 @@
#define EHCI_MAX_CLKS 4
#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv)
+#define BCM_USB_FIFO_THRESHOLD 0x00800040
+#define bcm_iproc_insnreg01 hostpc[0]
+
struct ehci_platform_priv {
struct clk *clks[EHCI_MAX_CLKS];
struct reset_control *rsts;
@@ -75,6 +78,11 @@
if (pdata->no_io_watchdog)
ehci->need_io_watchdog = 0;
+
+ if (of_device_is_compatible(pdev->dev.of_node, "brcm,xgs-iproc-ehci"))
+ ehci_writel(ehci, BCM_USB_FIFO_THRESHOLD,
+ &ehci->regs->bcm_iproc_insnreg01);
+
return 0;
}
@@ -286,6 +294,12 @@
"has-transaction-translator"))
hcd->has_tt = 1;
+ if (of_device_is_compatible(dev->dev.of_node,
+ "aspeed,ast2500-ehci") ||
+ of_device_is_compatible(dev->dev.of_node,
+ "aspeed,ast2600-ehci"))
+ ehci->is_aspeed = 1;
+
if (soc_device_match(quirk_poll_match))
priv->quirk_poll = true;
@@ -410,8 +424,7 @@
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int ehci_platform_suspend(struct device *dev)
+static int __maybe_unused ehci_platform_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
@@ -433,7 +446,7 @@
return ret;
}
-static int ehci_platform_resume(struct device *dev)
+static int __maybe_unused ehci_platform_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
@@ -455,12 +468,15 @@
ehci_resume(hcd, priv->reset_on_resume);
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
if (priv->quirk_poll)
quirk_poll_init(priv);
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct of_device_id vt8500_ehci_ids[] = {
{ .compatible = "via,vt8500-ehci", },
@@ -471,11 +487,13 @@
};
MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id ehci_acpi_match[] = {
{ "PNP0D20", 0 }, /* EHCI controller without debug */
{ }
};
MODULE_DEVICE_TABLE(acpi, ehci_acpi_match);
+#endif
static const struct platform_device_id ehci_platform_table[] = {
{ "ehci-platform", 0 },
@@ -493,7 +511,7 @@
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ehci-platform",
- .pm = &ehci_platform_pm_ops,
+ .pm = pm_ptr(&ehci_platform_pm_ops),
.of_match_table = vt8500_ehci_ids,
.acpi_match_table = ACPI_PTR(ehci_acpi_match),
}
diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c
index a2b610d..2d462fb 100644
--- a/drivers/usb/host/ehci-pmcmsp.c
+++ b/drivers/usb/host/ehci-pmcmsp.c
@@ -107,7 +107,7 @@
if (!request_mem_region(res->start, res_len, "mab regs"))
return -EBUSY;
- dev->mab_regs = ioremap_nocache(res->start, res_len);
+ dev->mab_regs = ioremap(res->start, res_len);
if (dev->mab_regs == NULL) {
retval = -ENOMEM;
goto err1;
@@ -124,7 +124,7 @@
retval = -EBUSY;
goto err2;
}
- dev->usbid_regs = ioremap_nocache(res->start, res_len);
+ dev->usbid_regs = ioremap(res->start, res_len);
if (dev->usbid_regs == NULL) {
retval = -ENOMEM;
goto err3;
@@ -178,7 +178,7 @@
retval = -EBUSY;
goto err1;
}
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 8a5c9b3..a826715 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -874,7 +874,7 @@
switch (urb->dev->speed) {
case USB_SPEED_LOW:
info1 |= QH_LOW_SPEED;
- /* FALL THROUGH */
+ fallthrough;
case USB_SPEED_FULL:
/* EPS 0 means "full" */
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index da7b00a..6dfb242 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -307,26 +307,6 @@
#ifdef CONFIG_USB_EHCI_TT_NEWSCHED
-/* Which uframe does the low/fullspeed transfer start in?
- *
- * The parameter is the mask of ssplits in "H-frame" terms
- * and this returns the transfer start uframe in "B-frame" terms,
- * which allows both to match, e.g. a ssplit in "H-frame" uframe 0
- * will cause a transfer in "B-frame" uframe 0. "B-frames" lag
- * "H-frames" by 1 uframe. See the EHCI spec sec 4.5 and figure 4.7.
- */
-static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask)
-{
- unsigned char smask = hc32_to_cpu(ehci, mask) & QH_SMASK;
-
- if (!smask) {
- ehci_err(ehci, "invalid empty smask!\n");
- /* uframe 7 can't have bw so this will indicate failure */
- return 7;
- }
- return ffs(smask) - 1;
-}
-
static const unsigned char
max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 };
@@ -2475,7 +2455,7 @@
ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n",
type, frame, q.ptr);
/* BUG(); */
- /* FALL THROUGH */
+ fallthrough;
case Q_TYPE_QH:
case Q_TYPE_FSTN:
/* End of the iTDs and siTDs */
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
index 2afde14..c25c51d 100644
--- a/drivers/usb/host/ehci-sh.c
+++ b/drivers/usb/host/ehci-sh.c
@@ -8,7 +8,6 @@
*/
#include <linux/platform_device.h>
#include <linux/clk.h>
-#include <linux/platform_data/ehci-sh.h>
struct ehci_sh_priv {
struct clk *iclk, *fclk;
@@ -76,7 +75,6 @@
{
struct resource *res;
struct ehci_sh_priv *priv;
- struct ehci_sh_platdata *pdata;
struct usb_hcd *hcd;
int irq, ret;
@@ -89,8 +87,6 @@
goto fail_create_hcd;
}
- pdata = dev_get_platdata(&pdev->dev);
-
/* initialize hcd */
hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev,
dev_name(&pdev->dev));
@@ -127,9 +123,6 @@
clk_enable(priv->fclk);
clk_enable(priv->iclk);
- if (pdata && pdata->phy_init)
- pdata->phy_init();
-
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to add hcd");
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c
index add796c..3694e45 100644
--- a/drivers/usb/host/ehci-spear.c
+++ b/drivers/usb/host/ehci-spear.c
@@ -34,8 +34,7 @@
static struct hc_driver __read_mostly ehci_spear_hc_driver;
-#ifdef CONFIG_PM_SLEEP
-static int ehci_spear_drv_suspend(struct device *dev)
+static int __maybe_unused ehci_spear_drv_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
bool do_wakeup = device_may_wakeup(dev);
@@ -43,14 +42,13 @@
return ehci_suspend(hcd, do_wakeup);
}
-static int ehci_spear_drv_resume(struct device *dev)
+static int __maybe_unused ehci_spear_drv_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
ehci_resume(hcd, false);
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend,
ehci_spear_drv_resume);
@@ -155,7 +153,7 @@
.driver = {
.name = "spear-ehci",
.bus = &platform_bus_type,
- .pm = &ehci_spear_pm_ops,
+ .pm = pm_ptr(&ehci_spear_pm_ops),
.of_match_table = spear_ehci_id_table,
}
};
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 4d2cdec..869d9c4 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -42,12 +42,10 @@
};
struct tegra_ehci_hcd {
- struct tegra_usb_phy *phy;
struct clk *clk;
struct reset_control *rst;
int port_resuming;
bool needs_double_reset;
- enum tegra_usb_phy_port_speed port_speed;
};
static int tegra_reset_usb_controller(struct platform_device *pdev)
@@ -284,7 +282,7 @@
struct dma_aligned_buffer {
void *kmalloc_ptr;
void *old_xfer_buffer;
- u8 data[0];
+ u8 data[];
};
static void free_dma_aligned_buffer(struct urb *urb)
@@ -480,16 +478,9 @@
}
u_phy->otg->host = hcd_to_bus(hcd);
- err = usb_phy_set_suspend(hcd->usb_phy, 0);
- if (err) {
- dev_err(&pdev->dev, "Failed to power on the phy\n");
- goto cleanup_phy;
- }
-
irq = platform_get_irq(pdev, 0);
- if (!irq) {
- dev_err(&pdev->dev, "Failed to get IRQ\n");
- err = -ENODEV;
+ if (irq < 0) {
+ err = irq;
goto cleanup_phy;
}
@@ -521,16 +512,10 @@
struct tegra_ehci_hcd *tegra =
(struct tegra_ehci_hcd *)hcd_to_ehci(hcd)->priv;
- otg_set_host(hcd->usb_phy->otg, NULL);
-
- usb_phy_shutdown(hcd->usb_phy);
usb_remove_hcd(hcd);
-
- reset_control_assert(tegra->rst);
- udelay(1);
-
+ otg_set_host(hcd->usb_phy->otg, NULL);
+ usb_phy_shutdown(hcd->usb_phy);
clk_disable_unprepare(tegra->clk);
-
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index ac5e967..59fd523 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2001-2002 by David Brownell
*/
@@ -218,6 +218,7 @@
unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
unsigned need_oc_pp_cycle:1; /* MPC834X port power */
unsigned imx28_write_fix:1; /* For Freescale i.MX28 */
+ unsigned is_aspeed:1;
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
@@ -255,7 +256,7 @@
struct list_head tt_list;
/* platform-specific data -- must come last */
- unsigned long priv[0] __aligned(sizeof(s64));
+ unsigned long priv[] __aligned(sizeof(s64));
};
/* convert between an HCD pointer and the corresponding EHCI_HCD */
@@ -460,7 +461,7 @@
struct list_head td_list;
unsigned span;
unsigned first_packet;
- struct ehci_iso_packet packet[0];
+ struct ehci_iso_packet packet[];
};
/*
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index 0473387..a8e1048 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -396,6 +396,7 @@
case PIPE_CONTROL:
/* 1 td fro setup,1 for ack */
size = 2;
+ fallthrough;
case PIPE_BULK:
/* one td for every 4096 bytes(can be up to 8k) */
size += urb->transfer_buffer_length / 4096;
diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c
index 3235d53..a45ede8 100644
--- a/drivers/usb/host/fhci-sched.c
+++ b/drivers/usb/host/fhci-sched.c
@@ -677,7 +677,7 @@
enable_irq(fhci_to_hcd(fhci)->irq);
}
-DECLARE_TASKLET(fhci_tasklet, process_done_list, 0);
+DECLARE_TASKLET_OLD(fhci_tasklet, process_done_list);
/* transfer complted callback */
u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci)
@@ -701,7 +701,6 @@
u32 data_len = urb->transfer_buffer_length;
int urb_state = 0;
int toggle = 0;
- struct td *td;
u8 *data;
u16 cnt = 0;
@@ -770,7 +769,7 @@
usb_endpoint_maxp(&urb->ep->desc)) == 0))
urb_state = US_BULK0;
while (data_len > 4096) {
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
FHCI_TA_IN,
cnt ? USB_TD_TOGGLE_CARRY :
@@ -781,7 +780,7 @@
cnt++;
}
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
cnt ? USB_TD_TOGGLE_CARRY : toggle,
data, data_len, 0, 0, true);
@@ -789,7 +788,7 @@
if (urb->transfer_flags & URB_ZERO_PACKET &&
cnt < urb_priv->num_of_tds) {
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
FHCI_TA_IN,
USB_TD_TOGGLE_CARRY, NULL, 0, 0, 0, true);
@@ -798,7 +797,7 @@
break;
case FHCI_TF_INTR:
urb->start_frame = get_frame_num(fhci) + 1;
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN,
USB_TD_TOGGLE_DATA0, data, data_len,
urb->interval, urb->start_frame, true);
@@ -808,12 +807,12 @@
ed->max_pkt_size = usb_endpoint_maxp(&urb->ep->desc);
/* setup stage */
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP,
USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true);
/* data stage */
if (data_len > 0) {
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
FHCI_TA_IN,
USB_TD_TOGGLE_DATA1, data, data_len, 0, 0,
@@ -822,12 +821,12 @@
/* status stage */
if (data_len > 0)
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
(usb_pipeout(urb->pipe) ? FHCI_TA_IN :
FHCI_TA_OUT),
USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
else
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt++,
FHCI_TA_IN,
USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true);
@@ -844,7 +843,7 @@
*/
frame += cnt * urb->interval;
frame &= 0x07ff;
- td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
+ fhci_td_fill(fhci, urb, urb_priv, ed, cnt,
usb_pipeout(urb->pipe) ? FHCI_TA_OUT :
FHCI_TA_IN,
USB_TD_TOGGLE_DATA0,
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
index f3308ce..d98fd9e 100644
--- a/drivers/usb/host/fhci-tds.c
+++ b/drivers/usb/host/fhci-tds.c
@@ -467,17 +467,15 @@
/* Reset the Tx BD ring */
void fhci_flush_bds(struct fhci_usb *usb)
{
- u16 extra_data;
u16 td_status;
- u32 buf;
struct usb_td __iomem *td;
struct endpoint *ep = usb->ep0;
td = ep->td_base;
while (1) {
td_status = in_be16(&td->status);
- buf = in_be32(&td->buf_ptr);
- extra_data = in_be16(&td->extra);
+ in_be32(&td->buf_ptr);
+ in_be16(&td->extra);
/* if the TD is not empty - we'll confirm it as Timeout */
if (td_status & TD_R)
@@ -524,7 +522,6 @@
{
u8 mode;
u16 tb_ptr;
- u16 extra_data;
u16 td_status;
u32 buf_ptr;
struct usb_td __iomem *td;
@@ -538,7 +535,7 @@
td = cpm_muram_addr(tb_ptr);
td_status = in_be16(&td->status);
buf_ptr = in_be32(&td->buf_ptr);
- extra_data = in_be16(&td->extra);
+ in_be16(&td->extra);
do {
if (td_status & TD_R) {
out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
@@ -552,7 +549,7 @@
td = next_bd(ep->td_base, td, td_status);
td_status = in_be16(&td->status);
buf_ptr = in_be32(&td->buf_ptr);
- extra_data = in_be16(&td->extra);
+ in_be16(&td->extra);
} while ((td_status & TD_R) || buf_ptr);
fhci_td_transaction_confirm(usb);
diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h
index 2ce5031..81fbc01 100644
--- a/drivers/usb/host/fhci.h
+++ b/drivers/usb/host/fhci.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Freescale QUICC Engine USB Host Controller Driver
*
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index f457e08..ff0b345 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -32,6 +32,7 @@
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/clk.h>
#include <asm/byteorder.h>
@@ -883,18 +884,15 @@
u32 mask, u32 done, int usec)
{
u32 result;
+ int ret;
- do {
- result = fotg210_readl(fotg210, ptr);
- if (result == ~(u32)0) /* card removed */
- return -ENODEV;
- result &= mask;
- if (result == done)
- return 0;
- udelay(1);
- usec--;
- } while (usec > 0);
- return -ETIMEDOUT;
+ ret = readl_poll_timeout_atomic(ptr, result,
+ ((result & mask) == done ||
+ result == U32_MAX), 1, usec);
+ if (result == U32_MAX) /* card removed */
+ return -ENODEV;
+
+ return ret;
}
/* Force HC to halt state from unknown (EHCI spec section 2.3).
@@ -2804,7 +2802,7 @@
switch (urb->dev->speed) {
case USB_SPEED_LOW:
info1 |= QH_LOW_SPEED;
- /* FALL THROUGH */
+ fallthrough;
case USB_SPEED_FULL:
/* EPS 0 means "full" */
@@ -4631,7 +4629,7 @@
default:
fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n",
type, frame, q.ptr);
- /* FALL THROUGH */
+ fallthrough;
case Q_TYPE_QH:
case Q_TYPE_FSTN:
/* End of the iTDs and siTDs */
@@ -5007,7 +5005,6 @@
{
struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
u32 temp;
- u32 hcc_params;
hcd->uses_new_polling = 1;
@@ -5030,7 +5027,7 @@
* Scsi_Host.highmem_io, and so forth. It's readonly to all
* host side drivers though.
*/
- hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params);
+ fotg210_readl(fotg210, &fotg210->caps->hcc_params);
/*
* Philips, Intel, and maybe others need CMD_RUN before the
@@ -5409,7 +5406,7 @@
*/
if (tmp)
start_unlink_async(fotg210, qh);
- /* FALL THROUGH */
+ fallthrough;
case QH_STATE_UNLINK: /* wait for hw to finish? */
case QH_STATE_UNLINK_WAIT:
idle_timeout:
@@ -5423,7 +5420,7 @@
qh_destroy(fotg210, qh);
break;
}
- /* fall through */
+ fallthrough;
default:
/* caller was supposed to have unlinked any requests;
* that's not our job. just leak this memory.
@@ -5555,7 +5552,7 @@
iowrite32(value, &fotg210->regs->otgcsr);
}
-/**
+/*
* fotg210_hcd_probe - initialize faraday FOTG210 HCDs
*
* Allocates basic resources for this USB host controller, and
@@ -5643,8 +5640,10 @@
return retval;
failed_dis_clk:
- if (!IS_ERR(fotg210->pclk))
+ if (!IS_ERR(fotg210->pclk)) {
clk_disable_unprepare(fotg210->pclk);
+ clk_put(fotg210->pclk);
+ }
failed_put_hcd:
usb_put_hcd(hcd);
fail_create_hcd:
@@ -5652,7 +5651,7 @@
return retval;
}
-/**
+/*
* fotg210_hcd_remove - shutdown processing for EHCI HCDs
* @dev: USB Host Controller being removed
*
@@ -5662,8 +5661,10 @@
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd);
- if (!IS_ERR(fotg210->pclk))
+ if (!IS_ERR(fotg210->pclk)) {
clk_disable_unprepare(fotg210->pclk);
+ clk_put(fotg210->pclk);
+ }
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h
index 291add9..67f5951 100644
--- a/drivers/usb/host/fotg210.h
+++ b/drivers/usb/host/fotg210.h
@@ -490,7 +490,7 @@
struct fotg210_iso_sched {
struct list_head td_list;
unsigned span;
- struct fotg210_iso_packet packet[0];
+ struct fotg210_iso_packet packet[];
};
/*
diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c
index 7fcf1d9..02a1344 100644
--- a/drivers/usb/host/imx21-dbg.c
+++ b/drivers/usb/host/imx21-dbg.c
@@ -419,7 +419,7 @@
{
struct dentry *root;
- root = debugfs_create_dir(dev_name(imx21->dev), NULL);
+ root = debugfs_create_dir(dev_name(imx21->dev), usb_debug_root);
imx21->debug_root = root;
debugfs_create_file("status", S_IRUGO, root, imx21, &debug_status_fops);
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index 5835f99..b2716cb 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -224,7 +224,7 @@
((u32) maxpacket << DW0_MAXPKTSIZ));
}
-/**
+/*
* Copy buffer to data controller data memory.
* We cannot use memcpy_toio() because the hardware requires 32bit writes
*/
diff --git a/drivers/usb/host/imx21-hcd.h b/drivers/usb/host/imx21-hcd.h
index 7b9cf0a..96d1675 100644
--- a/drivers/usb/host/imx21-hcd.h
+++ b/drivers/usb/host/imx21-hcd.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Macros and prototypes for i.MX21
*
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index a87c0b2..3055d9a 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1019,7 +1019,7 @@
spin_lock_irqsave(&isp116x->lock, flags);
isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_OCIC);
spin_unlock_irqrestore(&isp116x->lock, flags);
- /* fall through */
+ fallthrough;
case C_HUB_LOCAL_POWER:
DBG("C_HUB_LOCAL_POWER\n");
break;
@@ -1421,10 +1421,10 @@
isp116x_write_reg32(isp116x, HCCONTROL,
(val & ~HCCONTROL_HCFS) |
HCCONTROL_USB_RESET);
- /* fall through */
+ fallthrough;
case HCCONTROL_USB_RESET:
ret = -EBUSY;
- /* fall through */
+ fallthrough;
default: /* HCCONTROL_USB_SUSPEND */
spin_unlock_irqrestore(&isp116x->lock, flags);
break;
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 96f8daa..2cecb36 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -1748,7 +1748,7 @@
isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
isp1362_hcd->hc_control |= OHCI_USB_RESET;
isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- /* FALL THROUGH */
+ fallthrough;
case OHCI_USB_RESET:
status = -EBUSY;
pr_warn("%s: needs reinit!\n", __func__);
@@ -2627,7 +2627,7 @@
{
struct usb_hcd *hcd;
struct isp1362_hcd *isp1362_hcd;
- struct resource *addr, *data, *irq_res;
+ struct resource *data, *irq_res;
void __iomem *addr_reg;
void __iomem *data_reg;
int irq;
@@ -2651,8 +2651,7 @@
irq = irq_res->start;
- addr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- addr_reg = devm_ioremap_resource(&pdev->dev, addr);
+ addr_reg = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(addr_reg))
return PTR_ERR(addr_reg);
diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h
index 4c49688..9bbfcc3 100644
--- a/drivers/usb/host/isp1362.h
+++ b/drivers/usb/host/isp1362.h
@@ -56,7 +56,7 @@
#define ISP1362_REG_NO(r) (r)
#define ISP1362_REG(name, addr, width, rw) \
-static isp1362_reg_t ISP1362_REG_##name = addr
+static isp1362_reg_t __maybe_unused ISP1362_REG_##name = addr
#define REG_ACCESS_TEST(r) do {} while (0)
#define REG_WIDTH_TEST(r, w) do {} while (0)
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 5ef0747..b875da0 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -11,9 +11,9 @@
*
* Based on:
* o MAX3421E datasheet
- * http://datasheets.maximintegrated.com/en/ds/MAX3421E.pdf
+ * https://datasheets.maximintegrated.com/en/ds/MAX3421E.pdf
* o MAX3421E Programming Guide
- * http://www.hdl.co.jp/ftpdata/utl-001/AN3785.pdf
+ * https://www.hdl.co.jp/ftpdata/utl-001/AN3785.pdf
* o gadget/dummy_hcd.c
* For USB HCD implementation.
* o Arduino MAX3421 driver
@@ -125,8 +125,6 @@
struct task_struct *spi_thread;
- struct max3421_hcd *next;
-
enum max3421_rh_state rh_state;
/* lower 16 bits contain port status, upper 16 bits the change mask: */
u32 port_status;
@@ -174,8 +172,6 @@
u8 retransmit; /* packet needs retransmission */
};
-static struct max3421_hcd *max3421_hcd_list;
-
#define MAX3421_FIFO_SIZE 64
#define MAX3421_SPI_DIR_RD 0 /* read register from MAX3421 */
@@ -315,7 +311,7 @@
};
/*
- * See http://www.beyondlogic.org/usbnutshell/usb4.shtml#Control for a
+ * See https://www.beyondlogic.org/usbnutshell/usb4.shtml#Control for a
* reasonable overview of how control transfers use the the IN/OUT
* tokens.
*/
@@ -899,7 +895,7 @@
spi_wr8(hcd, MAX3421_REG_HCTL,
BIT(sndtog + MAX3421_HCTL_SNDTOG0_BIT));
}
- /* FALL THROUGH */
+ fallthrough;
case MAX3421_HRSL_BADBC: /* bad byte count */
case MAX3421_HRSL_PIDERR: /* received PID is corrupted */
case MAX3421_HRSL_PKTERR: /* packet error (stuff, EOP) */
@@ -1699,7 +1695,7 @@
dev_dbg(hcd->self.controller, "power-off\n");
max3421_gpout_set_value(hcd, pdata->vbus_gpout,
!pdata->vbus_active_level);
- /* FALLS THROUGH */
+ fallthrough;
default:
max3421_hcd->port_status &= ~(1 << value);
}
@@ -1752,7 +1748,7 @@
break;
case USB_PORT_FEAT_RESET:
max3421_reset_port(hcd);
- /* FALLS THROUGH */
+ fallthrough;
default:
if ((max3421_hcd->port_status & USB_PORT_STAT_POWER)
!= 0)
@@ -1882,9 +1878,8 @@
}
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
max3421_hcd = hcd_to_max3421(hcd);
- max3421_hcd->next = max3421_hcd_list;
- max3421_hcd_list = max3421_hcd;
INIT_LIST_HEAD(&max3421_hcd->ep_list);
+ spi_set_drvdata(spi, max3421_hcd);
max3421_hcd->tx = kmalloc(sizeof(*max3421_hcd->tx), GFP_KERNEL);
if (!max3421_hcd->tx)
@@ -1934,28 +1929,18 @@
static int
max3421_remove(struct spi_device *spi)
{
- struct max3421_hcd *max3421_hcd = NULL, **prev;
- struct usb_hcd *hcd = NULL;
+ struct max3421_hcd *max3421_hcd;
+ struct usb_hcd *hcd;
unsigned long flags;
- for (prev = &max3421_hcd_list; *prev; prev = &(*prev)->next) {
- max3421_hcd = *prev;
- hcd = max3421_to_hcd(max3421_hcd);
- if (hcd->self.controller == &spi->dev)
- break;
- }
- if (!max3421_hcd) {
- dev_err(&spi->dev, "no MAX3421 HCD found for SPI device %p\n",
- spi);
- return -ENODEV;
- }
+ max3421_hcd = spi_get_drvdata(spi);
+ hcd = max3421_to_hcd(max3421_hcd);
usb_remove_hcd(hcd);
spin_lock_irqsave(&max3421_hcd->lock, flags);
kthread_stop(max3421_hcd->spi_thread);
- *prev = max3421_hcd->next;
spin_unlock_irqrestore(&max3421_hcd->lock, flags);
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index fc35a79..99e994f 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -115,7 +115,6 @@
static void at91_stop_hc(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct ohci_regs __iomem *regs = hcd->regs;
struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
dev_dbg(&pdev->dev, "stop\n");
@@ -123,7 +122,7 @@
/*
* Put the USB host controller into reset.
*/
- writel(0, ®s->control);
+ usb_hcd_platform_shutdown(pdev);
/*
* Stop the USB clocks.
@@ -154,7 +153,7 @@
/* always called with process context; sleeping is OK */
-/**
+/*
* usb_hcd_at91_probe - initialize AT91-based HCDs
* Context: !in_interrupt()
*
@@ -245,9 +244,8 @@
/* may be called with controller, bus, and devices active */
-/**
+/*
* usb_hcd_at91_remove - shutdown processing for AT91-based HCDs
- * @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_at91_probe(), first invoking
@@ -608,8 +606,6 @@
if (ohci_at91->wakeup)
enable_irq_wake(hcd->irq);
- ohci_at91_port_suspend(ohci_at91->sfr_regmap, 1);
-
ret = ohci_suspend(hcd, ohci_at91->wakeup);
if (ret) {
if (ohci_at91->wakeup)
@@ -628,7 +624,11 @@
/* flush the writes */
(void) ohci_readl (ohci, &ohci->regs->control);
+ msleep(1);
+ ohci_at91_port_suspend(ohci_at91->sfr_regmap, 1);
at91_stop_clock(ohci_at91);
+ } else {
+ ohci_at91_port_suspend(ohci_at91->sfr_regmap, 1);
}
return ret;
@@ -640,15 +640,15 @@
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
+ ohci_at91_port_suspend(ohci_at91->sfr_regmap, 0);
+
if (ohci_at91->wakeup)
disable_irq_wake(hcd->irq);
-
- at91_start_clock(ohci_at91);
+ else
+ at91_start_clock(ohci_at91);
ohci_resume(hcd, false);
- ohci_at91_port_suspend(ohci_at91->sfr_regmap, 0);
-
return 0;
}
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index d8b6c9f..5f5e8a6 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -19,7 +19,7 @@
#include "ohci.h"
-#define DRIVER_DESC "OHCI EXYNOS driver"
+#define DRIVER_DESC "OHCI Exynos driver"
static const char hcd_name[] = "ohci-exynos";
static struct hc_driver __read_mostly exynos_ohci_hc_driver;
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 833f512..73e13e7 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -16,7 +16,7 @@
* OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
* interfaces (though some non-x86 Intel chips use it). It supports
* smarter hardware than UHCI. A download link for the spec available
- * through the http://www.usb.org website.
+ * through the https://www.usb.org website.
*
* This file is licenced under the GPL.
*/
@@ -385,7 +385,7 @@
ed_free (ohci, ed);
break;
}
- /* fall through */
+ fallthrough;
default:
/* caller was supposed to have unlinked any requests;
* that's not our job. can't recover; must leak ed.
@@ -1055,7 +1055,7 @@
ed->ed_next = ohci->ed_rm_list;
ed->ed_prev = NULL;
ohci->ed_rm_list = ed;
- /* FALLTHROUGH */
+ fallthrough;
case ED_UNLINK:
break;
default:
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 634f3c7..44504c1 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -58,7 +58,7 @@
ohci->hc_control |= OHCI_USB_RESET;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
- /* FALL THROUGH */
+ fallthrough;
case OHCI_USB_RESET:
status = -EBUSY;
ohci_dbg (ohci, "needs reinit!\n");
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
index c561881..85878e8 100644
--- a/drivers/usb/host/ohci-nxp.c
+++ b/drivers/usb/host/ohci-nxp.c
@@ -150,7 +150,7 @@
static int ohci_hcd_nxp_probe(struct platform_device *pdev)
{
- struct usb_hcd *hcd = 0;
+ struct usb_hcd *hcd = NULL;
const struct hc_driver *driver = &ohci_nxp_hc_driver;
struct resource *res;
int ret = 0, irq;
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index d8d35d4..6374501 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -18,7 +18,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
@@ -53,38 +53,29 @@
#define DRIVER_DESC "OHCI OMAP driver"
-#ifdef CONFIG_TPS65010
-#include <linux/mfd/tps65010.h>
-#else
-
-#define LOW 0
-#define HIGH 1
-
-#define GPIO1 1
-
-static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value)
-{
- return 0;
-}
-
-#endif
-
-static struct clk *usb_host_ck;
-static struct clk *usb_dc_ck;
+struct ohci_omap_priv {
+ struct clk *usb_host_ck;
+ struct clk *usb_dc_ck;
+ struct gpio_desc *power;
+ struct gpio_desc *overcurrent;
+};
static const char hcd_name[] = "ohci-omap";
static struct hc_driver __read_mostly ohci_omap_hc_driver;
-static void omap_ohci_clock_power(int on)
+#define hcd_to_ohci_omap_priv(h) \
+ ((struct ohci_omap_priv *)hcd_to_ohci(h)->priv)
+
+static void omap_ohci_clock_power(struct ohci_omap_priv *priv, int on)
{
if (on) {
- clk_enable(usb_dc_ck);
- clk_enable(usb_host_ck);
+ clk_enable(priv->usb_dc_ck);
+ clk_enable(priv->usb_host_ck);
/* guesstimate for T5 == 1x 32K clock + APLL lock time */
udelay(100);
} else {
- clk_disable(usb_host_ck);
- clk_disable(usb_dc_ck);
+ clk_disable(priv->usb_host_ck);
+ clk_disable(priv->usb_dc_ck);
}
}
@@ -92,22 +83,22 @@
* Board specific gang-switched transceiver power on/off.
* NOTE: OSK supplies power from DC, not battery.
*/
-static int omap_ohci_transceiver_power(int on)
+static int omap_ohci_transceiver_power(struct ohci_omap_priv *priv, int on)
{
if (on) {
if (machine_is_omap_innovator() && cpu_is_omap1510())
__raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL)
| ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
- else if (machine_is_omap_osk())
- tps65010_set_gpio_out_value(GPIO1, LOW);
+ else if (priv->power)
+ gpiod_set_value_cansleep(priv->power, 0);
} else {
if (machine_is_omap_innovator() && cpu_is_omap1510())
__raw_writeb(__raw_readb(INNOVATOR_FPGA_CAM_USB_CONTROL)
& ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
- else if (machine_is_omap_osk())
- tps65010_set_gpio_out_value(GPIO1, HIGH);
+ else if (priv->power)
+ gpiod_set_value_cansleep(priv->power, 1);
}
return 0;
@@ -196,6 +187,7 @@
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
struct omap_usb_config *config = dev_get_platdata(hcd->self.controller);
+ struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
int need_transceiver = (config->otg != 0);
int ret;
@@ -235,7 +227,7 @@
}
#endif
- omap_ohci_clock_power(1);
+ omap_ohci_clock_power(priv, 1);
if (cpu_is_omap15xx()) {
omap_1510_local_bus_power(1);
@@ -266,8 +258,6 @@
/* gpio9 for overcurrent detction */
omap_cfg_reg(W8_1610_GPIO9);
- gpio_request(9, "OHCI overcurrent");
- gpio_direction_input(9);
/* for paranoia's sake: disable USB.PUEN */
omap_cfg_reg(W4_USB_HIGHZ);
@@ -281,7 +271,7 @@
}
/* FIXME hub_wq hub requests should manage power switching */
- omap_ohci_transceiver_power(1);
+ omap_ohci_transceiver_power(priv, 1);
/* board init will have already handled HMC and mux setup.
* any external transceiver should already be initialized
@@ -305,6 +295,7 @@
{
int retval, irq;
struct usb_hcd *hcd = 0;
+ struct ohci_omap_priv *priv;
if (pdev->num_resources != 2) {
dev_err(&pdev->dev, "invalid num_resources: %i\n",
@@ -318,34 +309,58 @@
return -ENODEV;
}
- usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck");
- if (IS_ERR(usb_host_ck))
- return PTR_ERR(usb_host_ck);
-
- if (!cpu_is_omap15xx())
- usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck");
- else
- usb_dc_ck = clk_get(&pdev->dev, "lb_ck");
-
- if (IS_ERR(usb_dc_ck)) {
- clk_put(usb_host_ck);
- return PTR_ERR(usb_dc_ck);
- }
-
-
hcd = usb_create_hcd(&ohci_omap_hc_driver, &pdev->dev,
dev_name(&pdev->dev));
- if (!hcd) {
- retval = -ENOMEM;
- goto err0;
- }
+ if (!hcd)
+ return -ENOMEM;
+
hcd->rsrc_start = pdev->resource[0].start;
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+ priv = hcd_to_ohci_omap_priv(hcd);
+
+ /* Obtain two optional GPIO lines */
+ priv->power = devm_gpiod_get_optional(&pdev->dev, "power", GPIOD_ASIS);
+ if (IS_ERR(priv->power)) {
+ retval = PTR_ERR(priv->power);
+ goto err_put_hcd;
+ }
+ if (priv->power)
+ gpiod_set_consumer_name(priv->power, "OHCI power");
+
+ /*
+ * This "overcurrent" GPIO line isn't really used in the code,
+ * but has a designated hardware function.
+ * TODO: implement proper overcurrent handling.
+ */
+ priv->overcurrent = devm_gpiod_get_optional(&pdev->dev, "overcurrent",
+ GPIOD_IN);
+ if (IS_ERR(priv->overcurrent)) {
+ retval = PTR_ERR(priv->overcurrent);
+ goto err_put_hcd;
+ }
+ if (priv->overcurrent)
+ gpiod_set_consumer_name(priv->overcurrent, "OHCI overcurrent");
+
+ priv->usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck");
+ if (IS_ERR(priv->usb_host_ck)) {
+ retval = PTR_ERR(priv->usb_host_ck);
+ goto err_put_hcd;
+ }
+
+ if (!cpu_is_omap15xx())
+ priv->usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck");
+ else
+ priv->usb_dc_ck = clk_get(&pdev->dev, "lb_ck");
+
+ if (IS_ERR(priv->usb_dc_ck)) {
+ retval = PTR_ERR(priv->usb_dc_ck);
+ goto err_put_host_ck;
+ }
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
dev_dbg(&pdev->dev, "request_mem_region failed\n");
retval = -EBUSY;
- goto err1;
+ goto err_put_dc_ck;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
@@ -370,11 +385,12 @@
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err1:
+err_put_dc_ck:
+ clk_put(priv->usb_dc_ck);
+err_put_host_ck:
+ clk_put(priv->usb_host_ck);
+err_put_hcd:
usb_put_hcd(hcd);
-err0:
- clk_put(usb_dc_ck);
- clk_put(usb_host_ck);
return retval;
}
@@ -393,21 +409,20 @@
static int ohci_hcd_omap_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
dev_dbg(hcd->self.controller, "stopping USB Controller\n");
usb_remove_hcd(hcd);
- omap_ohci_clock_power(0);
+ omap_ohci_clock_power(priv, 0);
if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
(void) otg_set_host(hcd->usb_phy->otg, 0);
usb_put_phy(hcd->usb_phy);
}
- if (machine_is_omap_osk())
- gpio_free(9);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ clk_put(priv->usb_dc_ck);
+ clk_put(priv->usb_host_ck);
usb_put_hcd(hcd);
- clk_put(usb_dc_ck);
- clk_put(usb_host_ck);
return 0;
}
@@ -419,6 +434,7 @@
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
bool do_wakeup = device_may_wakeup(&pdev->dev);
int ret;
@@ -430,7 +446,7 @@
if (ret)
return ret;
- omap_ohci_clock_power(0);
+ omap_ohci_clock_power(priv, 0);
return ret;
}
@@ -438,12 +454,13 @@
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
if (time_before(jiffies, ohci->next_statechange))
msleep(5);
ohci->next_statechange = jiffies;
- omap_ohci_clock_power(1);
+ omap_ohci_clock_power(priv, 1);
ohci_resume(hcd, false);
return 0;
}
@@ -470,7 +487,8 @@
static const struct ohci_driver_overrides omap_overrides __initconst = {
.product_desc = "OMAP OHCI",
- .reset = ohci_omap_reset
+ .reset = ohci_omap_reset,
+ .extra_priv_size = sizeof(struct ohci_omap_priv),
};
static int __init ohci_omap_init(void)
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index f4e13a3..41efe92 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -232,10 +232,6 @@
.driver_data = (unsigned long)ohci_quirk_qemu,
},
- /* FIXME for some of the early AMD 760 southbridges, OHCI
- * won't work at all. blacklist them.
- */
-
{},
};
@@ -277,21 +273,24 @@
static const struct pci_device_id pci_ids[] = { {
/* handle any USB OHCI controller */
PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0),
- .driver_data = (unsigned long) &ohci_pci_hc_driver,
}, {
/* The device in the ConneXT I/O hub has no class reg */
PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_OHCI),
- .driver_data = (unsigned long) &ohci_pci_hc_driver,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE (pci, pci_ids);
+static int ohci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return usb_hcd_pci_probe(dev, id, &ohci_pci_hc_driver);
+}
+
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver ohci_pci_driver = {
- .name = (char *) hcd_name,
+ .name = hcd_name,
.id_table = pci_ids,
- .probe = usb_hcd_pci_probe,
+ .probe = ohci_pci_probe,
.remove = usb_hcd_pci_remove,
.shutdown = usb_hcd_pci_shutdown,
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 7addfc2..4a8456f 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -299,6 +299,11 @@
}
ohci_resume(hcd, false);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
return 0;
}
#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 4ccb85a..3b44531 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -647,7 +647,7 @@
/* ... and periodic urbs have extra accounting */
periodic = ohci_to_hcd(ohci)->self.bandwidth_int_reqs++ == 0
&& ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0;
- /* FALLTHROUGH */
+ fallthrough;
case PIPE_BULK:
info = is_out
? TD_T_TOGGLE | TD_CC | TD_DP_OUT
@@ -879,11 +879,11 @@
case TD_DATAUNDERRUN:
if ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)
break;
- /* fallthrough */
+ fallthrough;
case TD_CC_STALL:
if (usb_pipecontrol (urb->pipe))
break;
- /* fallthrough */
+ fallthrough;
default:
ohci_dbg (ohci,
"urb %p path %s ep%d%s %08x cc %d --> status %d\n",
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index d961097..de5e570 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -343,7 +343,7 @@
return 0;
}
-/**
+/*
* ohci_hcd_s3c2410_probe - initialize S3C2410-based HCDs
* Context: !in_interrupt()
*
diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c
index fed43c6..3f3d62d 100644
--- a/drivers/usb/host/ohci-tmio.c
+++ b/drivers/usb/host/ohci-tmio.c
@@ -97,13 +97,13 @@
switch (ohci->num_ports) {
default:
dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports);
- /* fall through */
+ fallthrough;
case 3:
pm |= CCR_PM_USBPW3;
- /* fall through */
+ fallthrough;
case 2:
pm |= CCR_PM_USBPW2;
- /* fall through */
+ fallthrough;
case 1:
pm |= CCR_PM_USBPW1;
}
@@ -199,7 +199,7 @@
if (usb_disabled())
return -ENODEV;
- if (!cell)
+ if (!cell || !regs || !config || !sram)
return -EINVAL;
if (irq < 0)
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index b015b00..aac6285 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
/*
* OHCI HCD (Host Controller Driver) for USB.
*
@@ -162,7 +162,7 @@
/* map OHCI TD status codes (CC) to errno values */
-static const int cc_to_error [16] = {
+static const int __maybe_unused cc_to_error [16] = {
/* No Error */ 0,
/* CRC Error */ -EILSEQ,
/* Bit Stuff */ -EPROTO,
@@ -337,7 +337,7 @@
u16 length; // # tds in this request
u16 td_cnt; // tds already serviced
struct list_head pending;
- struct td *td [0]; // all TDs in this request
+ struct td *td[]; // all TDs in this request
} urb_priv_t;
@@ -435,7 +435,7 @@
struct dentry *debug_dir;
/* platform-specific data -- must come last */
- unsigned long priv[0] __aligned(sizeof(s64));
+ unsigned long priv[] __aligned(sizeof(s64));
};
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 6598524..e832909 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -24,6 +24,7 @@
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <asm/irq.h>
#include <asm/unaligned.h>
@@ -676,12 +677,12 @@
*/
/* Low level read/write registers functions */
-static inline u32 oxu_readl(void *base, u32 reg)
+static inline u32 oxu_readl(void __iomem *base, u32 reg)
{
return readl(base + reg);
}
-static inline void oxu_writel(void *base, u32 reg, u32 val)
+static inline void oxu_writel(void __iomem *base, u32 reg, u32 val)
{
writel(val, base + reg);
}
@@ -748,18 +749,16 @@
u32 mask, u32 done, int usec)
{
u32 result;
+ int ret;
- do {
- result = readl(ptr);
- if (result == ~(u32)0) /* card removed */
- return -ENODEV;
- result &= mask;
- if (result == done)
- return 0;
- udelay(1);
- usec--;
- } while (usec > 0);
- return -ETIMEDOUT;
+ ret = readl_poll_timeout_atomic(ptr, result,
+ ((result & mask) == done ||
+ result == U32_MAX),
+ 1, usec);
+ if (result == U32_MAX) /* card removed */
+ return -ENODEV;
+
+ return ret;
}
/* Force HC to halt state from unknown (EHCI spec section 2.3) */
@@ -1858,7 +1857,7 @@
switch (urb->dev->speed) {
case USB_SPEED_LOW:
info1 |= (1 << 12); /* EPS "low" */
- /* FALL THROUGH */
+ fallthrough;
case USB_SPEED_FULL:
/* EPS 0 means "full" */
@@ -2037,16 +2036,15 @@
static int submit_async(struct oxu_hcd *oxu, struct urb *urb,
struct list_head *qtd_list, gfp_t mem_flags)
{
- struct ehci_qtd *qtd;
- int epnum;
+ int epnum = urb->ep->desc.bEndpointAddress;
unsigned long flags;
struct ehci_qh *qh = NULL;
int rc = 0;
+#ifdef OXU_URB_TRACE
+ struct ehci_qtd *qtd;
qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
- epnum = urb->ep->desc.bEndpointAddress;
-#ifdef OXU_URB_TRACE
oxu_dbg(oxu, "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
__func__, urb->dev->devpath, urb,
epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
@@ -2783,11 +2781,15 @@
return;
oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down");
- for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; )
- (void) oxu_hub_control(oxu_to_hcd(oxu),
- is_on ? SetPortFeature : ClearPortFeature,
- USB_PORT_FEAT_POWER,
- port--, NULL, 0);
+ for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) {
+ if (is_on)
+ oxu_hub_control(oxu_to_hcd(oxu), SetPortFeature,
+ USB_PORT_FEAT_POWER, port--, NULL, 0);
+ else
+ oxu_hub_control(oxu_to_hcd(oxu), ClearPortFeature,
+ USB_PORT_FEAT_POWER, port--, NULL, 0);
+ }
+
msleep(20);
}
@@ -3374,7 +3376,7 @@
switch (qh->qh_state) {
case QH_STATE_LINKED:
intr_deschedule(oxu, qh);
- /* FALL THROUGH */
+ fallthrough;
case QH_STATE_IDLE:
qh_completions(oxu, qh);
break;
@@ -3446,7 +3448,7 @@
if (!tmp)
goto nogood;
unlink_async(oxu, qh);
- /* FALL THROUGH */
+ fallthrough;
case QH_STATE_UNLINK: /* wait for hw to finish? */
idle_timeout:
spin_unlock_irqrestore(&oxu->lock, flags);
@@ -3457,7 +3459,7 @@
qh_put(qh);
break;
}
- /* fall through */
+ fallthrough;
default:
nogood:
/* caller was supposed to have unlinked any requests;
@@ -4063,7 +4065,7 @@
* Module stuff
*/
-static void oxu_configuration(struct platform_device *pdev, void *base)
+static void oxu_configuration(struct platform_device *pdev, void __iomem *base)
{
u32 tmp;
@@ -4093,7 +4095,7 @@
oxu_writel(base, OXU_CHIPIRQEN_SET, OXU_USBSPHLPWUI | OXU_USBOTGLPWUI);
}
-static int oxu_verify_id(struct platform_device *pdev, void *base)
+static int oxu_verify_id(struct platform_device *pdev, void __iomem *base)
{
u32 id;
static const char * const bo[] = {
@@ -4121,7 +4123,7 @@
static const struct hc_driver oxu_hc_driver;
static struct usb_hcd *oxu_create(struct platform_device *pdev,
unsigned long memstart, unsigned long memlen,
- void *base, int irq, int otg)
+ void __iomem *base, int irq, int otg)
{
struct device *dev = &pdev->dev;
@@ -4160,7 +4162,7 @@
static int oxu_init(struct platform_device *pdev,
unsigned long memstart, unsigned long memlen,
- void *base, int irq)
+ void __iomem *base, int irq)
{
struct oxu_info *info = platform_get_drvdata(pdev);
struct usb_hcd *hcd;
@@ -4209,7 +4211,7 @@
static int oxu_drv_probe(struct platform_device *pdev)
{
struct resource *res;
- void *base;
+ void __iomem *base;
unsigned long memstart, memlen;
int irq, ret;
struct oxu_info *info;
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index f6d0449..ef08d68 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -16,6 +16,9 @@
#include <linux/export.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
+#include <linux/of.h>
+#include <linux/iopoll.h>
+
#include "pci-quirks.h"
#include "xhci-ext-caps.h"
@@ -205,7 +208,7 @@
{
unsigned long flags;
struct amd_chipset_info info;
- info.need_pll_quirk = 0;
+ info.need_pll_quirk = false;
spin_lock_irqsave(&amd_lock, flags);
@@ -229,10 +232,10 @@
case AMD_CHIPSET_SB800:
case AMD_CHIPSET_HUDSON2:
case AMD_CHIPSET_BOLTON:
- info.need_pll_quirk = 1;
+ info.need_pll_quirk = true;
break;
default:
- info.need_pll_quirk = 0;
+ info.need_pll_quirk = false;
break;
}
@@ -529,7 +532,7 @@
amd_chipset.nb_type = 0;
memset(&amd_chipset.sb_type, 0, sizeof(amd_chipset.sb_type));
amd_chipset.isoc_reqs = 0;
- amd_chipset.need_pll_quirk = 0;
+ amd_chipset.need_pll_quirk = false;
spin_unlock_irqrestore(&amd_lock, flags);
@@ -728,7 +731,7 @@
if (!pio_enabled(pdev))
return;
- for (i = 0; i < PCI_ROM_RESOURCE; i++)
+ for (i = 0; i < PCI_STD_NUM_BARS; i++)
if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
base = pci_resource_start(pdev, i);
break;
@@ -954,7 +957,8 @@
ehci_bios_handoff(pdev, op_reg_base, cap, offset);
break;
case 0: /* Illegal reserved cap, set cap=0 so we exit */
- cap = 0; /* fall through */
+ cap = 0;
+ fallthrough;
default:
dev_warn(&pdev->dev,
"EHCI: unrecognized capability %02x\n",
@@ -1009,15 +1013,9 @@
{
u32 result;
- do {
- result = readl(ptr);
- result &= mask;
- if (result == done)
- return 0;
- udelay(delay_usec);
- wait_usec -= delay_usec;
- } while (wait_usec > 0);
- return -ETIMEDOUT;
+ return readl_poll_timeout_atomic(ptr, result,
+ ((result & mask) == done),
+ delay_usec, wait_usec);
}
/*
@@ -1130,7 +1128,7 @@
}
EXPORT_SYMBOL_GPL(usb_disable_xhci_ports);
-/**
+/*
* PCI Quirks for xHCI.
*
* Takes care of the handoff between the Pre-OS (i.e. BIOS) and the OS.
@@ -1150,7 +1148,7 @@
if (!mmio_resource_enabled(pdev, 0))
return;
- base = ioremap_nocache(pci_resource_start(pdev, 0), len);
+ base = ioremap(pci_resource_start(pdev, 0), len);
if (base == NULL)
return;
@@ -1243,11 +1241,27 @@
static void quirk_usb_early_handoff(struct pci_dev *pdev)
{
+ struct device_node *parent;
+ bool is_rpi;
+
/* Skip Netlogic mips SoC's internal PCI USB controller.
* This device does not need/support EHCI/OHCI handoff
*/
if (pdev->vendor == 0x184e) /* vendor Netlogic */
return;
+
+ /*
+ * Bypass the Raspberry Pi 4 controller xHCI controller, things are
+ * taken care of by the board's co-processor.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) {
+ parent = of_get_parent(pdev->bus->dev.of_node);
+ is_rpi = of_device_is_compatible(parent, "brcm,bcm2711-pcie");
+ of_node_put(parent);
+ if (is_rpi)
+ return;
+ }
+
if (pdev->class != PCI_CLASS_SERIAL_USB_UHCI &&
pdev->class != PCI_CLASS_SERIAL_USB_OHCI &&
pdev->class != PCI_CLASS_SERIAL_USB_EHCI &&
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 0c03ac6..63719cd 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -475,16 +475,14 @@
static void clear_all_buffer(struct r8a66597 *r8a66597,
struct r8a66597_pipe *pipe)
{
- u16 tmp;
-
if (!pipe || pipe->info.pipenum == 0)
return;
pipe_stop(r8a66597, pipe);
r8a66597_bset(r8a66597, ACLRM, pipe->pipectr);
- tmp = r8a66597_read(r8a66597, pipe->pipectr);
- tmp = r8a66597_read(r8a66597, pipe->pipectr);
- tmp = r8a66597_read(r8a66597, pipe->pipectr);
+ r8a66597_read(r8a66597, pipe->pipectr);
+ r8a66597_read(r8a66597, pipe->pipectr);
+ r8a66597_read(r8a66597, pipe->pipectr);
r8a66597_bclr(r8a66597, ACLRM, pipe->pipectr);
}
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
index 51973a9..ab08147 100644
--- a/drivers/usb/host/r8a66597.h
+++ b/drivers/usb/host/r8a66597.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* R8A66597 HCD (Host Controller Driver)
*
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 936fddc..9465fce 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1791,7 +1791,7 @@
.suspend = sl811h_suspend,
.resume = sl811h_resume,
.driver = {
- .name = (char *) hcd_name,
+ .name = hcd_name,
},
};
EXPORT_SYMBOL(sl811h_driver);
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 4efee34..995bc52 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -71,7 +71,7 @@
/* Some boards misreport power switching/overcurrent*/
static bool distrust_firmware = true;
module_param(distrust_firmware, bool, 0);
-MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
+MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurrent"
"t setup");
static DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
/*
@@ -81,7 +81,6 @@
static struct mutex u132_module_lock;
static int u132_exiting;
static int u132_instances;
-static struct list_head u132_static_list;
/*
* end of the global variables protected by u132_module_lock
*/
@@ -177,7 +176,6 @@
};
struct u132 {
struct kref kref;
- struct list_head u132_list;
struct mutex sw_lock;
struct mutex scheduler_lock;
struct u132_platform_data *board;
@@ -254,7 +252,6 @@
struct usb_hcd *hcd = u132_to_hcd(u132);
u132->going += 1;
mutex_lock(&u132_module_lock);
- list_del_init(&u132->u132_list);
u132_instances -= 1;
mutex_unlock(&u132_module_lock);
dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13"
@@ -3089,7 +3086,6 @@
retval = 0;
hcd->rsrc_start = 0;
mutex_lock(&u132_module_lock);
- list_add_tail(&u132->u132_list, &u132_static_list);
u132->sequence_num = ++u132_instances;
mutex_unlock(&u132_module_lock);
u132_u132_init_kref(u132);
@@ -3192,7 +3188,6 @@
static int __init u132_hcd_init(void)
{
int retval;
- INIT_LIST_HEAD(&u132_static_list);
u132_instances = 0;
u132_exiting = 0;
mutex_init(&u132_module_lock);
@@ -3213,14 +3208,9 @@
module_init(u132_hcd_init);
static void __exit u132_hcd_exit(void)
{
- struct u132 *u132;
- struct u132 *temp;
mutex_lock(&u132_module_lock);
u132_exiting += 1;
mutex_unlock(&u132_module_lock);
- list_for_each_entry_safe(u132, temp, &u132_static_list, u132_list) {
- platform_device_unregister(u132->platform_dev);
- }
platform_driver_unregister(&u132_platform_driver);
printk(KERN_INFO "u132-hcd driver deregistered\n");
wait_event(u132_hcd_wait, u132_instances == 0);
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 0fa3d72..9b88745 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -287,17 +287,21 @@
static const struct pci_device_id uhci_pci_ids[] = { {
/* handle any USB UHCI controller */
PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0),
- .driver_data = (unsigned long) &uhci_driver,
}, { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
+static int uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return usb_hcd_pci_probe(dev, id, &uhci_driver);
+}
+
static struct pci_driver uhci_pci_driver = {
- .name = (char *)hcd_name,
+ .name = hcd_name,
.id_table = uhci_pci_ids,
- .probe = usb_hcd_pci_probe,
+ .probe = uhci_pci_probe,
.remove = usb_hcd_pci_remove,
.shutdown = uhci_shutdown,
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index 70dbd95..be9e9db 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -113,7 +113,8 @@
num_ports);
}
if (of_device_is_compatible(np, "aspeed,ast2400-uhci") ||
- of_device_is_compatible(np, "aspeed,ast2500-uhci")) {
+ of_device_is_compatible(np, "aspeed,ast2500-uhci") ||
+ of_device_is_compatible(np, "aspeed,ast2600-uhci")) {
uhci->is_aspeed = 1;
dev_info(&pdev->dev,
"Enabled Aspeed implementation workarounds\n");
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 93e2cca..ccb0156 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* xhci-dbgcap.c - xHCI debug capability support
*
* Copyright (C) 2017 Intel Corporation
@@ -14,24 +14,27 @@
#include "xhci-trace.h"
#include "xhci-dbgcap.h"
-static inline void *
-dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size,
- dma_addr_t *dma_handle, gfp_t flags)
+static void dbc_free_ctx(struct device *dev, struct xhci_container_ctx *ctx)
{
- void *vaddr;
-
- vaddr = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev,
- size, dma_handle, flags);
- return vaddr;
+ if (!ctx)
+ return;
+ dma_free_coherent(dev, ctx->size, ctx->bytes, ctx->dma);
+ kfree(ctx);
}
-static inline void
-dbc_dma_free_coherent(struct xhci_hcd *xhci, size_t size,
- void *cpu_addr, dma_addr_t dma_handle)
+/* we use only one segment for DbC rings */
+static void dbc_ring_free(struct device *dev, struct xhci_ring *ring)
{
- if (cpu_addr)
- dma_free_coherent(xhci_to_hcd(xhci)->self.sysdev,
- size, cpu_addr, dma_handle);
+ if (!ring)
+ return;
+
+ if (ring->first_seg && ring->first_seg->trbs) {
+ dma_free_coherent(dev, TRB_SEGMENT_SIZE,
+ ring->first_seg->trbs,
+ ring->first_seg->dma);
+ kfree(ring->first_seg);
+ }
+ kfree(ring);
}
static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings)
@@ -83,16 +86,14 @@
return string_length;
}
-static void xhci_dbc_init_contexts(struct xhci_hcd *xhci, u32 string_length)
+static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length)
{
- struct xhci_dbc *dbc;
struct dbc_info_context *info;
struct xhci_ep_ctx *ep_ctx;
u32 dev_info;
dma_addr_t deq, dma;
unsigned int max_burst;
- dbc = xhci->dbc;
if (!dbc)
return;
@@ -121,7 +122,7 @@
ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state);
/* Set DbC context and info registers: */
- xhci_write_64(xhci, dbc->ctx->dma, &dbc->regs->dccp);
+ lo_hi_writeq(dbc->ctx->dma, &dbc->regs->dccp);
dev_info = cpu_to_le32((DBC_VENDOR_ID << 16) | DBC_PROTOCOL);
writel(dev_info, &dbc->regs->devinfo1);
@@ -134,10 +135,8 @@
__releases(&dbc->lock)
__acquires(&dbc->lock)
{
- struct dbc_ep *dep = req->dep;
- struct xhci_dbc *dbc = dep->dbc;
- struct xhci_hcd *xhci = dbc->xhci;
- struct device *dev = xhci_to_hcd(dbc->xhci)->self.sysdev;
+ struct xhci_dbc *dbc = req->dbc;
+ struct device *dev = dbc->dev;
list_del_init(&req->list_pending);
req->trb_dma = 0;
@@ -151,11 +150,11 @@
dma_unmap_single(dev,
req->dma,
req->length,
- dbc_ep_dma_direction(dep));
+ dbc_ep_dma_direction(req));
/* Give back the transfer request: */
spin_unlock(&dbc->lock);
- req->complete(xhci, req);
+ req->complete(dbc, req);
spin_lock(&dbc->lock);
}
@@ -187,18 +186,25 @@
}
struct dbc_request *
-dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags)
+dbc_alloc_request(struct xhci_dbc *dbc, unsigned int direction, gfp_t flags)
{
struct dbc_request *req;
- req = kzalloc(sizeof(*req), gfp_flags);
+ if (direction != BULK_IN &&
+ direction != BULK_OUT)
+ return NULL;
+
+ if (!dbc)
+ return NULL;
+
+ req = kzalloc(sizeof(*req), flags);
if (!req)
return NULL;
- req->dep = dep;
+ req->dbc = dbc;
INIT_LIST_HEAD(&req->list_pending);
INIT_LIST_HEAD(&req->list_pool);
- req->direction = dep->direction;
+ req->direction = direction;
trace_xhci_dbc_alloc_request(req);
@@ -206,7 +212,7 @@
}
void
-dbc_free_request(struct dbc_ep *dep, struct dbc_request *req)
+dbc_free_request(struct dbc_request *req)
{
trace_xhci_dbc_free_request(req);
@@ -242,7 +248,7 @@
u64 addr;
union xhci_trb *trb;
unsigned int num_trbs;
- struct xhci_dbc *dbc = dep->dbc;
+ struct xhci_dbc *dbc = req->dbc;
struct xhci_ring *ring = dep->ring;
u32 length, control, cycle;
@@ -286,14 +292,12 @@
}
static int
-dbc_ep_do_queue(struct dbc_ep *dep, struct dbc_request *req)
+dbc_ep_do_queue(struct dbc_request *req)
{
int ret;
- struct device *dev;
- struct xhci_dbc *dbc = dep->dbc;
- struct xhci_hcd *xhci = dbc->xhci;
-
- dev = xhci_to_hcd(xhci)->self.sysdev;
+ struct xhci_dbc *dbc = req->dbc;
+ struct device *dev = dbc->dev;
+ struct dbc_ep *dep = &dbc->eps[req->direction];
if (!req->length || !req->buf)
return -EINVAL;
@@ -306,13 +310,13 @@
req->length,
dbc_ep_dma_direction(dep));
if (dma_mapping_error(dev, req->dma)) {
- xhci_err(xhci, "failed to map buffer\n");
+ dev_err(dbc->dev, "failed to map buffer\n");
return -EFAULT;
}
ret = xhci_dbc_queue_bulk_tx(dep, req);
if (ret) {
- xhci_err(xhci, "failed to queue trbs\n");
+ dev_err(dbc->dev, "failed to queue trbs\n");
dma_unmap_single(dev,
req->dma,
req->length,
@@ -325,16 +329,22 @@
return 0;
}
-int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req,
- gfp_t gfp_flags)
+int dbc_ep_queue(struct dbc_request *req)
{
unsigned long flags;
- struct xhci_dbc *dbc = dep->dbc;
+ struct xhci_dbc *dbc = req->dbc;
int ret = -ESHUTDOWN;
+ if (!dbc)
+ return -ENODEV;
+
+ if (req->direction != BULK_IN &&
+ req->direction != BULK_OUT)
+ return -EINVAL;
+
spin_lock_irqsave(&dbc->lock, flags);
if (dbc->state == DS_CONFIGURED)
- ret = dbc_ep_do_queue(dep, req);
+ ret = dbc_ep_do_queue(req);
spin_unlock_irqrestore(&dbc->lock, flags);
mod_delayed_work(system_wq, &dbc->event_work, 0);
@@ -344,10 +354,9 @@
return ret;
}
-static inline void xhci_dbc_do_eps_init(struct xhci_hcd *xhci, bool direction)
+static inline void xhci_dbc_do_eps_init(struct xhci_dbc *dbc, bool direction)
{
struct dbc_ep *dep;
- struct xhci_dbc *dbc = xhci->dbc;
dep = &dbc->eps[direction];
dep->dbc = dbc;
@@ -357,125 +366,205 @@
INIT_LIST_HEAD(&dep->list_pending);
}
-static void xhci_dbc_eps_init(struct xhci_hcd *xhci)
+static void xhci_dbc_eps_init(struct xhci_dbc *dbc)
{
- xhci_dbc_do_eps_init(xhci, BULK_OUT);
- xhci_dbc_do_eps_init(xhci, BULK_IN);
+ xhci_dbc_do_eps_init(dbc, BULK_OUT);
+ xhci_dbc_do_eps_init(dbc, BULK_IN);
}
-static void xhci_dbc_eps_exit(struct xhci_hcd *xhci)
+static void xhci_dbc_eps_exit(struct xhci_dbc *dbc)
{
- struct xhci_dbc *dbc = xhci->dbc;
-
memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps));
}
-static int xhci_dbc_mem_init(struct xhci_hcd *xhci, gfp_t flags)
+static int dbc_erst_alloc(struct device *dev, struct xhci_ring *evt_ring,
+ struct xhci_erst *erst, gfp_t flags)
+{
+ erst->entries = dma_alloc_coherent(dev, sizeof(struct xhci_erst_entry),
+ &erst->erst_dma_addr, flags);
+ if (!erst->entries)
+ return -ENOMEM;
+
+ erst->num_entries = 1;
+ erst->entries[0].seg_addr = cpu_to_le64(evt_ring->first_seg->dma);
+ erst->entries[0].seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
+ erst->entries[0].rsvd = 0;
+ return 0;
+}
+
+static void dbc_erst_free(struct device *dev, struct xhci_erst *erst)
+{
+ if (erst->entries)
+ dma_free_coherent(dev, sizeof(struct xhci_erst_entry),
+ erst->entries, erst->erst_dma_addr);
+ erst->entries = NULL;
+}
+
+static struct xhci_container_ctx *
+dbc_alloc_ctx(struct device *dev, gfp_t flags)
+{
+ struct xhci_container_ctx *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), flags);
+ if (!ctx)
+ return NULL;
+
+ /* xhci 7.6.9, all three contexts; info, ep-out and ep-in. Each 64 bytes*/
+ ctx->size = 3 * DBC_CONTEXT_SIZE;
+ ctx->bytes = dma_alloc_coherent(dev, ctx->size, &ctx->dma, flags);
+ if (!ctx->bytes) {
+ kfree(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+static struct xhci_ring *
+xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags)
+{
+ struct xhci_ring *ring;
+ struct xhci_segment *seg;
+ dma_addr_t dma;
+
+ ring = kzalloc(sizeof(*ring), flags);
+ if (!ring)
+ return NULL;
+
+ ring->num_segs = 1;
+ ring->type = type;
+
+ seg = kzalloc(sizeof(*seg), flags);
+ if (!seg)
+ goto seg_fail;
+
+ ring->first_seg = seg;
+ ring->last_seg = seg;
+ seg->next = seg;
+
+ seg->trbs = dma_alloc_coherent(dev, TRB_SEGMENT_SIZE, &dma, flags);
+ if (!seg->trbs)
+ goto dma_fail;
+
+ seg->dma = dma;
+
+ /* Only event ring does not use link TRB */
+ if (type != TYPE_EVENT) {
+ union xhci_trb *trb = &seg->trbs[TRBS_PER_SEGMENT - 1];
+
+ trb->link.segment_ptr = cpu_to_le64(dma);
+ trb->link.control = cpu_to_le32(LINK_TOGGLE | TRB_TYPE(TRB_LINK));
+ }
+ INIT_LIST_HEAD(&ring->td_list);
+ xhci_initialize_ring_info(ring, 1);
+ return ring;
+dma_fail:
+ kfree(seg);
+seg_fail:
+ kfree(ring);
+ return NULL;
+}
+
+static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
{
int ret;
dma_addr_t deq;
u32 string_length;
- struct xhci_dbc *dbc = xhci->dbc;
+ struct device *dev = dbc->dev;
/* Allocate various rings for events and transfers: */
- dbc->ring_evt = xhci_ring_alloc(xhci, 1, 1, TYPE_EVENT, 0, flags);
+ dbc->ring_evt = xhci_dbc_ring_alloc(dev, TYPE_EVENT, flags);
if (!dbc->ring_evt)
goto evt_fail;
- dbc->ring_in = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags);
+ dbc->ring_in = xhci_dbc_ring_alloc(dev, TYPE_BULK, flags);
if (!dbc->ring_in)
goto in_fail;
- dbc->ring_out = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags);
+ dbc->ring_out = xhci_dbc_ring_alloc(dev, TYPE_BULK, flags);
if (!dbc->ring_out)
goto out_fail;
/* Allocate and populate ERST: */
- ret = xhci_alloc_erst(xhci, dbc->ring_evt, &dbc->erst, flags);
+ ret = dbc_erst_alloc(dev, dbc->ring_evt, &dbc->erst, flags);
if (ret)
goto erst_fail;
/* Allocate context data structure: */
- dbc->ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags);
+ dbc->ctx = dbc_alloc_ctx(dev, flags); /* was sysdev, and is still */
if (!dbc->ctx)
goto ctx_fail;
/* Allocate the string table: */
dbc->string_size = sizeof(struct dbc_str_descs);
- dbc->string = dbc_dma_alloc_coherent(xhci,
- dbc->string_size,
- &dbc->string_dma,
- flags);
+ dbc->string = dma_alloc_coherent(dev, dbc->string_size,
+ &dbc->string_dma, flags);
if (!dbc->string)
goto string_fail;
/* Setup ERST register: */
writel(dbc->erst.erst_size, &dbc->regs->ersts);
- xhci_write_64(xhci, dbc->erst.erst_dma_addr, &dbc->regs->erstba);
+
+ lo_hi_writeq(dbc->erst.erst_dma_addr, &dbc->regs->erstba);
deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg,
dbc->ring_evt->dequeue);
- xhci_write_64(xhci, deq, &dbc->regs->erdp);
+ lo_hi_writeq(deq, &dbc->regs->erdp);
/* Setup strings and contexts: */
string_length = xhci_dbc_populate_strings(dbc->string);
- xhci_dbc_init_contexts(xhci, string_length);
+ xhci_dbc_init_contexts(dbc, string_length);
- xhci_dbc_eps_init(xhci);
+ xhci_dbc_eps_init(dbc);
dbc->state = DS_INITIALIZED;
return 0;
string_fail:
- xhci_free_container_ctx(xhci, dbc->ctx);
+ dbc_free_ctx(dev, dbc->ctx);
dbc->ctx = NULL;
ctx_fail:
- xhci_free_erst(xhci, &dbc->erst);
+ dbc_erst_free(dev, &dbc->erst);
erst_fail:
- xhci_ring_free(xhci, dbc->ring_out);
+ dbc_ring_free(dev, dbc->ring_out);
dbc->ring_out = NULL;
out_fail:
- xhci_ring_free(xhci, dbc->ring_in);
+ dbc_ring_free(dev, dbc->ring_in);
dbc->ring_in = NULL;
in_fail:
- xhci_ring_free(xhci, dbc->ring_evt);
+ dbc_ring_free(dev, dbc->ring_evt);
dbc->ring_evt = NULL;
evt_fail:
return -ENOMEM;
}
-static void xhci_dbc_mem_cleanup(struct xhci_hcd *xhci)
+static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc)
{
- struct xhci_dbc *dbc = xhci->dbc;
-
if (!dbc)
return;
- xhci_dbc_eps_exit(xhci);
+ xhci_dbc_eps_exit(dbc);
if (dbc->string) {
- dbc_dma_free_coherent(xhci,
- dbc->string_size,
- dbc->string, dbc->string_dma);
+ dma_free_coherent(dbc->dev, dbc->string_size,
+ dbc->string, dbc->string_dma);
dbc->string = NULL;
}
- xhci_free_container_ctx(xhci, dbc->ctx);
+ dbc_free_ctx(dbc->dev, dbc->ctx);
dbc->ctx = NULL;
- xhci_free_erst(xhci, &dbc->erst);
- xhci_ring_free(xhci, dbc->ring_out);
- xhci_ring_free(xhci, dbc->ring_in);
- xhci_ring_free(xhci, dbc->ring_evt);
+ dbc_erst_free(dbc->dev, &dbc->erst);
+ dbc_ring_free(dbc->dev, dbc->ring_out);
+ dbc_ring_free(dbc->dev, dbc->ring_in);
+ dbc_ring_free(dbc->dev, dbc->ring_evt);
dbc->ring_in = NULL;
dbc->ring_out = NULL;
dbc->ring_evt = NULL;
}
-static int xhci_do_dbc_start(struct xhci_hcd *xhci)
+static int xhci_do_dbc_start(struct xhci_dbc *dbc)
{
int ret;
u32 ctrl;
- struct xhci_dbc *dbc = xhci->dbc;
if (dbc->state != DS_DISABLED)
return -EINVAL;
@@ -487,7 +576,7 @@
if (ret)
return ret;
- ret = xhci_dbc_mem_init(xhci, GFP_ATOMIC);
+ ret = xhci_dbc_mem_init(dbc, GFP_ATOMIC);
if (ret)
return ret;
@@ -505,10 +594,8 @@
return 0;
}
-static int xhci_do_dbc_stop(struct xhci_hcd *xhci)
+static int xhci_do_dbc_stop(struct xhci_dbc *dbc)
{
- struct xhci_dbc *dbc = xhci->dbc;
-
if (dbc->state == DS_DISABLED)
return -1;
@@ -518,76 +605,81 @@
return 0;
}
-static int xhci_dbc_start(struct xhci_hcd *xhci)
+static int xhci_dbc_start(struct xhci_dbc *dbc)
{
int ret;
unsigned long flags;
- struct xhci_dbc *dbc = xhci->dbc;
WARN_ON(!dbc);
- pm_runtime_get_sync(xhci_to_hcd(xhci)->self.controller);
+ pm_runtime_get_sync(dbc->dev); /* note this was self.controller */
spin_lock_irqsave(&dbc->lock, flags);
- ret = xhci_do_dbc_start(xhci);
+ ret = xhci_do_dbc_start(dbc);
spin_unlock_irqrestore(&dbc->lock, flags);
if (ret) {
- pm_runtime_put(xhci_to_hcd(xhci)->self.controller);
+ pm_runtime_put(dbc->dev); /* note this was self.controller */
return ret;
}
return mod_delayed_work(system_wq, &dbc->event_work, 1);
}
-static void xhci_dbc_stop(struct xhci_hcd *xhci)
+static void xhci_dbc_stop(struct xhci_dbc *dbc)
{
int ret;
unsigned long flags;
- struct xhci_dbc *dbc = xhci->dbc;
- struct dbc_port *port = &dbc->port;
WARN_ON(!dbc);
+ switch (dbc->state) {
+ case DS_DISABLED:
+ return;
+ case DS_CONFIGURED:
+ case DS_STALLED:
+ if (dbc->driver->disconnect)
+ dbc->driver->disconnect(dbc);
+ break;
+ default:
+ break;
+ }
+
cancel_delayed_work_sync(&dbc->event_work);
- if (port->registered)
- xhci_dbc_tty_unregister_device(xhci);
-
spin_lock_irqsave(&dbc->lock, flags);
- ret = xhci_do_dbc_stop(xhci);
+ ret = xhci_do_dbc_stop(dbc);
spin_unlock_irqrestore(&dbc->lock, flags);
if (!ret) {
- xhci_dbc_mem_cleanup(xhci);
- pm_runtime_put_sync(xhci_to_hcd(xhci)->self.controller);
+ xhci_dbc_mem_cleanup(dbc);
+ pm_runtime_put_sync(dbc->dev); /* note, was self.controller */
}
}
static void
-dbc_handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
+dbc_handle_port_status(struct xhci_dbc *dbc, union xhci_trb *event)
{
u32 portsc;
- struct xhci_dbc *dbc = xhci->dbc;
portsc = readl(&dbc->regs->portsc);
if (portsc & DBC_PORTSC_CONN_CHANGE)
- xhci_info(xhci, "DbC port connect change\n");
+ dev_info(dbc->dev, "DbC port connect change\n");
if (portsc & DBC_PORTSC_RESET_CHANGE)
- xhci_info(xhci, "DbC port reset change\n");
+ dev_info(dbc->dev, "DbC port reset change\n");
if (portsc & DBC_PORTSC_LINK_CHANGE)
- xhci_info(xhci, "DbC port link status change\n");
+ dev_info(dbc->dev, "DbC port link status change\n");
if (portsc & DBC_PORTSC_CONFIG_CHANGE)
- xhci_info(xhci, "DbC config error change\n");
+ dev_info(dbc->dev, "DbC config error change\n");
/* Port reset change bit will be cleared in other place: */
writel(portsc & ~DBC_PORTSC_RESET_CHANGE, &dbc->regs->portsc);
}
-static void dbc_handle_xfer_event(struct xhci_hcd *xhci, union xhci_trb *event)
+static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
{
struct dbc_ep *dep;
struct xhci_ring *ring;
@@ -601,13 +693,13 @@
remain_length = EVENT_TRB_LEN(le32_to_cpu(event->generic.field[2]));
ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3]));
dep = (ep_id == EPID_OUT) ?
- get_out_ep(xhci) : get_in_ep(xhci);
+ get_out_ep(dbc) : get_in_ep(dbc);
ring = dep->ring;
switch (comp_code) {
case COMP_SUCCESS:
remain_length = 0;
- /* FALLTHROUGH */
+ fallthrough;
case COMP_SHORT_PACKET:
status = 0;
break;
@@ -615,11 +707,11 @@
case COMP_BABBLE_DETECTED_ERROR:
case COMP_USB_TRANSACTION_ERROR:
case COMP_STALL_ERROR:
- xhci_warn(xhci, "tx error %d detected\n", comp_code);
+ dev_warn(dbc->dev, "tx error %d detected\n", comp_code);
status = -comp_code;
break;
default:
- xhci_err(xhci, "unknown tx error %d\n", comp_code);
+ dev_err(dbc->dev, "unknown tx error %d\n", comp_code);
status = -comp_code;
break;
}
@@ -633,7 +725,7 @@
}
if (!req) {
- xhci_warn(xhci, "no matched request\n");
+ dev_warn(dbc->dev, "no matched request\n");
return;
}
@@ -644,13 +736,23 @@
xhci_dbc_giveback(req, status);
}
+static void inc_evt_deq(struct xhci_ring *ring)
+{
+ /* If on the last TRB of the segment go back to the beginning */
+ if (ring->dequeue == &ring->deq_seg->trbs[TRBS_PER_SEGMENT - 1]) {
+ ring->cycle_state ^= 1;
+ ring->dequeue = ring->deq_seg->trbs;
+ return;
+ }
+ ring->dequeue++;
+}
+
static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
{
dma_addr_t deq;
struct dbc_ep *dep;
union xhci_trb *evt;
u32 ctrl, portsc;
- struct xhci_hcd *xhci = dbc->xhci;
bool update_erdp = false;
/* DbC state machine: */
@@ -663,7 +765,7 @@
portsc = readl(&dbc->regs->portsc);
if (portsc & DBC_PORTSC_CONN_STATUS) {
dbc->state = DS_CONNECTED;
- xhci_info(xhci, "DbC connected\n");
+ dev_info(dbc->dev, "DbC connected\n");
}
return EVT_DONE;
@@ -671,7 +773,7 @@
ctrl = readl(&dbc->regs->control);
if (ctrl & DBC_CTRL_DBC_RUN) {
dbc->state = DS_CONFIGURED;
- xhci_info(xhci, "DbC configured\n");
+ dev_info(dbc->dev, "DbC configured\n");
portsc = readl(&dbc->regs->portsc);
writel(portsc, &dbc->regs->portsc);
return EVT_GSER;
@@ -683,7 +785,7 @@
portsc = readl(&dbc->regs->portsc);
if (!(portsc & DBC_PORTSC_PORT_ENABLED) &&
!(portsc & DBC_PORTSC_CONN_STATUS)) {
- xhci_info(xhci, "DbC cable unplugged\n");
+ dev_info(dbc->dev, "DbC cable unplugged\n");
dbc->state = DS_ENABLED;
xhci_dbc_flush_requests(dbc);
@@ -692,7 +794,7 @@
/* Handle debug port reset event: */
if (portsc & DBC_PORTSC_RESET_CHANGE) {
- xhci_info(xhci, "DbC port reset\n");
+ dev_info(dbc->dev, "DbC port reset\n");
writel(portsc, &dbc->regs->portsc);
dbc->state = DS_ENABLED;
xhci_dbc_flush_requests(dbc);
@@ -704,16 +806,16 @@
ctrl = readl(&dbc->regs->control);
if ((ctrl & DBC_CTRL_HALT_IN_TR) ||
(ctrl & DBC_CTRL_HALT_OUT_TR)) {
- xhci_info(xhci, "DbC Endpoint stall\n");
+ dev_info(dbc->dev, "DbC Endpoint stall\n");
dbc->state = DS_STALLED;
if (ctrl & DBC_CTRL_HALT_IN_TR) {
- dep = get_in_ep(xhci);
+ dep = get_in_ep(dbc);
xhci_dbc_flush_endpoint_requests(dep);
}
if (ctrl & DBC_CTRL_HALT_OUT_TR) {
- dep = get_out_ep(xhci);
+ dep = get_out_ep(dbc);
xhci_dbc_flush_endpoint_requests(dep);
}
@@ -738,7 +840,7 @@
return EVT_DONE;
default:
- xhci_err(xhci, "Unknown DbC state %d\n", dbc->state);
+ dev_err(dbc->dev, "Unknown DbC state %d\n", dbc->state);
break;
}
@@ -756,16 +858,17 @@
switch (le32_to_cpu(evt->event_cmd.flags) & TRB_TYPE_BITMASK) {
case TRB_TYPE(TRB_PORT_STATUS):
- dbc_handle_port_status(xhci, evt);
+ dbc_handle_port_status(dbc, evt);
break;
case TRB_TYPE(TRB_TRANSFER):
- dbc_handle_xfer_event(xhci, evt);
+ dbc_handle_xfer_event(dbc, evt);
break;
default:
break;
}
- inc_deq(xhci, dbc->ring_evt);
+ inc_evt_deq(dbc->ring_evt);
+
evt = dbc->ring_evt->dequeue;
update_erdp = true;
}
@@ -774,7 +877,7 @@
if (update_erdp) {
deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg,
dbc->ring_evt->dequeue);
- xhci_write_64(xhci, deq, &dbc->regs->erdp);
+ lo_hi_writeq(deq, &dbc->regs->erdp);
}
return EVT_DONE;
@@ -782,14 +885,11 @@
static void xhci_dbc_handle_events(struct work_struct *work)
{
- int ret;
enum evtreturn evtr;
struct xhci_dbc *dbc;
unsigned long flags;
- struct xhci_hcd *xhci;
dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work);
- xhci = dbc->xhci;
spin_lock_irqsave(&dbc->lock, flags);
evtr = xhci_dbc_do_handle_events(dbc);
@@ -797,21 +897,17 @@
switch (evtr) {
case EVT_GSER:
- ret = xhci_dbc_tty_register_device(xhci);
- if (ret) {
- xhci_err(xhci, "failed to alloc tty device\n");
- break;
- }
-
- xhci_info(xhci, "DbC now attached to /dev/ttyDBC0\n");
+ if (dbc->driver->configure)
+ dbc->driver->configure(dbc);
break;
case EVT_DISC:
- xhci_dbc_tty_unregister_device(xhci);
+ if (dbc->driver->disconnect)
+ dbc->driver->disconnect(dbc);
break;
case EVT_DONE:
break;
default:
- xhci_info(xhci, "stop handling dbc events\n");
+ dev_info(dbc->dev, "stop handling dbc events\n");
return;
}
@@ -864,6 +960,7 @@
spin_unlock_irqrestore(&xhci->lock, flags);
dbc->xhci = xhci;
+ dbc->dev = xhci_to_hcd(xhci)->self.sysdev;
INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events);
spin_lock_init(&dbc->lock);
@@ -912,13 +1009,15 @@
const char *buf, size_t count)
{
struct xhci_hcd *xhci;
+ struct xhci_dbc *dbc;
xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ dbc = xhci->dbc;
if (!strncmp(buf, "enable", 6))
- xhci_dbc_start(xhci);
+ xhci_dbc_start(dbc);
else if (!strncmp(buf, "disable", 7))
- xhci_dbc_stop(xhci);
+ xhci_dbc_stop(dbc);
else
return -EINVAL;
@@ -936,7 +1035,7 @@
if (ret)
goto init_err3;
- ret = xhci_dbc_tty_register_driver(xhci);
+ ret = xhci_dbc_tty_probe(xhci);
if (ret)
goto init_err2;
@@ -947,7 +1046,7 @@
return 0;
init_err1:
- xhci_dbc_tty_unregister_driver();
+ xhci_dbc_tty_remove(xhci->dbc);
init_err2:
xhci_do_dbc_exit(xhci);
init_err3:
@@ -962,8 +1061,8 @@
return;
device_remove_file(dev, &dev_attr_dbc);
- xhci_dbc_tty_unregister_driver();
- xhci_dbc_stop(xhci);
+ xhci_dbc_tty_remove(xhci->dbc);
+ xhci_dbc_stop(xhci->dbc);
xhci_do_dbc_exit(xhci);
}
@@ -978,7 +1077,7 @@
if (dbc->state == DS_CONFIGURED)
dbc->resume_required = 1;
- xhci_dbc_stop(xhci);
+ xhci_dbc_stop(dbc);
return 0;
}
@@ -993,7 +1092,7 @@
if (dbc->resume_required) {
dbc->resume_required = 0;
- xhci_dbc_start(xhci);
+ xhci_dbc_start(dbc);
}
return ret;
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index ce0c607..c70b78d 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -84,28 +84,11 @@
DS_STALLED,
};
-struct dbc_request {
- void *buf;
- unsigned int length;
- dma_addr_t dma;
- void (*complete)(struct xhci_hcd *xhci,
- struct dbc_request *req);
- struct list_head list_pool;
- int status;
- unsigned int actual;
-
- struct dbc_ep *dep;
- struct list_head list_pending;
- dma_addr_t trb_dma;
- union xhci_trb *trb;
- unsigned direction:1;
-};
-
struct dbc_ep {
struct xhci_dbc *dbc;
struct list_head list_pending;
struct xhci_ring *ring;
- unsigned direction:1;
+ unsigned int direction:1;
};
#define DBC_QUEUE_SIZE 16
@@ -127,12 +110,16 @@
struct kfifo write_fifo;
bool registered;
- struct dbc_ep *in;
- struct dbc_ep *out;
+};
+
+struct dbc_driver {
+ int (*configure)(struct xhci_dbc *dbc);
+ void (*disconnect)(struct xhci_dbc *dbc);
};
struct xhci_dbc {
spinlock_t lock; /* device access */
+ struct device *dev;
struct xhci_hcd *xhci;
struct dbc_regs __iomem *regs;
struct xhci_ring *ring_evt;
@@ -150,7 +137,25 @@
unsigned resume_required:1;
struct dbc_ep eps[2];
- struct dbc_port port;
+ const struct dbc_driver *driver;
+ void *priv;
+};
+
+struct dbc_request {
+ void *buf;
+ unsigned int length;
+ dma_addr_t dma;
+ void (*complete)(struct xhci_dbc *dbc,
+ struct dbc_request *req);
+ struct list_head list_pool;
+ int status;
+ unsigned int actual;
+
+ struct xhci_dbc *dbc;
+ struct list_head list_pending;
+ dma_addr_t trb_dma;
+ union xhci_trb *trb;
+ unsigned direction:1;
};
#define dbc_bulkout_ctx(d) \
@@ -178,30 +183,26 @@
EVT_DISC,
};
-static inline struct dbc_ep *get_in_ep(struct xhci_hcd *xhci)
+static inline struct dbc_ep *get_in_ep(struct xhci_dbc *dbc)
{
- struct xhci_dbc *dbc = xhci->dbc;
-
return &dbc->eps[BULK_IN];
}
-static inline struct dbc_ep *get_out_ep(struct xhci_hcd *xhci)
+static inline struct dbc_ep *get_out_ep(struct xhci_dbc *dbc)
{
- struct xhci_dbc *dbc = xhci->dbc;
-
return &dbc->eps[BULK_OUT];
}
#ifdef CONFIG_USB_XHCI_DBGCAP
int xhci_dbc_init(struct xhci_hcd *xhci);
void xhci_dbc_exit(struct xhci_hcd *xhci);
-int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci);
-void xhci_dbc_tty_unregister_driver(void);
-int xhci_dbc_tty_register_device(struct xhci_hcd *xhci);
-void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci);
-struct dbc_request *dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags);
-void dbc_free_request(struct dbc_ep *dep, struct dbc_request *req);
-int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req, gfp_t gfp_flags);
+int xhci_dbc_tty_probe(struct xhci_hcd *xhci);
+void xhci_dbc_tty_remove(struct xhci_dbc *dbc);
+struct dbc_request *dbc_alloc_request(struct xhci_dbc *dbc,
+ unsigned int direction,
+ gfp_t flags);
+void dbc_free_request(struct dbc_request *req);
+int dbc_ep_queue(struct dbc_request *req);
#ifdef CONFIG_PM
int xhci_dbc_suspend(struct xhci_hcd *xhci);
int xhci_dbc_resume(struct xhci_hcd *xhci);
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
index be726c7..2a53b28 100644
--- a/drivers/usb/host/xhci-dbgtty.c
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* xhci-dbgtty.c - tty glue for xHCI debug capability
*
* Copyright (C) 2017 Intel Corporation
@@ -14,6 +14,16 @@
#include "xhci.h"
#include "xhci-dbgcap.h"
+static int dbc_tty_init(void);
+static void dbc_tty_exit(void);
+
+static struct tty_driver *dbc_tty_driver;
+
+static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc)
+{
+ return dbc->priv;
+}
+
static unsigned int
dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size)
{
@@ -48,7 +58,7 @@
list_del(&req->list_pool);
spin_unlock(&port->port_lock);
- status = dbc_ep_queue(port->out, req, GFP_ATOMIC);
+ status = dbc_ep_queue(req);
spin_lock(&port->port_lock);
if (status) {
@@ -80,7 +90,7 @@
req->length = DBC_MAX_PACKET;
spin_unlock(&port->port_lock);
- status = dbc_ep_queue(port->in, req, GFP_ATOMIC);
+ status = dbc_ep_queue(req);
spin_lock(&port->port_lock);
if (status) {
@@ -91,11 +101,10 @@
}
static void
-dbc_read_complete(struct xhci_hcd *xhci, struct dbc_request *req)
+dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req)
{
unsigned long flags;
- struct xhci_dbc *dbc = xhci->dbc;
- struct dbc_port *port = &dbc->port;
+ struct dbc_port *port = dbc_to_port(dbc);
spin_lock_irqsave(&port->port_lock, flags);
list_add_tail(&req->list_pool, &port->read_queue);
@@ -103,11 +112,10 @@
spin_unlock_irqrestore(&port->port_lock, flags);
}
-static void dbc_write_complete(struct xhci_hcd *xhci, struct dbc_request *req)
+static void dbc_write_complete(struct xhci_dbc *dbc, struct dbc_request *req)
{
unsigned long flags;
- struct xhci_dbc *dbc = xhci->dbc;
- struct dbc_port *port = &dbc->port;
+ struct dbc_port *port = dbc_to_port(dbc);
spin_lock_irqsave(&port->port_lock, flags);
list_add(&req->list_pool, &port->write_pool);
@@ -118,35 +126,36 @@
case -ESHUTDOWN:
break;
default:
- xhci_warn(xhci, "unexpected write complete status %d\n",
+ dev_warn(dbc->dev, "unexpected write complete status %d\n",
req->status);
break;
}
spin_unlock_irqrestore(&port->port_lock, flags);
}
-static void xhci_dbc_free_req(struct dbc_ep *dep, struct dbc_request *req)
+static void xhci_dbc_free_req(struct dbc_request *req)
{
kfree(req->buf);
- dbc_free_request(dep, req);
+ dbc_free_request(req);
}
static int
-xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head,
- void (*fn)(struct xhci_hcd *, struct dbc_request *))
+xhci_dbc_alloc_requests(struct xhci_dbc *dbc, unsigned int direction,
+ struct list_head *head,
+ void (*fn)(struct xhci_dbc *, struct dbc_request *))
{
int i;
struct dbc_request *req;
for (i = 0; i < DBC_QUEUE_SIZE; i++) {
- req = dbc_alloc_request(dep, GFP_KERNEL);
+ req = dbc_alloc_request(dbc, direction, GFP_KERNEL);
if (!req)
break;
req->length = DBC_MAX_PACKET;
req->buf = kmalloc(req->length, GFP_KERNEL);
if (!req->buf) {
- dbc_free_request(dep, req);
+ dbc_free_request(req);
break;
}
@@ -158,14 +167,14 @@
}
static void
-xhci_dbc_free_requests(struct dbc_ep *dep, struct list_head *head)
+xhci_dbc_free_requests(struct list_head *head)
{
struct dbc_request *req;
while (!list_empty(head)) {
req = list_entry(head->next, struct dbc_request, list_pool);
list_del(&req->list_pool);
- xhci_dbc_free_req(dep, req);
+ xhci_dbc_free_req(req);
}
}
@@ -279,63 +288,14 @@
.unthrottle = dbc_tty_unthrottle,
};
-static struct tty_driver *dbc_tty_driver;
-
-int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci)
-{
- int status;
- struct xhci_dbc *dbc = xhci->dbc;
-
- dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW |
- TTY_DRIVER_DYNAMIC_DEV);
- if (IS_ERR(dbc_tty_driver)) {
- status = PTR_ERR(dbc_tty_driver);
- dbc_tty_driver = NULL;
- return status;
- }
-
- dbc_tty_driver->driver_name = "dbc_serial";
- dbc_tty_driver->name = "ttyDBC";
-
- dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
- dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
- dbc_tty_driver->init_termios = tty_std_termios;
- dbc_tty_driver->init_termios.c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- dbc_tty_driver->init_termios.c_ispeed = 9600;
- dbc_tty_driver->init_termios.c_ospeed = 9600;
- dbc_tty_driver->driver_state = &dbc->port;
-
- tty_set_operations(dbc_tty_driver, &dbc_tty_ops);
-
- status = tty_register_driver(dbc_tty_driver);
- if (status) {
- xhci_err(xhci,
- "can't register dbc tty driver, err %d\n", status);
- put_tty_driver(dbc_tty_driver);
- dbc_tty_driver = NULL;
- }
-
- return status;
-}
-
-void xhci_dbc_tty_unregister_driver(void)
-{
- if (dbc_tty_driver) {
- tty_unregister_driver(dbc_tty_driver);
- put_tty_driver(dbc_tty_driver);
- dbc_tty_driver = NULL;
- }
-}
-
-static void dbc_rx_push(unsigned long _port)
+static void dbc_rx_push(struct tasklet_struct *t)
{
struct dbc_request *req;
struct tty_struct *tty;
unsigned long flags;
bool do_push = false;
bool disconnect = false;
- struct dbc_port *port = (void *)_port;
+ struct dbc_port *port = from_tasklet(port, t, push);
struct list_head *queue = &port->read_queue;
spin_lock_irqsave(&port->port_lock, flags);
@@ -418,17 +378,15 @@
};
static void
-xhci_dbc_tty_init_port(struct xhci_hcd *xhci, struct dbc_port *port)
+xhci_dbc_tty_init_port(struct xhci_dbc *dbc, struct dbc_port *port)
{
tty_port_init(&port->port);
spin_lock_init(&port->port_lock);
- tasklet_init(&port->push, dbc_rx_push, (unsigned long)port);
+ tasklet_setup(&port->push, dbc_rx_push);
INIT_LIST_HEAD(&port->read_pool);
INIT_LIST_HEAD(&port->read_queue);
INIT_LIST_HEAD(&port->write_pool);
- port->in = get_in_ep(xhci);
- port->out = get_out_ep(xhci);
port->port.ops = &dbc_port_ops;
port->n_read = 0;
}
@@ -440,66 +398,157 @@
tty_port_destroy(&port->port);
}
-int xhci_dbc_tty_register_device(struct xhci_hcd *xhci)
+static int xhci_dbc_tty_register_device(struct xhci_dbc *dbc)
{
int ret;
struct device *tty_dev;
- struct xhci_dbc *dbc = xhci->dbc;
- struct dbc_port *port = &dbc->port;
+ struct dbc_port *port = dbc_to_port(dbc);
- xhci_dbc_tty_init_port(xhci, port);
+ if (port->registered)
+ return -EBUSY;
+
+ xhci_dbc_tty_init_port(dbc, port);
+
+ ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL);
+ if (ret)
+ goto err_exit_port;
+
+ ret = xhci_dbc_alloc_requests(dbc, BULK_IN, &port->read_pool,
+ dbc_read_complete);
+ if (ret)
+ goto err_free_fifo;
+
+ ret = xhci_dbc_alloc_requests(dbc, BULK_OUT, &port->write_pool,
+ dbc_write_complete);
+ if (ret)
+ goto err_free_requests;
+
tty_dev = tty_port_register_device(&port->port,
dbc_tty_driver, 0, NULL);
if (IS_ERR(tty_dev)) {
ret = PTR_ERR(tty_dev);
- goto register_fail;
+ goto err_free_requests;
}
- ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL);
- if (ret)
- goto buf_alloc_fail;
-
- ret = xhci_dbc_alloc_requests(port->in, &port->read_pool,
- dbc_read_complete);
- if (ret)
- goto request_fail;
-
- ret = xhci_dbc_alloc_requests(port->out, &port->write_pool,
- dbc_write_complete);
- if (ret)
- goto request_fail;
-
port->registered = true;
return 0;
-request_fail:
- xhci_dbc_free_requests(port->in, &port->read_pool);
- xhci_dbc_free_requests(port->out, &port->write_pool);
+err_free_requests:
+ xhci_dbc_free_requests(&port->read_pool);
+ xhci_dbc_free_requests(&port->write_pool);
+err_free_fifo:
kfifo_free(&port->write_fifo);
-
-buf_alloc_fail:
- tty_unregister_device(dbc_tty_driver, 0);
-
-register_fail:
+err_exit_port:
xhci_dbc_tty_exit_port(port);
- xhci_err(xhci, "can't register tty port, err %d\n", ret);
+ dev_err(dbc->dev, "can't register tty port, err %d\n", ret);
return ret;
}
-void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci)
+static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc)
{
- struct xhci_dbc *dbc = xhci->dbc;
- struct dbc_port *port = &dbc->port;
+ struct dbc_port *port = dbc_to_port(dbc);
+ if (!port->registered)
+ return;
tty_unregister_device(dbc_tty_driver, 0);
xhci_dbc_tty_exit_port(port);
port->registered = false;
kfifo_free(&port->write_fifo);
- xhci_dbc_free_requests(get_out_ep(xhci), &port->read_pool);
- xhci_dbc_free_requests(get_out_ep(xhci), &port->read_queue);
- xhci_dbc_free_requests(get_in_ep(xhci), &port->write_pool);
+ xhci_dbc_free_requests(&port->read_pool);
+ xhci_dbc_free_requests(&port->read_queue);
+ xhci_dbc_free_requests(&port->write_pool);
+}
+
+static const struct dbc_driver dbc_driver = {
+ .configure = xhci_dbc_tty_register_device,
+ .disconnect = xhci_dbc_tty_unregister_device,
+};
+
+int xhci_dbc_tty_probe(struct xhci_hcd *xhci)
+{
+ struct xhci_dbc *dbc = xhci->dbc;
+ struct dbc_port *port;
+ int status;
+
+ /* dbc_tty_init will be called by module init() in the future */
+ status = dbc_tty_init();
+ if (status)
+ return status;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port) {
+ status = -ENOMEM;
+ goto out;
+ }
+
+ dbc->driver = &dbc_driver;
+ dbc->priv = port;
+
+
+ dbc_tty_driver->driver_state = port;
+
+ return 0;
+out:
+ /* dbc_tty_exit will be called by module_exit() in the future */
+ dbc_tty_exit();
+ return status;
+}
+
+/*
+ * undo what probe did, assume dbc is stopped already.
+ * we also assume tty_unregister_device() is called before this
+ */
+void xhci_dbc_tty_remove(struct xhci_dbc *dbc)
+{
+ struct dbc_port *port = dbc_to_port(dbc);
+
+ dbc->driver = NULL;
+ dbc->priv = NULL;
+ kfree(port);
+
+ /* dbc_tty_exit will be called by module_exit() in the future */
+ dbc_tty_exit();
+}
+
+static int dbc_tty_init(void)
+{
+ int ret;
+
+ dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV);
+ if (IS_ERR(dbc_tty_driver))
+ return PTR_ERR(dbc_tty_driver);
+
+ dbc_tty_driver->driver_name = "dbc_serial";
+ dbc_tty_driver->name = "ttyDBC";
+
+ dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ dbc_tty_driver->init_termios = tty_std_termios;
+ dbc_tty_driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ dbc_tty_driver->init_termios.c_ispeed = 9600;
+ dbc_tty_driver->init_termios.c_ospeed = 9600;
+
+ tty_set_operations(dbc_tty_driver, &dbc_tty_ops);
+
+ ret = tty_register_driver(dbc_tty_driver);
+ if (ret) {
+ pr_err("Can't register dbc tty driver\n");
+ put_tty_driver(dbc_tty_driver);
+ }
+ return ret;
+}
+
+static void dbc_tty_exit(void)
+{
+ if (dbc_tty_driver) {
+ tty_unregister_driver(dbc_tty_driver);
+ put_tty_driver(dbc_tty_driver);
+ dbc_tty_driver = NULL;
+ }
}
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index f5c8e4e..dc832dd 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -110,6 +110,7 @@
kfree(regset);
}
+__printf(6, 7)
static void xhci_debugfs_regset(struct xhci_hcd *xhci, u32 base,
const struct debugfs_reg32 *regs,
size_t nregs, struct dentry *parent,
@@ -260,11 +261,13 @@
struct xhci_slot_ctx *slot_ctx;
struct xhci_slot_priv *priv = s->private;
struct xhci_virt_device *dev = priv->dev;
+ char str[XHCI_MSG_MAX];
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(le32_to_cpu(slot_ctx->dev_info),
+ xhci_decode_slot_context(str,
+ le32_to_cpu(slot_ctx->dev_info),
le32_to_cpu(slot_ctx->dev_info2),
le32_to_cpu(slot_ctx->tt_info),
le32_to_cpu(slot_ctx->dev_state)));
@@ -280,6 +283,7 @@
struct xhci_ep_ctx *ep_ctx;
struct xhci_slot_priv *priv = s->private;
struct xhci_virt_device *dev = priv->dev;
+ char str[XHCI_MSG_MAX];
xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus));
@@ -287,7 +291,8 @@
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
dma = dev->out_ctx->dma + (ep_index + 1) * CTX_SIZE(xhci->hcc_params);
seq_printf(s, "%pad: %s\n", &dma,
- xhci_decode_ep_context(le32_to_cpu(ep_ctx->ep_info),
+ xhci_decode_ep_context(str,
+ le32_to_cpu(ep_ctx->ep_info),
le32_to_cpu(ep_ctx->ep_info2),
le64_to_cpu(ep_ctx->deq),
le32_to_cpu(ep_ctx->tx_info)));
@@ -452,9 +457,11 @@
if (!epriv)
return;
+ epriv->show_ring = dev->eps[ep_index].ring;
+
snprintf(epriv->name, sizeof(epriv->name), "ep%02d", ep_index);
epriv->root = xhci_debugfs_create_ring_dir(xhci,
- &dev->eps[ep_index].ring,
+ &epriv->show_ring,
epriv->name,
spriv->root);
spriv->eps[ep_index] = epriv;
@@ -476,6 +483,111 @@
kfree(epriv);
}
+static int xhci_stream_id_show(struct seq_file *s, void *unused)
+{
+ struct xhci_ep_priv *epriv = s->private;
+
+ if (!epriv->stream_info)
+ return -EPERM;
+
+ seq_printf(s, "Show stream ID %d trb ring, supported [1 - %d]\n",
+ epriv->stream_id, epriv->stream_info->num_streams - 1);
+
+ return 0;
+}
+
+static int xhci_stream_id_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, xhci_stream_id_show, inode->i_private);
+}
+
+static ssize_t xhci_stream_id_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct xhci_ep_priv *epriv = s->private;
+ int ret;
+ u16 stream_id; /* MaxPStreams + 1 <= 16 */
+
+ if (!epriv->stream_info)
+ return -EPERM;
+
+ /* Decimal number */
+ ret = kstrtou16_from_user(ubuf, count, 10, &stream_id);
+ if (ret)
+ return ret;
+
+ if (stream_id == 0 || stream_id >= epriv->stream_info->num_streams)
+ return -EINVAL;
+
+ epriv->stream_id = stream_id;
+ epriv->show_ring = epriv->stream_info->stream_rings[stream_id];
+
+ return count;
+}
+
+static const struct file_operations stream_id_fops = {
+ .open = xhci_stream_id_open,
+ .write = xhci_stream_id_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int xhci_stream_context_array_show(struct seq_file *s, void *unused)
+{
+ struct xhci_ep_priv *epriv = s->private;
+ struct xhci_stream_ctx *stream_ctx;
+ dma_addr_t dma;
+ int id;
+
+ if (!epriv->stream_info)
+ return -EPERM;
+
+ seq_printf(s, "Allocated %d streams and %d stream context array entries\n",
+ epriv->stream_info->num_streams,
+ epriv->stream_info->num_stream_ctxs);
+
+ for (id = 0; id < epriv->stream_info->num_stream_ctxs; id++) {
+ stream_ctx = epriv->stream_info->stream_ctx_array + id;
+ dma = epriv->stream_info->ctx_array_dma + id * 16;
+ if (id < epriv->stream_info->num_streams)
+ seq_printf(s, "%pad stream id %d deq %016llx\n", &dma,
+ id, le64_to_cpu(stream_ctx->stream_ring));
+ else
+ seq_printf(s, "%pad stream context entry not used deq %016llx\n",
+ &dma, le64_to_cpu(stream_ctx->stream_ring));
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(xhci_stream_context_array);
+
+void xhci_debugfs_create_stream_files(struct xhci_hcd *xhci,
+ struct xhci_virt_device *dev,
+ int ep_index)
+{
+ struct xhci_slot_priv *spriv = dev->debugfs_private;
+ struct xhci_ep_priv *epriv;
+
+ if (!spriv || !spriv->eps[ep_index] ||
+ !dev->eps[ep_index].stream_info)
+ return;
+
+ epriv = spriv->eps[ep_index];
+ epriv->stream_info = dev->eps[ep_index].stream_info;
+
+ /* Show trb ring of stream ID 1 by default */
+ epriv->stream_id = 1;
+ epriv->show_ring = epriv->stream_info->stream_rings[1];
+ debugfs_create_file("stream_id", 0644,
+ epriv->root, epriv,
+ &stream_id_fops);
+ debugfs_create_file("stream_context_array", 0444,
+ epriv->root, epriv,
+ &xhci_stream_context_array_fops);
+}
+
void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_slot_priv *priv;
diff --git a/drivers/usb/host/xhci-debugfs.h b/drivers/usb/host/xhci-debugfs.h
index f7a4e24..7c074b4 100644
--- a/drivers/usb/host/xhci-debugfs.h
+++ b/drivers/usb/host/xhci-debugfs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* xhci-debugfs.h - xHCI debugfs interface
*
@@ -91,6 +91,9 @@
struct xhci_ep_priv {
char name[DEBUGFS_NAMELEN];
struct dentry *root;
+ struct xhci_stream_info *stream_info;
+ struct xhci_ring *show_ring;
+ unsigned int stream_id;
};
struct xhci_slot_priv {
@@ -113,6 +116,9 @@
void xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
int ep_index);
+void xhci_debugfs_create_stream_files(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) { }
@@ -128,6 +134,10 @@
xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
int ep_index) { }
+static inline void
+xhci_debugfs_create_stream_files(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-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 2208fa6..e8af0a1 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* xHCI host controller driver
*
diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c
index 73aba46..0836985 100644
--- a/drivers/usb/host/xhci-histb.c
+++ b/drivers/usb/host/xhci-histb.c
@@ -219,8 +219,7 @@
if (irq < 0)
return irq;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- histb->ctrl = devm_ioremap_resource(&pdev->dev, res);
+ histb->ctrl = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(histb->ctrl))
return PTR_ERR(histb->ctrl);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 6358d4e..71b018e 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -171,7 +171,6 @@
{
u16 temp;
- desc->bPwrOn2PwrGood = 10; /* xhci section 5.4.9 says 20ms max */
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports;
@@ -206,6 +205,7 @@
desc->bDescriptorType = USB_DT_HUB;
temp = 1 + (ports / 8);
desc->bDescLength = USB_DT_HUB_NONVAR_SIZE + 2 * temp;
+ desc->bPwrOn2PwrGood = 10; /* xhci section 5.4.8 says 20ms */
/* The Device Removable bits are reported on a byte granularity.
* If the port doesn't exist within that byte, the bit is set to 0.
@@ -258,6 +258,7 @@
xhci_common_hub_descriptor(xhci, desc, ports);
desc->bDescriptorType = USB_DT_SS_HUB;
desc->bDescLength = USB_DT_SS_HUB_SIZE;
+ desc->bPwrOn2PwrGood = 50; /* usb 3.1 may fail if less than 100ms */
/* header decode latency should be zero for roothubs,
* see section 4.23.5.2.
@@ -567,6 +568,7 @@
*/
static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
u16 index, bool on, unsigned long *flags)
+ __must_hold(&xhci->lock)
{
struct xhci_hub *rhub;
struct xhci_port *port;
@@ -611,12 +613,13 @@
temp |= test_mode << PORT_TEST_MODE_SHIFT;
writel(temp, port->addr + PORTPMSC);
xhci->test_mode = test_mode;
- if (test_mode == TEST_FORCE_EN)
+ if (test_mode == USB_TEST_FORCE_ENABLE)
xhci_start(xhci);
}
static int xhci_enter_test_mode(struct xhci_hcd *xhci,
u16 test_mode, u16 wIndex, unsigned long *flags)
+ __must_hold(&xhci->lock)
{
int i, retval;
@@ -628,6 +631,7 @@
continue;
retval = xhci_disable_slot(xhci, i);
+ xhci_free_virt_device(xhci, i);
if (retval)
xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n",
i, retval);
@@ -664,7 +668,7 @@
xhci_err(xhci, "Not in test mode, do nothing.\n");
return 0;
}
- if (xhci->test_mode == TEST_FORCE_EN &&
+ if (xhci->test_mode == USB_TEST_FORCE_ENABLE &&
!(xhci->xhc_state & XHCI_STATE_HALTED)) {
retval = xhci_halt(xhci);
if (retval)
@@ -1240,7 +1244,8 @@
temp = readl(ports[wIndex]->addr);
/* Disable port */
if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
- xhci_dbg(xhci, "Disable port %d\n", wIndex);
+ xhci_dbg(xhci, "Disable port %d-%d\n",
+ hcd->self.busnum, wIndex + 1);
temp = xhci_port_state_to_neutral(temp);
/*
* Clear all change bits, so that we get a new
@@ -1256,7 +1261,8 @@
/* Put link in RxDetect (enable port) */
if (link_state == USB_SS_PORT_LS_RX_DETECT) {
- xhci_dbg(xhci, "Enable port %d\n", wIndex);
+ xhci_dbg(xhci, "Enable port %d-%d\n",
+ hcd->self.busnum, wIndex + 1);
xhci_set_link_state(xhci, ports[wIndex],
link_state);
temp = readl(ports[wIndex]->addr);
@@ -1288,8 +1294,8 @@
goto error;
}
- xhci_dbg(xhci, "Enable compliance mode transition for port %d\n",
- wIndex);
+ xhci_dbg(xhci, "Enable compliance mode transition for port %d-%d\n",
+ hcd->self.busnum, wIndex + 1);
xhci_set_link_state(xhci, ports[wIndex],
link_state);
@@ -1303,8 +1309,9 @@
}
/* Can't set port link state above '3' (U3) */
if (link_state > USB_SS_PORT_LS_U3) {
- xhci_warn(xhci, "Cannot set port %d link state %d\n",
- wIndex, link_state);
+ xhci_warn(xhci, "Cannot set port %d-%d link state %d\n",
+ hcd->self.busnum, wIndex + 1,
+ link_state);
goto error;
}
@@ -1339,8 +1346,8 @@
spin_unlock_irqrestore(&xhci->lock, flags);
if (!wait_for_completion_timeout(&bus_state->u3exit_done[wIndex],
msecs_to_jiffies(100)))
- xhci_dbg(xhci, "missing U0 port change event for port %d\n",
- wIndex);
+ xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n",
+ hcd->self.busnum, wIndex + 1);
spin_lock_irqsave(&xhci->lock, flags);
temp = readl(ports[wIndex]->addr);
break;
@@ -1385,15 +1392,15 @@
writel(temp, ports[wIndex]->addr);
temp = readl(ports[wIndex]->addr);
- xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
+ xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n",
+ hcd->self.busnum, wIndex + 1, temp);
break;
case USB_PORT_FEAT_REMOTE_WAKE_MASK:
xhci_set_remote_wake_mask(xhci, ports[wIndex],
wake_mask);
temp = readl(ports[wIndex]->addr);
- xhci_dbg(xhci, "set port remote wake mask, "
- "actual port %d status = 0x%x\n",
- wIndex, temp);
+ xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n",
+ hcd->self.busnum, wIndex + 1, temp);
break;
case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR;
@@ -1420,7 +1427,8 @@
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
if (hcd->speed != HCD_USB2)
goto error;
- if (test_mode > TEST_FORCE_EN || test_mode < TEST_J)
+ if (test_mode > USB_TEST_FORCE_ENABLE ||
+ test_mode < USB_TEST_J)
goto error;
retval = xhci_enter_test_mode(xhci, test_mode, wIndex,
&flags);
@@ -1478,7 +1486,7 @@
break;
case USB_PORT_FEAT_C_SUSPEND:
bus_state->port_c_suspend &= ~(1 << wIndex);
- /* fall through */
+ fallthrough;
case USB_PORT_FEAT_C_RESET:
case USB_PORT_FEAT_C_BH_PORT_RESET:
case USB_PORT_FEAT_C_CONNECTION:
@@ -1634,8 +1642,8 @@
spin_unlock_irqrestore(&xhci->lock, flags);
msleep(XHCI_PORT_POLLING_LFPS_TIME);
spin_lock_irqsave(&xhci->lock, flags);
- xhci_dbg(xhci, "port %d polling in bus suspend, waiting\n",
- port_index);
+ xhci_dbg(xhci, "port %d-%d polling in bus suspend, waiting\n",
+ hcd->self.busnum, port_index + 1);
goto retry;
}
/* bail out if port detected a over-current condition */
@@ -1653,7 +1661,8 @@
xhci_dbg(xhci, "Bus suspend bailout, port connect change\n");
return -EBUSY;
}
- xhci_dbg(xhci, "port %d not suspended\n", port_index);
+ xhci_dbg(xhci, "port %d-%d not suspended\n",
+ hcd->self.busnum, port_index + 1);
t2 &= ~PORT_PLS_MASK;
t2 |= PORT_LINK_STROBE | XDEV_U3;
set_bit(port_index, &bus_state->bus_suspended);
@@ -1787,7 +1796,8 @@
if ((xhci->quirks & XHCI_MISSING_CAS) &&
(hcd->speed >= HCD_USB3) &&
xhci_port_missing_cas_quirk(ports[port_index])) {
- xhci_dbg(xhci, "reset stuck port %d\n", port_index);
+ xhci_dbg(xhci, "reset stuck port %d-%d\n",
+ hcd->self.busnum, port_index + 1);
clear_bit(port_index, &bus_state->bus_suspended);
continue;
}
@@ -1834,8 +1844,8 @@
sret = xhci_handshake(ports[port_index]->addr, PORT_PLC,
PORT_PLC, 10 * 1000);
if (sret) {
- xhci_warn(xhci, "port %d resume PLC timeout\n",
- port_index);
+ xhci_warn(xhci, "port %d-%d resume PLC timeout\n",
+ hcd->self.busnum, port_index + 1);
continue;
}
xhci_test_and_clear_bit(xhci, ports[port_index], PORT_PLC);
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 160caef..ed380ee 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -96,8 +96,9 @@
* DMA address of the next segment. The caller needs to set any Link TRB
* related flags, such as End TRB, Toggle Cycle, and no snoop.
*/
-static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev,
- struct xhci_segment *next, enum xhci_ring_type type)
+static void xhci_link_segments(struct xhci_segment *prev,
+ struct xhci_segment *next,
+ enum xhci_ring_type type, bool chain_links)
{
u32 val;
@@ -112,11 +113,7 @@
val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
val &= ~TRB_TYPE_BITMASK;
val |= TRB_TYPE(TRB_LINK);
- /* Always set the chain bit with 0.95 hardware */
- /* Set chain bit for isoc rings on AMD 0.96 host */
- if (xhci_link_trb_quirk(xhci) ||
- (type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)))
+ if (chain_links)
val |= TRB_CHAIN;
prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
}
@@ -131,13 +128,19 @@
unsigned int num_segs)
{
struct xhci_segment *next;
+ bool chain_links;
if (!ring || !first || !last)
return;
+ /* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
+ chain_links = !!(xhci_link_trb_quirk(xhci) ||
+ (ring->type == TYPE_ISOC &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST)));
+
next = ring->enq_seg->next;
- xhci_link_segments(xhci, ring->enq_seg, first, ring->type);
- xhci_link_segments(xhci, last, next, ring->type);
+ xhci_link_segments(ring->enq_seg, first, ring->type, chain_links);
+ xhci_link_segments(last, next, ring->type, chain_links);
ring->num_segs += num_segs;
ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs;
@@ -290,8 +293,8 @@
kfree(ring);
}
-static void xhci_initialize_ring_info(struct xhci_ring *ring,
- unsigned int cycle_state)
+void xhci_initialize_ring_info(struct xhci_ring *ring,
+ unsigned int cycle_state)
{
/* The ring is empty, so the enqueue pointer == dequeue pointer */
ring->enqueue = ring->first_seg->trbs;
@@ -321,6 +324,12 @@
enum xhci_ring_type type, unsigned int max_packet, gfp_t flags)
{
struct xhci_segment *prev;
+ bool chain_links;
+
+ /* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
+ chain_links = !!(xhci_link_trb_quirk(xhci) ||
+ (type == TYPE_ISOC &&
+ (xhci->quirks & XHCI_AMD_0x96_HOST)));
prev = xhci_segment_alloc(xhci, cycle_state, max_packet, flags);
if (!prev)
@@ -341,18 +350,18 @@
}
return -ENOMEM;
}
- xhci_link_segments(xhci, prev, next, type);
+ xhci_link_segments(prev, next, type, chain_links);
prev = next;
num_segs--;
}
- xhci_link_segments(xhci, prev, *first, type);
+ xhci_link_segments(prev, *first, type, chain_links);
*last = prev;
return 0;
}
-/**
+/*
* Create a new ring with zero or more segments.
*
* Link each segment together into a ring.
@@ -1302,7 +1311,7 @@
interval = xhci_parse_microframe_interval(udev, ep);
break;
}
- /* Fall through - SS and HS isoc/int have same decoding */
+ fallthrough; /* SS and HS isoc/int have same decoding */
case USB_SPEED_SUPER_PLUS:
case USB_SPEED_SUPER:
@@ -1322,7 +1331,7 @@
* since it uses the same rules as low speed interrupt
* endpoints.
*/
- /* fall through */
+ fallthrough;
case USB_SPEED_LOW:
if (usb_endpoint_xfer_int(&ep->desc) ||
@@ -2253,8 +2262,11 @@
if (!rhub->num_ports)
return;
- rhub->ports = kcalloc_node(rhub->num_ports, sizeof(rhub->ports), flags,
- dev_to_node(dev));
+ rhub->ports = kcalloc_node(rhub->num_ports, sizeof(*rhub->ports),
+ flags, dev_to_node(dev));
+ if (!rhub->ports)
+ return;
+
for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
if (xhci->hw_ports[i].rhub != rhub ||
xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY)
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 5c0eb35..1c33157 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -77,7 +77,7 @@
{
struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
u32 value, check_val;
- int u3_ports_disabed = 0;
+ int u3_ports_disabled = 0;
int ret;
int i;
@@ -92,7 +92,7 @@
/* 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++;
+ u3_ports_disabled++;
continue;
}
@@ -117,7 +117,7 @@
check_val = STS1_SYSPLL_STABLE | STS1_REF_RST |
STS1_SYS125_RST | STS1_XHCI_RST;
- if (mtk->num_u3_ports > u3_ports_disabed)
+ if (mtk->num_u3_ports > u3_ports_disabled)
check_val |= STS1_U3_MAC_RST;
ret = readl_poll_timeout(&ippc->ip_pw_sts1, value,
@@ -500,11 +500,6 @@
goto disable_clk;
}
- /* Initialize dma_mask and coherent_dma_mask to 32-bits */
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
- if (ret)
- goto disable_clk;
-
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 985e7a1..2fc0568 100644
--- a/drivers/usb/host/xhci-mtk.h
+++ b/drivers/usb/host/xhci-mtk.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015 MediaTek Inc.
* Author:
@@ -99,7 +99,7 @@
u32 pkts;
u32 cs_count;
u32 burst_mode;
- u32 bw_budget_table[0];
+ u32 bw_budget_table[];
};
#define MU3C_U3_PORT_MAX 4
diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c
index f27d5c2..8ca1a23 100644
--- a/drivers/usb/host/xhci-mvebu.c
+++ b/drivers/usb/host/xhci-mvebu.c
@@ -110,7 +110,7 @@
phy_exit:
phy_exit(phy);
phy_put:
- phy_put(phy);
+ of_phy_put(phy);
phy_out:
return 0;
diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h
index 74b4d21..01bf3fc 100644
--- a/drivers/usb/host/xhci-mvebu.h
+++ b/drivers/usb/host/xhci-mvebu.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2014 Marvell
*
diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c
new file mode 100644
index 0000000..96692db
--- /dev/null
+++ b/drivers/usb/host/xhci-pci-renesas.c
@@ -0,0 +1,639 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2019-2020 Linaro Limited */
+
+#include <linux/acpi.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#include "xhci.h"
+#include "xhci-trace.h"
+#include "xhci-pci.h"
+
+#define RENESAS_FW_VERSION 0x6C
+#define RENESAS_ROM_CONFIG 0xF0
+#define RENESAS_FW_STATUS 0xF4
+#define RENESAS_FW_STATUS_MSB 0xF5
+#define RENESAS_ROM_STATUS 0xF6
+#define RENESAS_ROM_STATUS_MSB 0xF7
+#define RENESAS_DATA0 0xF8
+#define RENESAS_DATA1 0xFC
+
+#define RENESAS_FW_VERSION_FIELD GENMASK(23, 7)
+#define RENESAS_FW_VERSION_OFFSET 8
+
+#define RENESAS_FW_STATUS_DOWNLOAD_ENABLE BIT(0)
+#define RENESAS_FW_STATUS_LOCK BIT(1)
+#define RENESAS_FW_STATUS_RESULT GENMASK(6, 4)
+ #define RENESAS_FW_STATUS_INVALID 0
+ #define RENESAS_FW_STATUS_SUCCESS BIT(4)
+ #define RENESAS_FW_STATUS_ERROR BIT(5)
+#define RENESAS_FW_STATUS_SET_DATA0 BIT(8)
+#define RENESAS_FW_STATUS_SET_DATA1 BIT(9)
+
+#define RENESAS_ROM_STATUS_ACCESS BIT(0)
+#define RENESAS_ROM_STATUS_ERASE BIT(1)
+#define RENESAS_ROM_STATUS_RELOAD BIT(2)
+#define RENESAS_ROM_STATUS_RESULT GENMASK(6, 4)
+ #define RENESAS_ROM_STATUS_NO_RESULT 0
+ #define RENESAS_ROM_STATUS_SUCCESS BIT(4)
+ #define RENESAS_ROM_STATUS_ERROR BIT(5)
+#define RENESAS_ROM_STATUS_SET_DATA0 BIT(8)
+#define RENESAS_ROM_STATUS_SET_DATA1 BIT(9)
+#define RENESAS_ROM_STATUS_ROM_EXISTS BIT(15)
+
+#define RENESAS_ROM_ERASE_MAGIC 0x5A65726F
+#define RENESAS_ROM_WRITE_MAGIC 0x53524F4D
+
+#define RENESAS_RETRY 10000
+#define RENESAS_DELAY 10
+
+static int renesas_fw_download_image(struct pci_dev *dev,
+ const u32 *fw, size_t step, bool rom)
+{
+ size_t i;
+ int err;
+ u8 fw_status;
+ bool data0_or_data1;
+ u32 status_reg;
+
+ if (rom)
+ status_reg = RENESAS_ROM_STATUS_MSB;
+ else
+ status_reg = RENESAS_FW_STATUS_MSB;
+
+ /*
+ * The hardware does alternate between two 32-bit pages.
+ * (This is because each row of the firmware is 8 bytes).
+ *
+ * for even steps we use DATA0, for odd steps DATA1.
+ */
+ data0_or_data1 = (step & 1) == 1;
+
+ /* step+1. Read "Set DATAX" and confirm it is cleared. */
+ for (i = 0; i < RENESAS_RETRY; i++) {
+ err = pci_read_config_byte(dev, status_reg, &fw_status);
+ if (err) {
+ dev_err(&dev->dev, "Read Status failed: %d\n",
+ pcibios_err_to_errno(err));
+ return pcibios_err_to_errno(err);
+ }
+ if (!(fw_status & BIT(data0_or_data1)))
+ break;
+
+ udelay(RENESAS_DELAY);
+ }
+ if (i == RENESAS_RETRY) {
+ dev_err(&dev->dev, "Timeout for Set DATAX step: %zd\n", step);
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * step+2. Write FW data to "DATAX".
+ * "LSB is left" => force little endian
+ */
+ err = pci_write_config_dword(dev, data0_or_data1 ?
+ RENESAS_DATA1 : RENESAS_DATA0,
+ (__force u32)cpu_to_le32(fw[step]));
+ if (err) {
+ dev_err(&dev->dev, "Write to DATAX failed: %d\n",
+ pcibios_err_to_errno(err));
+ return pcibios_err_to_errno(err);
+ }
+
+ udelay(100);
+
+ /* step+3. Set "Set DATAX". */
+ err = pci_write_config_byte(dev, status_reg, BIT(data0_or_data1));
+ if (err) {
+ dev_err(&dev->dev, "Write config for DATAX failed: %d\n",
+ pcibios_err_to_errno(err));
+ return pcibios_err_to_errno(err);
+ }
+
+ return 0;
+}
+
+static int renesas_fw_verify(const void *fw_data,
+ size_t length)
+{
+ u16 fw_version_pointer;
+ u16 fw_version;
+
+ /*
+ * The Firmware's Data Format is describe in
+ * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124
+ */
+
+ /*
+ * The bootrom chips of the big brother have sizes up to 64k, let's
+ * assume that's the biggest the firmware can get.
+ */
+ if (length < 0x1000 || length >= 0x10000) {
+ pr_err("firmware is size %zd is not (4k - 64k).",
+ length);
+ return -EINVAL;
+ }
+
+ /* The First 2 bytes are fixed value (55aa). "LSB on Left" */
+ if (get_unaligned_le16(fw_data) != 0x55aa) {
+ pr_err("no valid firmware header found.");
+ return -EINVAL;
+ }
+
+ /* verify the firmware version position and print it. */
+ fw_version_pointer = get_unaligned_le16(fw_data + 4);
+ if (fw_version_pointer + 2 >= length) {
+ pr_err("fw ver pointer is outside of the firmware image");
+ return -EINVAL;
+ }
+
+ fw_version = get_unaligned_le16(fw_data + fw_version_pointer);
+ pr_err("got firmware version: %02x.", fw_version);
+
+ return 0;
+}
+
+static bool renesas_check_rom(struct pci_dev *pdev)
+{
+ u16 rom_status;
+ int retval;
+
+ /* Check if external ROM exists */
+ retval = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_status);
+ if (retval)
+ return false;
+
+ rom_status &= RENESAS_ROM_STATUS_ROM_EXISTS;
+ if (rom_status) {
+ dev_dbg(&pdev->dev, "External ROM exists\n");
+ return true; /* External ROM exists */
+ }
+
+ return false;
+}
+
+static int renesas_check_rom_state(struct pci_dev *pdev)
+{
+ u16 rom_state;
+ u32 version;
+ int err;
+
+ /* check FW version */
+ err = pci_read_config_dword(pdev, RENESAS_FW_VERSION, &version);
+ if (err)
+ return pcibios_err_to_errno(err);
+
+ version &= RENESAS_FW_VERSION_FIELD;
+ version = version >> RENESAS_FW_VERSION_OFFSET;
+ dev_dbg(&pdev->dev, "Found ROM version: %x\n", version);
+
+ /*
+ * Test if ROM is present and loaded, if so we can skip everything
+ */
+ err = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_state);
+ if (err)
+ return pcibios_err_to_errno(err);
+
+ if (rom_state & BIT(15)) {
+ /* ROM exists */
+ dev_dbg(&pdev->dev, "ROM exists\n");
+
+ /* Check the "Result Code" Bits (6:4) and act accordingly */
+ switch (rom_state & RENESAS_ROM_STATUS_RESULT) {
+ case RENESAS_ROM_STATUS_SUCCESS:
+ return 0;
+
+ case RENESAS_ROM_STATUS_NO_RESULT: /* No result yet */
+ dev_dbg(&pdev->dev, "Unknown ROM status ...\n");
+ return -ENOENT;
+
+ case RENESAS_ROM_STATUS_ERROR: /* Error State */
+ default: /* All other states are marked as "Reserved states" */
+ dev_err(&pdev->dev, "Invalid ROM..");
+ break;
+ }
+ }
+
+ return -EIO;
+}
+
+static int renesas_fw_check_running(struct pci_dev *pdev)
+{
+ u8 fw_state;
+ int err;
+
+ /*
+ * Test if the device is actually needing the firmware. As most
+ * BIOSes will initialize the device for us. If the device is
+ * initialized.
+ */
+ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_state);
+ if (err)
+ return pcibios_err_to_errno(err);
+
+ /*
+ * Check if "FW Download Lock" is locked. If it is and the FW is
+ * ready we can simply continue. If the FW is not ready, we have
+ * to give up.
+ */
+ if (fw_state & RENESAS_FW_STATUS_LOCK) {
+ dev_dbg(&pdev->dev, "FW Download Lock is engaged.");
+
+ if (fw_state & RENESAS_FW_STATUS_SUCCESS)
+ return 0;
+
+ dev_err(&pdev->dev,
+ "FW Download Lock is set and FW is not ready. Giving Up.");
+ return -EIO;
+ }
+
+ /*
+ * Check if "FW Download Enable" is set. If someone (us?) tampered
+ * with it and it can't be reset, we have to give up too... and
+ * ask for a forgiveness and a reboot.
+ */
+ if (fw_state & RENESAS_FW_STATUS_DOWNLOAD_ENABLE) {
+ dev_err(&pdev->dev,
+ "FW Download Enable is stale. Giving Up (poweroff/reboot needed).");
+ return -EIO;
+ }
+
+ /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */
+ switch (fw_state & RENESAS_FW_STATUS_RESULT) {
+ case 0: /* No result yet */
+ dev_dbg(&pdev->dev, "FW is not ready/loaded yet.");
+
+ /* tell the caller, that this device needs the firmware. */
+ return 1;
+
+ case RENESAS_FW_STATUS_SUCCESS: /* Success, device should be working. */
+ dev_dbg(&pdev->dev, "FW is ready.");
+ return 0;
+
+ case RENESAS_FW_STATUS_ERROR: /* Error State */
+ dev_err(&pdev->dev,
+ "hardware is in an error state. Giving up (poweroff/reboot needed).");
+ return -ENODEV;
+
+ default: /* All other states are marked as "Reserved states" */
+ dev_err(&pdev->dev,
+ "hardware is in an invalid state %lx. Giving up (poweroff/reboot needed).",
+ (fw_state & RENESAS_FW_STATUS_RESULT) >> 4);
+ return -EINVAL;
+ }
+}
+
+static int renesas_fw_download(struct pci_dev *pdev,
+ const struct firmware *fw)
+{
+ const u32 *fw_data = (const u32 *)fw->data;
+ size_t i;
+ int err;
+ u8 fw_status;
+
+ /*
+ * For more information and the big picture: please look at the
+ * "Firmware Download Sequence" in "7.1 FW Download Interface"
+ * of R19UH0078EJ0500 Rev.5.00 page 131
+ */
+
+ /*
+ * 0. Set "FW Download Enable" bit in the
+ * "FW Download Control & Status Register" at 0xF4
+ */
+ err = pci_write_config_byte(pdev, RENESAS_FW_STATUS,
+ RENESAS_FW_STATUS_DOWNLOAD_ENABLE);
+ if (err)
+ return pcibios_err_to_errno(err);
+
+ /* 1 - 10 follow one step after the other. */
+ for (i = 0; i < fw->size / 4; i++) {
+ err = renesas_fw_download_image(pdev, fw_data, i, false);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Firmware Download Step %zd failed at position %zd bytes with (%d).",
+ i, i * 4, err);
+ return err;
+ }
+ }
+
+ /*
+ * This sequence continues until the last data is written to
+ * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1"
+ * is cleared by the hardware beforehand.
+ */
+ for (i = 0; i < RENESAS_RETRY; i++) {
+ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS_MSB,
+ &fw_status);
+ if (err)
+ return pcibios_err_to_errno(err);
+ if (!(fw_status & (BIT(0) | BIT(1))))
+ break;
+
+ udelay(RENESAS_DELAY);
+ }
+ if (i == RENESAS_RETRY)
+ dev_warn(&pdev->dev, "Final Firmware Download step timed out.");
+
+ /*
+ * 11. After finishing writing the last data of FW, the
+ * System Software must clear "FW Download Enable"
+ */
+ err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, 0);
+ if (err)
+ return pcibios_err_to_errno(err);
+
+ /* 12. Read "Result Code" and confirm it is good. */
+ for (i = 0; i < RENESAS_RETRY; i++) {
+ err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_status);
+ if (err)
+ return pcibios_err_to_errno(err);
+ if (fw_status & RENESAS_FW_STATUS_SUCCESS)
+ break;
+
+ udelay(RENESAS_DELAY);
+ }
+ if (i == RENESAS_RETRY) {
+ /* Timed out / Error - let's see if we can fix this */
+ err = renesas_fw_check_running(pdev);
+ switch (err) {
+ case 0: /*
+ * we shouldn't end up here.
+ * maybe it took a little bit longer.
+ * But all should be well?
+ */
+ break;
+
+ case 1: /* (No result yet! */
+ dev_err(&pdev->dev, "FW Load timedout");
+ return -ETIMEDOUT;
+
+ default:
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void renesas_rom_erase(struct pci_dev *pdev)
+{
+ int retval, i;
+ u8 status;
+
+ dev_dbg(&pdev->dev, "Performing ROM Erase...\n");
+ retval = pci_write_config_dword(pdev, RENESAS_DATA0,
+ RENESAS_ROM_ERASE_MAGIC);
+ if (retval) {
+ dev_err(&pdev->dev, "ROM erase, magic word write failed: %d\n",
+ pcibios_err_to_errno(retval));
+ return;
+ }
+
+ retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
+ if (retval) {
+ dev_err(&pdev->dev, "ROM status read failed: %d\n",
+ pcibios_err_to_errno(retval));
+ return;
+ }
+ status |= RENESAS_ROM_STATUS_ERASE;
+ retval = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, status);
+ if (retval) {
+ dev_err(&pdev->dev, "ROM erase set word write failed\n");
+ return;
+ }
+
+ /* sleep a bit while ROM is erased */
+ msleep(20);
+
+ for (i = 0; i < RENESAS_RETRY; i++) {
+ retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS,
+ &status);
+ status &= RENESAS_ROM_STATUS_ERASE;
+ if (!status)
+ break;
+
+ mdelay(RENESAS_DELAY);
+ }
+
+ if (i == RENESAS_RETRY)
+ dev_dbg(&pdev->dev, "Chip erase timedout: %x\n", status);
+
+ dev_dbg(&pdev->dev, "ROM Erase... Done success\n");
+}
+
+static bool renesas_setup_rom(struct pci_dev *pdev, const struct firmware *fw)
+{
+ const u32 *fw_data = (const u32 *)fw->data;
+ int err, i;
+ u8 status;
+
+ /* 2. Write magic word to Data0 */
+ err = pci_write_config_dword(pdev, RENESAS_DATA0,
+ RENESAS_ROM_WRITE_MAGIC);
+ if (err)
+ return false;
+
+ /* 3. Set External ROM access */
+ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS,
+ RENESAS_ROM_STATUS_ACCESS);
+ if (err)
+ goto remove_bypass;
+
+ /* 4. Check the result */
+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
+ if (err)
+ goto remove_bypass;
+ status &= GENMASK(6, 4);
+ if (status) {
+ dev_err(&pdev->dev,
+ "setting external rom failed: %x\n", status);
+ goto remove_bypass;
+ }
+
+ /* 5 to 16 Write FW to DATA0/1 while checking SetData0/1 */
+ for (i = 0; i < fw->size / 4; i++) {
+ err = renesas_fw_download_image(pdev, fw_data, i, true);
+ if (err) {
+ dev_err(&pdev->dev,
+ "ROM Download Step %d failed at position %d bytes with (%d)\n",
+ i, i * 4, err);
+ goto remove_bypass;
+ }
+ }
+
+ /*
+ * wait till DATA0/1 is cleared
+ */
+ for (i = 0; i < RENESAS_RETRY; i++) {
+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS_MSB,
+ &status);
+ if (err)
+ goto remove_bypass;
+ if (!(status & (BIT(0) | BIT(1))))
+ break;
+
+ udelay(RENESAS_DELAY);
+ }
+ if (i == RENESAS_RETRY) {
+ dev_err(&pdev->dev, "Final Firmware ROM Download step timed out\n");
+ goto remove_bypass;
+ }
+
+ /* 17. Remove bypass */
+ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0);
+ if (err)
+ return false;
+
+ udelay(10);
+
+ /* 18. check result */
+ for (i = 0; i < RENESAS_RETRY; i++) {
+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
+ if (err) {
+ dev_err(&pdev->dev, "Read ROM status failed:%d\n",
+ pcibios_err_to_errno(err));
+ return false;
+ }
+ status &= RENESAS_ROM_STATUS_RESULT;
+ if (status == RENESAS_ROM_STATUS_SUCCESS) {
+ dev_dbg(&pdev->dev, "Download ROM success\n");
+ break;
+ }
+ udelay(RENESAS_DELAY);
+ }
+ if (i == RENESAS_RETRY) { /* Timed out */
+ dev_err(&pdev->dev,
+ "Download to external ROM TO: %x\n", status);
+ return false;
+ }
+
+ dev_dbg(&pdev->dev, "Download to external ROM succeeded\n");
+
+ /* Last step set Reload */
+ err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS,
+ RENESAS_ROM_STATUS_RELOAD);
+ if (err) {
+ dev_err(&pdev->dev, "Set ROM execute failed: %d\n",
+ pcibios_err_to_errno(err));
+ return false;
+ }
+
+ /*
+ * wait till Reload is cleared
+ */
+ for (i = 0; i < RENESAS_RETRY; i++) {
+ err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
+ if (err)
+ return false;
+ if (!(status & RENESAS_ROM_STATUS_RELOAD))
+ break;
+
+ udelay(RENESAS_DELAY);
+ }
+ if (i == RENESAS_RETRY) {
+ dev_err(&pdev->dev, "ROM Exec timed out: %x\n", status);
+ return false;
+ }
+
+ return true;
+
+remove_bypass:
+ pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0);
+ return false;
+}
+
+static int renesas_load_fw(struct pci_dev *pdev, const struct firmware *fw)
+{
+ int err = 0;
+ bool rom;
+
+ /* Check if the device has external ROM */
+ rom = renesas_check_rom(pdev);
+ if (rom) {
+ /* perform chip erase first */
+ renesas_rom_erase(pdev);
+
+ /* lets try loading fw on ROM first */
+ rom = renesas_setup_rom(pdev, fw);
+ if (!rom) {
+ dev_dbg(&pdev->dev,
+ "ROM load failed, falling back on FW load\n");
+ } else {
+ dev_dbg(&pdev->dev,
+ "ROM load success\n");
+ goto exit;
+ }
+ }
+
+ err = renesas_fw_download(pdev, fw);
+
+exit:
+ if (err)
+ dev_err(&pdev->dev, "firmware failed to download (%d).", err);
+ return err;
+}
+
+int renesas_xhci_check_request_fw(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct xhci_driver_data *driver_data =
+ (struct xhci_driver_data *)id->driver_data;
+ const char *fw_name = driver_data->firmware;
+ const struct firmware *fw;
+ bool has_rom;
+ int err;
+
+ /* Check if device has ROM and loaded, if so skip everything */
+ has_rom = renesas_check_rom(pdev);
+ if (has_rom) {
+ err = renesas_check_rom_state(pdev);
+ if (!err)
+ return 0;
+ else if (err != -ENOENT)
+ has_rom = false;
+ }
+
+ err = renesas_fw_check_running(pdev);
+ /* Continue ahead, if the firmware is already running. */
+ if (err == 0)
+ return 0;
+
+ /* no firmware interface available */
+ if (err != 1)
+ return has_rom ? 0 : err;
+
+ pci_dev_get(pdev);
+ err = firmware_request_nowarn(&fw, fw_name, &pdev->dev);
+ pci_dev_put(pdev);
+ if (err) {
+ if (has_rom) {
+ dev_info(&pdev->dev, "failed to load firmware %s, fallback to ROM\n",
+ fw_name);
+ return 0;
+ }
+ dev_err(&pdev->dev, "failed to load firmware %s: %d\n",
+ fw_name, err);
+ return err;
+ }
+
+ err = renesas_fw_verify(fw->data, fw->size);
+ if (err)
+ goto exit;
+
+ err = renesas_load_fw(pdev, fw);
+exit:
+ release_firmware(fw);
+ return err;
+}
+EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
+
+void renesas_xhci_pci_exit(struct pci_dev *dev)
+{
+}
+EXPORT_SYMBOL_GPL(renesas_xhci_pci_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index d242779..dafb58f 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -12,9 +12,11 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/acpi.h>
+#include <linux/reset.h>
#include "xhci.h"
#include "xhci-trace.h"
+#include "xhci-pci.h"
#define SSIC_PORT_NUM 2
#define SSIC_PORT_CFG2 0x880c
@@ -28,6 +30,7 @@
#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1009 0x1009
+#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1100 0x1100
#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400
#define PCI_VENDOR_ID_ETRON 0x1b6f
@@ -53,11 +56,23 @@
#define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI 0x15f0
#define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13
#define PCI_DEVICE_ID_INTEL_CML_XHCI 0xa3af
+#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13
+#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI 0x1138
+#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_XHCI 0x461e
#define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9
#define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba
#define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb
#define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc
+#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_1 0x161a
+#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_2 0x161b
+#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_3 0x161d
+#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_4 0x161e
+#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_5 0x15d6
+#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_6 0x15d7
+#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_7 0x161c
+#define PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_8 0x161f
+
#define PCI_DEVICE_ID_ASMEDIA_1042_XHCI 0x1042
#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142
#define PCI_DEVICE_ID_ASMEDIA_1142_XHCI 0x1242
@@ -93,7 +108,16 @@
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
- struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct xhci_driver_data *driver_data;
+ const struct pci_device_id *id;
+
+ id = pci_match_id(pdev->driver->id_table, pdev);
+
+ if (id && id->driver_data) {
+ driver_data = (struct xhci_driver_data *)id->driver_data;
+ xhci->quirks |= driver_data->quirks;
+ }
/* Look for vendor-specific quirks */
if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
@@ -132,6 +156,10 @@
pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1009)
xhci->quirks |= XHCI_BROKEN_STREAMS;
+ if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
+ pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1100)
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+
if (pdev->vendor == PCI_VENDOR_ID_NEC)
xhci->quirks |= XHCI_NEC_HOST;
@@ -230,7 +258,10 @@
pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI))
+ pdev->device == PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_XHCI))
xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
@@ -257,6 +288,11 @@
pdev->device == 0x3432)
xhci->quirks |= XHCI_BROKEN_STREAMS;
+ if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) {
+ xhci->quirks |= XHCI_LPM_SUPPORT;
+ xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS;
+ }
+
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
pdev->device == PCI_DEVICE_ID_ASMEDIA_1042_XHCI)
xhci->quirks |= XHCI_BROKEN_STREAMS;
@@ -288,6 +324,17 @@
pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4))
xhci->quirks |= XHCI_NO_SOFT_RETRY;
+ if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+ (pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_1 ||
+ pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_2 ||
+ pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_3 ||
+ pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_4 ||
+ pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_5 ||
+ pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_6 ||
+ pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_7 ||
+ pdev->device == PCI_DEVICE_ID_AMD_YELLOW_CARP_XHCI_8))
+ xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
+
if (xhci->quirks & XHCI_RESET_ON_RESUME)
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"QUIRK: Resetting on resume");
@@ -347,10 +394,21 @@
{
int retval;
struct xhci_hcd *xhci;
- struct hc_driver *driver;
struct usb_hcd *hcd;
+ struct xhci_driver_data *driver_data;
+ struct reset_control *reset;
- driver = (struct hc_driver *)id->driver_data;
+ driver_data = (struct xhci_driver_data *)id->driver_data;
+ if (driver_data && driver_data->quirks & XHCI_RENESAS_FW_QUIRK) {
+ retval = renesas_xhci_check_request_fw(dev, id);
+ if (retval)
+ return retval;
+ }
+
+ reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL);
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+ reset_control_reset(reset);
/* Prevent runtime suspending between USB-2 and USB-3 initialization */
pm_runtime_get_noresume(&dev->dev);
@@ -361,7 +419,7 @@
* to say USB 2.0, but I'm not sure what the implications would be in
* the other parts of the HCD code.
*/
- retval = usb_hcd_pci_probe(dev, id);
+ retval = usb_hcd_pci_probe(dev, id, &xhci_pci_hc_driver);
if (retval)
goto put_runtime_pm;
@@ -369,8 +427,9 @@
/* USB 2.0 roothub is stored in the PCI device now. */
hcd = dev_get_drvdata(&dev->dev);
xhci = hcd_to_xhci(hcd);
- xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev,
- pci_name(dev), hcd);
+ xhci->reset = reset;
+ xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev,
+ pci_name(dev), hcd);
if (!xhci->shared_hcd) {
retval = -ENOMEM;
goto dealloc_usb2_hcd;
@@ -412,6 +471,9 @@
struct xhci_hcd *xhci;
xhci = hcd_to_xhci(pci_get_drvdata(dev));
+ if (xhci->quirks & XHCI_RENESAS_FW_QUIRK)
+ renesas_xhci_pci_exit(dev);
+
xhci->xhc_state |= XHCI_STATE_REMOVING;
if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW)
@@ -529,6 +591,8 @@
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval = 0;
+ reset_control_reset(xhci->reset);
+
/* The BIOS on systems with the Intel Panther Point chipset may or may
* not support xHCI natively. That means that during system resume, it
* may switch the ports back to EHCI so that users can use their
@@ -575,19 +639,37 @@
/*-------------------------------------------------------------------------*/
+static const struct xhci_driver_data reneses_data = {
+ .quirks = XHCI_RENESAS_FW_QUIRK,
+ .firmware = "renesas_usb_fw.mem",
+};
+
/* PCI driver selection metadata; PCI hotplugging uses this */
-static const struct pci_device_id pci_ids[] = { {
+static const struct pci_device_id pci_ids[] = {
+ { PCI_DEVICE(0x1912, 0x0014),
+ .driver_data = (unsigned long)&reneses_data,
+ },
+ { PCI_DEVICE(0x1912, 0x0015),
+ .driver_data = (unsigned long)&reneses_data,
+ },
/* handle any USB 3.0 xHCI controller */
- PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
- .driver_data = (unsigned long) &xhci_pci_hc_driver,
+ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
},
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, pci_ids);
+/*
+ * Without CONFIG_USB_XHCI_PCI_RENESAS renesas_xhci_check_request_fw() won't
+ * load firmware, so don't encumber the xhci-pci driver with it.
+ */
+#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
+MODULE_FIRMWARE("renesas_usb_fw.mem");
+#endif
+
/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver xhci_pci_driver = {
- .name = (char *) hcd_name,
+ .name = hcd_name,
.id_table = pci_ids,
.probe = xhci_pci_probe,
diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h
new file mode 100644
index 0000000..acd7cf0
--- /dev/null
+++ b/drivers/usb/host/xhci-pci.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2019-2020 Linaro Limited */
+
+#ifndef XHCI_PCI_H
+#define XHCI_PCI_H
+
+#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
+int renesas_xhci_check_request_fw(struct pci_dev *dev,
+ const struct pci_device_id *id);
+void renesas_xhci_pci_exit(struct pci_dev *dev);
+
+#else
+static int renesas_xhci_check_request_fw(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return 0;
+}
+
+static void renesas_xhci_pci_exit(struct pci_dev *dev) { };
+
+#endif
+
+struct xhci_driver_data {
+ u64 quirks;
+ const char *firmware;
+};
+
+#endif
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 84cfa85..dc570ce 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -2,7 +2,7 @@
/*
* xhci-plat.c - xHCI host controller driver platform Bus Glue.
*
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com
* Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* A lot of code borrowed from the Linux xHCI driver.
@@ -64,6 +64,16 @@
return priv->init_quirk(hcd);
}
+static int xhci_priv_suspend_quirk(struct usb_hcd *hcd)
+{
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ if (!priv->suspend_quirk)
+ return 0;
+
+ return priv->suspend_quirk(hcd);
+}
+
static int xhci_priv_resume_quirk(struct usb_hcd *hcd)
{
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
@@ -123,6 +133,10 @@
SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3)
};
+static const struct xhci_plat_priv xhci_plat_brcm = {
+ .quirks = XHCI_RESET_ON_RESUME,
+};
+
static const struct of_device_id usb_xhci_of_match[] = {
{
.compatible = "generic-xhci",
@@ -158,6 +172,12 @@
}, {
.compatible = "renesas,rcar-gen3-xhci",
.data = &xhci_plat_renesas_rcar_gen3,
+ }, {
+ .compatible = "brcm,xhci-brcm-v2",
+ .data = &xhci_plat_brcm,
+ }, {
+ .compatible = "brcm,bcm7445-xhci",
+ .data = &xhci_plat_brcm,
},
{},
};
@@ -232,8 +252,7 @@
goto disable_runtime;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs);
goto put_hcd;
@@ -268,15 +287,18 @@
if (ret)
goto disable_reg_clk;
- priv_match = of_device_get_match_data(&pdev->dev);
+ if (pdev->dev.of_node)
+ priv_match = of_device_get_match_data(&pdev->dev);
+ else
+ priv_match = dev_get_platdata(&pdev->dev);
+
if (priv_match) {
priv = hcd_to_xhci_priv(hcd);
/* Just copy data for now */
- if (priv_match)
- *priv = *priv_match;
+ *priv = *priv_match;
}
- device_wakeup_enable(hcd->self.controller);
+ device_set_wakeup_capable(&pdev->dev, true);
xhci->main_hcd = hcd;
xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
@@ -329,6 +351,9 @@
if ((xhci->quirks & XHCI_SKIP_PHY_INIT) || (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)))
hcd->skip_phy_initialization = 1;
+ if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK))
+ xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
+
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
goto disable_usb_phy;
@@ -410,14 +435,17 @@
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int ret;
+ if (pm_runtime_suspended(dev))
+ pm_runtime_resume(dev);
+
+ ret = xhci_priv_suspend_quirk(hcd);
+ if (ret)
+ return ret;
/*
* xhci_suspend() needs `do_wakeup` to know whether host is allowed
- * to do wakeup during suspend. Since xhci_plat_suspend is currently
- * only designed for system suspend, device_may_wakeup() is enough
- * to dertermine whether host is allowed to do wakeup. Need to
- * reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
- * also applies to runtime suspend.
+ * to do wakeup during suspend.
*/
return xhci_suspend(xhci, device_may_wakeup(dev));
}
@@ -432,13 +460,26 @@
if (ret)
return ret;
- return xhci_resume(xhci, 0);
+ ret = xhci_resume(xhci, 0);
+ if (ret)
+ return ret;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
}
static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ int ret;
+
+ ret = xhci_priv_suspend_quirk(hcd);
+ if (ret)
+ return ret;
return xhci_suspend(xhci, true);
}
@@ -459,12 +500,14 @@
NULL)
};
+#ifdef CONFIG_ACPI
static const struct acpi_device_id usb_xhci_acpi_match[] = {
/* XHCI-compliant USB Controller */
{ "PNP0D10", },
{ }
};
MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);
+#endif
static struct platform_driver usb_xhci_driver = {
.probe = xhci_plat_probe,
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index b774915..561d0b7 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* xhci-plat.h - xHCI host controller driver platform Bus Glue.
*
@@ -16,6 +16,7 @@
int (*plat_setup)(struct usb_hcd *);
void (*plat_start)(struct usb_hcd *);
int (*init_quirk)(struct usb_hcd *);
+ int (*suspend_quirk)(struct usb_hcd *);
int (*resume_quirk)(struct usb_hcd *);
};
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index 3da75b3..9888ba7 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -6,6 +6,7 @@
*/
#include <linux/firmware.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
@@ -127,8 +128,7 @@
void __iomem *regs = hcd->regs;
struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
const struct firmware *fw;
- int retval, index, j, time;
- int timeout = 10000;
+ int retval, index, j;
u32 data, val, temp;
u32 quirks = 0;
const struct soc_device_attribute *attr;
@@ -173,32 +173,19 @@
temp |= RCAR_USB3_DL_CTRL_FW_SET_DATA0;
writel(temp, regs + RCAR_USB3_DL_CTRL);
- for (time = 0; time < timeout; time++) {
- val = readl(regs + RCAR_USB3_DL_CTRL);
- if ((val & RCAR_USB3_DL_CTRL_FW_SET_DATA0) == 0)
- break;
- udelay(1);
- }
- if (time == timeout) {
- retval = -ETIMEDOUT;
+ retval = readl_poll_timeout_atomic(regs + RCAR_USB3_DL_CTRL,
+ val, !(val & RCAR_USB3_DL_CTRL_FW_SET_DATA0),
+ 1, 10000);
+ if (retval < 0)
break;
- }
}
temp = readl(regs + RCAR_USB3_DL_CTRL);
temp &= ~RCAR_USB3_DL_CTRL_ENABLE;
writel(temp, regs + RCAR_USB3_DL_CTRL);
- for (time = 0; time < timeout; time++) {
- val = readl(regs + RCAR_USB3_DL_CTRL);
- if (val & RCAR_USB3_DL_CTRL_FW_SUCCESS) {
- retval = 0;
- break;
- }
- udelay(1);
- }
- if (time == timeout)
- retval = -ETIMEDOUT;
+ retval = readl_poll_timeout_atomic((regs + RCAR_USB3_DL_CTRL),
+ val, val & RCAR_USB3_DL_CTRL_FW_SUCCESS, 1, 10000);
release_firmware(fw);
@@ -207,18 +194,12 @@
static bool xhci_rcar_wait_for_pll_active(struct usb_hcd *hcd)
{
- int timeout = 1000;
+ int retval;
u32 val, mask = RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK;
- while (timeout > 0) {
- val = readl(hcd->regs + RCAR_USB3_AXH_STA);
- if ((val & mask) == mask)
- return true;
- udelay(1);
- timeout--;
- }
-
- return false;
+ retval = readl_poll_timeout_atomic(hcd->regs + RCAR_USB3_AXH_STA,
+ val, (val & mask) == mask, 1, 1000);
+ return !retval;
}
/* This function needs to initialize a "phy" of usb before */
diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h
index 012744a..048ad3b 100644
--- a/drivers/usb/host/xhci-rcar.h
+++ b/drivers/usb/host/xhci-rcar.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* drivers/usb/host/xhci-rcar.h
*
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 1228b3d..76389c0 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -280,6 +280,9 @@
return;
xhci_dbg(xhci, "// Ding dong!\n");
+
+ trace_xhci_ring_host_doorbell(0, DB_VALUE_HOST);
+
writel(DB_VALUE_HOST, &xhci->dba->doorbell[0]);
/* Flush PCI posted writes */
readl(&xhci->dba->doorbell[0]);
@@ -339,16 +342,29 @@
/* Must be called with xhci->lock held, releases and aquires lock back */
static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
{
- u64 temp_64;
+ struct xhci_segment *new_seg = xhci->cmd_ring->deq_seg;
+ union xhci_trb *new_deq = xhci->cmd_ring->dequeue;
+ u64 crcr;
int ret;
xhci_dbg(xhci, "Abort command ring\n");
reinit_completion(&xhci->cmd_ring_stop_completion);
- temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
- &xhci->op_regs->cmd_ring);
+ /*
+ * The control bits like command stop, abort are located in lower
+ * dword of the command ring control register.
+ * Some controllers require all 64 bits to be written to abort the ring.
+ * Make sure the upper dword is valid, pointing to the next command,
+ * avoiding corrupting the command ring pointer in case the command ring
+ * is stopped by the time the upper dword is written.
+ */
+ next_trb(xhci, NULL, &new_seg, &new_deq);
+ if (trb_is_link(new_deq))
+ next_trb(xhci, NULL, &new_seg, &new_deq);
+
+ crcr = xhci_trb_virt_to_dma(new_seg, new_deq);
+ xhci_write_64(xhci, crcr | CMD_RING_ABORT, &xhci->op_regs->cmd_ring);
/* Section 4.6.1.2 of xHCI 1.0 spec says software should also time the
* completion of the Command Abort operation. If CRR is not negated in 5
@@ -401,6 +417,9 @@
if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) ||
(ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT))
return;
+
+ trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
+
writel(DB_VALUE(ep_index, stream_id), db_addr);
/* The CPU has better things to do at this point than wait for a
* write-posting flush. It'll get there soon enough.
@@ -550,7 +569,10 @@
struct xhci_virt_ep *ep = &dev->eps[ep_index];
struct xhci_ring *ep_ring;
struct xhci_segment *new_seg;
+ struct xhci_segment *halted_seg = NULL;
union xhci_trb *new_deq;
+ union xhci_trb *halted_trb;
+ int index = 0;
dma_addr_t addr;
u64 hw_dequeue;
bool cycle_found = false;
@@ -588,7 +610,28 @@
hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
new_seg = ep_ring->deq_seg;
new_deq = ep_ring->dequeue;
- state->new_cycle_state = hw_dequeue & 0x1;
+
+ /*
+ * Quirk: xHC write-back of the DCS field in the hardware dequeue
+ * pointer is wrong - use the cycle state of the TRB pointed to by
+ * the dequeue pointer.
+ */
+ if (xhci->quirks & XHCI_EP_CTX_BROKEN_DCS &&
+ !(ep->ep_state & EP_HAS_STREAMS))
+ halted_seg = trb_in_td(xhci, cur_td->start_seg,
+ cur_td->first_trb, cur_td->last_trb,
+ hw_dequeue & ~0xf, false);
+ if (halted_seg) {
+ index = ((dma_addr_t)(hw_dequeue & ~0xf) - halted_seg->dma) /
+ sizeof(*halted_trb);
+ halted_trb = &halted_seg->trbs[index];
+ state->new_cycle_state = halted_trb->generic.field[3] & 0x1;
+ xhci_dbg(xhci, "Endpoint DCS = %d TRB index = %d cycle = %d\n",
+ (u8)(hw_dequeue & 0x1), index,
+ state->new_cycle_state);
+ } else {
+ state->new_cycle_state = hw_dequeue & 0x1;
+ }
state->stream_id = stream_id;
/*
@@ -692,10 +735,8 @@
}
xhci_urb_free_priv(urb_priv);
usb_hcd_unlink_urb_from_ep(hcd, urb);
- spin_unlock(&xhci->lock);
trace_xhci_urb_giveback(urb);
usb_hcd_giveback_urb(hcd, urb, status);
- spin_lock(&xhci->lock);
}
static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
@@ -1000,6 +1041,8 @@
struct xhci_virt_ep *ep = from_timer(ep, t, stop_cmd_timer);
struct xhci_hcd *xhci = ep->xhci;
unsigned long flags;
+ u32 usbsts;
+ char str[XHCI_MSG_MAX];
spin_lock_irqsave(&xhci->lock, flags);
@@ -1010,8 +1053,11 @@
xhci_dbg(xhci, "Stop EP timer raced with cmd completion, exit");
return;
}
+ usbsts = readl(&xhci->op_regs->status);
xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n");
+ xhci_warn(xhci, "USBSTS:%s\n", xhci_decode_usbsts(str, usbsts));
+
ep->ep_state &= ~EP_STOP_CMD_PENDING;
xhci_halt(xhci);
@@ -1252,7 +1298,6 @@
if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
/* Delete default control endpoint resources */
xhci_free_device_endpoint_resources(xhci, virt_dev, true);
- xhci_free_virt_device(xhci, slot_id);
}
static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
@@ -2133,7 +2178,7 @@
break;
xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
trb_comp_code, ep_index);
- /* else fall through */
+ fallthrough;
case COMP_STALL_ERROR:
/* Did we transfer part of the data (middle) phase? */
if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
@@ -2473,6 +2518,10 @@
status = -EPIPE;
break;
case COMP_SPLIT_TRANSACTION_ERROR:
+ xhci_dbg(xhci, "Split transaction error for slot %u ep %u\n",
+ slot_id, ep_index);
+ status = -EPROTO;
+ break;
case COMP_USB_TRANSACTION_ERROR:
xhci_dbg(xhci, "Transfer error for slot %u ep %u on endpoint\n",
slot_id, ep_index);
@@ -3768,6 +3817,24 @@
return start_frame;
}
+/* Check if we should generate event interrupt for a TD in an isoc URB */
+static bool trb_block_event_intr(struct xhci_hcd *xhci, int num_tds, int i)
+{
+ if (xhci->hci_version < 0x100)
+ return false;
+ /* always generate an event interrupt for the last TD */
+ if (i == num_tds - 1)
+ return false;
+ /*
+ * If AVOID_BEI is set the host handles full event rings poorly,
+ * generate an event at least every 8th TD to clear the event ring
+ */
+ if (i && xhci->quirks & XHCI_AVOID_BEI)
+ return !!(i % 8);
+
+ return true;
+}
+
/* This is for isoc transfer */
static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index)
@@ -3875,10 +3942,7 @@
more_trbs_coming = false;
td->last_trb = ep_ring->enqueue;
field |= TRB_IOC;
- /* set BEI, except for the last TD */
- if (xhci->hci_version >= 0x100 &&
- !(xhci->quirks & XHCI_AVOID_BEI) &&
- i < num_tds - 1)
+ if (trb_block_event_intr(xhci, num_tds, i))
field |= TRB_BEI;
}
/* Calculate TRB length */
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 6087b1f..50bb91b 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -11,6 +11,7 @@
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -23,6 +24,9 @@
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/role.h>
#include <soc/tegra/pmc.h>
#include "xhci.h"
@@ -38,23 +42,30 @@
#define XUSB_CFG_4 0x010
#define XUSB_BASE_ADDR_SHIFT 15
#define XUSB_BASE_ADDR_MASK 0x1ffff
+#define XUSB_CFG_16 0x040
+#define XUSB_CFG_24 0x060
+#define XUSB_CFG_AXI_CFG 0x0f8
#define XUSB_CFG_ARU_C11_CSBRANGE 0x41c
+#define XUSB_CFG_ARU_CONTEXT 0x43c
+#define XUSB_CFG_ARU_CONTEXT_HS_PLS 0x478
+#define XUSB_CFG_ARU_CONTEXT_FS_PLS 0x47c
+#define XUSB_CFG_ARU_CONTEXT_HSFS_SPEED 0x480
+#define XUSB_CFG_ARU_CONTEXT_HSFS_PP 0x484
#define XUSB_CFG_CSB_BASE_ADDR 0x800
/* FPCI mailbox registers */
-#define XUSB_CFG_ARU_MBOX_CMD 0x0e4
+/* XUSB_CFG_ARU_MBOX_CMD */
#define MBOX_DEST_FALC BIT(27)
#define MBOX_DEST_PME BIT(28)
#define MBOX_DEST_SMI BIT(29)
#define MBOX_DEST_XHCI BIT(30)
#define MBOX_INT_EN BIT(31)
-#define XUSB_CFG_ARU_MBOX_DATA_IN 0x0e8
+/* XUSB_CFG_ARU_MBOX_DATA_IN and XUSB_CFG_ARU_MBOX_DATA_OUT */
#define CMD_DATA_SHIFT 0
#define CMD_DATA_MASK 0xffffff
#define CMD_TYPE_SHIFT 24
#define CMD_TYPE_MASK 0xff
-#define XUSB_CFG_ARU_MBOX_DATA_OUT 0x0ec
-#define XUSB_CFG_ARU_MBOX_OWNER 0x0f0
+/* XUSB_CFG_ARU_MBOX_OWNER */
#define MBOX_OWNER_NONE 0
#define MBOX_OWNER_FW 1
#define MBOX_OWNER_SW 2
@@ -63,11 +74,20 @@
#define MBOX_SMI_INTR_EN BIT(3)
/* IPFS registers */
+#define IPFS_XUSB_HOST_MSI_BAR_SZ_0 0x0c0
+#define IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0 0x0c4
+#define IPFS_XUSB_HOST_MSI_FPCI_BAR_ST_0 0x0c8
+#define IPFS_XUSB_HOST_MSI_VEC0_0 0x100
+#define IPFS_XUSB_HOST_MSI_EN_VEC0_0 0x140
#define IPFS_XUSB_HOST_CONFIGURATION_0 0x180
#define IPFS_EN_FPCI BIT(0)
+#define IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0 0x184
#define IPFS_XUSB_HOST_INTR_MASK_0 0x188
#define IPFS_IP_INT_MASK BIT(16)
+#define IPFS_XUSB_HOST_INTR_ENABLE_0 0x198
+#define IPFS_XUSB_HOST_UFPCI_CONFIG_0 0x19c
#define IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0 0x1bc
+#define IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0 0x1dc
#define CSB_PAGE_SELECT_MASK 0x7fffff
#define CSB_PAGE_SELECT_SHIFT 9
@@ -102,6 +122,8 @@
#define L2IMEMOP_ACTION_SHIFT 24
#define L2IMEMOP_INVALIDATE_ALL (0x40 << L2IMEMOP_ACTION_SHIFT)
#define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << L2IMEMOP_ACTION_SHIFT)
+#define XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT 0x101a18
+#define L2IMEMOP_RESULT_VLD BIT(31)
#define XUSB_CSB_MP_APMAP 0x10181c
#define APMAP_BOOTPATH BIT(31)
@@ -146,12 +168,32 @@
unsigned int num;
};
+struct tegra_xusb_mbox_regs {
+ u16 cmd;
+ u16 data_in;
+ u16 data_out;
+ u16 owner;
+};
+
+struct tegra_xusb_context_soc {
+ struct {
+ const unsigned int *offsets;
+ unsigned int num_offsets;
+ } ipfs;
+
+ struct {
+ const unsigned int *offsets;
+ unsigned int num_offsets;
+ } fpci;
+};
+
struct tegra_xusb_soc {
const char *firmware;
const char * const *supply_names;
unsigned int num_supplies;
const struct tegra_xusb_phy_type *phy_types;
unsigned int num_types;
+ const struct tegra_xusb_context_soc *context;
struct {
struct {
@@ -160,8 +202,17 @@
} usb2, ulpi, hsic, usb3;
} ports;
+ struct tegra_xusb_mbox_regs mbox;
+
bool scale_ss_clock;
bool has_ipfs;
+ bool lpm_support;
+ bool otg_reset_sspi;
+};
+
+struct tegra_xusb_context {
+ u32 *ipfs;
+ u32 *fpci;
};
struct tegra_xusb {
@@ -204,12 +255,22 @@
struct phy **phys;
unsigned int num_phys;
+ struct usb_phy **usbphy;
+ unsigned int num_usb_phys;
+ int otg_usb2_port;
+ int otg_usb3_port;
+ bool host_mode;
+ struct notifier_block id_nb;
+ struct work_struct id_work;
+
/* Firmware loading related */
struct {
size_t size;
void *virt;
dma_addr_t phys;
} fw;
+
+ struct tegra_xusb_context context;
};
static struct hc_driver __read_mostly tegra_xhci_hc_driver;
@@ -395,15 +456,15 @@
* ACK/NAK messages.
*/
if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) {
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ value = fpci_readl(tegra, tegra->soc->mbox.owner);
if (value != MBOX_OWNER_NONE) {
dev_err(tegra->dev, "mailbox is busy\n");
return -EBUSY;
}
- fpci_writel(tegra, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER);
+ fpci_writel(tegra, MBOX_OWNER_SW, tegra->soc->mbox.owner);
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ value = fpci_readl(tegra, tegra->soc->mbox.owner);
if (value != MBOX_OWNER_SW) {
dev_err(tegra->dev, "failed to acquire mailbox\n");
return -EBUSY;
@@ -413,17 +474,17 @@
}
value = tegra_xusb_mbox_pack(msg);
- fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_DATA_IN);
+ fpci_writel(tegra, value, tegra->soc->mbox.data_in);
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD);
+ value = fpci_readl(tegra, tegra->soc->mbox.cmd);
value |= MBOX_INT_EN | MBOX_DEST_FALC;
- fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD);
+ fpci_writel(tegra, value, tegra->soc->mbox.cmd);
if (wait_for_idle) {
unsigned long timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ value = fpci_readl(tegra, tegra->soc->mbox.owner);
if (value == MBOX_OWNER_NONE)
break;
@@ -431,7 +492,7 @@
}
if (time_after(jiffies, timeout))
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ value = fpci_readl(tegra, tegra->soc->mbox.owner);
if (value != MBOX_OWNER_NONE)
return -ETIMEDOUT;
@@ -605,16 +666,16 @@
mutex_lock(&tegra->lock);
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_DATA_OUT);
+ value = fpci_readl(tegra, tegra->soc->mbox.data_out);
tegra_xusb_mbox_unpack(&msg, value);
- value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD);
+ value = fpci_readl(tegra, tegra->soc->mbox.cmd);
value &= ~MBOX_DEST_SMI;
- fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD);
+ fpci_writel(tegra, value, tegra->soc->mbox.cmd);
/* clear mailbox owner if no ACK/NAK is required */
if (!tegra_xusb_mbox_cmd_requires_ack(msg.cmd))
- fpci_writel(tegra, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER);
+ fpci_writel(tegra, MBOX_OWNER_NONE, tegra->soc->mbox.owner);
tegra_xusb_mbox_handle(tegra, &msg);
@@ -622,9 +683,9 @@
return IRQ_HANDLED;
}
-static void tegra_xusb_config(struct tegra_xusb *tegra,
- struct resource *regs)
+static void tegra_xusb_config(struct tegra_xusb *tegra)
{
+ u32 regs = tegra->hcd->rsrc_start;
u32 value;
if (tegra->soc->has_ipfs) {
@@ -638,7 +699,7 @@
/* Program BAR0 space */
value = fpci_readl(tegra, XUSB_CFG_4);
value &= ~(XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
- value |= regs->start & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
+ value |= regs & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
fpci_writel(tegra, value, XUSB_CFG_4);
usleep_range(100, 200);
@@ -792,17 +853,34 @@
return err;
}
-static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
+#ifdef CONFIG_PM_SLEEP
+static int tegra_xusb_init_context(struct tegra_xusb *tegra)
{
- unsigned int code_tag_blocks, code_size_blocks, code_blocks;
+ const struct tegra_xusb_context_soc *soc = tegra->soc->context;
+
+ tegra->context.ipfs = devm_kcalloc(tegra->dev, soc->ipfs.num_offsets,
+ sizeof(u32), GFP_KERNEL);
+ if (!tegra->context.ipfs)
+ return -ENOMEM;
+
+ tegra->context.fpci = devm_kcalloc(tegra->dev, soc->fpci.num_offsets,
+ sizeof(u32), GFP_KERNEL);
+ if (!tegra->context.fpci)
+ return -ENOMEM;
+
+ return 0;
+}
+#else
+static inline int tegra_xusb_init_context(struct tegra_xusb *tegra)
+{
+ return 0;
+}
+#endif
+
+static int tegra_xusb_request_firmware(struct tegra_xusb *tegra)
+{
struct tegra_xusb_fw_header *header;
- struct device *dev = tegra->dev;
const struct firmware *fw;
- unsigned long timeout;
- time64_t timestamp;
- struct tm time;
- u64 address;
- u32 value;
int err;
err = request_firmware(&fw, tegra->soc->firmware, tegra->dev);
@@ -827,6 +905,26 @@
memcpy(tegra->fw.virt, fw->data, tegra->fw.size);
release_firmware(fw);
+ return 0;
+}
+
+static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
+{
+ unsigned int code_tag_blocks, code_size_blocks, code_blocks;
+ struct xhci_cap_regs __iomem *cap = tegra->regs;
+ struct tegra_xusb_fw_header *header;
+ struct device *dev = tegra->dev;
+ struct xhci_op_regs __iomem *op;
+ unsigned long timeout;
+ time64_t timestamp;
+ struct tm time;
+ u64 address;
+ u32 value;
+ int err;
+
+ header = (struct tegra_xusb_fw_header *)tegra->fw.virt;
+ op = tegra->regs + HC_LENGTH(readl(&cap->hc_capbase));
+
if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) {
dev_info(dev, "Firmware already loaded, Falcon state %#x\n",
csb_readl(tegra, XUSB_FALC_CPUCTL));
@@ -881,26 +979,37 @@
csb_writel(tegra, 0, XUSB_FALC_DMACTL);
- msleep(50);
+ /* wait for RESULT_VLD to get set */
+#define tegra_csb_readl(offset) csb_readl(tegra, offset)
+ err = readx_poll_timeout(tegra_csb_readl,
+ XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT, value,
+ value & L2IMEMOP_RESULT_VLD, 100, 10000);
+ if (err < 0) {
+ dev_err(dev, "DMA controller not ready %#010x\n", value);
+ return err;
+ }
+#undef tegra_csb_readl
csb_writel(tegra, le32_to_cpu(header->boot_codetag),
XUSB_FALC_BOOTVEC);
- /* Boot Falcon CPU and wait for it to enter the STOPPED (idle) state. */
- timeout = jiffies + msecs_to_jiffies(5);
-
+ /* Boot Falcon CPU and wait for USBSTS_CNR to get cleared. */
csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL);
- while (time_before(jiffies, timeout)) {
- if (csb_readl(tegra, XUSB_FALC_CPUCTL) == CPUCTL_STATE_STOPPED)
+ timeout = jiffies + msecs_to_jiffies(200);
+
+ do {
+ value = readl(&op->status);
+ if ((value & STS_CNR) == 0)
break;
- usleep_range(100, 200);
- }
+ usleep_range(1000, 2000);
+ } while (time_is_after_jiffies(timeout));
- if (csb_readl(tegra, XUSB_FALC_CPUCTL) != CPUCTL_STATE_STOPPED) {
- dev_err(dev, "Falcon failed to start, state: %#x\n",
- csb_readl(tegra, XUSB_FALC_CPUCTL));
+ value = readl(&op->status);
+ if (value & STS_CNR) {
+ value = csb_readl(tegra, XUSB_FALC_CPUCTL);
+ dev_err(dev, "XHCI controller not read: %#010x\n", value);
return -EIO;
}
@@ -965,11 +1074,238 @@
return 0;
}
-static int tegra_xusb_probe(struct platform_device *pdev)
+static int __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
{
struct tegra_xusb_mbox_msg msg;
- struct resource *res, *regs;
+ int err;
+
+ /* Enable firmware messages from controller. */
+ msg.cmd = MBOX_CMD_MSG_ENABLED;
+ msg.data = 0;
+
+ err = tegra_xusb_mbox_send(tegra, &msg);
+ if (err < 0)
+ dev_err(tegra->dev, "failed to enable messages: %d\n", err);
+
+ return err;
+}
+
+static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
+{
+ int err;
+
+ mutex_lock(&tegra->lock);
+ err = __tegra_xusb_enable_firmware_messages(tegra);
+ mutex_unlock(&tegra->lock);
+
+ return err;
+}
+
+static void tegra_xhci_set_port_power(struct tegra_xusb *tegra, bool main,
+ bool set)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ struct usb_hcd *hcd = main ? xhci->main_hcd : xhci->shared_hcd;
+ unsigned int wait = (!main && !set) ? 1000 : 10;
+ u16 typeReq = set ? SetPortFeature : ClearPortFeature;
+ u16 wIndex = main ? tegra->otg_usb2_port + 1 : tegra->otg_usb3_port + 1;
+ u32 status;
+ u32 stat_power = main ? USB_PORT_STAT_POWER : USB_SS_PORT_STAT_POWER;
+ u32 status_val = set ? stat_power : 0;
+
+ dev_dbg(tegra->dev, "%s():%s %s port power\n", __func__,
+ set ? "set" : "clear", main ? "HS" : "SS");
+
+ hcd->driver->hub_control(hcd, typeReq, USB_PORT_FEAT_POWER, wIndex,
+ NULL, 0);
+
+ do {
+ tegra_xhci_hc_driver.hub_control(hcd, GetPortStatus, 0, wIndex,
+ (char *) &status, sizeof(status));
+ if (status_val == (status & stat_power))
+ break;
+
+ if (!main && !set)
+ usleep_range(600, 700);
+ else
+ usleep_range(10, 20);
+ } while (--wait > 0);
+
+ if (status_val != (status & stat_power))
+ dev_info(tegra->dev, "failed to %s %s PP %d\n",
+ set ? "set" : "clear",
+ main ? "HS" : "SS", status);
+}
+
+static struct phy *tegra_xusb_get_phy(struct tegra_xusb *tegra, char *name,
+ int port)
+{
+ unsigned int i, phy_count = 0;
+
+ for (i = 0; i < tegra->soc->num_types; i++) {
+ if (!strncmp(tegra->soc->phy_types[i].name, name,
+ strlen(name)))
+ return tegra->phys[phy_count+port];
+
+ phy_count += tegra->soc->phy_types[i].num;
+ }
+
+ return NULL;
+}
+
+static void tegra_xhci_id_work(struct work_struct *work)
+{
+ struct tegra_xusb *tegra = container_of(work, struct tegra_xusb,
+ id_work);
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ struct tegra_xusb_mbox_msg msg;
+ struct phy *phy = tegra_xusb_get_phy(tegra, "usb2",
+ tegra->otg_usb2_port);
+ u32 status;
+ int ret;
+
+ dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off");
+
+ mutex_lock(&tegra->lock);
+
+ if (tegra->host_mode)
+ phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST);
+ else
+ phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
+
+ mutex_unlock(&tegra->lock);
+
+ if (tegra->host_mode) {
+ /* switch to host mode */
+ if (tegra->otg_usb3_port >= 0) {
+ if (tegra->soc->otg_reset_sspi) {
+ /* set PP=0 */
+ tegra_xhci_hc_driver.hub_control(
+ xhci->shared_hcd, GetPortStatus,
+ 0, tegra->otg_usb3_port+1,
+ (char *) &status, sizeof(status));
+ if (status & USB_SS_PORT_STAT_POWER)
+ tegra_xhci_set_port_power(tegra, false,
+ false);
+
+ /* reset OTG port SSPI */
+ msg.cmd = MBOX_CMD_RESET_SSPI;
+ msg.data = tegra->otg_usb3_port+1;
+
+ ret = tegra_xusb_mbox_send(tegra, &msg);
+ if (ret < 0) {
+ dev_info(tegra->dev,
+ "failed to RESET_SSPI %d\n",
+ ret);
+ }
+ }
+
+ tegra_xhci_set_port_power(tegra, false, true);
+ }
+
+ tegra_xhci_set_port_power(tegra, true, true);
+
+ } else {
+ if (tegra->otg_usb3_port >= 0)
+ tegra_xhci_set_port_power(tegra, false, false);
+
+ tegra_xhci_set_port_power(tegra, true, false);
+ }
+}
+
+static int tegra_xusb_get_usb2_port(struct tegra_xusb *tegra,
+ struct usb_phy *usbphy)
+{
+ unsigned int i;
+
+ for (i = 0; i < tegra->num_usb_phys; i++) {
+ if (tegra->usbphy[i] && usbphy == tegra->usbphy[i])
+ return i;
+ }
+
+ return -1;
+}
+
+static int tegra_xhci_id_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct tegra_xusb *tegra = container_of(nb, struct tegra_xusb,
+ id_nb);
+ struct usb_phy *usbphy = (struct usb_phy *)data;
+
+ dev_dbg(tegra->dev, "%s(): action is %d", __func__, usbphy->last_event);
+
+ if ((tegra->host_mode && usbphy->last_event == USB_EVENT_ID) ||
+ (!tegra->host_mode && usbphy->last_event != USB_EVENT_ID)) {
+ dev_dbg(tegra->dev, "Same role(%d) received. Ignore",
+ tegra->host_mode);
+ return NOTIFY_OK;
+ }
+
+ tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy);
+ tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(
+ tegra->padctl,
+ tegra->otg_usb2_port);
+
+ tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false;
+
+ schedule_work(&tegra->id_work);
+
+ return NOTIFY_OK;
+}
+
+static int tegra_xusb_init_usb_phy(struct tegra_xusb *tegra)
+{
+ unsigned int i;
+
+ tegra->usbphy = devm_kcalloc(tegra->dev, tegra->num_usb_phys,
+ sizeof(*tegra->usbphy), GFP_KERNEL);
+ if (!tegra->usbphy)
+ return -ENOMEM;
+
+ INIT_WORK(&tegra->id_work, tegra_xhci_id_work);
+ tegra->id_nb.notifier_call = tegra_xhci_id_notify;
+ tegra->otg_usb2_port = -EINVAL;
+ tegra->otg_usb3_port = -EINVAL;
+
+ for (i = 0; i < tegra->num_usb_phys; i++) {
+ struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i);
+
+ if (!phy)
+ continue;
+
+ tegra->usbphy[i] = devm_usb_get_phy_by_node(tegra->dev,
+ phy->dev.of_node,
+ &tegra->id_nb);
+ if (!IS_ERR(tegra->usbphy[i])) {
+ dev_dbg(tegra->dev, "usbphy-%d registered", i);
+ otg_set_host(tegra->usbphy[i]->otg, &tegra->hcd->self);
+ } else {
+ /*
+ * usb-phy is optional, continue if its not available.
+ */
+ tegra->usbphy[i] = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra)
+{
+ unsigned int i;
+
+ cancel_work_sync(&tegra->id_work);
+
+ for (i = 0; i < tegra->num_usb_phys; i++)
+ if (tegra->usbphy[i])
+ otg_set_host(tegra->usbphy[i]->otg, NULL);
+}
+
+static int tegra_xusb_probe(struct platform_device *pdev)
+{
struct tegra_xusb *tegra;
+ struct resource *regs;
struct xhci_hcd *xhci;
unsigned int i, j, k;
struct phy *phy;
@@ -985,19 +1321,21 @@
mutex_init(&tegra->lock);
tegra->dev = &pdev->dev;
+ err = tegra_xusb_init_context(tegra);
+ if (err < 0)
+ return err;
+
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tegra->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- tegra->fpci_base = devm_ioremap_resource(&pdev->dev, res);
+ tegra->fpci_base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(tegra->fpci_base))
return PTR_ERR(tegra->fpci_base);
if (tegra->soc->has_ipfs) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res);
+ tegra->ipfs_base = devm_platform_ioremap_resource(pdev, 2);
if (IS_ERR(tegra->ipfs_base))
return PTR_ERR(tegra->ipfs_base);
}
@@ -1126,8 +1464,9 @@
goto put_powerdomains;
}
- for (i = 0; i < tegra->soc->num_supplies; i++)
- tegra->supplies[i].supply = tegra->soc->supply_names[i];
+ regulator_bulk_set_supply_names(tegra->supplies,
+ tegra->soc->supply_names,
+ tegra->soc->num_supplies);
err = devm_regulator_bulk_get(&pdev->dev, tegra->soc->num_supplies,
tegra->supplies);
@@ -1136,8 +1475,11 @@
goto put_powerdomains;
}
- for (i = 0; i < tegra->soc->num_types; i++)
+ for (i = 0; i < tegra->soc->num_types; i++) {
+ if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4))
+ tegra->num_usb_phys = tegra->soc->phy_types[i].num;
tegra->num_phys += tegra->soc->phy_types[i].num;
+ }
tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
sizeof(*tegra->phys), GFP_KERNEL);
@@ -1173,6 +1515,10 @@
goto put_powerdomains;
}
+ tegra->hcd->regs = tegra->regs;
+ tegra->hcd->rsrc_start = regs->start;
+ tegra->hcd->rsrc_len = resource_size(regs);
+
/*
* This must happen after usb_create_hcd(), because usb_create_hcd()
* will overwrite the drvdata of the device with the hcd it creates.
@@ -1185,19 +1531,6 @@
goto put_hcd;
}
- pm_runtime_enable(&pdev->dev);
- if (pm_runtime_enabled(&pdev->dev))
- err = pm_runtime_get_sync(&pdev->dev);
- else
- err = tegra_xusb_runtime_resume(&pdev->dev);
-
- if (err < 0) {
- dev_err(&pdev->dev, "failed to enable device: %d\n", err);
- goto disable_phy;
- }
-
- tegra_xusb_config(tegra, regs);
-
/*
* The XUSB Falcon microcontroller can only address 40 bits, so set
* the DMA mask accordingly.
@@ -1205,19 +1538,35 @@
err = dma_set_mask_and_coherent(tegra->dev, DMA_BIT_MASK(40));
if (err < 0) {
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
- goto put_rpm;
+ goto disable_phy;
}
+ err = tegra_xusb_request_firmware(tegra);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request firmware: %d\n", err);
+ goto disable_phy;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ if (!pm_runtime_enabled(&pdev->dev))
+ err = tegra_xusb_runtime_resume(&pdev->dev);
+ else
+ err = pm_runtime_get_sync(&pdev->dev);
+
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to enable device: %d\n", err);
+ goto free_firmware;
+ }
+
+ tegra_xusb_config(tegra);
+
err = tegra_xusb_load_firmware(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to load firmware: %d\n", err);
goto put_rpm;
}
- tegra->hcd->regs = tegra->regs;
- tegra->hcd->rsrc_start = regs->start;
- tegra->hcd->rsrc_len = resource_size(regs);
-
err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED);
if (err < 0) {
dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err);
@@ -1244,21 +1593,12 @@
goto put_usb3;
}
- mutex_lock(&tegra->lock);
-
- /* Enable firmware messages from controller. */
- msg.cmd = MBOX_CMD_MSG_ENABLED;
- msg.data = 0;
-
- err = tegra_xusb_mbox_send(tegra, &msg);
+ err = tegra_xusb_enable_firmware_messages(tegra);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable messages: %d\n", err);
- mutex_unlock(&tegra->lock);
goto remove_usb3;
}
- mutex_unlock(&tegra->lock);
-
err = devm_request_threaded_irq(&pdev->dev, tegra->mbox_irq,
tegra_xusb_mbox_irq,
tegra_xusb_mbox_thread, 0,
@@ -1268,6 +1608,12 @@
goto remove_usb3;
}
+ err = tegra_xusb_init_usb_phy(tegra);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to init USB PHY: %d\n", err);
+ goto remove_usb3;
+ }
+
return 0;
remove_usb3:
@@ -1281,6 +1627,9 @@
tegra_xusb_runtime_suspend(&pdev->dev);
put_hcd:
usb_put_hcd(tegra->hcd);
+free_firmware:
+ dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt,
+ tegra->fw.phys);
disable_phy:
tegra_xusb_phy_disable(tegra);
pm_runtime_disable(&pdev->dev);
@@ -1301,6 +1650,8 @@
struct tegra_xusb *tegra = platform_get_drvdata(pdev);
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ tegra_xusb_deinit_usb_phy(tegra);
+
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
xhci->shared_hcd = NULL;
@@ -1328,22 +1679,176 @@
}
#ifdef CONFIG_PM_SLEEP
+static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
+{
+ struct device *dev = hub->hcd->self.controller;
+ bool status = true;
+ unsigned int i;
+ u32 value;
+
+ for (i = 0; i < hub->num_ports; i++) {
+ value = readl(hub->ports[i]->addr);
+ if ((value & PORT_PE) == 0)
+ continue;
+
+ if ((value & PORT_PLS_MASK) != XDEV_U3) {
+ dev_info(dev, "%u-%u isn't suspended: %#010x\n",
+ hub->hcd->self.busnum, i + 1, value);
+ status = false;
+ }
+ }
+
+ return status;
+}
+
+static int tegra_xusb_check_ports(struct tegra_xusb *tegra)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ if (!xhci_hub_ports_suspended(&xhci->usb2_rhub) ||
+ !xhci_hub_ports_suspended(&xhci->usb3_rhub))
+ err = -EBUSY;
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
+ return err;
+}
+
+static void tegra_xusb_save_context(struct tegra_xusb *tegra)
+{
+ const struct tegra_xusb_context_soc *soc = tegra->soc->context;
+ struct tegra_xusb_context *ctx = &tegra->context;
+ unsigned int i;
+
+ if (soc->ipfs.num_offsets > 0) {
+ for (i = 0; i < soc->ipfs.num_offsets; i++)
+ ctx->ipfs[i] = ipfs_readl(tegra, soc->ipfs.offsets[i]);
+ }
+
+ if (soc->fpci.num_offsets > 0) {
+ for (i = 0; i < soc->fpci.num_offsets; i++)
+ ctx->fpci[i] = fpci_readl(tegra, soc->fpci.offsets[i]);
+ }
+}
+
+static void tegra_xusb_restore_context(struct tegra_xusb *tegra)
+{
+ const struct tegra_xusb_context_soc *soc = tegra->soc->context;
+ struct tegra_xusb_context *ctx = &tegra->context;
+ unsigned int i;
+
+ if (soc->fpci.num_offsets > 0) {
+ for (i = 0; i < soc->fpci.num_offsets; i++)
+ fpci_writel(tegra, ctx->fpci[i], soc->fpci.offsets[i]);
+ }
+
+ if (soc->ipfs.num_offsets > 0) {
+ for (i = 0; i < soc->ipfs.num_offsets; i++)
+ ipfs_writel(tegra, ctx->ipfs[i], soc->ipfs.offsets[i]);
+ }
+}
+
+static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool wakeup)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ int err;
+
+ err = tegra_xusb_check_ports(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "not all ports suspended: %d\n", err);
+ return err;
+ }
+
+ err = xhci_suspend(xhci, wakeup);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err);
+ return err;
+ }
+
+ tegra_xusb_save_context(tegra);
+ tegra_xusb_phy_disable(tegra);
+ tegra_xusb_clk_disable(tegra);
+
+ return 0;
+}
+
+static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ int err;
+
+ err = tegra_xusb_clk_enable(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to enable clocks: %d\n", err);
+ return err;
+ }
+
+ err = tegra_xusb_phy_enable(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to enable PHYs: %d\n", err);
+ goto disable_clk;
+ }
+
+ tegra_xusb_config(tegra);
+ tegra_xusb_restore_context(tegra);
+
+ err = tegra_xusb_load_firmware(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to load firmware: %d\n", err);
+ goto disable_phy;
+ }
+
+ err = __tegra_xusb_enable_firmware_messages(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to enable messages: %d\n", err);
+ goto disable_phy;
+ }
+
+ err = xhci_resume(xhci, true);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to resume XHCI: %d\n", err);
+ goto disable_phy;
+ }
+
+ return 0;
+
+disable_phy:
+ tegra_xusb_phy_disable(tegra);
+disable_clk:
+ tegra_xusb_clk_disable(tegra);
+ return err;
+}
+
static int tegra_xusb_suspend(struct device *dev)
{
struct tegra_xusb *tegra = dev_get_drvdata(dev);
- struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
bool wakeup = device_may_wakeup(dev);
+ int err;
- /* TODO: Powergate controller across suspend/resume. */
- return xhci_suspend(xhci, wakeup);
+ synchronize_irq(tegra->mbox_irq);
+
+ mutex_lock(&tegra->lock);
+ err = tegra_xusb_enter_elpg(tegra, wakeup);
+ mutex_unlock(&tegra->lock);
+
+ return err;
}
static int tegra_xusb_resume(struct device *dev)
{
struct tegra_xusb *tegra = dev_get_drvdata(dev);
- struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ bool wakeup = device_may_wakeup(dev);
+ int err;
- return xhci_resume(xhci, 0);
+ mutex_lock(&tegra->lock);
+ err = tegra_xusb_exit_elpg(tegra, wakeup);
+ mutex_unlock(&tegra->lock);
+
+ return err;
}
#endif
@@ -1357,11 +1862,7 @@
"avddio-pex",
"dvddio-pex",
"avdd-usb",
- "avdd-pll-utmip",
- "avdd-pll-erefe",
- "avdd-usb-ss-pll",
"hvdd-usb-ss",
- "hvdd-usb-ss-pll-e",
};
static const struct tegra_xusb_phy_type tegra124_phy_types[] = {
@@ -1370,12 +1871,49 @@
{ .name = "hsic", .num = 2, },
};
+static const unsigned int tegra124_xusb_context_ipfs[] = {
+ IPFS_XUSB_HOST_MSI_BAR_SZ_0,
+ IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0,
+ IPFS_XUSB_HOST_MSI_FPCI_BAR_ST_0,
+ IPFS_XUSB_HOST_MSI_VEC0_0,
+ IPFS_XUSB_HOST_MSI_EN_VEC0_0,
+ IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0,
+ IPFS_XUSB_HOST_INTR_MASK_0,
+ IPFS_XUSB_HOST_INTR_ENABLE_0,
+ IPFS_XUSB_HOST_UFPCI_CONFIG_0,
+ IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0,
+ IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0,
+};
+
+static const unsigned int tegra124_xusb_context_fpci[] = {
+ XUSB_CFG_ARU_CONTEXT_HS_PLS,
+ XUSB_CFG_ARU_CONTEXT_FS_PLS,
+ XUSB_CFG_ARU_CONTEXT_HSFS_SPEED,
+ XUSB_CFG_ARU_CONTEXT_HSFS_PP,
+ XUSB_CFG_ARU_CONTEXT,
+ XUSB_CFG_AXI_CFG,
+ XUSB_CFG_24,
+ XUSB_CFG_16,
+};
+
+static const struct tegra_xusb_context_soc tegra124_xusb_context = {
+ .ipfs = {
+ .num_offsets = ARRAY_SIZE(tegra124_xusb_context_ipfs),
+ .offsets = tegra124_xusb_context_ipfs,
+ },
+ .fpci = {
+ .num_offsets = ARRAY_SIZE(tegra124_xusb_context_fpci),
+ .offsets = tegra124_xusb_context_fpci,
+ },
+};
+
static const struct tegra_xusb_soc tegra124_soc = {
.firmware = "nvidia/tegra124/xusb.bin",
.supply_names = tegra124_supply_names,
.num_supplies = ARRAY_SIZE(tegra124_supply_names),
.phy_types = tegra124_phy_types,
.num_types = ARRAY_SIZE(tegra124_phy_types),
+ .context = &tegra124_xusb_context,
.ports = {
.usb2 = { .offset = 4, .count = 4, },
.hsic = { .offset = 6, .count = 2, },
@@ -1383,6 +1921,13 @@
},
.scale_ss_clock = true,
.has_ipfs = true,
+ .otg_reset_sspi = false,
+ .mbox = {
+ .cmd = 0xe4,
+ .data_in = 0xe8,
+ .data_out = 0xec,
+ .owner = 0xf0,
+ },
};
MODULE_FIRMWARE("nvidia/tegra124/xusb.bin");
@@ -1390,10 +1935,6 @@
"dvddio-pex",
"hvddio-pex",
"avdd-usb",
- "avdd-pll-utmip",
- "avdd-pll-uerefe",
- "dvdd-pex-pll",
- "hvdd-pex-pll-e",
};
static const struct tegra_xusb_phy_type tegra210_phy_types[] = {
@@ -1408,6 +1949,7 @@
.num_supplies = ARRAY_SIZE(tegra210_supply_names),
.phy_types = tegra210_phy_types,
.num_types = ARRAY_SIZE(tegra210_phy_types),
+ .context = &tegra124_xusb_context,
.ports = {
.usb2 = { .offset = 4, .count = 4, },
.hsic = { .offset = 8, .count = 1, },
@@ -1415,6 +1957,13 @@
},
.scale_ss_clock = false,
.has_ipfs = true,
+ .otg_reset_sspi = true,
+ .mbox = {
+ .cmd = 0xe4,
+ .data_in = 0xe8,
+ .data_out = 0xec,
+ .owner = 0xf0,
+ },
};
MODULE_FIRMWARE("nvidia/tegra210/xusb.bin");
@@ -1428,12 +1977,20 @@
{ .name = "hsic", .num = 1, },
};
+static const struct tegra_xusb_context_soc tegra186_xusb_context = {
+ .fpci = {
+ .num_offsets = ARRAY_SIZE(tegra124_xusb_context_fpci),
+ .offsets = tegra124_xusb_context_fpci,
+ },
+};
+
static const struct tegra_xusb_soc tegra186_soc = {
.firmware = "nvidia/tegra186/xusb.bin",
.supply_names = tegra186_supply_names,
.num_supplies = ARRAY_SIZE(tegra186_supply_names),
.phy_types = tegra186_phy_types,
.num_types = ARRAY_SIZE(tegra186_phy_types),
+ .context = &tegra186_xusb_context,
.ports = {
.usb3 = { .offset = 0, .count = 3, },
.usb2 = { .offset = 3, .count = 3, },
@@ -1441,12 +1998,53 @@
},
.scale_ss_clock = false,
.has_ipfs = false,
+ .otg_reset_sspi = false,
+ .mbox = {
+ .cmd = 0xe4,
+ .data_in = 0xe8,
+ .data_out = 0xec,
+ .owner = 0xf0,
+ },
+ .lpm_support = true,
};
+static const char * const tegra194_supply_names[] = {
+};
+
+static const struct tegra_xusb_phy_type tegra194_phy_types[] = {
+ { .name = "usb3", .num = 4, },
+ { .name = "usb2", .num = 4, },
+};
+
+static const struct tegra_xusb_soc tegra194_soc = {
+ .firmware = "nvidia/tegra194/xusb.bin",
+ .supply_names = tegra194_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra194_supply_names),
+ .phy_types = tegra194_phy_types,
+ .num_types = ARRAY_SIZE(tegra194_phy_types),
+ .context = &tegra186_xusb_context,
+ .ports = {
+ .usb3 = { .offset = 0, .count = 4, },
+ .usb2 = { .offset = 4, .count = 4, },
+ },
+ .scale_ss_clock = false,
+ .has_ipfs = false,
+ .otg_reset_sspi = false,
+ .mbox = {
+ .cmd = 0x68,
+ .data_in = 0x6c,
+ .data_out = 0x70,
+ .owner = 0x74,
+ },
+ .lpm_support = true,
+};
+MODULE_FIRMWARE("nvidia/tegra194/xusb.bin");
+
static const struct of_device_id tegra_xusb_of_match[] = {
{ .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc },
{ .compatible = "nvidia,tegra210-xusb", .data = &tegra210_soc },
{ .compatible = "nvidia,tegra186-xusb", .data = &tegra186_soc },
+ { .compatible = "nvidia,tegra194-xusb", .data = &tegra194_soc },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
@@ -1463,7 +2061,11 @@
static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
+ struct tegra_xusb *tegra = dev_get_drvdata(dev);
+
xhci->quirks |= XHCI_PLAT;
+ if (tegra && tegra->soc->lpm_support)
+ xhci->quirks |= XHCI_LPM_SUPPORT;
}
static int tegra_xhci_setup(struct usb_hcd *hcd)
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index dab2af3..a5da020 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* xHCI host controller driver
*
@@ -322,6 +322,7 @@
__field(u32, info2)
__field(u64, deq)
__field(u32, tx_info)
+ __dynamic_array(char, str, XHCI_MSG_MAX)
),
TP_fast_assign(
__entry->info = le32_to_cpu(ctx->ep_info);
@@ -329,8 +330,8 @@
__entry->deq = le64_to_cpu(ctx->deq);
__entry->tx_info = le32_to_cpu(ctx->tx_info);
),
- TP_printk("%s", xhci_decode_ep_context(__entry->info,
- __entry->info2, __entry->deq, __entry->tx_info)
+ TP_printk("%s", xhci_decode_ep_context(__get_str(str),
+ __entry->info, __entry->info2, __entry->deq, __entry->tx_info)
)
);
@@ -367,6 +368,7 @@
__field(u32, info2)
__field(u32, tt_info)
__field(u32, state)
+ __dynamic_array(char, str, XHCI_MSG_MAX)
),
TP_fast_assign(
__entry->info = le32_to_cpu(ctx->dev_info);
@@ -374,9 +376,9 @@
__entry->tt_info = le64_to_cpu(ctx->tt_info);
__entry->state = le32_to_cpu(ctx->dev_state);
),
- TP_printk("%s", xhci_decode_slot_context(__entry->info,
- __entry->info2, __entry->tt_info,
- __entry->state)
+ TP_printk("%s", xhci_decode_slot_context(__get_str(str),
+ __entry->info, __entry->info2,
+ __entry->tt_info, __entry->state)
)
);
@@ -431,12 +433,13 @@
TP_STRUCT__entry(
__field(u32, drop)
__field(u32, add)
+ __dynamic_array(char, str, XHCI_MSG_MAX)
),
TP_fast_assign(
__entry->drop = le32_to_cpu(ctrl_ctx->drop_flags);
__entry->add = le32_to_cpu(ctrl_ctx->add_flags);
),
- TP_printk("%s", xhci_decode_ctrl_ctx(__entry->drop, __entry->add)
+ TP_printk("%s", xhci_decode_ctrl_ctx(__get_str(str), __entry->drop, __entry->add)
)
);
@@ -549,6 +552,33 @@
TP_ARGS(portnum, portsc)
);
+DECLARE_EVENT_CLASS(xhci_log_doorbell,
+ TP_PROTO(u32 slot, u32 doorbell),
+ TP_ARGS(slot, doorbell),
+ TP_STRUCT__entry(
+ __field(u32, slot)
+ __field(u32, doorbell)
+ __dynamic_array(char, str, XHCI_MSG_MAX)
+ ),
+ TP_fast_assign(
+ __entry->slot = slot;
+ __entry->doorbell = doorbell;
+ ),
+ TP_printk("Ring doorbell for %s",
+ xhci_decode_doorbell(__get_str(str), __entry->slot, __entry->doorbell)
+ )
+);
+
+DEFINE_EVENT(xhci_log_doorbell, xhci_ring_ep_doorbell,
+ TP_PROTO(u32 slot, u32 doorbell),
+ TP_ARGS(slot, doorbell)
+);
+
+DEFINE_EVENT(xhci_log_doorbell, xhci_ring_host_doorbell,
+ TP_PROTO(u32 slot, u32 doorbell),
+ TP_ARGS(slot, doorbell)
+);
+
DECLARE_EVENT_CLASS(xhci_dbc_log_request,
TP_PROTO(struct dbc_request *req),
TP_ARGS(req),
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 505da49..49f7429 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1091,6 +1091,7 @@
int retval = 0;
bool comp_timer_running = false;
bool pending_portevent = false;
+ bool reinit_xhc = false;
if (!hcd->state)
return 0;
@@ -1107,10 +1108,11 @@
set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
spin_lock_irq(&xhci->lock);
- if ((xhci->quirks & XHCI_RESET_ON_RESUME) || xhci->broken_suspend)
- hibernated = true;
- if (!hibernated) {
+ if (hibernated || xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend)
+ reinit_xhc = true;
+
+ if (!reinit_xhc) {
/*
* Some controllers might lose power during suspend, so wait
* for controller not ready bit to clear, just as in xHC init.
@@ -1143,12 +1145,17 @@
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
}
- temp = readl(&xhci->op_regs->status);
}
- /* If restore operation fails, re-initialize the HC during resume */
- if ((temp & STS_SRE) || hibernated) {
+ temp = readl(&xhci->op_regs->status);
+ /* re-initialize the HC on Restore Error, or Host Controller Error */
+ if (temp & (STS_SRE | STS_HCE)) {
+ reinit_xhc = true;
+ xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp);
+ }
+
+ if (reinit_xhc) {
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
!(xhci_all_ports_seen_u0(xhci))) {
del_timer_sync(&xhci->comp_mode_recovery_timer);
@@ -1286,7 +1293,7 @@
return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
}
-/**
+/*
* xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and
* HCDs. Find the index for an endpoint given its descriptor. Use the return
* value to right shift 1 for the bitmask.
@@ -1480,9 +1487,12 @@
struct urb_priv *urb_priv;
int num_tds;
- if (!urb || xhci_check_args(hcd, urb->dev, urb->ep,
- true, true, __func__) <= 0)
+ if (!urb)
return -EINVAL;
+ ret = xhci_check_args(hcd, urb->dev, urb->ep,
+ true, true, __func__);
+ if (ret <= 0)
+ return ret ? ret : -EINVAL;
slot_id = urb->dev->slot_id;
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
@@ -3172,10 +3182,13 @@
return;
/* Bail out if toggle is already being cleared by a endpoint reset */
+ spin_lock_irqsave(&xhci->lock, flags);
if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) {
ep->ep_state &= ~EP_HARD_CLEAR_TOGGLE;
+ spin_unlock_irqrestore(&xhci->lock, flags);
return;
}
+ spin_unlock_irqrestore(&xhci->lock, flags);
/* Only interrupt and bulk ep's use data toggle, USB2 spec 5.5.4-> */
if (usb_endpoint_xfer_control(&host_ep->desc) ||
usb_endpoint_xfer_isoc(&host_ep->desc))
@@ -3261,8 +3274,10 @@
xhci_free_command(xhci, cfg_cmd);
cleanup:
xhci_free_command(xhci, stop_cmd);
+ spin_lock_irqsave(&xhci->lock, flags);
if (ep->ep_state & EP_SOFT_CLEAR_TOGGLE)
ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE;
+ spin_unlock_irqrestore(&xhci->lock, flags);
}
static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
@@ -3277,7 +3292,7 @@
return -EINVAL;
ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__);
if (ret <= 0)
- return -EINVAL;
+ return ret ? ret : -EINVAL;
if (usb_ss_max_streams(&ep->ss_ep_comp) == 0) {
xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion"
" descriptor for ep 0x%x does not support streams\n",
@@ -3557,6 +3572,10 @@
xhci_free_command(xhci, config_cmd);
spin_unlock_irqrestore(&xhci->lock, flags);
+ for (i = 0; i < num_eps; i++) {
+ ep_index = xhci_get_endpoint_index(&eps[i]->desc);
+ xhci_debugfs_create_stream_files(xhci, vdev, ep_index);
+ }
/* Subtract 1 for stream 0, which drivers can't use */
return num_streams - 1;
@@ -3884,7 +3903,6 @@
struct xhci_slot_ctx *slot_ctx;
int i, ret;
-#ifndef CONFIG_USB_DEFAULT_PERSIST
/*
* We called pm_runtime_get_noresume when the device was attached.
* Decrement the counter here to allow controller to runtime suspend
@@ -3892,7 +3910,6 @@
*/
if (xhci->quirks & XHCI_RESET_ON_RESUME)
pm_runtime_put_noidle(hcd->self.controller);
-#endif
ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
/* If the host is halted due to driver unload, we still need to free the
@@ -3911,9 +3928,8 @@
del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
}
virt_dev->udev = NULL;
- ret = xhci_disable_slot(xhci, udev->slot_id);
- if (ret)
- xhci_free_virt_device(xhci, udev->slot_id);
+ xhci_disable_slot(xhci, udev->slot_id);
+ xhci_free_virt_device(xhci, udev->slot_id);
}
int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
@@ -3923,7 +3939,7 @@
u32 state;
int ret = 0;
- command = xhci_alloc_command(xhci, false, GFP_KERNEL);
+ command = xhci_alloc_command(xhci, true, GFP_KERNEL);
if (!command)
return -ENOMEM;
@@ -3948,6 +3964,15 @@
}
xhci_ring_cmd_db(xhci);
spin_unlock_irqrestore(&xhci->lock, flags);
+
+ wait_for_completion(command->completion);
+
+ if (command->status != COMP_SUCCESS)
+ xhci_warn(xhci, "Unsuccessful disable slot %u command, status %d\n",
+ slot_id, command->status);
+
+ xhci_free_command(xhci, command);
+
return ret;
}
@@ -4044,23 +4069,20 @@
xhci_debugfs_create_slot(xhci, slot_id);
-#ifndef CONFIG_USB_DEFAULT_PERSIST
/*
* If resetting upon resume, we can't put the controller into runtime
* suspend if there is a device attached.
*/
if (xhci->quirks & XHCI_RESET_ON_RESUME)
pm_runtime_get_noresume(hcd->self.controller);
-#endif
/* Is this a LS or FS device under a HS hub? */
/* Hub or peripherial? */
return 1;
disable_slot:
- ret = xhci_disable_slot(xhci, udev->slot_id);
- if (ret)
- xhci_free_virt_device(xhci, udev->slot_id);
+ xhci_disable_slot(xhci, udev->slot_id);
+ xhci_free_virt_device(xhci, udev->slot_id);
return 0;
}
@@ -4190,6 +4212,7 @@
mutex_unlock(&xhci->mutex);
ret = xhci_disable_slot(xhci, udev->slot_id);
+ xhci_free_virt_device(xhci, udev->slot_id);
if (!ret)
xhci_alloc_dev(hcd, udev);
kfree(command->completion);
@@ -4641,7 +4664,7 @@
break;
}
/* Otherwise the calculation is the same as isoc eps */
- /* fall through */
+ fallthrough;
case USB_ENDPOINT_XFER_ISOC:
timeout_ns = xhci_service_interval_to_ns(desc);
timeout_ns = DIV_ROUND_UP_ULL(timeout_ns * 105, 100);
@@ -5330,7 +5353,8 @@
* generic hardware linkage
*/
.irq = xhci_irq,
- .flags = HCD_MEMORY | HCD_DMA | HCD_USB3 | HCD_SHARED,
+ .flags = HCD_MEMORY | HCD_DMA | HCD_USB3 | HCD_SHARED |
+ HCD_BH,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 02df309..45584a2 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* xHCI host controller driver
@@ -1646,7 +1646,7 @@
struct urb_priv {
int num_tds;
int num_tds_done;
- struct xhci_td td[0];
+ struct xhci_td td[];
};
/*
@@ -1774,6 +1774,8 @@
/* optional clocks */
struct clk *clk;
struct clk *reg_clk;
+ /* optional reset controller */
+ struct reset_control *reset;
/* data structures */
struct xhci_device_context_array *dcbaa;
struct xhci_ring *cmd_ring;
@@ -1877,9 +1879,12 @@
#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34)
#define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35)
+#define XHCI_RENESAS_FW_QUIRK BIT_ULL(36)
#define XHCI_SKIP_PHY_INIT BIT_ULL(37)
#define XHCI_DISABLE_SPARSE BIT_ULL(38)
+#define XHCI_SG_TRB_CACHE_SIZE_QUIRK BIT_ULL(39)
#define XHCI_NO_SOFT_RETRY BIT_ULL(40)
+#define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -1909,7 +1914,7 @@
void *dbc;
/* platform-specific data -- must come last */
- unsigned long priv[0] __aligned(sizeof(s64));
+ unsigned long priv[] __aligned(sizeof(s64));
};
/* Platform specific overrides to generic XHCI hc_driver ops */
@@ -2023,6 +2028,8 @@
struct xhci_ring *evt_ring,
struct xhci_erst *erst,
gfp_t flags);
+void xhci_initialize_ring_info(struct xhci_ring *ring,
+ unsigned int cycle_state);
void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst);
void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev,
@@ -2442,10 +2449,9 @@
return str;
}
-static inline const char *xhci_decode_ctrl_ctx(unsigned long drop,
- unsigned long add)
+static inline const char *xhci_decode_ctrl_ctx(char *str,
+ unsigned long drop, unsigned long add)
{
- static char str[1024];
unsigned int bit;
int ret = 0;
@@ -2471,10 +2477,9 @@
return str;
}
-static inline const char *xhci_decode_slot_context(u32 info, u32 info2,
- u32 tt_info, u32 state)
+static inline const char *xhci_decode_slot_context(char *str,
+ u32 info, u32 info2, u32 tt_info, u32 state)
{
- static char str[1024];
u32 speed;
u32 hub;
u32 mtt;
@@ -2603,6 +2608,62 @@
return str;
}
+static inline const char *xhci_decode_usbsts(char *str, u32 usbsts)
+{
+ int ret = 0;
+
+ if (usbsts == ~(u32)0)
+ return " 0xffffffff";
+ if (usbsts & STS_HALT)
+ ret += sprintf(str + ret, " HCHalted");
+ if (usbsts & STS_FATAL)
+ ret += sprintf(str + ret, " HSE");
+ if (usbsts & STS_EINT)
+ ret += sprintf(str + ret, " EINT");
+ if (usbsts & STS_PORT)
+ ret += sprintf(str + ret, " PCD");
+ if (usbsts & STS_SAVE)
+ ret += sprintf(str + ret, " SSS");
+ if (usbsts & STS_RESTORE)
+ ret += sprintf(str + ret, " RSS");
+ if (usbsts & STS_SRE)
+ ret += sprintf(str + ret, " SRE");
+ if (usbsts & STS_CNR)
+ ret += sprintf(str + ret, " CNR");
+ if (usbsts & STS_HCE)
+ ret += sprintf(str + ret, " HCE");
+
+ return str;
+}
+
+static inline const char *xhci_decode_doorbell(char *str, u32 slot, u32 doorbell)
+{
+ u8 ep;
+ u16 stream;
+ int ret;
+
+ ep = (doorbell & 0xff);
+ stream = doorbell >> 16;
+
+ if (slot == 0) {
+ sprintf(str, "Command Ring %d", doorbell);
+ return str;
+ }
+ ret = sprintf(str, "Slot %d ", slot);
+ if (ep > 0 && ep < 32)
+ ret = sprintf(str + ret, "ep%d%s",
+ ep / 2,
+ ep % 2 ? "in" : "out");
+ else if (ep == 0 || ep < 248)
+ ret = sprintf(str + ret, "Reserved %d", ep);
+ else
+ ret = sprintf(str + ret, "Vendor Defined %d", ep);
+ if (stream)
+ ret = sprintf(str + ret, " Stream %d", stream);
+
+ return str;
+}
+
static inline const char *xhci_ep_state_string(u8 state)
{
switch (state) {
@@ -2643,10 +2704,9 @@
}
}
-static inline const char *xhci_decode_ep_context(u32 info, u32 info2, u64 deq,
- u32 tx_info)
+static inline const char *xhci_decode_ep_context(char *str, u32 info,
+ u32 info2, u64 deq, u32 tx_info)
{
- static char str[1024];
int ret;
u32 esit;
diff --git a/drivers/usb/image/Kconfig b/drivers/usb/image/Kconfig
index 26c75f3..a65c638 100644
--- a/drivers/usb/image/Kconfig
+++ b/drivers/usb/image/Kconfig
@@ -6,7 +6,7 @@
config USB_MDC800
tristate "USB Mustek MDC800 Digital Camera support"
- ---help---
+ help
Say Y here if you want to connect this type of still camera to
your computer's USB port. This driver can be used with gphoto 0.4.3
and higher (look at <http://www.gphoto.org/>).
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index 2388674..fc0e22c 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -880,7 +880,7 @@
return -EIO;
}
mdc800->pic_len=-1;
- /* fall through */
+ fallthrough;
case 0x09: /* Download Thumbnail */
mdc800->download_left=answersize+64;
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 7a6b122..59b02a5 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -389,7 +389,7 @@
res = usb_submit_urb( transfer, GFP_ATOMIC );
if ( unlikely(res) ) {
MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
- context->srb->result = DID_ERROR << 16;
+ set_host_byte(context->srb, DID_ERROR);
mts_transfer_cleanup(transfer);
}
}
@@ -438,7 +438,7 @@
scsi_set_resid(context->srb, context->data_length -
transfer->actual_length);
} else if ( unlikely(status) ) {
- context->srb->result = (status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
+ set_host_byte(context->srb, (status == -ENOENT ? DID_ABORT : DID_ERROR));
}
mts_get_status(transfer);
@@ -455,12 +455,12 @@
if (status == -ENOENT) {
/* We are being killed */
MTS_DEBUG_GOT_HERE();
- context->srb->result = DID_ABORT<<16;
+ set_host_byte(context->srb, DID_ABORT);
} else {
/* A genuine error has occurred */
MTS_DEBUG_GOT_HERE();
- context->srb->result = DID_ERROR<<16;
+ set_host_byte(context->srb, DID_ERROR);
}
mts_transfer_cleanup(transfer);
@@ -495,7 +495,7 @@
scsi_sg_count(context->srb));
if (unlikely(status)) {
- context->srb->result = (status == -ENOENT ? DID_ABORT : DID_ERROR)<<16;
+ set_host_byte(context->srb, (status == -ENOENT ? DID_ABORT : DID_ERROR));
mts_transfer_cleanup(transfer);
}
@@ -566,7 +566,6 @@
mts_scsi_queuecommand_lck(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
- int err = 0;
int res;
MTS_DEBUG_GOT_HERE();
@@ -579,7 +578,7 @@
MTS_DEBUG("this device doesn't exist\n");
- srb->result = DID_BAD_TARGET << 16;
+ set_host_byte(srb, DID_BAD_TARGET);
if(likely(callback != NULL))
callback(srb);
@@ -606,14 +605,14 @@
if(unlikely(res)){
MTS_ERROR("error %d submitting URB\n",(int)res);
- srb->result = DID_ERROR << 16;
+ set_host_byte(srb, DID_ERROR);
if(likely(callback != NULL))
callback(srb);
}
out:
- return err;
+ return 0;
}
static DEF_SCSI_QCMD(mts_scsi_queuecommand)
diff --git a/drivers/usb/isp1760/isp1760-core.h b/drivers/usb/isp1760/isp1760-core.h
index 97cb4d7..d9a0a4c 100644
--- a/drivers/usb/isp1760/isp1760-core.h
+++ b/drivers/usb/isp1760/isp1760-core.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the NXP ISP1760 chip
*
diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
index 320fc47..33ae656 100644
--- a/drivers/usb/isp1760/isp1760-hcd.c
+++ b/drivers/usb/isp1760/isp1760-hcd.c
@@ -22,6 +22,7 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <asm/unaligned.h>
@@ -380,18 +381,15 @@
u32 mask, u32 done, int usec)
{
u32 result;
+ int ret;
- do {
- result = reg_read32(hcd->regs, reg);
- if (result == ~0)
- return -ENODEV;
- result &= mask;
- if (result == done)
- return 0;
- udelay(1);
- usec--;
- } while (usec > 0);
- return -ETIMEDOUT;
+ ret = readl_poll_timeout_atomic(hcd->regs + reg, result,
+ ((result & mask) == done ||
+ result == U32_MAX), 1, usec);
+ if (result == U32_MAX)
+ return -ENODEV;
+
+ return ret;
}
/* reset a non-running (STS_HALT == 1) controller */
@@ -788,11 +786,11 @@
mem_reads8(hcd->regs, qtd->payload_addr,
qtd->data_buffer,
qtd->actual_length);
- /* Fall through */
+ fallthrough;
case OUT_PID:
qtd->urb->actual_length +=
qtd->actual_length;
- /* Fall through */
+ fallthrough;
case SETUP_PID:
break;
}
@@ -1032,8 +1030,6 @@
urb->status = -EOVERFLOW;
else if (FROM_DW3_CERR(ptd->dw3))
urb->status = -EPIPE; /* Stall */
- else if (ptd->dw3 & DW3_ERROR_BIT)
- urb->status = -EPROTO; /* XactErr */
else
urb->status = -EPROTO; /* Unknown */
/*
diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c
index 07cc82f..ccd30f8 100644
--- a/drivers/usb/isp1760/isp1760-if.c
+++ b/drivers/usb/isp1760/isp1760-if.c
@@ -50,7 +50,7 @@
}
/* map available memory */
- iobase = ioremap_nocache(mem_start, mem_length);
+ iobase = ioremap(mem_start, mem_length);
if (!iobase) {
printk(KERN_ERR "Error ioremap failed\n");
release_mem_region(mem_start, mem_length);
@@ -101,7 +101,7 @@
return -EBUSY;
}
- iobase = ioremap_nocache(mem_start, mem_length);
+ iobase = ioremap(mem_start, mem_length);
if (!iobase) {
printk(KERN_ERR "ioremap #1\n");
release_mem_region(mem_start, mem_length);
diff --git a/drivers/usb/isp1760/isp1760-regs.h b/drivers/usb/isp1760/isp1760-regs.h
index 1f00c38..fedc4f5 100644
--- a/drivers/usb/isp1760/isp1760-regs.h
+++ b/drivers/usb/isp1760/isp1760-regs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the NXP ISP1760 chip
*
diff --git a/drivers/usb/isp1760/isp1760-udc.h b/drivers/usb/isp1760/isp1760-udc.h
index 2d0b887..d2df650 100644
--- a/drivers/usb/isp1760/isp1760-udc.h
+++ b/drivers/usb/isp1760/isp1760-udc.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for the NXP ISP1761 device controller
*
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 9bce583..6818ea6 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -6,7 +6,7 @@
config USB_EMI62
tristate "EMI 6|2m USB Audio interface support"
- ---help---
+ help
This driver loads firmware to Emagic EMI 6|2m low latency USB
Audio and Midi interface.
@@ -20,7 +20,7 @@
config USB_EMI26
tristate "EMI 2|6 USB Audio interface support"
- ---help---
+ help
This driver loads firmware to Emagic EMI 2|6 low latency USB
Audio interface.
@@ -78,7 +78,7 @@
driver supports the pre-programmed devices (incl. firmware)
by AK Modul-Bus Computer GmbH.
- Please see: http://www.ak-modul-bus.de/stat/mikrocontroller.html
+ Please see: https://www.ak-modul-bus.de/stat/mikrocontroller.html
To compile this driver as a module, choose M here: the
module will be called cypress_cy7c63.
@@ -106,7 +106,7 @@
This driver creates an entry "/dev/idmouseX" or "/dev/usb/idmouseX",
which can be used by, e.g.,"cat /dev/idmouse0 > fingerprint.pnm".
- See also <http://www.fs.tum.de/~echtler/idmouse/>.
+ See also <https://www.fs.tum.de/~echtler/idmouse/>.
config USB_FTDI_ELAN
tristate "Elan PCMCIA CardBus Adapter USB Client"
@@ -137,6 +137,16 @@
Say Y here if you want to control the backlight of Apple Cinema
Displays over USB. This driver provides a sysfs interface.
+config APPLE_MFI_FASTCHARGE
+ tristate "Fast charge control for iOS devices"
+ select POWER_SUPPLY
+ help
+ Say Y here if you want to control whether iOS devices will
+ fast charge from the USB interface, as implemented in "MFi"
+ chargers.
+
+ It is safe to say M here.
+
source "drivers/usb/misc/sisusbvga/Kconfig"
config USB_LD
@@ -181,8 +191,8 @@
including sample test device firmware and "how to use it".
config USB_EHSET_TEST_FIXTURE
- tristate "USB EHSET Test Fixture driver"
- help
+ tristate "USB EHSET Test Fixture driver"
+ help
Say Y here if you want to support the special test fixture device
used for the USB-IF Embedded Host High-Speed Electrical Test procedure.
@@ -233,17 +243,17 @@
Say Y or M here if you need to configure such a device via SMBus.
config USB_HSIC_USB3503
- tristate "USB3503 HSIC to USB20 Driver"
- depends on I2C
- select REGMAP_I2C
- help
- This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
+ tristate "USB3503 HSIC to USB20 Driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
config USB_HSIC_USB4604
- tristate "USB4604 HSIC to USB20 Driver"
- depends on I2C
- help
- This option enables support for SMSC USB4604 HSIC to USB 2.0 Driver.
+ tristate "USB4604 HSIC to USB20 Driver"
+ depends on I2C
+ help
+ This option enables support for SMSC USB4604 HSIC to USB 2.0 Driver.
config USB_LINK_LAYER_TEST
tristate "USB Link Layer Test driver"
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 0d416eb..da39bdd 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -11,6 +11,7 @@
obj-$(CONFIG_USB_EMI62) += emi62.o
obj-$(CONFIG_USB_EZUSB_FX2) += ezusb.o
obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
+obj-$(CONFIG_APPLE_MFI_FASTCHARGE) += apple-mfi-fastcharge.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 96495fc..45a3879 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -109,7 +109,7 @@
function, size, size, data);
}
-/**
+/*
* adu_abort_transfers
* aborts transfers and frees associated data structures
*/
@@ -643,7 +643,7 @@
.minor_base = ADU_MINOR_BASE,
};
-/**
+/*
* adu_probe
*
* Called by the usb core when a new device is connected that it thinks
@@ -754,7 +754,7 @@
return retval;
}
-/**
+/*
* adu_disconnect
*
* Called by the usb core when the device is removed from the system.
diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c
new file mode 100644
index 0000000..9de0171
--- /dev/null
+++ b/drivers/usb/misc/apple-mfi-fastcharge.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Fast-charge control for Apple "MFi" devices
+ *
+ * Copyright (C) 2019 Bastien Nocera <hadess@hadess.net>
+ */
+
+/* Standard include files */
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
+MODULE_DESCRIPTION("Fast-charge control for Apple \"MFi\" devices");
+MODULE_LICENSE("GPL");
+
+#define TRICKLE_CURRENT_MA 0
+#define FAST_CURRENT_MA 2500
+
+#define APPLE_VENDOR_ID 0x05ac /* Apple */
+
+/* The product ID is defined as starting with 0x12nn, as per the
+ * "Choosing an Apple Device USB Configuration" section in
+ * release R9 (2012) of the "MFi Accessory Hardware Specification"
+ *
+ * To distinguish an Apple device, a USB host can check the device
+ * descriptor of attached USB devices for the following fields:
+ * ■ Vendor ID: 0x05AC
+ * ■ Product ID: 0x12nn
+ *
+ * Those checks will be done in .match() and .probe().
+ */
+
+static const struct usb_device_id mfi_fc_id_table[] = {
+ { .idVendor = APPLE_VENDOR_ID,
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR },
+ {},
+};
+
+MODULE_DEVICE_TABLE(usb, mfi_fc_id_table);
+
+/* Driver-local specific stuff */
+struct mfi_device {
+ struct usb_device *udev;
+ struct power_supply *battery;
+ int charge_type;
+};
+
+static int apple_mfi_fc_set_charge_type(struct mfi_device *mfi,
+ const union power_supply_propval *val)
+{
+ int current_ma;
+ int retval;
+ __u8 request_type;
+
+ if (mfi->charge_type == val->intval) {
+ dev_dbg(&mfi->udev->dev, "charge type %d already set\n",
+ mfi->charge_type);
+ return 0;
+ }
+
+ switch (val->intval) {
+ case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
+ current_ma = TRICKLE_CURRENT_MA;
+ break;
+ case POWER_SUPPLY_CHARGE_TYPE_FAST:
+ current_ma = FAST_CURRENT_MA;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ request_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ retval = usb_control_msg(mfi->udev, usb_sndctrlpipe(mfi->udev, 0),
+ 0x40, /* Vendor‐defined power request */
+ request_type,
+ current_ma, /* wValue, current offset */
+ current_ma, /* wIndex, current offset */
+ NULL, 0, USB_CTRL_GET_TIMEOUT);
+ if (retval) {
+ dev_dbg(&mfi->udev->dev, "retval = %d\n", retval);
+ return retval;
+ }
+
+ mfi->charge_type = val->intval;
+
+ return 0;
+}
+
+static int apple_mfi_fc_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct mfi_device *mfi = power_supply_get_drvdata(psy);
+
+ dev_dbg(&mfi->udev->dev, "prop: %d\n", psp);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = mfi->charge_type;
+ break;
+ case POWER_SUPPLY_PROP_SCOPE:
+ val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int apple_mfi_fc_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct mfi_device *mfi = power_supply_get_drvdata(psy);
+ int ret;
+
+ dev_dbg(&mfi->udev->dev, "prop: %d\n", psp);
+
+ ret = pm_runtime_get_sync(&mfi->udev->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&mfi->udev->dev);
+ return ret;
+ }
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ ret = apple_mfi_fc_set_charge_type(mfi, val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ pm_runtime_mark_last_busy(&mfi->udev->dev);
+ pm_runtime_put_autosuspend(&mfi->udev->dev);
+
+ return ret;
+}
+
+static int apple_mfi_fc_property_is_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static enum power_supply_property apple_mfi_fc_properties[] = {
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_SCOPE
+};
+
+static const struct power_supply_desc apple_mfi_fc_desc = {
+ .name = "apple_mfi_fastcharge",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = apple_mfi_fc_properties,
+ .num_properties = ARRAY_SIZE(apple_mfi_fc_properties),
+ .get_property = apple_mfi_fc_get_property,
+ .set_property = apple_mfi_fc_set_property,
+ .property_is_writeable = apple_mfi_fc_property_is_writeable
+};
+
+static bool mfi_fc_match(struct usb_device *udev)
+{
+ int idProduct;
+
+ idProduct = le16_to_cpu(udev->descriptor.idProduct);
+ /* See comment above mfi_fc_id_table[] */
+ return (idProduct >= 0x1200 && idProduct <= 0x12ff);
+}
+
+static int mfi_fc_probe(struct usb_device *udev)
+{
+ struct power_supply_config battery_cfg = {};
+ struct mfi_device *mfi = NULL;
+ int err;
+
+ if (!mfi_fc_match(udev))
+ return -ENODEV;
+
+ mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL);
+ if (!mfi) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ battery_cfg.drv_data = mfi;
+
+ mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ mfi->battery = power_supply_register(&udev->dev,
+ &apple_mfi_fc_desc,
+ &battery_cfg);
+ if (IS_ERR(mfi->battery)) {
+ dev_err(&udev->dev, "Can't register battery\n");
+ err = PTR_ERR(mfi->battery);
+ goto error;
+ }
+
+ mfi->udev = usb_get_dev(udev);
+ dev_set_drvdata(&udev->dev, mfi);
+
+ return 0;
+
+error:
+ kfree(mfi);
+ return err;
+}
+
+static void mfi_fc_disconnect(struct usb_device *udev)
+{
+ struct mfi_device *mfi;
+
+ mfi = dev_get_drvdata(&udev->dev);
+ if (mfi->battery)
+ power_supply_unregister(mfi->battery);
+ dev_set_drvdata(&udev->dev, NULL);
+ usb_put_dev(mfi->udev);
+ kfree(mfi);
+}
+
+static struct usb_device_driver mfi_fc_driver = {
+ .name = "apple-mfi-fastcharge",
+ .probe = mfi_fc_probe,
+ .disconnect = mfi_fc_disconnect,
+ .id_table = mfi_fc_id_table,
+ .match = mfi_fc_match,
+ .generic_subclass = 1,
+};
+
+static int __init mfi_fc_driver_init(void)
+{
+ return usb_register_device_driver(&mfi_fc_driver, THIS_MODULE);
+}
+
+static void __exit mfi_fc_driver_exit(void)
+{
+ usb_deregister_device_driver(&mfi_fc_driver);
+}
+
+module_init(mfi_fc_driver_init);
+module_exit(mfi_fc_driver_exit);
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
index ba1eaab..c8098e9 100644
--- a/drivers/usb/misc/appledisplay.c
+++ b/drivers/usb/misc/appledisplay.c
@@ -89,7 +89,7 @@
dev_err(dev,
"OVERFLOW with data length %d, actual length is %d\n",
ACD_URB_BUFFER_LEN, pdata->urb->actual_length);
- /* fall through */
+ fallthrough;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
@@ -305,8 +305,7 @@
if (pdata->urb) {
usb_kill_urb(pdata->urb);
cancel_delayed_work_sync(&pdata->work);
- if (pdata->urbdata)
- usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
+ usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
pdata->urbdata, pdata->urb->transfer_dma);
usb_free_urb(pdata->urb);
}
@@ -343,20 +342,8 @@
.disconnect = appledisplay_disconnect,
.id_table = appledisplay_table,
};
-
-static int __init appledisplay_init(void)
-{
- return usb_register(&appledisplay_driver);
-}
-
-static void __exit appledisplay_exit(void)
-{
- usb_deregister(&appledisplay_driver);
-}
+module_usb_driver(appledisplay_driver);
MODULE_AUTHOR("Michael Hanselmann");
MODULE_DESCRIPTION("Apple Cinema Display driver");
MODULE_LICENSE("GPL");
-
-module_init(appledisplay_init);
-module_exit(appledisplay_exit);
diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c
index 7895d61..2752e1f 100644
--- a/drivers/usb/misc/ehset.c
+++ b/drivers/usb/misc/ehset.c
@@ -33,28 +33,28 @@
ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
USB_REQ_SET_FEATURE, USB_RT_PORT,
USB_PORT_FEAT_TEST,
- (TEST_SE0_NAK << 8) | portnum,
+ (USB_TEST_SE0_NAK << 8) | portnum,
NULL, 0, 1000);
break;
case TEST_J_PID:
ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
USB_REQ_SET_FEATURE, USB_RT_PORT,
USB_PORT_FEAT_TEST,
- (TEST_J << 8) | portnum,
+ (USB_TEST_J << 8) | portnum,
NULL, 0, 1000);
break;
case TEST_K_PID:
ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
USB_REQ_SET_FEATURE, USB_RT_PORT,
USB_PORT_FEAT_TEST,
- (TEST_K << 8) | portnum,
+ (USB_TEST_K << 8) | portnum,
NULL, 0, 1000);
break;
case TEST_PACKET_PID:
ret = usb_control_msg(hub_udev, usb_sndctrlpipe(hub_udev, 0),
USB_REQ_SET_FEATURE, USB_RT_PORT,
USB_PORT_FEAT_TEST,
- (TEST_PACKET << 8) | portnum,
+ (USB_TEST_PACKET << 8) | portnum,
NULL, 0, 1000);
break;
case TEST_HS_HOST_PORT_SUSPEND_RESUME:
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index cdee3af..157b31d 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -202,6 +202,7 @@
mutex_unlock(&ftdi_module_lock);
kfree(ftdi->bulk_in_buffer);
ftdi->bulk_in_buffer = NULL;
+ kfree(ftdi);
}
static void ftdi_elan_put_kref(struct usb_ftdi *ftdi)
@@ -333,7 +334,8 @@
*respond->result = -ESHUTDOWN;
*respond->value = 0;
complete(&respond->wait_completion);
- } mutex_unlock(&ftdi->u132_lock);
+ }
+ mutex_unlock(&ftdi->u132_lock);
}
static void ftdi_elan_abandon_targets(struct usb_ftdi *ftdi)
@@ -763,7 +765,8 @@
struct u132_command *command = &ftdi->command[COMMAND_MASK &
i++];
total_size += 5 + command->follows;
- } return total_size;
+ }
+ return total_size;
}
static int ftdi_elan_command_engine(struct usb_ftdi *ftdi)
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index bb24527..e9437a1 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -56,11 +56,10 @@
#define FTIP_SCROLL 0x24
#define ftip_command(dev, command, value, index) \
- usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), command, \
+ usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), command, \
USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, value, index, NULL, 0, 1000)
MODULE_DEVICE_TABLE(usb, idmouse_table);
-static DEFINE_MUTEX(open_disc_mutex);
/* structure to hold all of our device specific stuff */
struct usb_idmouse {
@@ -158,8 +157,8 @@
/* loop over a blocking bulk read to get data from the device */
while (bytes_read < IMGSIZE) {
- result = usb_bulk_msg (dev->udev,
- usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr),
+ result = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer + bytes_read,
dev->bulk_in_size, &bulk_read, 5000);
if (result < 0) {
@@ -223,21 +222,17 @@
int result;
/* get the interface from minor number and driver information */
- interface = usb_find_interface (&idmouse_driver, iminor (inode));
+ interface = usb_find_interface(&idmouse_driver, iminor(inode));
if (!interface)
return -ENODEV;
- mutex_lock(&open_disc_mutex);
/* get the device information block from the interface */
dev = usb_get_intfdata(interface);
- if (!dev) {
- mutex_unlock(&open_disc_mutex);
+ if (!dev)
return -ENODEV;
- }
/* lock this device */
mutex_lock(&dev->lock);
- mutex_unlock(&open_disc_mutex);
/* check if already open */
if (dev->open) {
@@ -251,7 +246,7 @@
result = usb_autopm_get_interface(interface);
if (result)
goto error;
- result = idmouse_create_image (dev);
+ result = idmouse_create_image(dev);
usb_autopm_put_interface(interface);
if (result)
goto error;
@@ -280,27 +275,17 @@
if (dev == NULL)
return -ENODEV;
- mutex_lock(&open_disc_mutex);
/* lock our device */
mutex_lock(&dev->lock);
- /* are we really open? */
- if (dev->open <= 0) {
- mutex_unlock(&dev->lock);
- mutex_unlock(&open_disc_mutex);
- return -ENODEV;
- }
-
--dev->open;
if (!dev->present) {
/* the device was unplugged before the file was released */
mutex_unlock(&dev->lock);
- mutex_unlock(&open_disc_mutex);
idmouse_delete(dev);
} else {
mutex_unlock(&dev->lock);
- mutex_unlock(&open_disc_mutex);
}
return 0;
}
@@ -379,7 +364,6 @@
if (result) {
/* something prevented us from registering this device */
dev_err(&interface->dev, "Unable to allocate minor number.\n");
- usb_set_intfdata(interface, NULL);
idmouse_delete(dev);
return result;
}
@@ -392,19 +376,13 @@
static void idmouse_disconnect(struct usb_interface *interface)
{
- struct usb_idmouse *dev;
-
- /* get device structure */
- dev = usb_get_intfdata(interface);
+ struct usb_idmouse *dev = usb_get_intfdata(interface);
/* give back our minor */
usb_deregister_dev(interface, &idmouse_class);
- mutex_lock(&open_disc_mutex);
- usb_set_intfdata(interface, NULL);
/* lock the device */
mutex_lock(&dev->lock);
- mutex_unlock(&open_disc_mutex);
/* prevent device read, write and ioctl */
dev->present = 0;
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 103c69c..72a06af 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -10,7 +10,7 @@
* usb-skeleton.c by Greg Kroah-Hartman <greg@kroah.com>
* brlvger.c by Stephane Dalton <sdalton@videotron.ca>
- * and St�hane Doyon <s.doyon@videotron.ca>
+ * and Stephane Doyon <s.doyon@videotron.ca>
*
* Released under the GPLv2.
*/
@@ -99,10 +99,6 @@
/* globals */
/*--------------*/
-/*
- * USB spec identifies 5 second timeouts.
- */
-#define GET_TIMEOUT 5
#define USB_REQ_GET_REPORT 0x01
//#if 0
static int usb_get_report(struct usb_device *dev,
@@ -114,7 +110,7 @@
USB_DIR_IN | USB_TYPE_CLASS |
USB_RECIP_INTERFACE, (type << 8) + id,
inter->desc.bInterfaceNumber, buf, size,
- GET_TIMEOUT*HZ);
+ USB_CTRL_GET_TIMEOUT);
}
//#endif
@@ -129,7 +125,7 @@
USB_TYPE_CLASS | USB_RECIP_INTERFACE,
(type << 8) + id,
intf->cur_altsetting->desc.bInterfaceNumber, buf,
- size, HZ);
+ size, 1000);
}
/*---------------------*/
@@ -245,7 +241,7 @@
wake_up_interruptible(&dev->write_wait);
}
-/**
+/*
* iowarrior_delete
*/
static inline void iowarrior_delete(struct iowarrior *dev)
@@ -272,7 +268,7 @@
return (read_idx == intr_idx ? -1 : read_idx);
}
-/**
+/*
* iowarrior_read
*/
static ssize_t iowarrior_read(struct file *file, char __user *buffer,
@@ -476,7 +472,7 @@
return retval;
}
-/**
+/*
* iowarrior_ioctl
*/
static long iowarrior_ioctl(struct file *file, unsigned int cmd,
@@ -593,7 +589,7 @@
return retval;
}
-/**
+/*
* iowarrior_open
*/
static int iowarrior_open(struct inode *inode, struct file *file)
@@ -641,7 +637,7 @@
return retval;
}
-/**
+/*
* iowarrior_release
*/
static int iowarrior_release(struct inode *inode, struct file *file)
@@ -743,7 +739,7 @@
/*---------------------------------*/
/* probe and disconnect functions */
/*---------------------------------*/
-/**
+/*
* iowarrior_probe
*
* Called by the usb core when a new device is connected that it thinks
@@ -885,7 +881,7 @@
return retval;
}
-/**
+/*
* iowarrior_disconnect
*
* Called by the usb core when the device is removed from the system.
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index 8f86b4e..670e4d9 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0+
-/**
+/*
* Generic USB driver for report based interrupt in/out devices
* like LD Didactic's USB devices. LD Didactic's USB devices are
* HID devices which do not use HID report definitons (they use
@@ -184,7 +184,7 @@
static struct usb_driver ld_usb_driver;
-/**
+/*
* ld_usb_abort_transfers
* aborts transfers and frees associated data structures
*/
@@ -199,7 +199,7 @@
usb_kill_urb(dev->interrupt_out_urb);
}
-/**
+/*
* ld_usb_delete
*/
static void ld_usb_delete(struct ld_usb *dev)
@@ -213,7 +213,7 @@
kfree(dev);
}
-/**
+/*
* ld_usb_interrupt_in_callback
*/
static void ld_usb_interrupt_in_callback(struct urb *urb)
@@ -274,7 +274,7 @@
wake_up_interruptible(&dev->read_wait);
}
-/**
+/*
* ld_usb_interrupt_out_callback
*/
static void ld_usb_interrupt_out_callback(struct urb *urb)
@@ -294,7 +294,7 @@
wake_up_interruptible(&dev->write_wait);
}
-/**
+/*
* ld_usb_open
*/
static int ld_usb_open(struct inode *inode, struct file *file)
@@ -365,7 +365,7 @@
return retval;
}
-/**
+/*
* ld_usb_release
*/
static int ld_usb_release(struct inode *inode, struct file *file)
@@ -407,7 +407,7 @@
return retval;
}
-/**
+/*
* ld_usb_poll
*/
static __poll_t ld_usb_poll(struct file *file, poll_table *wait)
@@ -431,7 +431,7 @@
return mask;
}
-/**
+/*
* ld_usb_read
*/
static ssize_t ld_usb_read(struct file *file, char __user *buffer, size_t count,
@@ -518,7 +518,7 @@
return retval;
}
-/**
+/*
* ld_usb_write
*/
static ssize_t ld_usb_write(struct file *file, const char __user *buffer,
@@ -641,7 +641,7 @@
.minor_base = USB_LD_MINOR_BASE,
};
-/**
+/*
* ld_usb_probe
*
* Called by the usb core when a new device is connected that it thinks
@@ -745,7 +745,7 @@
return retval;
}
-/**
+/*
* ld_usb_disconnect
*
* Called by the usb core when the device is removed from the system.
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 23061f1..1c9e091 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -157,19 +157,19 @@
#define LEGO_USB_TOWER_REQUEST_GET_VERSION 0xFD
struct tower_reset_reply {
- __le16 size; /* little-endian */
+ __le16 size;
__u8 err_code;
__u8 spare;
-} __attribute__ ((packed));
+};
struct tower_get_version_reply {
- __le16 size; /* little-endian */
+ __le16 size;
__u8 err_code;
__u8 spare;
__u8 major;
__u8 minor;
- __le16 build_no; /* little-endian */
-} __attribute__ ((packed));
+ __le16 build_no;
+};
/* table of devices that work with this driver */
@@ -178,7 +178,7 @@
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, tower_table);
+MODULE_DEVICE_TABLE(usb, tower_table);
#define LEGO_USB_TOWER_MINOR_BASE 160
@@ -186,13 +186,13 @@
/* Structure to hold all of our device specific stuff */
struct lego_usb_tower {
struct mutex lock; /* locks this structure */
- struct usb_device* udev; /* save off the usb device pointer */
+ struct usb_device *udev; /* save off the usb device pointer */
unsigned char minor; /* the starting minor number for this device */
int open_count; /* number of times this port has been opened */
unsigned long disconnected:1;
- char* read_buffer;
+ char *read_buffer;
size_t read_buffer_length; /* this much came in */
size_t read_packet_length; /* this much will be returned on read */
spinlock_t read_buffer_lock;
@@ -202,16 +202,15 @@
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
- char* interrupt_in_buffer;
- struct usb_endpoint_descriptor* interrupt_in_endpoint;
- struct urb* interrupt_in_urb;
+ char *interrupt_in_buffer;
+ struct usb_endpoint_descriptor *interrupt_in_endpoint;
+ struct urb *interrupt_in_urb;
int interrupt_in_interval;
- int interrupt_in_running;
int interrupt_in_done;
- char* interrupt_out_buffer;
- struct usb_endpoint_descriptor* interrupt_out_endpoint;
- struct urb* interrupt_out_urb;
+ char *interrupt_out_buffer;
+ struct usb_endpoint_descriptor *interrupt_out_endpoint;
+ struct urb *interrupt_out_urb;
int interrupt_out_interval;
int interrupt_out_busy;
@@ -219,21 +218,20 @@
/* local function prototypes */
-static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos);
-static ssize_t tower_write (struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
-static inline void tower_delete (struct lego_usb_tower *dev);
-static int tower_open (struct inode *inode, struct file *file);
-static int tower_release (struct inode *inode, struct file *file);
-static __poll_t tower_poll (struct file *file, poll_table *wait);
-static loff_t tower_llseek (struct file *file, loff_t off, int whence);
+static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos);
+static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
+static inline void tower_delete(struct lego_usb_tower *dev);
+static int tower_open(struct inode *inode, struct file *file);
+static int tower_release(struct inode *inode, struct file *file);
+static __poll_t tower_poll(struct file *file, poll_table *wait);
+static loff_t tower_llseek(struct file *file, loff_t off, int whence);
-static void tower_abort_transfers (struct lego_usb_tower *dev);
-static void tower_check_for_read_packet (struct lego_usb_tower *dev);
-static void tower_interrupt_in_callback (struct urb *urb);
-static void tower_interrupt_out_callback (struct urb *urb);
+static void tower_check_for_read_packet(struct lego_usb_tower *dev);
+static void tower_interrupt_in_callback(struct urb *urb);
+static void tower_interrupt_out_callback(struct urb *urb);
-static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id);
-static void tower_disconnect (struct usb_interface *interface);
+static int tower_probe(struct usb_interface *interface, const struct usb_device_id *id);
+static void tower_disconnect(struct usb_interface *interface);
/* file operations needed when we register this driver */
@@ -273,7 +271,7 @@
};
-/**
+/*
* lego_usb_tower_debug_data
*/
static inline void lego_usb_tower_debug_data(struct device *dev,
@@ -285,46 +283,38 @@
}
-/**
+/*
* tower_delete
*/
-static inline void tower_delete (struct lego_usb_tower *dev)
+static inline void tower_delete(struct lego_usb_tower *dev)
{
/* free data structures */
usb_free_urb(dev->interrupt_in_urb);
usb_free_urb(dev->interrupt_out_urb);
- kfree (dev->read_buffer);
- kfree (dev->interrupt_in_buffer);
- kfree (dev->interrupt_out_buffer);
+ kfree(dev->read_buffer);
+ kfree(dev->interrupt_in_buffer);
+ kfree(dev->interrupt_out_buffer);
usb_put_dev(dev->udev);
- kfree (dev);
+ kfree(dev);
}
-/**
+/*
* tower_open
*/
-static int tower_open (struct inode *inode, struct file *file)
+static int tower_open(struct inode *inode, struct file *file)
{
struct lego_usb_tower *dev = NULL;
int subminor;
int retval = 0;
struct usb_interface *interface;
- struct tower_reset_reply *reset_reply;
+ struct tower_reset_reply reset_reply;
int result;
- reset_reply = kmalloc(sizeof(*reset_reply), GFP_KERNEL);
-
- if (!reset_reply) {
- retval = -ENOMEM;
- goto exit;
- }
-
nonseekable_open(inode, file);
subminor = iminor(inode);
- interface = usb_find_interface (&tower_driver, subminor);
-
+ interface = usb_find_interface(&tower_driver, subminor);
if (!interface) {
pr_err("error, can't find device for minor %d\n", subminor);
retval = -ENODEV;
@@ -351,15 +341,12 @@
}
/* reset the tower */
- result = usb_control_msg (dev->udev,
- usb_rcvctrlpipe(dev->udev, 0),
- LEGO_USB_TOWER_REQUEST_RESET,
- USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
- 0,
- 0,
- reset_reply,
- sizeof(*reset_reply),
- 1000);
+ result = usb_control_msg_recv(dev->udev, 0,
+ LEGO_USB_TOWER_REQUEST_RESET,
+ USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+ 0, 0,
+ &reset_reply, sizeof(reset_reply), 1000,
+ GFP_KERNEL);
if (result < 0) {
dev_err(&dev->udev->dev,
"LEGO USB Tower reset control request failed\n");
@@ -370,24 +357,22 @@
/* initialize in direction */
dev->read_buffer_length = 0;
dev->read_packet_length = 0;
- usb_fill_int_urb (dev->interrupt_in_urb,
- dev->udev,
- usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
- dev->interrupt_in_buffer,
- usb_endpoint_maxp(dev->interrupt_in_endpoint),
- tower_interrupt_in_callback,
- dev,
- dev->interrupt_in_interval);
+ usb_fill_int_urb(dev->interrupt_in_urb,
+ dev->udev,
+ usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ usb_endpoint_maxp(dev->interrupt_in_endpoint),
+ tower_interrupt_in_callback,
+ dev,
+ dev->interrupt_in_interval);
- dev->interrupt_in_running = 1;
dev->interrupt_in_done = 0;
mb();
- retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL);
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
if (retval) {
dev_err(&dev->udev->dev,
"Couldn't submit interrupt_in_urb %d\n", retval);
- dev->interrupt_in_running = 0;
goto unlock_exit;
}
@@ -400,20 +385,18 @@
mutex_unlock(&dev->lock);
exit:
- kfree(reset_reply);
return retval;
}
-/**
+/*
* tower_release
*/
-static int tower_release (struct inode *inode, struct file *file)
+static int tower_release(struct inode *inode, struct file *file)
{
struct lego_usb_tower *dev;
int retval = 0;
dev = file->private_data;
-
if (dev == NULL) {
retval = -ENODEV;
goto exit;
@@ -421,57 +404,33 @@
mutex_lock(&dev->lock);
- if (dev->open_count != 1) {
- dev_dbg(&dev->udev->dev, "%s: device not opened exactly once\n",
- __func__);
- retval = -ENODEV;
- goto unlock_exit;
- }
-
if (dev->disconnected) {
/* the device was unplugged before the file was released */
/* unlock here as tower_delete frees dev */
mutex_unlock(&dev->lock);
- tower_delete (dev);
+ tower_delete(dev);
goto exit;
}
/* wait until write transfer is finished */
if (dev->interrupt_out_busy) {
- wait_event_interruptible_timeout (dev->write_wait, !dev->interrupt_out_busy, 2 * HZ);
+ wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy,
+ 2 * HZ);
}
- tower_abort_transfers (dev);
+
+ /* shutdown transfers */
+ usb_kill_urb(dev->interrupt_in_urb);
+ usb_kill_urb(dev->interrupt_out_urb);
+
dev->open_count = 0;
-unlock_exit:
mutex_unlock(&dev->lock);
exit:
return retval;
}
-
-/**
- * tower_abort_transfers
- * aborts transfers and frees associated data structures
- */
-static void tower_abort_transfers (struct lego_usb_tower *dev)
-{
- if (dev == NULL)
- return;
-
- /* shutdown transfer */
- if (dev->interrupt_in_running) {
- dev->interrupt_in_running = 0;
- mb();
- usb_kill_urb(dev->interrupt_in_urb);
- }
- if (dev->interrupt_out_busy)
- usb_kill_urb(dev->interrupt_out_urb);
-}
-
-
-/**
+/*
* tower_check_for_read_packet
*
* To get correct semantics for signals and non-blocking I/O
@@ -479,23 +438,23 @@
* until it has been there unchanged for at least
* dev->packet_timeout_jiffies, or until the buffer is full.
*/
-static void tower_check_for_read_packet (struct lego_usb_tower *dev)
+static void tower_check_for_read_packet(struct lego_usb_tower *dev)
{
- spin_lock_irq (&dev->read_buffer_lock);
+ spin_lock_irq(&dev->read_buffer_lock);
if (!packet_timeout
|| time_after(jiffies, dev->read_last_arrival + dev->packet_timeout_jiffies)
|| dev->read_buffer_length == read_buffer_size) {
dev->read_packet_length = dev->read_buffer_length;
}
dev->interrupt_in_done = 0;
- spin_unlock_irq (&dev->read_buffer_lock);
+ spin_unlock_irq(&dev->read_buffer_lock);
}
-/**
+/*
* tower_poll
*/
-static __poll_t tower_poll (struct file *file, poll_table *wait)
+static __poll_t tower_poll(struct file *file, poll_table *wait)
{
struct lego_usb_tower *dev;
__poll_t mask = 0;
@@ -509,30 +468,28 @@
poll_wait(file, &dev->write_wait, wait);
tower_check_for_read_packet(dev);
- if (dev->read_packet_length > 0) {
+ if (dev->read_packet_length > 0)
mask |= EPOLLIN | EPOLLRDNORM;
- }
- if (!dev->interrupt_out_busy) {
+ if (!dev->interrupt_out_busy)
mask |= EPOLLOUT | EPOLLWRNORM;
- }
return mask;
}
-/**
+/*
* tower_llseek
*/
-static loff_t tower_llseek (struct file *file, loff_t off, int whence)
+static loff_t tower_llseek(struct file *file, loff_t off, int whence)
{
return -ESPIPE; /* unseekable */
}
-/**
+/*
* tower_read
*/
-static ssize_t tower_read (struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct lego_usb_tower *dev;
size_t bytes_to_read;
@@ -551,7 +508,6 @@
/* verify that the device wasn't unplugged */
if (dev->disconnected) {
retval = -ENODEV;
- pr_err("No device or device unplugged %d\n", retval);
goto unlock_exit;
}
@@ -561,21 +517,19 @@
goto unlock_exit;
}
- if (read_timeout) {
+ if (read_timeout)
timeout = jiffies + msecs_to_jiffies(read_timeout);
- }
/* wait for data */
- tower_check_for_read_packet (dev);
+ tower_check_for_read_packet(dev);
while (dev->read_packet_length == 0) {
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
goto unlock_exit;
}
retval = wait_event_interruptible_timeout(dev->read_wait, dev->interrupt_in_done, dev->packet_timeout_jiffies);
- if (retval < 0) {
+ if (retval < 0)
goto unlock_exit;
- }
/* reset read timeout during read or write activity */
if (read_timeout
@@ -583,28 +537,27 @@
timeout = jiffies + msecs_to_jiffies(read_timeout);
}
/* check for read timeout */
- if (read_timeout && time_after (jiffies, timeout)) {
+ if (read_timeout && time_after(jiffies, timeout)) {
retval = -ETIMEDOUT;
goto unlock_exit;
}
- tower_check_for_read_packet (dev);
+ tower_check_for_read_packet(dev);
}
/* copy the data from read_buffer into userspace */
bytes_to_read = min(count, dev->read_packet_length);
- if (copy_to_user (buffer, dev->read_buffer, bytes_to_read)) {
+ if (copy_to_user(buffer, dev->read_buffer, bytes_to_read)) {
retval = -EFAULT;
goto unlock_exit;
}
- spin_lock_irq (&dev->read_buffer_lock);
+ spin_lock_irq(&dev->read_buffer_lock);
dev->read_buffer_length -= bytes_to_read;
dev->read_packet_length -= bytes_to_read;
- for (i=0; i<dev->read_buffer_length; i++) {
+ for (i = 0; i < dev->read_buffer_length; i++)
dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read];
- }
- spin_unlock_irq (&dev->read_buffer_lock);
+ spin_unlock_irq(&dev->read_buffer_lock);
retval = bytes_to_read;
@@ -617,10 +570,10 @@
}
-/**
+/*
* tower_write
*/
-static ssize_t tower_write (struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct lego_usb_tower *dev;
size_t bytes_to_write;
@@ -637,7 +590,6 @@
/* verify that the device wasn't unplugged */
if (dev->disconnected) {
retval = -ENODEV;
- pr_err("No device or device unplugged %d\n", retval);
goto unlock_exit;
}
@@ -653,10 +605,10 @@
retval = -EAGAIN;
goto unlock_exit;
}
- retval = wait_event_interruptible (dev->write_wait, !dev->interrupt_out_busy);
- if (retval) {
+ retval = wait_event_interruptible(dev->write_wait,
+ !dev->interrupt_out_busy);
+ if (retval)
goto unlock_exit;
- }
}
/* write the data into interrupt_out_buffer from userspace */
@@ -664,7 +616,7 @@
dev_dbg(&dev->udev->dev, "%s: count = %zd, bytes_to_write = %zd\n",
__func__, count, bytes_to_write);
- if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) {
+ if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) {
retval = -EFAULT;
goto unlock_exit;
}
@@ -682,7 +634,7 @@
dev->interrupt_out_busy = 1;
wmb();
- retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL);
+ retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
if (retval) {
dev->interrupt_out_busy = 0;
dev_err(&dev->udev->dev,
@@ -700,10 +652,10 @@
}
-/**
+/*
* tower_interrupt_in_callback
*/
-static void tower_interrupt_in_callback (struct urb *urb)
+static void tower_interrupt_in_callback(struct urb *urb)
{
struct lego_usb_tower *dev = urb->context;
int status = urb->status;
@@ -729,9 +681,9 @@
if (urb->actual_length > 0) {
spin_lock_irqsave(&dev->read_buffer_lock, flags);
if (dev->read_buffer_length + urb->actual_length < read_buffer_size) {
- memcpy (dev->read_buffer + dev->read_buffer_length,
- dev->interrupt_in_buffer,
- urb->actual_length);
+ memcpy(dev->read_buffer + dev->read_buffer_length,
+ dev->interrupt_in_buffer,
+ urb->actual_length);
dev->read_buffer_length += urb->actual_length;
dev->read_last_arrival = jiffies;
dev_dbg(&dev->udev->dev, "%s: received %d bytes\n",
@@ -744,25 +696,21 @@
}
resubmit:
- /* resubmit if we're still running */
- if (dev->interrupt_in_running) {
- retval = usb_submit_urb (dev->interrupt_in_urb, GFP_ATOMIC);
- if (retval)
- dev_err(&dev->udev->dev,
- "%s: usb_submit_urb failed (%d)\n",
- __func__, retval);
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);
+ if (retval) {
+ dev_err(&dev->udev->dev, "%s: usb_submit_urb failed (%d)\n",
+ __func__, retval);
}
-
exit:
dev->interrupt_in_done = 1;
- wake_up_interruptible (&dev->read_wait);
+ wake_up_interruptible(&dev->read_wait);
}
-/**
+/*
* tower_interrupt_out_callback
*/
-static void tower_interrupt_out_callback (struct urb *urb)
+static void tower_interrupt_out_callback(struct urb *urb)
{
struct lego_usb_tower *dev = urb->context;
int status = urb->status;
@@ -784,54 +732,33 @@
}
-/**
+/*
* tower_probe
*
* Called by the usb core when a new device is connected that it thinks
* this driver might be interested in.
*/
-static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id)
+static int tower_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct device *idev = &interface->dev;
struct usb_device *udev = interface_to_usbdev(interface);
- struct lego_usb_tower *dev = NULL;
- struct tower_get_version_reply *get_version_reply = NULL;
+ struct lego_usb_tower *dev;
+ struct tower_get_version_reply get_version_reply;
int retval = -ENOMEM;
int result;
/* allocate memory for our device state and initialize it */
-
- dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
-
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
goto exit;
mutex_init(&dev->lock);
-
dev->udev = usb_get_dev(udev);
- dev->open_count = 0;
- dev->disconnected = 0;
-
- dev->read_buffer = NULL;
- dev->read_buffer_length = 0;
- dev->read_packet_length = 0;
- spin_lock_init (&dev->read_buffer_lock);
+ spin_lock_init(&dev->read_buffer_lock);
dev->packet_timeout_jiffies = msecs_to_jiffies(packet_timeout);
dev->read_last_arrival = jiffies;
-
- init_waitqueue_head (&dev->read_wait);
- init_waitqueue_head (&dev->write_wait);
-
- dev->interrupt_in_buffer = NULL;
- dev->interrupt_in_endpoint = NULL;
- dev->interrupt_in_urb = NULL;
- dev->interrupt_in_running = 0;
- dev->interrupt_in_done = 0;
-
- dev->interrupt_out_buffer = NULL;
- dev->interrupt_out_endpoint = NULL;
- dev->interrupt_out_urb = NULL;
- dev->interrupt_out_busy = 0;
+ init_waitqueue_head(&dev->read_wait);
+ init_waitqueue_head(&dev->write_wait);
result = usb_find_common_endpoints_reverse(interface->cur_altsetting,
NULL, NULL,
@@ -843,16 +770,16 @@
goto error;
}
- dev->read_buffer = kmalloc (read_buffer_size, GFP_KERNEL);
+ dev->read_buffer = kmalloc(read_buffer_size, GFP_KERNEL);
if (!dev->read_buffer)
goto error;
- dev->interrupt_in_buffer = kmalloc (usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL);
+ dev->interrupt_in_buffer = kmalloc(usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL);
if (!dev->interrupt_in_buffer)
goto error;
dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->interrupt_in_urb)
goto error;
- dev->interrupt_out_buffer = kmalloc (write_buffer_size, GFP_KERNEL);
+ dev->interrupt_out_buffer = kmalloc(write_buffer_size, GFP_KERNEL);
if (!dev->interrupt_out_buffer)
goto error;
dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -861,41 +788,30 @@
dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
- get_version_reply = kmalloc(sizeof(*get_version_reply), GFP_KERNEL);
-
- if (!get_version_reply) {
- retval = -ENOMEM;
- goto error;
- }
-
/* get the firmware version and log it */
- result = usb_control_msg (udev,
- usb_rcvctrlpipe(udev, 0),
- LEGO_USB_TOWER_REQUEST_GET_VERSION,
- USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
- 0,
- 0,
- get_version_reply,
- sizeof(*get_version_reply),
- 1000);
- if (result != sizeof(*get_version_reply)) {
- if (result >= 0)
- result = -EIO;
+ result = usb_control_msg_recv(udev, 0,
+ LEGO_USB_TOWER_REQUEST_GET_VERSION,
+ USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+ 0,
+ 0,
+ &get_version_reply,
+ sizeof(get_version_reply),
+ 1000, GFP_KERNEL);
+ if (result) {
dev_err(idev, "get version request failed: %d\n", result);
retval = result;
goto error;
}
dev_info(&interface->dev,
"LEGO USB Tower firmware version is %d.%d build %d\n",
- get_version_reply->major,
- get_version_reply->minor,
- le16_to_cpu(get_version_reply->build_no));
+ get_version_reply.major,
+ get_version_reply.minor,
+ le16_to_cpu(get_version_reply.build_no));
/* we can register the device now, as it is ready */
- usb_set_intfdata (interface, dev);
+ usb_set_intfdata(interface, dev);
- retval = usb_register_dev (interface, &tower_class);
-
+ retval = usb_register_dev(interface, &tower_class);
if (retval) {
/* something prevented us from registering this driver */
dev_err(idev, "Not able to get a minor for this device.\n");
@@ -909,32 +825,30 @@
USB_MAJOR, dev->minor);
exit:
- kfree(get_version_reply);
return retval;
error:
- kfree(get_version_reply);
tower_delete(dev);
return retval;
}
-/**
+/*
* tower_disconnect
*
* Called by the usb core when the device is removed from the system.
*/
-static void tower_disconnect (struct usb_interface *interface)
+static void tower_disconnect(struct usb_interface *interface)
{
struct lego_usb_tower *dev;
int minor;
- dev = usb_get_intfdata (interface);
+ dev = usb_get_intfdata(interface);
minor = dev->minor;
/* give back our minor and prevent further open() */
- usb_deregister_dev (interface, &tower_class);
+ usb_deregister_dev(interface, &tower_class);
/* stop I/O */
usb_poison_urb(dev->interrupt_in_urb);
@@ -945,7 +859,7 @@
/* if the device is not opened, then we clean up right now */
if (!dev->open_count) {
mutex_unlock(&dev->lock);
- tower_delete (dev);
+ tower_delete(dev);
} else {
dev->disconnected = 1;
/* wake up pollers */
@@ -962,6 +876,4 @@
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
-#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
-#endif
diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig
index df7404c..c12cdd0 100644
--- a/drivers/usb/misc/sisusbvga/Kconfig
+++ b/drivers/usb/misc/sisusbvga/Kconfig
@@ -4,7 +4,7 @@
tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)"
depends on (USB_MUSB_HDRC || USB_EHCI_HCD)
select FONT_SUPPORT if USB_SISUSBVGA_CON
- ---help---
+ help
Say Y here if you intend to attach a USB2VGA dongle based on a
Net2280 and a SiS315 chip.
@@ -18,7 +18,7 @@
bool "Text console and mode switching support" if USB_SISUSBVGA
depends on VT && BROKEN
select FONT_8x16
- ---help---
+ help
Say Y here if you want a VGA text console via the USB dongle or
want to support userland applications that utilize the driver's
display mode switching capabilities.
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 0734e6d..f08de33 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -1283,7 +1283,7 @@
return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, adr, data);
}
-int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
+int sisusb_copy_memory(struct sisusb_usb_data *sisusb, u8 *src,
u32 dest, int length)
{
size_t dummy;
@@ -1307,7 +1307,7 @@
#ifdef SISUSBENDIANTEST
static void sisusb_testreadwrite(struct sisusb_usb_data *sisusb)
{
- static char srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
+ static u8 srcbuffer[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 };
char destbuffer[10];
int i, j;
@@ -2340,7 +2340,7 @@
}
} else if (sisusb->scrbuf) {
- ret |= sisusb_copy_memory(sisusb, (char *)sisusb->scrbuf,
+ ret |= sisusb_copy_memory(sisusb, (u8 *)sisusb->scrbuf,
sisusb->vrambase, sisusb->scrbuf_size);
}
diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h
index 8a5e6bb..c0fb9e1 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.h
+++ b/drivers/usb/misc/sisusbvga/sisusb.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
/*
* sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles
*
diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c
index cd01553..c63e545 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_con.c
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -302,14 +302,15 @@
/* interface routine */
static u8
-sisusbcon_build_attr(struct vc_data *c, u8 color, u8 intensity,
- u8 blink, u8 underline, u8 reverse, u8 unused)
+sisusbcon_build_attr(struct vc_data *c, u8 color, enum vc_intensity intensity,
+ bool blink, bool underline, bool reverse,
+ bool unused)
{
u8 attr = color;
if (underline)
attr = (attr & 0xf0) | c->vc_ulcolor;
- else if (intensity == 0)
+ else if (intensity == VCI_HALF_BRIGHT)
attr = (attr & 0xf0) | c->vc_halfcolor;
if (reverse)
@@ -320,7 +321,7 @@
if (blink)
attr ^= 0x80;
- if (intensity == 2)
+ if (intensity == VCI_BOLD)
attr ^= 0x08;
return attr;
@@ -509,7 +510,7 @@
/* Restore the screen contents */
memcpy((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf, length);
- sisusb_copy_memory(sisusb, (char *)c->vc_origin,
+ sisusb_copy_memory(sisusb, (u8 *)c->vc_origin,
sisusb_haddr(sisusb, c, 0, 0), length);
mutex_unlock(&sisusb->lock);
@@ -615,7 +616,7 @@
sisusbcon_memsetw((u16 *)c->vc_origin,
c->vc_video_erase_char,
c->vc_screenbuf_size);
- sisusb_copy_memory(sisusb, (char *)c->vc_origin,
+ sisusb_copy_memory(sisusb, (u8 *)c->vc_origin,
sisusb_haddr(sisusb, c, 0, 0),
c->vc_screenbuf_size);
sisusb->con_blanked = 1;
@@ -726,7 +727,7 @@
baseline = c->vc_font.height - (c->vc_font.height < 10 ? 1 : 2);
- switch (c->vc_cursor_type & 0x0f) {
+ switch (CUR_SIZE(c->vc_cursor_type)) {
case CUR_BLOCK: from = 1;
to = c->vc_font.height;
break;
@@ -897,18 +898,18 @@
if (copyall)
sisusb_copy_memory(sisusb,
- (char *)c->vc_origin,
+ (u8 *)c->vc_origin,
sisusb_haddr(sisusb, c, 0, 0),
c->vc_screenbuf_size);
else if (dir == SM_UP)
sisusb_copy_memory(sisusb,
- (char *)c->vc_origin + c->vc_screenbuf_size - delta,
+ (u8 *)c->vc_origin + c->vc_screenbuf_size - delta,
sisusb_haddr(sisusb, c, 0, 0) +
c->vc_screenbuf_size - delta,
delta);
else
sisusb_copy_memory(sisusb,
- (char *)c->vc_origin,
+ (u8 *)c->vc_origin,
sisusb_haddr(sisusb, c, 0, 0),
delta);
@@ -1226,7 +1227,7 @@
sisusb->font_backup = vmalloc(array_size(charcount, 32));
if (sisusb->font_backup) {
- memcpy(sisusb->font_backup, font->data, charcount * 32);
+ memcpy(sisusb->font_backup, font->data, array_size(charcount, 32));
sisusb->font_backup_size = charcount;
sisusb->font_backup_height = font->height;
sisusb->font_backup_512 = (charcount == 512) ? 1 : 0;
diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.c b/drivers/usb/misc/sisusbvga/sisusb_init.c
index 66f6ab5..7c11198 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_init.c
+++ b/drivers/usb/misc/sisusbvga/sisusb_init.c
@@ -45,6 +45,7 @@
#include "sisusb.h"
#include "sisusb_init.h"
+#include "sisusb_tables.h"
/*********************************************/
/* POINTER INITIALIZATION */
diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.h b/drivers/usb/misc/sisusbvga/sisusb_init.h
index ace0998..b5cd77a 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_init.h
+++ b/drivers/usb/misc/sisusbvga/sisusb_init.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/* $XFree86$ */
/* $XdotOrg$ */
/*
@@ -147,668 +147,6 @@
#define SIS_VIDEO_PLAYBACK 0x02 - 0x30
#define SIS_CRT2_PORT_04 0x04 - 0x30
-/* Mode numbers */
-static const unsigned short ModeIndex_320x200[] = { 0x59, 0x41, 0x00, 0x4f };
-static const unsigned short ModeIndex_320x240[] = { 0x50, 0x56, 0x00, 0x53 };
-static const unsigned short ModeIndex_400x300[] = { 0x51, 0x57, 0x00, 0x54 };
-static const unsigned short ModeIndex_512x384[] = { 0x52, 0x58, 0x00, 0x5c };
-static const unsigned short ModeIndex_640x400[] = { 0x2f, 0x5d, 0x00, 0x5e };
-static const unsigned short ModeIndex_640x480[] = { 0x2e, 0x44, 0x00, 0x62 };
-static const unsigned short ModeIndex_720x480[] = { 0x31, 0x33, 0x00, 0x35 };
-static const unsigned short ModeIndex_720x576[] = { 0x32, 0x34, 0x00, 0x36 };
-static const unsigned short ModeIndex_768x576[] = { 0x5f, 0x60, 0x00, 0x61 };
-static const unsigned short ModeIndex_800x480[] = { 0x70, 0x7a, 0x00, 0x76 };
-static const unsigned short ModeIndex_800x600[] = { 0x30, 0x47, 0x00, 0x63 };
-static const unsigned short ModeIndex_848x480[] = { 0x39, 0x3b, 0x00, 0x3e };
-static const unsigned short ModeIndex_856x480[] = { 0x3f, 0x42, 0x00, 0x45 };
-static const unsigned short ModeIndex_960x540[] = { 0x1d, 0x1e, 0x00, 0x1f };
-static const unsigned short ModeIndex_960x600[] = { 0x20, 0x21, 0x00, 0x22 };
-static const unsigned short ModeIndex_1024x768[] = { 0x38, 0x4a, 0x00, 0x64 };
-static const unsigned short ModeIndex_1024x576[] = { 0x71, 0x74, 0x00, 0x77 };
-static const unsigned short ModeIndex_1152x864[] = { 0x29, 0x2a, 0x00, 0x2b };
-static const unsigned short ModeIndex_1280x720[] = { 0x79, 0x75, 0x00, 0x78 };
-static const unsigned short ModeIndex_1280x768[] = { 0x23, 0x24, 0x00, 0x25 };
-static const unsigned short ModeIndex_1280x1024[] = { 0x3a, 0x4d, 0x00, 0x65 };
-
-static const unsigned char SiS_MDA_DAC[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
- 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F
-};
-
-static const unsigned char SiS_CGA_DAC[] = {
- 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
- 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
- 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
- 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
- 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
- 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
- 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
- 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F
-};
-
-static const unsigned char SiS_EGA_DAC[] = {
- 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x05, 0x15,
- 0x20, 0x30, 0x24, 0x34, 0x21, 0x31, 0x25, 0x35,
- 0x08, 0x18, 0x0C, 0x1C, 0x09, 0x19, 0x0D, 0x1D,
- 0x28, 0x38, 0x2C, 0x3C, 0x29, 0x39, 0x2D, 0x3D,
- 0x02, 0x12, 0x06, 0x16, 0x03, 0x13, 0x07, 0x17,
- 0x22, 0x32, 0x26, 0x36, 0x23, 0x33, 0x27, 0x37,
- 0x0A, 0x1A, 0x0E, 0x1E, 0x0B, 0x1B, 0x0F, 0x1F,
- 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F
-};
-
-static const unsigned char SiS_VGA_DAC[] = {
- 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
- 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
- 0x00, 0x05, 0x08, 0x0B, 0x0E, 0x11, 0x14, 0x18,
- 0x1C, 0x20, 0x24, 0x28, 0x2D, 0x32, 0x38, 0x3F,
- 0x00, 0x10, 0x1F, 0x2F, 0x3F, 0x1F, 0x27, 0x2F,
- 0x37, 0x3F, 0x2D, 0x31, 0x36, 0x3A, 0x3F, 0x00,
- 0x07, 0x0E, 0x15, 0x1C, 0x0E, 0x11, 0x15, 0x18,
- 0x1C, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x00, 0x04,
- 0x08, 0x0C, 0x10, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
- 0x0B, 0x0C, 0x0D, 0x0F, 0x10
-};
-
-static const struct SiS_St SiSUSB_SModeIDTable[] = {
- {0x03, 0x0010, 0x18, 0x02, 0x02, 0x00, 0x01, 0x03, 0x40},
- {0xff, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
-};
-
-static const struct SiS_StResInfo_S SiSUSB_StResInfo[] = {
- {640, 400},
- {640, 350},
- {720, 400},
- {720, 350},
- {640, 480}
-};
-
-static const struct SiS_ModeResInfo SiSUSB_ModeResInfo[] = {
- {320, 200, 8, 8}, /* 0x00 */
- {320, 240, 8, 8}, /* 0x01 */
- {320, 400, 8, 8}, /* 0x02 */
- {400, 300, 8, 8}, /* 0x03 */
- {512, 384, 8, 8}, /* 0x04 */
- {640, 400, 8, 16}, /* 0x05 */
- {640, 480, 8, 16}, /* 0x06 */
- {800, 600, 8, 16}, /* 0x07 */
- {1024, 768, 8, 16}, /* 0x08 */
- {1280, 1024, 8, 16}, /* 0x09 */
- {1600, 1200, 8, 16}, /* 0x0a */
- {1920, 1440, 8, 16}, /* 0x0b */
- {2048, 1536, 8, 16}, /* 0x0c */
- {720, 480, 8, 16}, /* 0x0d */
- {720, 576, 8, 16}, /* 0x0e */
- {1280, 960, 8, 16}, /* 0x0f */
- {800, 480, 8, 16}, /* 0x10 */
- {1024, 576, 8, 16}, /* 0x11 */
- {1280, 720, 8, 16}, /* 0x12 */
- {856, 480, 8, 16}, /* 0x13 */
- {1280, 768, 8, 16}, /* 0x14 */
- {1400, 1050, 8, 16}, /* 0x15 */
- {1152, 864, 8, 16}, /* 0x16 */
- {848, 480, 8, 16}, /* 0x17 */
- {1360, 768, 8, 16}, /* 0x18 */
- {1024, 600, 8, 16}, /* 0x19 */
- {1152, 768, 8, 16}, /* 0x1a */
- {768, 576, 8, 16}, /* 0x1b */
- {1360, 1024, 8, 16}, /* 0x1c */
- {1680, 1050, 8, 16}, /* 0x1d */
- {1280, 800, 8, 16}, /* 0x1e */
- {1920, 1080, 8, 16}, /* 0x1f */
- {960, 540, 8, 16}, /* 0x20 */
- {960, 600, 8, 16} /* 0x21 */
-};
-
-static const struct SiS_StandTable SiSUSB_StandTable[] = {
- /* MD_3_400 - mode 0x03 - 400 */
- {
- 0x50, 0x18, 0x10, 0x1000,
- {0x00, 0x03, 0x00, 0x02},
- 0x67,
- {0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
- 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
- 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3,
- 0xff},
- {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
- 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
- 0x0c, 0x00, 0x0f, 0x08},
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x00, 0xff}
- },
- /* Generic for VGA and higher */
- {
- 0x00, 0x00, 0x00, 0x0000,
- {0x01, 0x0f, 0x00, 0x0e},
- 0x23,
- {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
- 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xea, 0x8c, 0xdf, 0x28, 0x40, 0xe7, 0x04, 0xa3,
- 0xff},
- {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x01, 0x00, 0x00, 0x00},
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff}
- }
-};
-
-static const struct SiS_Ext SiSUSB_EModeIDTable[] = {
- {0x2e, 0x0a1b, 0x0101, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x8 */
- {0x2f, 0x0a1b, 0x0100, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x05, 0x10, 0}, /* 640x400x8 */
- {0x30, 0x2a1b, 0x0103, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x8 */
- {0x31, 0x4a1b, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x8 */
- {0x32, 0x4a1b, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x8 */
- {0x33, 0x4a1d, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x16 */
- {0x34, 0x6a1d, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x16 */
- {0x35, 0x4a1f, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x32 */
- {0x36, 0x6a1f, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x32 */
- {0x38, 0x0a1b, 0x0105, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x8 */
- {0x3a, 0x0e3b, 0x0107, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x8 */
- {0x41, 0x9a1d, 0x010e, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x16 */
- {0x44, 0x0a1d, 0x0111, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x16 */
- {0x47, 0x2a1d, 0x0114, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x16 */
- {0x4a, 0x0a3d, 0x0117, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x16 */
- {0x4d, 0x0e7d, 0x011a, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x16 */
- {0x50, 0x9a1b, 0x0132, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x8 */
- {0x51, 0xba1b, 0x0133, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x8 */
- {0x52, 0xba1b, 0x0134, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x8 */
- {0x56, 0x9a1d, 0x0135, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x16 */
- {0x57, 0xba1d, 0x0136, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x16 */
- {0x58, 0xba1d, 0x0137, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x16 */
- {0x59, 0x9a1b, 0x0138, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x8 */
- {0x5c, 0xba1f, 0x0000, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x32 */
- {0x5d, 0x0a1d, 0x0139, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x07, 0x10, 0}, /* 640x400x16 */
- {0x5e, 0x0a1f, 0x0000, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x07, 0x10, 0}, /* 640x400x32 */
- {0x62, 0x0a3f, 0x013a, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x32 */
- {0x63, 0x2a3f, 0x013b, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x32 */
- {0x64, 0x0a7f, 0x013c, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x32 */
- {0x65, 0x0eff, 0x013d, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x32 */
- {0x70, 0x6a1b, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x8 */
- {0x71, 0x4a1b, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x8 */
- {0x74, 0x4a1d, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x16 */
- {0x75, 0x0a3d, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x16 */
- {0x76, 0x6a1f, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x32 */
- {0x77, 0x4a1f, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x32 */
- {0x78, 0x0a3f, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x32 */
- {0x79, 0x0a3b, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x8 */
- {0x7a, 0x6a1d, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x16 */
- {0x23, 0x0e3b, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x8 */
- {0x24, 0x0e7d, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x16 */
- {0x25, 0x0eff, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x32 */
- {0x39, 0x6a1b, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28, -1}, /* 848x480 */
- {0x3b, 0x6a3d, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28,
- -1},
- {0x3e, 0x6a7f, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28,
- -1},
- {0x3f, 0x6a1b, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a, -1}, /* 856x480 */
- {0x42, 0x6a3d, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a,
- -1},
- {0x45, 0x6a7f, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a,
- -1},
- {0x4f, 0x9a1f, 0x0000, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x32 */
- {0x53, 0x9a1f, 0x0000, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x32 */
- {0x54, 0xba1f, 0x0000, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x32 */
- {0x5f, 0x6a1b, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c, -1}, /* 768x576 */
- {0x60, 0x6a1d, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c,
- -1},
- {0x61, 0x6a3f, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c,
- -1},
- {0x1d, 0x6a1b, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d, -1}, /* 960x540 */
- {0x1e, 0x6a3d, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d,
- -1},
- {0x1f, 0x6a7f, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d,
- -1},
- {0x20, 0x6a1b, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e, -1}, /* 960x600 */
- {0x21, 0x6a3d, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e,
- -1},
- {0x22, 0x6a7f, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e,
- -1},
- {0x29, 0x4e1b, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33, -1}, /* 1152x864 */
- {0x2a, 0x4e3d, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33,
- -1},
- {0x2b, 0x4e7f, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33,
- -1},
- {0xff, 0x0000, 0x0000, 0, 0x00, 0x00, 0x00, 0x00, 0x00, -1}
-};
-
-static const struct SiS_Ext2 SiSUSB_RefIndex[] = {
- {0x085f, 0x0d, 0x03, 0x05, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x0 */
- {0x0067, 0x0e, 0x04, 0x05, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x1 */
- {0x0067, 0x0f, 0x08, 0x48, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x2 */
- {0x0067, 0x10, 0x07, 0x8b, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x3 */
- {0x0047, 0x11, 0x0a, 0x00, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x4 */
- {0x0047, 0x12, 0x0d, 0x00, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x5 */
- {0x0047, 0x13, 0x13, 0x00, 0x05, 0x30, 800, 600, 0x20, 0x00, 0x00}, /* 0x6 */
- {0x0107, 0x14, 0x1c, 0x00, 0x05, 0x30, 800, 600, 0x20, 0x00, 0x00}, /* 0x7 */
- {0xc85f, 0x05, 0x00, 0x04, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0x8 */
- {0xc067, 0x06, 0x02, 0x04, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0x9 */
- {0xc067, 0x07, 0x02, 0x47, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xa */
- {0xc067, 0x08, 0x03, 0x8a, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xb */
- {0xc047, 0x09, 0x05, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xc */
- {0xc047, 0x0a, 0x09, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xd */
- {0xc047, 0x0b, 0x0e, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xe */
- {0xc047, 0x0c, 0x15, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xf */
- {0x487f, 0x04, 0x00, 0x00, 0x00, 0x2f, 640, 400, 0x30, 0x55, 0x6e}, /* 0x10 */
- {0xc06f, 0x3c, 0x01, 0x06, 0x13, 0x31, 720, 480, 0x30, 0x00, 0x00}, /* 0x11 */
- {0x006f, 0x3d, 0x6f, 0x06, 0x14, 0x32, 720, 576, 0x30, 0x00, 0x00}, /* 0x12 (6f was 03) */
- {0x0087, 0x15, 0x06, 0x00, 0x06, 0x38, 1024, 768, 0x30, 0x00, 0x00}, /* 0x13 */
- {0xc877, 0x16, 0x0b, 0x06, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x14 */
- {0xc067, 0x17, 0x0f, 0x49, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x15 */
- {0x0067, 0x18, 0x11, 0x00, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x16 */
- {0x0047, 0x19, 0x16, 0x8c, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x17 */
- {0x0107, 0x1a, 0x1b, 0x00, 0x06, 0x38, 1024, 768, 0x10, 0x00, 0x00}, /* 0x18 */
- {0x0107, 0x1b, 0x1f, 0x00, 0x06, 0x38, 1024, 768, 0x10, 0x00, 0x00}, /* 0x19 */
- {0x407f, 0x00, 0x00, 0x00, 0x00, 0x41, 320, 200, 0x30, 0x56, 0x4e}, /* 0x1a */
- {0xc07f, 0x01, 0x00, 0x04, 0x04, 0x50, 320, 240, 0x30, 0x00, 0x00}, /* 0x1b */
- {0x007f, 0x02, 0x04, 0x05, 0x05, 0x51, 400, 300, 0x30, 0x00, 0x00}, /* 0x1c */
- {0xc077, 0x03, 0x0b, 0x06, 0x06, 0x52, 512, 384, 0x30, 0x00, 0x00}, /* 0x1d */
- {0x0077, 0x32, 0x40, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x1e */
- {0x0047, 0x33, 0x07, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x1f */
- {0x0047, 0x34, 0x0a, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x20 */
- {0x0077, 0x35, 0x0b, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x21 */
- {0x0047, 0x36, 0x11, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x22 */
- {0x0047, 0x37, 0x16, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x23 */
- {0x1137, 0x38, 0x19, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x24 */
- {0x1107, 0x39, 0x1e, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x25 */
- {0x1307, 0x3a, 0x20, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x26 */
- {0x0077, 0x42, 0x5b, 0x08, 0x11, 0x23, 1280, 768, 0x30, 0x00, 0x00}, /* 0x27 */
- {0x0087, 0x45, 0x57, 0x00, 0x16, 0x39, 848, 480, 0x30, 0x00, 0x00}, /* 0x28 38Hzi */
- {0xc067, 0x46, 0x55, 0x0b, 0x16, 0x39, 848, 480, 0x30, 0x00, 0x00}, /* 0x29 848x480-60Hz */
- {0x0087, 0x47, 0x57, 0x00, 0x17, 0x3f, 856, 480, 0x30, 0x00, 0x00}, /* 0x2a 856x480-38Hzi */
- {0xc067, 0x48, 0x57, 0x00, 0x17, 0x3f, 856, 480, 0x30, 0x00, 0x00}, /* 0x2b 856x480-60Hz */
- {0x006f, 0x4d, 0x71, 0x06, 0x15, 0x5f, 768, 576, 0x30, 0x00, 0x00}, /* 0x2c 768x576-56Hz */
- {0x0067, 0x52, 0x6a, 0x00, 0x1c, 0x1d, 960, 540, 0x30, 0x00, 0x00}, /* 0x2d 960x540 60Hz */
- {0x0077, 0x53, 0x6b, 0x0b, 0x1d, 0x20, 960, 600, 0x30, 0x00, 0x00}, /* 0x2e 960x600 60Hz */
- {0x0087, 0x1c, 0x11, 0x00, 0x07, 0x3a, 1280, 1024, 0x30, 0x00, 0x00}, /* 0x2f */
- {0x0137, 0x1d, 0x19, 0x07, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x30 */
- {0x0107, 0x1e, 0x1e, 0x00, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x31 */
- {0x0207, 0x1f, 0x20, 0x00, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x32 */
- {0x0127, 0x54, 0x6d, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x33 1152x864-60Hz */
- {0x0127, 0x44, 0x19, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x34 1152x864-75Hz */
- {0x0127, 0x4a, 0x1e, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x35 1152x864-85Hz */
- {0xffff, 0x00, 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0x00, 0x00}
-};
-
-static const struct SiS_CRT1Table SiSUSB_CRT1Table[] = {
- {{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0xbf, 0x1f,
- 0x9c, 0x8e, 0x8f, 0x96, 0xb9, 0x30, 0x00, 0x00,
- 0x00}}, /* 0x0 */
- {{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0x0b, 0x3e,
- 0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x00,
- 0x00}}, /* 0x1 */
- {{0x3d, 0x31, 0x31, 0x81, 0x37, 0x1f, 0x72, 0xf0,
- 0x58, 0x8c, 0x57, 0x57, 0x73, 0x20, 0x00, 0x05,
- 0x01}}, /* 0x2 */
- {{0x4f, 0x3f, 0x3f, 0x93, 0x45, 0x0d, 0x24, 0xf5,
- 0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x01,
- 0x01}}, /* 0x3 */
- {{0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
- 0x9c, 0x8e, 0x8f, 0x96, 0xb9, 0x30, 0x00, 0x05,
- 0x00}}, /* 0x4 */
- {{0x5f, 0x4f, 0x4f, 0x83, 0x55, 0x81, 0x0b, 0x3e,
- 0xe9, 0x8b, 0xdf, 0xe8, 0x0c, 0x00, 0x00, 0x05,
- 0x00}}, /* 0x5 */
- {{0x63, 0x4f, 0x4f, 0x87, 0x56, 0x9b, 0x06, 0x3e,
- 0xe8, 0x8a, 0xdf, 0xe7, 0x07, 0x00, 0x00, 0x01,
- 0x00}}, /* 0x6 */
- {{0x64, 0x4f, 0x4f, 0x88, 0x55, 0x9d, 0xf2, 0x1f,
- 0xe0, 0x83, 0xdf, 0xdf, 0xf3, 0x10, 0x00, 0x01,
- 0x00}}, /* 0x7 */
- {{0x63, 0x4f, 0x4f, 0x87, 0x5a, 0x81, 0xfb, 0x1f,
- 0xe0, 0x83, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x05,
- 0x00}}, /* 0x8 */
- {{0x65, 0x4f, 0x4f, 0x89, 0x58, 0x80, 0xfb, 0x1f,
- 0xe0, 0x83, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x05,
- 0x61}}, /* 0x9 */
- {{0x65, 0x4f, 0x4f, 0x89, 0x58, 0x80, 0x01, 0x3e,
- 0xe0, 0x83, 0xdf, 0xdf, 0x02, 0x00, 0x00, 0x05,
- 0x61}}, /* 0xa */
- {{0x67, 0x4f, 0x4f, 0x8b, 0x58, 0x81, 0x0d, 0x3e,
- 0xe0, 0x83, 0xdf, 0xdf, 0x0e, 0x00, 0x00, 0x05,
- 0x61}}, /* 0xb */
- {{0x65, 0x4f, 0x4f, 0x89, 0x57, 0x9f, 0xfb, 0x1f,
- 0xe6, 0x8a, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x01,
- 0x00}}, /* 0xc */
- {{0x7b, 0x63, 0x63, 0x9f, 0x6a, 0x93, 0x6f, 0xf0,
- 0x58, 0x8a, 0x57, 0x57, 0x70, 0x20, 0x00, 0x05,
- 0x01}}, /* 0xd */
- {{0x7f, 0x63, 0x63, 0x83, 0x6c, 0x1c, 0x72, 0xf0,
- 0x58, 0x8c, 0x57, 0x57, 0x73, 0x20, 0x00, 0x06,
- 0x01}}, /* 0xe */
- {{0x7d, 0x63, 0x63, 0x81, 0x6e, 0x1d, 0x98, 0xf0,
- 0x7c, 0x82, 0x57, 0x57, 0x99, 0x00, 0x00, 0x06,
- 0x01}}, /* 0xf */
- {{0x7f, 0x63, 0x63, 0x83, 0x69, 0x13, 0x6f, 0xf0,
- 0x58, 0x8b, 0x57, 0x57, 0x70, 0x20, 0x00, 0x06,
- 0x01}}, /* 0x10 */
- {{0x7e, 0x63, 0x63, 0x82, 0x6b, 0x13, 0x75, 0xf0,
- 0x58, 0x8b, 0x57, 0x57, 0x76, 0x20, 0x00, 0x06,
- 0x01}}, /* 0x11 */
- {{0x81, 0x63, 0x63, 0x85, 0x6d, 0x18, 0x7a, 0xf0,
- 0x58, 0x8b, 0x57, 0x57, 0x7b, 0x20, 0x00, 0x06,
- 0x61}}, /* 0x12 */
- {{0x83, 0x63, 0x63, 0x87, 0x6e, 0x19, 0x81, 0xf0,
- 0x58, 0x8b, 0x57, 0x57, 0x82, 0x20, 0x00, 0x06,
- 0x61}}, /* 0x13 */
- {{0x85, 0x63, 0x63, 0x89, 0x6f, 0x1a, 0x91, 0xf0,
- 0x58, 0x8b, 0x57, 0x57, 0x92, 0x20, 0x00, 0x06,
- 0x61}}, /* 0x14 */
- {{0x99, 0x7f, 0x7f, 0x9d, 0x84, 0x1a, 0x96, 0x1f,
- 0x7f, 0x83, 0x7f, 0x7f, 0x97, 0x10, 0x00, 0x02,
- 0x00}}, /* 0x15 */
- {{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf5,
- 0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
- 0x01}}, /* 0x16 */
- {{0xa1, 0x7f, 0x7f, 0x85, 0x86, 0x97, 0x24, 0xf5,
- 0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
- 0x01}}, /* 0x17 */
- {{0x9f, 0x7f, 0x7f, 0x83, 0x85, 0x91, 0x1e, 0xf5,
- 0x00, 0x83, 0xff, 0xff, 0x1f, 0x10, 0x00, 0x02,
- 0x01}}, /* 0x18 */
- {{0xa7, 0x7f, 0x7f, 0x8b, 0x89, 0x95, 0x26, 0xf5,
- 0x00, 0x83, 0xff, 0xff, 0x27, 0x10, 0x00, 0x02,
- 0x01}}, /* 0x19 */
- {{0xa9, 0x7f, 0x7f, 0x8d, 0x8c, 0x9a, 0x2c, 0xf5,
- 0x00, 0x83, 0xff, 0xff, 0x2d, 0x14, 0x00, 0x02,
- 0x62}}, /* 0x1a */
- {{0xab, 0x7f, 0x7f, 0x8f, 0x8d, 0x9b, 0x35, 0xf5,
- 0x00, 0x83, 0xff, 0xff, 0x36, 0x14, 0x00, 0x02,
- 0x62}}, /* 0x1b */
- {{0xcf, 0x9f, 0x9f, 0x93, 0xb2, 0x01, 0x14, 0xba,
- 0x00, 0x83, 0xff, 0xff, 0x15, 0x00, 0x00, 0x03,
- 0x00}}, /* 0x1c */
- {{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x28, 0x5a,
- 0x00, 0x83, 0xff, 0xff, 0x29, 0x09, 0x00, 0x07,
- 0x01}}, /* 0x1d */
- {{0xce, 0x9f, 0x9f, 0x92, 0xa5, 0x17, 0x28, 0x5a,
- 0x00, 0x83, 0xff, 0xff, 0x29, 0x09, 0x00, 0x07,
- 0x01}}, /* 0x1e */
- {{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0x2e, 0x5a,
- 0x00, 0x83, 0xff, 0xff, 0x2f, 0x09, 0x00, 0x07,
- 0x01}}, /* 0x1f */
- {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
- 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
- 0x00}}, /* 0x20 */
- {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
- 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
- 0x00}}, /* 0x21 */
- {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
- 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
- 0x00}}, /* 0x22 */
- {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
- 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
- 0x00}}, /* 0x23 */
- {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
- 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
- 0x00}}, /* 0x24 */
- {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
- 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
- 0x00}}, /* 0x25 */
- {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
- 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
- 0x00}}, /* 0x26 */
- {{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
- 0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
- 0x00}}, /* 0x27 */
- {{0x43, 0xef, 0xef, 0x87, 0x06, 0x00, 0xd4, 0x1f,
- 0xa0, 0x83, 0x9f, 0x9f, 0xd5, 0x1f, 0x41, 0x05,
- 0x63}}, /* 0x28 */
- {{0x45, 0xef, 0xef, 0x89, 0x07, 0x01, 0xd9, 0x1f,
- 0xa0, 0x83, 0x9f, 0x9f, 0xda, 0x1f, 0x41, 0x05,
- 0x63}}, /* 0x29 */
- {{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
- 0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
- 0x00}}, /* 0x2a */
- {{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
- 0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
- 0x00}}, /* 0x2b */
- {{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
- 0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
- 0x00}}, /* 0x2c */
- {{0x59, 0xff, 0xff, 0x9d, 0x17, 0x13, 0x33, 0xba,
- 0x00, 0x83, 0xff, 0xff, 0x34, 0x0f, 0x41, 0x05,
- 0x44}}, /* 0x2d */
- {{0x5b, 0xff, 0xff, 0x9f, 0x18, 0x14, 0x38, 0xba,
- 0x00, 0x83, 0xff, 0xff, 0x39, 0x0f, 0x41, 0x05,
- 0x44}}, /* 0x2e */
- {{0x5b, 0xff, 0xff, 0x9f, 0x18, 0x14, 0x3d, 0xba,
- 0x00, 0x83, 0xff, 0xff, 0x3e, 0x0f, 0x41, 0x05,
- 0x44}}, /* 0x2f */
- {{0x5d, 0xff, 0xff, 0x81, 0x19, 0x95, 0x41, 0xba,
- 0x00, 0x84, 0xff, 0xff, 0x42, 0x0f, 0x41, 0x05,
- 0x44}}, /* 0x30 */
- {{0x55, 0xff, 0xff, 0x99, 0x0d, 0x0c, 0x3e, 0xba,
- 0x00, 0x84, 0xff, 0xff, 0x3f, 0x0f, 0x41, 0x05,
- 0x00}}, /* 0x31 */
- {{0x7f, 0x63, 0x63, 0x83, 0x6c, 0x1c, 0x72, 0xba,
- 0x27, 0x8b, 0xdf, 0xdf, 0x73, 0x00, 0x00, 0x06,
- 0x01}}, /* 0x32 */
- {{0x7f, 0x63, 0x63, 0x83, 0x69, 0x13, 0x6f, 0xba,
- 0x26, 0x89, 0xdf, 0xdf, 0x6f, 0x00, 0x00, 0x06,
- 0x01}}, /* 0x33 */
- {{0x7f, 0x63, 0x63, 0x82, 0x6b, 0x13, 0x75, 0xba,
- 0x29, 0x8c, 0xdf, 0xdf, 0x75, 0x00, 0x00, 0x06,
- 0x01}}, /* 0x34 */
- {{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf1,
- 0xaf, 0x85, 0x3f, 0x3f, 0x25, 0x30, 0x00, 0x02,
- 0x01}}, /* 0x35 */
- {{0x9f, 0x7f, 0x7f, 0x83, 0x85, 0x91, 0x1e, 0xf1,
- 0xad, 0x81, 0x3f, 0x3f, 0x1f, 0x30, 0x00, 0x02,
- 0x01}}, /* 0x36 */
- {{0xa7, 0x7f, 0x7f, 0x88, 0x89, 0x95, 0x26, 0xf1,
- 0xb1, 0x85, 0x3f, 0x3f, 0x27, 0x30, 0x00, 0x02,
- 0x01}}, /* 0x37 */
- {{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x28, 0xc4,
- 0x7a, 0x8e, 0xcf, 0xcf, 0x29, 0x21, 0x00, 0x07,
- 0x01}}, /* 0x38 */
- {{0xce, 0x9f, 0x9f, 0x92, 0xa5, 0x17, 0x28, 0xd4,
- 0x7a, 0x8e, 0xcf, 0xcf, 0x29, 0x21, 0x00, 0x07,
- 0x01}}, /* 0x39 */
- {{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0x2e, 0xd4,
- 0x7d, 0x81, 0xcf, 0xcf, 0x2f, 0x21, 0x00, 0x07,
- 0x01}}, /* 0x3a */
- {{0xdc, 0x9f, 0x9f, 0x80, 0xaf, 0x9d, 0xe6, 0xff,
- 0xc0, 0x83, 0xbf, 0xbf, 0xe7, 0x10, 0x00, 0x07,
- 0x01}}, /* 0x3b */
- {{0x6b, 0x59, 0x59, 0x8f, 0x5e, 0x8c, 0x0b, 0x3e,
- 0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x05,
- 0x00}}, /* 0x3c */
- {{0x6d, 0x59, 0x59, 0x91, 0x60, 0x89, 0x53, 0xf0,
- 0x41, 0x84, 0x3f, 0x3f, 0x54, 0x00, 0x00, 0x05,
- 0x41}}, /* 0x3d */
- {{0x86, 0x6a, 0x6a, 0x8a, 0x74, 0x06, 0x8c, 0x15,
- 0x4f, 0x83, 0xef, 0xef, 0x8d, 0x30, 0x00, 0x02,
- 0x00}}, /* 0x3e */
- {{0x81, 0x6a, 0x6a, 0x85, 0x70, 0x00, 0x0f, 0x3e,
- 0xeb, 0x8e, 0xdf, 0xdf, 0x10, 0x00, 0x00, 0x02,
- 0x00}}, /* 0x3f */
- {{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x1e, 0xf1,
- 0xae, 0x85, 0x57, 0x57, 0x1f, 0x30, 0x00, 0x02,
- 0x01}}, /* 0x40 */
- {{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf5,
- 0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
- 0x01}}, /* 0x41 */
- {{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x20, 0xf5,
- 0x03, 0x88, 0xff, 0xff, 0x21, 0x10, 0x00, 0x07,
- 0x01}}, /* 0x42 */
- {{0xe6, 0xae, 0xae, 0x8a, 0xbd, 0x90, 0x3d, 0x10,
- 0x1a, 0x8d, 0x19, 0x19, 0x3e, 0x2f, 0x00, 0x03,
- 0x00}}, /* 0x43 */
- {{0xc3, 0x8f, 0x8f, 0x87, 0x9b, 0x0b, 0x82, 0xef,
- 0x60, 0x83, 0x5f, 0x5f, 0x83, 0x10, 0x00, 0x07,
- 0x01}}, /* 0x44 */
- {{0x86, 0x69, 0x69, 0x8A, 0x74, 0x06, 0x8C, 0x15,
- 0x4F, 0x83, 0xEF, 0xEF, 0x8D, 0x30, 0x00, 0x02,
- 0x00}}, /* 0x45 */
- {{0x83, 0x69, 0x69, 0x87, 0x6f, 0x1d, 0x03, 0x3E,
- 0xE5, 0x8d, 0xDF, 0xe4, 0x04, 0x00, 0x00, 0x06,
- 0x00}}, /* 0x46 */
- {{0x86, 0x6A, 0x6A, 0x8A, 0x74, 0x06, 0x8C, 0x15,
- 0x4F, 0x83, 0xEF, 0xEF, 0x8D, 0x30, 0x00, 0x02,
- 0x00}}, /* 0x47 */
- {{0x81, 0x6A, 0x6A, 0x85, 0x70, 0x00, 0x0F, 0x3E,
- 0xEB, 0x8E, 0xDF, 0xDF, 0x10, 0x00, 0x00, 0x02,
- 0x00}}, /* 0x48 */
- {{0xdd, 0xa9, 0xa9, 0x81, 0xb4, 0x97, 0x26, 0xfd,
- 0x01, 0x8d, 0xff, 0x00, 0x27, 0x10, 0x00, 0x03,
- 0x01}}, /* 0x49 */
- {{0xd9, 0x8f, 0x8f, 0x9d, 0xba, 0x0a, 0x8a, 0xff,
- 0x60, 0x8b, 0x5f, 0x5f, 0x8b, 0x10, 0x00, 0x03,
- 0x01}}, /* 0x4a */
- {{0xea, 0xae, 0xae, 0x8e, 0xba, 0x82, 0x40, 0x10,
- 0x1b, 0x87, 0x19, 0x1a, 0x41, 0x0f, 0x00, 0x03,
- 0x00}}, /* 0x4b */
- {{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0xf1, 0xff,
- 0xc0, 0x83, 0xbf, 0xbf, 0xf2, 0x10, 0x00, 0x07,
- 0x01}}, /* 0x4c */
- {{0x75, 0x5f, 0x5f, 0x99, 0x66, 0x90, 0x53, 0xf0,
- 0x41, 0x84, 0x3f, 0x3f, 0x54, 0x00, 0x00, 0x05,
- 0x41}},
- {{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0x0b, 0x3e,
- 0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x00,
- 0x00}}, /* 0x4e */
- {{0xcd, 0x9f, 0x9f, 0x91, 0xab, 0x1c, 0x3a, 0xff,
- 0x20, 0x83, 0x1f, 0x1f, 0x3b, 0x10, 0x00, 0x07,
- 0x21}}, /* 0x4f */
- {{0x15, 0xd1, 0xd1, 0x99, 0xe2, 0x19, 0x3d, 0x10,
- 0x1a, 0x8d, 0x19, 0x19, 0x3e, 0x2f, 0x01, 0x0c,
- 0x20}}, /* 0x50 */
- {{0x0e, 0xef, 0xef, 0x92, 0xfe, 0x03, 0x30, 0xf0,
- 0x1e, 0x83, 0x1b, 0x1c, 0x31, 0x00, 0x01, 0x00,
- 0x61}}, /* 0x51 */
- {{0x85, 0x77, 0x77, 0x89, 0x7d, 0x01, 0x31, 0xf0,
- 0x1e, 0x84, 0x1b, 0x1c, 0x32, 0x00, 0x00, 0x02,
- 0x41}}, /* 0x52 */
- {{0x87, 0x77, 0x77, 0x8b, 0x81, 0x0b, 0x68, 0xf0,
- 0x5a, 0x80, 0x57, 0x57, 0x69, 0x00, 0x00, 0x02,
- 0x01}}, /* 0x53 */
- {{0xcd, 0x8f, 0x8f, 0x91, 0x9b, 0x1b, 0x7a, 0xff,
- 0x64, 0x8c, 0x5f, 0x62, 0x7b, 0x10, 0x00, 0x07,
- 0x41}} /* 0x54 */
-};
-
-static const struct SiS_VCLKData SiSUSB_VCLKData[] = {
- {0x1b, 0xe1, 25}, /* 0x00 */
- {0x4e, 0xe4, 28}, /* 0x01 */
- {0x57, 0xe4, 31}, /* 0x02 */
- {0xc3, 0xc8, 36}, /* 0x03 */
- {0x42, 0xe2, 40}, /* 0x04 */
- {0xfe, 0xcd, 43}, /* 0x05 */
- {0x5d, 0xc4, 44}, /* 0x06 */
- {0x52, 0xe2, 49}, /* 0x07 */
- {0x53, 0xe2, 50}, /* 0x08 */
- {0x74, 0x67, 52}, /* 0x09 */
- {0x6d, 0x66, 56}, /* 0x0a */
- {0x5a, 0x64, 65}, /* 0x0b */
- {0x46, 0x44, 67}, /* 0x0c */
- {0xb1, 0x46, 68}, /* 0x0d */
- {0xd3, 0x4a, 72}, /* 0x0e */
- {0x29, 0x61, 75}, /* 0x0f */
- {0x6e, 0x46, 76}, /* 0x10 */
- {0x2b, 0x61, 78}, /* 0x11 */
- {0x31, 0x42, 79}, /* 0x12 */
- {0xab, 0x44, 83}, /* 0x13 */
- {0x46, 0x25, 84}, /* 0x14 */
- {0x78, 0x29, 86}, /* 0x15 */
- {0x62, 0x44, 94}, /* 0x16 */
- {0x2b, 0x41, 104}, /* 0x17 */
- {0x3a, 0x23, 105}, /* 0x18 */
- {0x70, 0x44, 108}, /* 0x19 */
- {0x3c, 0x23, 109}, /* 0x1a */
- {0x5e, 0x43, 113}, /* 0x1b */
- {0xbc, 0x44, 116}, /* 0x1c */
- {0xe0, 0x46, 132}, /* 0x1d */
- {0x54, 0x42, 135}, /* 0x1e */
- {0xea, 0x2a, 139}, /* 0x1f */
- {0x41, 0x22, 157}, /* 0x20 */
- {0x70, 0x24, 162}, /* 0x21 */
- {0x30, 0x21, 175}, /* 0x22 */
- {0x4e, 0x22, 189}, /* 0x23 */
- {0xde, 0x26, 194}, /* 0x24 */
- {0x62, 0x06, 202}, /* 0x25 */
- {0x3f, 0x03, 229}, /* 0x26 */
- {0xb8, 0x06, 234}, /* 0x27 */
- {0x34, 0x02, 253}, /* 0x28 */
- {0x58, 0x04, 255}, /* 0x29 */
- {0x24, 0x01, 265}, /* 0x2a */
- {0x9b, 0x02, 267}, /* 0x2b */
- {0x70, 0x05, 270}, /* 0x2c */
- {0x25, 0x01, 272}, /* 0x2d */
- {0x9c, 0x02, 277}, /* 0x2e */
- {0x27, 0x01, 286}, /* 0x2f */
- {0x3c, 0x02, 291}, /* 0x30 */
- {0xef, 0x0a, 292}, /* 0x31 */
- {0xf6, 0x0a, 310}, /* 0x32 */
- {0x95, 0x01, 315}, /* 0x33 */
- {0xf0, 0x09, 324}, /* 0x34 */
- {0xfe, 0x0a, 331}, /* 0x35 */
- {0xf3, 0x09, 332}, /* 0x36 */
- {0xea, 0x08, 340}, /* 0x37 */
- {0xe8, 0x07, 376}, /* 0x38 */
- {0xde, 0x06, 389}, /* 0x39 */
- {0x52, 0x2a, 54}, /* 0x3a 301 TV */
- {0x52, 0x6a, 27}, /* 0x3b 301 TV */
- {0x62, 0x24, 70}, /* 0x3c 301 TV */
- {0x62, 0x64, 70}, /* 0x3d 301 TV */
- {0xa8, 0x4c, 30}, /* 0x3e 301 TV */
- {0x20, 0x26, 33}, /* 0x3f 301 TV */
- {0x31, 0xc2, 39}, /* 0x40 */
- {0x60, 0x36, 30}, /* 0x41 Chrontel */
- {0x40, 0x4a, 28}, /* 0x42 Chrontel */
- {0x9f, 0x46, 44}, /* 0x43 Chrontel */
- {0x97, 0x2c, 26}, /* 0x44 */
- {0x44, 0xe4, 25}, /* 0x45 Chrontel */
- {0x7e, 0x32, 47}, /* 0x46 Chrontel */
- {0x8a, 0x24, 31}, /* 0x47 Chrontel */
- {0x97, 0x2c, 26}, /* 0x48 Chrontel */
- {0xce, 0x3c, 39}, /* 0x49 */
- {0x52, 0x4a, 36}, /* 0x4a Chrontel */
- {0x34, 0x61, 95}, /* 0x4b */
- {0x78, 0x27, 108}, /* 0x4c - was 102 */
- {0x66, 0x43, 123}, /* 0x4d Modes 0x26-0x28 (1400x1050) */
- {0x41, 0x4e, 21}, /* 0x4e */
- {0xa1, 0x4a, 29}, /* 0x4f Chrontel */
- {0x19, 0x42, 42}, /* 0x50 */
- {0x54, 0x46, 58}, /* 0x51 Chrontel */
- {0x25, 0x42, 61}, /* 0x52 */
- {0x44, 0x44, 66}, /* 0x53 Chrontel */
- {0x3a, 0x62, 70}, /* 0x54 Chrontel */
- {0x62, 0xc6, 34}, /* 0x55 848x480-60 */
- {0x6a, 0xc6, 37}, /* 0x56 848x480-75 - TEMP */
- {0xbf, 0xc8, 35}, /* 0x57 856x480-38i,60 */
- {0x30, 0x23, 88}, /* 0x58 1360x768-62 (is 60Hz!) */
- {0x52, 0x07, 149}, /* 0x59 1280x960-85 */
- {0x56, 0x07, 156}, /* 0x5a 1400x1050-75 */
- {0x70, 0x29, 81}, /* 0x5b 1280x768 LCD */
- {0x45, 0x25, 83}, /* 0x5c 1280x800 */
- {0x70, 0x0a, 147}, /* 0x5d 1680x1050 */
- {0x70, 0x24, 162}, /* 0x5e 1600x1200 */
- {0x5a, 0x64, 65}, /* 0x5f 1280x720 - temp */
- {0x63, 0x46, 68}, /* 0x60 1280x768_2 */
- {0x31, 0x42, 79}, /* 0x61 1280x768_3 - temp */
- {0, 0, 0}, /* 0x62 - custom (will be filled out at run-time) */
- {0x5a, 0x64, 65}, /* 0x63 1280x720 (LCD LVDS) */
- {0x70, 0x28, 90}, /* 0x64 1152x864@60 */
- {0x41, 0xc4, 32}, /* 0x65 848x480@60 */
- {0x5c, 0xc6, 32}, /* 0x66 856x480@60 */
- {0x76, 0xe7, 27}, /* 0x67 720x480@60 */
- {0x5f, 0xc6, 33}, /* 0x68 720/768x576@60 */
- {0x52, 0x27, 75}, /* 0x69 1920x1080i 60Hz interlaced */
- {0x7c, 0x6b, 38}, /* 0x6a 960x540@60 */
- {0xe3, 0x56, 41}, /* 0x6b 960x600@60 */
- {0x45, 0x25, 83}, /* 0x6c 1280x800 */
- {0x70, 0x28, 90}, /* 0x6d 1152x864@60 */
- {0x15, 0xe1, 20}, /* 0x6e 640x400@60 (fake, not actually used) */
- {0x5f, 0xc6, 33}, /* 0x6f 720x576@60 */
- {0x37, 0x5a, 10}, /* 0x70 320x200@60 (fake, not actually used) */
- {0x2b, 0xc2, 35} /* 0x71 768@576@60 */
-};
-
int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
@@ -828,7 +166,7 @@
void sisusb_delete(struct kref *kref);
int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 * data);
-int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
+int sisusb_copy_memory(struct sisusb_usb_data *sisusb, u8 *src,
u32 dest, int length);
int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
int sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
diff --git a/drivers/usb/misc/sisusbvga/sisusb_struct.h b/drivers/usb/misc/sisusbvga/sisusb_struct.h
index 706d770..3df64d2 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_struct.h
+++ b/drivers/usb/misc/sisusbvga/sisusb_struct.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* General structure definitions for universal mode switching modules
*
diff --git a/drivers/usb/misc/sisusbvga/sisusb_tables.h b/drivers/usb/misc/sisusbvga/sisusb_tables.h
new file mode 100644
index 0000000..56972f1
--- /dev/null
+++ b/drivers/usb/misc/sisusbvga/sisusb_tables.h
@@ -0,0 +1,688 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/* $XFree86$ */
+/* $XdotOrg$ */
+/*
+ * Data tables for init.c
+ *
+ * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, the following license terms
+ * apply:
+ *
+ * * 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 named License,
+ * * or 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.
+ * *
+ * * You should have received a copy of the GNU General Public License
+ * * along with this program; if not, write to the Free Software
+ * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * Redistribution and use in source and binary forms, with or without
+ * * modification, are permitted provided that the following conditions
+ * * are met:
+ * * 1) Redistributions of source code must retain the above copyright
+ * * notice, this list of conditions and the following disclaimer.
+ * * 2) Redistributions in binary form must reproduce the above copyright
+ * * notice, this list of conditions and the following disclaimer in the
+ * * documentation and/or other materials provided with the distribution.
+ * * 3) The name of the author may not be used to endorse or promote products
+ * * derived from this software without specific prior written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#ifndef _SISUSB_TABLES_H_
+#define _SISUSB_TABLES_H_
+
+static const unsigned char SiS_MDA_DAC[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F
+};
+
+static const unsigned char SiS_CGA_DAC[] = {
+ 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+ 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+ 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+ 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+ 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+ 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+ 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+ 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F
+};
+
+static const unsigned char SiS_EGA_DAC[] = {
+ 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x05, 0x15,
+ 0x20, 0x30, 0x24, 0x34, 0x21, 0x31, 0x25, 0x35,
+ 0x08, 0x18, 0x0C, 0x1C, 0x09, 0x19, 0x0D, 0x1D,
+ 0x28, 0x38, 0x2C, 0x3C, 0x29, 0x39, 0x2D, 0x3D,
+ 0x02, 0x12, 0x06, 0x16, 0x03, 0x13, 0x07, 0x17,
+ 0x22, 0x32, 0x26, 0x36, 0x23, 0x33, 0x27, 0x37,
+ 0x0A, 0x1A, 0x0E, 0x1E, 0x0B, 0x1B, 0x0F, 0x1F,
+ 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F
+};
+
+static const unsigned char SiS_VGA_DAC[] = {
+ 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+ 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+ 0x00, 0x05, 0x08, 0x0B, 0x0E, 0x11, 0x14, 0x18,
+ 0x1C, 0x20, 0x24, 0x28, 0x2D, 0x32, 0x38, 0x3F,
+ 0x00, 0x10, 0x1F, 0x2F, 0x3F, 0x1F, 0x27, 0x2F,
+ 0x37, 0x3F, 0x2D, 0x31, 0x36, 0x3A, 0x3F, 0x00,
+ 0x07, 0x0E, 0x15, 0x1C, 0x0E, 0x11, 0x15, 0x18,
+ 0x1C, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x00, 0x04,
+ 0x08, 0x0C, 0x10, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
+ 0x0B, 0x0C, 0x0D, 0x0F, 0x10
+};
+
+static const struct SiS_St SiSUSB_SModeIDTable[] = {
+ {0x03, 0x0010, 0x18, 0x02, 0x02, 0x00, 0x01, 0x03, 0x40},
+ {0xff, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+
+static const struct SiS_ModeResInfo SiSUSB_ModeResInfo[] = {
+ {320, 200, 8, 8}, /* 0x00 */
+ {320, 240, 8, 8}, /* 0x01 */
+ {320, 400, 8, 8}, /* 0x02 */
+ {400, 300, 8, 8}, /* 0x03 */
+ {512, 384, 8, 8}, /* 0x04 */
+ {640, 400, 8, 16}, /* 0x05 */
+ {640, 480, 8, 16}, /* 0x06 */
+ {800, 600, 8, 16}, /* 0x07 */
+ {1024, 768, 8, 16}, /* 0x08 */
+ {1280, 1024, 8, 16}, /* 0x09 */
+ {1600, 1200, 8, 16}, /* 0x0a */
+ {1920, 1440, 8, 16}, /* 0x0b */
+ {2048, 1536, 8, 16}, /* 0x0c */
+ {720, 480, 8, 16}, /* 0x0d */
+ {720, 576, 8, 16}, /* 0x0e */
+ {1280, 960, 8, 16}, /* 0x0f */
+ {800, 480, 8, 16}, /* 0x10 */
+ {1024, 576, 8, 16}, /* 0x11 */
+ {1280, 720, 8, 16}, /* 0x12 */
+ {856, 480, 8, 16}, /* 0x13 */
+ {1280, 768, 8, 16}, /* 0x14 */
+ {1400, 1050, 8, 16}, /* 0x15 */
+ {1152, 864, 8, 16}, /* 0x16 */
+ {848, 480, 8, 16}, /* 0x17 */
+ {1360, 768, 8, 16}, /* 0x18 */
+ {1024, 600, 8, 16}, /* 0x19 */
+ {1152, 768, 8, 16}, /* 0x1a */
+ {768, 576, 8, 16}, /* 0x1b */
+ {1360, 1024, 8, 16}, /* 0x1c */
+ {1680, 1050, 8, 16}, /* 0x1d */
+ {1280, 800, 8, 16}, /* 0x1e */
+ {1920, 1080, 8, 16}, /* 0x1f */
+ {960, 540, 8, 16}, /* 0x20 */
+ {960, 600, 8, 16} /* 0x21 */
+};
+
+static const struct SiS_StandTable SiSUSB_StandTable[] = {
+ /* MD_3_400 - mode 0x03 - 400 */
+ {
+ 0x50, 0x18, 0x10, 0x1000,
+ {0x00, 0x03, 0x00, 0x02},
+ 0x67,
+ {0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3,
+ 0xff},
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x0c, 0x00, 0x0f, 0x08},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x00, 0xff}
+ },
+ /* Generic for VGA and higher */
+ {
+ 0x00, 0x00, 0x00, 0x0000,
+ {0x01, 0x0f, 0x00, 0x0e},
+ 0x23,
+ {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xea, 0x8c, 0xdf, 0x28, 0x40, 0xe7, 0x04, 0xa3,
+ 0xff},
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x01, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff}
+ }
+};
+
+static const struct SiS_Ext SiSUSB_EModeIDTable[] = {
+ {0x2e, 0x0a1b, 0x0101, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x8 */
+ {0x2f, 0x0a1b, 0x0100, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x05, 0x10, 0}, /* 640x400x8 */
+ {0x30, 0x2a1b, 0x0103, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x8 */
+ {0x31, 0x4a1b, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x8 */
+ {0x32, 0x4a1b, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x8 */
+ {0x33, 0x4a1d, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x16 */
+ {0x34, 0x6a1d, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x16 */
+ {0x35, 0x4a1f, 0x0000, SIS_RI_720x480, 0x00, 0x00, 0x06, 0x06, 0x11, -1}, /* 720x480x32 */
+ {0x36, 0x6a1f, 0x0000, SIS_RI_720x576, 0x00, 0x00, 0x06, 0x06, 0x12, -1}, /* 720x576x32 */
+ {0x38, 0x0a1b, 0x0105, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x8 */
+ {0x3a, 0x0e3b, 0x0107, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x8 */
+ {0x41, 0x9a1d, 0x010e, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x16 */
+ {0x44, 0x0a1d, 0x0111, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x16 */
+ {0x47, 0x2a1d, 0x0114, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x16 */
+ {0x4a, 0x0a3d, 0x0117, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x16 */
+ {0x4d, 0x0e7d, 0x011a, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x16 */
+ {0x50, 0x9a1b, 0x0132, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x8 */
+ {0x51, 0xba1b, 0x0133, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x8 */
+ {0x52, 0xba1b, 0x0134, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x8 */
+ {0x56, 0x9a1d, 0x0135, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x16 */
+ {0x57, 0xba1d, 0x0136, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x16 */
+ {0x58, 0xba1d, 0x0137, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x16 */
+ {0x59, 0x9a1b, 0x0138, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x8 */
+ {0x5c, 0xba1f, 0x0000, SIS_RI_512x384, 0x00, 0x00, 0x00, 0x00, 0x1d, 4}, /* 512x384x32 */
+ {0x5d, 0x0a1d, 0x0139, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x07, 0x10, 0}, /* 640x400x16 */
+ {0x5e, 0x0a1f, 0x0000, SIS_RI_640x400, 0x00, 0x00, 0x05, 0x07, 0x10, 0}, /* 640x400x32 */
+ {0x62, 0x0a3f, 0x013a, SIS_RI_640x480, 0x00, 0x00, 0x05, 0x05, 0x08, 2}, /* 640x480x32 */
+ {0x63, 0x2a3f, 0x013b, SIS_RI_800x600, 0x00, 0x00, 0x07, 0x06, 0x00, 3}, /* 800x600x32 */
+ {0x64, 0x0a7f, 0x013c, SIS_RI_1024x768, 0x00, 0x00, 0x08, 0x07, 0x13, 4}, /* 1024x768x32 */
+ {0x65, 0x0eff, 0x013d, SIS_RI_1280x1024, 0x00, 0x00, 0x00, 0x00, 0x2f, 8}, /* 1280x1024x32 */
+ {0x70, 0x6a1b, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x8 */
+ {0x71, 0x4a1b, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x8 */
+ {0x74, 0x4a1d, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x16 */
+ {0x75, 0x0a3d, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x16 */
+ {0x76, 0x6a1f, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x32 */
+ {0x77, 0x4a1f, 0x0000, SIS_RI_1024x576, 0x00, 0x00, 0x00, 0x00, 0x21, -1}, /* 1024x576x32 */
+ {0x78, 0x0a3f, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x32 */
+ {0x79, 0x0a3b, 0x0000, SIS_RI_1280x720, 0x00, 0x00, 0x00, 0x00, 0x24, 5}, /* 1280x720x8 */
+ {0x7a, 0x6a1d, 0x0000, SIS_RI_800x480, 0x00, 0x00, 0x07, 0x07, 0x1e, -1}, /* 800x480x16 */
+ {0x23, 0x0e3b, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x8 */
+ {0x24, 0x0e7d, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x16 */
+ {0x25, 0x0eff, 0x0000, SIS_RI_1280x768, 0x00, 0x00, 0x00, 0x00, 0x27, 6}, /* 1280x768x32 */
+ {0x39, 0x6a1b, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28, -1}, /* 848x480 */
+ {0x3b, 0x6a3d, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28,
+ -1},
+ {0x3e, 0x6a7f, 0x0000, SIS_RI_848x480, 0x00, 0x00, 0x00, 0x00, 0x28,
+ -1},
+ {0x3f, 0x6a1b, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a, -1}, /* 856x480 */
+ {0x42, 0x6a3d, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a,
+ -1},
+ {0x45, 0x6a7f, 0x0000, SIS_RI_856x480, 0x00, 0x00, 0x00, 0x00, 0x2a,
+ -1},
+ {0x4f, 0x9a1f, 0x0000, SIS_RI_320x200, 0x00, 0x00, 0x04, 0x04, 0x1a, 0}, /* 320x200x32 */
+ {0x53, 0x9a1f, 0x0000, SIS_RI_320x240, 0x00, 0x00, 0x04, 0x04, 0x1b, 2}, /* 320x240x32 */
+ {0x54, 0xba1f, 0x0000, SIS_RI_400x300, 0x00, 0x00, 0x07, 0x07, 0x1c, 3}, /* 400x300x32 */
+ {0x5f, 0x6a1b, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c, -1}, /* 768x576 */
+ {0x60, 0x6a1d, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c,
+ -1},
+ {0x61, 0x6a3f, 0x0000, SIS_RI_768x576, 0x00, 0x00, 0x06, 0x06, 0x2c,
+ -1},
+ {0x1d, 0x6a1b, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d, -1}, /* 960x540 */
+ {0x1e, 0x6a3d, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d,
+ -1},
+ {0x1f, 0x6a7f, 0x0000, SIS_RI_960x540, 0x00, 0x00, 0x00, 0x00, 0x2d,
+ -1},
+ {0x20, 0x6a1b, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e, -1}, /* 960x600 */
+ {0x21, 0x6a3d, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e,
+ -1},
+ {0x22, 0x6a7f, 0x0000, SIS_RI_960x600, 0x00, 0x00, 0x00, 0x00, 0x2e,
+ -1},
+ {0x29, 0x4e1b, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33, -1}, /* 1152x864 */
+ {0x2a, 0x4e3d, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33,
+ -1},
+ {0x2b, 0x4e7f, 0x0000, SIS_RI_1152x864, 0x00, 0x00, 0x00, 0x00, 0x33,
+ -1},
+ {0xff, 0x0000, 0x0000, 0, 0x00, 0x00, 0x00, 0x00, 0x00, -1}
+};
+
+static const struct SiS_Ext2 SiSUSB_RefIndex[] = {
+ {0x085f, 0x0d, 0x03, 0x05, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x0 */
+ {0x0067, 0x0e, 0x04, 0x05, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x1 */
+ {0x0067, 0x0f, 0x08, 0x48, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x2 */
+ {0x0067, 0x10, 0x07, 0x8b, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x3 */
+ {0x0047, 0x11, 0x0a, 0x00, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x4 */
+ {0x0047, 0x12, 0x0d, 0x00, 0x05, 0x30, 800, 600, 0x40, 0x00, 0x00}, /* 0x5 */
+ {0x0047, 0x13, 0x13, 0x00, 0x05, 0x30, 800, 600, 0x20, 0x00, 0x00}, /* 0x6 */
+ {0x0107, 0x14, 0x1c, 0x00, 0x05, 0x30, 800, 600, 0x20, 0x00, 0x00}, /* 0x7 */
+ {0xc85f, 0x05, 0x00, 0x04, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0x8 */
+ {0xc067, 0x06, 0x02, 0x04, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0x9 */
+ {0xc067, 0x07, 0x02, 0x47, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xa */
+ {0xc067, 0x08, 0x03, 0x8a, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xb */
+ {0xc047, 0x09, 0x05, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xc */
+ {0xc047, 0x0a, 0x09, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xd */
+ {0xc047, 0x0b, 0x0e, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xe */
+ {0xc047, 0x0c, 0x15, 0x00, 0x04, 0x2e, 640, 480, 0x40, 0x00, 0x00}, /* 0xf */
+ {0x487f, 0x04, 0x00, 0x00, 0x00, 0x2f, 640, 400, 0x30, 0x55, 0x6e}, /* 0x10 */
+ {0xc06f, 0x3c, 0x01, 0x06, 0x13, 0x31, 720, 480, 0x30, 0x00, 0x00}, /* 0x11 */
+ {0x006f, 0x3d, 0x6f, 0x06, 0x14, 0x32, 720, 576, 0x30, 0x00, 0x00}, /* 0x12 (6f was 03) */
+ {0x0087, 0x15, 0x06, 0x00, 0x06, 0x38, 1024, 768, 0x30, 0x00, 0x00}, /* 0x13 */
+ {0xc877, 0x16, 0x0b, 0x06, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x14 */
+ {0xc067, 0x17, 0x0f, 0x49, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x15 */
+ {0x0067, 0x18, 0x11, 0x00, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x16 */
+ {0x0047, 0x19, 0x16, 0x8c, 0x06, 0x38, 1024, 768, 0x20, 0x00, 0x00}, /* 0x17 */
+ {0x0107, 0x1a, 0x1b, 0x00, 0x06, 0x38, 1024, 768, 0x10, 0x00, 0x00}, /* 0x18 */
+ {0x0107, 0x1b, 0x1f, 0x00, 0x06, 0x38, 1024, 768, 0x10, 0x00, 0x00}, /* 0x19 */
+ {0x407f, 0x00, 0x00, 0x00, 0x00, 0x41, 320, 200, 0x30, 0x56, 0x4e}, /* 0x1a */
+ {0xc07f, 0x01, 0x00, 0x04, 0x04, 0x50, 320, 240, 0x30, 0x00, 0x00}, /* 0x1b */
+ {0x007f, 0x02, 0x04, 0x05, 0x05, 0x51, 400, 300, 0x30, 0x00, 0x00}, /* 0x1c */
+ {0xc077, 0x03, 0x0b, 0x06, 0x06, 0x52, 512, 384, 0x30, 0x00, 0x00}, /* 0x1d */
+ {0x0077, 0x32, 0x40, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x1e */
+ {0x0047, 0x33, 0x07, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x1f */
+ {0x0047, 0x34, 0x0a, 0x08, 0x18, 0x70, 800, 480, 0x30, 0x00, 0x00}, /* 0x20 */
+ {0x0077, 0x35, 0x0b, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x21 */
+ {0x0047, 0x36, 0x11, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x22 */
+ {0x0047, 0x37, 0x16, 0x09, 0x19, 0x71, 1024, 576, 0x30, 0x00, 0x00}, /* 0x23 */
+ {0x1137, 0x38, 0x19, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x24 */
+ {0x1107, 0x39, 0x1e, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x25 */
+ {0x1307, 0x3a, 0x20, 0x0a, 0x0c, 0x75, 1280, 720, 0x30, 0x00, 0x00}, /* 0x26 */
+ {0x0077, 0x42, 0x5b, 0x08, 0x11, 0x23, 1280, 768, 0x30, 0x00, 0x00}, /* 0x27 */
+ {0x0087, 0x45, 0x57, 0x00, 0x16, 0x39, 848, 480, 0x30, 0x00, 0x00}, /* 0x28 38Hzi */
+ {0xc067, 0x46, 0x55, 0x0b, 0x16, 0x39, 848, 480, 0x30, 0x00, 0x00}, /* 0x29 848x480-60Hz */
+ {0x0087, 0x47, 0x57, 0x00, 0x17, 0x3f, 856, 480, 0x30, 0x00, 0x00}, /* 0x2a 856x480-38Hzi */
+ {0xc067, 0x48, 0x57, 0x00, 0x17, 0x3f, 856, 480, 0x30, 0x00, 0x00}, /* 0x2b 856x480-60Hz */
+ {0x006f, 0x4d, 0x71, 0x06, 0x15, 0x5f, 768, 576, 0x30, 0x00, 0x00}, /* 0x2c 768x576-56Hz */
+ {0x0067, 0x52, 0x6a, 0x00, 0x1c, 0x1d, 960, 540, 0x30, 0x00, 0x00}, /* 0x2d 960x540 60Hz */
+ {0x0077, 0x53, 0x6b, 0x0b, 0x1d, 0x20, 960, 600, 0x30, 0x00, 0x00}, /* 0x2e 960x600 60Hz */
+ {0x0087, 0x1c, 0x11, 0x00, 0x07, 0x3a, 1280, 1024, 0x30, 0x00, 0x00}, /* 0x2f */
+ {0x0137, 0x1d, 0x19, 0x07, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x30 */
+ {0x0107, 0x1e, 0x1e, 0x00, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x31 */
+ {0x0207, 0x1f, 0x20, 0x00, 0x07, 0x3a, 1280, 1024, 0x00, 0x00, 0x00}, /* 0x32 */
+ {0x0127, 0x54, 0x6d, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x33 1152x864-60Hz */
+ {0x0127, 0x44, 0x19, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x34 1152x864-75Hz */
+ {0x0127, 0x4a, 0x1e, 0x00, 0x1a, 0x29, 1152, 864, 0x30, 0x00, 0x00}, /* 0x35 1152x864-85Hz */
+ {0xffff, 0x00, 0x00, 0x00, 0x00, 0x00, 0, 0, 0, 0x00, 0x00}
+};
+
+static const struct SiS_CRT1Table SiSUSB_CRT1Table[] = {
+ {{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0xbf, 0x1f,
+ 0x9c, 0x8e, 0x8f, 0x96, 0xb9, 0x30, 0x00, 0x00,
+ 0x00}}, /* 0x0 */
+ {{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0x0b, 0x3e,
+ 0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x00,
+ 0x00}}, /* 0x1 */
+ {{0x3d, 0x31, 0x31, 0x81, 0x37, 0x1f, 0x72, 0xf0,
+ 0x58, 0x8c, 0x57, 0x57, 0x73, 0x20, 0x00, 0x05,
+ 0x01}}, /* 0x2 */
+ {{0x4f, 0x3f, 0x3f, 0x93, 0x45, 0x0d, 0x24, 0xf5,
+ 0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x01,
+ 0x01}}, /* 0x3 */
+ {{0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+ 0x9c, 0x8e, 0x8f, 0x96, 0xb9, 0x30, 0x00, 0x05,
+ 0x00}}, /* 0x4 */
+ {{0x5f, 0x4f, 0x4f, 0x83, 0x55, 0x81, 0x0b, 0x3e,
+ 0xe9, 0x8b, 0xdf, 0xe8, 0x0c, 0x00, 0x00, 0x05,
+ 0x00}}, /* 0x5 */
+ {{0x63, 0x4f, 0x4f, 0x87, 0x56, 0x9b, 0x06, 0x3e,
+ 0xe8, 0x8a, 0xdf, 0xe7, 0x07, 0x00, 0x00, 0x01,
+ 0x00}}, /* 0x6 */
+ {{0x64, 0x4f, 0x4f, 0x88, 0x55, 0x9d, 0xf2, 0x1f,
+ 0xe0, 0x83, 0xdf, 0xdf, 0xf3, 0x10, 0x00, 0x01,
+ 0x00}}, /* 0x7 */
+ {{0x63, 0x4f, 0x4f, 0x87, 0x5a, 0x81, 0xfb, 0x1f,
+ 0xe0, 0x83, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x05,
+ 0x00}}, /* 0x8 */
+ {{0x65, 0x4f, 0x4f, 0x89, 0x58, 0x80, 0xfb, 0x1f,
+ 0xe0, 0x83, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x05,
+ 0x61}}, /* 0x9 */
+ {{0x65, 0x4f, 0x4f, 0x89, 0x58, 0x80, 0x01, 0x3e,
+ 0xe0, 0x83, 0xdf, 0xdf, 0x02, 0x00, 0x00, 0x05,
+ 0x61}}, /* 0xa */
+ {{0x67, 0x4f, 0x4f, 0x8b, 0x58, 0x81, 0x0d, 0x3e,
+ 0xe0, 0x83, 0xdf, 0xdf, 0x0e, 0x00, 0x00, 0x05,
+ 0x61}}, /* 0xb */
+ {{0x65, 0x4f, 0x4f, 0x89, 0x57, 0x9f, 0xfb, 0x1f,
+ 0xe6, 0x8a, 0xdf, 0xdf, 0xfc, 0x10, 0x00, 0x01,
+ 0x00}}, /* 0xc */
+ {{0x7b, 0x63, 0x63, 0x9f, 0x6a, 0x93, 0x6f, 0xf0,
+ 0x58, 0x8a, 0x57, 0x57, 0x70, 0x20, 0x00, 0x05,
+ 0x01}}, /* 0xd */
+ {{0x7f, 0x63, 0x63, 0x83, 0x6c, 0x1c, 0x72, 0xf0,
+ 0x58, 0x8c, 0x57, 0x57, 0x73, 0x20, 0x00, 0x06,
+ 0x01}}, /* 0xe */
+ {{0x7d, 0x63, 0x63, 0x81, 0x6e, 0x1d, 0x98, 0xf0,
+ 0x7c, 0x82, 0x57, 0x57, 0x99, 0x00, 0x00, 0x06,
+ 0x01}}, /* 0xf */
+ {{0x7f, 0x63, 0x63, 0x83, 0x69, 0x13, 0x6f, 0xf0,
+ 0x58, 0x8b, 0x57, 0x57, 0x70, 0x20, 0x00, 0x06,
+ 0x01}}, /* 0x10 */
+ {{0x7e, 0x63, 0x63, 0x82, 0x6b, 0x13, 0x75, 0xf0,
+ 0x58, 0x8b, 0x57, 0x57, 0x76, 0x20, 0x00, 0x06,
+ 0x01}}, /* 0x11 */
+ {{0x81, 0x63, 0x63, 0x85, 0x6d, 0x18, 0x7a, 0xf0,
+ 0x58, 0x8b, 0x57, 0x57, 0x7b, 0x20, 0x00, 0x06,
+ 0x61}}, /* 0x12 */
+ {{0x83, 0x63, 0x63, 0x87, 0x6e, 0x19, 0x81, 0xf0,
+ 0x58, 0x8b, 0x57, 0x57, 0x82, 0x20, 0x00, 0x06,
+ 0x61}}, /* 0x13 */
+ {{0x85, 0x63, 0x63, 0x89, 0x6f, 0x1a, 0x91, 0xf0,
+ 0x58, 0x8b, 0x57, 0x57, 0x92, 0x20, 0x00, 0x06,
+ 0x61}}, /* 0x14 */
+ {{0x99, 0x7f, 0x7f, 0x9d, 0x84, 0x1a, 0x96, 0x1f,
+ 0x7f, 0x83, 0x7f, 0x7f, 0x97, 0x10, 0x00, 0x02,
+ 0x00}}, /* 0x15 */
+ {{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf5,
+ 0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
+ 0x01}}, /* 0x16 */
+ {{0xa1, 0x7f, 0x7f, 0x85, 0x86, 0x97, 0x24, 0xf5,
+ 0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
+ 0x01}}, /* 0x17 */
+ {{0x9f, 0x7f, 0x7f, 0x83, 0x85, 0x91, 0x1e, 0xf5,
+ 0x00, 0x83, 0xff, 0xff, 0x1f, 0x10, 0x00, 0x02,
+ 0x01}}, /* 0x18 */
+ {{0xa7, 0x7f, 0x7f, 0x8b, 0x89, 0x95, 0x26, 0xf5,
+ 0x00, 0x83, 0xff, 0xff, 0x27, 0x10, 0x00, 0x02,
+ 0x01}}, /* 0x19 */
+ {{0xa9, 0x7f, 0x7f, 0x8d, 0x8c, 0x9a, 0x2c, 0xf5,
+ 0x00, 0x83, 0xff, 0xff, 0x2d, 0x14, 0x00, 0x02,
+ 0x62}}, /* 0x1a */
+ {{0xab, 0x7f, 0x7f, 0x8f, 0x8d, 0x9b, 0x35, 0xf5,
+ 0x00, 0x83, 0xff, 0xff, 0x36, 0x14, 0x00, 0x02,
+ 0x62}}, /* 0x1b */
+ {{0xcf, 0x9f, 0x9f, 0x93, 0xb2, 0x01, 0x14, 0xba,
+ 0x00, 0x83, 0xff, 0xff, 0x15, 0x00, 0x00, 0x03,
+ 0x00}}, /* 0x1c */
+ {{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x28, 0x5a,
+ 0x00, 0x83, 0xff, 0xff, 0x29, 0x09, 0x00, 0x07,
+ 0x01}}, /* 0x1d */
+ {{0xce, 0x9f, 0x9f, 0x92, 0xa5, 0x17, 0x28, 0x5a,
+ 0x00, 0x83, 0xff, 0xff, 0x29, 0x09, 0x00, 0x07,
+ 0x01}}, /* 0x1e */
+ {{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0x2e, 0x5a,
+ 0x00, 0x83, 0xff, 0xff, 0x2f, 0x09, 0x00, 0x07,
+ 0x01}}, /* 0x1f */
+ {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+ 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+ 0x00}}, /* 0x20 */
+ {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+ 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+ 0x00}}, /* 0x21 */
+ {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+ 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+ 0x00}}, /* 0x22 */
+ {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+ 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+ 0x00}}, /* 0x23 */
+ {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+ 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+ 0x00}}, /* 0x24 */
+ {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+ 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+ 0x00}}, /* 0x25 */
+ {{0x09, 0xc7, 0xc7, 0x8d, 0xd3, 0x0b, 0xe0, 0x10,
+ 0xb0, 0x83, 0xaf, 0xaf, 0xe1, 0x2f, 0x01, 0x04,
+ 0x00}}, /* 0x26 */
+ {{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
+ 0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
+ 0x00}}, /* 0x27 */
+ {{0x43, 0xef, 0xef, 0x87, 0x06, 0x00, 0xd4, 0x1f,
+ 0xa0, 0x83, 0x9f, 0x9f, 0xd5, 0x1f, 0x41, 0x05,
+ 0x63}}, /* 0x28 */
+ {{0x45, 0xef, 0xef, 0x89, 0x07, 0x01, 0xd9, 0x1f,
+ 0xa0, 0x83, 0x9f, 0x9f, 0xda, 0x1f, 0x41, 0x05,
+ 0x63}}, /* 0x29 */
+ {{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
+ 0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
+ 0x00}}, /* 0x2a */
+ {{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
+ 0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
+ 0x00}}, /* 0x2b */
+ {{0x40, 0xef, 0xef, 0x84, 0x03, 0x1d, 0xda, 0x1f,
+ 0xa0, 0x83, 0x9f, 0x9f, 0xdb, 0x1f, 0x41, 0x01,
+ 0x00}}, /* 0x2c */
+ {{0x59, 0xff, 0xff, 0x9d, 0x17, 0x13, 0x33, 0xba,
+ 0x00, 0x83, 0xff, 0xff, 0x34, 0x0f, 0x41, 0x05,
+ 0x44}}, /* 0x2d */
+ {{0x5b, 0xff, 0xff, 0x9f, 0x18, 0x14, 0x38, 0xba,
+ 0x00, 0x83, 0xff, 0xff, 0x39, 0x0f, 0x41, 0x05,
+ 0x44}}, /* 0x2e */
+ {{0x5b, 0xff, 0xff, 0x9f, 0x18, 0x14, 0x3d, 0xba,
+ 0x00, 0x83, 0xff, 0xff, 0x3e, 0x0f, 0x41, 0x05,
+ 0x44}}, /* 0x2f */
+ {{0x5d, 0xff, 0xff, 0x81, 0x19, 0x95, 0x41, 0xba,
+ 0x00, 0x84, 0xff, 0xff, 0x42, 0x0f, 0x41, 0x05,
+ 0x44}}, /* 0x30 */
+ {{0x55, 0xff, 0xff, 0x99, 0x0d, 0x0c, 0x3e, 0xba,
+ 0x00, 0x84, 0xff, 0xff, 0x3f, 0x0f, 0x41, 0x05,
+ 0x00}}, /* 0x31 */
+ {{0x7f, 0x63, 0x63, 0x83, 0x6c, 0x1c, 0x72, 0xba,
+ 0x27, 0x8b, 0xdf, 0xdf, 0x73, 0x00, 0x00, 0x06,
+ 0x01}}, /* 0x32 */
+ {{0x7f, 0x63, 0x63, 0x83, 0x69, 0x13, 0x6f, 0xba,
+ 0x26, 0x89, 0xdf, 0xdf, 0x6f, 0x00, 0x00, 0x06,
+ 0x01}}, /* 0x33 */
+ {{0x7f, 0x63, 0x63, 0x82, 0x6b, 0x13, 0x75, 0xba,
+ 0x29, 0x8c, 0xdf, 0xdf, 0x75, 0x00, 0x00, 0x06,
+ 0x01}}, /* 0x34 */
+ {{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf1,
+ 0xaf, 0x85, 0x3f, 0x3f, 0x25, 0x30, 0x00, 0x02,
+ 0x01}}, /* 0x35 */
+ {{0x9f, 0x7f, 0x7f, 0x83, 0x85, 0x91, 0x1e, 0xf1,
+ 0xad, 0x81, 0x3f, 0x3f, 0x1f, 0x30, 0x00, 0x02,
+ 0x01}}, /* 0x36 */
+ {{0xa7, 0x7f, 0x7f, 0x88, 0x89, 0x95, 0x26, 0xf1,
+ 0xb1, 0x85, 0x3f, 0x3f, 0x27, 0x30, 0x00, 0x02,
+ 0x01}}, /* 0x37 */
+ {{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x28, 0xc4,
+ 0x7a, 0x8e, 0xcf, 0xcf, 0x29, 0x21, 0x00, 0x07,
+ 0x01}}, /* 0x38 */
+ {{0xce, 0x9f, 0x9f, 0x92, 0xa5, 0x17, 0x28, 0xd4,
+ 0x7a, 0x8e, 0xcf, 0xcf, 0x29, 0x21, 0x00, 0x07,
+ 0x01}}, /* 0x39 */
+ {{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0x2e, 0xd4,
+ 0x7d, 0x81, 0xcf, 0xcf, 0x2f, 0x21, 0x00, 0x07,
+ 0x01}}, /* 0x3a */
+ {{0xdc, 0x9f, 0x9f, 0x80, 0xaf, 0x9d, 0xe6, 0xff,
+ 0xc0, 0x83, 0xbf, 0xbf, 0xe7, 0x10, 0x00, 0x07,
+ 0x01}}, /* 0x3b */
+ {{0x6b, 0x59, 0x59, 0x8f, 0x5e, 0x8c, 0x0b, 0x3e,
+ 0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x05,
+ 0x00}}, /* 0x3c */
+ {{0x6d, 0x59, 0x59, 0x91, 0x60, 0x89, 0x53, 0xf0,
+ 0x41, 0x84, 0x3f, 0x3f, 0x54, 0x00, 0x00, 0x05,
+ 0x41}}, /* 0x3d */
+ {{0x86, 0x6a, 0x6a, 0x8a, 0x74, 0x06, 0x8c, 0x15,
+ 0x4f, 0x83, 0xef, 0xef, 0x8d, 0x30, 0x00, 0x02,
+ 0x00}}, /* 0x3e */
+ {{0x81, 0x6a, 0x6a, 0x85, 0x70, 0x00, 0x0f, 0x3e,
+ 0xeb, 0x8e, 0xdf, 0xdf, 0x10, 0x00, 0x00, 0x02,
+ 0x00}}, /* 0x3f */
+ {{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x1e, 0xf1,
+ 0xae, 0x85, 0x57, 0x57, 0x1f, 0x30, 0x00, 0x02,
+ 0x01}}, /* 0x40 */
+ {{0xa3, 0x7f, 0x7f, 0x87, 0x86, 0x97, 0x24, 0xf5,
+ 0x02, 0x88, 0xff, 0xff, 0x25, 0x10, 0x00, 0x02,
+ 0x01}}, /* 0x41 */
+ {{0xce, 0x9f, 0x9f, 0x92, 0xa9, 0x17, 0x20, 0xf5,
+ 0x03, 0x88, 0xff, 0xff, 0x21, 0x10, 0x00, 0x07,
+ 0x01}}, /* 0x42 */
+ {{0xe6, 0xae, 0xae, 0x8a, 0xbd, 0x90, 0x3d, 0x10,
+ 0x1a, 0x8d, 0x19, 0x19, 0x3e, 0x2f, 0x00, 0x03,
+ 0x00}}, /* 0x43 */
+ {{0xc3, 0x8f, 0x8f, 0x87, 0x9b, 0x0b, 0x82, 0xef,
+ 0x60, 0x83, 0x5f, 0x5f, 0x83, 0x10, 0x00, 0x07,
+ 0x01}}, /* 0x44 */
+ {{0x86, 0x69, 0x69, 0x8A, 0x74, 0x06, 0x8C, 0x15,
+ 0x4F, 0x83, 0xEF, 0xEF, 0x8D, 0x30, 0x00, 0x02,
+ 0x00}}, /* 0x45 */
+ {{0x83, 0x69, 0x69, 0x87, 0x6f, 0x1d, 0x03, 0x3E,
+ 0xE5, 0x8d, 0xDF, 0xe4, 0x04, 0x00, 0x00, 0x06,
+ 0x00}}, /* 0x46 */
+ {{0x86, 0x6A, 0x6A, 0x8A, 0x74, 0x06, 0x8C, 0x15,
+ 0x4F, 0x83, 0xEF, 0xEF, 0x8D, 0x30, 0x00, 0x02,
+ 0x00}}, /* 0x47 */
+ {{0x81, 0x6A, 0x6A, 0x85, 0x70, 0x00, 0x0F, 0x3E,
+ 0xEB, 0x8E, 0xDF, 0xDF, 0x10, 0x00, 0x00, 0x02,
+ 0x00}}, /* 0x48 */
+ {{0xdd, 0xa9, 0xa9, 0x81, 0xb4, 0x97, 0x26, 0xfd,
+ 0x01, 0x8d, 0xff, 0x00, 0x27, 0x10, 0x00, 0x03,
+ 0x01}}, /* 0x49 */
+ {{0xd9, 0x8f, 0x8f, 0x9d, 0xba, 0x0a, 0x8a, 0xff,
+ 0x60, 0x8b, 0x5f, 0x5f, 0x8b, 0x10, 0x00, 0x03,
+ 0x01}}, /* 0x4a */
+ {{0xea, 0xae, 0xae, 0x8e, 0xba, 0x82, 0x40, 0x10,
+ 0x1b, 0x87, 0x19, 0x1a, 0x41, 0x0f, 0x00, 0x03,
+ 0x00}}, /* 0x4b */
+ {{0xd3, 0x9f, 0x9f, 0x97, 0xab, 0x1f, 0xf1, 0xff,
+ 0xc0, 0x83, 0xbf, 0xbf, 0xf2, 0x10, 0x00, 0x07,
+ 0x01}}, /* 0x4c */
+ {{0x75, 0x5f, 0x5f, 0x99, 0x66, 0x90, 0x53, 0xf0,
+ 0x41, 0x84, 0x3f, 0x3f, 0x54, 0x00, 0x00, 0x05,
+ 0x41}},
+ {{0x2d, 0x27, 0x28, 0x90, 0x2c, 0x80, 0x0b, 0x3e,
+ 0xe9, 0x8b, 0xdf, 0xe7, 0x04, 0x00, 0x00, 0x00,
+ 0x00}}, /* 0x4e */
+ {{0xcd, 0x9f, 0x9f, 0x91, 0xab, 0x1c, 0x3a, 0xff,
+ 0x20, 0x83, 0x1f, 0x1f, 0x3b, 0x10, 0x00, 0x07,
+ 0x21}}, /* 0x4f */
+ {{0x15, 0xd1, 0xd1, 0x99, 0xe2, 0x19, 0x3d, 0x10,
+ 0x1a, 0x8d, 0x19, 0x19, 0x3e, 0x2f, 0x01, 0x0c,
+ 0x20}}, /* 0x50 */
+ {{0x0e, 0xef, 0xef, 0x92, 0xfe, 0x03, 0x30, 0xf0,
+ 0x1e, 0x83, 0x1b, 0x1c, 0x31, 0x00, 0x01, 0x00,
+ 0x61}}, /* 0x51 */
+ {{0x85, 0x77, 0x77, 0x89, 0x7d, 0x01, 0x31, 0xf0,
+ 0x1e, 0x84, 0x1b, 0x1c, 0x32, 0x00, 0x00, 0x02,
+ 0x41}}, /* 0x52 */
+ {{0x87, 0x77, 0x77, 0x8b, 0x81, 0x0b, 0x68, 0xf0,
+ 0x5a, 0x80, 0x57, 0x57, 0x69, 0x00, 0x00, 0x02,
+ 0x01}}, /* 0x53 */
+ {{0xcd, 0x8f, 0x8f, 0x91, 0x9b, 0x1b, 0x7a, 0xff,
+ 0x64, 0x8c, 0x5f, 0x62, 0x7b, 0x10, 0x00, 0x07,
+ 0x41}} /* 0x54 */
+};
+
+static const struct SiS_VCLKData SiSUSB_VCLKData[] = {
+ {0x1b, 0xe1, 25}, /* 0x00 */
+ {0x4e, 0xe4, 28}, /* 0x01 */
+ {0x57, 0xe4, 31}, /* 0x02 */
+ {0xc3, 0xc8, 36}, /* 0x03 */
+ {0x42, 0xe2, 40}, /* 0x04 */
+ {0xfe, 0xcd, 43}, /* 0x05 */
+ {0x5d, 0xc4, 44}, /* 0x06 */
+ {0x52, 0xe2, 49}, /* 0x07 */
+ {0x53, 0xe2, 50}, /* 0x08 */
+ {0x74, 0x67, 52}, /* 0x09 */
+ {0x6d, 0x66, 56}, /* 0x0a */
+ {0x5a, 0x64, 65}, /* 0x0b */
+ {0x46, 0x44, 67}, /* 0x0c */
+ {0xb1, 0x46, 68}, /* 0x0d */
+ {0xd3, 0x4a, 72}, /* 0x0e */
+ {0x29, 0x61, 75}, /* 0x0f */
+ {0x6e, 0x46, 76}, /* 0x10 */
+ {0x2b, 0x61, 78}, /* 0x11 */
+ {0x31, 0x42, 79}, /* 0x12 */
+ {0xab, 0x44, 83}, /* 0x13 */
+ {0x46, 0x25, 84}, /* 0x14 */
+ {0x78, 0x29, 86}, /* 0x15 */
+ {0x62, 0x44, 94}, /* 0x16 */
+ {0x2b, 0x41, 104}, /* 0x17 */
+ {0x3a, 0x23, 105}, /* 0x18 */
+ {0x70, 0x44, 108}, /* 0x19 */
+ {0x3c, 0x23, 109}, /* 0x1a */
+ {0x5e, 0x43, 113}, /* 0x1b */
+ {0xbc, 0x44, 116}, /* 0x1c */
+ {0xe0, 0x46, 132}, /* 0x1d */
+ {0x54, 0x42, 135}, /* 0x1e */
+ {0xea, 0x2a, 139}, /* 0x1f */
+ {0x41, 0x22, 157}, /* 0x20 */
+ {0x70, 0x24, 162}, /* 0x21 */
+ {0x30, 0x21, 175}, /* 0x22 */
+ {0x4e, 0x22, 189}, /* 0x23 */
+ {0xde, 0x26, 194}, /* 0x24 */
+ {0x62, 0x06, 202}, /* 0x25 */
+ {0x3f, 0x03, 229}, /* 0x26 */
+ {0xb8, 0x06, 234}, /* 0x27 */
+ {0x34, 0x02, 253}, /* 0x28 */
+ {0x58, 0x04, 255}, /* 0x29 */
+ {0x24, 0x01, 265}, /* 0x2a */
+ {0x9b, 0x02, 267}, /* 0x2b */
+ {0x70, 0x05, 270}, /* 0x2c */
+ {0x25, 0x01, 272}, /* 0x2d */
+ {0x9c, 0x02, 277}, /* 0x2e */
+ {0x27, 0x01, 286}, /* 0x2f */
+ {0x3c, 0x02, 291}, /* 0x30 */
+ {0xef, 0x0a, 292}, /* 0x31 */
+ {0xf6, 0x0a, 310}, /* 0x32 */
+ {0x95, 0x01, 315}, /* 0x33 */
+ {0xf0, 0x09, 324}, /* 0x34 */
+ {0xfe, 0x0a, 331}, /* 0x35 */
+ {0xf3, 0x09, 332}, /* 0x36 */
+ {0xea, 0x08, 340}, /* 0x37 */
+ {0xe8, 0x07, 376}, /* 0x38 */
+ {0xde, 0x06, 389}, /* 0x39 */
+ {0x52, 0x2a, 54}, /* 0x3a 301 TV */
+ {0x52, 0x6a, 27}, /* 0x3b 301 TV */
+ {0x62, 0x24, 70}, /* 0x3c 301 TV */
+ {0x62, 0x64, 70}, /* 0x3d 301 TV */
+ {0xa8, 0x4c, 30}, /* 0x3e 301 TV */
+ {0x20, 0x26, 33}, /* 0x3f 301 TV */
+ {0x31, 0xc2, 39}, /* 0x40 */
+ {0x60, 0x36, 30}, /* 0x41 Chrontel */
+ {0x40, 0x4a, 28}, /* 0x42 Chrontel */
+ {0x9f, 0x46, 44}, /* 0x43 Chrontel */
+ {0x97, 0x2c, 26}, /* 0x44 */
+ {0x44, 0xe4, 25}, /* 0x45 Chrontel */
+ {0x7e, 0x32, 47}, /* 0x46 Chrontel */
+ {0x8a, 0x24, 31}, /* 0x47 Chrontel */
+ {0x97, 0x2c, 26}, /* 0x48 Chrontel */
+ {0xce, 0x3c, 39}, /* 0x49 */
+ {0x52, 0x4a, 36}, /* 0x4a Chrontel */
+ {0x34, 0x61, 95}, /* 0x4b */
+ {0x78, 0x27, 108}, /* 0x4c - was 102 */
+ {0x66, 0x43, 123}, /* 0x4d Modes 0x26-0x28 (1400x1050) */
+ {0x41, 0x4e, 21}, /* 0x4e */
+ {0xa1, 0x4a, 29}, /* 0x4f Chrontel */
+ {0x19, 0x42, 42}, /* 0x50 */
+ {0x54, 0x46, 58}, /* 0x51 Chrontel */
+ {0x25, 0x42, 61}, /* 0x52 */
+ {0x44, 0x44, 66}, /* 0x53 Chrontel */
+ {0x3a, 0x62, 70}, /* 0x54 Chrontel */
+ {0x62, 0xc6, 34}, /* 0x55 848x480-60 */
+ {0x6a, 0xc6, 37}, /* 0x56 848x480-75 - TEMP */
+ {0xbf, 0xc8, 35}, /* 0x57 856x480-38i,60 */
+ {0x30, 0x23, 88}, /* 0x58 1360x768-62 (is 60Hz!) */
+ {0x52, 0x07, 149}, /* 0x59 1280x960-85 */
+ {0x56, 0x07, 156}, /* 0x5a 1400x1050-75 */
+ {0x70, 0x29, 81}, /* 0x5b 1280x768 LCD */
+ {0x45, 0x25, 83}, /* 0x5c 1280x800 */
+ {0x70, 0x0a, 147}, /* 0x5d 1680x1050 */
+ {0x70, 0x24, 162}, /* 0x5e 1600x1200 */
+ {0x5a, 0x64, 65}, /* 0x5f 1280x720 - temp */
+ {0x63, 0x46, 68}, /* 0x60 1280x768_2 */
+ {0x31, 0x42, 79}, /* 0x61 1280x768_3 - temp */
+ {0, 0, 0}, /* 0x62 - custom (will be filled out at run-time) */
+ {0x5a, 0x64, 65}, /* 0x63 1280x720 (LCD LVDS) */
+ {0x70, 0x28, 90}, /* 0x64 1152x864@60 */
+ {0x41, 0xc4, 32}, /* 0x65 848x480@60 */
+ {0x5c, 0xc6, 32}, /* 0x66 856x480@60 */
+ {0x76, 0xe7, 27}, /* 0x67 720x480@60 */
+ {0x5f, 0xc6, 33}, /* 0x68 720/768x576@60 */
+ {0x52, 0x27, 75}, /* 0x69 1920x1080i 60Hz interlaced */
+ {0x7c, 0x6b, 38}, /* 0x6a 960x540@60 */
+ {0xe3, 0x56, 41}, /* 0x6b 960x600@60 */
+ {0x45, 0x25, 83}, /* 0x6c 1280x800 */
+ {0x70, 0x28, 90}, /* 0x6d 1152x864@60 */
+ {0x15, 0xe1, 20}, /* 0x6e 640x400@60 (fake, not actually used) */
+ {0x5f, 0xc6, 33}, /* 0x6f 720x576@60 */
+ {0x37, 0x5a, 10}, /* 0x70 320x200@60 (fake, not actually used) */
+ {0x2b, 0xc2, 35} /* 0x71 768@576@60 */
+};
+
+#endif
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index 6ca9111..29fe577 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/nls.h>
#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
/* Internal Register Set Addresses & Default Values acc. to DS00001692C */
@@ -26,10 +27,6 @@
#define USB251XB_ADDR_PRODUCT_ID_LSB 0x02
#define USB251XB_ADDR_PRODUCT_ID_MSB 0x03
-#define USB251XB_DEF_PRODUCT_ID_12 0x2512 /* USB2512B/12Bi */
-#define USB251XB_DEF_PRODUCT_ID_13 0x2513 /* USB2513B/13Bi */
-#define USB251XB_DEF_PRODUCT_ID_14 0x2514 /* USB2514B/14Bi */
-#define USB251XB_DEF_PRODUCT_ID_17 0x2517 /* USB2517/17i */
#define USB251XB_ADDR_DEVICE_ID_LSB 0x04
#define USB251XB_ADDR_DEVICE_ID_MSB 0x05
@@ -74,7 +71,6 @@
#define USB251XB_ADDR_PRODUCT_STRING_LEN 0x14
#define USB251XB_ADDR_PRODUCT_STRING 0x54
-#define USB251XB_DEF_PRODUCT_STRING "USB251xB/xBi/7i"
#define USB251XB_ADDR_SERIAL_STRING_LEN 0x15
#define USB251XB_ADDR_SERIAL_STRING 0x92
@@ -116,6 +112,7 @@
struct usb251xb {
struct device *dev;
struct i2c_client *i2c;
+ struct regulator *vdd;
u8 skip_config;
struct gpio_desc *gpio_reset;
u16 vendor_id;
@@ -159,6 +156,14 @@
char product_str[USB251XB_STRING_BUFSIZE / 2]; /* ASCII string */
};
+static const struct usb251xb_data usb2422_data = {
+ .product_id = 0x2422,
+ .port_cnt = 2,
+ .led_support = false,
+ .bat_support = true,
+ .product_str = "USB2422",
+};
+
static const struct usb251xb_data usb2512b_data = {
.product_id = 0x2512,
.port_cnt = 2,
@@ -261,20 +266,19 @@
}
#endif
-static void usb251xb_reset(struct usb251xb *hub, int state)
+static void usb251xb_reset(struct usb251xb *hub)
{
if (!hub->gpio_reset)
return;
i2c_lock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT);
- gpiod_set_value_cansleep(hub->gpio_reset, state);
+ gpiod_set_value_cansleep(hub->gpio_reset, 1);
+ usleep_range(1, 10); /* >=1us RESET_N asserted */
+ gpiod_set_value_cansleep(hub->gpio_reset, 0);
/* wait for hub recovery/stabilization */
- if (!state)
- usleep_range(500, 750); /* >=500us at power on */
- else
- usleep_range(1, 10); /* >=1us at power down */
+ usleep_range(500, 750); /* >=500us after RESET_N deasserted */
i2c_unlock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT);
}
@@ -292,7 +296,7 @@
i2c_wb[0] = 0x01;
i2c_wb[1] = USB251XB_STATUS_COMMAND_ATTACH;
- usb251xb_reset(hub, 0);
+ usb251xb_reset(hub);
err = i2c_smbus_write_i2c_block_data(hub->i2c,
USB251XB_ADDR_STATUS_COMMAND, 2, i2c_wb);
@@ -342,7 +346,7 @@
i2c_wb[USB251XB_ADDR_PORT_MAP_7] = hub->port_map7;
i2c_wb[USB251XB_ADDR_STATUS_COMMAND] = USB251XB_STATUS_COMMAND_ATTACH;
- usb251xb_reset(hub, 0);
+ usb251xb_reset(hub);
/* write registers */
for (i = 0; i < (USB251XB_I2C_REG_SZ / USB251XB_I2C_WRITE_SZ); i++) {
@@ -593,6 +597,9 @@
static const struct of_device_id usb251xb_of_match[] = {
{
+ .compatible = "microchip,usb2422",
+ .data = &usb2422_data,
+ }, {
.compatible = "microchip,usb2512b",
.data = &usb2512b_data,
}, {
@@ -629,6 +636,13 @@
}
#endif /* CONFIG_OF */
+static void usb251xb_regulator_disable_action(void *data)
+{
+ struct usb251xb *hub = data;
+
+ regulator_disable(hub->vdd);
+}
+
static int usb251xb_probe(struct usb251xb *hub)
{
struct device *dev = hub->dev;
@@ -665,6 +679,19 @@
if (err)
return err;
+ hub->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(hub->vdd))
+ return PTR_ERR(hub->vdd);
+
+ err = regulator_enable(hub->vdd);
+ if (err)
+ return err;
+
+ err = devm_add_action_or_reset(dev,
+ usb251xb_regulator_disable_action, hub);
+ if (err)
+ return err;
+
err = usb251xb_connect(hub);
if (err) {
dev_err(dev, "Failed to connect hub (%d)\n", err);
@@ -692,7 +719,31 @@
return usb251xb_probe(hub);
}
+static int __maybe_unused usb251xb_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct usb251xb *hub = i2c_get_clientdata(client);
+
+ return regulator_disable(hub->vdd);
+}
+
+static int __maybe_unused usb251xb_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct usb251xb *hub = i2c_get_clientdata(client);
+ int err;
+
+ err = regulator_enable(hub->vdd);
+ if (err)
+ return err;
+
+ return usb251xb_connect(hub);
+}
+
+static SIMPLE_DEV_PM_OPS(usb251xb_pm_ops, usb251xb_suspend, usb251xb_resume);
+
static const struct i2c_device_id usb251xb_id[] = {
+ { "usb2422", 0 },
{ "usb2512b", 0 },
{ "usb2512bi", 0 },
{ "usb2513b", 0 },
@@ -709,6 +760,7 @@
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(usb251xb_of_match),
+ .pm = &usb251xb_pm_ops,
},
.probe = usb251xb_i2c_probe,
.id_table = usb251xb_id,
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index 72f39a9..48099c6 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -7,11 +7,10 @@
#include <linux/clk.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/platform_data/usb3503.h>
#include <linux/regmap.h>
@@ -47,19 +46,19 @@
struct device *dev;
struct clk *clk;
u8 port_off_mask;
- int gpio_intn;
- int gpio_reset;
- int gpio_connect;
+ struct gpio_desc *intn;
+ struct gpio_desc *reset;
+ struct gpio_desc *connect;
bool secondary_ref_clk;
};
static int usb3503_reset(struct usb3503 *hub, int state)
{
- if (!state && gpio_is_valid(hub->gpio_connect))
- gpio_set_value_cansleep(hub->gpio_connect, 0);
+ if (!state && hub->connect)
+ gpiod_set_value_cansleep(hub->connect, 0);
- if (gpio_is_valid(hub->gpio_reset))
- gpio_set_value_cansleep(hub->gpio_reset, state);
+ if (hub->reset)
+ gpiod_set_value_cansleep(hub->reset, !state);
/* Wait T_HUBINIT == 4ms for hub logic to stabilize */
if (state)
@@ -115,8 +114,8 @@
}
}
- if (gpio_is_valid(hub->gpio_connect))
- gpio_set_value_cansleep(hub->gpio_connect, 1);
+ if (hub->connect)
+ gpiod_set_value_cansleep(hub->connect, 1);
hub->mode = USB3503_MODE_HUB;
dev_info(dev, "switched to HUB mode\n");
@@ -163,13 +162,11 @@
int err;
u32 mode = USB3503_MODE_HUB;
const u32 *property;
+ enum gpiod_flags flags;
int len;
if (pdata) {
hub->port_off_mask = pdata->port_off_mask;
- hub->gpio_intn = pdata->gpio_intn;
- hub->gpio_connect = pdata->gpio_connect;
- hub->gpio_reset = pdata->gpio_reset;
hub->mode = pdata->initial_mode;
} else if (np) {
u32 rate = 0;
@@ -230,59 +227,38 @@
}
}
- hub->gpio_intn = of_get_named_gpio(np, "intn-gpios", 0);
- if (hub->gpio_intn == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- hub->gpio_connect = of_get_named_gpio(np, "connect-gpios", 0);
- if (hub->gpio_connect == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- hub->gpio_reset = of_get_named_gpio(np, "reset-gpios", 0);
- if (hub->gpio_reset == -EPROBE_DEFER)
- return -EPROBE_DEFER;
of_property_read_u32(np, "initial-mode", &mode);
hub->mode = mode;
}
- if (hub->port_off_mask && !hub->regmap)
- dev_err(dev, "Ports disabled with no control interface\n");
+ if (hub->secondary_ref_clk)
+ flags = GPIOD_OUT_LOW;
+ else
+ flags = GPIOD_OUT_HIGH;
+ hub->intn = devm_gpiod_get_optional(dev, "intn", flags);
+ if (IS_ERR(hub->intn))
+ return PTR_ERR(hub->intn);
+ if (hub->intn)
+ gpiod_set_consumer_name(hub->intn, "usb3503 intn");
- if (gpio_is_valid(hub->gpio_intn)) {
- int val = hub->secondary_ref_clk ? GPIOF_OUT_INIT_LOW :
- GPIOF_OUT_INIT_HIGH;
- err = devm_gpio_request_one(dev, hub->gpio_intn, val,
- "usb3503 intn");
- if (err) {
- dev_err(dev,
- "unable to request GPIO %d as interrupt pin (%d)\n",
- hub->gpio_intn, err);
- return err;
- }
- }
+ hub->connect = devm_gpiod_get_optional(dev, "connect", GPIOD_OUT_LOW);
+ if (IS_ERR(hub->connect))
+ return PTR_ERR(hub->connect);
+ if (hub->connect)
+ gpiod_set_consumer_name(hub->connect, "usb3503 connect");
- if (gpio_is_valid(hub->gpio_connect)) {
- err = devm_gpio_request_one(dev, hub->gpio_connect,
- GPIOF_OUT_INIT_LOW, "usb3503 connect");
- if (err) {
- dev_err(dev,
- "unable to request GPIO %d as connect pin (%d)\n",
- hub->gpio_connect, err);
- return err;
- }
- }
-
- if (gpio_is_valid(hub->gpio_reset)) {
- err = devm_gpio_request_one(dev, hub->gpio_reset,
- GPIOF_OUT_INIT_LOW, "usb3503 reset");
+ hub->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(hub->reset))
+ return PTR_ERR(hub->reset);
+ if (hub->reset) {
/* Datasheet defines a hardware reset to be at least 100us */
usleep_range(100, 10000);
- if (err) {
- dev_err(dev,
- "unable to request GPIO %d as reset pin (%d)\n",
- hub->gpio_reset, err);
- return err;
- }
+ gpiod_set_consumer_name(hub->reset, "usb3503 reset");
}
+ if (hub->port_off_mask && !hub->regmap)
+ dev_err(dev, "Ports disabled with no control interface\n");
+
usb3503_switch_mode(hub, hub->mode);
dev_info(dev, "%s: probed in %s mode\n", __func__,
@@ -346,8 +322,7 @@
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int usb3503_suspend(struct usb3503 *hub)
+static int __maybe_unused usb3503_suspend(struct usb3503 *hub)
{
usb3503_switch_mode(hub, USB3503_MODE_STANDBY);
clk_disable_unprepare(hub->clk);
@@ -355,7 +330,7 @@
return 0;
}
-static int usb3503_resume(struct usb3503 *hub)
+static int __maybe_unused usb3503_resume(struct usb3503 *hub)
{
clk_prepare_enable(hub->clk);
usb3503_switch_mode(hub, hub->mode);
@@ -363,30 +338,29 @@
return 0;
}
-static int usb3503_i2c_suspend(struct device *dev)
+static int __maybe_unused usb3503_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
return usb3503_suspend(i2c_get_clientdata(client));
}
-static int usb3503_i2c_resume(struct device *dev)
+static int __maybe_unused usb3503_i2c_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
return usb3503_resume(i2c_get_clientdata(client));
}
-static int usb3503_platform_suspend(struct device *dev)
+static int __maybe_unused usb3503_platform_suspend(struct device *dev)
{
return usb3503_suspend(dev_get_drvdata(dev));
}
-static int usb3503_platform_resume(struct device *dev)
+static int __maybe_unused usb3503_platform_resume(struct device *dev)
{
return usb3503_resume(dev_get_drvdata(dev));
}
-#endif
static SIMPLE_DEV_PM_OPS(usb3503_i2c_pm_ops, usb3503_i2c_suspend,
usb3503_i2c_resume);
@@ -412,7 +386,7 @@
static struct i2c_driver usb3503_i2c_driver = {
.driver = {
.name = USB3503_I2C_NAME,
- .pm = &usb3503_i2c_pm_ops,
+ .pm = pm_ptr(&usb3503_i2c_pm_ops),
.of_match_table = of_match_ptr(usb3503_of_match),
},
.probe = usb3503_i2c_probe,
@@ -424,7 +398,7 @@
.driver = {
.name = USB3503_I2C_NAME,
.of_match_table = of_match_ptr(usb3503_of_match),
- .pm = &usb3503_platform_pm_ops,
+ .pm = pm_ptr(&usb3503_platform_pm_ops),
},
.probe = usb3503_platform_probe,
.remove = usb3503_platform_remove,
diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c
index 1b4de65..2142af9 100644
--- a/drivers/usb/misc/usb4604.c
+++ b/drivers/usb/misc/usb4604.c
@@ -112,8 +112,7 @@
return usb4604_probe(hub);
}
-#ifdef CONFIG_PM_SLEEP
-static int usb4604_i2c_suspend(struct device *dev)
+static int __maybe_unused usb4604_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct usb4604 *hub = i2c_get_clientdata(client);
@@ -123,7 +122,7 @@
return 0;
}
-static int usb4604_i2c_resume(struct device *dev)
+static int __maybe_unused usb4604_i2c_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct usb4604 *hub = i2c_get_clientdata(client);
@@ -132,7 +131,6 @@
return 0;
}
-#endif
static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
usb4604_i2c_resume);
@@ -154,7 +152,7 @@
static struct i2c_driver usb4604_i2c_driver = {
.driver = {
.name = "usb4604",
- .pm = &usb4604_i2c_pm_ops,
+ .pm = pm_ptr(&usb4604_i2c_pm_ops),
.of_match_table = of_match_ptr(usb4604_of_match),
},
.probe = usb4604_i2c_probe,
diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h
index 4bf7773..1584efb 100644
--- a/drivers/usb/misc/usb_u132.h
+++ b/drivers/usb/misc/usb_u132.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Common Header File for the Elan Digital Systems U132 adapter
* this file should be included by both the "ftdi-u132" and
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 61e9e98..bb546f6 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -187,7 +187,6 @@
break;
default:
return -ENOTTY;
- break;
}
return 0;
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index bae8889..150090e 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -182,7 +182,7 @@
case USB_ENDPOINT_XFER_ISOC:
if (dev->info->iso)
endpoint_update(edi, &iso_in, &iso_out, e);
- /* FALLTHROUGH */
+ fallthrough;
default:
continue;
}
@@ -364,7 +364,7 @@
switch (pattern) {
default:
- /* FALLTHROUGH */
+ fallthrough;
case 0:
memset(buf, 0, len);
break;
@@ -681,7 +681,7 @@
return dev->buf[0];
case 0:
retval = -ERANGE;
- /* FALLTHROUGH */
+ fallthrough;
default:
return retval;
}
@@ -1951,7 +1951,7 @@
dev_err(&ctx->dev->intf->dev,
"resubmit err %d\n",
status);
- /* FALLTHROUGH */
+ fallthrough;
case -ENODEV: /* disconnected */
case -ESHUTDOWN: /* endpoint disabled */
ctx->submit_error = 1;
@@ -2043,7 +2043,7 @@
unsigned i;
unsigned long packets = 0;
int status = 0;
- struct urb *urbs[MAX_SGLEN];
+ struct urb **urbs;
if (!param->sglen || param->iterations > UINT_MAX / param->sglen)
return -EINVAL;
@@ -2051,6 +2051,10 @@
if (param->sglen > MAX_SGLEN)
return -EINVAL;
+ urbs = kcalloc(param->sglen, sizeof(*urbs), GFP_KERNEL);
+ if (!urbs)
+ return -ENOMEM;
+
memset(&context, 0, sizeof(context));
context.count = param->iterations * param->sglen;
context.dev = dev;
@@ -2137,6 +2141,8 @@
else if (context.errors >
(context.is_iso ? context.packet_count / 10 : 0))
status = -EIO;
+
+ kfree(urbs);
return status;
fail:
@@ -2144,6 +2150,8 @@
if (urbs[i])
simple_free_urb(urbs[i]);
}
+
+ kfree(urbs);
return status;
}
diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c
index 08b72bb..6c3d760 100644
--- a/drivers/usb/misc/yurex.c
+++ b/drivers/usb/misc/yurex.c
@@ -96,15 +96,13 @@
if (dev->cntl_urb) {
usb_kill_urb(dev->cntl_urb);
kfree(dev->cntl_req);
- if (dev->cntl_buffer)
- usb_free_coherent(dev->udev, YUREX_BUF_SIZE,
+ usb_free_coherent(dev->udev, YUREX_BUF_SIZE,
dev->cntl_buffer, dev->cntl_urb->transfer_dma);
usb_free_urb(dev->cntl_urb);
}
if (dev->urb) {
usb_kill_urb(dev->urb);
- if (dev->int_buffer)
- usb_free_coherent(dev->udev, YUREX_BUF_SIZE,
+ usb_free_coherent(dev->udev, YUREX_BUF_SIZE,
dev->int_buffer, dev->urb->transfer_dma);
usb_free_urb(dev->urb);
}
@@ -472,7 +470,7 @@
break;
case CMD_SET:
data++;
- /* FALL THROUGH */
+ fallthrough;
case '0' ... '9':
set = 1;
c = c2 = simple_strtoull(data, NULL, 0);
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index bc5ecd5..39cb141 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -414,7 +414,7 @@
mon_text_read_head_t(rp, &ptr, ep);
mon_text_read_statset(rp, &ptr, ep);
- ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ ptr.cnt += scnprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
" %d", ep->length);
mon_text_read_data(rp, &ptr, ep);
@@ -462,7 +462,7 @@
} else {
mon_text_read_statset(rp, &ptr, ep);
}
- ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+ ptr.cnt += scnprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
" %d", ep->length);
mon_text_read_data(rp, &ptr, ep);
@@ -520,7 +520,7 @@
case USB_ENDPOINT_XFER_CONTROL: utype = 'C'; break;
default: /* PIPE_BULK */ utype = 'B';
}
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
"%lx %u %c %c%c:%03u:%02u",
ep->id, ep->tstamp, ep->type,
utype, udir, ep->devnum, ep->epnum);
@@ -538,7 +538,7 @@
case USB_ENDPOINT_XFER_CONTROL: utype = 'C'; break;
default: /* PIPE_BULK */ utype = 'B';
}
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
"%lx %u %c %c%c:%d:%03u:%u",
ep->id, ep->tstamp, ep->type,
utype, udir, ep->busnum, ep->devnum, ep->epnum);
@@ -549,7 +549,7 @@
{
if (ep->setup_flag == 0) { /* Setup packet is present and captured */
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" s %02x %02x %04x %04x %04x",
ep->setup[0],
ep->setup[1],
@@ -557,10 +557,10 @@
(ep->setup[5] << 8) | ep->setup[4],
(ep->setup[7] << 8) | ep->setup[6]);
} else if (ep->setup_flag != '-') { /* Unable to capture setup packet */
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %c __ __ ____ ____ ____", ep->setup_flag);
} else { /* No setup for this kind of URB */
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %d", ep->status);
}
}
@@ -568,7 +568,7 @@
static void mon_text_read_intstat(struct mon_reader_text *rp,
struct mon_text_ptr *p, const struct mon_event_text *ep)
{
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %d:%d", ep->status, ep->interval);
}
@@ -576,10 +576,10 @@
struct mon_text_ptr *p, const struct mon_event_text *ep)
{
if (ep->type == 'S') {
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %d:%d:%d", ep->status, ep->interval, ep->start_frame);
} else {
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %d:%d:%d:%d",
ep->status, ep->interval, ep->start_frame, ep->error_count);
}
@@ -592,7 +592,7 @@
int i;
const struct mon_iso_desc *dp;
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %d", ep->numdesc);
ndesc = ep->numdesc;
if (ndesc > ISODESC_MAX)
@@ -601,7 +601,7 @@
ndesc = 0;
dp = ep->isodesc;
for (i = 0; i < ndesc; i++) {
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %d:%u:%u", dp->status, dp->offset, dp->length);
dp++;
}
@@ -614,28 +614,28 @@
if ((data_len = ep->length) > 0) {
if (ep->data_flag == 0) {
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" =");
if (data_len >= DATA_MAX)
data_len = DATA_MAX;
for (i = 0; i < data_len; i++) {
if (i % 4 == 0) {
- p->cnt += snprintf(p->pbuf + p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt,
p->limit - p->cnt,
" ");
}
- p->cnt += snprintf(p->pbuf + p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt,
p->limit - p->cnt,
"%02x", ep->data[i]);
}
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
"\n");
} else {
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
" %c\n", ep->data_flag);
}
} else {
- p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
+ p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
}
}
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
index 6087be2..aef0a0b 100644
--- a/drivers/usb/mtu3/mtu3.h
+++ b/drivers/usb/mtu3/mtu3.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* mtu3.h - MediaTek USB3 DRD header
*
@@ -293,8 +293,6 @@
const struct usb_endpoint_descriptor *desc;
int flags;
- u8 wedged;
- u8 busy;
};
struct mtu3_request {
@@ -348,7 +346,8 @@
struct usb_gadget_driver *gadget_driver;
struct mtu3_request ep0_req;
u8 setup_buf[EP0_RESPONSE_BUF];
- u32 max_speed;
+ enum usb_device_speed max_speed;
+ enum usb_device_speed speed;
unsigned is_active:1;
unsigned may_wakeup:1;
@@ -371,12 +370,6 @@
return container_of(g, struct mtu3, g);
}
-static inline int is_first_entry(const struct list_head *list,
- const struct list_head *head)
-{
- return list_is_last(head, list);
-}
-
static inline struct mtu3_request *to_mtu3_request(struct usb_request *req)
{
return req ? container_of(req, struct mtu3_request, request) : NULL;
@@ -433,6 +426,7 @@
void mtu3_start(struct mtu3 *mtu);
void mtu3_stop(struct mtu3 *mtu);
void mtu3_dev_on_off(struct mtu3 *mtu, int is_on);
+void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed);
int mtu3_gadget_setup(struct mtu3 *mtu);
void mtu3_gadget_cleanup(struct mtu3 *mtu);
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
index e3780d4..3d328df 100644
--- a/drivers/usb/mtu3/mtu3_core.c
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -151,17 +151,6 @@
mtu3_clrbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
}
-/* disable all interrupts */
-static void mtu3_intr_disable(struct mtu3 *mtu)
-{
- void __iomem *mbase = mtu->mac_base;
-
- /* Disable level 1 interrupts */
- mtu3_writel(mbase, U3D_LV1IECR, ~0x0);
- /* Disable endpoint interrupts */
- mtu3_writel(mbase, U3D_EPIECR, ~0x0);
-}
-
static void mtu3_intr_status_clear(struct mtu3 *mtu)
{
void __iomem *mbase = mtu->mac_base;
@@ -174,6 +163,18 @@
mtu3_writel(mbase, U3D_LTSSM_INTR, ~0x0);
/* Clear speed change interrupt status */
mtu3_writel(mbase, U3D_DEV_LINK_INTR, ~0x0);
+ /* Clear QMU interrupt status */
+ mtu3_writel(mbase, U3D_QISAR0, ~0x0);
+}
+
+/* disable all interrupts */
+static void mtu3_intr_disable(struct mtu3 *mtu)
+{
+ /* Disable level 1 interrupts */
+ mtu3_writel(mtu->mac_base, U3D_LV1IECR, ~0x0);
+ /* Disable endpoint interrupts */
+ mtu3_writel(mtu->mac_base, U3D_EPIECR, ~0x0);
+ mtu3_intr_status_clear(mtu);
}
/* enable system global interrupt */
@@ -206,6 +207,71 @@
mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR);
}
+void mtu3_set_speed(struct mtu3 *mtu, enum usb_device_speed speed)
+{
+ void __iomem *mbase = mtu->mac_base;
+
+ if (speed > mtu->max_speed)
+ speed = mtu->max_speed;
+
+ switch (speed) {
+ case USB_SPEED_FULL:
+ /* disable U3 SS function */
+ mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
+ /* disable HS function */
+ mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+ break;
+ case USB_SPEED_HIGH:
+ mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
+ /* HS/FS detected by HW */
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+ break;
+ case USB_SPEED_SUPER:
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+ mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0),
+ SSUSB_U3_PORT_SSP_SPEED);
+ break;
+ case USB_SPEED_SUPER_PLUS:
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+ mtu3_setbits(mtu->ippc_base, SSUSB_U3_CTRL(0),
+ SSUSB_U3_PORT_SSP_SPEED);
+ break;
+ default:
+ dev_err(mtu->dev, "invalid speed: %s\n",
+ usb_speed_string(speed));
+ return;
+ }
+
+ mtu->speed = speed;
+ dev_dbg(mtu->dev, "set speed: %s\n", usb_speed_string(speed));
+}
+
+/* CSR registers will be reset to default value if port is disabled */
+static void mtu3_csr_init(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+
+ if (mtu->is_u3_ip) {
+ /* disable LGO_U1/U2 by default */
+ mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL,
+ SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE);
+ /* enable accept LGO_U1/U2 link command from host */
+ mtu3_setbits(mbase, U3D_LINK_POWER_CONTROL,
+ SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE);
+ /* device responses to u3_exit from host automatically */
+ mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN);
+ /* automatically build U2 link when U3 detect fail */
+ mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH);
+ /* auto clear SOFT_CONN when clear USB3_EN if work as HS */
+ mtu3_setbits(mbase, U3D_U3U2_SWITCH_CTRL, SOFTCON_CLR_AUTO_EN);
+ }
+
+ /* delay about 0.1us from detecting reset to send chirp-K */
+ mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
+ /* enable automatical HWRW from L1 */
+ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE);
+}
+
/* reset: u2 - data toggle, u3 - SeqN, flow control status etc */
static void mtu3_ep_reset(struct mtu3_ep *mep)
{
@@ -253,13 +319,13 @@
void mtu3_dev_on_off(struct mtu3 *mtu, int is_on)
{
- if (mtu->is_u3_ip && mtu->max_speed >= USB_SPEED_SUPER)
+ if (mtu->is_u3_ip && mtu->speed >= USB_SPEED_SUPER)
mtu3_ss_func_set(mtu, is_on);
else
mtu3_hs_softconn_set(mtu, is_on);
dev_info(mtu->dev, "gadget (%s) pullup D%s\n",
- usb_speed_string(mtu->max_speed), is_on ? "+" : "-");
+ usb_speed_string(mtu->speed), is_on ? "+" : "-");
}
void mtu3_start(struct mtu3 *mtu)
@@ -271,13 +337,8 @@
mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
- /*
- * When disable U2 port, USB2_CSR's register will be reset to
- * default value after re-enable it again(HS is enabled by default).
- * So if force mac to work as FS, disable HS function.
- */
- if (mtu->max_speed == USB_SPEED_FULL)
- mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+ mtu3_csr_init(mtu);
+ mtu3_set_speed(mtu, mtu->speed);
/* Initialize the default interrupts */
mtu3_intr_enable(mtu);
@@ -292,7 +353,6 @@
dev_dbg(mtu->dev, "%s\n", __func__);
mtu3_intr_disable(mtu);
- mtu3_intr_status_clear(mtu);
if (mtu->softconnect)
mtu3_dev_on_off(mtu, 0);
@@ -549,66 +609,19 @@
kfree(mtu->ep_array);
}
-static void mtu3_set_speed(struct mtu3 *mtu)
-{
- void __iomem *mbase = mtu->mac_base;
-
- if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH))
- mtu->max_speed = USB_SPEED_HIGH;
-
- if (mtu->max_speed == USB_SPEED_FULL) {
- /* disable U3 SS function */
- mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
- /* disable HS function */
- mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
- } else if (mtu->max_speed == USB_SPEED_HIGH) {
- mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
- /* HS/FS detected by HW */
- mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
- } else if (mtu->max_speed == USB_SPEED_SUPER) {
- mtu3_clrbits(mtu->ippc_base, SSUSB_U3_CTRL(0),
- SSUSB_U3_PORT_SSP_SPEED);
- }
-
- dev_info(mtu->dev, "max_speed: %s\n",
- usb_speed_string(mtu->max_speed));
-}
-
static void mtu3_regs_init(struct mtu3 *mtu)
{
-
void __iomem *mbase = mtu->mac_base;
/* be sure interrupts are disabled before registration of ISR */
mtu3_intr_disable(mtu);
- mtu3_intr_status_clear(mtu);
- if (mtu->is_u3_ip) {
- /* disable LGO_U1/U2 by default */
- mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL,
- SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE);
- /* enable accept LGO_U1/U2 link command from host */
- mtu3_setbits(mbase, U3D_LINK_POWER_CONTROL,
- SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE);
- /* device responses to u3_exit from host automatically */
- mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN);
- /* automatically build U2 link when U3 detect fail */
- mtu3_setbits(mbase, U3D_USB2_TEST_MODE, U2U3_AUTO_SWITCH);
- /* auto clear SOFT_CONN when clear USB3_EN if work as HS */
- mtu3_setbits(mbase, U3D_U3U2_SWITCH_CTRL, SOFTCON_CLR_AUTO_EN);
- }
+ mtu3_csr_init(mtu);
- mtu3_set_speed(mtu);
-
- /* delay about 0.1us from detecting reset to send chirp-K */
- mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
/* U2/U3 detected by HW */
mtu3_writel(mbase, U3D_DEVICE_CONF, 0);
/* vbus detected by HW */
mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON);
- /* enable automatical HWRW from L1 */
- mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE);
-
/* use new QMU format when HW version >= 0x1003 */
if (mtu->gen2cp)
mtu3_writel(mbase, U3D_QFCR, ~0x0);
@@ -763,6 +776,34 @@
return IRQ_HANDLED;
}
+static void mtu3_check_params(struct mtu3 *mtu)
+{
+ /* check the max_speed parameter */
+ switch (mtu->max_speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ case USB_SPEED_SUPER_PLUS:
+ break;
+ default:
+ dev_err(mtu->dev, "invalid max_speed: %s\n",
+ usb_speed_string(mtu->max_speed));
+ fallthrough;
+ case USB_SPEED_UNKNOWN:
+ /* default as SSP */
+ mtu->max_speed = USB_SPEED_SUPER_PLUS;
+ break;
+ }
+
+ if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH))
+ mtu->max_speed = USB_SPEED_HIGH;
+
+ mtu->speed = mtu->max_speed;
+
+ dev_info(mtu->dev, "max_speed: %s\n",
+ usb_speed_string(mtu->max_speed));
+}
+
static int mtu3_hw_init(struct mtu3 *mtu)
{
u32 value;
@@ -778,6 +819,8 @@
dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version,
mtu->is_u3_ip ? "U3" : "U2");
+ mtu3_check_params(mtu);
+
mtu3_device_reset(mtu);
ret = mtu3_device_enable(mtu);
@@ -801,7 +844,7 @@
mtu3_mem_free(mtu);
}
-/**
+/*
* we set 32-bit DMA mask by default, here check whether the controller
* supports 36-bit DMA or not, if it does, set 36-bit DMA mask.
*/
@@ -832,7 +875,6 @@
struct device *dev = ssusb->dev;
struct platform_device *pdev = to_platform_device(dev);
struct mtu3 *mtu = NULL;
- struct resource *res;
int ret = -ENOMEM;
mtu = devm_kzalloc(dev, sizeof(struct mtu3), GFP_KERNEL);
@@ -844,8 +886,7 @@
return mtu->irq;
dev_info(dev, "irq %d\n", mtu->irq);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
- mtu->mac_base = devm_ioremap_resource(dev, res);
+ mtu->mac_base = devm_platform_ioremap_resource_byname(pdev, "mac");
if (IS_ERR(mtu->mac_base)) {
dev_err(dev, "error mapping memory for dev mac\n");
return PTR_ERR(mtu->mac_base);
@@ -859,23 +900,6 @@
mtu->ssusb = ssusb;
mtu->max_speed = usb_get_maximum_speed(dev);
- /* check the max_speed parameter */
- switch (mtu->max_speed) {
- case USB_SPEED_FULL:
- case USB_SPEED_HIGH:
- case USB_SPEED_SUPER:
- case USB_SPEED_SUPER_PLUS:
- break;
- default:
- dev_err(dev, "invalid max_speed: %s\n",
- usb_speed_string(mtu->max_speed));
- /* fall through */
- case USB_SPEED_UNKNOWN:
- /* default as SSP */
- mtu->max_speed = USB_SPEED_SUPER_PLUS;
- break;
- }
-
dev_dbg(dev, "mac_base=0x%p, ippc_base=0x%p\n",
mtu->mac_base, mtu->ippc_base);
diff --git a/drivers/usb/mtu3/mtu3_debug.h b/drivers/usb/mtu3/mtu3_debug.h
index e96a692..3084c46 100644
--- a/drivers/usb/mtu3/mtu3_debug.h
+++ b/drivers/usb/mtu3/mtu3_debug.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* mtu3_debug.h - debug header
*
@@ -12,6 +12,8 @@
#include <linux/debugfs.h>
+struct ssusb_mtk;
+
#define MTU3_DEBUGFS_NAME_LEN 32
struct mtu3_regset {
diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c
index 25b9635..7537bfd 100644
--- a/drivers/usb/mtu3/mtu3_debugfs.c
+++ b/drivers/usb/mtu3/mtu3_debugfs.c
@@ -276,7 +276,7 @@
.release = single_release,
};
-static struct debugfs_reg32 mtu3_prb_regs[] = {
+static const struct debugfs_reg32 mtu3_prb_regs[] = {
dump_prb_reg("enable", U3D_SSUSB_PRB_CTRL0),
dump_prb_reg("byte-sell", U3D_SSUSB_PRB_CTRL1),
dump_prb_reg("byte-selh", U3D_SSUSB_PRB_CTRL2),
@@ -349,7 +349,7 @@
static void mtu3_debugfs_create_prb_files(struct mtu3 *mtu)
{
struct ssusb_mtk *ssusb = mtu->ssusb;
- struct debugfs_reg32 *regs;
+ const struct debugfs_reg32 *regs;
struct dentry *dir_prb;
int i;
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 08e1844..04f666e 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -320,9 +320,9 @@
mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
}
-static int ssusb_role_sw_set(struct device *dev, enum usb_role role)
+static int ssusb_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
{
- struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+ struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
bool to_host = false;
if (role == USB_ROLE_HOST)
@@ -334,9 +334,9 @@
return 0;
}
-static enum usb_role ssusb_role_sw_get(struct device *dev)
+static enum usb_role ssusb_role_sw_get(struct usb_role_switch *sw)
{
- struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+ struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
enum usb_role role;
role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
@@ -356,6 +356,7 @@
role_sx_desc.set = ssusb_role_sw_set;
role_sx_desc.get = ssusb_role_sw_get;
role_sx_desc.fwnode = dev_fwnode(ssusb->dev);
+ role_sx_desc.driver_data = ssusb;
otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc);
return PTR_ERR_OR_ZERO(otg_sx->role_sw);
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
index 5e58c4d..760fe7d 100644
--- a/drivers/usb/mtu3/mtu3_dr.h
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* mtu3_dr.h - dual role switch and host glue layer header
*
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index 619c459..b7a6363 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -15,17 +15,12 @@
__releases(mep->mtu->lock)
__acquires(mep->mtu->lock)
{
- struct mtu3_request *mreq;
- struct mtu3 *mtu;
- int busy = mep->busy;
+ struct mtu3_request *mreq = to_mtu3_request(req);
+ struct mtu3 *mtu = mreq->mtu;
- mreq = to_mtu3_request(req);
list_del(&mreq->list);
- if (mreq->request.status == -EINPROGRESS)
- mreq->request.status = status;
-
- mtu = mreq->mtu;
- mep->busy = 1;
+ if (req->status == -EINPROGRESS)
+ req->status = status;
trace_mtu3_req_complete(mreq);
spin_unlock(&mtu->lock);
@@ -34,20 +29,17 @@
if (mep->epnum)
usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
- dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n", mep->name,
- req, req->status, mreq->request.actual, mreq->request.length);
+ dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n",
+ mep->name, req, req->status, req->actual, req->length);
- usb_gadget_giveback_request(&mep->ep, &mreq->request);
-
+ usb_gadget_giveback_request(&mep->ep, req);
spin_lock(&mtu->lock);
- mep->busy = busy;
}
static void nuke(struct mtu3_ep *mep, const int status)
{
struct mtu3_request *mreq = NULL;
- mep->busy = 1;
if (list_empty(&mep->req_list))
return;
@@ -85,7 +77,7 @@
if (usb_endpoint_xfer_int(desc) ||
usb_endpoint_xfer_isoc(desc)) {
interval = desc->bInterval;
- interval = clamp_val(interval, 1, 16) - 1;
+ interval = clamp_val(interval, 1, 16);
if (usb_endpoint_xfer_isoc(desc) && comp_desc)
mult = comp_desc->bmAttributes;
}
@@ -97,10 +89,17 @@
if (usb_endpoint_xfer_isoc(desc) ||
usb_endpoint_xfer_int(desc)) {
interval = desc->bInterval;
- interval = clamp_val(interval, 1, 16) - 1;
+ interval = clamp_val(interval, 1, 16);
mult = usb_endpoint_maxp_mult(desc) - 1;
}
break;
+ case USB_SPEED_FULL:
+ if (usb_endpoint_xfer_isoc(desc))
+ interval = clamp_val(desc->bInterval, 1, 16);
+ else if (usb_endpoint_xfer_int(desc))
+ interval = clamp_val(desc->bInterval, 1, 255);
+
+ break;
default:
break; /*others are ignored */
}
@@ -193,9 +192,7 @@
if (ret)
goto error;
- mep->busy = 0;
- mep->wedged = 0;
- mep->flags |= MTU3_EP_ENABLED;
+ mep->flags = MTU3_EP_ENABLED;
mtu->active_ep++;
error:
@@ -223,7 +220,7 @@
spin_lock_irqsave(&mtu->lock, flags);
mtu3_ep_disable(mep);
- mep->flags &= ~MTU3_EP_ENABLED;
+ mep->flags = 0;
mtu->active_ep--;
spin_unlock_irqrestore(&(mtu->lock), flags);
@@ -245,6 +242,7 @@
mreq->request.dma = DMA_ADDR_INVALID;
mreq->epnum = mep->epnum;
mreq->mep = mep;
+ INIT_LIST_HEAD(&mreq->list);
trace_mtu3_alloc_request(mreq);
return &mreq->request;
@@ -261,23 +259,15 @@
static int mtu3_gadget_queue(struct usb_ep *ep,
struct usb_request *req, gfp_t gfp_flags)
{
- struct mtu3_ep *mep;
- struct mtu3_request *mreq;
- struct mtu3 *mtu;
+ struct mtu3_ep *mep = to_mtu3_ep(ep);
+ struct mtu3_request *mreq = to_mtu3_request(req);
+ struct mtu3 *mtu = mep->mtu;
unsigned long flags;
int ret = 0;
- if (!ep || !req)
- return -EINVAL;
-
if (!req->buf)
return -ENODATA;
- mep = to_mtu3_ep(ep);
- mtu = mep->mtu;
- mreq = to_mtu3_request(req);
- mreq->mtu = mtu;
-
if (mreq->mep != mep)
return -EINVAL;
@@ -301,6 +291,7 @@
return -ESHUTDOWN;
}
+ mreq->mtu = mtu;
mreq->request.actual = 0;
mreq->request.status = -EINPROGRESS;
@@ -333,11 +324,11 @@
struct mtu3_ep *mep = to_mtu3_ep(ep);
struct mtu3_request *mreq = to_mtu3_request(req);
struct mtu3_request *r;
+ struct mtu3 *mtu = mep->mtu;
unsigned long flags;
int ret = 0;
- struct mtu3 *mtu = mep->mtu;
- if (!ep || !req || mreq->mep != mep)
+ if (mreq->mep != mep)
return -EINVAL;
dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req);
@@ -377,9 +368,6 @@
unsigned long flags;
int ret = 0;
- if (!ep)
- return -EINVAL;
-
dev_dbg(mtu->dev, "%s : %s...", __func__, ep->name);
spin_lock_irqsave(&mtu->lock, flags);
@@ -403,7 +391,7 @@
goto done;
}
} else {
- mep->wedged = 0;
+ mep->flags &= ~MTU3_EP_WEDGE;
}
dev_dbg(mtu->dev, "%s %s stall\n", ep->name, value ? "set" : "clear");
@@ -422,10 +410,7 @@
{
struct mtu3_ep *mep = to_mtu3_ep(ep);
- if (!ep)
- return -EINVAL;
-
- mep->wedged = 1;
+ mep->flags |= MTU3_EP_WEDGE;
return usb_ep_set_halt(ep);
}
@@ -589,6 +574,19 @@
return 0;
}
+static void
+mtu3_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed)
+{
+ struct mtu3 *mtu = gadget_to_mtu3(g);
+ unsigned long flags;
+
+ dev_dbg(mtu->dev, "%s %s\n", __func__, usb_speed_string(speed));
+
+ spin_lock_irqsave(&mtu->lock, flags);
+ mtu3_set_speed(mtu, speed);
+ spin_unlock_irqrestore(&mtu->lock, flags);
+}
+
static const struct usb_gadget_ops mtu3_gadget_ops = {
.get_frame = mtu3_gadget_get_frame,
.wakeup = mtu3_gadget_wakeup,
@@ -596,6 +594,7 @@
.pullup = mtu3_gadget_pullup,
.udc_start = mtu3_gadget_start,
.udc_stop = mtu3_gadget_stop,
+ .udc_set_speed = mtu3_gadget_set_speed,
};
static void mtu3_state_reset(struct mtu3 *mtu)
@@ -671,8 +670,6 @@
int mtu3_gadget_setup(struct mtu3 *mtu)
{
- int ret;
-
mtu->g.ops = &mtu3_gadget_ops;
mtu->g.max_speed = mtu->max_speed;
mtu->g.speed = USB_SPEED_UNKNOWN;
@@ -683,11 +680,7 @@
mtu3_gadget_init_eps(mtu);
- ret = usb_add_gadget_udc(mtu->dev, &mtu->g);
- if (ret)
- dev_err(mtu->dev, "failed to register udc\n");
-
- return ret;
+ return usb_add_gadget_udc(mtu->dev, &mtu->g);
}
void mtu3_gadget_cleanup(struct mtu3 *mtu)
diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c
index 4da216c..0ca4721 100644
--- a/drivers/usb/mtu3/mtu3_gadget_ep0.c
+++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c
@@ -153,6 +153,15 @@
set ? "SEND" : "CLEAR", decode_ep0_state(mtu));
}
+static void ep0_do_status_stage(struct mtu3 *mtu)
+{
+ void __iomem *mbase = mtu->mac_base;
+ u32 value;
+
+ value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+ mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND);
+}
+
static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq);
static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req)
@@ -269,20 +278,20 @@
u32 value;
switch (le16_to_cpu(setup->wIndex) >> 8) {
- case TEST_J:
- dev_dbg(mtu->dev, "TEST_J\n");
+ case USB_TEST_J:
+ dev_dbg(mtu->dev, "USB_TEST_J\n");
mtu->test_mode_nr = TEST_J_MODE;
break;
- case TEST_K:
- dev_dbg(mtu->dev, "TEST_K\n");
+ case USB_TEST_K:
+ dev_dbg(mtu->dev, "USB_TEST_K\n");
mtu->test_mode_nr = TEST_K_MODE;
break;
- case TEST_SE0_NAK:
- dev_dbg(mtu->dev, "TEST_SE0_NAK\n");
+ case USB_TEST_SE0_NAK:
+ dev_dbg(mtu->dev, "USB_TEST_SE0_NAK\n");
mtu->test_mode_nr = TEST_SE0_NAK_MODE;
break;
- case TEST_PACKET:
- dev_dbg(mtu->dev, "TEST_PACKET\n");
+ case USB_TEST_PACKET:
+ dev_dbg(mtu->dev, "USB_TEST_PACKET\n");
mtu->test_mode_nr = TEST_PACKET_MODE;
break;
default:
@@ -297,8 +306,7 @@
ep0_load_test_packet(mtu);
/* send status before entering test mode. */
- value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
- mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND);
+ ep0_do_status_stage(mtu);
/* wait for ACK status sent by host */
readl_poll_timeout_atomic(mbase + U3D_EP0CSR, value,
@@ -409,7 +417,7 @@
handled = 1;
/* ignore request if endpoint is wedged */
- if (mep->wedged)
+ if (mep->flags & MTU3_EP_WEDGE)
break;
mtu3_ep_stall_set(mep, set);
@@ -632,7 +640,6 @@
{
struct usb_ctrlrequest setup;
struct mtu3_request *mreq;
- void __iomem *mbase = mtu->mac_base;
int handled = 0;
ep0_read_setup(mtu, &setup);
@@ -664,14 +671,19 @@
if (mtu->test_mode) {
; /* nothing to do */
} else if (handled == USB_GADGET_DELAYED_STATUS) {
- /* handle the delay STATUS phase till receive ep_queue on ep0 */
- mtu->delayed_status = true;
+
+ mreq = next_ep0_request(mtu);
+ if (mreq) {
+ /* already asked us to continue delayed status */
+ ep0_do_status_stage(mtu);
+ ep0_req_giveback(mtu, &mreq->request);
+ } else {
+ /* do delayed STATUS stage till receive ep0_queue */
+ mtu->delayed_status = true;
+ }
} else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */
- mtu3_writel(mbase, U3D_EP0CSR,
- (mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS)
- | EP0_SETUPPKTRDY | EP0_DATAEND);
-
+ ep0_do_status_stage(mtu);
/* complete zlp request directly */
mreq = next_ep0_request(mtu);
if (mreq && !mreq->request.length)
@@ -802,12 +814,9 @@
}
if (mtu->delayed_status) {
- u32 csr;
mtu->delayed_status = false;
- csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
- csr |= EP0_SETUPPKTRDY | EP0_DATAEND;
- mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
+ ep0_do_status_stage(mtu);
/* needn't giveback the request for handling delay STATUS */
return 0;
}
diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
index 8382d06..072db1f 100644
--- a/drivers/usb/mtu3/mtu3_hw_regs.h
+++ b/drivers/usb/mtu3/mtu3_hw_regs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions
*
@@ -134,7 +134,7 @@
#define TX_W1C_BITS (~(TX_SENTSTALL))
/* U3D_TX1CSR1 */
-#define TX_MAX_PKT_G2(x) (((x) & 0x7f) << 24)
+#define TX_MAX_PKT_G2(x) (((x) & 0xff) << 24)
#define TX_MULT_G2(x) (((x) & 0x7) << 21)
#define TX_MULT_OG(x) (((x) & 0x3) << 22)
#define TX_MAX_PKT_OG(x) (((x) & 0x3f) << 16)
@@ -173,7 +173,7 @@
#define RX_W1C_BITS (~(RX_SENTSTALL | RX_RXPKTRDY))
/* U3D_RX1CSR1 */
-#define RX_MAX_PKT_G2(x) (((x) & 0x7f) << 24)
+#define RX_MAX_PKT_G2(x) (((x) & 0xff) << 24)
#define RX_MULT_G2(x) (((x) & 0x7) << 21)
#define RX_MULT_OG(x) (((x) & 0x3) << 22)
#define RX_MAX_PKT_OG(x) (((x) & 0x3f) << 16)
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
index 9c256ea..d44d541 100644
--- a/drivers/usb/mtu3/mtu3_plat.c
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -216,7 +216,6 @@
struct device_node *node = pdev->dev.of_node;
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
struct device *dev = &pdev->dev;
- struct resource *res;
int i;
int ret;
@@ -263,8 +262,7 @@
}
}
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc");
- ssusb->ippc_base = devm_ioremap_resource(dev, res);
+ ssusb->ippc_base = devm_platform_ioremap_resource_byname(pdev, "ippc");
if (IS_ERR(ssusb->ippc_base))
return PTR_ERR(ssusb->ippc_base);
diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c
index 3f414f9..2ea3157 100644
--- a/drivers/usb/mtu3/mtu3_qmu.c
+++ b/drivers/usb/mtu3/mtu3_qmu.c
@@ -273,6 +273,8 @@
gpd->dw3_info |= cpu_to_le32(GPD_EXT_FLAG_ZLP);
}
+ /* prevent reorder, make sure GPD's HWO is set last */
+ mb();
gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_IOC | GPD_FLAGS_HWO);
mreq->gpd = gpd;
@@ -306,6 +308,8 @@
gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma));
ext_addr |= GPD_EXT_NGP(mtu, upper_32_bits(enq_dma));
gpd->dw3_info = cpu_to_le32(ext_addr);
+ /* prevent reorder, make sure GPD's HWO is set last */
+ mb();
gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_IOC | GPD_FLAGS_HWO);
mreq->gpd = gpd;
@@ -445,7 +449,8 @@
return;
}
mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_TXPKTRDY);
-
+ /* prevent reorder, make sure GPD's HWO is set last */
+ mb();
/* by pass the current GDP */
gpd_current->dw0_info |= cpu_to_le32(GPD_FLAGS_BPS | GPD_FLAGS_HWO);
diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h
index 9cfde20..66e1c0a 100644
--- a/drivers/usb/mtu3/mtu3_qmu.h
+++ b/drivers/usb/mtu3/mtu3_qmu.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* mtu3_qmu.h - Queue Management Unit driver header
*
diff --git a/drivers/usb/mtu3/mtu3_trace.c b/drivers/usb/mtu3/mtu3_trace.c
index 4f5e785..d17ddb8 100644
--- a/drivers/usb/mtu3/mtu3_trace.c
+++ b/drivers/usb/mtu3/mtu3_trace.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* mtu3_trace.c - trace support
*
* Copyright (C) 2019 MediaTek Inc.
@@ -8,6 +8,7 @@
*/
#define CREATE_TRACE_POINTS
+#include "mtu3_debug.h"
#include "mtu3_trace.h"
void mtu3_dbg_trace(struct device *dev, const char *fmt, ...)
diff --git a/drivers/usb/mtu3/mtu3_trace.h b/drivers/usb/mtu3/mtu3_trace.h
index 050e30f..1b89763 100644
--- a/drivers/usb/mtu3/mtu3_trace.h
+++ b/drivers/usb/mtu3/mtu3_trace.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/**
* mtu3_trace.h - trace support
*
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 52f8e2b..4d61df6 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -101,7 +101,6 @@
config USB_MUSB_DSPS
tristate "TI DSPS platforms"
- select USB_MUSB_AM335X_CHILD
depends on ARCH_OMAP2PLUS || COMPILE_TEST
depends on OF_IRQ
@@ -111,13 +110,18 @@
config USB_MUSB_JZ4740
tristate "JZ4740"
- depends on NOP_USB_XCEIV
+ depends on OF
depends on MIPS || COMPILE_TEST
depends on USB_MUSB_GADGET
- depends on USB=n || USB_OTG_BLACKLIST_HUB
+ depends on USB=n || USB_OTG_DISABLE_EXTERNAL_HUB
+ select USB_ROLE_SWITCH
-config USB_MUSB_AM335X_CHILD
- tristate
+config USB_MUSB_MEDIATEK
+ tristate "MediaTek platforms"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on NOP_USB_XCEIV
+ select GENERIC_PHY
+ select USB_ROLE_SWITCH
comment "MUSB DMA mode"
@@ -142,7 +146,7 @@
config USB_INVENTRA_DMA
bool 'Inventra'
- depends on USB_MUSB_OMAP2PLUS
+ depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK || USB_MUSB_JZ4740
help
Enable DMA transfers using Mentor's engine.
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index 3a88c79..9322473 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -24,9 +24,7 @@
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o
obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o
-
-
-obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o
+obj-$(CONFIG_USB_MUSB_MEDIATEK) += mediatek.o
# the kconfig must guarantee that only one of the
# possible I/O schemes will be enabled at a time ...
diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c
index b4d6d9b..edb5b63 100644
--- a/drivers/usb/musb/cppi_dma.c
+++ b/drivers/usb/musb/cppi_dma.c
@@ -975,7 +975,7 @@
musb_dbg(musb, "%cX DMA%d not allocated!",
cppi_ch->transmit ? 'T' : 'R',
cppi_ch->index);
- /* FALLTHROUGH */
+ fallthrough;
case MUSB_DMA_STATUS_FREE:
break;
}
@@ -1146,7 +1146,7 @@
struct musb_hw_ep *hw_ep = NULL;
u32 rx, tx;
int i, index;
- unsigned long uninitialized_var(flags);
+ unsigned long flags;
cppi = container_of(musb->dma_controller, struct cppi, controller);
if (cppi->irq)
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index fb6bbd2..7044355 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -13,7 +13,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/usb_phy_generic.h>
@@ -25,10 +25,6 @@
#include "musb_core.h"
-#ifdef CONFIG_MACH_DAVINCI_EVM
-#define GPIO_nVBUS_DRV 160
-#endif
-
#include "davinci.h"
#include "cppi_dma.h"
@@ -40,6 +36,9 @@
struct device *dev;
struct platform_device *musb;
struct clk *clk;
+ bool vbus_state;
+ struct gpio_desc *vbus;
+ struct work_struct vbus_work;
};
/* REVISIT (PM) we should be able to keep the PHY in low power mode most
@@ -135,43 +134,44 @@
* when J10 is out, and TI documents it as handling OTG.
*/
-#ifdef CONFIG_MACH_DAVINCI_EVM
-
-static int vbus_state = -1;
-
/* I2C operations are always synchronous, and require a task context.
* With unloaded systems, using the shared workqueue seems to suffice
* to satisfy the 100msec A_WAIT_VRISE timeout...
*/
-static void evm_deferred_drvvbus(struct work_struct *ignored)
+static void evm_deferred_drvvbus(struct work_struct *work)
{
- gpio_set_value_cansleep(GPIO_nVBUS_DRV, vbus_state);
- vbus_state = !vbus_state;
+ struct davinci_glue *glue = container_of(work, struct davinci_glue,
+ vbus_work);
+
+ gpiod_set_value_cansleep(glue->vbus, glue->vbus_state);
+ glue->vbus_state = !glue->vbus_state;
}
-#endif /* EVM */
-
-static void davinci_musb_source_power(struct musb *musb, int is_on, int immediate)
+static void davinci_musb_source_power(struct musb *musb, int is_on,
+ int immediate)
{
-#ifdef CONFIG_MACH_DAVINCI_EVM
+ struct davinci_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ /* This GPIO handling is entirely optional */
+ if (!glue->vbus)
+ return;
+
if (is_on)
is_on = 1;
- if (vbus_state == is_on)
+ if (glue->vbus_state == is_on)
return;
- vbus_state = !is_on; /* 0/1 vs "-1 == unknown/init" */
+ /* 0/1 vs "-1 == unknown/init" */
+ glue->vbus_state = !is_on;
if (machine_is_davinci_evm()) {
- static DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus);
-
if (immediate)
- gpio_set_value_cansleep(GPIO_nVBUS_DRV, vbus_state);
+ gpiod_set_value_cansleep(glue->vbus, glue->vbus_state);
else
- schedule_work(&evm_vbus_work);
+ schedule_work(&glue->vbus_work);
}
if (immediate)
- vbus_state = is_on;
-#endif
+ glue->vbus_state = is_on;
}
static void davinci_musb_set_vbus(struct musb *musb, int is_on)
@@ -524,6 +524,15 @@
pdata->platform_ops = &davinci_ops;
+ glue->vbus = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
+ if (IS_ERR(glue->vbus)) {
+ ret = PTR_ERR(glue->vbus);
+ goto err0;
+ } else {
+ glue->vbus_state = -1;
+ INIT_WORK(&glue->vbus_work, evm_deferred_drvvbus);
+ }
+
usb_phy_generic_register();
platform_set_drvdata(pdev, glue);
diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h
index e021485..c8e67d1 100644
--- a/drivers/usb/musb/davinci.h
+++ b/drivers/usb/musb/davinci.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2006 by Texas Instruments
*/
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index e3b8c84..c4fe1f4 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -12,21 +12,26 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/usb/role.h>
#include <linux/usb/usb_phy_generic.h>
#include "musb_core.h"
struct jz4740_glue {
- struct device *dev;
- struct platform_device *musb;
+ struct platform_device *pdev;
+ struct musb *musb;
struct clk *clk;
+ struct usb_role_switch *role_sw;
};
static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
{
- unsigned long flags;
- irqreturn_t retval = IRQ_NONE;
- struct musb *musb = __hci;
+ unsigned long flags;
+ irqreturn_t retval = IRQ_NONE, retval_dma = IRQ_NONE;
+ struct musb *musb = __hci;
+
+ if (IS_ENABLED(CONFIG_USB_INVENTRA_DMA) && musb->dma_controller)
+ retval_dma = dma_controller_irq(irq, musb->dma_controller);
spin_lock_irqsave(&musb->lock, flags);
@@ -40,43 +45,70 @@
* never see them set
*/
musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME |
- MUSB_INTR_RESET | MUSB_INTR_SOF;
+ MUSB_INTR_RESET | MUSB_INTR_SOF;
if (musb->int_usb || musb->int_tx || musb->int_rx)
retval = musb_interrupt(musb);
spin_unlock_irqrestore(&musb->lock, flags);
- return retval;
+ if (retval == IRQ_HANDLED || retval_dma == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
}
static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
-{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
-{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
-{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
+ { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
};
static const struct musb_hdrc_config jz4740_musb_config = {
/* Silicon does not implement USB OTG. */
- .multipoint = 0,
+ .multipoint = 0,
/* Max EPs scanned, driver will decide which EP can be used. */
- .num_eps = 4,
+ .num_eps = 4,
/* RAMbits needed to configure EPs from table */
- .ram_bits = 9,
- .fifo_cfg = jz4740_musb_fifo_cfg,
- .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg),
+ .ram_bits = 9,
+ .fifo_cfg = jz4740_musb_fifo_cfg,
+ .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg),
};
-static struct musb_hdrc_platform_data jz4740_musb_platform_data = {
- .mode = MUSB_PERIPHERAL,
- .config = &jz4740_musb_config,
-};
+static int jz4740_musb_role_switch_set(struct usb_role_switch *sw,
+ enum usb_role role)
+{
+ struct jz4740_glue *glue = usb_role_switch_get_drvdata(sw);
+ struct usb_phy *phy = glue->musb->xceiv;
+
+ switch (role) {
+ case USB_ROLE_NONE:
+ atomic_notifier_call_chain(&phy->notifier, USB_EVENT_NONE, phy);
+ break;
+ case USB_ROLE_DEVICE:
+ atomic_notifier_call_chain(&phy->notifier, USB_EVENT_VBUS, phy);
+ break;
+ case USB_ROLE_HOST:
+ atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ID, phy);
+ break;
+ }
+
+ return 0;
+}
static int jz4740_musb_init(struct musb *musb)
{
struct device *dev = musb->controller->parent;
+ struct jz4740_glue *glue = dev_get_drvdata(dev);
+ struct usb_role_switch_desc role_sw_desc = {
+ .set = jz4740_musb_role_switch_set,
+ .driver_data = glue,
+ .fwnode = dev_fwnode(dev),
+ };
int err;
+ glue->musb = musb;
+
if (dev->of_node)
musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0);
else
@@ -88,7 +120,14 @@
return err;
}
- /* Silicon does not implement ConfigData register.
+ glue->role_sw = usb_role_switch_register(dev, &role_sw_desc);
+ if (IS_ERR(glue->role_sw)) {
+ dev_err(dev, "Failed to register USB role switch");
+ return PTR_ERR(glue->role_sw);
+ }
+
+ /*
+ * Silicon does not implement ConfigData register.
* Set dyn_fifo to avoid reading EP config from hardware.
*/
musb->dyn_fifo = true;
@@ -98,73 +137,122 @@
return 0;
}
-/*
- * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA,
- * so let's not set up the dma function pointers yet.
- */
+static int jz4740_musb_exit(struct musb *musb)
+{
+ struct jz4740_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+ usb_role_switch_unregister(glue->role_sw);
+
+ return 0;
+}
+
static const struct musb_platform_ops jz4740_musb_ops = {
.quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP,
.fifo_mode = 2,
.init = jz4740_musb_init,
+ .exit = jz4740_musb_exit,
+#ifdef CONFIG_USB_INVENTRA_DMA
+ .dma_init = musbhs_dma_controller_create_noirq,
+ .dma_exit = musbhs_dma_controller_destroy,
+#endif
+};
+
+static const struct musb_hdrc_platform_data jz4740_musb_pdata = {
+ .mode = MUSB_PERIPHERAL,
+ .config = &jz4740_musb_config,
+ .platform_ops = &jz4740_musb_ops,
+};
+
+static struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
+ { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
+};
+
+static struct musb_hdrc_config jz4770_musb_config = {
+ .multipoint = 1,
+ .num_eps = 11,
+ .ram_bits = 11,
+ .fifo_cfg = jz4770_musb_fifo_cfg,
+ .fifo_cfg_size = ARRAY_SIZE(jz4770_musb_fifo_cfg),
+};
+
+static const struct musb_hdrc_platform_data jz4770_musb_pdata = {
+ .mode = MUSB_PERIPHERAL, /* TODO: support OTG */
+ .config = &jz4770_musb_config,
+ .platform_ops = &jz4740_musb_ops,
};
static int jz4740_probe(struct platform_device *pdev)
{
- struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data;
+ struct device *dev = &pdev->dev;
+ const struct musb_hdrc_platform_data *pdata;
struct platform_device *musb;
struct jz4740_glue *glue;
- struct clk *clk;
+ struct clk *clk;
int ret;
- glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
return -ENOMEM;
+ pdata = of_device_get_match_data(dev);
+ if (!pdata) {
+ dev_err(dev, "missing platform data");
+ return -EINVAL;
+ }
+
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
if (!musb) {
- dev_err(&pdev->dev, "failed to allocate musb device\n");
+ dev_err(dev, "failed to allocate musb device");
return -ENOMEM;
}
- clk = devm_clk_get(&pdev->dev, "udc");
+ clk = devm_clk_get(dev, "udc");
if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "failed to get clock\n");
+ dev_err(dev, "failed to get clock");
ret = PTR_ERR(clk);
goto err_platform_device_put;
}
ret = clk_prepare_enable(clk);
if (ret) {
- dev_err(&pdev->dev, "failed to enable clock\n");
+ dev_err(dev, "failed to enable clock");
goto err_platform_device_put;
}
- musb->dev.parent = &pdev->dev;
+ musb->dev.parent = dev;
+ musb->dev.dma_mask = &musb->dev.coherent_dma_mask;
+ musb->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- glue->dev = &pdev->dev;
- glue->musb = musb;
+ glue->pdev = musb;
glue->clk = clk;
- pdata->platform_ops = &jz4740_musb_ops;
-
platform_set_drvdata(pdev, glue);
ret = platform_device_add_resources(musb, pdev->resource,
pdev->num_resources);
if (ret) {
- dev_err(&pdev->dev, "failed to add resources\n");
+ dev_err(dev, "failed to add resources");
goto err_clk_disable;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
- dev_err(&pdev->dev, "failed to add platform_data\n");
+ dev_err(dev, "failed to add platform_data");
goto err_clk_disable;
}
ret = platform_device_add(musb);
if (ret) {
- dev_err(&pdev->dev, "failed to register musb device\n");
+ dev_err(dev, "failed to register musb device");
goto err_clk_disable;
}
@@ -179,28 +267,27 @@
static int jz4740_remove(struct platform_device *pdev)
{
- struct jz4740_glue *glue = platform_get_drvdata(pdev);
+ struct jz4740_glue *glue = platform_get_drvdata(pdev);
- platform_device_unregister(glue->musb);
+ platform_device_unregister(glue->pdev);
clk_disable_unprepare(glue->clk);
return 0;
}
-#ifdef CONFIG_OF
static const struct of_device_id jz4740_musb_of_match[] = {
- { .compatible = "ingenic,jz4740-musb" },
- {},
+ { .compatible = "ingenic,jz4740-musb", .data = &jz4740_musb_pdata },
+ { .compatible = "ingenic,jz4770-musb", .data = &jz4770_musb_pdata },
+ { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, jz4740_musb_of_match);
-#endif
static struct platform_driver jz4740_driver = {
.probe = jz4740_probe,
.remove = jz4740_remove,
.driver = {
.name = "musb-jz4740",
- .of_match_table = of_match_ptr(jz4740_musb_of_match),
+ .of_match_table = jz4740_musb_of_match,
},
};
diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c
new file mode 100644
index 0000000..6b92d03
--- /dev/null
+++ b/drivers/usb/musb/mediatek.c
@@ -0,0 +1,592 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 MediaTek Inc.
+ *
+ * Author:
+ * Min Guo <min.guo@mediatek.com>
+ * Yonglong Wu <yonglong.wu@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/usb/role.h>
+#include <linux/usb/usb_phy_generic.h>
+#include "musb_core.h"
+#include "musb_dma.h"
+
+#define USB_L1INTS 0x00a0
+#define USB_L1INTM 0x00a4
+#define MTK_MUSB_TXFUNCADDR 0x0480
+
+/* MediaTek controller toggle enable and status reg */
+#define MUSB_RXTOG 0x80
+#define MUSB_RXTOGEN 0x82
+#define MUSB_TXTOG 0x84
+#define MUSB_TXTOGEN 0x86
+#define MTK_TOGGLE_EN GENMASK(15, 0)
+
+#define TX_INT_STATUS BIT(0)
+#define RX_INT_STATUS BIT(1)
+#define USBCOM_INT_STATUS BIT(2)
+#define DMA_INT_STATUS BIT(3)
+
+#define DMA_INTR_STATUS_MSK GENMASK(7, 0)
+#define DMA_INTR_UNMASK_SET_MSK GENMASK(31, 24)
+
+struct mtk_glue {
+ struct device *dev;
+ struct musb *musb;
+ struct platform_device *musb_pdev;
+ struct platform_device *usb_phy;
+ struct phy *phy;
+ struct usb_phy *xceiv;
+ enum phy_mode phy_mode;
+ struct clk *main;
+ struct clk *mcu;
+ struct clk *univpll;
+ enum usb_role role;
+ struct usb_role_switch *role_sw;
+};
+
+static int mtk_musb_clks_get(struct mtk_glue *glue)
+{
+ struct device *dev = glue->dev;
+
+ glue->main = devm_clk_get(dev, "main");
+ if (IS_ERR(glue->main)) {
+ dev_err(dev, "fail to get main clock\n");
+ return PTR_ERR(glue->main);
+ }
+
+ glue->mcu = devm_clk_get(dev, "mcu");
+ if (IS_ERR(glue->mcu)) {
+ dev_err(dev, "fail to get mcu clock\n");
+ return PTR_ERR(glue->mcu);
+ }
+
+ glue->univpll = devm_clk_get(dev, "univpll");
+ if (IS_ERR(glue->univpll)) {
+ dev_err(dev, "fail to get univpll clock\n");
+ return PTR_ERR(glue->univpll);
+ }
+
+ return 0;
+}
+
+static int mtk_musb_clks_enable(struct mtk_glue *glue)
+{
+ int ret;
+
+ ret = clk_prepare_enable(glue->main);
+ if (ret) {
+ dev_err(glue->dev, "failed to enable main clock\n");
+ goto err_main_clk;
+ }
+
+ ret = clk_prepare_enable(glue->mcu);
+ if (ret) {
+ dev_err(glue->dev, "failed to enable mcu clock\n");
+ goto err_mcu_clk;
+ }
+
+ ret = clk_prepare_enable(glue->univpll);
+ if (ret) {
+ dev_err(glue->dev, "failed to enable univpll clock\n");
+ goto err_univpll_clk;
+ }
+
+ return 0;
+
+err_univpll_clk:
+ clk_disable_unprepare(glue->mcu);
+err_mcu_clk:
+ clk_disable_unprepare(glue->main);
+err_main_clk:
+ return ret;
+}
+
+static void mtk_musb_clks_disable(struct mtk_glue *glue)
+{
+ clk_disable_unprepare(glue->univpll);
+ clk_disable_unprepare(glue->mcu);
+ clk_disable_unprepare(glue->main);
+}
+
+static int mtk_otg_switch_set(struct mtk_glue *glue, enum usb_role role)
+{
+ struct musb *musb = glue->musb;
+ u8 devctl = readb(musb->mregs + MUSB_DEVCTL);
+ enum usb_role new_role;
+
+ if (role == glue->role)
+ return 0;
+
+ switch (role) {
+ case USB_ROLE_HOST:
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
+ glue->phy_mode = PHY_MODE_USB_HOST;
+ new_role = USB_ROLE_HOST;
+ if (glue->role == USB_ROLE_NONE)
+ phy_power_on(glue->phy);
+
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+ MUSB_HST_MODE(musb);
+ break;
+ case USB_ROLE_DEVICE:
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
+ glue->phy_mode = PHY_MODE_USB_DEVICE;
+ new_role = USB_ROLE_DEVICE;
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+ if (glue->role == USB_ROLE_NONE)
+ phy_power_on(glue->phy);
+
+ MUSB_DEV_MODE(musb);
+ break;
+ case USB_ROLE_NONE:
+ glue->phy_mode = PHY_MODE_USB_OTG;
+ new_role = USB_ROLE_NONE;
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+ if (glue->role != USB_ROLE_NONE)
+ phy_power_off(glue->phy);
+
+ break;
+ default:
+ dev_err(glue->dev, "Invalid State\n");
+ return -EINVAL;
+ }
+
+ glue->role = new_role;
+ phy_set_mode(glue->phy, glue->phy_mode);
+
+ return 0;
+}
+
+static int musb_usb_role_sx_set(struct usb_role_switch *sw, enum usb_role role)
+{
+ return mtk_otg_switch_set(usb_role_switch_get_drvdata(sw), role);
+}
+
+static enum usb_role musb_usb_role_sx_get(struct usb_role_switch *sw)
+{
+ struct mtk_glue *glue = usb_role_switch_get_drvdata(sw);
+
+ return glue->role;
+}
+
+static int mtk_otg_switch_init(struct mtk_glue *glue)
+{
+ struct usb_role_switch_desc role_sx_desc = { 0 };
+
+ role_sx_desc.set = musb_usb_role_sx_set;
+ role_sx_desc.get = musb_usb_role_sx_get;
+ role_sx_desc.fwnode = dev_fwnode(glue->dev);
+ role_sx_desc.driver_data = glue;
+ glue->role_sw = usb_role_switch_register(glue->dev, &role_sx_desc);
+
+ return PTR_ERR_OR_ZERO(glue->role_sw);
+}
+
+static void mtk_otg_switch_exit(struct mtk_glue *glue)
+{
+ return usb_role_switch_unregister(glue->role_sw);
+}
+
+static irqreturn_t generic_interrupt(int irq, void *__hci)
+{
+ unsigned long flags;
+ irqreturn_t retval = IRQ_NONE;
+ struct musb *musb = __hci;
+
+ spin_lock_irqsave(&musb->lock, flags);
+ musb->int_usb = musb_clearb(musb->mregs, MUSB_INTRUSB);
+ musb->int_rx = musb_clearw(musb->mregs, MUSB_INTRRX);
+ musb->int_tx = musb_clearw(musb->mregs, MUSB_INTRTX);
+
+ if ((musb->int_usb & MUSB_INTR_RESET) && !is_host_active(musb)) {
+ /* ep0 FADDR must be 0 when (re)entering peripheral mode */
+ musb_ep_select(musb->mregs, 0);
+ musb_writeb(musb->mregs, MUSB_FADDR, 0);
+ }
+
+ if (musb->int_usb || musb->int_tx || musb->int_rx)
+ retval = musb_interrupt(musb);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return retval;
+}
+
+static irqreturn_t mtk_musb_interrupt(int irq, void *dev_id)
+{
+ irqreturn_t retval = IRQ_NONE;
+ struct musb *musb = (struct musb *)dev_id;
+ u32 l1_ints;
+
+ l1_ints = musb_readl(musb->mregs, USB_L1INTS) &
+ musb_readl(musb->mregs, USB_L1INTM);
+
+ if (l1_ints & (TX_INT_STATUS | RX_INT_STATUS | USBCOM_INT_STATUS))
+ retval = generic_interrupt(irq, musb);
+
+#if defined(CONFIG_USB_INVENTRA_DMA)
+ if (l1_ints & DMA_INT_STATUS)
+ retval = dma_controller_irq(irq, musb->dma_controller);
+#endif
+ return retval;
+}
+
+static u32 mtk_musb_busctl_offset(u8 epnum, u16 offset)
+{
+ return MTK_MUSB_TXFUNCADDR + offset + 8 * epnum;
+}
+
+static u8 mtk_musb_clearb(void __iomem *addr, unsigned int offset)
+{
+ u8 data;
+
+ /* W1C */
+ data = musb_readb(addr, offset);
+ musb_writeb(addr, offset, data);
+ return data;
+}
+
+static u16 mtk_musb_clearw(void __iomem *addr, unsigned int offset)
+{
+ u16 data;
+
+ /* W1C */
+ data = musb_readw(addr, offset);
+ musb_writew(addr, offset, data);
+ return data;
+}
+
+static int mtk_musb_set_mode(struct musb *musb, u8 mode)
+{
+ struct device *dev = musb->controller;
+ struct mtk_glue *glue = dev_get_drvdata(dev->parent);
+ enum phy_mode new_mode;
+ enum usb_role new_role;
+
+ switch (mode) {
+ case MUSB_HOST:
+ new_mode = PHY_MODE_USB_HOST;
+ new_role = USB_ROLE_HOST;
+ break;
+ case MUSB_PERIPHERAL:
+ new_mode = PHY_MODE_USB_DEVICE;
+ new_role = USB_ROLE_DEVICE;
+ break;
+ case MUSB_OTG:
+ new_mode = PHY_MODE_USB_OTG;
+ new_role = USB_ROLE_NONE;
+ break;
+ default:
+ dev_err(glue->dev, "Invalid mode request\n");
+ return -EINVAL;
+ }
+
+ if (glue->phy_mode == new_mode)
+ return 0;
+
+ if (musb->port_mode != MUSB_OTG) {
+ dev_err(glue->dev, "Does not support changing modes\n");
+ return -EINVAL;
+ }
+
+ mtk_otg_switch_set(glue, new_role);
+ return 0;
+}
+
+static int mtk_musb_init(struct musb *musb)
+{
+ struct device *dev = musb->controller;
+ struct mtk_glue *glue = dev_get_drvdata(dev->parent);
+ int ret;
+
+ glue->musb = musb;
+ musb->phy = glue->phy;
+ musb->xceiv = glue->xceiv;
+ musb->is_host = false;
+ musb->isr = mtk_musb_interrupt;
+
+ /* Set TX/RX toggle enable */
+ musb_writew(musb->mregs, MUSB_TXTOGEN, MTK_TOGGLE_EN);
+ musb_writew(musb->mregs, MUSB_RXTOGEN, MTK_TOGGLE_EN);
+
+ if (musb->port_mode == MUSB_OTG) {
+ ret = mtk_otg_switch_init(glue);
+ if (ret)
+ return ret;
+ }
+
+ ret = phy_init(glue->phy);
+ if (ret)
+ goto err_phy_init;
+
+ ret = phy_power_on(glue->phy);
+ if (ret)
+ goto err_phy_power_on;
+
+ phy_set_mode(glue->phy, glue->phy_mode);
+
+#if defined(CONFIG_USB_INVENTRA_DMA)
+ musb_writel(musb->mregs, MUSB_HSDMA_INTR,
+ DMA_INTR_STATUS_MSK | DMA_INTR_UNMASK_SET_MSK);
+#endif
+ musb_writel(musb->mregs, USB_L1INTM, TX_INT_STATUS | RX_INT_STATUS |
+ USBCOM_INT_STATUS | DMA_INT_STATUS);
+ return 0;
+
+err_phy_power_on:
+ phy_exit(glue->phy);
+err_phy_init:
+ mtk_otg_switch_exit(glue);
+ return ret;
+}
+
+static u16 mtk_musb_get_toggle(struct musb_qh *qh, int is_out)
+{
+ struct musb *musb = qh->hw_ep->musb;
+ u8 epnum = qh->hw_ep->epnum;
+ u16 toggle;
+
+ toggle = musb_readw(musb->mregs, is_out ? MUSB_TXTOG : MUSB_RXTOG);
+ return toggle & (1 << epnum);
+}
+
+static u16 mtk_musb_set_toggle(struct musb_qh *qh, int is_out, struct urb *urb)
+{
+ struct musb *musb = qh->hw_ep->musb;
+ u8 epnum = qh->hw_ep->epnum;
+ u16 value, toggle;
+
+ toggle = usb_gettoggle(urb->dev, qh->epnum, is_out);
+
+ if (is_out) {
+ value = musb_readw(musb->mregs, MUSB_TXTOG);
+ value |= toggle << epnum;
+ musb_writew(musb->mregs, MUSB_TXTOG, value);
+ } else {
+ value = musb_readw(musb->mregs, MUSB_RXTOG);
+ value |= toggle << epnum;
+ musb_writew(musb->mregs, MUSB_RXTOG, value);
+ }
+
+ return 0;
+}
+
+static int mtk_musb_exit(struct musb *musb)
+{
+ struct device *dev = musb->controller;
+ struct mtk_glue *glue = dev_get_drvdata(dev->parent);
+
+ mtk_otg_switch_exit(glue);
+ phy_power_off(glue->phy);
+ phy_exit(glue->phy);
+ mtk_musb_clks_disable(glue);
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ return 0;
+}
+
+static const struct musb_platform_ops mtk_musb_ops = {
+ .quirks = MUSB_DMA_INVENTRA,
+ .init = mtk_musb_init,
+ .get_toggle = mtk_musb_get_toggle,
+ .set_toggle = mtk_musb_set_toggle,
+ .exit = mtk_musb_exit,
+#ifdef CONFIG_USB_INVENTRA_DMA
+ .dma_init = musbhs_dma_controller_create_noirq,
+ .dma_exit = musbhs_dma_controller_destroy,
+#endif
+ .clearb = mtk_musb_clearb,
+ .clearw = mtk_musb_clearw,
+ .busctl_offset = mtk_musb_busctl_offset,
+ .set_mode = mtk_musb_set_mode,
+};
+
+#define MTK_MUSB_MAX_EP_NUM 8
+#define MTK_MUSB_RAM_BITS 11
+
+static struct musb_fifo_cfg mtk_musb_mode_cfg[] = {
+ { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
+ { .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 1024, },
+ { .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 1024, },
+ { .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, },
+ { .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 64, },
+};
+
+static const struct musb_hdrc_config mtk_musb_hdrc_config = {
+ .fifo_cfg = mtk_musb_mode_cfg,
+ .fifo_cfg_size = ARRAY_SIZE(mtk_musb_mode_cfg),
+ .multipoint = true,
+ .dyn_fifo = true,
+ .num_eps = MTK_MUSB_MAX_EP_NUM,
+ .ram_bits = MTK_MUSB_RAM_BITS,
+};
+
+static const struct platform_device_info mtk_dev_info = {
+ .name = "musb-hdrc",
+ .id = PLATFORM_DEVID_AUTO,
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
+static int mtk_musb_probe(struct platform_device *pdev)
+{
+ struct musb_hdrc_platform_data *pdata;
+ struct mtk_glue *glue;
+ struct platform_device_info pinfo;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ return -ENOMEM;
+
+ glue->dev = dev;
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = of_platform_populate(np, NULL, NULL, dev);
+ if (ret) {
+ dev_err(dev, "failed to create child devices at %p\n", np);
+ return ret;
+ }
+
+ ret = mtk_musb_clks_get(glue);
+ if (ret)
+ return ret;
+
+ pdata->config = &mtk_musb_hdrc_config;
+ pdata->platform_ops = &mtk_musb_ops;
+ pdata->mode = usb_get_dr_mode(dev);
+
+ if (IS_ENABLED(CONFIG_USB_MUSB_HOST))
+ pdata->mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_MUSB_GADGET))
+ pdata->mode = USB_DR_MODE_PERIPHERAL;
+
+ switch (pdata->mode) {
+ case USB_DR_MODE_HOST:
+ glue->phy_mode = PHY_MODE_USB_HOST;
+ glue->role = USB_ROLE_HOST;
+ break;
+ case USB_DR_MODE_PERIPHERAL:
+ glue->phy_mode = PHY_MODE_USB_DEVICE;
+ glue->role = USB_ROLE_DEVICE;
+ break;
+ case USB_DR_MODE_OTG:
+ glue->phy_mode = PHY_MODE_USB_OTG;
+ glue->role = USB_ROLE_NONE;
+ break;
+ default:
+ dev_err(&pdev->dev, "Error 'dr_mode' property\n");
+ return -EINVAL;
+ }
+
+ glue->phy = devm_of_phy_get_by_index(dev, np, 0);
+ if (IS_ERR(glue->phy)) {
+ dev_err(dev, "fail to getting phy %ld\n",
+ PTR_ERR(glue->phy));
+ return PTR_ERR(glue->phy);
+ }
+
+ glue->usb_phy = usb_phy_generic_register();
+ if (IS_ERR(glue->usb_phy)) {
+ dev_err(dev, "fail to registering usb-phy %ld\n",
+ PTR_ERR(glue->usb_phy));
+ return PTR_ERR(glue->usb_phy);
+ }
+
+ glue->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ if (IS_ERR(glue->xceiv)) {
+ ret = PTR_ERR(glue->xceiv);
+ dev_err(dev, "fail to getting usb-phy %d\n", ret);
+ goto err_unregister_usb_phy;
+ }
+
+ platform_set_drvdata(pdev, glue);
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ ret = mtk_musb_clks_enable(glue);
+ if (ret)
+ goto err_enable_clk;
+
+ pinfo = mtk_dev_info;
+ pinfo.parent = dev;
+ pinfo.res = pdev->resource;
+ pinfo.num_res = pdev->num_resources;
+ pinfo.data = pdata;
+ pinfo.size_data = sizeof(*pdata);
+
+ glue->musb_pdev = platform_device_register_full(&pinfo);
+ if (IS_ERR(glue->musb_pdev)) {
+ ret = PTR_ERR(glue->musb_pdev);
+ dev_err(dev, "failed to register musb device: %d\n", ret);
+ goto err_device_register;
+ }
+
+ return 0;
+
+err_device_register:
+ mtk_musb_clks_disable(glue);
+err_enable_clk:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+err_unregister_usb_phy:
+ usb_phy_generic_unregister(glue->usb_phy);
+ return ret;
+}
+
+static int mtk_musb_remove(struct platform_device *pdev)
+{
+ struct mtk_glue *glue = platform_get_drvdata(pdev);
+ struct platform_device *usb_phy = glue->usb_phy;
+
+ platform_device_unregister(glue->musb_pdev);
+ usb_phy_generic_unregister(usb_phy);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mtk_musb_match[] = {
+ {.compatible = "mediatek,mtk-musb",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_musb_match);
+#endif
+
+static struct platform_driver mtk_musb_driver = {
+ .probe = mtk_musb_probe,
+ .remove = mtk_musb_remove,
+ .driver = {
+ .name = "musb-mtk",
+ .of_match_table = of_match_ptr(mtk_musb_match),
+ },
+};
+
+module_platform_driver(mtk_musb_driver);
+
+MODULE_DESCRIPTION("MediaTek MUSB Glue Layer");
+MODULE_AUTHOR("Min Guo <min.guo@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c
deleted file mode 100644
index 5f04f8e..0000000
--- a/drivers/usb/musb/musb_am335x.c
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/module.h>
-#include <linux/of_platform.h>
-
-static int am335x_child_probe(struct platform_device *pdev)
-{
- int ret;
-
- pm_runtime_enable(&pdev->dev);
-
- ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
- if (ret)
- goto err;
-
- return 0;
-err:
- pm_runtime_disable(&pdev->dev);
- return ret;
-}
-
-static const struct of_device_id am335x_child_of_match[] = {
- { .compatible = "ti,am33xx-usb" },
- { },
-};
-MODULE_DEVICE_TABLE(of, am335x_child_of_match);
-
-static struct platform_driver am335x_child_driver = {
- .probe = am335x_child_probe,
- .driver = {
- .name = "am335x-usb-childs",
- .of_match_table = am335x_child_of_match,
- },
-};
-
-static int __init am335x_child_init(void)
-{
- return platform_driver_register(&am335x_child_driver);
-}
-module_init(am335x_child_init);
-
-MODULE_DESCRIPTION("AM33xx child devices");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 18c79bc..4c8f011 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -73,6 +73,7 @@
#include <linux/prefetch.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
#include <linux/usb/of.h>
@@ -246,7 +247,7 @@
return 0x80 + (0x08 * epnum) + offset;
}
-static u8 musb_default_readb(const void __iomem *addr, unsigned offset)
+static u8 musb_default_readb(void __iomem *addr, u32 offset)
{
u8 data = __raw_readb(addr + offset);
@@ -254,13 +255,13 @@
return data;
}
-static void musb_default_writeb(void __iomem *addr, unsigned offset, u8 data)
+static void musb_default_writeb(void __iomem *addr, u32 offset, u8 data)
{
trace_musb_writeb(__builtin_return_address(0), addr, offset, data);
__raw_writeb(data, addr + offset);
}
-static u16 musb_default_readw(const void __iomem *addr, unsigned offset)
+static u16 musb_default_readw(void __iomem *addr, u32 offset)
{
u16 data = __raw_readw(addr + offset);
@@ -268,12 +269,44 @@
return data;
}
-static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data)
+static void musb_default_writew(void __iomem *addr, u32 offset, u16 data)
{
trace_musb_writew(__builtin_return_address(0), addr, offset, data);
__raw_writew(data, addr + offset);
}
+static u16 musb_default_get_toggle(struct musb_qh *qh, int is_out)
+{
+ void __iomem *epio = qh->hw_ep->regs;
+ u16 csr;
+
+ if (is_out)
+ csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE;
+ else
+ csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE;
+
+ return csr;
+}
+
+static u16 musb_default_set_toggle(struct musb_qh *qh, int is_out,
+ struct urb *urb)
+{
+ u16 csr;
+ u16 toggle;
+
+ toggle = usb_gettoggle(urb->dev, qh->epnum, is_out);
+
+ if (is_out)
+ csr = toggle ? (MUSB_TXCSR_H_WR_DATATOGGLE
+ | MUSB_TXCSR_H_DATATOGGLE)
+ : MUSB_TXCSR_CLRDATATOG;
+ else
+ csr = toggle ? (MUSB_RXCSR_H_WR_DATATOGGLE
+ | MUSB_RXCSR_H_DATATOGGLE) : 0;
+
+ return csr;
+}
+
/*
* Load an endpoint's FIFO
*/
@@ -364,19 +397,25 @@
/*
* Old style IO functions
*/
-u8 (*musb_readb)(const void __iomem *addr, unsigned offset);
+u8 (*musb_readb)(void __iomem *addr, u32 offset);
EXPORT_SYMBOL_GPL(musb_readb);
-void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data);
+void (*musb_writeb)(void __iomem *addr, u32 offset, u8 data);
EXPORT_SYMBOL_GPL(musb_writeb);
-u16 (*musb_readw)(const void __iomem *addr, unsigned offset);
+u8 (*musb_clearb)(void __iomem *addr, u32 offset);
+EXPORT_SYMBOL_GPL(musb_clearb);
+
+u16 (*musb_readw)(void __iomem *addr, u32 offset);
EXPORT_SYMBOL_GPL(musb_readw);
-void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data);
+void (*musb_writew)(void __iomem *addr, u32 offset, u16 data);
EXPORT_SYMBOL_GPL(musb_writew);
-u32 musb_readl(const void __iomem *addr, unsigned offset)
+u16 (*musb_clearw)(void __iomem *addr, u32 offset);
+EXPORT_SYMBOL_GPL(musb_clearw);
+
+u32 musb_readl(void __iomem *addr, u32 offset)
{
u32 data = __raw_readl(addr + offset);
@@ -385,7 +424,7 @@
}
EXPORT_SYMBOL_GPL(musb_readl);
-void musb_writel(void __iomem *addr, unsigned offset, u32 data)
+void musb_writel(void __iomem *addr, u32 offset, u32 data)
{
trace_musb_writel(__builtin_return_address(0), addr, offset, data);
__raw_writel(data, addr + offset);
@@ -414,6 +453,108 @@
return hw_ep->musb->io.write_fifo(hw_ep, len, src);
}
+static u8 musb_read_devctl(struct musb *musb)
+{
+ return musb_readb(musb->mregs, MUSB_DEVCTL);
+}
+
+/**
+ * musb_set_host - set and initialize host mode
+ * @musb: musb controller driver data
+ *
+ * At least some musb revisions need to enable devctl session bit in
+ * peripheral mode to switch to host mode. Initializes things to host
+ * mode and sets A_IDLE. SoC glue needs to advance state further
+ * based on phy provided VBUS state.
+ *
+ * Note that the SoC glue code may need to wait for musb to settle
+ * on enable before calling this to avoid babble.
+ */
+int musb_set_host(struct musb *musb)
+{
+ int error = 0;
+ u8 devctl;
+
+ if (!musb)
+ return -EINVAL;
+
+ devctl = musb_read_devctl(musb);
+ if (!(devctl & MUSB_DEVCTL_BDEVICE)) {
+ dev_info(musb->controller,
+ "%s: already in host mode: %02x\n",
+ __func__, devctl);
+ goto init_data;
+ }
+
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ error = readx_poll_timeout(musb_read_devctl, musb, devctl,
+ !(devctl & MUSB_DEVCTL_BDEVICE), 5000,
+ 1000000);
+ if (error) {
+ dev_err(musb->controller, "%s: could not set host: %02x\n",
+ __func__, devctl);
+
+ return error;
+ }
+
+init_data:
+ musb->is_active = 1;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(musb_set_host);
+
+/**
+ * musb_set_peripheral - set and initialize peripheral mode
+ * @musb: musb controller driver data
+ *
+ * Clears devctl session bit and initializes things for peripheral
+ * mode and sets B_IDLE. SoC glue needs to advance state further
+ * based on phy provided VBUS state.
+ */
+int musb_set_peripheral(struct musb *musb)
+{
+ int error = 0;
+ u8 devctl;
+
+ if (!musb)
+ return -EINVAL;
+
+ devctl = musb_read_devctl(musb);
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ dev_info(musb->controller,
+ "%s: already in peripheral mode: %02x\n",
+ __func__, devctl);
+
+ goto init_data;
+ }
+
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ error = readx_poll_timeout(musb_read_devctl, musb, devctl,
+ devctl & MUSB_DEVCTL_BDEVICE, 5000,
+ 1000000);
+ if (error) {
+ dev_err(musb->controller, "%s: could not set peripheral: %02x\n",
+ __func__, devctl);
+
+ return error;
+ }
+
+init_data:
+ musb->is_active = 0;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(musb_set_peripheral);
+
/*-------------------------------------------------------------------------*/
/* for high speed test mode; see USB 2.0 spec 7.1.20 */
@@ -711,7 +852,7 @@
case OTG_STATE_B_IDLE:
if (!musb->is_active)
break;
- /* fall through */
+ fallthrough;
case OTG_STATE_B_PERIPHERAL:
musb_g_suspend(musb);
musb->is_active = musb->g.b_hnp_enable;
@@ -831,9 +972,8 @@
case OTG_STATE_A_PERIPHERAL:
musb_hnp_stop(musb);
musb_root_disconnect(musb);
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_B_WAIT_ACON:
- /* FALLTHROUGH */
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_IDLE:
musb_g_disconnect(musb);
@@ -868,7 +1008,7 @@
switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND:
musb_g_reset(musb);
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
/* never use invalid T(a_wait_bcon) */
musb_dbg(musb, "HNP: in %s, %d msec timeout",
@@ -889,7 +1029,7 @@
break;
case OTG_STATE_B_IDLE:
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_B_PERIPHERAL:
musb_g_reset(musb);
break;
@@ -909,7 +1049,6 @@
* @param musb instance pointer
* @param int_usb register contents
* @param devctl
- * @param power
*/
static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
@@ -1015,7 +1154,6 @@
static void musb_disable_interrupts(struct musb *musb)
{
void __iomem *mbase = musb->mregs;
- u16 temp;
/* disable interrupts */
musb_writeb(mbase, MUSB_INTRUSBE, 0);
@@ -1025,9 +1163,9 @@
musb_writew(mbase, MUSB_INTRRXE, 0);
/* flush pending interrupts */
- temp = musb_readb(mbase, MUSB_INTRUSB);
- temp = musb_readw(mbase, MUSB_INTRTX);
- temp = musb_readw(mbase, MUSB_INTRRX);
+ musb_clearb(mbase, MUSB_INTRUSB);
+ musb_clearw(mbase, MUSB_INTRTX);
+ musb_clearw(mbase, MUSB_INTRRX);
}
static void musb_enable_interrupts(struct musb *musb)
@@ -1332,7 +1470,7 @@
switch (fifo_mode) {
default:
fifo_mode = 0;
- /* FALLTHROUGH */
+ fallthrough;
case 0:
cfg = mode_0_cfg;
n = ARRAY_SIZE(mode_0_cfg);
@@ -1498,8 +1636,8 @@
musb->is_multipoint = 0;
type = "";
if (IS_ENABLED(CONFIG_USB) &&
- !IS_ENABLED(CONFIG_USB_OTG_BLACKLIST_HUB)) {
- pr_err("%s: kernel must blacklist external hubs\n",
+ !IS_ENABLED(CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB)) {
+ pr_err("%s: kernel must disable external hubs, please fix the configuration\n",
musb_driver_name);
}
}
@@ -1656,7 +1794,7 @@
EXPORT_SYMBOL_GPL(musb_interrupt);
#ifndef CONFIG_MUSB_PIO_ONLY
-static bool use_dma = 1;
+static bool use_dma = true;
/* "modprobe ... use_dma=0" etc */
module_param(use_dma, bool, 0644);
@@ -1882,7 +2020,7 @@
musb->quirk_retries--;
return;
}
- /* fall through */
+ fallthrough;
case MUSB_QUIRK_A_DISCONNECT_19:
if (musb->quirk_retries && !musb->flush_irq_work) {
musb_dbg(musb,
@@ -2260,10 +2398,19 @@
musb_readb = musb->ops->readb;
if (musb->ops->writeb)
musb_writeb = musb->ops->writeb;
+ if (musb->ops->clearb)
+ musb_clearb = musb->ops->clearb;
+ else
+ musb_clearb = musb_readb;
+
if (musb->ops->readw)
musb_readw = musb->ops->readw;
if (musb->ops->writew)
musb_writew = musb->ops->writew;
+ if (musb->ops->clearw)
+ musb_clearw = musb->ops->clearw;
+ else
+ musb_clearw = musb_readw;
#ifndef CONFIG_MUSB_PIO_ONLY
if (!musb->ops->dma_init || !musb->ops->dma_exit) {
@@ -2285,6 +2432,16 @@
else
musb->io.write_fifo = musb_default_write_fifo;
+ if (musb->ops->get_toggle)
+ musb->io.get_toggle = musb->ops->get_toggle;
+ else
+ musb->io.get_toggle = musb_default_get_toggle;
+
+ if (musb->ops->set_toggle)
+ musb->io.set_toggle = musb->ops->set_toggle;
+ else
+ musb->io.set_toggle = musb_default_set_toggle;
+
if (!musb->xceiv->io_ops) {
musb->xceiv->io_dev = musb->controller;
musb->xceiv->io_priv = musb->mregs;
@@ -2448,14 +2605,12 @@
{
struct device *dev = &pdev->dev;
int irq = platform_get_irq_byname(pdev, "mc");
- struct resource *iomem;
void __iomem *base;
if (irq <= 0)
return -ENODEV;
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(dev, iomem);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -2802,7 +2957,7 @@
static struct platform_driver musb_driver = {
.driver = {
- .name = (char *)musb_driver_name,
+ .name = musb_driver_name,
.bus = &platform_bus_type,
.pm = MUSB_DEV_PM_OPS,
.dev_groups = musb_groups,
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 04203b7..dbe5623 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MUSB OTG driver defines
*
@@ -27,6 +27,7 @@
struct musb;
struct musb_hw_ep;
struct musb_ep;
+struct musb_qh;
/* Helper defines for struct musb->hwvers */
#define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f)
@@ -119,10 +120,14 @@
* @fifo_offset: returns the fifo offset
* @readb: read 8 bits
* @writeb: write 8 bits
+ * @clearb: could be clear-on-readb or W1C
* @readw: read 16 bits
* @writew: write 16 bits
+ * @clearw: could be clear-on-readw or W1C
* @read_fifo: reads the fifo
* @write_fifo: writes to fifo
+ * @get_toggle: platform specific get toggle function
+ * @set_toggle: platform specific set toggle function
* @dma_init: platform specific dma init function
* @dma_exit: platform specific dma exit function
* @init: turns on clocks, sets up platform-specific registers, etc
@@ -161,12 +166,16 @@
u16 fifo_mode;
u32 (*fifo_offset)(u8 epnum);
u32 (*busctl_offset)(u8 epnum, u16 offset);
- u8 (*readb)(const void __iomem *addr, unsigned offset);
- void (*writeb)(void __iomem *addr, unsigned offset, u8 data);
- u16 (*readw)(const void __iomem *addr, unsigned offset);
- void (*writew)(void __iomem *addr, unsigned offset, u16 data);
+ u8 (*readb)(void __iomem *addr, u32 offset);
+ void (*writeb)(void __iomem *addr, u32 offset, u8 data);
+ u8 (*clearb)(void __iomem *addr, u32 offset);
+ u16 (*readw)(void __iomem *addr, u32 offset);
+ void (*writew)(void __iomem *addr, u32 offset, u16 data);
+ u16 (*clearw)(void __iomem *addr, u32 offset);
void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
+ u16 (*get_toggle)(struct musb_qh *qh, int is_out);
+ u16 (*set_toggle)(struct musb_qh *qh, int is_out, struct urb *urb);
struct dma_controller *
(*dma_init) (struct musb *musb, void __iomem *base);
void (*dma_exit)(struct dma_controller *c);
@@ -487,6 +496,9 @@
extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst);
+extern int musb_set_host(struct musb *musb);
+extern int musb_set_peripheral(struct musb *musb);
+
extern void musb_load_testpacket(struct musb *);
extern irqreturn_t musb_interrupt(struct musb *);
diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h
index c444a80..e5b3506 100644
--- a/drivers/usb/musb/musb_debug.h
+++ b/drivers/usb/musb/musb_debug.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MUSB OTG driver debug defines
*
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 0c6204a..30a89aa 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -325,7 +325,7 @@
{
struct dentry *root;
- root = debugfs_create_dir(dev_name(musb->controller), NULL);
+ root = debugfs_create_dir(dev_name(musb->controller), usb_debug_root);
musb->debugfs_root = root;
debugfs_create_file("regdump", S_IRUGO, root, musb, &musb_regdump_fops);
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 8f60271..7d67b69 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MUSB OTG driver DMA controller abstraction
*
@@ -35,6 +35,12 @@
* whether shared with the Inventra core or separate.
*/
+#define MUSB_HSDMA_BASE 0x200
+#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
+#define MUSB_HSDMA_CONTROL 0x4
+#define MUSB_HSDMA_ADDRESS 0x8
+#define MUSB_HSDMA_COUNT 0xc
+
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
#ifdef CONFIG_MUSB_PIO_ONLY
@@ -191,6 +197,9 @@
extern struct dma_controller *
musbhs_dma_controller_create(struct musb *musb, void __iomem *base);
extern void musbhs_dma_controller_destroy(struct dma_controller *c);
+extern struct dma_controller *
+musbhs_dma_controller_create_noirq(struct musb *musb, void __iomem *base);
+extern irqreturn_t dma_controller_irq(int irq, void *private_data);
extern struct dma_controller *
tusb_dma_controller_create(struct musb *musb, void __iomem *base);
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 89d659c..b593583 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -36,7 +36,7 @@
static const struct of_device_id musb_dsps_of_match[];
-/**
+/*
* DSPS musb wrapper register offset.
* FIXME: This should be expanded to have all the wrapper registers from TI DSPS
* musb ips.
@@ -96,7 +96,7 @@
u32 rx_mode;
};
-/**
+/*
* DSPS glue structure.
*/
struct dsps_glue {
@@ -162,7 +162,7 @@
#define USBSS_IRQ_PD_COMP (1 << 2)
-/**
+/*
* dsps_musb_enable - enable interrupts
*/
static void dsps_musb_enable(struct musb *musb)
@@ -188,7 +188,7 @@
dsps_mod_timer(glue, -1);
}
-/**
+/*
* dsps_musb_disable - disable HDRC and flush interrupts
*/
static void dsps_musb_disable(struct musb *musb)
@@ -232,7 +232,7 @@
dsps_mod_timer_optional(glue);
break;
}
- /* fall through */
+ fallthrough;
case OTG_STATE_A_WAIT_BCON:
/* keep VBUS on for host-only mode */
@@ -242,7 +242,7 @@
}
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
skip_session = 1;
- /* fall through */
+ fallthrough;
case OTG_STATE_A_IDLE:
case OTG_STATE_B_IDLE:
@@ -411,7 +411,7 @@
char buf[128];
sprintf(buf, "%s.dsps", dev_name(musb->controller));
- root = debugfs_create_dir(buf, NULL);
+ root = debugfs_create_dir(buf, usb_debug_root);
glue->dbgfs_root = root;
glue->regset.regs = dsps_musb_regs;
@@ -795,7 +795,7 @@
case USB_SPEED_SUPER:
dev_warn(dev, "ignore incorrect maximum_speed "
"(super-speed) setting in dts");
- /* fall through */
+ fallthrough;
default:
config->maximum_speed = USB_SPEED_HIGH;
}
@@ -899,11 +899,13 @@
if (usb_get_dr_mode(&pdev->dev) == USB_DR_MODE_PERIPHERAL) {
ret = dsps_setup_optional_vbus_irq(pdev, glue);
if (ret)
- goto err;
+ goto unregister_pdev;
}
return 0;
+unregister_pdev:
+ platform_device_unregister(glue->musb);
err:
pm_runtime_disable(&pdev->dev);
iounmap(glue->usbss_base);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index ffe462a..fb806b3 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1085,7 +1085,6 @@
u8 epnum;
struct musb_ep *musb_ep;
void __iomem *epio;
- int status = 0;
musb_ep = to_musb_ep(ep);
musb = musb_ep->musb;
@@ -1118,7 +1117,7 @@
musb_dbg(musb, "%s", musb_ep->end_point.name);
- return status;
+ return 0;
}
/*
@@ -1248,9 +1247,11 @@
status = musb_queue_resume_work(musb,
musb_ep_restart_resume_work,
request);
- if (status < 0)
+ if (status < 0) {
dev_err(musb->controller, "%s resume work: %i\n",
__func__, status);
+ list_del(&request->list);
+ }
}
unlock:
@@ -1316,7 +1317,7 @@
}
/*
- * Set or clear the halt bit of an endpoint. A halted enpoint won't tx/rx any
+ * Set or clear the halt bit of an endpoint. A halted endpoint won't tx/rx any
* data but will queue requests.
*
* exported to ep0 code
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index d026636..f49f25b 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MUSB OTG driver peripheral defines
*
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index 91a5027..6d73367 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -311,27 +311,23 @@
goto stall;
switch (ctrlrequest->wIndex >> 8) {
- case 1:
- pr_debug("TEST_J\n");
- /* TEST_J */
+ case USB_TEST_J:
+ pr_debug("USB_TEST_J\n");
musb->test_mode_nr =
MUSB_TEST_J;
break;
- case 2:
- /* TEST_K */
- pr_debug("TEST_K\n");
+ case USB_TEST_K:
+ pr_debug("USB_TEST_K\n");
musb->test_mode_nr =
MUSB_TEST_K;
break;
- case 3:
- /* TEST_SE0_NAK */
- pr_debug("TEST_SE0_NAK\n");
+ case USB_TEST_SE0_NAK:
+ pr_debug("USB_TEST_SE0_NAK\n");
musb->test_mode_nr =
MUSB_TEST_SE0_NAK;
break;
- case 4:
- /* TEST_PACKET */
- pr_debug("TEST_PACKET\n");
+ case USB_TEST_PACKET:
+ pr_debug("USB_TEST_PACKET\n");
musb->test_mode_nr =
MUSB_TEST_PACKET;
break;
@@ -739,7 +735,7 @@
musb_writeb(mbase, MUSB_TESTMODE,
musb->test_mode_nr);
}
- /* FALLTHROUGH */
+ fallthrough;
case MUSB_EP0_STAGE_STATUSOUT:
/* end of sequence #1: write to host (TX state) */
@@ -771,7 +767,7 @@
*/
retval = IRQ_HANDLED;
musb->ep0_state = MUSB_EP0_STAGE_SETUP;
- /* FALLTHROUGH */
+ fallthrough;
case MUSB_EP0_STAGE_SETUP:
setup:
@@ -1028,7 +1024,7 @@
case MUSB_EP0_STAGE_ACKWAIT: /* STALL for zero-length data */
case MUSB_EP0_STAGE_RX: /* control-OUT data */
csr = musb_readw(regs, MUSB_CSR0);
- /* FALLTHROUGH */
+ fallthrough;
/* It's also OK to issue stalls during callbacks when a non-empty
* DATA stage buffer has been read (or even written).
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index fa9922c..30c5e7d 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -286,26 +286,6 @@
spin_lock(&musb->lock);
}
-/* For bulk/interrupt endpoints only */
-static inline void musb_save_toggle(struct musb_qh *qh, int is_in,
- struct urb *urb)
-{
- void __iomem *epio = qh->hw_ep->regs;
- u16 csr;
-
- /*
- * FIXME: the current Mentor DMA code seems to have
- * problems getting toggle correct.
- */
-
- if (is_in)
- csr = musb_readw(epio, MUSB_RXCSR) & MUSB_RXCSR_H_DATATOGGLE;
- else
- csr = musb_readw(epio, MUSB_TXCSR) & MUSB_TXCSR_H_DATATOGGLE;
-
- usb_settoggle(urb->dev, qh->epnum, !is_in, csr ? 1 : 0);
-}
-
/*
* Advance this hardware endpoint's queue, completing the specified URB and
* advancing to either the next URB queued to that qh, or else invalidating
@@ -320,6 +300,7 @@
struct musb_hw_ep *ep = qh->hw_ep;
int ready = qh->is_ready;
int status;
+ u16 toggle;
status = (urb->status == -EINPROGRESS) ? 0 : urb->status;
@@ -327,7 +308,8 @@
switch (qh->type) {
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
- musb_save_toggle(qh, is_in, urb);
+ toggle = musb->io.get_toggle(qh, !is_in);
+ usb_settoggle(urb->dev, qh->epnum, !is_in, toggle ? 1 : 0);
break;
case USB_ENDPOINT_XFER_ISOC:
if (status == 0 && urb->error_count)
@@ -378,7 +360,7 @@
qh = first_qh(head);
break;
}
- /* fall through */
+ fallthrough;
case USB_ENDPOINT_XFER_ISOC:
case USB_ENDPOINT_XFER_INT:
@@ -772,13 +754,8 @@
);
csr |= MUSB_TXCSR_MODE;
- if (!hw_ep->tx_double_buffered) {
- if (usb_gettoggle(urb->dev, qh->epnum, 1))
- csr |= MUSB_TXCSR_H_WR_DATATOGGLE
- | MUSB_TXCSR_H_DATATOGGLE;
- else
- csr |= MUSB_TXCSR_CLRDATATOG;
- }
+ if (!hw_ep->tx_double_buffered)
+ csr |= musb->io.set_toggle(qh, is_out, urb);
musb_writew(epio, MUSB_TXCSR, csr);
/* REVISIT may need to clear FLUSHFIFO ... */
@@ -860,17 +837,12 @@
/* IN/receive */
} else {
- u16 csr;
+ u16 csr = 0;
if (hw_ep->rx_reinit) {
musb_rx_reinit(musb, qh, epnum);
+ csr |= musb->io.set_toggle(qh, is_out, urb);
- /* init new state: toggle and NYET, maybe DMA later */
- if (usb_gettoggle(urb->dev, qh->epnum, 0))
- csr = MUSB_RXCSR_H_WR_DATATOGGLE
- | MUSB_RXCSR_H_DATATOGGLE;
- else
- csr = 0;
if (qh->type == USB_ENDPOINT_XFER_INT)
csr |= MUSB_RXCSR_DISNYET;
@@ -933,6 +905,7 @@
void __iomem *epio = ep->regs;
struct musb_qh *cur_qh, *next_qh;
u16 rx_csr, tx_csr;
+ u16 toggle;
musb_ep_select(mbase, ep->epnum);
if (is_in) {
@@ -970,7 +943,8 @@
urb->actual_length += dma->actual_len;
dma->actual_len = 0L;
}
- musb_save_toggle(cur_qh, is_in, urb);
+ toggle = musb->io.get_toggle(cur_qh, !is_in);
+ usb_settoggle(urb->dev, cur_qh->epnum, !is_in, toggle ? 1 : 0);
if (is_in) {
/* move cur_qh to end of queue */
@@ -1045,7 +1019,7 @@
musb->ep0_stage = MUSB_EP0_OUT;
more = true;
}
- /* FALLTHROUGH */
+ fallthrough;
case MUSB_EP0_OUT:
fifo_count = min_t(size_t, qh->maxpacket,
urb->transfer_buffer_length -
@@ -1800,9 +1774,15 @@
status = -EPIPE;
} else if (rx_csr & MUSB_RXCSR_H_ERROR) {
- musb_dbg(musb, "end %d RX proto error", epnum);
+ dev_err(musb->controller, "ep%d RX three-strikes error", epnum);
- status = -EPROTO;
+ /*
+ * The three-strikes error could only happen when the USB
+ * device is not accessible, for example detached or powered
+ * off. So return the fatal error -ESHUTDOWN so hopefully the
+ * USB device drivers won't immediately resubmit the same URB.
+ */
+ status = -ESHUTDOWN;
musb_writeb(epio, MUSB_RXINTERVAL, 0);
rx_csr &= ~MUSB_RXCSR_H_ERROR;
@@ -2242,7 +2222,7 @@
interval = max_t(u8, epd->bInterval, 1);
break;
}
- /* FALLTHROUGH */
+ fallthrough;
case USB_ENDPOINT_XFER_ISOC:
/* ISO always uses logarithmic encoding */
interval = min_t(u8, epd->bInterval, 16);
@@ -2569,7 +2549,7 @@
struct musb_temp_buffer {
void *kmalloc_ptr;
void *old_xfer_buffer;
- u8 data[0];
+ u8 data[];
};
static void musb_free_temp_buffer(struct urb *urb)
diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h
index 2999845..4804d4d 100644
--- a/drivers/usb/musb/musb_host.h
+++ b/drivers/usb/musb/musb_host.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MUSB OTG driver host defines
*
@@ -97,7 +97,6 @@
static inline void musb_host_rx(struct musb *musb, u8 epnum) {}
static inline void musb_root_disconnect(struct musb *musb) {}
static inline void musb_host_resume_root_hub(struct musb *musb) {}
-static inline void musb_host_poll_rh_status(struct musb *musb) {}
static inline void musb_host_poke_root_hub(struct musb *musb) {}
static inline int musb_port_suspend(struct musb *musb, bool do_suspend)
{
diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h
index 8058a58..12874d3 100644
--- a/drivers/usb/musb/musb_io.h
+++ b/drivers/usb/musb/musb_io.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MUSB OTG driver register I/O
*
@@ -22,6 +22,8 @@
* @read_fifo: platform specific function to read fifo
* @write_fifo: platform specific function to write fifo
* @busctl_offset: platform specific function to get busctl offset
+ * @get_toggle: platform specific function to get toggle
+ * @set_toggle: platform specific function to set toggle
*/
struct musb_io {
u32 (*ep_offset)(u8 epnum, u16 offset);
@@ -30,14 +32,18 @@
void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
u32 (*busctl_offset)(u8 epnum, u16 offset);
+ u16 (*get_toggle)(struct musb_qh *qh, int is_out);
+ u16 (*set_toggle)(struct musb_qh *qh, int is_out, struct urb *urb);
};
/* Do not add new entries here, add them the struct musb_io instead */
-extern u8 (*musb_readb)(const void __iomem *addr, unsigned offset);
-extern void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data);
-extern u16 (*musb_readw)(const void __iomem *addr, unsigned offset);
-extern void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data);
-extern u32 musb_readl(const void __iomem *addr, unsigned offset);
-extern void musb_writel(void __iomem *addr, unsigned offset, u32 data);
+extern u8 (*musb_readb)(void __iomem *addr, u32 offset);
+extern void (*musb_writeb)(void __iomem *addr, u32 offset, u8 data);
+extern u8 (*musb_clearb)(void __iomem *addr, u32 offset);
+extern u16 (*musb_readw)(void __iomem *addr, u32 offset);
+extern void (*musb_writew)(void __iomem *addr, u32 offset, u16 data);
+extern u16 (*musb_clearw)(void __iomem *addr, u32 offset);
+extern u32 musb_readl(void __iomem *addr, u32 offset);
+extern void musb_writel(void __iomem *addr, u32 offset, u32 data);
#endif
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index 5cd7264..5fa1109 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MUSB OTG driver register defines
*
diff --git a/drivers/usb/musb/musb_trace.h b/drivers/usb/musb/musb_trace.h
index a97d618..380ebc7 100644
--- a/drivers/usb/musb/musb_trace.h
+++ b/drivers/usb/musb/musb_trace.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* musb_trace.h - MUSB Controller Trace Support
*
@@ -38,11 +38,12 @@
);
DECLARE_EVENT_CLASS(musb_regb,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u8 data),
TP_ARGS(caller, addr, offset, data),
TP_STRUCT__entry(
__field(void *, caller)
- __field(const void *, addr)
+ __field(const void __iomem *, addr)
__field(unsigned int, offset)
__field(u8, data)
),
@@ -57,21 +58,24 @@
);
DEFINE_EVENT(musb_regb, musb_readb,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u8 data),
TP_ARGS(caller, addr, offset, data)
);
DEFINE_EVENT(musb_regb, musb_writeb,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u8 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u8 data),
TP_ARGS(caller, addr, offset, data)
);
DECLARE_EVENT_CLASS(musb_regw,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u16 data),
TP_ARGS(caller, addr, offset, data),
TP_STRUCT__entry(
__field(void *, caller)
- __field(const void *, addr)
+ __field(const void __iomem *, addr)
__field(unsigned int, offset)
__field(u16, data)
),
@@ -86,21 +90,24 @@
);
DEFINE_EVENT(musb_regw, musb_readw,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u16 data),
TP_ARGS(caller, addr, offset, data)
);
DEFINE_EVENT(musb_regw, musb_writew,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u16 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u16 data),
TP_ARGS(caller, addr, offset, data)
);
DECLARE_EVENT_CLASS(musb_regl,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u32 data),
TP_ARGS(caller, addr, offset, data),
TP_STRUCT__entry(
__field(void *, caller)
- __field(const void *, addr)
+ __field(const void __iomem *, addr)
__field(unsigned int, offset)
__field(u32, data)
),
@@ -115,12 +122,14 @@
);
DEFINE_EVENT(musb_regl, musb_readl,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u32 data),
TP_ARGS(caller, addr, offset, data)
);
DEFINE_EVENT(musb_regl, musb_writel,
- TP_PROTO(void *caller, const void *addr, unsigned int offset, u32 data),
+ TP_PROTO(void *caller, const void __iomem *addr,
+ unsigned int offset, u32 data),
TP_ARGS(caller, addr, offset, data)
);
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index a84ec27..cafc695 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -211,7 +211,7 @@
musb->g.is_a_peripheral = 1;
break;
}
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_A_HOST:
musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
musb->is_active = 0;
@@ -385,25 +385,25 @@
wIndex >>= 8;
switch (wIndex) {
- case 1:
- pr_debug("TEST_J\n");
+ case USB_TEST_J:
+ pr_debug("USB_TEST_J\n");
temp = MUSB_TEST_J;
break;
- case 2:
- pr_debug("TEST_K\n");
+ case USB_TEST_K:
+ pr_debug("USB_TEST_K\n");
temp = MUSB_TEST_K;
break;
- case 3:
- pr_debug("TEST_SE0_NAK\n");
+ case USB_TEST_SE0_NAK:
+ pr_debug("USB_TEST_SE0_NAK\n");
temp = MUSB_TEST_SE0_NAK;
break;
- case 4:
- pr_debug("TEST_PACKET\n");
+ case USB_TEST_PACKET:
+ pr_debug("USB_TEST_PACKET\n");
temp = MUSB_TEST_PACKET;
musb_load_testpacket(musb);
break;
- case 5:
- pr_debug("TEST_FORCE_ENABLE\n");
+ case USB_TEST_FORCE_ENABLE:
+ pr_debug("USB_TEST_FORCE_ENABLE\n");
temp = MUSB_TEST_FORCE_HOST
| MUSB_TEST_FORCE_HS;
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 2d3751d..0aacfc8 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -10,12 +10,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "musb_core.h"
-
-#define MUSB_HSDMA_BASE 0x200
-#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0)
-#define MUSB_HSDMA_CONTROL 0x4
-#define MUSB_HSDMA_ADDRESS 0x8
-#define MUSB_HSDMA_COUNT 0xc
+#include "musb_dma.h"
#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \
(MUSB_HSDMA_BASE + (_bchannel << 4) + _offset)
@@ -268,7 +263,7 @@
return 0;
}
-static irqreturn_t dma_controller_irq(int irq, void *private_data)
+irqreturn_t dma_controller_irq(int irq, void *private_data)
{
struct musb_dma_controller *controller = private_data;
struct musb *musb = controller->private_data;
@@ -289,7 +284,7 @@
spin_lock_irqsave(&musb->lock, flags);
- int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR);
+ int_hsdma = musb_clearb(mbase, MUSB_HSDMA_INTR);
if (!int_hsdma) {
musb_dbg(musb, "spurious DMA irq");
@@ -383,6 +378,7 @@
spin_unlock_irqrestore(&musb->lock, flags);
return retval;
}
+EXPORT_SYMBOL_GPL(dma_controller_irq);
void musbhs_dma_controller_destroy(struct dma_controller *c)
{
@@ -398,18 +394,10 @@
}
EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy);
-struct dma_controller *musbhs_dma_controller_create(struct musb *musb,
- void __iomem *base)
+static struct musb_dma_controller *
+dma_controller_alloc(struct musb *musb, void __iomem *base)
{
struct musb_dma_controller *controller;
- struct device *dev = musb->controller;
- struct platform_device *pdev = to_platform_device(dev);
- int irq = platform_get_irq_byname(pdev, "dma");
-
- if (irq <= 0) {
- dev_err(dev, "No DMA interrupt line!\n");
- return NULL;
- }
controller = kzalloc(sizeof(*controller), GFP_KERNEL);
if (!controller)
@@ -423,6 +411,25 @@
controller->controller.channel_release = dma_channel_release;
controller->controller.channel_program = dma_channel_program;
controller->controller.channel_abort = dma_channel_abort;
+ return controller;
+}
+
+struct dma_controller *
+musbhs_dma_controller_create(struct musb *musb, void __iomem *base)
+{
+ struct musb_dma_controller *controller;
+ struct device *dev = musb->controller;
+ struct platform_device *pdev = to_platform_device(dev);
+ int irq = platform_get_irq_byname(pdev, "dma");
+
+ if (irq <= 0) {
+ dev_err(dev, "No DMA interrupt line!\n");
+ return NULL;
+ }
+
+ controller = dma_controller_alloc(musb, base);
+ if (!controller)
+ return NULL;
if (request_irq(irq, dma_controller_irq, 0,
dev_name(musb->controller), controller)) {
@@ -437,3 +444,16 @@
return &controller->controller;
}
EXPORT_SYMBOL_GPL(musbhs_dma_controller_create);
+
+struct dma_controller *
+musbhs_dma_controller_create_noirq(struct musb *musb, void __iomem *base)
+{
+ struct musb_dma_controller *controller;
+
+ controller = dma_controller_alloc(musb, base);
+ if (!controller)
+ return NULL;
+
+ return &controller->controller;
+}
+EXPORT_SYMBOL_GPL(musbhs_dma_controller_create_noirq);
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 5c93226..4232f1c 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -38,69 +38,6 @@
static struct omap2430_glue *_glue;
-static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
-{
- struct usb_otg *otg = musb->xceiv->otg;
- u8 devctl;
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
- /* HDRC controls CPEN, but beware current surges during device
- * connect. They can trigger transient overcurrent conditions
- * that must be ignored.
- */
-
- devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-
- if (is_on) {
- if (musb->xceiv->otg->state == OTG_STATE_A_IDLE) {
- int loops = 100;
- /* start the session */
- devctl |= MUSB_DEVCTL_SESSION;
- musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
- /*
- * Wait for the musb to set as A device to enable the
- * VBUS
- */
- while (musb_readb(musb->mregs, MUSB_DEVCTL) &
- MUSB_DEVCTL_BDEVICE) {
-
- mdelay(5);
- cpu_relax();
-
- if (time_after(jiffies, timeout)
- || loops-- <= 0) {
- dev_err(musb->controller,
- "configured as A device timeout");
- break;
- }
- }
-
- otg_set_vbus(otg, 1);
- } else {
- musb->is_active = 1;
- musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
- devctl |= MUSB_DEVCTL_SESSION;
- MUSB_HST_MODE(musb);
- }
- } else {
- musb->is_active = 0;
-
- /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
- * jumping right to B_IDLE...
- */
-
- musb->xceiv->otg->state = OTG_STATE_B_IDLE;
- devctl &= ~MUSB_DEVCTL_SESSION;
-
- MUSB_DEV_MODE(musb);
- }
- musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
-
- dev_dbg(musb->controller, "VBUS %s, devctl %02x "
- /* otg %3x conf %08x prcm %08x */ "\n",
- usb_otg_state_string(musb->xceiv->otg->state),
- musb_readb(musb->mregs, MUSB_DEVCTL));
-}
-
static inline void omap2430_low_level_exit(struct musb *musb)
{
u32 l;
@@ -140,24 +77,52 @@
return 0;
}
+/*
+ * HDRC controls CPEN, but beware current surges during device connect.
+ * They can trigger transient overcurrent conditions that must be ignored.
+ *
+ * Note that we're skipping A_WAIT_VFALL -> A_IDLE and jumping right to B_IDLE
+ * as set by musb_set_peripheral().
+ */
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
{
struct musb *musb = glue_to_musb(glue);
- struct musb_hdrc_platform_data *pdata =
- dev_get_platdata(musb->controller);
- struct omap_musb_board_data *data = pdata->board_data;
+ int error;
pm_runtime_get_sync(musb->controller);
+
+ dev_dbg(musb->controller, "VBUS %s, devctl %02x\n",
+ usb_otg_state_string(musb->xceiv->otg->state),
+ musb_readb(musb->mregs, MUSB_DEVCTL));
+
switch (glue->status) {
case MUSB_ID_GROUND:
dev_dbg(musb->controller, "ID GND\n");
-
- musb->xceiv->otg->state = OTG_STATE_A_IDLE;
- musb->xceiv->last_event = USB_EVENT_ID;
- if (musb->gadget_driver) {
- omap_control_usb_set_mode(glue->control_otghs,
- USB_MODE_HOST);
- omap2430_musb_set_vbus(musb, 1);
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_IDLE:
+ error = musb_set_host(musb);
+ if (error)
+ break;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
+ fallthrough;
+ case OTG_STATE_A_WAIT_VRISE:
+ case OTG_STATE_A_WAIT_BCON:
+ case OTG_STATE_A_HOST:
+ /*
+ * On multiple ID ground interrupts just keep enabling
+ * VBUS. At least cpcap VBUS shuts down otherwise.
+ */
+ otg_set_vbus(musb->xceiv->otg, 1);
+ break;
+ default:
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
+ musb->xceiv->last_event = USB_EVENT_ID;
+ if (musb->gadget_driver) {
+ omap_control_usb_set_mode(glue->control_otghs,
+ USB_MODE_HOST);
+ otg_set_vbus(musb->xceiv->otg, 1);
+ }
+ break;
}
break;
@@ -174,12 +139,8 @@
dev_dbg(musb->controller, "VBUS Disconnect\n");
musb->xceiv->last_event = USB_EVENT_NONE;
- if (musb->gadget_driver)
- omap2430_musb_set_vbus(musb, 0);
-
- if (data->interface_type == MUSB_INTERFACE_UTMI)
- otg_set_vbus(musb->xceiv->otg, 0);
-
+ musb_set_peripheral(musb);
+ otg_set_vbus(musb->xceiv->otg, 0);
omap_control_usb_set_mode(glue->control_otghs,
USB_MODE_DISCONNECT);
break;
@@ -226,7 +187,6 @@
u32 l;
int status = 0;
struct device *dev = musb->controller;
- struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
struct musb_hdrc_platform_data *plat = dev_get_platdata(dev);
struct omap_musb_board_data *data = plat->board_data;
@@ -282,50 +242,17 @@
musb_readl(musb->mregs, OTG_INTERFSEL),
musb_readl(musb->mregs, OTG_SIMENABLE));
- if (glue->status != MUSB_UNKNOWN)
- omap_musb_set_mailbox(glue);
-
return 0;
}
static void omap2430_musb_enable(struct musb *musb)
{
- u8 devctl;
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
- struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev);
- struct omap_musb_board_data *data = pdata->board_data;
-
- switch (glue->status) {
-
- case MUSB_ID_GROUND:
- omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST);
- if (data->interface_type != MUSB_INTERFACE_UTMI)
- break;
- devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
- /* start the session */
- devctl |= MUSB_DEVCTL_SESSION;
- musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
- while (musb_readb(musb->mregs, MUSB_DEVCTL) &
- MUSB_DEVCTL_BDEVICE) {
- cpu_relax();
-
- if (time_after(jiffies, timeout)) {
- dev_err(dev, "configured as A device timeout");
- break;
- }
- }
- break;
-
- case MUSB_VBUS_VALID:
- omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
- break;
-
- default:
- break;
- }
+ if (glue->status == MUSB_UNKNOWN)
+ glue->status = MUSB_VBUS_OFF;
+ omap_musb_set_mailbox(glue);
}
static void omap2430_musb_disable(struct musb *musb)
@@ -550,6 +477,9 @@
musb_writel(musb->mregs, OTG_INTERFSEL,
musb->context.otg_interfsel);
+ /* Wait for musb to get oriented. Otherwise we can get babble */
+ usleep_range(200000, 250000);
+
return 0;
}
diff --git a/drivers/usb/musb/omap2430.h b/drivers/usb/musb/omap2430.h
index 859008f..939a036 100644
--- a/drivers/usb/musb/omap2430.h
+++ b/drivers/usb/musb/omap2430.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2006 by Texas Instruments
*/
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index 832a41f..f3f76f2 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -407,7 +407,7 @@
return SUNXI_MUSB_TXFUNCADDR + offset;
}
-static u8 sunxi_musb_readb(const void __iomem *addr, unsigned offset)
+static u8 sunxi_musb_readb(void __iomem *addr, u32 offset)
{
struct sunxi_glue *glue;
@@ -520,7 +520,7 @@
(int)(addr - sunxi_musb->mregs));
}
-static u16 sunxi_musb_readw(const void __iomem *addr, unsigned offset)
+static u16 sunxi_musb_readw(void __iomem *addr, u32 offset)
{
if (addr == sunxi_musb->mregs) {
/* generic control or fifo control reg access */
@@ -781,6 +781,8 @@
pinfo.name = "musb-hdrc";
pinfo.id = PLATFORM_DEVID_AUTO;
pinfo.parent = &pdev->dev;
+ pinfo.fwnode = of_fwnode_handle(pdev->dev.of_node);
+ pinfo.of_node_reused = true;
pinfo.res = pdev->resource;
pinfo.num_res = pdev->num_resources;
pinfo.data = &pdata;
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 3945328..038307f 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -142,7 +142,7 @@
/*
* TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
*/
-static u8 tusb_readb(const void __iomem *addr, unsigned offset)
+static u8 tusb_readb(void __iomem *addr, u32 offset)
{
u16 tmp;
u8 val;
@@ -156,7 +156,7 @@
return val;
}
-static void tusb_writeb(void __iomem *addr, unsigned offset, u8 data)
+static void tusb_writeb(void __iomem *addr, u32 offset, u8 data)
{
u16 tmp;
@@ -190,6 +190,7 @@
}
if (len > 0) {
/* Write the rest 1 - 3 bytes to FIFO */
+ val = 0;
memcpy(&val, buf, len);
musb_writel(fifo, 0, val);
}
@@ -464,7 +465,7 @@
dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n",
usb_otg_state_string(musb->xceiv->otg->state));
}
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_A_IDLE:
tusb_musb_set_vbus(musb, 0);
default:
@@ -1102,6 +1103,11 @@
/* dma address for async dma */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ pr_debug("no async dma resource?\n");
+ ret = -ENODEV;
+ goto done;
+ }
musb->async = mem->start;
/* dma address for sync dma */
diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h
index fd8025b..8a25356 100644
--- a/drivers/usb/musb/tusb6010.h
+++ b/drivers/usb/musb/tusb6010.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Definitions for TUSB6010 USB 2.0 OTG Dual Role controller
*
diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index d19bb3e..d5cf5e8 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -310,9 +310,9 @@
dma_channel->max_len = SZ_16M;
ux500_channel->dma_chan =
- dma_request_slave_channel(dev, chan_names[ch_num]);
+ dma_request_chan(dev, chan_names[ch_num]);
- if (!ux500_channel->dma_chan)
+ if (IS_ERR(ux500_channel->dma_chan))
ux500_channel->dma_chan =
dma_request_channel(mask,
data ?
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 24b4f09..ef4787c 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -162,7 +162,7 @@
config USB_TEGRA_PHY
tristate "NVIDIA Tegra USB PHY Driver"
- depends on ARCH_TEGRA
+ depends on ARCH_TEGRA || COMPILE_TEST
select USB_COMMON
select USB_PHY
select USB_ULPI
@@ -172,7 +172,7 @@
config USB_ULPI
bool "Generic ULPI Transceiver Driver"
- depends on ARM || ARM64
+ depends on ARM || ARM64 || COMPILE_TEST
select USB_ULPI_VIEWPORT
help
Enable this to support ULPI connected USB OTG transceivers which
@@ -184,4 +184,12 @@
Provides read/write operations to the ULPI phy register set for
controllers with a viewport register (e.g. Chipidea/ARC controllers).
+config JZ4770_PHY
+ tristate "Ingenic SoCs Transceiver Driver"
+ depends on MIPS || COMPILE_TEST
+ select USB_PHY
+ help
+ This driver provides PHY support for the USB controller found
+ on the JZ-series and X-series SoCs from Ingenic.
+
endmenu
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index df1d990..b352bdb 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -24,3 +24,4 @@
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
+obj-$(CONFIG_JZ4770_PHY) += phy-jz4770.o
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 4bb4b1d..4c52ba9 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -108,7 +108,8 @@
USB_IDLE = 0,
USB_PERIPHERAL,
USB_HOST,
- USB_DEDICATED_CHG
+ USB_DEDICATED_CHG,
+ USB_UART
};
/* Register USB_LINK_STATUS interrupt */
@@ -330,7 +331,7 @@
switch (lsts) {
case USB_LINK_ACA_RID_B_8505:
event = UX500_MUSB_RIDB;
- /* Fall through */
+ fallthrough;
case USB_LINK_NOT_CONFIGURED_8505:
case USB_LINK_RESERVED0_8505:
case USB_LINK_RESERVED1_8505:
@@ -351,7 +352,7 @@
case USB_LINK_ACA_RID_C_NM_8505:
event = UX500_MUSB_RIDC;
- /* Fall through */
+ fallthrough;
case USB_LINK_STD_HOST_NC_8505:
case USB_LINK_STD_HOST_C_NS_8505:
case USB_LINK_STD_HOST_C_S_8505:
@@ -370,7 +371,7 @@
case USB_LINK_ACA_RID_A_8505:
case USB_LINK_ACA_DOCK_CHGR_8505:
event = UX500_MUSB_RIDA;
- /* Fall through */
+ fallthrough;
case USB_LINK_HM_IDGND_8505:
if (ab->mode == USB_IDLE) {
ab->mode = USB_HOST;
@@ -393,6 +394,24 @@
usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
break;
+ /*
+ * FIXME: For now we rely on the boot firmware to set up the necessary
+ * PHY/pin configuration for UART mode.
+ *
+ * AB8505 does not seem to report any status change for UART cables,
+ * possibly because it cannot detect them autonomously.
+ * We may need to measure the ID resistance manually to reliably
+ * detect UART cables after bootup.
+ */
+ case USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8505:
+ case USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8505:
+ if (ab->mode == USB_IDLE) {
+ ab->mode = USB_UART;
+ ab8500_usb_peri_phy_en(ab);
+ }
+
+ break;
+
default:
break;
}
@@ -425,7 +444,7 @@
switch (lsts) {
case USB_LINK_ACA_RID_B_8500:
event = UX500_MUSB_RIDB;
- /* Fall through */
+ fallthrough;
case USB_LINK_NOT_CONFIGURED_8500:
case USB_LINK_NOT_VALID_LINK_8500:
ab->mode = USB_IDLE;
@@ -442,7 +461,7 @@
case USB_LINK_ACA_RID_C_HS_8500:
case USB_LINK_ACA_RID_C_HS_CHIRP_8500:
event = UX500_MUSB_RIDC;
- /* Fall through */
+ fallthrough;
case USB_LINK_STD_HOST_NC_8500:
case USB_LINK_STD_HOST_C_NS_8500:
case USB_LINK_STD_HOST_C_S_8500:
@@ -462,7 +481,7 @@
case USB_LINK_ACA_RID_A_8500:
event = UX500_MUSB_RIDA;
- /* Fall through */
+ fallthrough;
case USB_LINK_HM_IDGND_8500:
if (ab->mode == USB_IDLE) {
ab->mode = USB_HOST;
@@ -499,7 +518,7 @@
* 3. Enable AB regulators
* 4. Enable USB phy
* 5. Reset the musb controller
- * 6. Switch the ULPI GPIO pins to fucntion mode
+ * 6. Switch the ULPI GPIO pins to function mode
* 7. Enable the musb Peripheral5 clock
* 8. Restore MUSB context
*/
@@ -566,6 +585,11 @@
ab->vbus_draw = 0;
}
+ if (ab->mode == USB_UART) {
+ ab8500_usb_peri_phy_dis(ab);
+ ab->mode = USB_IDLE;
+ }
+
if (is_ab8500_2p0(ab->ab8500)) {
if (ab->mode == USB_DEDICATED_CHG) {
ab8500_usb_wd_linkstatus(ab,
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index d16dfc3..97e6603 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -149,7 +149,6 @@
static int am335x_control_usb_probe(struct platform_device *pdev)
{
- struct resource *res;
struct am335x_control_usb *ctrl_usb;
const struct of_device_id *of_id;
const struct phy_control *phy_ctrl;
@@ -166,13 +165,11 @@
ctrl_usb->dev = &pdev->dev;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
- ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res);
+ ctrl_usb->phy_reg = devm_platform_ioremap_resource_byname(pdev, "phy_ctrl");
if (IS_ERR(ctrl_usb->phy_reg))
return PTR_ERR(ctrl_usb->phy_reg);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wakeup");
- ctrl_usb->wkup = devm_ioremap_resource(&pdev->dev, res);
+ ctrl_usb->wkup = devm_platform_ioremap_resource_byname(pdev, "wakeup");
if (IS_ERR(ctrl_usb->wkup))
return PTR_ERR(ctrl_usb->wkup);
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c
index f5f0568..8524475 100644
--- a/drivers/usb/phy/phy-am335x.c
+++ b/drivers/usb/phy/phy-am335x.c
@@ -57,7 +57,7 @@
am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1);
- ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, NULL);
+ ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen);
if (ret)
return ret;
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 446c7bf..9727042 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -913,10 +913,10 @@
break;
case FSL_USB2_PHY_UTMI_WIDE:
temp |= PORTSC_PTW_16BIT;
- /* fall through */
+ fallthrough;
case FSL_USB2_PHY_UTMI:
temp |= PORTSC_PTS_UTMI;
- /* fall through */
+ fallthrough;
default:
break;
}
@@ -959,159 +959,6 @@
return 0;
}
-/*
- * state file in sysfs
- */
-static ssize_t show_fsl_usb2_otg_state(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct otg_fsm *fsm = &fsl_otg_dev->fsm;
- char *next = buf;
- unsigned size = PAGE_SIZE;
- int t;
-
- mutex_lock(&fsm->lock);
-
- /* basic driver infomation */
- t = scnprintf(next, size,
- DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n",
- DRIVER_VERSION);
- size -= t;
- next += t;
-
- /* Registers */
- t = scnprintf(next, size,
- "OTGSC: 0x%08x\n"
- "PORTSC: 0x%08x\n"
- "USBMODE: 0x%08x\n"
- "USBCMD: 0x%08x\n"
- "USBSTS: 0x%08x\n"
- "USBINTR: 0x%08x\n",
- fsl_readl(&usb_dr_regs->otgsc),
- fsl_readl(&usb_dr_regs->portsc),
- fsl_readl(&usb_dr_regs->usbmode),
- fsl_readl(&usb_dr_regs->usbcmd),
- fsl_readl(&usb_dr_regs->usbsts),
- fsl_readl(&usb_dr_regs->usbintr));
- size -= t;
- next += t;
-
- /* State */
- t = scnprintf(next, size,
- "OTG state: %s\n\n",
- usb_otg_state_string(fsl_otg_dev->phy.otg->state));
- size -= t;
- next += t;
-
- /* State Machine Variables */
- t = scnprintf(next, size,
- "a_bus_req: %d\n"
- "b_bus_req: %d\n"
- "a_bus_resume: %d\n"
- "a_bus_suspend: %d\n"
- "a_conn: %d\n"
- "a_sess_vld: %d\n"
- "a_srp_det: %d\n"
- "a_vbus_vld: %d\n"
- "b_bus_resume: %d\n"
- "b_bus_suspend: %d\n"
- "b_conn: %d\n"
- "b_se0_srp: %d\n"
- "b_ssend_srp: %d\n"
- "b_sess_vld: %d\n"
- "id: %d\n",
- fsm->a_bus_req,
- fsm->b_bus_req,
- fsm->a_bus_resume,
- fsm->a_bus_suspend,
- fsm->a_conn,
- fsm->a_sess_vld,
- fsm->a_srp_det,
- fsm->a_vbus_vld,
- fsm->b_bus_resume,
- fsm->b_bus_suspend,
- fsm->b_conn,
- fsm->b_se0_srp,
- fsm->b_ssend_srp,
- fsm->b_sess_vld,
- fsm->id);
- size -= t;
- next += t;
-
- mutex_unlock(&fsm->lock);
-
- return PAGE_SIZE - size;
-}
-
-static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL);
-
-static struct attribute *fsl_otg_attrs[] = {
- &dev_attr_fsl_usb2_otg_state.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(fsl_otg);
-
-/* Char driver interface to control some OTG input */
-
-/*
- * Handle some ioctl command, such as get otg
- * status and set host suspend
- */
-static long fsl_otg_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- u32 retval = 0;
-
- switch (cmd) {
- case GET_OTG_STATUS:
- retval = fsl_otg_dev->host_working;
- break;
-
- case SET_A_SUSPEND_REQ:
- fsl_otg_dev->fsm.a_suspend_req_inf = arg;
- break;
-
- case SET_A_BUS_DROP:
- fsl_otg_dev->fsm.a_bus_drop = arg;
- break;
-
- case SET_A_BUS_REQ:
- fsl_otg_dev->fsm.a_bus_req = arg;
- break;
-
- case SET_B_BUS_REQ:
- fsl_otg_dev->fsm.b_bus_req = arg;
- break;
-
- default:
- break;
- }
-
- otg_statemachine(&fsl_otg_dev->fsm);
-
- return retval;
-}
-
-static int fsl_otg_open(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int fsl_otg_release(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static const struct file_operations otg_fops = {
- .owner = THIS_MODULE,
- .llseek = NULL,
- .read = NULL,
- .write = NULL,
- .unlocked_ioctl = fsl_otg_ioctl,
- .open = fsl_otg_open,
- .release = fsl_otg_release,
-};
-
static int fsl_otg_probe(struct platform_device *pdev)
{
int ret;
@@ -1133,12 +980,6 @@
return ret;
}
- ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops);
- if (ret) {
- dev_err(&pdev->dev, "unable to register FSL OTG device\n");
- return ret;
- }
-
return ret;
}
@@ -1155,8 +996,6 @@
kfree(fsl_otg_dev->phy.otg);
kfree(fsl_otg_dev);
- unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME);
-
if (pdata->exit)
pdata->exit(pdev);
@@ -1169,7 +1008,6 @@
.driver = {
.name = driver_name,
.owner = THIS_MODULE,
- .dev_groups = fsl_otg_groups,
},
};
diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h
index 43d410f..d70341a 100644
--- a/drivers/usb/phy/phy-fsl-usb.h
+++ b/drivers/usb/phy/phy-fsl-usb.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. */
#include <linux/usb/otg-fsm.h>
@@ -371,21 +371,7 @@
u8 otg_port;
};
-/* For SRP and HNP handle */
-#define FSL_OTG_MAJOR 240
#define FSL_OTG_NAME "fsl-usb2-otg"
-/* Command to OTG driver ioctl */
-#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR
-/* if otg work as host, it should return 1, otherwise return 0 */
-#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int)
-#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int)
-#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int)
-#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int)
-#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int)
-#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int)
-#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int)
-#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int)
-#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int)
void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer);
void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer);
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index a53b89b..661a229 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -21,8 +21,7 @@
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include "phy-generic.h"
@@ -204,8 +203,7 @@
return 0;
}
-int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
- struct usb_phy_generic_platform_data *pdata)
+int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
{
enum usb_phy_type type = USB_PHY_TYPE_USB2;
int err = 0;
@@ -221,28 +219,15 @@
needs_vcc = of_property_read_bool(node, "vcc-supply");
needs_clk = of_property_read_bool(node, "clocks");
- nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
- GPIOD_ASIS);
- err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
- if (!err) {
- nop->gpiod_vbus = devm_gpiod_get_optional(dev,
- "vbus-detect",
- GPIOD_ASIS);
- err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
- }
- } else if (pdata) {
- type = pdata->type;
- clk_rate = pdata->clk_rate;
- needs_vcc = pdata->needs_vcc;
- if (gpio_is_valid(pdata->gpio_reset)) {
- err = devm_gpio_request_one(dev, pdata->gpio_reset,
- GPIOF_ACTIVE_LOW,
- dev_name(dev));
- if (!err)
- nop->gpiod_reset =
- gpio_to_desc(pdata->gpio_reset);
- }
- nop->gpiod_vbus = pdata->gpiod_vbus;
+ }
+ nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_ASIS);
+ err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
+ if (!err) {
+ nop->gpiod_vbus = devm_gpiod_get_optional(dev,
+ "vbus-detect",
+ GPIOD_ASIS);
+ err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
}
if (err == -EPROBE_DEFER)
@@ -308,7 +293,7 @@
if (!nop)
return -ENOMEM;
- err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
+ err = usb_phy_gen_create_phy(dev, nop);
if (err)
return err;
if (nop->gpiod_vbus) {
diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h
index 9728962..7ee8021 100644
--- a/drivers/usb/phy/phy-generic.h
+++ b/drivers/usb/phy/phy-generic.h
@@ -22,7 +22,6 @@
int usb_gen_phy_init(struct usb_phy *phy);
void usb_gen_phy_shutdown(struct usb_phy *phy);
-int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
- struct usb_phy_generic_platform_data *pdata);
+int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop);
#endif
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index 553e257..f13f553 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -7,7 +7,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
@@ -17,7 +17,6 @@
#include <linux/regulator/consumer.h>
#include <linux/usb/gadget.h>
-#include <linux/usb/gpio_vbus.h>
#include <linux/usb/otg.h>
@@ -29,6 +28,8 @@
* Needs to be loaded before the UDC driver that will use it.
*/
struct gpio_vbus_data {
+ struct gpio_desc *vbus_gpiod;
+ struct gpio_desc *pullup_gpiod;
struct usb_phy phy;
struct device *dev;
struct regulator *vbus_draw;
@@ -83,38 +84,30 @@
gpio_vbus->mA = mA;
}
-static int is_vbus_powered(struct gpio_vbus_mach_info *pdata)
+static int is_vbus_powered(struct gpio_vbus_data *gpio_vbus)
{
- int vbus;
-
- vbus = gpio_get_value(pdata->gpio_vbus);
- if (pdata->gpio_vbus_inverted)
- vbus = !vbus;
-
- return vbus;
+ return gpiod_get_value(gpio_vbus->vbus_gpiod);
}
static void gpio_vbus_work(struct work_struct *work)
{
struct gpio_vbus_data *gpio_vbus =
container_of(work, struct gpio_vbus_data, work.work);
- struct gpio_vbus_mach_info *pdata = dev_get_platdata(gpio_vbus->dev);
- int gpio, status, vbus;
+ int status, vbus;
if (!gpio_vbus->phy.otg->gadget)
return;
- vbus = is_vbus_powered(pdata);
+ vbus = is_vbus_powered(gpio_vbus);
if ((vbus ^ gpio_vbus->vbus) == 0)
return;
gpio_vbus->vbus = vbus;
/* Peripheral controllers which manage the pullup themselves won't have
- * gpio_pullup configured here. If it's configured here, we'll do what
- * isp1301_omap::b_peripheral() does and enable the pullup here... although
- * that may complicate usb_gadget_{,dis}connect() support.
+ * a pullup GPIO configured here. If it's configured here, we'll do
+ * what isp1301_omap::b_peripheral() does and enable the pullup here...
+ * although that may complicate usb_gadget_{,dis}connect() support.
*/
- gpio = pdata->gpio_pullup;
if (vbus) {
status = USB_EVENT_VBUS;
@@ -126,16 +119,16 @@
set_vbus_draw(gpio_vbus, 100);
/* optionally enable D+ pullup */
- if (gpio_is_valid(gpio))
- gpio_set_value(gpio, !pdata->gpio_pullup_inverted);
+ if (gpio_vbus->pullup_gpiod)
+ gpiod_set_value(gpio_vbus->pullup_gpiod, 1);
atomic_notifier_call_chain(&gpio_vbus->phy.notifier,
status, gpio_vbus->phy.otg->gadget);
usb_phy_set_event(&gpio_vbus->phy, USB_EVENT_ENUMERATED);
} else {
/* optionally disable D+ pullup */
- if (gpio_is_valid(gpio))
- gpio_set_value(gpio, pdata->gpio_pullup_inverted);
+ if (gpio_vbus->pullup_gpiod)
+ gpiod_set_value(gpio_vbus->pullup_gpiod, 0);
set_vbus_draw(gpio_vbus, 0);
@@ -154,12 +147,11 @@
static irqreturn_t gpio_vbus_irq(int irq, void *data)
{
struct platform_device *pdev = data;
- struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev);
struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
struct usb_otg *otg = gpio_vbus->phy.otg;
dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n",
- is_vbus_powered(pdata) ? "supplied" : "inactive",
+ is_vbus_powered(gpio_vbus) ? "supplied" : "inactive",
otg->gadget ? otg->gadget->name : "none");
if (otg->gadget)
@@ -175,22 +167,18 @@
struct usb_gadget *gadget)
{
struct gpio_vbus_data *gpio_vbus;
- struct gpio_vbus_mach_info *pdata;
struct platform_device *pdev;
- int gpio;
gpio_vbus = container_of(otg->usb_phy, struct gpio_vbus_data, phy);
pdev = to_platform_device(gpio_vbus->dev);
- pdata = dev_get_platdata(gpio_vbus->dev);
- gpio = pdata->gpio_pullup;
if (!gadget) {
dev_dbg(&pdev->dev, "unregistering gadget '%s'\n",
otg->gadget->name);
/* optionally disable D+ pullup */
- if (gpio_is_valid(gpio))
- gpio_set_value(gpio, pdata->gpio_pullup_inverted);
+ if (gpio_vbus->pullup_gpiod)
+ gpiod_set_value(gpio_vbus->pullup_gpiod, 0);
set_vbus_draw(gpio_vbus, 0);
@@ -242,16 +230,12 @@
static int gpio_vbus_probe(struct platform_device *pdev)
{
- struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev);
struct gpio_vbus_data *gpio_vbus;
struct resource *res;
- int err, gpio, irq;
+ struct device *dev = &pdev->dev;
+ int err, irq;
unsigned long irqflags;
- if (!pdata || !gpio_is_valid(pdata->gpio_vbus))
- return -EINVAL;
- gpio = pdata->gpio_vbus;
-
gpio_vbus = devm_kzalloc(&pdev->dev, sizeof(struct gpio_vbus_data),
GFP_KERNEL);
if (!gpio_vbus)
@@ -273,37 +257,43 @@
gpio_vbus->phy.otg->usb_phy = &gpio_vbus->phy;
gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral;
- err = devm_gpio_request(&pdev->dev, gpio, "vbus_detect");
- if (err) {
- dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n",
- gpio, err);
+ /* Look up the VBUS sensing GPIO */
+ gpio_vbus->vbus_gpiod = devm_gpiod_get(dev, "vbus", GPIOD_IN);
+ if (IS_ERR(gpio_vbus->vbus_gpiod)) {
+ err = PTR_ERR(gpio_vbus->vbus_gpiod);
+ dev_err(&pdev->dev, "can't request vbus gpio, err: %d\n", err);
return err;
}
- gpio_direction_input(gpio);
+ gpiod_set_consumer_name(gpio_vbus->vbus_gpiod, "vbus_detect");
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res) {
irq = res->start;
irqflags = (res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED;
} else {
- irq = gpio_to_irq(gpio);
+ irq = gpiod_to_irq(gpio_vbus->vbus_gpiod);
irqflags = VBUS_IRQ_FLAGS;
}
gpio_vbus->irq = irq;
- /* if data line pullup is in use, initialize it to "not pulling up" */
- gpio = pdata->gpio_pullup;
- if (gpio_is_valid(gpio)) {
- err = devm_gpio_request(&pdev->dev, gpio, "udc_pullup");
- if (err) {
- dev_err(&pdev->dev,
- "can't request pullup gpio %d, err: %d\n",
- gpio, err);
- return err;
- }
- gpio_direction_output(gpio, pdata->gpio_pullup_inverted);
+ /*
+ * The VBUS sensing GPIO should have a pulldown, which will normally be
+ * part of a resistor ladder turning a 4.0V-5.25V level on VBUS into a
+ * value the GPIO detects as active. Some systems will use comparators.
+ * Get the optional D+ or D- pullup GPIO. If the data line pullup is
+ * in use, initialize it to "not pulling up"
+ */
+ gpio_vbus->pullup_gpiod = devm_gpiod_get_optional(dev, "pullup",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(gpio_vbus->pullup_gpiod)) {
+ err = PTR_ERR(gpio_vbus->pullup_gpiod);
+ dev_err(&pdev->dev, "can't request pullup gpio, err: %d\n",
+ err);
+ return err;
}
+ if (gpio_vbus->pullup_gpiod)
+ gpiod_set_consumer_name(gpio_vbus->pullup_gpiod, "udc_pullup");
err = devm_request_irq(&pdev->dev, irq, gpio_vbus_irq, irqflags,
"vbus_detect", pdev);
@@ -330,7 +320,7 @@
return err;
}
- device_init_wakeup(&pdev->dev, pdata->wakeup);
+ /* TODO: wakeup could be enabled here with device_init_wakeup(dev, 1) */
return 0;
}
diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c
index 7041ba0..4a6462c 100644
--- a/drivers/usb/phy/phy-isp1301-omap.c
+++ b/drivers/usb/phy/phy-isp1301-omap.c
@@ -581,11 +581,11 @@
/* HNP failed for some reason (A_AIDL_BDIS timeout) */
notresponding(isp);
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_A_VBUS_ERR:
isp->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
pr_debug(" --> a_wait_vfall\n");
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_A_WAIT_VFALL:
/* FIXME usbcore thinks port power is still on ... */
clr |= OTG1_VBUS_DRV;
@@ -595,7 +595,7 @@
isp->phy.otg->state = OTG_STATE_A_WAIT_VRISE;
pr_debug(" --> a_wait_vrise\n");
}
- /* FALLTHROUGH */
+ fallthrough;
default:
toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV);
}
@@ -945,10 +945,10 @@
switch (state) {
case OTG_STATE_B_IDLE:
a_idle(isp, "idle");
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_A_IDLE:
enable_vbus_source(isp);
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_A_WAIT_VRISE:
/* we skip over OTG_STATE_A_WAIT_BCON, since
* the HC will transition to A_HOST (or
@@ -1032,12 +1032,12 @@
OTG1_DP_PULLUP);
dump_regs(isp, __func__);
#endif
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_B_SRP_INIT:
b_idle(isp, __func__);
l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
omap_writel(l, OTG_CTRL);
- /* FALLTHROUGH */
+ fallthrough;
case OTG_STATE_B_IDLE:
if (otg->gadget && (isp_bstat & OTG_B_SESS_VLD)) {
#ifdef CONFIG_USB_OTG
diff --git a/drivers/usb/phy/phy-jz4770.c b/drivers/usb/phy/phy-jz4770.c
new file mode 100644
index 0000000..f6d3731
--- /dev/null
+++ b/drivers/usb/phy/phy-jz4770.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ingenic SoCs USB PHY driver
+ * Copyright (c) Paul Cercueil <paul@crapouillou.net>
+ * Copyright (c) 漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>
+ * Copyright (c) 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>
+
+/* OTGPHY register offsets */
+#define REG_USBPCR_OFFSET 0x00
+#define REG_USBRDT_OFFSET 0x04
+#define REG_USBVBFIL_OFFSET 0x08
+#define REG_USBPCR1_OFFSET 0x0c
+
+/* bits within the USBPCR register */
+#define USBPCR_USB_MODE BIT(31)
+#define USBPCR_AVLD_REG BIT(30)
+#define USBPCR_COMMONONN BIT(25)
+#define USBPCR_VBUSVLDEXT BIT(24)
+#define USBPCR_VBUSVLDEXTSEL BIT(23)
+#define USBPCR_POR BIT(22)
+#define USBPCR_SIDDQ BIT(21)
+#define USBPCR_OTG_DISABLE BIT(20)
+#define USBPCR_TXPREEMPHTUNE BIT(6)
+
+#define USBPCR_IDPULLUP_LSB 28
+#define USBPCR_IDPULLUP_MASK GENMASK(29, USBPCR_IDPULLUP_LSB)
+#define USBPCR_IDPULLUP_ALWAYS (0x2 << USBPCR_IDPULLUP_LSB)
+#define USBPCR_IDPULLUP_SUSPEND (0x1 << USBPCR_IDPULLUP_LSB)
+#define USBPCR_IDPULLUP_OTG (0x0 << USBPCR_IDPULLUP_LSB)
+
+#define USBPCR_COMPDISTUNE_LSB 17
+#define USBPCR_COMPDISTUNE_MASK GENMASK(19, USBPCR_COMPDISTUNE_LSB)
+#define USBPCR_COMPDISTUNE_DFT (0x4 << USBPCR_COMPDISTUNE_LSB)
+
+#define USBPCR_OTGTUNE_LSB 14
+#define USBPCR_OTGTUNE_MASK GENMASK(16, USBPCR_OTGTUNE_LSB)
+#define USBPCR_OTGTUNE_DFT (0x4 << USBPCR_OTGTUNE_LSB)
+
+#define USBPCR_SQRXTUNE_LSB 11
+#define USBPCR_SQRXTUNE_MASK GENMASK(13, USBPCR_SQRXTUNE_LSB)
+#define USBPCR_SQRXTUNE_DCR_20PCT (0x7 << USBPCR_SQRXTUNE_LSB)
+#define USBPCR_SQRXTUNE_DFT (0x3 << USBPCR_SQRXTUNE_LSB)
+
+#define USBPCR_TXFSLSTUNE_LSB 7
+#define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB)
+#define USBPCR_TXFSLSTUNE_DCR_50PPT (0xf << USBPCR_TXFSLSTUNE_LSB)
+#define USBPCR_TXFSLSTUNE_DCR_25PPT (0x7 << USBPCR_TXFSLSTUNE_LSB)
+#define USBPCR_TXFSLSTUNE_DFT (0x3 << USBPCR_TXFSLSTUNE_LSB)
+#define USBPCR_TXFSLSTUNE_INC_25PPT (0x1 << USBPCR_TXFSLSTUNE_LSB)
+#define USBPCR_TXFSLSTUNE_INC_50PPT (0x0 << USBPCR_TXFSLSTUNE_LSB)
+
+#define USBPCR_TXHSXVTUNE_LSB 4
+#define USBPCR_TXHSXVTUNE_MASK GENMASK(5, USBPCR_TXHSXVTUNE_LSB)
+#define USBPCR_TXHSXVTUNE_DFT (0x3 << USBPCR_TXHSXVTUNE_LSB)
+#define USBPCR_TXHSXVTUNE_DCR_15MV (0x1 << USBPCR_TXHSXVTUNE_LSB)
+
+#define USBPCR_TXRISETUNE_LSB 4
+#define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB)
+#define USBPCR_TXRISETUNE_DFT (0x3 << USBPCR_TXRISETUNE_LSB)
+
+#define USBPCR_TXVREFTUNE_LSB 0
+#define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB)
+#define USBPCR_TXVREFTUNE_INC_25PPT (0x7 << USBPCR_TXVREFTUNE_LSB)
+#define USBPCR_TXVREFTUNE_DFT (0x5 << USBPCR_TXVREFTUNE_LSB)
+
+/* bits within the USBRDTR register */
+#define USBRDT_UTMI_RST BIT(27)
+#define USBRDT_HB_MASK BIT(26)
+#define USBRDT_VBFIL_LD_EN BIT(25)
+#define USBRDT_IDDIG_EN BIT(24)
+#define USBRDT_IDDIG_REG BIT(23)
+#define USBRDT_VBFIL_EN BIT(2)
+
+/* bits within the USBPCR1 register */
+#define USBPCR1_BVLD_REG BIT(31)
+#define USBPCR1_DPPD BIT(29)
+#define USBPCR1_DMPD BIT(28)
+#define USBPCR1_USB_SEL BIT(28)
+#define USBPCR1_WORD_IF_16BIT BIT(19)
+
+enum ingenic_usb_phy_version {
+ ID_JZ4770,
+ ID_JZ4780,
+ ID_X1000,
+ ID_X1830,
+};
+
+struct ingenic_soc_info {
+ enum ingenic_usb_phy_version version;
+
+ void (*usb_phy_init)(struct usb_phy *phy);
+};
+
+struct jz4770_phy {
+ const struct ingenic_soc_info *soc_info;
+
+ struct usb_phy phy;
+ struct usb_otg otg;
+ struct device *dev;
+ void __iomem *base;
+ struct clk *clk;
+ struct regulator *vcc_supply;
+};
+
+static inline struct jz4770_phy *otg_to_jz4770_phy(struct usb_otg *otg)
+{
+ return container_of(otg, struct jz4770_phy, otg);
+}
+
+static inline struct jz4770_phy *phy_to_jz4770_phy(struct usb_phy *phy)
+{
+ return container_of(phy, struct jz4770_phy, phy);
+}
+
+static int ingenic_usb_phy_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct jz4770_phy *priv = otg_to_jz4770_phy(otg);
+ u32 reg;
+
+ if (priv->soc_info->version >= ID_X1000) {
+ reg = readl(priv->base + REG_USBPCR1_OFFSET);
+ reg |= USBPCR1_BVLD_REG;
+ writel(reg, priv->base + REG_USBPCR1_OFFSET);
+ }
+
+ reg = readl(priv->base + REG_USBPCR_OFFSET);
+ reg &= ~USBPCR_USB_MODE;
+ reg |= USBPCR_VBUSVLDEXT | USBPCR_VBUSVLDEXTSEL | USBPCR_OTG_DISABLE;
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+
+ return 0;
+}
+
+static int ingenic_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct jz4770_phy *priv = otg_to_jz4770_phy(otg);
+ u32 reg;
+
+ reg = readl(priv->base + REG_USBPCR_OFFSET);
+ reg &= ~(USBPCR_VBUSVLDEXT | USBPCR_VBUSVLDEXTSEL | USBPCR_OTG_DISABLE);
+ reg |= USBPCR_USB_MODE;
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+
+ return 0;
+}
+
+static int ingenic_usb_phy_init(struct usb_phy *phy)
+{
+ struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
+ int err;
+ u32 reg;
+
+ err = regulator_enable(priv->vcc_supply);
+ if (err) {
+ dev_err(priv->dev, "Unable to enable VCC: %d\n", err);
+ return err;
+ }
+
+ err = clk_prepare_enable(priv->clk);
+ if (err) {
+ dev_err(priv->dev, "Unable to start clock: %d\n", err);
+ return err;
+ }
+
+ priv->soc_info->usb_phy_init(phy);
+
+ /* Wait for PHY to reset */
+ usleep_range(30, 300);
+ reg = readl(priv->base + REG_USBPCR_OFFSET);
+ writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET);
+ usleep_range(300, 1000);
+
+ return 0;
+}
+
+static void ingenic_usb_phy_shutdown(struct usb_phy *phy)
+{
+ struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
+
+ clk_disable_unprepare(priv->clk);
+ regulator_disable(priv->vcc_supply);
+}
+
+static void ingenic_usb_phy_remove(void *phy)
+{
+ usb_remove_phy(phy);
+}
+
+static void jz4770_usb_phy_init(struct usb_phy *phy)
+{
+ struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
+ u32 reg;
+
+ reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_IDPULLUP_ALWAYS |
+ USBPCR_COMPDISTUNE_DFT | USBPCR_OTGTUNE_DFT | USBPCR_SQRXTUNE_DFT |
+ USBPCR_TXFSLSTUNE_DFT | USBPCR_TXRISETUNE_DFT | USBPCR_TXVREFTUNE_DFT |
+ USBPCR_POR;
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static void jz4780_usb_phy_init(struct usb_phy *phy)
+{
+ struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
+ u32 reg;
+
+ reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL |
+ USBPCR1_WORD_IF_16BIT;
+ writel(reg, priv->base + REG_USBPCR1_OFFSET);
+
+ reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR;
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static void x1000_usb_phy_init(struct usb_phy *phy)
+{
+ struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
+ u32 reg;
+
+ reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT;
+ writel(reg, priv->base + REG_USBPCR1_OFFSET);
+
+ reg = USBPCR_SQRXTUNE_DCR_20PCT | USBPCR_TXPREEMPHTUNE |
+ USBPCR_TXHSXVTUNE_DCR_15MV | USBPCR_TXVREFTUNE_INC_25PPT |
+ USBPCR_COMMONONN | USBPCR_POR;
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static void x1830_usb_phy_init(struct usb_phy *phy)
+{
+ struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
+ u32 reg;
+
+ /* rdt */
+ writel(USBRDT_VBFIL_EN | USBRDT_UTMI_RST, priv->base + REG_USBRDT_OFFSET);
+
+ reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT |
+ USBPCR1_DMPD | USBPCR1_DPPD;
+ writel(reg, priv->base + REG_USBPCR1_OFFSET);
+
+ reg = USBPCR_IDPULLUP_OTG | USBPCR_VBUSVLDEXT | USBPCR_TXPREEMPHTUNE |
+ USBPCR_COMMONONN | USBPCR_POR;
+ writel(reg, priv->base + REG_USBPCR_OFFSET);
+}
+
+static const struct ingenic_soc_info jz4770_soc_info = {
+ .version = ID_JZ4770,
+
+ .usb_phy_init = jz4770_usb_phy_init,
+};
+
+static const struct ingenic_soc_info jz4780_soc_info = {
+ .version = ID_JZ4780,
+
+ .usb_phy_init = jz4780_usb_phy_init,
+};
+
+static const struct ingenic_soc_info x1000_soc_info = {
+ .version = ID_X1000,
+
+ .usb_phy_init = x1000_usb_phy_init,
+};
+
+static const struct ingenic_soc_info x1830_soc_info = {
+ .version = ID_X1830,
+
+ .usb_phy_init = x1830_usb_phy_init,
+};
+
+static const struct of_device_id ingenic_usb_phy_of_matches[] = {
+ { .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info },
+ { .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info },
+ { .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info },
+ { .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches);
+
+static int jz4770_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct jz4770_phy *priv;
+ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->soc_info = device_get_match_data(&pdev->dev);
+ if (!priv->soc_info) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ priv->dev = dev;
+ priv->phy.dev = dev;
+ priv->phy.otg = &priv->otg;
+ priv->phy.label = "ingenic-usb-phy";
+ priv->phy.init = ingenic_usb_phy_init;
+ priv->phy.shutdown = ingenic_usb_phy_shutdown;
+
+ priv->otg.state = OTG_STATE_UNDEFINED;
+ priv->otg.usb_phy = &priv->phy;
+ priv->otg.set_host = ingenic_usb_phy_set_host;
+ priv->otg.set_peripheral = ingenic_usb_phy_set_peripheral;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base)) {
+ dev_err(dev, "Failed to map registers\n");
+ return PTR_ERR(priv->base);
+ }
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ err = PTR_ERR(priv->clk);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get clock\n");
+ return err;
+ }
+
+ priv->vcc_supply = devm_regulator_get(dev, "vcc");
+ if (IS_ERR(priv->vcc_supply)) {
+ err = PTR_ERR(priv->vcc_supply);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get regulator\n");
+ return err;
+ }
+
+ err = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2);
+ if (err) {
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "Unable to register PHY\n");
+ return err;
+ }
+
+ return devm_add_action_or_reset(dev, ingenic_usb_phy_remove, &priv->phy);
+}
+
+static struct platform_driver ingenic_phy_driver = {
+ .probe = jz4770_phy_probe,
+ .driver = {
+ .name = "jz4770-phy",
+ .of_match_table = of_match_ptr(ingenic_usb_phy_of_matches),
+ },
+};
+module_platform_driver(ingenic_phy_driver);
+
+MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
+MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_DESCRIPTION("Ingenic SoCs USB PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c
index 1987126..358d05c 100644
--- a/drivers/usb/phy/phy-keystone.c
+++ b/drivers/usb/phy/phy-keystone.c
@@ -2,7 +2,7 @@
/*
* phy-keystone - USB PHY, talking to dwc3 controller in Keystone.
*
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com
*
* Author: WingMan Kwok <w-kwok2@ti.com>
*/
@@ -66,19 +66,17 @@
{
struct device *dev = &pdev->dev;
struct keystone_usbphy *k_phy;
- struct resource *res;
int ret;
k_phy = devm_kzalloc(dev, sizeof(*k_phy), GFP_KERNEL);
if (!k_phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- k_phy->phy_ctrl = devm_ioremap_resource(dev, res);
+ k_phy->phy_ctrl = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(k_phy->phy_ctrl))
return PTR_ERR(k_phy->phy_ctrl);
- ret = usb_phy_gen_create_phy(dev, &k_phy->usb_phy_gen, NULL);
+ ret = usb_phy_gen_create_phy(dev, &k_phy->usb_phy_gen);
if (ret)
return ret;
diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c
index 06b47f1..576d925 100644
--- a/drivers/usb/phy/phy-mv-usb.c
+++ b/drivers/usb/phy/phy-mv-usb.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
@@ -135,8 +136,8 @@
static int mv_otg_reset(struct mv_otg *mvotg)
{
- unsigned int loops;
u32 tmp;
+ int ret;
/* Stop the controller */
tmp = readl(&mvotg->op_regs->usbcmd);
@@ -146,15 +147,12 @@
/* Reset the controller to get default values */
writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd);
- loops = 500;
- while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) {
- if (loops == 0) {
- dev_err(&mvotg->pdev->dev,
- "Wait for RESET completed TIMEOUT\n");
- return -ETIMEDOUT;
- }
- loops--;
- udelay(20);
+ ret = readl_poll_timeout_atomic(&mvotg->op_regs->usbcmd, tmp,
+ (tmp & USBCMD_CTRL_RESET), 10, 10000);
+ if (ret < 0) {
+ dev_err(&mvotg->pdev->dev,
+ "Wait for RESET completed TIMEOUT\n");
+ return ret;
}
writel(0x0, &mvotg->op_regs->usbintr);
@@ -334,7 +332,7 @@
switch (old_state) {
case OTG_STATE_UNDEFINED:
mvotg->phy.otg->state = OTG_STATE_B_IDLE;
- /* FALL THROUGH */
+ fallthrough;
case OTG_STATE_B_IDLE:
if (otg_ctrl->id == 0)
mvotg->phy.otg->state = OTG_STATE_A_IDLE;
diff --git a/drivers/usb/phy/phy-mv-usb.h b/drivers/usb/phy/phy-mv-usb.h
index 96701a1..5d5c0ab 100644
--- a/drivers/usb/phy/phy-mv-usb.h
+++ b/drivers/usb/phy/phy-mv-usb.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
*/
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 70b8c82..67b39dc 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -710,7 +710,6 @@
static int mxs_phy_probe(struct platform_device *pdev)
{
- struct resource *res;
void __iomem *base;
struct clk *clk;
struct mxs_phy *mxs_phy;
@@ -723,8 +722,7 @@
if (!of_id)
return -ENODEV;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, res);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index ea7ef1d..03a3337 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -9,54 +9,55 @@
* Venu Byravarasu <vbyravarasu@nvidia.com>
*/
-#include <linux/resource.h>
#include <linux/delay.h>
-#include <linux/slab.h>
#include <linux/err.h>
#include <linux/export.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
#include <linux/iopoll.h>
-#include <linux/gpio.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/usb/otg.h>
-#include <linux/usb/ulpi.h>
-#include <linux/usb/of.h>
-#include <linux/usb/ehci_def.h>
-#include <linux/usb/tegra_usb_phy.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
#include <linux/regulator/consumer.h>
-#define ULPI_VIEWPORT 0x170
+#include <linux/usb/ehci_def.h>
+#include <linux/usb/of.h>
+#include <linux/usb/tegra_usb_phy.h>
+#include <linux/usb/ulpi.h>
+
+#define ULPI_VIEWPORT 0x170
/* PORTSC PTS/PHCD bits, Tegra20 only */
-#define TEGRA_USB_PORTSC1 0x184
-#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
-#define TEGRA_USB_PORTSC1_PHCD (1 << 23)
+#define TEGRA_USB_PORTSC1 0x184
+#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
+#define TEGRA_USB_PORTSC1_PHCD BIT(23)
/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
-#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4
-#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
-#define TEGRA_USB_HOSTPC1_DEVLC_PHCD (1 << 22)
+#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4
+#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
+#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22)
/* Bits of PORTSC1, which will get cleared by writing 1 into them */
#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
-#define USB_SUSP_CTRL 0x400
-#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
-#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
-#define USB_SUSP_CLR (1 << 5)
-#define USB_PHY_CLK_VALID (1 << 7)
-#define UTMIP_RESET (1 << 11)
-#define UHSIC_RESET (1 << 11)
-#define UTMIP_PHY_ENABLE (1 << 12)
-#define ULPI_PHY_ENABLE (1 << 13)
-#define USB_SUSP_SET (1 << 14)
-#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+#define USB_SUSP_CTRL 0x400
+#define USB_WAKE_ON_CNNT_EN_DEV BIT(3)
+#define USB_WAKE_ON_DISCON_EN_DEV BIT(4)
+#define USB_SUSP_CLR BIT(5)
+#define USB_PHY_CLK_VALID BIT(7)
+#define UTMIP_RESET BIT(11)
+#define UHSIC_RESET BIT(11)
+#define UTMIP_PHY_ENABLE BIT(12)
+#define ULPI_PHY_ENABLE BIT(13)
+#define USB_SUSP_SET BIT(14)
+#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
-#define USB1_LEGACY_CTRL 0x410
-#define USB1_NO_LEGACY_MODE (1 << 0)
+#define USB1_LEGACY_CTRL 0x410
+#define USB1_NO_LEGACY_MODE BIT(0)
#define USB1_VBUS_SENSE_CTL_MASK (3 << 1)
#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1)
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \
@@ -64,94 +65,94 @@
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1)
#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1)
-#define ULPI_TIMING_CTRL_0 0x424
-#define ULPI_OUTPUT_PINMUX_BYP (1 << 10)
-#define ULPI_CLKOUT_PINMUX_BYP (1 << 11)
+#define ULPI_TIMING_CTRL_0 0x424
+#define ULPI_OUTPUT_PINMUX_BYP BIT(10)
+#define ULPI_CLKOUT_PINMUX_BYP BIT(11)
-#define ULPI_TIMING_CTRL_1 0x428
-#define ULPI_DATA_TRIMMER_LOAD (1 << 0)
-#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1)
-#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16)
-#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17)
-#define ULPI_DIR_TRIMMER_LOAD (1 << 24)
-#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
+#define ULPI_TIMING_CTRL_1 0x428
+#define ULPI_DATA_TRIMMER_LOAD BIT(0)
+#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1)
+#define ULPI_STPDIRNXT_TRIMMER_LOAD BIT(16)
+#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17)
+#define ULPI_DIR_TRIMMER_LOAD BIT(24)
+#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
-#define UTMIP_PLL_CFG1 0x804
+#define UTMIP_PLL_CFG1 0x804
#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
-#define UTMIP_XCVR_CFG0 0x808
+#define UTMIP_XCVR_CFG0 0x808
#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0)
#define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22)
#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8)
#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10)
-#define UTMIP_FORCE_PD_POWERDOWN (1 << 14)
-#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16)
-#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18)
-#define UTMIP_XCVR_LSBIAS_SEL (1 << 21)
+#define UTMIP_FORCE_PD_POWERDOWN BIT(14)
+#define UTMIP_FORCE_PD2_POWERDOWN BIT(16)
+#define UTMIP_FORCE_PDZI_POWERDOWN BIT(18)
+#define UTMIP_XCVR_LSBIAS_SEL BIT(21)
#define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4)
#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25)
-#define UTMIP_BIAS_CFG0 0x80c
-#define UTMIP_OTGPD (1 << 11)
-#define UTMIP_BIASPD (1 << 10)
-#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0)
-#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2)
-#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24)
+#define UTMIP_BIAS_CFG0 0x80c
+#define UTMIP_OTGPD BIT(11)
+#define UTMIP_BIASPD BIT(10)
+#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0)
+#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2)
+#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24)
-#define UTMIP_HSRX_CFG0 0x810
-#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
-#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15)
+#define UTMIP_HSRX_CFG0 0x810
+#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
+#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15)
-#define UTMIP_HSRX_CFG1 0x814
-#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
+#define UTMIP_HSRX_CFG1 0x814
+#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
-#define UTMIP_TX_CFG0 0x820
-#define UTMIP_FS_PREABMLE_J (1 << 19)
-#define UTMIP_HS_DISCON_DISABLE (1 << 8)
+#define UTMIP_TX_CFG0 0x820
+#define UTMIP_FS_PREABMLE_J BIT(19)
+#define UTMIP_HS_DISCON_DISABLE BIT(8)
-#define UTMIP_MISC_CFG0 0x824
-#define UTMIP_DPDM_OBSERVE (1 << 26)
-#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
-#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf)
-#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe)
-#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
-#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
-#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22)
+#define UTMIP_MISC_CFG0 0x824
+#define UTMIP_DPDM_OBSERVE BIT(26)
+#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
+#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
+#define UTMIP_SUSPEND_EXIT_ON_EDGE BIT(22)
-#define UTMIP_MISC_CFG1 0x828
-#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18)
-#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6)
+#define UTMIP_MISC_CFG1 0x828
+#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18)
+#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6)
-#define UTMIP_DEBOUNCE_CFG0 0x82c
-#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0)
+#define UTMIP_DEBOUNCE_CFG0 0x82c
+#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0)
-#define UTMIP_BAT_CHRG_CFG0 0x830
-#define UTMIP_PD_CHRG (1 << 0)
+#define UTMIP_BAT_CHRG_CFG0 0x830
+#define UTMIP_PD_CHRG BIT(0)
-#define UTMIP_SPARE_CFG0 0x834
-#define FUSE_SETUP_SEL (1 << 3)
+#define UTMIP_SPARE_CFG0 0x834
+#define FUSE_SETUP_SEL BIT(3)
-#define UTMIP_XCVR_CFG1 0x838
-#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0)
-#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2)
-#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4)
-#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
+#define UTMIP_XCVR_CFG1 0x838
+#define UTMIP_FORCE_PDDISC_POWERDOWN BIT(0)
+#define UTMIP_FORCE_PDCHRP_POWERDOWN BIT(2)
+#define UTMIP_FORCE_PDDR_POWERDOWN BIT(4)
+#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
-#define UTMIP_BIAS_CFG1 0x83c
-#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
+#define UTMIP_BIAS_CFG1 0x83c
+#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
/* For Tegra30 and above only, the address is different in Tegra20 */
-#define USB_USBMODE 0x1f8
-#define USB_USBMODE_MASK (3 << 0)
-#define USB_USBMODE_HOST (3 << 0)
-#define USB_USBMODE_DEVICE (2 << 0)
+#define USB_USBMODE 0x1f8
+#define USB_USBMODE_MASK (3 << 0)
+#define USB_USBMODE_HOST (3 << 0)
+#define USB_USBMODE_DEVICE (2 << 0)
static DEFINE_SPINLOCK(utmip_pad_lock);
-static int utmip_pad_count;
+static unsigned int utmip_pad_count;
struct tegra_xtal_freq {
- int freq;
+ unsigned int freq;
u8 enable_delay;
u8 stable_count;
u8 active_delay;
@@ -194,43 +195,49 @@
},
};
+static inline struct tegra_usb_phy *to_tegra_usb_phy(struct usb_phy *u_phy)
+{
+ return container_of(u_phy, struct tegra_usb_phy, u_phy);
+}
+
static void set_pts(struct tegra_usb_phy *phy, u8 pts_val)
{
void __iomem *base = phy->regs;
- unsigned long val;
+ u32 val;
if (phy->soc_config->has_hostpc) {
- val = readl(base + TEGRA_USB_HOSTPC1_DEVLC);
+ val = readl_relaxed(base + TEGRA_USB_HOSTPC1_DEVLC);
val &= ~TEGRA_USB_HOSTPC1_DEVLC_PTS(~0);
val |= TEGRA_USB_HOSTPC1_DEVLC_PTS(pts_val);
- writel(val, base + TEGRA_USB_HOSTPC1_DEVLC);
+ writel_relaxed(val, base + TEGRA_USB_HOSTPC1_DEVLC);
} else {
- val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS;
+ val = readl_relaxed(base + TEGRA_USB_PORTSC1);
+ val &= ~TEGRA_PORTSC1_RWC_BITS;
val &= ~TEGRA_USB_PORTSC1_PTS(~0);
val |= TEGRA_USB_PORTSC1_PTS(pts_val);
- writel(val, base + TEGRA_USB_PORTSC1);
+ writel_relaxed(val, base + TEGRA_USB_PORTSC1);
}
}
static void set_phcd(struct tegra_usb_phy *phy, bool enable)
{
void __iomem *base = phy->regs;
- unsigned long val;
+ u32 val;
if (phy->soc_config->has_hostpc) {
- val = readl(base + TEGRA_USB_HOSTPC1_DEVLC);
+ val = readl_relaxed(base + TEGRA_USB_HOSTPC1_DEVLC);
if (enable)
val |= TEGRA_USB_HOSTPC1_DEVLC_PHCD;
else
val &= ~TEGRA_USB_HOSTPC1_DEVLC_PHCD;
- writel(val, base + TEGRA_USB_HOSTPC1_DEVLC);
+ writel_relaxed(val, base + TEGRA_USB_HOSTPC1_DEVLC);
} else {
- val = readl(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS;
+ val = readl_relaxed(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS;
if (enable)
val |= TEGRA_USB_PORTSC1_PHCD;
else
val &= ~TEGRA_USB_PORTSC1_PHCD;
- writel(val, base + TEGRA_USB_PORTSC1);
+ writel_relaxed(val, base + TEGRA_USB_PORTSC1);
}
}
@@ -238,23 +245,6 @@
{
int ret;
- phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads");
- if (IS_ERR(phy->pad_clk)) {
- ret = PTR_ERR(phy->pad_clk);
- dev_err(phy->u_phy.dev,
- "Failed to get UTMIP pad clock: %d\n", ret);
- return ret;
- }
-
- phy->pad_rst = devm_reset_control_get_optional_shared(
- phy->u_phy.dev, "utmi-pads");
- if (IS_ERR(phy->pad_rst)) {
- ret = PTR_ERR(phy->pad_rst);
- dev_err(phy->u_phy.dev,
- "Failed to get UTMI-pads reset: %d\n", ret);
- return ret;
- }
-
ret = clk_prepare_enable(phy->pad_clk);
if (ret) {
dev_err(phy->u_phy.dev,
@@ -315,18 +305,21 @@
return ret;
}
-static void utmip_pad_power_on(struct tegra_usb_phy *phy)
+static int utmip_pad_power_on(struct tegra_usb_phy *phy)
{
- unsigned long val, flags;
- void __iomem *base = phy->pad_regs;
struct tegra_utmip_config *config = phy->config;
+ void __iomem *base = phy->pad_regs;
+ u32 val;
+ int err;
- clk_prepare_enable(phy->pad_clk);
+ err = clk_prepare_enable(phy->pad_clk);
+ if (err)
+ return err;
- spin_lock_irqsave(&utmip_pad_lock, flags);
+ spin_lock(&utmip_pad_lock);
if (utmip_pad_count++ == 0) {
- val = readl(base + UTMIP_BIAS_CFG0);
+ val = readl_relaxed(base + UTMIP_BIAS_CFG0);
val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
if (phy->soc_config->requires_extra_tuning_parameters) {
@@ -338,53 +331,59 @@
val |= UTMIP_HSDISCON_LEVEL(config->hsdiscon_level);
val |= UTMIP_HSDISCON_LEVEL_MSB(config->hsdiscon_level);
}
- writel(val, base + UTMIP_BIAS_CFG0);
+ writel_relaxed(val, base + UTMIP_BIAS_CFG0);
}
- spin_unlock_irqrestore(&utmip_pad_lock, flags);
-
- clk_disable_unprepare(phy->pad_clk);
-}
-
-static int utmip_pad_power_off(struct tegra_usb_phy *phy)
-{
- unsigned long val, flags;
- void __iomem *base = phy->pad_regs;
-
- if (!utmip_pad_count) {
- dev_err(phy->u_phy.dev, "UTMIP pad already powered off\n");
- return -EINVAL;
- }
-
- clk_prepare_enable(phy->pad_clk);
-
- spin_lock_irqsave(&utmip_pad_lock, flags);
-
- if (--utmip_pad_count == 0) {
- val = readl(base + UTMIP_BIAS_CFG0);
- val |= UTMIP_OTGPD | UTMIP_BIASPD;
- writel(val, base + UTMIP_BIAS_CFG0);
- }
-
- spin_unlock_irqrestore(&utmip_pad_lock, flags);
+ spin_unlock(&utmip_pad_lock);
clk_disable_unprepare(phy->pad_clk);
return 0;
}
+static int utmip_pad_power_off(struct tegra_usb_phy *phy)
+{
+ void __iomem *base = phy->pad_regs;
+ u32 val;
+ int ret;
+
+ ret = clk_prepare_enable(phy->pad_clk);
+ if (ret)
+ return ret;
+
+ spin_lock(&utmip_pad_lock);
+
+ if (!utmip_pad_count) {
+ dev_err(phy->u_phy.dev, "UTMIP pad already powered off\n");
+ ret = -EINVAL;
+ goto ulock;
+ }
+
+ if (--utmip_pad_count == 0) {
+ val = readl_relaxed(base + UTMIP_BIAS_CFG0);
+ val |= UTMIP_OTGPD | UTMIP_BIASPD;
+ writel_relaxed(val, base + UTMIP_BIAS_CFG0);
+ }
+ulock:
+ spin_unlock(&utmip_pad_lock);
+
+ clk_disable_unprepare(phy->pad_clk);
+
+ return ret;
+}
+
static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
{
u32 tmp;
- return readl_poll_timeout(reg, tmp, (tmp & mask) == result,
- 2000, 6000);
+ return readl_relaxed_poll_timeout(reg, tmp, (tmp & mask) == result,
+ 2000, 6000);
}
static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
{
- unsigned long val;
void __iomem *base = phy->regs;
+ u32 val;
/*
* The USB driver may have already initiated the phy clock
@@ -395,27 +394,28 @@
return;
if (phy->is_legacy_phy) {
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val |= USB_SUSP_SET;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
- udelay(10);
+ usleep_range(10, 100);
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_SET;
- writel(val, base + USB_SUSP_CTRL);
- } else
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+ } else {
set_phcd(phy, true);
+ }
- if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0))
dev_err(phy->u_phy.dev,
"Timeout waiting for PHY to stabilize on disable\n");
}
static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
{
- unsigned long val;
void __iomem *base = phy->regs;
+ u32 val;
/*
* The USB driver may have already initiated the phy clock
@@ -427,97 +427,101 @@
return;
if (phy->is_legacy_phy) {
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val |= USB_SUSP_CLR;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
- udelay(10);
+ usleep_range(10, 100);
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_CLR;
- writel(val, base + USB_SUSP_CTRL);
- } else
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+ } else {
set_phcd(phy, false);
+ }
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
- USB_PHY_CLK_VALID))
+ USB_PHY_CLK_VALID))
dev_err(phy->u_phy.dev,
"Timeout waiting for PHY to stabilize on enable\n");
}
static int utmi_phy_power_on(struct tegra_usb_phy *phy)
{
- unsigned long val;
- void __iomem *base = phy->regs;
struct tegra_utmip_config *config = phy->config;
+ void __iomem *base = phy->regs;
+ u32 val;
+ int err;
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val |= UTMIP_RESET;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
if (phy->is_legacy_phy) {
- val = readl(base + USB1_LEGACY_CTRL);
+ val = readl_relaxed(base + USB1_LEGACY_CTRL);
val |= USB1_NO_LEGACY_MODE;
- writel(val, base + USB1_LEGACY_CTRL);
+ writel_relaxed(val, base + USB1_LEGACY_CTRL);
}
- val = readl(base + UTMIP_TX_CFG0);
+ val = readl_relaxed(base + UTMIP_TX_CFG0);
val |= UTMIP_FS_PREABMLE_J;
- writel(val, base + UTMIP_TX_CFG0);
+ writel_relaxed(val, base + UTMIP_TX_CFG0);
- val = readl(base + UTMIP_HSRX_CFG0);
+ val = readl_relaxed(base + UTMIP_HSRX_CFG0);
val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0));
val |= UTMIP_IDLE_WAIT(config->idle_wait_delay);
val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit);
- writel(val, base + UTMIP_HSRX_CFG0);
+ writel_relaxed(val, base + UTMIP_HSRX_CFG0);
- val = readl(base + UTMIP_HSRX_CFG1);
+ val = readl_relaxed(base + UTMIP_HSRX_CFG1);
val &= ~UTMIP_HS_SYNC_START_DLY(~0);
val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay);
- writel(val, base + UTMIP_HSRX_CFG1);
+ writel_relaxed(val, base + UTMIP_HSRX_CFG1);
- val = readl(base + UTMIP_DEBOUNCE_CFG0);
+ val = readl_relaxed(base + UTMIP_DEBOUNCE_CFG0);
val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce);
- writel(val, base + UTMIP_DEBOUNCE_CFG0);
+ writel_relaxed(val, base + UTMIP_DEBOUNCE_CFG0);
- val = readl(base + UTMIP_MISC_CFG0);
+ val = readl_relaxed(base + UTMIP_MISC_CFG0);
val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
- writel(val, base + UTMIP_MISC_CFG0);
+ writel_relaxed(val, base + UTMIP_MISC_CFG0);
if (!phy->soc_config->utmi_pll_config_in_car_module) {
- val = readl(base + UTMIP_MISC_CFG1);
+ val = readl_relaxed(base + UTMIP_MISC_CFG1);
val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) |
UTMIP_PLLU_STABLE_COUNT(~0));
val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) |
UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count);
- writel(val, base + UTMIP_MISC_CFG1);
+ writel_relaxed(val, base + UTMIP_MISC_CFG1);
- val = readl(base + UTMIP_PLL_CFG1);
+ val = readl_relaxed(base + UTMIP_PLL_CFG1);
val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
- writel(val, base + UTMIP_PLL_CFG1);
+ writel_relaxed(val, base + UTMIP_PLL_CFG1);
}
if (phy->mode == USB_DR_MODE_PERIPHERAL) {
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV);
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
- val = readl(base + UTMIP_BAT_CHRG_CFG0);
+ val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0);
val &= ~UTMIP_PD_CHRG;
- writel(val, base + UTMIP_BAT_CHRG_CFG0);
+ writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0);
} else {
- val = readl(base + UTMIP_BAT_CHRG_CFG0);
+ val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0);
val |= UTMIP_PD_CHRG;
- writel(val, base + UTMIP_BAT_CHRG_CFG0);
+ writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0);
}
- utmip_pad_power_on(phy);
+ err = utmip_pad_power_on(phy);
+ if (err)
+ return err;
- val = readl(base + UTMIP_XCVR_CFG0);
+ val = readl_relaxed(base + UTMIP_XCVR_CFG0);
val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL |
UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) |
@@ -535,57 +539,57 @@
val |= UTMIP_XCVR_HSSLEW(config->xcvr_hsslew);
val |= UTMIP_XCVR_HSSLEW_MSB(config->xcvr_hsslew);
}
- writel(val, base + UTMIP_XCVR_CFG0);
+ writel_relaxed(val, base + UTMIP_XCVR_CFG0);
- val = readl(base + UTMIP_XCVR_CFG1);
+ val = readl_relaxed(base + UTMIP_XCVR_CFG1);
val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0));
val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj);
- writel(val, base + UTMIP_XCVR_CFG1);
+ writel_relaxed(val, base + UTMIP_XCVR_CFG1);
- val = readl(base + UTMIP_BIAS_CFG1);
+ val = readl_relaxed(base + UTMIP_BIAS_CFG1);
val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
- writel(val, base + UTMIP_BIAS_CFG1);
+ writel_relaxed(val, base + UTMIP_BIAS_CFG1);
- val = readl(base + UTMIP_SPARE_CFG0);
+ val = readl_relaxed(base + UTMIP_SPARE_CFG0);
if (config->xcvr_setup_use_fuses)
val |= FUSE_SETUP_SEL;
else
val &= ~FUSE_SETUP_SEL;
- writel(val, base + UTMIP_SPARE_CFG0);
+ writel_relaxed(val, base + UTMIP_SPARE_CFG0);
if (!phy->is_legacy_phy) {
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val |= UTMIP_PHY_ENABLE;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
}
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val &= ~UTMIP_RESET;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
if (phy->is_legacy_phy) {
- val = readl(base + USB1_LEGACY_CTRL);
+ val = readl_relaxed(base + USB1_LEGACY_CTRL);
val &= ~USB1_VBUS_SENSE_CTL_MASK;
val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD;
- writel(val, base + USB1_LEGACY_CTRL);
+ writel_relaxed(val, base + USB1_LEGACY_CTRL);
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_SET;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
}
utmi_phy_clk_enable(phy);
if (phy->soc_config->requires_usbmode_setup) {
- val = readl(base + USB_USBMODE);
+ val = readl_relaxed(base + USB_USBMODE);
val &= ~USB_USBMODE_MASK;
if (phy->mode == USB_DR_MODE_HOST)
val |= USB_USBMODE_HOST;
else
val |= USB_USBMODE_DEVICE;
- writel(val, base + USB_USBMODE);
+ writel_relaxed(val, base + USB_USBMODE);
}
if (!phy->is_legacy_phy)
@@ -596,258 +600,252 @@
static int utmi_phy_power_off(struct tegra_usb_phy *phy)
{
- unsigned long val;
void __iomem *base = phy->regs;
+ u32 val;
utmi_phy_clk_disable(phy);
if (phy->mode == USB_DR_MODE_PERIPHERAL) {
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5);
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
}
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val |= UTMIP_RESET;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
- val = readl(base + UTMIP_BAT_CHRG_CFG0);
+ val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0);
val |= UTMIP_PD_CHRG;
- writel(val, base + UTMIP_BAT_CHRG_CFG0);
+ writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0);
- val = readl(base + UTMIP_XCVR_CFG0);
+ val = readl_relaxed(base + UTMIP_XCVR_CFG0);
val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
UTMIP_FORCE_PDZI_POWERDOWN;
- writel(val, base + UTMIP_XCVR_CFG0);
+ writel_relaxed(val, base + UTMIP_XCVR_CFG0);
- val = readl(base + UTMIP_XCVR_CFG1);
+ val = readl_relaxed(base + UTMIP_XCVR_CFG1);
val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
UTMIP_FORCE_PDDR_POWERDOWN;
- writel(val, base + UTMIP_XCVR_CFG1);
+ writel_relaxed(val, base + UTMIP_XCVR_CFG1);
return utmip_pad_power_off(phy);
}
static void utmi_phy_preresume(struct tegra_usb_phy *phy)
{
- unsigned long val;
void __iomem *base = phy->regs;
+ u32 val;
- val = readl(base + UTMIP_TX_CFG0);
+ val = readl_relaxed(base + UTMIP_TX_CFG0);
val |= UTMIP_HS_DISCON_DISABLE;
- writel(val, base + UTMIP_TX_CFG0);
+ writel_relaxed(val, base + UTMIP_TX_CFG0);
}
static void utmi_phy_postresume(struct tegra_usb_phy *phy)
{
- unsigned long val;
void __iomem *base = phy->regs;
+ u32 val;
- val = readl(base + UTMIP_TX_CFG0);
+ val = readl_relaxed(base + UTMIP_TX_CFG0);
val &= ~UTMIP_HS_DISCON_DISABLE;
- writel(val, base + UTMIP_TX_CFG0);
+ writel_relaxed(val, base + UTMIP_TX_CFG0);
}
static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
enum tegra_usb_phy_port_speed port_speed)
{
- unsigned long val;
void __iomem *base = phy->regs;
+ u32 val;
- val = readl(base + UTMIP_MISC_CFG0);
+ val = readl_relaxed(base + UTMIP_MISC_CFG0);
val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
else
val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
- writel(val, base + UTMIP_MISC_CFG0);
- udelay(1);
+ writel_relaxed(val, base + UTMIP_MISC_CFG0);
+ usleep_range(1, 10);
- val = readl(base + UTMIP_MISC_CFG0);
+ val = readl_relaxed(base + UTMIP_MISC_CFG0);
val |= UTMIP_DPDM_OBSERVE;
- writel(val, base + UTMIP_MISC_CFG0);
- udelay(10);
+ writel_relaxed(val, base + UTMIP_MISC_CFG0);
+ usleep_range(10, 100);
}
static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
{
- unsigned long val;
void __iomem *base = phy->regs;
+ u32 val;
- val = readl(base + UTMIP_MISC_CFG0);
+ val = readl_relaxed(base + UTMIP_MISC_CFG0);
val &= ~UTMIP_DPDM_OBSERVE;
- writel(val, base + UTMIP_MISC_CFG0);
- udelay(10);
+ writel_relaxed(val, base + UTMIP_MISC_CFG0);
+ usleep_range(10, 100);
}
static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
{
- int ret;
- unsigned long val;
void __iomem *base = phy->regs;
+ u32 val;
+ int err;
- ret = gpio_direction_output(phy->reset_gpio, 0);
- if (ret < 0) {
- dev_err(phy->u_phy.dev, "GPIO %d not set to 0: %d\n",
- phy->reset_gpio, ret);
- return ret;
- }
- msleep(5);
- ret = gpio_direction_output(phy->reset_gpio, 1);
- if (ret < 0) {
- dev_err(phy->u_phy.dev, "GPIO %d not set to 1: %d\n",
- phy->reset_gpio, ret);
- return ret;
- }
+ gpiod_set_value_cansleep(phy->reset_gpio, 1);
- clk_prepare_enable(phy->clk);
- msleep(1);
+ err = clk_prepare_enable(phy->clk);
+ if (err)
+ return err;
- val = readl(base + USB_SUSP_CTRL);
+ usleep_range(5000, 6000);
+
+ gpiod_set_value_cansleep(phy->reset_gpio, 0);
+
+ usleep_range(1000, 2000);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val |= UHSIC_RESET;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
- val = readl(base + ULPI_TIMING_CTRL_0);
+ val = readl_relaxed(base + ULPI_TIMING_CTRL_0);
val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
- writel(val, base + ULPI_TIMING_CTRL_0);
+ writel_relaxed(val, base + ULPI_TIMING_CTRL_0);
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val |= ULPI_PHY_ENABLE;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
val = 0;
- writel(val, base + ULPI_TIMING_CTRL_1);
+ writel_relaxed(val, base + ULPI_TIMING_CTRL_1);
val |= ULPI_DATA_TRIMMER_SEL(4);
val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
val |= ULPI_DIR_TRIMMER_SEL(4);
- writel(val, base + ULPI_TIMING_CTRL_1);
- udelay(10);
+ writel_relaxed(val, base + ULPI_TIMING_CTRL_1);
+ usleep_range(10, 100);
val |= ULPI_DATA_TRIMMER_LOAD;
val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
val |= ULPI_DIR_TRIMMER_LOAD;
- writel(val, base + ULPI_TIMING_CTRL_1);
+ writel_relaxed(val, base + ULPI_TIMING_CTRL_1);
/* Fix VbusInvalid due to floating VBUS */
- ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
- if (ret) {
- dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", ret);
- return ret;
+ err = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
+ if (err) {
+ dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", err);
+ goto disable_clk;
}
- ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
- if (ret) {
- dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", ret);
- return ret;
+ err = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
+ if (err) {
+ dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", err);
+ goto disable_clk;
}
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val |= USB_SUSP_CLR;
- writel(val, base + USB_SUSP_CTRL);
- udelay(100);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+ usleep_range(100, 1000);
- val = readl(base + USB_SUSP_CTRL);
+ val = readl_relaxed(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_CLR;
- writel(val, base + USB_SUSP_CTRL);
+ writel_relaxed(val, base + USB_SUSP_CTRL);
return 0;
+
+disable_clk:
+ clk_disable_unprepare(phy->clk);
+
+ return err;
}
static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
{
- clk_disable(phy->clk);
- return gpio_direction_output(phy->reset_gpio, 0);
-}
+ gpiod_set_value_cansleep(phy->reset_gpio, 1);
+ usleep_range(5000, 6000);
+ clk_disable_unprepare(phy->clk);
-static void tegra_usb_phy_close(struct tegra_usb_phy *phy)
-{
- if (!IS_ERR(phy->vbus))
- regulator_disable(phy->vbus);
-
- if (!phy->is_ulpi_phy)
- utmip_pad_close(phy);
-
- clk_disable_unprepare(phy->pll_u);
+ return 0;
}
static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
{
+ int err;
+
+ if (phy->powered_on)
+ return 0;
+
if (phy->is_ulpi_phy)
- return ulpi_phy_power_on(phy);
+ err = ulpi_phy_power_on(phy);
else
- return utmi_phy_power_on(phy);
+ err = utmi_phy_power_on(phy);
+ if (err)
+ return err;
+
+ phy->powered_on = true;
+
+ return 0;
}
static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
{
+ int err;
+
+ if (!phy->powered_on)
+ return 0;
+
if (phy->is_ulpi_phy)
- return ulpi_phy_power_off(phy);
+ err = ulpi_phy_power_off(phy);
else
- return utmi_phy_power_off(phy);
+ err = utmi_phy_power_off(phy);
+ if (err)
+ return err;
+
+ phy->powered_on = false;
+
+ return 0;
}
-static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend)
+static void tegra_usb_phy_shutdown(struct usb_phy *u_phy)
{
- struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
+
+ if (WARN_ON(!phy->freq))
+ return;
+
+ tegra_usb_phy_power_off(phy);
+
+ if (!phy->is_ulpi_phy)
+ utmip_pad_close(phy);
+
+ regulator_disable(phy->vbus);
+ clk_disable_unprepare(phy->pll_u);
+
+ phy->freq = NULL;
+}
+
+static int tegra_usb_phy_set_suspend(struct usb_phy *u_phy, int suspend)
+{
+ struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
+
+ if (WARN_ON(!phy->freq))
+ return -EINVAL;
+
if (suspend)
return tegra_usb_phy_power_off(phy);
else
return tegra_usb_phy_power_on(phy);
}
-static int ulpi_open(struct tegra_usb_phy *phy)
+static int tegra_usb_phy_init(struct usb_phy *u_phy)
{
- int err;
-
- phy->clk = devm_clk_get(phy->u_phy.dev, "ulpi-link");
- if (IS_ERR(phy->clk)) {
- err = PTR_ERR(phy->clk);
- dev_err(phy->u_phy.dev, "Failed to get ULPI clock: %d\n", err);
- return err;
- }
-
- err = devm_gpio_request(phy->u_phy.dev, phy->reset_gpio,
- "ulpi_phy_reset_b");
- if (err < 0) {
- dev_err(phy->u_phy.dev, "Request failed for GPIO %d: %d\n",
- phy->reset_gpio, err);
- return err;
- }
-
- err = gpio_direction_output(phy->reset_gpio, 0);
- if (err < 0) {
- dev_err(phy->u_phy.dev,
- "GPIO %d direction not set to output: %d\n",
- phy->reset_gpio, err);
- return err;
- }
-
- phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
- if (!phy->ulpi) {
- dev_err(phy->u_phy.dev, "Failed to create ULPI OTG\n");
- err = -ENOMEM;
- return err;
- }
-
- phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT;
- return 0;
-}
-
-static int tegra_usb_phy_init(struct tegra_usb_phy *phy)
-{
+ struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
unsigned long parent_rate;
- int i;
+ unsigned int i;
int err;
- phy->pll_u = devm_clk_get(phy->u_phy.dev, "pll_u");
- if (IS_ERR(phy->pll_u)) {
- err = PTR_ERR(phy->pll_u);
- dev_err(phy->u_phy.dev,
- "Failed to get pll_u clock: %d\n", err);
- return err;
- }
+ if (WARN_ON(phy->freq))
+ return 0;
err = clk_prepare_enable(phy->pll_u);
if (err)
@@ -864,64 +862,74 @@
dev_err(phy->u_phy.dev, "Invalid pll_u parent rate %ld\n",
parent_rate);
err = -EINVAL;
- goto fail;
+ goto disable_clk;
}
- if (!IS_ERR(phy->vbus)) {
- err = regulator_enable(phy->vbus);
- if (err) {
- dev_err(phy->u_phy.dev,
- "Failed to enable USB VBUS regulator: %d\n",
- err);
- goto fail;
- }
+ err = regulator_enable(phy->vbus);
+ if (err) {
+ dev_err(phy->u_phy.dev,
+ "Failed to enable USB VBUS regulator: %d\n", err);
+ goto disable_clk;
}
- if (phy->is_ulpi_phy)
- err = ulpi_open(phy);
- else
+ if (!phy->is_ulpi_phy) {
err = utmip_pad_open(phy);
- if (err < 0)
- goto fail;
+ if (err)
+ goto disable_vbus;
+ }
+
+ err = tegra_usb_phy_power_on(phy);
+ if (err)
+ goto close_phy;
return 0;
-fail:
+close_phy:
+ if (!phy->is_ulpi_phy)
+ utmip_pad_close(phy);
+
+disable_vbus:
+ regulator_disable(phy->vbus);
+
+disable_clk:
clk_disable_unprepare(phy->pll_u);
+
+ phy->freq = NULL;
+
return err;
}
-void tegra_usb_phy_preresume(struct usb_phy *x)
+void tegra_usb_phy_preresume(struct usb_phy *u_phy)
{
- struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
if (!phy->is_ulpi_phy)
utmi_phy_preresume(phy);
}
EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume);
-void tegra_usb_phy_postresume(struct usb_phy *x)
+void tegra_usb_phy_postresume(struct usb_phy *u_phy)
{
- struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
if (!phy->is_ulpi_phy)
utmi_phy_postresume(phy);
}
EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume);
-void tegra_ehci_phy_restore_start(struct usb_phy *x,
- enum tegra_usb_phy_port_speed port_speed)
+void tegra_ehci_phy_restore_start(struct usb_phy *u_phy,
+ enum tegra_usb_phy_port_speed port_speed)
{
- struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
if (!phy->is_ulpi_phy)
utmi_phy_restore_start(phy, port_speed);
}
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start);
-void tegra_ehci_phy_restore_end(struct usb_phy *x)
+void tegra_ehci_phy_restore_end(struct usb_phy *u_phy)
{
- struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
if (!phy->is_ulpi_phy)
utmi_phy_restore_end(phy);
@@ -932,21 +940,25 @@
u8 *dest)
{
u32 value;
- int err = of_property_read_u32(pdev->dev.of_node, param, &value);
- *dest = (u8)value;
- if (err < 0)
+ int err;
+
+ err = of_property_read_u32(pdev->dev.of_node, param, &value);
+ if (err)
dev_err(&pdev->dev,
"Failed to read USB UTMI parameter %s: %d\n",
param, err);
+ else
+ *dest = value;
+
return err;
}
static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
struct platform_device *pdev)
{
+ struct tegra_utmip_config *config;
struct resource *res;
int err;
- struct tegra_utmip_config *config;
tegra_phy->is_ulpi_phy = false;
@@ -956,8 +968,12 @@
return -ENXIO;
}
+ /*
+ * Note that UTMI pad registers are shared by all PHYs, therefore
+ * devm_platform_ioremap_resource() can't be used here.
+ */
tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
+ resource_size(res));
if (!tegra_phy->pad_regs) {
dev_err(&pdev->dev, "Failed to remap UTMI pad regs\n");
return -ENOMEM;
@@ -971,49 +987,49 @@
config = tegra_phy->config;
err = read_utmi_param(pdev, "nvidia,hssync-start-delay",
- &config->hssync_start_delay);
- if (err < 0)
+ &config->hssync_start_delay);
+ if (err)
return err;
err = read_utmi_param(pdev, "nvidia,elastic-limit",
- &config->elastic_limit);
- if (err < 0)
+ &config->elastic_limit);
+ if (err)
return err;
err = read_utmi_param(pdev, "nvidia,idle-wait-delay",
- &config->idle_wait_delay);
- if (err < 0)
+ &config->idle_wait_delay);
+ if (err)
return err;
err = read_utmi_param(pdev, "nvidia,term-range-adj",
- &config->term_range_adj);
- if (err < 0)
+ &config->term_range_adj);
+ if (err)
return err;
err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew",
- &config->xcvr_lsfslew);
- if (err < 0)
+ &config->xcvr_lsfslew);
+ if (err)
return err;
err = read_utmi_param(pdev, "nvidia,xcvr-lsrslew",
- &config->xcvr_lsrslew);
- if (err < 0)
+ &config->xcvr_lsrslew);
+ if (err)
return err;
if (tegra_phy->soc_config->requires_extra_tuning_parameters) {
err = read_utmi_param(pdev, "nvidia,xcvr-hsslew",
- &config->xcvr_hsslew);
- if (err < 0)
+ &config->xcvr_hsslew);
+ if (err)
return err;
err = read_utmi_param(pdev, "nvidia,hssquelch-level",
- &config->hssquelch_level);
- if (err < 0)
+ &config->hssquelch_level);
+ if (err)
return err;
err = read_utmi_param(pdev, "nvidia,hsdiscon-level",
- &config->hsdiscon_level);
- if (err < 0)
+ &config->hsdiscon_level);
+ if (err)
return err;
}
@@ -1022,8 +1038,8 @@
if (!config->xcvr_setup_use_fuses) {
err = read_utmi_param(pdev, "nvidia,xcvr-setup",
- &config->xcvr_setup);
- if (err < 0)
+ &config->xcvr_setup);
+ if (err)
return err;
}
@@ -1053,23 +1069,20 @@
static int tegra_usb_phy_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
- struct resource *res;
- struct tegra_usb_phy *tegra_phy = NULL;
struct device_node *np = pdev->dev.of_node;
+ struct tegra_usb_phy *tegra_phy;
enum usb_phy_interface phy_type;
+ struct reset_control *reset;
+ struct gpio_desc *gpiod;
+ struct resource *res;
+ struct usb_phy *phy;
int err;
tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL);
if (!tegra_phy)
return -ENOMEM;
- match = of_match_device(tegra_usb_phy_id_table, &pdev->dev);
- if (!match) {
- dev_err(&pdev->dev, "Error: No device match found\n");
- return -ENODEV;
- }
- tegra_phy->soc_config = match->data;
+ tegra_phy->soc_config = of_device_get_match_data(&pdev->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
@@ -1077,8 +1090,12 @@
return -ENXIO;
}
+ /*
+ * Note that PHY and USB controller are using shared registers,
+ * therefore devm_platform_ioremap_resource() can't be used here.
+ */
tegra_phy->regs = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
+ resource_size(res));
if (!tegra_phy->regs) {
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
return -ENOMEM;
@@ -1087,33 +1104,6 @@
tegra_phy->is_legacy_phy =
of_property_read_bool(np, "nvidia,has-legacy-mode");
- phy_type = of_usb_get_phy_mode(np);
- switch (phy_type) {
- case USBPHY_INTERFACE_MODE_UTMI:
- err = utmi_phy_probe(tegra_phy, pdev);
- if (err < 0)
- return err;
- break;
-
- case USBPHY_INTERFACE_MODE_ULPI:
- tegra_phy->is_ulpi_phy = true;
-
- tegra_phy->reset_gpio =
- of_get_named_gpio(np, "nvidia,phy-reset-gpio", 0);
- if (!gpio_is_valid(tegra_phy->reset_gpio)) {
- dev_err(&pdev->dev,
- "Invalid GPIO: %d\n", tegra_phy->reset_gpio);
- return tegra_phy->reset_gpio;
- }
- tegra_phy->config = NULL;
- break;
-
- default:
- dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
- phy_type);
- return -EINVAL;
- }
-
if (of_find_property(np, "dr_mode", NULL))
tegra_phy->mode = usb_get_dr_mode(&pdev->dev);
else
@@ -1125,31 +1115,91 @@
}
/* On some boards, the VBUS regulator doesn't need to be controlled */
- if (of_find_property(np, "vbus-supply", NULL)) {
- tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus");
- if (IS_ERR(tegra_phy->vbus))
- return PTR_ERR(tegra_phy->vbus);
- } else {
- dev_notice(&pdev->dev, "no vbus regulator");
- tegra_phy->vbus = ERR_PTR(-ENODEV);
+ tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus");
+ if (IS_ERR(tegra_phy->vbus))
+ return PTR_ERR(tegra_phy->vbus);
+
+ tegra_phy->pll_u = devm_clk_get(&pdev->dev, "pll_u");
+ err = PTR_ERR_OR_ZERO(tegra_phy->pll_u);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to get pll_u clock: %d\n", err);
+ return err;
+ }
+
+ phy_type = of_usb_get_phy_mode(np);
+ switch (phy_type) {
+ case USBPHY_INTERFACE_MODE_UTMI:
+ err = utmi_phy_probe(tegra_phy, pdev);
+ if (err)
+ return err;
+
+ tegra_phy->pad_clk = devm_clk_get(&pdev->dev, "utmi-pads");
+ err = PTR_ERR_OR_ZERO(tegra_phy->pad_clk);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Failed to get UTMIP pad clock: %d\n", err);
+ return err;
+ }
+
+ reset = devm_reset_control_get_optional_shared(&pdev->dev,
+ "utmi-pads");
+ err = PTR_ERR_OR_ZERO(reset);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Failed to get UTMI-pads reset: %d\n", err);
+ return err;
+ }
+ tegra_phy->pad_rst = reset;
+ break;
+
+ case USBPHY_INTERFACE_MODE_ULPI:
+ tegra_phy->is_ulpi_phy = true;
+
+ tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link");
+ err = PTR_ERR_OR_ZERO(tegra_phy->clk);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Failed to get ULPI clock: %d\n", err);
+ return err;
+ }
+
+ gpiod = devm_gpiod_get_from_of_node(&pdev->dev, np,
+ "nvidia,phy-reset-gpio",
+ 0, GPIOD_OUT_HIGH,
+ "ulpi_phy_reset_b");
+ err = PTR_ERR_OR_ZERO(gpiod);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Request failed for reset GPIO: %d\n", err);
+ return err;
+ }
+ tegra_phy->reset_gpio = gpiod;
+
+ phy = devm_otg_ulpi_create(&pdev->dev,
+ &ulpi_viewport_access_ops, 0);
+ if (!phy) {
+ dev_err(&pdev->dev, "Failed to create ULPI OTG\n");
+ return -ENOMEM;
+ }
+
+ tegra_phy->ulpi = phy;
+ tegra_phy->ulpi->io_priv = tegra_phy->regs + ULPI_VIEWPORT;
+ break;
+
+ default:
+ dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
+ phy_type);
+ return -EINVAL;
}
tegra_phy->u_phy.dev = &pdev->dev;
- err = tegra_usb_phy_init(tegra_phy);
- if (err < 0)
- return err;
-
- tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend;
+ tegra_phy->u_phy.init = tegra_usb_phy_init;
+ tegra_phy->u_phy.shutdown = tegra_usb_phy_shutdown;
+ tegra_phy->u_phy.set_suspend = tegra_usb_phy_set_suspend;
platform_set_drvdata(pdev, tegra_phy);
- err = usb_add_phy_dev(&tegra_phy->u_phy);
- if (err < 0) {
- tegra_usb_phy_close(tegra_phy);
- return err;
- }
-
- return 0;
+ return usb_add_phy_dev(&tegra_phy->u_phy);
}
static int tegra_usb_phy_remove(struct platform_device *pdev)
@@ -1157,7 +1207,6 @@
struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev);
usb_remove_phy(&tegra_phy->u_phy);
- tegra_usb_phy_close(tegra_phy);
return 0;
}
diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c
index 9337c30..ab3c38a 100644
--- a/drivers/usb/phy/phy-twl6030-usb.c
+++ b/drivers/usb/phy/phy-twl6030-usb.c
@@ -2,7 +2,7 @@
/*
* twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver.
*
- * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com
*
* Author: Hema HK <hemahk@ti.com>
*/
diff --git a/drivers/usb/phy/phy-ulpi-viewport.c b/drivers/usb/phy/phy-ulpi-viewport.c
index 7a14e0e..0f61e32 100644
--- a/drivers/usb/phy/phy-ulpi-viewport.c
+++ b/drivers/usb/phy/phy-ulpi-viewport.c
@@ -7,6 +7,7 @@
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
@@ -20,16 +21,9 @@
static int ulpi_viewport_wait(void __iomem *view, u32 mask)
{
- unsigned long usec = 2000;
+ u32 val;
- while (usec--) {
- if (!(readl(view) & mask))
- return 0;
-
- udelay(1);
- }
-
- return -ETIMEDOUT;
+ return readl_poll_timeout_atomic(view, val, !(val & mask), 1, 2000);
}
static int ulpi_viewport_read(struct usb_phy *otg, u32 reg)
diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c
index a43c493..e683a37 100644
--- a/drivers/usb/phy/phy-ulpi.c
+++ b/drivers/usb/phy/phy-ulpi.c
@@ -240,6 +240,21 @@
return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
}
+static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg,
+ struct usb_phy_io_ops *ops,
+ unsigned int flags)
+{
+ phy->label = "ULPI";
+ phy->flags = flags;
+ phy->io_ops = ops;
+ phy->otg = otg;
+ phy->init = ulpi_init;
+
+ otg->usb_phy = phy;
+ otg->set_host = ulpi_set_host;
+ otg->set_vbus = ulpi_set_vbus;
+}
+
struct usb_phy *
otg_ulpi_create(struct usb_phy_io_ops *ops,
unsigned int flags)
@@ -257,17 +272,32 @@
return NULL;
}
- phy->label = "ULPI";
- phy->flags = flags;
- phy->io_ops = ops;
- phy->otg = otg;
- phy->init = ulpi_init;
-
- otg->usb_phy = phy;
- otg->set_host = ulpi_set_host;
- otg->set_vbus = ulpi_set_vbus;
+ otg_ulpi_init(phy, otg, ops, flags);
return phy;
}
EXPORT_SYMBOL_GPL(otg_ulpi_create);
+struct usb_phy *
+devm_otg_ulpi_create(struct device *dev,
+ struct usb_phy_io_ops *ops,
+ unsigned int flags)
+{
+ struct usb_phy *phy;
+ struct usb_otg *otg;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return NULL;
+
+ otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ devm_kfree(dev, phy);
+ return NULL;
+ }
+
+ otg_ulpi_init(phy, otg, ops, flags);
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(devm_otg_ulpi_create);
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index 0277f62..b47285f 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -34,6 +34,14 @@
struct notifier_block *nb;
};
+static const char *const usb_chger_type[] = {
+ [UNKNOWN_TYPE] = "USB_CHARGER_UNKNOWN_TYPE",
+ [SDP_TYPE] = "USB_CHARGER_SDP_TYPE",
+ [CDP_TYPE] = "USB_CHARGER_CDP_TYPE",
+ [DCP_TYPE] = "USB_CHARGER_DCP_TYPE",
+ [ACA_TYPE] = "USB_CHARGER_ACA_TYPE",
+};
+
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@@ -80,7 +88,7 @@
/**
* usb_phy_notify_charger_work - notify the USB charger state
- * @work - the charger work to notify the USB charger state
+ * @work: the charger work to notify the USB charger state
*
* This work can be issued when USB charger state has been changed or
* USB charger current has been changed, then we can notify the current
@@ -98,7 +106,8 @@
{
struct usb_phy *usb_phy = container_of(work, struct usb_phy, chg_work);
char uchger_state[50] = { 0 };
- char *envp[] = { uchger_state, NULL };
+ char uchger_type[50] = { 0 };
+ char *envp[] = { uchger_state, uchger_type, NULL };
unsigned int min, max;
switch (usb_phy->chg_state) {
@@ -122,6 +131,8 @@
return;
}
+ snprintf(uchger_type, ARRAY_SIZE(uchger_type),
+ "USB_CHARGER_TYPE=%s", usb_chger_type[usb_phy->chg_type]);
kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp);
}
@@ -149,9 +160,9 @@
/**
* usb_phy_get_charger_type - get charger type from extcon subsystem
- * @nb -the notifier block to determine charger type
- * @state - the cable state
- * @data - private data
+ * @nb: the notifier block to determine charger type
+ * @state: the cable state
+ * @data: private data
*
* Determin the charger type from extcon subsystem which also means the
* charger state has been chaned, then we should notify this event.
@@ -167,8 +178,8 @@
/**
* usb_phy_set_charger_current - set the USB charger current
- * @usb_phy - the USB phy to be used
- * @mA - the current need to be set
+ * @usb_phy: the USB phy to be used
+ * @mA: the current need to be set
*
* Usually we only change the charger default current when USB finished the
* enumeration as one SDP charger. As one SDP charger, usb_phy_set_power()
@@ -220,9 +231,9 @@
/**
* usb_phy_get_charger_current - get the USB charger current
- * @usb_phy - the USB phy to be used
- * @min - the minimum current
- * @max - the maximum current
+ * @usb_phy: the USB phy to be used
+ * @min: the minimum current
+ * @max: the maximum current
*
* Usually we will notify the maximum current to power user, but for some
* special case, power user also need the minimum current value. Then the
@@ -258,8 +269,8 @@
/**
* usb_phy_set_charger_state - set the USB charger state
- * @usb_phy - the USB phy to be used
- * @state - the new state need to be set for charger
+ * @usb_phy: the USB phy to be used
+ * @state: the new state need to be set for charger
*
* The usb phy driver can issue this function when the usb phy driver
* detected the charger state has been changed, in this case the charger
@@ -403,8 +414,8 @@
/**
* devm_usb_get_phy - find the USB PHY
- * @dev - device that requests this phy
- * @type - the type of the phy the controller requires
+ * @dev: device that requests this phy
+ * @type: the type of the phy the controller requires
*
* Gets the phy using usb_get_phy(), and associates a device with it using
* devres. On driver detach, release function is invoked on the devres data,
@@ -433,7 +444,7 @@
/**
* usb_get_phy - find the USB PHY
- * @type - the type of the phy the controller requires
+ * @type: the type of the phy the controller requires
*
* Returns the phy driver, after getting a refcount to it; or
* -ENODEV if there is no such phy. The caller is responsible for
@@ -469,9 +480,9 @@
/**
* devm_usb_get_phy_by_node - find the USB PHY by device_node
- * @dev - device that requests this phy
- * @node - the device_node for the phy device.
- * @nb - a notifier_block to register with the phy.
+ * @dev: device that requests this phy
+ * @node: the device_node for the phy device.
+ * @nb: a notifier_block to register with the phy.
*
* Returns the phy driver associated with the given device_node,
* after getting a refcount to it, -ENODEV if there is no such phy or
@@ -529,9 +540,9 @@
/**
* devm_usb_get_phy_by_phandle - find the USB PHY by phandle
- * @dev - device that requests this phy
- * @phandle - name of the property holding the phy phandle value
- * @index - the index of the phy
+ * @dev: device that requests this phy
+ * @phandle: name of the property holding the phy phandle value
+ * @index: the index of the phy
*
* Returns the phy driver associated with the given phandle value,
* after getting a refcount to it, -ENODEV if there is no such phy or
@@ -567,8 +578,8 @@
/**
* devm_usb_put_phy - release the USB PHY
- * @dev - device that wants to release this phy
- * @phy - the phy returned by devm_usb_get_phy()
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_usb_get_phy()
*
* destroys the devres associated with this phy and invokes usb_put_phy
* to release the phy.
@@ -604,9 +615,9 @@
EXPORT_SYMBOL_GPL(usb_put_phy);
/**
- * usb_add_phy - declare the USB PHY
+ * usb_add_phy: declare the USB PHY
* @x: the USB phy to be used; or NULL
- * @type - the type of this PHY
+ * @type: the type of this PHY
*
* This call is exclusively for use by phy drivers, which
* coordinate the activities of drivers for host and peripheral
@@ -703,6 +714,7 @@
/**
* usb_phy_set_event - set event to phy event
* @x: the phy returned by usb_get_phy();
+ * @event: event to set
*
* This sets event to phy event
*/
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index a3c30b6..3af91b2 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -8,11 +8,10 @@
*/
#include <linux/clk.h>
#include <linux/err.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -590,9 +589,10 @@
{
const struct renesas_usbhs_platform_info *info;
struct usbhs_priv *priv;
- struct resource *res, *irq_res;
+ struct resource *irq_res;
struct device *dev = &pdev->dev;
- int ret, gpio;
+ struct gpio_desc *gpiod;
+ int ret;
u32 tmp;
/* check device node */
@@ -619,8 +619,7 @@
if (!priv)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(&pdev->dev, res);
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
@@ -658,10 +657,9 @@
priv->dparam.pio_dma_border = 64; /* 64byte */
if (!of_property_read_u32(dev_of_node(dev), "renesas,buswait", &tmp))
priv->dparam.buswait_bwait = tmp;
- gpio = of_get_named_gpio_flags(dev_of_node(dev), "renesas,enable-gpio",
- 0, NULL);
- if (gpio > 0)
- priv->dparam.enable_gpio = gpio;
+ gpiod = devm_gpiod_get_optional(dev, "renesas,enable", GPIOD_IN);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
/* FIXME */
/* runtime power control ? */
@@ -709,13 +707,10 @@
usbhs_sys_clock_ctrl(priv, 0);
/* check GPIO determining if USB function should be enabled */
- if (priv->dparam.enable_gpio) {
- gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL);
- ret = !gpio_get_value(priv->dparam.enable_gpio);
- gpio_free(priv->dparam.enable_gpio);
+ if (gpiod) {
+ ret = !gpiod_get_value(gpiod);
if (ret) {
- dev_warn(dev, "USB function not selected (GPIO %d)\n",
- priv->dparam.enable_gpio);
+ dev_warn(dev, "USB function not selected (GPIO)\n");
ret = -ENOTSUPP;
goto probe_end_mod_exit;
}
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index ef1735d..eb34d76 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
/*
* Renesas USB driver
*
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 2010444..02f49d7 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -1281,11 +1281,11 @@
*/
snprintf(name, sizeof(name), "ch%d", channel);
if (channel & 1) {
- fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
+ fifo->tx_chan = dma_request_chan(dev, name);
if (IS_ERR(fifo->tx_chan))
fifo->tx_chan = NULL;
} else {
- fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
+ fifo->rx_chan = dma_request_chan(dev, name);
if (IS_ERR(fifo->rx_chan))
fifo->rx_chan = NULL;
}
diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h
index 4a7dc23..039a2b9 100644
--- a/drivers/usb/renesas_usbhs/fifo.h
+++ b/drivers/usb/renesas_usbhs/fifo.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
/*
* Renesas USB driver
*
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index 10fc655..b98112c 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -169,17 +169,7 @@
*/
int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state)
{
- int state = irq_state->intsts0 & DVSQ_MASK;
-
- switch (state) {
- case POWER_STATE:
- case DEFAULT_STATE:
- case ADDRESS_STATE:
- case CONFIGURATION_STATE:
- return state;
- }
-
- return -EIO;
+ return (int)irq_state->intsts0 & DVSQ_MASK;
}
int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state)
@@ -348,10 +338,6 @@
* usbhs_interrupt
*/
- /*
- * it don't enable DVSE (intenb0) here
- * but "mod->irq_dev_state" will be called.
- */
if (info->irq_vbus)
intenb0 |= VBSE;
@@ -362,6 +348,9 @@
if (mod->irq_ctrl_stage)
intenb0 |= CTRE;
+ if (mod->irq_dev_state)
+ intenb0 |= DVSE;
+
if (mod->irq_empty && mod->irq_bempsts) {
usbhs_write(priv, BEMPENB, mod->irq_bempsts);
intenb0 |= BEMPE;
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h
index 65dc19c..56b7106 100644
--- a/drivers/usb/renesas_usbhs/mod.h
+++ b/drivers/usb/renesas_usbhs/mod.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
/*
* Renesas USB driver
*
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 53489ca..105132a 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -509,7 +509,7 @@
case READ_STATUS_STAGE:
case WRITE_STATUS_STAGE:
usbhs_dcp_control_transfer_done(pipe);
- /* fall through */
+ fallthrough;
default:
return ret;
}
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 40576e7..75fff2e 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -308,7 +308,7 @@
switch (pid) {
case PID_STALL11:
usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
- /* fall-through */
+ fallthrough;
case PID_STALL10:
usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
}
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
index 3b13052..a4ae9f9 100644
--- a/drivers/usb/renesas_usbhs/pipe.h
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
/*
* Renesas USB driver
*
diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c
index 440d213..52756fc 100644
--- a/drivers/usb/renesas_usbhs/rcar2.c
+++ b/drivers/usb/renesas_usbhs/rcar2.c
@@ -6,8 +6,6 @@
* Copyright (C) 2019 Renesas Electronics Corporation
*/
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include "common.h"
#include "rcar2.h"
@@ -34,7 +32,7 @@
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
if (priv->phy) {
- phy_put(priv->phy);
+ phy_put(&pdev->dev, priv->phy);
priv->phy = NULL;
}
diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h
index 7d88732..046d07e 100644
--- a/drivers/usb/renesas_usbhs/rcar2.h
+++ b/drivers/usb/renesas_usbhs/rcar2.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
#include "common.h"
extern const struct renesas_usbhs_platform_info usbhs_rcar_gen2_plat_info;
diff --git a/drivers/usb/renesas_usbhs/rcar3.h b/drivers/usb/renesas_usbhs/rcar3.h
index c7c5ec1..d13db30 100644
--- a/drivers/usb/renesas_usbhs/rcar3.h
+++ b/drivers/usb/renesas_usbhs/rcar3.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
#include "common.h"
extern const struct renesas_usbhs_platform_info usbhs_rcar_gen3_plat_info;
diff --git a/drivers/usb/renesas_usbhs/rza.h b/drivers/usb/renesas_usbhs/rza.h
index 1ca42a6..a29b75f 100644
--- a/drivers/usb/renesas_usbhs/rza.h
+++ b/drivers/usb/renesas_usbhs/rza.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
#include "common.h"
extern const struct renesas_usbhs_platform_info usbhs_rza1_plat_info;
diff --git a/drivers/usb/renesas_usbhs/rza2.c b/drivers/usb/renesas_usbhs/rza2.c
index 0217495..3eed333 100644
--- a/drivers/usb/renesas_usbhs/rza2.c
+++ b/drivers/usb/renesas_usbhs/rza2.c
@@ -29,7 +29,7 @@
{
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
- phy_put(priv->phy);
+ phy_put(&pdev->dev, priv->phy);
priv->phy = NULL;
return 0;
diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c
index 97e3d75..33b637d 100644
--- a/drivers/usb/roles/class.c
+++ b/drivers/usb/roles/class.c
@@ -48,9 +48,11 @@
mutex_lock(&sw->lock);
- ret = sw->set(sw->dev.parent, role);
- if (!ret)
+ ret = sw->set(sw, role);
+ if (!ret) {
sw->role = role;
+ kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
+ }
mutex_unlock(&sw->lock);
@@ -75,7 +77,7 @@
mutex_lock(&sw->lock);
if (sw->get)
- role = sw->get(sw->dev.parent);
+ role = sw->get(sw);
else
role = sw->role;
@@ -85,19 +87,15 @@
}
EXPORT_SYMBOL_GPL(usb_role_switch_get_role);
-static void *usb_role_switch_match(struct device_connection *con, int ep,
+static void *usb_role_switch_match(struct fwnode_handle *fwnode, const char *id,
void *data)
{
struct device *dev;
- if (con->fwnode) {
- if (con->id && !fwnode_property_present(con->fwnode, con->id))
- return NULL;
+ if (id && !fwnode_property_present(fwnode, id))
+ return NULL;
- dev = class_find_device_by_fwnode(role_class, con->fwnode);
- } else {
- dev = class_find_device_by_name(role_class, con->endpoint[ep]);
- }
+ dev = class_find_device_by_fwnode(role_class, fwnode);
return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
}
@@ -175,10 +173,33 @@
}
EXPORT_SYMBOL_GPL(usb_role_switch_put);
+/**
+ * usb_role_switch_find_by_fwnode - Find USB role switch with its fwnode
+ * @fwnode: fwnode of the USB Role Switch
+ *
+ * Finds and returns role switch with @fwnode. The reference count for the
+ * found switch is incremented.
+ */
+struct usb_role_switch *
+usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode)
+{
+ struct device *dev;
+
+ if (!fwnode)
+ return NULL;
+
+ dev = class_find_device_by_fwnode(role_class, fwnode);
+ if (dev)
+ WARN_ON(!try_module_get(dev->parent->driver->owner));
+
+ return dev ? to_role_switch(dev) : NULL;
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode);
+
static umode_t
usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n)
{
- struct device *dev = container_of(kobj, typeof(*dev), kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct usb_role_switch *sw = to_role_switch(dev);
if (sw->allow_userspace_control)
@@ -308,7 +329,9 @@
sw->dev.fwnode = desc->fwnode;
sw->dev.class = role_class;
sw->dev.type = &usb_role_dev_type;
- dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent));
+ dev_set_drvdata(&sw->dev, desc->driver_data);
+ dev_set_name(&sw->dev, "%s-role-switch",
+ desc->name ? desc->name : dev_name(parent));
ret = device_register(&sw->dev);
if (ret) {
@@ -335,6 +358,27 @@
}
EXPORT_SYMBOL_GPL(usb_role_switch_unregister);
+/**
+ * usb_role_switch_set_drvdata - Assign private data pointer to a switch
+ * @sw: USB Role Switch
+ * @data: Private data pointer
+ */
+void usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data)
+{
+ dev_set_drvdata(&sw->dev, data);
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_set_drvdata);
+
+/**
+ * usb_role_switch_get_drvdata - Get the private data pointer of a switch
+ * @sw: USB Role Switch
+ */
+void *usb_role_switch_get_drvdata(struct usb_role_switch *sw)
+{
+ return dev_get_drvdata(&sw->dev);
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata);
+
static int __init usb_roles_init(void)
{
role_class = class_create(THIS_MODULE, "usb_role");
diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
index 4098513..5c96e92 100644
--- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
+++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
@@ -42,6 +42,7 @@
#define DRV_NAME "intel_xhci_usb_sw"
struct intel_xhci_usb_data {
+ struct device *dev;
struct usb_role_switch *role_sw;
void __iomem *base;
bool enable_sw_switch;
@@ -51,9 +52,10 @@
"intel-xhci-usb-sw",
};
-static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
+static int intel_xhci_usb_set_role(struct usb_role_switch *sw,
+ enum usb_role role)
{
- struct intel_xhci_usb_data *data = dev_get_drvdata(dev);
+ struct intel_xhci_usb_data *data = usb_role_switch_get_drvdata(sw);
unsigned long timeout;
acpi_status status;
u32 glk, val;
@@ -66,11 +68,11 @@
*/
status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk);
if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) {
- dev_err(dev, "Error could not acquire lock\n");
+ dev_err(data->dev, "Error could not acquire lock\n");
return -EIO;
}
- pm_runtime_get_sync(dev);
+ pm_runtime_get_sync(data->dev);
/*
* Set idpin value as requested.
@@ -112,7 +114,7 @@
do {
val = readl(data->base + DUAL_ROLE_CFG1);
if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST)) {
- pm_runtime_put(dev);
+ pm_runtime_put(data->dev);
return 0;
}
@@ -120,21 +122,21 @@
usleep_range(5000, 10000);
} while (time_before(jiffies, timeout));
- pm_runtime_put(dev);
+ pm_runtime_put(data->dev);
- dev_warn(dev, "Timeout waiting for role-switch\n");
+ dev_warn(data->dev, "Timeout waiting for role-switch\n");
return -ETIMEDOUT;
}
-static enum usb_role intel_xhci_usb_get_role(struct device *dev)
+static enum usb_role intel_xhci_usb_get_role(struct usb_role_switch *sw)
{
- struct intel_xhci_usb_data *data = dev_get_drvdata(dev);
+ struct intel_xhci_usb_data *data = usb_role_switch_get_drvdata(sw);
enum usb_role role;
u32 val;
- pm_runtime_get_sync(dev);
+ pm_runtime_get_sync(data->dev);
val = readl(data->base + DUAL_ROLE_CFG0);
- pm_runtime_put(dev);
+ pm_runtime_put(data->dev);
if (!(val & SW_IDPIN))
role = USB_ROLE_HOST;
@@ -161,7 +163,7 @@
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
- data->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ data->base = devm_ioremap(dev, res->start, resource_size(res));
if (!data->base)
return -ENOMEM;
@@ -175,7 +177,9 @@
sw_desc.get = intel_xhci_usb_get_role,
sw_desc.allow_userspace_control = true,
sw_desc.fwnode = software_node_fwnode(&intel_xhci_usb_node);
+ sw_desc.driver_data = data;
+ data->dev = dev;
data->enable_sw_switch = !device_property_read_bool(dev,
"sw_switch_disable");
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 67279c6..4007fa2 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -6,7 +6,7 @@
menuconfig USB_SERIAL
tristate "USB Serial Converter support"
depends on TTY
- ---help---
+ help
Say Y here if you have a USB device that provides normal serial
ports, or acts like a serial device, and you want to connect it to
your USB bus.
@@ -23,7 +23,7 @@
config USB_SERIAL_CONSOLE
bool "USB Serial Console device support"
depends on USB_SERIAL=y
- ---help---
+ help
If you say Y here, it will be possible to use a USB to serial
converter port as the system console (the system console is the
device which receives all kernel messages and warnings and which
@@ -123,15 +123,12 @@
config USB_SERIAL_DIGI_ACCELEPORT
tristate "USB Digi International AccelePort USB Serial Driver"
- ---help---
+ help
Say Y here if you want to use Digi AccelePort USB 2 or 4 devices,
2 port (plus parallel port) and 4 port USB serial converters. The
parallel port on the USB 2 appears as a third serial port on Linux.
The Digi Acceleport USB 8 is not yet supported by this driver.
- This driver works under SMP with the usb-uhci driver. It does not
- work under SMP with the uhci driver.
-
To compile this driver as a module, choose M here: the
module will be called digi_acceleport.
@@ -171,7 +168,7 @@
config USB_SERIAL_FTDI_SIO
tristate "USB FTDI Single Port Serial Driver"
- ---help---
+ help
Say Y here if you want to use a FTDI SIO single port USB to serial
converter device. The implementation I have is called the USC-1000.
This driver has also been tested with the 245 and 232 devices.
@@ -216,7 +213,7 @@
config USB_SERIAL_EDGEPORT
tristate "USB Inside Out Edgeport Serial Driver"
- ---help---
+ help
Say Y here if you want to use any of the following devices from
Inside Out Networks (Digi):
Edgeport/4
@@ -269,19 +266,19 @@
config USB_SERIAL_GARMIN
- tristate "USB Garmin GPS driver"
- help
- Say Y here if you want to connect to your Garmin GPS.
- Should work with most Garmin GPS devices which have a native USB port.
+ tristate "USB Garmin GPS driver"
+ help
+ Say Y here if you want to connect to your Garmin GPS.
+ Should work with most Garmin GPS devices which have a native USB port.
- See <http://sourceforge.net/projects/garmin-gps> for the latest
- version of the driver.
+ See <http://sourceforge.net/projects/garmin-gps> for the latest
+ version of the driver.
- To compile this driver as a module, choose M here: the
- module will be called garmin_gps.
+ To compile this driver as a module, choose M here: the
+ module will be called garmin_gps.
config USB_SERIAL_IPW
- tristate "USB IPWireless (3G UMTS TDD) Driver"
+ tristate "USB IPWireless (3G UMTS TDD) Driver"
select USB_SERIAL_WWAN
help
Say Y here if you want to use a IPWireless USB modem such as
@@ -314,7 +311,7 @@
config USB_SERIAL_KEYSPAN
tristate "USB Keyspan USA-xxx Serial Driver"
select USB_EZUSB_FX2
- ---help---
+ help
Say Y here if you want to use Keyspan USB to serial converter
devices. This driver makes use of Keyspan's official firmware
and was developed with their support. You must also include
@@ -327,7 +324,7 @@
config USB_SERIAL_KLSI
tristate "USB KL5KUSB105 (Palmconnect) Driver"
- ---help---
+ help
Say Y here if you want to use a KL5KUSB105 - based single port
serial adapter. The most widely known -- and currently the only
tested -- device in this category is the PalmConnect USB Serial
@@ -341,25 +338,25 @@
module will be called kl5kusb105.
config USB_SERIAL_KOBIL_SCT
- tristate "USB KOBIL chipcard reader"
- ---help---
- Say Y here if you want to use one of the following KOBIL USB chipcard
- readers:
+ tristate "USB KOBIL chipcard reader"
+ help
+ Say Y here if you want to use one of the following KOBIL USB chipcard
+ readers:
- - USB TWIN
- - KAAN Standard Plus
- - KAAN SIM
- - SecOVID Reader Plus
- - B1 Professional
- - KAAN Professional
+ - USB TWIN
+ - KAAN Standard Plus
+ - KAAN SIM
+ - SecOVID Reader Plus
+ - B1 Professional
+ - KAAN Professional
- Note that you need a current CT-API.
- To compile this driver as a module, choose M here: the
+ Note that you need a current CT-API.
+ To compile this driver as a module, choose M here: the
module will be called kobil_sct.
config USB_SERIAL_MCT_U232
tristate "USB MCT Single Port Serial Driver"
- ---help---
+ help
Say Y here if you want to use a USB Serial single port adapter from
Magic Control Technology Corp. (U232 is one of the model numbers).
@@ -371,7 +368,7 @@
config USB_SERIAL_METRO
tristate "USB Metrologic Instruments USB-POS Barcode Scanner Driver"
- ---help---
+ help
Say Y here if you want to use a USB POS Metrologic barcode scanner.
To compile this driver as a module, choose M here: the
@@ -379,7 +376,7 @@
config USB_SERIAL_MOS7720
tristate "USB Moschip 7720 Serial Driver"
- ---help---
+ help
Say Y here if you want to use USB Serial single and double
port adapters from Moschip Semiconductor Tech.
@@ -391,14 +388,14 @@
depends on USB_SERIAL_MOS7720
depends on PARPORT=y || PARPORT=USB_SERIAL_MOS7720
select PARPORT_NOT_PC
- ---help---
+ help
Say Y if you have a Moschip 7715 device and would like to use
the parallel port it provides. The port will register with
the parport subsystem as a low-level driver.
config USB_SERIAL_MOS7840
tristate "USB Moschip 7840/7820 USB Serial Driver"
- ---help---
+ help
Say Y here if you want to use a MCS7840 Quad-Serial or MCS7820
Dual-Serial port device from MosChip Semiconductor.
@@ -412,7 +409,7 @@
config USB_SERIAL_MXUPORT
tristate "USB Moxa UPORT Serial Driver"
- ---help---
+ help
Say Y here if you want to use a MOXA UPort Serial hub.
This driver supports:
@@ -458,7 +455,7 @@
tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller"
help
Say Y here if you want to use the OTi-6858 single port USB to serial
- converter device.
+ converter device.
To compile this driver as a module, choose M here: the
module will be called oti6858.
@@ -531,7 +528,7 @@
config USB_SERIAL_CYBERJACK
tristate "USB REINER SCT cyberJack pinpad/e-com chipcard reader"
- ---help---
+ help
Say Y here if you want to use a cyberJack pinpad/e-com USB chipcard
reader. This is an interface to ISO 7816 compatible contact-based
chipcards, e.g. GSM SIMs.
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
index 84d5295..a1df686 100644
--- a/drivers/usb/serial/aircable.c
+++ b/drivers/usb/serial/aircable.c
@@ -117,7 +117,7 @@
static void aircable_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- char *data = (char *)urb->transfer_buffer;
+ char *data = urb->transfer_buffer;
int has_headers;
int count;
int len;
diff --git a/drivers/usb/serial/belkin_sa.h b/drivers/usb/serial/belkin_sa.h
index a13a98d..89ec30c 100644
--- a/drivers/usb/serial/belkin_sa.h
+++ b/drivers/usb/serial/belkin_sa.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Definitions for Belkin USB Serial Adapter Driver
*
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index a82ba9c..a2a38fc 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -48,12 +48,6 @@
#define CH341_BIT_DCD 0x08
#define CH341_BITS_MODEM_STAT 0x0f /* all bits */
-/*******************************/
-/* baudrate calculation factor */
-/*******************************/
-#define CH341_BAUDBASE_FACTOR 1532620800
-#define CH341_BAUDBASE_DIVMAX 3
-
/* Break support - the information used to implement this was gleaned from
* the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato.
*/
@@ -65,7 +59,11 @@
#define CH341_REQ_MODEM_CTRL 0xA4
#define CH341_REG_BREAK 0x05
+#define CH341_REG_PRESCALER 0x12
+#define CH341_REG_DIVISOR 0x13
#define CH341_REG_LCR 0x18
+#define CH341_REG_LCR2 0x25
+
#define CH341_NBREAK_BITS 0x01
#define CH341_LCR_ENABLE_RX 0x80
@@ -79,11 +77,14 @@
#define CH341_LCR_CS6 0x01
#define CH341_LCR_CS5 0x00
+#define CH341_QUIRK_LIMITED_PRESCALER BIT(0)
+#define CH341_QUIRK_SIMULATE_BREAK BIT(1)
+
static const struct usb_device_id id_table[] = {
- { USB_DEVICE(0x1a86, 0x5512) },
{ USB_DEVICE(0x1a86, 0x5523) },
{ USB_DEVICE(0x1a86, 0x7522) },
{ USB_DEVICE(0x1a86, 0x7523) },
+ { USB_DEVICE(0x2184, 0x0057) },
{ USB_DEVICE(0x4348, 0x5523) },
{ USB_DEVICE(0x9986, 0x7523) },
{ },
@@ -97,6 +98,7 @@
u8 msr;
u8 lcr;
unsigned long quirks;
+ unsigned long break_end;
};
static void ch341_set_termios(struct tty_struct *tty,
@@ -148,41 +150,129 @@
return 0;
}
-static int ch341_set_baudrate_lcr(struct usb_device *dev,
- struct ch341_private *priv, u8 lcr)
+#define CH341_CLKRATE 48000000
+#define CH341_CLK_DIV(ps, fact) (1 << (12 - 3 * (ps) - (fact)))
+#define CH341_MIN_RATE(ps) (CH341_CLKRATE / (CH341_CLK_DIV((ps), 1) * 512))
+
+static const speed_t ch341_min_rates[] = {
+ CH341_MIN_RATE(0),
+ CH341_MIN_RATE(1),
+ CH341_MIN_RATE(2),
+ CH341_MIN_RATE(3),
+};
+
+/* Supported range is 46 to 3000000 bps. */
+#define CH341_MIN_BPS DIV_ROUND_UP(CH341_CLKRATE, CH341_CLK_DIV(0, 0) * 256)
+#define CH341_MAX_BPS (CH341_CLKRATE / (CH341_CLK_DIV(3, 0) * 2))
+
+/*
+ * The device line speed is given by the following equation:
+ *
+ * baudrate = 48000000 / (2^(12 - 3 * ps - fact) * div), where
+ *
+ * 0 <= ps <= 3,
+ * 0 <= fact <= 1,
+ * 2 <= div <= 256 if fact = 0, or
+ * 9 <= div <= 256 if fact = 1
+ */
+static int ch341_get_divisor(struct ch341_private *priv, speed_t speed)
{
- short a;
- int r;
- unsigned long factor;
- short divisor;
+ unsigned int fact, div, clk_div;
+ bool force_fact0 = false;
+ int ps;
- if (!priv->baud_rate)
- return -EINVAL;
- factor = (CH341_BAUDBASE_FACTOR / priv->baud_rate);
- divisor = CH341_BAUDBASE_DIVMAX;
+ /*
+ * Clamp to supported range, this makes the (ps < 0) and (div < 2)
+ * sanity checks below redundant.
+ */
+ speed = clamp_val(speed, CH341_MIN_BPS, CH341_MAX_BPS);
- while ((factor > 0xfff0) && divisor) {
- factor >>= 3;
- divisor--;
+ /*
+ * Start with highest possible base clock (fact = 1) that will give a
+ * divisor strictly less than 512.
+ */
+ fact = 1;
+ for (ps = 3; ps >= 0; ps--) {
+ if (speed > ch341_min_rates[ps])
+ break;
}
- if (factor > 0xfff0)
+ if (ps < 0)
return -EINVAL;
- factor = 0x10000 - factor;
- a = (factor & 0xff00) | divisor;
+ /* Determine corresponding divisor, rounding down. */
+ clk_div = CH341_CLK_DIV(ps, fact);
+ div = CH341_CLKRATE / (clk_div * speed);
+
+ /* Some devices require a lower base clock if ps < 3. */
+ if (ps < 3 && (priv->quirks & CH341_QUIRK_LIMITED_PRESCALER))
+ force_fact0 = true;
+
+ /* Halve base clock (fact = 0) if required. */
+ if (div < 9 || div > 255 || force_fact0) {
+ div /= 2;
+ clk_div *= 2;
+ fact = 0;
+ }
+
+ if (div < 2)
+ return -EINVAL;
+
+ /*
+ * Pick next divisor if resulting rate is closer to the requested one,
+ * scale up to avoid rounding errors on low rates.
+ */
+ if (16 * CH341_CLKRATE / (clk_div * div) - 16 * speed >=
+ 16 * speed - 16 * CH341_CLKRATE / (clk_div * (div + 1)))
+ div++;
+
+ /*
+ * Prefer lower base clock (fact = 0) if even divisor.
+ *
+ * Note that this makes the receiver more tolerant to errors.
+ */
+ if (fact == 1 && div % 2 == 0) {
+ div /= 2;
+ fact = 0;
+ }
+
+ return (0x100 - div) << 8 | fact << 2 | ps;
+}
+
+static int ch341_set_baudrate_lcr(struct usb_device *dev,
+ struct ch341_private *priv,
+ speed_t baud_rate, u8 lcr)
+{
+ int val;
+ int r;
+
+ if (!baud_rate)
+ return -EINVAL;
+
+ val = ch341_get_divisor(priv, baud_rate);
+ if (val < 0)
+ return -EINVAL;
/*
* CH341A buffers data until a full endpoint-size packet (32 bytes)
* has been received unless bit 7 is set.
*/
- a |= BIT(7);
+ val |= BIT(7);
- r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a);
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
+ CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER,
+ val);
if (r)
return r;
- r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr);
+ /*
+ * Chip versions before version 0x30 as read using
+ * CH341_REQ_READ_VERSION used separate registers for line control
+ * (stop bits, parity and word length). Version 0x30 and above use
+ * CH341_REG_LCR only and CH341_REG_LCR2 is always set to zero.
+ */
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
+ CH341_REG_LCR2 << 8 | CH341_REG_LCR, lcr);
if (r)
return r;
@@ -239,7 +329,7 @@
if (r < 0)
goto out;
- r = ch341_set_baudrate_lcr(dev, priv, priv->lcr);
+ r = ch341_set_baudrate_lcr(dev, priv, priv->baud_rate, priv->lcr);
if (r < 0)
goto out;
@@ -272,7 +362,8 @@
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
CH341_REG_BREAK, 0, buffer, size, DEFAULT_TIMEOUT);
if (r == -EPIPE) {
- dev_dbg(&port->dev, "break control not supported\n");
+ dev_info(&port->dev, "break control not supported, using simulated break\n");
+ quirks = CH341_QUIRK_LIMITED_PRESCALER | CH341_QUIRK_SIMULATE_BREAK;
r = 0;
goto out;
}
@@ -453,7 +544,8 @@
if (baud_rate) {
priv->baud_rate = baud_rate;
- r = ch341_set_baudrate_lcr(port->serial->dev, priv, lcr);
+ r = ch341_set_baudrate_lcr(port->serial->dev, priv,
+ priv->baud_rate, lcr);
if (r < 0 && old_termios) {
priv->baud_rate = tty_termios_baud_rate(old_termios);
tty_termios_copy_hw(&tty->termios, old_termios);
@@ -472,15 +564,98 @@
ch341_set_handshake(port->serial->dev, priv->mcr);
}
+/*
+ * A subset of all CH34x devices don't support a real break condition and
+ * reading CH341_REG_BREAK fails (see also ch341_detect_quirks). This function
+ * simulates a break condition by lowering the baud rate to the minimum
+ * supported by the hardware upon enabling the break condition and sending
+ * a NUL byte.
+ *
+ * Incoming data is corrupted while the break condition is being simulated.
+ *
+ * Normally the duration of the break condition can be controlled individually
+ * by userspace using TIOCSBRK and TIOCCBRK or by passing an argument to
+ * TCSBRKP. Due to how the simulation is implemented the duration can't be
+ * controlled. The duration is always about (1s / 46bd * 9bit) = 196ms.
+ */
+static void ch341_simulate_break(struct tty_struct *tty, int break_state)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ch341_private *priv = usb_get_serial_port_data(port);
+ unsigned long now, delay;
+ int r;
+
+ if (break_state != 0) {
+ dev_dbg(&port->dev, "enter break state requested\n");
+
+ r = ch341_set_baudrate_lcr(port->serial->dev, priv,
+ CH341_MIN_BPS,
+ CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8);
+ if (r < 0) {
+ dev_err(&port->dev,
+ "failed to change baud rate to %u: %d\n",
+ CH341_MIN_BPS, r);
+ goto restore;
+ }
+
+ r = tty_put_char(tty, '\0');
+ if (r < 0) {
+ dev_err(&port->dev,
+ "failed to write NUL byte for simulated break condition: %d\n",
+ r);
+ goto restore;
+ }
+
+ /*
+ * Compute expected transmission duration including safety
+ * margin. The original baud rate is only restored after the
+ * computed point in time.
+ *
+ * 11 bits = 1 start, 8 data, 1 stop, 1 margin
+ */
+ priv->break_end = jiffies + (11 * HZ / CH341_MIN_BPS);
+
+ return;
+ }
+
+ dev_dbg(&port->dev, "leave break state requested\n");
+
+ now = jiffies;
+
+ if (time_before(now, priv->break_end)) {
+ /* Wait until NUL byte is written */
+ delay = priv->break_end - now;
+ dev_dbg(&port->dev,
+ "wait %d ms while transmitting NUL byte at %u baud\n",
+ jiffies_to_msecs(delay), CH341_MIN_BPS);
+ schedule_timeout_interruptible(delay);
+ }
+
+restore:
+ /* Restore original baud rate */
+ r = ch341_set_baudrate_lcr(port->serial->dev, priv, priv->baud_rate,
+ priv->lcr);
+ if (r < 0)
+ dev_err(&port->dev,
+ "restoring original baud rate of %u failed: %d\n",
+ priv->baud_rate, r);
+}
+
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
{
const uint16_t ch341_break_reg =
((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
struct usb_serial_port *port = tty->driver_data;
+ struct ch341_private *priv = usb_get_serial_port_data(port);
int r;
uint16_t reg_contents;
uint8_t *break_reg;
+ if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) {
+ ch341_simulate_break(tty, break_state);
+ return;
+ }
+
break_reg = kmalloc(2, GFP_KERNEL);
if (!break_reg)
return;
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index 7d28930..b97aa40 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -79,7 +79,7 @@
if (*s)
doflow = (*s++ == 'r');
}
-
+
/* Sane default */
if (baud == 0)
baud = 9600;
@@ -102,6 +102,9 @@
break;
}
+ if (doflow)
+ cflag |= CRTSCTS;
+
/*
* no need to check the index here: if the index is wrong, console
* code won't call us
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 8e5878e..7ac6680 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -50,8 +50,12 @@
static int cp210x_port_probe(struct usb_serial_port *);
static int cp210x_port_remove(struct usb_serial_port *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
+static void cp210x_process_read_urb(struct urb *urb);
+static void cp210x_enable_event_mode(struct usb_serial_port *port);
+static void cp210x_disable_event_mode(struct usb_serial_port *port);
static const struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x0404, 0x034C) }, /* NCR Retail IO Box */
{ USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */
{ USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
{ USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */
@@ -69,6 +73,7 @@
{ USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */
{ USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */
{ USB_DEVICE(0x0FDE, 0xCA05) }, /* OWL Wireless Electricity Monitor CM-160 */
+ { USB_DEVICE(0x106F, 0x0003) }, /* CPI / Money Controls Bulk Coin Recycler */
{ USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */
{ USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
{ USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
@@ -234,6 +239,7 @@
{ USB_DEVICE(0x1FB9, 0x0602) }, /* Lake Shore Model 648 Magnet Power Supply */
{ USB_DEVICE(0x1FB9, 0x0700) }, /* Lake Shore Model 737 VSM Controller */
{ USB_DEVICE(0x1FB9, 0x0701) }, /* Lake Shore Model 776 Hall Matrix */
+ { USB_DEVICE(0x2184, 0x0030) }, /* GW Instek GDM-834x Digital Multimeter */
{ USB_DEVICE(0x2626, 0xEA60) }, /* Aruba Networks 7xxx USB Serial Console */
{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
@@ -257,11 +263,24 @@
speed_t min_speed;
speed_t max_speed;
bool use_actual_rate;
+ bool no_event_mode;
+};
+
+enum cp210x_event_state {
+ ES_DATA,
+ ES_ESCAPE,
+ ES_LSR,
+ ES_LSR_DATA_0,
+ ES_LSR_DATA_1,
+ ES_MSR
};
struct cp210x_port_private {
- __u8 bInterfaceNumber;
+ u8 bInterfaceNumber;
bool has_swapped_line_ctl;
+ bool event_mode;
+ enum cp210x_event_state event_state;
+ u8 lsr;
};
static struct usb_serial_driver cp210x_device = {
@@ -282,12 +301,14 @@
.unthrottle = usb_serial_generic_unthrottle,
.tiocmget = cp210x_tiocmget,
.tiocmset = cp210x_tiocmset,
+ .get_icount = usb_serial_generic_get_icount,
.attach = cp210x_attach,
.disconnect = cp210x_disconnect,
.release = cp210x_release,
.port_probe = cp210x_port_probe,
.port_remove = cp210x_port_remove,
- .dtr_rts = cp210x_dtr_rts
+ .dtr_rts = cp210x_dtr_rts,
+ .process_read_urb = cp210x_process_read_urb,
};
static struct usb_serial_driver * const serial_drivers[] = {
@@ -409,13 +430,22 @@
*/
#define PURGE_ALL 0x000f
+/* CP210X_EMBED_EVENTS */
+#define CP210X_ESCCHAR 0xec
+
+#define CP210X_LSR_OVERRUN BIT(1)
+#define CP210X_LSR_PARITY BIT(2)
+#define CP210X_LSR_FRAME BIT(3)
+#define CP210X_LSR_BREAK BIT(4)
+
+
/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */
struct cp210x_flow_ctl {
__le32 ulControlHandshake;
__le32 ulFlowReplace;
__le32 ulXonLimit;
__le32 ulXoffLimit;
-} __packed;
+};
/* cp210x_flow_ctl::ulControlHandshake */
#define CP210X_SERIAL_DTR_MASK GENMASK(1, 0)
@@ -449,7 +479,7 @@
struct cp210x_pin_mode {
u8 eci;
u8 sci;
-} __packed;
+};
#define CP210X_PIN_MODE_MODEM 0
#define CP210X_PIN_MODE_GPIO BIT(0)
@@ -518,7 +548,7 @@
struct cp210x_gpio_write {
u8 mask;
u8 state;
-} __packed;
+};
/*
* Helper to get interface number when we only have struct usb_serial.
@@ -821,6 +851,7 @@
static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
{
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
int result;
result = cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_ENABLE);
@@ -832,21 +863,144 @@
/* Configure the termios structure */
cp210x_get_termios(tty, port);
- /* The baud rate must be initialised on cp2104 */
- if (tty)
+ if (tty) {
+ /* The baud rate must be initialised on cp2104 */
cp210x_change_speed(tty, port, NULL);
- return usb_serial_generic_open(tty, port);
+ if (I_INPCK(tty))
+ cp210x_enable_event_mode(port);
+ }
+
+ result = usb_serial_generic_open(tty, port);
+ if (result)
+ goto err_disable;
+
+ return 0;
+
+err_disable:
+ cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_DISABLE);
+ port_priv->event_mode = false;
+
+ return result;
}
static void cp210x_close(struct usb_serial_port *port)
{
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+
usb_serial_generic_close(port);
/* Clear both queues; cp2108 needs this to avoid an occasional hang */
cp210x_write_u16_reg(port, CP210X_PURGE, PURGE_ALL);
cp210x_write_u16_reg(port, CP210X_IFC_ENABLE, UART_DISABLE);
+
+ /* Disabling the interface disables event-insertion mode. */
+ port_priv->event_mode = false;
+}
+
+static void cp210x_process_lsr(struct usb_serial_port *port, unsigned char lsr, char *flag)
+{
+ if (lsr & CP210X_LSR_BREAK) {
+ port->icount.brk++;
+ *flag = TTY_BREAK;
+ } else if (lsr & CP210X_LSR_PARITY) {
+ port->icount.parity++;
+ *flag = TTY_PARITY;
+ } else if (lsr & CP210X_LSR_FRAME) {
+ port->icount.frame++;
+ *flag = TTY_FRAME;
+ }
+
+ if (lsr & CP210X_LSR_OVERRUN) {
+ port->icount.overrun++;
+ tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+ }
+}
+
+static bool cp210x_process_char(struct usb_serial_port *port, unsigned char *ch, char *flag)
+{
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+
+ switch (port_priv->event_state) {
+ case ES_DATA:
+ if (*ch == CP210X_ESCCHAR) {
+ port_priv->event_state = ES_ESCAPE;
+ break;
+ }
+ return false;
+ case ES_ESCAPE:
+ switch (*ch) {
+ case 0:
+ dev_dbg(&port->dev, "%s - escape char\n", __func__);
+ *ch = CP210X_ESCCHAR;
+ port_priv->event_state = ES_DATA;
+ return false;
+ case 1:
+ port_priv->event_state = ES_LSR_DATA_0;
+ break;
+ case 2:
+ port_priv->event_state = ES_LSR;
+ break;
+ case 3:
+ port_priv->event_state = ES_MSR;
+ break;
+ default:
+ dev_err(&port->dev, "malformed event 0x%02x\n", *ch);
+ port_priv->event_state = ES_DATA;
+ break;
+ }
+ break;
+ case ES_LSR_DATA_0:
+ port_priv->lsr = *ch;
+ port_priv->event_state = ES_LSR_DATA_1;
+ break;
+ case ES_LSR_DATA_1:
+ dev_dbg(&port->dev, "%s - lsr = 0x%02x, data = 0x%02x\n",
+ __func__, port_priv->lsr, *ch);
+ cp210x_process_lsr(port, port_priv->lsr, flag);
+ port_priv->event_state = ES_DATA;
+ return false;
+ case ES_LSR:
+ dev_dbg(&port->dev, "%s - lsr = 0x%02x\n", __func__, *ch);
+ port_priv->lsr = *ch;
+ cp210x_process_lsr(port, port_priv->lsr, flag);
+ port_priv->event_state = ES_DATA;
+ break;
+ case ES_MSR:
+ dev_dbg(&port->dev, "%s - msr = 0x%02x\n", __func__, *ch);
+ /* unimplemented */
+ port_priv->event_state = ES_DATA;
+ break;
+ }
+
+ return true;
+}
+
+static void cp210x_process_read_urb(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+ unsigned char *ch = urb->transfer_buffer;
+ char flag;
+ int i;
+
+ if (!urb->actual_length)
+ return;
+
+ if (port_priv->event_mode) {
+ for (i = 0; i < urb->actual_length; i++, ch++) {
+ flag = TTY_NORMAL;
+
+ if (cp210x_process_char(port, ch, &flag))
+ continue;
+
+ tty_insert_flip_char(&port->port, *ch, flag);
+ }
+ } else {
+ tty_insert_flip_string(&port->port, ch, urb->actual_length);
+ }
+ tty_flip_buffer_push(&port->port);
}
/*
@@ -1179,6 +1333,45 @@
tty_encode_baud_rate(tty, baud, baud);
}
+static void cp210x_enable_event_mode(struct usb_serial_port *port)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(port->serial);
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+ int ret;
+
+ if (port_priv->event_mode)
+ return;
+
+ if (priv->no_event_mode)
+ return;
+
+ port_priv->event_state = ES_DATA;
+ port_priv->event_mode = true;
+
+ ret = cp210x_write_u16_reg(port, CP210X_EMBED_EVENTS, CP210X_ESCCHAR);
+ if (ret) {
+ dev_err(&port->dev, "failed to enable events: %d\n", ret);
+ port_priv->event_mode = false;
+ }
+}
+
+static void cp210x_disable_event_mode(struct usb_serial_port *port)
+{
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+ int ret;
+
+ if (!port_priv->event_mode)
+ return;
+
+ ret = cp210x_write_u16_reg(port, CP210X_EMBED_EVENTS, 0);
+ if (ret) {
+ dev_err(&port->dev, "failed to disable events: %d\n", ret);
+ return;
+ }
+
+ port_priv->event_mode = false;
+}
+
static void cp210x_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
@@ -1301,6 +1494,14 @@
sizeof(flow_ctl));
}
+ /*
+ * Enable event-insertion mode only if input parity checking is
+ * enabled for now.
+ */
+ if (I_INPCK(tty))
+ cp210x_enable_event_mode(port);
+ else
+ cp210x_disable_event_mode(port);
}
static int cp210x_tiocmset(struct tty_struct *tty,
@@ -1551,6 +1752,8 @@
/* 2 banks of GPIO - One for the pins taken from each serial port */
if (intf_num == 0) {
+ priv->gc.ngpio = 2;
+
if (mode.eci == CP210X_PIN_MODE_MODEM) {
/* mark all GPIOs of this interface as reserved */
priv->gpio_altfunc = 0xff;
@@ -1561,8 +1764,9 @@
priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
CP210X_ECI_GPIO_MODE_MASK) >>
CP210X_ECI_GPIO_MODE_OFFSET);
- priv->gc.ngpio = 2;
} else if (intf_num == 1) {
+ priv->gc.ngpio = 3;
+
if (mode.sci == CP210X_PIN_MODE_MODEM) {
/* mark all GPIOs of this interface as reserved */
priv->gpio_altfunc = 0xff;
@@ -1573,7 +1777,6 @@
priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
CP210X_SCI_GPIO_MODE_MASK) >>
CP210X_SCI_GPIO_MODE_OFFSET);
- priv->gc.ngpio = 3;
} else {
return -ENODEV;
}
@@ -1893,6 +2096,46 @@
priv->use_actual_rate = use_actual_rate;
}
+static void cp2102_determine_quirks(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ u8 *buf;
+ int ret;
+
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ return;
+ /*
+ * Some (possibly counterfeit) CP2102 do not support event-insertion
+ * mode and respond differently to malformed vendor requests.
+ * Specifically, they return one instead of two bytes when sent a
+ * two-byte part-number request.
+ */
+ ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ CP210X_VENDOR_SPECIFIC, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_PARTNUM, 0, buf, 2, USB_CTRL_GET_TIMEOUT);
+ if (ret == 1) {
+ dev_dbg(&serial->interface->dev,
+ "device does not support event-insertion mode\n");
+ priv->no_event_mode = true;
+ }
+
+ kfree(buf);
+}
+
+static void cp210x_determine_quirks(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+
+ switch (priv->partnum) {
+ case CP210X_PARTNUM_CP2102:
+ cp2102_determine_quirks(serial);
+ break;
+ default:
+ break;
+ }
+}
+
static int cp210x_attach(struct usb_serial *serial)
{
int result;
@@ -1913,6 +2156,7 @@
usb_set_serial_data(serial, priv);
+ cp210x_determine_quirks(serial);
cp210x_init_max_speed(serial);
result = cp210x_gpio_init(serial);
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 36dd688..2e40908 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -276,7 +276,7 @@
old_rdtodo = priv->rdtodo;
if (old_rdtodo > SHRT_MAX - size) {
- dev_dbg(dev, "To many bulk_in urbs to do.\n");
+ dev_dbg(dev, "Too many bulk_in urbs to do.\n");
spin_unlock_irqrestore(&priv->lock, flags);
goto resubmit;
}
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index ecda821..cc02860 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -1048,7 +1048,7 @@
return;
case -EPIPE:
/* Can't call usb_clear_halt while in_interrupt */
- /* FALLS THROUGH */
+ fallthrough;
default:
/* something ugly is going on... */
dev_err(dev, "%s - unexpected nonzero read status received: %d\n",
@@ -1197,7 +1197,7 @@
return;
case -EPIPE:
/* Cannot call usb_clear_halt while in_interrupt */
- /* FALLTHROUGH */
+ fallthrough;
default:
dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
__func__, status);
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index e0ef758..0d606fa 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1449,7 +1449,7 @@
struct usb_serial_port *port = urb->context;
struct usb_serial *serial = port->serial;
struct tty_struct *tty;
- struct digi_port *priv = usb_get_serial_port_data(port);
+ struct digi_port *priv;
unsigned char *buf = urb->transfer_buffer;
int opcode, line, status, val;
unsigned long flags;
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
index 43fa1f0..0c7eacc 100644
--- a/drivers/usb/serial/f81232.c
+++ b/drivers/usb/serial/f81232.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Fintek F81232 USB to serial adaptor driver
+ * Fintek F81532A/534A/535/536 USB to 2/4/8/12 serial adaptor driver
*
* Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org)
* Copyright (C) 2012 Linux Foundation
@@ -21,11 +22,45 @@
#include <linux/usb/serial.h>
#include <linux/serial_reg.h>
-static const struct usb_device_id id_table[] = {
- { USB_DEVICE(0x1934, 0x0706) },
+#define F81232_ID \
+ { USB_DEVICE(0x1934, 0x0706) } /* 1 port UART device */
+
+#define F81534A_SERIES_ID \
+ { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ \
+ { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ \
+ { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ \
+ { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ \
+ { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ \
+ { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ \
+ { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ \
+ { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \
+ { USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */
+
+#define F81534A_CTRL_ID \
+ { USB_DEVICE(0x2c42, 0x16f8) } /* Global control device */
+
+static const struct usb_device_id f81232_id_table[] = {
+ F81232_ID,
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE(usb, id_table);
+
+static const struct usb_device_id f81534a_id_table[] = {
+ F81534A_SERIES_ID,
+ { } /* Terminating entry */
+};
+
+static const struct usb_device_id f81534a_ctrl_id_table[] = {
+ F81534A_CTRL_ID,
+ { } /* Terminating entry */
+};
+
+static const struct usb_device_id combined_id_table[] = {
+ F81232_ID,
+ F81534A_SERIES_ID,
+ F81534A_CTRL_ID,
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, combined_id_table);
/* Maximum baudrate for F81232 */
#define F81232_MAX_BAUDRATE 1500000
@@ -35,6 +70,7 @@
#define F81232_REGISTER_REQUEST 0xa0
#define F81232_GET_REGISTER 0xc0
#define F81232_SET_REGISTER 0x40
+#define F81534A_ACCESS_REG_RETRY 2
#define SERIAL_BASE_ADDRESS 0x0120
#define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS)
@@ -61,6 +97,22 @@
#define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0))
#define F81232_CLK_MASK GENMASK(1, 0)
+#define F81534A_MODE_REG 0x107
+#define F81534A_TRIGGER_MASK GENMASK(3, 2)
+#define F81534A_TRIGGER_MULTIPLE_4X BIT(3)
+#define F81534A_FIFO_128BYTE (BIT(1) | BIT(0))
+
+/* Serial port self GPIO control, 2bytes [control&output data][input data] */
+#define F81534A_GPIO_REG 0x10e
+#define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */
+#define F81534A_GPIO_MODE1_DIR BIT(5)
+#define F81534A_GPIO_MODE0_DIR BIT(4)
+#define F81534A_GPIO_MODE2_OUTPUT BIT(2)
+#define F81534A_GPIO_MODE1_OUTPUT BIT(1)
+#define F81534A_GPIO_MODE0_OUTPUT BIT(0)
+
+#define F81534A_CTRL_CMD_ENABLE_PORT 0x116
+
struct f81232_private {
struct mutex lock;
u8 modem_control;
@@ -322,10 +374,38 @@
__func__, retval);
}
+static char f81232_handle_lsr(struct usb_serial_port *port, u8 lsr)
+{
+ struct f81232_private *priv = usb_get_serial_port_data(port);
+ char tty_flag = TTY_NORMAL;
+
+ if (!(lsr & UART_LSR_BRK_ERROR_BITS))
+ return tty_flag;
+
+ if (lsr & UART_LSR_BI) {
+ tty_flag = TTY_BREAK;
+ port->icount.brk++;
+ usb_serial_handle_break(port);
+ } else if (lsr & UART_LSR_PE) {
+ tty_flag = TTY_PARITY;
+ port->icount.parity++;
+ } else if (lsr & UART_LSR_FE) {
+ tty_flag = TTY_FRAME;
+ port->icount.frame++;
+ }
+
+ if (lsr & UART_LSR_OE) {
+ port->icount.overrun++;
+ schedule_work(&priv->lsr_work);
+ tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+ }
+
+ return tty_flag;
+}
+
static void f81232_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- struct f81232_private *priv = usb_get_serial_port_data(port);
unsigned char *data = urb->transfer_buffer;
char tty_flag;
unsigned int i;
@@ -341,31 +421,10 @@
/* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */
for (i = 0; i < urb->actual_length; i += 2) {
- tty_flag = TTY_NORMAL;
lsr = data[i];
+ tty_flag = f81232_handle_lsr(port, lsr);
- if (lsr & UART_LSR_BRK_ERROR_BITS) {
- if (lsr & UART_LSR_BI) {
- tty_flag = TTY_BREAK;
- port->icount.brk++;
- usb_serial_handle_break(port);
- } else if (lsr & UART_LSR_PE) {
- tty_flag = TTY_PARITY;
- port->icount.parity++;
- } else if (lsr & UART_LSR_FE) {
- tty_flag = TTY_FRAME;
- port->icount.frame++;
- }
-
- if (lsr & UART_LSR_OE) {
- port->icount.overrun++;
- schedule_work(&priv->lsr_work);
- tty_insert_flip_char(&port->port, 0,
- TTY_OVERRUN);
- }
- }
-
- if (port->port.console && port->sysrq) {
+ if (port->sysrq) {
if (usb_serial_handle_sysrq_char(port, data[i + 1]))
continue;
}
@@ -376,6 +435,47 @@
tty_flip_buffer_push(&port->port);
}
+static void f81534a_process_read_urb(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ char tty_flag;
+ unsigned int i;
+ u8 lsr;
+ u8 len;
+
+ if (urb->actual_length < 3) {
+ dev_err(&port->dev, "short message received: %d\n",
+ urb->actual_length);
+ return;
+ }
+
+ len = data[0];
+ if (len != urb->actual_length) {
+ dev_err(&port->dev, "malformed message received: %d (%d)\n",
+ urb->actual_length, len);
+ return;
+ }
+
+ /* bulk-in data: [LEN][Data.....][LSR] */
+ lsr = data[len - 1];
+ tty_flag = f81232_handle_lsr(port, lsr);
+
+ if (port->sysrq) {
+ for (i = 1; i < len - 1; ++i) {
+ if (!usb_serial_handle_sysrq_char(port, data[i])) {
+ tty_insert_flip_char(&port->port, data[i],
+ tty_flag);
+ }
+ }
+ } else {
+ tty_insert_flip_string_fixed_flag(&port->port, &data[1],
+ tty_flag, len - 2);
+ }
+
+ tty_flip_buffer_push(&port->port);
+}
+
static void f81232_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
@@ -659,6 +759,24 @@
return 0;
}
+static int f81534a_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+ int status;
+ u8 mask;
+ u8 val;
+
+ val = F81534A_TRIGGER_MULTIPLE_4X | F81534A_FIFO_128BYTE;
+ mask = F81534A_TRIGGER_MASK | F81534A_FIFO_128BYTE;
+
+ status = f81232_set_mask_register(port, F81534A_MODE_REG, mask, val);
+ if (status) {
+ dev_err(&port->dev, "failed to set MODE_REG: %d\n", status);
+ return status;
+ }
+
+ return f81232_open(tty, port);
+}
+
static void f81232_close(struct usb_serial_port *port)
{
struct f81232_private *port_priv = usb_get_serial_port_data(port);
@@ -678,6 +796,20 @@
f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
}
+static bool f81232_tx_empty(struct usb_serial_port *port)
+{
+ int status;
+ u8 tmp;
+
+ status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp);
+ if (!status) {
+ if ((tmp & UART_LSR_TEMT) != UART_LSR_TEMT)
+ return false;
+ }
+
+ return true;
+}
+
static int f81232_carrier_raised(struct usb_serial_port *port)
{
u8 msr;
@@ -728,11 +860,98 @@
dev_warn(&port->dev, "read LSR failed: %d\n", status);
}
+static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg,
+ u16 size, void *val)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int retry = F81534A_ACCESS_REG_RETRY;
+ int status;
+ u8 *tmp;
+
+ tmp = kmemdup(val, size, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ while (retry--) {
+ status = usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ F81232_REGISTER_REQUEST,
+ F81232_SET_REGISTER,
+ reg,
+ 0,
+ tmp,
+ size,
+ USB_CTRL_SET_TIMEOUT);
+ if (status < 0) {
+ status = usb_translate_errors(status);
+ if (status == -EIO)
+ continue;
+ } else if (status != size) {
+ /* Retry on short transfers */
+ status = -EIO;
+ continue;
+ } else {
+ status = 0;
+ }
+
+ break;
+ }
+
+ if (status) {
+ dev_err(&intf->dev, "failed to set register 0x%x: %d\n",
+ reg, status);
+ }
+
+ kfree(tmp);
+ return status;
+}
+
+static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en)
+{
+ unsigned char enable[2] = {0};
+ int status;
+
+ /*
+ * Enable all available serial ports, define as following:
+ * bit 15 : Reset behavior (when HUB got soft reset)
+ * 0: maintain all serial port enabled state.
+ * 1: disable all serial port.
+ * bit 0~11 : Serial port enable bit.
+ */
+ if (en) {
+ enable[0] = 0xff;
+ enable[1] = 0x8f;
+ }
+
+ status = f81534a_ctrl_set_register(intf, F81534A_CTRL_CMD_ENABLE_PORT,
+ sizeof(enable), enable);
+ if (status)
+ dev_err(&intf->dev, "failed to enable ports: %d\n", status);
+
+ return status;
+}
+
+static int f81534a_ctrl_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return f81534a_ctrl_enable_all_ports(intf, true);
+}
+
+static void f81534a_ctrl_disconnect(struct usb_interface *intf)
+{
+ f81534a_ctrl_enable_all_ports(intf, false);
+}
+
+static int f81534a_ctrl_resume(struct usb_interface *intf)
+{
+ return f81534a_ctrl_enable_all_ports(intf, true);
+}
+
static int f81232_port_probe(struct usb_serial_port *port)
{
struct f81232_private *priv;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&port->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -748,14 +967,17 @@
return 0;
}
-static int f81232_port_remove(struct usb_serial_port *port)
+static int f81534a_port_probe(struct usb_serial_port *port)
{
- struct f81232_private *priv;
+ int status;
- priv = usb_get_serial_port_data(port);
- kfree(priv);
+ /* tri-state with pull-high, default RS232 Mode */
+ status = f81232_set_register(port, F81534A_GPIO_REG,
+ F81534A_GPIO_MODE2_DIR);
+ if (status)
+ return status;
- return 0;
+ return f81232_port_probe(port);
}
static int f81232_suspend(struct usb_serial *serial, pm_message_t message)
@@ -799,7 +1021,7 @@
.owner = THIS_MODULE,
.name = "f81232",
},
- .id_table = id_table,
+ .id_table = f81232_id_table,
.num_ports = 1,
.bulk_in_size = 256,
.bulk_out_size = 256,
@@ -813,22 +1035,82 @@
.tiocmget = f81232_tiocmget,
.tiocmset = f81232_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
+ .tx_empty = f81232_tx_empty,
.process_read_urb = f81232_process_read_urb,
.read_int_callback = f81232_read_int_callback,
.port_probe = f81232_port_probe,
- .port_remove = f81232_port_remove,
+ .suspend = f81232_suspend,
+ .resume = f81232_resume,
+};
+
+static struct usb_serial_driver f81534a_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "f81534a",
+ },
+ .id_table = f81534a_id_table,
+ .num_ports = 1,
+ .open = f81534a_open,
+ .close = f81232_close,
+ .dtr_rts = f81232_dtr_rts,
+ .carrier_raised = f81232_carrier_raised,
+ .get_serial = f81232_get_serial_info,
+ .break_ctl = f81232_break_ctl,
+ .set_termios = f81232_set_termios,
+ .tiocmget = f81232_tiocmget,
+ .tiocmset = f81232_tiocmset,
+ .tiocmiwait = usb_serial_generic_tiocmiwait,
+ .tx_empty = f81232_tx_empty,
+ .process_read_urb = f81534a_process_read_urb,
+ .read_int_callback = f81232_read_int_callback,
+ .port_probe = f81534a_port_probe,
.suspend = f81232_suspend,
.resume = f81232_resume,
};
static struct usb_serial_driver * const serial_drivers[] = {
&f81232_device,
+ &f81534a_device,
NULL,
};
-module_usb_serial_driver(serial_drivers, id_table);
+static struct usb_driver f81534a_ctrl_driver = {
+ .name = "f81534a_ctrl",
+ .id_table = f81534a_ctrl_id_table,
+ .probe = f81534a_ctrl_probe,
+ .disconnect = f81534a_ctrl_disconnect,
+ .resume = f81534a_ctrl_resume,
+};
-MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
+static int __init f81232_init(void)
+{
+ int status;
+
+ status = usb_register_driver(&f81534a_ctrl_driver, THIS_MODULE,
+ KBUILD_MODNAME);
+ if (status)
+ return status;
+
+ status = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME,
+ combined_id_table);
+ if (status) {
+ usb_deregister(&f81534a_ctrl_driver);
+ return status;
+ }
+
+ return 0;
+}
+
+static void __exit f81232_exit(void)
+{
+ usb_serial_deregister_drivers(serial_drivers);
+ usb_deregister(&f81534a_ctrl_driver);
+}
+
+module_init(f81232_init);
+module_exit(f81232_exit);
+
+MODULE_DESCRIPTION("Fintek F81232/532A/534A/535/536 USB to serial driver");
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
MODULE_AUTHOR("Peter Hong <peter_hong@fintek.com.tw>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c
index 2b39bda..5661fd0 100644
--- a/drivers/usb/serial/f81534.c
+++ b/drivers/usb/serial/f81534.c
@@ -1238,7 +1238,7 @@
schedule_work(&port_priv->lsr_work);
}
- if (port->port.console && port->sysrq) {
+ if (port->sysrq) {
if (usb_serial_handle_sysrq_char(port, data[i]))
continue;
}
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 1ec623e..b74621d 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -969,6 +969,7 @@
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_VX_023_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_VX_034_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_101_PID) },
+ { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_159_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_1_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_2_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_3_PID) },
@@ -977,12 +978,14 @@
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_6_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_7_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_160_8_PID) },
+ { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_235_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_257_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_279_1_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_279_2_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_279_3_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_279_4_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_313_PID) },
+ { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_320_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_324_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_346_1_PID) },
{ USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_346_2_PID) },
@@ -1577,7 +1580,8 @@
dev_dbg(&port->dev, "%s: bcdDevice = 0x%x, bNumInterfaces = %u\n", __func__,
version, interfaces);
if (interfaces > 1) {
- int inter;
+ struct usb_interface *intf = serial->interface;
+ int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
/* Multiple interfaces.*/
if (version == 0x0800) {
@@ -1592,16 +1596,15 @@
priv->chip_type = FT2232C;
/* Determine interface code. */
- inter = serial->interface->altsetting->desc.bInterfaceNumber;
- if (inter == 0) {
+ if (ifnum == 0)
priv->interface = INTERFACE_A;
- } else if (inter == 1) {
+ else if (ifnum == 1)
priv->interface = INTERFACE_B;
- } else if (inter == 2) {
+ else if (ifnum == 2)
priv->interface = INTERFACE_C;
- } else if (inter == 3) {
+ else if (ifnum == 3)
priv->interface = INTERFACE_D;
- }
+
/* BM-type devices have a bug where bcdDevice gets set
* to 0x200 when iSerialNumber is 0. */
if (version < 0x500) {
@@ -2341,12 +2344,11 @@
*/
static int ftdi_jtag_probe(struct usb_serial *serial)
{
- struct usb_device *udev = serial->dev;
- struct usb_interface *interface = serial->interface;
+ struct usb_interface *intf = serial->interface;
+ int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
- if (interface == udev->actconfig->interface[0]) {
- dev_info(&udev->dev,
- "Ignoring serial port reserved for JTAG\n");
+ if (ifnum == 0) {
+ dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n");
return -ENODEV;
}
@@ -2378,12 +2380,11 @@
*/
static int ftdi_stmclite_probe(struct usb_serial *serial)
{
- struct usb_device *udev = serial->dev;
- struct usb_interface *interface = serial->interface;
+ struct usb_interface *intf = serial->interface;
+ int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
- if (interface == udev->actconfig->interface[0] ||
- interface == udev->actconfig->interface[1]) {
- dev_info(&udev->dev, "Ignoring serial port reserved for JTAG\n");
+ if (ifnum < 2) {
+ dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n");
return -ENODEV;
}
@@ -2495,6 +2496,7 @@
struct ftdi_private *priv, unsigned char *buf, int len)
{
unsigned char status;
+ bool brkint = false;
int i;
char flag;
@@ -2546,13 +2548,17 @@
*/
flag = TTY_NORMAL;
if (buf[1] & FTDI_RS_ERR_MASK) {
- /* Break takes precedence over parity, which takes precedence
- * over framing errors */
- if (buf[1] & FTDI_RS_BI) {
- flag = TTY_BREAK;
+ /*
+ * Break takes precedence over parity, which takes precedence
+ * over framing errors. Note that break is only associated
+ * with the last character in the buffer and only when it's a
+ * NUL.
+ */
+ if (buf[1] & FTDI_RS_BI && buf[len - 1] == '\0') {
port->icount.brk++;
- usb_serial_handle_break(port);
- } else if (buf[1] & FTDI_RS_PE) {
+ brkint = true;
+ }
+ if (buf[1] & FTDI_RS_PE) {
flag = TTY_PARITY;
port->icount.parity++;
} else if (buf[1] & FTDI_RS_FE) {
@@ -2568,8 +2574,13 @@
port->icount.rx += len - 2;
- if (port->port.console && port->sysrq) {
+ if (brkint || port->sysrq) {
for (i = 2; i < len; i++) {
+ if (brkint && i == len - 1) {
+ if (usb_serial_handle_break(port))
+ return len - 3;
+ flag = TTY_BREAK;
+ }
if (usb_serial_handle_sysrq_char(port, buf[i]))
continue;
tty_insert_flip_char(&port->port, buf[i], flag);
@@ -2586,7 +2597,7 @@
{
struct usb_serial_port *port = urb->context;
struct ftdi_private *priv = usb_get_serial_port_data(port);
- char *data = (char *)urb->transfer_buffer;
+ char *data = urb->transfer_buffer;
int i;
int len;
int count = 0;
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index a79a132..be1641e 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -302,7 +302,7 @@
/*
* FTDI_SIO_GET_LATENCY_TIMER
*
- * Set the timeout interval. The FTDI collects data from the slave
+ * Set the timeout interval. The FTDI collects data from the
* device, transmitting it to the host when either A) 62 bytes are
* received, or B) the timeout interval has elapsed and the buffer
* contains at least 1 byte. Setting this value to a small number
@@ -324,7 +324,7 @@
/*
* FTDI_SIO_SET_LATENCY_TIMER
*
- * Set the timeout interval. The FTDI collects data from the slave
+ * Set the timeout interval. The FTDI collects data from the
* device, transmitting it to the host when either A) 62 bytes are
* received, or B) the timeout interval has elapsed and the buffer
* contains at least 1 byte. Setting this value to a small number
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 755858c..d1a9564 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -1506,6 +1506,9 @@
#define BRAINBOXES_VX_023_PID 0x1003 /* VX-023 ExpressCard 1 Port RS422/485 */
#define BRAINBOXES_VX_034_PID 0x1004 /* VX-034 ExpressCard 2 Port RS422/485 */
#define BRAINBOXES_US_101_PID 0x1011 /* US-101 1xRS232 */
+#define BRAINBOXES_US_159_PID 0x1021 /* US-159 1xRS232 */
+#define BRAINBOXES_US_235_PID 0x1017 /* US-235 1xRS232 */
+#define BRAINBOXES_US_320_PID 0x1019 /* US-320 1xRS422/485 */
#define BRAINBOXES_US_324_PID 0x1013 /* US-324 1xRS422/485 1Mbaud */
#define BRAINBOXES_US_606_1_PID 0x2001 /* US-606 6 Port RS232 Serial Port 1 and 2 */
#define BRAINBOXES_US_606_2_PID 0x2002 /* US-606 6 Port RS232 Serial Port 3 and 4 */
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index f29c3a9..c02c19b 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -104,7 +104,7 @@
int seq;
/* the real size of the data array, always > 0 */
int size;
- __u8 data[1];
+ __u8 data[];
};
/* structure used to keep the current state of the driver */
@@ -179,19 +179,22 @@
= { 0, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0 };
static unsigned char const GARMIN_BULK_IN_AVAIL_REPLY[]
= { 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_STOP_TRANSFER_REQ[]
+ = { 20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_STOP_TRANSFER_REQ_V2[]
+ = { 20, 0, 0, 0, 10, 0, 0, 0, 1, 0, 0, 0, 0 };
+
+/* packets currently unused, left as documentation */
+#if 0
static unsigned char const GARMIN_APP_LAYER_REPLY[]
= { 0x14, 0, 0, 0 };
static unsigned char const GARMIN_START_PVT_REQ[]
= { 20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 49, 0 };
static unsigned char const GARMIN_STOP_PVT_REQ[]
= { 20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 50, 0 };
-static unsigned char const GARMIN_STOP_TRANSFER_REQ[]
- = { 20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 0, 0 };
-static unsigned char const GARMIN_STOP_TRANSFER_REQ_V2[]
- = { 20, 0, 0, 0, 10, 0, 0, 0, 1, 0, 0, 0, 0 };
static unsigned char const PRIVATE_REQ[]
= { 0x4B, 0x6E, 0x10, 0x01, 0xFF, 0, 0, 0, 0xFF, 0, 0, 0 };
-
+#endif
static const struct usb_device_id id_table[] = {
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 1be8bea..d10aa3d 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -345,7 +345,7 @@
void usb_serial_generic_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- char *ch = (char *)urb->transfer_buffer;
+ char *ch = urb->transfer_buffer;
int i;
if (!urb->actual_length)
@@ -355,13 +355,13 @@
* stuff like 3G modems, so shortcircuit it in the 99.9999999% of
* cases where the USB serial is not a console anyway.
*/
- if (!port->port.console || !port->sysrq) {
- tty_insert_flip_string(&port->port, ch, urb->actual_length);
- } else {
+ if (port->sysrq) {
for (i = 0; i < urb->actual_length; i++, ch++) {
if (!usb_serial_handle_sysrq_char(port, *ch))
tty_insert_flip_char(&port->port, *ch, TTY_NORMAL);
}
+ } else {
+ tty_insert_flip_string(&port->port, ch, urb->actual_length);
}
tty_flip_buffer_push(&port->port);
}
@@ -417,7 +417,7 @@
/*
* Make sure URB is marked as free before checking the throttled flag
* to avoid racing with unthrottle() on another CPU. Matches the
- * smp_mb() in unthrottle().
+ * smp_mb__after_atomic() in unthrottle().
*/
smp_mb__after_atomic();
@@ -489,7 +489,7 @@
* Matches the smp_mb__after_atomic() in
* usb_serial_generic_read_bulk_callback().
*/
- smp_mb();
+ smp_mb__after_atomic();
usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
}
@@ -571,10 +571,10 @@
}
EXPORT_SYMBOL_GPL(usb_serial_generic_get_icount);
-#ifdef CONFIG_MAGIC_SYSRQ
+#if defined(CONFIG_USB_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
{
- if (port->sysrq && port->port.console) {
+ if (port->sysrq) {
if (ch && time_before(jiffies, port->sysrq)) {
handle_sysrq(ch);
port->sysrq = 0;
@@ -584,16 +584,13 @@
}
return 0;
}
-#else
-int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch)
-{
- return 0;
-}
-#endif
EXPORT_SYMBOL_GPL(usb_serial_handle_sysrq_char);
int usb_serial_handle_break(struct usb_serial_port *port)
{
+ if (!port->port.console)
+ return 0;
+
if (!port->sysrq) {
port->sysrq = jiffies + HZ*5;
return 1;
@@ -602,6 +599,7 @@
return 0;
}
EXPORT_SYMBOL_GPL(usb_serial_handle_break);
+#endif
/**
* usb_serial_handle_dcd_change - handle a change of carrier detect state
@@ -609,12 +607,10 @@
* @tty: tty for the port
* @status: new carrier detect status, nonzero if active
*/
-void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
+void usb_serial_handle_dcd_change(struct usb_serial_port *port,
struct tty_struct *tty, unsigned int status)
{
- struct tty_port *port = &usb_port->port;
-
- dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status);
+ dev_dbg(&port->dev, "%s - status %d\n", __func__, status);
if (tty) {
struct tty_ldisc *ld = tty_ldisc_ref(tty);
@@ -627,7 +623,7 @@
}
if (status)
- wake_up_interruptible(&port->open_wait);
+ wake_up_interruptible(&port->port.open_wait);
else if (tty && !C_CLOCAL(tty))
tty_hangup(tty);
}
diff --git a/drivers/usb/serial/io_16654.h b/drivers/usb/serial/io_16654.h
index 4980f72..f18501f 100644
--- a/drivers/usb/serial/io_16654.h
+++ b/drivers/usb/serial/io_16654.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/************************************************************************
*
* 16654.H Definitions for 16C654 UART used on EdgePorts
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 3b4d1ff..4b48ef4 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1752,7 +1752,7 @@
edge_serial->rxState = EXPECT_HDR2;
break;
}
- /* Fall through */
+ fallthrough;
case EXPECT_HDR2:
edge_serial->rxHeader2 = *buffer;
++buffer;
@@ -1804,7 +1804,7 @@
edge_serial->rxState = EXPECT_DATA;
break;
}
- /* Fall through */
+ fallthrough;
case EXPECT_DATA: /* Expect data */
if (bufferLength < edge_serial->rxBytesRemaining) {
rxLen = bufferLength;
diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h
index 2e7fedb..43ba53a 100644
--- a/drivers/usb/serial/io_edgeport.h
+++ b/drivers/usb/serial/io_edgeport.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/************************************************************************
*
* io_edgeport.h Edgeport Linux Interface definitions
diff --git a/drivers/usb/serial/io_ionsp.h b/drivers/usb/serial/io_ionsp.h
index 4b8e482..db4fce8 100644
--- a/drivers/usb/serial/io_ionsp.h
+++ b/drivers/usb/serial/io_ionsp.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/************************************************************************
*
* IONSP.H Definitions for I/O Networks Serial Protocol
diff --git a/drivers/usb/serial/io_ti.h b/drivers/usb/serial/io_ti.h
index 9bbcee3..50b899d 100644
--- a/drivers/usb/serial/io_ti.h
+++ b/drivers/usb/serial/io_ti.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*****************************************************************************
*
* Copyright (C) 1997-2002 Inside Out Networks, Inc.
diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h
index c38e87a..52cbc35 100644
--- a/drivers/usb/serial/io_usbvend.h
+++ b/drivers/usb/serial/io_usbvend.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/************************************************************************
*
* USBVEND.H Vendor-specific USB definitions
@@ -593,7 +593,7 @@
__u8 Type; // Type of descriptor
__le16 Size; // Size of data only not including header
__u8 CheckSum; // Checksum (8 bit sum of data only)
- __u8 Data[0]; // Data starts here
+ __u8 Data[]; // Data starts here
} __attribute__((packed));
// for 5152 devices only (type 2 record)
@@ -601,7 +601,7 @@
struct ti_i2c_firmware_rec {
__u8 Ver_Major; // Firmware Major version number
__u8 Ver_Minor; // Firmware Minor version number
- __u8 Data[0]; // Download starts here
+ __u8 Data[]; // Download starts here
} __attribute__((packed));
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 627bea7..172261a 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -76,6 +76,8 @@
.description = "IR Dongle",
.id_table = ir_id_table,
.num_ports = 1,
+ .num_bulk_in = 1,
+ .num_bulk_out = 1,
.set_termios = ir_set_termios,
.attach = ir_startup,
.write = ir_write,
@@ -197,9 +199,6 @@
struct usb_irda_cs_descriptor *irda_desc;
int rates;
- if (serial->num_bulk_in < 1 || serial->num_bulk_out < 1)
- return -ENODEV;
-
irda_desc = irda_usb_find_class_desc(serial, 0);
if (!irda_desc) {
dev_err(&serial->dev->dev,
@@ -376,23 +375,15 @@
tty_flip_buffer_push(&port->port);
}
-static void ir_set_termios_callback(struct urb *urb)
-{
- kfree(urb->transfer_buffer);
-
- if (urb->status)
- dev_dbg(&urb->dev->dev, "%s - non-zero urb status: %d\n",
- __func__, urb->status);
-}
-
static void ir_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
- struct urb *urb;
+ struct usb_device *udev = port->serial->dev;
unsigned char *transfer_buffer;
- int result;
+ int actual_length;
speed_t baud;
int ir_baud;
+ int ret;
baud = tty_get_baud_rate(tty);
@@ -447,42 +438,22 @@
/*
* send the baud change out on an "empty" data packet
*/
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb)
- return;
-
transfer_buffer = kmalloc(1, GFP_KERNEL);
if (!transfer_buffer)
- goto err_buf;
+ return;
*transfer_buffer = ir_xbof | ir_baud;
- usb_fill_bulk_urb(
- urb,
- port->serial->dev,
- usb_sndbulkpipe(port->serial->dev,
- port->bulk_out_endpointAddress),
- transfer_buffer,
- 1,
- ir_set_termios_callback,
- port);
-
- urb->transfer_flags = URB_ZERO_PACKET;
-
- result = usb_submit_urb(urb, GFP_KERNEL);
- if (result) {
- dev_err(&port->dev, "%s - failed to submit urb: %d\n",
- __func__, result);
- goto err_subm;
+ ret = usb_bulk_msg(udev,
+ usb_sndbulkpipe(udev, port->bulk_out_endpointAddress),
+ transfer_buffer, 1, &actual_length, 5000);
+ if (ret || actual_length != 1) {
+ if (!ret)
+ ret = -EIO;
+ dev_err(&port->dev, "failed to change line speed: %d\n", ret);
}
- usb_free_urb(urb);
-
- return;
-err_subm:
kfree(transfer_buffer);
-err_buf:
- usb_free_urb(urb);
}
static int __init ir_init(void)
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 7e4ec0b..c142051 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -158,7 +158,6 @@
static void iuu_rxcmd(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- int result;
int status = urb->status;
if (status) {
@@ -174,7 +173,7 @@
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 1,
read_rxcmd_callback, port);
- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
static int iuu_reset(struct usb_serial_port *port, u8 wt)
@@ -241,7 +240,6 @@
static void iuu_status_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- int result;
int status = urb->status;
dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
@@ -250,7 +248,7 @@
port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, 256,
iuu_update_status_callback, port);
- result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ usb_submit_urb(port->read_urb, GFP_ATOMIC);
}
static int iuu_status(struct usb_serial_port *port)
@@ -351,7 +349,6 @@
static void iuu_led_activity_on(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- int result;
char *buf_ptr = port->write_urb->transfer_buffer;
if (xmas) {
@@ -367,13 +364,12 @@
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 8 ,
iuu_rxcmd, port);
- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
static void iuu_led_activity_off(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- int result;
char *buf_ptr = port->write_urb->transfer_buffer;
if (xmas) {
@@ -388,7 +384,7 @@
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 8 ,
iuu_rxcmd, port);
- result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+ usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
diff --git a/drivers/usb/serial/iuu_phoenix.h b/drivers/usb/serial/iuu_phoenix.h
index b400b26..87992b2 100644
--- a/drivers/usb/serial/iuu_phoenix.h
+++ b/drivers/usb/serial/iuu_phoenix.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Infinity Unlimited USB Phoenix driver
*
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index aa3dbce..451759f 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -2910,22 +2910,22 @@
for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i) {
p_priv->in_buffer[i] = kzalloc(IN_BUFLEN, GFP_KERNEL);
if (!p_priv->in_buffer[i])
- goto err_in_buffer;
+ goto err_free_in_buffer;
}
for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i) {
p_priv->out_buffer[i] = kzalloc(OUT_BUFLEN, GFP_KERNEL);
if (!p_priv->out_buffer[i])
- goto err_out_buffer;
+ goto err_free_out_buffer;
}
p_priv->inack_buffer = kzalloc(INACK_BUFLEN, GFP_KERNEL);
if (!p_priv->inack_buffer)
- goto err_inack_buffer;
+ goto err_free_out_buffer;
p_priv->outcont_buffer = kzalloc(OUTCONT_BUFLEN, GFP_KERNEL);
if (!p_priv->outcont_buffer)
- goto err_outcont_buffer;
+ goto err_free_inack_buffer;
p_priv->device_details = d_details;
@@ -2971,15 +2971,14 @@
return 0;
-err_outcont_buffer:
+err_free_inack_buffer:
kfree(p_priv->inack_buffer);
-err_inack_buffer:
+err_free_out_buffer:
for (i = 0; i < ARRAY_SIZE(p_priv->out_buffer); ++i)
kfree(p_priv->out_buffer[i]);
-err_out_buffer:
+err_free_in_buffer:
for (i = 0; i < ARRAY_SIZE(p_priv->in_buffer); ++i)
kfree(p_priv->in_buffer[i]);
-err_in_buffer:
kfree(p_priv);
return -ENOMEM;
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 5dd228d..aec32bf 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -668,11 +668,10 @@
/* download the firmware to a "fake" device (pre-renumeration) */
static int keyspan_pda_fake_startup(struct usb_serial *serial)
{
- int response;
const char *fw_name;
/* download the firmware here ... */
- response = ezusb_fx1_set_reset(serial->dev, 1);
+ ezusb_fx1_set_reset(serial->dev, 1);
if (0) { ; }
#ifdef KEYSPAN
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index e9882ba..49aacb0 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -499,7 +499,7 @@
break;
default:
speed = 9600;
- /* fall through */
+ fallthrough;
case 9600:
urb_val = SUSBCR_SBR_9600;
break;
@@ -526,6 +526,10 @@
0,
KOBIL_TIMEOUT
);
+ if (result) {
+ dev_err(&port->dev, "failed to update line settings: %d\n",
+ result);
+ }
}
static int kobil_ioctl(struct tty_struct *tty,
diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h
index 0084edf..e3d09a8 100644
--- a/drivers/usb/serial/mct_u232.h
+++ b/drivers/usb/serial/mct_u232.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Definitions for MCT (Magic Control Technology) USB-RS232 Converter Driver
*
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index f5caf38..c713d98 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -284,11 +284,12 @@
* port callback had to be deferred because the disconnect mutex could not be
* obtained at the time.
*/
-static void send_deferred_urbs(unsigned long _mos_parport)
+static void send_deferred_urbs(struct tasklet_struct *t)
{
int ret_val;
unsigned long flags;
- struct mos7715_parport *mos_parport = (void *)_mos_parport;
+ struct mos7715_parport *mos_parport = from_tasklet(mos_parport, t,
+ urb_tasklet);
struct urbtracker *urbtrack, *tmp;
struct list_head *cursor, *next;
struct device *dev;
@@ -720,8 +721,7 @@
INIT_LIST_HEAD(&mos_parport->deferred_urbs);
usb_set_serial_data(serial, mos_parport); /* hijack private pointer */
mos_parport->serial = serial;
- tasklet_init(&mos_parport->urb_tasklet, send_deferred_urbs,
- (unsigned long) mos_parport);
+ tasklet_setup(&mos_parport->urb_tasklet, send_deferred_urbs);
init_completion(&mos_parport->syncmsg_compl);
/* cycle parallel port reset bit */
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 2b8a0d4..48a9a04 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -89,17 +89,10 @@
/* For higher baud Rates use TIOCEXBAUD */
#define TIOCEXBAUD 0x5462
-/* vendor id and device id defines */
-
-/* The native mos7840/7820 component */
-#define USB_VENDOR_ID_MOSCHIP 0x9710
-#define MOSCHIP_DEVICE_ID_7840 0x7840
-#define MOSCHIP_DEVICE_ID_7843 0x7843
-#define MOSCHIP_DEVICE_ID_7820 0x7820
-#define MOSCHIP_DEVICE_ID_7810 0x7810
-/* The native component can have its vendor/device id's overridden
- * in vendor-specific implementations. Such devices can be handled
- * by making a change here, in id_table.
+/*
+ * Vendor id and device id defines
+ *
+ * NOTE: Do not add new defines, add entries directly to the id_table instead.
*/
#define USB_VENDOR_ID_BANDB 0x0856
#define BANDB_DEVICE_ID_USO9ML2_2 0xAC22
@@ -114,19 +107,6 @@
#define BANDB_DEVICE_ID_USOPTL4_2P 0xBC02
#define BANDB_DEVICE_ID_USOPTL4_4 0xAC44
#define BANDB_DEVICE_ID_USOPTL4_4P 0xBC03
-#define BANDB_DEVICE_ID_USOPTL2_4 0xAC24
-
-/* This driver also supports
- * ATEN UC2324 device using Moschip MCS7840
- * ATEN UC2322 device using Moschip MCS7820
- * MOXA UPort 2210 device using Moschip MCS7820
- */
-#define USB_VENDOR_ID_ATENINTL 0x0557
-#define ATENINTL_DEVICE_ID_UC2324 0x2011
-#define ATENINTL_DEVICE_ID_UC2322 0x7820
-
-#define USB_VENDOR_ID_MOXA 0x110a
-#define MOXA_DEVICE_ID_2210 0x2210
/* Interrupt Routine Defines */
@@ -175,31 +155,36 @@
#define LED_OFF_MS 500
enum mos7840_flag {
- MOS7840_FLAG_CTRL_BUSY,
MOS7840_FLAG_LED_BUSY,
};
+#define MCS_PORT_MASK GENMASK(2, 0)
+#define MCS_PORTS(nr) ((nr) & MCS_PORT_MASK)
+#define MCS_LED BIT(3)
+
+#define MCS_DEVICE(vid, pid, flags) \
+ USB_DEVICE((vid), (pid)), .driver_info = (flags)
+
static const struct usb_device_id id_table[] = {
- {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
- {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7843)},
- {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
- {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7810)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4P)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2P)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4P)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL2_4)},
- {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)},
- {USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)},
- {USB_DEVICE(USB_VENDOR_ID_MOXA, MOXA_DEVICE_ID_2210)},
+ { MCS_DEVICE(0x0557, 0x2011, MCS_PORTS(4)) }, /* ATEN UC2324 */
+ { MCS_DEVICE(0x0557, 0x7820, MCS_PORTS(2)) }, /* ATEN UC2322 */
+ { MCS_DEVICE(0x110a, 0x2210, MCS_PORTS(2)) }, /* Moxa UPort 2210 */
+ { MCS_DEVICE(0x9710, 0x7810, MCS_PORTS(1) | MCS_LED) }, /* ASIX MCS7810 */
+ { MCS_DEVICE(0x9710, 0x7820, MCS_PORTS(2)) }, /* MosChip MCS7820 */
+ { MCS_DEVICE(0x9710, 0x7840, MCS_PORTS(4)) }, /* MosChip MCS7840 */
+ { MCS_DEVICE(0x9710, 0x7843, MCS_PORTS(3)) }, /* ASIX MCS7840 3 port */
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2P) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4P) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2P) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4) },
+ { USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4P) },
{} /* terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
@@ -211,19 +196,12 @@
struct urb *read_urb; /* read URB for this port */
__u8 shadowLCR; /* last LCR value received */
__u8 shadowMCR; /* last MCR value received */
- char open;
- char open_ports;
struct usb_serial_port *port; /* loop back to the owner of this object */
/* Offsets */
__u8 SpRegOffset;
__u8 ControlRegOffset;
__u8 DcrRegOffset;
- /* for processing control URBS in interrupt context */
- struct urb *control_urb;
- struct usb_ctrlrequest *dr;
- char *ctrl_buf;
- int MsrLsr;
spinlock_t pool_lock;
struct urb *write_urb_pool[NUM_URBS];
@@ -365,150 +343,11 @@
/************************************************************************/
/************************************************************************/
-/* I N T E R F A C E F U N C T I O N S */
-/* I N T E R F A C E F U N C T I O N S */
-/************************************************************************/
-/************************************************************************/
-
-static inline void mos7840_set_port_private(struct usb_serial_port *port,
- struct moschip_port *data)
-{
- usb_set_serial_port_data(port, (void *)data);
-}
-
-static inline struct moschip_port *mos7840_get_port_private(struct
- usb_serial_port
- *port)
-{
- return (struct moschip_port *)usb_get_serial_port_data(port);
-}
-
-static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
-{
- struct moschip_port *mos7840_port;
- struct async_icount *icount;
- mos7840_port = port;
- if (new_msr &
- (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI |
- MOS_MSR_DELTA_CD)) {
- icount = &mos7840_port->port->icount;
-
- /* update input line counters */
- if (new_msr & MOS_MSR_DELTA_CTS)
- icount->cts++;
- if (new_msr & MOS_MSR_DELTA_DSR)
- icount->dsr++;
- if (new_msr & MOS_MSR_DELTA_CD)
- icount->dcd++;
- if (new_msr & MOS_MSR_DELTA_RI)
- icount->rng++;
-
- wake_up_interruptible(&port->port->port.delta_msr_wait);
- }
-}
-
-static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
-{
- struct async_icount *icount;
-
- if (new_lsr & SERIAL_LSR_BI) {
- /*
- * Parity and Framing errors only count if they
- * occur exclusive of a break being
- * received.
- */
- new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI);
- }
-
- /* update input line counters */
- icount = &port->port->icount;
- if (new_lsr & SERIAL_LSR_BI)
- icount->brk++;
- if (new_lsr & SERIAL_LSR_OE)
- icount->overrun++;
- if (new_lsr & SERIAL_LSR_PE)
- icount->parity++;
- if (new_lsr & SERIAL_LSR_FE)
- icount->frame++;
-}
-
-/************************************************************************/
-/************************************************************************/
/* U S B C A L L B A C K F U N C T I O N S */
/* U S B C A L L B A C K F U N C T I O N S */
/************************************************************************/
/************************************************************************/
-static void mos7840_control_callback(struct urb *urb)
-{
- unsigned char *data;
- struct moschip_port *mos7840_port;
- struct device *dev = &urb->dev->dev;
- __u8 regval = 0x0;
- int status = urb->status;
-
- mos7840_port = urb->context;
-
- switch (status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
- goto out;
- default:
- dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
- goto out;
- }
-
- dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length);
- if (urb->actual_length < 1)
- goto out;
-
- dev_dbg(dev, "%s mos7840_port->MsrLsr is %d port %d\n", __func__,
- mos7840_port->MsrLsr, mos7840_port->port_num);
- data = urb->transfer_buffer;
- regval = (__u8) data[0];
- dev_dbg(dev, "%s data is %x\n", __func__, regval);
- if (mos7840_port->MsrLsr == 0)
- mos7840_handle_new_msr(mos7840_port, regval);
- else if (mos7840_port->MsrLsr == 1)
- mos7840_handle_new_lsr(mos7840_port, regval);
-out:
- clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mos7840_port->flags);
-}
-
-static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
- __u16 *val)
-{
- struct usb_device *dev = mcs->port->serial->dev;
- struct usb_ctrlrequest *dr = mcs->dr;
- unsigned char *buffer = mcs->ctrl_buf;
- int ret;
-
- if (test_and_set_bit_lock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags))
- return -EBUSY;
-
- dr->bRequestType = MCS_RD_RTYPE;
- dr->bRequest = MCS_RDREQ;
- dr->wValue = cpu_to_le16(Wval); /* 0 */
- dr->wIndex = cpu_to_le16(reg);
- dr->wLength = cpu_to_le16(2);
-
- usb_fill_control_urb(mcs->control_urb, dev, usb_rcvctrlpipe(dev, 0),
- (unsigned char *)dr, buffer, 2,
- mos7840_control_callback, mcs);
- mcs->control_urb->transfer_buffer_length = 2;
- ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
- if (ret)
- clear_bit_unlock(MOS7840_FLAG_CTRL_BUSY, &mcs->flags);
-
- return ret;
-}
-
static void mos7840_set_led_callback(struct urb *urb)
{
switch (urb->status) {
@@ -585,146 +424,6 @@
}
/*****************************************************************************
- * mos7840_interrupt_callback
- * this is the callback function for when we have received data on the
- * interrupt endpoint.
- *****************************************************************************/
-
-static void mos7840_interrupt_callback(struct urb *urb)
-{
- int result;
- int length;
- struct moschip_port *mos7840_port;
- struct usb_serial *serial;
- __u16 Data;
- unsigned char *data;
- __u8 sp[5];
- int i, rv = 0;
- __u16 wval, wreg = 0;
- int status = urb->status;
-
- switch (status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
- __func__, status);
- return;
- default:
- dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
- __func__, status);
- goto exit;
- }
-
- length = urb->actual_length;
- data = urb->transfer_buffer;
-
- serial = urb->context;
-
- /* Moschip get 5 bytes
- * Byte 1 IIR Port 1 (port.number is 0)
- * Byte 2 IIR Port 2 (port.number is 1)
- * Byte 3 IIR Port 3 (port.number is 2)
- * Byte 4 IIR Port 4 (port.number is 3)
- * Byte 5 FIFO status for both */
-
- if (length > 5) {
- dev_dbg(&urb->dev->dev, "%s", "Wrong data !!!\n");
- return;
- }
-
- sp[0] = (__u8) data[0];
- sp[1] = (__u8) data[1];
- sp[2] = (__u8) data[2];
- sp[3] = (__u8) data[3];
-
- for (i = 0; i < serial->num_ports; i++) {
- mos7840_port = mos7840_get_port_private(serial->port[i]);
- wval = ((__u16)serial->port[i]->port_number + 1) << 8;
- if (mos7840_port->open) {
- if (sp[i] & 0x01) {
- dev_dbg(&urb->dev->dev, "SP%d No Interrupt !!!\n", i);
- } else {
- switch (sp[i] & 0x0f) {
- case SERIAL_IIR_RLS:
- dev_dbg(&urb->dev->dev, "Serial Port %d: Receiver status error or \n", i);
- dev_dbg(&urb->dev->dev, "address bit detected in 9-bit mode\n");
- mos7840_port->MsrLsr = 1;
- wreg = LINE_STATUS_REGISTER;
- break;
- case SERIAL_IIR_MS:
- dev_dbg(&urb->dev->dev, "Serial Port %d: Modem status change\n", i);
- mos7840_port->MsrLsr = 0;
- wreg = MODEM_STATUS_REGISTER;
- break;
- }
- rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data);
- }
- }
- }
- if (!(rv < 0))
- /* the completion handler for the control urb will resubmit */
- return;
-exit:
- result = usb_submit_urb(urb, GFP_ATOMIC);
- if (result) {
- dev_err(&urb->dev->dev,
- "%s - Error %d submitting interrupt urb\n",
- __func__, result);
- }
-}
-
-static int mos7840_port_paranoia_check(struct usb_serial_port *port,
- const char *function)
-{
- if (!port) {
- pr_debug("%s - port == NULL\n", function);
- return -1;
- }
- if (!port->serial) {
- pr_debug("%s - port->serial == NULL\n", function);
- return -1;
- }
-
- return 0;
-}
-
-/* Inline functions to check the sanity of a pointer that is passed to us */
-static int mos7840_serial_paranoia_check(struct usb_serial *serial,
- const char *function)
-{
- if (!serial) {
- pr_debug("%s - serial == NULL\n", function);
- return -1;
- }
- if (!serial->type) {
- pr_debug("%s - serial->type == NULL!\n", function);
- return -1;
- }
-
- return 0;
-}
-
-static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port,
- const char *function)
-{
- /* if no port was specified, or it fails a paranoia check */
- if (!port ||
- mos7840_port_paranoia_check(port, function) ||
- mos7840_serial_paranoia_check(port->serial, function)) {
- /* then say that we don't have a valid usb_serial thing,
- * which will end up genrating -ENODEV return values */
- return NULL;
- }
-
- return port->serial;
-}
-
-/*****************************************************************************
* mos7840_bulk_in_callback
* this is the callback function for when we have received data on the
* bulk in endpoint.
@@ -732,35 +431,18 @@
static void mos7840_bulk_in_callback(struct urb *urb)
{
+ struct moschip_port *mos7840_port = urb->context;
+ struct usb_serial_port *port = mos7840_port->port;
int retval;
unsigned char *data;
- struct usb_serial *serial;
- struct usb_serial_port *port;
- struct moschip_port *mos7840_port;
int status = urb->status;
- mos7840_port = urb->context;
- if (!mos7840_port)
- return;
-
if (status) {
dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
mos7840_port->read_urb_busy = false;
return;
}
- port = mos7840_port->port;
- if (mos7840_port_paranoia_check(port, __func__)) {
- mos7840_port->read_urb_busy = false;
- return;
- }
-
- serial = mos7840_get_usb_serial(port, __func__);
- if (!serial) {
- mos7840_port->read_urb_busy = false;
- return;
- }
-
data = urb->transfer_buffer;
usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
@@ -772,12 +454,6 @@
dev_dbg(&port->dev, "icount.rx is %d:\n", port->icount.rx);
}
- if (!mos7840_port->read_urb) {
- dev_dbg(&port->dev, "%s", "URB KILLED !!!\n");
- mos7840_port->read_urb_busy = false;
- return;
- }
-
if (mos7840_port->has_led)
mos7840_led_activity(port);
@@ -798,14 +474,12 @@
static void mos7840_bulk_out_data_callback(struct urb *urb)
{
- struct moschip_port *mos7840_port;
- struct usb_serial_port *port;
+ struct moschip_port *mos7840_port = urb->context;
+ struct usb_serial_port *port = mos7840_port->port;
int status = urb->status;
unsigned long flags;
int i;
- mos7840_port = urb->context;
- port = mos7840_port->port;
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; i++) {
if (urb == mos7840_port->write_urb_pool[i]) {
@@ -820,11 +494,7 @@
return;
}
- if (mos7840_port_paranoia_check(port, __func__))
- return;
-
- if (mos7840_port->open)
- tty_port_tty_wakeup(&port->port);
+ tty_port_tty_wakeup(&port->port);
}
@@ -841,32 +511,16 @@
static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
{
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
int response;
int j;
- struct usb_serial *serial;
struct urb *urb;
__u16 Data;
int status;
- struct moschip_port *mos7840_port;
- struct moschip_port *port0;
-
- if (mos7840_port_paranoia_check(port, __func__))
- return -ENODEV;
-
- serial = port->serial;
-
- if (mos7840_serial_paranoia_check(serial, __func__))
- return -ENODEV;
-
- mos7840_port = mos7840_get_port_private(port);
- port0 = mos7840_get_port_private(serial->port[0]);
-
- if (mos7840_port == NULL || port0 == NULL)
- return -ENODEV;
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
- port0->open_ports++;
/* Initialising the write urb pool */
for (j = 0; j < NUM_URBS; ++j) {
@@ -1017,41 +671,6 @@
status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset,
Data);
- /* Check to see if we've set up our endpoint info yet *
- * (can't set it up in mos7840_startup as the structures *
- * were not set up at that time.) */
- if (port0->open_ports == 1) {
- /* FIXME: Buffer never NULL, so URB is not submitted. */
- if (serial->port[0]->interrupt_in_buffer == NULL) {
- /* set up interrupt urb */
- usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
- serial->dev,
- usb_rcvintpipe(serial->dev,
- serial->port[0]->interrupt_in_endpointAddress),
- serial->port[0]->interrupt_in_buffer,
- serial->port[0]->interrupt_in_urb->
- transfer_buffer_length,
- mos7840_interrupt_callback,
- serial,
- serial->port[0]->interrupt_in_urb->interval);
-
- /* start interrupt read for mos7840 */
- response =
- usb_submit_urb(serial->port[0]->interrupt_in_urb,
- GFP_KERNEL);
- if (response) {
- dev_err(&port->dev, "%s - Error %d submitting "
- "interrupt urb\n", __func__, response);
- }
-
- }
-
- }
-
- /* see if we've set up our endpoint info yet *
- * (can't set it up in mos7840_startup as the *
- * structures were not set up at that time.) */
-
dev_dbg(&port->dev, "port number is %d\n", port->port_number);
dev_dbg(&port->dev, "minor number is %d\n", port->minor);
dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
@@ -1091,9 +710,6 @@
/* initialize our port settings */
/* Must set to enable ints! */
mos7840_port->shadowMCR = MCR_MASTER_IE;
- /* send a open port command */
- mos7840_port->open = 1;
- /* mos7840_change_port_settings(mos7840_port,old_termios); */
return 0;
err:
@@ -1120,17 +736,10 @@
static int mos7840_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
int i;
int chars = 0;
unsigned long flags;
- struct moschip_port *mos7840_port;
-
- if (mos7840_port_paranoia_check(port, __func__))
- return 0;
-
- mos7840_port = mos7840_get_port_private(port);
- if (mos7840_port == NULL)
- return 0;
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
@@ -1152,25 +761,10 @@
static void mos7840_close(struct usb_serial_port *port)
{
- struct usb_serial *serial;
- struct moschip_port *mos7840_port;
- struct moschip_port *port0;
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
int j;
__u16 Data;
- if (mos7840_port_paranoia_check(port, __func__))
- return;
-
- serial = mos7840_get_usb_serial(port, __func__);
- if (!serial)
- return;
-
- mos7840_port = mos7840_get_port_private(port);
- port0 = mos7840_get_port_private(serial->port[0]);
-
- if (mos7840_port == NULL || port0 == NULL)
- return;
-
for (j = 0; j < NUM_URBS; ++j)
usb_kill_urb(mos7840_port->write_urb_pool[j]);
@@ -1185,22 +779,11 @@
usb_kill_urb(mos7840_port->read_urb);
mos7840_port->read_urb_busy = false;
- port0->open_ports--;
- dev_dbg(&port->dev, "%s in close%d\n", __func__, port0->open_ports);
- if (port0->open_ports == 0) {
- if (serial->port[0]->interrupt_in_urb) {
- dev_dbg(&port->dev, "Shutdown interrupt_in_urb\n");
- usb_kill_urb(serial->port[0]->interrupt_in_urb);
- }
- }
-
Data = 0x0;
mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
Data = 0x00;
mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
-
- mos7840_port->open = 0;
}
/*****************************************************************************
@@ -1210,21 +793,8 @@
static void mos7840_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
unsigned char data;
- struct usb_serial *serial;
- struct moschip_port *mos7840_port;
-
- if (mos7840_port_paranoia_check(port, __func__))
- return;
-
- serial = mos7840_get_usb_serial(port, __func__);
- if (!serial)
- return;
-
- mos7840_port = mos7840_get_port_private(port);
-
- if (mos7840_port == NULL)
- return;
if (break_state == -1)
data = mos7840_port->shadowLCR | LCR_SET_BREAK;
@@ -1249,17 +819,10 @@
static int mos7840_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
int i;
int room = 0;
unsigned long flags;
- struct moschip_port *mos7840_port;
-
- if (mos7840_port_paranoia_check(port, __func__))
- return -1;
-
- mos7840_port = mos7840_get_port_private(port);
- if (mos7840_port == NULL)
- return -1;
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
@@ -1285,29 +848,17 @@
static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *data, int count)
{
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
int status;
int i;
int bytes_sent = 0;
int transfer_size;
unsigned long flags;
-
- struct moschip_port *mos7840_port;
- struct usb_serial *serial;
struct urb *urb;
/* __u16 Data; */
const unsigned char *current_position = data;
- if (mos7840_port_paranoia_check(port, __func__))
- return -1;
-
- serial = port->serial;
- if (mos7840_serial_paranoia_check(serial, __func__))
- return -1;
-
- mos7840_port = mos7840_get_port_private(port);
- if (mos7840_port == NULL)
- return -1;
-
/* try to find a free urb in the list */
urb = NULL;
@@ -1390,22 +941,9 @@
static void mos7840_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- struct moschip_port *mos7840_port;
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
int status;
- if (mos7840_port_paranoia_check(port, __func__))
- return;
-
- mos7840_port = mos7840_get_port_private(port);
-
- if (mos7840_port == NULL)
- return;
-
- if (!mos7840_port->open) {
- dev_dbg(&port->dev, "%s", "port not opened\n");
- return;
- }
-
/* if we are implementing XON/XOFF, send the stop character */
if (I_IXOFF(tty)) {
unsigned char stop_char = STOP_CHAR(tty);
@@ -1432,19 +970,8 @@
static void mos7840_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
int status;
- struct moschip_port *mos7840_port = mos7840_get_port_private(port);
-
- if (mos7840_port_paranoia_check(port, __func__))
- return;
-
- if (mos7840_port == NULL)
- return;
-
- if (!mos7840_port->open) {
- dev_dbg(&port->dev, "%s - port not opened\n", __func__);
- return;
- }
/* if we are implementing XON/XOFF, send the start character */
if (I_IXOFF(tty)) {
@@ -1467,15 +994,10 @@
static int mos7840_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- struct moschip_port *mos7840_port;
unsigned int result;
__u16 msr;
__u16 mcr;
int status;
- mos7840_port = mos7840_get_port_private(port);
-
- if (mos7840_port == NULL)
- return -ENODEV;
status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
if (status < 0)
@@ -1500,15 +1022,10 @@
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
- struct moschip_port *mos7840_port;
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
unsigned int mcr;
int status;
- mos7840_port = mos7840_get_port_private(port);
-
- if (mos7840_port == NULL)
- return -ENODEV;
-
/* FIXME: What locks the port registers ? */
mcr = mos7840_port->shadowMCR;
if (clear & TIOCM_RTS)
@@ -1585,21 +1102,11 @@
static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
int baudRate)
{
+ struct usb_serial_port *port = mos7840_port->port;
int divisor = 0;
int status;
__u16 Data;
__u16 clk_sel_val;
- struct usb_serial_port *port;
-
- if (mos7840_port == NULL)
- return -1;
-
- port = mos7840_port->port;
- if (mos7840_port_paranoia_check(port, __func__))
- return -1;
-
- if (mos7840_serial_paranoia_check(port->serial, __func__))
- return -1;
dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudRate);
/* reset clk_uart_sel in spregOffset */
@@ -1688,6 +1195,7 @@
static void mos7840_change_port_settings(struct tty_struct *tty,
struct moschip_port *mos7840_port, struct ktermios *old_termios)
{
+ struct usb_serial_port *port = mos7840_port->port;
int baud;
unsigned cflag;
__u8 lData;
@@ -1695,23 +1203,6 @@
__u8 lStop;
int status;
__u16 Data;
- struct usb_serial_port *port;
-
- if (mos7840_port == NULL)
- return;
-
- port = mos7840_port->port;
-
- if (mos7840_port_paranoia_check(port, __func__))
- return;
-
- if (mos7840_serial_paranoia_check(port->serial, __func__))
- return;
-
- if (!mos7840_port->open) {
- dev_dbg(&port->dev, "%s - port not opened\n", __func__);
- return;
- }
lData = LCR_BITS_8;
lStop = LCR_STOP_1;
@@ -1846,37 +1337,13 @@
struct usb_serial_port *port,
struct ktermios *old_termios)
{
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
int status;
- struct usb_serial *serial;
- struct moschip_port *mos7840_port;
-
- if (mos7840_port_paranoia_check(port, __func__))
- return;
-
- serial = port->serial;
-
- if (mos7840_serial_paranoia_check(serial, __func__))
- return;
-
- mos7840_port = mos7840_get_port_private(port);
-
- if (mos7840_port == NULL)
- return;
-
- if (!mos7840_port->open) {
- dev_dbg(&port->dev, "%s - port not opened\n", __func__);
- return;
- }
/* change the port settings to the new ones specified */
mos7840_change_port_settings(tty, mos7840_port, old_termios);
- if (!mos7840_port->read_urb) {
- dev_dbg(&port->dev, "%s", "URB KILLED !!!!!\n");
- return;
- }
-
if (!mos7840_port->read_urb_busy) {
mos7840_port->read_urb_busy = true;
status = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
@@ -1923,7 +1390,7 @@
struct serial_struct *ss)
{
struct usb_serial_port *port = tty->driver_data;
- struct moschip_port *mos7840_port = mos7840_get_port_private(port);
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
ss->type = PORT_16550A;
ss->line = mos7840_port->port->minor;
@@ -1946,15 +1413,6 @@
{
struct usb_serial_port *port = tty->driver_data;
void __user *argp = (void __user *)arg;
- struct moschip_port *mos7840_port;
-
- if (mos7840_port_paranoia_check(port, __func__))
- return -1;
-
- mos7840_port = mos7840_get_port_private(port);
-
- if (mos7840_port == NULL)
- return -1;
switch (cmd) {
/* return number of bytes available */
@@ -1969,6 +1427,13 @@
return -ENOIOCTLCMD;
}
+/*
+ * Check if GPO (pin 42) is connected to GPI (pin 33) as recommended by ASIX
+ * for MCS7810 by bit-banging a 16-bit word.
+ *
+ * Note that GPO is really RTS of the third port so this will toggle RTS of
+ * port two or three on two- and four-port devices.
+ */
static int mos7810_check(struct usb_serial *serial)
{
int i, pass_count = 0;
@@ -2026,22 +1491,12 @@
static int mos7840_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
- u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
- u16 vid = le16_to_cpu(serial->dev->descriptor.idVendor);
+ unsigned long device_flags = id->driver_info;
u8 *buf;
- int device_type;
- if (product == MOSCHIP_DEVICE_ID_7810 ||
- product == MOSCHIP_DEVICE_ID_7820 ||
- product == MOSCHIP_DEVICE_ID_7843) {
- device_type = product;
+ /* Skip device-type detection if we already have device flags. */
+ if (device_flags)
goto out;
- }
-
- if (vid == USB_VENDOR_ID_MOXA && product == MOXA_DEVICE_ID_2210) {
- device_type = MOSCHIP_DEVICE_ID_7820;
- goto out;
- }
buf = kzalloc(VENDOR_READ_LENGTH, GFP_KERNEL);
if (!buf)
@@ -2053,15 +1508,15 @@
/* For a MCS7840 device GPIO0 must be set to 1 */
if (buf[0] & 0x01)
- device_type = MOSCHIP_DEVICE_ID_7840;
+ device_flags = MCS_PORTS(4);
else if (mos7810_check(serial))
- device_type = MOSCHIP_DEVICE_ID_7810;
+ device_flags = MCS_PORTS(1) | MCS_LED;
else
- device_type = MOSCHIP_DEVICE_ID_7820;
+ device_flags = MCS_PORTS(2);
kfree(buf);
out:
- usb_set_serial_data(serial, (void *)(unsigned long)device_type);
+ usb_set_serial_data(serial, (void *)device_flags);
return 0;
}
@@ -2069,19 +1524,10 @@
static int mos7840_calc_num_ports(struct usb_serial *serial,
struct usb_serial_endpoints *epds)
{
- int device_type = (unsigned long)usb_get_serial_data(serial);
- int num_ports;
+ unsigned long device_flags = (unsigned long)usb_get_serial_data(serial);
+ int num_ports = MCS_PORTS(device_flags);
- if (device_type == MOSCHIP_DEVICE_ID_7843)
- num_ports = 3;
- else
- num_ports = (device_type >> 4) & 0x000F;
-
- /*
- * num_ports is currently never zero as device_type is one of
- * MOSCHIP_DEVICE_ID_78{1,2,4}0.
- */
- if (num_ports == 0)
+ if (num_ports == 0 || num_ports > 4)
return -ENODEV;
if (epds->num_bulk_in < num_ports || epds->num_bulk_out < num_ports) {
@@ -2092,10 +1538,27 @@
return num_ports;
}
+static int mos7840_attach(struct usb_serial *serial)
+{
+ struct device *dev = &serial->interface->dev;
+ int status;
+ u16 val;
+
+ /* Zero Length flag enable */
+ val = 0x0f;
+ status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, val);
+ if (status < 0)
+ dev_dbg(dev, "Writing ZLP_REG5 failed status-0x%x\n", status);
+ else
+ dev_dbg(dev, "ZLP_REG5 Writing success status%d\n", status);
+
+ return status;
+}
+
static int mos7840_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
- int device_type = (unsigned long)usb_get_serial_data(serial);
+ unsigned long device_flags = (unsigned long)usb_get_serial_data(serial);
struct moschip_port *mos7840_port;
int status;
int pnum;
@@ -2116,7 +1579,6 @@
* common to all port */
mos7840_port->port = port;
- mos7840_set_port_private(port, mos7840_port);
spin_lock_init(&mos7840_port->pool_lock);
/* minor is not initialised until later by
@@ -2142,14 +1604,14 @@
mos7840_port->DcrRegOffset = 0x16 + 3 * (phy_num - 2);
}
mos7840_dump_serial_port(port, mos7840_port);
- mos7840_set_port_private(port, mos7840_port);
+ usb_set_serial_port_data(port, mos7840_port);
/* enable rx_disable bit in control register */
status = mos7840_get_reg_sync(port,
mos7840_port->ControlRegOffset, &Data);
if (status < 0) {
dev_dbg(&port->dev, "Reading ControlReg failed status-0x%x\n", status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "ControlReg Reading success val is %x, status%d\n", Data, status);
Data |= 0x08; /* setting driver done bit */
@@ -2161,7 +1623,7 @@
mos7840_port->ControlRegOffset, Data);
if (status < 0) {
dev_dbg(&port->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "ControlReg Writing success(rx_disable) status%d\n", status);
@@ -2172,7 +1634,7 @@
(__u16) (mos7840_port->DcrRegOffset + 0), Data);
if (status < 0) {
dev_dbg(&port->dev, "Writing DCR0 failed status-0x%x\n", status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "DCR0 Writing success status%d\n", status);
@@ -2181,7 +1643,7 @@
(__u16) (mos7840_port->DcrRegOffset + 1), Data);
if (status < 0) {
dev_dbg(&port->dev, "Writing DCR1 failed status-0x%x\n", status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "DCR1 Writing success status%d\n", status);
@@ -2190,7 +1652,7 @@
(__u16) (mos7840_port->DcrRegOffset + 2), Data);
if (status < 0) {
dev_dbg(&port->dev, "Writing DCR2 failed status-0x%x\n", status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "DCR2 Writing success status%d\n", status);
@@ -2199,7 +1661,7 @@
status = mos7840_set_reg_sync(port, CLK_START_VALUE_REGISTER, Data);
if (status < 0) {
dev_dbg(&port->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status);
@@ -2216,7 +1678,7 @@
status = mos7840_set_uart_reg(port, SCRATCH_PAD_REGISTER, Data);
if (status < 0) {
dev_dbg(&port->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status);
@@ -2230,7 +1692,7 @@
(__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num)));
if (status < 0) {
dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 2, status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 2, status);
} else {
@@ -2242,27 +1704,16 @@
(__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1));
if (status < 0) {
dev_dbg(&port->dev, "Writing ZLP_REG%d failed status-0x%x\n", pnum + 1, status);
- goto out;
+ goto error;
} else
dev_dbg(&port->dev, "ZLP_REG%d Writing success status%d\n", pnum + 1, status);
}
- mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL);
- mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
- mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest),
- GFP_KERNEL);
- if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf ||
- !mos7840_port->dr) {
- status = -ENOMEM;
- goto error;
- }
- mos7840_port->has_led = false;
+ mos7840_port->has_led = device_flags & MCS_LED;
/* Initialize LED timers */
- if (device_type == MOSCHIP_DEVICE_ID_7810) {
- mos7840_port->has_led = true;
-
+ if (mos7840_port->has_led) {
mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL);
mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr),
GFP_KERNEL);
@@ -2282,24 +1733,11 @@
/* Turn off LED */
mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300);
}
-out:
- if (pnum == serial->num_ports - 1) {
- /* Zero Length flag enable */
- Data = 0x0f;
- status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
- if (status < 0) {
- dev_dbg(&port->dev, "Writing ZLP_REG5 failed status-0x%x\n", status);
- goto error;
- } else
- dev_dbg(&port->dev, "ZLP_REG5 Writing success status%d\n", status);
- }
+
return 0;
error:
kfree(mos7840_port->led_dr);
usb_free_urb(mos7840_port->led_urb);
- kfree(mos7840_port->dr);
- kfree(mos7840_port->ctrl_buf);
- usb_free_urb(mos7840_port->control_urb);
kfree(mos7840_port);
return status;
@@ -2307,9 +1745,7 @@
static int mos7840_port_remove(struct usb_serial_port *port)
{
- struct moschip_port *mos7840_port;
-
- mos7840_port = mos7840_get_port_private(port);
+ struct moschip_port *mos7840_port = usb_get_serial_port_data(port);
if (mos7840_port->has_led) {
/* Turn off LED */
@@ -2322,10 +1758,7 @@
usb_free_urb(mos7840_port->led_urb);
kfree(mos7840_port->led_dr);
}
- usb_kill_urb(mos7840_port->control_urb);
- usb_free_urb(mos7840_port->control_urb);
- kfree(mos7840_port->ctrl_buf);
- kfree(mos7840_port->dr);
+
kfree(mos7840_port);
return 0;
@@ -2348,18 +1781,17 @@
.unthrottle = mos7840_unthrottle,
.calc_num_ports = mos7840_calc_num_ports,
.probe = mos7840_probe,
+ .attach = mos7840_attach,
.ioctl = mos7840_ioctl,
.get_serial = mos7840_get_serial_info,
.set_termios = mos7840_set_termios,
.break_ctl = mos7840_break,
.tiocmget = mos7840_tiocmget,
.tiocmset = mos7840_tiocmset,
- .tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.port_probe = mos7840_port_probe,
.port_remove = mos7840_port_remove,
.read_bulk_callback = mos7840_bulk_in_callback,
- .read_int_callback = mos7840_interrupt_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index 2513ee9..5d38c2a 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -327,14 +327,14 @@
{
int i;
- if (!port->port.console || !port->sysrq) {
- tty_insert_flip_string(&port->port, data, size);
- } else {
+ if (port->sysrq) {
for (i = 0; i < size; i++, data++) {
if (!usb_serial_handle_sysrq_char(port, *data))
tty_insert_flip_char(&port->port, *data,
TTY_NORMAL);
}
+ } else {
+ tty_insert_flip_string(&port->port, data, size);
}
tty_flip_buffer_push(&port->port);
}
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index ed2b4e6..0af7680 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -41,6 +41,9 @@
bool rts;
bool cts;
int outstanding_urbs;
+ int outstanding_bytes;
+
+ struct usb_anchor anchor;
};
@@ -149,6 +152,15 @@
return res;
}
+static void opticon_close(struct usb_serial_port *port)
+{
+ struct opticon_private *priv = usb_get_serial_port_data(port);
+
+ usb_kill_anchored_urbs(&priv->anchor);
+
+ usb_serial_generic_close(port);
+}
+
static void opticon_write_control_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
@@ -169,6 +181,7 @@
spin_lock_irqsave(&priv->lock, flags);
--priv->outstanding_urbs;
+ priv->outstanding_bytes -= urb->transfer_buffer_length;
spin_unlock_irqrestore(&priv->lock, flags);
usb_serial_port_softint(port);
@@ -182,8 +195,8 @@
struct urb *urb;
unsigned char *buffer;
unsigned long flags;
- int status;
struct usb_ctrlrequest *dr;
+ int ret = -ENOMEM;
spin_lock_irqsave(&priv->lock, flags);
if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
@@ -192,19 +205,16 @@
return 0;
}
priv->outstanding_urbs++;
+ priv->outstanding_bytes += count;
spin_unlock_irqrestore(&priv->lock, flags);
buffer = kmalloc(count, GFP_ATOMIC);
- if (!buffer) {
- count = -ENOMEM;
+ if (!buffer)
goto error_no_buffer;
- }
urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!urb) {
- count = -ENOMEM;
+ if (!urb)
goto error_no_urb;
- }
memcpy(buffer, buf, count);
@@ -213,10 +223,8 @@
/* The connected devices do not have a bulk write endpoint,
* to transmit data to de barcode device the control endpoint is used */
dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
- if (!dr) {
- count = -ENOMEM;
+ if (!dr)
goto error_no_dr;
- }
dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT;
dr->bRequest = 0x01;
@@ -229,13 +237,13 @@
(unsigned char *)dr, buffer, count,
opticon_write_control_callback, port);
+ usb_anchor_urb(urb, &priv->anchor);
+
/* send it down the pipe */
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status) {
- dev_err(&port->dev,
- "%s - usb_submit_urb(write endpoint) failed status = %d\n",
- __func__, status);
- count = status;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret) {
+ dev_err(&port->dev, "failed to submit write urb: %d\n", ret);
+ usb_unanchor_urb(urb);
goto error;
}
@@ -253,8 +261,10 @@
error_no_buffer:
spin_lock_irqsave(&priv->lock, flags);
--priv->outstanding_urbs;
+ priv->outstanding_bytes -= count;
spin_unlock_irqrestore(&priv->lock, flags);
- return count;
+
+ return ret;
}
static int opticon_write_room(struct tty_struct *tty)
@@ -279,6 +289,20 @@
return 2048;
}
+static int opticon_chars_in_buffer(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct opticon_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ int count;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ count = priv->outstanding_bytes;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return count;
+}
+
static int opticon_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
@@ -354,6 +378,7 @@
return -ENOMEM;
spin_lock_init(&priv->lock);
+ init_usb_anchor(&priv->anchor);
usb_set_serial_port_data(port, priv);
@@ -381,8 +406,10 @@
.port_probe = opticon_port_probe,
.port_remove = opticon_port_remove,
.open = opticon_open,
+ .close = opticon_close,
.write = opticon_write,
.write_room = opticon_write_room,
+ .chars_in_buffer = opticon_chars_in_buffer,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.get_serial = get_serial_info,
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index d42ca13..b878f4c 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -198,6 +198,8 @@
#define DELL_PRODUCT_5821E 0x81d7
#define DELL_PRODUCT_5821E_ESIM 0x81e0
+#define DELL_PRODUCT_5829E_ESIM 0x81e4
+#define DELL_PRODUCT_5829E 0x81e6
#define KYOCERA_VENDOR_ID 0x0c88
#define KYOCERA_PRODUCT_KPC650 0x17da
@@ -246,11 +248,13 @@
/* These Quectel products use Quectel's vendor ID */
#define QUECTEL_PRODUCT_EC21 0x0121
#define QUECTEL_PRODUCT_EC25 0x0125
+#define QUECTEL_PRODUCT_EG91 0x0191
#define QUECTEL_PRODUCT_EG95 0x0195
#define QUECTEL_PRODUCT_BG96 0x0296
#define QUECTEL_PRODUCT_EP06 0x0306
#define QUECTEL_PRODUCT_EM12 0x0512
#define QUECTEL_PRODUCT_RM500Q 0x0800
+#define QUECTEL_PRODUCT_EC200S_CN 0x6002
#define QUECTEL_PRODUCT_EC200T 0x6026
#define CMOTECH_VENDOR_ID 0x16d8
@@ -1061,6 +1065,10 @@
.driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
{ USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5821E_ESIM),
.driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
+ { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5829E),
+ .driver_info = RSVD(0) | RSVD(6) },
+ { USB_DEVICE(DELL_VENDOR_ID, DELL_PRODUCT_5829E_ESIM),
+ .driver_info = RSVD(0) | RSVD(6) },
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, /* ADU-E100, ADU-310 */
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) },
{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) },
@@ -1111,6 +1119,9 @@
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25, 0xff, 0xff, 0xff),
.driver_info = NUMEP2 },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG91, 0xff, 0xff, 0xff),
+ .driver_info = NUMEP2 },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG91, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95, 0xff, 0xff, 0xff),
.driver_info = NUMEP2 },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG95, 0xff, 0, 0) },
@@ -1128,6 +1139,7 @@
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x10),
.driver_info = ZLP },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) },
{ USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
@@ -1205,6 +1217,22 @@
.driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1056, 0xff), /* Telit FD980 */
.driver_info = NCTRL(2) | RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1060, 0xff), /* Telit LN920 (rmnet) */
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1061, 0xff), /* Telit LN920 (MBIM) */
+ .driver_info = NCTRL(0) | RSVD(1) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1062, 0xff), /* Telit LN920 (RNDIS) */
+ .driver_info = NCTRL(2) | RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1063, 0xff), /* Telit LN920 (ECM) */
+ .driver_info = NCTRL(0) | RSVD(1) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1070, 0xff), /* Telit FN990 (rmnet) */
+ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1071, 0xff), /* Telit FN990 (MBIM) */
+ .driver_info = NCTRL(0) | RSVD(1) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1072, 0xff), /* Telit FN990 (RNDIS) */
+ .driver_info = NCTRL(2) | RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff), /* Telit FN990 (ECM) */
+ .driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
.driver_info = NCTRL(0) | RSVD(1) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM),
@@ -1219,6 +1247,8 @@
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1203, 0xff), /* Telit LE910Cx (RNDIS) */
.driver_info = NCTRL(2) | RSVD(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1204, 0xff), /* Telit LE910Cx (MBIM) */
+ .driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920),
@@ -1249,8 +1279,16 @@
.driver_info = NCTRL(2) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x7011, 0xff), /* Telit LE910-S1 (ECM) */
.driver_info = NCTRL(2) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x701a, 0xff), /* Telit LE910R1 (RNDIS) */
+ .driver_info = NCTRL(2) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x701b, 0xff), /* Telit LE910R1 (ECM) */
+ .driver_info = NCTRL(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, 0x9010), /* Telit SBL FN980 flashing device */
.driver_info = NCTRL(0) | ZLP },
+ { USB_DEVICE(TELIT_VENDOR_ID, 0x9200), /* Telit LE910S1 flashing device */
+ .driver_info = NCTRL(0) | ZLP },
+ { USB_DEVICE(TELIT_VENDOR_ID, 0x9201), /* Telit LE910R1 flashing device */
+ .driver_info = NCTRL(0) | ZLP },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
.driver_info = RSVD(1) },
@@ -1623,6 +1661,8 @@
.driver_info = RSVD(2) },
{ USB_DEVICE_INTERFACE_CLASS(ZTE_VENDOR_ID, 0x1476, 0xff) }, /* GosunCn ZTE WeLink ME3630 (ECM/NCM mode) */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1481, 0xff, 0x00, 0x00) }, /* ZTE MF871A */
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1485, 0xff, 0xff, 0xff), /* ZTE MF286D */
+ .driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) },
@@ -1650,7 +1690,6 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0060, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff),
.driver_info = RSVD(1) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff),
@@ -2068,6 +2107,8 @@
.driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
{ USB_DEVICE(0x0489, 0xe0b5), /* Foxconn T77W968 ESIM */
.driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
+ { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0db, 0xff), /* Foxconn T99W265 MBIM */
+ .driver_info = RSVD(3) },
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x2cb7, 0x0104), /* Fibocom NL678 series */
@@ -2077,6 +2118,9 @@
{ USB_DEVICE_AND_INTERFACE_INFO(0x2cb7, 0x010b, 0xff, 0xff, 0x30) }, /* Fibocom FG150 Diag */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2cb7, 0x010b, 0xff, 0, 0) }, /* Fibocom FG150 AT */
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */
+ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a2, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */
+ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a4, 0xff), /* Fibocom FM101-GL (laptop MBIM) */
+ .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */
@@ -2220,8 +2264,7 @@
dev_dbg(dev, "%s: urb %p port %p has data %p\n", __func__, urb, port, portdata);
if (status == 0) {
- struct usb_ctrlrequest *req_pkt =
- (struct usb_ctrlrequest *)urb->transfer_buffer;
+ struct usb_ctrlrequest *req_pkt = urb->transfer_buffer;
if (!req_pkt) {
dev_dbg(dev, "%s: NULL req_pkt\n", __func__);
diff --git a/drivers/usb/serial/oti6858.h b/drivers/usb/serial/oti6858.h
index 1226bf2..5c25836 100644
--- a/drivers/usb/serial/oti6858.h
+++ b/drivers/usb/serial/oti6858.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Ours Technology Inc. OTi-6858 USB to serial adapter driver.
*/
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 9600cee..1bbe18f 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -47,6 +47,12 @@
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_TB) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GC) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GB) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GT) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GL) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GE) },
+ { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GS) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID),
@@ -133,9 +139,11 @@
#define VENDOR_WRITE_REQUEST_TYPE 0x40
#define VENDOR_WRITE_REQUEST 0x01
+#define VENDOR_WRITE_NREQUEST 0x80
#define VENDOR_READ_REQUEST_TYPE 0xc0
#define VENDOR_READ_REQUEST 0x01
+#define VENDOR_READ_NREQUEST 0x81
#define UART_STATE_INDEX 8
#define UART_STATE_MSR_MASK 0x8b
@@ -151,11 +159,24 @@
#define PL2303_FLOWCTRL_MASK 0xf0
+#define PL2303_READ_TYPE_HX_STATUS 0x8080
+
+#define PL2303_HXN_RESET_REG 0x07
+#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02
+#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01
+
+#define PL2303_HXN_FLOWCTRL_REG 0x0a
+#define PL2303_HXN_FLOWCTRL_MASK 0x1c
+#define PL2303_HXN_FLOWCTRL_NONE 0x1c
+#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18
+#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c
+
static void pl2303_set_break(struct usb_serial_port *port, bool enable);
enum pl2303_type {
TYPE_01, /* Type 0 and 1 (difference unknown) */
TYPE_HX, /* HX version of the pl2303 chip */
+ TYPE_HXN, /* HXN version of the pl2303 chip */
TYPE_COUNT
};
@@ -163,6 +184,7 @@
speed_t max_baud_rate;
unsigned long quirks;
unsigned int no_autoxonxoff:1;
+ unsigned int no_divisors:1;
};
struct pl2303_serial_private {
@@ -187,16 +209,27 @@
[TYPE_HX] = {
.max_baud_rate = 12000000,
},
+ [TYPE_HXN] = {
+ .max_baud_rate = 12000000,
+ .no_divisors = true,
+ },
};
static int pl2303_vendor_read(struct usb_serial *serial, u16 value,
unsigned char buf[1])
{
+ struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
struct device *dev = &serial->interface->dev;
+ u8 request;
int res;
+ if (spriv->type == &pl2303_type_data[TYPE_HXN])
+ request = VENDOR_READ_NREQUEST;
+ else
+ request = VENDOR_READ_REQUEST;
+
res = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
- VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
+ request, VENDOR_READ_REQUEST_TYPE,
value, 0, buf, 1, 100);
if (res != 1) {
dev_err(dev, "%s - failed to read [%04x]: %d\n", __func__,
@@ -214,13 +247,20 @@
static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index)
{
+ struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
struct device *dev = &serial->interface->dev;
+ u8 request;
int res;
dev_dbg(dev, "%s - [%04x] = %02x\n", __func__, value, index);
+ if (spriv->type == &pl2303_type_data[TYPE_HXN])
+ request = VENDOR_WRITE_NREQUEST;
+ else
+ request = VENDOR_WRITE_REQUEST;
+
res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- VENDOR_WRITE_REQUEST, VENDOR_WRITE_REQUEST_TYPE,
+ request, VENDOR_WRITE_REQUEST_TYPE,
value, index, NULL, 0, 100);
if (res) {
dev_err(dev, "%s - failed to write [%04x]: %d\n", __func__,
@@ -233,6 +273,7 @@
static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val)
{
+ struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
int ret = 0;
u8 *buf;
@@ -240,7 +281,11 @@
if (!buf)
return -ENOMEM;
- ret = pl2303_vendor_read(serial, reg | 0x80, buf);
+ if (spriv->type == &pl2303_type_data[TYPE_HXN])
+ ret = pl2303_vendor_read(serial, reg, buf);
+ else
+ ret = pl2303_vendor_read(serial, reg | 0x80, buf);
+
if (ret)
goto out_free;
@@ -323,6 +368,7 @@
struct pl2303_serial_private *spriv;
enum pl2303_type type = TYPE_01;
unsigned char *buf;
+ int res;
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
if (!spriv)
@@ -344,26 +390,37 @@
type = TYPE_01; /* type 1 */
dev_dbg(&serial->interface->dev, "device type: %d\n", type);
+ if (type == TYPE_HX) {
+ res = usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
+ PL2303_READ_TYPE_HX_STATUS, 0, buf, 1, 100);
+ if (res != 1)
+ type = TYPE_HXN;
+ }
+
spriv->type = &pl2303_type_data[type];
spriv->quirks = (unsigned long)usb_get_serial_data(serial);
spriv->quirks |= spriv->type->quirks;
usb_set_serial_data(serial, spriv);
- pl2303_vendor_read(serial, 0x8484, buf);
- pl2303_vendor_write(serial, 0x0404, 0);
- pl2303_vendor_read(serial, 0x8484, buf);
- pl2303_vendor_read(serial, 0x8383, buf);
- pl2303_vendor_read(serial, 0x8484, buf);
- pl2303_vendor_write(serial, 0x0404, 1);
- pl2303_vendor_read(serial, 0x8484, buf);
- pl2303_vendor_read(serial, 0x8383, buf);
- pl2303_vendor_write(serial, 0, 1);
- pl2303_vendor_write(serial, 1, 0);
- if (spriv->quirks & PL2303_QUIRK_LEGACY)
- pl2303_vendor_write(serial, 2, 0x24);
- else
- pl2303_vendor_write(serial, 2, 0x44);
+ if (type != TYPE_HXN) {
+ pl2303_vendor_read(serial, 0x8484, buf);
+ pl2303_vendor_write(serial, 0x0404, 0);
+ pl2303_vendor_read(serial, 0x8484, buf);
+ pl2303_vendor_read(serial, 0x8383, buf);
+ pl2303_vendor_read(serial, 0x8484, buf);
+ pl2303_vendor_write(serial, 0x0404, 1);
+ pl2303_vendor_read(serial, 0x8484, buf);
+ pl2303_vendor_read(serial, 0x8383, buf);
+ pl2303_vendor_write(serial, 0, 1);
+ pl2303_vendor_write(serial, 1, 0);
+ if (spriv->quirks & PL2303_QUIRK_LEGACY)
+ pl2303_vendor_write(serial, 2, 0x24);
+ else
+ pl2303_vendor_write(serial, 2, 0x44);
+ }
kfree(buf);
@@ -517,8 +574,12 @@
baud = min_t(speed_t, baud, spriv->type->max_baud_rate);
/*
* Use direct method for supported baud rates, otherwise use divisors.
+ * Newer chip types do not support divisor encoding.
*/
- baud_sup = pl2303_get_supported_baud_rate(baud);
+ if (spriv->type->no_divisors)
+ baud_sup = baud;
+ else
+ baud_sup = pl2303_get_supported_baud_rate(baud);
if (baud == baud_sup)
baud = pl2303_encode_baud_rate_direct(buf, baud);
@@ -722,14 +783,31 @@
}
if (C_CRTSCTS(tty)) {
- if (spriv->quirks & PL2303_QUIRK_LEGACY)
+ if (spriv->quirks & PL2303_QUIRK_LEGACY) {
pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x40);
- else
+ } else if (spriv->type == &pl2303_type_data[TYPE_HXN]) {
+ pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
+ PL2303_HXN_FLOWCTRL_MASK,
+ PL2303_HXN_FLOWCTRL_RTS_CTS);
+ } else {
pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60);
+ }
} else if (pl2303_enable_xonxoff(tty, spriv->type)) {
- pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0);
+ if (spriv->type == &pl2303_type_data[TYPE_HXN]) {
+ pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
+ PL2303_HXN_FLOWCTRL_MASK,
+ PL2303_HXN_FLOWCTRL_XON_XOFF);
+ } else {
+ pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0);
+ }
} else {
- pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0);
+ if (spriv->type == &pl2303_type_data[TYPE_HXN]) {
+ pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG,
+ PL2303_HXN_FLOWCTRL_MASK,
+ PL2303_HXN_FLOWCTRL_NONE);
+ } else {
+ pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0);
+ }
}
kfree(buf);
@@ -770,8 +848,14 @@
usb_clear_halt(serial->dev, port->read_urb->pipe);
} else {
/* reset upstream data pipes */
- pl2303_vendor_write(serial, 8, 0);
- pl2303_vendor_write(serial, 9, 0);
+ if (spriv->type == &pl2303_type_data[TYPE_HXN]) {
+ pl2303_vendor_write(serial, PL2303_HXN_RESET_REG,
+ PL2303_HXN_RESET_UPSTREAM_PIPE |
+ PL2303_HXN_RESET_DOWNSTREAM_PIPE);
+ } else {
+ pl2303_vendor_write(serial, 8, 0);
+ pl2303_vendor_write(serial, 9, 0);
+ }
}
/* Setup termios */
@@ -1025,7 +1109,7 @@
if (line_status & UART_OVERRUN_ERROR)
tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
- if (port->port.console && port->sysrq) {
+ if (port->sysrq) {
for (i = 0; i < urb->actual_length; ++i)
if (!usb_serial_handle_sysrq_char(port, data[i]))
tty_insert_flip_char(&port->port, data[i],
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 3e54425..6097ee8 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Prolific PL2303 USB to serial adaptor driver header file
*/
@@ -9,6 +9,12 @@
#define PL2303_VENDOR_ID 0x067b
#define PL2303_PRODUCT_ID 0x2303
#define PL2303_PRODUCT_ID_TB 0x2304
+#define PL2303_PRODUCT_ID_GC 0x23a3
+#define PL2303_PRODUCT_ID_GB 0x23b3
+#define PL2303_PRODUCT_ID_GT 0x23c3
+#define PL2303_PRODUCT_ID_GL 0x23d3
+#define PL2303_PRODUCT_ID_GE 0x23e3
+#define PL2303_PRODUCT_ID_GS 0x23f3
#define PL2303_PRODUCT_ID_RSAQ2 0x04bb
#define PL2303_PRODUCT_ID_DCU11 0x1234
#define PL2303_PRODUCT_ID_PHAROS 0xaaa0
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 0f60363..c18bf81 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -165,6 +165,7 @@
{DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */
{DEVICE_SWI(0x1199, 0x9090)}, /* Sierra Wireless EM7565 QDL */
{DEVICE_SWI(0x1199, 0x9091)}, /* Sierra Wireless EM7565 */
+ {DEVICE_SWI(0x1199, 0x90d2)}, /* Sierra Wireless EM9191 QDL */
{DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
@@ -243,11 +244,11 @@
/* QDL mode */
/* Gobi 2000 has a single altsetting, older ones have two */
if (serial->interface->num_altsetting == 2)
- intf = &serial->interface->altsetting[1];
+ intf = usb_altnum_to_altsetting(serial->interface, 1);
else if (serial->interface->num_altsetting > 2)
goto done;
- if (intf->desc.bNumEndpoints == 2 &&
+ if (intf && intf->desc.bNumEndpoints == 2 &&
usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
dev_dbg(dev, "QDL port found\n");
@@ -366,9 +367,8 @@
* a specific function, while the subclass indicate a
* specific firmware source
*
- * This is a blacklist of functions known to be
- * non-serial. The rest are assumed to be serial and
- * will be handled by this driver
+ * This is a list of functions known to be non-serial. The rest
+ * are assumed to be serial and will be handled by this driver
*/
switch (intf->desc.bInterfaceProtocol) {
/* QMI combined (qmi_wwan) */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index d172e86..a2c3c09 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -480,21 +480,6 @@
}
}
-/* not needed, kept to document functionality */
-static void qt2_process_xmit_empty(struct usb_serial_port *port,
- unsigned char *ch)
-{
- int bytes_written;
-
- bytes_written = (int)(*ch) + (int)(*(ch + 1) << 4);
-}
-
-/* not needed, kept to document functionality */
-static void qt2_process_flush(struct usb_serial_port *port, unsigned char *ch)
-{
- return;
-}
-
static void qt2_process_read_urb(struct urb *urb)
{
struct usb_serial *serial;
@@ -540,7 +525,7 @@
__func__);
break;
}
- qt2_process_xmit_empty(port, ch + 3);
+ /* bytes_written = (ch[1] << 4) + ch[0]; */
i += 4;
escapeflag = true;
break;
@@ -569,7 +554,6 @@
break;
case QT2_REC_FLUSH:
case QT2_XMIT_FLUSH:
- qt2_process_flush(port, ch + 2);
i += 2;
escapeflag = true;
break;
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index a43263a..57fc3c3 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -45,10 +45,9 @@
static bool nmea;
-/* Used in interface blacklisting */
-struct sierra_iface_info {
- const u32 infolen; /* number of interface numbers on blacklist */
- const u8 *ifaceinfo; /* pointer to the array holding the numbers */
+struct sierra_iface_list {
+ const u8 *nums; /* array of interface numbers */
+ size_t count; /* number of elements in array */
};
struct sierra_intf_private {
@@ -101,38 +100,19 @@
return num_ports;
}
-static int is_blacklisted(const u8 ifnum,
- const struct sierra_iface_info *blacklist)
+static bool is_listed(const u8 ifnum, const struct sierra_iface_list *list)
{
- const u8 *info;
int i;
- if (blacklist) {
- info = blacklist->ifaceinfo;
+ if (!list)
+ return false;
- for (i = 0; i < blacklist->infolen; i++) {
- if (info[i] == ifnum)
- return 1;
- }
+ for (i = 0; i < list->count; i++) {
+ if (list->nums[i] == ifnum)
+ return true;
}
- return 0;
-}
-static int is_himemory(const u8 ifnum,
- const struct sierra_iface_info *himemorylist)
-{
- const u8 *info;
- int i;
-
- if (himemorylist) {
- info = himemorylist->ifaceinfo;
-
- for (i=0; i < himemorylist->infolen; i++) {
- if (info[i] == ifnum)
- return 1;
- }
- }
- return 0;
+ return false;
}
static u8 sierra_interface_num(struct usb_serial *serial)
@@ -143,6 +123,7 @@
static int sierra_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
+ const struct sierra_iface_list *ignore_list;
int result = 0;
struct usb_device *udev;
u8 ifnum;
@@ -161,10 +142,10 @@
usb_set_interface(udev, ifnum, 1);
}
- if (is_blacklisted(ifnum,
- (struct sierra_iface_info *)id->driver_info)) {
- dev_dbg(&serial->dev->dev,
- "Ignoring blacklisted interface #%d\n", ifnum);
+ ignore_list = (const struct sierra_iface_list *)id->driver_info;
+
+ if (is_listed(ifnum, ignore_list)) {
+ dev_dbg(&serial->dev->dev, "Ignoring interface #%d\n", ifnum);
return -ENODEV;
}
@@ -173,22 +154,22 @@
/* interfaces with higher memory requirements */
static const u8 hi_memory_typeA_ifaces[] = { 0, 2 };
-static const struct sierra_iface_info typeA_interface_list = {
- .infolen = ARRAY_SIZE(hi_memory_typeA_ifaces),
- .ifaceinfo = hi_memory_typeA_ifaces,
+static const struct sierra_iface_list typeA_interface_list = {
+ .nums = hi_memory_typeA_ifaces,
+ .count = ARRAY_SIZE(hi_memory_typeA_ifaces),
};
static const u8 hi_memory_typeB_ifaces[] = { 3, 4, 5, 6 };
-static const struct sierra_iface_info typeB_interface_list = {
- .infolen = ARRAY_SIZE(hi_memory_typeB_ifaces),
- .ifaceinfo = hi_memory_typeB_ifaces,
+static const struct sierra_iface_list typeB_interface_list = {
+ .nums = hi_memory_typeB_ifaces,
+ .count = ARRAY_SIZE(hi_memory_typeB_ifaces),
};
-/* 'blacklist' of interfaces not served by this driver */
+/* 'ignorelist' of interfaces not served by this driver */
static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11, 19, 20 };
-static const struct sierra_iface_info direct_ip_interface_blacklist = {
- .infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces),
- .ifaceinfo = direct_ip_non_serial_ifaces,
+static const struct sierra_iface_list direct_ip_interface_ignore = {
+ .nums = direct_ip_non_serial_ifaces,
+ .count = ARRAY_SIZE(direct_ip_non_serial_ifaces),
};
static const struct usb_device_id id_table[] = {
@@ -264,19 +245,19 @@
{ USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */
/* Sierra Wireless Direct IP modems */
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68A3, 0xFF, 0xFF, 0xFF),
- .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+ .driver_info = (kernel_ulong_t)&direct_ip_interface_ignore
},
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68AA, 0xFF, 0xFF, 0xFF),
- .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+ .driver_info = (kernel_ulong_t)&direct_ip_interface_ignore
},
{ USB_DEVICE(0x1199, 0x68AB) }, /* Sierra Wireless AR8550 */
/* AT&T Direct IP LTE modems */
{ USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68AA, 0xFF, 0xFF, 0xFF),
- .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+ .driver_info = (kernel_ulong_t)&direct_ip_interface_ignore
},
/* Airprime/Sierra Wireless Direct IP modems */
{ USB_DEVICE_AND_INTERFACE_INFO(0x0F3D, 0x68A3, 0xFF, 0xFF, 0xFF),
- .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
+ .driver_info = (kernel_ulong_t)&direct_ip_interface_ignore
},
{ }
@@ -589,8 +570,7 @@
urb, port, portdata);
if (status == 0) {
- struct usb_ctrlrequest *req_pkt =
- (struct usb_ctrlrequest *)urb->transfer_buffer;
+ struct usb_ctrlrequest *req_pkt = urb->transfer_buffer;
if (!req_pkt) {
dev_dbg(&port->dev, "%s: NULL req_pkt\n",
@@ -879,7 +859,7 @@
{
struct usb_serial *serial = port->serial;
struct sierra_port_private *portdata;
- const struct sierra_iface_info *himemoryp;
+ const struct sierra_iface_list *himemory_list;
u8 ifnum;
portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
@@ -898,16 +878,16 @@
if (serial->num_ports == 1) {
/* Get interface number for composite device */
ifnum = sierra_interface_num(serial);
- himemoryp = &typeB_interface_list;
+ himemory_list = &typeB_interface_list;
} else {
/* This is really the usb-serial port number of the interface
* rather than the interface number.
*/
ifnum = port->port_number;
- himemoryp = &typeA_interface_list;
+ himemory_list = &typeA_interface_list;
}
- if (is_himemory(ifnum, himemoryp)) {
+ if (is_listed(ifnum, himemory_list)) {
portdata->num_out_urbs = N_OUT_URB_HM;
portdata->num_in_urbs = N_IN_URB_HM;
}
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index f6aea9f..7d39d35 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -495,7 +495,7 @@
static void ssu100_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- char *packet = (char *)urb->transfer_buffer;
+ char *packet = urb->transfer_buffer;
char flag = TTY_NORMAL;
u32 len = urb->actual_length;
int i;
@@ -517,13 +517,14 @@
if (!len)
return; /* status only */
- if (port->port.console && port->sysrq) {
+ if (port->sysrq) {
for (i = 0; i < len; i++, ch++) {
if (!usb_serial_handle_sysrq_char(port, *ch))
tty_insert_flip_char(&port->port, *ch, flag);
}
- } else
+ } else {
tty_insert_flip_string_fixed_flag(&port->port, ch, flag, len);
+ }
tty_flip_buffer_push(&port->port);
}
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index acc115d..afc4f96 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -220,7 +220,7 @@
u8 bDataCounter;
__be16 wBaseAddrHi;
__be16 wBaseAddrLo;
- u8 bData[0];
+ u8 bData[];
} __packed;
struct ti_read_data_request {
@@ -235,7 +235,7 @@
__u8 bCmdCode;
__u8 bModuleId;
__u8 bErrorCode;
- __u8 bData[0];
+ __u8 bData[];
} __packed;
/* Interrupt struct */
diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c
index 1ba1401..0a2268c 100644
--- a/drivers/usb/serial/upd78f0730.c
+++ b/drivers/usb/serial/upd78f0730.c
@@ -332,7 +332,7 @@
tty->termios.c_cflag &= ~CSIZE;
tty->termios.c_cflag |= CS8;
dev_warn(dev, "data size is not supported, using 8 bits\n");
- /* fall through */
+ fallthrough;
case CS8:
request.params |= UPD78F0730_DATA_SIZE_8_BITS;
dev_dbg(dev, "%s - 8 data bits\n", __func__);
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index dc7a65b..27e3bb5 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -288,7 +288,7 @@
/**
* serial_cleanup - free resources post close/hangup
- * @port: port to free up
+ * @tty: tty to clean up
*
* Do the resource freeing and refcount dropping for the port.
* Avoid freeing the console.
diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h
index 4bd69d0..622d639 100644
--- a/drivers/usb/serial/visor.h
+++ b/drivers/usb/serial/visor.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* USB HandSpring Visor driver
*
diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h
index 269e727..7e63074 100644
--- a/drivers/usb/serial/whiteheat.h
+++ b/drivers/usb/serial/whiteheat.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* USB ConnectTech WhiteHEAT driver
*
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index 59aad38..d17b60a 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -9,7 +9,7 @@
config USB_STORAGE
tristate "USB Mass Storage support"
depends on SCSI
- ---help---
+ help
Say Y here if you want to connect USB mass storage devices to your
computer's USB port. This is the driver you need for USB
floppy drives, USB hard disks, USB tape drives, USB CD-ROMs,
@@ -57,13 +57,13 @@
tristate "Freecom USB/ATAPI Bridge support"
help
Support for the Freecom USB to IDE/ATAPI adaptor.
- Freecom has a web page at <http://www.freecom.de/>.
+ Freecom has a web page at <https://www.freecom.de/>.
If this driver is compiled as a module, it will be named ums-freecom.
config USB_STORAGE_ISD200
tristate "ISD-200 USB/ATA Bridge support"
- ---help---
+ help
Say Y here if you want to use USB Mass Store devices based
on the In-Systems Design ISD-200 USB/ATA bridge.
@@ -165,7 +165,7 @@
config USB_STORAGE_CYPRESS_ATACB
tristate "SAT emulation on Cypress USB/ATA Bridge with ATACB"
- ---help---
+ help
Say Y here if you want to use SAT (ata pass through) on devices based
on the Cypress USB/ATA bridge supporting ATACB. This will allow you
to use tools to tune and monitor your drive (like hdparm or smartctl).
@@ -177,7 +177,7 @@
config USB_STORAGE_ENE_UB6250
tristate "USB ENE card reader support"
- ---help---
+ help
Say Y here if you wish to control a ENE SD/MS Card reader.
Note that this driver does not support SM cards.
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
index ddab2cd..20b857e 100644
--- a/drivers/usb/storage/alauda.c
+++ b/drivers/usb/storage/alauda.c
@@ -453,9 +453,8 @@
{
struct alauda_info *info = (struct alauda_info *) us->extra;
unsigned char status[2];
- int rc;
- rc = alauda_get_media_status(us, status);
+ alauda_get_media_status(us, status);
/* Check for no media or door open */
if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10)
diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h
index 16ce060..a6505ce 100644
--- a/drivers/usb/storage/debug.h
+++ b/drivers/usb/storage/debug.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for USB Mass Storage compliant devices
* Debugging Functions Header File
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index 8b1b730..98c1aa5 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -561,7 +561,7 @@
residue = min(residue, transfer_length);
if (us->srb != NULL)
scsi_set_resid(us->srb, max(scsi_get_resid(us->srb),
- (int)residue));
+ residue));
}
if (bcs->Status != US_BULK_STAT_OK)
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
index 34e7eaf..3d5f7d0 100644
--- a/drivers/usb/storage/freecom.c
+++ b/drivers/usb/storage/freecom.c
@@ -11,7 +11,7 @@
*
* This driver was developed with information provided in FREECOM's USB
* Programmers Reference Guide. For further information contact Freecom
- * (http://www.freecom.de/)
+ * (https://www.freecom.de/)
*/
#include <linux/module.h>
diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h
index 2dbf9c7..dcd7b7e 100644
--- a/drivers/usb/storage/initializers.h
+++ b/drivers/usb/storage/initializers.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Header file for Special Initializers for certain USB Mass Storage devices
*
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 89f5e33..3c76336 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1383,7 +1383,7 @@
ATA_CMD_MEDIA_LOCK : ATA_CMD_MEDIA_UNLOCK;
isd200_srb_set_bufflen(srb, 0);
} else {
- usb_stor_dbg(us, " Not removeable media, just report okay\n");
+ usb_stor_dbg(us, " Not removable media, just report okay\n");
srb->result = SAM_STAT_GOOD;
sendToTransport = 0;
}
diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h
index 072f1ff..1d10246 100644
--- a/drivers/usb/storage/protocol.h
+++ b/drivers/usb/storage/protocol.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for USB Mass Storage compliant devices
* Protocol Functions Header File
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 2adcabe..e5a971b 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -298,7 +298,7 @@
} else {
/*
- * Non-disk-type devices don't need to blacklist any pages
+ * Non-disk-type devices don't need to ignore any pages
* or to force 192-byte transfer lengths for MODE SENSE.
* But they do need to use MODE SENSE(10).
*/
@@ -370,8 +370,8 @@
/* check for state-transition errors */
if (us->srb != NULL) {
- printk(KERN_ERR "usb-storage: Error in %s: us->srb = %p\n",
- __func__, us->srb);
+ dev_err(&us->pusb_intf->dev,
+ "Error in %s: us->srb = %p\n", __func__, us->srb);
return SCSI_MLQUEUE_HOST_BUSY;
}
diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h
index 2bc5ea0..2a79c3e 100644
--- a/drivers/usb/storage/scsiglue.h
+++ b/drivers/usb/storage/scsiglue.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for USB Mass Storage compliant devices
* SCSI Connecting Glue Header File
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index ba955d6..15dc258 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -554,8 +554,8 @@
static unsigned long sddr55_get_capacity(struct us_data *us) {
- unsigned char uninitialized_var(manufacturerID);
- unsigned char uninitialized_var(deviceID);
+ unsigned char manufacturerID;
+ unsigned char deviceID;
int result;
struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra;
@@ -592,7 +592,7 @@
case 0x64:
info->pageshift = 8;
info->smallpageshift = 1;
- /* fall through */
+ fallthrough;
case 0x5d: // 5d is a ROM card with pagesize 512.
return 0x00200000;
diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c
index e605cbc..b9f78ef 100644
--- a/drivers/usb/storage/sierra_ms.c
+++ b/drivers/usb/storage/sierra_ms.c
@@ -129,15 +129,11 @@
int result, retries;
struct swoc_info *swocInfo;
struct usb_device *udev;
- struct Scsi_Host *sh;
retries = 3;
result = 0;
udev = us->pusb_dev;
- sh = us_to_host(us);
- scsi_get_host_dev(sh);
-
/* Force Modem mode */
if (swi_tru_install == TRU_FORCE_MODEM) {
usb_stor_dbg(us, "SWIMS: Forcing Modem Mode\n");
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 737b765..7cc8813 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -1291,8 +1291,7 @@
} else {
residue = min(residue, transfer_length);
- scsi_set_resid(srb, max(scsi_get_resid(srb),
- (int) residue));
+ scsi_set_resid(srb, max(scsi_get_resid(srb), residue));
}
}
diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
index fb3bb4e..74ffd0d 100644
--- a/drivers/usb/storage/transport.h
+++ b/drivers/usb/storage/transport.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for USB Mass Storage compliant devices
* Transport Functions Header File
diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h
index 3734a25..3f720fa 100644
--- a/drivers/usb/storage/uas-detect.h
+++ b/drivers/usb/storage/uas-detect.h
@@ -120,7 +120,7 @@
if (flags & US_FL_IGNORE_UAS) {
dev_warn(&udev->dev,
- "UAS is blacklisted for this device, using usb-storage instead\n");
+ "UAS is ignored for this device, using usb-storage instead\n");
return 0;
}
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 678903d..ff6f41e 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -279,17 +279,17 @@
switch (response_code) {
case RC_INCORRECT_LUN:
- cmnd->result = DID_BAD_TARGET << 16;
+ set_host_byte(cmnd, DID_BAD_TARGET);
break;
case RC_TMF_SUCCEEDED:
- cmnd->result = DID_OK << 16;
+ set_host_byte(cmnd, DID_OK);
break;
case RC_TMF_NOT_SUPPORTED:
- cmnd->result = DID_TARGET_FAILURE << 16;
+ set_host_byte(cmnd, DID_TARGET_FAILURE);
break;
default:
uas_log_cmd_state(cmnd, "response iu", response_code);
- cmnd->result = DID_ERROR << 16;
+ set_host_byte(cmnd, DID_ERROR);
break;
}
@@ -660,7 +660,7 @@
spin_lock_irqsave(&devinfo->lock, flags);
if (devinfo->resetting) {
- cmnd->result = DID_ERROR << 16;
+ set_host_byte(cmnd, DID_ERROR);
cmnd->scsi_done(cmnd);
goto zombie;
}
@@ -687,7 +687,7 @@
break;
case DMA_BIDIRECTIONAL:
cmdinfo->state |= ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB;
- /* fall through */
+ fallthrough;
case DMA_TO_DEVICE:
cmdinfo->state |= ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB;
case DMA_NONE:
@@ -704,7 +704,7 @@
* of queueing, no matter how fatal the error
*/
if (err == -ENODEV) {
- cmnd->result = DID_ERROR << 16;
+ set_host_byte(cmnd, DID_ERROR);
cmnd->scsi_done(cmnd);
goto zombie;
}
@@ -910,7 +910,6 @@
.eh_abort_handler = uas_eh_abort_handler,
.eh_device_reset_handler = uas_eh_device_reset_handler,
.this_id = -1,
- .sg_tablesize = SG_NONE,
.skip_settle_delay = 1,
.dma_boundary = PAGE_SIZE - 1,
};
diff --git a/drivers/usb/storage/unusual_alauda.h b/drivers/usb/storage/unusual_alauda.h
index 0ec8c99..13f61ec 100644
--- a/drivers/usb/storage/unusual_alauda.h
+++ b/drivers/usb/storage/unusual_alauda.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for the Alauda-based card readers
*/
diff --git a/drivers/usb/storage/unusual_cypress.h b/drivers/usb/storage/unusual_cypress.h
index fb99e52..0547daf 100644
--- a/drivers/usb/storage/unusual_cypress.h
+++ b/drivers/usb/storage/unusual_cypress.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for devices based on the Cypress USB/ATA bridge
* with support for ATACB
diff --git a/drivers/usb/storage/unusual_datafab.h b/drivers/usb/storage/unusual_datafab.h
index fdab5e7..5335b5d 100644
--- a/drivers/usb/storage/unusual_datafab.h
+++ b/drivers/usb/storage/unusual_datafab.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for the Datafab USB Compact Flash reader
*/
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 861153d..1a05e3d 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for USB Mass Storage compliant devices
* Unusual Devices File
@@ -44,7 +44,7 @@
* mode. Existing userspace solutions are superior.
*
* New mode switching devices should instead be added to the database
- * maintained at http://www.draisberghof.de/usb_modeswitch/
+ * maintained at https://www.draisberghof.de/usb_modeswitch/
*/
#if !defined(CONFIG_USB_STORAGE_SDDR09) && \
@@ -407,6 +407,16 @@
USB_SC_SCSI, USB_PR_BULK, NULL, US_FL_SINGLE_LUN),
/*
+ * Reported by James Buren <braewoods+lkml@braewoods.net>
+ * Virtual ISOs cannot be remounted if ejected while the device is locked
+ * Disable locking to mimic Windows behavior that bypasses the issue
+ */
+UNUSUAL_DEV( 0x04c5, 0x2028, 0x0001, 0x0001,
+ "iODD",
+ "2531/2541",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE),
+
+/*
* Not sure who reported this originally but
* Pavel Machek <pavel@ucw.cz> reported that the extra US_FL_SINGLE_LUN
* flag be added */
@@ -416,9 +426,16 @@
USB_SC_UFI, USB_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN),
/*
- * Reported by Ondrej Zary <linux@rainbow-software.org>
+ * Reported by Ondrej Zary <linux@zary.sk>
* The device reports one sector more and breaks when that sector is accessed
+ * Firmwares older than 2.6c (the latest one and the only that claims Linux
+ * support) have also broken tag handling
*/
+UNUSUAL_DEV( 0x04ce, 0x0002, 0x0000, 0x026b,
+ "ScanLogic",
+ "SL11R-IDE",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY | US_FL_BULK_IGNORE_TAG),
UNUSUAL_DEV( 0x04ce, 0x0002, 0x026c, 0x026c,
"ScanLogic",
"SL11R-IDE",
@@ -2284,6 +2301,16 @@
USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init,
US_FL_SCM_MULT_TARG ),
+/*
+ * Reported by DocMAX <mail@vacharakis.de>
+ * and Thomas Weißschuh <linux@weissschuh.net>
+ */
+UNUSUAL_DEV( 0x2109, 0x0715, 0x9999, 0x9999,
+ "VIA Labs, Inc.",
+ "VL817 SATA Bridge",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_UAS),
+
UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001,
"ST",
"2A",
diff --git a/drivers/usb/storage/unusual_ene_ub6250.h b/drivers/usb/storage/unusual_ene_ub6250.h
index 9134b91..a3b32ab 100644
--- a/drivers/usb/storage/unusual_ene_ub6250.h
+++ b/drivers/usb/storage/unusual_ene_ub6250.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
#if defined(CONFIG_USB_STORAGE_ENE_UB6250) || \
defined(CONFIG_USB_STORAGE_ENE_UB6250_MODULE)
diff --git a/drivers/usb/storage/unusual_freecom.h b/drivers/usb/storage/unusual_freecom.h
index 949231c..9ca6863 100644
--- a/drivers/usb/storage/unusual_freecom.h
+++ b/drivers/usb/storage/unusual_freecom.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for the Freecom USB/IDE adaptor
*/
diff --git a/drivers/usb/storage/unusual_isd200.h b/drivers/usb/storage/unusual_isd200.h
index d03a02c..f248190 100644
--- a/drivers/usb/storage/unusual_isd200.h
+++ b/drivers/usb/storage/unusual_isd200.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for In-System Design, Inc. ISD200 ASIC
*/
diff --git a/drivers/usb/storage/unusual_jumpshot.h b/drivers/usb/storage/unusual_jumpshot.h
index c323338..44878f8 100644
--- a/drivers/usb/storage/unusual_jumpshot.h
+++ b/drivers/usb/storage/unusual_jumpshot.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for the Lexar "Jumpshot" Compact Flash reader
*/
diff --git a/drivers/usb/storage/unusual_karma.h b/drivers/usb/storage/unusual_karma.h
index 8f1eebd..9fbed4c 100644
--- a/drivers/usb/storage/unusual_karma.h
+++ b/drivers/usb/storage/unusual_karma.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for the Rio Karma
*/
diff --git a/drivers/usb/storage/unusual_onetouch.h b/drivers/usb/storage/unusual_onetouch.h
index c76d4e9..cdfee8f 100644
--- a/drivers/usb/storage/unusual_onetouch.h
+++ b/drivers/usb/storage/unusual_onetouch.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for the Maxtor OneTouch USB hard drive's button
*/
diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h
index 7e14c2d..945dcb1 100644
--- a/drivers/usb/storage/unusual_realtek.h
+++ b/drivers/usb/storage/unusual_realtek.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for Realtek RTS51xx USB card reader
*
diff --git a/drivers/usb/storage/unusual_sddr09.h b/drivers/usb/storage/unusual_sddr09.h
index 650cf28..bfb6509 100644
--- a/drivers/usb/storage/unusual_sddr09.h
+++ b/drivers/usb/storage/unusual_sddr09.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for SanDisk SDDR-09 SmartMedia reader
*/
diff --git a/drivers/usb/storage/unusual_sddr55.h b/drivers/usb/storage/unusual_sddr55.h
index e89df2c..6d6f76e 100644
--- a/drivers/usb/storage/unusual_sddr55.h
+++ b/drivers/usb/storage/unusual_sddr55.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for SanDisk SDDR-55 SmartMedia reader
*/
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index c7db6c9..4051c8c 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for USB Attached SCSI devices - Unusual Devices File
*
@@ -50,7 +50,7 @@
"LaCie",
"Rugged USB3-FW",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
- US_FL_IGNORE_UAS),
+ US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME),
/*
* Apricorn USB3 dongle sometimes returns "USBSUSBSUSBS" in response to SCSI
diff --git a/drivers/usb/storage/unusual_usbat.h b/drivers/usb/storage/unusual_usbat.h
index 05abf68..f9d3e5e 100644
--- a/drivers/usb/storage/unusual_usbat.h
+++ b/drivers/usb/storage/unusual_usbat.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Unusual Devices File for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable
*/
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 2349dfa..90aa9c1 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -121,12 +121,12 @@
.initFunction = init_function, \
}
-static struct us_unusual_dev us_unusual_dev_list[] = {
+static const struct us_unusual_dev us_unusual_dev_list[] = {
# include "unusual_devs.h"
{ } /* Terminating entry */
};
-static struct us_unusual_dev for_dynamic_ids =
+static const struct us_unusual_dev for_dynamic_ids =
USUAL_DEV(USB_SC_SCSI, USB_PR_BULK);
#undef UNUSUAL_DEV
@@ -586,7 +586,7 @@
/* Get the unusual_devs entries and the string descriptors */
static int get_device_info(struct us_data *us, const struct usb_device_id *id,
- struct us_unusual_dev *unusual_dev)
+ const struct us_unusual_dev *unusual_dev)
{
struct usb_device *dev = us->pusb_dev;
struct usb_interface_descriptor *idesc =
@@ -936,7 +936,7 @@
int usb_stor_probe1(struct us_data **pus,
struct usb_interface *intf,
const struct usb_device_id *id,
- struct us_unusual_dev *unusual_dev,
+ const struct us_unusual_dev *unusual_dev,
struct scsi_host_template *sht)
{
struct Scsi_Host *host;
@@ -1095,7 +1095,7 @@
static int storage_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- struct us_unusual_dev *unusual_dev;
+ const struct us_unusual_dev *unusual_dev;
struct us_data *us;
int result;
int size;
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 85052cd..0451fac 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Driver for USB Mass Storage compliant devices
* Main Header File
@@ -93,7 +93,8 @@
struct mutex dev_mutex; /* protect pusb_dev */
struct usb_device *pusb_dev; /* this usb_device */
struct usb_interface *pusb_intf; /* this interface */
- struct us_unusual_dev *unusual_dev; /* device-filter entry */
+ const struct us_unusual_dev *unusual_dev;
+ /* device-filter entry */
unsigned long fflags; /* fixed flags from filter */
unsigned long dflags; /* dynamic atomic bitflags */
unsigned int send_bulk_pipe; /* cached pipe values */
@@ -185,7 +186,7 @@
extern int usb_stor_probe1(struct us_data **pus,
struct usb_interface *intf,
const struct usb_device_id *id,
- struct us_unusual_dev *unusual_dev,
+ const struct us_unusual_dev *unusual_dev,
struct scsi_host_template *sht);
extern int usb_stor_probe2(struct us_data *us);
extern void usb_stor_disconnect(struct usb_interface *intf);
diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c
index cfd12e5..5295128 100644
--- a/drivers/usb/storage/usual-tables.c
+++ b/drivers/usb/storage/usual-tables.c
@@ -40,7 +40,7 @@
.driver_info = (flags) \
}
-struct usb_device_id usb_storage_usb_ids[] = {
+const struct usb_device_id usb_storage_usb_ids[] = {
# include "unusual_devs.h"
{ } /* Terminating entry */
};
@@ -68,7 +68,7 @@
.bcdmax = bcdDeviceMax, \
}
-static struct ignore_entry ignore_ids[] = {
+static const struct ignore_entry ignore_ids[] = {
# include "unusual_alauda.h"
# include "unusual_cypress.h"
# include "unusual_datafab.h"
@@ -92,7 +92,7 @@
{
struct usb_device *udev;
unsigned vid, pid, bcd;
- struct ignore_entry *p;
+ const struct ignore_entry *p;
udev = interface_to_usbdev(intf);
vid = le16_to_cpu(udev->descriptor.idVendor);
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 895e241..0d953c6 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -50,10 +50,22 @@
source "drivers/usb/typec/ucsi/Kconfig"
+config TYPEC_HD3SS3220
+ tristate "TI HD3SS3220 Type-C DRP Port controller driver"
+ depends on I2C
+ depends on USB_ROLE_SWITCH
+ help
+ Say Y or M here if your system has TI HD3SS3220 Type-C DRP Port
+ controller driver.
+
+ If you choose to build this driver as a dynamically linked module, the
+ module will be called hd3ss3220.ko.
+
config TYPEC_TPS6598X
tristate "TI TPS6598x USB Power Delivery controller driver"
depends on I2C
- select REGMAP_I2C
+ depends on REGMAP_I2C
+ depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
help
Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power
Delivery controller.
@@ -61,6 +73,31 @@
If you choose to build this driver as a dynamically linked module, the
module will be called tps6598x.ko.
+config TYPEC_STUSB160X
+ tristate "STMicroelectronics STUSB160x Type-C controller driver"
+ depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y or M here if your system has STMicroelectronics STUSB160x
+ Type-C port controller.
+
+ If you choose to build this driver as a dynamically linked module, the
+ module will be called stusb160x.ko.
+
+config TYPEC_QCOM_PMIC
+ tristate "Qualcomm PMIC USB Type-C driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
+ help
+ Driver for supporting role switch over the Qualcomm PMIC. This will
+ handle the USB Type-C role and orientation detection reported by the
+ QCOM PMIC if the PMIC has the capability to handle USB Type-C
+ detection.
+
+ It will also enable the VBUS output to connected devices when a
+ DFP connection is made.
+
source "drivers/usb/typec/mux/Kconfig"
source "drivers/usb/typec/altmodes/Kconfig"
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 6696b72..d03b48c 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -4,5 +4,8 @@
obj-$(CONFIG_TYPEC) += altmodes/
obj-$(CONFIG_TYPEC_TCPM) += tcpm/
obj-$(CONFIG_TYPEC_UCSI) += ucsi/
+obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o
obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
+obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o
+obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o
obj-$(CONFIG_TYPEC) += mux/
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 4092248..e62e5e3 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* USB Typec-C DisplayPort Alternate Mode driver
*
* Copyright (C) 2018 Intel Corporation
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/usb/pd_vdo.h>
#include <linux/usb/typec_dp.h>
+#include "displayport.h"
#define DP_HEADER(_dp, cmd) (VDO((_dp)->alt->svid, 1, cmd) | \
VDO_OPOS(USB_TYPEC_DP_MODE))
@@ -188,8 +189,8 @@
switch (dp->state) {
case DP_STATE_ENTER:
- ret = typec_altmode_enter(dp->alt);
- if (ret)
+ ret = typec_altmode_enter(dp->alt, NULL);
+ if (ret && ret != -EBUSY)
dev_err(&dp->alt->dev, "failed to enter mode\n");
break;
case DP_STATE_UPDATE:
@@ -306,7 +307,8 @@
static int dp_altmode_activate(struct typec_altmode *alt, int activate)
{
- return activate ? typec_altmode_enter(alt) : typec_altmode_exit(alt);
+ return activate ? typec_altmode_enter(alt, NULL) :
+ typec_altmode_exit(alt);
}
static const struct typec_altmode_ops dp_altmode_ops = {
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index c950171..e8ddb81 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* Bus for USB Type-C Alternate Modes
*
* Copyright (C) 2018 Intel Corporation
@@ -10,26 +10,30 @@
#include "bus.h"
-static inline int typec_altmode_set_mux(struct altmode *alt, u8 state)
+static inline int
+typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data)
{
- return alt->mux ? alt->mux->set(alt->mux, state) : 0;
+ struct typec_mux_state state;
+
+ if (!alt->mux)
+ return 0;
+
+ state.alt = &alt->adev;
+ state.mode = conf;
+ state.data = data;
+
+ return alt->mux->set(alt->mux, &state);
}
-static int typec_altmode_set_state(struct typec_altmode *adev, int state)
+static int typec_altmode_set_state(struct typec_altmode *adev,
+ unsigned long conf, void *data)
{
bool is_port = is_typec_port(adev->dev.parent);
struct altmode *port_altmode;
- int ret;
port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner;
- ret = typec_altmode_set_mux(port_altmode, state);
- if (ret)
- return ret;
-
- blocking_notifier_call_chain(&port_altmode->nh, state, NULL);
-
- return 0;
+ return typec_altmode_set_mux(port_altmode, conf, data);
}
/* -------------------------------------------------------------------------- */
@@ -67,13 +71,10 @@
is_port = is_typec_port(adev->dev.parent);
partner = altmode->partner;
- ret = typec_altmode_set_mux(is_port ? altmode : partner, (u8)conf);
+ ret = typec_altmode_set_mux(is_port ? altmode : partner, conf, data);
if (ret)
return ret;
- blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh,
- conf, data);
-
if (partner->adev.ops && partner->adev.ops->notify)
return partner->adev.ops->notify(&partner->adev, conf, data);
@@ -84,12 +85,14 @@
/**
* typec_altmode_enter - Enter Mode
* @adev: The alternate mode
+ * @vdo: VDO for the Enter Mode command
*
* The alternate mode drivers use this function to enter mode. The port drivers
* use this to inform the alternate mode drivers that the partner has initiated
- * Enter Mode command.
+ * Enter Mode command. If the alternate mode does not require VDO, @vdo must be
+ * NULL.
*/
-int typec_altmode_enter(struct typec_altmode *adev)
+int typec_altmode_enter(struct typec_altmode *adev, u32 *vdo)
{
struct altmode *partner = to_altmode(adev)->partner;
struct typec_altmode *pdev = &partner->adev;
@@ -101,13 +104,16 @@
if (!pdev->ops || !pdev->ops->enter)
return -EOPNOTSUPP;
+ if (is_typec_port(pdev->dev.parent) && !pdev->active)
+ return -EPERM;
+
/* Moving to USB Safe State */
- ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE);
+ ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL);
if (ret)
return ret;
/* Enter Mode */
- return pdev->ops->enter(pdev);
+ return pdev->ops->enter(pdev, vdo);
}
EXPORT_SYMBOL_GPL(typec_altmode_enter);
@@ -130,7 +136,7 @@
return -EOPNOTSUPP;
/* Moving to USB Safe State */
- ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE);
+ ret = typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL);
if (ret)
return ret;
@@ -386,7 +392,7 @@
drv->remove(to_typec_altmode(dev));
if (adev->active) {
- WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE));
+ WARN_ON(typec_altmode_set_state(adev, TYPEC_STATE_SAFE, NULL));
typec_altmode_update_active(adev, false);
}
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 0c9661c..8ba8112 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -22,8 +22,6 @@
struct altmode *partner;
struct altmode *plug[2];
-
- struct blocking_notifier_head nh;
};
#define to_altmode(d) container_of(d, struct altmode, adev)
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 526509e..c7d44da 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -53,6 +53,7 @@
struct typec_mux *mux;
const struct typec_capability *cap;
+ const struct typec_operations *ops;
};
#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
@@ -205,69 +206,6 @@
put_device(&adev->dev);
}
-static void *typec_port_match(struct device_connection *con, int ep, void *data)
-{
- struct device *dev;
-
- /*
- * FIXME: Check does the fwnode supports the requested SVID. If it does
- * we need to return ERR_PTR(-PROBE_DEFER) when there is no device.
- */
- if (con->fwnode)
- return class_find_device_by_fwnode(typec_class, con->fwnode);
-
- dev = class_find_device_by_name(typec_class, con->endpoint[ep]);
-
- return dev ? dev : ERR_PTR(-EPROBE_DEFER);
-}
-
-struct typec_altmode *
-typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode,
- struct notifier_block *nb)
-{
- struct typec_device_id id = { svid, mode, };
- struct device *altmode_dev;
- struct device *port_dev;
- struct altmode *altmode;
- int ret;
-
- /* Find the port linked to the caller */
- port_dev = device_connection_find_match(dev, NULL, NULL,
- typec_port_match);
- if (IS_ERR_OR_NULL(port_dev))
- return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV);
-
- /* Find the altmode with matching svid */
- altmode_dev = device_find_child(port_dev, &id, altmode_match);
-
- put_device(port_dev);
-
- if (!altmode_dev)
- return ERR_PTR(-ENODEV);
-
- altmode = to_altmode(to_typec_altmode(altmode_dev));
-
- /* Register notifier */
- ret = blocking_notifier_chain_register(&altmode->nh, nb);
- if (ret) {
- put_device(altmode_dev);
- return ERR_PTR(ret);
- }
-
- return &altmode->adev;
-}
-EXPORT_SYMBOL_GPL(typec_altmode_register_notifier);
-
-void typec_altmode_unregister_notifier(struct typec_altmode *adev,
- struct notifier_block *nb)
-{
- struct altmode *altmode = to_altmode(adev);
-
- blocking_notifier_chain_unregister(&altmode->nh, nb);
- put_device(&adev->dev);
-}
-EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier);
-
/**
* typec_altmode_update_active - Report Enter/Exit mode
* @adev: Handle to the alternate mode
@@ -431,7 +369,28 @@
&dev_attr_vdo.attr,
NULL
};
-ATTRIBUTE_GROUPS(typec_altmode);
+
+static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
+
+ if (attr == &dev_attr_active.attr)
+ if (!adev->ops || !adev->ops->activate)
+ return 0444;
+
+ return attr->mode;
+}
+
+static struct attribute_group typec_altmode_group = {
+ .is_visible = typec_altmode_attr_is_visible,
+ .attrs = typec_altmode_attrs,
+};
+
+static const struct attribute_group *typec_altmode_groups[] = {
+ &typec_altmode_group,
+ NULL
+};
static int altmode_id_get(struct device *dev)
{
@@ -518,9 +477,7 @@
dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id);
/* Link partners and plugs with the ports */
- if (is_port)
- BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh);
- else
+ if (!is_port)
typec_altmode_set_partner(alt);
/* The partners are bind to drivers */
@@ -625,7 +582,7 @@
* SVID listed in response to Discover Modes command need to be listed in an
* array in @desc.
*
- * Returns handle to the alternate mode on success or NULL on failure.
+ * Returns handle to the alternate mode on success or ERR_PTR on failure.
*/
struct typec_altmode *
typec_partner_register_altmode(struct typec_partner *partner,
@@ -835,6 +792,52 @@
.release = typec_cable_release,
};
+static int cable_match(struct device *dev, void *data)
+{
+ return is_typec_cable(dev);
+}
+
+/**
+ * typec_cable_get - Get a reference to the USB Type-C cable
+ * @port: The USB Type-C Port the cable is connected to
+ *
+ * The caller must decrement the reference count with typec_cable_put() after
+ * use.
+ */
+struct typec_cable *typec_cable_get(struct typec_port *port)
+{
+ struct device *dev;
+
+ dev = device_find_child(&port->dev, NULL, cable_match);
+ if (!dev)
+ return NULL;
+
+ return to_typec_cable(dev);
+}
+EXPORT_SYMBOL_GPL(typec_cable_get);
+
+/**
+ * typec_cable_put - Decrement the reference count on USB Type-C cable
+ * @cable: The USB Type-C cable
+ */
+void typec_cable_put(struct typec_cable *cable)
+{
+ put_device(&cable->dev);
+}
+EXPORT_SYMBOL_GPL(typec_cable_put);
+
+/**
+ * typec_cable_is_active - Check is the USB Type-C cable active or passive
+ * @cable: The USB Type-C Cable
+ *
+ * Return 1 if the cable is active or 0 if it's passive.
+ */
+int typec_cable_is_active(struct typec_cable *cable)
+{
+ return cable->active;
+}
+EXPORT_SYMBOL_GPL(typec_cable_is_active);
+
/**
* typec_cable_set_identity - Report result from Discover Identity command
* @cable: The cable updated identity values
@@ -916,6 +919,12 @@
/* ------------------------------------------------------------------------- */
/* USB Type-C ports */
+static const char * const typec_orientations[] = {
+ [TYPEC_ORIENTATION_NONE] = "unknown",
+ [TYPEC_ORIENTATION_NORMAL] = "normal",
+ [TYPEC_ORIENTATION_REVERSE] = "reverse",
+};
+
static const char * const typec_roles[] = {
[TYPEC_SINK] = "sink",
[TYPEC_SOURCE] = "source",
@@ -957,7 +966,7 @@
return -EOPNOTSUPP;
}
- if (!port->cap->try_role) {
+ if (!port->ops || !port->ops->try_role) {
dev_dbg(dev, "Setting preferred role not supported\n");
return -EOPNOTSUPP;
}
@@ -970,7 +979,7 @@
return -EINVAL;
}
- ret = port->cap->try_role(port->cap, role);
+ ret = port->ops->try_role(port, role);
if (ret)
return ret;
@@ -1001,7 +1010,7 @@
struct typec_port *port = to_typec_port(dev);
int ret;
- if (!port->cap->dr_set) {
+ if (!port->ops || !port->ops->dr_set) {
dev_dbg(dev, "data role swapping not supported\n");
return -EOPNOTSUPP;
}
@@ -1016,7 +1025,7 @@
goto unlock_and_ret;
}
- ret = port->cap->dr_set(port->cap, ret);
+ ret = port->ops->dr_set(port, ret);
if (ret)
goto unlock_and_ret;
@@ -1046,12 +1055,7 @@
struct typec_port *port = to_typec_port(dev);
int ret;
- if (!port->cap->pd_revision) {
- dev_dbg(dev, "USB Power Delivery not supported\n");
- return -EOPNOTSUPP;
- }
-
- if (!port->cap->pr_set) {
+ if (!port->ops || !port->ops->pr_set) {
dev_dbg(dev, "power role swapping not supported\n");
return -EOPNOTSUPP;
}
@@ -1073,7 +1077,7 @@
goto unlock_and_ret;
}
- ret = port->cap->pr_set(port->cap, ret);
+ ret = port->ops->pr_set(port, ret);
if (ret)
goto unlock_and_ret;
@@ -1104,7 +1108,8 @@
int ret;
enum typec_port_type type;
- if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) {
+ if (port->cap->type != TYPEC_PORT_DRP ||
+ !port->ops || !port->ops->port_type_set) {
dev_dbg(dev, "changing port type not supported\n");
return -EOPNOTSUPP;
}
@@ -1121,7 +1126,7 @@
goto unlock_and_ret;
}
- ret = port->cap->port_type_set(port->cap, type);
+ ret = port->ops->port_type_set(port, type);
if (ret)
goto unlock_and_ret;
@@ -1177,7 +1182,7 @@
return -EOPNOTSUPP;
}
- if (!port->cap->vconn_set) {
+ if (!port->ops || !port->ops->vconn_set) {
dev_dbg(dev, "VCONN swapping not supported\n");
return -EOPNOTSUPP;
}
@@ -1186,7 +1191,7 @@
if (ret)
return ret;
- ret = port->cap->vconn_set(port->cap, (enum typec_role)source);
+ ret = port->ops->vconn_set(port, (enum typec_role)source);
if (ret)
return ret;
@@ -1247,6 +1252,16 @@
}
static DEVICE_ATTR_RO(usb_power_delivery_revision);
+static ssize_t orientation_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ return sprintf(buf, "%s\n", typec_orientations[port->orientation]);
+}
+static DEVICE_ATTR_RO(orientation);
+
static struct attribute *typec_attrs[] = {
&dev_attr_data_role.attr,
&dev_attr_power_operation_mode.attr,
@@ -1257,9 +1272,54 @@
&dev_attr_usb_typec_revision.attr,
&dev_attr_vconn_source.attr,
&dev_attr_port_type.attr,
+ &dev_attr_orientation.attr,
NULL,
};
-ATTRIBUTE_GROUPS(typec);
+
+static umode_t typec_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct typec_port *port = to_typec_port(kobj_to_dev(kobj));
+
+ if (attr == &dev_attr_data_role.attr) {
+ if (port->cap->data != TYPEC_PORT_DRD ||
+ !port->ops || !port->ops->dr_set)
+ return 0444;
+ } else if (attr == &dev_attr_power_role.attr) {
+ if (port->cap->type != TYPEC_PORT_DRP ||
+ !port->ops || !port->ops->pr_set)
+ return 0444;
+ } else if (attr == &dev_attr_vconn_source.attr) {
+ if (!port->cap->pd_revision ||
+ !port->ops || !port->ops->vconn_set)
+ return 0444;
+ } else if (attr == &dev_attr_preferred_role.attr) {
+ if (port->cap->type != TYPEC_PORT_DRP ||
+ !port->ops || !port->ops->try_role)
+ return 0444;
+ } else if (attr == &dev_attr_port_type.attr) {
+ if (!port->ops || !port->ops->port_type_set)
+ return 0;
+ if (port->cap->type != TYPEC_PORT_DRP)
+ return 0444;
+ } else if (attr == &dev_attr_orientation.attr) {
+ if (port->cap->orientation_aware)
+ return 0444;
+ return 0;
+ }
+
+ return attr->mode;
+}
+
+static struct attribute_group typec_group = {
+ .is_visible = typec_attr_is_visible,
+ .attrs = typec_attrs,
+};
+
+static const struct attribute_group *typec_groups[] = {
+ &typec_group,
+ NULL
+};
static int typec_uevent(struct device *dev, struct kobj_uevent_env *env)
{
@@ -1280,6 +1340,7 @@
ida_destroy(&port->mode_ids);
typec_switch_put(port->sw);
typec_mux_put(port->mux);
+ kfree(port->cap);
kfree(port);
}
@@ -1390,6 +1451,36 @@
EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
/**
+ * typec_find_pwr_opmode - Get the typec power operation mode capability
+ * @name: power operation mode string
+ *
+ * This routine is used to find the typec_pwr_opmode by its string @name.
+ *
+ * Returns typec_pwr_opmode if success, otherwise negative error code.
+ */
+int typec_find_pwr_opmode(const char *name)
+{
+ return match_string(typec_pwr_opmodes,
+ ARRAY_SIZE(typec_pwr_opmodes), name);
+}
+EXPORT_SYMBOL_GPL(typec_find_pwr_opmode);
+
+/**
+ * typec_find_orientation - Convert orientation string to enum typec_orientation
+ * @name: Orientation string
+ *
+ * This routine is used to find the typec_orientation by its string name @name.
+ *
+ * Returns the orientation value on success, otherwise negative error code.
+ */
+int typec_find_orientation(const char *name)
+{
+ return match_string(typec_orientations, ARRAY_SIZE(typec_orientations),
+ name);
+}
+EXPORT_SYMBOL_GPL(typec_find_orientation);
+
+/**
* typec_find_port_power_role - Get the typec port power capability
* @name: port power capability string
*
@@ -1448,13 +1539,13 @@
{
int ret;
- if (port->sw) {
- ret = port->sw->set(port->sw, orientation);
- if (ret)
- return ret;
- }
+ ret = typec_switch_set(port->sw, orientation);
+ if (ret)
+ return ret;
port->orientation = orientation;
+ sysfs_notify(&port->dev.kobj, NULL, "orientation");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
return 0;
}
@@ -1482,13 +1573,27 @@
*/
int typec_set_mode(struct typec_port *port, int mode)
{
- return port->mux ? port->mux->set(port->mux, mode) : 0;
+ struct typec_mux_state state = { };
+
+ state.mode = mode;
+
+ return typec_mux_set(port->mux, &state);
}
EXPORT_SYMBOL_GPL(typec_set_mode);
/* --------------------------------------- */
/**
+ * typec_get_drvdata - Return private driver data pointer
+ * @port: USB Type-C port
+ */
+void *typec_get_drvdata(struct typec_port *port)
+{
+ return dev_get_drvdata(&port->dev);
+}
+EXPORT_SYMBOL_GPL(typec_get_drvdata);
+
+/**
* typec_port_register_altmode - Register USB Type-C Port Alternate Mode
* @port: USB Type-C Port that supports the alternate mode
* @desc: Description of the alternate mode
@@ -1581,7 +1686,7 @@
mutex_init(&port->port_type_lock);
port->id = id;
- port->cap = cap;
+ port->ops = cap->ops;
port->port_type = cap->type;
port->prefer_role = cap->prefer_role;
@@ -1591,6 +1696,13 @@
port->dev.fwnode = cap->fwnode;
port->dev.type = &typec_port_dev_type;
dev_set_name(&port->dev, "port%d", id);
+ dev_set_drvdata(&port->dev, cap->driver_data);
+
+ port->cap = kmemdup(cap, sizeof(*cap), GFP_KERNEL);
+ if (!port->cap) {
+ put_device(&port->dev);
+ return ERR_PTR(-ENOMEM);
+ }
port->sw = typec_switch_get(&port->dev);
if (IS_ERR(port->sw)) {
diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
new file mode 100644
index 0000000..f633ec1
--- /dev/null
+++ b/drivers/usb/typec/hd3ss3220.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * TI HD3SS3220 Type-C DRP Port Controller Driver
+ *
+ * Copyright (C) 2019 Renesas Electronics Corp.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/usb/role.h>
+#include <linux/irqreturn.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/usb/typec.h>
+#include <linux/delay.h>
+
+#define HD3SS3220_REG_CN_STAT_CTRL 0x09
+#define HD3SS3220_REG_GEN_CTRL 0x0A
+#define HD3SS3220_REG_DEV_REV 0xA0
+
+/* Register HD3SS3220_REG_CN_STAT_CTRL*/
+#define HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK (BIT(7) | BIT(6))
+#define HD3SS3220_REG_CN_STAT_CTRL_AS_DFP BIT(6)
+#define HD3SS3220_REG_CN_STAT_CTRL_AS_UFP BIT(7)
+#define HD3SS3220_REG_CN_STAT_CTRL_TO_ACCESSORY (BIT(7) | BIT(6))
+#define HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS BIT(4)
+
+/* Register HD3SS3220_REG_GEN_CTRL*/
+#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK (BIT(2) | BIT(1))
+#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT 0x00
+#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK BIT(1)
+#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC (BIT(2) | BIT(1))
+
+struct hd3ss3220 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct usb_role_switch *role_sw;
+ struct typec_port *port;
+};
+
+static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int src_pref)
+{
+ return regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
+ HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK,
+ src_pref);
+}
+
+static enum usb_role hd3ss3220_get_attached_state(struct hd3ss3220 *hd3ss3220)
+{
+ unsigned int reg_val;
+ enum usb_role attached_state;
+ int ret;
+
+ ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL,
+ ®_val);
+ if (ret < 0)
+ return ret;
+
+ switch (reg_val & HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK) {
+ case HD3SS3220_REG_CN_STAT_CTRL_AS_DFP:
+ attached_state = USB_ROLE_HOST;
+ break;
+ case HD3SS3220_REG_CN_STAT_CTRL_AS_UFP:
+ attached_state = USB_ROLE_DEVICE;
+ break;
+ default:
+ attached_state = USB_ROLE_NONE;
+ break;
+ }
+
+ return attached_state;
+}
+
+static int hd3ss3220_dr_set(struct typec_port *port, enum typec_data_role role)
+{
+ struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port);
+ enum usb_role role_val;
+ int pref, ret = 0;
+
+ if (role == TYPEC_HOST) {
+ role_val = USB_ROLE_HOST;
+ pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC;
+ } else {
+ role_val = USB_ROLE_DEVICE;
+ pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK;
+ }
+
+ ret = hd3ss3220_set_source_pref(hd3ss3220, pref);
+ usleep_range(10, 100);
+
+ usb_role_switch_set_role(hd3ss3220->role_sw, role_val);
+ typec_set_data_role(hd3ss3220->port, role);
+
+ return ret;
+}
+
+static const struct typec_operations hd3ss3220_ops = {
+ .dr_set = hd3ss3220_dr_set
+};
+
+static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
+{
+ enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
+
+ usb_role_switch_set_role(hd3ss3220->role_sw, role_state);
+ if (role_state == USB_ROLE_NONE)
+ hd3ss3220_set_source_pref(hd3ss3220,
+ HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT);
+
+ switch (role_state) {
+ case USB_ROLE_HOST:
+ typec_set_data_role(hd3ss3220->port, TYPEC_HOST);
+ break;
+ case USB_ROLE_DEVICE:
+ typec_set_data_role(hd3ss3220->port, TYPEC_DEVICE);
+ break;
+ default:
+ break;
+ }
+}
+
+static irqreturn_t hd3ss3220_irq(struct hd3ss3220 *hd3ss3220)
+{
+ int err;
+
+ hd3ss3220_set_role(hd3ss3220);
+ err = regmap_update_bits_base(hd3ss3220->regmap,
+ HD3SS3220_REG_CN_STAT_CTRL,
+ HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS,
+ HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS,
+ NULL, false, true);
+ if (err < 0)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t hd3ss3220_irq_handler(int irq, void *data)
+{
+ struct i2c_client *client = to_i2c_client(data);
+ struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client);
+
+ return hd3ss3220_irq(hd3ss3220);
+}
+
+static const struct regmap_config config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x0A,
+};
+
+static int hd3ss3220_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct typec_capability typec_cap = { };
+ struct hd3ss3220 *hd3ss3220;
+ struct fwnode_handle *connector, *ep;
+ int ret;
+ unsigned int data;
+
+ hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220),
+ GFP_KERNEL);
+ if (!hd3ss3220)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, hd3ss3220);
+
+ hd3ss3220->dev = &client->dev;
+ hd3ss3220->regmap = devm_regmap_init_i2c(client, &config);
+ if (IS_ERR(hd3ss3220->regmap))
+ return PTR_ERR(hd3ss3220->regmap);
+
+ hd3ss3220_set_source_pref(hd3ss3220,
+ HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT);
+ /* For backward compatibility check the connector child node first */
+ connector = device_get_named_child_node(hd3ss3220->dev, "connector");
+ if (connector) {
+ hd3ss3220->role_sw = fwnode_usb_role_switch_get(connector);
+ } else {
+ ep = fwnode_graph_get_next_endpoint(dev_fwnode(hd3ss3220->dev), NULL);
+ if (!ep)
+ return -ENODEV;
+ connector = fwnode_graph_get_remote_port_parent(ep);
+ fwnode_handle_put(ep);
+ if (!connector)
+ return -ENODEV;
+ hd3ss3220->role_sw = usb_role_switch_get(hd3ss3220->dev);
+ }
+
+ if (IS_ERR(hd3ss3220->role_sw)) {
+ ret = PTR_ERR(hd3ss3220->role_sw);
+ goto err_put_fwnode;
+ }
+
+ typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
+ typec_cap.driver_data = hd3ss3220;
+ typec_cap.type = TYPEC_PORT_DRP;
+ typec_cap.data = TYPEC_PORT_DRD;
+ typec_cap.ops = &hd3ss3220_ops;
+ typec_cap.fwnode = connector;
+
+ hd3ss3220->port = typec_register_port(&client->dev, &typec_cap);
+ if (IS_ERR(hd3ss3220->port)) {
+ ret = PTR_ERR(hd3ss3220->port);
+ goto err_put_role;
+ }
+
+ hd3ss3220_set_role(hd3ss3220);
+ ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data);
+ if (ret < 0)
+ goto err_unreg_port;
+
+ if (data & HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS) {
+ ret = regmap_write(hd3ss3220->regmap,
+ HD3SS3220_REG_CN_STAT_CTRL,
+ data | HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS);
+ if (ret < 0)
+ goto err_unreg_port;
+ }
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ hd3ss3220_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "hd3ss3220", &client->dev);
+ if (ret)
+ goto err_unreg_port;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, HD3SS3220_REG_DEV_REV);
+ if (ret < 0)
+ goto err_unreg_port;
+
+ fwnode_handle_put(connector);
+
+ dev_info(&client->dev, "probed revision=0x%x\n", ret);
+
+ return 0;
+err_unreg_port:
+ typec_unregister_port(hd3ss3220->port);
+err_put_role:
+ usb_role_switch_put(hd3ss3220->role_sw);
+err_put_fwnode:
+ fwnode_handle_put(connector);
+
+ return ret;
+}
+
+static int hd3ss3220_remove(struct i2c_client *client)
+{
+ struct hd3ss3220 *hd3ss3220 = i2c_get_clientdata(client);
+
+ typec_unregister_port(hd3ss3220->port);
+ usb_role_switch_put(hd3ss3220->role_sw);
+
+ return 0;
+}
+
+static const struct of_device_id dev_ids[] = {
+ { .compatible = "ti,hd3ss3220"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, dev_ids);
+
+static struct i2c_driver hd3ss3220_driver = {
+ .driver = {
+ .name = "hd3ss3220",
+ .of_match_table = of_match_ptr(dev_ids),
+ },
+ .probe = hd3ss3220_probe,
+ .remove = hd3ss3220_remove,
+};
+
+module_i2c_driver(hd3ss3220_driver);
+
+MODULE_AUTHOR("Biju Das <biju.das@bp.renesas.com>");
+MODULE_DESCRIPTION("TI HD3SS3220 DRP Port Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c
index 2e60cee..b9035c3 100644
--- a/drivers/usb/typec/mux.c
+++ b/drivers/usb/typec/mux.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* USB Type-C Multiplexer/DeMultiplexer Switch support
*
* Copyright (C) 2018 Intel Corporation
@@ -17,11 +17,6 @@
#include "bus.h"
-static int name_match(struct device *dev, const void *name)
-{
- return !strcmp((const char *)name, dev_name(dev));
-}
-
static bool dev_name_ends_with(struct device *dev, const char *suffix)
{
const char *name = dev_name(dev);
@@ -39,49 +34,44 @@
return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-switch");
}
-static void *typec_switch_match(struct device_connection *con, int ep,
+static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id,
void *data)
{
struct device *dev;
- if (con->fwnode) {
- if (con->id && !fwnode_property_present(con->fwnode, con->id))
- return NULL;
+ if (id && !fwnode_property_present(fwnode, id))
+ return NULL;
- dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
- switch_fwnode_match);
- } else {
- dev = class_find_device(&typec_mux_class, NULL,
- con->endpoint[ep], name_match);
- }
+ dev = class_find_device(&typec_mux_class, NULL, fwnode,
+ switch_fwnode_match);
return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
}
/**
- * typec_switch_get - Find USB Type-C orientation switch
- * @dev: The caller device
+ * fwnode_typec_switch_get - Find USB Type-C orientation switch
+ * @fwnode: The caller device node
*
* Finds a switch linked with @dev. Returns a reference to the switch on
* success, NULL if no matching connection was found, or
* ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
* has not been enumerated yet.
*/
-struct typec_switch *typec_switch_get(struct device *dev)
+struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
{
struct typec_switch *sw;
- sw = device_connection_find_match(dev, "orientation-switch", NULL,
+ sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL,
typec_switch_match);
if (!IS_ERR_OR_NULL(sw))
WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
return sw;
}
-EXPORT_SYMBOL_GPL(typec_switch_get);
+EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
/**
- * typec_put_switch - Release USB Type-C orientation switch
+ * typec_switch_put - Release USB Type-C orientation switch
* @sw: USB Type-C orientation switch
*
* Decrement reference count for @sw.
@@ -137,7 +127,8 @@
sw->dev.class = &typec_mux_class;
sw->dev.type = &typec_switch_dev_type;
sw->dev.driver_data = desc->drvdata;
- dev_set_name(&sw->dev, "%s-switch", dev_name(parent));
+ dev_set_name(&sw->dev, "%s-switch",
+ desc->name ? desc->name : dev_name(parent));
ret = device_add(&sw->dev);
if (ret) {
@@ -150,6 +141,16 @@
}
EXPORT_SYMBOL_GPL(typec_switch_register);
+int typec_switch_set(struct typec_switch *sw,
+ enum typec_orientation orientation)
+{
+ if (IS_ERR_OR_NULL(sw))
+ return 0;
+
+ return sw->set(sw, orientation);
+}
+EXPORT_SYMBOL_GPL(typec_switch_set);
+
/**
* typec_switch_unregister - Unregister USB Type-C orientation switch
* @sw: USB Type-C orientation switch
@@ -182,40 +183,35 @@
return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-mux");
}
-static void *typec_mux_match(struct device_connection *con, int ep, void *data)
+static void *typec_mux_match(struct fwnode_handle *fwnode, const char *id,
+ void *data)
{
const struct typec_altmode_desc *desc = data;
struct device *dev;
bool match;
int nval;
u16 *val;
+ int ret;
int i;
- if (!con->fwnode) {
- dev = class_find_device(&typec_mux_class, NULL,
- con->endpoint[ep], name_match);
-
- return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
- }
-
/*
* Check has the identifier already been "consumed". If it
* has, no need to do any extra connection identification.
*/
- match = !con->id;
+ match = !id;
if (match)
goto find_mux;
/* Accessory Mode muxes */
if (!desc) {
- match = fwnode_property_present(con->fwnode, "accessory");
+ match = fwnode_property_present(fwnode, "accessory");
if (match)
goto find_mux;
return NULL;
}
/* Alternate Mode muxes */
- nval = fwnode_property_count_u16(con->fwnode, "svid");
+ nval = fwnode_property_count_u16(fwnode, "svid");
if (nval <= 0)
return NULL;
@@ -223,10 +219,10 @@
if (!val)
return ERR_PTR(-ENOMEM);
- nval = fwnode_property_read_u16_array(con->fwnode, "svid", val, nval);
- if (nval < 0) {
+ ret = fwnode_property_read_u16_array(fwnode, "svid", val, nval);
+ if (ret < 0) {
kfree(val);
- return ERR_PTR(nval);
+ return ERR_PTR(ret);
}
for (i = 0; i < nval; i++) {
@@ -240,15 +236,15 @@
return NULL;
find_mux:
- dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
+ dev = class_find_device(&typec_mux_class, NULL, fwnode,
mux_fwnode_match);
return dev ? to_typec_mux(dev) : ERR_PTR(-EPROBE_DEFER);
}
/**
- * typec_mux_get - Find USB Type-C Multiplexer
- * @dev: The caller device
+ * fwnode_typec_mux_get - Find USB Type-C Multiplexer
+ * @fwnode: The caller device node
* @desc: Alt Mode description
*
* Finds a mux linked to the caller. This function is primarily meant for the
@@ -256,19 +252,19 @@
* matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
* was found but the mux has not been enumerated yet.
*/
-struct typec_mux *typec_mux_get(struct device *dev,
- const struct typec_altmode_desc *desc)
+struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode,
+ const struct typec_altmode_desc *desc)
{
struct typec_mux *mux;
- mux = device_connection_find_match(dev, "mode-switch", (void *)desc,
+ mux = fwnode_connection_find_match(fwnode, "mode-switch", (void *)desc,
typec_mux_match);
if (!IS_ERR_OR_NULL(mux))
WARN_ON(!try_module_get(mux->dev.parent->driver->owner));
return mux;
}
-EXPORT_SYMBOL_GPL(typec_mux_get);
+EXPORT_SYMBOL_GPL(fwnode_typec_mux_get);
/**
* typec_mux_put - Release handle to a Multiplexer
@@ -285,6 +281,15 @@
}
EXPORT_SYMBOL_GPL(typec_mux_put);
+int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
+{
+ if (IS_ERR_OR_NULL(mux))
+ return 0;
+
+ return mux->set(mux, state);
+}
+EXPORT_SYMBOL_GPL(typec_mux_set);
+
static void typec_mux_release(struct device *dev)
{
kfree(to_typec_mux(dev));
@@ -326,7 +331,8 @@
mux->dev.class = &typec_mux_class;
mux->dev.type = &typec_mux_dev_type;
mux->dev.driver_data = desc->drvdata;
- dev_set_name(&mux->dev, "%s-mux", dev_name(parent));
+ dev_set_name(&mux->dev, "%s-mux",
+ desc->name ? desc->name : dev_name(parent));
ret = device_add(&mux->dev);
if (ret) {
diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
index 01ed0d5..edead55 100644
--- a/drivers/usb/typec/mux/Kconfig
+++ b/drivers/usb/typec/mux/Kconfig
@@ -9,4 +9,14 @@
Say Y or M if your system has a Pericom PI3USB30532 Type-C cross
switch / mux chip found on some devices with a Type-C port.
+config TYPEC_MUX_INTEL_PMC
+ tristate "Intel PMC mux control"
+ depends on ACPI
+ depends on INTEL_SCU_IPC
+ select USB_ROLE_SWITCH
+ help
+ Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can
+ control the USB role switch and also the multiplexer/demultiplexer
+ switches used with USB Type-C Alternate Modes.
+
endmenu
diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
index 1332e46..280a6f5 100644
--- a/drivers/usb/typec/mux/Makefile
+++ b/drivers/usb/typec/mux/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
+obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
new file mode 100644
index 0000000..acdef6f
--- /dev/null
+++ b/drivers/usb/typec/mux/intel_pmc_mux.c
@@ -0,0 +1,688 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Intel PMC USB mux control
+ *
+ * Copyright (C) 2020 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_tbt.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define PMC_USBC_CMD 0xa7
+
+/* Response status bits */
+#define PMC_USB_RESP_STATUS_FAILURE BIT(0)
+#define PMC_USB_RESP_STATUS_FATAL BIT(1)
+
+/* "Usage" OOB Message field values */
+enum {
+ PMC_USB_CONNECT,
+ PMC_USB_DISCONNECT,
+ PMC_USB_SAFE_MODE,
+ PMC_USB_ALT_MODE,
+ PMC_USB_DP_HPD,
+};
+
+#define PMC_USB_MSG_USB2_PORT_SHIFT 0
+#define PMC_USB_MSG_USB3_PORT_SHIFT 4
+#define PMC_USB_MSG_UFP_SHIFT 4
+#define PMC_USB_MSG_ORI_HSL_SHIFT 5
+#define PMC_USB_MSG_ORI_AUX_SHIFT 6
+
+/* Alt Mode Request */
+struct altmode_req {
+ u8 usage;
+ u8 mode_type;
+ u8 mode_id;
+ u8 reserved;
+ u32 mode_data;
+} __packed;
+
+#define PMC_USB_MODE_TYPE_SHIFT 4
+
+enum {
+ PMC_USB_MODE_TYPE_USB,
+ PMC_USB_MODE_TYPE_DP,
+ PMC_USB_MODE_TYPE_TBT,
+};
+
+/* Common Mode Data bits */
+#define PMC_USB_ALTMODE_ACTIVE_CABLE BIT(2)
+
+#define PMC_USB_ALTMODE_ORI_SHIFT 1
+#define PMC_USB_ALTMODE_UFP_SHIFT 3
+
+/* DP specific Mode Data bits */
+#define PMC_USB_ALTMODE_DP_MODE_SHIFT 8
+
+/* TBT specific Mode Data bits */
+#define PMC_USB_ALTMODE_TBT_TYPE BIT(17)
+#define PMC_USB_ALTMODE_CABLE_TYPE BIT(18)
+#define PMC_USB_ALTMODE_ACTIVE_LINK BIT(20)
+#define PMC_USB_ALTMODE_FORCE_LSR BIT(23)
+#define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25)
+#define PMC_USB_ALTMODE_CABLE_USB31 1
+#define PMC_USB_ALTMODE_CABLE_10GPS 2
+#define PMC_USB_ALTMODE_CABLE_20GPS 3
+#define PMC_USB_ALTMODE_TBT_GEN(_g_) (((_g_) & GENMASK(1, 0)) << 28)
+
+/* Display HPD Request bits */
+#define PMC_USB_DP_HPD_LVL BIT(4)
+#define PMC_USB_DP_HPD_IRQ BIT(5)
+
+/*
+ * Input Output Manager (IOM) PORT STATUS
+ */
+#define IOM_PORT_STATUS_OFFSET 0x560
+
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_MASK GENMASK(9, 6)
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_SHIFT 6
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_USB 0x03
+/* activity type: Safe Mode */
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_SAFE_MODE 0x04
+/* activity type: Display Port */
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_DP 0x05
+/* activity type: Display Port Multi Function Device */
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_DP_MFD 0x06
+/* activity type: Thunderbolt */
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_TBT 0x07
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_ALT_MODE_USB 0x0c
+#define IOM_PORT_STATUS_ACTIVITY_TYPE_ALT_MODE_TBT_USB 0x0d
+/* Upstream Facing Port Information */
+#define IOM_PORT_STATUS_UFP BIT(10)
+/* Display Port Hot Plug Detect status */
+#define IOM_PORT_STATUS_DHPD_HPD_STATUS_MASK GENMASK(13, 12)
+#define IOM_PORT_STATUS_DHPD_HPD_STATUS_SHIFT 12
+#define IOM_PORT_STATUS_DHPD_HPD_STATUS_ASSERT 0x01
+#define IOM_PORT_STATUS_DHPD_HPD_SOURCE_TBT BIT(14)
+#define IOM_PORT_STATUS_CONNECTED BIT(31)
+
+#define IOM_PORT_ACTIVITY_IS(_status_, _type_) \
+ ((((_status_) & IOM_PORT_STATUS_ACTIVITY_TYPE_MASK) >> \
+ IOM_PORT_STATUS_ACTIVITY_TYPE_SHIFT) == \
+ (IOM_PORT_STATUS_ACTIVITY_TYPE_##_type_))
+
+#define IOM_PORT_HPD_ASSERTED(_status_) \
+ ((((_status_) & IOM_PORT_STATUS_DHPD_HPD_STATUS_MASK) >> \
+ IOM_PORT_STATUS_DHPD_HPD_STATUS_SHIFT) & \
+ IOM_PORT_STATUS_DHPD_HPD_STATUS_ASSERT)
+
+struct pmc_usb;
+
+struct pmc_usb_port {
+ int num;
+ u32 iom_status;
+ struct pmc_usb *pmc;
+ struct typec_mux *typec_mux;
+ struct typec_switch *typec_sw;
+ struct usb_role_switch *usb_sw;
+
+ enum typec_orientation orientation;
+ enum usb_role role;
+
+ u8 usb2_port;
+ u8 usb3_port;
+
+ enum typec_orientation sbu_orientation;
+ enum typec_orientation hsl_orientation;
+};
+
+struct pmc_usb {
+ u8 num_ports;
+ struct device *dev;
+ struct intel_scu_ipc_dev *ipc;
+ struct pmc_usb_port *port;
+ struct acpi_device *iom_adev;
+ void __iomem *iom_base;
+};
+
+static void update_port_status(struct pmc_usb_port *port)
+{
+ u8 port_num;
+
+ /* SoC expects the USB Type-C port numbers to start with 0 */
+ port_num = port->usb3_port - 1;
+
+ port->iom_status = readl(port->pmc->iom_base + IOM_PORT_STATUS_OFFSET +
+ port_num * sizeof(u32));
+}
+
+static int sbu_orientation(struct pmc_usb_port *port)
+{
+ if (port->sbu_orientation)
+ return port->sbu_orientation - 1;
+
+ return port->orientation - 1;
+}
+
+static int hsl_orientation(struct pmc_usb_port *port)
+{
+ if (port->hsl_orientation)
+ return port->hsl_orientation - 1;
+
+ return port->orientation - 1;
+}
+
+static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
+{
+ u8 response[4];
+ int ret;
+
+ /*
+ * Error bit will always be 0 with the USBC command.
+ * Status can be checked from the response message if the
+ * function intel_scu_ipc_dev_command succeeds.
+ */
+ ret = intel_scu_ipc_dev_command(port->pmc->ipc, PMC_USBC_CMD, 0, msg,
+ len, response, sizeof(response));
+
+ if (ret)
+ return ret;
+
+ if (response[2] & PMC_USB_RESP_STATUS_FAILURE) {
+ if (response[2] & PMC_USB_RESP_STATUS_FATAL)
+ return -EIO;
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int
+pmc_usb_mux_dp_hpd(struct pmc_usb_port *port, struct typec_displayport_data *dp)
+{
+ u8 msg[2] = { };
+ int ret;
+
+ msg[0] = PMC_USB_DP_HPD;
+ msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+
+ /* Configure HPD first if HPD,IRQ comes together */
+ if (!IOM_PORT_HPD_ASSERTED(port->iom_status) &&
+ dp->status & DP_STATUS_IRQ_HPD &&
+ dp->status & DP_STATUS_HPD_STATE) {
+ msg[1] = PMC_USB_DP_HPD_LVL;
+ ret = pmc_usb_command(port, msg, sizeof(msg));
+ if (ret)
+ return ret;
+ }
+
+ if (dp->status & DP_STATUS_IRQ_HPD)
+ msg[1] = PMC_USB_DP_HPD_IRQ;
+
+ if (dp->status & DP_STATUS_HPD_STATE)
+ msg[1] |= PMC_USB_DP_HPD_LVL;
+
+ return pmc_usb_command(port, msg, sizeof(msg));
+}
+
+static int
+pmc_usb_mux_dp(struct pmc_usb_port *port, struct typec_mux_state *state)
+{
+ struct typec_displayport_data *data = state->data;
+ struct altmode_req req = { };
+ int ret;
+
+ if (IOM_PORT_ACTIVITY_IS(port->iom_status, DP) ||
+ IOM_PORT_ACTIVITY_IS(port->iom_status, DP_MFD)) {
+ if (IOM_PORT_HPD_ASSERTED(port->iom_status) &&
+ (!(data->status & DP_STATUS_IRQ_HPD) &&
+ data->status & DP_STATUS_HPD_STATE))
+ return 0;
+
+ return pmc_usb_mux_dp_hpd(port, state->data);
+ }
+
+ req.usage = PMC_USB_ALT_MODE;
+ req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+ req.mode_type = PMC_USB_MODE_TYPE_DP << PMC_USB_MODE_TYPE_SHIFT;
+
+ req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT;
+ req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT;
+
+ req.mode_data |= (state->mode - TYPEC_STATE_MODAL) <<
+ PMC_USB_ALTMODE_DP_MODE_SHIFT;
+
+ ret = pmc_usb_command(port, (void *)&req, sizeof(req));
+ if (ret)
+ return ret;
+
+ if (data->status & (DP_STATUS_IRQ_HPD | DP_STATUS_HPD_STATE))
+ return pmc_usb_mux_dp_hpd(port, state->data);
+
+ return 0;
+}
+
+static int
+pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state)
+{
+ struct typec_thunderbolt_data *data = state->data;
+ u8 cable_speed = TBT_CABLE_SPEED(data->cable_mode);
+ struct altmode_req req = { };
+
+ if (IOM_PORT_ACTIVITY_IS(port->iom_status, TBT) ||
+ IOM_PORT_ACTIVITY_IS(port->iom_status, ALT_MODE_TBT_USB))
+ return 0;
+
+ req.usage = PMC_USB_ALT_MODE;
+ req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+ req.mode_type = PMC_USB_MODE_TYPE_TBT << PMC_USB_MODE_TYPE_SHIFT;
+
+ req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT;
+ req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT;
+
+ if (TBT_ADAPTER(data->device_mode) == TBT_ADAPTER_TBT3)
+ req.mode_data |= PMC_USB_ALTMODE_TBT_TYPE;
+
+ if (data->cable_mode & TBT_CABLE_OPTICAL)
+ req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE;
+
+ if (data->cable_mode & TBT_CABLE_LINK_TRAINING)
+ req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK;
+
+ if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE)
+ req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+
+ req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed);
+
+ return pmc_usb_command(port, (void *)&req, sizeof(req));
+}
+
+static int
+pmc_usb_mux_usb4(struct pmc_usb_port *port, struct typec_mux_state *state)
+{
+ struct enter_usb_data *data = state->data;
+ struct altmode_req req = { };
+ u8 cable_speed;
+
+ if (IOM_PORT_ACTIVITY_IS(port->iom_status, TBT) ||
+ IOM_PORT_ACTIVITY_IS(port->iom_status, ALT_MODE_TBT_USB))
+ return 0;
+
+ req.usage = PMC_USB_ALT_MODE;
+ req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+ req.mode_type = PMC_USB_MODE_TYPE_TBT << PMC_USB_MODE_TYPE_SHIFT;
+
+ /* USB4 Mode */
+ req.mode_data = PMC_USB_ALTMODE_FORCE_LSR;
+
+ if (data->active_link_training)
+ req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK;
+
+ req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT;
+ req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT;
+
+ switch ((data->eudo & EUDO_CABLE_TYPE_MASK) >> EUDO_CABLE_TYPE_SHIFT) {
+ case EUDO_CABLE_TYPE_PASSIVE:
+ break;
+ case EUDO_CABLE_TYPE_OPTICAL:
+ req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE;
+ fallthrough;
+ default:
+ req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+ break;
+ }
+
+ cable_speed = (data->eudo & EUDO_CABLE_SPEED_MASK) >> EUDO_CABLE_SPEED_SHIFT;
+ req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed);
+
+ return pmc_usb_command(port, (void *)&req, sizeof(req));
+}
+
+static int pmc_usb_mux_safe_state(struct pmc_usb_port *port)
+{
+ u8 msg;
+
+ if (IOM_PORT_ACTIVITY_IS(port->iom_status, SAFE_MODE))
+ return 0;
+
+ msg = PMC_USB_SAFE_MODE;
+ msg |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+
+ return pmc_usb_command(port, &msg, sizeof(msg));
+}
+
+static int pmc_usb_disconnect(struct pmc_usb_port *port)
+{
+ struct typec_displayport_data data = { };
+ u8 msg[2];
+
+ if (!(port->iom_status & IOM_PORT_STATUS_CONNECTED))
+ return 0;
+
+ /* Clear DisplayPort HPD if it's still asserted. */
+ if (IOM_PORT_HPD_ASSERTED(port->iom_status))
+ pmc_usb_mux_dp_hpd(port, &data);
+
+ msg[0] = PMC_USB_DISCONNECT;
+ msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+
+ msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT;
+
+ return pmc_usb_command(port, msg, sizeof(msg));
+}
+
+static int pmc_usb_connect(struct pmc_usb_port *port, enum usb_role role)
+{
+ u8 ufp = role == USB_ROLE_DEVICE ? 1 : 0;
+ u8 msg[2];
+ int ret;
+
+ if (port->orientation == TYPEC_ORIENTATION_NONE)
+ return -EINVAL;
+
+ if (port->iom_status & IOM_PORT_STATUS_CONNECTED) {
+ if (port->role == role || port->role == USB_ROLE_NONE)
+ return 0;
+
+ /* Role swap */
+ ret = pmc_usb_disconnect(port);
+ if (ret)
+ return ret;
+ }
+
+ msg[0] = PMC_USB_CONNECT;
+ msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+
+ msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT;
+ msg[1] |= ufp << PMC_USB_MSG_UFP_SHIFT;
+ msg[1] |= hsl_orientation(port) << PMC_USB_MSG_ORI_HSL_SHIFT;
+ msg[1] |= sbu_orientation(port) << PMC_USB_MSG_ORI_AUX_SHIFT;
+
+ return pmc_usb_command(port, msg, sizeof(msg));
+}
+
+static int
+pmc_usb_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
+{
+ struct pmc_usb_port *port = typec_mux_get_drvdata(mux);
+
+ update_port_status(port);
+
+ if (port->orientation == TYPEC_ORIENTATION_NONE || port->role == USB_ROLE_NONE)
+ return 0;
+
+ if (state->mode == TYPEC_STATE_SAFE)
+ return pmc_usb_mux_safe_state(port);
+ if (state->mode == TYPEC_STATE_USB)
+ return pmc_usb_connect(port, port->role);
+
+ if (state->alt) {
+ switch (state->alt->svid) {
+ case USB_TYPEC_TBT_SID:
+ return pmc_usb_mux_tbt(port, state);
+ case USB_TYPEC_DP_SID:
+ return pmc_usb_mux_dp(port, state);
+ }
+ } else {
+ switch (state->mode) {
+ case TYPEC_MODE_USB2:
+ /* REVISIT: Try with usb3_port set to 0? */
+ break;
+ case TYPEC_MODE_USB3:
+ return pmc_usb_connect(port, port->role);
+ case TYPEC_MODE_USB4:
+ return pmc_usb_mux_usb4(port, state);
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int pmc_usb_set_orientation(struct typec_switch *sw,
+ enum typec_orientation orientation)
+{
+ struct pmc_usb_port *port = typec_switch_get_drvdata(sw);
+
+ update_port_status(port);
+
+ port->orientation = orientation;
+
+ return 0;
+}
+
+static int pmc_usb_set_role(struct usb_role_switch *sw, enum usb_role role)
+{
+ struct pmc_usb_port *port = usb_role_switch_get_drvdata(sw);
+ int ret;
+
+ update_port_status(port);
+
+ if (role == USB_ROLE_NONE)
+ ret = pmc_usb_disconnect(port);
+ else
+ ret = pmc_usb_connect(port, role);
+
+ port->role = role;
+
+ return ret;
+}
+
+static int pmc_usb_register_port(struct pmc_usb *pmc, int index,
+ struct fwnode_handle *fwnode)
+{
+ struct pmc_usb_port *port = &pmc->port[index];
+ struct usb_role_switch_desc desc = { };
+ struct typec_switch_desc sw_desc = { };
+ struct typec_mux_desc mux_desc = { };
+ const char *str;
+ int ret;
+
+ ret = fwnode_property_read_u8(fwnode, "usb2-port-number", &port->usb2_port);
+ if (ret)
+ return ret;
+
+ ret = fwnode_property_read_u8(fwnode, "usb3-port-number", &port->usb3_port);
+ if (ret)
+ return ret;
+
+ ret = fwnode_property_read_string(fwnode, "sbu-orientation", &str);
+ if (!ret)
+ port->sbu_orientation = typec_find_orientation(str);
+
+ ret = fwnode_property_read_string(fwnode, "hsl-orientation", &str);
+ if (!ret)
+ port->hsl_orientation = typec_find_orientation(str);
+
+ port->num = index;
+ port->pmc = pmc;
+
+ sw_desc.fwnode = fwnode;
+ sw_desc.drvdata = port;
+ sw_desc.name = fwnode_get_name(fwnode);
+ sw_desc.set = pmc_usb_set_orientation;
+
+ port->typec_sw = typec_switch_register(pmc->dev, &sw_desc);
+ if (IS_ERR(port->typec_sw))
+ return PTR_ERR(port->typec_sw);
+
+ mux_desc.fwnode = fwnode;
+ mux_desc.drvdata = port;
+ mux_desc.name = fwnode_get_name(fwnode);
+ mux_desc.set = pmc_usb_mux_set;
+
+ port->typec_mux = typec_mux_register(pmc->dev, &mux_desc);
+ if (IS_ERR(port->typec_mux)) {
+ ret = PTR_ERR(port->typec_mux);
+ goto err_unregister_switch;
+ }
+
+ desc.fwnode = fwnode;
+ desc.driver_data = port;
+ desc.name = fwnode_get_name(fwnode);
+ desc.set = pmc_usb_set_role;
+
+ port->usb_sw = usb_role_switch_register(pmc->dev, &desc);
+ if (IS_ERR(port->usb_sw)) {
+ ret = PTR_ERR(port->usb_sw);
+ goto err_unregister_mux;
+ }
+
+ return 0;
+
+err_unregister_mux:
+ typec_mux_unregister(port->typec_mux);
+
+err_unregister_switch:
+ typec_switch_unregister(port->typec_sw);
+
+ return ret;
+}
+
+static int is_memory(struct acpi_resource *res, void *data)
+{
+ struct resource r;
+
+ return !acpi_dev_resource_memory(res, &r);
+}
+
+static int pmc_usb_probe_iom(struct pmc_usb *pmc)
+{
+ struct list_head resource_list;
+ struct resource_entry *rentry;
+ struct acpi_device *adev;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev("INTC1072", NULL, -1);
+ if (!adev)
+ return -ENODEV;
+
+ INIT_LIST_HEAD(&resource_list);
+ ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL);
+ if (ret < 0)
+ return ret;
+
+ rentry = list_first_entry_or_null(&resource_list, struct resource_entry, node);
+ if (rentry)
+ pmc->iom_base = devm_ioremap_resource(pmc->dev, rentry->res);
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ if (!pmc->iom_base) {
+ put_device(&adev->dev);
+ return -ENOMEM;
+ }
+
+ if (IS_ERR(pmc->iom_base)) {
+ put_device(&adev->dev);
+ return PTR_ERR(pmc->iom_base);
+ }
+
+ pmc->iom_adev = adev;
+
+ return 0;
+}
+
+static int pmc_usb_probe(struct platform_device *pdev)
+{
+ struct fwnode_handle *fwnode = NULL;
+ struct pmc_usb *pmc;
+ int i = 0;
+ int ret;
+
+ pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
+ if (!pmc)
+ return -ENOMEM;
+
+ device_for_each_child_node(&pdev->dev, fwnode)
+ pmc->num_ports++;
+
+ /* The IOM microcontroller has a limitation of max 4 ports. */
+ if (pmc->num_ports > 4) {
+ dev_err(&pdev->dev, "driver limited to 4 ports\n");
+ return -ERANGE;
+ }
+
+ pmc->port = devm_kcalloc(&pdev->dev, pmc->num_ports,
+ sizeof(struct pmc_usb_port), GFP_KERNEL);
+ if (!pmc->port)
+ return -ENOMEM;
+
+ pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev);
+ if (!pmc->ipc)
+ return -ENODEV;
+
+ pmc->dev = &pdev->dev;
+
+ ret = pmc_usb_probe_iom(pmc);
+ if (ret)
+ return ret;
+
+ /*
+ * For every physical USB connector (USB2 and USB3 combo) there is a
+ * child ACPI device node under the PMC mux ACPI device object.
+ */
+ for (i = 0; i < pmc->num_ports; i++) {
+ fwnode = device_get_next_child_node(pmc->dev, fwnode);
+ if (!fwnode)
+ break;
+
+ ret = pmc_usb_register_port(pmc, i, fwnode);
+ if (ret) {
+ fwnode_handle_put(fwnode);
+ goto err_remove_ports;
+ }
+ }
+
+ platform_set_drvdata(pdev, pmc);
+
+ return 0;
+
+err_remove_ports:
+ for (i = 0; i < pmc->num_ports; i++) {
+ typec_switch_unregister(pmc->port[i].typec_sw);
+ typec_mux_unregister(pmc->port[i].typec_mux);
+ usb_role_switch_unregister(pmc->port[i].usb_sw);
+ }
+
+ put_device(&pmc->iom_adev->dev);
+
+ return ret;
+}
+
+static int pmc_usb_remove(struct platform_device *pdev)
+{
+ struct pmc_usb *pmc = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < pmc->num_ports; i++) {
+ typec_switch_unregister(pmc->port[i].typec_sw);
+ typec_mux_unregister(pmc->port[i].typec_mux);
+ usb_role_switch_unregister(pmc->port[i].usb_sw);
+ }
+
+ put_device(&pmc->iom_adev->dev);
+
+ return 0;
+}
+
+static const struct acpi_device_id pmc_usb_acpi_ids[] = {
+ { "INTC105C", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, pmc_usb_acpi_ids);
+
+static struct platform_driver pmc_usb_driver = {
+ .driver = {
+ .name = "intel_pmc_usb",
+ .acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids),
+ },
+ .probe = pmc_usb_probe,
+ .remove = pmc_usb_remove,
+};
+
+module_platform_driver(pmc_usb_driver);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel PMC USB mux control");
diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c
index 5585b10..7afe275 100644
--- a/drivers/usb/typec/mux/pi3usb30532.c
+++ b/drivers/usb/typec/mux/pi3usb30532.c
@@ -73,7 +73,8 @@
return ret;
}
-static int pi3usb30532_mux_set(struct typec_mux *mux, int state)
+static int
+pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
{
struct pi3usb30532 *pi = typec_mux_get_drvdata(mux);
u8 new_conf;
@@ -82,7 +83,7 @@
mutex_lock(&pi->lock);
new_conf = pi->conf;
- switch (state) {
+ switch (state->mode) {
case TYPEC_STATE_SAFE:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_OPEN;
@@ -113,8 +114,8 @@
static int pi3usb30532_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
- struct typec_switch_desc sw_desc;
- struct typec_mux_desc mux_desc;
+ struct typec_switch_desc sw_desc = { };
+ struct typec_mux_desc mux_desc = { };
struct pi3usb30532 *pi;
int ret;
diff --git a/drivers/usb/typec/qcom-pmic-typec.c b/drivers/usb/typec/qcom-pmic-typec.c
new file mode 100644
index 0000000..a0454a8
--- /dev/null
+++ b/drivers/usb/typec/qcom-pmic-typec.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec_mux.h>
+
+#define TYPEC_MISC_STATUS 0xb
+#define CC_ATTACHED BIT(0)
+#define CC_ORIENTATION BIT(1)
+#define SNK_SRC_MODE BIT(6)
+#define TYPEC_MODE_CFG 0x44
+#define TYPEC_DISABLE_CMD BIT(0)
+#define EN_SNK_ONLY BIT(1)
+#define EN_SRC_ONLY BIT(2)
+#define TYPEC_VCONN_CONTROL 0x46
+#define VCONN_EN_SRC BIT(0)
+#define VCONN_EN_VAL BIT(1)
+#define TYPEC_EXIT_STATE_CFG 0x50
+#define SEL_SRC_UPPER_REF BIT(2)
+#define TYPEC_INTR_EN_CFG_1 0x5e
+#define TYPEC_INTR_EN_CFG_1_MASK GENMASK(7, 0)
+
+struct qcom_pmic_typec {
+ struct device *dev;
+ struct regmap *regmap;
+ u32 base;
+
+ struct typec_port *port;
+ struct usb_role_switch *role_sw;
+
+ struct regulator *vbus_reg;
+ bool vbus_enabled;
+};
+
+static void qcom_pmic_typec_enable_vbus_regulator(struct qcom_pmic_typec
+ *qcom_usb, bool enable)
+{
+ int ret;
+
+ if (enable == qcom_usb->vbus_enabled)
+ return;
+
+ if (enable) {
+ ret = regulator_enable(qcom_usb->vbus_reg);
+ if (ret)
+ return;
+ } else {
+ ret = regulator_disable(qcom_usb->vbus_reg);
+ if (ret)
+ return;
+ }
+ qcom_usb->vbus_enabled = enable;
+}
+
+static void qcom_pmic_typec_check_connection(struct qcom_pmic_typec *qcom_usb)
+{
+ enum typec_orientation orientation;
+ enum usb_role role;
+ unsigned int stat;
+ bool enable_vbus;
+
+ regmap_read(qcom_usb->regmap, qcom_usb->base + TYPEC_MISC_STATUS,
+ &stat);
+
+ if (stat & CC_ATTACHED) {
+ orientation = (stat & CC_ORIENTATION) ?
+ TYPEC_ORIENTATION_REVERSE :
+ TYPEC_ORIENTATION_NORMAL;
+ typec_set_orientation(qcom_usb->port, orientation);
+
+ role = (stat & SNK_SRC_MODE) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+ if (role == USB_ROLE_HOST)
+ enable_vbus = true;
+ else
+ enable_vbus = false;
+ } else {
+ role = USB_ROLE_NONE;
+ enable_vbus = false;
+ }
+
+ qcom_pmic_typec_enable_vbus_regulator(qcom_usb, enable_vbus);
+ usb_role_switch_set_role(qcom_usb->role_sw, role);
+}
+
+static irqreturn_t qcom_pmic_typec_interrupt(int irq, void *_qcom_usb)
+{
+ struct qcom_pmic_typec *qcom_usb = _qcom_usb;
+
+ qcom_pmic_typec_check_connection(qcom_usb);
+ return IRQ_HANDLED;
+}
+
+static void qcom_pmic_typec_typec_hw_init(struct qcom_pmic_typec *qcom_usb,
+ enum typec_port_type type)
+{
+ u8 mode = 0;
+
+ regmap_update_bits(qcom_usb->regmap,
+ qcom_usb->base + TYPEC_INTR_EN_CFG_1,
+ TYPEC_INTR_EN_CFG_1_MASK, 0);
+
+ if (type == TYPEC_PORT_SRC)
+ mode = EN_SRC_ONLY;
+ else if (type == TYPEC_PORT_SNK)
+ mode = EN_SNK_ONLY;
+
+ regmap_update_bits(qcom_usb->regmap, qcom_usb->base + TYPEC_MODE_CFG,
+ EN_SNK_ONLY | EN_SRC_ONLY, mode);
+
+ regmap_update_bits(qcom_usb->regmap,
+ qcom_usb->base + TYPEC_VCONN_CONTROL,
+ VCONN_EN_SRC | VCONN_EN_VAL, VCONN_EN_SRC);
+ regmap_update_bits(qcom_usb->regmap,
+ qcom_usb->base + TYPEC_EXIT_STATE_CFG,
+ SEL_SRC_UPPER_REF, SEL_SRC_UPPER_REF);
+}
+
+static int qcom_pmic_typec_probe(struct platform_device *pdev)
+{
+ struct qcom_pmic_typec *qcom_usb;
+ struct device *dev = &pdev->dev;
+ struct fwnode_handle *fwnode;
+ struct typec_capability cap;
+ const char *buf;
+ int ret, irq, role;
+ u32 reg;
+
+ ret = device_property_read_u32(dev, "reg", ®);
+ if (ret < 0) {
+ dev_err(dev, "missing base address\n");
+ return ret;
+ }
+
+ qcom_usb = devm_kzalloc(dev, sizeof(*qcom_usb), GFP_KERNEL);
+ if (!qcom_usb)
+ return -ENOMEM;
+
+ qcom_usb->dev = dev;
+ qcom_usb->base = reg;
+
+ qcom_usb->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!qcom_usb->regmap) {
+ dev_err(dev, "Failed to get regmap\n");
+ return -EINVAL;
+ }
+
+ qcom_usb->vbus_reg = devm_regulator_get(qcom_usb->dev, "usb_vbus");
+ if (IS_ERR(qcom_usb->vbus_reg))
+ return PTR_ERR(qcom_usb->vbus_reg);
+
+ fwnode = device_get_named_child_node(dev, "connector");
+ if (!fwnode)
+ return -EINVAL;
+
+ ret = fwnode_property_read_string(fwnode, "power-role", &buf);
+ if (!ret) {
+ role = typec_find_port_power_role(buf);
+ if (role < 0)
+ role = TYPEC_PORT_SNK;
+ } else {
+ role = TYPEC_PORT_SNK;
+ }
+ cap.type = role;
+
+ ret = fwnode_property_read_string(fwnode, "data-role", &buf);
+ if (!ret) {
+ role = typec_find_port_data_role(buf);
+ if (role < 0)
+ role = TYPEC_PORT_UFP;
+ } else {
+ role = TYPEC_PORT_UFP;
+ }
+ cap.data = role;
+
+ cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
+ cap.fwnode = fwnode;
+ qcom_usb->port = typec_register_port(dev, &cap);
+ if (IS_ERR(qcom_usb->port)) {
+ ret = PTR_ERR(qcom_usb->port);
+ dev_err(dev, "Failed to register type c port %d\n", ret);
+ goto err_put_node;
+ }
+ fwnode_handle_put(fwnode);
+
+ qcom_usb->role_sw = fwnode_usb_role_switch_get(dev_fwnode(qcom_usb->dev));
+ if (IS_ERR(qcom_usb->role_sw)) {
+ if (PTR_ERR(qcom_usb->role_sw) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get role switch\n");
+ ret = PTR_ERR(qcom_usb->role_sw);
+ goto err_typec_port;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ goto err_usb_role_sw;
+
+ ret = devm_request_threaded_irq(qcom_usb->dev, irq, NULL,
+ qcom_pmic_typec_interrupt, IRQF_ONESHOT,
+ "qcom-pmic-typec", qcom_usb);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ goto err_usb_role_sw;
+ }
+
+ platform_set_drvdata(pdev, qcom_usb);
+ qcom_pmic_typec_typec_hw_init(qcom_usb, cap.type);
+ qcom_pmic_typec_check_connection(qcom_usb);
+
+ return 0;
+
+err_usb_role_sw:
+ usb_role_switch_put(qcom_usb->role_sw);
+err_typec_port:
+ typec_unregister_port(qcom_usb->port);
+err_put_node:
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+static int qcom_pmic_typec_remove(struct platform_device *pdev)
+{
+ struct qcom_pmic_typec *qcom_usb = platform_get_drvdata(pdev);
+
+ usb_role_switch_set_role(qcom_usb->role_sw, USB_ROLE_NONE);
+ qcom_pmic_typec_enable_vbus_regulator(qcom_usb, 0);
+
+ typec_unregister_port(qcom_usb->port);
+ usb_role_switch_put(qcom_usb->role_sw);
+
+ return 0;
+}
+
+static const struct of_device_id qcom_pmic_typec_table[] = {
+ { .compatible = "qcom,pm8150b-usb-typec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table);
+
+static struct platform_driver qcom_pmic_typec = {
+ .driver = {
+ .name = "qcom,pmic-typec",
+ .of_match_table = qcom_pmic_typec_table,
+ },
+ .probe = qcom_pmic_typec_probe,
+ .remove = qcom_pmic_typec_remove,
+};
+module_platform_driver(qcom_pmic_typec);
+
+MODULE_DESCRIPTION("QCOM PMIC USB type C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c
new file mode 100644
index 0000000..3d3848e
--- /dev/null
+++ b/drivers/usb/typec/stusb160x.c
@@ -0,0 +1,876 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STUSB160x Type-C controller family driver
+ *
+ * Copyright (C) 2020, STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec.h>
+
+#define STUSB160X_ALERT_STATUS 0x0B /* RC */
+#define STUSB160X_ALERT_STATUS_MASK_CTRL 0x0C /* RW */
+#define STUSB160X_CC_CONNECTION_STATUS_TRANS 0x0D /* RC */
+#define STUSB160X_CC_CONNECTION_STATUS 0x0E /* RO */
+#define STUSB160X_MONITORING_STATUS_TRANS 0x0F /* RC */
+#define STUSB160X_MONITORING_STATUS 0x10 /* RO */
+#define STUSB160X_CC_OPERATION_STATUS 0x11 /* RO */
+#define STUSB160X_HW_FAULT_STATUS_TRANS 0x12 /* RC */
+#define STUSB160X_HW_FAULT_STATUS 0x13 /* RO */
+#define STUSB160X_CC_CAPABILITY_CTRL 0x18 /* RW */
+#define STUSB160X_CC_VCONN_SWITCH_CTRL 0x1E /* RW */
+#define STUSB160X_VCONN_MONITORING_CTRL 0x20 /* RW */
+#define STUSB160X_VBUS_MONITORING_RANGE_CTRL 0x22 /* RW */
+#define STUSB160X_RESET_CTRL 0x23 /* RW */
+#define STUSB160X_VBUS_DISCHARGE_TIME_CTRL 0x25 /* RW */
+#define STUSB160X_VBUS_DISCHARGE_STATUS 0x26 /* RO */
+#define STUSB160X_VBUS_ENABLE_STATUS 0x27 /* RO */
+#define STUSB160X_CC_POWER_MODE_CTRL 0x28 /* RW */
+#define STUSB160X_VBUS_MONITORING_CTRL 0x2E /* RW */
+#define STUSB1600_REG_MAX 0x2F /* RO - Reserved */
+
+/* STUSB160X_ALERT_STATUS/STUSB160X_ALERT_STATUS_MASK_CTRL bitfields */
+#define STUSB160X_HW_FAULT BIT(4)
+#define STUSB160X_MONITORING BIT(5)
+#define STUSB160X_CC_CONNECTION BIT(6)
+#define STUSB160X_ALL_ALERTS GENMASK(6, 4)
+
+/* STUSB160X_CC_CONNECTION_STATUS_TRANS bitfields */
+#define STUSB160X_CC_ATTACH_TRANS BIT(0)
+
+/* STUSB160X_CC_CONNECTION_STATUS bitfields */
+#define STUSB160X_CC_ATTACH BIT(0)
+#define STUSB160X_CC_VCONN_SUPPLY BIT(1)
+#define STUSB160X_CC_DATA_ROLE(s) (!!((s) & BIT(2)))
+#define STUSB160X_CC_POWER_ROLE(s) (!!((s) & BIT(3)))
+#define STUSB160X_CC_ATTACHED_MODE GENMASK(7, 5)
+
+/* STUSB160X_MONITORING_STATUS_TRANS bitfields */
+#define STUSB160X_VCONN_PRESENCE_TRANS BIT(0)
+#define STUSB160X_VBUS_PRESENCE_TRANS BIT(1)
+#define STUSB160X_VBUS_VSAFE0V_TRANS BIT(2)
+#define STUSB160X_VBUS_VALID_TRANS BIT(3)
+
+/* STUSB160X_MONITORING_STATUS bitfields */
+#define STUSB160X_VCONN_PRESENCE BIT(0)
+#define STUSB160X_VBUS_PRESENCE BIT(1)
+#define STUSB160X_VBUS_VSAFE0V BIT(2)
+#define STUSB160X_VBUS_VALID BIT(3)
+
+/* STUSB160X_CC_OPERATION_STATUS bitfields */
+#define STUSB160X_TYPEC_FSM_STATE GENMASK(4, 0)
+#define STUSB160X_SINK_POWER_STATE GENMASK(6, 5)
+#define STUSB160X_CC_ATTACHED BIT(7)
+
+/* STUSB160X_HW_FAULT_STATUS_TRANS bitfields */
+#define STUSB160X_VCONN_SW_OVP_FAULT_TRANS BIT(0)
+#define STUSB160X_VCONN_SW_OCP_FAULT_TRANS BIT(1)
+#define STUSB160X_VCONN_SW_RVP_FAULT_TRANS BIT(2)
+#define STUSB160X_VPU_VALID_TRANS BIT(4)
+#define STUSB160X_VPU_OVP_FAULT_TRANS BIT(5)
+#define STUSB160X_THERMAL_FAULT BIT(7)
+
+/* STUSB160X_HW_FAULT_STATUS bitfields */
+#define STUSB160X_VCONN_SW_OVP_FAULT_CC2 BIT(0)
+#define STUSB160X_VCONN_SW_OVP_FAULT_CC1 BIT(1)
+#define STUSB160X_VCONN_SW_OCP_FAULT_CC2 BIT(2)
+#define STUSB160X_VCONN_SW_OCP_FAULT_CC1 BIT(3)
+#define STUSB160X_VCONN_SW_RVP_FAULT_CC2 BIT(4)
+#define STUSB160X_VCONN_SW_RVP_FAULT_CC1 BIT(5)
+#define STUSB160X_VPU_VALID BIT(6)
+#define STUSB160X_VPU_OVP_FAULT BIT(7)
+
+/* STUSB160X_CC_CAPABILITY_CTRL bitfields */
+#define STUSB160X_CC_VCONN_SUPPLY_EN BIT(0)
+#define STUSB160X_CC_VCONN_DISCHARGE_EN BIT(4)
+#define STUSB160X_CC_CURRENT_ADVERTISED GENMASK(7, 6)
+
+/* STUSB160X_VCONN_SWITCH_CTRL bitfields */
+#define STUSB160X_CC_VCONN_SWITCH_ILIM GENMASK(3, 0)
+
+/* STUSB160X_VCONN_MONITORING_CTRL bitfields */
+#define STUSB160X_VCONN_UVLO_THRESHOLD BIT(6)
+#define STUSB160X_VCONN_MONITORING_EN BIT(7)
+
+/* STUSB160X_VBUS_MONITORING_RANGE_CTRL bitfields */
+#define STUSB160X_SHIFT_LOW_VBUS_LIMIT GENMASK(3, 0)
+#define STUSB160X_SHIFT_HIGH_VBUS_LIMIT GENMASK(7, 4)
+
+/* STUSB160X_RESET_CTRL bitfields */
+#define STUSB160X_SW_RESET_EN BIT(0)
+
+/* STUSB160X_VBUS_DISCHARGE_TIME_CTRL bitfields */
+#define STUSBXX02_VBUS_DISCHARGE_TIME_TO_PDO GENMASK(3, 0)
+#define STUSB160X_VBUS_DISCHARGE_TIME_TO_0V GENMASK(7, 4)
+
+/* STUSB160X_VBUS_DISCHARGE_STATUS bitfields */
+#define STUSB160X_VBUS_DISCHARGE_EN BIT(7)
+
+/* STUSB160X_VBUS_ENABLE_STATUS bitfields */
+#define STUSB160X_VBUS_SOURCE_EN BIT(0)
+#define STUSB160X_VBUS_SINK_EN BIT(1)
+
+/* STUSB160X_CC_POWER_MODE_CTRL bitfields */
+#define STUSB160X_CC_POWER_MODE GENMASK(2, 0)
+
+/* STUSB160X_VBUS_MONITORING_CTRL bitfields */
+#define STUSB160X_VDD_UVLO_DISABLE BIT(0)
+#define STUSB160X_VBUS_VSAFE0V_THRESHOLD GENMASK(2, 1)
+#define STUSB160X_VBUS_RANGE_DISABLE BIT(4)
+#define STUSB160X_VDD_OVLO_DISABLE BIT(6)
+
+enum stusb160x_pwr_mode {
+ SOURCE_WITH_ACCESSORY,
+ SINK_WITH_ACCESSORY,
+ SINK_WITHOUT_ACCESSORY,
+ DUAL_WITH_ACCESSORY,
+ DUAL_WITH_ACCESSORY_AND_TRY_SRC,
+ DUAL_WITH_ACCESSORY_AND_TRY_SNK,
+};
+
+enum stusb160x_attached_mode {
+ NO_DEVICE_ATTACHED,
+ SINK_ATTACHED,
+ SOURCE_ATTACHED,
+ DEBUG_ACCESSORY_ATTACHED,
+ AUDIO_ACCESSORY_ATTACHED,
+};
+
+struct stusb160x {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator *vdd_supply;
+ struct regulator *vsys_supply;
+ struct regulator *vconn_supply;
+ struct regulator *main_supply;
+
+ struct typec_port *port;
+ struct typec_capability capability;
+ struct typec_partner *partner;
+
+ enum typec_port_type port_type;
+ enum typec_pwr_opmode pwr_opmode;
+ bool vbus_on;
+
+ struct usb_role_switch *role_sw;
+};
+
+static bool stusb160x_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STUSB160X_ALERT_STATUS_MASK_CTRL:
+ case STUSB160X_CC_CAPABILITY_CTRL:
+ case STUSB160X_CC_VCONN_SWITCH_CTRL:
+ case STUSB160X_VCONN_MONITORING_CTRL:
+ case STUSB160X_VBUS_MONITORING_RANGE_CTRL:
+ case STUSB160X_RESET_CTRL:
+ case STUSB160X_VBUS_DISCHARGE_TIME_CTRL:
+ case STUSB160X_CC_POWER_MODE_CTRL:
+ case STUSB160X_VBUS_MONITORING_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stusb160x_reg_readable(struct device *dev, unsigned int reg)
+{
+ if (reg <= 0x0A ||
+ (reg >= 0x14 && reg <= 0x17) ||
+ (reg >= 0x19 && reg <= 0x1D) ||
+ (reg >= 0x29 && reg <= 0x2D) ||
+ (reg == 0x1F || reg == 0x21 || reg == 0x24 || reg == 0x2F))
+ return false;
+ else
+ return true;
+}
+
+static bool stusb160x_reg_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STUSB160X_ALERT_STATUS:
+ case STUSB160X_CC_CONNECTION_STATUS_TRANS:
+ case STUSB160X_CC_CONNECTION_STATUS:
+ case STUSB160X_MONITORING_STATUS_TRANS:
+ case STUSB160X_MONITORING_STATUS:
+ case STUSB160X_CC_OPERATION_STATUS:
+ case STUSB160X_HW_FAULT_STATUS_TRANS:
+ case STUSB160X_HW_FAULT_STATUS:
+ case STUSB160X_VBUS_DISCHARGE_STATUS:
+ case STUSB160X_VBUS_ENABLE_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stusb160x_reg_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STUSB160X_ALERT_STATUS:
+ case STUSB160X_CC_CONNECTION_STATUS_TRANS:
+ case STUSB160X_MONITORING_STATUS_TRANS:
+ case STUSB160X_HW_FAULT_STATUS_TRANS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config stusb1600_regmap_config = {
+ .reg_bits = 8,
+ .reg_stride = 1,
+ .val_bits = 8,
+ .max_register = STUSB1600_REG_MAX,
+ .writeable_reg = stusb160x_reg_writeable,
+ .readable_reg = stusb160x_reg_readable,
+ .volatile_reg = stusb160x_reg_volatile,
+ .precious_reg = stusb160x_reg_precious,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static bool stusb160x_get_vconn(struct stusb160x *chip)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(chip->regmap, STUSB160X_CC_CAPABILITY_CTRL, &val);
+ if (ret) {
+ dev_err(chip->dev, "Unable to get Vconn status: %d\n", ret);
+ return false;
+ }
+
+ return !!FIELD_GET(STUSB160X_CC_VCONN_SUPPLY_EN, val);
+}
+
+static int stusb160x_set_vconn(struct stusb160x *chip, bool on)
+{
+ int ret;
+
+ /* Manage VCONN input supply */
+ if (chip->vconn_supply) {
+ if (on) {
+ ret = regulator_enable(chip->vconn_supply);
+ if (ret) {
+ dev_err(chip->dev,
+ "failed to enable vconn supply: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ regulator_disable(chip->vconn_supply);
+ }
+ }
+
+ /* Manage VCONN monitoring and power path */
+ ret = regmap_update_bits(chip->regmap, STUSB160X_VCONN_MONITORING_CTRL,
+ STUSB160X_VCONN_MONITORING_EN,
+ on ? STUSB160X_VCONN_MONITORING_EN : 0);
+ if (ret)
+ goto vconn_reg_disable;
+
+ return 0;
+
+vconn_reg_disable:
+ if (chip->vconn_supply && on)
+ regulator_disable(chip->vconn_supply);
+
+ return ret;
+}
+
+static enum typec_pwr_opmode stusb160x_get_pwr_opmode(struct stusb160x *chip)
+{
+ u32 val;
+ int ret;
+
+ ret = regmap_read(chip->regmap, STUSB160X_CC_CAPABILITY_CTRL, &val);
+ if (ret) {
+ dev_err(chip->dev, "Unable to get pwr opmode: %d\n", ret);
+ return TYPEC_PWR_MODE_USB;
+ }
+
+ return FIELD_GET(STUSB160X_CC_CURRENT_ADVERTISED, val);
+}
+
+static enum typec_accessory stusb160x_get_accessory(u32 status)
+{
+ enum stusb160x_attached_mode mode;
+
+ mode = FIELD_GET(STUSB160X_CC_ATTACHED_MODE, status);
+
+ switch (mode) {
+ case DEBUG_ACCESSORY_ATTACHED:
+ return TYPEC_ACCESSORY_DEBUG;
+ case AUDIO_ACCESSORY_ATTACHED:
+ return TYPEC_ACCESSORY_AUDIO;
+ default:
+ return TYPEC_ACCESSORY_NONE;
+ }
+}
+
+static enum typec_role stusb160x_get_vconn_role(u32 status)
+{
+ if (FIELD_GET(STUSB160X_CC_VCONN_SUPPLY, status))
+ return TYPEC_SOURCE;
+
+ return TYPEC_SINK;
+}
+
+static void stusb160x_set_data_role(struct stusb160x *chip,
+ enum typec_data_role data_role,
+ bool attached)
+{
+ enum usb_role usb_role = USB_ROLE_NONE;
+
+ if (attached) {
+ if (data_role == TYPEC_HOST)
+ usb_role = USB_ROLE_HOST;
+ else
+ usb_role = USB_ROLE_DEVICE;
+ }
+
+ usb_role_switch_set_role(chip->role_sw, usb_role);
+ typec_set_data_role(chip->port, data_role);
+}
+
+static int stusb160x_attach(struct stusb160x *chip, u32 status)
+{
+ struct typec_partner_desc desc;
+ int ret;
+
+ if ((STUSB160X_CC_POWER_ROLE(status) == TYPEC_SOURCE) &&
+ chip->vdd_supply) {
+ ret = regulator_enable(chip->vdd_supply);
+ if (ret) {
+ dev_err(chip->dev,
+ "Failed to enable Vbus supply: %d\n", ret);
+ return ret;
+ }
+ chip->vbus_on = true;
+ }
+
+ desc.usb_pd = false;
+ desc.accessory = stusb160x_get_accessory(status);
+ desc.identity = NULL;
+
+ chip->partner = typec_register_partner(chip->port, &desc);
+ if (IS_ERR(chip->partner)) {
+ ret = PTR_ERR(chip->partner);
+ goto vbus_disable;
+ }
+
+ typec_set_pwr_role(chip->port, STUSB160X_CC_POWER_ROLE(status));
+ typec_set_pwr_opmode(chip->port, stusb160x_get_pwr_opmode(chip));
+ typec_set_vconn_role(chip->port, stusb160x_get_vconn_role(status));
+ stusb160x_set_data_role(chip, STUSB160X_CC_DATA_ROLE(status), true);
+
+ return 0;
+
+vbus_disable:
+ if (chip->vbus_on) {
+ regulator_disable(chip->vdd_supply);
+ chip->vbus_on = false;
+ }
+
+ return ret;
+}
+
+static void stusb160x_detach(struct stusb160x *chip, u32 status)
+{
+ typec_unregister_partner(chip->partner);
+ chip->partner = NULL;
+
+ typec_set_pwr_role(chip->port, STUSB160X_CC_POWER_ROLE(status));
+ typec_set_pwr_opmode(chip->port, TYPEC_PWR_MODE_USB);
+ typec_set_vconn_role(chip->port, stusb160x_get_vconn_role(status));
+ stusb160x_set_data_role(chip, STUSB160X_CC_DATA_ROLE(status), false);
+
+ if (chip->vbus_on) {
+ regulator_disable(chip->vdd_supply);
+ chip->vbus_on = false;
+ }
+}
+
+static irqreturn_t stusb160x_irq_handler(int irq, void *data)
+{
+ struct stusb160x *chip = data;
+ u32 pending, trans, status;
+ int ret;
+
+ ret = regmap_read(chip->regmap, STUSB160X_ALERT_STATUS, &pending);
+ if (ret)
+ goto err;
+
+ if (pending & STUSB160X_CC_CONNECTION) {
+ ret = regmap_read(chip->regmap,
+ STUSB160X_CC_CONNECTION_STATUS_TRANS, &trans);
+ if (ret)
+ goto err;
+ ret = regmap_read(chip->regmap,
+ STUSB160X_CC_CONNECTION_STATUS, &status);
+ if (ret)
+ goto err;
+
+ if (trans & STUSB160X_CC_ATTACH_TRANS) {
+ if (status & STUSB160X_CC_ATTACH) {
+ ret = stusb160x_attach(chip, status);
+ if (ret)
+ goto err;
+ } else {
+ stusb160x_detach(chip, status);
+ }
+ }
+ }
+err:
+ return IRQ_HANDLED;
+}
+
+static int stusb160x_irq_init(struct stusb160x *chip, int irq)
+{
+ u32 status;
+ int ret;
+
+ ret = regmap_read(chip->regmap,
+ STUSB160X_CC_CONNECTION_STATUS, &status);
+ if (ret)
+ return ret;
+
+ if (status & STUSB160X_CC_ATTACH) {
+ ret = stusb160x_attach(chip, status);
+ if (ret)
+ dev_err(chip->dev, "attach failed: %d\n", ret);
+ }
+
+ ret = devm_request_threaded_irq(chip->dev, irq, NULL,
+ stusb160x_irq_handler, IRQF_ONESHOT,
+ dev_name(chip->dev), chip);
+ if (ret)
+ goto partner_unregister;
+
+ /* Unmask CC_CONNECTION events */
+ ret = regmap_write_bits(chip->regmap, STUSB160X_ALERT_STATUS_MASK_CTRL,
+ STUSB160X_CC_CONNECTION, 0);
+ if (ret)
+ goto partner_unregister;
+
+ return 0;
+
+partner_unregister:
+ if (chip->partner) {
+ typec_unregister_partner(chip->partner);
+ chip->partner = NULL;
+ }
+
+ return ret;
+}
+
+static int stusb160x_chip_init(struct stusb160x *chip)
+{
+ u32 val;
+ int ret;
+
+ /* Change the default Type-C power mode */
+ if (chip->port_type == TYPEC_PORT_SRC)
+ ret = regmap_update_bits(chip->regmap,
+ STUSB160X_CC_POWER_MODE_CTRL,
+ STUSB160X_CC_POWER_MODE,
+ SOURCE_WITH_ACCESSORY);
+ else if (chip->port_type == TYPEC_PORT_SNK)
+ ret = regmap_update_bits(chip->regmap,
+ STUSB160X_CC_POWER_MODE_CTRL,
+ STUSB160X_CC_POWER_MODE,
+ SINK_WITH_ACCESSORY);
+ else /* (chip->port_type == TYPEC_PORT_DRP) */
+ ret = regmap_update_bits(chip->regmap,
+ STUSB160X_CC_POWER_MODE_CTRL,
+ STUSB160X_CC_POWER_MODE,
+ DUAL_WITH_ACCESSORY);
+ if (ret)
+ return ret;
+
+ if (chip->port_type == TYPEC_PORT_SNK)
+ goto skip_src;
+
+ /* Change the default Type-C Source power operation mode capability */
+ ret = regmap_update_bits(chip->regmap, STUSB160X_CC_CAPABILITY_CTRL,
+ STUSB160X_CC_CURRENT_ADVERTISED,
+ FIELD_PREP(STUSB160X_CC_CURRENT_ADVERTISED,
+ chip->pwr_opmode));
+ if (ret)
+ return ret;
+
+ /* Manage Type-C Source Vconn supply */
+ if (stusb160x_get_vconn(chip)) {
+ ret = stusb160x_set_vconn(chip, true);
+ if (ret)
+ return ret;
+ }
+
+skip_src:
+ /* Mask all events interrupts - to be unmasked with interrupt support */
+ ret = regmap_update_bits(chip->regmap, STUSB160X_ALERT_STATUS_MASK_CTRL,
+ STUSB160X_ALL_ALERTS, STUSB160X_ALL_ALERTS);
+ if (ret)
+ return ret;
+
+ /* Read status at least once to clear any stale interrupts */
+ regmap_read(chip->regmap, STUSB160X_ALERT_STATUS, &val);
+ regmap_read(chip->regmap, STUSB160X_CC_CONNECTION_STATUS_TRANS, &val);
+ regmap_read(chip->regmap, STUSB160X_MONITORING_STATUS_TRANS, &val);
+ regmap_read(chip->regmap, STUSB160X_HW_FAULT_STATUS_TRANS, &val);
+
+ return 0;
+}
+
+static int stusb160x_get_fw_caps(struct stusb160x *chip,
+ struct fwnode_handle *fwnode)
+{
+ const char *cap_str;
+ int ret;
+
+ chip->capability.fwnode = fwnode;
+
+ /*
+ * Supported port type can be configured through device tree
+ * else it is read from chip registers in stusb160x_get_caps.
+ */
+ ret = fwnode_property_read_string(fwnode, "power-role", &cap_str);
+ if (!ret) {
+ ret = typec_find_port_power_role(cap_str);
+ if (ret < 0)
+ return ret;
+ chip->port_type = ret;
+ }
+ chip->capability.type = chip->port_type;
+
+ /* Skip DRP/Source capabilities in case of Sink only */
+ if (chip->port_type == TYPEC_PORT_SNK)
+ return 0;
+
+ if (chip->port_type == TYPEC_PORT_DRP)
+ chip->capability.prefer_role = TYPEC_SINK;
+
+ /*
+ * Supported power operation mode can be configured through device tree
+ * else it is read from chip registers in stusb160x_get_caps.
+ */
+ ret = fwnode_property_read_string(fwnode, "typec-power-opmode", &cap_str);
+ if (!ret) {
+ ret = typec_find_pwr_opmode(cap_str);
+ /* Power delivery not yet supported */
+ if (ret < 0 || ret == TYPEC_PWR_MODE_PD) {
+ dev_err(chip->dev, "bad power operation mode: %d\n", ret);
+ return -EINVAL;
+ }
+ chip->pwr_opmode = ret;
+ }
+
+ return 0;
+}
+
+static int stusb160x_get_caps(struct stusb160x *chip)
+{
+ enum typec_port_type *type = &chip->capability.type;
+ enum typec_port_data *data = &chip->capability.data;
+ enum typec_accessory *accessory = chip->capability.accessory;
+ u32 val;
+ int ret;
+
+ chip->capability.revision = USB_TYPEC_REV_1_2;
+
+ ret = regmap_read(chip->regmap, STUSB160X_CC_POWER_MODE_CTRL, &val);
+ if (ret)
+ return ret;
+
+ switch (FIELD_GET(STUSB160X_CC_POWER_MODE, val)) {
+ case SOURCE_WITH_ACCESSORY:
+ *type = TYPEC_PORT_SRC;
+ *data = TYPEC_PORT_DFP;
+ *accessory++ = TYPEC_ACCESSORY_AUDIO;
+ *accessory++ = TYPEC_ACCESSORY_DEBUG;
+ break;
+ case SINK_WITH_ACCESSORY:
+ *type = TYPEC_PORT_SNK;
+ *data = TYPEC_PORT_UFP;
+ *accessory++ = TYPEC_ACCESSORY_AUDIO;
+ *accessory++ = TYPEC_ACCESSORY_DEBUG;
+ break;
+ case SINK_WITHOUT_ACCESSORY:
+ *type = TYPEC_PORT_SNK;
+ *data = TYPEC_PORT_UFP;
+ break;
+ case DUAL_WITH_ACCESSORY:
+ case DUAL_WITH_ACCESSORY_AND_TRY_SRC:
+ case DUAL_WITH_ACCESSORY_AND_TRY_SNK:
+ *type = TYPEC_PORT_DRP;
+ *data = TYPEC_PORT_DRD;
+ *accessory++ = TYPEC_ACCESSORY_AUDIO;
+ *accessory++ = TYPEC_ACCESSORY_DEBUG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ chip->port_type = *type;
+ chip->pwr_opmode = stusb160x_get_pwr_opmode(chip);
+
+ return 0;
+}
+
+static const struct of_device_id stusb160x_of_match[] = {
+ { .compatible = "st,stusb1600", .data = &stusb1600_regmap_config},
+ {},
+};
+MODULE_DEVICE_TABLE(of, stusb160x_of_match);
+
+static int stusb160x_probe(struct i2c_client *client)
+{
+ struct stusb160x *chip;
+ const struct of_device_id *match;
+ struct regmap_config *regmap_config;
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ chip = devm_kzalloc(&client->dev, sizeof(struct stusb160x), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+
+ match = i2c_of_match_device(stusb160x_of_match, client);
+ regmap_config = (struct regmap_config *)match->data;
+ chip->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(&client->dev,
+ "Failed to allocate register map:%d\n", ret);
+ return ret;
+ }
+
+ chip->dev = &client->dev;
+
+ chip->vsys_supply = devm_regulator_get_optional(chip->dev, "vsys");
+ if (IS_ERR(chip->vsys_supply)) {
+ ret = PTR_ERR(chip->vsys_supply);
+ if (ret != -ENODEV)
+ return ret;
+ chip->vsys_supply = NULL;
+ }
+
+ chip->vdd_supply = devm_regulator_get_optional(chip->dev, "vdd");
+ if (IS_ERR(chip->vdd_supply)) {
+ ret = PTR_ERR(chip->vdd_supply);
+ if (ret != -ENODEV)
+ return ret;
+ chip->vdd_supply = NULL;
+ }
+
+ chip->vconn_supply = devm_regulator_get_optional(chip->dev, "vconn");
+ if (IS_ERR(chip->vconn_supply)) {
+ ret = PTR_ERR(chip->vconn_supply);
+ if (ret != -ENODEV)
+ return ret;
+ chip->vconn_supply = NULL;
+ }
+
+ fwnode = device_get_named_child_node(chip->dev, "connector");
+ if (!fwnode)
+ return -ENODEV;
+
+ /*
+ * When both VDD and VSYS power supplies are present, the low power
+ * supply VSYS is selected when VSYS voltage is above 3.1 V.
+ * Otherwise VDD is selected.
+ */
+ if (chip->vdd_supply &&
+ (!chip->vsys_supply ||
+ (regulator_get_voltage(chip->vsys_supply) <= 3100000)))
+ chip->main_supply = chip->vdd_supply;
+ else
+ chip->main_supply = chip->vsys_supply;
+
+ if (chip->main_supply) {
+ ret = regulator_enable(chip->main_supply);
+ if (ret) {
+ dev_err(chip->dev,
+ "Failed to enable main supply: %d\n", ret);
+ goto fwnode_put;
+ }
+ }
+
+ /* Get configuration from chip */
+ ret = stusb160x_get_caps(chip);
+ if (ret) {
+ dev_err(chip->dev, "Failed to get port caps: %d\n", ret);
+ goto main_reg_disable;
+ }
+
+ /* Get optional re-configuration from device tree */
+ ret = stusb160x_get_fw_caps(chip, fwnode);
+ if (ret) {
+ dev_err(chip->dev, "Failed to get connector caps: %d\n", ret);
+ goto main_reg_disable;
+ }
+
+ ret = stusb160x_chip_init(chip);
+ if (ret) {
+ dev_err(chip->dev, "Failed to init port: %d\n", ret);
+ goto main_reg_disable;
+ }
+
+ chip->port = typec_register_port(chip->dev, &chip->capability);
+ if (IS_ERR(chip->port)) {
+ ret = PTR_ERR(chip->port);
+ goto all_reg_disable;
+ }
+
+ /*
+ * Default power operation mode initialization: will be updated upon
+ * attach/detach interrupt
+ */
+ typec_set_pwr_opmode(chip->port, chip->pwr_opmode);
+
+ if (client->irq) {
+ chip->role_sw = fwnode_usb_role_switch_get(fwnode);
+ if (IS_ERR(chip->role_sw)) {
+ ret = PTR_ERR(chip->role_sw);
+ if (ret != -EPROBE_DEFER)
+ dev_err(chip->dev,
+ "Failed to get usb role switch: %d\n",
+ ret);
+ goto port_unregister;
+ }
+
+ ret = stusb160x_irq_init(chip, client->irq);
+ if (ret)
+ goto role_sw_put;
+ } else {
+ /*
+ * If Source or Dual power role, need to enable VDD supply
+ * providing Vbus if present. In case of interrupt support,
+ * VDD supply will be dynamically managed upon attach/detach
+ * interrupt.
+ */
+ if (chip->port_type != TYPEC_PORT_SNK && chip->vdd_supply) {
+ ret = regulator_enable(chip->vdd_supply);
+ if (ret) {
+ dev_err(chip->dev,
+ "Failed to enable VDD supply: %d\n",
+ ret);
+ goto port_unregister;
+ }
+ chip->vbus_on = true;
+ }
+ }
+
+ fwnode_handle_put(fwnode);
+
+ return 0;
+
+role_sw_put:
+ if (chip->role_sw)
+ usb_role_switch_put(chip->role_sw);
+port_unregister:
+ typec_unregister_port(chip->port);
+all_reg_disable:
+ if (stusb160x_get_vconn(chip))
+ stusb160x_set_vconn(chip, false);
+main_reg_disable:
+ if (chip->main_supply)
+ regulator_disable(chip->main_supply);
+fwnode_put:
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+static int stusb160x_remove(struct i2c_client *client)
+{
+ struct stusb160x *chip = i2c_get_clientdata(client);
+
+ if (chip->partner) {
+ typec_unregister_partner(chip->partner);
+ chip->partner = NULL;
+ }
+
+ if (chip->vbus_on)
+ regulator_disable(chip->vdd_supply);
+
+ if (chip->role_sw)
+ usb_role_switch_put(chip->role_sw);
+
+ typec_unregister_port(chip->port);
+
+ if (stusb160x_get_vconn(chip))
+ stusb160x_set_vconn(chip, false);
+
+ if (chip->main_supply)
+ regulator_disable(chip->main_supply);
+
+ return 0;
+}
+
+static int __maybe_unused stusb160x_suspend(struct device *dev)
+{
+ struct stusb160x *chip = dev_get_drvdata(dev);
+
+ /* Mask interrupts */
+ return regmap_update_bits(chip->regmap,
+ STUSB160X_ALERT_STATUS_MASK_CTRL,
+ STUSB160X_ALL_ALERTS, STUSB160X_ALL_ALERTS);
+}
+
+static int __maybe_unused stusb160x_resume(struct device *dev)
+{
+ struct stusb160x *chip = dev_get_drvdata(dev);
+ u32 status;
+ int ret;
+
+ ret = regcache_sync(chip->regmap);
+ if (ret)
+ return ret;
+
+ /* Check if attach/detach occurred during low power */
+ ret = regmap_read(chip->regmap,
+ STUSB160X_CC_CONNECTION_STATUS, &status);
+ if (ret)
+ return ret;
+
+ if (chip->partner && !(status & STUSB160X_CC_ATTACH))
+ stusb160x_detach(chip, status);
+
+ if (!chip->partner && (status & STUSB160X_CC_ATTACH)) {
+ ret = stusb160x_attach(chip, status);
+ if (ret)
+ dev_err(chip->dev, "attach failed: %d\n", ret);
+ }
+
+ /* Unmask interrupts */
+ return regmap_write_bits(chip->regmap, STUSB160X_ALERT_STATUS_MASK_CTRL,
+ STUSB160X_CC_CONNECTION, 0);
+}
+
+static SIMPLE_DEV_PM_OPS(stusb160x_pm_ops, stusb160x_suspend, stusb160x_resume);
+
+static struct i2c_driver stusb160x_driver = {
+ .driver = {
+ .name = "stusb160x",
+ .pm = &stusb160x_pm_ops,
+ .of_match_table = stusb160x_of_match,
+ },
+ .probe_new = stusb160x_probe,
+ .remove = stusb160x_remove,
+};
+module_i2c_driver(stusb160x_driver);
+
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STUSB160x Type-C controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
index 5b986d6..557f392 100644
--- a/drivers/usb/typec/tcpm/Kconfig
+++ b/drivers/usb/typec/tcpm/Kconfig
@@ -27,6 +27,20 @@
Type-C Port Controller Manager to provide USB PD and USB
Type-C functionalities.
+config TYPEC_MT6360
+ tristate "Mediatek MT6360 Type-C driver"
+ depends on MFD_MT6360
+ help
+ Mediatek MT6360 is a multi-functional IC that includes
+ USB Type-C. It works with Type-C Port Controller Manager
+ to provide USB PD and USB Type-C functionalities.
+
+config TYPEC_TCPCI_MAXIM
+ tristate "Maxim TCPCI based Type-C chip driver"
+ help
+ MAXIM TCPCI based Type-C/PD chip driver. Works with
+ with Type-C Port Controller Manager.
+
endif # TYPEC_TCPCI
config TYPEC_FUSB302
@@ -41,8 +55,8 @@
config TYPEC_WCOVE
tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
depends on ACPI
+ depends on MFD_INTEL_PMC_BXT
depends on INTEL_SOC_PMIC
- depends on INTEL_PMC_IPC
depends on BXT_WC_PMIC_OPREGION
help
This driver adds support for USB Type-C on Intel Broxton platforms
diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile
index a5ff6c8..7d499f3 100644
--- a/drivers/usb/typec/tcpm/Makefile
+++ b/drivers/usb/typec/tcpm/Makefile
@@ -1,7 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
-obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
-obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
-typec_wcove-y := wcove.o
-obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
-obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
+obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
+obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
+obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
+typec_wcove-y := wcove.o
+obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
+obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
+obj-$(CONFIG_TYPEC_MT6360) += tcpci_mt6360.o
+obj-$(CONFIG_TYPEC_TCPCI_MAXIM) += tcpci_maxim.o
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index b498960..700e38e 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -9,14 +9,13 @@
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/extcon.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/proc_fs.h>
#include <linux/regulator/consumer.h>
@@ -83,7 +82,7 @@
struct work_struct irq_work;
bool irq_suspended;
bool irq_while_suspended;
- int gpio_int_n;
+ struct gpio_desc *gpio_int_n;
int gpio_int_n_irq;
struct extcon_dev *extcon;
@@ -179,6 +178,7 @@
mutex_unlock(&chip->logbuffer_lock);
}
+__printf(2, 3)
static void fusb302_log(struct fusb302_chip *chip, const char *fmt, ...)
{
va_list args;
@@ -669,25 +669,27 @@
ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
FUSB_REG_MASK_BC_LVL |
FUSB_REG_MASK_COMP_CHNG,
- FUSB_REG_MASK_COMP_CHNG);
- if (ret < 0) {
- fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
- ret);
- goto done;
- }
- chip->intr_comp_chng = true;
- break;
- case TYPEC_CC_RD:
- ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
- FUSB_REG_MASK_BC_LVL |
- FUSB_REG_MASK_COMP_CHNG,
FUSB_REG_MASK_BC_LVL);
if (ret < 0) {
fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
ret);
goto done;
}
+ chip->intr_comp_chng = true;
+ chip->intr_bc_lvl = false;
+ break;
+ case TYPEC_CC_RD:
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
+ FUSB_REG_MASK_BC_LVL |
+ FUSB_REG_MASK_COMP_CHNG,
+ FUSB_REG_MASK_COMP_CHNG);
+ if (ret < 0) {
+ fusb302_log(chip, "cannot set SRC interrupt, ret=%d",
+ ret);
+ goto done;
+ }
chip->intr_bc_lvl = true;
+ chip->intr_comp_chng = false;
break;
default:
break;
@@ -1618,30 +1620,17 @@
static int init_gpio(struct fusb302_chip *chip)
{
- struct device_node *node;
+ struct device *dev = chip->dev;
int ret = 0;
- node = chip->dev->of_node;
- chip->gpio_int_n = of_get_named_gpio(node, "fcs,int_n", 0);
- if (!gpio_is_valid(chip->gpio_int_n)) {
- ret = chip->gpio_int_n;
- dev_err(chip->dev, "cannot get named GPIO Int_N, ret=%d", ret);
- return ret;
+ chip->gpio_int_n = devm_gpiod_get(dev, "fcs,int_n", GPIOD_IN);
+ if (IS_ERR(chip->gpio_int_n)) {
+ dev_err(dev, "failed to request gpio_int_n\n");
+ return PTR_ERR(chip->gpio_int_n);
}
- ret = devm_gpio_request(chip->dev, chip->gpio_int_n, "fcs,int_n");
+ ret = gpiod_to_irq(chip->gpio_int_n);
if (ret < 0) {
- dev_err(chip->dev, "cannot request GPIO Int_N, ret=%d", ret);
- return ret;
- }
- ret = gpio_direction_input(chip->gpio_int_n);
- if (ret < 0) {
- dev_err(chip->dev,
- "cannot set GPIO Int_N to input, ret=%d", ret);
- return ret;
- }
- ret = gpio_to_irq(chip->gpio_int_n);
- if (ret < 0) {
- dev_err(chip->dev,
+ dev_err(dev,
"cannot request IRQ for GPIO Int_N, ret=%d", ret);
return ret;
}
diff --git a/drivers/usb/typec/tcpm/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h
index 00b39d3..edc0e4b 100644
--- a/drivers/usb/typec/tcpm/fusb302_reg.h
+++ b/drivers/usb/typec/tcpm/fusb302_reg.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright 2016-2017 Google, Inc
*
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 59e304a..a06da18 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -47,6 +47,12 @@
struct tcpci_data data;
};
+struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci)
+{
+ return tcpci->port;
+}
+EXPORT_SYMBOL_GPL(tcpci_get_tcpm_port);
+
static inline struct tcpci *tcpc_to_tcpci(struct tcpc_dev *tcpc)
{
return container_of(tcpc, struct tcpci, tcpc);
@@ -166,7 +172,7 @@
case 0x3:
if (sink)
return TYPEC_CC_RP_3_0;
- /* fall through */
+ fallthrough;
case 0x0:
default:
return TYPEC_CC_OPEN;
@@ -206,12 +212,47 @@
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg;
int ret;
+ enum typec_cc_status cc1, cc2;
- /* Keep the disconnect cc line open */
+ /* Obtain Rp setting from role control */
ret = regmap_read(tcpci->regmap, TCPC_ROLE_CTRL, ®);
if (ret < 0)
return ret;
+ ret = tcpci_get_cc(tcpc, &cc1, &cc2);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * When port has drp toggling enabled, ROLE_CONTROL would only have the initial
+ * terminations for the toggling and does not indicate the final cc
+ * terminations when ConnectionResult is 0 i.e. drp toggling stops and
+ * the connection is resolbed. Infer port role from TCPC_CC_STATUS based on the
+ * terminations seen. The port role is then used to set the cc terminations.
+ */
+ if (reg & TCPC_ROLE_CTRL_DRP) {
+ /* Disable DRP for the OPEN setting to take effect */
+ reg = reg & ~TCPC_ROLE_CTRL_DRP;
+
+ if (polarity == TYPEC_POLARITY_CC2) {
+ reg &= ~(TCPC_ROLE_CTRL_CC2_MASK << TCPC_ROLE_CTRL_CC2_SHIFT);
+ /* Local port is source */
+ if (cc2 == TYPEC_CC_RD)
+ /* Role control would have the Rp setting when DRP was enabled */
+ reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC2_SHIFT;
+ else
+ reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT;
+ } else {
+ reg &= ~(TCPC_ROLE_CTRL_CC1_MASK << TCPC_ROLE_CTRL_CC1_SHIFT);
+ /* Local port is source */
+ if (cc1 == TYPEC_CC_RD)
+ /* Role control would have the Rp setting when DRP was enabled */
+ reg |= TCPC_ROLE_CTRL_CC_RP << TCPC_ROLE_CTRL_CC1_SHIFT;
+ else
+ reg |= TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT;
+ }
+ }
+
if (polarity == TYPEC_POLARITY_CC2)
reg |= TCPC_ROLE_CTRL_CC_OPEN << TCPC_ROLE_CTRL_CC1_SHIFT;
else
@@ -242,6 +283,30 @@
enable ? TCPC_POWER_CTRL_VCONN_ENABLE : 0);
}
+static int tcpci_enable_frs(struct tcpc_dev *dev, bool enable)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(dev);
+ int ret;
+
+ /* To prevent disconnect during FRS, set disconnect threshold to 3.5V */
+ ret = tcpci_write16(tcpci, TCPC_VBUS_SINK_DISCONNECT_THRESH, enable ? 0 : 0x8c);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(tcpci->regmap, TCPC_POWER_CTRL, TCPC_FAST_ROLE_SWAP_EN, enable ?
+ TCPC_FAST_ROLE_SWAP_EN : 0);
+
+ return ret;
+}
+
+static int tcpci_set_bist_data(struct tcpc_dev *tcpc, bool enable)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+
+ return regmap_update_bits(tcpci->regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_BIST_TM,
+ enable ? TCPC_TCPC_CTRL_BIST_TM : 0);
+}
+
static int tcpci_set_roles(struct tcpc_dev *tcpc, bool attached,
enum typec_role role, enum typec_data_role data)
{
@@ -294,6 +359,13 @@
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
int ret;
+ if (tcpci->data->set_vbus) {
+ ret = tcpci->data->set_vbus(tcpci, tcpci->data, source, sink);
+ /* Bypass when ret > 0 */
+ if (ret != 0)
+ return ret < 0 ? ret : 0;
+ }
+
/* Disable both source and sink first before enabling anything */
if (!source) {
@@ -337,23 +409,47 @@
int ret;
cnt = msg ? pd_header_cnt(header) * 4 : 0;
- ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
- if (ret < 0)
- return ret;
+ /**
+ * TCPCI spec forbids direct access of TCPC_TX_DATA.
+ * But, since some of the chipsets offer this capability,
+ * it's fair to support both.
+ */
+ if (tcpci->data->TX_BUF_BYTE_x_hidden) {
+ u8 buf[TCPC_TRANSMIT_BUFFER_MAX_LEN] = {0,};
+ u8 pos = 0;
- ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
- if (ret < 0)
- return ret;
+ /* Payload + header + TCPC_TX_BYTE_CNT */
+ buf[pos++] = cnt + 2;
- if (cnt > 0) {
- ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA,
- &msg->payload, cnt);
+ if (msg)
+ memcpy(&buf[pos], &msg->header, sizeof(msg->header));
+
+ pos += sizeof(header);
+
+ if (cnt > 0)
+ memcpy(&buf[pos], msg->payload, cnt);
+
+ pos += cnt;
+ ret = regmap_raw_write(tcpci->regmap, TCPC_TX_BYTE_CNT, buf, pos);
if (ret < 0)
return ret;
+ } else {
+ ret = regmap_write(tcpci->regmap, TCPC_TX_BYTE_CNT, cnt + 2);
+ if (ret < 0)
+ return ret;
+
+ ret = tcpci_write16(tcpci, TCPC_TX_HDR, header);
+ if (ret < 0)
+ return ret;
+
+ if (cnt > 0) {
+ ret = regmap_raw_write(tcpci->regmap, TCPC_TX_DATA, &msg->payload, cnt);
+ if (ret < 0)
+ return ret;
+ }
}
- reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) |
- (type << TCPC_TRANSMIT_TYPE_SHIFT);
+ reg = (PD_RETRY_COUNT << TCPC_TRANSMIT_RETRY_SHIFT) | (type << TCPC_TRANSMIT_TYPE_SHIFT);
ret = regmap_write(tcpci->regmap, TCPC_TRANSMIT, reg);
if (ret < 0)
return ret;
@@ -545,6 +641,8 @@
tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx;
tcpci->tcpc.set_roles = tcpci_set_roles;
tcpci->tcpc.pd_transmit = tcpci_pd_transmit;
+ tcpci->tcpc.set_bist_data = tcpci_set_bist_data;
+ tcpci->tcpc.enable_frs = tcpci_enable_frs;
err = tcpci_parse_config(tcpci);
if (err < 0)
diff --git a/drivers/usb/typec/tcpm/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
index 303ebde..5ef07a5 100644
--- a/drivers/usb/typec/tcpm/tcpci.h
+++ b/drivers/usb/typec/tcpm/tcpci.h
@@ -16,6 +16,8 @@
#define TCPC_PD_INT_REV 0xa
#define TCPC_ALERT 0x10
+#define TCPC_ALERT_EXTND BIT(14)
+#define TCPC_ALERT_EXTENDED_STATUS BIT(13)
#define TCPC_ALERT_VBUS_DISCNCT BIT(11)
#define TCPC_ALERT_RX_BUF_OVF BIT(10)
#define TCPC_ALERT_FAULT BIT(9)
@@ -32,10 +34,18 @@
#define TCPC_ALERT_MASK 0x12
#define TCPC_POWER_STATUS_MASK 0x14
#define TCPC_FAULT_STATUS_MASK 0x15
+
+#define TCPC_EXTENDED_STATUS_MASK 0x16
+#define TCPC_EXTENDED_STATUS_MASK_VSAFE0V BIT(0)
+
+#define TCPC_ALERT_EXTENDED_MASK 0x17
+#define TCPC_SINK_FAST_ROLE_SWAP BIT(0)
+
#define TCPC_CONFIG_STD_OUTPUT 0x18
#define TCPC_TCPC_CTRL 0x19
#define TCPC_TCPC_CTRL_ORIENTATION BIT(0)
+#define TCPC_TCPC_CTRL_BIST_TM BIT(1)
#define TCPC_ROLE_CTRL 0x1a
#define TCPC_ROLE_CTRL_DRP BIT(6)
@@ -57,6 +67,7 @@
#define TCPC_POWER_CTRL 0x1c
#define TCPC_POWER_CTRL_VCONN_ENABLE BIT(0)
+#define TCPC_FAST_ROLE_SWAP_EN BIT(7)
#define TCPC_CC_STATUS 0x1d
#define TCPC_CC_STATUS_TOGGLING BIT(5)
@@ -68,11 +79,14 @@
#define TCPC_POWER_STATUS 0x1e
#define TCPC_POWER_STATUS_UNINIT BIT(6)
+#define TCPC_POWER_STATUS_SOURCING_VBUS BIT(4)
#define TCPC_POWER_STATUS_VBUS_DET BIT(3)
#define TCPC_POWER_STATUS_VBUS_PRES BIT(2)
#define TCPC_FAULT_STATUS 0x1f
+#define TCPC_ALERT_EXTENDED 0x21
+
#define TCPC_COMMAND 0x23
#define TCPC_CMD_WAKE_I2C 0x11
#define TCPC_CMD_DISABLE_VBUS_DETECT 0x22
@@ -103,6 +117,7 @@
#define TCPC_RX_BYTE_CNT 0x30
#define TCPC_RX_BUF_FRAME_TYPE 0x31
+#define TCPC_RX_BUF_FRAME_TYPE_SOP 0
#define TCPC_RX_HDR 0x32
#define TCPC_RX_DATA 0x34 /* through 0x4f */
@@ -122,18 +137,29 @@
#define TCPC_VBUS_VOLTAGE_ALARM_HI_CFG 0x76
#define TCPC_VBUS_VOLTAGE_ALARM_LO_CFG 0x78
+/* I2C_WRITE_BYTE_COUNT + 1 when TX_BUF_BYTE_x is only accessible I2C_WRITE_BYTE_COUNT */
+#define TCPC_TRANSMIT_BUFFER_MAX_LEN 31
+
+/*
+ * @TX_BUF_BYTE_x_hidden
+ * optional; Set when TX_BUF_BYTE_x can only be accessed through I2C_WRITE_BYTE_COUNT.
+ */
struct tcpci;
struct tcpci_data {
struct regmap *regmap;
+ unsigned char TX_BUF_BYTE_x_hidden:1;
int (*init)(struct tcpci *tcpci, struct tcpci_data *data);
int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data,
bool enable);
int (*start_drp_toggling)(struct tcpci *tcpci, struct tcpci_data *data,
enum typec_cc_status cc);
+ int (*set_vbus)(struct tcpci *tcpci, struct tcpci_data *data, bool source, bool sink);
};
struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data);
void tcpci_unregister_port(struct tcpci *tcpci);
irqreturn_t tcpci_irq(struct tcpci *tcpci);
+struct tcpm_port;
+struct tcpm_port *tcpci_get_tcpm_port(struct tcpci *tcpci);
#endif /* __LINUX_USB_TCPCI_H */
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c
new file mode 100644
index 0000000..723d7dd
--- /dev/null
+++ b/drivers/usb/typec/tcpm/tcpci_maxim.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Google LLC
+ *
+ * MAXIM TCPCI based TCPC driver
+ */
+
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/usb/pd.h>
+#include <linux/usb/tcpm.h>
+#include <linux/usb/typec.h>
+
+#include "tcpci.h"
+
+#define PD_ACTIVITY_TIMEOUT_MS 10000
+
+#define TCPC_VENDOR_ALERT 0x80
+
+#define TCPC_RECEIVE_BUFFER_COUNT_OFFSET 0
+#define TCPC_RECEIVE_BUFFER_FRAME_TYPE_OFFSET 1
+#define TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET 2
+
+/*
+ * LongMessage not supported, hence 32 bytes for buf to be read from RECEIVE_BUFFER.
+ * DEVICE_CAPABILITIES_2.LongMessage = 0, the value in READABLE_BYTE_COUNT reg shall be
+ * less than or equal to 31. Since, RECEIVE_BUFFER len = 31 + 1(READABLE_BYTE_COUNT).
+ */
+#define TCPC_RECEIVE_BUFFER_LEN 32
+
+#define MAX_BUCK_BOOST_SID 0x69
+#define MAX_BUCK_BOOST_OP 0xb9
+#define MAX_BUCK_BOOST_OFF 0
+#define MAX_BUCK_BOOST_SOURCE 0xa
+#define MAX_BUCK_BOOST_SINK 0x5
+
+struct max_tcpci_chip {
+ struct tcpci_data data;
+ struct tcpci *tcpci;
+ struct device *dev;
+ struct i2c_client *client;
+ struct tcpm_port *port;
+};
+
+static const struct regmap_range max_tcpci_tcpci_range[] = {
+ regmap_reg_range(0x00, 0x95)
+};
+
+const struct regmap_access_table max_tcpci_tcpci_write_table = {
+ .yes_ranges = max_tcpci_tcpci_range,
+ .n_yes_ranges = ARRAY_SIZE(max_tcpci_tcpci_range),
+};
+
+static const struct regmap_config max_tcpci_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x95,
+ .wr_table = &max_tcpci_tcpci_write_table,
+};
+
+static struct max_tcpci_chip *tdata_to_max_tcpci(struct tcpci_data *tdata)
+{
+ return container_of(tdata, struct max_tcpci_chip, data);
+}
+
+static int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val)
+{
+ return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u16));
+}
+
+static int max_tcpci_write16(struct max_tcpci_chip *chip, unsigned int reg, u16 val)
+{
+ return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u16));
+}
+
+static int max_tcpci_read8(struct max_tcpci_chip *chip, unsigned int reg, u8 *val)
+{
+ return regmap_raw_read(chip->data.regmap, reg, val, sizeof(u8));
+}
+
+static int max_tcpci_write8(struct max_tcpci_chip *chip, unsigned int reg, u8 val)
+{
+ return regmap_raw_write(chip->data.regmap, reg, &val, sizeof(u8));
+}
+
+static void max_tcpci_init_regs(struct max_tcpci_chip *chip)
+{
+ u16 alert_mask = 0;
+ int ret;
+
+ ret = max_tcpci_write16(chip, TCPC_ALERT, 0xffff);
+ if (ret < 0) {
+ dev_err(chip->dev, "Error writing to TCPC_ALERT ret:%d\n", ret);
+ return;
+ }
+
+ ret = max_tcpci_write16(chip, TCPC_VENDOR_ALERT, 0xffff);
+ if (ret < 0) {
+ dev_err(chip->dev, "Error writing to TCPC_VENDOR_ALERT ret:%d\n", ret);
+ return;
+ }
+
+ ret = max_tcpci_write8(chip, TCPC_ALERT_EXTENDED, 0xff);
+ if (ret < 0) {
+ dev_err(chip->dev, "Unable to clear TCPC_ALERT_EXTENDED ret:%d\n", ret);
+ return;
+ }
+
+ alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | TCPC_ALERT_TX_FAILED |
+ TCPC_ALERT_RX_HARD_RST | TCPC_ALERT_RX_STATUS | TCPC_ALERT_CC_STATUS |
+ TCPC_ALERT_VBUS_DISCNCT | TCPC_ALERT_RX_BUF_OVF | TCPC_ALERT_POWER_STATUS |
+ /* Enable Extended alert for detecting Fast Role Swap Signal */
+ TCPC_ALERT_EXTND;
+
+ ret = max_tcpci_write16(chip, TCPC_ALERT_MASK, alert_mask);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Error enabling TCPC_ALERT: TCPC_ALERT_MASK write failed ret:%d\n", ret);
+ return;
+ }
+
+ /* Enable vbus voltage monitoring and voltage alerts */
+ ret = max_tcpci_write8(chip, TCPC_POWER_CTRL, 0);
+ if (ret < 0) {
+ dev_err(chip->dev, "Error writing to TCPC_POWER_CTRL ret:%d\n", ret);
+ return;
+ }
+
+ ret = max_tcpci_write8(chip, TCPC_ALERT_EXTENDED_MASK, TCPC_SINK_FAST_ROLE_SWAP);
+ if (ret < 0)
+ return;
+}
+
+static void process_rx(struct max_tcpci_chip *chip, u16 status)
+{
+ struct pd_message msg;
+ u8 count, frame_type, rx_buf[TCPC_RECEIVE_BUFFER_LEN];
+ int ret, payload_index;
+ u8 *rx_buf_ptr;
+
+ /*
+ * READABLE_BYTE_COUNT: Indicates the number of bytes in the RX_BUF_BYTE_x registers
+ * plus one (for the RX_BUF_FRAME_TYPE) Table 4-36.
+ * Read the count and frame type.
+ */
+ ret = regmap_raw_read(chip->data.regmap, TCPC_RX_BYTE_CNT, rx_buf, 2);
+ if (ret < 0) {
+ dev_err(chip->dev, "TCPC_RX_BYTE_CNT read failed ret:%d", ret);
+ return;
+ }
+
+ count = rx_buf[TCPC_RECEIVE_BUFFER_COUNT_OFFSET];
+ frame_type = rx_buf[TCPC_RECEIVE_BUFFER_FRAME_TYPE_OFFSET];
+
+ if (count == 0 || frame_type != TCPC_RX_BUF_FRAME_TYPE_SOP) {
+ max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
+ dev_err(chip->dev, "%s", count == 0 ? "error: count is 0" :
+ "error frame_type is not SOP");
+ return;
+ }
+
+ if (count > sizeof(struct pd_message) || count + 1 > TCPC_RECEIVE_BUFFER_LEN) {
+ dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d", count);
+ return;
+ }
+
+ /*
+ * Read count + 1 as RX_BUF_BYTE_x is hidden and can only be read through
+ * TCPC_RX_BYTE_CNT
+ */
+ count += 1;
+ ret = regmap_raw_read(chip->data.regmap, TCPC_RX_BYTE_CNT, rx_buf, count);
+ if (ret < 0) {
+ dev_err(chip->dev, "Error: TCPC_RX_BYTE_CNT read failed: %d", ret);
+ return;
+ }
+
+ rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET;
+ msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr);
+ rx_buf_ptr = rx_buf_ptr + sizeof(msg.header);
+ for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++,
+ rx_buf_ptr += sizeof(msg.payload[0]))
+ msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr);
+
+ /*
+ * Read complete, clear RX status alert bit.
+ * Clear overflow as well if set.
+ */
+ ret = max_tcpci_write16(chip, TCPC_ALERT, status & TCPC_ALERT_RX_BUF_OVF ?
+ TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF :
+ TCPC_ALERT_RX_STATUS);
+ if (ret < 0)
+ return;
+
+ tcpm_pd_receive(chip->port, &msg);
+}
+
+static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata, bool source, bool sink)
+{
+ struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
+ u8 buffer_source[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SOURCE};
+ u8 buffer_sink[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SINK};
+ u8 buffer_none[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_OFF};
+ struct i2c_client *i2c = chip->client;
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = MAX_BUCK_BOOST_SID,
+ .flags = i2c->flags & I2C_M_TEN,
+ .len = 2,
+ .buf = source ? buffer_source : sink ? buffer_sink : buffer_none,
+ },
+ };
+
+ if (source && sink) {
+ dev_err(chip->dev, "Both source and sink set\n");
+ return -EINVAL;
+ }
+
+ ret = i2c_transfer(i2c->adapter, msgs, 1);
+
+ return ret < 0 ? ret : 1;
+}
+
+static void process_power_status(struct max_tcpci_chip *chip)
+{
+ u8 pwr_status;
+ int ret;
+
+ ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &pwr_status);
+ if (ret < 0)
+ return;
+
+ if (pwr_status == 0xff) {
+ max_tcpci_init_regs(chip);
+ } else if (pwr_status & TCPC_POWER_STATUS_SOURCING_VBUS) {
+ tcpm_sourcing_vbus(chip->port);
+ /*
+ * Alawys re-enable boost here.
+ * In normal case, when say an headset is attached, TCPM would
+ * have instructed to TCPC to enable boost, so the call is a
+ * no-op.
+ * But for Fast Role Swap case, Boost turns on autonomously without
+ * AP intervention, but, needs AP to enable source mode explicitly
+ * for AP to regain control.
+ */
+ max_tcpci_set_vbus(chip->tcpci, &chip->data, true, false);
+ } else {
+ tcpm_vbus_change(chip->port);
+ }
+}
+
+static void process_tx(struct max_tcpci_chip *chip, u16 status)
+{
+ if (status & TCPC_ALERT_TX_SUCCESS)
+ tcpm_pd_transmit_complete(chip->port, TCPC_TX_SUCCESS);
+ else if (status & TCPC_ALERT_TX_DISCARDED)
+ tcpm_pd_transmit_complete(chip->port, TCPC_TX_DISCARDED);
+ else if (status & TCPC_ALERT_TX_FAILED)
+ tcpm_pd_transmit_complete(chip->port, TCPC_TX_FAILED);
+
+ /* Reinit regs as Hard reset sets them to default value */
+ if ((status & TCPC_ALERT_TX_SUCCESS) && (status & TCPC_ALERT_TX_FAILED))
+ max_tcpci_init_regs(chip);
+}
+
+static irqreturn_t _max_tcpci_irq(struct max_tcpci_chip *chip, u16 status)
+{
+ u16 mask;
+ int ret;
+ u8 reg_status;
+
+ /*
+ * Clear alert status for everything except RX_STATUS, which shouldn't
+ * be cleared until we have successfully retrieved message.
+ */
+ if (status & ~TCPC_ALERT_RX_STATUS) {
+ mask = status & TCPC_ALERT_RX_BUF_OVF ?
+ status & ~(TCPC_ALERT_RX_STATUS | TCPC_ALERT_RX_BUF_OVF) :
+ status & ~TCPC_ALERT_RX_STATUS;
+ ret = max_tcpci_write16(chip, TCPC_ALERT, mask);
+ if (ret < 0) {
+ dev_err(chip->dev, "ALERT clear failed\n");
+ return ret;
+ }
+ }
+
+ if (status & TCPC_ALERT_RX_BUF_OVF && !(status & TCPC_ALERT_RX_STATUS)) {
+ ret = max_tcpci_write16(chip, TCPC_ALERT, (TCPC_ALERT_RX_STATUS |
+ TCPC_ALERT_RX_BUF_OVF));
+ if (ret < 0) {
+ dev_err(chip->dev, "ALERT clear failed\n");
+ return ret;
+ }
+ }
+
+ if (status & TCPC_ALERT_EXTND) {
+ ret = max_tcpci_read8(chip, TCPC_ALERT_EXTENDED, ®_status);
+ if (ret < 0)
+ return ret;
+
+ ret = max_tcpci_write8(chip, TCPC_ALERT_EXTENDED, reg_status);
+ if (ret < 0)
+ return ret;
+
+ if (reg_status & TCPC_SINK_FAST_ROLE_SWAP) {
+ dev_info(chip->dev, "FRS Signal");
+ tcpm_sink_frs(chip->port);
+ }
+ }
+
+ if (status & TCPC_ALERT_RX_STATUS)
+ process_rx(chip, status);
+
+ if (status & TCPC_ALERT_VBUS_DISCNCT)
+ tcpm_vbus_change(chip->port);
+
+ if (status & TCPC_ALERT_CC_STATUS)
+ tcpm_cc_change(chip->port);
+
+ if (status & TCPC_ALERT_POWER_STATUS)
+ process_power_status(chip);
+
+ if (status & TCPC_ALERT_RX_HARD_RST) {
+ tcpm_pd_hard_reset(chip->port);
+ max_tcpci_init_regs(chip);
+ }
+
+ if (status & TCPC_ALERT_TX_SUCCESS || status & TCPC_ALERT_TX_DISCARDED || status &
+ TCPC_ALERT_TX_FAILED)
+ process_tx(chip, status);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max_tcpci_irq(int irq, void *dev_id)
+{
+ struct max_tcpci_chip *chip = dev_id;
+ u16 status;
+ irqreturn_t irq_return;
+ int ret;
+
+ if (!chip->port)
+ return IRQ_HANDLED;
+
+ ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
+ if (ret < 0) {
+ dev_err(chip->dev, "ALERT read failed\n");
+ return ret;
+ }
+ while (status) {
+ irq_return = _max_tcpci_irq(chip, status);
+ /* Do not return if the ALERT is already set. */
+ ret = max_tcpci_read16(chip, TCPC_ALERT, &status);
+ if (ret < 0)
+ break;
+ }
+
+ return irq_return;
+}
+
+static irqreturn_t max_tcpci_isr(int irq, void *dev_id)
+{
+ struct max_tcpci_chip *chip = dev_id;
+
+ pm_wakeup_event(chip->dev, PD_ACTIVITY_TIMEOUT_MS);
+
+ if (!chip->port)
+ return IRQ_HANDLED;
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int max_tcpci_init_alert(struct max_tcpci_chip *chip, struct i2c_client *client)
+{
+ int ret;
+
+ ret = devm_request_threaded_irq(chip->dev, client->irq, max_tcpci_isr, max_tcpci_irq,
+ (IRQF_TRIGGER_LOW | IRQF_ONESHOT), dev_name(chip->dev),
+ chip);
+
+ if (ret < 0)
+ return ret;
+
+ enable_irq_wake(client->irq);
+ return 0;
+}
+
+static int max_tcpci_start_toggling(struct tcpci *tcpci, struct tcpci_data *tdata,
+ enum typec_cc_status cc)
+{
+ struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata);
+
+ max_tcpci_init_regs(chip);
+
+ return 0;
+}
+
+static int tcpci_init(struct tcpci *tcpci, struct tcpci_data *data)
+{
+ /*
+ * Generic TCPCI overwrites the regs once this driver initializes
+ * them. Prevent this by returning -1.
+ */
+ return -1;
+}
+
+static int max_tcpci_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id)
+{
+ int ret;
+ struct max_tcpci_chip *chip;
+ u8 power_status;
+
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->client = client;
+ chip->data.regmap = devm_regmap_init_i2c(client, &max_tcpci_regmap_config);
+ if (IS_ERR(chip->data.regmap)) {
+ dev_err(&client->dev, "Regmap init failed\n");
+ return PTR_ERR(chip->data.regmap);
+ }
+
+ chip->dev = &client->dev;
+ i2c_set_clientdata(client, chip);
+
+ ret = max_tcpci_read8(chip, TCPC_POWER_STATUS, &power_status);
+ if (ret < 0)
+ return ret;
+
+ /* Chip level tcpci callbacks */
+ chip->data.set_vbus = max_tcpci_set_vbus;
+ chip->data.start_drp_toggling = max_tcpci_start_toggling;
+ chip->data.TX_BUF_BYTE_x_hidden = true;
+ chip->data.init = tcpci_init;
+
+ max_tcpci_init_regs(chip);
+ chip->tcpci = tcpci_register_port(chip->dev, &chip->data);
+ if (IS_ERR_OR_NULL(chip->tcpci)) {
+ dev_err(&client->dev, "TCPCI port registration failed");
+ ret = PTR_ERR(chip->tcpci);
+ return PTR_ERR(chip->tcpci);
+ }
+ chip->port = tcpci_get_tcpm_port(chip->tcpci);
+ ret = max_tcpci_init_alert(chip, client);
+ if (ret < 0)
+ goto unreg_port;
+
+ device_init_wakeup(chip->dev, true);
+ return 0;
+
+unreg_port:
+ tcpci_unregister_port(chip->tcpci);
+
+ return ret;
+}
+
+static int max_tcpci_remove(struct i2c_client *client)
+{
+ struct max_tcpci_chip *chip = i2c_get_clientdata(client);
+
+ if (!IS_ERR_OR_NULL(chip->tcpci))
+ tcpci_unregister_port(chip->tcpci);
+
+ return 0;
+}
+
+static const struct i2c_device_id max_tcpci_id[] = {
+ { "maxtcpc", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max_tcpci_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id max_tcpci_of_match[] = {
+ { .compatible = "maxim,tcpc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max_tcpci_of_match);
+#endif
+
+static struct i2c_driver max_tcpci_i2c_driver = {
+ .driver = {
+ .name = "maxtcpc",
+ .of_match_table = of_match_ptr(max_tcpci_of_match),
+ },
+ .probe = max_tcpci_probe,
+ .remove = max_tcpci_remove,
+ .id_table = max_tcpci_id,
+};
+module_i2c_driver(max_tcpci_i2c_driver);
+
+MODULE_AUTHOR("Badhri Jagan Sridharan <badhri@google.com>");
+MODULE_DESCRIPTION("Maxim TCPCI based USB Type-C Port Controller Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/tcpm/tcpci_mt6360.c b/drivers/usb/typec/tcpm/tcpci_mt6360.c
new file mode 100644
index 0000000..f1bd9e0
--- /dev/null
+++ b/drivers/usb/typec/tcpm/tcpci_mt6360.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 MediaTek Inc.
+ *
+ * Author: ChiYuan Huang <cy_huang@richtek.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/usb/tcpm.h>
+
+#include "tcpci.h"
+
+#define MT6360_REG_VCONNCTRL1 0x8C
+#define MT6360_REG_MODECTRL2 0x8F
+#define MT6360_REG_SWRESET 0xA0
+#define MT6360_REG_DEBCTRL1 0xA1
+#define MT6360_REG_DRPCTRL1 0xA2
+#define MT6360_REG_DRPCTRL2 0xA3
+#define MT6360_REG_I2CTORST 0xBF
+#define MT6360_REG_RXCTRL2 0xCF
+#define MT6360_REG_CTDCTRL2 0xEC
+
+/* MT6360_REG_VCONNCTRL1 */
+#define MT6360_VCONNCL_ENABLE BIT(0)
+/* MT6360_REG_RXCTRL2 */
+#define MT6360_OPEN40M_ENABLE BIT(7)
+/* MT6360_REG_CTDCTRL2 */
+#define MT6360_RPONESHOT_ENABLE BIT(6)
+
+struct mt6360_tcpc_info {
+ struct tcpci_data tdata;
+ struct tcpci *tcpci;
+ struct device *dev;
+ int irq;
+};
+
+static inline int mt6360_tcpc_read16(struct regmap *regmap,
+ unsigned int reg, u16 *val)
+{
+ return regmap_raw_read(regmap, reg, val, sizeof(u16));
+}
+
+static inline int mt6360_tcpc_write16(struct regmap *regmap,
+ unsigned int reg, u16 val)
+{
+ return regmap_raw_write(regmap, reg, &val, sizeof(u16));
+}
+
+static int mt6360_tcpc_init(struct tcpci *tcpci, struct tcpci_data *tdata)
+{
+ struct regmap *regmap = tdata->regmap;
+ int ret;
+
+ ret = regmap_write(regmap, MT6360_REG_SWRESET, 0x01);
+ if (ret)
+ return ret;
+
+ /* after reset command, wait 1~2ms to wait IC action */
+ usleep_range(1000, 2000);
+
+ /* write all alert to masked */
+ ret = mt6360_tcpc_write16(regmap, TCPC_ALERT_MASK, 0);
+ if (ret)
+ return ret;
+
+ /* config I2C timeout reset enable , and timeout to 200ms */
+ ret = regmap_write(regmap, MT6360_REG_I2CTORST, 0x8F);
+ if (ret)
+ return ret;
+
+ /* config CC Detect Debounce : 26.7*val us */
+ ret = regmap_write(regmap, MT6360_REG_DEBCTRL1, 0x10);
+ if (ret)
+ return ret;
+
+ /* DRP Toggle Cycle : 51.2 + 6.4*val ms */
+ ret = regmap_write(regmap, MT6360_REG_DRPCTRL1, 4);
+ if (ret)
+ return ret;
+
+ /* DRP Duyt Ctrl : dcSRC: /1024 */
+ ret = mt6360_tcpc_write16(regmap, MT6360_REG_DRPCTRL2, 330);
+ if (ret)
+ return ret;
+
+ /* Enable VCONN Current Limit function */
+ ret = regmap_update_bits(regmap, MT6360_REG_VCONNCTRL1, MT6360_VCONNCL_ENABLE,
+ MT6360_VCONNCL_ENABLE);
+ if (ret)
+ return ret;
+
+ /* Enable cc open 40ms when pmic send vsysuv signal */
+ ret = regmap_update_bits(regmap, MT6360_REG_RXCTRL2, MT6360_OPEN40M_ENABLE,
+ MT6360_OPEN40M_ENABLE);
+ if (ret)
+ return ret;
+
+ /* Enable Rpdet oneshot detection */
+ ret = regmap_update_bits(regmap, MT6360_REG_CTDCTRL2, MT6360_RPONESHOT_ENABLE,
+ MT6360_RPONESHOT_ENABLE);
+ if (ret)
+ return ret;
+
+ /* Set shipping mode off, AUTOIDLE on */
+ return regmap_write(regmap, MT6360_REG_MODECTRL2, 0x7A);
+}
+
+static irqreturn_t mt6360_irq(int irq, void *dev_id)
+{
+ struct mt6360_tcpc_info *mti = dev_id;
+
+ return tcpci_irq(mti->tcpci);
+}
+
+static int mt6360_tcpc_probe(struct platform_device *pdev)
+{
+ struct mt6360_tcpc_info *mti;
+ int ret;
+
+ mti = devm_kzalloc(&pdev->dev, sizeof(*mti), GFP_KERNEL);
+ if (!mti)
+ return -ENOMEM;
+
+ mti->dev = &pdev->dev;
+
+ mti->tdata.regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!mti->tdata.regmap) {
+ dev_err(&pdev->dev, "Failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ mti->irq = platform_get_irq_byname(pdev, "PD_IRQB");
+ if (mti->irq < 0)
+ return mti->irq;
+
+ mti->tdata.init = mt6360_tcpc_init;
+ mti->tcpci = tcpci_register_port(&pdev->dev, &mti->tdata);
+ if (IS_ERR(mti->tcpci)) {
+ dev_err(&pdev->dev, "Failed to register tcpci port\n");
+ return PTR_ERR(mti->tcpci);
+ }
+
+ ret = devm_request_threaded_irq(mti->dev, mti->irq, NULL, mt6360_irq, IRQF_ONESHOT,
+ dev_name(&pdev->dev), mti);
+ if (ret) {
+ dev_err(mti->dev, "Failed to register irq\n");
+ tcpci_unregister_port(mti->tcpci);
+ return ret;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+ platform_set_drvdata(pdev, mti);
+
+ return 0;
+}
+
+static int mt6360_tcpc_remove(struct platform_device *pdev)
+{
+ struct mt6360_tcpc_info *mti = platform_get_drvdata(pdev);
+
+ disable_irq(mti->irq);
+ tcpci_unregister_port(mti->tcpci);
+ return 0;
+}
+
+static int __maybe_unused mt6360_tcpc_suspend(struct device *dev)
+{
+ struct mt6360_tcpc_info *mti = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(mti->irq);
+
+ return 0;
+}
+
+static int __maybe_unused mt6360_tcpc_resume(struct device *dev)
+{
+ struct mt6360_tcpc_info *mti = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(mti->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mt6360_tcpc_pm_ops, mt6360_tcpc_suspend, mt6360_tcpc_resume);
+
+static const struct of_device_id __maybe_unused mt6360_tcpc_of_id[] = {
+ { .compatible = "mediatek,mt6360-tcpc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt6360_tcpc_of_id);
+
+static struct platform_driver mt6360_tcpc_driver = {
+ .driver = {
+ .name = "mt6360-tcpc",
+ .pm = &mt6360_tcpc_pm_ops,
+ .of_match_table = mt6360_tcpc_of_id,
+ },
+ .probe = mt6360_tcpc_probe,
+ .remove = mt6360_tcpc_remove,
+};
+module_platform_driver(mt6360_tcpc_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("MT6360 USB Type-C Port Controller Interface Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index b40db48..8333c80 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -8,8 +8,10 @@
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/device.h>
+#include <linux/hrtimer.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
+#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
@@ -28,7 +30,8 @@
#include <linux/usb/role.h>
#include <linux/usb/tcpm.h>
#include <linux/usb/typec_altmode.h>
-#include <linux/workqueue.h>
+
+#include <uapi/linux/sched/types.h>
#define FOREACH_STATE(S) \
S(INVALID_STATE), \
@@ -103,6 +106,13 @@
S(VCONN_SWAP_TURN_ON_VCONN), \
S(VCONN_SWAP_TURN_OFF_VCONN), \
\
+ S(FR_SWAP_SEND), \
+ S(FR_SWAP_SEND_TIMEOUT), \
+ S(FR_SWAP_SNK_SRC_TRANSITION_TO_OFF), \
+ S(FR_SWAP_SNK_SRC_NEW_SINK_READY), \
+ S(FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED), \
+ S(FR_SWAP_CANCEL), \
+ \
S(SNK_TRY), \
S(SNK_TRY_WAIT), \
S(SNK_TRY_WAIT_DEBOUNCE), \
@@ -124,6 +134,9 @@
S(GET_PPS_STATUS_SEND), \
S(GET_PPS_STATUS_SEND_TIMEOUT), \
\
+ S(GET_SINK_CAP), \
+ S(GET_SINK_CAP_TIMEOUT), \
+ \
S(ERROR_RECOVERY), \
S(PORT_RESET), \
S(PORT_RESET_WAIT_OFF)
@@ -159,11 +172,33 @@
PD_MSG_DATA_SOURCE_CAP,
};
+enum adev_actions {
+ ADEV_NONE = 0,
+ ADEV_NOTIFY_USB_AND_QUEUE_VDM,
+ ADEV_QUEUE_VDM,
+ ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL,
+ ADEV_ATTENTION,
+};
+
+/*
+ * Initial current capability of the new source when vSafe5V is applied during PD3.0 Fast Role Swap.
+ * Based on "Table 6-14 Fixed Supply PDO - Sink" of "USB Power Delivery Specification Revision 3.0,
+ * Version 1.2"
+ */
+enum frs_typec_current {
+ FRS_NOT_SUPPORTED,
+ FRS_DEFAULT_POWER,
+ FRS_5V_1P5A,
+ FRS_5V_3A,
+};
+
/* Events from low level driver */
#define TCPM_CC_EVENT BIT(0)
#define TCPM_VBUS_EVENT BIT(1)
#define TCPM_RESET_EVENT BIT(2)
+#define TCPM_FRS_EVENT BIT(3)
+#define TCPM_SOURCING_VBUS BIT(4)
#define LOG_BUFFER_ENTRIES 1024
#define LOG_BUFFER_ENTRY_SIZE 128
@@ -173,6 +208,8 @@
#define SVID_DISCOVERY_MAX 16
#define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX)
+#define GET_SINK_CAP_RETRY_MS 100
+
struct pd_mode_data {
int svid_index; /* current SVID index */
int nsvids;
@@ -210,7 +247,7 @@
struct device *dev;
struct mutex lock; /* tcpm state machine lock */
- struct workqueue_struct *wq;
+ struct kthread_worker *wq;
struct typec_capability typec_caps;
struct typec_port *typec_port;
@@ -254,15 +291,19 @@
enum tcpm_state prev_state;
enum tcpm_state state;
enum tcpm_state delayed_state;
- unsigned long delayed_runtime;
+ ktime_t delayed_runtime;
unsigned long delay_ms;
spinlock_t pd_event_lock;
u32 pd_events;
- struct work_struct event_work;
- struct delayed_work state_machine;
- struct delayed_work vdm_state_machine;
+ struct kthread_work event_work;
+ struct hrtimer state_machine_timer;
+ struct kthread_work state_machine;
+ struct hrtimer vdm_state_machine_timer;
+ struct kthread_work vdm_state_machine;
+ struct hrtimer enable_frs_timer;
+ struct kthread_work enable_frs;
bool state_machine_running;
struct completion tx_complete;
@@ -340,6 +381,12 @@
/* port belongs to a self powered device */
bool self_powered;
+ /* FRS */
+ enum frs_typec_current frs_current;
+
+ /* Sink caps have been queried */
+ bool sink_cap_done;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
struct mutex logbuffer_lock; /* log buffer access lock */
@@ -350,7 +397,7 @@
};
struct pd_rx_event {
- struct work_struct work;
+ struct kthread_work work;
struct tcpm_port *port;
struct pd_message msg;
};
@@ -391,6 +438,14 @@
((port)->try_src_count == 0 && (port)->try_role == TYPEC_SOURCE && \
(port)->port_type == TYPEC_PORT_DRP)
+#define tcpm_data_role_for_source(port) \
+ ((port)->typec_caps.data == TYPEC_PORT_UFP ? \
+ TYPEC_DEVICE : TYPEC_HOST)
+
+#define tcpm_data_role_for_sink(port) \
+ ((port)->typec_caps.data == TYPEC_PORT_DFP ? \
+ TYPEC_HOST : TYPEC_DEVICE)
+
static enum tcpm_state tcpm_default_state(struct tcpm_port *port)
{
if (port->port_type == TYPEC_PORT_DRP) {
@@ -398,9 +453,6 @@
return SNK_UNATTACHED;
else if (port->try_role == TYPEC_SOURCE)
return SRC_UNATTACHED;
- else if (port->tcpc->config &&
- port->tcpc->config->default_role == TYPEC_SINK)
- return SNK_UNATTACHED;
/* Fall through to return SRC_UNATTACHED */
} else if (port->port_type == TYPEC_PORT_SNK) {
return SNK_UNATTACHED;
@@ -408,12 +460,6 @@
return SRC_UNATTACHED;
}
-static inline
-struct tcpm_port *typec_cap_to_tcpm(const struct typec_capability *cap)
-{
- return container_of(cap, struct tcpm_port, typec_caps);
-}
-
static bool tcpm_port_is_disconnected(struct tcpm_port *port)
{
return (!port->attached && port->cc1 == TYPEC_CC_OPEN &&
@@ -816,10 +862,30 @@
else
orientation = TYPEC_ORIENTATION_REVERSE;
- if (data == TYPEC_HOST)
- usb_role = USB_ROLE_HOST;
- else
- usb_role = USB_ROLE_DEVICE;
+ if (port->typec_caps.data == TYPEC_PORT_DRD) {
+ if (data == TYPEC_HOST)
+ usb_role = USB_ROLE_HOST;
+ else
+ usb_role = USB_ROLE_DEVICE;
+ } else if (port->typec_caps.data == TYPEC_PORT_DFP) {
+ if (data == TYPEC_HOST) {
+ if (role == TYPEC_SOURCE)
+ usb_role = USB_ROLE_HOST;
+ else
+ usb_role = USB_ROLE_NONE;
+ } else {
+ return -ENOTSUPP;
+ }
+ } else {
+ if (data == TYPEC_DEVICE) {
+ if (role == TYPEC_SINK)
+ usb_role = USB_ROLE_DEVICE;
+ else
+ usb_role = USB_ROLE_NONE;
+ } else {
+ return -ENOTSUPP;
+ }
+ }
ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation);
if (ret < 0)
@@ -906,6 +972,37 @@
return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
}
+static void mod_tcpm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
+{
+ if (delay_ms) {
+ hrtimer_start(&port->state_machine_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
+ } else {
+ hrtimer_cancel(&port->state_machine_timer);
+ kthread_queue_work(port->wq, &port->state_machine);
+ }
+}
+
+static void mod_vdm_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
+{
+ if (delay_ms) {
+ hrtimer_start(&port->vdm_state_machine_timer, ms_to_ktime(delay_ms),
+ HRTIMER_MODE_REL);
+ } else {
+ hrtimer_cancel(&port->vdm_state_machine_timer);
+ kthread_queue_work(port->wq, &port->vdm_state_machine);
+ }
+}
+
+static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
+{
+ if (delay_ms) {
+ hrtimer_start(&port->enable_frs_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL);
+ } else {
+ hrtimer_cancel(&port->enable_frs_timer);
+ kthread_queue_work(port->wq, &port->enable_frs);
+ }
+}
+
static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
unsigned int delay_ms)
{
@@ -914,9 +1011,8 @@
tcpm_states[port->state], tcpm_states[state],
delay_ms);
port->delayed_state = state;
- mod_delayed_work(port->wq, &port->state_machine,
- msecs_to_jiffies(delay_ms));
- port->delayed_runtime = jiffies + msecs_to_jiffies(delay_ms);
+ mod_tcpm_delayed_work(port, delay_ms);
+ port->delayed_runtime = ktime_add(ktime_get(), ms_to_ktime(delay_ms));
port->delay_ms = delay_ms;
} else {
tcpm_log(port, "state change %s -> %s",
@@ -931,7 +1027,7 @@
* machine.
*/
if (!port->state_machine_running)
- mod_delayed_work(port->wq, &port->state_machine, 0);
+ mod_tcpm_delayed_work(port, 0);
}
}
@@ -952,7 +1048,7 @@
enum pd_msg_request message)
{
port->queued_message = message;
- mod_delayed_work(port->wq, &port->state_machine, 0);
+ mod_tcpm_delayed_work(port, 0);
}
/*
@@ -961,24 +1057,38 @@
static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
const u32 *data, int cnt)
{
+ WARN_ON(!mutex_is_locked(&port->lock));
+
+ /* Make sure we are not still processing a previous VDM packet */
+ WARN_ON(port->vdm_state > VDM_STATE_DONE);
+
port->vdo_count = cnt + 1;
port->vdo_data[0] = header;
memcpy(&port->vdo_data[1], data, sizeof(u32) * cnt);
/* Set ready, vdm state machine will actually send */
port->vdm_retries = 0;
port->vdm_state = VDM_STATE_READY;
+
+ mod_vdm_delayed_work(port, 0);
}
-static void svdm_consume_identity(struct tcpm_port *port, const __le32 *payload,
- int cnt)
+static void tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header,
+ const u32 *data, int cnt)
{
- u32 vdo = le32_to_cpu(payload[VDO_INDEX_IDH]);
- u32 product = le32_to_cpu(payload[VDO_INDEX_PRODUCT]);
+ mutex_lock(&port->lock);
+ tcpm_queue_vdm(port, header, data, cnt);
+ mutex_unlock(&port->lock);
+}
+
+static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt)
+{
+ u32 vdo = p[VDO_INDEX_IDH];
+ u32 product = p[VDO_INDEX_PRODUCT];
memset(&port->mode_data, 0, sizeof(port->mode_data));
port->partner_ident.id_header = vdo;
- port->partner_ident.cert_stat = le32_to_cpu(payload[VDO_INDEX_CSTAT]);
+ port->partner_ident.cert_stat = p[VDO_INDEX_CSTAT];
port->partner_ident.product = product;
typec_partner_set_identity(port->partner);
@@ -988,17 +1098,15 @@
PD_PRODUCT_PID(product), product & 0xffff);
}
-static bool svdm_consume_svids(struct tcpm_port *port, const __le32 *payload,
- int cnt)
+static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt)
{
struct pd_mode_data *pmdata = &port->mode_data;
int i;
for (i = 1; i < cnt; i++) {
- u32 p = le32_to_cpu(payload[i]);
u16 svid;
- svid = (p >> 16) & 0xffff;
+ svid = (p[i] >> 16) & 0xffff;
if (!svid)
return false;
@@ -1008,7 +1116,7 @@
pmdata->svids[pmdata->nsvids++] = svid;
tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid);
- svid = p & 0xffff;
+ svid = p[i] & 0xffff;
if (!svid)
return false;
@@ -1024,8 +1132,7 @@
return false;
}
-static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
- int cnt)
+static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt)
{
struct pd_mode_data *pmdata = &port->mode_data;
struct typec_altmode_desc *paltmode;
@@ -1042,7 +1149,7 @@
paltmode->svid = pmdata->svids[pmdata->svid_index];
paltmode->mode = i;
- paltmode->vdo = le32_to_cpu(payload[i]);
+ paltmode->vdo = p[i];
tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
pmdata->altmodes, paltmode->svid,
@@ -1061,30 +1168,28 @@
for (i = 0; i < modep->altmodes; i++) {
altmode = typec_partner_register_altmode(port->partner,
&modep->altmode_desc[i]);
- if (!altmode)
+ if (IS_ERR(altmode)) {
tcpm_log(port, "Failed to register partner SVID 0x%04x",
modep->altmode_desc[i].svid);
+ altmode = NULL;
+ }
port->partner_altmode[i] = altmode;
}
}
#define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
-static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
- u32 *response)
+static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
+ const u32 *p, int cnt, u32 *response,
+ enum adev_actions *adev_action)
{
- struct typec_altmode *adev;
struct typec_altmode *pdev;
struct pd_mode_data *modep;
- u32 p[PD_MAX_PAYLOAD];
int rlen = 0;
int cmd_type;
int cmd;
int i;
- for (i = 0; i < cnt; i++)
- p[i] = le32_to_cpu(payload[i]);
-
cmd_type = PD_VDO_CMDT(p[0]);
cmd = PD_VDO_CMD(p[0]);
@@ -1093,9 +1198,6 @@
modep = &port->mode_data;
- adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX,
- PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
-
pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX,
PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
@@ -1121,8 +1223,7 @@
break;
case CMD_ATTENTION:
/* Attention command does not have response */
- if (adev)
- typec_altmode_attention(adev, p[1]);
+ *adev_action = ADEV_ATTENTION;
return 0;
default:
break;
@@ -1145,13 +1246,13 @@
switch (cmd) {
case CMD_DISCOVER_IDENT:
/* 6.4.4.3.1 */
- svdm_consume_identity(port, payload, cnt);
+ svdm_consume_identity(port, p, cnt);
response[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID);
rlen = 1;
break;
case CMD_DISCOVER_SVID:
/* 6.4.4.3.2 */
- if (svdm_consume_svids(port, payload, cnt)) {
+ if (svdm_consume_svids(port, p, cnt)) {
response[0] = VDO(USB_SID_PD, 1,
CMD_DISCOVER_SVID);
rlen = 1;
@@ -1163,7 +1264,7 @@
break;
case CMD_DISCOVER_MODES:
/* 6.4.4.3.3 */
- svdm_consume_modes(port, payload, cnt);
+ svdm_consume_modes(port, p, cnt);
modep->svid_index++;
if (modep->svid_index < modep->nsvids) {
u16 svid = modep->svids[modep->svid_index];
@@ -1176,23 +1277,15 @@
case CMD_ENTER_MODE:
if (adev && pdev) {
typec_altmode_update_active(pdev, true);
-
- if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) {
- response[0] = VDO(adev->svid, 1,
- CMD_EXIT_MODE);
- response[0] |= VDO_OPOS(adev->mode);
- return 1;
- }
+ *adev_action = ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL;
}
return 0;
case CMD_EXIT_MODE:
if (adev && pdev) {
typec_altmode_update_active(pdev, false);
-
/* Back to USB Operation */
- WARN_ON(typec_altmode_notify(adev,
- TYPEC_STATE_USB,
- NULL));
+ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM;
+ return 0;
}
break;
default:
@@ -1203,11 +1296,8 @@
switch (cmd) {
case CMD_ENTER_MODE:
/* Back to USB Operation */
- if (adev)
- WARN_ON(typec_altmode_notify(adev,
- TYPEC_STATE_USB,
- NULL));
- break;
+ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM;
+ return 0;
default:
break;
}
@@ -1217,39 +1307,96 @@
}
/* Informing the alternate mode drivers about everything */
- if (adev)
- typec_altmode_vdm(adev, p[0], &p[1], cnt);
-
+ *adev_action = ADEV_QUEUE_VDM;
return rlen;
}
static void tcpm_handle_vdm_request(struct tcpm_port *port,
const __le32 *payload, int cnt)
{
- int rlen = 0;
+ enum adev_actions adev_action = ADEV_NONE;
+ struct typec_altmode *adev;
+ u32 p[PD_MAX_PAYLOAD];
u32 response[8] = { };
- u32 p0 = le32_to_cpu(payload[0]);
+ int i, rlen = 0;
+
+ for (i = 0; i < cnt; i++)
+ p[i] = le32_to_cpu(payload[i]);
+
+ adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX,
+ PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
if (port->vdm_state == VDM_STATE_BUSY) {
/* If UFP responded busy retry after timeout */
- if (PD_VDO_CMDT(p0) == CMDT_RSP_BUSY) {
+ if (PD_VDO_CMDT(p[0]) == CMDT_RSP_BUSY) {
port->vdm_state = VDM_STATE_WAIT_RSP_BUSY;
- port->vdo_retry = (p0 & ~VDO_CMDT_MASK) |
+ port->vdo_retry = (p[0] & ~VDO_CMDT_MASK) |
CMDT_INIT;
- mod_delayed_work(port->wq, &port->vdm_state_machine,
- msecs_to_jiffies(PD_T_VDM_BUSY));
+ mod_vdm_delayed_work(port, PD_T_VDM_BUSY);
return;
}
port->vdm_state = VDM_STATE_DONE;
}
- if (PD_VDO_SVDM(p0))
- rlen = tcpm_pd_svdm(port, payload, cnt, response);
+ if (PD_VDO_SVDM(p[0]))
+ rlen = tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action);
- if (rlen > 0) {
- tcpm_queue_vdm(port, response[0], &response[1], rlen - 1);
- mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
+ /*
+ * We are done with any state stored in the port struct now, except
+ * for any port struct changes done by the tcpm_queue_vdm() call
+ * below, which is a separate operation.
+ *
+ * So we can safely release the lock here; and we MUST release the
+ * lock here to avoid an AB BA lock inversion:
+ *
+ * If we keep the lock here then the lock ordering in this path is:
+ * 1. tcpm_pd_rx_handler take the tcpm port lock
+ * 2. One of the typec_altmode_* calls below takes the alt-mode's lock
+ *
+ * And we also have this ordering:
+ * 1. alt-mode driver takes the alt-mode's lock
+ * 2. alt-mode driver calls tcpm_altmode_enter which takes the
+ * tcpm port lock
+ *
+ * Dropping our lock here avoids this.
+ */
+ mutex_unlock(&port->lock);
+
+ if (adev) {
+ switch (adev_action) {
+ case ADEV_NONE:
+ break;
+ case ADEV_NOTIFY_USB_AND_QUEUE_VDM:
+ WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL));
+ typec_altmode_vdm(adev, p[0], &p[1], cnt);
+ break;
+ case ADEV_QUEUE_VDM:
+ typec_altmode_vdm(adev, p[0], &p[1], cnt);
+ break;
+ case ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL:
+ if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) {
+ response[0] = VDO(adev->svid, 1, CMD_EXIT_MODE);
+ response[0] |= VDO_OPOS(adev->mode);
+ rlen = 1;
+ }
+ break;
+ case ADEV_ATTENTION:
+ typec_altmode_attention(adev, p[1]);
+ break;
+ }
}
+
+ /*
+ * We must re-take the lock here to balance the unlock in
+ * tcpm_pd_rx_handler, note that no changes, other then the
+ * tcpm_queue_vdm call, are made while the lock is held again.
+ * All that is done after the call is unwinding the call stack until
+ * we return to tcpm_pd_rx_handler and do the unlock there.
+ */
+ mutex_lock(&port->lock);
+
+ if (rlen > 0)
+ tcpm_queue_vdm(port, response[0], &response[1], rlen - 1);
}
static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd,
@@ -1264,8 +1411,6 @@
header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ?
1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), cmd);
tcpm_queue_vdm(port, header, data, count);
-
- mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
}
static unsigned int vdm_ready_timeout(u32 vdm_hdr)
@@ -1332,8 +1477,7 @@
port->vdm_retries = 0;
port->vdm_state = VDM_STATE_BUSY;
timeout = vdm_ready_timeout(port->vdo_data[0]);
- mod_delayed_work(port->wq, &port->vdm_state_machine,
- timeout);
+ mod_vdm_delayed_work(port, timeout);
}
break;
case VDM_STATE_WAIT_RSP_BUSY:
@@ -1362,10 +1506,9 @@
}
}
-static void vdm_state_machine_work(struct work_struct *work)
+static void vdm_state_machine_work(struct kthread_work *work)
{
- struct tcpm_port *port = container_of(work, struct tcpm_port,
- vdm_state_machine.work);
+ struct tcpm_port *port = container_of(work, struct tcpm_port, vdm_state_machine);
enum vdm_states prev_state;
mutex_lock(&port->lock);
@@ -1503,19 +1646,15 @@
return 0;
}
-static int tcpm_altmode_enter(struct typec_altmode *altmode)
+static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo)
{
struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
u32 header;
- mutex_lock(&port->lock);
- header = VDO(altmode->svid, 1, CMD_ENTER_MODE);
+ header = VDO(altmode->svid, vdo ? 2 : 1, CMD_ENTER_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm(port, header, NULL, 0);
- mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
- mutex_unlock(&port->lock);
-
+ tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0);
return 0;
}
@@ -1524,14 +1663,10 @@
struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
u32 header;
- mutex_lock(&port->lock);
header = VDO(altmode->svid, 1, CMD_EXIT_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm(port, header, NULL, 0);
- mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
- mutex_unlock(&port->lock);
-
+ tcpm_queue_vdm_unlocked(port, header, NULL, 0);
return 0;
}
@@ -1540,10 +1675,7 @@
{
struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
- mutex_lock(&port->lock);
- tcpm_queue_vdm(port, header, data, count - 1);
- mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
- mutex_unlock(&port->lock);
+ tcpm_queue_vdm_unlocked(port, header, data, count - 1);
return 0;
}
@@ -1600,6 +1732,9 @@
unsigned int cnt = pd_header_cnt_le(msg->header);
unsigned int rev = pd_header_rev_le(msg->header);
unsigned int i;
+ enum frs_typec_current frs_current;
+ bool frs_enable;
+ int ret;
switch (type) {
case PD_DATA_SOURCE_CAP:
@@ -1669,7 +1804,21 @@
/* We don't do anything with this at the moment... */
for (i = 0; i < cnt; i++)
port->sink_caps[i] = le32_to_cpu(msg->payload[i]);
+
+ frs_current = (port->sink_caps[0] & PDO_FIXED_FRS_CURR_MASK) >>
+ PDO_FIXED_FRS_CURR_SHIFT;
+ frs_enable = frs_current && (frs_current <= port->frs_current);
+ tcpm_log(port,
+ "Port partner FRS capable partner_frs_current:%u port_frs_current:%u enable:%c",
+ frs_current, port->frs_current, frs_enable ? 'y' : 'n');
+ if (frs_enable) {
+ ret = port->tcpc->enable_frs(port->tcpc, true);
+ tcpm_log(port, "Enable FRS %s, ret:%d\n", ret ? "fail" : "success", ret);
+ }
+
port->nr_sink_caps = cnt;
+ port->sink_cap_done = true;
+ tcpm_set_state(port, SNK_READY, 0);
break;
case PD_DATA_VENDOR_DEF:
tcpm_handle_vdm_request(port, msg->payload, cnt);
@@ -1764,6 +1913,9 @@
case VCONN_SWAP_WAIT_FOR_VCONN:
tcpm_set_state(port, VCONN_SWAP_TURN_OFF_VCONN, 0);
break;
+ case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
+ tcpm_set_state(port, FR_SWAP_SNK_SRC_NEW_SINK_READY, 0);
+ break;
default:
break;
}
@@ -1803,6 +1955,13 @@
-EAGAIN : -EOPNOTSUPP);
tcpm_set_state(port, VCONN_SWAP_CANCEL, 0);
break;
+ case FR_SWAP_SEND:
+ tcpm_set_state(port, FR_SWAP_CANCEL, 0);
+ break;
+ case GET_SINK_CAP:
+ port->sink_cap_done = true;
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
default:
break;
}
@@ -1841,6 +2000,9 @@
case VCONN_SWAP_SEND:
tcpm_set_state(port, VCONN_SWAP_START, 0);
break;
+ case FR_SWAP_SEND:
+ tcpm_set_state(port, FR_SWAP_SNK_SRC_TRANSITION_TO_OFF, 0);
+ break;
default:
break;
}
@@ -1849,7 +2011,7 @@
tcpm_set_state(port, SOFT_RESET, 0);
break;
case PD_CTRL_DR_SWAP:
- if (port->port_type != TYPEC_PORT_DRP) {
+ if (port->typec_caps.data != TYPEC_PORT_DRD) {
tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
break;
}
@@ -1963,7 +2125,7 @@
}
}
-static void tcpm_pd_rx_handler(struct work_struct *work)
+static void tcpm_pd_rx_handler(struct kthread_work *work)
{
struct pd_rx_event *event = container_of(work,
struct pd_rx_event, work);
@@ -2025,10 +2187,10 @@
if (!event)
return;
- INIT_WORK(&event->work, tcpm_pd_rx_handler);
+ kthread_init_work(&event->work, tcpm_pd_rx_handler);
event->port = port;
memcpy(&event->msg, msg, sizeof(*msg));
- queue_work(port->wq, &event->work);
+ kthread_queue_work(port->wq, &event->work);
}
EXPORT_SYMBOL_GPL(tcpm_pd_receive);
@@ -2081,9 +2243,9 @@
} while (port->queued_message != PD_MSG_NONE);
if (port->delayed_state != INVALID_STATE) {
- if (time_is_after_jiffies(port->delayed_runtime)) {
- mod_delayed_work(port->wq, &port->state_machine,
- port->delayed_runtime - jiffies);
+ if (ktime_after(port->delayed_runtime, ktime_get())) {
+ mod_tcpm_delayed_work(port, ktime_to_ms(ktime_sub(port->delayed_runtime,
+ ktime_get())));
return true;
}
port->delayed_state = INVALID_STATE;
@@ -2653,7 +2815,8 @@
if (ret < 0)
return ret;
- ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST);
+ ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
+ tcpm_data_role_for_source(port));
if (ret < 0)
return ret;
@@ -2743,6 +2906,11 @@
port->try_src_count = 0;
port->try_snk_count = 0;
port->usb_type = POWER_SUPPLY_USB_TYPE_C;
+ power_supply_changed(port->psy);
+ port->nr_sink_caps = 0;
+ port->sink_cap_done = false;
+ if (port->tcpc->enable_frs)
+ port->tcpc->enable_frs(port->tcpc, false);
}
static void tcpm_detach(struct tcpm_port *port)
@@ -2753,6 +2921,11 @@
if (!port->attached)
return;
+ if (port->tcpc->set_bist_data) {
+ tcpm_log(port, "disable BIST MODE TESTDATA");
+ port->tcpc->set_bist_data(port->tcpc, false);
+ }
+
tcpm_reset_port(port);
}
@@ -2773,7 +2946,8 @@
if (ret < 0)
return ret;
- ret = tcpm_set_roles(port, true, TYPEC_SINK, TYPEC_DEVICE);
+ ret = tcpm_set_roles(port, true, TYPEC_SINK,
+ tcpm_data_role_for_sink(port));
if (ret < 0)
return ret;
@@ -2799,7 +2973,8 @@
if (port->attached)
return 0;
- ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST);
+ ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
+ tcpm_data_role_for_source(port));
if (ret < 0)
return ret;
@@ -3118,11 +3293,7 @@
tcpm_try_src(port) ? SRC_TRY
: SNK_ATTACHED,
0);
- else
- /* Wait for VBUS, but not forever */
- tcpm_set_state(port, PORT_RESET, PD_T_PS_SOURCE_ON);
break;
-
case SRC_TRY:
port->try_src_count++;
tcpm_set_cc(port, tcpm_rp_cc(port));
@@ -3209,10 +3380,9 @@
case SNK_DISCOVERY_DEBOUNCE_DONE:
if (!tcpm_port_is_disconnected(port) &&
tcpm_port_is_sink(port) &&
- time_is_after_jiffies(port->delayed_runtime)) {
+ ktime_after(port->delayed_runtime, ktime_get())) {
tcpm_set_state(port, SNK_DISCOVERY,
- jiffies_to_msecs(port->delayed_runtime -
- jiffies));
+ ktime_to_ms(ktime_sub(port->delayed_runtime, ktime_get())));
break;
}
tcpm_set_state(port, unattached_state(port), 0);
@@ -3285,10 +3455,9 @@
tcpm_swap_complete(port, 0);
tcpm_typec_connect(port);
tcpm_check_send_discover(port);
+ mod_enable_frs_delayed_work(port, 0);
tcpm_pps_complete(port, port->pps_status);
-
power_supply_changed(port->psy);
-
break;
/* Accessory states */
@@ -3312,9 +3481,13 @@
tcpm_set_state(port, HARD_RESET_START, 0);
break;
case HARD_RESET_START:
+ port->sink_cap_done = false;
+ if (port->tcpc->enable_frs)
+ port->tcpc->enable_frs(port->tcpc, false);
port->hard_reset_count++;
port->tcpc->set_pd_rx(port->tcpc, false);
tcpm_unregister_altmodes(port);
+ port->nr_sink_caps = 0;
port->send_discover = true;
if (port->pwr_role == TYPEC_SOURCE)
tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF,
@@ -3323,13 +3496,31 @@
tcpm_set_state(port, SNK_HARD_RESET_SINK_OFF, 0);
break;
case SRC_HARD_RESET_VBUS_OFF:
- tcpm_set_vconn(port, true);
+ /*
+ * 7.1.5 Response to Hard Resets
+ * Hard Reset Signaling indicates a communication failure has occurred and the
+ * Source Shall stop driving VCONN, Shall remove Rp from the VCONN pin and Shall
+ * drive VBUS to vSafe0V as shown in Figure 7-9.
+ */
+ tcpm_set_vconn(port, false);
tcpm_set_vbus(port, false);
tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE,
- TYPEC_HOST);
- tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
+ tcpm_data_role_for_source(port));
+ /*
+ * If tcpc fails to notify vbus off, TCPM will wait for PD_T_SAFE_0V +
+ * PD_T_SRC_RECOVER before turning vbus back on.
+ * From Table 7-12 Sequence Description for a Source Initiated Hard Reset:
+ * 4. Policy Engine waits tPSHardReset after sending Hard Reset Signaling and then
+ * tells the Device Policy Manager to instruct the power supply to perform a
+ * Hard Reset. The transition to vSafe0V Shall occur within tSafe0V (t2).
+ * 5. After tSrcRecover the Source applies power to VBUS in an attempt to
+ * re-establish communication with the Sink and resume USB Default Operation.
+ * The transition to vSafe5V Shall occur within tSrcTurnOn(t4).
+ */
+ tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SAFE_0V + PD_T_SRC_RECOVER);
break;
case SRC_HARD_RESET_VBUS_ON:
+ tcpm_set_vconn(port, true);
tcpm_set_vbus(port, true);
port->tcpc->set_pd_rx(port->tcpc, true);
tcpm_set_attached_state(port, true);
@@ -3341,7 +3532,7 @@
if (port->pd_capable)
tcpm_set_charge(port, false);
tcpm_set_roles(port, port->self_powered, TYPEC_SINK,
- TYPEC_DEVICE);
+ tcpm_data_role_for_sink(port));
/*
* VBUS may or may not toggle, depending on the adapter.
* If it doesn't toggle, transition to SNK_HARD_RESET_SINK_ON
@@ -3428,6 +3619,35 @@
tcpm_set_state(port, ready_state(port), 0);
break;
+ case FR_SWAP_SEND:
+ if (tcpm_pd_send_control(port, PD_CTRL_FR_SWAP)) {
+ tcpm_set_state(port, ERROR_RECOVERY, 0);
+ break;
+ }
+ tcpm_set_state_cond(port, FR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE);
+ break;
+ case FR_SWAP_SEND_TIMEOUT:
+ tcpm_set_state(port, ERROR_RECOVERY, 0);
+ break;
+ case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
+ tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_OFF);
+ break;
+ case FR_SWAP_SNK_SRC_NEW_SINK_READY:
+ if (port->vbus_source)
+ tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0);
+ else
+ tcpm_set_state(port, ERROR_RECOVERY, PD_T_RECEIVER_RESPONSE);
+ break;
+ case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
+ tcpm_set_pwr_role(port, TYPEC_SOURCE);
+ if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) {
+ tcpm_set_state(port, ERROR_RECOVERY, 0);
+ break;
+ }
+ tcpm_set_cc(port, tcpm_rp_cc(port));
+ tcpm_set_state(port, SRC_STARTUP, PD_T_SWAP_SRC_START);
+ break;
+
/* PR_Swap states */
case PR_SWAP_ACCEPT:
tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
@@ -3551,17 +3771,29 @@
else
tcpm_set_state(port, SNK_READY, 0);
break;
+ case FR_SWAP_CANCEL:
+ if (port->pwr_role == TYPEC_SOURCE)
+ tcpm_set_state(port, SRC_READY, 0);
+ else
+ tcpm_set_state(port, SNK_READY, 0);
+ break;
case BIST_RX:
switch (BDO_MODE_MASK(port->bist_request)) {
case BDO_MODE_CARRIER2:
tcpm_pd_transmit(port, TCPC_TX_BIST_MODE_2, NULL);
+ tcpm_set_state(port, unattached_state(port),
+ PD_T_BIST_CONT_MODE);
+ break;
+ case BDO_MODE_TESTDATA:
+ if (port->tcpc->set_bist_data) {
+ tcpm_log(port, "Enable BIST MODE TESTDATA");
+ port->tcpc->set_bist_data(port->tcpc, true);
+ }
break;
default:
break;
}
- /* Always switch to unattached state */
- tcpm_set_state(port, unattached_state(port), 0);
break;
case GET_STATUS_SEND:
tcpm_pd_send_control(port, PD_CTRL_GET_STATUS);
@@ -3579,6 +3811,14 @@
case GET_PPS_STATUS_SEND_TIMEOUT:
tcpm_set_state(port, ready_state(port), 0);
break;
+ case GET_SINK_CAP:
+ tcpm_pd_send_control(port, PD_CTRL_GET_SINK_CAP);
+ tcpm_set_state(port, GET_SINK_CAP_TIMEOUT, PD_T_SENDER_RESPONSE);
+ break;
+ case GET_SINK_CAP_TIMEOUT:
+ port->sink_cap_done = true;
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
case ERROR_RECOVERY:
tcpm_swap_complete(port, -EPROTO);
tcpm_pps_complete(port, -EPROTO);
@@ -3601,10 +3841,9 @@
}
}
-static void tcpm_state_machine_work(struct work_struct *work)
+static void tcpm_state_machine_work(struct kthread_work *work)
{
- struct tcpm_port *port = container_of(work, struct tcpm_port,
- state_machine.work);
+ struct tcpm_port *port = container_of(work, struct tcpm_port, state_machine);
enum tcpm_state prev_state;
mutex_lock(&port->lock);
@@ -3679,11 +3918,16 @@
tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
break;
case SRC_ATTACHED:
+ case SRC_STARTUP:
case SRC_SEND_CAPABILITIES:
case SRC_READY:
if (tcpm_port_is_disconnected(port) ||
- !tcpm_port_is_source(port))
- tcpm_set_state(port, SRC_UNATTACHED, 0);
+ !tcpm_port_is_source(port)) {
+ if (port->port_type == TYPEC_PORT_SRC)
+ tcpm_set_state(port, SRC_UNATTACHED, 0);
+ else
+ tcpm_set_state(port, SNK_UNATTACHED, 0);
+ }
break;
case SNK_UNATTACHED:
if (tcpm_port_is_sink(port))
@@ -3791,6 +4035,13 @@
* Ignore it.
*/
break;
+ case FR_SWAP_SEND:
+ case FR_SWAP_SEND_TIMEOUT:
+ case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
+ case FR_SWAP_SNK_SRC_NEW_SINK_READY:
+ case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
+ /* Do nothing, CC change expected */
+ break;
case PORT_RESET:
case PORT_RESET_WAIT_OFF:
@@ -3861,6 +4112,9 @@
case SRC_TRY_DEBOUNCE:
/* Do nothing, waiting for sink detection */
break;
+ case FR_SWAP_SNK_SRC_NEW_SINK_READY:
+ tcpm_set_state(port, FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED, 0);
+ break;
case PORT_RESET:
case PORT_RESET_WAIT_OFF:
@@ -3885,7 +4139,11 @@
tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0);
break;
case SRC_HARD_RESET_VBUS_OFF:
- tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, 0);
+ /*
+ * After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait
+ * tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V.
+ */
+ tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
break;
case HARD_RESET_SEND:
break;
@@ -3906,7 +4164,8 @@
case SNK_TRYWAIT_DEBOUNCE:
break;
case SNK_ATTACH_WAIT:
- tcpm_set_state(port, SNK_UNATTACHED, 0);
+ case SNK_DEBOUNCED:
+ /* Do nothing, as TCPM is still waiting for vbus to reaach VSAFE5V to connect */
break;
case SNK_NEGOTIATE_CAPABILITIES:
@@ -3936,6 +4195,14 @@
*/
break;
+ case FR_SWAP_SEND:
+ case FR_SWAP_SEND_TIMEOUT:
+ case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF:
+ case FR_SWAP_SNK_SRC_NEW_SINK_READY:
+ case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED:
+ /* Do nothing, vbus drop expected */
+ break;
+
default:
if (port->pwr_role == TYPEC_SINK &&
port->attached)
@@ -3947,6 +4214,9 @@
static void _tcpm_pd_hard_reset(struct tcpm_port *port)
{
tcpm_log_force(port, "Received hard reset");
+ if (port->bist_request == BDO_MODE_TESTDATA && port->tcpc->set_bist_data)
+ port->tcpc->set_bist_data(port->tcpc, false);
+
/*
* If we keep receiving hard reset requests, executing the hard reset
* must have failed. Revert to error recovery if that happens.
@@ -3957,7 +4227,7 @@
0);
}
-static void tcpm_pd_event_handler(struct work_struct *work)
+static void tcpm_pd_event_handler(struct kthread_work *work)
{
struct tcpm_port *port = container_of(work, struct tcpm_port,
event_work);
@@ -3987,6 +4257,25 @@
if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0)
_tcpm_cc_change(port, cc1, cc2);
}
+ if (events & TCPM_FRS_EVENT) {
+ if (port->state == SNK_READY)
+ tcpm_set_state(port, FR_SWAP_SEND, 0);
+ else
+ tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready");
+ }
+ if (events & TCPM_SOURCING_VBUS) {
+ tcpm_log(port, "sourcing vbus");
+ /*
+ * In fast role swap case TCPC autonomously sources vbus. Set vbus_source
+ * true as TCPM wouldn't have called tcpm_set_vbus.
+ *
+ * When vbus is sourced on the command on TCPM i.e. TCPM called
+ * tcpm_set_vbus to source vbus, vbus_source would already be true.
+ */
+ port->vbus_source = true;
+ _tcpm_pd_vbus_on(port);
+ }
+
spin_lock(&port->pd_event_lock);
}
spin_unlock(&port->pd_event_lock);
@@ -3998,7 +4287,7 @@
spin_lock(&port->pd_event_lock);
port->pd_events |= TCPM_CC_EVENT;
spin_unlock(&port->pd_event_lock);
- queue_work(port->wq, &port->event_work);
+ kthread_queue_work(port->wq, &port->event_work);
}
EXPORT_SYMBOL_GPL(tcpm_cc_change);
@@ -4007,7 +4296,7 @@
spin_lock(&port->pd_event_lock);
port->pd_events |= TCPM_VBUS_EVENT;
spin_unlock(&port->pd_event_lock);
- queue_work(port->wq, &port->event_work);
+ kthread_queue_work(port->wq, &port->event_work);
}
EXPORT_SYMBOL_GPL(tcpm_vbus_change);
@@ -4016,20 +4305,63 @@
spin_lock(&port->pd_event_lock);
port->pd_events = TCPM_RESET_EVENT;
spin_unlock(&port->pd_event_lock);
- queue_work(port->wq, &port->event_work);
+ kthread_queue_work(port->wq, &port->event_work);
}
EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset);
-static int tcpm_dr_set(const struct typec_capability *cap,
- enum typec_data_role data)
+void tcpm_sink_frs(struct tcpm_port *port)
{
- struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ spin_lock(&port->pd_event_lock);
+ port->pd_events |= TCPM_FRS_EVENT;
+ spin_unlock(&port->pd_event_lock);
+ kthread_queue_work(port->wq, &port->event_work);
+}
+EXPORT_SYMBOL_GPL(tcpm_sink_frs);
+
+void tcpm_sourcing_vbus(struct tcpm_port *port)
+{
+ spin_lock(&port->pd_event_lock);
+ port->pd_events |= TCPM_SOURCING_VBUS;
+ spin_unlock(&port->pd_event_lock);
+ kthread_queue_work(port->wq, &port->event_work);
+}
+EXPORT_SYMBOL_GPL(tcpm_sourcing_vbus);
+
+static void tcpm_enable_frs_work(struct kthread_work *work)
+{
+ struct tcpm_port *port = container_of(work, struct tcpm_port, enable_frs);
+
+ mutex_lock(&port->lock);
+ /* Not FRS capable */
+ if (!port->connected || port->port_type != TYPEC_PORT_DRP ||
+ port->pwr_opmode != TYPEC_PWR_MODE_PD ||
+ !port->tcpc->enable_frs ||
+ /* Sink caps queried */
+ port->sink_cap_done || port->negotiated_rev < PD_REV30)
+ goto unlock;
+
+ /* Send when the state machine is idle */
+ if (port->state != SNK_READY || port->vdm_state != VDM_STATE_DONE || port->send_discover)
+ goto resched;
+
+ tcpm_set_state(port, GET_SINK_CAP, 0);
+ port->sink_cap_done = true;
+
+resched:
+ mod_enable_frs_delayed_work(port, GET_SINK_CAP_RETRY_MS);
+unlock:
+ mutex_unlock(&port->lock);
+}
+
+static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data)
+{
+ struct tcpm_port *port = typec_get_drvdata(p);
int ret;
mutex_lock(&port->swap_lock);
mutex_lock(&port->lock);
- if (port->port_type != TYPEC_PORT_DRP) {
+ if (port->typec_caps.data != TYPEC_PORT_DRD) {
ret = -EINVAL;
goto port_unlock;
}
@@ -4088,10 +4420,9 @@
return ret;
}
-static int tcpm_pr_set(const struct typec_capability *cap,
- enum typec_role role)
+static int tcpm_pr_set(struct typec_port *p, enum typec_role role)
{
- struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ struct tcpm_port *port = typec_get_drvdata(p);
int ret;
mutex_lock(&port->swap_lock);
@@ -4132,10 +4463,9 @@
return ret;
}
-static int tcpm_vconn_set(const struct typec_capability *cap,
- enum typec_role role)
+static int tcpm_vconn_set(struct typec_port *p, enum typec_role role)
{
- struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ struct tcpm_port *port = typec_get_drvdata(p);
int ret;
mutex_lock(&port->swap_lock);
@@ -4172,16 +4502,16 @@
return ret;
}
-static int tcpm_try_role(const struct typec_capability *cap, int role)
+static int tcpm_try_role(struct typec_port *p, int role)
{
- struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ struct tcpm_port *port = typec_get_drvdata(p);
struct tcpc_dev *tcpc = port->tcpc;
int ret = 0;
mutex_lock(&port->lock);
if (tcpc->try_role)
ret = tcpc->try_role(tcpc, role);
- if (!ret && (!tcpc->config || !tcpc->config->try_role_hw))
+ if (!ret)
port->try_role = role;
port->try_src_count = 0;
port->try_snk_count = 0;
@@ -4381,10 +4711,9 @@
tcpm_set_state(port, PORT_RESET, 0);
}
-static int tcpm_port_type_set(const struct typec_capability *cap,
- enum typec_port_type type)
+static int tcpm_port_type_set(struct typec_port *p, enum typec_port_type type)
{
- struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ struct tcpm_port *port = typec_get_drvdata(p);
mutex_lock(&port->lock);
if (type == port->port_type)
@@ -4409,6 +4738,14 @@
return 0;
}
+static const struct typec_operations tcpm_ops = {
+ .try_role = tcpm_try_role,
+ .dr_set = tcpm_dr_set,
+ .pr_set = tcpm_pr_set,
+ .vconn_set = tcpm_vconn_set,
+ .port_type_set = tcpm_port_type_set
+};
+
void tcpm_tcpc_reset(struct tcpm_port *port)
{
mutex_lock(&port->lock);
@@ -4418,40 +4755,12 @@
}
EXPORT_SYMBOL_GPL(tcpm_tcpc_reset);
-static int tcpm_copy_pdos(u32 *dest_pdo, const u32 *src_pdo,
- unsigned int nr_pdo)
-{
- unsigned int i;
-
- if (nr_pdo > PDO_MAX_OBJECTS)
- nr_pdo = PDO_MAX_OBJECTS;
-
- for (i = 0; i < nr_pdo; i++)
- dest_pdo[i] = src_pdo[i];
-
- return nr_pdo;
-}
-
-static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo,
- unsigned int nr_vdo)
-{
- unsigned int i;
-
- if (nr_vdo > VDO_MAX_OBJECTS)
- nr_vdo = VDO_MAX_OBJECTS;
-
- for (i = 0; i < nr_vdo; i++)
- dest_vdo[i] = src_vdo[i];
-
- return nr_vdo;
-}
-
static int tcpm_fw_get_caps(struct tcpm_port *port,
struct fwnode_handle *fwnode)
{
const char *cap_str;
int ret;
- u32 mw;
+ u32 mw, frs_current;
if (!fwnode)
return -EINVAL;
@@ -4520,6 +4829,13 @@
port->self_powered = fwnode_property_read_bool(fwnode, "self-powered");
+ /* FRS can only be supported byb DRP ports */
+ if (port->port_type == TYPEC_PORT_DRP) {
+ ret = fwnode_property_read_u32(fwnode, "frs-typec-current", &frs_current);
+ if (ret >= 0 && frs_current <= FRS_5V_3A)
+ port->frs_current = frs_current;
+ }
+
return 0;
}
@@ -4748,35 +5064,34 @@
return PTR_ERR_OR_ZERO(port->psy);
}
-static int tcpm_copy_caps(struct tcpm_port *port,
- const struct tcpc_config *tcfg)
+static enum hrtimer_restart state_machine_timer_handler(struct hrtimer *timer)
{
- if (tcpm_validate_caps(port, tcfg->src_pdo, tcfg->nr_src_pdo) ||
- tcpm_validate_caps(port, tcfg->snk_pdo, tcfg->nr_snk_pdo))
- return -EINVAL;
+ struct tcpm_port *port = container_of(timer, struct tcpm_port, state_machine_timer);
- port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcfg->src_pdo,
- tcfg->nr_src_pdo);
- port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcfg->snk_pdo,
- tcfg->nr_snk_pdo);
+ kthread_queue_work(port->wq, &port->state_machine);
+ return HRTIMER_NORESTART;
+}
- port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcfg->snk_vdo,
- tcfg->nr_snk_vdo);
+static enum hrtimer_restart vdm_state_machine_timer_handler(struct hrtimer *timer)
+{
+ struct tcpm_port *port = container_of(timer, struct tcpm_port, vdm_state_machine_timer);
- port->operating_snk_mw = tcfg->operating_snk_mw;
+ kthread_queue_work(port->wq, &port->vdm_state_machine);
+ return HRTIMER_NORESTART;
+}
- port->typec_caps.prefer_role = tcfg->default_role;
- port->typec_caps.type = tcfg->type;
- port->typec_caps.data = tcfg->data;
- port->self_powered = tcfg->self_powered;
+static enum hrtimer_restart enable_frs_timer_handler(struct hrtimer *timer)
+{
+ struct tcpm_port *port = container_of(timer, struct tcpm_port, enable_frs_timer);
- return 0;
+ kthread_queue_work(port->wq, &port->enable_frs);
+ return HRTIMER_NORESTART;
}
struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
{
struct tcpm_port *port;
- int i, err;
+ int err;
if (!dev || !tcpc ||
!tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc ||
@@ -4794,12 +5109,21 @@
mutex_init(&port->lock);
mutex_init(&port->swap_lock);
- port->wq = create_singlethread_workqueue(dev_name(dev));
- if (!port->wq)
- return ERR_PTR(-ENOMEM);
- INIT_DELAYED_WORK(&port->state_machine, tcpm_state_machine_work);
- INIT_DELAYED_WORK(&port->vdm_state_machine, vdm_state_machine_work);
- INIT_WORK(&port->event_work, tcpm_pd_event_handler);
+ port->wq = kthread_create_worker(0, dev_name(dev));
+ if (IS_ERR(port->wq))
+ return ERR_CAST(port->wq);
+ sched_set_fifo(port->wq->task);
+
+ kthread_init_work(&port->state_machine, tcpm_state_machine_work);
+ kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work);
+ kthread_init_work(&port->event_work, tcpm_pd_event_handler);
+ kthread_init_work(&port->enable_frs, tcpm_enable_frs_work);
+ hrtimer_init(&port->state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ port->state_machine_timer.function = state_machine_timer_handler;
+ hrtimer_init(&port->vdm_state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ port->vdm_state_machine_timer.function = vdm_state_machine_timer_handler;
+ hrtimer_init(&port->enable_frs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ port->enable_frs_timer.function = enable_frs_timer_handler;
spin_lock_init(&port->pd_event_lock);
@@ -4809,24 +5133,17 @@
tcpm_debugfs_init(port);
err = tcpm_fw_get_caps(port, tcpc->fwnode);
- if ((err < 0) && tcpc->config)
- err = tcpm_copy_caps(port, tcpc->config);
if (err < 0)
goto out_destroy_wq;
- if (!tcpc->config || !tcpc->config->try_role_hw)
- port->try_role = port->typec_caps.prefer_role;
- else
- port->try_role = TYPEC_NO_PREFERRED_ROLE;
+ port->try_role = port->typec_caps.prefer_role;
port->typec_caps.fwnode = tcpc->fwnode;
port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */
- port->typec_caps.dr_set = tcpm_dr_set;
- port->typec_caps.pr_set = tcpm_pr_set;
- port->typec_caps.vconn_set = tcpm_vconn_set;
- port->typec_caps.try_role = tcpm_try_role;
- port->typec_caps.port_type_set = tcpm_port_type_set;
+ port->typec_caps.driver_data = port;
+ port->typec_caps.ops = &tcpm_ops;
+ port->typec_caps.orientation_aware = 1;
port->partner_desc.identity = &port->partner_ident;
port->port_type = port->typec_caps.type;
@@ -4848,29 +5165,6 @@
goto out_role_sw_put;
}
- if (tcpc->config && tcpc->config->alt_modes) {
- const struct typec_altmode_desc *paltmode = tcpc->config->alt_modes;
-
- i = 0;
- while (paltmode->svid && i < ARRAY_SIZE(port->port_altmode)) {
- struct typec_altmode *alt;
-
- alt = typec_port_register_altmode(port->typec_port,
- paltmode);
- if (IS_ERR(alt)) {
- tcpm_log(port,
- "%s: failed to register port alternate mode 0x%x",
- dev_name(dev), paltmode->svid);
- break;
- }
- typec_altmode_set_drvdata(alt, port);
- alt->ops = &tcpm_altmode_ops;
- port->port_altmode[i] = alt;
- i++;
- paltmode++;
- }
- }
-
mutex_lock(&port->lock);
tcpm_init(port);
mutex_unlock(&port->lock);
@@ -4882,7 +5176,7 @@
usb_role_switch_put(port->role_sw);
out_destroy_wq:
tcpm_debugfs_exit(port);
- destroy_workqueue(port->wq);
+ kthread_destroy_worker(port->wq);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(tcpm_register_port);
@@ -4891,13 +5185,17 @@
{
int i;
+ hrtimer_cancel(&port->enable_frs_timer);
+ hrtimer_cancel(&port->vdm_state_machine_timer);
+ hrtimer_cancel(&port->state_machine_timer);
+
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);
usb_role_switch_put(port->role_sw);
tcpm_debugfs_exit(port);
- destroy_workqueue(port->wq);
+ kthread_destroy_worker(port->wq);
}
EXPORT_SYMBOL_GPL(tcpm_unregister_port);
diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c
index a38d140..6cb5c8e 100644
--- a/drivers/usb/typec/tps6598x.c
+++ b/drivers/usb/typec/tps6598x.c
@@ -12,6 +12,7 @@
#include <linux/regmap.h>
#include <linux/interrupt.h>
#include <linux/usb/typec.h>
+#include <linux/usb/role.h>
/* Register offsets */
#define TPS_REG_VID 0x00
@@ -61,7 +62,6 @@
struct tps6598x_rx_identity_reg {
u8 status;
struct usb_pd_identity identity;
- u32 vdo[3];
} __packed;
/* Standard Task return codes */
@@ -94,12 +94,12 @@
struct typec_port *port;
struct typec_partner *partner;
struct usb_pd_identity partner_identity;
- struct typec_capability typec_cap;
+ struct usb_role_switch *role_sw;
};
/*
* Max data bytes for Data1, Data2, and other registers. See ch 1.3.2:
- * http://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf
+ * https://www.ti.com/lit/ug/slvuan1a/slvuan1a.pdf
*/
#define TPS_MAX_LEN 64
@@ -109,7 +109,7 @@
u8 data[TPS_MAX_LEN + 1];
int ret;
- if (WARN_ON(len + 1 > sizeof(data)))
+ if (len + 1 > sizeof(data))
return -EINVAL;
if (!tps->i2c_protocol)
@@ -191,6 +191,23 @@
return 0;
}
+static void tps6598x_set_data_role(struct tps6598x *tps,
+ enum typec_data_role role, bool connected)
+{
+ enum usb_role role_val;
+
+ if (role == TYPEC_HOST)
+ role_val = USB_ROLE_HOST;
+ else
+ role_val = USB_ROLE_DEVICE;
+
+ if (!connected)
+ role_val = USB_ROLE_NONE;
+
+ usb_role_switch_set_role(tps->role_sw, role_val);
+ typec_set_data_role(tps->port, role);
+}
+
static int tps6598x_connect(struct tps6598x *tps, u32 status)
{
struct typec_partner_desc desc;
@@ -221,7 +238,7 @@
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));
+ tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), true);
tps->partner = typec_register_partner(tps->port, &desc);
if (IS_ERR(tps->partner))
@@ -241,7 +258,7 @@
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));
+ tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), false);
}
static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd,
@@ -307,11 +324,10 @@
return 0;
}
-static int
-tps6598x_dr_set(const struct typec_capability *cap, enum typec_data_role role)
+static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role)
{
- struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap);
const char *cmd = (role == TYPEC_DEVICE) ? "SWUF" : "SWDF";
+ struct tps6598x *tps = typec_get_drvdata(port);
u32 status;
int ret;
@@ -330,7 +346,7 @@
goto out_unlock;
}
- typec_set_data_role(tps->port, role);
+ tps6598x_set_data_role(tps, role, true);
out_unlock:
mutex_unlock(&tps->lock);
@@ -338,11 +354,10 @@
return ret;
}
-static int
-tps6598x_pr_set(const struct typec_capability *cap, enum typec_role role)
+static int tps6598x_pr_set(struct typec_port *port, enum typec_role role)
{
- struct tps6598x *tps = container_of(cap, struct tps6598x, typec_cap);
const char *cmd = (role == TYPEC_SINK) ? "SWSk" : "SWSr";
+ struct tps6598x *tps = typec_get_drvdata(port);
u32 status;
int ret;
@@ -369,6 +384,11 @@
return ret;
}
+static const struct typec_operations tps6598x_ops = {
+ .dr_set = tps6598x_dr_set,
+ .pr_set = tps6598x_pr_set,
+};
+
static irqreturn_t tps6598x_interrupt(int irq, void *data)
{
struct tps6598x *tps = data;
@@ -448,7 +468,9 @@
static int tps6598x_probe(struct i2c_client *client)
{
+ struct typec_capability typec_cap = { };
struct tps6598x *tps;
+ struct fwnode_handle *fwnode;
u32 status;
u32 conf;
u32 vid;
@@ -492,42 +514,57 @@
if (ret < 0)
return ret;
- 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->typec_cap.pr_set = tps6598x_pr_set;
- tps->typec_cap.dr_set = tps6598x_dr_set;
+ fwnode = device_get_named_child_node(&client->dev, "connector");
+ if (!fwnode)
+ return -ENODEV;
+
+ tps->role_sw = fwnode_usb_role_switch_get(fwnode);
+ if (IS_ERR(tps->role_sw)) {
+ ret = PTR_ERR(tps->role_sw);
+ goto err_fwnode_put;
+ }
+
+ typec_cap.revision = USB_TYPEC_REV_1_2;
+ typec_cap.pd_revision = 0x200;
+ typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
+ typec_cap.driver_data = tps;
+ typec_cap.ops = &tps6598x_ops;
+ typec_cap.fwnode = fwnode;
switch (TPS_SYSCONF_PORTINFO(conf)) {
case TPS_PORTINFO_SINK_ACCESSORY:
case TPS_PORTINFO_SINK:
- tps->typec_cap.type = TYPEC_PORT_SNK;
- tps->typec_cap.data = TYPEC_PORT_UFP;
+ typec_cap.type = TYPEC_PORT_SNK;
+ typec_cap.data = TYPEC_PORT_UFP;
break;
case TPS_PORTINFO_DRP_UFP_DRD:
case TPS_PORTINFO_DRP_DFP_DRD:
- tps->typec_cap.type = TYPEC_PORT_DRP;
- tps->typec_cap.data = TYPEC_PORT_DRD;
+ typec_cap.type = TYPEC_PORT_DRP;
+ typec_cap.data = TYPEC_PORT_DRD;
break;
case TPS_PORTINFO_DRP_UFP:
- tps->typec_cap.type = TYPEC_PORT_DRP;
- tps->typec_cap.data = TYPEC_PORT_UFP;
+ typec_cap.type = TYPEC_PORT_DRP;
+ typec_cap.data = TYPEC_PORT_UFP;
break;
case TPS_PORTINFO_DRP_DFP:
- tps->typec_cap.type = TYPEC_PORT_DRP;
- tps->typec_cap.data = TYPEC_PORT_DFP;
+ typec_cap.type = TYPEC_PORT_DRP;
+ typec_cap.data = TYPEC_PORT_DFP;
break;
case TPS_PORTINFO_SOURCE:
- tps->typec_cap.type = TYPEC_PORT_SRC;
- tps->typec_cap.data = TYPEC_PORT_DFP;
+ typec_cap.type = TYPEC_PORT_SRC;
+ typec_cap.data = TYPEC_PORT_DFP;
break;
default:
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_role_put;
}
- tps->port = typec_register_port(&client->dev, &tps->typec_cap);
- if (IS_ERR(tps->port))
- return PTR_ERR(tps->port);
+ tps->port = typec_register_port(&client->dev, &typec_cap);
+ if (IS_ERR(tps->port)) {
+ ret = PTR_ERR(tps->port);
+ goto err_role_put;
+ }
+ fwnode_handle_put(fwnode);
if (status & TPS_STATUS_PLUG_PRESENT) {
ret = tps6598x_connect(tps, status);
@@ -542,12 +579,19 @@
if (ret) {
tps6598x_disconnect(tps, 0);
typec_unregister_port(tps->port);
- return ret;
+ goto err_role_put;
}
i2c_set_clientdata(client, tps);
return 0;
+
+err_role_put:
+ usb_role_switch_put(tps->role_sw);
+err_fwnode_put:
+ fwnode_handle_put(fwnode);
+
+ return ret;
}
static int tps6598x_remove(struct i2c_client *client)
@@ -556,10 +600,17 @@
tps6598x_disconnect(tps, 0);
typec_unregister_port(tps->port);
+ usb_role_switch_put(tps->role_sw);
return 0;
}
+static const struct of_device_id tps6598x_of_match[] = {
+ { .compatible = "ti,tps6598x", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, tps6598x_of_match);
+
static const struct i2c_device_id tps6598x_id[] = {
{ "tps6598x" },
{ }
@@ -569,6 +620,7 @@
static struct i2c_driver tps6598x_i2c_driver = {
.driver = {
.name = "tps6598x",
+ .of_match_table = tps6598x_of_match,
},
.probe_new = tps6598x_probe,
.remove = tps6598x_remove,
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index 15c2ac7..2192d7c 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -18,7 +18,7 @@
for every supported interface method.
The UCSI specification can be downloaded from:
- http://www.intel.com/content/www/us/en/io/universal-serial-bus/usb-type-c-ucsi-spec.html
+ https://www.intel.com/content/www/us/en/io/universal-serial-bus/usb-type-c-ucsi-spec.html
To compile the driver as a module, choose M here: the module will be
called typec_ucsi.
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index b35e15a..8a8eb5c 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -7,6 +7,10 @@
typec_ucsi-$(CONFIG_TRACING) += trace.o
+ifneq ($(CONFIG_POWER_SUPPLY),)
+ typec_ucsi-y += psy.o
+endif
+
ifneq ($(CONFIG_TYPEC_DP_ALTMODE),)
typec_ucsi-y += displayport.o
endif
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index 166c2aa..261131c 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -45,10 +45,11 @@
* -EOPNOTSUPP.
*/
-static int ucsi_displayport_enter(struct typec_altmode *alt)
+static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
{
struct ucsi_dp *dp = typec_altmode_get_drvdata(alt);
- struct ucsi_control ctrl;
+ struct ucsi *ucsi = dp->con->ucsi;
+ u64 command;
u8 cur = 0;
int ret;
@@ -59,25 +60,21 @@
dev_warn(&p->dev,
"firmware doesn't support alternate mode overriding\n");
- mutex_unlock(&dp->con->lock);
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ goto err_unlock;
}
- UCSI_CMD_GET_CURRENT_CAM(ctrl, dp->con->num);
- ret = ucsi_send_command(dp->con->ucsi, &ctrl, &cur, sizeof(cur));
+ command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num);
+ ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur));
if (ret < 0) {
- if (dp->con->ucsi->ppm->data->version > 0x0100) {
- mutex_unlock(&dp->con->lock);
- return ret;
- }
+ if (ucsi->version > 0x0100)
+ goto err_unlock;
cur = 0xff;
}
if (cur != 0xff) {
- mutex_unlock(&dp->con->lock);
- if (dp->con->port_altmode[cur] == alt)
- return 0;
- return -EBUSY;
+ ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
+ goto err_unlock;
}
/*
@@ -94,16 +91,17 @@
dp->vdo_size = 1;
schedule_work(&dp->work);
-
+ ret = 0;
+err_unlock:
mutex_unlock(&dp->con->lock);
- return 0;
+ return ret;
}
static int ucsi_displayport_exit(struct typec_altmode *alt)
{
struct ucsi_dp *dp = typec_altmode_get_drvdata(alt);
- struct ucsi_control ctrl;
+ u64 command;
int ret = 0;
mutex_lock(&dp->con->lock);
@@ -117,8 +115,8 @@
goto out_unlock;
}
- ctrl.raw_cmd = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0);
- ret = ucsi_send_command(dp->con->ucsi, &ctrl, NULL, 0);
+ command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0);
+ ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0);
if (ret < 0)
goto out_unlock;
@@ -172,14 +170,14 @@
static int ucsi_displayport_configure(struct ucsi_dp *dp)
{
u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
- struct ucsi_control ctrl;
+ u64 command;
if (!dp->override)
return 0;
- ctrl.raw_cmd = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins);
+ command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins);
- return ucsi_send_command(dp->con->ucsi, &ctrl, NULL, 0);
+ return ucsi_send_command(dp->con->ucsi, command, NULL, 0);
}
static int ucsi_displayport_vdm(struct typec_altmode *alt,
@@ -290,8 +288,6 @@
struct typec_altmode *alt;
struct ucsi_dp *dp;
- mutex_lock(&con->lock);
-
/* We can't rely on the firmware with the capabilities. */
desc->vdo |= DP_CAP_DP_SIGNALING | DP_CAP_RECEPTACLE;
@@ -300,15 +296,12 @@
desc->vdo |= all_assignments << 16;
alt = typec_port_register_altmode(con->port, desc);
- if (IS_ERR(alt)) {
- mutex_unlock(&con->lock);
+ if (IS_ERR(alt))
return alt;
- }
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
if (!dp) {
typec_unregister_altmode(alt);
- mutex_unlock(&con->lock);
return ERR_PTR(-ENOMEM);
}
@@ -321,7 +314,5 @@
alt->ops = &ucsi_displayport_ops;
typec_altmode_set_drvdata(alt, dp);
- mutex_unlock(&con->lock);
-
return alt;
}
diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c
new file mode 100644
index 0000000..571a51e
--- /dev/null
+++ b/drivers/usb/typec/ucsi/psy.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Power Supply for UCSI
+ *
+ * Copyright (C) 2020, Intel Corporation
+ * Author: K V, Abhilash <abhilash.k.v@intel.com>
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/property.h>
+#include <linux/usb/pd.h>
+
+#include "ucsi.h"
+
+/* Power Supply access to expose source power information */
+enum ucsi_psy_online_states {
+ UCSI_PSY_OFFLINE = 0,
+ UCSI_PSY_FIXED_ONLINE,
+ UCSI_PSY_PROG_ONLINE,
+};
+
+static enum power_supply_property ucsi_psy_props[] = {
+ POWER_SUPPLY_PROP_USB_TYPE,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int ucsi_psy_get_online(struct ucsi_connector *con,
+ union power_supply_propval *val)
+{
+ val->intval = UCSI_PSY_OFFLINE;
+ if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
+ (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
+ val->intval = UCSI_PSY_FIXED_ONLINE;
+ return 0;
+}
+
+static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
+ union power_supply_propval *val)
+{
+ u32 pdo;
+
+ switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+ case UCSI_CONSTAT_PWR_OPMODE_PD:
+ pdo = con->src_pdos[0];
+ val->intval = pdo_fixed_voltage(pdo) * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+ case UCSI_CONSTAT_PWR_OPMODE_BC:
+ case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+ val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+ break;
+ default:
+ val->intval = 0;
+ break;
+ }
+ return 0;
+}
+
+static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
+ union power_supply_propval *val)
+{
+ u32 pdo;
+
+ switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+ case UCSI_CONSTAT_PWR_OPMODE_PD:
+ if (con->num_pdos > 0) {
+ pdo = con->src_pdos[con->num_pdos - 1];
+ val->intval = pdo_fixed_voltage(pdo) * 1000;
+ } else {
+ val->intval = 0;
+ }
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+ case UCSI_CONSTAT_PWR_OPMODE_BC:
+ case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+ val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+ break;
+ default:
+ val->intval = 0;
+ break;
+ }
+ return 0;
+}
+
+static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
+ union power_supply_propval *val)
+{
+ int index;
+ u32 pdo;
+
+ switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+ case UCSI_CONSTAT_PWR_OPMODE_PD:
+ index = rdo_index(con->rdo);
+ if (index > 0) {
+ pdo = con->src_pdos[index - 1];
+ val->intval = pdo_fixed_voltage(pdo) * 1000;
+ } else {
+ val->intval = 0;
+ }
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+ case UCSI_CONSTAT_PWR_OPMODE_BC:
+ case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+ val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+ break;
+ default:
+ val->intval = 0;
+ break;
+ }
+ return 0;
+}
+
+static int ucsi_psy_get_current_max(struct ucsi_connector *con,
+ union power_supply_propval *val)
+{
+ u32 pdo;
+
+ switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+ case UCSI_CONSTAT_PWR_OPMODE_PD:
+ if (con->num_pdos > 0) {
+ pdo = con->src_pdos[con->num_pdos - 1];
+ val->intval = pdo_max_current(pdo) * 1000;
+ } else {
+ val->intval = 0;
+ }
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+ val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+ val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_BC:
+ case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+ /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
+ default:
+ val->intval = 0;
+ break;
+ }
+ return 0;
+}
+
+static int ucsi_psy_get_current_now(struct ucsi_connector *con,
+ union power_supply_propval *val)
+{
+ u16 flags = con->status.flags;
+
+ if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
+ val->intval = rdo_op_current(con->rdo) * 1000;
+ else
+ val->intval = 0;
+ return 0;
+}
+
+static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
+ union power_supply_propval *val)
+{
+ u16 flags = con->status.flags;
+
+ val->intval = POWER_SUPPLY_USB_TYPE_C;
+ if (flags & UCSI_CONSTAT_CONNECTED &&
+ UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
+ val->intval = POWER_SUPPLY_USB_TYPE_PD;
+
+ return 0;
+}
+
+static int ucsi_psy_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ucsi_connector *con = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ return ucsi_psy_get_usb_type(con, val);
+ case POWER_SUPPLY_PROP_ONLINE:
+ return ucsi_psy_get_online(con, val);
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ return ucsi_psy_get_voltage_min(con, val);
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ return ucsi_psy_get_voltage_max(con, val);
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ return ucsi_psy_get_voltage_now(con, val);
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ return ucsi_psy_get_current_max(con, val);
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ return ucsi_psy_get_current_now(con, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static enum power_supply_usb_type ucsi_psy_usb_types[] = {
+ POWER_SUPPLY_USB_TYPE_C,
+ POWER_SUPPLY_USB_TYPE_PD,
+ POWER_SUPPLY_USB_TYPE_PD_PPS,
+};
+
+int ucsi_register_port_psy(struct ucsi_connector *con)
+{
+ struct power_supply_config psy_cfg = {};
+ struct device *dev = con->ucsi->dev;
+ char *psy_name;
+
+ psy_cfg.drv_data = con;
+ psy_cfg.fwnode = dev_fwnode(dev);
+
+ psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
+ dev_name(dev), con->num);
+ if (!psy_name)
+ return -ENOMEM;
+
+ con->psy_desc.name = psy_name;
+ con->psy_desc.type = POWER_SUPPLY_TYPE_USB,
+ con->psy_desc.usb_types = ucsi_psy_usb_types;
+ con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
+ con->psy_desc.properties = ucsi_psy_props,
+ con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props),
+ con->psy_desc.get_property = ucsi_psy_get_prop;
+
+ con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
+
+ return PTR_ERR_OR_ZERO(con->psy);
+}
+
+void ucsi_unregister_port_psy(struct ucsi_connector *con)
+{
+ if (IS_ERR_OR_NULL(con->psy))
+ return;
+
+ power_supply_unregister(con->psy);
+ con->psy = NULL;
+}
+
+void ucsi_port_psy_changed(struct ucsi_connector *con)
+{
+ if (IS_ERR_OR_NULL(con->psy))
+ return;
+
+ power_supply_changed(con->psy);
+}
diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c
index 1dabafb..cb62ad8 100644
--- a/drivers/usb/typec/ucsi/trace.c
+++ b/drivers/usb/typec/ucsi/trace.c
@@ -33,29 +33,18 @@
return ucsi_cmd_strs[(cmd >= ARRAY_SIZE(ucsi_cmd_strs)) ? 0 : cmd];
}
-static const char * const ucsi_ack_strs[] = {
- [0] = "",
- [UCSI_ACK_EVENT] = "event",
- [UCSI_ACK_CMD] = "command",
-};
-
-const char *ucsi_ack_str(u8 ack)
-{
- return ucsi_ack_strs[(ack >= ARRAY_SIZE(ucsi_ack_strs)) ? 0 : ack];
-}
-
const char *ucsi_cci_str(u32 cci)
{
- if (cci & GENMASK(7, 0)) {
- if (cci & BIT(29))
+ if (UCSI_CCI_CONNECTOR(cci)) {
+ if (cci & UCSI_CCI_ACK_COMPLETE)
return "Event pending (ACK completed)";
- if (cci & BIT(31))
+ if (cci & UCSI_CCI_COMMAND_COMPLETE)
return "Event pending (command completed)";
return "Connector Change";
}
- if (cci & BIT(29))
+ if (cci & UCSI_CCI_ACK_COMPLETE)
return "ACK completed";
- if (cci & BIT(31))
+ if (cci & UCSI_CCI_COMMAND_COMPLETE)
return "Command completed";
return "";
diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h
index 783ec9c..a0d3a93 100644
--- a/drivers/usb/typec/ucsi/trace.h
+++ b/drivers/usb/typec/ucsi/trace.h
@@ -10,54 +10,18 @@
#include <linux/usb/typec_altmode.h>
const char *ucsi_cmd_str(u64 raw_cmd);
-const char *ucsi_ack_str(u8 ack);
const char *ucsi_cci_str(u32 cci);
const char *ucsi_recipient_str(u8 recipient);
-DECLARE_EVENT_CLASS(ucsi_log_ack,
- TP_PROTO(u8 ack),
- TP_ARGS(ack),
- TP_STRUCT__entry(
- __field(u8, ack)
- ),
- TP_fast_assign(
- __entry->ack = ack;
- ),
- TP_printk("ACK %s", ucsi_ack_str(__entry->ack))
-);
-
-DEFINE_EVENT(ucsi_log_ack, ucsi_ack,
- TP_PROTO(u8 ack),
- TP_ARGS(ack)
-);
-
-DECLARE_EVENT_CLASS(ucsi_log_control,
- TP_PROTO(struct ucsi_control *ctrl),
- TP_ARGS(ctrl),
- TP_STRUCT__entry(
- __field(u64, ctrl)
- ),
- TP_fast_assign(
- __entry->ctrl = ctrl->raw_cmd;
- ),
- TP_printk("control=%08llx (%s)", __entry->ctrl,
- ucsi_cmd_str(__entry->ctrl))
-);
-
-DEFINE_EVENT(ucsi_log_control, ucsi_command,
- TP_PROTO(struct ucsi_control *ctrl),
- TP_ARGS(ctrl)
-);
-
DECLARE_EVENT_CLASS(ucsi_log_command,
- TP_PROTO(struct ucsi_control *ctrl, int ret),
- TP_ARGS(ctrl, ret),
+ TP_PROTO(u64 command, int ret),
+ TP_ARGS(command, ret),
TP_STRUCT__entry(
__field(u64, ctrl)
__field(int, ret)
),
TP_fast_assign(
- __entry->ctrl = ctrl->raw_cmd;
+ __entry->ctrl = command;
__entry->ret = ret;
),
TP_printk("%s -> %s (err=%d)", ucsi_cmd_str(__entry->ctrl),
@@ -66,30 +30,13 @@
);
DEFINE_EVENT(ucsi_log_command, ucsi_run_command,
- TP_PROTO(struct ucsi_control *ctrl, int ret),
- TP_ARGS(ctrl, ret)
+ TP_PROTO(u64 command, int ret),
+ TP_ARGS(command, ret)
);
DEFINE_EVENT(ucsi_log_command, ucsi_reset_ppm,
- TP_PROTO(struct ucsi_control *ctrl, int ret),
- TP_ARGS(ctrl, ret)
-);
-
-DECLARE_EVENT_CLASS(ucsi_log_cci,
- TP_PROTO(u32 cci),
- TP_ARGS(cci),
- TP_STRUCT__entry(
- __field(u32, cci)
- ),
- TP_fast_assign(
- __entry->cci = cci;
- ),
- TP_printk("CCI=%08x %s", __entry->cci, ucsi_cci_str(__entry->cci))
-);
-
-DEFINE_EVENT(ucsi_log_cci, ucsi_notify,
- TP_PROTO(u32 cci),
- TP_ARGS(cci)
+ TP_PROTO(u64 command, int ret),
+ TP_ARGS(command, ret)
);
DECLARE_EVENT_CLASS(ucsi_log_connector_status,
@@ -109,13 +56,13 @@
TP_fast_assign(
__entry->port = port - 1;
__entry->change = status->change;
- __entry->opmode = status->pwr_op_mode;
- __entry->connected = status->connected;
- __entry->pwr_dir = status->pwr_dir;
- __entry->partner_flags = status->partner_flags;
- __entry->partner_type = status->partner_type;
+ __entry->opmode = UCSI_CONSTAT_PWR_OPMODE(status->flags);
+ __entry->connected = !!(status->flags & UCSI_CONSTAT_CONNECTED);
+ __entry->pwr_dir = !!(status->flags & UCSI_CONSTAT_PWR_DIR);
+ __entry->partner_flags = UCSI_CONSTAT_PARTNER_FLAGS(status->flags);
+ __entry->partner_type = UCSI_CONSTAT_PARTNER_TYPE(status->flags);
__entry->request_data_obj = status->request_data_obj;
- __entry->bc_status = status->bc_status;
+ __entry->bc_status = UCSI_CONSTAT_BC_STATUS(status->pwr_status);
),
TP_printk("port%d status: change=%04x, opmode=%x, connected=%d, "
"sourcing=%d, partner_flags=%x, partner_type=%x, "
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index f3934f2..3bfa800 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -17,9 +17,6 @@
#include "ucsi.h"
#include "trace.h"
-#define to_ucsi_connector(_cap_) container_of(_cap_, struct ucsi_connector, \
- typec_cap)
-
/*
* UCSI_TIMEOUT_MS - PPM communication timeout
*
@@ -39,182 +36,155 @@
*/
#define UCSI_SWAP_TIMEOUT_MS 5000
-static inline int ucsi_sync(struct ucsi *ucsi)
+static int ucsi_acknowledge_command(struct ucsi *ucsi)
{
- if (ucsi->ppm && ucsi->ppm->sync)
- return ucsi->ppm->sync(ucsi->ppm);
- return 0;
+ u64 ctrl;
+
+ ctrl = UCSI_ACK_CC_CI;
+ ctrl |= UCSI_ACK_COMMAND_COMPLETE;
+
+ return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
}
-static int ucsi_command(struct ucsi *ucsi, struct ucsi_control *ctrl)
+static int ucsi_acknowledge_connector_change(struct ucsi *ucsi)
{
- int ret;
+ u64 ctrl;
- trace_ucsi_command(ctrl);
+ ctrl = UCSI_ACK_CC_CI;
+ ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
- set_bit(COMMAND_PENDING, &ucsi->flags);
-
- ret = ucsi->ppm->cmd(ucsi->ppm, ctrl);
- if (ret)
- goto err_clear_flag;
-
- if (!wait_for_completion_timeout(&ucsi->complete,
- msecs_to_jiffies(UCSI_TIMEOUT_MS))) {
- dev_warn(ucsi->dev, "PPM NOT RESPONDING\n");
- ret = -ETIMEDOUT;
- }
-
-err_clear_flag:
- clear_bit(COMMAND_PENDING, &ucsi->flags);
-
- return ret;
+ return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
}
-static int ucsi_ack(struct ucsi *ucsi, u8 ack)
+static int ucsi_exec_command(struct ucsi *ucsi, u64 command);
+
+static int ucsi_read_error(struct ucsi *ucsi)
{
- struct ucsi_control ctrl;
- int ret;
-
- trace_ucsi_ack(ack);
-
- set_bit(ACK_PENDING, &ucsi->flags);
-
- UCSI_CMD_ACK(ctrl, ack);
- ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
- if (ret)
- goto out_clear_bit;
-
- /* Waiting for ACK with ACK CMD, but not with EVENT for now */
- if (ack == UCSI_ACK_EVENT)
- goto out_clear_bit;
-
- if (!wait_for_completion_timeout(&ucsi->complete,
- msecs_to_jiffies(UCSI_TIMEOUT_MS)))
- ret = -ETIMEDOUT;
-
-out_clear_bit:
- clear_bit(ACK_PENDING, &ucsi->flags);
-
- if (ret)
- dev_err(ucsi->dev, "%s: failed\n", __func__);
-
- return ret;
-}
-
-static int ucsi_run_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
- void *data, size_t size)
-{
- struct ucsi_control _ctrl;
- u8 data_length;
u16 error;
int ret;
- ret = ucsi_command(ucsi, ctrl);
+ /* Acknowlege the command that failed */
+ ret = ucsi_acknowledge_command(ucsi);
if (ret)
- goto err;
+ return ret;
- switch (ucsi->status) {
- case UCSI_IDLE:
- ret = ucsi_sync(ucsi);
- if (ret)
- dev_warn(ucsi->dev, "%s: sync failed\n", __func__);
+ ret = ucsi_exec_command(ucsi, UCSI_GET_ERROR_STATUS);
+ if (ret < 0)
+ return ret;
- if (data)
- memcpy(data, ucsi->ppm->data->message_in, size);
+ ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, &error, sizeof(error));
+ if (ret)
+ return ret;
- data_length = ucsi->ppm->data->cci.data_length;
-
- ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
- if (!ret)
- ret = data_length;
+ switch (error) {
+ case UCSI_ERROR_INCOMPATIBLE_PARTNER:
+ return -EOPNOTSUPP;
+ case UCSI_ERROR_CC_COMMUNICATION_ERR:
+ return -ECOMM;
+ case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
+ return -EPROTO;
+ case UCSI_ERROR_DEAD_BATTERY:
+ dev_warn(ucsi->dev, "Dead battery condition!\n");
+ return -EPERM;
+ case UCSI_ERROR_INVALID_CON_NUM:
+ case UCSI_ERROR_UNREGONIZED_CMD:
+ case UCSI_ERROR_INVALID_CMD_ARGUMENT:
+ dev_err(ucsi->dev, "possible UCSI driver bug %u\n", error);
+ return -EINVAL;
+ case UCSI_ERROR_OVERCURRENT:
+ dev_warn(ucsi->dev, "Overcurrent condition\n");
break;
- case UCSI_BUSY:
- /* The caller decides whether to cancel or not */
- ret = -EBUSY;
+ case UCSI_ERROR_PARTNER_REJECTED_SWAP:
+ dev_warn(ucsi->dev, "Partner rejected swap\n");
break;
- case UCSI_ERROR:
- ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
- if (ret)
- break;
-
- _ctrl.raw_cmd = 0;
- _ctrl.cmd.cmd = UCSI_GET_ERROR_STATUS;
- ret = ucsi_command(ucsi, &_ctrl);
- if (ret) {
- dev_err(ucsi->dev, "reading error failed!\n");
- break;
- }
-
- memcpy(&error, ucsi->ppm->data->message_in, sizeof(error));
-
- /* Something has really gone wrong */
- if (WARN_ON(ucsi->status == UCSI_ERROR)) {
- ret = -ENODEV;
- break;
- }
-
- ret = ucsi_ack(ucsi, UCSI_ACK_CMD);
- if (ret)
- break;
-
- switch (error) {
- case UCSI_ERROR_INCOMPATIBLE_PARTNER:
- ret = -EOPNOTSUPP;
- break;
- case UCSI_ERROR_CC_COMMUNICATION_ERR:
- ret = -ECOMM;
- break;
- case UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL:
- ret = -EPROTO;
- break;
- case UCSI_ERROR_DEAD_BATTERY:
- dev_warn(ucsi->dev, "Dead battery condition!\n");
- ret = -EPERM;
- break;
- /* The following mean a bug in this driver */
- case UCSI_ERROR_INVALID_CON_NUM:
- case UCSI_ERROR_UNREGONIZED_CMD:
- case UCSI_ERROR_INVALID_CMD_ARGUMENT:
- dev_warn(ucsi->dev,
- "%s: possible UCSI driver bug - error 0x%x\n",
- __func__, error);
- ret = -EINVAL;
- break;
- default:
- dev_warn(ucsi->dev,
- "%s: error without status\n", __func__);
- ret = -EIO;
- break;
- }
+ case UCSI_ERROR_HARD_RESET:
+ dev_warn(ucsi->dev, "Hard reset occurred\n");
+ break;
+ case UCSI_ERROR_PPM_POLICY_CONFLICT:
+ dev_warn(ucsi->dev, "PPM Policy conflict\n");
+ break;
+ case UCSI_ERROR_SWAP_REJECTED:
+ dev_warn(ucsi->dev, "Swap rejected\n");
+ break;
+ case UCSI_ERROR_UNDEFINED:
+ default:
+ dev_err(ucsi->dev, "unknown error %u\n", error);
break;
}
-err:
- trace_ucsi_run_command(ctrl, ret);
-
- return ret;
+ return -EIO;
}
-int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
- void *retval, size_t size)
+static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
{
+ u32 cci;
+ int ret;
+
+ ret = ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
+ if (ret)
+ return ret;
+
+ ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ if (ret)
+ return ret;
+
+ if (cci & UCSI_CCI_BUSY)
+ return -EBUSY;
+
+ if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
+ return -EIO;
+
+ if (cci & UCSI_CCI_NOT_SUPPORTED)
+ return -EOPNOTSUPP;
+
+ if (cci & UCSI_CCI_ERROR) {
+ if (cmd == UCSI_GET_ERROR_STATUS)
+ return -EIO;
+ return ucsi_read_error(ucsi);
+ }
+
+ return UCSI_CCI_LENGTH(cci);
+}
+
+int ucsi_send_command(struct ucsi *ucsi, u64 command,
+ void *data, size_t size)
+{
+ u8 length;
int ret;
mutex_lock(&ucsi->ppm_lock);
- ret = ucsi_run_command(ucsi, ctrl, retval, size);
- mutex_unlock(&ucsi->ppm_lock);
+ ret = ucsi_exec_command(ucsi, command);
+ if (ret < 0)
+ goto out;
+
+ length = ret;
+
+ if (data) {
+ ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, data, size);
+ if (ret)
+ goto out;
+ }
+
+ ret = ucsi_acknowledge_command(ucsi);
+ if (ret)
+ goto out;
+
+ ret = length;
+out:
+ mutex_unlock(&ucsi->ppm_lock);
return ret;
}
EXPORT_SYMBOL_GPL(ucsi_send_command);
int ucsi_resume(struct ucsi *ucsi)
{
- struct ucsi_control ctrl;
+ u64 command;
/* Restore UCSI notification enable mask after system resume */
- UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL);
- return ucsi_send_command(ucsi, &ctrl, NULL, 0);
+ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
+
+ return ucsi_send_command(ucsi, command, NULL, 0);
}
EXPORT_SYMBOL_GPL(ucsi_resume);
/* -------------------------------------------------------------------------- */
@@ -222,15 +192,15 @@
void ucsi_altmode_update_active(struct ucsi_connector *con)
{
const struct typec_altmode *altmode = NULL;
- struct ucsi_control ctrl;
+ u64 command;
int ret;
u8 cur;
int i;
- UCSI_CMD_GET_CURRENT_CAM(ctrl, con->num);
- ret = ucsi_run_command(con->ucsi, &ctrl, &cur, sizeof(cur));
+ command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur));
if (ret < 0) {
- if (con->ucsi->ppm->data->version > 0x0100) {
+ if (con->ucsi->version > 0x0100) {
dev_err(con->ucsi->dev,
"GET_CURRENT_CAM command failed\n");
return;
@@ -300,9 +270,16 @@
switch (desc->svid) {
case USB_TYPEC_DP_SID:
- case USB_TYPEC_NVIDIA_VLINK_SID:
alt = ucsi_register_displayport(con, override, i, desc);
break;
+ case USB_TYPEC_NVIDIA_VLINK_SID:
+ if (desc->vdo == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
+ alt = typec_port_register_altmode(con->port,
+ desc);
+ else
+ alt = ucsi_register_displayport(con, override,
+ i, desc);
+ break;
default:
alt = typec_port_register_altmode(con->port, desc);
break;
@@ -351,13 +328,89 @@
return ret;
}
+static int
+ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
+{
+ int max_altmodes = UCSI_MAX_ALTMODES;
+ struct typec_altmode_desc desc;
+ struct ucsi_altmode alt;
+ struct ucsi_altmode orig[UCSI_MAX_ALTMODES];
+ struct ucsi_altmode updated[UCSI_MAX_ALTMODES];
+ struct ucsi *ucsi = con->ucsi;
+ bool multi_dp = false;
+ u64 command;
+ int ret;
+ int len;
+ int i;
+ int k = 0;
+
+ if (recipient == UCSI_RECIPIENT_CON)
+ max_altmodes = con->ucsi->cap.num_alt_modes;
+
+ memset(orig, 0, sizeof(orig));
+ memset(updated, 0, sizeof(updated));
+
+ /* First get all the alternate modes */
+ for (i = 0; i < max_altmodes; i++) {
+ memset(&alt, 0, sizeof(alt));
+ command = UCSI_GET_ALTERNATE_MODES;
+ command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
+ command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
+ command |= UCSI_GET_ALTMODE_OFFSET(i);
+ len = ucsi_send_command(con->ucsi, command, &alt, sizeof(alt));
+ /*
+ * We are collecting all altmodes first and then registering.
+ * Some type-C device will return zero length data beyond last
+ * alternate modes. We should not return if length is zero.
+ */
+ if (len < 0)
+ return len;
+
+ /* We got all altmodes, now break out and register them */
+ if (!len || !alt.svid)
+ break;
+
+ orig[k].mid = alt.mid;
+ orig[k].svid = alt.svid;
+ k++;
+ }
+ /*
+ * Update the original altmode table as some ppms may report
+ * multiple DP altmodes.
+ */
+ if (recipient == UCSI_RECIPIENT_CON)
+ multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated);
+
+ /* now register altmodes */
+ for (i = 0; i < max_altmodes; i++) {
+ memset(&desc, 0, sizeof(desc));
+ if (multi_dp && recipient == UCSI_RECIPIENT_CON) {
+ desc.svid = updated[i].svid;
+ desc.vdo = updated[i].mid;
+ } else {
+ desc.svid = orig[i].svid;
+ desc.vdo = orig[i].mid;
+ }
+ desc.roles = TYPEC_PORT_DRD;
+
+ if (!desc.svid)
+ return 0;
+
+ ret = ucsi_register_altmode(con, &desc, recipient);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
{
int max_altmodes = UCSI_MAX_ALTMODES;
struct typec_altmode_desc desc;
struct ucsi_altmode alt[2];
- struct ucsi_control ctrl;
- int num = 1;
+ u64 command;
+ int num;
int ret;
int len;
int j;
@@ -369,13 +422,19 @@
if (recipient == UCSI_RECIPIENT_SOP && con->partner_altmode[0])
return 0;
+ if (con->ucsi->ops->update_altmodes)
+ return ucsi_register_altmodes_nvidia(con, recipient);
+
if (recipient == UCSI_RECIPIENT_CON)
max_altmodes = con->ucsi->cap.num_alt_modes;
for (i = 0; i < max_altmodes;) {
memset(alt, 0, sizeof(alt));
- UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient, con->num, i, 1);
- len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt));
+ command = UCSI_GET_ALTERNATE_MODES;
+ command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
+ command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
+ command |= UCSI_GET_ALTMODE_OFFSET(i);
+ len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt));
if (len <= 0)
return len;
@@ -426,7 +485,8 @@
while (adev[i]) {
if (recipient == UCSI_RECIPIENT_SOP &&
(adev[i]->svid == USB_TYPEC_DP_SID ||
- adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID)) {
+ (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
+ adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) {
pdev = typec_altmode_get_partner(adev[i]);
ucsi_displayport_remove_partner((void *)pdev);
}
@@ -435,19 +495,68 @@
}
}
+static int ucsi_get_pdos(struct ucsi_connector *con, int is_partner,
+ u32 *pdos, int offset, int num_pdos)
+{
+ struct ucsi *ucsi = con->ucsi;
+ u64 command;
+ int ret;
+
+ command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num);
+ command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner);
+ command |= UCSI_GET_PDOS_PDO_OFFSET(offset);
+ command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1);
+ command |= UCSI_GET_PDOS_SRC_PDOS;
+ ret = ucsi_send_command(ucsi, command, pdos + offset,
+ num_pdos * sizeof(u32));
+ if (ret < 0)
+ dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret);
+ if (ret == 0 && offset == 0)
+ dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n");
+
+ return ret;
+}
+
+static void ucsi_get_src_pdos(struct ucsi_connector *con, int is_partner)
+{
+ int ret;
+
+ /* UCSI max payload means only getting at most 4 PDOs at a time */
+ ret = ucsi_get_pdos(con, 1, con->src_pdos, 0, UCSI_MAX_PDOS);
+ if (ret < 0)
+ return;
+
+ con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */
+ if (con->num_pdos < UCSI_MAX_PDOS)
+ return;
+
+ /* get the remaining PDOs, if any */
+ ret = ucsi_get_pdos(con, 1, con->src_pdos, UCSI_MAX_PDOS,
+ PDO_MAX_OBJECTS - UCSI_MAX_PDOS);
+ if (ret < 0)
+ return;
+
+ con->num_pdos += ret / sizeof(u32);
+}
+
static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
{
- switch (con->status.pwr_op_mode) {
+ switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
case UCSI_CONSTAT_PWR_OPMODE_PD:
+ con->rdo = con->status.request_data_obj;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
+ ucsi_get_src_pdos(con, 1);
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+ con->rdo = 0;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A);
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+ con->rdo = 0;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A);
break;
default:
+ con->rdo = 0;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB);
break;
}
@@ -455,6 +564,7 @@
static int ucsi_register_partner(struct ucsi_connector *con)
{
+ u8 pwr_opmode = UCSI_CONSTAT_PWR_OPMODE(con->status.flags);
struct typec_partner_desc desc;
struct typec_partner *partner;
@@ -463,7 +573,7 @@
memset(&desc, 0, sizeof(desc));
- switch (con->status.partner_type) {
+ switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
case UCSI_CONSTAT_PARTNER_TYPE_DEBUG:
desc.accessory = TYPEC_ACCESSORY_DEBUG;
break;
@@ -474,7 +584,7 @@
break;
}
- desc.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD;
+ desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD;
partner = typec_register_partner(con->port, &desc);
if (IS_ERR(partner)) {
@@ -506,8 +616,10 @@
if (!con->partner)
return;
- switch (con->status.partner_type) {
+ switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
typec_set_data_role(con->port, TYPEC_HOST);
break;
case UCSI_CONSTAT_PARTNER_TYPE_DFP:
@@ -531,29 +643,128 @@
ucsi_altmode_update_active(con);
}
-static void ucsi_connector_change(struct work_struct *work)
+static void ucsi_handle_connector_change(struct work_struct *work)
{
struct ucsi_connector *con = container_of(work, struct ucsi_connector,
work);
struct ucsi *ucsi = con->ucsi;
- struct ucsi_control ctrl;
+ struct ucsi_connector_status pre_ack_status;
+ struct ucsi_connector_status post_ack_status;
+ enum typec_role role;
+ u16 inferred_changes;
+ u16 changed_flags;
+ u64 command;
int ret;
mutex_lock(&con->lock);
- UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
- ret = ucsi_send_command(ucsi, &ctrl, &con->status, sizeof(con->status));
+ /*
+ * Some/many PPMs have an issue where all fields in the change bitfield
+ * are cleared when an ACK is send. This will causes any change
+ * between GET_CONNECTOR_STATUS and ACK to be lost.
+ *
+ * We work around this by re-fetching the connector status afterwards.
+ * We then infer any changes that we see have happened but that may not
+ * be represented in the change bitfield.
+ *
+ * Also, even though we don't need to know the currently supported alt
+ * modes, we run the GET_CAM_SUPPORTED command to ensure the PPM does
+ * not get stuck in case it assumes we do.
+ * Always do this, rather than relying on UCSI_CONSTAT_CAM_CHANGE to be
+ * set in the change bitfield.
+ *
+ * We end up with the following actions:
+ * 1. UCSI_GET_CONNECTOR_STATUS, store result, update unprocessed_changes
+ * 2. UCSI_GET_CAM_SUPPORTED, discard result
+ * 3. ACK connector change
+ * 4. UCSI_GET_CONNECTOR_STATUS, store result
+ * 5. Infere lost changes by comparing UCSI_GET_CONNECTOR_STATUS results
+ * 6. If PPM reported a new change, then restart in order to ACK
+ * 7. Process everything as usual.
+ *
+ * We may end up seeing a change twice, but we can only miss extremely
+ * short transitional changes.
+ */
+
+ /* 1. First UCSI_GET_CONNECTOR_STATUS */
+ command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(ucsi, command, &pre_ack_status,
+ sizeof(pre_ack_status));
+ if (ret < 0) {
+ dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
+ __func__, ret);
+ goto out_unlock;
+ }
+ con->unprocessed_changes |= pre_ack_status.change;
+
+ /* 2. Run UCSI_GET_CAM_SUPPORTED and discard the result. */
+ command = UCSI_GET_CAM_SUPPORTED;
+ command |= UCSI_CONNECTOR_NUMBER(con->num);
+ ucsi_send_command(con->ucsi, command, NULL, 0);
+
+ /* 3. ACK connector change */
+ ret = ucsi_acknowledge_connector_change(ucsi);
+ clear_bit(EVENT_PENDING, &ucsi->flags);
+ if (ret) {
+ dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
+ goto out_unlock;
+ }
+
+ /* 4. Second UCSI_GET_CONNECTOR_STATUS */
+ command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(ucsi, command, &post_ack_status,
+ sizeof(post_ack_status));
if (ret < 0) {
dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
__func__, ret);
goto out_unlock;
}
- if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE)
+ /* 5. Inferre any missing changes */
+ changed_flags = pre_ack_status.flags ^ post_ack_status.flags;
+ inferred_changes = 0;
+ if (UCSI_CONSTAT_PWR_OPMODE(changed_flags) != 0)
+ inferred_changes |= UCSI_CONSTAT_POWER_OPMODE_CHANGE;
+
+ if (changed_flags & UCSI_CONSTAT_CONNECTED)
+ inferred_changes |= UCSI_CONSTAT_CONNECT_CHANGE;
+
+ if (changed_flags & UCSI_CONSTAT_PWR_DIR)
+ inferred_changes |= UCSI_CONSTAT_POWER_DIR_CHANGE;
+
+ if (UCSI_CONSTAT_PARTNER_FLAGS(changed_flags) != 0)
+ inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE;
+
+ if (UCSI_CONSTAT_PARTNER_TYPE(changed_flags) != 0)
+ inferred_changes |= UCSI_CONSTAT_PARTNER_CHANGE;
+
+ /* Mask out anything that was correctly notified in the later call. */
+ inferred_changes &= ~post_ack_status.change;
+ if (inferred_changes)
+ dev_dbg(ucsi->dev, "%s: Inferred changes that would have been lost: 0x%04x\n",
+ __func__, inferred_changes);
+
+ con->unprocessed_changes |= inferred_changes;
+
+ /* 6. If PPM reported a new change, then restart in order to ACK */
+ if (post_ack_status.change)
+ goto out_unlock;
+
+ /* 7. Continue as if nothing happened */
+ con->status = post_ack_status;
+ con->status.change = con->unprocessed_changes;
+ con->unprocessed_changes = 0;
+
+ role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
+
+ if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE ||
+ con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE) {
ucsi_pwr_opmode_change(con);
+ ucsi_port_psy_changed(con);
+ }
if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
- typec_set_pwr_role(con->port, con->status.pwr_dir);
+ typec_set_pwr_role(con->port, role);
/* Complete pending power role swap */
if (!completion_done(&con->complete))
@@ -561,10 +772,12 @@
}
if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) {
- typec_set_pwr_role(con->port, con->status.pwr_dir);
+ typec_set_pwr_role(con->port, role);
- switch (con->status.partner_type) {
+ switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
typec_set_data_role(con->port, TYPEC_HOST);
break;
case UCSI_CONSTAT_PARTNER_TYPE_DFP:
@@ -574,148 +787,119 @@
break;
}
- if (con->status.connected)
+ if (con->status.flags & UCSI_CONSTAT_CONNECTED)
ucsi_register_partner(con);
else
ucsi_unregister_partner(con);
- }
- if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) {
- /*
- * We don't need to know the currently supported alt modes here.
- * Running GET_CAM_SUPPORTED command just to make sure the PPM
- * does not get stuck in case it assumes we do so.
- */
- UCSI_CMD_GET_CAM_SUPPORTED(ctrl, con->num);
- ucsi_run_command(con->ucsi, &ctrl, NULL, 0);
+ ucsi_port_psy_changed(con);
}
if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE)
ucsi_partner_change(con);
- ret = ucsi_ack(ucsi, UCSI_ACK_EVENT);
- if (ret)
- dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);
-
trace_ucsi_connector_change(con->num, &con->status);
out_unlock:
- clear_bit(EVENT_PENDING, &ucsi->flags);
+ if (test_and_clear_bit(EVENT_PENDING, &ucsi->flags)) {
+ schedule_work(&con->work);
+ mutex_unlock(&con->lock);
+ return;
+ }
+
+ clear_bit(EVENT_PROCESSING, &ucsi->flags);
mutex_unlock(&con->lock);
}
/**
- * ucsi_notify - PPM notification handler
- * @ucsi: Source UCSI Interface for the notifications
- *
- * Handle notifications from PPM of @ucsi.
+ * ucsi_connector_change - Process Connector Change Event
+ * @ucsi: UCSI Interface
+ * @num: Connector number
*/
-void ucsi_notify(struct ucsi *ucsi)
+void ucsi_connector_change(struct ucsi *ucsi, u8 num)
{
- struct ucsi_cci *cci;
+ struct ucsi_connector *con = &ucsi->connector[num - 1];
- /* There is no requirement to sync here, but no harm either. */
- ucsi_sync(ucsi);
-
- cci = &ucsi->ppm->data->cci;
-
- if (cci->error)
- ucsi->status = UCSI_ERROR;
- else if (cci->busy)
- ucsi->status = UCSI_BUSY;
- else
- ucsi->status = UCSI_IDLE;
-
- if (cci->cmd_complete && test_bit(COMMAND_PENDING, &ucsi->flags)) {
- complete(&ucsi->complete);
- } else if (cci->ack_complete && test_bit(ACK_PENDING, &ucsi->flags)) {
- complete(&ucsi->complete);
- } else if (cci->connector_change) {
- struct ucsi_connector *con;
-
- con = &ucsi->connector[cci->connector_change - 1];
-
- if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
- schedule_work(&con->work);
+ if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) {
+ dev_dbg(ucsi->dev, "Bogus connector change event\n");
+ return;
}
- trace_ucsi_notify(ucsi->ppm->data->raw_cci);
+ set_bit(EVENT_PENDING, &ucsi->flags);
+
+ if (!test_and_set_bit(EVENT_PROCESSING, &ucsi->flags))
+ schedule_work(&con->work);
}
-EXPORT_SYMBOL_GPL(ucsi_notify);
+EXPORT_SYMBOL_GPL(ucsi_connector_change);
/* -------------------------------------------------------------------------- */
static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
{
- struct ucsi_control ctrl;
+ u64 command;
- UCSI_CMD_CONNECTOR_RESET(ctrl, con, hard);
+ command = UCSI_CONNECTOR_RESET | UCSI_CONNECTOR_NUMBER(con->num);
+ command |= hard ? UCSI_CONNECTOR_RESET_HARD : 0;
- return ucsi_send_command(con->ucsi, &ctrl, NULL, 0);
+ return ucsi_send_command(con->ucsi, command, NULL, 0);
}
static int ucsi_reset_ppm(struct ucsi *ucsi)
{
- struct ucsi_control ctrl;
+ u64 command = UCSI_PPM_RESET;
unsigned long tmo;
+ u32 cci;
int ret;
- ctrl.raw_cmd = 0;
- ctrl.cmd.cmd = UCSI_PPM_RESET;
- trace_ucsi_command(&ctrl);
- ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
- if (ret)
- goto err;
+ mutex_lock(&ucsi->ppm_lock);
+
+ ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
+ sizeof(command));
+ if (ret < 0)
+ goto out;
tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
do {
- /* Here sync is critical. */
- ret = ucsi_sync(ucsi);
- if (ret)
- goto err;
-
- if (ucsi->ppm->data->cci.reset_complete)
- break;
-
- /* If the PPM is still doing something else, reset it again. */
- if (ucsi->ppm->data->raw_cci) {
- dev_warn_ratelimited(ucsi->dev,
- "Failed to reset PPM! Trying again..\n");
-
- trace_ucsi_command(&ctrl);
- ret = ucsi->ppm->cmd(ucsi->ppm, &ctrl);
- if (ret)
- goto err;
+ if (time_is_before_jiffies(tmo)) {
+ ret = -ETIMEDOUT;
+ goto out;
}
- /* Letting the PPM settle down. */
+ ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ if (ret)
+ goto out;
+
+ /* If the PPM is still doing something else, reset it again. */
+ if (cci & ~UCSI_CCI_RESET_COMPLETE) {
+ ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL,
+ &command,
+ sizeof(command));
+ if (ret < 0)
+ goto out;
+ }
+
msleep(20);
+ } while (!(cci & UCSI_CCI_RESET_COMPLETE));
- ret = -ETIMEDOUT;
- } while (time_is_after_jiffies(tmo));
-
-err:
- trace_ucsi_reset_ppm(&ctrl, ret);
-
+out:
+ mutex_unlock(&ucsi->ppm_lock);
return ret;
}
-static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl)
+static int ucsi_role_cmd(struct ucsi_connector *con, u64 command)
{
int ret;
- ret = ucsi_send_command(con->ucsi, ctrl, NULL, 0);
+ ret = ucsi_send_command(con->ucsi, command, NULL, 0);
if (ret == -ETIMEDOUT) {
- struct ucsi_control c;
+ u64 c;
/* PPM most likely stopped responding. Resetting everything. */
- mutex_lock(&con->ucsi->ppm_lock);
ucsi_reset_ppm(con->ucsi);
- mutex_unlock(&con->ucsi->ppm_lock);
- UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
- ucsi_send_command(con->ucsi, &c, NULL, 0);
+ c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy;
+ ucsi_send_command(con->ucsi, c, NULL, 0);
ucsi_reset_connector(con, true);
}
@@ -723,11 +907,11 @@
return ret;
}
-static int
-ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role)
+static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role)
{
- struct ucsi_connector *con = to_ucsi_connector(cap);
- struct ucsi_control ctrl;
+ struct ucsi_connector *con = typec_get_drvdata(port);
+ u8 partner_type;
+ u64 command;
int ret = 0;
mutex_lock(&con->lock);
@@ -737,14 +921,17 @@
goto out_unlock;
}
- if ((con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP &&
+ partner_type = UCSI_CONSTAT_PARTNER_TYPE(con->status.flags);
+ if ((partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP &&
role == TYPEC_DEVICE) ||
- (con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP &&
+ (partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP &&
role == TYPEC_HOST))
goto out_unlock;
- UCSI_CMD_SET_UOR(ctrl, con, role);
- ret = ucsi_role_cmd(con, &ctrl);
+ command = UCSI_SET_UOR | UCSI_CONNECTOR_NUMBER(con->num);
+ command |= UCSI_SET_UOR_ROLE(role);
+ command |= UCSI_SET_UOR_ACCEPT_ROLE_SWAPS;
+ ret = ucsi_role_cmd(con, command);
if (ret < 0)
goto out_unlock;
@@ -758,11 +945,11 @@
return ret < 0 ? ret : 0;
}
-static int
-ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role)
+static int ucsi_pr_swap(struct typec_port *port, enum typec_role role)
{
- struct ucsi_connector *con = to_ucsi_connector(cap);
- struct ucsi_control ctrl;
+ struct ucsi_connector *con = typec_get_drvdata(port);
+ enum typec_role cur_role;
+ u64 command;
int ret = 0;
mutex_lock(&con->lock);
@@ -772,11 +959,15 @@
goto out_unlock;
}
- if (con->status.pwr_dir == role)
+ cur_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
+
+ if (cur_role == role)
goto out_unlock;
- UCSI_CMD_SET_PDR(ctrl, con, role);
- ret = ucsi_role_cmd(con, &ctrl);
+ command = UCSI_SET_PDR | UCSI_CONNECTOR_NUMBER(con->num);
+ command |= UCSI_SET_PDR_ROLE(role);
+ command |= UCSI_SET_PDR_ACCEPT_ROLE_SWAPS;
+ ret = ucsi_role_cmd(con, command);
if (ret < 0)
goto out_unlock;
@@ -787,7 +978,8 @@
}
/* Something has gone wrong while swapping the role */
- if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) {
+ if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) !=
+ UCSI_CONSTAT_PWR_OPMODE_PD) {
ucsi_reset_connector(con, true);
ret = -EPROTO;
}
@@ -798,6 +990,12 @@
return ret;
}
+static const struct typec_operations ucsi_ops = {
+ .dr_set = ucsi_dr_swap,
+ .pr_set = ucsi_pr_swap
+};
+
+/* Caller must call fwnode_handle_put() after use */
static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
{
struct fwnode_handle *fwnode;
@@ -814,20 +1012,24 @@
struct ucsi_connector *con = &ucsi->connector[index];
struct typec_capability *cap = &con->typec_cap;
enum typec_accessory *accessory = cap->accessory;
- struct ucsi_control ctrl;
+ u64 command;
int ret;
- INIT_WORK(&con->work, ucsi_connector_change);
+ INIT_WORK(&con->work, ucsi_handle_connector_change);
init_completion(&con->complete);
mutex_init(&con->lock);
con->num = index + 1;
con->ucsi = ucsi;
+ /* Delay other interactions with the con until registration is complete */
+ mutex_lock(&con->lock);
+
/* Get connector capability */
- UCSI_CMD_GET_CONNECTOR_CAPABILITY(ctrl, con->num);
- ret = ucsi_run_command(ucsi, &ctrl, &con->cap, sizeof(con->cap));
+ command = UCSI_GET_CONNECTOR_CAPABILITY;
+ command |= UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap));
if (ret < 0)
- return ret;
+ goto out_unlock;
if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP)
cap->data = TYPEC_PORT_DRD;
@@ -836,11 +1038,12 @@
else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP)
cap->data = TYPEC_PORT_UFP;
- if (con->cap.provider && con->cap.consumer)
+ if ((con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) &&
+ (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER))
cap->type = TYPEC_PORT_DRP;
- else if (con->cap.provider)
+ else if (con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER)
cap->type = TYPEC_PORT_SRC;
- else if (con->cap.consumer)
+ else if (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER)
cap->type = TYPEC_PORT_SNK;
cap->revision = ucsi->cap.typec_version;
@@ -853,33 +1056,42 @@
*accessory = TYPEC_ACCESSORY_DEBUG;
cap->fwnode = ucsi_find_fwnode(con);
- cap->dr_set = ucsi_dr_swap;
- cap->pr_set = ucsi_pr_swap;
+ cap->driver_data = con;
+ cap->ops = &ucsi_ops;
+
+ ret = ucsi_register_port_psy(con);
+ if (ret)
+ goto out;
/* Register the connector */
con->port = typec_register_port(ucsi->dev, cap);
- if (IS_ERR(con->port))
- return PTR_ERR(con->port);
+ if (IS_ERR(con->port)) {
+ ret = PTR_ERR(con->port);
+ goto out;
+ }
/* Alternate modes */
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON);
- if (ret)
+ if (ret) {
dev_err(ucsi->dev, "con%d: failed to register alt modes\n",
con->num);
-
- /* Get the status */
- UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num);
- ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status));
- if (ret < 0) {
- dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
- return 0;
+ goto out;
}
- ucsi_pwr_opmode_change(con);
- typec_set_pwr_role(con->port, con->status.pwr_dir);
+ /* Get the status */
+ command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status));
+ if (ret < 0) {
+ dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
+ ret = 0;
+ goto out;
+ }
+ ret = 0; /* ucsi_send_command() returns length on success */
- switch (con->status.partner_type) {
+ switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+ case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
typec_set_data_role(con->port, TYPEC_HOST);
break;
case UCSI_CONSTAT_PARTNER_TYPE_DFP:
@@ -890,34 +1102,48 @@
}
/* Check if there is already something connected */
- if (con->status.connected)
+ if (con->status.flags & UCSI_CONSTAT_CONNECTED) {
+ typec_set_pwr_role(con->port,
+ !!(con->status.flags & UCSI_CONSTAT_PWR_DIR));
+ ucsi_pwr_opmode_change(con);
ucsi_register_partner(con);
+ ucsi_port_psy_changed(con);
+ }
if (con->partner) {
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
- if (ret)
+ if (ret) {
dev_err(ucsi->dev,
"con%d: failed to register alternate modes\n",
con->num);
- else
+ ret = 0;
+ } else {
ucsi_altmode_update_active(con);
+ }
}
trace_ucsi_register_port(con->num, &con->status);
- return 0;
+out:
+ fwnode_handle_put(cap->fwnode);
+out_unlock:
+ mutex_unlock(&con->lock);
+ return ret;
}
-static void ucsi_init(struct work_struct *work)
+/**
+ * ucsi_init - Initialize UCSI interface
+ * @ucsi: UCSI to be initialized
+ *
+ * Registers all ports @ucsi has and enables all notification events.
+ */
+static int ucsi_init(struct ucsi *ucsi)
{
- struct ucsi *ucsi = container_of(work, struct ucsi, work);
struct ucsi_connector *con;
- struct ucsi_control ctrl;
+ u64 command;
int ret;
int i;
- mutex_lock(&ucsi->ppm_lock);
-
/* Reset the PPM */
ret = ucsi_reset_ppm(ucsi);
if (ret) {
@@ -926,15 +1152,15 @@
}
/* Enable basic notifications */
- UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE |
- UCSI_ENABLE_NTFY_ERROR);
- ret = ucsi_run_command(ucsi, &ctrl, NULL, 0);
+ ucsi->ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
+ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
+ ret = ucsi_send_command(ucsi, command, NULL, 0);
if (ret < 0)
goto err_reset;
/* Get PPM capabilities */
- UCSI_CMD_GET_CAPABILITY(ctrl);
- ret = ucsi_run_command(ucsi, &ctrl, &ucsi->cap, sizeof(ucsi->cap));
+ command = UCSI_GET_CAPABILITY;
+ ret = ucsi_send_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap));
if (ret < 0)
goto err_reset;
@@ -959,19 +1185,19 @@
}
/* Enable all notifications */
- UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_ALL);
- ret = ucsi_run_command(ucsi, &ctrl, NULL, 0);
+ ucsi->ntfy = UCSI_ENABLE_NTFY_ALL;
+ command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
+ ret = ucsi_send_command(ucsi, command, NULL, 0);
if (ret < 0)
goto err_unregister;
- mutex_unlock(&ucsi->ppm_lock);
-
- return;
+ return 0;
err_unregister:
for (con = ucsi->connector; con->port; con++) {
ucsi_unregister_partner(con);
ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
+ ucsi_unregister_port_psy(con);
typec_unregister_port(con->port);
con->port = NULL;
}
@@ -980,75 +1206,126 @@
memset(&ucsi->cap, 0, sizeof(ucsi->cap));
ucsi_reset_ppm(ucsi);
err:
- mutex_unlock(&ucsi->ppm_lock);
- dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);
+ return ret;
+}
+
+static void ucsi_init_work(struct work_struct *work)
+{
+ struct ucsi *ucsi = container_of(work, struct ucsi, work);
+ int ret;
+
+ ret = ucsi_init(ucsi);
+ if (ret)
+ dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);
}
/**
- * ucsi_register_ppm - Register UCSI PPM Interface
- * @dev: Device interface to the PPM
- * @ppm: The PPM interface
- *
- * Allocates UCSI instance, associates it with @ppm and returns it to the
- * caller, and schedules initialization of the interface.
+ * ucsi_get_drvdata - Return private driver data pointer
+ * @ucsi: UCSI interface
*/
-struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm)
+void *ucsi_get_drvdata(struct ucsi *ucsi)
+{
+ return ucsi->driver_data;
+}
+EXPORT_SYMBOL_GPL(ucsi_get_drvdata);
+
+/**
+ * ucsi_get_drvdata - Assign private driver data pointer
+ * @ucsi: UCSI interface
+ * @data: Private data pointer
+ */
+void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
+{
+ ucsi->driver_data = data;
+}
+EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
+
+/**
+ * ucsi_create - Allocate UCSI instance
+ * @dev: Device interface to the PPM (Platform Policy Manager)
+ * @ops: I/O routines
+ */
+struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
{
struct ucsi *ucsi;
+ if (!ops || !ops->read || !ops->sync_write || !ops->async_write)
+ return ERR_PTR(-EINVAL);
+
ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
if (!ucsi)
return ERR_PTR(-ENOMEM);
- INIT_WORK(&ucsi->work, ucsi_init);
- init_completion(&ucsi->complete);
+ INIT_WORK(&ucsi->work, ucsi_init_work);
mutex_init(&ucsi->ppm_lock);
-
ucsi->dev = dev;
- ucsi->ppm = ppm;
-
- /*
- * Communication with the PPM takes a lot of time. It is not reasonable
- * to initialize the driver here. Using a work for now.
- */
- queue_work(system_long_wq, &ucsi->work);
+ ucsi->ops = ops;
return ucsi;
}
-EXPORT_SYMBOL_GPL(ucsi_register_ppm);
+EXPORT_SYMBOL_GPL(ucsi_create);
/**
- * ucsi_unregister_ppm - Unregister UCSI PPM Interface
- * @ucsi: struct ucsi associated with the PPM
- *
- * Unregister UCSI PPM that was created with ucsi_register().
+ * ucsi_destroy - Free UCSI instance
+ * @ucsi: UCSI instance to be freed
*/
-void ucsi_unregister_ppm(struct ucsi *ucsi)
+void ucsi_destroy(struct ucsi *ucsi)
{
- struct ucsi_control ctrl;
+ kfree(ucsi);
+}
+EXPORT_SYMBOL_GPL(ucsi_destroy);
+
+/**
+ * ucsi_register - Register UCSI interface
+ * @ucsi: UCSI instance
+ */
+int ucsi_register(struct ucsi *ucsi)
+{
+ int ret;
+
+ ret = ucsi->ops->read(ucsi, UCSI_VERSION, &ucsi->version,
+ sizeof(ucsi->version));
+ if (ret)
+ return ret;
+
+ if (!ucsi->version)
+ return -ENODEV;
+
+ queue_work(system_long_wq, &ucsi->work);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ucsi_register);
+
+/**
+ * ucsi_unregister - Unregister UCSI interface
+ * @ucsi: UCSI interface to be unregistered
+ *
+ * Unregister UCSI interface that was created with ucsi_register().
+ */
+void ucsi_unregister(struct ucsi *ucsi)
+{
+ u64 cmd = UCSI_SET_NOTIFICATION_ENABLE;
int i;
/* Make sure that we are not in the middle of driver initialization */
cancel_work_sync(&ucsi->work);
- /* Disable everything except command complete notification */
- UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE)
- ucsi_send_command(ucsi, &ctrl, NULL, 0);
+ /* Disable notifications */
+ ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
for (i = 0; i < ucsi->cap.num_connectors; i++) {
cancel_work_sync(&ucsi->connector[i].work);
ucsi_unregister_partner(&ucsi->connector[i]);
ucsi_unregister_altmodes(&ucsi->connector[i],
UCSI_RECIPIENT_CON);
+ ucsi_unregister_port_psy(&ucsi->connector[i]);
typec_unregister_port(ucsi->connector[i].port);
}
- ucsi_reset_ppm(ucsi);
-
kfree(ucsi->connector);
- kfree(ucsi);
}
-EXPORT_SYMBOL_GPL(ucsi_unregister_ppm);
+EXPORT_SYMBOL_GPL(ucsi_unregister);
MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index de87d0b..fce23ad 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -5,182 +5,66 @@
#include <linux/bitops.h>
#include <linux/device.h>
+#include <linux/power_supply.h>
#include <linux/types.h>
#include <linux/usb/typec.h>
+#include <linux/usb/pd.h>
/* -------------------------------------------------------------------------- */
-/* Command Status and Connector Change Indication (CCI) data structure */
-struct ucsi_cci {
- u8:1; /* reserved */
- u8 connector_change:7;
- u8 data_length;
- u16:9; /* reserved */
- u16 not_supported:1;
- u16 cancel_complete:1;
- u16 reset_complete:1;
- u16 busy:1;
- u16 ack_complete:1;
- u16 error:1;
- u16 cmd_complete:1;
-} __packed;
+struct ucsi;
+struct ucsi_altmode;
-/* Default fields in CONTROL data structure */
-struct ucsi_command {
- u8 cmd;
- u8 length;
- u64 data:48;
-} __packed;
+/* UCSI offsets (Bytes) */
+#define UCSI_VERSION 0
+#define UCSI_CCI 4
+#define UCSI_CONTROL 8
+#define UCSI_MESSAGE_IN 16
+#define UCSI_MESSAGE_OUT 32
-/* ACK Command structure */
-struct ucsi_ack_cmd {
- u8 cmd;
- u8 length;
- u8 cci_ack:1;
- u8 cmd_ack:1;
- u8:6; /* reserved */
-} __packed;
+/* Command Status and Connector Change Indication (CCI) bits */
+#define UCSI_CCI_CONNECTOR(_c_) (((_c_) & GENMASK(7, 1)) >> 1)
+#define UCSI_CCI_LENGTH(_c_) (((_c_) & GENMASK(15, 8)) >> 8)
+#define UCSI_CCI_NOT_SUPPORTED BIT(25)
+#define UCSI_CCI_CANCEL_COMPLETE BIT(26)
+#define UCSI_CCI_RESET_COMPLETE BIT(27)
+#define UCSI_CCI_BUSY BIT(28)
+#define UCSI_CCI_ACK_COMPLETE BIT(29)
+#define UCSI_CCI_ERROR BIT(30)
+#define UCSI_CCI_COMMAND_COMPLETE BIT(31)
-/* Connector Reset Command structure */
-struct ucsi_con_rst {
- u8 cmd;
- u8 length;
- u8 con_num:7;
- u8 hard_reset:1;
-} __packed;
-
-/* Set USB Operation Mode Command structure */
-struct ucsi_uor_cmd {
- u8 cmd;
- u8 length;
- u16 con_num:7;
- u16 role:3;
-#define UCSI_UOR_ROLE_DFP BIT(0)
-#define UCSI_UOR_ROLE_UFP BIT(1)
-#define UCSI_UOR_ROLE_DRP BIT(2)
- u16:6; /* reserved */
-} __packed;
-
-/* Get Alternate Modes Command structure */
-struct ucsi_altmode_cmd {
- u8 cmd;
- u8 length;
- u8 recipient;
-#define UCSI_RECIPIENT_CON 0
-#define UCSI_RECIPIENT_SOP 1
-#define UCSI_RECIPIENT_SOP_P 2
-#define UCSI_RECIPIENT_SOP_PP 3
- u8 con_num;
- u8 offset;
- u8 num_altmodes;
-} __packed;
-
-struct ucsi_control {
- union {
- u64 raw_cmd;
- struct ucsi_command cmd;
- struct ucsi_uor_cmd uor;
- struct ucsi_ack_cmd ack;
- struct ucsi_con_rst con_rst;
- struct ucsi_altmode_cmd alt;
- };
+/**
+ * struct ucsi_operations - UCSI I/O operations
+ * @read: Read operation
+ * @sync_write: Blocking write operation
+ * @async_write: Non-blocking write operation
+ * @update_altmodes: Squashes duplicate DP altmodes
+ *
+ * Read and write routines for UCSI interface. @sync_write must wait for the
+ * Command Completion Event from the PPM before returning, and @async_write must
+ * return immediately after sending the data to the PPM.
+ */
+struct ucsi_operations {
+ int (*read)(struct ucsi *ucsi, unsigned int offset,
+ void *val, size_t val_len);
+ int (*sync_write)(struct ucsi *ucsi, unsigned int offset,
+ const void *val, size_t val_len);
+ int (*async_write)(struct ucsi *ucsi, unsigned int offset,
+ const void *val, size_t val_len);
+ bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
+ struct ucsi_altmode *updated);
};
-#define __UCSI_CMD(_ctrl_, _cmd_) \
-{ \
- (_ctrl_).raw_cmd = 0; \
- (_ctrl_).cmd.cmd = _cmd_; \
-}
+struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops);
+void ucsi_destroy(struct ucsi *ucsi);
+int ucsi_register(struct ucsi *ucsi);
+void ucsi_unregister(struct ucsi *ucsi);
+void *ucsi_get_drvdata(struct ucsi *ucsi);
+void ucsi_set_drvdata(struct ucsi *ucsi, void *data);
-/* Helper for preparing ucsi_control for CONNECTOR_RESET command. */
-#define UCSI_CMD_CONNECTOR_RESET(_ctrl_, _con_, _hard_) \
-{ \
- __UCSI_CMD(_ctrl_, UCSI_CONNECTOR_RESET) \
- (_ctrl_).con_rst.con_num = (_con_)->num; \
- (_ctrl_).con_rst.hard_reset = _hard_; \
-}
+void ucsi_connector_change(struct ucsi *ucsi, u8 num);
-/* Helper for preparing ucsi_control for ACK_CC_CI command. */
-#define UCSI_CMD_ACK(_ctrl_, _ack_) \
-{ \
- __UCSI_CMD(_ctrl_, UCSI_ACK_CC_CI) \
- (_ctrl_).ack.cci_ack = ((_ack_) == UCSI_ACK_EVENT); \
- (_ctrl_).ack.cmd_ack = ((_ack_) == UCSI_ACK_CMD); \
-}
-
-/* Helper for preparing ucsi_control for SET_NOTIFY_ENABLE command. */
-#define UCSI_CMD_SET_NTFY_ENABLE(_ctrl_, _ntfys_) \
-{ \
- __UCSI_CMD(_ctrl_, UCSI_SET_NOTIFICATION_ENABLE) \
- (_ctrl_).cmd.data = _ntfys_; \
-}
-
-/* Helper for preparing ucsi_control for GET_CAPABILITY command. */
-#define UCSI_CMD_GET_CAPABILITY(_ctrl_) \
-{ \
- __UCSI_CMD(_ctrl_, UCSI_GET_CAPABILITY) \
-}
-
-/* Helper for preparing ucsi_control for GET_CONNECTOR_CAPABILITY command. */
-#define UCSI_CMD_GET_CONNECTOR_CAPABILITY(_ctrl_, _con_) \
-{ \
- __UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_CAPABILITY) \
- (_ctrl_).cmd.data = _con_; \
-}
-
-/* Helper for preparing ucsi_control for GET_ALTERNATE_MODES command. */
-#define UCSI_CMD_GET_ALTERNATE_MODES(_ctrl_, _r_, _con_num_, _o_, _num_)\
-{ \
- __UCSI_CMD((_ctrl_), UCSI_GET_ALTERNATE_MODES) \
- _ctrl_.alt.recipient = (_r_); \
- _ctrl_.alt.con_num = (_con_num_); \
- _ctrl_.alt.offset = (_o_); \
- _ctrl_.alt.num_altmodes = (_num_) - 1; \
-}
-
-/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */
-#define UCSI_CMD_GET_CAM_SUPPORTED(_ctrl_, _con_) \
-{ \
- __UCSI_CMD((_ctrl_), UCSI_GET_CAM_SUPPORTED) \
- _ctrl_.cmd.data = (_con_); \
-}
-
-/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */
-#define UCSI_CMD_GET_CURRENT_CAM(_ctrl_, _con_) \
-{ \
- __UCSI_CMD((_ctrl_), UCSI_GET_CURRENT_CAM) \
- _ctrl_.cmd.data = (_con_); \
-}
-
-/* Helper for preparing ucsi_control for GET_CONNECTOR_STATUS command. */
-#define UCSI_CMD_GET_CONNECTOR_STATUS(_ctrl_, _con_) \
-{ \
- __UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_STATUS) \
- (_ctrl_).cmd.data = _con_; \
-}
-
-#define __UCSI_ROLE(_ctrl_, _cmd_, _con_num_) \
-{ \
- __UCSI_CMD(_ctrl_, _cmd_) \
- (_ctrl_).uor.con_num = _con_num_; \
- (_ctrl_).uor.role = UCSI_UOR_ROLE_DRP; \
-}
-
-/* Helper for preparing ucsi_control for SET_UOR command. */
-#define UCSI_CMD_SET_UOR(_ctrl_, _con_, _role_) \
-{ \
- __UCSI_ROLE(_ctrl_, UCSI_SET_UOR, (_con_)->num) \
- (_ctrl_).uor.role |= (_role_) == TYPEC_HOST ? UCSI_UOR_ROLE_DFP : \
- UCSI_UOR_ROLE_UFP; \
-}
-
-/* Helper for preparing ucsi_control for SET_PDR command. */
-#define UCSI_CMD_SET_PDR(_ctrl_, _con_, _role_) \
-{ \
- __UCSI_ROLE(_ctrl_, UCSI_SET_PDR, (_con_)->num) \
- (_ctrl_).uor.role |= (_role_) == TYPEC_SOURCE ? UCSI_UOR_ROLE_DFP : \
- UCSI_UOR_ROLE_UFP; \
-}
+/* -------------------------------------------------------------------------- */
/* Commands */
#define UCSI_PPM_RESET 0x01
@@ -203,24 +87,59 @@
#define UCSI_GET_CONNECTOR_STATUS 0x12
#define UCSI_GET_ERROR_STATUS 0x13
-/* ACK_CC_CI commands */
-#define UCSI_ACK_EVENT 1
-#define UCSI_ACK_CMD 2
+#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16)
+#define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff)
-/* Bits for SET_NOTIFICATION_ENABLE command */
-#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(0)
-#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(1)
-#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(2)
-#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(5)
-#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(6)
-#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(7)
-#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(8)
-#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(9)
-#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(11)
-#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(12)
-#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(14)
-#define UCSI_ENABLE_NTFY_ERROR BIT(15)
-#define UCSI_ENABLE_NTFY_ALL 0xdbe7
+/* CONNECTOR_RESET command bits */
+#define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */
+
+/* ACK_CC_CI bits */
+#define UCSI_ACK_CONNECTOR_CHANGE BIT(16)
+#define UCSI_ACK_COMMAND_COMPLETE BIT(17)
+
+/* SET_NOTIFICATION_ENABLE command bits */
+#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(16)
+#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(17)
+#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(18)
+#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(21)
+#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(22)
+#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(23)
+#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(24)
+#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(25)
+#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(27)
+#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(28)
+#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(30)
+#define UCSI_ENABLE_NTFY_ERROR BIT(31)
+#define UCSI_ENABLE_NTFY_ALL 0xdbe70000
+
+/* SET_UOR command bits */
+#define UCSI_SET_UOR_ROLE(_r_) (((_r_) == TYPEC_HOST ? 1 : 2) << 23)
+#define UCSI_SET_UOR_ACCEPT_ROLE_SWAPS BIT(25)
+
+/* SET_PDF command bits */
+#define UCSI_SET_PDR_ROLE(_r_) (((_r_) == TYPEC_SOURCE ? 1 : 2) << 23)
+#define UCSI_SET_PDR_ACCEPT_ROLE_SWAPS BIT(25)
+
+/* GET_ALTERNATE_MODES command bits */
+#define UCSI_ALTMODE_RECIPIENT(_r_) (((_r_) >> 16) & 0x7)
+#define UCSI_GET_ALTMODE_RECIPIENT(_r_) ((u64)(_r_) << 16)
+#define UCSI_RECIPIENT_CON 0
+#define UCSI_RECIPIENT_SOP 1
+#define UCSI_RECIPIENT_SOP_P 2
+#define UCSI_RECIPIENT_SOP_PP 3
+#define UCSI_GET_ALTMODE_CONNECTOR_NUMBER(_r_) ((u64)(_r_) << 24)
+#define UCSI_ALTMODE_OFFSET(_r_) (((_r_) >> 32) & 0xff)
+#define UCSI_GET_ALTMODE_OFFSET(_r_) ((u64)(_r_) << 32)
+#define UCSI_GET_ALTMODE_NUM_ALTMODES(_r_) ((u64)(_r_) << 40)
+
+/* GET_PDOS command bits */
+#define UCSI_GET_PDOS_PARTNER_PDO(_r_) ((u64)(_r_) << 23)
+#define UCSI_GET_PDOS_PDO_OFFSET(_r_) ((u64)(_r_) << 24)
+#define UCSI_GET_PDOS_NUM_PDOS(_r_) ((u64)(_r_) << 32)
+#define UCSI_MAX_PDOS (4)
+#define UCSI_GET_PDOS_SRC_PDOS ((u64)1 << 34)
+
+/* -------------------------------------------------------------------------- */
/* Error information returned by PPM in response to GET_ERROR_STATUS command. */
#define UCSI_ERROR_UNREGONIZED_CMD BIT(0)
@@ -230,6 +149,18 @@
#define UCSI_ERROR_CC_COMMUNICATION_ERR BIT(4)
#define UCSI_ERROR_DEAD_BATTERY BIT(5)
#define UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL BIT(6)
+#define UCSI_ERROR_OVERCURRENT BIT(7)
+#define UCSI_ERROR_UNDEFINED BIT(8)
+#define UCSI_ERROR_PARTNER_REJECTED_SWAP BIT(9)
+#define UCSI_ERROR_HARD_RESET BIT(10)
+#define UCSI_ERROR_PPM_POLICY_CONFLICT BIT(11)
+#define UCSI_ERROR_SWAP_REJECTED BIT(12)
+
+#define UCSI_SET_NEW_CAM_ENTER(x) (((x) >> 23) & 0x1)
+#define UCSI_SET_NEW_CAM_GET_AM(x) (((x) >> 24) & 0xff)
+#define UCSI_SET_NEW_CAM_AM_MASK (0xff << 24)
+#define UCSI_SET_NEW_CAM_SET_AM(x) (((x) & 0xff) << 24)
+#define UCSI_CMD_CONNECTOR_MASK (0x7)
/* Data structure filled by PPM in response to GET_CAPABILITY command. */
struct ucsi_capability {
@@ -241,8 +172,8 @@
#define UCSI_CAP_ATTR_POWER_AC_SUPPLY BIT(8)
#define UCSI_CAP_ATTR_POWER_OTHER BIT(10)
#define UCSI_CAP_ATTR_POWER_VBUS BIT(14)
- u32 num_connectors:8;
- u32 features:24;
+ u8 num_connectors;
+ u8 features;
#define UCSI_CAP_SET_UOM BIT(0)
#define UCSI_CAP_SET_PDM BIT(1)
#define UCSI_CAP_ALT_MODE_DETAILS BIT(2)
@@ -251,8 +182,9 @@
#define UCSI_CAP_CABLE_DETAILS BIT(5)
#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
#define UCSI_CAP_PD_RESET BIT(7)
+ u16 reserved_1;
u8 num_alt_modes;
- u8 reserved;
+ u8 reserved_2;
u16 bc_version;
u16 pd_version;
u16 typec_version;
@@ -269,9 +201,9 @@
#define UCSI_CONCAP_OPMODE_USB2 BIT(5)
#define UCSI_CONCAP_OPMODE_USB3 BIT(6)
#define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7)
- u8 provider:1;
- u8 consumer:1;
- u8:6; /* reserved */
+ u8 flags;
+#define UCSI_CONCAP_FLAG_PROVIDER BIT(0)
+#define UCSI_CONCAP_FLAG_CONSUMER BIT(1)
} __packed;
struct ucsi_altmode {
@@ -283,18 +215,17 @@
struct ucsi_cable_property {
u16 speed_supported;
u8 current_capability;
- u8 vbus_in_cable:1;
- u8 active_cable:1;
- u8 directionality:1;
- u8 plug_type:2;
-#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0
-#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1
-#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2
-#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3
- u8 mode_support:1;
- u8:2; /* reserved */
- u8 latency:4;
- u8:4; /* reserved */
+ u8 flags;
+#define UCSI_CABLE_PROP_FLAG_VBUS_IN_CABLE BIT(0)
+#define UCSI_CABLE_PROP_FLAG_ACTIVE_CABLE BIT(1)
+#define UCSI_CABLE_PROP_FLAG_DIRECTIONALITY BIT(2)
+#define UCSI_CABLE_PROP_FLAG_PLUG_TYPE(_f_) ((_f_) & GENMASK(3, 0))
+#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A 0
+#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B 1
+#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C 2
+#define UCSI_CABLE_PROPERTY_PLUG_OTHER 3
+#define UCSI_CABLE_PROP_MODE_SUPPORT BIT(5)
+ u8 latency;
} __packed;
/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */
@@ -311,83 +242,47 @@
#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12)
#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14)
#define UCSI_CONSTAT_ERROR BIT(15)
- u16 pwr_op_mode:3;
-#define UCSI_CONSTAT_PWR_OPMODE_NONE 0
-#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1
-#define UCSI_CONSTAT_PWR_OPMODE_BC 2
-#define UCSI_CONSTAT_PWR_OPMODE_PD 3
-#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4
-#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5
- u16 connected:1;
- u16 pwr_dir:1;
- u16 partner_flags:8;
-#define UCSI_CONSTAT_PARTNER_FLAG_USB BIT(0)
-#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE BIT(1)
- u16 partner_type:3;
-#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1
-#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2
-#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */
-#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */
-#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5
-#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
+ u16 flags;
+#define UCSI_CONSTAT_PWR_OPMODE(_f_) ((_f_) & GENMASK(2, 0))
+#define UCSI_CONSTAT_PWR_OPMODE_NONE 0
+#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1
+#define UCSI_CONSTAT_PWR_OPMODE_BC 2
+#define UCSI_CONSTAT_PWR_OPMODE_PD 3
+#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4
+#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5
+#define UCSI_CONSTAT_CONNECTED BIT(3)
+#define UCSI_CONSTAT_PWR_DIR BIT(4)
+#define UCSI_CONSTAT_PARTNER_FLAGS(_f_) (((_f_) & GENMASK(12, 5)) >> 5)
+#define UCSI_CONSTAT_PARTNER_FLAG_USB 1
+#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE 2
+#define UCSI_CONSTAT_PARTNER_TYPE(_f_) (((_f_) & GENMASK(15, 13)) >> 13)
+#define UCSI_CONSTAT_PARTNER_TYPE_DFP 1
+#define UCSI_CONSTAT_PARTNER_TYPE_UFP 2
+#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */
+#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */
+#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5
+#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6
u32 request_data_obj;
- u8 bc_status:2;
-#define UCSI_CONSTAT_BC_NOT_CHARGING 0
-#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
-#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
-#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
- u8 provider_cap_limit_reason:4;
-#define UCSI_CONSTAT_CAP_PWR_LOWERED 0
-#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1
- u8:2; /* reserved */
+ u8 pwr_status;
+#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(2, 0))
+#define UCSI_CONSTAT_BC_NOT_CHARGING 0
+#define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1
+#define UCSI_CONSTAT_BC_SLOW_CHARGING 2
+#define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3
+#define UCSI_CONSTAT_PROVIDER_CAP_LIMIT(_p_) (((_p_) & GENMASK(6, 3)) >> 3)
+#define UCSI_CONSTAT_CAP_PWR_LOWERED 0
+#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT 1
} __packed;
/* -------------------------------------------------------------------------- */
-struct ucsi;
-
-struct ucsi_data {
- u16 version;
- u16 reserved;
- union {
- u32 raw_cci;
- struct ucsi_cci cci;
- };
- struct ucsi_control ctrl;
- u32 message_in[4];
- u32 message_out[4];
-} __packed;
-
-/*
- * struct ucsi_ppm - Interface to UCSI Platform Policy Manager
- * @data: memory location to the UCSI data structures
- * @cmd: UCSI command execution routine
- * @sync: Refresh UCSI mailbox (the data structures)
- */
-struct ucsi_ppm {
- struct ucsi_data *data;
- int (*cmd)(struct ucsi_ppm *, struct ucsi_control *);
- int (*sync)(struct ucsi_ppm *);
-};
-
-struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm);
-void ucsi_unregister_ppm(struct ucsi *ucsi);
-void ucsi_notify(struct ucsi *ucsi);
-
-/* -------------------------------------------------------------------------- */
-
-enum ucsi_status {
- UCSI_IDLE = 0,
- UCSI_BUSY,
- UCSI_ERROR,
-};
-
struct ucsi {
+ u16 version;
struct device *dev;
- struct ucsi_ppm *ppm;
+ struct driver_data *driver_data;
- enum ucsi_status status;
- struct completion complete;
+ const struct ucsi_operations *ops;
+
struct ucsi_capability cap;
struct ucsi_connector *connector;
@@ -396,16 +291,24 @@
/* PPM Communication lock */
struct mutex ppm_lock;
+ /* The latest "Notification Enable" bits (SET_NOTIFICATION_ENABLE) */
+ u64 ntfy;
+
/* PPM communication flags */
unsigned long flags;
#define EVENT_PENDING 0
#define COMMAND_PENDING 1
#define ACK_PENDING 2
+#define EVENT_PROCESSING 3
};
#define UCSI_MAX_SVID 5
#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6)
+#define UCSI_TYPEC_VSAFE5V 5000
+#define UCSI_TYPEC_1_5_CURRENT 1500
+#define UCSI_TYPEC_3_0_CURRENT 3000
+
struct ucsi_connector {
int num;
@@ -422,16 +325,32 @@
struct typec_capability typec_cap;
+ u16 unprocessed_changes;
struct ucsi_connector_status status;
struct ucsi_connector_capability cap;
+ struct power_supply *psy;
+ struct power_supply_desc psy_desc;
+ u32 rdo;
+ u32 src_pdos[PDO_MAX_OBJECTS];
+ int num_pdos;
};
-int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
+int ucsi_send_command(struct ucsi *ucsi, u64 command,
void *retval, size_t size);
void ucsi_altmode_update_active(struct ucsi_connector *con);
int ucsi_resume(struct ucsi *ucsi);
+#if IS_ENABLED(CONFIG_POWER_SUPPLY)
+int ucsi_register_port_psy(struct ucsi_connector *con);
+void ucsi_unregister_port_psy(struct ucsi_connector *con);
+void ucsi_port_psy_changed(struct ucsi_connector *con);
+#else
+static inline int ucsi_register_port_psy(struct ucsi_connector *con) { return 0; }
+static inline void ucsi_unregister_port_psy(struct ucsi_connector *con) { }
+static inline void ucsi_port_psy_changed(struct ucsi_connector *con) { }
+#endif /* CONFIG_POWER_SUPPLY */
+
#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
struct typec_altmode *
ucsi_register_displayport(struct ucsi_connector *con,
@@ -453,4 +372,11 @@
ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
#endif /* CONFIG_TYPEC_DP_ALTMODE */
+/*
+ * NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink
+ * DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3
+ */
+#define USB_TYPEC_NVIDIA_VLINK_DP_VDO 0x1
+#define USB_TYPEC_NVIDIA_VLINK_DBG_VDO 0x3
+
#endif /* __DRIVER_USB_TYPEC_UCSI_H */
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index dda8bd3..0497643 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -19,7 +19,9 @@
struct ucsi_acpi {
struct device *dev;
struct ucsi *ucsi;
- struct ucsi_ppm ppm;
+ void __iomem *base;
+ struct completion complete;
+ unsigned long flags;
guid_t guid;
};
@@ -39,27 +41,74 @@
return 0;
}
-static int ucsi_acpi_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
+static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
+ void *val, size_t val_len)
{
- struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+ int ret;
- ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
+ ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
+ if (ret)
+ return ret;
+
+ memcpy(val, (const void __force *)(ua->base + offset), val_len);
+
+ return 0;
+}
+
+static int ucsi_acpi_async_write(struct ucsi *ucsi, unsigned int offset,
+ const void *val, size_t val_len)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+
+ memcpy((void __force *)(ua->base + offset), val, val_len);
return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
}
-static int ucsi_acpi_sync(struct ucsi_ppm *ppm)
+static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
+ const void *val, size_t val_len)
{
- struct ucsi_acpi *ua = container_of(ppm, struct ucsi_acpi, ppm);
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+ int ret;
- return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
+ set_bit(COMMAND_PENDING, &ua->flags);
+
+ ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
+ if (ret)
+ goto out_clear_bit;
+
+ if (!wait_for_completion_timeout(&ua->complete, 60 * HZ))
+ ret = -ETIMEDOUT;
+
+out_clear_bit:
+ clear_bit(COMMAND_PENDING, &ua->flags);
+
+ return ret;
}
+static const struct ucsi_operations ucsi_acpi_ops = {
+ .read = ucsi_acpi_read,
+ .sync_write = ucsi_acpi_sync_write,
+ .async_write = ucsi_acpi_async_write
+};
+
static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
{
struct ucsi_acpi *ua = data;
+ u32 cci;
+ int ret;
- ucsi_notify(ua->ucsi);
+ ret = ucsi_acpi_read(ua->ucsi, UCSI_CCI, &cci, sizeof(cci));
+ if (ret)
+ return;
+
+ if (UCSI_CCI_CONNECTOR(cci))
+ ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
+
+ if (test_bit(COMMAND_PENDING, &ua->flags) &&
+ cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
+ complete(&ua->complete);
}
static int ucsi_acpi_probe(struct platform_device *pdev)
@@ -83,7 +132,7 @@
return -ENODEV;
}
- /* This will make sure we can use ioremap_nocache() */
+ /* This will make sure we can use ioremap() */
status = acpi_release_memory(ACPI_HANDLE(&pdev->dev), res, 1);
if (ACPI_FAILURE(status))
return -ENOMEM;
@@ -94,35 +143,39 @@
* it can not be requested here, and we can not use
* devm_ioremap_resource().
*/
- ua->ppm.data = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!ua->ppm.data)
+ ua->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!ua->base)
return -ENOMEM;
- if (!ua->ppm.data->version)
- return -ENODEV;
-
ret = guid_parse(UCSI_DSM_UUID, &ua->guid);
if (ret)
return ret;
- ua->ppm.cmd = ucsi_acpi_cmd;
- ua->ppm.sync = ucsi_acpi_sync;
+ init_completion(&ua->complete);
ua->dev = &pdev->dev;
+ ua->ucsi = ucsi_create(&pdev->dev, &ucsi_acpi_ops);
+ if (IS_ERR(ua->ucsi))
+ return PTR_ERR(ua->ucsi);
+
+ ucsi_set_drvdata(ua->ucsi, ua);
+
status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
ACPI_DEVICE_NOTIFY,
ucsi_acpi_notify, ua);
if (ACPI_FAILURE(status)) {
dev_err(&pdev->dev, "failed to install notify handler\n");
+ ucsi_destroy(ua->ucsi);
return -ENODEV;
}
- ua->ucsi = ucsi_register_ppm(&pdev->dev, &ua->ppm);
- if (IS_ERR(ua->ucsi)) {
+ ret = ucsi_register(ua->ucsi);
+ if (ret) {
acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
ACPI_DEVICE_NOTIFY,
ucsi_acpi_notify);
- return PTR_ERR(ua->ucsi);
+ ucsi_destroy(ua->ucsi);
+ return ret;
}
platform_set_drvdata(pdev, ua);
@@ -134,7 +187,8 @@
{
struct ucsi_acpi *ua = platform_get_drvdata(pdev);
- ucsi_unregister_ppm(ua->ucsi);
+ ucsi_unregister(ua->ucsi);
+ ucsi_destroy(ua->ucsi);
acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY,
ucsi_acpi_notify);
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index d772fce..6db7c8d 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/usb/typec_dp.h>
#include <asm/unaligned.h>
#include "ucsi.h"
@@ -124,6 +125,10 @@
#define CCG_FW_BUILD_NVIDIA (('n' << 8) | 'v')
#define CCG_OLD_FW_VERSION (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10))
+/* Altmode offset for NVIDIA Function Test Board (FTB) */
+#define NVIDIA_FTB_DP_OFFSET (2)
+#define NVIDIA_FTB_DBG_OFFSET (3)
+
struct version_info {
struct version_format base;
struct version_format app;
@@ -173,11 +178,20 @@
u8 length;
};
+struct ucsi_ccg_altmode {
+ u16 svid;
+ u32 mid;
+ u8 linked_idx;
+ u8 active_idx;
+#define UCSI_MULTI_DP_INDEX (0xff)
+ bool checked;
+} __packed;
+
struct ucsi_ccg {
struct device *dev;
struct ucsi *ucsi;
- struct ucsi_ppm ppm;
struct i2c_client *client;
+
struct ccg_dev_info info;
/* version info for boot, primary and secondary */
struct version_info version[FW2 + 1];
@@ -196,6 +210,13 @@
/* fw build with vendor information */
u16 fw_build;
struct work_struct pm_work;
+
+ struct completion complete;
+
+ u64 last_cmd_sent;
+ bool has_multiple_dp;
+ struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
+ struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
};
static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
@@ -243,7 +264,7 @@
return 0;
}
-static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
+static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
{
struct i2c_client *client = uc->client;
unsigned char *buf;
@@ -304,7 +325,7 @@
if (status < 0)
return status;
- if (!data)
+ if (!(data & DEV_INT))
return 0;
status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
@@ -317,88 +338,298 @@
return -ETIMEDOUT;
}
-static int ucsi_ccg_send_data(struct ucsi_ccg *uc)
+static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data)
{
- u8 *ppm = (u8 *)uc->ppm.data;
- int status;
- u16 rab;
+ u8 cam, new_cam;
- rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out));
- status = ccg_write(uc, rab, ppm +
- offsetof(struct ucsi_data, message_out),
- sizeof(uc->ppm.data->message_out));
- if (status < 0)
- return status;
-
- rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl));
- return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl),
- sizeof(uc->ppm.data->ctrl));
+ cam = data[0];
+ new_cam = uc->orig[cam].linked_idx;
+ uc->updated[new_cam].active_idx = cam;
+ data[0] = new_cam;
}
-static int ucsi_ccg_recv_data(struct ucsi_ccg *uc)
+static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi,
+ struct ucsi_altmode *orig,
+ struct ucsi_altmode *updated)
{
- u8 *ppm = (u8 *)uc->ppm.data;
- int status;
- u16 rab;
+ struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
+ struct ucsi_ccg_altmode *alt, *new_alt;
+ int i, j, k = 0;
+ bool found = false;
- rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci));
- status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci),
- sizeof(uc->ppm.data->cci));
- if (status < 0)
- return status;
+ alt = uc->orig;
+ new_alt = uc->updated;
+ memset(uc->updated, 0, sizeof(uc->updated));
- rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in));
- return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in),
- sizeof(uc->ppm.data->message_in));
+ /*
+ * Copy original connector altmodes to new structure.
+ * We need this before second loop since second loop
+ * checks for duplicate altmodes.
+ */
+ for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
+ alt[i].svid = orig[i].svid;
+ alt[i].mid = orig[i].mid;
+ if (!alt[i].svid)
+ break;
+ }
+
+ for (i = 0; i < UCSI_MAX_ALTMODES; i++) {
+ if (!alt[i].svid)
+ break;
+
+ /* already checked and considered */
+ if (alt[i].checked)
+ continue;
+
+ if (!DP_CONF_GET_PIN_ASSIGN(alt[i].mid)) {
+ /* Found Non DP altmode */
+ new_alt[k].svid = alt[i].svid;
+ new_alt[k].mid |= alt[i].mid;
+ new_alt[k].linked_idx = i;
+ alt[i].linked_idx = k;
+ updated[k].svid = new_alt[k].svid;
+ updated[k].mid = new_alt[k].mid;
+ k++;
+ continue;
+ }
+
+ for (j = i + 1; j < UCSI_MAX_ALTMODES; j++) {
+ if (alt[i].svid != alt[j].svid ||
+ !DP_CONF_GET_PIN_ASSIGN(alt[j].mid)) {
+ continue;
+ } else {
+ /* Found duplicate DP mode */
+ new_alt[k].svid = alt[i].svid;
+ new_alt[k].mid |= alt[i].mid | alt[j].mid;
+ new_alt[k].linked_idx = UCSI_MULTI_DP_INDEX;
+ alt[i].linked_idx = k;
+ alt[j].linked_idx = k;
+ alt[j].checked = true;
+ found = true;
+ }
+ }
+ if (found) {
+ uc->has_multiple_dp = true;
+ } else {
+ /* Didn't find any duplicate DP altmode */
+ new_alt[k].svid = alt[i].svid;
+ new_alt[k].mid |= alt[i].mid;
+ new_alt[k].linked_idx = i;
+ alt[i].linked_idx = k;
+ }
+ updated[k].svid = new_alt[k].svid;
+ updated[k].mid = new_alt[k].mid;
+ k++;
+ }
+ return found;
}
-static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc)
+static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc,
+ struct ucsi_connector *con,
+ u64 *cmd)
{
- int status;
- unsigned char data;
+ struct ucsi_ccg_altmode *new_port, *port;
+ struct typec_altmode *alt = NULL;
+ u8 new_cam, cam, pin;
+ bool enter_new_mode;
+ int i, j, k = 0xff;
- status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
- if (status < 0)
- return status;
+ port = uc->orig;
+ new_cam = UCSI_SET_NEW_CAM_GET_AM(*cmd);
+ new_port = &uc->updated[new_cam];
+ cam = new_port->linked_idx;
+ enter_new_mode = UCSI_SET_NEW_CAM_ENTER(*cmd);
- return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
+ /*
+ * If CAM is UCSI_MULTI_DP_INDEX then this is DP altmode
+ * with multiple DP mode. Find out CAM for best pin assignment
+ * among all DP mode. Priorite pin E->D->C after making sure
+ * the partner supports that pin.
+ */
+ if (cam == UCSI_MULTI_DP_INDEX) {
+ if (enter_new_mode) {
+ for (i = 0; con->partner_altmode[i]; i++) {
+ alt = con->partner_altmode[i];
+ if (alt->svid == new_port->svid)
+ break;
+ }
+ /*
+ * alt will always be non NULL since this is
+ * UCSI_SET_NEW_CAM command and so there will be
+ * at least one con->partner_altmode[i] with svid
+ * matching with new_port->svid.
+ */
+ for (j = 0; port[j].svid; j++) {
+ pin = DP_CONF_GET_PIN_ASSIGN(port[j].mid);
+ if (alt && port[j].svid == alt->svid &&
+ (pin & DP_CONF_GET_PIN_ASSIGN(alt->vdo))) {
+ /* prioritize pin E->D->C */
+ if (k == 0xff || (k != 0xff && pin >
+ DP_CONF_GET_PIN_ASSIGN(port[k].mid))
+ ) {
+ k = j;
+ }
+ }
+ }
+ cam = k;
+ new_port->active_idx = cam;
+ } else {
+ cam = new_port->active_idx;
+ }
+ }
+ *cmd &= ~UCSI_SET_NEW_CAM_AM_MASK;
+ *cmd |= UCSI_SET_NEW_CAM_SET_AM(cam);
}
-static int ucsi_ccg_sync(struct ucsi_ppm *ppm)
+/*
+ * Change the order of vdo values of NVIDIA test device FTB
+ * (Function Test Board) which reports altmode list with vdo=0x3
+ * first and then vdo=0x. Current logic to assign mode value is
+ * based on order in altmode list and it causes a mismatch of CON
+ * and SOP altmodes since NVIDIA GPU connector has order of vdo=0x1
+ * first and then vdo=0x3
+ */
+static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
+ struct ucsi_altmode *alt)
{
- struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
- int status;
-
- status = ucsi_ccg_recv_data(uc);
- if (status < 0)
- return status;
-
- /* ack interrupt to allow next command to run */
- return ucsi_ccg_ack_interrupt(uc);
+ switch (UCSI_ALTMODE_OFFSET(uc->last_cmd_sent)) {
+ case NVIDIA_FTB_DP_OFFSET:
+ if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
+ alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO |
+ DP_CAP_DP_SIGNALING | DP_CAP_USB |
+ DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_E));
+ break;
+ case NVIDIA_FTB_DBG_OFFSET:
+ if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DP_VDO)
+ alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DBG_VDO;
+ break;
+ default:
+ break;
+ }
}
-static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
+static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
+ void *val, size_t val_len)
{
- struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
+ struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
+ u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
+ struct ucsi_altmode *alt;
+ int ret;
- ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
- return ucsi_ccg_send_data(uc);
+ ret = ccg_read(uc, reg, val, val_len);
+ if (ret)
+ return ret;
+
+ if (offset != UCSI_MESSAGE_IN)
+ return ret;
+
+ switch (UCSI_COMMAND(uc->last_cmd_sent)) {
+ case UCSI_GET_CURRENT_CAM:
+ if (uc->has_multiple_dp)
+ ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val);
+ break;
+ case UCSI_GET_ALTERNATE_MODES:
+ if (UCSI_ALTMODE_RECIPIENT(uc->last_cmd_sent) ==
+ UCSI_RECIPIENT_SOP) {
+ alt = val;
+ if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
+ ucsi_ccg_nvidia_altmode(uc, alt);
+ }
+ break;
+ default:
+ break;
+ }
+ uc->last_cmd_sent = 0;
+
+ return ret;
}
+static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
+ const void *val, size_t val_len)
+{
+ u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
+
+ return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len);
+}
+
+static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
+ const void *val, size_t val_len)
+{
+ struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
+ struct ucsi_connector *con;
+ int con_index;
+ int ret;
+
+ mutex_lock(&uc->lock);
+ pm_runtime_get_sync(uc->dev);
+ set_bit(DEV_CMD_PENDING, &uc->flags);
+
+ if (offset == UCSI_CONTROL && val_len == sizeof(uc->last_cmd_sent)) {
+ uc->last_cmd_sent = *(u64 *)val;
+
+ if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
+ uc->has_multiple_dp) {
+ con_index = (uc->last_cmd_sent >> 16) &
+ UCSI_CMD_CONNECTOR_MASK;
+ con = &uc->ucsi->connector[con_index - 1];
+ ucsi_ccg_update_set_new_cam_cmd(uc, con, (u64 *)val);
+ }
+ }
+
+ ret = ucsi_ccg_async_write(ucsi, offset, val, val_len);
+ if (ret)
+ goto err_clear_bit;
+
+ if (!wait_for_completion_timeout(&uc->complete, msecs_to_jiffies(5000)))
+ ret = -ETIMEDOUT;
+
+err_clear_bit:
+ clear_bit(DEV_CMD_PENDING, &uc->flags);
+ pm_runtime_put_sync(uc->dev);
+ mutex_unlock(&uc->lock);
+
+ return ret;
+}
+
+static const struct ucsi_operations ucsi_ccg_ops = {
+ .read = ucsi_ccg_read,
+ .sync_write = ucsi_ccg_sync_write,
+ .async_write = ucsi_ccg_async_write,
+ .update_altmodes = ucsi_ccg_update_altmodes
+};
+
static irqreturn_t ccg_irq_handler(int irq, void *data)
{
+ u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI);
struct ucsi_ccg *uc = data;
+ u8 intr_reg;
+ u32 cci;
+ int ret;
- ucsi_notify(uc->ucsi);
+ ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
+ if (ret)
+ return ret;
+
+ ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci));
+ if (ret)
+ goto err_clear_irq;
+
+ if (UCSI_CCI_CONNECTOR(cci))
+ ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
+
+ if (test_bit(DEV_CMD_PENDING, &uc->flags) &&
+ cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
+ complete(&uc->complete);
+
+err_clear_irq:
+ ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
return IRQ_HANDLED;
}
static void ccg_pm_workaround_work(struct work_struct *pm_work)
{
- struct ucsi_ccg *uc = container_of(pm_work, struct ucsi_ccg, pm_work);
-
- ucsi_notify(uc->ucsi);
+ ccg_irq_handler(0, container_of(pm_work, struct ucsi_ccg, pm_work));
}
static int get_fw_info(struct ucsi_ccg *uc)
@@ -1027,12 +1258,13 @@
return status;
}
- uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
- if (IS_ERR(uc->ucsi)) {
- dev_err(uc->dev, "ucsi_register_ppm failed\n");
- return PTR_ERR(uc->ucsi);
+ status = ucsi_register(uc->ucsi);
+ if (status) {
+ dev_err(uc->dev, "failed to register the interface\n");
+ return status;
}
+ pm_runtime_enable(uc->dev);
return 0;
}
@@ -1047,7 +1279,8 @@
return;
if (flash_mode != FLASH_NOT_NEEDED) {
- ucsi_unregister_ppm(uc->ucsi);
+ ucsi_unregister(uc->ucsi);
+ pm_runtime_disable(uc->dev);
free_irq(uc->irq, uc);
ccg_fw_update(uc, flash_mode);
@@ -1091,21 +1324,15 @@
struct device *dev = &client->dev;
struct ucsi_ccg *uc;
int status;
- u16 rab;
uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
if (!uc)
return -ENOMEM;
- uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL);
- if (!uc->ppm.data)
- return -ENOMEM;
-
- uc->ppm.cmd = ucsi_ccg_cmd;
- uc->ppm.sync = ucsi_ccg_sync;
uc->dev = dev;
uc->client = client;
mutex_init(&uc->lock);
+ init_completion(&uc->complete);
INIT_WORK(&uc->work, ccg_update_firmware);
INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
@@ -1133,30 +1360,25 @@
if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK)
uc->port_num++;
+ uc->ucsi = ucsi_create(dev, &ucsi_ccg_ops);
+ if (IS_ERR(uc->ucsi))
+ return PTR_ERR(uc->ucsi);
+
+ ucsi_set_drvdata(uc->ucsi, uc);
+
status = request_threaded_irq(client->irq, NULL, ccg_irq_handler,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
dev_name(dev), uc);
if (status < 0) {
dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
- return status;
+ goto out_ucsi_destroy;
}
uc->irq = client->irq;
- uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
- if (IS_ERR(uc->ucsi)) {
- dev_err(uc->dev, "ucsi_register_ppm failed\n");
- return PTR_ERR(uc->ucsi);
- }
-
- rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version));
- status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) +
- offsetof(struct ucsi_data, version),
- sizeof(uc->ppm.data->version));
- if (status < 0) {
- ucsi_unregister_ppm(uc->ucsi);
- return status;
- }
+ status = ucsi_register(uc->ucsi);
+ if (status)
+ goto out_free_irq;
i2c_set_clientdata(client, uc);
@@ -1167,6 +1389,13 @@
pm_runtime_idle(uc->dev);
return 0;
+
+out_free_irq:
+ free_irq(uc->irq, uc);
+out_ucsi_destroy:
+ ucsi_destroy(uc->ucsi);
+
+ return status;
}
static int ucsi_ccg_remove(struct i2c_client *client)
@@ -1175,8 +1404,9 @@
cancel_work_sync(&uc->pm_work);
cancel_work_sync(&uc->work);
- ucsi_unregister_ppm(uc->ucsi);
pm_runtime_disable(uc->dev);
+ ucsi_unregister(uc->ucsi);
+ ucsi_destroy(uc->ucsi);
free_irq(uc->irq, uc);
return 0;
diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig
index 7bbae7a..b9f94e2 100644
--- a/drivers/usb/usbip/Kconfig
+++ b/drivers/usb/usbip/Kconfig
@@ -5,7 +5,7 @@
depends on NET
select USB_COMMON
select SGL_ALLOC
- ---help---
+ help
This enables pushing USB packets over IP to allow remote
machines direct access to USB devices. It provides the
USB/IP core that is required by both drivers.
@@ -21,7 +21,7 @@
config USBIP_VHCI_HCD
tristate "VHCI hcd"
depends on USBIP_CORE && USB
- ---help---
+ help
This enables the USB/IP virtual host controller driver,
which is run on the remote machine.
@@ -33,7 +33,7 @@
range 1 15
default 8
depends on USBIP_VHCI_HCD
- ---help---
+ help
To increase number of ports available for USB/IP virtual
host controller driver, this defines number of ports per
USB/IP virtual host controller.
@@ -43,7 +43,7 @@
range 1 128
default 1
depends on USBIP_VHCI_HCD
- ---help---
+ help
To increase number of ports available for USB/IP virtual
host controller driver, this defines number of USB/IP
virtual host controllers as if adding physical host
@@ -52,7 +52,7 @@
config USBIP_HOST
tristate "Host driver"
depends on USBIP_CORE && USB
- ---help---
+ help
This enables the USB/IP host driver, which is run on the
machine that is sharing the USB devices.
@@ -62,7 +62,7 @@
config USBIP_VUDC
tristate "VUDC driver"
depends on USBIP_CORE && USB_GADGET
- ---help---
+ help
This enables the USB/IP virtual USB device controller
driver, which is run on the host machine, allowing the
machine itself to act as a device.
@@ -73,5 +73,5 @@
config USBIP_DEBUG
bool "Debug messages for USB/IP"
depends on USBIP_CORE
- ---help---
+ help
This enables the debug messages from the USB/IP drivers.
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index e2b0195..325c220 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -424,7 +424,7 @@
case USB_ENDPOINT_XFER_BULK:
if (is_out)
allowed |= URB_ZERO_PACKET;
- /* FALLTHROUGH */
+ fallthrough;
default: /* all non-iso endpoints */
if (!is_out)
allowed |= URB_SHORT_NOT_OK;
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index 36010a8..b1c2f67 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -291,7 +291,7 @@
kfree(iov);
usbip_event_add(&sdev->ud,
SDEV_EVENT_ERROR_TCP);
- return -1;
+ return -1;
}
}
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index e4b9667..4ce6c6a 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -755,13 +755,7 @@
static int __init usbip_core_init(void)
{
- int ret;
-
- ret = usbip_init_eh();
- if (ret)
- return ret;
-
- return 0;
+ return usbip_init_eh();
}
static void __exit usbip_core_exit(void)
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 170abb0..b07b292 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -516,7 +516,7 @@
case USB_PORT_FEAT_U1_TIMEOUT:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_U1_TIMEOUT\n");
- /* Fall through */
+ fallthrough;
case USB_PORT_FEAT_U2_TIMEOUT:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n");
@@ -569,7 +569,7 @@
"supported for USB 2.0 roothub\n");
goto error;
}
- /* FALLS THROUGH */
+ fallthrough;
case USB_PORT_FEAT_RESET:
usbip_dbg_vhci_rh(
" SetPortFeature: USB_PORT_FEAT_RESET\n");
@@ -592,8 +592,7 @@
/* 50msec reset signaling */
vhci_hcd->re_timeout = jiffies + msecs_to_jiffies(50);
-
- /* FALLS THROUGH */
+ fallthrough;
default:
usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
wValue);
@@ -808,8 +807,14 @@
usb_hcd_unlink_urb_from_ep(hcd, urb);
no_need_unlink:
spin_unlock_irqrestore(&vhci->lock, flags);
- if (!ret)
+ if (!ret) {
+ /* usb_hcd_giveback_urb() should be called with
+ * irqs disabled
+ */
+ local_irq_disable();
usb_hcd_giveback_urb(hcd, urb, urb->status);
+ local_irq_enable();
+ }
return ret;
}
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index 00fc987..266024c 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -27,7 +27,7 @@
switch (status) {
case -ENOENT:
- /* fall through */
+ fallthrough;
case -ECONNRESET:
dev_dbg(&urb->dev->dev,
"urb seq# %u was unlinked %ssynchronously\n",
diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c
index c9db846..7e801fe 100644
--- a/drivers/usb/usbip/vudc_transfer.c
+++ b/drivers/usb/usbip/vudc_transfer.c
@@ -404,7 +404,7 @@
* for now, give unlimited bandwidth
*/
limit += urb->transfer_buffer_length;
- /* fallthrough */
+ fallthrough;
default:
treat_control_like_bulk:
total -= transfer(udc, urb, ep, limit);
@@ -479,7 +479,7 @@
return;
case VUDC_TR_IDLE:
t->state = VUDC_TR_RUNNING;
- /* fallthrough */
+ fallthrough;
case VUDC_TR_STOPPED:
/* we may want to kick timer to unqueue urbs */
mod_timer(&t->timer, time);