On 12/12/18 12:22 PM, Felipe Balbi wrote: > > Hi > > Pawel Laszczak <pawell@xxxxxxxxxxx> writes: >>>> + cdns->phy = devm_phy_get(dev, "cdns3,usbphy"); >>>> + if (IS_ERR(cdns->phy)) { >>>> + ret = PTR_ERR(cdns->phy); >>>> + if (ret == -ENOSYS || ret == -ENODEV) { >>> >>> Are you sure you can get ENOSYS here? Have you checked output of >>> checkpatch --strict? >>> -:852: WARNING: ENOSYS means 'invalid syscall nr' and nothing else >> >> Yes this error code can be returned by related to phy function if >> CONFIG_GENERIC_PHY is disabled. >> >> I have seen this warning in output of checkpatch --strict . > > Kishon, seems like you shouldn't be using that error value. hmm, yeah should change the return value to -ENODEV when GENERIC_PHY is disabled. Thanks Kishon > > <snip> > >>>> + case USB_REQ_SET_ISOCH_DELAY: >>>> + sprintf(str, "Set Isochronous Delay Delay: %d ns", wValue); >>>> + break; >>>> + default: >>>> + sprintf(str, >>>> + "SETUP BRT: %02x BR: %02x V: %04x I: %04x L: %04x\n", >>>> + bRequestType, bRequest, >>>> + wValue, wIndex, wLength); >>>> + } >>>> + >>>> + return str; >>>> +} >>> >>> All of these are a flat out copy of dwc3's implementation. It's much, >>> much better to turn dwc3's implementation into a generic, reusable >>> library function then spinning your own as a duplicated effort. >> I agree, >> It would be nice to have a set of decoding function in some generic library. It could be used >> also by other drivers. >> Who should do this ? > > since you're the first to reuse it, then it should be you :-) > >>>> +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, >>>> + struct usb_ctrlrequest *ctrl_req) >>>> +{ >>>> + enum usb_device_state device_state = priv_dev->gadget.state; >>>> + struct cdns3_endpoint *priv_ep; >>>> + u32 config = le16_to_cpu(ctrl_req->wValue); >>>> + int result = 0; >>>> + int i; >>>> + >>>> + switch (device_state) { >>>> + case USB_STATE_ADDRESS: >>>> + /* Configure non-control EPs */ >>>> + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { >>>> + priv_ep = priv_dev->eps[i]; >>>> + if (!priv_ep) >>>> + continue; >>>> + >>>> + if (priv_ep->flags & EP_CLAIMED) >>>> + cdns3_ep_config(priv_ep); >>>> + } >>>> + >>>> + result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); >>>> + >>>> + if (result) >>>> + return result; >>>> + >>>> + if (config) { >>>> + cdns3_set_hw_configuration(priv_dev); >>>> + } else { >>>> + cdns3_gadget_unconfig(priv_dev); >>>> + usb_gadget_set_state(&priv_dev->gadget, >>>> + USB_STATE_ADDRESS); >>> >>> this is wrong. If address is zero, state should be default, not >>> addressed. Addressed would be used on the other branch here, when you >>> have a non-zero address >> >> If address is zero then state should be USB_STATE_DEFAULT. Driver >> enters to this branch only if gadget.state = USB_STATE_ADDRESS >> (address != 0). This state can be changed in composite.c file after >> delegating request to gadget driver. Because this state could be >> changed driver simple restore USB_STATE_ADDRESS if config = 0. > > nevermind, I read this as being the handler for Set Address. This is Set > Config, instead. > >>>> + } >>>> + break; >>>> + case USB_STATE_CONFIGURED: >>> >>> where do you set this state? >> It's set in set_config in composite.c file. > > indeed > >>>> + case USB_DEVICE_TEST_MODE: >>>> + if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH) >>>> + return -EINVAL; >>>> + >>>> + tmode = le16_to_cpu(ctrl->wIndex); >>>> + >>>> + if (!set || (tmode & 0xff) != 0) >>>> + return -EINVAL; >>>> + >>>> + switch (tmode >> 8) { >>>> + case TEST_J: >>>> + case TEST_K: >>>> + case TEST_SE0_NAK: >>>> + case TEST_PACKET: >>>> + cdns3_set_register_bit(&priv_dev->regs->usb_cmd, >>>> + USB_CMD_STMODE | >>>> + USB_STS_TMODE_SEL(tmode - 1)); >>> >>> I'm 90% sure this won't work. There's a reason why we only enter the >>> requested test mode from status stage. How have you tested this? >> >> From USB spec: >> "The transition to test mode must be complete no later than 3 ms after the completion of the status stage of the >> request." > > exactly, _after_ completion of status stage :-) > >> But I don't remember any issues with this test on other ours >> drivers. Maybe status stage is armed in this case by controller. I >> have to confirm how it works with hardware team. Driver doesn't know >> when status stage was completed. We don't have any event on status >> stage completion. I haven't checked it yet with tester on this >> driver. > > Let's consider the scenario where you're requesting Test_Packet mode. If > you start transmitting the test pattern from setup stage, how can you > even transmit status stage? > >>>> +void cdns3_gadget_unconfig(struct cdns3_device *priv_dev) >>>> +{ >>>> + /* RESET CONFIGURATION */ >>>> + writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf); >>>> + >>>> + cdns3_allow_enable_l1(priv_dev, 0); >>>> + priv_dev->hw_configured_flag = 0; >>>> + priv_dev->onchip_mem_allocated_size = 0; >>> >>> clear all test modes? Reset ep0 max_packet_size to 512? >> Test mode should be reset automatically on exit. > > on exit? Which exit? Did you test this on USB Reset? Did you run USBCV > on Super and High speed with any gadget? > >> W can't clear this mode in register. It's WAC (write only and auto clear) bit. >> This function only reset endpoint configuration in controller register. >> Ep0 max_packet_size should be unchanged here. Ep0 max_packet it's updated on >> Connect/Disconnect/Reset events. > > right, and this is called for both reset and disconnect (see below). If > you're telling me that test mode and ep0 wMaxPacketSize is handled > elsewhere, that's fine. > > + /* Disconnection detected */ > + if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) { > + if (priv_dev->gadget_driver && > + priv_dev->gadget_driver->disconnect) { > + spin_unlock(&priv_dev->lock); > + priv_dev->gadget_driver->disconnect(&priv_dev->gadget); > + spin_lock(&priv_dev->lock); > + } > + > + priv_dev->gadget.speed = USB_SPEED_UNKNOWN; > + usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); > + cdns3_gadget_unconfig(priv_dev); > + } > + > + /* reset*/ > + if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) { > + /*read again to check the actuall speed*/ > + speed = cdns3_get_speed(priv_dev); > + usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT); > + priv_dev->gadget.speed = speed; > + cdns3_gadget_unconfig(priv_dev); > + cdns3_ep0_config(priv_dev); > + } > > >> Maybe this function should be called cdns3_hw_reset_eps_config. > > perhaps > >> It doesn't unconfigure whole gadget but only reset endpoints configuration kept by >> controller. >> I will change this function. > > ok > >>>> +static irqreturn_t cdns3_device_irq_handler(int irq, void *data) >>>> +{ >>>> + struct cdns3_device *priv_dev; >>>> + struct cdns3 *cdns = data; >>>> + irqreturn_t ret = IRQ_NONE; >>>> + unsigned long flags; >>>> + u32 reg; >>>> + >>>> + priv_dev = cdns->gadget_dev; >>>> + spin_lock_irqsave(&priv_dev->lock, flags); >>> >>> you're already running in hardirq context. Why do you need this lock at >>> all? I would be better to use the hardirq handler to mask your >>> interrupts, so they don't fire again, then used the top-half (softirq) >>> handler to actually handle the interrupts. >> >> Yes, spin_lock_irqsave is not necessary here. >> >> Do you mean replacing devm_request_irq with a request_threaded_irq ? >> I have single interrupt line shared between Host, Driver, DRD/OTG. >> I'm not sure if it will work more efficiently. > > The whole idea for running very little in hardirq context is to give the > scheduler a chance to decide what should run. This is important to > reduce latency when running with RT patchset applied, for > example. However, I'll give you that, it's a minor requirement. It's > just that, to me, it's a small detail that's easy to implement. > >>>> + /* check USB device interrupt */ >>>> + reg = readl(&priv_dev->regs->usb_ists); >>>> + writel(reg, &priv_dev->regs->usb_ists); >>>> + >>>> + if (reg) { >>>> + dev_dbg(priv_dev->dev, "IRQ: usb_ists: %08X\n", reg); >>> >>> I strongly advise against using dev_dbg() for debugging. Even more so >>> inside your IRQ handler. >> Ok, It's not necessary in this place, especially, that there is invoked trace point >> inside cdns3_check_usb_interrupt_proceed which makes the same. > > overhead. Tracepoints are really, really low overhead. The string > decoding doesn't happen until you read the trace file. >