cirrus-bd.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. /*
  2. * Big-data logging support for Cirrus Logic Smart Amplifiers
  3. *
  4. * Copyright 2017 Cirrus Logic
  5. *
  6. * Author: David Rhodes <[email protected]>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #include <linux/module.h>
  13. #include <linux/miscdevice.h>
  14. #include <linux/device.h>
  15. #include <linux/uaccess.h>
  16. #include <linux/delay.h>
  17. #include <linux/regmap.h>
  18. #include <linux/slab.h>
  19. #include <linux/syscalls.h>
  20. #include <linux/file.h>
  21. #include <linux/fcntl.h>
  22. #include <linux/fs.h>
  23. #include <linux/init.h>
  24. #include <linux/platform_device.h>
  25. #include <linux/firmware.h>
  26. #include <linux/vmalloc.h>
  27. #include <linux/workqueue.h>
  28. #include <linux/power_supply.h>
  29. #include <linux/pm_runtime.h>
  30. #include <linux/fs.h>
  31. #include <linux/ktime.h>
  32. #include <sound/cirrus/core.h>
  33. #include <sound/cirrus/big_data.h>
  34. #include <linux/firmware/cirrus/wmfw.h>
  35. #include "wm_adsp.h"
  36. #define CIRRUS_BD_VERSION "5.01.18"
  37. #define CIRRUS_BD_DIR_NAME "cirrus_bd"
  38. int cirrus_bd_write_ctl(struct cirrus_amp *amp, const char *name,
  39. int type, unsigned int id, unsigned int value)
  40. {
  41. int ret;
  42. char *ctl_name = kzalloc(PAGE_SIZE, GFP_KERNEL);
  43. if (!ctl_name)
  44. return -ENOMEM;
  45. snprintf(ctl_name, PAGE_SIZE, "%s%s", amp->bd.bd_prefix, name);
  46. ret = cirrus_amp_write_ctl(amp, ctl_name, type, id, value);
  47. kfree(ctl_name);
  48. return ret;
  49. }
  50. int cirrus_bd_read_ctl(struct cirrus_amp *amp, const char *name,
  51. int type, unsigned int id, unsigned int *value)
  52. {
  53. int ret;
  54. char *ctl_name = kzalloc(PAGE_SIZE, GFP_KERNEL);
  55. if (!ctl_name)
  56. return -ENOMEM;
  57. snprintf(ctl_name, PAGE_SIZE, "%s%s", amp->bd.bd_prefix, name);
  58. ret = cirrus_amp_read_ctl(amp, ctl_name, type, id, value);
  59. kfree(ctl_name);
  60. return ret;
  61. }
  62. void cirrus_bd_store_values(const char *mfd_suffix)
  63. {
  64. unsigned int max_exc = 0, over_exc_count = 0, max_temp = 0,
  65. over_temp_count = 0, abnm_mute = 0;
  66. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(mfd_suffix);
  67. if (!amp)
  68. return;
  69. if (amp->runtime_pm)
  70. pm_runtime_get_sync(amp->component->dev);
  71. cirrus_bd_read_ctl(amp, "MAX_TEMP", WMFW_ADSP2_XM,
  72. amp->bd.bd_alg_id, &max_temp);
  73. cirrus_bd_read_ctl(amp, "MAX_EXC", WMFW_ADSP2_XM,
  74. amp->bd.bd_alg_id, &max_exc);
  75. cirrus_bd_read_ctl(amp, "OVER_TEMP_COUNT", WMFW_ADSP2_XM,
  76. amp->bd.bd_alg_id, &over_temp_count);
  77. cirrus_bd_read_ctl(amp, "OVER_EXC_COUNT", WMFW_ADSP2_XM,
  78. amp->bd.bd_alg_id, &over_exc_count);
  79. cirrus_bd_read_ctl(amp, "ABNORMAL_MUTE", WMFW_ADSP2_XM,
  80. amp->bd.bd_alg_id, &abnm_mute);
  81. if (abnm_mute)
  82. max_temp = CIRRUS_BD_ERR_TEMP;
  83. else if (max_temp > (amp->bd.max_temp_limit * (1 << CIRRUS_BD_TEMP_RADIX))
  84. && over_temp_count == 0)
  85. max_temp = (amp->bd.max_temp_limit *
  86. (1 << CIRRUS_BD_TEMP_RADIX));
  87. amp->bd.over_temp_count += over_temp_count;
  88. amp->bd.over_exc_count += over_exc_count;
  89. if (max_exc > amp->bd.max_exc)
  90. amp->bd.max_exc = max_exc;
  91. if (max_temp > amp->bd.max_temp)
  92. amp->bd.max_temp = max_temp;
  93. amp->bd.abnm_mute += abnm_mute;
  94. amp->bd.max_temp_keep = amp->bd.max_temp;
  95. amp_group->last_bd_update = ktime_to_ns(ktime_get());
  96. dev_info(amp_group->bd_dev, "Values stored for amp%s:\n", mfd_suffix);
  97. dev_info(amp_group->bd_dev, "Max Excursion:\t\t%d.%04d\n",
  98. amp->bd.max_exc >> CIRRUS_BD_EXC_RADIX,
  99. (amp->bd.max_exc & (((1 << CIRRUS_BD_EXC_RADIX) - 1))) *
  100. 10000 / (1 << CIRRUS_BD_EXC_RADIX));
  101. dev_info(amp_group->bd_dev, "Over Excursion Count:\t%d\n",
  102. amp->bd.over_exc_count);
  103. dev_info(amp_group->bd_dev, "Max Temp:\t\t\t%d.%04d\n",
  104. amp->bd.max_temp >> CIRRUS_BD_TEMP_RADIX,
  105. (amp->bd.max_temp & (((1 << CIRRUS_BD_TEMP_RADIX) - 1))) *
  106. 10000 / (1 << CIRRUS_BD_TEMP_RADIX));
  107. dev_info(amp_group->bd_dev, "Over Temp Count:\t\t%d\n",
  108. amp->bd.over_temp_count);
  109. dev_info(amp_group->bd_dev, "Abnormal Mute:\t\t%d\n",
  110. amp->bd.abnm_mute);
  111. dev_info(amp_group->bd_dev, "Timestamp:\t\t%llu\n",
  112. amp_group->last_bd_update);
  113. cirrus_bd_write_ctl(amp, "MAX_TEMP", WMFW_ADSP2_XM,
  114. amp->bd.bd_alg_id, 0);
  115. cirrus_bd_write_ctl(amp, "MAX_EXC", WMFW_ADSP2_XM,
  116. amp->bd.bd_alg_id, 0);
  117. cirrus_bd_write_ctl(amp, "OVER_TEMP_COUNT", WMFW_ADSP2_XM,
  118. amp->bd.bd_alg_id, 0);
  119. cirrus_bd_write_ctl(amp, "OVER_EXC_COUNT", WMFW_ADSP2_XM,
  120. amp->bd.bd_alg_id, 0);
  121. cirrus_bd_write_ctl(amp, "ABNORMAL_MUTE", WMFW_ADSP2_XM,
  122. amp->bd.bd_alg_id, 0);
  123. if (amp->runtime_pm) {
  124. pm_runtime_mark_last_busy(amp->component->dev);
  125. pm_runtime_put_autosuspend(amp->component->dev);
  126. }
  127. }
  128. EXPORT_SYMBOL_GPL(cirrus_bd_store_values);
  129. void cirrus_bd_amp_err(const char *mfd_suffix)
  130. {
  131. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(mfd_suffix);
  132. if (!amp)
  133. return;
  134. if (amp->error_callback)
  135. amp->error_callback(mfd_suffix);
  136. }
  137. EXPORT_SYMBOL_GPL(cirrus_bd_amp_err);
  138. void cirrus_bd_bst_short(const char *mfd_suffix)
  139. {
  140. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(mfd_suffix);
  141. if (!amp)
  142. return;
  143. if (amp->error_callback)
  144. amp->error_callback(mfd_suffix);
  145. }
  146. EXPORT_SYMBOL_GPL(cirrus_bd_bst_short);
  147. /***** SYSFS Interfaces *****/
  148. static ssize_t cirrus_bd_version_show(struct device *dev,
  149. struct device_attribute *attr,
  150. char *buf)
  151. {
  152. return sprintf(buf, CIRRUS_BD_VERSION "\n");
  153. }
  154. static ssize_t cirrus_bd_version_store(struct device *dev,
  155. struct device_attribute *attr,
  156. const char *buf, size_t size)
  157. {
  158. return 0;
  159. }
  160. static ssize_t cirrus_bd_max_exc_show(struct device *dev,
  161. struct device_attribute *attr,
  162. char *buf)
  163. {
  164. const char *suffix = &(attr->attr.name[strlen("max_exc")]);
  165. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
  166. int ret;
  167. if (!amp)
  168. return 0;
  169. ret = sprintf(buf, "%d.%04d\n", amp->bd.max_exc >> CIRRUS_BD_EXC_RADIX,
  170. (amp->bd.max_exc & ((1 << CIRRUS_BD_EXC_RADIX) - 1)) *
  171. 10000 / (1 << CIRRUS_BD_EXC_RADIX));
  172. amp->bd.max_exc = 0;
  173. return ret;
  174. }
  175. static ssize_t cirrus_bd_max_exc_store(struct device *dev,
  176. struct device_attribute *attr,
  177. const char *buf, size_t size)
  178. {
  179. return 0;
  180. }
  181. static ssize_t cirrus_bd_over_exc_count_show(struct device *dev,
  182. struct device_attribute *attr,
  183. char *buf)
  184. {
  185. const char *suffix = &(attr->attr.name[strlen("over_exc_count")]);
  186. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
  187. int ret;
  188. if (!amp)
  189. return 0;
  190. ret = sprintf(buf, "%d\n", amp->bd.over_exc_count);
  191. amp->bd.over_exc_count = 0;
  192. return ret;
  193. }
  194. static ssize_t cirrus_bd_over_exc_count_store(struct device *dev,
  195. struct device_attribute *attr,
  196. const char *buf, size_t size)
  197. {
  198. return 0;
  199. }
  200. static ssize_t cirrus_bd_max_temp_show(struct device *dev,
  201. struct device_attribute *attr,
  202. char *buf)
  203. {
  204. const char *suffix = &(attr->attr.name[strlen("max_temp")]);
  205. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
  206. int ret;
  207. if (!amp)
  208. return 0;
  209. ret = sprintf(buf, "%d.%04d\n",
  210. amp->bd.max_temp >> CIRRUS_BD_TEMP_RADIX,
  211. (amp->bd.max_temp & ((1 << CIRRUS_BD_TEMP_RADIX) - 1)) *
  212. 10000 / (1 << CIRRUS_BD_TEMP_RADIX));
  213. amp->bd.max_temp = 0;
  214. return ret;
  215. }
  216. static ssize_t cirrus_bd_max_temp_store(struct device *dev,
  217. struct device_attribute *attr,
  218. const char *buf, size_t size)
  219. {
  220. return 0;
  221. }
  222. static ssize_t cirrus_bd_max_temp_keep_show(struct device *dev,
  223. struct device_attribute *attr,
  224. char *buf)
  225. {
  226. const char *suffix = &(attr->attr.name[strlen("max_temp_keep")]);
  227. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
  228. int ret;
  229. if (!amp)
  230. return 0;
  231. ret = sprintf(buf, "%d.%04d\n",
  232. amp->bd.max_temp_keep >> CIRRUS_BD_TEMP_RADIX,
  233. (amp->bd.max_temp_keep &
  234. ((1 << CIRRUS_BD_TEMP_RADIX) - 1)) *
  235. 10000 / (1 << CIRRUS_BD_TEMP_RADIX));
  236. return ret;
  237. }
  238. static ssize_t cirrus_bd_max_temp_keep_store(struct device *dev,
  239. struct device_attribute *attr,
  240. const char *buf, size_t size)
  241. {
  242. return 0;
  243. }
  244. static ssize_t cirrus_bd_over_temp_count_show(struct device *dev,
  245. struct device_attribute *attr,
  246. char *buf)
  247. {
  248. const char *suffix = &(attr->attr.name[strlen("over_temp_count")]);
  249. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
  250. int ret;
  251. if (!amp)
  252. return 0;
  253. ret = sprintf(buf, "%d\n", amp->bd.over_temp_count);
  254. amp->bd.over_temp_count = 0;
  255. return ret;
  256. }
  257. static ssize_t cirrus_bd_over_temp_count_store(struct device *dev,
  258. struct device_attribute *attr,
  259. const char *buf, size_t size)
  260. {
  261. return 0;
  262. }
  263. static ssize_t cirrus_bd_abnm_mute_show(struct device *dev,
  264. struct device_attribute *attr,
  265. char *buf)
  266. {
  267. const char *suffix = &(attr->attr.name[strlen("abnm_mute")]);
  268. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
  269. int ret;
  270. if (!amp)
  271. return 0;
  272. ret = sprintf(buf, "%d\n", amp->bd.abnm_mute);
  273. amp->bd.abnm_mute = 0;
  274. return ret;
  275. }
  276. static ssize_t cirrus_bd_abnm_mute_store(struct device *dev,
  277. struct device_attribute *attr,
  278. const char *buf, size_t size)
  279. {
  280. return 0;
  281. }
  282. static ssize_t cirrus_bd_store_show(struct device *dev,
  283. struct device_attribute *attr,
  284. char *buf)
  285. {
  286. return 0;
  287. }
  288. static ssize_t cirrus_bd_store_store(struct device *dev,
  289. struct device_attribute *attr,
  290. const char *buf, size_t size)
  291. {
  292. int store;
  293. int ret = kstrtos32(buf, 10, &store);
  294. const char *suffix = &(attr->attr.name[strlen("store")]);
  295. struct cirrus_amp *amp = cirrus_get_amp_from_suffix(suffix);
  296. if (ret == 0 && store == 1 && amp)
  297. cirrus_bd_store_values(suffix);
  298. return size;
  299. }
  300. static DEVICE_ATTR(version, 0444, cirrus_bd_version_show,
  301. cirrus_bd_version_store);
  302. static struct attribute *cirrus_bd_attr_base[] = {
  303. &dev_attr_version.attr,
  304. NULL,
  305. };
  306. static struct device_attribute generic_amp_attrs[CIRRUS_BD_NUM_ATTRS_AMP] = {
  307. {
  308. .attr = {.mode = VERIFY_OCTAL_PERMISSIONS(0444)},
  309. .show = cirrus_bd_max_exc_show,
  310. .store = cirrus_bd_max_exc_store,
  311. },
  312. {
  313. .attr = {.mode = VERIFY_OCTAL_PERMISSIONS(0444)},
  314. .show = cirrus_bd_over_exc_count_show,
  315. .store = cirrus_bd_over_exc_count_store,
  316. },
  317. {
  318. .attr = {.mode = VERIFY_OCTAL_PERMISSIONS(0444)},
  319. .show = cirrus_bd_max_temp_show,
  320. .store = cirrus_bd_max_temp_store,
  321. },
  322. {
  323. .attr = {.mode = VERIFY_OCTAL_PERMISSIONS(0444)},
  324. .show = cirrus_bd_max_temp_keep_show,
  325. .store = cirrus_bd_max_temp_keep_store,
  326. },
  327. {
  328. .attr = {.mode = VERIFY_OCTAL_PERMISSIONS(0444)},
  329. .show = cirrus_bd_over_temp_count_show,
  330. .store = cirrus_bd_over_temp_count_store,
  331. },
  332. {
  333. .attr = {.mode = VERIFY_OCTAL_PERMISSIONS(0444)},
  334. .show = cirrus_bd_abnm_mute_show,
  335. .store = cirrus_bd_abnm_mute_store,
  336. },
  337. {
  338. .attr = {.mode = VERIFY_OCTAL_PERMISSIONS(0644)},
  339. .show = cirrus_bd_store_show,
  340. .store = cirrus_bd_store_store,
  341. },
  342. };
  343. static const char *generic_amp_attr_names[CIRRUS_BD_NUM_ATTRS_AMP] = {
  344. "max_exc",
  345. "over_exc_count",
  346. "max_temp",
  347. "max_temp_keep",
  348. "over_temp_count",
  349. "abnm_mute",
  350. "store"
  351. };
  352. static struct attribute_group cirrus_bd_attr_grp;
  353. static struct device_attribute
  354. amp_attrs_prealloc[CIRRUS_MAX_AMPS][CIRRUS_BD_NUM_ATTRS_AMP];
  355. static char attr_names_prealloc[CIRRUS_MAX_AMPS][CIRRUS_BD_NUM_ATTRS_AMP][30];
  356. struct device_attribute *cirrus_bd_create_amp_attrs(const char *mfd_suffix,
  357. const char *bd_suffix,
  358. int index)
  359. {
  360. struct device_attribute *amp_attrs_new;
  361. int i, suffix_len;
  362. const char *suffix;
  363. suffix = (bd_suffix) ? bd_suffix : mfd_suffix;
  364. suffix_len = strlen(suffix);
  365. if (index >= CIRRUS_MAX_AMPS)
  366. return NULL;
  367. amp_attrs_new = &(amp_attrs_prealloc[index][0]);
  368. memcpy(amp_attrs_new, &generic_amp_attrs,
  369. sizeof(struct device_attribute) *
  370. CIRRUS_BD_NUM_ATTRS_AMP);
  371. for (i = 0; i < CIRRUS_BD_NUM_ATTRS_AMP - 1; i++) {
  372. amp_attrs_new[i].attr.name = attr_names_prealloc[index][i];
  373. snprintf((char *)amp_attrs_new[i].attr.name,
  374. strlen(generic_amp_attr_names[i]) + suffix_len + 1,
  375. "%s%s", generic_amp_attr_names[i], suffix);
  376. }
  377. /* "store" is special and will always be assigned the MFD suffix */
  378. amp_attrs_new[CIRRUS_BD_NUM_ATTRS_AMP - 1].attr.name =
  379. attr_names_prealloc[index][CIRRUS_BD_NUM_ATTRS_AMP - 1];
  380. snprintf((char *)amp_attrs_new[CIRRUS_BD_NUM_ATTRS_AMP - 1].attr.name,
  381. strlen("store") + strlen(mfd_suffix) + 1,
  382. "%s%s", "store", mfd_suffix);
  383. return amp_attrs_new;
  384. }
  385. int cirrus_bd_init(void)
  386. {
  387. struct device_attribute *new_attrs;
  388. struct cirrus_amp *amp;
  389. int ret = 0, i, j, num_amps;
  390. if (!amp_group) {
  391. pr_err("%s: Empty amp group\n", __func__);
  392. return -ENODATA;
  393. }
  394. amp_group->bd_dev = device_create(cirrus_amp_class, NULL, 1, NULL,
  395. CIRRUS_BD_DIR_NAME);
  396. if (IS_ERR(amp_group->bd_dev)) {
  397. ret = PTR_ERR(amp_group->bd_dev);
  398. pr_err("%s: Failed to create BD device (%d)\n", __func__, ret);
  399. return ret;
  400. }
  401. dev_set_drvdata(amp_group->bd_dev, amp_group);
  402. num_amps = amp_group->num_amps;
  403. cirrus_bd_attr_grp.attrs = kzalloc(sizeof(struct attribute *) *
  404. (CIRRUS_BD_NUM_ATTRS_AMP * num_amps +
  405. CIRRUS_BD_NUM_ATTRS_BASE + 1),
  406. GFP_KERNEL);
  407. for (i = 0; i < num_amps; i++) {
  408. amp = &amp_group->amps[i];
  409. new_attrs = cirrus_bd_create_amp_attrs(amp->mfd_suffix,
  410. amp->bd.bd_suffix, i);
  411. for (j = 0; j < CIRRUS_BD_NUM_ATTRS_AMP; j++) {
  412. dev_dbg(amp_group->bd_dev, "New attribute: %s\n",
  413. new_attrs[j].attr.name);
  414. cirrus_bd_attr_grp.attrs[i * CIRRUS_BD_NUM_ATTRS_AMP
  415. + j] = &new_attrs[j].attr;
  416. }
  417. }
  418. memcpy(&cirrus_bd_attr_grp.attrs[num_amps * CIRRUS_BD_NUM_ATTRS_AMP],
  419. cirrus_bd_attr_base, sizeof(struct attribute *) *
  420. CIRRUS_BD_NUM_ATTRS_BASE);
  421. cirrus_bd_attr_grp.attrs[num_amps * CIRRUS_BD_NUM_ATTRS_AMP +
  422. CIRRUS_BD_NUM_ATTRS_BASE] = NULL;
  423. ret = sysfs_create_group(&amp_group->bd_dev->kobj, &cirrus_bd_attr_grp);
  424. if (ret < 0) {
  425. dev_err(amp_group->bd_dev,
  426. "Failed to create sysfs group (%d)\n", ret);
  427. device_del(amp_group->bd_dev);
  428. }
  429. return ret;
  430. }
  431. EXPORT_SYMBOL_GPL(cirrus_bd_init);
  432. void cirrus_bd_exit(void)
  433. {
  434. kfree(cirrus_bd_attr_grp.attrs);
  435. device_del(amp_group->bd_dev);
  436. }
  437. EXPORT_SYMBOL_GPL(cirrus_bd_exit);