ipa_rm.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/slab.h>
  6. #include <linux/workqueue.h>
  7. #include "ipa.h"
  8. #include "ipa_rm_dependency_graph.h"
  9. #include "ipa_rm_i.h"
  10. #include "ipa_common_i.h"
  11. static const char *resource_name_to_str[IPA_RM_RESOURCE_MAX] = {
  12. __stringify(IPA_RM_RESOURCE_Q6_PROD),
  13. __stringify(IPA_RM_RESOURCE_Q6_CONS),
  14. __stringify(IPA_RM_RESOURCE_USB_PROD),
  15. __stringify(IPA_RM_RESOURCE_USB_CONS),
  16. __stringify(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD),
  17. __stringify(IPA_RM_RESOURCE_USB_DPL_CONS),
  18. __stringify(IPA_RM_RESOURCE_HSIC_PROD),
  19. __stringify(IPA_RM_RESOURCE_HSIC_CONS),
  20. __stringify(IPA_RM_RESOURCE_STD_ECM_PROD),
  21. __stringify(IPA_RM_RESOURCE_APPS_CONS),
  22. __stringify(IPA_RM_RESOURCE_RNDIS_PROD),
  23. __stringify(RESERVED_CONS_11),
  24. __stringify(IPA_RM_RESOURCE_WWAN_0_PROD),
  25. __stringify(RESERVED_CONS_13),
  26. __stringify(IPA_RM_RESOURCE_WLAN_PROD),
  27. __stringify(IPA_RM_RESOURCE_WLAN_CONS),
  28. __stringify(IPA_RM_RESOURCE_ODU_ADAPT_PROD),
  29. __stringify(IPA_RM_RESOURCE_ODU_ADAPT_CONS),
  30. __stringify(IPA_RM_RESOURCE_MHI_PROD),
  31. __stringify(IPA_RM_RESOURCE_MHI_CONS),
  32. __stringify(IPA_RM_RESOURCE_ETHERNET_PROD),
  33. __stringify(IPA_RM_RESOURCE_ETHERNET_CONS),
  34. };
  35. struct ipa_rm_profile_vote_type {
  36. enum ipa_voltage_level volt[IPA_RM_RESOURCE_MAX];
  37. enum ipa_voltage_level curr_volt;
  38. u32 bw_resources[IPA_RM_RESOURCE_MAX];
  39. u32 curr_bw;
  40. };
  41. struct ipa_rm_context_type {
  42. struct ipa_rm_dep_graph *dep_graph;
  43. struct workqueue_struct *ipa_rm_wq;
  44. spinlock_t ipa_rm_lock;
  45. struct ipa_rm_profile_vote_type prof_vote;
  46. };
  47. static struct ipa_rm_context_type *ipa_rm_ctx;
  48. struct ipa_rm_notify_ipa_work_type {
  49. struct work_struct work;
  50. enum ipa_voltage_level volt;
  51. u32 bandwidth_mbps;
  52. };
  53. static int _ipa_rm_add_dependency(enum ipa_rm_resource_name resource_name,
  54. enum ipa_rm_resource_name depends_on_name,
  55. bool userspace_dep)
  56. {
  57. unsigned long flags;
  58. int result;
  59. if (unlikely(!ipa_rm_ctx)) {
  60. IPA_RM_ERR("IPA RM was not initialized\n");
  61. return -EINVAL;
  62. }
  63. IPA_RM_DBG("%s -> %s\n", ipa_rm_resource_str(resource_name),
  64. ipa_rm_resource_str(depends_on_name));
  65. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  66. result = ipa_rm_dep_graph_add_dependency(
  67. ipa_rm_ctx->dep_graph,
  68. resource_name,
  69. depends_on_name,
  70. userspace_dep);
  71. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  72. IPA_RM_DBG("EXIT with %d\n", result);
  73. return result;
  74. }
  75. /**
  76. * ipa_rm_add_dependency_from_ioctl() - create dependency between 2 resources
  77. * @resource_name: name of dependent resource
  78. * @depends_on_name: name of its dependency
  79. *
  80. * This function is expected to be called from IOCTL and the dependency will be
  81. * marked as is was added by the userspace.
  82. *
  83. * Returns: 0 on success, negative on failure
  84. *
  85. * Side effects: IPA_RM_RESORCE_GRANTED could be generated
  86. * in case client registered with IPA RM
  87. */
  88. int ipa_rm_add_dependency_from_ioctl(enum ipa_rm_resource_name resource_name,
  89. enum ipa_rm_resource_name depends_on_name)
  90. {
  91. return _ipa_rm_add_dependency(resource_name, depends_on_name, true);
  92. }
  93. static int _ipa_rm_add_dependency_sync(enum ipa_rm_resource_name resource_name,
  94. enum ipa_rm_resource_name depends_on_name,
  95. bool userspsace_dep)
  96. {
  97. int result;
  98. struct ipa_rm_resource *consumer;
  99. unsigned long time;
  100. unsigned long flags;
  101. if (unlikely(!ipa_rm_ctx)) {
  102. IPA_RM_ERR("IPA RM was not initialized\n");
  103. return -EINVAL;
  104. }
  105. IPA_RM_DBG("%s -> %s\n", ipa_rm_resource_str(resource_name),
  106. ipa_rm_resource_str(depends_on_name));
  107. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  108. result = ipa_rm_dep_graph_add_dependency(
  109. ipa_rm_ctx->dep_graph,
  110. resource_name,
  111. depends_on_name,
  112. userspsace_dep);
  113. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  114. if (result == -EINPROGRESS) {
  115. ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
  116. depends_on_name,
  117. &consumer);
  118. IPA_RM_DBG("%s waits for GRANT of %s.\n",
  119. ipa_rm_resource_str(resource_name),
  120. ipa_rm_resource_str(depends_on_name));
  121. time = wait_for_completion_timeout(
  122. &((struct ipa_rm_resource_cons *)consumer)->
  123. request_consumer_in_progress,
  124. HZ * 5);
  125. result = 0;
  126. if (!time) {
  127. IPA_RM_ERR("TIMEOUT waiting for %s GRANT event.",
  128. ipa_rm_resource_str(depends_on_name));
  129. result = -ETIME;
  130. } else {
  131. IPA_RM_DBG("%s waited for %s GRANT %lu time.\n",
  132. ipa_rm_resource_str(resource_name),
  133. ipa_rm_resource_str(depends_on_name),
  134. time);
  135. }
  136. }
  137. IPA_RM_DBG("EXIT with %d\n", result);
  138. return result;
  139. }
  140. /**
  141. * ipa_rm_add_dependency_sync_from_ioctl() - Create a dependency between 2
  142. * resources in a synchronized fashion. In case a producer resource is in
  143. * GRANTED state and the newly added consumer resource is in RELEASED state,
  144. * the consumer entity will be requested and the function will block until
  145. * the consumer is granted.
  146. * @resource_name: name of dependent resource
  147. * @depends_on_name: name of its dependency
  148. *
  149. * Returns: 0 on success, negative on failure
  150. *
  151. * Side effects: May block. See documentation above.
  152. */
  153. int ipa_rm_add_dependency_sync_from_ioctl(
  154. enum ipa_rm_resource_name resource_name,
  155. enum ipa_rm_resource_name depends_on_name)
  156. {
  157. return _ipa_rm_add_dependency_sync(resource_name, depends_on_name,
  158. true);
  159. }
  160. static int _ipa_rm_delete_dependency(enum ipa_rm_resource_name resource_name,
  161. enum ipa_rm_resource_name depends_on_name,
  162. bool userspace_dep)
  163. {
  164. unsigned long flags;
  165. int result;
  166. if (unlikely(!ipa_rm_ctx)) {
  167. IPA_RM_ERR("IPA RM was not initialized\n");
  168. return -EINVAL;
  169. }
  170. IPA_RM_DBG("%s -> %s\n", ipa_rm_resource_str(resource_name),
  171. ipa_rm_resource_str(depends_on_name));
  172. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  173. result = ipa_rm_dep_graph_delete_dependency(
  174. ipa_rm_ctx->dep_graph,
  175. resource_name,
  176. depends_on_name,
  177. userspace_dep);
  178. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  179. IPA_RM_DBG("EXIT with %d\n", result);
  180. return result;
  181. }
  182. /**
  183. * ipa_rm_delete_dependency_fron_ioctl() - delete dependency between 2 resources
  184. * @resource_name: name of dependent resource
  185. * @depends_on_name: name of its dependency
  186. *
  187. * This function is expected to be called from IOCTL and the dependency will be
  188. * marked as is was added by the userspace.
  189. *
  190. * Returns: 0 on success, negative on failure
  191. *
  192. * Side effects: IPA_RM_RESORCE_GRANTED could be generated
  193. * in case client registered with IPA RM
  194. */
  195. int ipa_rm_delete_dependency_from_ioctl(enum ipa_rm_resource_name resource_name,
  196. enum ipa_rm_resource_name depends_on_name)
  197. {
  198. return _ipa_rm_delete_dependency(resource_name, depends_on_name, true);
  199. }
  200. void delayed_release_work_func(struct work_struct *work)
  201. {
  202. unsigned long flags;
  203. struct ipa_rm_resource *resource;
  204. struct ipa_rm_delayed_release_work_type *rwork = container_of(
  205. to_delayed_work(work),
  206. struct ipa_rm_delayed_release_work_type,
  207. work);
  208. if (!IPA_RM_RESORCE_IS_CONS(rwork->resource_name)) {
  209. IPA_RM_ERR("can be called on CONS only\n");
  210. kfree(rwork);
  211. return;
  212. }
  213. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  214. if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
  215. rwork->resource_name,
  216. &resource) != 0) {
  217. IPA_RM_ERR("resource does not exists\n");
  218. goto bail;
  219. }
  220. ipa_rm_resource_consumer_release(
  221. (struct ipa_rm_resource_cons *)resource, rwork->needed_bw,
  222. rwork->dec_usage_count);
  223. bail:
  224. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  225. kfree(rwork);
  226. }
  227. /**
  228. * ipa_rm_request_resource_with_timer() - requests the specified consumer
  229. * resource and releases it after 1 second
  230. * @resource_name: name of the requested resource
  231. *
  232. * Returns: 0 on success, negative on failure
  233. */
  234. int ipa_rm_request_resource_with_timer(enum ipa_rm_resource_name resource_name)
  235. {
  236. unsigned long flags;
  237. struct ipa_rm_resource *resource;
  238. struct ipa_rm_delayed_release_work_type *release_work;
  239. int result;
  240. if (!IPA_RM_RESORCE_IS_CONS(resource_name)) {
  241. IPA_RM_ERR("can be called on CONS only\n");
  242. return -EINVAL;
  243. }
  244. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  245. if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
  246. resource_name,
  247. &resource) != 0) {
  248. IPA_RM_ERR("resource does not exists\n");
  249. result = -EPERM;
  250. goto bail;
  251. }
  252. result = ipa_rm_resource_consumer_request(
  253. (struct ipa_rm_resource_cons *)resource, 0, false, true);
  254. if (result != 0 && result != -EINPROGRESS) {
  255. IPA_RM_ERR("consumer request returned error %d\n", result);
  256. result = -EPERM;
  257. goto bail;
  258. }
  259. release_work = kzalloc(sizeof(*release_work), GFP_ATOMIC);
  260. if (!release_work) {
  261. result = -ENOMEM;
  262. goto bail;
  263. }
  264. release_work->resource_name = resource->name;
  265. release_work->needed_bw = 0;
  266. release_work->dec_usage_count = false;
  267. INIT_DELAYED_WORK(&release_work->work, delayed_release_work_func);
  268. schedule_delayed_work(&release_work->work,
  269. msecs_to_jiffies(IPA_RM_RELEASE_DELAY_IN_MSEC));
  270. result = 0;
  271. bail:
  272. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  273. return result;
  274. }
  275. static void ipa_rm_wq_handler(struct work_struct *work)
  276. {
  277. unsigned long flags;
  278. struct ipa_rm_resource *resource;
  279. struct ipa_rm_wq_work_type *ipa_rm_work =
  280. container_of(work,
  281. struct ipa_rm_wq_work_type,
  282. work);
  283. IPA_RM_DBG_LOW("%s cmd=%d event=%d notify_registered_only=%d\n",
  284. ipa_rm_resource_str(ipa_rm_work->resource_name),
  285. ipa_rm_work->wq_cmd,
  286. ipa_rm_work->event,
  287. ipa_rm_work->notify_registered_only);
  288. switch (ipa_rm_work->wq_cmd) {
  289. case IPA_RM_WQ_NOTIFY_PROD:
  290. if (!IPA_RM_RESORCE_IS_PROD(ipa_rm_work->resource_name)) {
  291. IPA_RM_ERR("resource is not PROD\n");
  292. goto free_work;
  293. }
  294. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  295. if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
  296. ipa_rm_work->resource_name,
  297. &resource) != 0){
  298. IPA_RM_ERR("resource does not exists\n");
  299. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  300. goto free_work;
  301. }
  302. ipa_rm_resource_producer_notify_clients(
  303. (struct ipa_rm_resource_prod *)resource,
  304. ipa_rm_work->event,
  305. ipa_rm_work->notify_registered_only);
  306. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  307. break;
  308. case IPA_RM_WQ_NOTIFY_CONS:
  309. break;
  310. case IPA_RM_WQ_RESOURCE_CB:
  311. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  312. if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
  313. ipa_rm_work->resource_name,
  314. &resource) != 0){
  315. IPA_RM_ERR("resource does not exists\n");
  316. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  317. goto free_work;
  318. }
  319. ipa_rm_resource_consumer_handle_cb(
  320. (struct ipa_rm_resource_cons *)resource,
  321. ipa_rm_work->event);
  322. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  323. break;
  324. default:
  325. break;
  326. }
  327. free_work:
  328. kfree((void *) work);
  329. }
  330. static void ipa_rm_wq_resume_handler(struct work_struct *work)
  331. {
  332. unsigned long flags;
  333. struct ipa_rm_resource *resource;
  334. struct ipa_rm_wq_suspend_resume_work_type *ipa_rm_work =
  335. container_of(work,
  336. struct ipa_rm_wq_suspend_resume_work_type,
  337. work);
  338. IPA_RM_DBG_LOW("resume work handler: %s",
  339. ipa_rm_resource_str(ipa_rm_work->resource_name));
  340. if (!IPA_RM_RESORCE_IS_CONS(ipa_rm_work->resource_name)) {
  341. IPA_RM_ERR("resource is not CONS\n");
  342. return;
  343. }
  344. IPA_ACTIVE_CLIENTS_INC_RESOURCE(ipa_rm_resource_str(
  345. ipa_rm_work->resource_name));
  346. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  347. if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
  348. ipa_rm_work->resource_name,
  349. &resource) != 0){
  350. IPA_RM_ERR("resource does not exists\n");
  351. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  352. IPA_ACTIVE_CLIENTS_DEC_RESOURCE(ipa_rm_resource_str(
  353. ipa_rm_work->resource_name));
  354. goto bail;
  355. }
  356. ipa_rm_resource_consumer_request_work(
  357. (struct ipa_rm_resource_cons *)resource,
  358. ipa_rm_work->prev_state, ipa_rm_work->needed_bw, true,
  359. ipa_rm_work->inc_usage_count);
  360. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  361. bail:
  362. kfree(ipa_rm_work);
  363. }
  364. static void ipa_rm_wq_suspend_handler(struct work_struct *work)
  365. {
  366. unsigned long flags;
  367. struct ipa_rm_resource *resource;
  368. struct ipa_rm_wq_suspend_resume_work_type *ipa_rm_work =
  369. container_of(work,
  370. struct ipa_rm_wq_suspend_resume_work_type,
  371. work);
  372. IPA_RM_DBG_LOW("suspend work handler: %s",
  373. ipa_rm_resource_str(ipa_rm_work->resource_name));
  374. if (!IPA_RM_RESORCE_IS_CONS(ipa_rm_work->resource_name)) {
  375. IPA_RM_ERR("resource is not CONS\n");
  376. return;
  377. }
  378. ipa3_suspend_resource_sync(ipa_rm_work->resource_name);
  379. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  380. if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
  381. ipa_rm_work->resource_name,
  382. &resource) != 0){
  383. IPA_RM_ERR("resource does not exists\n");
  384. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  385. return;
  386. }
  387. ipa_rm_resource_consumer_release_work(
  388. (struct ipa_rm_resource_cons *)resource,
  389. ipa_rm_work->prev_state,
  390. true);
  391. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  392. kfree(ipa_rm_work);
  393. }
  394. /**
  395. * ipa_rm_wq_send_cmd() - send a command for deferred work
  396. * @wq_cmd: command that should be executed
  397. * @resource_name: resource on which command should be executed
  398. * @notify_registered_only: notify only clients registered by
  399. * ipa_rm_register()
  400. *
  401. * Returns: 0 on success, negative otherwise
  402. */
  403. int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd,
  404. enum ipa_rm_resource_name resource_name,
  405. enum ipa_rm_event event,
  406. bool notify_registered_only)
  407. {
  408. int result = -ENOMEM;
  409. struct ipa_rm_wq_work_type *work = kzalloc(sizeof(*work), GFP_ATOMIC);
  410. if (work) {
  411. INIT_WORK((struct work_struct *)work, ipa_rm_wq_handler);
  412. work->wq_cmd = wq_cmd;
  413. work->resource_name = resource_name;
  414. work->event = event;
  415. work->notify_registered_only = notify_registered_only;
  416. result = queue_work(ipa_rm_ctx->ipa_rm_wq,
  417. (struct work_struct *)work);
  418. }
  419. return result;
  420. }
  421. int ipa_rm_wq_send_suspend_cmd(enum ipa_rm_resource_name resource_name,
  422. enum ipa_rm_resource_state prev_state,
  423. u32 needed_bw)
  424. {
  425. int result = -ENOMEM;
  426. struct ipa_rm_wq_suspend_resume_work_type *work = kzalloc(sizeof(*work),
  427. GFP_ATOMIC);
  428. if (work) {
  429. INIT_WORK((struct work_struct *)work,
  430. ipa_rm_wq_suspend_handler);
  431. work->resource_name = resource_name;
  432. work->prev_state = prev_state;
  433. work->needed_bw = needed_bw;
  434. result = queue_work(ipa_rm_ctx->ipa_rm_wq,
  435. (struct work_struct *)work);
  436. }
  437. return result;
  438. }
  439. int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name,
  440. enum ipa_rm_resource_state prev_state,
  441. u32 needed_bw,
  442. bool inc_usage_count)
  443. {
  444. int result = -ENOMEM;
  445. struct ipa_rm_wq_suspend_resume_work_type *work = kzalloc(sizeof(*work),
  446. GFP_ATOMIC);
  447. if (work) {
  448. INIT_WORK((struct work_struct *)work, ipa_rm_wq_resume_handler);
  449. work->resource_name = resource_name;
  450. work->prev_state = prev_state;
  451. work->needed_bw = needed_bw;
  452. work->inc_usage_count = inc_usage_count;
  453. result = queue_work(ipa_rm_ctx->ipa_rm_wq,
  454. (struct work_struct *)work);
  455. } else {
  456. IPA_RM_ERR("no mem\n");
  457. }
  458. return result;
  459. }
  460. /**
  461. * ipa_rm_initialize() - initialize IPA RM component
  462. *
  463. * Returns: 0 on success, negative otherwise
  464. */
  465. int ipa_rm_initialize(void)
  466. {
  467. int result;
  468. ipa_rm_ctx = kzalloc(sizeof(*ipa_rm_ctx), GFP_KERNEL);
  469. if (!ipa_rm_ctx) {
  470. IPA_RM_ERR("no mem\n");
  471. result = -ENOMEM;
  472. goto bail;
  473. }
  474. ipa_rm_ctx->ipa_rm_wq = create_singlethread_workqueue("ipa_rm_wq");
  475. if (!ipa_rm_ctx->ipa_rm_wq) {
  476. IPA_RM_ERR("create workqueue failed\n");
  477. result = -ENOMEM;
  478. goto create_wq_fail;
  479. }
  480. result = ipa_rm_dep_graph_create(&(ipa_rm_ctx->dep_graph));
  481. if (result) {
  482. IPA_RM_ERR("create dependency graph failed\n");
  483. goto graph_alloc_fail;
  484. }
  485. spin_lock_init(&ipa_rm_ctx->ipa_rm_lock);
  486. IPA_RM_DBG("SUCCESS\n");
  487. return 0;
  488. graph_alloc_fail:
  489. destroy_workqueue(ipa_rm_ctx->ipa_rm_wq);
  490. create_wq_fail:
  491. kfree(ipa_rm_ctx);
  492. bail:
  493. return result;
  494. }
  495. /**
  496. * ipa_rm_stat() - print RM stat
  497. * @buf: [in] The user buff used to print
  498. * @size: [in] The size of buf
  499. * Returns: number of bytes used on success, negative on failure
  500. *
  501. * This function is called by ipa_debugfs in order to receive
  502. * a full picture of the current state of the RM
  503. */
  504. int ipa_rm_stat(char *buf, int size)
  505. {
  506. unsigned long flags;
  507. int i, cnt = 0, result = EINVAL;
  508. struct ipa_rm_resource *resource = NULL;
  509. u32 sum_bw_prod = 0;
  510. u32 sum_bw_cons = 0;
  511. if (!buf || size < 0)
  512. return result;
  513. spin_lock_irqsave(&ipa_rm_ctx->ipa_rm_lock, flags);
  514. for (i = 0; i < IPA_RM_RESOURCE_MAX; ++i) {
  515. if (!IPA_RM_RESORCE_IS_PROD(i))
  516. continue;
  517. result = ipa_rm_dep_graph_get_resource(
  518. ipa_rm_ctx->dep_graph,
  519. i,
  520. &resource);
  521. if (!result) {
  522. result = ipa_rm_resource_producer_print_stat(
  523. resource, buf + cnt,
  524. size-cnt);
  525. if (result < 0)
  526. goto bail;
  527. cnt += result;
  528. }
  529. }
  530. for (i = 0; i < IPA_RM_RESOURCE_MAX; i++) {
  531. if (IPA_RM_RESORCE_IS_PROD(i))
  532. sum_bw_prod += ipa_rm_ctx->prof_vote.bw_resources[i];
  533. else
  534. sum_bw_cons += ipa_rm_ctx->prof_vote.bw_resources[i];
  535. }
  536. result = scnprintf(buf + cnt, size - cnt,
  537. "All prod bandwidth: %d, All cons bandwidth: %d\n",
  538. sum_bw_prod, sum_bw_cons);
  539. cnt += result;
  540. result = scnprintf(buf + cnt, size - cnt,
  541. "Voting: voltage %d, bandwidth %d\n",
  542. ipa_rm_ctx->prof_vote.curr_volt,
  543. ipa_rm_ctx->prof_vote.curr_bw);
  544. cnt += result;
  545. result = cnt;
  546. bail:
  547. spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags);
  548. return result;
  549. }
  550. /**
  551. * ipa_rm_resource_str() - returns string that represent the resource
  552. * @resource_name: [in] resource name
  553. */
  554. const char *ipa_rm_resource_str(enum ipa_rm_resource_name resource_name)
  555. {
  556. if (resource_name < 0 || resource_name >= IPA_RM_RESOURCE_MAX)
  557. return "INVALID RESOURCE";
  558. return resource_name_to_str[resource_name];
  559. };
  560. static void ipa_rm_perf_profile_notify_to_ipa_work(struct work_struct *work)
  561. {
  562. struct ipa_rm_notify_ipa_work_type *notify_work = container_of(work,
  563. struct ipa_rm_notify_ipa_work_type,
  564. work);
  565. int res;
  566. IPA_RM_DBG_LOW("calling to IPA driver. voltage %d bandwidth %d\n",
  567. notify_work->volt, notify_work->bandwidth_mbps);
  568. res = ipa3_set_required_perf_profile(notify_work->volt,
  569. notify_work->bandwidth_mbps);
  570. if (res) {
  571. IPA_RM_ERR("ipa3_set_required_perf_profile failed %d\n", res);
  572. goto bail;
  573. }
  574. IPA_RM_DBG_LOW("IPA driver notified\n");
  575. bail:
  576. kfree(notify_work);
  577. }
  578. static void ipa_rm_perf_profile_notify_to_ipa(enum ipa_voltage_level volt,
  579. u32 bandwidth)
  580. {
  581. struct ipa_rm_notify_ipa_work_type *work;
  582. work = kzalloc(sizeof(*work), GFP_ATOMIC);
  583. if (!work)
  584. return;
  585. INIT_WORK(&work->work, ipa_rm_perf_profile_notify_to_ipa_work);
  586. work->volt = volt;
  587. work->bandwidth_mbps = bandwidth;
  588. queue_work(ipa_rm_ctx->ipa_rm_wq, &work->work);
  589. }
  590. /**
  591. * ipa_rm_perf_profile_change() - change performance profile vote for resource
  592. * @resource_name: [in] resource name
  593. *
  594. * change bandwidth and voltage vote based on resource state.
  595. */
  596. void ipa_rm_perf_profile_change(enum ipa_rm_resource_name resource_name)
  597. {
  598. enum ipa_voltage_level old_volt;
  599. u32 *bw_ptr;
  600. u32 old_bw;
  601. struct ipa_rm_resource *resource;
  602. int i;
  603. u32 sum_bw_prod = 0;
  604. u32 sum_bw_cons = 0;
  605. IPA_RM_DBG_LOW("%s\n", ipa_rm_resource_str(resource_name));
  606. if (ipa_rm_dep_graph_get_resource(ipa_rm_ctx->dep_graph,
  607. resource_name,
  608. &resource) != 0) {
  609. IPA_RM_ERR("resource does not exists\n");
  610. WARN_ON(1);
  611. return;
  612. }
  613. old_volt = ipa_rm_ctx->prof_vote.curr_volt;
  614. old_bw = ipa_rm_ctx->prof_vote.curr_bw;
  615. bw_ptr = &ipa_rm_ctx->prof_vote.bw_resources[resource_name];
  616. switch (resource->state) {
  617. case IPA_RM_GRANTED:
  618. case IPA_RM_REQUEST_IN_PROGRESS:
  619. IPA_RM_DBG_LOW("max_bw = %d, needed_bw = %d\n",
  620. resource->max_bw, resource->needed_bw);
  621. *bw_ptr = min(resource->max_bw, resource->needed_bw);
  622. ipa_rm_ctx->prof_vote.volt[resource_name] =
  623. resource->floor_voltage;
  624. break;
  625. case IPA_RM_RELEASE_IN_PROGRESS:
  626. case IPA_RM_RELEASED:
  627. *bw_ptr = 0;
  628. ipa_rm_ctx->prof_vote.volt[resource_name] = 0;
  629. break;
  630. default:
  631. IPA_RM_ERR("unknown state %d\n", resource->state);
  632. WARN_ON(1);
  633. return;
  634. }
  635. IPA_RM_DBG_LOW("resource bandwidth: %d voltage: %d\n", *bw_ptr,
  636. resource->floor_voltage);
  637. ipa_rm_ctx->prof_vote.curr_volt = IPA_VOLTAGE_UNSPECIFIED;
  638. for (i = 0; i < IPA_RM_RESOURCE_MAX; i++) {
  639. if (ipa_rm_ctx->prof_vote.volt[i] >
  640. ipa_rm_ctx->prof_vote.curr_volt) {
  641. ipa_rm_ctx->prof_vote.curr_volt =
  642. ipa_rm_ctx->prof_vote.volt[i];
  643. }
  644. }
  645. for (i = 0; i < IPA_RM_RESOURCE_MAX; i++) {
  646. if (IPA_RM_RESORCE_IS_PROD(i))
  647. sum_bw_prod += ipa_rm_ctx->prof_vote.bw_resources[i];
  648. else
  649. sum_bw_cons += ipa_rm_ctx->prof_vote.bw_resources[i];
  650. }
  651. IPA_RM_DBG_LOW("all prod bandwidth: %d all cons bandwidth: %d\n",
  652. sum_bw_prod, sum_bw_cons);
  653. ipa_rm_ctx->prof_vote.curr_bw = min(sum_bw_prod, sum_bw_cons);
  654. if (ipa_rm_ctx->prof_vote.curr_volt == old_volt &&
  655. ipa_rm_ctx->prof_vote.curr_bw == old_bw) {
  656. IPA_RM_DBG_LOW("same voting\n");
  657. return;
  658. }
  659. IPA_RM_DBG_LOW("new voting: voltage %d bandwidth %d\n",
  660. ipa_rm_ctx->prof_vote.curr_volt,
  661. ipa_rm_ctx->prof_vote.curr_bw);
  662. ipa_rm_perf_profile_notify_to_ipa(ipa_rm_ctx->prof_vote.curr_volt,
  663. ipa_rm_ctx->prof_vote.curr_bw);
  664. return;
  665. };
  666. /**
  667. * ipa_rm_exit() - free all IPA RM resources
  668. */
  669. void ipa_rm_exit(void)
  670. {
  671. IPA_RM_DBG("ENTER\n");
  672. ipa_rm_dep_graph_delete(ipa_rm_ctx->dep_graph);
  673. destroy_workqueue(ipa_rm_ctx->ipa_rm_wq);
  674. kfree(ipa_rm_ctx);
  675. ipa_rm_ctx = NULL;
  676. IPA_RM_DBG("EXIT\n");
  677. }