risc-v/mpfs: usb: fix usb restart

Issuing the following commands doesn't succeed:
  - conn
  - disconn
  - conn
This USB driver doesn't even disconnect without this patch.

If the USB driver has been started from another hart, closing it
will not disable the PLIC interrupt. This means it's possible many
harts get the USB interrupt and thus make the USB look very unstable.

Fix these problems by disconnecting the USB via the USB_POWER
register at shutdown and disabling the interrupt at shutdown. Also
clear the software internals via the mpfs_sw_setup() for the conn /
disconect loop to succeed.

Signed-off-by: Eero Nurkkala <eero.nurkkala@offcode.fi>
This commit is contained in:
Eero Nurkkala 2022-09-08 14:54:21 +03:00 committed by Xiang Xiao
parent 0322a61510
commit 5cedf1ef2a

View file

@ -307,6 +307,40 @@ static void mpfs_modifyreg16(uintptr_t addr, uint16_t clearbits,
spin_unlock_irqrestore(NULL, flags);
}
/****************************************************************************
* Name: mpfs_modifyreg8
*
* Description:
* Atomically modify the specified bits in the memory mapped register.
* This also checks the addr range is valid.
*
* Input Parameters:
* addr - Address to access
* clearbits - Bits to clear
* setbits - Bits to set
*
* Returned Value:
* None
*
****************************************************************************/
static void mpfs_modifyreg8(uintptr_t addr, uint8_t clearbits,
uint8_t setbits)
{
irqstate_t flags;
uint8_t regval;
DEBUGASSERT((addr >= MPFS_USB_BASE) && addr < (MPFS_USB_BASE +
MPFS_USB_REG_MAX));
flags = spin_lock_irqsave(NULL);
regval = getreg8(addr);
regval &= ~clearbits;
regval |= setbits;
putreg8(regval, addr);
spin_unlock_irqrestore(NULL, flags);
}
/****************************************************************************
* Register Operations
****************************************************************************/
@ -3691,9 +3725,15 @@ static void mpfs_hw_shutdown(struct mpfs_usbdev_s *priv)
{
priv->usbdev.speed = USB_SPEED_UNKNOWN;
/* Force disconnect and give some time to finish it up */
mpfs_modifyreg8(MPFS_USB_POWER, POWER_REG_SOFT_CONN_MASK, 0);
nxsig_usleep(1000);
/* Disable all interrupts */
mpfs_putreg8(0, MPFS_USB_ENABLE);
up_disable_irq(MPFS_IRQ_USB_MC);
/* Disable clocking to the peripheral */
@ -3818,6 +3858,8 @@ int usbdev_register(struct usbdevclass_driver_s *driver)
DEBUGASSERT(driver != NULL);
mpfs_sw_setup(priv);
/* First hook up the driver */
priv->driver = driver;