Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18456641 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "dwc3-msm: Add USB role switch handling"

parents 4c77e0c0 a6f73bd8
Loading
Loading
Loading
Loading
+94 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <linux/extcon.h>
#include <linux/reset.h>
#include <linux/usb/dwc3-msm.h>
#include <linux/usb/role.h>

#include "core.h"
#include "gadget.h"
@@ -362,6 +363,12 @@ static const char * const gsi_op_strings[] = {
	"FREE_TRBS", "SET_CLR_BLOCK_DBL", "CHECK_FOR_SUSP",
	"EP_DISABLE" };

static const char * const usb_role_strings[] = {
	"NONE",
	"HOST",
	"DEVICE"
};

struct dwc3_msm;

struct extcon_nb {
@@ -471,6 +478,8 @@ struct dwc3_msm {

	u64			dummy_gsi_db;
	dma_addr_t		dummy_gsi_db_dma;

	struct usb_role_switch *role_switch;
};

#define USB_HSPHY_3P3_VOL_MIN		3050000 /* uV */
@@ -3551,6 +3560,79 @@ static int dwc3_msm_extcon_register(struct dwc3_msm *mdwc)
	return 0;
}

static inline const char *usb_role_string(enum usb_role role)
{
	if (role < ARRAY_SIZE(usb_role_strings))
		return usb_role_strings[role];

	return "Invalid";
}

static enum usb_role dwc3_msm_usb_get_role(struct device *dev)
{
	struct dwc3_msm *mdwc = dev_get_drvdata(dev);
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	enum usb_role role;

	if (mdwc->vbus_active)
		role = USB_ROLE_DEVICE;
	else if (mdwc->id_state == DWC3_ID_GROUND)
		role = USB_ROLE_HOST;
	else
		role = USB_ROLE_NONE;

	dbg_log_string("get_role:%s\n", usb_role_string(role));
	return role;
}

static int dwc3_msm_usb_set_role(struct device *dev, enum usb_role role)
{
	struct dwc3_msm *mdwc = dev_get_drvdata(dev);
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	enum usb_role cur_role = USB_ROLE_NONE;

	cur_role = dwc3_msm_usb_get_role(dev);

	switch (role) {
	case USB_ROLE_HOST:
		mdwc->vbus_active = false;
		mdwc->id_state = DWC3_ID_GROUND;
		break;

	case USB_ROLE_DEVICE:
		mdwc->vbus_active = true;
		mdwc->id_state = DWC3_ID_FLOAT;
		break;

	case USB_ROLE_NONE:
		mdwc->vbus_active = false;
		mdwc->id_state = DWC3_ID_FLOAT;
		break;
	}

	dbg_log_string("cur_role:%s new_role:%s\n", usb_role_string(cur_role),
						usb_role_string(role));

	/*
	 * For boot up without USB cable connected case, don't check
	 * previous role value to allow resetting USB controller and
	 * PHYs.
	 */
	if (mdwc->drd_state != DRD_STATE_UNDEFINED && cur_role == role) {
		dbg_log_string("no USB role change");
		return 0;
	}

	dwc3_ext_event_notify(mdwc);
	return 0;
}

static struct usb_role_switch_desc role_desc = {
	.set = dwc3_msm_usb_set_role,
	.get = dwc3_msm_usb_get_role,
	.allow_userspace_control = true,
};

