123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- /*
- * Copyright 2012-15 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- * Authors: AMD
- *
- */
- #include "dm_services.h"
- /*
- * Pre-requisites: headers required by header of this unit
- */
- #include "include/i2caux_interface.h"
- #include "engine.h"
- /*
- * Header of this unit
- */
- #include "aux_engine.h"
- /*
- * Post-requisites: headers required by this unit
- */
- #include "include/link_service_types.h"
- /*
- * This unit
- */
- enum {
- AUX_INVALID_REPLY_RETRY_COUNTER = 1,
- AUX_TIMED_OUT_RETRY_COUNTER = 2,
- AUX_DEFER_RETRY_COUNTER = 6
- };
- #define FROM_ENGINE(ptr) \
- container_of((ptr), struct aux_engine, base)
- #define DC_LOGGER \
- engine->base.ctx->logger
- enum i2caux_engine_type dal_aux_engine_get_engine_type(
- const struct engine *engine)
- {
- return I2CAUX_ENGINE_TYPE_AUX;
- }
- bool dal_aux_engine_acquire(
- struct engine *engine,
- struct ddc *ddc)
- {
- struct aux_engine *aux_engine = FROM_ENGINE(engine);
- enum gpio_result result;
- if (aux_engine->funcs->is_engine_available) {
- /*check whether SW could use the engine*/
- if (!aux_engine->funcs->is_engine_available(aux_engine)) {
- return false;
- }
- }
- result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE,
- GPIO_DDC_CONFIG_TYPE_MODE_AUX);
- if (result != GPIO_RESULT_OK)
- return false;
- if (!aux_engine->funcs->acquire_engine(aux_engine)) {
- dal_ddc_close(ddc);
- return false;
- }
- engine->ddc = ddc;
- return true;
- }
- struct read_command_context {
- uint8_t *buffer;
- uint32_t current_read_length;
- uint32_t offset;
- enum i2caux_transaction_status status;
- struct aux_request_transaction_data request;
- struct aux_reply_transaction_data reply;
- uint8_t returned_byte;
- uint32_t timed_out_retry_aux;
- uint32_t invalid_reply_retry_aux;
- uint32_t defer_retry_aux;
- uint32_t defer_retry_i2c;
- uint32_t invalid_reply_retry_aux_on_ack;
- bool transaction_complete;
- bool operation_succeeded;
- };
- static void process_read_reply(
- struct aux_engine *engine,
- struct read_command_context *ctx)
- {
- engine->funcs->process_channel_reply(engine, &ctx->reply);
- switch (ctx->reply.status) {
- case AUX_TRANSACTION_REPLY_AUX_ACK:
- ctx->defer_retry_aux = 0;
- if (ctx->returned_byte > ctx->current_read_length) {
- ctx->status =
- I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
- ctx->operation_succeeded = false;
- } else {
- ctx->current_read_length = ctx->returned_byte;
- ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
- ctx->transaction_complete = true;
- ctx->operation_succeeded = true;
- }
- break;
- case AUX_TRANSACTION_REPLY_AUX_NACK:
- ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
- ctx->operation_succeeded = false;
- break;
- case AUX_TRANSACTION_REPLY_AUX_DEFER:
- ++ctx->defer_retry_aux;
- if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) {
- ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
- ctx->operation_succeeded = false;
- }
- break;
- case AUX_TRANSACTION_REPLY_I2C_DEFER:
- ctx->defer_retry_aux = 0;
- ++ctx->defer_retry_i2c;
- if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) {
- ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
- ctx->operation_succeeded = false;
- }
- break;
- default:
- ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
- ctx->operation_succeeded = false;
- }
- }
- static void process_read_request(
- struct aux_engine *engine,
- struct read_command_context *ctx)
- {
- enum aux_channel_operation_result operation_result;
- engine->funcs->submit_channel_request(engine, &ctx->request);
- operation_result = engine->funcs->get_channel_status(
- engine, &ctx->returned_byte);
- switch (operation_result) {
- case AUX_CHANNEL_OPERATION_SUCCEEDED:
- if (ctx->returned_byte > ctx->current_read_length) {
- ctx->status =
- I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
- ctx->operation_succeeded = false;
- } else {
- ctx->timed_out_retry_aux = 0;
- ctx->invalid_reply_retry_aux = 0;
- ctx->reply.length = ctx->returned_byte;
- ctx->reply.data = ctx->buffer;
- process_read_reply(engine, ctx);
- }
- break;
- case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
- ++ctx->invalid_reply_retry_aux;
- if (ctx->invalid_reply_retry_aux >
- AUX_INVALID_REPLY_RETRY_COUNTER) {
- ctx->status =
- I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
- ctx->operation_succeeded = false;
- } else
- udelay(400);
- break;
- case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
- ++ctx->timed_out_retry_aux;
- if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
- ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
- ctx->operation_succeeded = false;
- } else {
- /* DP 1.2a, table 2-58:
- * "S3: AUX Request CMD PENDING:
- * retry 3 times, with 400usec wait on each"
- * The HW timeout is set to 550usec,
- * so we should not wait here */
- }
- break;
- default:
- ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
- ctx->operation_succeeded = false;
- }
- }
- static bool read_command(
- struct aux_engine *engine,
- struct i2caux_transaction_request *request,
- bool middle_of_transaction)
- {
- struct read_command_context ctx;
- ctx.buffer = request->payload.data;
- ctx.current_read_length = request->payload.length;
- ctx.offset = 0;
- ctx.timed_out_retry_aux = 0;
- ctx.invalid_reply_retry_aux = 0;
- ctx.defer_retry_aux = 0;
- ctx.defer_retry_i2c = 0;
- ctx.invalid_reply_retry_aux_on_ack = 0;
- ctx.transaction_complete = false;
- ctx.operation_succeeded = true;
- if (request->payload.address_space ==
- I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
- ctx.request.type = AUX_TRANSACTION_TYPE_DP;
- ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ;
- ctx.request.address = request->payload.address;
- } else if (request->payload.address_space ==
- I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
- ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
- ctx.request.action = middle_of_transaction ?
- I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT :
- I2CAUX_TRANSACTION_ACTION_I2C_READ;
- ctx.request.address = request->payload.address >> 1;
- } else {
- /* in DAL2, there was no return in such case */
- BREAK_TO_DEBUGGER();
- return false;
- }
- ctx.request.delay = 0;
- do {
- memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length);
- ctx.request.data = ctx.buffer + ctx.offset;
- ctx.request.length = ctx.current_read_length;
- process_read_request(engine, &ctx);
- request->status = ctx.status;
- if (ctx.operation_succeeded && !ctx.transaction_complete)
- if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
- msleep(engine->delay);
- } while (ctx.operation_succeeded && !ctx.transaction_complete);
- if (request->payload.address_space ==
- I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
- DC_LOG_I2C_AUX("READ: addr:0x%x value:0x%x Result:%d",
- request->payload.address,
- request->payload.data[0],
- ctx.operation_succeeded);
- }
- request->payload.length = ctx.reply.length;
- return ctx.operation_succeeded;
- }
- struct write_command_context {
- bool mot;
- uint8_t *buffer;
- uint32_t current_write_length;
- enum i2caux_transaction_status status;
- struct aux_request_transaction_data request;
- struct aux_reply_transaction_data reply;
- uint8_t returned_byte;
- uint32_t timed_out_retry_aux;
- uint32_t invalid_reply_retry_aux;
- uint32_t defer_retry_aux;
- uint32_t defer_retry_i2c;
- uint32_t max_defer_retry;
- uint32_t ack_m_retry;
- uint8_t reply_data[DEFAULT_AUX_MAX_DATA_SIZE];
- bool transaction_complete;
- bool operation_succeeded;
- };
- static void process_write_reply(
- struct aux_engine *engine,
- struct write_command_context *ctx)
- {
- engine->funcs->process_channel_reply(engine, &ctx->reply);
- switch (ctx->reply.status) {
- case AUX_TRANSACTION_REPLY_AUX_ACK:
- ctx->operation_succeeded = true;
- if (ctx->returned_byte) {
- ctx->request.action = ctx->mot ?
- I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
- I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
- ctx->current_write_length = 0;
- ++ctx->ack_m_retry;
- if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) {
- ctx->status =
- I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
- ctx->operation_succeeded = false;
- } else
- udelay(300);
- } else {
- ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
- ctx->defer_retry_aux = 0;
- ctx->ack_m_retry = 0;
- ctx->transaction_complete = true;
- }
- break;
- case AUX_TRANSACTION_REPLY_AUX_NACK:
- ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
- ctx->operation_succeeded = false;
- break;
- case AUX_TRANSACTION_REPLY_AUX_DEFER:
- ++ctx->defer_retry_aux;
- if (ctx->defer_retry_aux > ctx->max_defer_retry) {
- ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
- ctx->operation_succeeded = false;
- }
- break;
- case AUX_TRANSACTION_REPLY_I2C_DEFER:
- ctx->defer_retry_aux = 0;
- ctx->current_write_length = 0;
- ctx->request.action = ctx->mot ?
- I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
- I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
- ++ctx->defer_retry_i2c;
- if (ctx->defer_retry_i2c > ctx->max_defer_retry) {
- ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
- ctx->operation_succeeded = false;
- }
- break;
- default:
- ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
- ctx->operation_succeeded = false;
- }
- }
- static void process_write_request(
- struct aux_engine *engine,
- struct write_command_context *ctx)
- {
- enum aux_channel_operation_result operation_result;
- engine->funcs->submit_channel_request(engine, &ctx->request);
- operation_result = engine->funcs->get_channel_status(
- engine, &ctx->returned_byte);
- switch (operation_result) {
- case AUX_CHANNEL_OPERATION_SUCCEEDED:
- ctx->timed_out_retry_aux = 0;
- ctx->invalid_reply_retry_aux = 0;
- ctx->reply.length = ctx->returned_byte;
- ctx->reply.data = ctx->reply_data;
- process_write_reply(engine, ctx);
- break;
- case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
- ++ctx->invalid_reply_retry_aux;
- if (ctx->invalid_reply_retry_aux >
- AUX_INVALID_REPLY_RETRY_COUNTER) {
- ctx->status =
- I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
- ctx->operation_succeeded = false;
- } else
- udelay(400);
- break;
- case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
- ++ctx->timed_out_retry_aux;
- if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
- ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
- ctx->operation_succeeded = false;
- } else {
- /* DP 1.2a, table 2-58:
- * "S3: AUX Request CMD PENDING:
- * retry 3 times, with 400usec wait on each"
- * The HW timeout is set to 550usec,
- * so we should not wait here */
- }
- break;
- default:
- ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
- ctx->operation_succeeded = false;
- }
- }
- static bool write_command(
- struct aux_engine *engine,
- struct i2caux_transaction_request *request,
- bool middle_of_transaction)
- {
- struct write_command_context ctx;
- ctx.mot = middle_of_transaction;
- ctx.buffer = request->payload.data;
- ctx.current_write_length = request->payload.length;
- ctx.timed_out_retry_aux = 0;
- ctx.invalid_reply_retry_aux = 0;
- ctx.defer_retry_aux = 0;
- ctx.defer_retry_i2c = 0;
- ctx.ack_m_retry = 0;
- ctx.transaction_complete = false;
- ctx.operation_succeeded = true;
- if (request->payload.address_space ==
- I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
- ctx.request.type = AUX_TRANSACTION_TYPE_DP;
- ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE;
- ctx.request.address = request->payload.address;
- } else if (request->payload.address_space ==
- I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
- ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
- ctx.request.action = middle_of_transaction ?
- I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT :
- I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
- ctx.request.address = request->payload.address >> 1;
- } else {
- /* in DAL2, there was no return in such case */
- BREAK_TO_DEBUGGER();
- return false;
- }
- ctx.request.delay = 0;
- ctx.max_defer_retry =
- (engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ?
- engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER;
- do {
- ctx.request.data = ctx.buffer;
- ctx.request.length = ctx.current_write_length;
- process_write_request(engine, &ctx);
- request->status = ctx.status;
- if (ctx.operation_succeeded && !ctx.transaction_complete)
- if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
- msleep(engine->delay);
- } while (ctx.operation_succeeded && !ctx.transaction_complete);
- if (request->payload.address_space ==
- I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
- DC_LOG_I2C_AUX("WRITE: addr:0x%x value:0x%x Result:%d",
- request->payload.address,
- request->payload.data[0],
- ctx.operation_succeeded);
- }
- return ctx.operation_succeeded;
- }
- static bool end_of_transaction_command(
- struct aux_engine *engine,
- struct i2caux_transaction_request *request)
- {
- struct i2caux_transaction_request dummy_request;
- uint8_t dummy_data;
- /* [tcheng] We only need to send the stop (read with MOT = 0)
- * for I2C-over-Aux, not native AUX */
- if (request->payload.address_space !=
- I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C)
- return false;
- dummy_request.operation = request->operation;
- dummy_request.payload.address_space = request->payload.address_space;
- dummy_request.payload.address = request->payload.address;
- /*
- * Add a dummy byte due to some receiver quirk
- * where one byte is sent along with MOT = 0.
- * Ideally this should be 0.
- */
- dummy_request.payload.length = 0;
- dummy_request.payload.data = &dummy_data;
- if (request->operation == I2CAUX_TRANSACTION_READ)
- return read_command(engine, &dummy_request, false);
- else
- return write_command(engine, &dummy_request, false);
- /* according Syed, it does not need now DoDummyMOT */
- }
- bool dal_aux_engine_submit_request(
- struct engine *engine,
- struct i2caux_transaction_request *request,
- bool middle_of_transaction)
- {
- struct aux_engine *aux_engine = FROM_ENGINE(engine);
- bool result;
- bool mot_used = true;
- switch (request->operation) {
- case I2CAUX_TRANSACTION_READ:
- result = read_command(aux_engine, request, mot_used);
- break;
- case I2CAUX_TRANSACTION_WRITE:
- result = write_command(aux_engine, request, mot_used);
- break;
- default:
- result = false;
- }
- /* [tcheng]
- * need to send stop for the last transaction to free up the AUX
- * if the above command fails, this would be the last transaction */
- if (!middle_of_transaction || !result)
- end_of_transaction_command(aux_engine, request);
- /* mask AUX interrupt */
- return result;
- }
- void dal_aux_engine_construct(
- struct aux_engine *engine,
- struct dc_context *ctx)
- {
- dal_i2caux_construct_engine(&engine->base, ctx);
- engine->delay = 0;
- engine->max_defer_write_retry = 0;
- }
- void dal_aux_engine_destruct(
- struct aux_engine *engine)
- {
- dal_i2caux_destruct_engine(&engine->base);
- }
|