/* * 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" #include "dm_event_log.h" /* * Pre-requisites: headers required by header of this unit */ #include "include/i2caux_interface.h" #include "engine.h" #include "i2c_engine.h" /* * Header of this unit */ #include "i2c_hw_engine.h" /* * Post-requisites: headers required by this unit */ /* * This unit */ /* * @brief * Cast 'struct i2c_engine *' * to 'struct i2c_hw_engine *' */ #define FROM_I2C_ENGINE(ptr) \ container_of((ptr), struct i2c_hw_engine, base) /* * @brief * Cast 'struct engine *' * to 'struct i2c_hw_engine *' */ #define FROM_ENGINE(ptr) \ FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base)) enum i2caux_engine_type dal_i2c_hw_engine_get_engine_type( const struct engine *engine) { return I2CAUX_ENGINE_TYPE_I2C_DDC_HW; } bool dal_i2c_hw_engine_submit_request( struct engine *engine, struct i2caux_transaction_request *i2caux_request, bool middle_of_transaction) { struct i2c_hw_engine *hw_engine = FROM_ENGINE(engine); struct i2c_request_transaction_data request; uint32_t transaction_timeout; enum i2c_channel_operation_result operation_result; bool result = false; /* We need following: * transaction length will not exceed * the number of free bytes in HW buffer (minus one for address)*/ if (i2caux_request->payload.length >= hw_engine->funcs->get_hw_buffer_available_size(hw_engine)) { i2caux_request->status = I2CAUX_TRANSACTION_STATUS_FAILED_BUFFER_OVERFLOW; return false; } if (i2caux_request->operation == I2CAUX_TRANSACTION_READ) request.action = middle_of_transaction ? I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT : I2CAUX_TRANSACTION_ACTION_I2C_READ; else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE) request.action = middle_of_transaction ? I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT : I2CAUX_TRANSACTION_ACTION_I2C_WRITE; else { i2caux_request->status = I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION; /* [anaumov] in DAL2, there was no "return false" */ return false; } request.address = (uint8_t)i2caux_request->payload.address; request.length = i2caux_request->payload.length; request.data = i2caux_request->payload.data; /* obtain timeout value before submitting request */ transaction_timeout = hw_engine->funcs->get_transaction_timeout( hw_engine, i2caux_request->payload.length + 1); hw_engine->base.funcs->submit_channel_request( &hw_engine->base, &request); /* EVENT_LOG_AUX_REQ(engine->ddc->pin_data->en, EVENT_LOG_AUX_ORIGIN_I2C, */ /* request.action, request.address, request.length, request.data); */ if ((request.status == I2C_CHANNEL_OPERATION_FAILED) || (request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY)) { i2caux_request->status = I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY; return false; } /* wait until transaction proceed */ operation_result = hw_engine->funcs->wait_on_operation_result( hw_engine, transaction_timeout, I2C_CHANNEL_OPERATION_ENGINE_BUSY); /* update transaction status */ switch (operation_result) { case I2C_CHANNEL_OPERATION_SUCCEEDED: i2caux_request->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED; result = true; break; case I2C_CHANNEL_OPERATION_NO_RESPONSE: i2caux_request->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK; break; case I2C_CHANNEL_OPERATION_TIMEOUT: i2caux_request->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT; break; case I2C_CHANNEL_OPERATION_FAILED: i2caux_request->status = I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE; break; default: i2caux_request->status = I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION; } if (result && (i2caux_request->operation == I2CAUX_TRANSACTION_READ)) { struct i2c_reply_transaction_data reply; reply.data = i2caux_request->payload.data; reply.length = i2caux_request->payload.length; hw_engine->base.funcs-> process_channel_reply(&hw_engine->base, &reply); /* EVENT_LOG_AUX_REP(engine->ddc->pin_data->en, EVENT_LOG_AUX_ORIGIN_I2C, */ /* AUX_TRANSACTION_REPLY_I2C_ACK, reply.length, reply.data); */ } return result; } bool dal_i2c_hw_engine_acquire_engine( struct i2c_engine *engine, struct ddc *ddc) { enum gpio_result result; uint32_t current_speed; result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE, GPIO_DDC_CONFIG_TYPE_MODE_I2C); if (result != GPIO_RESULT_OK) return false; engine->base.ddc = ddc; current_speed = engine->funcs->get_speed(engine); if (current_speed) FROM_I2C_ENGINE(engine)->original_speed = current_speed; return true; } /* * @brief * Queries in a loop for current engine status * until retrieved status matches 'expected_result', or timeout occurs. * Timeout given in microseconds * and the status query frequency is also one per microsecond. */ enum i2c_channel_operation_result dal_i2c_hw_engine_wait_on_operation_result( struct i2c_hw_engine *engine, uint32_t timeout, enum i2c_channel_operation_result expected_result) { enum i2c_channel_operation_result result; uint32_t i = 0; if (!timeout) return I2C_CHANNEL_OPERATION_SUCCEEDED; do { result = engine->base.funcs->get_channel_status( &engine->base, NULL); if (result != expected_result) break; udelay(1); ++i; } while (i < timeout); return result; } void dal_i2c_hw_engine_construct( struct i2c_hw_engine *engine, struct dc_context *ctx) { dal_i2c_engine_construct(&engine->base, ctx); engine->original_speed = I2CAUX_DEFAULT_I2C_HW_SPEED; engine->default_speed = I2CAUX_DEFAULT_I2C_HW_SPEED; } void dal_i2c_hw_engine_destruct( struct i2c_hw_engine *engine) { dal_i2c_engine_destruct(&engine->base); }