static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
@@ -4112,7 +4194,17 @@ static int dwc3_msm_probe(struct platform_device *pdev)
		} else {
			queue_delayed_work(mdwc->sm_usb_wq, &mdwc->sm_work, 0);
		}
	} else {
	}

	if (of_property_read_bool(node, "usb-role-switch")) {
		role_desc.fwnode = dev_fwnode(&pdev->dev);
		mdwc->role_switch = usb_role_switch_register(mdwc->dev,
								&role_desc);
		if (IS_ERR(mdwc->role_switch))
			return PTR_ERR(mdwc->role_switch);
	}

	if (!mdwc->role_switch && !mdwc->extcon) {
		switch (dwc->dr_mode) {
		case USB_DR_MODE_OTG:
			if (of_property_read_bool(node,
@@ -4168,6 +4260,7 @@ static int dwc3_msm_remove(struct platform_device *pdev)
	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
	int i, ret_pm;

	usb_role_switch_unregister(mdwc->role_switch);
	device_remove_file(&pdev->dev, &dev_attr_mode);
	device_remove_file(&pdev->dev, &dev_attr_speed);
	device_remove_file(&pdev->dev, &dev_attr_bus_vote);
+32 −0
Original line number Diff line number Diff line
@@ -473,15 +473,19 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
static void ucsi_partner_change(struct ucsi_connector *con)
{
	int ret;
	struct ucsi *ucsi = con->ucsi;
	enum usb_role u_role = USB_ROLE_NONE;

	if (!con->partner)
		return;

	switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
	case UCSI_CONSTAT_PARTNER_TYPE_UFP:
		u_role = USB_ROLE_HOST;
		typec_set_data_role(con->port, TYPEC_HOST);
		break;
	case UCSI_CONSTAT_PARTNER_TYPE_DFP:
		u_role = USB_ROLE_DEVICE;
		typec_set_data_role(con->port, TYPEC_DEVICE);
		break;
	default:
@@ -492,6 +496,11 @@ static void ucsi_partner_change(struct ucsi_connector *con)
	if (!completion_done(&con->complete))
		complete(&con->complete);

	ret = usb_role_switch_set_role(ucsi->usb_role_sw, u_role);
	if (ret)
		dev_err(ucsi->dev, "%s(): failed to set role(%d):%d\n",
						__func__, u_role, ret);

	/* Can't rely on Partner Flags field. Always checking the alt modes. */
	ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
	if (ret)
@@ -510,6 +519,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
	enum typec_role role;
	u64 command;
	int ret;
	enum usb_role u_role = USB_ROLE_NONE;

	mutex_lock(&con->lock);

@@ -540,9 +550,11 @@ static void ucsi_handle_connector_change(struct work_struct *work)

		switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
		case UCSI_CONSTAT_PARTNER_TYPE_UFP:
			u_role = USB_ROLE_HOST;
			typec_set_data_role(con->port, TYPEC_HOST);
			break;
		case UCSI_CONSTAT_PARTNER_TYPE_DFP:
			u_role = USB_ROLE_DEVICE;
			typec_set_data_role(con->port, TYPEC_DEVICE);
			break;
		default:
@@ -553,6 +565,11 @@ static void ucsi_handle_connector_change(struct work_struct *work)
			ucsi_register_partner(con);
		else
			ucsi_unregister_partner(con);

		ret = usb_role_switch_set_role(ucsi->usb_role_sw, u_role);
		if (ret)
			dev_err(ucsi->dev, "%s(): failed to set role(%d):%d\n",
							__func__, u_role, ret);
	}

	if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) {
@@ -776,6 +793,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
	enum typec_accessory *accessory = cap->accessory;
	u64 command;
	int ret;
	enum usb_role role = USB_ROLE_NONE;

	INIT_WORK(&con->work, ucsi_handle_connector_change);
	init_completion(&con->complete);
@@ -840,9 +858,11 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)

	switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
	case UCSI_CONSTAT_PARTNER_TYPE_UFP:
		role = USB_ROLE_HOST;
		typec_set_data_role(con->port, TYPEC_HOST);
		break;
	case UCSI_CONSTAT_PARTNER_TYPE_DFP:
		role = USB_ROLE_DEVICE;
		typec_set_data_role(con->port, TYPEC_DEVICE);
		break;
	default:
@@ -857,6 +877,11 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
		ucsi_register_partner(con);
	}

	ret = usb_role_switch_set_role(ucsi->usb_role_sw, role);
	if (ret)
		dev_err(ucsi->dev, "%s(): failed to set role(%d):%d\n",
						__func__, role, ret);

	if (con->partner) {
		ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
		if (ret)
@@ -920,6 +945,13 @@ int ucsi_init(struct ucsi *ucsi)
		goto err_reset;
	}

	ucsi->usb_role_sw = fwnode_usb_role_switch_get(dev_fwnode(ucsi->dev));
	if (IS_ERR_OR_NULL(ucsi->usb_role_sw)) {
		dev_err(ucsi->dev, "%s(): Unable to find usb role switch\n",
								__func__);
		ucsi->usb_role_sw = NULL;
	}

	/* Register all connectors */
	for (i = 0; i < ucsi->cap.num_connectors; i++) {
		ret = ucsi_register_port(ucsi, i);
+3 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#include <linux/device.h>
#include <linux/types.h>
#include <linux/usb/typec.h>
#include <linux/usb/role.h>

/* -------------------------------------------------------------------------- */

@@ -277,6 +278,8 @@ struct ucsi {
#define EVENT_PENDING	0
#define COMMAND_PENDING	1
#define ACK_PENDING	2

	struct usb_role_switch *usb_role_sw;
};

#define UCSI_MAX_SVID		5