dwc3-sti-glue.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * STiH407 family DWC3 specific Glue layer
  4. *
  5. * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
  6. * Author(s): Patrice Chotard, <patrice.chotard@st.com> for STMicroelectronics.
  7. */
  8. #include <common.h>
  9. #include <asm/io.h>
  10. #include <dm.h>
  11. #include <errno.h>
  12. #include <fdtdec.h>
  13. #include <linux/libfdt.h>
  14. #include <dm/lists.h>
  15. #include <regmap.h>
  16. #include <reset-uclass.h>
  17. #include <syscon.h>
  18. #include <usb.h>
  19. #include <linux/usb/dwc3.h>
  20. #include <linux/usb/otg.h>
  21. #include <dwc3-sti-glue.h>
  22. DECLARE_GLOBAL_DATA_PTR;
  23. /*
  24. * struct sti_dwc3_glue_platdata - dwc3 STi glue driver private structure
  25. * @syscfg_base: addr for the glue syscfg
  26. * @glue_base: addr for the glue registers
  27. * @syscfg_offset: usb syscfg control offset
  28. * @powerdown_ctl: rest controller for powerdown signal
  29. * @softreset_ctl: reset controller for softreset signal
  30. * @mode: drd static host/device config
  31. */
  32. struct sti_dwc3_glue_platdata {
  33. phys_addr_t syscfg_base;
  34. phys_addr_t glue_base;
  35. phys_addr_t syscfg_offset;
  36. struct reset_ctl powerdown_ctl;
  37. struct reset_ctl softreset_ctl;
  38. enum usb_dr_mode mode;
  39. };
  40. static int sti_dwc3_glue_drd_init(struct sti_dwc3_glue_platdata *plat)
  41. {
  42. unsigned long val;
  43. val = readl(plat->syscfg_base + plat->syscfg_offset);
  44. val &= USB3_CONTROL_MASK;
  45. switch (plat->mode) {
  46. case USB_DR_MODE_PERIPHERAL:
  47. val &= ~(USB3_DELAY_VBUSVALID
  48. | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
  49. | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
  50. | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
  51. val |= USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID;
  52. break;
  53. case USB_DR_MODE_HOST:
  54. val &= ~(USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID
  55. | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
  56. | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
  57. | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
  58. val |= USB3_DELAY_VBUSVALID;
  59. break;
  60. default:
  61. pr_err("Unsupported mode of operation %d\n", plat->mode);
  62. return -EINVAL;
  63. }
  64. writel(val, plat->syscfg_base + plat->syscfg_offset);
  65. return 0;
  66. }
  67. static void sti_dwc3_glue_init(struct sti_dwc3_glue_platdata *plat)
  68. {
  69. unsigned long reg;
  70. reg = readl(plat->glue_base + CLKRST_CTRL);
  71. reg |= AUX_CLK_EN | EXT_CFG_RESET_N | XHCI_REVISION;
  72. reg &= ~SW_PIPEW_RESET_N;
  73. writel(reg, plat->glue_base + CLKRST_CTRL);
  74. /* configure mux for vbus, powerpresent and bvalid signals */
  75. reg = readl(plat->glue_base + USB2_VBUS_MNGMNT_SEL1);
  76. reg |= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG) |
  77. SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG) |
  78. SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG);
  79. writel(reg, plat->glue_base + USB2_VBUS_MNGMNT_SEL1);
  80. setbits_le32(plat->glue_base + CLKRST_CTRL, SW_PIPEW_RESET_N);
  81. }
  82. static int sti_dwc3_glue_ofdata_to_platdata(struct udevice *dev)
  83. {
  84. struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev);
  85. struct udevice *syscon;
  86. struct regmap *regmap;
  87. int ret;
  88. u32 reg[4];
  89. ret = fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev),
  90. "reg", reg, ARRAY_SIZE(reg));
  91. if (ret) {
  92. pr_err("unable to find st,stih407-dwc3 reg property(%d)\n", ret);
  93. return ret;
  94. }
  95. plat->glue_base = reg[0];
  96. plat->syscfg_offset = reg[2];
  97. /* get corresponding syscon phandle */
  98. ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "st,syscfg",
  99. &syscon);
  100. if (ret) {
  101. pr_err("unable to find syscon device (%d)\n", ret);
  102. return ret;
  103. }
  104. /* get syscfg-reg base address */
  105. regmap = syscon_get_regmap(syscon);
  106. if (!regmap) {
  107. pr_err("unable to find regmap\n");
  108. return -ENODEV;
  109. }
  110. plat->syscfg_base = regmap->ranges[0].start;
  111. /* get powerdown reset */
  112. ret = reset_get_by_name(dev, "powerdown", &plat->powerdown_ctl);
  113. if (ret) {
  114. pr_err("can't get powerdown reset for %s (%d)", dev->name, ret);
  115. return ret;
  116. }
  117. /* get softreset reset */
  118. ret = reset_get_by_name(dev, "softreset", &plat->softreset_ctl);
  119. if (ret)
  120. pr_err("can't get soft reset for %s (%d)", dev->name, ret);
  121. return ret;
  122. };
  123. static int sti_dwc3_glue_bind(struct udevice *dev)
  124. {
  125. struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev);
  126. int dwc3_node;
  127. /* check if one subnode is present */
  128. dwc3_node = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev));
  129. if (dwc3_node <= 0) {
  130. pr_err("Can't find subnode for %s\n", dev->name);
  131. return -ENODEV;
  132. }
  133. /* check if the subnode compatible string is the dwc3 one*/
  134. if (fdt_node_check_compatible(gd->fdt_blob, dwc3_node,
  135. "snps,dwc3") != 0) {
  136. pr_err("Can't find dwc3 subnode for %s\n", dev->name);
  137. return -ENODEV;
  138. }
  139. /* retrieve the DWC3 dual role mode */
  140. plat->mode = usb_get_dr_mode(dwc3_node);
  141. if (plat->mode == USB_DR_MODE_UNKNOWN)
  142. /* by default set dual role mode to HOST */
  143. plat->mode = USB_DR_MODE_HOST;
  144. return dm_scan_fdt_dev(dev);
  145. }
  146. static int sti_dwc3_glue_probe(struct udevice *dev)
  147. {
  148. struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev);
  149. int ret;
  150. /* deassert both powerdown and softreset */
  151. ret = reset_deassert(&plat->powerdown_ctl);
  152. if (ret < 0) {
  153. pr_err("DWC3 powerdown reset deassert failed: %d", ret);
  154. return ret;
  155. }
  156. ret = reset_deassert(&plat->softreset_ctl);
  157. if (ret < 0) {
  158. pr_err("DWC3 soft reset deassert failed: %d", ret);
  159. goto softreset_err;
  160. }
  161. ret = sti_dwc3_glue_drd_init(plat);
  162. if (ret)
  163. goto init_err;
  164. sti_dwc3_glue_init(plat);
  165. return 0;
  166. init_err:
  167. ret = reset_assert(&plat->softreset_ctl);
  168. if (ret < 0) {
  169. pr_err("DWC3 soft reset deassert failed: %d", ret);
  170. return ret;
  171. }
  172. softreset_err:
  173. ret = reset_assert(&plat->powerdown_ctl);
  174. if (ret < 0)
  175. pr_err("DWC3 powerdown reset deassert failed: %d", ret);
  176. return ret;
  177. }
  178. static int sti_dwc3_glue_remove(struct udevice *dev)
  179. {
  180. struct sti_dwc3_glue_platdata *plat = dev_get_platdata(dev);
  181. int ret;
  182. /* assert both powerdown and softreset */
  183. ret = reset_assert(&plat->powerdown_ctl);
  184. if (ret < 0) {
  185. pr_err("DWC3 powerdown reset deassert failed: %d", ret);
  186. return ret;
  187. }
  188. ret = reset_assert(&plat->softreset_ctl);
  189. if (ret < 0)
  190. pr_err("DWC3 soft reset deassert failed: %d", ret);
  191. return ret;
  192. }
  193. static const struct udevice_id sti_dwc3_glue_ids[] = {
  194. { .compatible = "st,stih407-dwc3" },
  195. { }
  196. };
  197. U_BOOT_DRIVER(dwc3_sti_glue) = {
  198. .name = "dwc3_sti_glue",
  199. .id = UCLASS_MISC,
  200. .of_match = sti_dwc3_glue_ids,
  201. .ofdata_to_platdata = sti_dwc3_glue_ofdata_to_platdata,
  202. .probe = sti_dwc3_glue_probe,
  203. .remove = sti_dwc3_glue_remove,
  204. .bind = sti_dwc3_glue_bind,
  205. .platdata_auto_alloc_size = sizeof(struct sti_dwc3_glue_platdata),
  206. .flags = DM_FLAG_ALLOC_PRIV_DMA,
  207. };