You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
BACKPORT: UPSTREAM: usb: typec: ucsi: Wait for the USB role switches
When role switch module probe late than ucsi module,
fwnode_usb_role_switch_get() will return -EPROBE_DEFER,
it is better to restart ucsi init work to find
it again every 100ms, total wait time is 10 second.
It also means change ucsi init work to delayed_work.
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Linyu Yuan <quic_linyyuan@quicinc.com>
Link: https://lore.kernel.org/r/1650881886-25530-3-git-send-email-quic_linyyuan@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 3c16251153
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git)
Bug: 260537721
[ugoswami: Fixed KMI breakage by moving the delayed_work & work_count
to a new parent structure]
Change-Id: Ia4e2521c40b9b0ffe8b6f13ec19990e7f3a60e7a
Signed-off-by: Udipto Goswami <quic_ugoswami@quicinc.com>
This commit is contained in:
committed by
Udipto Goswami
parent
37725ca62e
commit
01b3d953ac
@@ -1061,6 +1061,15 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
||||
con->num = index + 1;
|
||||
con->ucsi = ucsi;
|
||||
|
||||
cap->fwnode = ucsi_find_fwnode(con);
|
||||
con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode);
|
||||
if (IS_ERR(con->usb_role_sw)) {
|
||||
dev_err(ucsi->dev, "con%d: failed to get usb role switch\n",
|
||||
con->num);
|
||||
return PTR_ERR(con->usb_role_sw);
|
||||
}
|
||||
|
||||
|
||||
/* Delay other interactions with the con until registration is complete */
|
||||
mutex_lock(&con->lock);
|
||||
|
||||
@@ -1096,7 +1105,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
||||
if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY)
|
||||
*accessory = TYPEC_ACCESSORY_DEBUG;
|
||||
|
||||
cap->fwnode = ucsi_find_fwnode(con);
|
||||
cap->driver_data = con;
|
||||
cap->ops = &ucsi_ops;
|
||||
|
||||
@@ -1154,13 +1162,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
|
||||
ucsi_port_psy_changed(con);
|
||||
}
|
||||
|
||||
con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode);
|
||||
if (IS_ERR(con->usb_role_sw)) {
|
||||
dev_err(ucsi->dev, "con%d: failed to get usb role switch\n",
|
||||
con->num);
|
||||
con->usb_role_sw = NULL;
|
||||
}
|
||||
|
||||
/* Only notify USB controller if partner supports USB data */
|
||||
if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB))
|
||||
u_role = USB_ROLE_NONE;
|
||||
@@ -1273,12 +1274,21 @@ err:
|
||||
|
||||
static void ucsi_init_work(struct work_struct *work)
|
||||
{
|
||||
struct ucsi *ucsi = container_of(work, struct ucsi, work);
|
||||
struct ucsi_android *aucsi = container_of(work,
|
||||
struct ucsi_android, work.work);
|
||||
int ret;
|
||||
|
||||
ret = ucsi_init(ucsi);
|
||||
ret = ucsi_init(&aucsi->ucsi);
|
||||
if (ret)
|
||||
dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);
|
||||
dev_err(aucsi->ucsi.dev, "PPM init failed (%d)\n", ret);
|
||||
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
if (aucsi->work_count++ > UCSI_ROLE_SWITCH_WAIT_COUNT)
|
||||
return;
|
||||
|
||||
queue_delayed_work(system_long_wq, &aucsi->work,
|
||||
UCSI_ROLE_SWITCH_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1310,15 +1320,17 @@ EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
|
||||
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
|
||||
{
|
||||
struct ucsi *ucsi;
|
||||
struct ucsi_android *aucsi;
|
||||
|
||||
if (!ops || !ops->read || !ops->sync_write || !ops->async_write)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
|
||||
if (!ucsi)
|
||||
aucsi = kzalloc(sizeof(*aucsi), GFP_KERNEL);
|
||||
if (!aucsi)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_WORK(&ucsi->work, ucsi_init_work);
|
||||
ucsi = &aucsi->ucsi;
|
||||
INIT_DELAYED_WORK(&aucsi->work, ucsi_init_work);
|
||||
mutex_init(&ucsi->ppm_lock);
|
||||
ucsi->dev = dev;
|
||||
ucsi->ops = ops;
|
||||
@@ -1333,7 +1345,9 @@ EXPORT_SYMBOL_GPL(ucsi_create);
|
||||
*/
|
||||
void ucsi_destroy(struct ucsi *ucsi)
|
||||
{
|
||||
kfree(ucsi);
|
||||
struct ucsi_android *aucsi = container_of(ucsi,
|
||||
struct ucsi_android, ucsi);
|
||||
kfree(aucsi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ucsi_destroy);
|
||||
|
||||
@@ -1343,6 +1357,8 @@ EXPORT_SYMBOL_GPL(ucsi_destroy);
|
||||
*/
|
||||
int ucsi_register(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_android *aucsi = container_of(ucsi,
|
||||
struct ucsi_android, ucsi);
|
||||
int ret;
|
||||
|
||||
ret = ucsi->ops->read(ucsi, UCSI_VERSION, &ucsi->version,
|
||||
@@ -1353,7 +1369,7 @@ int ucsi_register(struct ucsi *ucsi)
|
||||
if (!ucsi->version)
|
||||
return -ENODEV;
|
||||
|
||||
queue_work(system_long_wq, &ucsi->work);
|
||||
queue_delayed_work(system_long_wq, &aucsi->work, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1367,11 +1383,13 @@ EXPORT_SYMBOL_GPL(ucsi_register);
|
||||
*/
|
||||
void ucsi_unregister(struct ucsi *ucsi)
|
||||
{
|
||||
struct ucsi_android *aucsi = container_of(ucsi,
|
||||
struct ucsi_android, 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);
|
||||
cancel_delayed_work_sync(&aucsi->work);
|
||||
|
||||
/* Disable notifications */
|
||||
ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
|
||||
|
||||
@@ -289,6 +289,9 @@ struct ucsi {
|
||||
struct ucsi_connector *connector;
|
||||
|
||||
struct work_struct work;
|
||||
#define UCSI_ROLE_SWITCH_RETRY_PER_HZ 10
|
||||
#define UCSI_ROLE_SWITCH_INTERVAL (HZ / UCSI_ROLE_SWITCH_RETRY_PER_HZ)
|
||||
#define UCSI_ROLE_SWITCH_WAIT_COUNT (10 * UCSI_ROLE_SWITCH_RETRY_PER_HZ)
|
||||
|
||||
/* PPM Communication lock */
|
||||
struct mutex ppm_lock;
|
||||
@@ -304,6 +307,25 @@ struct ucsi {
|
||||
#define EVENT_PROCESSING 3
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ucsi_android - contains parameters without modifying the format
|
||||
* of ucsi struct.
|
||||
* @ucsi: contains the ucsi reference.
|
||||
* @work: work structure for queuing ucsi_init_work.
|
||||
* @work_count: to track the wait count(MAX= UCSI_ROLE_SWITCH_WAIT_COUNT).
|
||||
*
|
||||
* Required to address Bug: 260537721
|
||||
* If the role switch module probes late the
|
||||
* fwnode_usb_role_switch_get() will fail with -EPROBE_DEFER.
|
||||
* To recover from this, restart the ucsi_init_work
|
||||
* to find the fwnode again using a delayed workqueue.
|
||||
*/
|
||||
struct ucsi_android {
|
||||
struct ucsi ucsi;
|
||||
struct delayed_work work;
|
||||
int work_count;
|
||||
};
|
||||
|
||||
#define UCSI_MAX_SVID 5
|
||||
#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user