btfm_slim.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /* Copyright (c) 2016-2018, 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/init.h>
  13. #include <linux/kernel.h>
  14. #include <linux/module.h>
  15. #include <linux/of_gpio.h>
  16. #include <linux/delay.h>
  17. #include <linux/gpio.h>
  18. #include <linux/debugfs.h>
  19. #include <linux/ratelimit.h>
  20. #include <linux/slab.h>
  21. #include <sound/pcm.h>
  22. #include <sound/pcm_params.h>
  23. #include <sound/soc.h>
  24. #include <sound/soc-dapm.h>
  25. #include <sound/tlv.h>
  26. #include "btfm_slim.h"
  27. #include "btfm_slim_wcn3990.h"
  28. #include <linux/bluetooth-power.h>
  29. int btfm_slim_write(struct btfmslim *btfmslim,
  30. uint16_t reg, int bytes, void *src, uint8_t pgd)
  31. {
  32. int ret, i;
  33. struct slim_ele_access msg;
  34. int slim_write_tries = SLIM_SLAVE_RW_MAX_TRIES;
  35. BTFMSLIM_DBG("Write to %s", pgd?"PGD":"IFD");
  36. msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg;
  37. msg.num_bytes = bytes;
  38. msg.comp = NULL;
  39. for ( ; slim_write_tries != 0; slim_write_tries--) {
  40. mutex_lock(&btfmslim->xfer_lock);
  41. ret = slim_change_val_element(pgd ? btfmslim->slim_pgd :
  42. &btfmslim->slim_ifd, &msg, src, bytes);
  43. mutex_unlock(&btfmslim->xfer_lock);
  44. if (ret == 0)
  45. break;
  46. usleep_range(5000, 5100);
  47. }
  48. if (ret) {
  49. BTFMSLIM_ERR("failed (%d)", ret);
  50. return ret;
  51. }
  52. for (i = 0; i < bytes; i++)
  53. BTFMSLIM_DBG("Write 0x%02x to reg 0x%x", ((uint8_t *)src)[i],
  54. reg + i);
  55. return 0;
  56. }
  57. int btfm_slim_write_pgd(struct btfmslim *btfmslim,
  58. uint16_t reg, int bytes, void *src)
  59. {
  60. return btfm_slim_write(btfmslim, reg, bytes, src, PGD);
  61. }
  62. int btfm_slim_write_inf(struct btfmslim *btfmslim,
  63. uint16_t reg, int bytes, void *src)
  64. {
  65. return btfm_slim_write(btfmslim, reg, bytes, src, IFD);
  66. }
  67. int btfm_slim_read(struct btfmslim *btfmslim, unsigned short reg,
  68. int bytes, void *dest, uint8_t pgd)
  69. {
  70. int ret, i;
  71. struct slim_ele_access msg;
  72. int slim_read_tries = SLIM_SLAVE_RW_MAX_TRIES;
  73. BTFMSLIM_DBG("Read from %s", pgd?"PGD":"IFD");
  74. msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg;
  75. msg.num_bytes = bytes;
  76. msg.comp = NULL;
  77. for ( ; slim_read_tries != 0; slim_read_tries--) {
  78. mutex_lock(&btfmslim->xfer_lock);
  79. ret = slim_request_val_element(pgd ? btfmslim->slim_pgd :
  80. &btfmslim->slim_ifd, &msg, dest, bytes);
  81. mutex_unlock(&btfmslim->xfer_lock);
  82. if (ret == 0)
  83. break;
  84. usleep_range(5000, 5100);
  85. }
  86. if (ret)
  87. BTFMSLIM_ERR("failed (%d)", ret);
  88. for (i = 0; i < bytes; i++)
  89. BTFMSLIM_DBG("Read 0x%02x from reg 0x%x", ((uint8_t *)dest)[i],
  90. reg + i);
  91. return 0;
  92. }
  93. int btfm_slim_read_pgd(struct btfmslim *btfmslim,
  94. uint16_t reg, int bytes, void *dest)
  95. {
  96. return btfm_slim_read(btfmslim, reg, bytes, dest, PGD);
  97. }
  98. int btfm_slim_read_inf(struct btfmslim *btfmslim,
  99. uint16_t reg, int bytes, void *dest)
  100. {
  101. return btfm_slim_read(btfmslim, reg, bytes, dest, IFD);
  102. }
  103. int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
  104. uint8_t rxport, uint32_t rates, uint8_t grp, uint8_t nchan)
  105. {
  106. int ret, i;
  107. struct slim_ch prop;
  108. struct btfmslim_ch *chan = ch;
  109. uint16_t ch_h[2];
  110. if (!btfmslim || !ch)
  111. return -EINVAL;
  112. BTFMSLIM_DBG("port: %d ch: %d", ch->port, ch->ch);
  113. /* Define the channel with below parameters */
  114. prop.prot = ((rates == 44100) || (rates == 88200)) ?
  115. SLIM_PUSH : SLIM_AUTO_ISO;
  116. prop.baser = ((rates == 44100) || (rates == 88200)) ?
  117. SLIM_RATE_11025HZ : SLIM_RATE_4000HZ;
  118. prop.dataf = ((rates == 48000) || (rates == 44100) ||
  119. (rates == 88200) || (rates == 96000)) ?
  120. SLIM_CH_DATAF_NOT_DEFINED : SLIM_CH_DATAF_LPCM_AUDIO;
  121. /* for feedback channel PCM bit should not be set */
  122. if (btfm_feedback_ch_setting) {
  123. BTFMSLIM_DBG("port open for feedback ch, not setting PCM bit");
  124. prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
  125. /* reset so that next port open sets the data format properly */
  126. btfm_feedback_ch_setting = 0;
  127. }
  128. prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
  129. prop.ratem = ((rates == 44100) || (rates == 88200)) ?
  130. (rates/11025) : (rates/4000);
  131. prop.sampleszbits = 16;
  132. ch_h[0] = ch->ch_hdl;
  133. ch_h[1] = (grp) ? (ch+1)->ch_hdl : 0;
  134. BTFMSLIM_INFO("channel define - prot:%d, dataf:%d, auxf:%d",
  135. prop.prot, prop.dataf, prop.auxf);
  136. BTFMSLIM_INFO("channel define - rates:%d, baser:%d, ratem:%d",
  137. rates, prop.baser, prop.ratem);
  138. ret = slim_define_ch(btfmslim->slim_pgd, &prop, ch_h, nchan, grp,
  139. &ch->grph);
  140. if (ret < 0) {
  141. BTFMSLIM_ERR("slim_define_ch failed ret[%d]", ret);
  142. goto error;
  143. }
  144. for (i = 0; i < nchan; i++, ch++) {
  145. /* Enable port through registration setting */
  146. if (btfmslim->vendor_port_en) {
  147. ret = btfmslim->vendor_port_en(btfmslim, ch->port,
  148. rxport, 1);
  149. if (ret < 0) {
  150. BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
  151. ret);
  152. goto error;
  153. }
  154. }
  155. if (rxport) {
  156. BTFMSLIM_INFO("slim_connect_sink(port: %d, ch: %d)",
  157. ch->port, ch->ch);
  158. /* Connect Port with channel given by Machine driver*/
  159. ret = slim_connect_sink(btfmslim->slim_pgd,
  160. &ch->port_hdl, 1, ch->ch_hdl);
  161. if (ret < 0) {
  162. BTFMSLIM_ERR("slim_connect_sink failed ret[%d]",
  163. ret);
  164. goto remove_channel;
  165. }
  166. } else {
  167. BTFMSLIM_INFO("slim_connect_src(port: %d, ch: %d)",
  168. ch->port, ch->ch);
  169. /* Connect Port with channel given by Machine driver*/
  170. ret = slim_connect_src(btfmslim->slim_pgd, ch->port_hdl,
  171. ch->ch_hdl);
  172. if (ret < 0) {
  173. BTFMSLIM_ERR("slim_connect_src failed ret[%d]",
  174. ret);
  175. goto remove_channel;
  176. }
  177. }
  178. }
  179. /* Activate the channel immediately */
  180. BTFMSLIM_INFO(
  181. "port: %d, ch: %d, grp: %d, ch->grph: 0x%x, ch_hdl: 0x%x",
  182. chan->port, chan->ch, grp, chan->grph, chan->ch_hdl);
  183. ret = slim_control_ch(btfmslim->slim_pgd, (grp ? chan->grph :
  184. chan->ch_hdl), SLIM_CH_ACTIVATE, true);
  185. if (ret < 0) {
  186. BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
  187. goto remove_channel;
  188. }
  189. error:
  190. return ret;
  191. remove_channel:
  192. /* Remove the channel immediately*/
  193. ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
  194. SLIM_CH_REMOVE, true);
  195. if (ret < 0)
  196. BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
  197. return ret;
  198. }
  199. int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
  200. uint8_t rxport, uint8_t grp, uint8_t nchan)
  201. {
  202. int ret, i;
  203. if (!btfmslim || !ch)
  204. return -EINVAL;
  205. BTFMSLIM_INFO("port:%d, grp: %d, ch->grph:0x%x, ch->ch_hdl:0x%x ",
  206. ch->port, grp, ch->grph, ch->ch_hdl);
  207. /* For 44.1/88.2 Khz A2DP Rx, disconnect the port first */
  208. if (rxport &&
  209. (btfmslim->sample_rate == 44100 ||
  210. btfmslim->sample_rate == 88200)) {
  211. BTFMSLIM_DBG("disconnecting the ports, removing the channel");
  212. ret = slim_disconnect_ports(btfmslim->slim_pgd,
  213. &ch->port_hdl, 1);
  214. if (ret < 0) {
  215. BTFMSLIM_ERR("slim_disconnect_ports failed ret[%d]",
  216. ret);
  217. }
  218. }
  219. /* Remove the channel immediately*/
  220. ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
  221. SLIM_CH_REMOVE, true);
  222. if (ret < 0) {
  223. BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
  224. if (btfmslim->sample_rate != 44100 &&
  225. btfmslim->sample_rate != 88200) {
  226. ret = slim_disconnect_ports(btfmslim->slim_pgd,
  227. &ch->port_hdl, 1);
  228. if (ret < 0) {
  229. BTFMSLIM_ERR("disconnect_ports failed ret[%d]",
  230. ret);
  231. goto error;
  232. }
  233. }
  234. }
  235. /* Disable port through registration setting */
  236. for (i = 0; i < nchan; i++, ch++) {
  237. if (btfmslim->vendor_port_en) {
  238. ret = btfmslim->vendor_port_en(btfmslim, ch->port,
  239. rxport, 0);
  240. if (ret < 0) {
  241. BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
  242. ret);
  243. break;
  244. }
  245. }
  246. }
  247. error:
  248. return ret;
  249. }
  250. static int btfm_slim_get_logical_addr(struct slim_device *slim)
  251. {
  252. int ret = 0;
  253. const unsigned long timeout = jiffies +
  254. msecs_to_jiffies(SLIM_SLAVE_PRESENT_TIMEOUT);
  255. do {
  256. ret = slim_get_logical_addr(slim, slim->e_addr,
  257. ARRAY_SIZE(slim->e_addr), &slim->laddr);
  258. if (!ret) {
  259. BTFMSLIM_DBG("Assigned l-addr: 0x%x", slim->laddr);
  260. break;
  261. }
  262. /* Give SLIMBUS time to report present and be ready. */
  263. usleep_range(1000, 1100);
  264. BTFMSLIM_DBG("retyring get logical addr");
  265. } while (time_before(jiffies, timeout));
  266. return ret;
  267. }
  268. static int btfm_slim_alloc_port(struct btfmslim *btfmslim)
  269. {
  270. int ret = -EINVAL, i;
  271. struct btfmslim_ch *rx_chs;
  272. struct btfmslim_ch *tx_chs;
  273. if (!btfmslim)
  274. return ret;
  275. rx_chs = btfmslim->rx_chs;
  276. tx_chs = btfmslim->tx_chs;
  277. if (!rx_chs || !tx_chs)
  278. return ret;
  279. BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl");
  280. for (i = 0 ; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
  281. (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, rx_chs++) {
  282. /* Get Rx port handler from slimbus driver based
  283. * on port number
  284. */
  285. ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
  286. rx_chs->port, &rx_chs->port_hdl, SLIM_SINK);
  287. if (ret < 0) {
  288. BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
  289. rx_chs->port, SLIM_SINK);
  290. return ret;
  291. }
  292. BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id,
  293. rx_chs->name, rx_chs->port, rx_chs->port_hdl,
  294. rx_chs->ch, rx_chs->ch_hdl);
  295. }
  296. BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl");
  297. for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
  298. (i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
  299. /* Get Tx port handler from slimbus driver based
  300. * on port number
  301. */
  302. ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
  303. tx_chs->port, &tx_chs->port_hdl, SLIM_SRC);
  304. if (ret < 0) {
  305. BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
  306. tx_chs->port, SLIM_SRC);
  307. return ret;
  308. }
  309. BTFMSLIM_DBG(" %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id,
  310. tx_chs->name, tx_chs->port, tx_chs->port_hdl,
  311. tx_chs->ch, tx_chs->ch_hdl);
  312. }
  313. return ret;
  314. }
  315. int btfm_slim_hw_init(struct btfmslim *btfmslim)
  316. {
  317. int ret;
  318. BTFMSLIM_DBG("");
  319. if (!btfmslim)
  320. return -EINVAL;
  321. if (btfmslim->enabled) {
  322. BTFMSLIM_DBG("Already enabled");
  323. return 0;
  324. }
  325. mutex_lock(&btfmslim->io_lock);
  326. /* Assign Logical Address for PGD (Ported Generic Device)
  327. * enumeration address
  328. */
  329. ret = btfm_slim_get_logical_addr(btfmslim->slim_pgd);
  330. if (ret) {
  331. BTFMSLIM_ERR("failed to get slimbus %s logical address: %d",
  332. btfmslim->slim_pgd->name, ret);
  333. goto error;
  334. }
  335. /* Assign Logical Address for Ported Generic Device
  336. * enumeration address
  337. */
  338. ret = btfm_slim_get_logical_addr(&btfmslim->slim_ifd);
  339. if (ret) {
  340. BTFMSLIM_ERR("failed to get slimbus %s logical address: %d",
  341. btfmslim->slim_ifd.name, ret);
  342. goto error;
  343. }
  344. /* Allocate ports with logical address to get port handler from
  345. * slimbus driver
  346. */
  347. ret = btfm_slim_alloc_port(btfmslim);
  348. if (ret)
  349. goto error;
  350. /* Start vendor specific initialization and get port information */
  351. if (btfmslim->vendor_init)
  352. ret = btfmslim->vendor_init(btfmslim);
  353. /* Only when all registers read/write successfully, it set to
  354. * enabled status
  355. */
  356. btfmslim->enabled = 1;
  357. error:
  358. mutex_unlock(&btfmslim->io_lock);
  359. return ret;
  360. }
  361. int btfm_slim_hw_deinit(struct btfmslim *btfmslim)
  362. {
  363. int ret = 0;
  364. if (!btfmslim)
  365. return -EINVAL;
  366. if (!btfmslim->enabled) {
  367. BTFMSLIM_DBG("Already disabled");
  368. return 0;
  369. }
  370. mutex_lock(&btfmslim->io_lock);
  371. btfmslim->enabled = 0;
  372. mutex_unlock(&btfmslim->io_lock);
  373. return ret;
  374. }
  375. static int btfm_slim_get_dt_info(struct btfmslim *btfmslim)
  376. {
  377. int ret = 0;
  378. struct slim_device *slim = btfmslim->slim_pgd;
  379. struct slim_device *slim_ifd = &btfmslim->slim_ifd;
  380. struct property *prop;
  381. if (!slim || !slim_ifd)
  382. return -EINVAL;
  383. if (slim->dev.of_node) {
  384. BTFMSLIM_DBG("Platform data from device tree (%s)",
  385. slim->name);
  386. ret = of_property_read_string(slim->dev.of_node,
  387. "qcom,btfm-slim-ifd", &slim_ifd->name);
  388. if (ret) {
  389. BTFMSLIM_ERR("Looking up %s property in node %s failed",
  390. "qcom,btfm-slim-ifd",
  391. slim->dev.of_node->full_name);
  392. return -ENODEV;
  393. }
  394. BTFMSLIM_DBG("qcom,btfm-slim-ifd (%s)", slim_ifd->name);
  395. prop = of_find_property(slim->dev.of_node,
  396. "qcom,btfm-slim-ifd-elemental-addr", NULL);
  397. if (!prop) {
  398. BTFMSLIM_ERR("Looking up %s property in node %s failed",
  399. "qcom,btfm-slim-ifd-elemental-addr",
  400. slim->dev.of_node->full_name);
  401. return -ENODEV;
  402. } else if (prop->length != 6) {
  403. BTFMSLIM_ERR(
  404. "invalid codec slim ifd addr. addr length= %d",
  405. prop->length);
  406. return -ENODEV;
  407. }
  408. memcpy(slim_ifd->e_addr, prop->value, 6);
  409. BTFMSLIM_DBG(
  410. "PGD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
  411. slim->e_addr[0], slim->e_addr[1], slim->e_addr[2],
  412. slim->e_addr[3], slim->e_addr[4], slim->e_addr[5]);
  413. BTFMSLIM_DBG(
  414. "IFD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
  415. slim_ifd->e_addr[0], slim_ifd->e_addr[1],
  416. slim_ifd->e_addr[2], slim_ifd->e_addr[3],
  417. slim_ifd->e_addr[4], slim_ifd->e_addr[5]);
  418. } else {
  419. BTFMSLIM_ERR("Platform data is not valid");
  420. }
  421. return ret;
  422. }
  423. static int btfm_slim_probe(struct slim_device *slim)
  424. {
  425. int ret = 0;
  426. struct btfmslim *btfm_slim;
  427. BTFMSLIM_DBG("");
  428. if (!slim->ctrl)
  429. return -EINVAL;
  430. /* Allocation btfmslim data pointer */
  431. btfm_slim = kzalloc(sizeof(struct btfmslim), GFP_KERNEL);
  432. if (btfm_slim == NULL) {
  433. BTFMSLIM_ERR("error, allocation failed");
  434. return -ENOMEM;
  435. }
  436. /* BTFM Slimbus driver control data configuration */
  437. btfm_slim->slim_pgd = slim;
  438. /* Assign vendor specific function */
  439. btfm_slim->rx_chs = SLIM_SLAVE_RXPORT;
  440. btfm_slim->tx_chs = SLIM_SLAVE_TXPORT;
  441. btfm_slim->vendor_init = SLIM_SLAVE_INIT;
  442. btfm_slim->vendor_port_en = SLIM_SLAVE_PORT_EN;
  443. /* Created Mutex for slimbus data transfer */
  444. mutex_init(&btfm_slim->io_lock);
  445. mutex_init(&btfm_slim->xfer_lock);
  446. /* Get Device tree node for Interface Device enumeration address */
  447. ret = btfm_slim_get_dt_info(btfm_slim);
  448. if (ret)
  449. goto dealloc;
  450. /* Add Interface Device for slimbus driver */
  451. ret = slim_add_device(btfm_slim->slim_pgd->ctrl, &btfm_slim->slim_ifd);
  452. if (ret) {
  453. BTFMSLIM_ERR("error, adding SLIMBUS device failed");
  454. goto dealloc;
  455. }
  456. /* Platform driver data allocation */
  457. slim->dev.platform_data = btfm_slim;
  458. /* Driver specific data allocation */
  459. btfm_slim->dev = &slim->dev;
  460. ret = btfm_slim_register_codec(&slim->dev);
  461. if (ret) {
  462. BTFMSLIM_ERR("error, registering slimbus codec failed");
  463. goto free;
  464. }
  465. ret = bt_register_slimdev(&slim->dev);
  466. if (ret < 0) {
  467. btfm_slim_unregister_codec(&slim->dev);
  468. goto free;
  469. }
  470. return ret;
  471. free:
  472. slim_remove_device(&btfm_slim->slim_ifd);
  473. dealloc:
  474. mutex_destroy(&btfm_slim->io_lock);
  475. mutex_destroy(&btfm_slim->xfer_lock);
  476. kfree(btfm_slim);
  477. return ret;
  478. }
  479. static int btfm_slim_remove(struct slim_device *slim)
  480. {
  481. struct btfmslim *btfm_slim = slim->dev.platform_data;
  482. BTFMSLIM_DBG("");
  483. mutex_destroy(&btfm_slim->io_lock);
  484. mutex_destroy(&btfm_slim->xfer_lock);
  485. snd_soc_unregister_codec(&slim->dev);
  486. BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_ifd");
  487. slim_remove_device(&btfm_slim->slim_ifd);
  488. kfree(btfm_slim);
  489. BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_pgd");
  490. slim_remove_device(slim);
  491. return 0;
  492. }
  493. static const struct slim_device_id btfm_slim_id[] = {
  494. {SLIM_SLAVE_COMPATIBLE_STR, 0},
  495. {}
  496. };
  497. static struct slim_driver btfm_slim_driver = {
  498. .driver = {
  499. .name = "btfmslim-driver",
  500. .owner = THIS_MODULE,
  501. },
  502. .probe = btfm_slim_probe,
  503. .remove = btfm_slim_remove,
  504. .id_table = btfm_slim_id
  505. };
  506. static int __init btfm_slim_init(void)
  507. {
  508. int ret;
  509. BTFMSLIM_DBG("");
  510. ret = slim_driver_register(&btfm_slim_driver);
  511. if (ret)
  512. BTFMSLIM_ERR("Failed to register slimbus driver: %d", ret);
  513. return ret;
  514. }
  515. static void __exit btfm_slim_exit(void)
  516. {
  517. BTFMSLIM_DBG("");
  518. slim_driver_unregister(&btfm_slim_driver);
  519. }
  520. module_init(btfm_slim_init);
  521. module_exit(btfm_slim_exit);
  522. MODULE_LICENSE("GPL v2");
  523. MODULE_DESCRIPTION("BTFM Slimbus Slave driver");