aux_engine.c 15 KB

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