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 #include "i2c_engine.h"
34 
35 /*
36  * Header of this unit
37  */
38 
39 #include "i2c_hw_engine.h"
40 
41 /*
42  * Post-requisites: headers required by this unit
43  */
44 
45 /*
46  * This unit
47  */
48 
49 /*
50  * @brief
51  * Cast 'struct i2c_engine *'
52  * to 'struct i2c_hw_engine *'
53  */
54 #define FROM_I2C_ENGINE(ptr) \
55 	container_of((ptr), struct i2c_hw_engine, base)
56 
57 /*
58  * @brief
59  * Cast 'struct engine *'
60  * to 'struct i2c_hw_engine *'
61  */
62 #define FROM_ENGINE(ptr) \
63 	FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base))
64 
65 enum i2caux_engine_type dal_i2c_hw_engine_get_engine_type(
66 	const struct engine *engine)
67 {
68 	return I2CAUX_ENGINE_TYPE_I2C_DDC_HW;
69 }
70 
71 bool dal_i2c_hw_engine_submit_request(
72 	struct engine *engine,
73 	struct i2caux_transaction_request *i2caux_request,
74 	bool middle_of_transaction)
75 {
76 	struct i2c_hw_engine *hw_engine = FROM_ENGINE(engine);
77 
78 	struct i2c_request_transaction_data request;
79 
80 	uint32_t transaction_timeout;
81 
82 	enum i2c_channel_operation_result operation_result;
83 
84 	bool result = false;
85 
86 	/* We need following:
87 	 * transaction length will not exceed
88 	 * the number of free bytes in HW buffer (minus one for address)*/
89 
90 	if (i2caux_request->payload.length >=
91 		hw_engine->funcs->get_hw_buffer_available_size(hw_engine)) {
92 		i2caux_request->status =
93 			I2CAUX_TRANSACTION_STATUS_FAILED_BUFFER_OVERFLOW;
94 		return false;
95 	}
96 
97 	if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
98 		request.action = middle_of_transaction ?
99 			I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT :
100 			I2CAUX_TRANSACTION_ACTION_I2C_READ;
101 	else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
102 		request.action = middle_of_transaction ?
103 			I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT :
104 			I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
105 	else {
106 		i2caux_request->status =
107 			I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION;
108 		/* [anaumov] in DAL2, there was no "return false" */
109 		return false;
110 	}
111 
112 	request.address = (uint8_t)i2caux_request->payload.address;
113 	request.length = i2caux_request->payload.length;
114 	request.data = i2caux_request->payload.data;
115 
116 	/* obtain timeout value before submitting request */
117 
118 	transaction_timeout = hw_engine->funcs->get_transaction_timeout(
119 		hw_engine, i2caux_request->payload.length + 1);
120 
121 	hw_engine->base.funcs->submit_channel_request(
122 		&hw_engine->base, &request);
123 
124 	if ((request.status == I2C_CHANNEL_OPERATION_FAILED) ||
125 		(request.status == I2C_CHANNEL_OPERATION_ENGINE_BUSY)) {
126 		i2caux_request->status =
127 			I2CAUX_TRANSACTION_STATUS_FAILED_CHANNEL_BUSY;
128 		return false;
129 	}
130 
131 	/* wait until transaction proceed */
132 
133 	operation_result = hw_engine->funcs->wait_on_operation_result(
134 		hw_engine,
135 		transaction_timeout,
136 		I2C_CHANNEL_OPERATION_ENGINE_BUSY);
137 
138 	/* update transaction status */
139 
140 	switch (operation_result) {
141 	case I2C_CHANNEL_OPERATION_SUCCEEDED:
142 		i2caux_request->status =
143 			I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
144 		result = true;
145 	break;
146 	case I2C_CHANNEL_OPERATION_NO_RESPONSE:
147 		i2caux_request->status =
148 			I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
149 	break;
150 	case I2C_CHANNEL_OPERATION_TIMEOUT:
151 		i2caux_request->status =
152 			I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
153 	break;
154 	case I2C_CHANNEL_OPERATION_FAILED:
155 		i2caux_request->status =
156 			I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE;
157 	break;
158 	default:
159 		i2caux_request->status =
160 			I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION;
161 	}
162 
163 	if (result && (i2caux_request->operation == I2CAUX_TRANSACTION_READ)) {
164 		struct i2c_reply_transaction_data reply;
165 
166 		reply.data = i2caux_request->payload.data;
167 		reply.length = i2caux_request->payload.length;
168 
169 		hw_engine->base.funcs->
170 			process_channel_reply(&hw_engine->base, &reply);
171 	}
172 
173 	return result;
174 }
175 
176 bool dal_i2c_hw_engine_acquire_engine(
177 	struct i2c_engine *engine,
178 	struct ddc *ddc)
179 {
180 	enum gpio_result result;
181 	uint32_t current_speed;
182 
183 	result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE,
184 		GPIO_DDC_CONFIG_TYPE_MODE_I2C);
185 
186 	if (result != GPIO_RESULT_OK)
187 		return false;
188 
189 	engine->base.ddc = ddc;
190 
191 	current_speed = engine->funcs->get_speed(engine);
192 
193 	if (current_speed)
194 		FROM_I2C_ENGINE(engine)->original_speed = current_speed;
195 
196 	return true;
197 }
198 /*
199  * @brief
200  * Queries in a loop for current engine status
201  * until retrieved status matches 'expected_result', or timeout occurs.
202  * Timeout given in microseconds
203  * and the status query frequency is also one per microsecond.
204  */
205 enum i2c_channel_operation_result dal_i2c_hw_engine_wait_on_operation_result(
206 	struct i2c_hw_engine *engine,
207 	uint32_t timeout,
208 	enum i2c_channel_operation_result expected_result)
209 {
210 	enum i2c_channel_operation_result result;
211 	uint32_t i = 0;
212 
213 	if (!timeout)
214 		return I2C_CHANNEL_OPERATION_SUCCEEDED;
215 
216 	do {
217 		result = engine->base.funcs->get_channel_status(
218 			&engine->base, NULL);
219 
220 		if (result != expected_result)
221 			break;
222 
223 		udelay(1);
224 
225 		++i;
226 	} while (i < timeout);
227 
228 	return result;
229 }
230 
231 void dal_i2c_hw_engine_construct(
232 	struct i2c_hw_engine *engine,
233 	struct dc_context *ctx)
234 {
235 	dal_i2c_engine_construct(&engine->base, ctx);
236 	engine->original_speed = I2CAUX_DEFAULT_I2C_HW_SPEED;
237 	engine->default_speed = I2CAUX_DEFAULT_I2C_HW_SPEED;
238 }
239 
240 void dal_i2c_hw_engine_destruct(
241 	struct i2c_hw_engine *engine)
242 {
243 	dal_i2c_engine_destruct(&engine->base);
244 }
245