xref: /dragonfly/sys/dev/drm/amd/display/dc/i2caux/i2c_generic_hw_engine.c (revision b843c749addef9340ee7d4e250b09fdd492602a1)
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 
dal_i2c_generic_hw_engine_get_engine_type(const struct engine * engine)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  */
dal_i2c_generic_hw_engine_submit_request(struct engine * engine,struct i2caux_transaction_request * i2caux_request,bool middle_of_transaction)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  */
dal_i2c_generic_hw_engine_get_transaction_timeout(const struct i2c_hw_engine * engine,uint32_t length)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 
dal_i2c_generic_hw_engine_construct(struct i2c_generic_hw_engine * engine,struct dc_context * ctx)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 
dal_i2c_generic_hw_engine_destruct(struct i2c_generic_hw_engine * engine)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