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 194 REG_UPDATE(AUX_INTERRUPT_CONTROL, AUX_SW_DONE_ACK, 1); 195 196 REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 0, 197 10, aux110->timeout_period/10); 198 199 /* set the delay and the number of bytes to write */ 200 201 /* The length include 202 * the 4 bit header and the 20 bit address 203 * (that is 3 byte). 204 * If the requested length is non zero this means 205 * an addition byte specifying the length is required. 206 */ 207 208 length = request->length ? 4 : 3; 209 if (is_write) 210 length += request->length; 211 212 REG_UPDATE_2(AUX_SW_CONTROL, 213 AUX_SW_START_DELAY, request->delay, 214 AUX_SW_WR_BYTES, length); 215 216 /* program action and address and payload data (if 'is_write') */ 217 value = REG_UPDATE_4(AUX_SW_DATA, 218 AUX_SW_INDEX, 0, 219 AUX_SW_DATA_RW, 0, 220 AUX_SW_AUTOINCREMENT_DISABLE, 1, 221 AUX_SW_DATA, COMPOSE_AUX_SW_DATA_16_20(request->action, request->address)); 222 223 value = REG_SET_2(AUX_SW_DATA, value, 224 AUX_SW_AUTOINCREMENT_DISABLE, 0, 225 AUX_SW_DATA, COMPOSE_AUX_SW_DATA_8_15(request->address)); 226 227 value = REG_SET(AUX_SW_DATA, value, 228 AUX_SW_DATA, COMPOSE_AUX_SW_DATA_0_7(request->address)); 229 230 if (request->length) { 231 value = REG_SET(AUX_SW_DATA, value, 232 AUX_SW_DATA, request->length - 1); 233 } 234 235 if (is_write) { 236 /* Load the HW buffer with the Data to be sent. 237 * This is relevant for write operation. 238 * For read, the data recived data will be 239 * processed in process_channel_reply(). 240 */ 241 uint32_t i = 0; 242 243 while (i < request->length) { 244 value = REG_SET(AUX_SW_DATA, value, 245 AUX_SW_DATA, request->data[i]); 246 247 ++i; 248 } 249 } 250 251 REG_UPDATE(AUX_SW_CONTROL, AUX_SW_GO, 1); 252 } 253 254 static int read_channel_reply(struct dce_aux *engine, uint32_t size, 255 uint8_t *buffer, uint8_t *reply_result, 256 uint32_t *sw_status) 257 { 258 struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); 259 uint32_t bytes_replied; 260 uint32_t reply_result_32; 261 262 *sw_status = REG_GET(AUX_SW_STATUS, AUX_SW_REPLY_BYTE_COUNT, 263 &bytes_replied); 264 265 /* In case HPD is LOW, exit AUX transaction */ 266 if ((*sw_status & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) 267 return -1; 268 269 /* Need at least the status byte */ 270 if (!bytes_replied) 271 return -1; 272 273 REG_UPDATE_1BY1_3(AUX_SW_DATA, 274 AUX_SW_INDEX, 0, 275 AUX_SW_AUTOINCREMENT_DISABLE, 1, 276 AUX_SW_DATA_RW, 1); 277 278 REG_GET(AUX_SW_DATA, AUX_SW_DATA, &reply_result_32); 279 reply_result_32 = reply_result_32 >> 4; 280 if (reply_result != NULL) 281 *reply_result = (uint8_t)reply_result_32; 282 283 if (reply_result_32 == 0) { /* ACK */ 284 uint32_t i = 0; 285 286 /* First byte was already used to get the command status */ 287 --bytes_replied; 288 289 /* Do not overflow buffer */ 290 if (bytes_replied > size) 291 return -1; 292 293 while (i < bytes_replied) { 294 uint32_t aux_sw_data_val; 295 296 REG_GET(AUX_SW_DATA, AUX_SW_DATA, &aux_sw_data_val); 297 buffer[i] = aux_sw_data_val; 298 ++i; 299 } 300 301 return i; 302 } 303 304 return 0; 305 } 306 307 static enum aux_channel_operation_result get_channel_status( 308 struct dce_aux *engine, 309 uint8_t *returned_bytes) 310 { 311 struct aux_engine_dce110 *aux110 = FROM_AUX_ENGINE(engine); 312 313 uint32_t value; 314 315 if (returned_bytes == NULL) { 316 /*caller pass NULL pointer*/ 317 ASSERT_CRITICAL(false); 318 return AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN; 319 } 320 *returned_bytes = 0; 321 322 /* poll to make sure that SW_DONE is asserted */ 323 value = REG_WAIT(AUX_SW_STATUS, AUX_SW_DONE, 1, 324 10, aux110->timeout_period/10); 325 326 /* in case HPD is LOW, exit AUX transaction */ 327 if ((value & AUX_SW_STATUS__AUX_SW_HPD_DISCON_MASK)) 328 return AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON; 329 330 /* Note that the following bits are set in 'status.bits' 331 * during CTS 4.2.1.2 (FW 3.3.1): 332 * AUX_SW_RX_MIN_COUNT_VIOL, AUX_SW_RX_INVALID_STOP, 333 * AUX_SW_RX_RECV_NO_DET, AUX_SW_RX_RECV_INVALID_H. 334 * 335 * AUX_SW_RX_MIN_COUNT_VIOL is an internal, 336 * HW debugging bit and should be ignored. 337 */ 338 if (value & AUX_SW_STATUS__AUX_SW_DONE_MASK) { 339 if ((value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_STATE_MASK) || 340 (value & AUX_SW_STATUS__AUX_SW_RX_TIMEOUT_MASK)) 341 return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; 342 343 else if ((value & AUX_SW_STATUS__AUX_SW_RX_INVALID_STOP_MASK) || 344 (value & AUX_SW_STATUS__AUX_SW_RX_RECV_NO_DET_MASK) || 345 (value & 346 AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_H_MASK) || 347 (value & AUX_SW_STATUS__AUX_SW_RX_RECV_INVALID_L_MASK)) 348 return AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; 349 350 *returned_bytes = get_reg_field_value(value, 351 AUX_SW_STATUS, 352 AUX_SW_REPLY_BYTE_COUNT); 353 354 if (*returned_bytes == 0) 355 return 356 AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY; 357 else { 358 *returned_bytes -= 1; 359 return AUX_CHANNEL_OPERATION_SUCCEEDED; 360 } 361 } else { 362 /*time_elapsed >= aux_engine->timeout_period 363 * AUX_SW_STATUS__AUX_SW_HPD_DISCON = at this point 364 */ 365 ASSERT_CRITICAL(false); 366 return AUX_CHANNEL_OPERATION_FAILED_TIMEOUT; 367 } 368 } 369 370 enum i2caux_engine_type get_engine_type( 371 const struct dce_aux *engine) 372 { 373 return I2CAUX_ENGINE_TYPE_AUX; 374 } 375 376 static bool acquire( 377 struct dce_aux *engine, 378 struct ddc *ddc) 379 { 380 381 enum gpio_result result; 382 383 if (!is_engine_available(engine)) 384 return false; 385 386 result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, 387 GPIO_DDC_CONFIG_TYPE_MODE_AUX); 388 389 if (result != GPIO_RESULT_OK) 390 return false; 391 392 if (!acquire_engine(engine)) { 393 dal_ddc_close(ddc); 394 return false; 395 } 396 397 engine->ddc = ddc; 398 399 return true; 400 } 401 402 void dce110_engine_destroy(struct dce_aux **engine) 403 { 404 405 struct aux_engine_dce110 *engine110 = FROM_AUX_ENGINE(*engine); 406 407 kfree(engine110); 408 *engine = NULL; 409 410 } 411 struct dce_aux *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine110, 412 struct dc_context *ctx, 413 uint32_t inst, 414 uint32_t timeout_period, 415 const struct dce110_aux_registers *regs) 416 { 417 aux_engine110->base.ddc = NULL; 418 aux_engine110->base.ctx = ctx; 419 aux_engine110->base.delay = 0; 420 aux_engine110->base.max_defer_write_retry = 0; 421 aux_engine110->base.inst = inst; 422 aux_engine110->timeout_period = timeout_period; 423 aux_engine110->regs = regs; 424 425 return &aux_engine110->base; 426 } 427 428 static enum i2caux_transaction_action i2caux_action_from_payload(struct aux_payload *payload) 429 { 430 if (payload->i2c_over_aux) { 431 if (payload->write) { 432 if (payload->mot) 433 return I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT; 434 return I2CAUX_TRANSACTION_ACTION_I2C_WRITE; 435 } 436 if (payload->mot) 437 return I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT; 438 return I2CAUX_TRANSACTION_ACTION_I2C_READ; 439 } 440 if (payload->write) 441 return I2CAUX_TRANSACTION_ACTION_DP_WRITE; 442 return I2CAUX_TRANSACTION_ACTION_DP_READ; 443 } 444 445 int dce_aux_transfer(struct ddc_service *ddc, 446 struct aux_payload *payload) 447 { 448 struct ddc *ddc_pin = ddc->ddc_pin; 449 struct dce_aux *aux_engine; 450 enum aux_channel_operation_result operation_result; 451 struct aux_request_transaction_data aux_req; 452 struct aux_reply_transaction_data aux_rep; 453 uint8_t returned_bytes = 0; 454 int res = -1; 455 uint32_t status; 456 457 memset(&aux_req, 0, sizeof(aux_req)); 458 memset(&aux_rep, 0, sizeof(aux_rep)); 459 460 aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; 461 acquire(aux_engine, ddc_pin); 462 463 if (payload->i2c_over_aux) 464 aux_req.type = AUX_TRANSACTION_TYPE_I2C; 465 else 466 aux_req.type = AUX_TRANSACTION_TYPE_DP; 467 468 aux_req.action = i2caux_action_from_payload(payload); 469 470 aux_req.address = payload->address; 471 aux_req.delay = payload->defer_delay * 10; 472 aux_req.length = payload->length; 473 aux_req.data = payload->data; 474 475 submit_channel_request(aux_engine, &aux_req); 476 operation_result = get_channel_status(aux_engine, &returned_bytes); 477 478 switch (operation_result) { 479 case AUX_CHANNEL_OPERATION_SUCCEEDED: 480 res = read_channel_reply(aux_engine, payload->length, 481 payload->data, payload->reply, 482 &status); 483 break; 484 case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: 485 res = 0; 486 break; 487 case AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN: 488 case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: 489 case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: 490 res = -1; 491 break; 492 } 493 release_engine(aux_engine); 494 return res; 495 } 496 497 #define AUX_RETRY_MAX 7 498 499 bool dce_aux_transfer_with_retries(struct ddc_service *ddc, 500 struct aux_payload *payload) 501 { 502 int i, ret = 0; 503 uint8_t reply; 504 bool payload_reply = true; 505 506 if (!payload->reply) { 507 payload_reply = false; 508 payload->reply = &reply; 509 } 510 511 for (i = 0; i < AUX_RETRY_MAX; i++) { 512 ret = dce_aux_transfer(ddc, payload); 513 514 if (ret >= 0) { 515 if (*payload->reply == 0) { 516 if (!payload_reply) 517 payload->reply = NULL; 518 return true; 519 } 520 } 521 522 udelay(1000); 523 } 524 return false; 525 } 526