Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

usb: musb: poll ID pin status in dual-role mode in mpfs glue layer

Similar to other platforms using the MUSB driver, PolarFire SoC lacks
an ID pin interrupt event, preventing several OTG-critical status
change events from being exposed. We need to rely on polling to detect
USB attach events for the dual-role port.

The otg state machine implementation is based on Texas Instruments
DA8xx/OMAP-L1x glue layer.

This has been tested on BeagleV-Fire with couple of devices in host mode
and with the Ethernet gadget driver in peripheral mode, in a wide
variety of plug orders.

Signed-off-by: Valentina Fernandez <valentina.fernandezalanis@microchip.com>
Link: https://lore.kernel.org/r/20240806131407.1542306-1-valentina.fernandezalanis@microchip.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Valentina Fernandez and committed by
Greg Kroah-Hartman
5cffefa1 8952e50e

+136 -24
+136 -24
drivers/usb/musb/mpfs.c
··· 49 49 .ram_bits = MPFS_MUSB_RAM_BITS, 50 50 }; 51 51 52 - static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci) 53 - { 54 - unsigned long flags; 55 - irqreturn_t ret = IRQ_NONE; 56 - struct musb *musb = __hci; 57 - 58 - spin_lock_irqsave(&musb->lock, flags); 59 - 60 - musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 61 - musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 62 - musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 63 - 64 - if (musb->int_usb || musb->int_tx || musb->int_rx) { 65 - musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 66 - musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 67 - musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 68 - ret = musb_interrupt(musb); 69 - } 70 - 71 - spin_unlock_irqrestore(&musb->lock, flags); 72 - 73 - return ret; 74 - } 75 - 76 52 static void mpfs_musb_set_vbus(struct musb *musb, int is_on) 77 53 { 78 54 u8 devctl; ··· 87 111 musb_readb(musb->mregs, MUSB_DEVCTL)); 88 112 } 89 113 114 + #define POLL_SECONDS 2 115 + 116 + static void otg_timer(struct timer_list *t) 117 + { 118 + struct musb *musb = from_timer(musb, t, dev_timer); 119 + void __iomem *mregs = musb->mregs; 120 + u8 devctl; 121 + unsigned long flags; 122 + 123 + /* 124 + * We poll because PolarFire SoC won't expose several OTG-critical 125 + * status change events (from the transceiver) otherwise. 126 + */ 127 + devctl = musb_readb(mregs, MUSB_DEVCTL); 128 + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, 129 + usb_otg_state_string(musb->xceiv->otg->state)); 130 + 131 + spin_lock_irqsave(&musb->lock, flags); 132 + switch (musb->xceiv->otg->state) { 133 + case OTG_STATE_A_WAIT_BCON: 134 + devctl &= ~MUSB_DEVCTL_SESSION; 135 + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); 136 + 137 + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); 138 + if (devctl & MUSB_DEVCTL_BDEVICE) { 139 + musb->xceiv->otg->state = OTG_STATE_B_IDLE; 140 + MUSB_DEV_MODE(musb); 141 + mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); 142 + } else { 143 + musb->xceiv->otg->state = OTG_STATE_A_IDLE; 144 + MUSB_HST_MODE(musb); 145 + } 146 + break; 147 + case OTG_STATE_A_WAIT_VFALL: 148 + if (devctl & MUSB_DEVCTL_VBUS) { 149 + mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); 150 + break; 151 + } 152 + musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; 153 + break; 154 + case OTG_STATE_B_IDLE: 155 + /* 156 + * There's no ID-changed IRQ, so we have no good way to tell 157 + * when to switch to the A-Default state machine (by setting 158 + * the DEVCTL.Session bit). 159 + * 160 + * Workaround: whenever we're in B_IDLE, try setting the 161 + * session flag every few seconds. If it works, ID was 162 + * grounded and we're now in the A-Default state machine. 163 + * 164 + * NOTE: setting the session flag is _supposed_ to trigger 165 + * SRP but clearly it doesn't. 166 + */ 167 + musb_writeb(mregs, MUSB_DEVCTL, devctl | MUSB_DEVCTL_SESSION); 168 + devctl = musb_readb(mregs, MUSB_DEVCTL); 169 + if (devctl & MUSB_DEVCTL_BDEVICE) 170 + mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); 171 + else 172 + musb->xceiv->otg->state = OTG_STATE_A_IDLE; 173 + break; 174 + default: 175 + break; 176 + } 177 + spin_unlock_irqrestore(&musb->lock, flags); 178 + } 179 + 180 + static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long timeout) 181 + { 182 + static unsigned long last_timer; 183 + 184 + if (timeout == 0) 185 + timeout = jiffies + msecs_to_jiffies(3); 186 + 187 + /* Never idle if active, or when VBUS timeout is not set as host */ 188 + if (musb->is_active || (musb->a_wait_bcon == 0 && 189 + musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) { 190 + dev_dbg(musb->controller, "%s active, deleting timer\n", 191 + usb_otg_state_string(musb->xceiv->otg->state)); 192 + del_timer(&musb->dev_timer); 193 + last_timer = jiffies; 194 + return; 195 + } 196 + 197 + if (time_after(last_timer, timeout) && timer_pending(&musb->dev_timer)) { 198 + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); 199 + return; 200 + } 201 + last_timer = timeout; 202 + 203 + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", 204 + usb_otg_state_string(musb->xceiv->otg->state), 205 + jiffies_to_msecs(timeout - jiffies)); 206 + mod_timer(&musb->dev_timer, timeout); 207 + } 208 + 209 + static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci) 210 + { 211 + unsigned long flags; 212 + irqreturn_t ret = IRQ_NONE; 213 + struct musb *musb = __hci; 214 + 215 + spin_lock_irqsave(&musb->lock, flags); 216 + 217 + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); 218 + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); 219 + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); 220 + 221 + if (musb->int_usb || musb->int_tx || musb->int_rx) { 222 + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); 223 + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); 224 + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); 225 + ret = musb_interrupt(musb); 226 + } 227 + 228 + /* Poll for ID change */ 229 + if (musb->xceiv->otg->state == OTG_STATE_B_IDLE) 230 + mod_timer(&musb->dev_timer, jiffies + POLL_SECONDS * HZ); 231 + 232 + spin_unlock_irqrestore(&musb->lock, flags); 233 + 234 + return ret; 235 + } 236 + 90 237 static int mpfs_musb_init(struct musb *musb) 91 238 { 92 239 struct device *dev = musb->controller; ··· 220 121 return PTR_ERR(musb->xceiv); 221 122 } 222 123 124 + timer_setup(&musb->dev_timer, otg_timer, 0); 125 + 223 126 musb->dyn_fifo = true; 224 127 musb->isr = mpfs_musb_interrupt; 225 128 ··· 230 129 return 0; 231 130 } 232 131 132 + static int mpfs_musb_exit(struct musb *musb) 133 + { 134 + del_timer_sync(&musb->dev_timer); 135 + 136 + return 0; 137 + } 138 + 233 139 static const struct musb_platform_ops mpfs_ops = { 234 140 .quirks = MUSB_DMA_INVENTRA, 235 141 .init = mpfs_musb_init, 142 + .exit = mpfs_musb_exit, 236 143 .fifo_mode = 2, 237 144 #ifdef CONFIG_USB_INVENTRA_DMA 238 145 .dma_init = musbhs_dma_controller_create, 239 146 .dma_exit = musbhs_dma_controller_destroy, 147 + #endif 148 + #ifndef CONFIG_USB_MUSB_HOST 149 + .try_idle = mpfs_musb_try_idle, 240 150 #endif 241 151 .set_vbus = mpfs_musb_set_vbus 242 152 };