Nitesh Gupta | 35b8ea6 | 2019-10-31 10:45:54 +0530 | [diff] [blame] | 1 | Overview of Linux kernel MHI support |
| 2 | ==================================== |
| 3 | |
| 4 | Modem-Host Interface (MHI) |
| 5 | ========================= |
| 6 | MHI used by the host to control and communicate with modem over high speed |
| 7 | peripheral bus. Even though MHI can be easily adapt to any peripheral buses, |
| 8 | primarily used with PCIe based devices. The host has one or more PCIe root |
| 9 | ports connected to modem device. The host has limited access to device memory |
| 10 | space, including register configuration and control the device operation. |
| 11 | Data transfers are invoked from the device. |
| 12 | |
| 13 | All data structures used by MHI are in the host system memory. Using PCIe |
| 14 | interface, the device accesses those data structures. MHI data structures and |
| 15 | data buffers in the host system memory regions are mapped for device. |
| 16 | |
| 17 | Memory spaces |
| 18 | ------------- |
| 19 | PCIe Configurations : Used for enumeration and resource management, such as |
| 20 | interrupt and base addresses. This is done by mhi control driver. |
| 21 | |
| 22 | MMIO |
| 23 | ---- |
| 24 | MHI MMIO : Memory mapped IO consists of set of registers in the device hardware, |
| 25 | which are mapped to the host memory space through PCIe base address register |
| 26 | (BAR) |
| 27 | |
| 28 | MHI control registers : Access to MHI configurations registers |
| 29 | (struct mhi_controller.regs). |
| 30 | |
| 31 | MHI BHI register: Boot host interface registers (struct mhi_controller.bhi) used |
| 32 | for firmware download before MHI initialization. |
| 33 | |
| 34 | Channel db array : Doorbell registers (struct mhi_chan.tre_ring.db_addr) used by |
| 35 | host to notify device there is new work to do. |
| 36 | |
| 37 | Event db array : Associated with event context array |
| 38 | (struct mhi_event.ring.db_addr), host uses to notify device free events are |
| 39 | available. |
| 40 | |
| 41 | Data structures |
| 42 | --------------- |
| 43 | Host memory : Directly accessed by the host to manage the MHI data structures |
| 44 | and buffers. The device accesses the host memory over the PCIe interface. |
| 45 | |
| 46 | Channel context array : All channel configurations are organized in channel |
| 47 | context data array. |
| 48 | |
| 49 | struct __packed mhi_chan_ctxt; |
| 50 | struct mhi_ctxt.chan_ctxt; |
| 51 | |
| 52 | Transfer rings : Used by host to schedule work items for a channel and organized |
| 53 | as a circular queue of transfer descriptors (TD). |
| 54 | |
| 55 | struct __packed mhi_tre; |
| 56 | struct mhi_chan.tre_ring; |
| 57 | |
| 58 | Event context array : All event configurations are organized in event context |
| 59 | data array. |
| 60 | |
| 61 | struct mhi_ctxt.er_ctxt; |
| 62 | struct __packed mhi_event_ctxt; |
| 63 | |
| 64 | Event rings: Used by device to send completion and state transition messages to |
| 65 | host |
| 66 | |
| 67 | struct mhi_event.ring; |
| 68 | struct __packed mhi_tre; |
| 69 | |
| 70 | Command context array: All command configurations are organized in command |
| 71 | context data array. |
| 72 | |
| 73 | struct __packed mhi_cmd_ctxt; |
| 74 | struct mhi_ctxt.cmd_ctxt; |
| 75 | |
| 76 | Command rings: Used by host to send MHI commands to device |
| 77 | |
| 78 | struct __packed mhi_tre; |
| 79 | struct mhi_cmd.ring; |
| 80 | |
| 81 | Transfer rings |
| 82 | -------------- |
| 83 | MHI channels are logical, unidirectional data pipes between host and device. |
| 84 | Each channel associated with a single transfer ring. The data direction can be |
| 85 | either inbound (device to host) or outbound (host to device). Transfer |
| 86 | descriptors are managed by using transfer rings, which are defined for each |
| 87 | channel between device and host and resides in the host memory. |
| 88 | |
| 89 | Transfer ring Pointer: Transfer Ring Array |
| 90 | [Read Pointer (RP)] ----------->[Ring Element] } TD |
| 91 | [Write Pointer (WP)]- [Ring Element] |
| 92 | - [Ring Element] |
| 93 | --------->[Ring Element] |
| 94 | [Ring Element] |
| 95 | |
| 96 | 1. Host allocate memory for transfer ring |
| 97 | 2. Host sets base, read pointer, write pointer in corresponding channel context |
| 98 | 3. Ring is considered empty when RP == WP |
| 99 | 4. Ring is considered full when WP + 1 == RP |
| 100 | 4. RP indicates the next element to be serviced by device |
| 101 | 4. When host new buffer to send, host update the Ring element with buffer |
| 102 | information |
| 103 | 5. Host increment the WP to next element |
| 104 | 6. Ring the associated channel DB. |
| 105 | |
| 106 | Event rings |
| 107 | ----------- |
| 108 | Events from the device to host are organized in event rings and defined in event |
| 109 | descriptors. Event rings are array of EDs that resides in the host memory. |
| 110 | |
| 111 | Transfer ring Pointer: Event Ring Array |
| 112 | [Read Pointer (RP)] ----------->[Ring Element] } ED |
| 113 | [Write Pointer (WP)]- [Ring Element] |
| 114 | - [Ring Element] |
| 115 | --------->[Ring Element] |
| 116 | [Ring Element] |
| 117 | |
| 118 | 1. Host allocate memory for event ring |
| 119 | 2. Host sets base, read pointer, write pointer in corresponding channel context |
| 120 | 3. Both host and device has local copy of RP, WP |
| 121 | 3. Ring is considered empty (no events to service) when WP + 1 == RP |
| 122 | 4. Ring is full of events when RP == WP |
| 123 | 4. RP - 1 = last event device programmed |
| 124 | 4. When there is a new event device need to send, device update ED pointed by RP |
| 125 | 5. Device increment RP to next element |
| 126 | 6. Device trigger and interrupt |
| 127 | |
| 128 | Example Operation for data transfer: |
| 129 | |
| 130 | 1. Host prepare TD with buffer information |
| 131 | 2. Host increment Chan[id].ctxt.WP |
| 132 | 3. Host ring channel DB register |
| 133 | 4. Device wakes up process the TD |
| 134 | 5. Device generate a completion event for that TD by updating ED |
| 135 | 6. Device increment Event[id].ctxt.RP |
| 136 | 7. Device trigger MSI to wake host |
| 137 | 8. Host wakes up and check event ring for completion event |
| 138 | 9. Host update the Event[i].ctxt.WP to indicate processed of completion event. |
| 139 | |
| 140 | Time sync |
| 141 | --------- |
| 142 | To synchronize two applications between host and external modem, MHI provide |
| 143 | native support to get external modems free running timer value in a fast |
| 144 | reliable method. MHI clients do not need to create client specific methods to |
| 145 | get modem time. |
| 146 | |
| 147 | When client requests modem time, MHI host will automatically capture host time |
| 148 | at that moment so clients are able to do accurate drift adjustment. |
| 149 | |
| 150 | Example: |
| 151 | |
| 152 | Client request time @ time T1 |
| 153 | |
| 154 | Host Time: Tx |
| 155 | Modem Time: Ty |
| 156 | |
| 157 | Client request time @ time T2 |
| 158 | Host Time: Txx |
| 159 | Modem Time: Tyy |
| 160 | |
| 161 | Then drift is: |
| 162 | Tyy - Ty + <drift> == Txx - Tx |
| 163 | |
| 164 | Clients are free to implement their own drift algorithms, what MHI host provide |
| 165 | is a way to accurately correlate host time with external modem time. |
| 166 | |
| 167 | To avoid link level latencies, controller must support capabilities to disable |
| 168 | any link level latency. |
| 169 | |
| 170 | During Time capture host will: |
| 171 | 1. Capture host time |
| 172 | 2. Trigger doorbell to capture modem time |
| 173 | |
| 174 | It's important time between Step 2 to Step 1 is deterministic as possible. |
| 175 | Therefore, MHI host will: |
| 176 | 1. Disable any MHI related to low power modes. |
| 177 | 2. Disable preemption |
| 178 | 3. Request bus master to disable any link level latencies. Controller |
| 179 | should disable all low power modes such as L0s, L1, L1ss. |
| 180 | |
| 181 | MHI States |
| 182 | ---------- |
| 183 | |
| 184 | enum MHI_STATE { |
| 185 | MHI_STATE_RESET : MHI is in reset state, POR state. Host is not allowed to |
| 186 | access device MMIO register space. |
| 187 | MHI_STATE_READY : Device is ready for initialization. Host can start MHI |
| 188 | initialization by programming MMIO |
| 189 | MHI_STATE_M0 : MHI is in fully active state, data transfer is active |
| 190 | MHI_STATE_M1 : Device in a suspended state |
| 191 | MHI_STATE_M2 : MHI in low power mode, device may enter lower power mode. |
| 192 | MHI_STATE_M3 : Both host and device in suspended state. PCIe link is not |
| 193 | accessible to device. |
| 194 | |
| 195 | MHI Initialization |
| 196 | ------------------ |
| 197 | |
| 198 | 1. After system boots, the device is enumerated over PCIe interface |
| 199 | 2. Host allocate MHI context for event, channel and command arrays |
| 200 | 3. Initialize context array, and prepare interrupts |
| 201 | 3. Host waits until device enter READY state |
| 202 | 4. Program MHI MMIO registers and set device into MHI_M0 state |
| 203 | 5. Wait for device to enter M0 state |
| 204 | |
| 205 | Linux Software Architecture |
| 206 | =========================== |
| 207 | |
| 208 | MHI Controller |
| 209 | -------------- |
| 210 | MHI controller is also the MHI bus master. In charge of managing the physical |
| 211 | link between host and device. Not involved in actual data transfer. At least |
| 212 | for PCIe based buses, for other type of bus, we can expand to add support. |
| 213 | |
| 214 | Roles: |
| 215 | 1. Turn on PCIe bus and configure the link |
| 216 | 2. Configure MSI, SMMU, and IOMEM |
| 217 | 3. Allocate struct mhi_controller and register with MHI bus framework |
| 218 | 2. Initiate power on and shutdown sequence |
| 219 | 3. Initiate suspend and resume |
| 220 | |
| 221 | Usage |
| 222 | ----- |
| 223 | |
| 224 | 1. Allocate control data structure by calling mhi_alloc_controller() |
| 225 | 2. Initialize mhi_controller with all the known information such as: |
| 226 | - Device Topology |
| 227 | - IOMMU window |
| 228 | - IOMEM mapping |
| 229 | - Device to use for memory allocation, and of_node with DT configuration |
| 230 | - Configure asynchronous callback functions |
| 231 | 3. Register MHI controller with MHI bus framework by calling |
| 232 | of_register_mhi_controller() |
| 233 | |
| 234 | After successfully registering controller can initiate any of these power modes: |
| 235 | |
| 236 | 1. Power up sequence |
| 237 | - mhi_prepare_for_power_up() |
| 238 | - mhi_async_power_up() |
| 239 | - mhi_sync_power_up() |
| 240 | 2. Power down sequence |
| 241 | - mhi_power_down() |
| 242 | - mhi_unprepare_after_power_down() |
| 243 | 3. Initiate suspend |
| 244 | - mhi_pm_suspend() |
| 245 | 4. Initiate resume |
| 246 | - mhi_pm_resume() |
| 247 | |
| 248 | MHI Devices |
| 249 | ----------- |
| 250 | Logical device that bind to maximum of two physical MHI channels. Once MHI is in |
| 251 | powered on state, each supported channel by controller will be allocated as a |
| 252 | mhi_device. |
| 253 | |
| 254 | Each supported device would be enumerated under |
| 255 | /sys/bus/mhi/devices/ |
| 256 | |
| 257 | struct mhi_device; |
| 258 | |
| 259 | MHI Driver |
| 260 | ---------- |
| 261 | Each MHI driver can bind to one or more MHI devices. MHI host driver will bind |
| 262 | mhi_device to mhi_driver. |
| 263 | |
| 264 | All registered drivers are visible under |
| 265 | /sys/bus/mhi/drivers/ |
| 266 | |
| 267 | struct mhi_driver; |
| 268 | |
| 269 | Usage |
| 270 | ----- |
| 271 | |
| 272 | 1. Register driver using mhi_driver_register |
| 273 | 2. Before sending data, prepare device for transfer by calling |
| 274 | mhi_prepare_for_transfer |
| 275 | 3. Initiate data transfer by calling mhi_queue_transfer |
| 276 | 4. After finish, call mhi_unprepare_from_transfer to end data transfer |