1 /* 2 * Copyright 2012-15 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: AMD 23 * 24 */ 25 26 #include "dm_services.h" 27 28 /* 29 * Pre-requisites: headers required by header of this unit 30 */ 31 #include "include/i2caux_interface.h" 32 #include "engine.h" 33 #include "i2c_engine.h" 34 #include "i2c_hw_engine.h" 35 36 /* 37 * Header of this unit 38 */ 39 40 #include "i2c_generic_hw_engine.h" 41 42 /* 43 * Post-requisites: headers required by this unit 44 */ 45 46 /* 47 * This unit 48 */ 49 50 /* 51 * @brief 52 * Cast 'struct i2c_hw_engine *' 53 * to 'struct i2c_generic_hw_engine *' 54 */ 55 #define FROM_I2C_HW_ENGINE(ptr) \ 56 container_of((ptr), struct i2c_generic_hw_engine, base) 57 58 /* 59 * @brief 60 * Cast 'struct i2c_engine *' 61 * to 'struct i2c_generic_hw_engine *' 62 */ 63 #define FROM_I2C_ENGINE(ptr) \ 64 FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base)) 65 66 /* 67 * @brief 68 * Cast 'struct engine *' 69 * to 'struct i2c_generic_hw_engine *' 70 */ 71 #define FROM_ENGINE(ptr) \ 72 FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) 73 74 enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type( 75 const struct engine *engine) 76 { 77 return I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW; 78 } 79 80 /* 81 * @brief 82 * Single transaction handling. 83 * Since transaction may be bigger than HW buffer size, 84 * it divides transaction to sub-transactions 85 * and uses batch transaction feature of the engine. 86 */ 87 bool dal_i2c_generic_hw_engine_submit_request( 88 struct engine *engine, 89 struct i2caux_transaction_request *i2caux_request, 90 bool middle_of_transaction) 91 { 92 struct i2c_generic_hw_engine *hw_engine = FROM_ENGINE(engine); 93 94 struct i2c_hw_engine *base = &hw_engine->base; 95 96 uint32_t max_payload_size = 97 base->funcs->get_hw_buffer_available_size(base); 98 99 bool initial_stop_bit = !middle_of_transaction; 100 101 struct i2c_generic_transaction_attributes attributes; 102 103 enum i2c_channel_operation_result operation_result = 104 I2C_CHANNEL_OPERATION_FAILED; 105 106 bool result = false; 107 108 /* setup transaction initial properties */ 109 110 uint8_t address = i2caux_request->payload.address; 111 uint8_t *current_payload = i2caux_request->payload.data; 112 uint32_t remaining_payload_size = i2caux_request->payload.length; 113 114 bool first_iteration = true; 115 116 if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) 117 attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_READ; 118 else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) 119 attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE; 120 else { 121 i2caux_request->status = 122 I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION; 123 return false; 124 } 125 126 /* Do batch transaction. 127 * Divide read/write data into payloads which fit HW buffer size. 128 * 1. Single transaction: 129 * start_bit = 1, stop_bit depends on session state, ack_on_read = 0; 130 * 2. Start of batch transaction: 131 * start_bit = 1, stop_bit = 0, ack_on_read = 1; 132 * 3. Middle of batch transaction: 133 * start_bit = 0, stop_bit = 0, ack_on_read = 1; 134 * 4. End of batch transaction: 135 * start_bit = 0, stop_bit depends on session state, ack_on_read = 0. 136 * Session stop bit is set if 'middle_of_transaction' = 0. */ 137 138 while (remaining_payload_size) { 139 uint32_t current_transaction_size; 140 uint32_t current_payload_size; 141 142 bool last_iteration; 143 bool stop_bit; 144 145 /* Calculate current transaction size and payload size. 146 * Transaction size = total number of bytes in transaction, 147 * including slave's address; 148 * Payload size = number of data bytes in transaction. */ 149 150 if (first_iteration) { 151 /* In the first sub-transaction we send slave's address 152 * thus we need to reserve one byte for it */ 153 current_transaction_size = 154 (remaining_payload_size > max_payload_size - 1) ? 155 max_payload_size : 156 remaining_payload_size + 1; 157 158 current_payload_size = current_transaction_size - 1; 159 } else { 160 /* Second and further sub-transactions will have 161 * entire buffer reserved for data */ 162 current_transaction_size = 163 (remaining_payload_size > max_payload_size) ? 164 max_payload_size : 165 remaining_payload_size; 166 167 current_payload_size = current_transaction_size; 168 } 169 170 last_iteration = 171 (remaining_payload_size == current_payload_size); 172 173 stop_bit = last_iteration ? initial_stop_bit : false; 174 175 /* write slave device address */ 176 177 if (first_iteration) 178 hw_engine->funcs->write_address(hw_engine, address); 179 180 /* write current portion of data, if requested */ 181 182 if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) 183 hw_engine->funcs->write_data( 184 hw_engine, 185 current_payload, 186 current_payload_size); 187 188 /* execute transaction */ 189 190 attributes.start_bit = first_iteration; 191 attributes.stop_bit = stop_bit; 192 attributes.last_read = last_iteration; 193 attributes.transaction_size = current_transaction_size; 194 195 hw_engine->funcs->execute_transaction(hw_engine, &attributes); 196 197 /* wait until transaction is processed; if it fails - quit */ 198 199 operation_result = base->funcs->wait_on_operation_result( 200 base, 201 base->funcs->get_transaction_timeout( 202 base, current_transaction_size), 203 I2C_CHANNEL_OPERATION_ENGINE_BUSY); 204 205 if (operation_result != I2C_CHANNEL_OPERATION_SUCCEEDED) 206 break; 207 208 /* read current portion of data, if requested */ 209 210 /* the read offset should be 1 for first sub-transaction, 211 * and 0 for any next one */ 212 213 if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) 214 hw_engine->funcs->read_data(hw_engine, current_payload, 215 current_payload_size, first_iteration ? 1 : 0); 216 217 /* update loop variables */ 218 219 first_iteration = false; 220 current_payload += current_payload_size; 221 remaining_payload_size -= current_payload_size; 222 } 223 224 /* update transaction status */ 225 226 switch (operation_result) { 227 case I2C_CHANNEL_OPERATION_SUCCEEDED: 228 i2caux_request->status = 229 I2CAUX_TRANSACTION_STATUS_SUCCEEDED; 230 result = true; 231 break; 232 case I2C_CHANNEL_OPERATION_NO_RESPONSE: 233 i2caux_request->status = 234 I2CAUX_TRANSACTION_STATUS_FAILED_NACK; 235 break; 236 case I2C_CHANNEL_OPERATION_TIMEOUT: 237 i2caux_request->status = 238 I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; 239 break; 240 case I2C_CHANNEL_OPERATION_FAILED: 241 i2caux_request->status = 242 I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE; 243 break; 244 default: 245 i2caux_request->status = 246 I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION; 247 } 248 249 return result; 250 } 251 252 /* 253 * @brief 254 * Returns number of microseconds to wait until timeout to be considered 255 */ 256 uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout( 257 const struct i2c_hw_engine *engine, 258 uint32_t length) 259 { 260 const struct i2c_engine *base = &engine->base; 261 262 uint32_t speed = base->funcs->get_speed(base); 263 264 if (!speed) 265 return 0; 266 267 /* total timeout = period_timeout * (start + data bits count + stop) */ 268 269 return ((1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed) * 270 (1 + (length << 3) + 1); 271 } 272 273 void dal_i2c_generic_hw_engine_construct( 274 struct i2c_generic_hw_engine *engine, 275 struct dc_context *ctx) 276 { 277 dal_i2c_hw_engine_construct(&engine->base, ctx); 278 } 279 280 void dal_i2c_generic_hw_engine_destruct( 281 struct i2c_generic_hw_engine *engine) 282 { 283 dal_i2c_hw_engine_destruct(&engine->base); 284 } 285