Skip to content

Commit

Permalink
usb/chipidea: Poll for overcurrent condition
Browse files Browse the repository at this point in the history
commit 230e239 from master-stream810

Signed-off-by: Fionn Cleary <[email protected]>
[adjust patch for imx8]
Signed-off-by: Peter Suti <[email protected]>
  • Loading branch information
Fionn Cleary authored and dasty committed Jun 21, 2022
1 parent 43c90db commit 8fb687e
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
2 changes: 2 additions & 0 deletions drivers/usb/chipidea/ci.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ struct ci_hdrc {
bool supports_runtime_pm;
bool in_lpm;
bool wakeup_int;
bool vbus_overcurrent;
enum ci_revision rev;
struct work_struct power_lost_work;
struct workqueue_struct *power_lost_wq;
Expand All @@ -278,6 +279,7 @@ struct ci_hdrc {
u32 pm_portsc;
u32 pm_usbmode;
struct mutex mutex;
struct delayed_work check_vbus_work;
};

static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
Expand Down
65 changes: 65 additions & 0 deletions drivers/usb/chipidea/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,62 @@ static void ci_power_lost_work(struct work_struct *work)
enable_irq(ci->irq);
}

static void imx8_check_vbus_overcurrent(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct ci_hdrc *ci =
container_of(dwork, struct ci_hdrc, check_vbus_work);
unsigned long flags = 0;
u32 reg;
int check_interval = 1000; /* Normal check interval in ms. */
const int overcurrent_disabled_time = 5000;
const int overcurrent_reenabled_time = 200;

spin_lock_irqsave(&ci->lock, flags);
/*
* According to the reference manual for the SOC, setting or
* clearing the PP bit in PORTSC should enable or disable
* power to the port. However, when tested it didn't actually
* disable the power to the port, so the regulator disable was
* added to really disable the power.
*/
if (ci->vbus_overcurrent) {
int ret;
dev_info(ci->dev, "reenabling port power after an overcurrent condition\n");
/* Reenabling the port like this doesn't work, any
* device plugged in will not been seen by the driver,
* some other reinitialisation is required, so leave
* this register alone for the moment, it doesn't work
* as advertised anyway. */
/* hw_write(ci, OP_PORTSC, PORTSC_PP, PORTSC_PP); */
ret = regulator_enable(ci->platdata->reg_vbus);
if (ret) {
/* Retry if enabling the regulator failed. */
dev_err(ci->dev, "reenabling vbus regulator failed!\n");
} else {
ci->vbus_overcurrent = false;
}
check_interval = overcurrent_reenabled_time;
} else {
/* check if vbus is valid */
reg = hw_read_id_reg(ci, 0x23c, 0xff);
if (!(reg & BIT(3))) {
dev_err(ci->dev, "vbus overcurrent detected, disabling port power!\n");
/* See comment in reenable code. */
/* hw_write(ci, OP_PORTSC, PORTSC_PP, 0); */
regulator_disable(ci->platdata->reg_vbus);
check_interval = overcurrent_disabled_time;
ci->vbus_overcurrent = true;
}
}
spin_unlock_irqrestore(&ci->lock, flags);

queue_delayed_work(
system_wq,
&ci->check_vbus_work,
msecs_to_jiffies(check_interval));
}

static int ci_hdrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
Expand Down Expand Up @@ -1238,6 +1294,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
dbg_create_files(ci);
mutex_init(&ci->mutex);

/* Init workqueue for checking VBUS overcurrent & start it. */
ci->vbus_overcurrent = false;
INIT_DELAYED_WORK(&ci->check_vbus_work,
imx8_check_vbus_overcurrent);
queue_delayed_work(
system_wq,
&ci->check_vbus_work,
msecs_to_jiffies(1000));

return 0;

remove_debug:
Expand Down

0 comments on commit 8fb687e

Please sign in to comment.