1*b843c749SSergey Zigachev /*
2*b843c749SSergey Zigachev  * Copyright 2012-15 Advanced Micro Devices, Inc.
3*b843c749SSergey Zigachev  *
4*b843c749SSergey Zigachev  * Permission is hereby granted, free of charge, to any person obtaining a
5*b843c749SSergey Zigachev  * copy of this software and associated documentation files (the "Software"),
6*b843c749SSergey Zigachev  * to deal in the Software without restriction, including without limitation
7*b843c749SSergey Zigachev  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*b843c749SSergey Zigachev  * and/or sell copies of the Software, and to permit persons to whom the
9*b843c749SSergey Zigachev  * Software is furnished to do so, subject to the following conditions:
10*b843c749SSergey Zigachev  *
11*b843c749SSergey Zigachev  * The above copyright notice and this permission notice shall be included in
12*b843c749SSergey Zigachev  * all copies or substantial portions of the Software.
13*b843c749SSergey Zigachev  *
14*b843c749SSergey Zigachev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*b843c749SSergey Zigachev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*b843c749SSergey Zigachev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17*b843c749SSergey Zigachev  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18*b843c749SSergey Zigachev  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19*b843c749SSergey Zigachev  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20*b843c749SSergey Zigachev  * OTHER DEALINGS IN THE SOFTWARE.
21*b843c749SSergey Zigachev  *
22*b843c749SSergey Zigachev  * Authors: AMD
23*b843c749SSergey Zigachev  *
24*b843c749SSergey Zigachev  */
25*b843c749SSergey Zigachev 
26*b843c749SSergey Zigachev #include "dm_services.h"
27*b843c749SSergey Zigachev 
28*b843c749SSergey Zigachev /*
29*b843c749SSergey Zigachev  * Pre-requisites: headers required by header of this unit
30*b843c749SSergey Zigachev  */
31*b843c749SSergey Zigachev #include "include/i2caux_interface.h"
32*b843c749SSergey Zigachev #include "engine.h"
33*b843c749SSergey Zigachev #include "i2c_engine.h"
34*b843c749SSergey Zigachev #include "i2c_hw_engine.h"
35*b843c749SSergey Zigachev 
36*b843c749SSergey Zigachev /*
37*b843c749SSergey Zigachev  * Header of this unit
38*b843c749SSergey Zigachev  */
39*b843c749SSergey Zigachev 
40*b843c749SSergey Zigachev #include "i2c_generic_hw_engine.h"
41*b843c749SSergey Zigachev 
42*b843c749SSergey Zigachev /*
43*b843c749SSergey Zigachev  * Post-requisites: headers required by this unit
44*b843c749SSergey Zigachev  */
45*b843c749SSergey Zigachev 
46*b843c749SSergey Zigachev /*
47*b843c749SSergey Zigachev  * This unit
48*b843c749SSergey Zigachev  */
49*b843c749SSergey Zigachev 
50*b843c749SSergey Zigachev /*
51*b843c749SSergey Zigachev  * @brief
52*b843c749SSergey Zigachev  * Cast 'struct i2c_hw_engine *'
53*b843c749SSergey Zigachev  * to 'struct i2c_generic_hw_engine *'
54*b843c749SSergey Zigachev  */
55*b843c749SSergey Zigachev #define FROM_I2C_HW_ENGINE(ptr) \
56*b843c749SSergey Zigachev 	container_of((ptr), struct i2c_generic_hw_engine, base)
57*b843c749SSergey Zigachev 
58*b843c749SSergey Zigachev /*
59*b843c749SSergey Zigachev  * @brief
60*b843c749SSergey Zigachev  * Cast 'struct i2c_engine *'
61*b843c749SSergey Zigachev  * to 'struct i2c_generic_hw_engine *'
62*b843c749SSergey Zigachev  */
63*b843c749SSergey Zigachev #define FROM_I2C_ENGINE(ptr) \
64*b843c749SSergey Zigachev 	FROM_I2C_HW_ENGINE(container_of((ptr), struct i2c_hw_engine, base))
65*b843c749SSergey Zigachev 
66*b843c749SSergey Zigachev /*
67*b843c749SSergey Zigachev  * @brief
68*b843c749SSergey Zigachev  * Cast 'struct engine *'
69*b843c749SSergey Zigachev  * to 'struct i2c_generic_hw_engine *'
70*b843c749SSergey Zigachev  */
71*b843c749SSergey Zigachev #define FROM_ENGINE(ptr) \
72*b843c749SSergey Zigachev 	FROM_I2C_ENGINE(container_of((ptr), struct i2c_engine, base))
73*b843c749SSergey Zigachev 
dal_i2c_generic_hw_engine_get_engine_type(const struct engine * engine)74*b843c749SSergey Zigachev enum i2caux_engine_type dal_i2c_generic_hw_engine_get_engine_type(
75*b843c749SSergey Zigachev 	const struct engine *engine)
76*b843c749SSergey Zigachev {
77*b843c749SSergey Zigachev 	return I2CAUX_ENGINE_TYPE_I2C_GENERIC_HW;
78*b843c749SSergey Zigachev }
79*b843c749SSergey Zigachev 
80*b843c749SSergey Zigachev /*
81*b843c749SSergey Zigachev  * @brief
82*b843c749SSergey Zigachev  * Single transaction handling.
83*b843c749SSergey Zigachev  * Since transaction may be bigger than HW buffer size,
84*b843c749SSergey Zigachev  * it divides transaction to sub-transactions
85*b843c749SSergey Zigachev  * and uses batch transaction feature of the engine.
86*b843c749SSergey Zigachev  */
dal_i2c_generic_hw_engine_submit_request(struct engine * engine,struct i2caux_transaction_request * i2caux_request,bool middle_of_transaction)87*b843c749SSergey Zigachev bool dal_i2c_generic_hw_engine_submit_request(
88*b843c749SSergey Zigachev 	struct engine *engine,
89*b843c749SSergey Zigachev 	struct i2caux_transaction_request *i2caux_request,
90*b843c749SSergey Zigachev 	bool middle_of_transaction)
91*b843c749SSergey Zigachev {
92*b843c749SSergey Zigachev 	struct i2c_generic_hw_engine *hw_engine = FROM_ENGINE(engine);
93*b843c749SSergey Zigachev 
94*b843c749SSergey Zigachev 	struct i2c_hw_engine *base = &hw_engine->base;
95*b843c749SSergey Zigachev 
96*b843c749SSergey Zigachev 	uint32_t max_payload_size =
97*b843c749SSergey Zigachev 		base->funcs->get_hw_buffer_available_size(base);
98*b843c749SSergey Zigachev 
99*b843c749SSergey Zigachev 	bool initial_stop_bit = !middle_of_transaction;
100*b843c749SSergey Zigachev 
101*b843c749SSergey Zigachev 	struct i2c_generic_transaction_attributes attributes;
102*b843c749SSergey Zigachev 
103*b843c749SSergey Zigachev 	enum i2c_channel_operation_result operation_result =
104*b843c749SSergey Zigachev 		I2C_CHANNEL_OPERATION_FAILED;
105*b843c749SSergey Zigachev 
106*b843c749SSergey Zigachev 	bool result = false;
107*b843c749SSergey Zigachev 
108*b843c749SSergey Zigachev 	/* setup transaction initial properties */
109*b843c749SSergey Zigachev 
110*b843c749SSergey Zigachev 	uint8_t address = i2caux_request->payload.address;
111*b843c749SSergey Zigachev 	uint8_t *current_payload = i2caux_request->payload.data;
112*b843c749SSergey Zigachev 	uint32_t remaining_payload_size = i2caux_request->payload.length;
113*b843c749SSergey Zigachev 
114*b843c749SSergey Zigachev 	bool first_iteration = true;
115*b843c749SSergey Zigachev 
116*b843c749SSergey Zigachev 	if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
117*b843c749SSergey Zigachev 		attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_READ;
118*b843c749SSergey Zigachev 	else if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
119*b843c749SSergey Zigachev 		attributes.action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
120*b843c749SSergey Zigachev 	else {
121*b843c749SSergey Zigachev 		i2caux_request->status =
122*b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_STATUS_FAILED_INVALID_OPERATION;
123*b843c749SSergey Zigachev 		return false;
124*b843c749SSergey Zigachev 	}
125*b843c749SSergey Zigachev 
126*b843c749SSergey Zigachev 	/* Do batch transaction.
127*b843c749SSergey Zigachev 	 * Divide read/write data into payloads which fit HW buffer size.
128*b843c749SSergey Zigachev 	 * 1. Single transaction:
129*b843c749SSergey Zigachev 	 *    start_bit = 1, stop_bit depends on session state, ack_on_read = 0;
130*b843c749SSergey Zigachev 	 * 2. Start of batch transaction:
131*b843c749SSergey Zigachev 	 *    start_bit = 1, stop_bit = 0, ack_on_read = 1;
132*b843c749SSergey Zigachev 	 * 3. Middle of batch transaction:
133*b843c749SSergey Zigachev 	 *    start_bit = 0, stop_bit = 0, ack_on_read = 1;
134*b843c749SSergey Zigachev 	 * 4. End of batch transaction:
135*b843c749SSergey Zigachev 	 *    start_bit = 0, stop_bit depends on session state, ack_on_read = 0.
136*b843c749SSergey Zigachev 	 * Session stop bit is set if 'middle_of_transaction' = 0. */
137*b843c749SSergey Zigachev 
138*b843c749SSergey Zigachev 	while (remaining_payload_size) {
139*b843c749SSergey Zigachev 		uint32_t current_transaction_size;
140*b843c749SSergey Zigachev 		uint32_t current_payload_size;
141*b843c749SSergey Zigachev 
142*b843c749SSergey Zigachev 		bool last_iteration;
143*b843c749SSergey Zigachev 		bool stop_bit;
144*b843c749SSergey Zigachev 
145*b843c749SSergey Zigachev 		/* Calculate current transaction size and payload size.
146*b843c749SSergey Zigachev 		 * Transaction size = total number of bytes in transaction,
147*b843c749SSergey Zigachev 		 * including slave's address;
148*b843c749SSergey Zigachev 		 * Payload size = number of data bytes in transaction. */
149*b843c749SSergey Zigachev 
150*b843c749SSergey Zigachev 		if (first_iteration) {
151*b843c749SSergey Zigachev 			/* In the first sub-transaction we send slave's address
152*b843c749SSergey Zigachev 			 * thus we need to reserve one byte for it */
153*b843c749SSergey Zigachev 			current_transaction_size =
154*b843c749SSergey Zigachev 			(remaining_payload_size > max_payload_size - 1) ?
155*b843c749SSergey Zigachev 				max_payload_size :
156*b843c749SSergey Zigachev 				remaining_payload_size + 1;
157*b843c749SSergey Zigachev 
158*b843c749SSergey Zigachev 			current_payload_size = current_transaction_size - 1;
159*b843c749SSergey Zigachev 		} else {
160*b843c749SSergey Zigachev 			/* Second and further sub-transactions will have
161*b843c749SSergey Zigachev 			 * entire buffer reserved for data */
162*b843c749SSergey Zigachev 			current_transaction_size =
163*b843c749SSergey Zigachev 				(remaining_payload_size > max_payload_size) ?
164*b843c749SSergey Zigachev 				max_payload_size :
165*b843c749SSergey Zigachev 				remaining_payload_size;
166*b843c749SSergey Zigachev 
167*b843c749SSergey Zigachev 			current_payload_size = current_transaction_size;
168*b843c749SSergey Zigachev 		}
169*b843c749SSergey Zigachev 
170*b843c749SSergey Zigachev 		last_iteration =
171*b843c749SSergey Zigachev 			(remaining_payload_size == current_payload_size);
172*b843c749SSergey Zigachev 
173*b843c749SSergey Zigachev 		stop_bit = last_iteration ? initial_stop_bit : false;
174*b843c749SSergey Zigachev 
175*b843c749SSergey Zigachev 		/* write slave device address */
176*b843c749SSergey Zigachev 
177*b843c749SSergey Zigachev 		if (first_iteration)
178*b843c749SSergey Zigachev 			hw_engine->funcs->write_address(hw_engine, address);
179*b843c749SSergey Zigachev 
180*b843c749SSergey Zigachev 		/* write current portion of data, if requested */
181*b843c749SSergey Zigachev 
182*b843c749SSergey Zigachev 		if (i2caux_request->operation == I2CAUX_TRANSACTION_WRITE)
183*b843c749SSergey Zigachev 			hw_engine->funcs->write_data(
184*b843c749SSergey Zigachev 				hw_engine,
185*b843c749SSergey Zigachev 				current_payload,
186*b843c749SSergey Zigachev 				current_payload_size);
187*b843c749SSergey Zigachev 
188*b843c749SSergey Zigachev 		/* execute transaction */
189*b843c749SSergey Zigachev 
190*b843c749SSergey Zigachev 		attributes.start_bit = first_iteration;
191*b843c749SSergey Zigachev 		attributes.stop_bit = stop_bit;
192*b843c749SSergey Zigachev 		attributes.last_read = last_iteration;
193*b843c749SSergey Zigachev 		attributes.transaction_size = current_transaction_size;
194*b843c749SSergey Zigachev 
195*b843c749SSergey Zigachev 		hw_engine->funcs->execute_transaction(hw_engine, &attributes);
196*b843c749SSergey Zigachev 
197*b843c749SSergey Zigachev 		/* wait until transaction is processed; if it fails - quit */
198*b843c749SSergey Zigachev 
199*b843c749SSergey Zigachev 		operation_result = base->funcs->wait_on_operation_result(
200*b843c749SSergey Zigachev 			base,
201*b843c749SSergey Zigachev 			base->funcs->get_transaction_timeout(
202*b843c749SSergey Zigachev 				base, current_transaction_size),
203*b843c749SSergey Zigachev 			I2C_CHANNEL_OPERATION_ENGINE_BUSY);
204*b843c749SSergey Zigachev 
205*b843c749SSergey Zigachev 		if (operation_result != I2C_CHANNEL_OPERATION_SUCCEEDED)
206*b843c749SSergey Zigachev 			break;
207*b843c749SSergey Zigachev 
208*b843c749SSergey Zigachev 		/* read current portion of data, if requested */
209*b843c749SSergey Zigachev 
210*b843c749SSergey Zigachev 		/* the read offset should be 1 for first sub-transaction,
211*b843c749SSergey Zigachev 		 * and 0 for any next one */
212*b843c749SSergey Zigachev 
213*b843c749SSergey Zigachev 		if (i2caux_request->operation == I2CAUX_TRANSACTION_READ)
214*b843c749SSergey Zigachev 			hw_engine->funcs->read_data(hw_engine, current_payload,
215*b843c749SSergey Zigachev 				current_payload_size, first_iteration ? 1 : 0);
216*b843c749SSergey Zigachev 
217*b843c749SSergey Zigachev 		/* update loop variables */
218*b843c749SSergey Zigachev 
219*b843c749SSergey Zigachev 		first_iteration = false;
220*b843c749SSergey Zigachev 		current_payload += current_payload_size;
221*b843c749SSergey Zigachev 		remaining_payload_size -= current_payload_size;
222*b843c749SSergey Zigachev 	}
223*b843c749SSergey Zigachev 
224*b843c749SSergey Zigachev 	/* update transaction status */
225*b843c749SSergey Zigachev 
226*b843c749SSergey Zigachev 	switch (operation_result) {
227*b843c749SSergey Zigachev 	case I2C_CHANNEL_OPERATION_SUCCEEDED:
228*b843c749SSergey Zigachev 		i2caux_request->status =
229*b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
230*b843c749SSergey Zigachev 		result = true;
231*b843c749SSergey Zigachev 	break;
232*b843c749SSergey Zigachev 	case I2C_CHANNEL_OPERATION_NO_RESPONSE:
233*b843c749SSergey Zigachev 		i2caux_request->status =
234*b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
235*b843c749SSergey Zigachev 	break;
236*b843c749SSergey Zigachev 	case I2C_CHANNEL_OPERATION_TIMEOUT:
237*b843c749SSergey Zigachev 		i2caux_request->status =
238*b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
239*b843c749SSergey Zigachev 	break;
240*b843c749SSergey Zigachev 	case I2C_CHANNEL_OPERATION_FAILED:
241*b843c749SSergey Zigachev 		i2caux_request->status =
242*b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_STATUS_FAILED_INCOMPLETE;
243*b843c749SSergey Zigachev 	break;
244*b843c749SSergey Zigachev 	default:
245*b843c749SSergey Zigachev 		i2caux_request->status =
246*b843c749SSergey Zigachev 			I2CAUX_TRANSACTION_STATUS_FAILED_OPERATION;
247*b843c749SSergey Zigachev 	}
248*b843c749SSergey Zigachev 
249*b843c749SSergey Zigachev 	return result;
250*b843c749SSergey Zigachev }
251*b843c749SSergey Zigachev 
252*b843c749SSergey Zigachev /*
253*b843c749SSergey Zigachev  * @brief
254*b843c749SSergey Zigachev  * Returns number of microseconds to wait until timeout to be considered
255*b843c749SSergey Zigachev  */
dal_i2c_generic_hw_engine_get_transaction_timeout(const struct i2c_hw_engine * engine,uint32_t length)256*b843c749SSergey Zigachev uint32_t dal_i2c_generic_hw_engine_get_transaction_timeout(
257*b843c749SSergey Zigachev 	const struct i2c_hw_engine *engine,
258*b843c749SSergey Zigachev 	uint32_t length)
259*b843c749SSergey Zigachev {
260*b843c749SSergey Zigachev 	const struct i2c_engine *base = &engine->base;
261*b843c749SSergey Zigachev 
262*b843c749SSergey Zigachev 	uint32_t speed = base->funcs->get_speed(base);
263*b843c749SSergey Zigachev 
264*b843c749SSergey Zigachev 	if (!speed)
265*b843c749SSergey Zigachev 		return 0;
266*b843c749SSergey Zigachev 
267*b843c749SSergey Zigachev 	/* total timeout = period_timeout * (start + data bits count + stop) */
268*b843c749SSergey Zigachev 
269*b843c749SSergey Zigachev 	return ((1000 * TRANSACTION_TIMEOUT_IN_I2C_CLOCKS) / speed) *
270*b843c749SSergey Zigachev 		(1 + (length << 3) + 1);
271*b843c749SSergey Zigachev }
272*b843c749SSergey Zigachev 
dal_i2c_generic_hw_engine_construct(struct i2c_generic_hw_engine * engine,struct dc_context * ctx)273*b843c749SSergey Zigachev void dal_i2c_generic_hw_engine_construct(
274*b843c749SSergey Zigachev 	struct i2c_generic_hw_engine *engine,
275*b843c749SSergey Zigachev 	struct dc_context *ctx)
276*b843c749SSergey Zigachev {
277*b843c749SSergey Zigachev 	dal_i2c_hw_engine_construct(&engine->base, ctx);
278*b843c749SSergey Zigachev }
279*b843c749SSergey Zigachev 
dal_i2c_generic_hw_engine_destruct(struct i2c_generic_hw_engine * engine)280*b843c749SSergey Zigachev void dal_i2c_generic_hw_engine_destruct(
281*b843c749SSergey Zigachev 	struct i2c_generic_hw_engine *engine)
282*b843c749SSergey Zigachev {
283*b843c749SSergey Zigachev 	dal_i2c_hw_engine_destruct(&engine->base);
284*b843c749SSergey Zigachev }
285