aux_engine.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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. #include "dm_services.h"
  26. /*
  27. * Pre-requisites: headers required by header of this unit
  28. */
  29. #include "include/i2caux_interface.h"
  30. #include "engine.h"
  31. /*
  32. * Header of this unit
  33. */
  34. #include "aux_engine.h"
  35. /*
  36. * Post-requisites: headers required by this unit
  37. */
  38. #include "include/link_service_types.h"
  39. /*
  40. * This unit
  41. */
  42. enum {
  43. AUX_INVALID_REPLY_RETRY_COUNTER = 1,
  44. AUX_TIMED_OUT_RETRY_COUNTER = 2,
  45. AUX_DEFER_RETRY_COUNTER = 6
  46. };
  47. #define FROM_ENGINE(ptr) \
  48. container_of((ptr), struct aux_engine, base)
  49. enum i2caux_engine_type dal_aux_engine_get_engine_type(
  50. const struct engine *engine)
  51. {
  52. return I2CAUX_ENGINE_TYPE_AUX;
  53. }
  54. bool dal_aux_engine_acquire(
  55. struct engine *engine,
  56. struct ddc *ddc)
  57. {
  58. struct aux_engine *aux_engine = FROM_ENGINE(engine);
  59. enum gpio_result result;
  60. if (aux_engine->funcs->is_engine_available) {
  61. /*check whether SW could use the engine*/
  62. if (!aux_engine->funcs->is_engine_available(aux_engine)) {
  63. return false;
  64. }
  65. }
  66. result = dal_ddc_open(ddc, GPIO_MODE_HARDWARE,
  67. GPIO_DDC_CONFIG_TYPE_MODE_AUX);
  68. if (result != GPIO_RESULT_OK)
  69. return false;
  70. if (!aux_engine->funcs->acquire_engine(aux_engine)) {
  71. dal_ddc_close(ddc);
  72. return false;
  73. }
  74. engine->ddc = ddc;
  75. return true;
  76. }
  77. struct read_command_context {
  78. uint8_t *buffer;
  79. uint32_t current_read_length;
  80. uint32_t offset;
  81. enum i2caux_transaction_status status;
  82. struct aux_request_transaction_data request;
  83. struct aux_reply_transaction_data reply;
  84. uint8_t returned_byte;
  85. uint32_t timed_out_retry_aux;
  86. uint32_t invalid_reply_retry_aux;
  87. uint32_t defer_retry_aux;
  88. uint32_t defer_retry_i2c;
  89. uint32_t invalid_reply_retry_aux_on_ack;
  90. bool transaction_complete;
  91. bool operation_succeeded;
  92. };
  93. static void process_read_reply(
  94. struct aux_engine *engine,
  95. struct read_command_context *ctx)
  96. {
  97. engine->funcs->process_channel_reply(engine, &ctx->reply);
  98. switch (ctx->reply.status) {
  99. case AUX_TRANSACTION_REPLY_AUX_ACK:
  100. ctx->defer_retry_aux = 0;
  101. if (ctx->returned_byte > ctx->current_read_length) {
  102. ctx->status =
  103. I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
  104. ctx->operation_succeeded = false;
  105. } else if (ctx->returned_byte < ctx->current_read_length) {
  106. ctx->current_read_length -= ctx->returned_byte;
  107. ctx->offset += ctx->returned_byte;
  108. ++ctx->invalid_reply_retry_aux_on_ack;
  109. if (ctx->invalid_reply_retry_aux_on_ack >
  110. AUX_INVALID_REPLY_RETRY_COUNTER) {
  111. ctx->status =
  112. I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
  113. ctx->operation_succeeded = false;
  114. }
  115. } else {
  116. ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
  117. ctx->transaction_complete = true;
  118. ctx->operation_succeeded = true;
  119. }
  120. break;
  121. case AUX_TRANSACTION_REPLY_AUX_NACK:
  122. ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
  123. ctx->operation_succeeded = false;
  124. break;
  125. case AUX_TRANSACTION_REPLY_AUX_DEFER:
  126. ++ctx->defer_retry_aux;
  127. if (ctx->defer_retry_aux > AUX_DEFER_RETRY_COUNTER) {
  128. ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
  129. ctx->operation_succeeded = false;
  130. }
  131. break;
  132. case AUX_TRANSACTION_REPLY_I2C_DEFER:
  133. ctx->defer_retry_aux = 0;
  134. ++ctx->defer_retry_i2c;
  135. if (ctx->defer_retry_i2c > AUX_DEFER_RETRY_COUNTER) {
  136. ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
  137. ctx->operation_succeeded = false;
  138. }
  139. break;
  140. default:
  141. ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
  142. ctx->operation_succeeded = false;
  143. }
  144. }
  145. static void process_read_request(
  146. struct aux_engine *engine,
  147. struct read_command_context *ctx)
  148. {
  149. enum aux_channel_operation_result operation_result;
  150. engine->funcs->submit_channel_request(engine, &ctx->request);
  151. operation_result = engine->funcs->get_channel_status(
  152. engine, &ctx->returned_byte);
  153. switch (operation_result) {
  154. case AUX_CHANNEL_OPERATION_SUCCEEDED:
  155. if (ctx->returned_byte > ctx->current_read_length) {
  156. ctx->status =
  157. I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
  158. ctx->operation_succeeded = false;
  159. } else {
  160. ctx->timed_out_retry_aux = 0;
  161. ctx->invalid_reply_retry_aux = 0;
  162. ctx->reply.length = ctx->returned_byte;
  163. ctx->reply.data = ctx->buffer;
  164. process_read_reply(engine, ctx);
  165. }
  166. break;
  167. case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
  168. ++ctx->invalid_reply_retry_aux;
  169. if (ctx->invalid_reply_retry_aux >
  170. AUX_INVALID_REPLY_RETRY_COUNTER) {
  171. ctx->status =
  172. I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
  173. ctx->operation_succeeded = false;
  174. } else
  175. udelay(400);
  176. break;
  177. case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
  178. ++ctx->timed_out_retry_aux;
  179. if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
  180. ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
  181. ctx->operation_succeeded = false;
  182. } else {
  183. /* DP 1.2a, table 2-58:
  184. * "S3: AUX Request CMD PENDING:
  185. * retry 3 times, with 400usec wait on each"
  186. * The HW timeout is set to 550usec,
  187. * so we should not wait here */
  188. }
  189. break;
  190. default:
  191. ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
  192. ctx->operation_succeeded = false;
  193. }
  194. }
  195. static bool read_command(
  196. struct aux_engine *engine,
  197. struct i2caux_transaction_request *request,
  198. bool middle_of_transaction)
  199. {
  200. struct read_command_context ctx;
  201. ctx.buffer = request->payload.data;
  202. ctx.current_read_length = request->payload.length;
  203. ctx.offset = 0;
  204. ctx.timed_out_retry_aux = 0;
  205. ctx.invalid_reply_retry_aux = 0;
  206. ctx.defer_retry_aux = 0;
  207. ctx.defer_retry_i2c = 0;
  208. ctx.invalid_reply_retry_aux_on_ack = 0;
  209. ctx.transaction_complete = false;
  210. ctx.operation_succeeded = true;
  211. if (request->payload.address_space ==
  212. I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
  213. ctx.request.type = AUX_TRANSACTION_TYPE_DP;
  214. ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_READ;
  215. ctx.request.address = request->payload.address;
  216. } else if (request->payload.address_space ==
  217. I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
  218. ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
  219. ctx.request.action = middle_of_transaction ?
  220. I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT :
  221. I2CAUX_TRANSACTION_ACTION_I2C_READ;
  222. ctx.request.address = request->payload.address >> 1;
  223. } else {
  224. /* in DAL2, there was no return in such case */
  225. BREAK_TO_DEBUGGER();
  226. return false;
  227. }
  228. ctx.request.delay = 0;
  229. do {
  230. memset(ctx.buffer + ctx.offset, 0, ctx.current_read_length);
  231. ctx.request.data = ctx.buffer + ctx.offset;
  232. ctx.request.length = ctx.current_read_length;
  233. process_read_request(engine, &ctx);
  234. request->status = ctx.status;
  235. if (ctx.operation_succeeded && !ctx.transaction_complete)
  236. if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
  237. msleep(engine->delay);
  238. } while (ctx.operation_succeeded && !ctx.transaction_complete);
  239. return ctx.operation_succeeded;
  240. }
  241. struct write_command_context {
  242. bool mot;
  243. uint8_t *buffer;
  244. uint32_t current_write_length;
  245. enum i2caux_transaction_status status;
  246. struct aux_request_transaction_data request;
  247. struct aux_reply_transaction_data reply;
  248. uint8_t returned_byte;
  249. uint32_t timed_out_retry_aux;
  250. uint32_t invalid_reply_retry_aux;
  251. uint32_t defer_retry_aux;
  252. uint32_t defer_retry_i2c;
  253. uint32_t max_defer_retry;
  254. uint32_t ack_m_retry;
  255. uint8_t reply_data[DEFAULT_AUX_MAX_DATA_SIZE];
  256. bool transaction_complete;
  257. bool operation_succeeded;
  258. };
  259. static void process_write_reply(
  260. struct aux_engine *engine,
  261. struct write_command_context *ctx)
  262. {
  263. engine->funcs->process_channel_reply(engine, &ctx->reply);
  264. switch (ctx->reply.status) {
  265. case AUX_TRANSACTION_REPLY_AUX_ACK:
  266. ctx->operation_succeeded = true;
  267. if (ctx->returned_byte) {
  268. ctx->request.action = ctx->mot ?
  269. I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
  270. I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
  271. ctx->current_write_length = 0;
  272. ++ctx->ack_m_retry;
  273. if (ctx->ack_m_retry > AUX_DEFER_RETRY_COUNTER) {
  274. ctx->status =
  275. I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
  276. ctx->operation_succeeded = false;
  277. } else
  278. udelay(300);
  279. } else {
  280. ctx->status = I2CAUX_TRANSACTION_STATUS_SUCCEEDED;
  281. ctx->defer_retry_aux = 0;
  282. ctx->ack_m_retry = 0;
  283. ctx->transaction_complete = true;
  284. }
  285. break;
  286. case AUX_TRANSACTION_REPLY_AUX_NACK:
  287. ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_NACK;
  288. ctx->operation_succeeded = false;
  289. break;
  290. case AUX_TRANSACTION_REPLY_AUX_DEFER:
  291. ++ctx->defer_retry_aux;
  292. if (ctx->defer_retry_aux > ctx->max_defer_retry) {
  293. ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
  294. ctx->operation_succeeded = false;
  295. }
  296. break;
  297. case AUX_TRANSACTION_REPLY_I2C_DEFER:
  298. ctx->defer_retry_aux = 0;
  299. ctx->current_write_length = 0;
  300. ctx->request.action = ctx->mot ?
  301. I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT :
  302. I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST;
  303. ++ctx->defer_retry_i2c;
  304. if (ctx->defer_retry_i2c > ctx->max_defer_retry) {
  305. ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
  306. ctx->operation_succeeded = false;
  307. }
  308. break;
  309. default:
  310. ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
  311. ctx->operation_succeeded = false;
  312. }
  313. }
  314. static void process_write_request(
  315. struct aux_engine *engine,
  316. struct write_command_context *ctx)
  317. {
  318. enum aux_channel_operation_result operation_result;
  319. engine->funcs->submit_channel_request(engine, &ctx->request);
  320. operation_result = engine->funcs->get_channel_status(
  321. engine, &ctx->returned_byte);
  322. switch (operation_result) {
  323. case AUX_CHANNEL_OPERATION_SUCCEEDED:
  324. ctx->timed_out_retry_aux = 0;
  325. ctx->invalid_reply_retry_aux = 0;
  326. ctx->reply.length = ctx->returned_byte;
  327. ctx->reply.data = ctx->reply_data;
  328. process_write_reply(engine, ctx);
  329. break;
  330. case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY:
  331. ++ctx->invalid_reply_retry_aux;
  332. if (ctx->invalid_reply_retry_aux >
  333. AUX_INVALID_REPLY_RETRY_COUNTER) {
  334. ctx->status =
  335. I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
  336. ctx->operation_succeeded = false;
  337. } else
  338. udelay(400);
  339. break;
  340. case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT:
  341. ++ctx->timed_out_retry_aux;
  342. if (ctx->timed_out_retry_aux > AUX_TIMED_OUT_RETRY_COUNTER) {
  343. ctx->status = I2CAUX_TRANSACTION_STATUS_FAILED_TIMEOUT;
  344. ctx->operation_succeeded = false;
  345. } else {
  346. /* DP 1.2a, table 2-58:
  347. * "S3: AUX Request CMD PENDING:
  348. * retry 3 times, with 400usec wait on each"
  349. * The HW timeout is set to 550usec,
  350. * so we should not wait here */
  351. }
  352. break;
  353. default:
  354. ctx->status = I2CAUX_TRANSACTION_STATUS_UNKNOWN;
  355. ctx->operation_succeeded = false;
  356. }
  357. }
  358. static bool write_command(
  359. struct aux_engine *engine,
  360. struct i2caux_transaction_request *request,
  361. bool middle_of_transaction)
  362. {
  363. struct write_command_context ctx;
  364. ctx.mot = middle_of_transaction;
  365. ctx.buffer = request->payload.data;
  366. ctx.current_write_length = request->payload.length;
  367. ctx.timed_out_retry_aux = 0;
  368. ctx.invalid_reply_retry_aux = 0;
  369. ctx.defer_retry_aux = 0;
  370. ctx.defer_retry_i2c = 0;
  371. ctx.ack_m_retry = 0;
  372. ctx.transaction_complete = false;
  373. ctx.operation_succeeded = true;
  374. if (request->payload.address_space ==
  375. I2CAUX_TRANSACTION_ADDRESS_SPACE_DPCD) {
  376. ctx.request.type = AUX_TRANSACTION_TYPE_DP;
  377. ctx.request.action = I2CAUX_TRANSACTION_ACTION_DP_WRITE;
  378. ctx.request.address = request->payload.address;
  379. } else if (request->payload.address_space ==
  380. I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C) {
  381. ctx.request.type = AUX_TRANSACTION_TYPE_I2C;
  382. ctx.request.action = middle_of_transaction ?
  383. I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT :
  384. I2CAUX_TRANSACTION_ACTION_I2C_WRITE;
  385. ctx.request.address = request->payload.address >> 1;
  386. } else {
  387. /* in DAL2, there was no return in such case */
  388. BREAK_TO_DEBUGGER();
  389. return false;
  390. }
  391. ctx.request.delay = 0;
  392. ctx.max_defer_retry =
  393. (engine->max_defer_write_retry > AUX_DEFER_RETRY_COUNTER) ?
  394. engine->max_defer_write_retry : AUX_DEFER_RETRY_COUNTER;
  395. do {
  396. ctx.request.data = ctx.buffer;
  397. ctx.request.length = ctx.current_write_length;
  398. process_write_request(engine, &ctx);
  399. request->status = ctx.status;
  400. if (ctx.operation_succeeded && !ctx.transaction_complete)
  401. if (ctx.request.type == AUX_TRANSACTION_TYPE_I2C)
  402. msleep(engine->delay);
  403. } while (ctx.operation_succeeded && !ctx.transaction_complete);
  404. return ctx.operation_succeeded;
  405. }
  406. static bool end_of_transaction_command(
  407. struct aux_engine *engine,
  408. struct i2caux_transaction_request *request)
  409. {
  410. struct i2caux_transaction_request dummy_request;
  411. uint8_t dummy_data;
  412. /* [tcheng] We only need to send the stop (read with MOT = 0)
  413. * for I2C-over-Aux, not native AUX */
  414. if (request->payload.address_space !=
  415. I2CAUX_TRANSACTION_ADDRESS_SPACE_I2C)
  416. return false;
  417. dummy_request.operation = request->operation;
  418. dummy_request.payload.address_space = request->payload.address_space;
  419. dummy_request.payload.address = request->payload.address;
  420. /*
  421. * Add a dummy byte due to some receiver quirk
  422. * where one byte is sent along with MOT = 0.
  423. * Ideally this should be 0.
  424. */
  425. dummy_request.payload.length = 0;
  426. dummy_request.payload.data = &dummy_data;
  427. if (request->operation == I2CAUX_TRANSACTION_READ)
  428. return read_command(engine, &dummy_request, false);
  429. else
  430. return write_command(engine, &dummy_request, false);
  431. /* according Syed, it does not need now DoDummyMOT */
  432. }
  433. bool dal_aux_engine_submit_request(
  434. struct engine *engine,
  435. struct i2caux_transaction_request *request,
  436. bool middle_of_transaction)
  437. {
  438. struct aux_engine *aux_engine = FROM_ENGINE(engine);
  439. bool result;
  440. bool mot_used = true;
  441. switch (request->operation) {
  442. case I2CAUX_TRANSACTION_READ:
  443. result = read_command(aux_engine, request, mot_used);
  444. break;
  445. case I2CAUX_TRANSACTION_WRITE:
  446. result = write_command(aux_engine, request, mot_used);
  447. break;
  448. default:
  449. result = false;
  450. }
  451. /* [tcheng]
  452. * need to send stop for the last transaction to free up the AUX
  453. * if the above command fails, this would be the last transaction */
  454. if (!middle_of_transaction || !result)
  455. end_of_transaction_command(aux_engine, request);
  456. /* mask AUX interrupt */
  457. return result;
  458. }
  459. void dal_aux_engine_construct(
  460. struct aux_engine *engine,
  461. struct dc_context *ctx)
  462. {
  463. dal_i2caux_construct_engine(&engine->base, ctx);
  464. engine->delay = 0;
  465. engine->max_defer_write_retry = 0;
  466. }
  467. void dal_aux_engine_destruct(
  468. struct aux_engine *engine)
  469. {
  470. dal_i2caux_destruct_engine(&engine->base);
  471. }