ipa_client.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #include <linux/delay.h>
  13. #include "ipa_i.h"
  14. /*
  15. * These values were determined empirically and shows good E2E bi-
  16. * directional throughputs
  17. */
  18. #define IPA_A2_HOLB_TMR_EN 0x1
  19. #define IPA_A2_HOLB_TMR_DEFAULT_VAL 0x1ff
  20. #define IPA_PKT_FLUSH_TO_US 100
  21. static void ipa_enable_data_path(u32 clnt_hdl)
  22. {
  23. if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_VIRTUAL) {
  24. /* IPA_HW_MODE_VIRTUAL lacks support for TAG IC & EP suspend */
  25. return;
  26. }
  27. if (ipa_ctx->ipa_hw_type == IPA_HW_v1_1)
  28. ipa_write_reg(ipa_ctx->mmio,
  29. IPA_ENDP_INIT_CTRL_n_OFST(clnt_hdl), 0);
  30. }
  31. static int ipa_disable_data_path(u32 clnt_hdl)
  32. {
  33. struct ipa_ep_context *ep = &ipa_ctx->ep[clnt_hdl];
  34. if (ipa_ctx->ipa_hw_mode == IPA_HW_MODE_VIRTUAL) {
  35. /* IPA_HW_MODE_VIRTUAL lacks support for TAG IC & EP suspend */
  36. return 0;
  37. }
  38. if (ipa_ctx->ipa_hw_type == IPA_HW_v1_1) {
  39. ipa_write_reg(ipa_ctx->mmio,
  40. IPA_ENDP_INIT_CTRL_n_OFST(clnt_hdl), 1);
  41. udelay(IPA_PKT_FLUSH_TO_US);
  42. if (IPA_CLIENT_IS_CONS(ep->client) &&
  43. ep->cfg.aggr.aggr_en == IPA_ENABLE_AGGR &&
  44. ep->cfg.aggr.aggr_time_limit)
  45. msleep(ep->cfg.aggr.aggr_time_limit);
  46. }
  47. return 0;
  48. }
  49. static int ipa_connect_configure_sps(const struct ipa_connect_params *in,
  50. struct ipa_ep_context *ep, int ipa_ep_idx)
  51. {
  52. int result = -EFAULT;
  53. /* Default Config */
  54. ep->ep_hdl = sps_alloc_endpoint();
  55. if (ep->ep_hdl == NULL) {
  56. IPAERR("SPS EP alloc failed EP.\n");
  57. return -EFAULT;
  58. }
  59. result = sps_get_config(ep->ep_hdl,
  60. &ep->connect);
  61. if (result) {
  62. IPAERR("fail to get config.\n");
  63. return -EFAULT;
  64. }
  65. /* Specific Config */
  66. if (IPA_CLIENT_IS_CONS(in->client)) {
  67. ep->connect.mode = SPS_MODE_SRC;
  68. ep->connect.destination =
  69. in->client_bam_hdl;
  70. ep->connect.source = ipa_ctx->bam_handle;
  71. ep->connect.dest_pipe_index =
  72. in->client_ep_idx;
  73. ep->connect.src_pipe_index = ipa_ep_idx;
  74. } else {
  75. ep->connect.mode = SPS_MODE_DEST;
  76. ep->connect.source = in->client_bam_hdl;
  77. ep->connect.destination = ipa_ctx->bam_handle;
  78. ep->connect.src_pipe_index = in->client_ep_idx;
  79. ep->connect.dest_pipe_index = ipa_ep_idx;
  80. }
  81. return 0;
  82. }
  83. static int ipa_connect_allocate_fifo(const struct ipa_connect_params *in,
  84. struct sps_mem_buffer *mem_buff_ptr,
  85. bool *fifo_in_pipe_mem_ptr,
  86. u32 *fifo_pipe_mem_ofst_ptr,
  87. u32 fifo_size, int ipa_ep_idx)
  88. {
  89. dma_addr_t dma_addr;
  90. u32 ofst;
  91. int result = -EFAULT;
  92. mem_buff_ptr->size = fifo_size;
  93. if (in->pipe_mem_preferred) {
  94. if (ipa_pipe_mem_alloc(&ofst, fifo_size)) {
  95. IPAERR("FIFO pipe mem alloc fail ep %u\n",
  96. ipa_ep_idx);
  97. mem_buff_ptr->base =
  98. dma_alloc_coherent(NULL,
  99. mem_buff_ptr->size,
  100. &dma_addr, GFP_KERNEL);
  101. } else {
  102. memset(mem_buff_ptr, 0, sizeof(struct sps_mem_buffer));
  103. result = sps_setup_bam2bam_fifo(mem_buff_ptr, ofst,
  104. fifo_size, 1);
  105. WARN_ON(result);
  106. *fifo_in_pipe_mem_ptr = 1;
  107. dma_addr = mem_buff_ptr->phys_base;
  108. *fifo_pipe_mem_ofst_ptr = ofst;
  109. }
  110. } else {
  111. mem_buff_ptr->base =
  112. dma_alloc_coherent(NULL, mem_buff_ptr->size,
  113. &dma_addr, GFP_KERNEL);
  114. }
  115. mem_buff_ptr->phys_base = dma_addr;
  116. if (mem_buff_ptr->base == NULL) {
  117. IPAERR("fail to get DMA memory.\n");
  118. return -EFAULT;
  119. }
  120. return 0;
  121. }
  122. static void ipa_program_holb(struct ipa_ep_context *ep, int ipa_ep_idx)
  123. {
  124. struct ipa_ep_cfg_holb holb;
  125. if (IPA_CLIENT_IS_PROD(ep->client))
  126. return;
  127. switch (ep->client) {
  128. case IPA_CLIENT_A2_TETHERED_CONS:
  129. case IPA_CLIENT_A2_EMBEDDED_CONS:
  130. holb.en = IPA_A2_HOLB_TMR_EN;
  131. holb.tmr_val = IPA_A2_HOLB_TMR_DEFAULT_VAL;
  132. break;
  133. default:
  134. return;
  135. }
  136. ipa_cfg_ep_holb(ipa_ep_idx, &holb);
  137. }
  138. /**
  139. * ipa_connect() - low-level IPA client connect
  140. * @in: [in] input parameters from client
  141. * @sps: [out] sps output from IPA needed by client for sps_connect
  142. * @clnt_hdl: [out] opaque client handle assigned by IPA to client
  143. *
  144. * Should be called by the driver of the peripheral that wants to connect to
  145. * IPA in BAM-BAM mode. these peripherals are A2, USB and HSIC. this api
  146. * expects caller to take responsibility to add any needed headers, routing
  147. * and filtering tables and rules as needed.
  148. *
  149. * Returns: 0 on success, negative on failure
  150. *
  151. * Note: Should not be called from atomic context
  152. */
  153. int ipa_connect(const struct ipa_connect_params *in, struct ipa_sps_params *sps,
  154. u32 *clnt_hdl)
  155. {
  156. int ipa_ep_idx;
  157. int result = -EFAULT;
  158. struct ipa_ep_context *ep;
  159. ipa_inc_client_enable_clks();
  160. if (in == NULL || sps == NULL || clnt_hdl == NULL ||
  161. in->client >= IPA_CLIENT_MAX ||
  162. in->desc_fifo_sz == 0 || in->data_fifo_sz == 0) {
  163. IPAERR("bad parm.\n");
  164. result = -EINVAL;
  165. goto fail;
  166. }
  167. ipa_ep_idx = ipa_get_ep_mapping(ipa_ctx->mode, in->client);
  168. if (ipa_ep_idx == -1) {
  169. IPAERR("fail to alloc EP.\n");
  170. goto fail;
  171. }
  172. ep = &ipa_ctx->ep[ipa_ep_idx];
  173. if (ep->valid) {
  174. IPAERR("EP already allocated.\n");
  175. goto fail;
  176. }
  177. memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
  178. ipa_enable_data_path(ipa_ep_idx);
  179. ep->valid = 1;
  180. ep->client = in->client;
  181. ep->client_notify = in->notify;
  182. ep->priv = in->priv;
  183. if (ipa_cfg_ep(ipa_ep_idx, &in->ipa_ep_cfg)) {
  184. IPAERR("fail to configure EP.\n");
  185. goto ipa_cfg_ep_fail;
  186. }
  187. result = ipa_connect_configure_sps(in, ep, ipa_ep_idx);
  188. if (result) {
  189. IPAERR("fail to configure SPS.\n");
  190. goto ipa_cfg_ep_fail;
  191. }
  192. if (in->desc.base == NULL) {
  193. result = ipa_connect_allocate_fifo(in, &ep->connect.desc,
  194. &ep->desc_fifo_in_pipe_mem,
  195. &ep->desc_fifo_pipe_mem_ofst,
  196. in->desc_fifo_sz, ipa_ep_idx);
  197. if (result) {
  198. IPAERR("fail to allocate DESC FIFO.\n");
  199. goto desc_mem_alloc_fail;
  200. }
  201. } else {
  202. IPADBG("client allocated DESC FIFO\n");
  203. ep->connect.desc = in->desc;
  204. ep->desc_fifo_client_allocated = 1;
  205. }
  206. IPADBG("Descriptor FIFO pa=0x%x, size=%d\n", ep->connect.desc.phys_base,
  207. ep->connect.desc.size);
  208. if (in->data.base == NULL) {
  209. result = ipa_connect_allocate_fifo(in, &ep->connect.data,
  210. &ep->data_fifo_in_pipe_mem,
  211. &ep->data_fifo_pipe_mem_ofst,
  212. in->data_fifo_sz, ipa_ep_idx);
  213. if (result) {
  214. IPAERR("fail to allocate DATA FIFO.\n");
  215. goto data_mem_alloc_fail;
  216. }
  217. } else {
  218. IPADBG("client allocated DATA FIFO\n");
  219. ep->connect.data = in->data;
  220. ep->data_fifo_client_allocated = 1;
  221. }
  222. IPADBG("Data FIFO pa=0x%x, size=%d\n", ep->connect.data.phys_base,
  223. ep->connect.data.size);
  224. ep->connect.event_thresh = IPA_EVENT_THRESHOLD;
  225. ep->connect.options = SPS_O_AUTO_ENABLE; /* BAM-to-BAM */
  226. if (IPA_CLIENT_IS_CONS(in->client))
  227. ep->connect.options |= SPS_O_NO_DISABLE;
  228. result = sps_connect(ep->ep_hdl, &ep->connect);
  229. if (result) {
  230. IPAERR("sps_connect fails.\n");
  231. goto sps_connect_fail;
  232. }
  233. sps->ipa_bam_hdl = ipa_ctx->bam_handle;
  234. sps->ipa_ep_idx = ipa_ep_idx;
  235. *clnt_hdl = ipa_ep_idx;
  236. memcpy(&sps->desc, &ep->connect.desc, sizeof(struct sps_mem_buffer));
  237. memcpy(&sps->data, &ep->connect.data, sizeof(struct sps_mem_buffer));
  238. ipa_program_holb(ep, ipa_ep_idx);
  239. IPADBG("client %d (ep: %d) connected\n", in->client, ipa_ep_idx);
  240. return 0;
  241. sps_connect_fail:
  242. if (!ep->data_fifo_in_pipe_mem)
  243. dma_free_coherent(NULL,
  244. ep->connect.data.size,
  245. ep->connect.data.base,
  246. ep->connect.data.phys_base);
  247. else
  248. ipa_pipe_mem_free(ep->data_fifo_pipe_mem_ofst,
  249. ep->connect.data.size);
  250. data_mem_alloc_fail:
  251. if (!ep->desc_fifo_in_pipe_mem)
  252. dma_free_coherent(NULL,
  253. ep->connect.desc.size,
  254. ep->connect.desc.base,
  255. ep->connect.desc.phys_base);
  256. else
  257. ipa_pipe_mem_free(ep->desc_fifo_pipe_mem_ofst,
  258. ep->connect.desc.size);
  259. desc_mem_alloc_fail:
  260. sps_free_endpoint(ep->ep_hdl);
  261. ipa_cfg_ep_fail:
  262. memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
  263. fail:
  264. ipa_dec_client_disable_clks();
  265. return result;
  266. }
  267. EXPORT_SYMBOL(ipa_connect);
  268. /**
  269. * ipa_disconnect() - low-level IPA client disconnect
  270. * @clnt_hdl: [in] opaque client handle assigned by IPA to client
  271. *
  272. * Should be called by the driver of the peripheral that wants to disconnect
  273. * from IPA in BAM-BAM mode. this api expects caller to take responsibility to
  274. * free any needed headers, routing and filtering tables and rules as needed.
  275. *
  276. * Returns: 0 on success, negative on failure
  277. *
  278. * Note: Should not be called from atomic context
  279. */
  280. int ipa_disconnect(u32 clnt_hdl)
  281. {
  282. int result;
  283. struct ipa_ep_context *ep;
  284. if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
  285. IPAERR("bad parm.\n");
  286. return -EINVAL;
  287. }
  288. ep = &ipa_ctx->ep[clnt_hdl];
  289. if (ep->suspended) {
  290. ipa_inc_client_enable_clks();
  291. ep->suspended = false;
  292. }
  293. result = ipa_disable_data_path(clnt_hdl);
  294. if (result) {
  295. IPAERR("disable data path failed res=%d clnt=%d.\n", result,
  296. clnt_hdl);
  297. return -EPERM;
  298. }
  299. result = sps_disconnect(ep->ep_hdl);
  300. if (result) {
  301. IPAERR("SPS disconnect failed.\n");
  302. return -EPERM;
  303. }
  304. if (!ep->desc_fifo_client_allocated &&
  305. ep->connect.desc.base) {
  306. if (!ep->desc_fifo_in_pipe_mem)
  307. dma_free_coherent(NULL,
  308. ep->connect.desc.size,
  309. ep->connect.desc.base,
  310. ep->connect.desc.phys_base);
  311. else
  312. ipa_pipe_mem_free(ep->desc_fifo_pipe_mem_ofst,
  313. ep->connect.desc.size);
  314. }
  315. if (!ep->data_fifo_client_allocated &&
  316. ep->connect.data.base) {
  317. if (!ep->data_fifo_in_pipe_mem)
  318. dma_free_coherent(NULL,
  319. ep->connect.data.size,
  320. ep->connect.data.base,
  321. ep->connect.data.phys_base);
  322. else
  323. ipa_pipe_mem_free(ep->data_fifo_pipe_mem_ofst,
  324. ep->connect.data.size);
  325. }
  326. result = sps_free_endpoint(ep->ep_hdl);
  327. if (result) {
  328. IPAERR("SPS de-alloc EP failed.\n");
  329. return -EPERM;
  330. }
  331. memset(&ipa_ctx->ep[clnt_hdl], 0, sizeof(struct ipa_ep_context));
  332. ipa_dec_client_disable_clks();
  333. IPADBG("client (ep: %d) disconnected\n", clnt_hdl);
  334. return 0;
  335. }
  336. EXPORT_SYMBOL(ipa_disconnect);
  337. /**
  338. * ipa_resume() - low-level IPA client resume
  339. * @clnt_hdl: [in] opaque client handle assigned by IPA to client
  340. *
  341. * Should be called by the driver of the peripheral that wants to resume IPA
  342. * connection. Resume IPA connection results in turning on IPA clocks in
  343. * case they were off as a result of suspend.
  344. * this api can be called only if a call to ipa_suspend() was
  345. * made.
  346. *
  347. * Returns: 0 on success, negative on failure
  348. *
  349. * Note: Should not be called from atomic context
  350. */
  351. int ipa_resume(u32 clnt_hdl)
  352. {
  353. struct ipa_ep_context *ep;
  354. if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
  355. IPAERR("bad parm. clnt_hdl %d\n", clnt_hdl);
  356. return -EINVAL;
  357. }
  358. ep = &ipa_ctx->ep[clnt_hdl];
  359. if (!ep->suspended) {
  360. IPAERR("EP not suspended. clnt_hdl %d\n", clnt_hdl);
  361. return -EPERM;
  362. }
  363. ipa_inc_client_enable_clks();
  364. ep->suspended = false;
  365. return 0;
  366. }
  367. EXPORT_SYMBOL(ipa_resume);
  368. /**
  369. * ipa_suspend() - low-level IPA client suspend
  370. * @clnt_hdl: [in] opaque client handle assigned by IPA to client
  371. *
  372. * Should be called by the driver of the peripheral that wants to suspend IPA
  373. * connection. Suspend IPA connection results in turning off IPA clocks in
  374. * case that there is no active clients using IPA. Pipes remains connected in
  375. * case of suspend.
  376. *
  377. * Returns: 0 on success, negative on failure
  378. *
  379. * Note: Should not be called from atomic context
  380. */
  381. int ipa_suspend(u32 clnt_hdl)
  382. {
  383. struct ipa_ep_context *ep;
  384. if (clnt_hdl >= IPA_NUM_PIPES || ipa_ctx->ep[clnt_hdl].valid == 0) {
  385. IPAERR("bad parm. clnt_hdl %d\n", clnt_hdl);
  386. return -EINVAL;
  387. }
  388. ep = &ipa_ctx->ep[clnt_hdl];
  389. if (ep->suspended) {
  390. IPAERR("EP already suspended. clnt_hdl %d\n", clnt_hdl);
  391. return -EPERM;
  392. }
  393. if (IPA_CLIENT_IS_CONS(ep->client) &&
  394. ep->cfg.aggr.aggr_en == IPA_ENABLE_AGGR &&
  395. ep->cfg.aggr.aggr_time_limit)
  396. msleep(ep->cfg.aggr.aggr_time_limit);
  397. ipa_dec_client_disable_clks();
  398. ep->suspended = true;
  399. return 0;
  400. }
  401. EXPORT_SYMBOL(ipa_suspend);