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 #include "i2c_hw_engine.h"
35 
36 /*
37  * Header of this unit
38  */
39 
40 #include "i2c_generic_hw_engine.h"
41 
42 /*
43  * Post-requisites: headers required by this unit
44  */
45 
46 /*
47  * This unit
48  */
49 
50 /*
51  * @brief
52  * Cast 'struct i2c_hw_engine *'
53  * to 'struct i2c_generic_hw_engine *'
54  */
55 #define FROM_I2C_HW_ENGINE(ptr) \
56 	container_of((ptr), struct i2c_generic_hw_engine, base)
57 
58 /*
59  * @brief
60  * Cast 'struct i2c_engine *'
61  * to 'struct i2c_generic_hw_engine *'
62  */
63 #define FROM_I2C_ENGINE(ptr) \
64 	FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base))
65 
66 /*
67  * @brief
68  * Cast 'struct engine *'
69  * to 'struct i2c_generic_hw_engine *'
70  */
71 #define FROM_ENGINE(ptr) \
72 	FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base))
73 
74 enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type(
75 	const struct engine *engine)
76 {
77 	return I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW;
78 }
79 
80 /*
81  * @brief
82  * Single transaction handling.
83  * Since transaction may be bigger than HW buffer size,
84  * it divides transaction to sub-transactions
85  * and uses batch transaction feature of the engine.
86  */
87 bool dal_i2c_generic_hw_engine_submit_request(
88 	struct engine *engine,
89 	struct i2caux_transaction_request *i2caux_request,
90 	bool middle_of_transaction)
91 {
92 	struct i2c_generic_hw_engine *hw_engine = FROM_ENGINE(engine);
93 
94 	struct i2c_hw_engine *base = &hw_engine->base;
95 
96 	uint32_t max_payload_size =
97 		base->funcs->get_hw_buffer_available_size(base);
98 
99 	bool initial_stop_bit = !middle_of_transaction;
100 
101 	struct i2c_generic_transaction_attributes attributes;
102 
103 	enum i2c_channel_operation_result operation_result =
104 		I2C_CHANNEL_OPERATION_FAILED;
105 
106 	bool result = false;
107 
108 	/* setup transaction initial properties */
109 
110 	uint8_t address = i2caux_request->payload.address;
111 	uint8_t *current_payload = i2caux_request->payload.data;
112 	uint32_t remaining_payload_size = i2caux_request->payload.length;
113 
114 	bool first_iteration = true;
115 
116 	if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
117 		attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_READ;
118 	else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
119 		attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
120 	else {
121 		i2caux_request->status =
122 			I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION;
123 		return false;
124 	}
125 
126 	/* Do batch transaction.
127 	 * Divide read/write data into payloads which fit HW buffer size.
128 	 * 1. Single transaction:
129 	 *    start_bit = 1, stop_bit depends on session state, ack_on_read = 0;
130 	 * 2. Start of batch transaction:
131 	 *    start_bit = 1, stop_bit = 0, ack_on_read = 1;
132 	 * 3. Middle of batch transaction:
133 	 *    start_bit = 0, stop_bit = 0, ack_on_read = 1;
134 	 * 4. End of batch transaction:
135 	 *    start_bit = 0, stop_bit depends on session state, ack_on_read = 0.
136 	 * Session stop bit is set if 'middle_of_transaction' = 0. */
137 
138 	while (remaining_payload_size) {
139 		uint32_t current_transaction_size;
140 		uint32_t current_payload_size;
141 
142 		bool last_iteration;
143 		bool stop_bit;
144 
145 		/* Calculate current transaction size and payload size.
146 		 * Transaction size = total number of bytes in transaction,
147 		 * including slave's address;
148 		 * Payload size = number of data bytes in transaction. */
149 
150 		if (first_iteration) {
151 			/* In the first sub-transaction we send slave's address
152 			 * thus we need to reserve one byte for it */
153 			current_transaction_size =
154 			(remaining_payload_size > max_payload_size - 1) ?
155 				max_payload_size :
156 				remaining_payload_size + 1;
157 
158 			current_payload_size = current_transaction_size - 1;
159 		} else {
160 			/* Second and further sub-transactions will have
161 			 * entire buffer reserved for data */
162 			current_transaction_size =
163 				(remaining_payload_size > max_payload_size) ?
164 				max_payload_size :
165 				remaining_payload_size;
166 
167 			current_payload_size = current_transaction_size;
168 		}
169 
170 		last_iteration =
171 			(remaining_payload_size == current_payload_size);
172 
173 		stop_bit = last_iteration ? initial_stop_bit : false;
174 
175 		/* write slave device address */
176 
177 		if (first_iteration)
178 			hw_engine->funcs->write_address(hw_engine, address);
179 
180 		/* write current portion of data, if requested */
181 
182 		if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
183 			hw_engine->funcs->write_data(
184 				hw_engine,
185 				current_payload,
186 				current_payload_size);
187 
188 		/* execute transaction */
189 
190 		attributes.start_bit = first_iteration;
191 		attributes.stop_bit = stop_bit;
192 		attributes.last_read = last_iteration;
193 		attributes.transaction_size = current_transaction_size;
194 
195 		hw_engine->funcs->execute_transaction(hw_engine, &attributes);
196 
197 		/* wait until transaction is processed; if it fails - quit */
198 
199 		operation_result = base->funcs->wait_on_operation_result(
200 			base,
201 			base->funcs->get_transaction_timeout(
202 				base, current_transaction_size),
203 			I2C_CHANNEL_OPERATION_ENGINE_BUSY);
204 
205 		if (operation_result != I2C_CHANNEL_OPERATION_SUCCEEDED)
206 			break;
207 
208 		/* read current portion of data, if requested */
209 
210 		/* the read offset should be 1 for first sub-transaction,
211 		 * and 0 for any next one */
212 
213 		if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
214 			hw_engine->funcs->read_data(hw_engine, current_payload,
215 				current_payload_size, first_iteration ? 1 : 0);
216 
217 		/* update loop variables */
218 
219 		first_iteration = false;
220 		current_payload += current_payload_size;
221 		remaining_payload_size -= current_payload_size;
222 	}
223 
224 	/* update transaction status */
225 
226 	switch (operation_result) {
227 	case I2C_CHANNEL_OPERATION_SUCCEEDED:
228 		i2caux_request->status =
229 			I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
230 		result = true;
231 	break;
232 	case I2C_CHANNEL_OPERATION_NO_RESPONSE:
233 		i2caux_request->status =
234 			I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
235 	break;
236 	case I2C_CHANNEL_OPERATION_TIMEOUT:
237 		i2caux_request->status =
238 			I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
239 	break;
240 	case I2C_CHANNEL_OPERATION_FAILED:
241 		i2caux_request->status =
242 			I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE;
243 	break;
244 	default:
245 		i2caux_request->status =
246 			I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION;
247 	}
248 
249 	return result;
250 }
251 
252 /*
253  * @brief
254  * Returns number of microseconds to wait until timeout to be considered
255  */
256 uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout(
257 	const struct i2c_hw_engine *engine,
258 	uint32_t length)
259 {
260 	const struct i2c_engine *base = &engine->base;
261 
262 	uint32_t speed = base->funcs->get_speed(base);
263 
264 	if (!speed)
265 		return 0;
266 
267 	/* total timeout = period_timeout * (start + data bits count + stop) */
268 
269 	return ((1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed) *
270 		(1 + (length << 3) + 1);
271 }
272 
273 void dal_i2c_generic_hw_engine_construct(
274 	struct i2c_generic_hw_engine *engine,
275 	struct dc_context *ctx)
276 {
277 	dal_i2c_hw_engine_construct(&engine->base, ctx);
278 }
279 
280 void dal_i2c_generic_hw_engine_destruct(
281 	struct i2c_generic_hw_engine *engine)
282 {
283 	dal_i2c_hw_engine_destruct(&engine->base);
284 }
285