blob: 54f845ece8d85e6088b625c314f471a2cde5b253 [file] [log] [blame]
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.
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.
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 already stopped during the port suspend which happens before root hub
suspend. Assert the RSTn pin to put the bridge chip in reset state and stop XO
(19.2 MHz) clock.
bus_resume: This method is called by the core during root hub resume. Turn on
the XO clock and de-assert the RSTn signal to bring the chip out of reset.
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
<debugfs root>/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.
"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.
"dump": Dumps all the register values to the kernel log buffer.
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".
Config options
==============
Set CONFIG_USB_SL811_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 can be completely power collapsed during suspend to avoid
leakage currents. As the bridge chip does not have any non-volatile memory,
the configuration data needs to be loaded during resume. This method has higher
power savings with higher resume latencies. Evaluate this approach.
- 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.