Introduction ============ USB UICC connectivity is required for MSM8x12. This SoC has only 1 USB controller which is used for peripheral mode and charging. Hence an external USB host controller over SPI is used to connect a USB UICC card. ICE40 FPGA based SPI to IC-USB (Inter-Chip USB) bridge chip is used. The ICE40 Host controller driver (ice40-hcd) is registered as a SPI protocol driver and interacts with the SPI subsystem on one side and interacts with the USB core on the other side. Hardware description ==================== The ICE40 devices are SRAM-based FPGAs. The SRAM memory cells are volatile, meaning that once power is removed from the device, its configuration is lost and must be reloaded on the next power-up. An on-chip non-volatile configuration memory or an external SPI flash are not used to store the configuration data due to increased power consumption. Instead, the software loads the configuration data through SPI interface after powering up the bridge chip. Once the configuration data is programmed successfully, the bridge chip will be ready for the USB host controller operations. The ICE40 device has an interrupt signal apart from the standard SPI signals CSn, SCLK, MOSI and MISO. It has support for 25 to 50 MHz frequencies. The maximum operating frequency during configuration loading is 25 MHz. The bridge chip requires two power supplies, SPI_VCC (1.8v - 3.3v) and VCC_CORE (1.2v). The SPI_VCC manages the SPI slave portion and VCC_CORE manages the USB serial engine (SIE) portion. It requires a 19.2 MHz reference clock and a 32 MHz clock is required for remote wakeup detection during suspend. The configuration loading sequence: - Assert the RSTn pin. This keeps bridge chip in reset state after downloading the configuration data. - The bridge chip samples the SPI interface chip select pin during power-up and enters SPI slave mode if it is low. Drive the chip select pin low before powering up the bridge chip. - Power-up the bridge chip by enabling SPI_VCC and VCC_CORE - De-assert the chip select pin after 50 usec. - Transfer the configuration data over SPI. Note that the bridge chip requires 49 dummy clock cycles after sending the data. - The bridge chip indicates the status of the configuration loading via config done pin. It may take 50 usec to assert this pin. The 19.2 MHz clock should be supplied before de-asserting the RSTn pin. A PLL is used to generate a 48MHz clock signal that then creates a 12MHz clock signal by a divider. When the PLLOK bit is set in USB Transfer Result register, it indicates that the PLL output is locked to the input reference clock. When it is 0, it indicates that the PLL is out of lock. It is recommended to assert the RSTn pin to re-synchronize the PLL to the reference clock when the PLL loses lock. The chip will be ready for the USB host controller operations after it is brought out of reset and PLL is synchronized to the reference clock. The software is responsible for initiating all the USB host transfers by writing the associated registers. The SIE in the bridge chip performs the USB host operations via the IC-USB bus based on the registers set by the software. The USB transfer results as well as the bus status like the peripheral connection, disconnection, resume, etc. are notified to software through the interrupt and the internal registers. The bridge chip provides the DP & DM pull-down resistor control to the software. The pull-down resistors are enabled automatically after the power up to force the SE0 condition on the bus. The software is required to disable these resistors before driving the reset on the bus. Control, Bulk and Interrupt transfers are supported. The data toggling states are not maintained in the hardware and should be serviced by the software. The bridge chip returns one of the following values for a USB transaction (SETUP/IN/OUT) via Transfer result register. xSUCCESS: Successful transfer. xBUSY: The SIE is busy with a USB transfer. xPKTERR: Packet Error (stuff, EOP). xPIDERR: PID check bits are incorrect. xNAK: Device returned NAK. This is not an error condition for IN/OUT. But it is an error condition for SETUP. xSTALL: Device returned STALL. xWRONGPID: Wrong PID is received. For example a IN transaction is attempted on OUT endpoint. xCRCERR: CRC error. xTOGERR: Toggle bit error. The SIE returns ACK when the toggle mismatch happens for IN transaction and returns this error code. Software should discard the data as it was received already in the previous transaction. xBADLEN: Too big packet size received. xTIMEOUT: Device failed to respond in time. Software description ==================== This driver is compiled as a module and is loaded by the userspace after getting the UICC card insertion event from the modem processor. The module is unloaded upon the UICC card removal. This driver registers as a SPI protocol driver. The SPI controller driver manages the chip select pin. This pin needs to be driven low before powering up the bridge chip. Hence this pin settings are overridden temporarily during the bridge chip power-up sequence. The original settings are restored before sending the configuration data to the bridge chip which acts as a SPI slave. Both pinctl and gpiomux framework allow this type of use case. The configuration data file is stored on the eMMC card. Firmware class API request_firmware() is used to read the configuration data file. The configuration data is then sent to the bridge chip via SPI interface. The bridge chip asserts the config done pin once the configuration is completed. This configuration data file is cached in kernel memory to improve the resume latency. The driver registers as a Full Speed (USB 1.1) HCD. The following methods are implemented that are part of hc_drive struct: reset: It is called one time by the core during HCD registration. The default address 0 is programmed and the line state is sampled to check if any device is connected. If any device is connected, the port flags are updated accordingly. As the module is loaded after the UICC card is inserted, the device would be present at this time. If the card is not inserted, this method returns an error code to fail the USB HCD registration. start: This method is called one time by the core during HCD registration. The bridge chip is programmed to transmit the SOFs. stop: The method is called one time by the core during HCD deregistration. The bridge chip is programmed to stop transmitting the SOFs. hub_control: This method is called by the core to manage the Root HUB. The hardware does not maintain port state. The software maintain the port state and provide the information to the core when required. The following HUB class requests are supported. - GetHubDescriptor: The HUB descriptor is sent to the core. Only 1 port is present. Over current protection and port power control are not supported. - SetPortFeature: The device reset and suspend are supported. The The DP & DM pull-down resistors are disabled before driving the reset as per the IC-USB spec. The reset signaling is stopped when the core queries the port status. - GetPortStatus: The device connection status is sent to the core. If a reset is in progress, it is stopped before returning the port status. - ClearPortFeature: The device resume (clear suspend) is supported. urb_enqueue: This method is called by the core to initiate a USB Control/Bulk transfer. If the endpoint private context is not present, it will be created to hold the endpoint number, host endpoint structure, transaction error count, halt state and unlink state. The URB is attached to the endpoint URB list. If the endpoint is not active, it is attached to the asynchronous schedule list and the work is scheduled to traverse this list. The traversal algorithm is explained later in this document. urb_dequeue: This method is called by the core when an URB is unlinked. If the endpoint is not active, the URB is unlinked immediately. Otherwise the endpoint is marked for unlink and URB is unlinked from the asynchronous schedule work. bus_suspend: This method is called by the core during root hub suspend. The SOFs are stopped to put the card in suspend state. The card takes ~3 msec to enter suspend. Turn off the SPI_VCC and VCC_CORE power supplies to avoid leakage current. The card state will not be lost as it has a different power supply. bus_resume: This method is called by the core during root hub resume. The configuration data must be loaded again as the bridge is power collapsed during suspend. Drive the resume signaling after loading the configuration data. endpoint_disable: This method is called by the core during the device disconnect. All the URB are unlinked by this time, so free the endpoint private structure. Asynchronous scheduling: All the active endpoints are queued to the asynchronous schedule list. A worker thread iterates over this circular list and process the URBs. Processing an URB involves initiating multiple SETUP/IN/OUT transactions and checking the result. After receiving the DATA/ACK, the toggle bit is inverted. A URB is finished when any of the following events occur: - The entire data is received for an OUT endpoint or a short packet is received for an IN endpoint. - The endpoint is stalled by the device. -EPIPE is returned. - Transaction error is occurred consecutively 3 times. -EPROTO is returned. - A NAK received for a SETUP transaction. - The URB is unlinked. The next transaction is issued on the next endpoint (if available) irrespective of the result of the current transaction. But the IN/OUT transaction of data or status phase is attempted immediately after the SETUP transaction for a control endpoint. If a NAK is received for this transaction, the control transfer is resumed next time when the control endpoint is encountered in the asynchronous schedule list. This is to give the control transfers priority over the bulk transfers. The endpoint is marked as halted when a URB is finished due to transaction errors or stall condition. The halted endpoint is removed from the asynchronous schedule list. It will be added again next time when a URB is enqueued on this endpoint. This driver provides debugfs interface and exports a file called "command" under /ice40 directory. The following strings can be echoed to this file. "poll": If the device is connected after the module is loaded, it will not be detected automatically. The bus is sampled when this string is echoed. If a device is connected, port flags are updated and core is notified about the device connect event. "force_disconnect": Echo this string to forcefully disconnect the attached device. This can be used to simulate the UICC card disconnect. Echo "poll" again to enumerate the card. "rwtest": Function Address register is written and read back to validate the contents. This should NOT be used while the usb device is connected. This is strictly for debugging purpose. Design Goals: ============= - Handle errors gracefully. Implement retry mechanism for transaction errors, memory failures. Mark HCD as dead for serious errors like SPI transaction errors to avoid further interactions with the attached USB device. - Keep the asynchronous schedule algorithm simple and efficient. Take advantage of the static configuration of the USB device. UICC cards has only CCID and Mass storage interfaces. These interface protocol allows only 1 active transfer on either in or out endpoint. - Add trace points to capture USB transactions. Driver parameters ================= The driver is compiled as a module and it accepts the configuration data file name as a module param called "firmware". The default configuration file name is "ice40.bin". The debugger module param allows the user to load the module without inserting the card. The module load will fail otherwise without a card. Config options ============== Set CONFIG_USB_ICE40_HCD to m to compile this driver as a module. The driver should not be compiled statically, because the configuration data is not available during kernel boot. To do ===== - The bridge chip has 2 IN FIFO and 2 OUT FIFO. Implement double buffering. - The bridge chip has an interrupt to indicate the transaction (IN/OUT) completion. The current implementation uses polling for simplicity and to avoid interrupt latencies. Evaluate interrupt approach. - The bridge chip is completely power collapsed during suspend to avoid leakage current. Remote wakeup can not be supported with this approach. The bridge chip needs to be powered on during suspend to detect the remote wakeup. Implement this feature if cards can support remote wakeup. - Implement Interrupt transfers if required. - The request_firmware() API copies the configuration data file to the kernel virtual memory. This memory can't be used for DMA. The current implementation copies this data into contiguous physical memory which is allocated via kmalloc. If this memory allocation fails, try to allocate multiple pages and submit the SPI message with multiple transfers.