| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | |
| 3 | //! IO polling. |
| 4 | //! |
| 5 | //! C header: [`include/linux/iopoll.h`](srctree/include/linux/iopoll.h). |
| 6 | |
| 7 | use crate:: |
| 8 | *, |
| 9 | cpu_relax, |
| 10 | might_sleep, |
| 11 | |
| 12 | |
| 13 | fsleep, |
| 14 | udelay, // |
| 15 | , |
| 16 | Delta, |
| 17 | Instant, |
| 18 | Monotonic, // |
| 19 | , |
| 20 | ; |
| 21 | |
| 22 | /// Polls periodically until a condition is met, an error occurs, |
| 23 | /// or the timeout is reached. |
| 24 | /// |
| 25 | /// The function repeatedly executes the given operation `op` closure and |
| 26 | /// checks its result using the condition closure `cond`. |
| 27 | /// |
| 28 | /// If `cond` returns `true`, the function returns successfully with |
| 29 | /// the result of `op`. Otherwise, it waits for a duration specified |
| 30 | /// by `sleep_delta` before executing `op` again. |
| 31 | /// |
| 32 | /// This process continues until either `op` returns an error, `cond` |
| 33 | /// returns `true`, or the timeout specified by `timeout_delta` is |
| 34 | /// reached. |
| 35 | /// |
| 36 | /// This function can only be used in a nonatomic context. |
| 37 | /// |
| 38 | /// # Errors |
| 39 | /// |
| 40 | /// If `op` returns an error, then that error is returned directly. |
| 41 | /// |
| 42 | /// If the timeout specified by `timeout_delta` is reached, then |
| 43 | /// `Err(ETIMEDOUT)` is returned. |
| 44 | /// |
| 45 | /// # Examples |
| 46 | /// |
| 47 | /// ```no_run |
| 48 | /// use kernel::io::{ |
| 49 | /// Io, |
| 50 | /// Mmio, |
| 51 | /// Region, |
| 52 | /// poll::read_poll_timeout, // |
| 53 | /// }; |
| 54 | /// use kernel::time::Delta; |
| 55 | /// |
| 56 | /// const HW_READY: u16 = 0x01; |
| 57 | /// |
| 58 | /// fn wait_for_hardware<const SIZE: usize>(io: Mmio<'_, Region<SIZE>>) -> Result { |
| 59 | /// read_poll_timeout( |
| 60 | /// // The `op` closure reads the value of a specific status register. |
| 61 | /// || io.try_read16(0x1000), |
| 62 | /// // The `cond` closure takes a reference to the value returned by `op` |
| 63 | /// // and checks whether the hardware is ready. |
| 64 | /// |val: &u16| *val == HW_READY, |
| 65 | /// Delta::from_millis(50), |
| 66 | /// Delta::from_secs(3), |
| 67 | /// )?; |
| 68 | /// Ok(()) |
| 69 | /// } |
| 70 | /// ``` |
| 71 | |
| 72 | |
| 73 | mut op: Op, |
| 74 | mut cond: Cond, |
| 75 | sleep_delta: Delta, |
| 76 | timeout_delta: Delta, |
| 77 | |
| 78 | |
| 79 | Op: FnMut() -> , |
| 80 | Cond: FnMut(&T) -> bool, |
| 81 | |
| 82 | let start: = now; |
| 83 | |
| 84 | // Unlike the C version, we always call `might_sleep()` unconditionally, |
| 85 | // as conditional calls are error-prone. We clearly separate |
| 86 | // `read_poll_timeout()` and `read_poll_timeout_atomic()` to aid |
| 87 | // tools like klint. |
| 88 | might_sleep; |
| 89 | |
| 90 | loop |
| 91 | let val = op?; |
| 92 | if cond |
| 93 | // Unlike the C version, we immediately return. |
| 94 | // We know the condition is met so we don't need to check again. |
| 95 | return Ok; |
| 96 | |
| 97 | |
| 98 | if start.elapsed > timeout_delta |
| 99 | // Unlike the C version, we immediately return. |
| 100 | // We have just called `op()` so we don't need to call it again. |
| 101 | return Err; |
| 102 | |
| 103 | |
| 104 | if !sleep_delta.is_zero |
| 105 | fsleep; |
| 106 | |
| 107 | |
| 108 | // `fsleep()` could be a busy-wait loop so we always call `cpu_relax()`. |
| 109 | cpu_relax; |
| 110 | |
| 111 | |
| 112 | |
| 113 | /// Polls periodically until a condition is met, an error occurs, |
| 114 | /// or the attempt limit is reached. |
| 115 | /// |
| 116 | /// The function repeatedly executes the given operation `op` closure and |
| 117 | /// checks its result using the condition closure `cond`. |
| 118 | /// |
| 119 | /// If `cond` returns `true`, the function returns successfully with the result of `op`. |
| 120 | /// Otherwise, it performs a busy wait for a duration specified by `delay_delta` |
| 121 | /// before executing `op` again. |
| 122 | /// |
| 123 | /// This process continues until either `op` returns an error, `cond` |
| 124 | /// returns `true`, or the attempt limit specified by `retry` is reached. |
| 125 | /// |
| 126 | /// # Errors |
| 127 | /// |
| 128 | /// If `op` returns an error, then that error is returned directly. |
| 129 | /// |
| 130 | /// If the attempt limit specified by `retry` is reached, then |
| 131 | /// `Err(ETIMEDOUT)` is returned. |
| 132 | /// |
| 133 | /// # Examples |
| 134 | /// |
| 135 | /// ```no_run |
| 136 | /// use kernel::io::{ |
| 137 | /// Io, |
| 138 | /// Mmio, |
| 139 | /// Region, |
| 140 | /// poll::read_poll_timeout_atomic, // |
| 141 | /// }; |
| 142 | /// use kernel::time::Delta; |
| 143 | /// |
| 144 | /// const HW_READY: u16 = 0x01; |
| 145 | /// |
| 146 | /// fn wait_for_hardware<const SIZE: usize>(io: Mmio<'_, Region<SIZE>>) -> Result { |
| 147 | /// read_poll_timeout_atomic( |
| 148 | /// // The `op` closure reads the value of a specific status register. |
| 149 | /// || io.try_read16(0x1000), |
| 150 | /// // The `cond` closure takes a reference to the value returned by `op` |
| 151 | /// // and checks whether the hardware is ready. |
| 152 | /// |val: &u16| *val == HW_READY, |
| 153 | /// Delta::from_micros(50), |
| 154 | /// 1000, |
| 155 | /// )?; |
| 156 | /// Ok(()) |
| 157 | /// } |
| 158 | /// ``` |
| 159 | |
| 160 | mut op: Op, |
| 161 | mut cond: Cond, |
| 162 | delay_delta: Delta, |
| 163 | retry: usize, |
| 164 | |
| 165 | |
| 166 | Op: FnMut() -> , |
| 167 | Cond: FnMut(&T) -> bool, |
| 168 | |
| 169 | for _ in 0..retry |
| 170 | let val = op?; |
| 171 | if cond |
| 172 | return Ok; |
| 173 | |
| 174 | |
| 175 | if !delay_delta.is_zero |
| 176 | udelay; |
| 177 | |
| 178 | |
| 179 | cpu_relax; |
| 180 | |
| 181 | |
| 182 | Err |
| 183 |