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 #include "core_types.h" 28 #include "dce_aux.h" 29 #include "dce/dce_11_0_sh_mask.h" 30 31 #define CTX \ 32 aux110->base.ctx 33 #define REG(reg_name)\ 34 (aux110->regs->reg_name) 35 36 #define DC_LOGGER \ 37 engine->ctx->logger 38 39 #include "reg_helper.h" 40 41 #define FROM_AUX_ENGINE(ptr) \ 42 container_of((ptr), struct aux_engine_dce110, base) 43 44 #define FROM_ENGINE(ptr) \ 45 FROM_AUX_ENGINE(container_of((ptr), struct dce_aux, base)) 46 47 #define FROM_AUX_ENGINE_ENGINE(ptr) \ 48 container_of((ptr), struct dce_aux, base) 49 enum { 50 AUX_INVALID_REPLY_RETRY_COUNTER = 1, 51 AUX_TIMED_OUT_RETRY_COUNTER = 2, 52 AUX_DEFER_RETRY_COUNTER = 6 53 }; 54 static void release_engine( 55 struct dce_aux *engine) 56 { 57 struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); 58 59 dal_ddc_close(engine->ddc); 60 61 engine->ddc = NULL; 62 63 REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_DONE_USING_AUX_REG, 1); 64 } 65 66 #define SW_CAN_ACCESS_AUX 1 67 #define DMCU_CAN_ACCESS_AUX 2 68 69 static bool is_engine_available( 70 struct dce_aux *engine) 71 { 72 struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); 73 74 uint32_t value = REG_READ(AUX_ARB_CONTROL); 75 uint32_t field = get_reg_field_value( 76 value, 77 AUX_ARB_CONTROL, 78 AUX_REG_RW_CNTL_STATUS); 79 80 return (field != DMCU_CAN_ACCESS_AUX); 81 } 82 static bool acquire_engine( 83 struct dce_aux *engine) 84 { 85 struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); 86 87 uint32_t value = REG_READ(AUX_ARB_CONTROL); 88 uint32_t field = get_reg_field_value( 89 value, 90 AUX_ARB_CONTROL, 91 AUX_REG_RW_CNTL_STATUS); 92 if (field == DMCU_CAN_ACCESS_AUX) 93 return false; 94 /* enable AUX before request SW to access AUX */ 95 value = REG_READ(AUX_CONTROL); 96 field = get_reg_field_value(value, 97 AUX_CONTROL, 98 AUX_EN); 99 100 if (field == 0) { 101 set_reg_field_value( 102 value, 103 1, 104 AUX_CONTROL, 105 AUX_EN); 106 107 if (REG(AUX_RESET_MASK)) { 108 /*DP_AUX block as part of the enable sequence*/ 109 set_reg_field_value( 110 value, 111 1, 112 AUX_CONTROL, 113 AUX_RESET); 114 } 115 116 REG_WRITE(AUX_CONTROL, value); 117 118 if (REG(AUX_RESET_MASK)) { 119 /*poll HW to make sure reset it done*/ 120 121 REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 1, 122 1, 11); 123 124 set_reg_field_value( 125 value, 126 0, 127 AUX_CONTROL, 128 AUX_RESET); 129 130 REG_WRITE(AUX_CONTROL, value); 131 132 REG_WAIT(AUX_CONTROL, AUX_RESET_DONE, 0, 133 1, 11); 134 } 135 } /*if (field)*/ 136 137 /* request SW to access AUX */ 138 REG_UPDATE(AUX_ARB_CONTROL, AUX_SW_USE_AUX_REG_REQ, 1); 139 140 value = REG_READ(AUX_ARB_CONTROL); 141 field = get_reg_field_value( 142 value, 143 AUX_ARB_CONTROL, 144 AUX_REG_RW_CNTL_STATUS); 145 146 return (field == SW_CAN_ACCESS_AUX); 147 } 148 149 #define COMPOSE_AUX_SW_DATA_16_20(command, address) \ 150 ((command) | ((0xF0000 & (address)) >> 16)) 151 152 #define COMPOSE_AUX_SW_DATA_8_15(address) \ 153 ((0xFF00 & (address)) >> 8) 154 155 #define COMPOSE_AUX_SW_DATA_0_7(address) \ 156 (0xFF & (address)) 157 158 static void submit_channel_request( 159 struct dce_aux *engine, 160 struct aux_request_transaction_data *request) 161 { 162 struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); 163 uint32_t value; 164 uint32_t length; 165 166 bool is_write = 167 ((request->type == AUX_TRANSACTION_TYPE_DP) && 168 (request->action == I2CAUX_TRANSACTION_ACTION_DP_WRITE)) || 169 ((request->type == AUX_TRANSACTION_TYPE_I2C) && 170 ((request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE) || 171 (request->action == I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT))); 172 if (REG(AUXN_IMPCAL)) { 173 /* clear_aux_error */ 174 REG_UPDATE_SEQ(AUXN_IMPCAL, AUXN_CALOUT_ERROR_AK, 175 1, 176 0); 177 178 REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_CALOUT_ERROR_AK, 179 1, 180 0); 181 182 /* force_default_calibrate */ 183 REG_UPDATE_1BY1_2(AUXN_IMPCAL, 184 AUXN_IMPCAL_ENABLE, 1, 185 AUXN_IMPCAL_OVERRIDE_ENABLE, 0); 186 187 /* bug? why AUXN update EN and OVERRIDE_EN 1 by 1 while AUX P toggles OVERRIDE? */ 188 189 REG_UPDATE_SEQ(AUXP_IMPCAL, AUXP_IMPCAL_OVERRIDE_ENABLE, 190 1, 191 0); 192 } 193 /* set the delay and the number of bytes to write */ 194 195 /* The length include 196 * the 4 bit header and the 20 bit address 197 * (that is 3 byte). 198 * If the requested length is non zero this means 199 * an addition byte specifying the length is required. 200 */ 201 202 length = request->length ? 4 : 3; 203 if (is_write) 204 length += request->length; 205 206 REG_UPDATE_2(AUX_SW_CONTROL, 207 AUX_SW_START_DELAY, request->delay, 208 AUX_SW_WR_BYTES, length); 209 210 /* program action and address and payload data (if 'is_write') */ 211 value = REG_UPDATE_4(AUX_SW_DATA, 212 AUX_SW_INDEX, 0, 213 AUX_SW_DATA_RW, 0, 214 AUX_SW_AUTOINCREMENT_DISABLE, 1, 215 AUX_SW_DATA, COMPOSE_AUX_SW_DATA_16_20(request->action, request->address)); 216 217 value = REG_SET_2(AUX_SW_DATA, value, 218 AUX_SW_AUTOINCREMENT_DISABLE, 0, 219 AUX_SW_DATA, COMPOSE_AUX_SW_DATA_8_15(request->address)); 220 221 value = REG_SET(AUX_SW_DATA, value, 222 AUX_SW_DATA, COMPOSE_AUX_SW_DATA_0_7(request->address)); 223 224 if (request->length) { 225 value = REG_SET(AUX_SW_DATA, value, 226 AUX_SW_DATA, request->length - 1); 227 } 228 229 if (is_write) { 230 /* Load the HW buffer with the Data to be sent. 231 * This is relevant for write operation. 232 * For read, the data recived data will be 233 * processed in process_channel_reply(). 234 */ 235 uint32_t i = 0; 236 237 while (i < request->length) { 238 value = REG_SET(AUX_SW_DATA, value, 239 AUX_SW_DATA, request->data[i]); 240 241 ++i; 242 } 243 } 244 245 REG_UPDATE(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, 1); 246 REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0, 247 10, aux110->timeout_period/10); 248 REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1); 249 } 250 251 static int read_channel_reply(struct dce_aux *engine, uint32_t size, 252 uint8_t *buffer, uint8_t *reply_result, 253 uint32_t *sw_status) 254 { 255 struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); 256 uint32_t bytes_replied; 257 uint32_t reply_result_32; 258 259 *sw_status = REG_GET(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, 260 &bytes_replied); 261 262 /* In case HPD is LOW, exit AUX transaction */ 263 if ((*sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) 264 return -1; 265 266 /* Need at least the status byte */ 267 if (!bytes_replied) 268 return -1; 269 270 REG_UPDATE_1BY1_3(AUX_SW_DATA, 271 AUX_SW_INDEX, 0, 272 AUX_SW_AUTOINCREMENT_DISABLE, 1, 273 AUX_SW_DATA_RW, 1); 274 275 REG_GET(AUX_SW_DATA, AUX_SW_DATA, &reply_result_32); 276 reply_result_32 = reply_result_32 >> 4; 277 if (reply_result != NULL) 278 *reply_result = (uint8_t)reply_result_32; 279 280 if (reply_result_32 == 0) { /* ACK */ 281 uint32_t i = 0; 282 283 /* First byte was already used to get the command status */ 284 --bytes_replied; 285 286 /* Do not overflow buffer */ 287 if (bytes_replied > size) 288 return -1; 289 290 while (i < bytes_replied) { 291 uint32_t aux_sw_data_val; 292 293 REG_GET(AUX_SW_DATA, AUX_SW_DATA, &aux_sw_data_val); 294 buffer[i] = aux_sw_data_val; 295 ++i; 296 } 297 298 return i; 299 } 300 301 return 0; 302 } 303 304 static enum aux_channel_operation_result get_channel_status( 305 struct dce_aux *engine, 306 uint8_t *returned_bytes) 307 { 308 struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); 309 310 uint32_t value; 311 312 if (returned_bytes == NULL) { 313 /*caller pass NULL pointer*/ 314 ASSERT_CRITICAL(false); 315 return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN; 316 } 317 *returned_bytes = 0; 318 319 /* poll to make sure that SW_DONE is asserted */ 320 value = REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 1, 321 10, aux110->timeout_period/10); 322 323 /* in case HPD is LOW, exit AUX transaction */ 324 if ((value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) 325 return AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON; 326 327 /* Note that the following bits are set in 'status.bits' 328 * during CTS 4.2.1.2 (FW 3.3.1): 329 * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, 330 * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. 331 * 332 * AUX_SW_RX_MIN_COUNT_VIOL is an internal, 333 * HW debugging bit and should be ignored. 334 */ 335 if (value & AUX_SW_STATUS__AUX_SW_DONE_MASK) { 336 if ((value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_STATE_MASK) || 337 (value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_MASK)) 338 return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; 339 340 else if ((value & AUX_SW_STATUS__AUX_SW_RX_INVALID_STOP_MASK) || 341 (value & AUX_SW_STATUS__AUX_SW_RX_RECV_NO_DET_MASK) || 342 (value & 343 AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_H_MASK) || 344 (value & AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_L_MASK)) 345 return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; 346 347 *returned_bytes = get_reg_field_value(value, 348 AUX_SW_STATUS, 349 AUX_SW_REPLY_BYTE_COUNT); 350 351 if (*returned_bytes == 0) 352 return 353 AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; 354 else { 355 *returned_bytes -= 1; 356 return AUX_CHANNEL_OPERATION_SUCCEEDED; 357 } 358 } else { 359 /*time_elapsed >= aux_engine->timeout_period 360 * AUX_SW_STATUS__AUX_SW_HPD_DISCON = at this point 361 */ 362 ASSERT_CRITICAL(false); 363 return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; 364 } 365 } 366 367 enum i2caux_engine_type get_engine_type( 368 const struct dce_aux *engine) 369 { 370 return I2CAUX_ENGINE_TYPE_AUX; 371 } 372 373 static bool acquire( 374 struct dce_aux *engine, 375 struct ddc *ddc) 376 { 377 378 enum gpio_result result; 379 380 if (!is_engine_available(engine)) 381 return false; 382 383 result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, 384 GPIO_DDC_CONFIG_TYPE_MODE_AUX); 385 386 if (result != GPIO_RESULT_OK) 387 return false; 388 389 if (!acquire_engine(engine)) { 390 dal_ddc_close(ddc); 391 return false; 392 } 393 394 engine->ddc = ddc; 395 396 return true; 397 } 398 399 void dce110_engine_destroy(struct dce_aux **engine) 400 { 401 402 struct aux_engine_dce110 *engine110 = FROM_AUX_ENGINE(*engine); 403 404 kfree(engine110); 405 *engine = NULL; 406 407 } 408 struct dce_aux *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine110, 409 struct dc_context *ctx, 410 uint32_t inst, 411 uint32_t timeout_period, 412 const struct dce110_aux_registers *regs) 413 { 414 aux_engine110->base.ddc = NULL; 415 aux_engine110->base.ctx = ctx; 416 aux_engine110->base.delay = 0; 417 aux_engine110->base.max_defer_write_retry = 0; 418 aux_engine110->base.inst = inst; 419 aux_engine110->timeout_period = timeout_period; 420 aux_engine110->regs = regs; 421 422 return &aux_engine110->base; 423 } 424 425 static enum i2caux_transaction_action i2caux_action_from_payload(struct aux_payload *payload) 426 { 427 if (payload->i2c_over_aux) { 428 if (payload->write) { 429 if (payload->mot) 430 return I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT; 431 return I2CAUX_TRANSACTION_ACTION_I2C_WRITE; 432 } 433 if (payload->mot) 434 return I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT; 435 return I2CAUX_TRANSACTION_ACTION_I2C_READ; 436 } 437 if (payload->write) 438 return I2CAUX_TRANSACTION_ACTION_DP_WRITE; 439 return I2CAUX_TRANSACTION_ACTION_DP_READ; 440 } 441 442 int dce_aux_transfer(struct ddc_service *ddc, 443 struct aux_payload *payload) 444 { 445 struct ddc *ddc_pin = ddc->ddc_pin; 446 struct dce_aux *aux_engine; 447 enum aux_channel_operation_result operation_result; 448 struct aux_request_transaction_data aux_req; 449 struct aux_reply_transaction_data aux_rep; 450 uint8_t returned_bytes = 0; 451 int res = -1; 452 uint32_t status; 453 454 memset(&aux_req, 0, sizeof(aux_req)); 455 memset(&aux_rep, 0, sizeof(aux_rep)); 456 457 aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; 458 acquire(aux_engine, ddc_pin); 459 460 if (payload->i2c_over_aux) 461 aux_req.type = AUX_TRANSACTION_TYPE_I2C; 462 else 463 aux_req.type = AUX_TRANSACTION_TYPE_DP; 464 465 aux_req.action = i2caux_action_from_payload(payload); 466 467 aux_req.address = payload->address; 468 aux_req.delay = payload->defer_delay * 10; 469 aux_req.length = payload->length; 470 aux_req.data = payload->data; 471 472 submit_channel_request(aux_engine, &aux_req); 473 operation_result = get_channel_status(aux_engine, &returned_bytes); 474 475 switch (operation_result) { 476 case AUX_CHANNEL_OPERATION_SUCCEEDED: 477 res = read_channel_reply(aux_engine, payload->length, 478 payload->data, payload->reply, 479 &status); 480 break; 481 case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: 482 res = 0; 483 break; 484 case AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN: 485 case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: 486 case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: 487 res = -1; 488 break; 489 } 490 release_engine(aux_engine); 491 return res; 492 } 493 494 #define AUX_RETRY_MAX 7 495 496 bool dce_aux_transfer_with_retries(struct ddc_service *ddc, 497 struct aux_payload *payload) 498 { 499 int i, ret = 0; 500 uint8_t reply; 501 bool payload_reply = true; 502 503 if (!payload->reply) { 504 payload_reply = false; 505 payload->reply = &reply; 506 } 507 508 for (i = 0; i < AUX_RETRY_MAX; i++) { 509 ret = dce_aux_transfer(ddc, payload); 510 511 if (ret >= 0) { 512 if (*payload->reply == 0) { 513 if (!payload_reply) 514 payload->reply = NULL; 515 return true; 516 } 517 } 518 519 msleep(1); 520 } 521 return false; 522 } 523