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 34 /* 35 * Header of this unit 36 */ 37 38 #include "aux_engine.h" 39 40 /* 41 * Post-requisites: headers required by this unit 42 */ 43 44 #include "include/link_service_types.h" 45 46 /* 47 * This unit 48 */ 49 50 enum { 51 AUX_INVALID_REPLY_RETRY_COUNTER = 1, 52 AUX_TIMED_OUT_RETRY_COUNTER = 2, 53 AUX_DEFER_RETRY_COUNTER = 6 54 }; 55 56 #define FROM_ENGINE(ptr) \ 57 container_of((ptr), struct aux_engine, base) 58 #define DC_LOGGER \ 59 engine->base.ctx->logger 60 61 enum i2caux_engine_type dal_aux_engine_get_engine_type( 62 const struct engine *engine) 63 { 64 return I2CAUX_ENGINE_TYPE_AUX; 65 } 66 67 bool dal_aux_engine_acquire( 68 struct engine *engine, 69 struct ddc *ddc) 70 { 71 struct aux_engine *aux_engine = FROM_ENGINE(engine); 72 73 enum gpio_result result; 74 if (aux_engine->funcs->is_engine_available) { 75 /*check whether SW could use the engine*/ 76 if (!aux_engine->funcs->is_engine_available(aux_engine)) { 77 return false; 78 } 79 } 80 81 result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, 82 GPIO_DDC_CONFIG_TYPE_MODE_AUX); 83 84 if (result != GPIO_RESULT_OK) 85 return false; 86 87 if (!aux_engine->funcs->acquire_engine(aux_engine)) { 88 dal_ddc_close(ddc); 89 return false; 90 } 91 92 engine->ddc = ddc; 93 94 return true; 95 } 96 97 struct read_command_context { 98 uint8_t *buffer; 99 uint32_t current_read_length; 100 uint32_t offset; 101 enum i2caux_transaction_status status; 102 103 struct aux_request_transaction_data request; 104 struct aux_reply_transaction_data reply; 105 106 uint8_t returned_byte; 107 108 uint32_t timed_out_retry_aux; 109 uint32_t invalid_reply_retry_aux; 110 uint32_t defer_retry_aux; 111 uint32_t defer_retry_i2c; 112 uint32_t invalid_reply_retry_aux_on_ack; 113 114 bool transaction_complete; 115 bool operation_succeeded; 116 }; 117 118 static void process_read_reply( 119 struct aux_engine *engine, 120 struct read_command_context *ctx) 121 { 122 engine->funcs->process_channel_reply(engine, &ctx->reply); 123 124 switch (ctx->reply.status) { 125 case AUX_TRANSACTION_REPLY_AUX_ACK: 126 ctx->defer_retry_aux = 0; 127 if (ctx->returned_byte > ctx->current_read_length) { 128 ctx->status = 129 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; 130 ctx->operation_succeeded = false; 131 } else if (ctx->returned_byte < ctx->current_read_length) { 132 ctx->current_read_length -= ctx->returned_byte; 133 134 ctx->offset += ctx->returned_byte; 135 136 ++ctx->invalid_reply_retry_aux_on_ack; 137 138 if (ctx->invalid_reply_retry_aux_on_ack > 139 AUX_INVALID_REPLY_RETRY_COUNTER) { 140 ctx->status = 141 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; 142 ctx->operation_succeeded = false; 143 } 144 } else { 145 ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; 146 ctx->transaction_complete = true; 147 ctx->operation_succeeded = true; 148 } 149 break; 150 case AUX_TRANSACTION_REPLY_AUX_NACK: 151 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; 152 ctx->operation_succeeded = false; 153 break; 154 case AUX_TRANSACTION_REPLY_AUX_DEFER: 155 ++ctx->defer_retry_aux; 156 157 if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) { 158 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; 159 ctx->operation_succeeded = false; 160 } 161 break; 162 case AUX_TRANSACTION_REPLY_I2C_DEFER: 163 ctx->defer_retry_aux = 0; 164 165 ++ctx->defer_retry_i2c; 166 167 if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) { 168 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; 169 ctx->operation_succeeded = false; 170 } 171 break; 172 case AUX_TRANSACTION_REPLY_HPD_DISCON: 173 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON; 174 ctx->operation_succeeded = false; 175 break; 176 default: 177 ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; 178 ctx->operation_succeeded = false; 179 } 180 } 181 182 static void process_read_request( 183 struct aux_engine *engine, 184 struct read_command_context *ctx) 185 { 186 enum aux_channel_operation_result operation_result; 187 188 engine->funcs->submit_channel_request(engine, &ctx->request); 189 190 operation_result = engine->funcs->get_channel_status( 191 engine, &ctx->returned_byte); 192 193 switch (operation_result) { 194 case AUX_CHANNEL_OPERATION_SUCCEEDED: 195 if (ctx->returned_byte > ctx->current_read_length) { 196 ctx->status = 197 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; 198 ctx->operation_succeeded = false; 199 } else { 200 ctx->timed_out_retry_aux = 0; 201 ctx->invalid_reply_retry_aux = 0; 202 203 ctx->reply.length = ctx->returned_byte; 204 ctx->reply.data = ctx->buffer; 205 206 process_read_reply(engine, ctx); 207 } 208 break; 209 case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: 210 ++ctx->invalid_reply_retry_aux; 211 212 if (ctx->invalid_reply_retry_aux > 213 AUX_INVALID_REPLY_RETRY_COUNTER) { 214 ctx->status = 215 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; 216 ctx->operation_succeeded = false; 217 } else 218 udelay(400); 219 break; 220 case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: 221 ++ctx->timed_out_retry_aux; 222 223 if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) { 224 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; 225 ctx->operation_succeeded = false; 226 } else { 227 /* DP 1.2a, table 2-58: 228 * "S3: AUX Request CMD PENDING: 229 * retry 3 times, with 400usec wait on each" 230 * The HW timeout is set to 550usec, 231 * so we should not wait here */ 232 } 233 break; 234 case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: 235 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON; 236 ctx->operation_succeeded = false; 237 break; 238 default: 239 ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; 240 ctx->operation_succeeded = false; 241 } 242 } 243 244 static bool read_command( 245 struct aux_engine *engine, 246 struct i2caux_transaction_request *request, 247 bool middle_of_transaction) 248 { 249 struct read_command_context ctx; 250 251 ctx.buffer = request->payload.data; 252 ctx.current_read_length = request->payload.length; 253 ctx.offset = 0; 254 ctx.timed_out_retry_aux = 0; 255 ctx.invalid_reply_retry_aux = 0; 256 ctx.defer_retry_aux = 0; 257 ctx.defer_retry_i2c = 0; 258 ctx.invalid_reply_retry_aux_on_ack = 0; 259 ctx.transaction_complete = false; 260 ctx.operation_succeeded = true; 261 262 if (request->payload.address_space == 263 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { 264 ctx.request.type = AUX_TRANSACTION_TYPE_DP; 265 ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ; 266 ctx.request.address = request->payload.address; 267 } else if (request->payload.address_space == 268 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) { 269 ctx.request.type = AUX_TRANSACTION_TYPE_I2C; 270 ctx.request.action = middle_of_transaction ? 271 I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : 272 I2CAUX_TRANSACTION_ACTION_I2C_READ; 273 ctx.request.address = request->payload.address >> 1; 274 } else { 275 /* in DAL2, there was no return in such case */ 276 BREAK_TO_DEBUGGER(); 277 return false; 278 } 279 280 ctx.request.delay = 0; 281 282 do { 283 memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length); 284 285 ctx.request.data = ctx.buffer + ctx.offset; 286 ctx.request.length = ctx.current_read_length; 287 288 process_read_request(engine, &ctx); 289 290 request->status = ctx.status; 291 292 if (ctx.operation_succeeded && !ctx.transaction_complete) 293 if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C) 294 msleep(engine->delay); 295 } while (ctx.operation_succeeded && !ctx.transaction_complete); 296 297 if (request->payload.address_space == 298 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { 299 DC_LOG_I2C_AUX("READ: addr:0x%x value:0x%x Result:%d", 300 request->payload.address, 301 request->payload.data[0], 302 ctx.operation_succeeded); 303 } 304 305 return ctx.operation_succeeded; 306 } 307 308 struct write_command_context { 309 bool mot; 310 311 uint8_t *buffer; 312 uint32_t current_write_length; 313 enum i2caux_transaction_status status; 314 315 struct aux_request_transaction_data request; 316 struct aux_reply_transaction_data reply; 317 318 uint8_t returned_byte; 319 320 uint32_t timed_out_retry_aux; 321 uint32_t invalid_reply_retry_aux; 322 uint32_t defer_retry_aux; 323 uint32_t defer_retry_i2c; 324 uint32_t max_defer_retry; 325 uint32_t ack_m_retry; 326 327 uint8_t reply_data[DEFAULT_AUX_MAX_DATA_SIZE]; 328 329 bool transaction_complete; 330 bool operation_succeeded; 331 }; 332 333 static void process_write_reply( 334 struct aux_engine *engine, 335 struct write_command_context *ctx) 336 { 337 engine->funcs->process_channel_reply(engine, &ctx->reply); 338 339 switch (ctx->reply.status) { 340 case AUX_TRANSACTION_REPLY_AUX_ACK: 341 ctx->operation_succeeded = true; 342 343 if (ctx->returned_byte) { 344 ctx->request.action = ctx->mot ? 345 I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT : 346 I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; 347 348 ctx->current_write_length = 0; 349 350 ++ctx->ack_m_retry; 351 352 if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) { 353 ctx->status = 354 I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; 355 ctx->operation_succeeded = false; 356 } else 357 udelay(300); 358 } else { 359 ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; 360 ctx->defer_retry_aux = 0; 361 ctx->ack_m_retry = 0; 362 ctx->transaction_complete = true; 363 } 364 break; 365 case AUX_TRANSACTION_REPLY_AUX_NACK: 366 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; 367 ctx->operation_succeeded = false; 368 break; 369 case AUX_TRANSACTION_REPLY_AUX_DEFER: 370 ++ctx->defer_retry_aux; 371 372 if (ctx->defer_retry_aux > ctx->max_defer_retry) { 373 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; 374 ctx->operation_succeeded = false; 375 } 376 break; 377 case AUX_TRANSACTION_REPLY_I2C_DEFER: 378 ctx->defer_retry_aux = 0; 379 ctx->current_write_length = 0; 380 381 ctx->request.action = ctx->mot ? 382 I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT : 383 I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; 384 385 ++ctx->defer_retry_i2c; 386 387 if (ctx->defer_retry_i2c > ctx->max_defer_retry) { 388 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; 389 ctx->operation_succeeded = false; 390 } 391 break; 392 case AUX_TRANSACTION_REPLY_HPD_DISCON: 393 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON; 394 ctx->operation_succeeded = false; 395 break; 396 default: 397 ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; 398 ctx->operation_succeeded = false; 399 } 400 } 401 402 static void process_write_request( 403 struct aux_engine *engine, 404 struct write_command_context *ctx) 405 { 406 enum aux_channel_operation_result operation_result; 407 408 engine->funcs->submit_channel_request(engine, &ctx->request); 409 410 operation_result = engine->funcs->get_channel_status( 411 engine, &ctx->returned_byte); 412 413 switch (operation_result) { 414 case AUX_CHANNEL_OPERATION_SUCCEEDED: 415 ctx->timed_out_retry_aux = 0; 416 ctx->invalid_reply_retry_aux = 0; 417 418 ctx->reply.length = ctx->returned_byte; 419 ctx->reply.data = ctx->reply_data; 420 421 process_write_reply(engine, ctx); 422 break; 423 case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: 424 ++ctx->invalid_reply_retry_aux; 425 426 if (ctx->invalid_reply_retry_aux > 427 AUX_INVALID_REPLY_RETRY_COUNTER) { 428 ctx->status = 429 I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR; 430 ctx->operation_succeeded = false; 431 } else 432 udelay(400); 433 break; 434 case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: 435 ++ctx->timed_out_retry_aux; 436 437 if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) { 438 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; 439 ctx->operation_succeeded = false; 440 } else { 441 /* DP 1.2a, table 2-58: 442 * "S3: AUX Request CMD PENDING: 443 * retry 3 times, with 400usec wait on each" 444 * The HW timeout is set to 550usec, 445 * so we should not wait here */ 446 } 447 break; 448 case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: 449 ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_HPD_DISCON; 450 ctx->operation_succeeded = false; 451 break; 452 default: 453 ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN; 454 ctx->operation_succeeded = false; 455 } 456 } 457 458 static bool write_command( 459 struct aux_engine *engine, 460 struct i2caux_transaction_request *request, 461 bool middle_of_transaction) 462 { 463 struct write_command_context ctx; 464 465 ctx.mot = middle_of_transaction; 466 ctx.buffer = request->payload.data; 467 ctx.current_write_length = request->payload.length; 468 ctx.timed_out_retry_aux = 0; 469 ctx.invalid_reply_retry_aux = 0; 470 ctx.defer_retry_aux = 0; 471 ctx.defer_retry_i2c = 0; 472 ctx.ack_m_retry = 0; 473 ctx.transaction_complete = false; 474 ctx.operation_succeeded = true; 475 476 if (request->payload.address_space == 477 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { 478 ctx.request.type = AUX_TRANSACTION_TYPE_DP; 479 ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE; 480 ctx.request.address = request->payload.address; 481 } else if (request->payload.address_space == 482 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) { 483 ctx.request.type = AUX_TRANSACTION_TYPE_I2C; 484 ctx.request.action = middle_of_transaction ? 485 I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : 486 I2CAUX_TRANSACTION_ACTION_I2C_WRITE; 487 ctx.request.address = request->payload.address >> 1; 488 } else { 489 /* in DAL2, there was no return in such case */ 490 BREAK_TO_DEBUGGER(); 491 return false; 492 } 493 494 ctx.request.delay = 0; 495 496 ctx.max_defer_retry = 497 (engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ? 498 engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER; 499 500 do { 501 ctx.request.data = ctx.buffer; 502 ctx.request.length = ctx.current_write_length; 503 504 process_write_request(engine, &ctx); 505 506 request->status = ctx.status; 507 508 if (ctx.operation_succeeded && !ctx.transaction_complete) 509 if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C) 510 msleep(engine->delay); 511 } while (ctx.operation_succeeded && !ctx.transaction_complete); 512 513 if (request->payload.address_space == 514 I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) { 515 DC_LOG_I2C_AUX("WRITE: addr:0x%x value:0x%x Result:%d", 516 request->payload.address, 517 request->payload.data[0], 518 ctx.operation_succeeded); 519 } 520 521 return ctx.operation_succeeded; 522 } 523 524 static bool end_of_transaction_command( 525 struct aux_engine *engine, 526 struct i2caux_transaction_request *request) 527 { 528 struct i2caux_transaction_request dummy_request; 529 uint8_t dummy_data; 530 531 /* [tcheng] We only need to send the stop (read with MOT = 0) 532 * for I2C-over-Aux, not native AUX */ 533 534 if (request->payload.address_space != 535 I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) 536 return false; 537 538 dummy_request.operation = request->operation; 539 dummy_request.payload.address_space = request->payload.address_space; 540 dummy_request.payload.address = request->payload.address; 541 542 /* 543 * Add a dummy byte due to some receiver quirk 544 * where one byte is sent along with MOT = 0. 545 * Ideally this should be 0. 546 */ 547 548 dummy_request.payload.length = 0; 549 dummy_request.payload.data = &dummy_data; 550 551 if (request->operation == I2CAUX_TRANSACTION_READ) 552 return read_command(engine, &dummy_request, false); 553 else 554 return write_command(engine, &dummy_request, false); 555 556 /* according Syed, it does not need now DoDummyMOT */ 557 } 558 559 bool dal_aux_engine_submit_request( 560 struct engine *engine, 561 struct i2caux_transaction_request *request, 562 bool middle_of_transaction) 563 { 564 struct aux_engine *aux_engine = FROM_ENGINE(engine); 565 566 bool result; 567 bool mot_used = true; 568 569 switch (request->operation) { 570 case I2CAUX_TRANSACTION_READ: 571 result = read_command(aux_engine, request, mot_used); 572 break; 573 case I2CAUX_TRANSACTION_WRITE: 574 result = write_command(aux_engine, request, mot_used); 575 break; 576 default: 577 result = false; 578 } 579 580 /* [tcheng] 581 * need to send stop for the last transaction to free up the AUX 582 * if the above command fails, this would be the last transaction */ 583 584 if (!middle_of_transaction || !result) 585 end_of_transaction_command(aux_engine, request); 586 587 /* mask AUX interrupt */ 588 589 return result; 590 } 591 592 void dal_aux_engine_construct( 593 struct aux_engine *engine, 594 struct dc_context *ctx) 595 { 596 dal_i2caux_construct_engine(&engine->base, ctx); 597 engine->delay = 0; 598 engine->max_defer_write_retry = 0; 599 } 600 601 void dal_aux_engine_destruct( 602 struct aux_engine *engine) 603 { 604 dal_i2caux_destruct_engine(&engine->base); 605 } 606