qcom_gsbi.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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. struct resource *res;
  116. void __iomem *base;
  117. struct gsbi_info *gsbi;
  118. int i, ret;
  119. u32 mask, gsbi_num;
  120. const struct crci_config *config = NULL;
  121. gsbi = devm_kzalloc(&pdev->dev, sizeof(*gsbi), GFP_KERNEL);
  122. if (!gsbi)
  123. return -ENOMEM;
  124. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  125. base = devm_ioremap_resource(&pdev->dev, res);
  126. if (IS_ERR(base))
  127. return PTR_ERR(base);
  128. /* get the tcsr node and setup the config and regmap */
  129. gsbi->tcsr = syscon_regmap_lookup_by_phandle(node, "syscon-tcsr");
  130. if (!IS_ERR(gsbi->tcsr)) {
  131. tcsr_node = of_parse_phandle(node, "syscon-tcsr", 0);
  132. if (tcsr_node) {
  133. match = of_match_node(tcsr_dt_match, tcsr_node);
  134. if (match)
  135. config = match->data;
  136. else
  137. dev_warn(&pdev->dev, "no matching TCSR\n");
  138. of_node_put(tcsr_node);
  139. }
  140. }
  141. if (of_property_read_u32(node, "cell-index", &gsbi_num)) {
  142. dev_err(&pdev->dev, "missing cell-index\n");
  143. return -EINVAL;
  144. }
  145. if (gsbi_num < 1 || gsbi_num > MAX_GSBI) {
  146. dev_err(&pdev->dev, "invalid cell-index\n");
  147. return -EINVAL;
  148. }
  149. if (of_property_read_u32(node, "qcom,mode", &gsbi->mode)) {
  150. dev_err(&pdev->dev, "missing mode configuration\n");
  151. return -EINVAL;
  152. }
  153. /* not required, so default to 0 if not present */
  154. of_property_read_u32(node, "qcom,crci", &gsbi->crci);
  155. dev_info(&pdev->dev, "GSBI port protocol: %d crci: %d\n",
  156. gsbi->mode, gsbi->crci);
  157. gsbi->hclk = devm_clk_get(&pdev->dev, "iface");
  158. if (IS_ERR(gsbi->hclk))
  159. return PTR_ERR(gsbi->hclk);
  160. clk_prepare_enable(gsbi->hclk);
  161. writel_relaxed((gsbi->mode << GSBI_PROTOCOL_SHIFT) | gsbi->crci,
  162. base + GSBI_CTRL_REG);
  163. /*
  164. * modify tcsr to reflect mode and ADM CRCI mux
  165. * Each gsbi contains a pair of bits, one for RX and one for TX
  166. * SPI mode requires both bits cleared, otherwise they are set
  167. */
  168. if (config) {
  169. for (i = 0; i < config->num_rows; i++) {
  170. mask = config->array[i][gsbi_num - 1];
  171. if (gsbi->mode == GSBI_PROT_SPI)
  172. regmap_update_bits(gsbi->tcsr,
  173. TCSR_ADM_CRCI_BASE + 4 * i, mask, 0);
  174. else
  175. regmap_update_bits(gsbi->tcsr,
  176. TCSR_ADM_CRCI_BASE + 4 * i, mask, mask);
  177. }
  178. }
  179. /* make sure the gsbi control write is not reordered */
  180. wmb();
  181. platform_set_drvdata(pdev, gsbi);
  182. ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
  183. if (ret)
  184. clk_disable_unprepare(gsbi->hclk);
  185. return ret;
  186. }
  187. static int gsbi_remove(struct platform_device *pdev)
  188. {
  189. struct gsbi_info *gsbi = platform_get_drvdata(pdev);
  190. clk_disable_unprepare(gsbi->hclk);
  191. return 0;
  192. }
  193. static const struct of_device_id gsbi_dt_match[] = {
  194. { .compatible = "qcom,gsbi-v1.0.0", },
  195. { },
  196. };
  197. MODULE_DEVICE_TABLE(of, gsbi_dt_match);
  198. static struct platform_driver gsbi_driver = {
  199. .driver = {
  200. .name = "gsbi",
  201. .of_match_table = gsbi_dt_match,
  202. },
  203. .probe = gsbi_probe,
  204. .remove = gsbi_remove,
  205. };
  206. module_platform_driver(gsbi_driver);
  207. MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>");
  208. MODULE_DESCRIPTION("QCOM GSBI driver");
  209. MODULE_LICENSE("GPL v2");