maxim_dsm_cal.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /*
  2. * maxim_dsm_cal.c -- Module for Rdc calibration
  3. *
  4. * Copyright 2014 Maxim Integrated Products
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. */
  10. #include <linux/init.h>
  11. #include <linux/module.h>
  12. #include <linux/device.h>
  13. #include <linux/workqueue.h>
  14. #include <linux/moduleparam.h>
  15. #include <linux/slab.h>
  16. #include <linux/regmap.h>
  17. #include <linux/delay.h>
  18. #include <linux/uaccess.h>
  19. #include <linux/syscalls.h>
  20. #include <linux/file.h>
  21. #include <linux/fcntl.h>
  22. #include <linux/power_supply.h>
  23. #include <sound/maxim_dsm.h>
  24. #include <sound/maxim_dsm_cal.h>
  25. #define DEBUG_MAXIM_DSM_CAL
  26. #ifdef DEBUG_MAXIM_DSM_CAL
  27. #define dbg_maxdsm(format, args...) \
  28. pr_info("[MAXIM_DSM_CAL] %s: " format "\n", __func__, ## args)
  29. #else
  30. #define dbg_maxdsm(format, args...)
  31. #endif /* DEBUG_MAXIM_DSM_CAL */
  32. struct class *g_class;
  33. struct maxim_dsm_cal *g_mdc;
  34. static int maxdsm_cal_read_file(char *filename, char *data, size_t size)
  35. {
  36. struct file *cal_filp;
  37. mm_segment_t old_fs = get_fs();
  38. int ret;
  39. set_fs(KERNEL_DS);
  40. cal_filp = filp_open(filename, O_RDONLY, 0660);
  41. if (IS_ERR(cal_filp)) {
  42. pr_err("%s: there is no dsm_cal file\n", __func__);
  43. set_fs(old_fs);
  44. ret = 0;
  45. return ret;
  46. }
  47. ret = cal_filp->f_op->read(cal_filp, data, size, &cal_filp->f_pos);
  48. if (ret != size) {
  49. pr_err("%s: can't read dsm calibration value to file\n",
  50. __func__);
  51. ret = -EIO;
  52. }
  53. filp_close(cal_filp, current->files);
  54. set_fs(old_fs);
  55. return ret;
  56. }
  57. static int maxdsm_cal_write_file(char *filename, char *data, size_t size)
  58. {
  59. struct file *cal_filp;
  60. mm_segment_t old_fs = get_fs();
  61. int ret;
  62. set_fs(KERNEL_DS);
  63. cal_filp = filp_open(filename,
  64. O_CREAT | O_TRUNC | O_WRONLY, 0660);
  65. if (IS_ERR(cal_filp)) {
  66. pr_err("%s: Can't open calibration file\n", __func__);
  67. set_fs(old_fs);
  68. ret = PTR_ERR(cal_filp);
  69. return ret;
  70. }
  71. ret = cal_filp->f_op->write(cal_filp, data, size, &cal_filp->f_pos);
  72. if (ret != size) {
  73. pr_err("%s: can't write dsm calibration value to file\n",
  74. __func__);
  75. ret = -EIO;
  76. }
  77. filp_close(cal_filp, current->files);
  78. set_fs(old_fs);
  79. return ret;
  80. }
  81. struct regmap *maxdsm_cal_set_regmap(
  82. struct regmap *regmap)
  83. {
  84. g_mdc->regmap = regmap;
  85. #if defined(CONFIG_SND_SOC_MAXIM_DSM) && defined(USE_DSM_LOG)
  86. maxdsm_set_regmap(g_mdc->regmap);
  87. #endif /* CONFIG_SND_SOC_MAXIM_DSM && USE_DSM_LOG */
  88. return g_mdc->regmap;
  89. }
  90. EXPORT_SYMBOL_GPL(maxdsm_cal_set_regmap);
  91. int maxdsm_cal_set_temp(uint32_t value)
  92. {
  93. char data[12];
  94. int ret;
  95. memset(data, 0x00, sizeof(data));
  96. sprintf(data, "%x", value);
  97. ret = maxdsm_cal_write_file(FILEPATH_TEMP_CAL,
  98. data, sizeof(data));
  99. g_mdc->values.temp = ret < 0 ? ret : value;
  100. return ret;
  101. }
  102. EXPORT_SYMBOL_GPL(maxdsm_cal_set_temp);
  103. int maxdsm_cal_get_temp(uint32_t *value)
  104. {
  105. char data[12];
  106. int ret;
  107. memset(data, 0x00, sizeof(data));
  108. ret = maxdsm_cal_read_file(FILEPATH_TEMP_CAL,
  109. data, sizeof(data));
  110. if (ret < 0)
  111. g_mdc->values.temp = ret;
  112. else
  113. ret = kstrtos32(data, 16, value);
  114. return ret;
  115. }
  116. EXPORT_SYMBOL_GPL(maxdsm_cal_get_temp);
  117. int maxdsm_cal_set_rdc(uint32_t value)
  118. {
  119. char data[12];
  120. int ret;
  121. memset(data, 0x00, sizeof(data));
  122. sprintf(data, "%x", value);
  123. ret = maxdsm_cal_write_file(FILEPATH_RDC_CAL,
  124. data, sizeof(data));
  125. g_mdc->values.rdc = ret < 0 ? ret : value;
  126. return ret;
  127. }
  128. EXPORT_SYMBOL_GPL(maxdsm_cal_set_rdc);
  129. int maxdsm_cal_get_rdc(uint32_t *value)
  130. {
  131. char data[12];
  132. int ret;
  133. memset(data, 0x00, sizeof(data));
  134. ret = maxdsm_cal_read_file(FILEPATH_RDC_CAL,
  135. data, sizeof(data));
  136. if (ret < 0)
  137. g_mdc->values.rdc = ret;
  138. else
  139. ret = kstrtos32(data, 16, value);
  140. return ret;
  141. }
  142. EXPORT_SYMBOL_GPL(maxdsm_cal_get_rdc);
  143. static int maxdsm_cal_regmap_write(struct regmap *regmap,
  144. unsigned int reg,
  145. unsigned int val)
  146. {
  147. return regmap ?
  148. regmap_write(g_mdc->regmap, reg, val) : -ENXIO;
  149. }
  150. static int maxdsm_cal_regmap_read(struct regmap *regmap,
  151. unsigned int reg,
  152. unsigned int *val)
  153. {
  154. return regmap ?
  155. regmap_read(g_mdc->regmap, reg, val) : -ENXIO;
  156. }
  157. static void maxdsm_cal_start_calibration(
  158. struct maxim_dsm_cal *mdc)
  159. {
  160. #ifdef CONFIG_SND_SOC_MAXIM_DSM
  161. mdc->platform_type = maxdsm_get_platform_type();
  162. #else
  163. mdc->platform_type = 0;
  164. #endif /* CONFIG_SND_SOC_MAXIM_DSM */
  165. dbg_maxdsm("platform_type=%d", mdc->platform_type);
  166. switch (mdc->platform_type) {
  167. case PLATFORM_TYPE_A:
  168. maxdsm_cal_regmap_read(mdc->regmap,
  169. ADDR_FEATURE_ENABLE,
  170. &mdc->info.feature_en);
  171. maxdsm_cal_regmap_write(mdc->regmap,
  172. ADDR_FEATURE_ENABLE,
  173. 0x3F);
  174. break;
  175. case PLATFORM_TYPE_B:
  176. #ifdef CONFIG_SND_SOC_MAXIM_DSM
  177. maxdsm_set_feature_en(1);
  178. #endif /* CONFIG_SND_SOC_MAXIM_DSM */
  179. break;
  180. default:
  181. break;
  182. }
  183. mdc->info.previous_jiffies = jiffies;
  184. }
  185. static uint32_t maxdsm_cal_read_dcresistance(
  186. struct maxim_dsm_cal *mdc)
  187. {
  188. uint32_t dcresistance = 0;
  189. switch (mdc->platform_type) {
  190. case PLATFORM_TYPE_A:
  191. maxdsm_cal_regmap_read(mdc->regmap,
  192. ADDR_RDC,
  193. &dcresistance);
  194. break;
  195. case PLATFORM_TYPE_B:
  196. #ifdef CONFIG_SND_SOC_MAXIM_DSM
  197. dcresistance = maxdsm_get_dcresistance();
  198. #endif /* CONFIG_SND_SOC_MAXIM_DSM */
  199. break;
  200. default:
  201. break;
  202. }
  203. return dcresistance;
  204. }
  205. static void maxdsm_cal_end_calibration(
  206. struct maxim_dsm_cal *mdc)
  207. {
  208. switch (mdc->platform_type) {
  209. case PLATFORM_TYPE_A:
  210. maxdsm_cal_regmap_write(mdc->regmap,
  211. ADDR_FEATURE_ENABLE,
  212. mdc->info.feature_en);
  213. break;
  214. case PLATFORM_TYPE_B:
  215. #ifdef CONFIG_SND_SOC_MAXIM_DSM
  216. maxdsm_set_rdc_temp(mdc->values.rdc,
  217. (uint32_t)mdc->values.temp / 10);
  218. maxdsm_set_feature_en(0);
  219. #endif /* CONFIG_SND_SOC_MAXIM_DSM */
  220. break;
  221. default:
  222. break;
  223. }
  224. }
  225. static int maxdsm_cal_get_temp_from_power_supply(void)
  226. {
  227. union power_supply_propval value = {0,};
  228. struct power_supply *psy;
  229. int temperature;
  230. psy = power_supply_get_by_name("battery");
  231. if (!psy) {
  232. pr_err("%s: Failed to get psy\n", __func__);
  233. temperature = 23 * 10;
  234. } else {
  235. psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &value);
  236. temperature = value.intval;
  237. }
  238. dbg_maxdsm("temperature=%d", temperature);
  239. return temperature;
  240. }
  241. static void maxdsm_cal_completed(struct maxim_dsm_cal *mdc)
  242. {
  243. char rdc[12] = {0,};
  244. char temp[12] = {0,};
  245. int ret;
  246. /* We try to get ambient temp by using power supply core */
  247. mdc->values.temp = maxdsm_cal_get_temp_from_power_supply();
  248. sprintf(rdc, "%x", mdc->values.rdc);
  249. sprintf(temp, "%x",
  250. mdc->values.temp < 0 ? 23 * 10 : mdc->values.temp);
  251. ret = maxdsm_cal_write_file(
  252. FILEPATH_TEMP_CAL, temp, sizeof(temp));
  253. if (ret < 0)
  254. mdc->values.temp = ret;
  255. ret = maxdsm_cal_write_file(
  256. FILEPATH_RDC_CAL, rdc, sizeof(rdc));
  257. if (ret < 0)
  258. mdc->values.rdc = ret;
  259. maxdsm_cal_end_calibration(mdc);
  260. mdc->values.status = 0;
  261. dbg_maxdsm("temp=%d rdc=%d", mdc->values.temp, mdc->values.rdc);
  262. }
  263. static void maxdsm_cal_work(struct work_struct *work)
  264. {
  265. struct maxim_dsm_cal *mdc;
  266. unsigned int dcresistance = 0;
  267. unsigned long diff;
  268. mdc = container_of(work, struct maxim_dsm_cal, work.work);
  269. mutex_lock(&mdc->mutex);
  270. dcresistance = maxdsm_cal_read_dcresistance(mdc);
  271. if (!(mdc->info.min > dcresistance
  272. || mdc->info.max < dcresistance) &&
  273. ((mdc->info.duration - mdc->info.remaining)
  274. > mdc->info.ignored_t)) {
  275. mdc->values.avg += dcresistance;
  276. mdc->values.count++;
  277. }
  278. diff = jiffies - mdc->info.previous_jiffies;
  279. mdc->info.remaining
  280. -= jiffies_to_msecs(diff);
  281. dbg_maxdsm("dcresistance=%d remaining=%d duration=%d",
  282. dcresistance,
  283. mdc->info.remaining,
  284. mdc->info.duration);
  285. if (mdc->info.remaining > 0
  286. && mdc->values.status) {
  287. mdc->info.previous_jiffies = jiffies;
  288. queue_delayed_work(mdc->wq,
  289. &mdc->work,
  290. msecs_to_jiffies(mdc->info.interval));
  291. } else {
  292. mdc->values.count > 0 ?
  293. do_div(mdc->values.avg, mdc->values.count) : 0;
  294. mdc->values.rdc = mdc->values.avg;
  295. maxdsm_cal_completed(mdc);
  296. }
  297. mutex_unlock(&mdc->mutex);
  298. }
  299. static void maxdsm_cal_check(
  300. struct maxim_dsm_cal *mdc, int action)
  301. {
  302. if (delayed_work_pending(&mdc->work))
  303. cancel_delayed_work(&mdc->work);
  304. if (action) {
  305. mdc->info.remaining = mdc->info.duration;
  306. mdc->values.count = mdc->values.avg = 0;
  307. maxdsm_cal_start_calibration(mdc);
  308. queue_delayed_work(mdc->wq,
  309. &mdc->work,
  310. 1);
  311. }
  312. }
  313. static ssize_t maxdsm_cal_min_show(struct device *dev,
  314. struct device_attribute *attr, char *buf)
  315. {
  316. return sprintf(buf, "%d", g_mdc->info.min);
  317. }
  318. static ssize_t maxdsm_cal_min_store(struct device *dev,
  319. struct device_attribute *attr, const char *buf, size_t size)
  320. {
  321. if (kstrtou32(buf, 0, &g_mdc->info.min))
  322. dev_err(dev,
  323. "%s: Failed converting from str to u32.\n", __func__);
  324. return size;
  325. }
  326. static DEVICE_ATTR(min, S_IRUGO | S_IWUSR | S_IWGRP,
  327. maxdsm_cal_min_show, maxdsm_cal_min_store);
  328. static ssize_t maxdsm_cal_max_show(struct device *dev,
  329. struct device_attribute *attr, char *buf)
  330. {
  331. return sprintf(buf, "%d", g_mdc->info.max);
  332. }
  333. static ssize_t maxdsm_cal_max_store(struct device *dev,
  334. struct device_attribute *attr, const char *buf, size_t size)
  335. {
  336. if (kstrtou32(buf, 0, &g_mdc->info.max))
  337. dev_err(dev,
  338. "%s: Failed converting from str to u32.\n", __func__);
  339. return size;
  340. }
  341. static DEVICE_ATTR(max, S_IRUGO | S_IWUSR | S_IWGRP,
  342. maxdsm_cal_max_show, maxdsm_cal_max_store);
  343. static ssize_t maxdsm_cal_duration_show(struct device *dev,
  344. struct device_attribute *attr, char *buf)
  345. {
  346. return sprintf(buf, "%d", g_mdc->info.duration);
  347. }
  348. static ssize_t maxdsm_cal_duration_store(struct device *dev,
  349. struct device_attribute *attr, const char *buf, size_t size)
  350. {
  351. if (kstrtou32(buf, 0, &g_mdc->info.duration))
  352. dev_err(dev,
  353. "%s: Failed converting from str to u32.\n", __func__);
  354. return size;
  355. }
  356. static DEVICE_ATTR(duration, S_IRUGO | S_IWUSR | S_IWGRP,
  357. maxdsm_cal_duration_show, maxdsm_cal_duration_store);
  358. static ssize_t maxdsm_cal_rdc_show(struct device *dev,
  359. struct device_attribute *attr, char *buf)
  360. {
  361. char rdc[12] = {0,};
  362. int ret;
  363. if (g_mdc->values.rdc == 0xFFFFFFFF) {
  364. ret = maxdsm_cal_read_file(
  365. FILEPATH_RDC_CAL, rdc, sizeof(rdc));
  366. if (ret < 0)
  367. g_mdc->values.rdc = ret;
  368. else
  369. ret = kstrtos32(rdc, 16, &g_mdc->values.rdc);
  370. }
  371. return sprintf(buf, "%x", g_mdc->values.rdc);
  372. }
  373. static ssize_t maxdsm_cal_rdc_store(struct device *dev,
  374. struct device_attribute *attr, const char *buf, size_t size)
  375. {
  376. char rdc[12] = {0,};
  377. int ret;
  378. if (kstrtos32(buf, 0, &g_mdc->values.rdc))
  379. dev_err(dev,
  380. "%s: Failed converting from str to u32.\n", __func__);
  381. else {
  382. sprintf(rdc, "%x", g_mdc->values.rdc);
  383. ret = maxdsm_cal_write_file(
  384. FILEPATH_RDC_CAL, rdc, sizeof(rdc));
  385. if (ret < 0)
  386. g_mdc->values.rdc = ret;
  387. }
  388. return size;
  389. }
  390. static DEVICE_ATTR(rdc, S_IRUGO | S_IWUSR | S_IWGRP,
  391. maxdsm_cal_rdc_show, maxdsm_cal_rdc_store);
  392. static ssize_t maxdsm_cal_temp_show(struct device *dev,
  393. struct device_attribute *attr, char *buf)
  394. {
  395. char temp[12] = {0,};
  396. int ret;
  397. if (g_mdc->values.temp == 0xFFFFFFFF) {
  398. ret = maxdsm_cal_read_file(
  399. FILEPATH_TEMP_CAL, temp, sizeof(temp));
  400. if (ret < 0)
  401. g_mdc->values.temp = ret;
  402. else
  403. ret = kstrtos32(temp, 16, &g_mdc->values.temp);
  404. }
  405. return sprintf(buf, "%x", g_mdc->values.temp);
  406. }
  407. static ssize_t maxdsm_cal_temp_store(struct device *dev,
  408. struct device_attribute *attr, const char *buf, size_t size)
  409. {
  410. char temp[12] = {0,};
  411. int ret;
  412. if (kstrtos32(buf, 0, &g_mdc->values.temp))
  413. dev_err(dev,
  414. "%s: Failed converting from str to u32.\n", __func__);
  415. else {
  416. sprintf(temp, "%x", g_mdc->values.temp);
  417. ret = maxdsm_cal_write_file(
  418. FILEPATH_TEMP_CAL, temp, sizeof(temp));
  419. if (ret < 0)
  420. g_mdc->values.temp = ret;
  421. }
  422. return size;
  423. }
  424. static DEVICE_ATTR(temp, S_IRUGO | S_IWUSR | S_IWGRP,
  425. maxdsm_cal_temp_show, maxdsm_cal_temp_store);
  426. static ssize_t maxdsm_cal_interval_show(struct device *dev,
  427. struct device_attribute *attr, char *buf)
  428. {
  429. return sprintf(buf, "%d", g_mdc->info.interval);
  430. }
  431. static ssize_t maxdsm_cal_interval_store(struct device *dev,
  432. struct device_attribute *attr, const char *buf, size_t size)
  433. {
  434. if (kstrtou32(buf, 0, &g_mdc->info.interval))
  435. dev_err(dev,
  436. "%s: Failed converting from str to u32.\n", __func__);
  437. return size;
  438. }
  439. static DEVICE_ATTR(interval, S_IRUGO | S_IWUSR | S_IWGRP,
  440. maxdsm_cal_interval_show, maxdsm_cal_interval_store);
  441. static ssize_t maxdsm_cal_ignored_t_show(struct device *dev,
  442. struct device_attribute *attr, char *buf)
  443. {
  444. return sprintf(buf, "%d", g_mdc->info.ignored_t);
  445. }
  446. static ssize_t maxdsm_cal_ignored_t_store(struct device *dev,
  447. struct device_attribute *attr, const char *buf, size_t size)
  448. {
  449. if (kstrtou32(buf, 0, &g_mdc->info.ignored_t))
  450. dev_err(dev,
  451. "%s: Failed converting from str to u32.\n", __func__);
  452. return size;
  453. }
  454. static DEVICE_ATTR(ignored_t, S_IRUGO | S_IWUSR | S_IWGRP,
  455. maxdsm_cal_ignored_t_show, maxdsm_cal_ignored_t_store);
  456. static ssize_t maxdsm_cal_status_show(struct device *dev,
  457. struct device_attribute *attr, char *buf)
  458. {
  459. return sprintf(buf, "%s\n",
  460. g_mdc->values.status ? "Enabled" : "Disabled");
  461. }
  462. static ssize_t maxdsm_cal_status_store(struct device *dev,
  463. struct device_attribute *attr, const char *buf, size_t size)
  464. {
  465. int status = 0;
  466. if (!kstrtou32(buf, 0, &status)) {
  467. if (status == g_mdc->values.status) {
  468. dbg_maxdsm("Already run. It will be ignored.");
  469. } else {
  470. mutex_lock(&g_mdc->mutex);
  471. g_mdc->values.status = status;
  472. mutex_unlock(&g_mdc->mutex);
  473. if (g_mdc->values.status)
  474. maxdsm_cal_check(g_mdc, 1);
  475. else
  476. maxdsm_cal_check(g_mdc, 0);
  477. }
  478. }
  479. return size;
  480. }
  481. static DEVICE_ATTR(status, S_IRUGO | S_IWUSR | S_IWGRP,
  482. maxdsm_cal_status_show, maxdsm_cal_status_store);
  483. static struct attribute *maxdsm_cal_attr[] = {
  484. &dev_attr_min.attr,
  485. &dev_attr_max.attr,
  486. &dev_attr_duration.attr,
  487. &dev_attr_rdc.attr,
  488. &dev_attr_temp.attr,
  489. &dev_attr_interval.attr,
  490. &dev_attr_ignored_t.attr,
  491. &dev_attr_status.attr,
  492. NULL,
  493. };
  494. static struct attribute_group maxdsm_cal_attr_grp = {
  495. .attrs = maxdsm_cal_attr,
  496. };
  497. static int __init maxdsm_cal_init(void)
  498. {
  499. struct maxim_dsm_cal *mdc;
  500. int ret = 0;
  501. g_mdc = kzalloc(sizeof(struct maxim_dsm_cal), GFP_KERNEL);
  502. if (g_mdc == NULL)
  503. return -ENOMEM;
  504. mdc = g_mdc;
  505. mdc->wq = create_singlethread_workqueue(WQ_NAME);
  506. if (mdc->wq == NULL) {
  507. kfree(g_mdc);
  508. return -ENOMEM;
  509. }
  510. INIT_DELAYED_WORK(&g_mdc->work, maxdsm_cal_work);
  511. mutex_init(&g_mdc->mutex);
  512. mdc->info.min = 0;
  513. mdc->info.max = 0xFFFFFFFF;
  514. mdc->info.duration = 1500; /* 1.5 secs */
  515. mdc->info.remaining = mdc->info.duration;
  516. mdc->info.interval = 100;
  517. mdc->info.ignored_t = 1000;
  518. mdc->values.rdc = 0xFFFFFFFF;
  519. mdc->values.temp = 0xFFFFFFFF;
  520. mdc->platform_type = 0xFFFFFFFF;
  521. if (!g_class)
  522. g_class = class_create(THIS_MODULE, DSM_NAME);
  523. mdc->class = g_class;
  524. if (mdc->class) {
  525. mdc->dev = device_create(mdc->class, NULL, 1, NULL, CLASS_NAME);
  526. if (!IS_ERR(mdc->dev)) {
  527. if (sysfs_create_group(&mdc->dev->kobj,
  528. &maxdsm_cal_attr_grp))
  529. dbg_maxdsm(
  530. "Failed to create sysfs group. ret=%d",
  531. ret);
  532. }
  533. }
  534. dbg_maxdsm("g_class=%p %p", g_class, mdc->class);
  535. dbg_maxdsm("Completed initialization");
  536. return ret;
  537. }
  538. module_init(maxdsm_cal_init);
  539. static void __exit maxdsm_cal_exit(void)
  540. {
  541. kfree(g_mdc);
  542. }
  543. module_exit(maxdsm_cal_exit);
  544. MODULE_DESCRIPTION(DRIVER_DESC);
  545. MODULE_AUTHOR(DRIVER_AUTHOR);
  546. MODULE_SUPPORTED_DEVICE(DRIVER_SUPPORTED);
  547. MODULE_LICENSE("GPL");