nci.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * NCI based driver for Samsung S3FWRN5 NFC chip
  4. *
  5. * Copyright (C) 2015 Samsung Electrnoics
  6. * Robert Baldyga <r.baldyga@samsung.com>
  7. */
  8. #include <linux/completion.h>
  9. #include <linux/firmware.h>
  10. #include "s3fwrn5.h"
  11. #include "nci.h"
  12. static int s3fwrn5_nci_prop_rsp(struct nci_dev *ndev, struct sk_buff *skb)
  13. {
  14. __u8 status = skb->data[0];
  15. nci_req_complete(ndev, status);
  16. return 0;
  17. }
  18. static struct nci_driver_ops s3fwrn5_nci_prop_ops[] = {
  19. {
  20. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  21. NCI_PROP_AGAIN),
  22. .rsp = s3fwrn5_nci_prop_rsp,
  23. },
  24. {
  25. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  26. NCI_PROP_GET_RFREG),
  27. .rsp = s3fwrn5_nci_prop_rsp,
  28. },
  29. {
  30. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  31. NCI_PROP_SET_RFREG),
  32. .rsp = s3fwrn5_nci_prop_rsp,
  33. },
  34. {
  35. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  36. NCI_PROP_GET_RFREG_VER),
  37. .rsp = s3fwrn5_nci_prop_rsp,
  38. },
  39. {
  40. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  41. NCI_PROP_SET_RFREG_VER),
  42. .rsp = s3fwrn5_nci_prop_rsp,
  43. },
  44. {
  45. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  46. NCI_PROP_START_RFREG),
  47. .rsp = s3fwrn5_nci_prop_rsp,
  48. },
  49. {
  50. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  51. NCI_PROP_STOP_RFREG),
  52. .rsp = s3fwrn5_nci_prop_rsp,
  53. },
  54. {
  55. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  56. NCI_PROP_FW_CFG),
  57. .rsp = s3fwrn5_nci_prop_rsp,
  58. },
  59. {
  60. .opcode = nci_opcode_pack(NCI_GID_PROPRIETARY,
  61. NCI_PROP_WR_RESET),
  62. .rsp = s3fwrn5_nci_prop_rsp,
  63. },
  64. };
  65. void s3fwrn5_nci_get_prop_ops(struct nci_driver_ops **ops, size_t *n)
  66. {
  67. *ops = s3fwrn5_nci_prop_ops;
  68. *n = ARRAY_SIZE(s3fwrn5_nci_prop_ops);
  69. }
  70. #define S3FWRN5_RFREG_SECTION_SIZE 252
  71. int s3fwrn5_nci_rf_configure(struct s3fwrn5_info *info, const char *fw_name)
  72. {
  73. const struct firmware *fw;
  74. struct nci_prop_fw_cfg_cmd fw_cfg;
  75. struct nci_prop_set_rfreg_cmd set_rfreg;
  76. struct nci_prop_stop_rfreg_cmd stop_rfreg;
  77. u32 checksum;
  78. int i, len;
  79. int ret;
  80. ret = request_firmware(&fw, fw_name, &info->ndev->nfc_dev->dev);
  81. if (ret < 0)
  82. return ret;
  83. /* Compute rfreg checksum */
  84. checksum = 0;
  85. for (i = 0; i < fw->size; i += 4)
  86. checksum += *((u32 *)(fw->data+i));
  87. /* Set default clock configuration for external crystal */
  88. fw_cfg.clk_type = 0x01;
  89. fw_cfg.clk_speed = 0xff;
  90. fw_cfg.clk_req = 0xff;
  91. ret = nci_prop_cmd(info->ndev, NCI_PROP_FW_CFG,
  92. sizeof(fw_cfg), (__u8 *)&fw_cfg);
  93. if (ret < 0)
  94. goto out;
  95. /* Start rfreg configuration */
  96. dev_info(&info->ndev->nfc_dev->dev,
  97. "rfreg configuration update: %s\n", fw_name);
  98. ret = nci_prop_cmd(info->ndev, NCI_PROP_START_RFREG, 0, NULL);
  99. if (ret < 0) {
  100. dev_err(&info->ndev->nfc_dev->dev,
  101. "Unable to start rfreg update\n");
  102. goto out;
  103. }
  104. /* Update rfreg */
  105. set_rfreg.index = 0;
  106. for (i = 0; i < fw->size; i += S3FWRN5_RFREG_SECTION_SIZE) {
  107. len = (fw->size - i < S3FWRN5_RFREG_SECTION_SIZE) ?
  108. (fw->size - i) : S3FWRN5_RFREG_SECTION_SIZE;
  109. memcpy(set_rfreg.data, fw->data+i, len);
  110. ret = nci_prop_cmd(info->ndev, NCI_PROP_SET_RFREG,
  111. len+1, (__u8 *)&set_rfreg);
  112. if (ret < 0) {
  113. dev_err(&info->ndev->nfc_dev->dev,
  114. "rfreg update error (code=%d)\n", ret);
  115. goto out;
  116. }
  117. set_rfreg.index++;
  118. }
  119. /* Finish rfreg configuration */
  120. stop_rfreg.checksum = checksum & 0xffff;
  121. ret = nci_prop_cmd(info->ndev, NCI_PROP_STOP_RFREG,
  122. sizeof(stop_rfreg), (__u8 *)&stop_rfreg);
  123. if (ret < 0) {
  124. dev_err(&info->ndev->nfc_dev->dev,
  125. "Unable to stop rfreg update\n");
  126. goto out;
  127. }
  128. dev_info(&info->ndev->nfc_dev->dev,
  129. "rfreg configuration update: success\n");
  130. out:
  131. release_firmware(fw);
  132. return ret;
  133. }