qcom_gsbi.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2014, The Linux foundation. All rights reserved.
  4. */
  5. #include <linux/clk.h>
  6. #include <linux/err.h>
  7. #include <linux/io.h>
  8. #include <linux/module.h>
  9. #include <linux/of.h>
  10. #include <linux/of_platform.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/regmap.h>
  13. #include <linux/mfd/syscon.h>
  14. #include <dt-bindings/soc/qcom,gsbi.h>
  15. #define GSBI_CTRL_REG 0x0000
  16. #define GSBI_PROTOCOL_SHIFT 4
  17. #define MAX_GSBI 12
  18. #define TCSR_ADM_CRCI_BASE 0x70
  19. struct crci_config {
  20. u32 num_rows;
  21. const u32 (*array)[MAX_GSBI];
  22. };
  23. static const u32 crci_ipq8064[][MAX_GSBI] = {
  24. {
  25. 0x000003, 0x00000c, 0x000030, 0x0000c0,
  26. 0x000300, 0x000c00, 0x003000, 0x00c000,
  27. 0x030000, 0x0c0000, 0x300000, 0xc00000
  28. },
  29. {
  30. 0x000003, 0x00000c, 0x000030, 0x0000c0,
  31. 0x000300, 0x000c00, 0x003000, 0x00c000,
  32. 0x030000, 0x0c0000, 0x300000, 0xc00000
  33. },
  34. };
  35. static const struct crci_config config_ipq8064 = {
  36. .num_rows = ARRAY_SIZE(crci_ipq8064),
  37. .array = crci_ipq8064,
  38. };
  39. static const unsigned int crci_apq8064[][MAX_GSBI] = {
  40. {
  41. 0x001800, 0x006000, 0x000030, 0x0000c0,
  42. 0x000300, 0x000400, 0x000000, 0x000000,
  43. 0x000000, 0x000000, 0x000000, 0x000000
  44. },
  45. {
  46. 0x000000, 0x000000, 0x000000, 0x000000,
  47. 0x000000, 0x000020, 0x0000c0, 0x000000,
  48. 0x000000, 0x000000, 0x000000, 0x000000
  49. },
  50. };
  51. static const struct crci_config config_apq8064 = {
  52. .num_rows = ARRAY_SIZE(crci_apq8064),
  53. .array = crci_apq8064,
  54. };
  55. static const unsigned int crci_msm8960[][MAX_GSBI] = {
  56. {
  57. 0x000003, 0x00000c, 0x000030, 0x0000c0,
  58. 0x000300, 0x000400, 0x000000, 0x000000,
  59. 0x000000, 0x000000, 0x000000, 0x000000
  60. },
  61. {
  62. 0x000000, 0x000000, 0x000000, 0x000000,
  63. 0x000000, 0x000020, 0x0000c0, 0x000300,
  64. 0x001800, 0x006000, 0x000000, 0x000000
  65. },
  66. };
  67. static const struct crci_config config_msm8960 = {
  68. .num_rows = ARRAY_SIZE(crci_msm8960),
  69. .array = crci_msm8960,
  70. };
  71. static const unsigned int crci_msm8660[][MAX_GSBI] = {
  72. { /* ADM 0 - B */
  73. 0x000003, 0x00000c, 0x000030, 0x0000c0,
  74. 0x000300, 0x000c00, 0x003000, 0x00c000,
  75. 0x030000, 0x0c0000, 0x300000, 0xc00000
  76. },
  77. { /* ADM 0 - B */
  78. 0x000003, 0x00000c, 0x000030, 0x0000c0,
  79. 0x000300, 0x000c00, 0x003000, 0x00c000,
  80. 0x030000, 0x0c0000, 0x300000, 0xc00000
  81. },
  82. { /* ADM 1 - A */
  83. 0x000003, 0x00000c, 0x000030, 0x0000c0,
  84. 0x000300, 0x000c00, 0x003000, 0x00c000,
  85. 0x030000, 0x0c0000, 0x300000, 0xc00000
  86. },
  87. { /* ADM 1 - B */
  88. 0x000003, 0x00000c, 0x000030, 0x0000c0,
  89. 0x000300, 0x000c00, 0x003000, 0x00c000,
  90. 0x030000, 0x0c0000, 0x300000, 0xc00000
  91. },
  92. };
  93. static const struct crci_config config_msm8660 = {
  94. .num_rows = ARRAY_SIZE(crci_msm8660),
  95. .array = crci_msm8660,
  96. };
  97. struct gsbi_info {
  98. struct clk *hclk;
  99. u32 mode;
  100. u32 crci;
  101. struct regmap *tcsr;
  102. };
  103. static const struct of_device_id tcsr_dt_match[] = {
  104. { .compatible = "qcom,tcsr-ipq8064", .data = &config_ipq8064},
  105. { .compatible = "qcom,tcsr-apq8064", .data = &config_apq8064},
  106. { .compatible = "qcom,tcsr-msm8960", .data = &config_msm8960},
  107. { .compatible = "qcom,tcsr-msm8660", .data = &config_msm8660},
  108. { },
  109. };
  110. static int gsbi_probe(struct platform_device *pdev)
  111. {
  112. struct device_node *node = pdev->dev.of_node;
  113. struct device_node *tcsr_node;
  114. const struct of_device_id *match;
  115. void __iomem *base;
  116. struct gsbi_info *gsbi;
  117. int i, ret;
  118. u32 mask, gsbi_num;
  119. const struct crci_config *config = NULL;
  120. gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
  121. if (!gsbi)
  122. return -ENOMEM;
  123. base = devm_platform_ioremap_resource(pdev, 0);
  124. if (IS_ERR(base))
  125. return PTR_ERR(base);
  126. /* get the tcsr node and setup the config and regmap */
  127. gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
  128. if (!IS_ERR(gsbi->tcsr)) {
  129. tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
  130. if (tcsr_node) {
  131. match = of_match_node(tcsr_dt_match, tcsr_node);
  132. if (match)
  133. config = match->data;
  134. else
  135. dev_warn(&pdev->dev, "no matching TCSR\n");
  136. of_node_put(tcsr_node);
  137. }
  138. }
  139. if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
  140. dev_err(&pdev->dev, "missing cell-index\n");
  141. return -EINVAL;
  142. }
  143. if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
  144. dev_err(&pdev->dev, "invalid cell-index\n");
  145. return -EINVAL;
  146. }
  147. if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
  148. dev_err(&pdev->dev, "missing mode configuration\n");
  149. return -EINVAL;
  150. }
  151. /* not required, so default to 0 if not present */
  152. of_property_read_u32(node, "qcom,crci", &gsbi->crci);
  153. dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n",
  154. gsbi->mode, gsbi->crci);
  155. gsbi->hclk = devm_clk_get(&pdev->dev, "iface");
  156. if (IS_ERR(gsbi->hclk))
  157. return PTR_ERR(gsbi->hclk);
  158. clk_prepare_enable(gsbi->hclk);
  159. writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
  160. base + GSBI_CTRL_REG);
  161. /*
  162. * modify tcsr to reflect mode and ADM CRCI mux
  163. * Each gsbi contains a pair of bits, one for RX and one for TX
  164. * SPI mode requires both bits cleared, otherwise they are set
  165. */
  166. if (config) {
  167. for (i = 0; i < config->num_rows; i++) {
  168. mask = config->array[i][gsbi_num - 1];
  169. if (gsbi->mode == GSBI_PROT_SPI)
  170. regmap_update_bits(gsbi->tcsr,
  171. TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
  172. else
  173. regmap_update_bits(gsbi->tcsr,
  174. TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
  175. }
  176. }
  177. /* make sure the gsbi control write is not reordered */
  178. wmb();
  179. platform_set_drvdata(pdev, gsbi);
  180. ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
  181. if (ret)
  182. clk_disable_unprepare(gsbi->hclk);
  183. return ret;
  184. }
  185. static int gsbi_remove(struct platform_device *pdev)
  186. {
  187. struct gsbi_info *gsbi = platform_get_drvdata(pdev);
  188. clk_disable_unprepare(gsbi->hclk);
  189. return 0;
  190. }
  191. static const struct of_device_id gsbi_dt_match[] = {
  192. { .compatible = "qcom,gsbi-v1.0.0", },
  193. { },
  194. };
  195. MODULE_DEVICE_TABLE(of, gsbi_dt_match);
  196. static struct platform_driver gsbi_driver = {
  197. .driver = {
  198. .name = "gsbi",
  199. .of_match_table = gsbi_dt_match,
  200. },
  201. .probe = gsbi_probe,
  202. .remove = gsbi_remove,
  203. };
  204. module_platform_driver(gsbi_driver);
  205. MODULE_AUTHOR("Andy Gross <[email protected]>");
  206. MODULE_DESCRIPTION("QCOM GSBI driver");
  207. MODULE_LICENSE("GPL v2");