bus_sdio.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * SDIO interface.
  4. *
  5. * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
  6. * Copyright (c) 2010, ST-Ericsson
  7. */
  8. #include <linux/module.h>
  9. #include <linux/mmc/sdio.h>
  10. #include <linux/mmc/sdio_func.h>
  11. #include <linux/mmc/card.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/of_irq.h>
  14. #include <linux/irq.h>
  15. #include "bus.h"
  16. #include "wfx.h"
  17. #include "hwio.h"
  18. #include "main.h"
  19. #include "bh.h"
  20. static const struct wfx_platform_data wfx_sdio_pdata = {
  21. .file_fw = "wfm_wf200",
  22. .file_pds = "wf200.pds",
  23. };
  24. struct wfx_sdio_priv {
  25. struct sdio_func *func;
  26. struct wfx_dev *core;
  27. u8 buf_id_tx;
  28. u8 buf_id_rx;
  29. int of_irq;
  30. };
  31. static int wfx_sdio_copy_from_io(void *priv, unsigned int reg_id,
  32. void *dst, size_t count)
  33. {
  34. struct wfx_sdio_priv *bus = priv;
  35. unsigned int sdio_addr = reg_id << 2;
  36. int ret;
  37. WARN(reg_id > 7, "chip only has 7 registers");
  38. WARN(((uintptr_t)dst) & 3, "unaligned buffer size");
  39. WARN(count & 3, "unaligned buffer address");
  40. /* Use queue mode buffers */
  41. if (reg_id == WFX_REG_IN_OUT_QUEUE)
  42. sdio_addr |= (bus->buf_id_rx + 1) << 7;
  43. ret = sdio_memcpy_fromio(bus->func, dst, sdio_addr, count);
  44. if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE)
  45. bus->buf_id_rx = (bus->buf_id_rx + 1) % 4;
  46. return ret;
  47. }
  48. static int wfx_sdio_copy_to_io(void *priv, unsigned int reg_id,
  49. const void *src, size_t count)
  50. {
  51. struct wfx_sdio_priv *bus = priv;
  52. unsigned int sdio_addr = reg_id << 2;
  53. int ret;
  54. WARN(reg_id > 7, "chip only has 7 registers");
  55. WARN(((uintptr_t)src) & 3, "unaligned buffer size");
  56. WARN(count & 3, "unaligned buffer address");
  57. /* Use queue mode buffers */
  58. if (reg_id == WFX_REG_IN_OUT_QUEUE)
  59. sdio_addr |= bus->buf_id_tx << 7;
  60. // FIXME: discards 'const' qualifier for src
  61. ret = sdio_memcpy_toio(bus->func, sdio_addr, (void *)src, count);
  62. if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE)
  63. bus->buf_id_tx = (bus->buf_id_tx + 1) % 32;
  64. return ret;
  65. }
  66. static void wfx_sdio_lock(void *priv)
  67. {
  68. struct wfx_sdio_priv *bus = priv;
  69. sdio_claim_host(bus->func);
  70. }
  71. static void wfx_sdio_unlock(void *priv)
  72. {
  73. struct wfx_sdio_priv *bus = priv;
  74. sdio_release_host(bus->func);
  75. }
  76. static void wfx_sdio_irq_handler(struct sdio_func *func)
  77. {
  78. struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
  79. wfx_bh_request_rx(bus->core);
  80. }
  81. static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv)
  82. {
  83. struct wfx_sdio_priv *bus = priv;
  84. sdio_claim_host(bus->func);
  85. wfx_bh_request_rx(bus->core);
  86. sdio_release_host(bus->func);
  87. return IRQ_HANDLED;
  88. }
  89. static int wfx_sdio_irq_subscribe(void *priv)
  90. {
  91. struct wfx_sdio_priv *bus = priv;
  92. u32 flags;
  93. int ret;
  94. u8 cccr;
  95. if (!bus->of_irq) {
  96. sdio_claim_host(bus->func);
  97. ret = sdio_claim_irq(bus->func, wfx_sdio_irq_handler);
  98. sdio_release_host(bus->func);
  99. return ret;
  100. }
  101. flags = irq_get_trigger_type(bus->of_irq);
  102. if (!flags)
  103. flags = IRQF_TRIGGER_HIGH;
  104. flags |= IRQF_ONESHOT;
  105. ret = devm_request_threaded_irq(&bus->func->dev, bus->of_irq, NULL,
  106. wfx_sdio_irq_handler_ext, flags,
  107. "wfx", bus);
  108. if (ret)
  109. return ret;
  110. sdio_claim_host(bus->func);
  111. cccr = sdio_f0_readb(bus->func, SDIO_CCCR_IENx, NULL);
  112. cccr |= BIT(0);
  113. cccr |= BIT(bus->func->num);
  114. sdio_f0_writeb(bus->func, cccr, SDIO_CCCR_IENx, NULL);
  115. sdio_release_host(bus->func);
  116. return 0;
  117. }
  118. static int wfx_sdio_irq_unsubscribe(void *priv)
  119. {
  120. struct wfx_sdio_priv *bus = priv;
  121. int ret;
  122. if (bus->of_irq)
  123. devm_free_irq(&bus->func->dev, bus->of_irq, bus);
  124. sdio_claim_host(bus->func);
  125. ret = sdio_release_irq(bus->func);
  126. sdio_release_host(bus->func);
  127. return ret;
  128. }
  129. static size_t wfx_sdio_align_size(void *priv, size_t size)
  130. {
  131. struct wfx_sdio_priv *bus = priv;
  132. return sdio_align_size(bus->func, size);
  133. }
  134. static const struct hwbus_ops wfx_sdio_hwbus_ops = {
  135. .copy_from_io = wfx_sdio_copy_from_io,
  136. .copy_to_io = wfx_sdio_copy_to_io,
  137. .irq_subscribe = wfx_sdio_irq_subscribe,
  138. .irq_unsubscribe = wfx_sdio_irq_unsubscribe,
  139. .lock = wfx_sdio_lock,
  140. .unlock = wfx_sdio_unlock,
  141. .align_size = wfx_sdio_align_size,
  142. };
  143. static const struct of_device_id wfx_sdio_of_match[] = {
  144. { .compatible = "silabs,wfx-sdio" },
  145. { .compatible = "silabs,wf200" },
  146. { },
  147. };
  148. MODULE_DEVICE_TABLE(of, wfx_sdio_of_match);
  149. static int wfx_sdio_probe(struct sdio_func *func,
  150. const struct sdio_device_id *id)
  151. {
  152. struct device_node *np = func->dev.of_node;
  153. struct wfx_sdio_priv *bus;
  154. int ret;
  155. if (func->num != 1) {
  156. dev_err(&func->dev, "SDIO function number is %d while it should always be 1 (unsupported chip?)\n",
  157. func->num);
  158. return -ENODEV;
  159. }
  160. bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL);
  161. if (!bus)
  162. return -ENOMEM;
  163. if (np) {
  164. if (!of_match_node(wfx_sdio_of_match, np)) {
  165. dev_warn(&func->dev, "no compatible device found in DT\n");
  166. return -ENODEV;
  167. }
  168. bus->of_irq = irq_of_parse_and_map(np, 0);
  169. } else {
  170. dev_warn(&func->dev,
  171. "device is not declared in DT, features will be limited\n");
  172. // FIXME: ignore VID/PID and only rely on device tree
  173. // return -ENODEV;
  174. }
  175. bus->func = func;
  176. sdio_set_drvdata(func, bus);
  177. func->card->quirks |= MMC_QUIRK_LENIENT_FN0 |
  178. MMC_QUIRK_BLKSZ_FOR_BYTE_MODE |
  179. MMC_QUIRK_BROKEN_BYTE_MODE_512;
  180. sdio_claim_host(func);
  181. ret = sdio_enable_func(func);
  182. // Block of 64 bytes is more efficient than 512B for frame sizes < 4k
  183. sdio_set_block_size(func, 64);
  184. sdio_release_host(func);
  185. if (ret)
  186. goto err0;
  187. bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata,
  188. &wfx_sdio_hwbus_ops, bus);
  189. if (!bus->core) {
  190. ret = -EIO;
  191. goto err1;
  192. }
  193. ret = wfx_probe(bus->core);
  194. if (ret)
  195. goto err1;
  196. return 0;
  197. err1:
  198. sdio_claim_host(func);
  199. sdio_disable_func(func);
  200. sdio_release_host(func);
  201. err0:
  202. return ret;
  203. }
  204. static void wfx_sdio_remove(struct sdio_func *func)
  205. {
  206. struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
  207. wfx_release(bus->core);
  208. sdio_claim_host(func);
  209. sdio_disable_func(func);
  210. sdio_release_host(func);
  211. }
  212. #define SDIO_VENDOR_ID_SILABS 0x0000
  213. #define SDIO_DEVICE_ID_SILABS_WF200 0x1000
  214. static const struct sdio_device_id wfx_sdio_ids[] = {
  215. { SDIO_DEVICE(SDIO_VENDOR_ID_SILABS, SDIO_DEVICE_ID_SILABS_WF200) },
  216. // FIXME: ignore VID/PID and only rely on device tree
  217. // { SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) },
  218. { },
  219. };
  220. MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids);
  221. struct sdio_driver wfx_sdio_driver = {
  222. .name = "wfx-sdio",
  223. .id_table = wfx_sdio_ids,
  224. .probe = wfx_sdio_probe,
  225. .remove = wfx_sdio_remove,
  226. .drv = {
  227. .owner = THIS_MODULE,
  228. .of_match_table = wfx_sdio_of_match,
  229. }
  230. };