summaryrefslogtreecommitdiff
path: root/drivers/usb/common
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/common')
-rw-r--r--drivers/usb/common/Kconfig4
-rw-r--r--drivers/usb/common/common.c155
-rw-r--r--drivers/usb/common/debug.c93
-rw-r--r--drivers/usb/common/led.c4
-rw-r--r--drivers/usb/common/ulpi.c95
-rw-r--r--drivers/usb/common/usb-conn-gpio.c138
-rw-r--r--drivers/usb/common/usb-otg-fsm.c7
7 files changed, 418 insertions, 78 deletions
diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig
index d611477aae41..b856622431a7 100644
--- a/drivers/usb/common/Kconfig
+++ b/drivers/usb/common/Kconfig
@@ -6,8 +6,7 @@ config USB_COMMON
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 @@ config USB_CONN_GPIO
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/common.c b/drivers/usb/common/common.c
index 1433260d99b4..fc0845f681be 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/of.h>
#include <linux/usb/otg.h>
@@ -25,6 +26,12 @@ static const char *const ep_type_names[] = {
[USB_ENDPOINT_XFER_INT] = "intr",
};
+/**
+ * usb_ep_type_string() - Returns human readable-name of the endpoint type.
+ * @ep_type: The endpoint type to return human-readable name for. If it's not
+ * any of the types: USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT},
+ * usually got by usb_endpoint_type(), the string 'unknown' will be returned.
+ */
const char *usb_ep_type_string(int ep_type)
{
if (ep_type < 0 || ep_type >= ARRAY_SIZE(ep_type_names))
@@ -34,6 +41,12 @@ const char *usb_ep_type_string(int ep_type)
}
EXPORT_SYMBOL_GPL(usb_ep_type_string);
+/**
+ * usb_otg_state_string() - returns human readable name of OTG state.
+ * @state: the OTG state to return the human readable name of. If it's not
+ * any of the states defined in usb_otg_state enum, 'UNDEFINED' will be
+ * returned.
+ */
const char *usb_otg_state_string(enum usb_otg_state state)
{
static const char *const names[] = {
@@ -69,6 +82,19 @@ static const char *const speed_names[] = {
[USB_SPEED_SUPER_PLUS] = "super-speed-plus",
};
+static const char *const ssp_rate[] = {
+ [USB_SSP_GEN_UNKNOWN] = "UNKNOWN",
+ [USB_SSP_GEN_2x1] = "super-speed-plus-gen2x1",
+ [USB_SSP_GEN_1x2] = "super-speed-plus-gen1x2",
+ [USB_SSP_GEN_2x2] = "super-speed-plus-gen2x2",
+};
+
+/**
+ * usb_speed_string() - Returns human readable-name of the speed.
+ * @speed: The speed to return human-readable name for. If it's not
+ * any of the speeds defined in usb_device_speed enum, string for
+ * USB_SPEED_UNKNOWN will be returned.
+ */
const char *usb_speed_string(enum usb_device_speed speed)
{
if (speed < 0 || speed >= ARRAY_SIZE(speed_names))
@@ -77,21 +103,60 @@ const char *usb_speed_string(enum usb_device_speed speed)
}
EXPORT_SYMBOL_GPL(usb_speed_string);
+/**
+ * usb_get_maximum_speed - Get maximum requested speed for a given USB
+ * controller.
+ * @dev: Pointer to the given USB controller device
+ *
+ * The function gets the maximum speed string from property "maximum-speed",
+ * and returns the corresponding enum usb_device_speed.
+ */
enum usb_device_speed usb_get_maximum_speed(struct device *dev)
{
+ const char *p = "maximum-speed";
+ int ret;
+
+ ret = device_property_match_property_string(dev, p, ssp_rate, ARRAY_SIZE(ssp_rate));
+ if (ret > 0)
+ return USB_SPEED_SUPER_PLUS;
+
+ ret = device_property_match_property_string(dev, p, speed_names, ARRAY_SIZE(speed_names));
+ if (ret > 0)
+ return ret;
+
+ return USB_SPEED_UNKNOWN;
+}
+EXPORT_SYMBOL_GPL(usb_get_maximum_speed);
+
+/**
+ * usb_get_maximum_ssp_rate - Get the signaling rate generation and lane count
+ * of a SuperSpeed Plus capable device.
+ * @dev: Pointer to the given USB controller device
+ *
+ * If the string from "maximum-speed" property is super-speed-plus-genXxY where
+ * 'X' is the generation number and 'Y' is the number of lanes, then this
+ * function returns the corresponding enum usb_ssp_rate.
+ */
+enum usb_ssp_rate usb_get_maximum_ssp_rate(struct device *dev)
+{
const char *maximum_speed;
int ret;
ret = device_property_read_string(dev, "maximum-speed", &maximum_speed);
if (ret < 0)
- return USB_SPEED_UNKNOWN;
-
- ret = match_string(speed_names, ARRAY_SIZE(speed_names), maximum_speed);
+ return USB_SSP_GEN_UNKNOWN;
- return (ret < 0) ? USB_SPEED_UNKNOWN : ret;
+ ret = match_string(ssp_rate, ARRAY_SIZE(ssp_rate), maximum_speed);
+ return (ret < 0) ? USB_SSP_GEN_UNKNOWN : ret;
}
-EXPORT_SYMBOL_GPL(usb_get_maximum_speed);
+EXPORT_SYMBOL_GPL(usb_get_maximum_ssp_rate);
+/**
+ * usb_state_string - Returns human readable name for the state.
+ * @state: The state to return a human-readable name for. If it's not
+ * any of the states devices in usb_device_state_string enum,
+ * the string UNKNOWN will be returned.
+ */
const char *usb_state_string(enum usb_device_state state)
{
static const char *const names[] = {
@@ -120,6 +185,14 @@ static const char *const usb_dr_modes[] = {
[USB_DR_MODE_OTG] = "otg",
};
+/**
+ * usb_get_dr_mode_from_string() - Get dual role mode for given string
+ * @str: String to find the corresponding dual role mode for
+ *
+ * This function performs a lookup for the given string and returns the
+ * corresponding enum usb_dr_mode. If no match for the string could be found,
+ * 'USB_DR_MODE_UNKNOWN' is returned.
+ */
static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str)
{
int ret;
@@ -141,6 +214,67 @@ enum usb_dr_mode usb_get_dr_mode(struct device *dev)
}
EXPORT_SYMBOL_GPL(usb_get_dr_mode);
+/**
+ * usb_get_role_switch_default_mode - Get default mode for given device
+ * @dev: Pointer to the given device
+ *
+ * The function gets string from property 'role-switch-default-mode',
+ * and returns the corresponding enum usb_dr_mode.
+ */
+enum usb_dr_mode usb_get_role_switch_default_mode(struct device *dev)
+{
+ const char *str;
+ int ret;
+
+ ret = device_property_read_string(dev, "role-switch-default-mode", &str);
+ if (ret < 0)
+ return USB_DR_MODE_UNKNOWN;
+
+ return usb_get_dr_mode_from_string(str);
+}
+EXPORT_SYMBOL_GPL(usb_get_role_switch_default_mode);
+
+/**
+ * usb_decode_interval - Decode bInterval into the time expressed in 1us unit
+ * @epd: The descriptor of the endpoint
+ * @speed: The speed that the endpoint works as
+ *
+ * Function returns the interval expressed in 1us unit for servicing
+ * endpoint for data transfers.
+ */
+unsigned int usb_decode_interval(const struct usb_endpoint_descriptor *epd,
+ enum usb_device_speed speed)
+{
+ unsigned int interval = 0;
+
+ switch (usb_endpoint_type(epd)) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* uframes per NAK */
+ if (speed == USB_SPEED_HIGH)
+ interval = epd->bInterval;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ interval = 1 << (epd->bInterval - 1);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ /* uframes per NAK */
+ if (speed == USB_SPEED_HIGH && usb_endpoint_dir_out(epd))
+ interval = epd->bInterval;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (speed >= USB_SPEED_HIGH)
+ interval = 1 << (epd->bInterval - 1);
+ else
+ interval = epd->bInterval;
+ break;
+ }
+
+ interval *= (speed >= USB_SPEED_HIGH) ? 125 : 1000;
+
+ return interval;
+}
+EXPORT_SYMBOL_GPL(usb_decode_interval);
+
#ifdef CONFIG_OF
/**
* of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device
@@ -155,14 +289,13 @@ EXPORT_SYMBOL_GPL(usb_get_dr_mode);
*/
enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
{
- struct device_node *controller = NULL;
+ struct device_node *controller;
struct of_phandle_args args;
const char *dr_mode;
int index;
int err;
- do {
- controller = of_find_node_with_property(controller, "phys");
+ for_each_node_with_property(controller, "phys") {
if (!of_device_is_available(controller))
continue;
index = 0;
@@ -185,7 +318,7 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
goto finish;
index++;
} while (args.np);
- } while (controller);
+ }
finish:
err = of_property_read_string(controller, "dr_mode", &dr_mode);
@@ -296,6 +429,9 @@ EXPORT_SYMBOL_GPL(usb_of_get_companion_dev);
struct dentry *usb_debug_root;
EXPORT_SYMBOL_GPL(usb_debug_root);
+DEFINE_MUTEX(usb_dynids_lock);
+EXPORT_SYMBOL_GPL(usb_dynids_lock);
+
static int __init usb_common_init(void)
{
usb_debug_root = debugfs_create_dir("usb", NULL);
@@ -312,4 +448,5 @@ static void __exit usb_common_exit(void)
subsys_initcall(usb_common_init);
module_exit(usb_common_exit);
+MODULE_DESCRIPTION("Common code for host and device side USB");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index 92a986aeaa5d..f204cec8d380 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -1,13 +1,14 @@
// 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>
*/
+#include <linux/kernel.h>
#include <linux/usb/ch9.h>
static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
@@ -53,15 +54,15 @@ static const char *usb_decode_device_feature(u16 wValue)
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,12 +208,28 @@ static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size)
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,
- __u8 bRequest, __u16 wValue, __u16 wIndex,
- __u16 wLength)
+static void usb_decode_ctrl_generic(char *str, size_t size, __u8 bRequestType,
+ __u8 bRequest, __u16 wValue, __u16 wIndex,
+ __u16 wLength)
+{
+ u8 recip = bRequestType & USB_RECIP_MASK;
+ u8 type = bRequestType & USB_TYPE_MASK;
+
+ snprintf(str, size,
+ "Type=%s Recipient=%s Dir=%s bRequest=%u wValue=%u wIndex=%u wLength=%u",
+ (type == USB_TYPE_STANDARD) ? "Standard" :
+ (type == USB_TYPE_VENDOR) ? "Vendor" :
+ (type == USB_TYPE_CLASS) ? "Class" : "Unknown",
+ (recip == USB_RECIP_DEVICE) ? "Device" :
+ (recip == USB_RECIP_INTERFACE) ? "Interface" :
+ (recip == USB_RECIP_ENDPOINT) ? "Endpoint" : "Unknown",
+ (bRequestType & USB_DIR_IN) ? "IN" : "OUT",
+ bRequest, wValue, wIndex, wLength);
+}
+
+static void usb_decode_ctrl_standard(char *str, size_t size, __u8 bRequestType,
+ __u8 bRequest, __u16 wValue, __u16 wIndex,
+ __u16 wLength)
{
switch (bRequest) {
case USB_REQ_GET_STATUS:
@@ -253,14 +270,48 @@ const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
usb_decode_set_isoch_delay(wValue, str, size);
break;
default:
- snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
- bRequestType, bRequest,
- (u8)(cpu_to_le16(wValue) & 0xff),
- (u8)(cpu_to_le16(wValue) >> 8),
- (u8)(cpu_to_le16(wIndex) & 0xff),
- (u8)(cpu_to_le16(wIndex) >> 8),
- (u8)(cpu_to_le16(wLength) & 0xff),
- (u8)(cpu_to_le16(wLength) >> 8));
+ usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
+ wValue, wIndex, wLength);
+ break;
+ }
+}
+
+/**
+ * usb_decode_ctrl - Returns human readable representation of control request.
+ * @str: buffer to return a human-readable representation of control request.
+ * This buffer should have about 200 bytes.
+ * @size: size of str buffer.
+ * @bRequestType: matches the USB bmRequestType field
+ * @bRequest: matches the USB bRequest field
+ * @wValue: matches the USB wValue field (CPU byte order)
+ * @wIndex: matches the USB wIndex field (CPU byte order)
+ * @wLength: matches the USB wLength field (CPU byte order)
+ *
+ * Function returns decoded, formatted and human-readable description of
+ * control request packet.
+ *
+ * The usage scenario for this is for tracepoints, so function as a return
+ * use the same value as in parameters. This approach allows to use this
+ * function in TP_printk
+ *
+ * Important: wValue, wIndex, wLength parameters before invoking this function
+ * should be processed by le16_to_cpu macro.
+ */
+const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
+ __u8 bRequest, __u16 wValue, __u16 wIndex,
+ __u16 wLength)
+{
+ switch (bRequestType & USB_TYPE_MASK) {
+ case USB_TYPE_STANDARD:
+ usb_decode_ctrl_standard(str, size, bRequestType, bRequest,
+ wValue, wIndex, wLength);
+ break;
+ case USB_TYPE_VENDOR:
+ case USB_TYPE_CLASS:
+ default:
+ usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
+ wValue, wIndex, wLength);
+ break;
}
return str;
diff --git a/drivers/usb/common/led.c b/drivers/usb/common/led.c
index 0865dd44a80a..1de18d90b134 100644
--- a/drivers/usb/common/led.c
+++ b/drivers/usb/common/led.c
@@ -14,8 +14,6 @@
#define BLINK_DELAY 30
-static unsigned long usb_blink_delay = BLINK_DELAY;
-
DEFINE_LED_TRIGGER(ledtrig_usb_gadget);
DEFINE_LED_TRIGGER(ledtrig_usb_host);
@@ -32,7 +30,7 @@ void usb_led_activity(enum usb_led_event ev)
break;
}
/* led_trigger_blink_oneshot() handles trig == NULL gracefully */
- led_trigger_blink_oneshot(trig, &usb_blink_delay, &usb_blink_delay, 0);
+ led_trigger_blink_oneshot(trig, BLINK_DELAY, BLINK_DELAY, 0);
}
EXPORT_SYMBOL_GPL(usb_led_activity);
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 9a2ab6751a23..4a2ee447b213 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
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk/clk-conf.h>
@@ -33,14 +34,17 @@ EXPORT_SYMBOL_GPL(ulpi_write);
/* -------------------------------------------------------------------------- */
-static int ulpi_match(struct device *dev, struct device_driver *driver)
+static int ulpi_match(struct device *dev, const struct device_driver *driver)
{
struct ulpi_driver *drv = to_ulpi_driver(driver);
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++)
@@ -51,9 +55,9 @@ static int ulpi_match(struct device *dev, struct device_driver *driver)
return 0;
}
-static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int ulpi_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct ulpi *ulpi = to_ulpi_dev(dev);
+ const struct ulpi *ulpi = to_ulpi_dev(dev);
int ret;
ret = of_device_uevent_modalias(dev, env);
@@ -78,17 +82,15 @@ static int ulpi_probe(struct device *dev)
return drv->probe(to_ulpi_dev(dev));
}
-static int ulpi_remove(struct device *dev)
+static void ulpi_remove(struct device *dev)
{
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
if (drv->remove)
drv->remove(to_ulpi_dev(dev));
-
- return 0;
}
-static struct bus_type ulpi_bus = {
+static const struct bus_type ulpi_bus = {
.name = "ulpi",
.match = ulpi_match,
.uevent = ulpi_uevent,
@@ -118,7 +120,7 @@ static struct attribute *ulpi_dev_attrs[] = {
NULL
};
-static struct attribute_group ulpi_dev_attr_group = {
+static const struct attribute_group ulpi_dev_attr_group = {
.attrs = ulpi_dev_attrs,
};
@@ -129,6 +131,7 @@ static const struct attribute_group *ulpi_dev_attr_groups[] = {
static void ulpi_dev_release(struct device *dev)
{
+ of_node_put(dev->of_node);
kfree(to_ulpi_dev(dev));
}
@@ -141,8 +144,9 @@ static const struct device_type ulpi_dev_type = {
/* -------------------------------------------------------------------------- */
/**
- * ulpi_register_driver - register a driver with the ULPI bus
+ * __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.
*/
@@ -225,13 +229,54 @@ static int ulpi_read_id(struct ulpi *ulpi)
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
return 0;
err:
- of_device_request_module(&ulpi->dev);
+ of_request_module(ulpi->dev.of_node);
+ return 0;
+}
+
+static int ulpi_regs_show(struct seq_file *seq, void *data)
+{
+ struct ulpi *ulpi = seq->private;
+
+#define ulpi_print(name, reg) do { \
+ int ret = ulpi_read(ulpi, reg); \
+ if (ret < 0) \
+ return ret; \
+ seq_printf(seq, name " %.02x\n", ret); \
+} while (0)
+
+ ulpi_print("Vendor ID Low ", ULPI_VENDOR_ID_LOW);
+ ulpi_print("Vendor ID High ", ULPI_VENDOR_ID_HIGH);
+ ulpi_print("Product ID Low ", ULPI_PRODUCT_ID_LOW);
+ ulpi_print("Product ID High ", ULPI_PRODUCT_ID_HIGH);
+ ulpi_print("Function Control ", ULPI_FUNC_CTRL);
+ ulpi_print("Interface Control ", ULPI_IFC_CTRL);
+ ulpi_print("OTG Control ", ULPI_OTG_CTRL);
+ ulpi_print("USB Interrupt Enable Rising ", ULPI_USB_INT_EN_RISE);
+ ulpi_print("USB Interrupt Enable Falling", ULPI_USB_INT_EN_FALL);
+ ulpi_print("USB Interrupt Status ", ULPI_USB_INT_STS);
+ ulpi_print("USB Interrupt Latch ", ULPI_USB_INT_LATCH);
+ ulpi_print("Debug ", ULPI_DEBUG);
+ ulpi_print("Scratch Register ", ULPI_SCRATCH);
+ ulpi_print("Carkit Control ", ULPI_CARKIT_CTRL);
+ ulpi_print("Carkit Interrupt Delay ", ULPI_CARKIT_INT_DELAY);
+ ulpi_print("Carkit Interrupt Enable ", ULPI_CARKIT_INT_EN);
+ ulpi_print("Carkit Interrupt Status ", ULPI_CARKIT_INT_STS);
+ ulpi_print("Carkit Interrupt Latch ", ULPI_CARKIT_INT_LATCH);
+ ulpi_print("Carkit Pulse Control ", ULPI_CARKIT_PLS_CTRL);
+ ulpi_print("Transmit Positive Width ", ULPI_TX_POS_WIDTH);
+ ulpi_print("Transmit Negative Width ", ULPI_TX_NEG_WIDTH);
+ ulpi_print("Receive Polarity Recovery ", ULPI_POLARITY_RECOVERY);
+
return 0;
}
+DEFINE_SHOW_ATTRIBUTE(ulpi_regs);
+
+static struct dentry *ulpi_root;
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
{
int ret;
+ struct dentry *root;
ulpi->dev.parent = dev; /* needed early for ops */
ulpi->dev.bus = &ulpi_bus;
@@ -245,12 +290,19 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
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;
+ }
+
+ root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root);
+ debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
ulpi->id.vendor, ulpi->id.product);
@@ -290,14 +342,14 @@ EXPORT_SYMBOL_GPL(ulpi_register_interface);
/**
* 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);
+ debugfs_lookup_and_remove(dev_name(&ulpi->dev), ulpi_root);
device_unregister(&ulpi->dev);
}
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
@@ -306,13 +358,20 @@ EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
static int __init ulpi_init(void)
{
- return bus_register(&ulpi_bus);
+ int ret;
+
+ ulpi_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ ret = bus_register(&ulpi_bus);
+ if (ret)
+ debugfs_remove(ulpi_root);
+ return ret;
}
subsys_initcall(ulpi_init);
static void __exit ulpi_exit(void)
{
bus_unregister(&ulpi_bus);
+ debugfs_remove(ulpi_root);
}
module_exit(ulpi_exit);
diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c
index ed204cbb63ea..421c3af38e06 100644
--- a/drivers/usb/common/usb-conn-gpio.c
+++ b/drivers/usb/common/usb-conn-gpio.c
@@ -17,8 +17,13 @@
#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/string_choices.h>
#include <linux/usb/role.h>
+#include <linux/idr.h>
+
+static DEFINE_IDA(usb_conn_ida);
#define USB_GPIO_DEB_MS 20 /* ms */
#define USB_GPIO_DEB_US ((USB_GPIO_DEB_MS) * 1000) /* us */
@@ -28,6 +33,7 @@
struct usb_conn_info {
struct device *dev;
+ int conn_id; /* store the IDA-allocated ID */
struct usb_role_switch *role_sw;
enum usb_role last_role;
struct regulator *vbus;
@@ -38,9 +44,13 @@ struct usb_conn_info {
struct gpio_desc *vbus_gpiod;
int id_irq;
int vbus_irq;
+
+ struct power_supply_desc desc;
+ struct power_supply *charger;
+ bool initial_detection;
};
-/**
+/*
* "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
@@ -79,22 +89,24 @@ static void usb_conn_detect_cable(struct work_struct *work)
else
role = USB_ROLE_NONE;
- dev_dbg(info->dev, "role %d/%d, gpios: id %d, vbus %d\n",
- info->last_role, role, id, vbus);
+ dev_dbg(info->dev, "role %s -> %s, gpios: id %d, vbus %d\n",
+ usb_role_string(info->last_role), usb_role_string(role), id, vbus);
- if (info->last_role == role) {
- dev_warn(info->dev, "repeated role: %d\n", role);
+ if (!info->initial_detection && info->last_role == role) {
+ dev_warn(info->dev, "repeated role: %s\n", usb_role_string(role));
return;
}
- if (info->last_role == USB_ROLE_HOST)
+ info->initial_detection = false;
+
+ 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 +114,11 @@ static void usb_conn_detect_cable(struct work_struct *work)
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",
+ str_enabled_disabled(regulator_is_enabled(info->vbus)));
+
+ power_supply_changed(info->charger);
}
static void usb_conn_queue_dwork(struct usb_conn_info *info,
@@ -121,6 +136,61 @@ static irqreturn_t usb_conn_isr(int irq, void *dev_id)
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 = {
+ .fwnode = dev_fwnode(dev),
+ };
+
+ info->conn_id = ida_alloc(&usb_conn_ida, GFP_KERNEL);
+ if (info->conn_id < 0)
+ return info->conn_id;
+
+ desc->name = devm_kasprintf(dev, GFP_KERNEL, "usb-charger-%d",
+ info->conn_id);
+ if (!desc->name) {
+ ida_free(&usb_conn_ida, info->conn_id);
+ return -ENOMEM;
+ }
+
+ 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 %d\n", info->conn_id);
+ ida_free(&usb_conn_ida, info->conn_id);
+ }
+
+ return PTR_ERR_OR_ZERO(info->charger);
+}
+
static int usb_conn_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -154,20 +224,21 @@ static int usb_conn_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&info->dw_det, usb_conn_detect_cable);
- 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");
- return PTR_ERR(info->vbus);
- }
+ info->vbus = devm_regulator_get_optional(dev, "vbus");
+ if (PTR_ERR(info->vbus) == -ENODEV)
+ info->vbus = NULL;
+
+ if (IS_ERR(info->vbus))
+ return dev_err_probe(dev, PTR_ERR(info->vbus), "failed to get vbus\n");
info->role_sw = usb_role_switch_get(dev);
- if (IS_ERR(info->role_sw)) {
- if (PTR_ERR(info->role_sw) != -EPROBE_DEFER)
- dev_err(dev, "failed to get role switch\n");
+ if (IS_ERR(info->role_sw))
+ return dev_err_probe(dev, PTR_ERR(info->role_sw),
+ "failed to get role switch\n");
- 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);
@@ -204,8 +275,10 @@ static int usb_conn_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, info);
+ device_set_wakeup_capable(&pdev->dev, true);
/* Perform initial detection */
+ info->initial_detection = true;
usb_conn_queue_dwork(info, 0);
return 0;
@@ -215,24 +288,33 @@ put_role_sw:
return ret;
}
-static int usb_conn_remove(struct platform_device *pdev)
+static void usb_conn_remove(struct platform_device *pdev)
{
struct usb_conn_info *info = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&info->dw_det);
- if (info->last_role == USB_ROLE_HOST)
+ if (info->charger)
+ ida_free(&usb_conn_ida, info->conn_id);
+
+ if (info->last_role == USB_ROLE_HOST && info->vbus)
regulator_disable(info->vbus);
usb_role_switch_put(info->role_sw);
-
- return 0;
}
static int __maybe_unused usb_conn_suspend(struct device *dev)
{
struct usb_conn_info *info = dev_get_drvdata(dev);
+ if (device_may_wakeup(dev)) {
+ if (info->id_gpiod)
+ enable_irq_wake(info->id_irq);
+ if (info->vbus_gpiod)
+ enable_irq_wake(info->vbus_irq);
+ return 0;
+ }
+
if (info->id_gpiod)
disable_irq(info->id_irq);
if (info->vbus_gpiod)
@@ -247,6 +329,14 @@ static int __maybe_unused usb_conn_resume(struct device *dev)
{
struct usb_conn_info *info = dev_get_drvdata(dev);
+ if (device_may_wakeup(dev)) {
+ if (info->id_gpiod)
+ disable_irq_wake(info->id_irq);
+ if (info->vbus_gpiod)
+ disable_irq_wake(info->vbus_irq);
+ return 0;
+ }
+
pinctrl_pm_select_default_state(dev);
if (info->id_gpiod)
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 3740cf95560e..e11803225775 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -193,7 +193,11 @@ static void otg_start_hnp_polling(struct otg_fsm *fsm)
if (!fsm->host_req_flag)
return;
- INIT_DELAYED_WORK(&fsm->hnp_polling_work, otg_hnp_polling_work);
+ if (!fsm->hnp_work_inited) {
+ INIT_DELAYED_WORK(&fsm->hnp_polling_work, otg_hnp_polling_work);
+ fsm->hnp_work_inited = true;
+ }
+
schedule_delayed_work(&fsm->hnp_polling_work,
msecs_to_jiffies(T_HOST_REQ_POLL));
}
@@ -445,4 +449,5 @@ int otg_statemachine(struct otg_fsm *fsm)
return fsm->state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
+MODULE_DESCRIPTION("OTG Finite State Machine");
MODULE_LICENSE("GPL");