pcie-dw-rockchip.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * PCIe host controller driver for Rockchip SoCs.
  4. *
  5. * Copyright (C) 2021 Rockchip Electronics Co., Ltd.
  6. * http://www.rock-chips.com
  7. *
  8. * Author: Simon Xue <[email protected]>
  9. */
  10. #include <linux/clk.h>
  11. #include <linux/gpio/consumer.h>
  12. #include <linux/irqchip/chained_irq.h>
  13. #include <linux/irqdomain.h>
  14. #include <linux/mfd/syscon.h>
  15. #include <linux/module.h>
  16. #include <linux/of_device.h>
  17. #include <linux/of_irq.h>
  18. #include <linux/phy/phy.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/regmap.h>
  21. #include <linux/reset.h>
  22. #include "pcie-designware.h"
  23. /*
  24. * The upper 16 bits of PCIE_CLIENT_CONFIG are a write
  25. * mask for the lower 16 bits.
  26. */
  27. #define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val))
  28. #define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val)
  29. #define HIWORD_DISABLE_BIT(val) HIWORD_UPDATE(val, ~val)
  30. #define to_rockchip_pcie(x) dev_get_drvdata((x)->dev)
  31. #define PCIE_CLIENT_RC_MODE HIWORD_UPDATE_BIT(0x40)
  32. #define PCIE_CLIENT_ENABLE_LTSSM HIWORD_UPDATE_BIT(0xc)
  33. #define PCIE_SMLH_LINKUP BIT(16)
  34. #define PCIE_RDLH_LINKUP BIT(17)
  35. #define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP)
  36. #define PCIE_L0S_ENTRY 0x11
  37. #define PCIE_CLIENT_GENERAL_CONTROL 0x0
  38. #define PCIE_CLIENT_INTR_STATUS_LEGACY 0x8
  39. #define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c
  40. #define PCIE_CLIENT_GENERAL_DEBUG 0x104
  41. #define PCIE_CLIENT_HOT_RESET_CTRL 0x180
  42. #define PCIE_CLIENT_LTSSM_STATUS 0x300
  43. #define PCIE_LTSSM_ENABLE_ENHANCE BIT(4)
  44. #define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0)
  45. struct rockchip_pcie {
  46. struct dw_pcie pci;
  47. void __iomem *apb_base;
  48. struct phy *phy;
  49. struct clk_bulk_data *clks;
  50. unsigned int clk_cnt;
  51. struct reset_control *rst;
  52. struct gpio_desc *rst_gpio;
  53. struct regulator *vpcie3v3;
  54. struct irq_domain *irq_domain;
  55. };
  56. static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip,
  57. u32 reg)
  58. {
  59. return readl_relaxed(rockchip->apb_base + reg);
  60. }
  61. static void rockchip_pcie_writel_apb(struct rockchip_pcie *rockchip,
  62. u32 val, u32 reg)
  63. {
  64. writel_relaxed(val, rockchip->apb_base + reg);
  65. }
  66. static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
  67. {
  68. struct irq_chip *chip = irq_desc_get_chip(desc);
  69. struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc);
  70. unsigned long reg, hwirq;
  71. chained_irq_enter(chip, desc);
  72. reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_LEGACY);
  73. for_each_set_bit(hwirq, &reg, 4)
  74. generic_handle_domain_irq(rockchip->irq_domain, hwirq);
  75. chained_irq_exit(chip, desc);
  76. }
  77. static void rockchip_intx_mask(struct irq_data *data)
  78. {
  79. rockchip_pcie_writel_apb(irq_data_get_irq_chip_data(data),
  80. HIWORD_UPDATE_BIT(BIT(data->hwirq)),
  81. PCIE_CLIENT_INTR_MASK_LEGACY);
  82. };
  83. static void rockchip_intx_unmask(struct irq_data *data)
  84. {
  85. rockchip_pcie_writel_apb(irq_data_get_irq_chip_data(data),
  86. HIWORD_DISABLE_BIT(BIT(data->hwirq)),
  87. PCIE_CLIENT_INTR_MASK_LEGACY);
  88. };
  89. static struct irq_chip rockchip_intx_irq_chip = {
  90. .name = "INTx",
  91. .irq_mask = rockchip_intx_mask,
  92. .irq_unmask = rockchip_intx_unmask,
  93. .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
  94. };
  95. static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
  96. irq_hw_number_t hwirq)
  97. {
  98. irq_set_chip_and_handler(irq, &rockchip_intx_irq_chip, handle_level_irq);
  99. irq_set_chip_data(irq, domain->host_data);
  100. return 0;
  101. }
  102. static const struct irq_domain_ops intx_domain_ops = {
  103. .map = rockchip_pcie_intx_map,
  104. };
  105. static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip)
  106. {
  107. struct device *dev = rockchip->pci.dev;
  108. struct device_node *intc;
  109. intc = of_get_child_by_name(dev->of_node, "legacy-interrupt-controller");
  110. if (!intc) {
  111. dev_err(dev, "missing child interrupt-controller node\n");
  112. return -EINVAL;
  113. }
  114. rockchip->irq_domain = irq_domain_add_linear(intc, PCI_NUM_INTX,
  115. &intx_domain_ops, rockchip);
  116. of_node_put(intc);
  117. if (!rockchip->irq_domain) {
  118. dev_err(dev, "failed to get a INTx IRQ domain\n");
  119. return -EINVAL;
  120. }
  121. return 0;
  122. }
  123. static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip)
  124. {
  125. rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM,
  126. PCIE_CLIENT_GENERAL_CONTROL);
  127. }
  128. static int rockchip_pcie_link_up(struct dw_pcie *pci)
  129. {
  130. struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
  131. u32 val = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_LTSSM_STATUS);
  132. if ((val & PCIE_LINKUP) == PCIE_LINKUP &&
  133. (val & PCIE_LTSSM_STATUS_MASK) == PCIE_L0S_ENTRY)
  134. return 1;
  135. return 0;
  136. }
  137. static int rockchip_pcie_start_link(struct dw_pcie *pci)
  138. {
  139. struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
  140. /* Reset device */
  141. gpiod_set_value_cansleep(rockchip->rst_gpio, 0);
  142. rockchip_pcie_enable_ltssm(rockchip);
  143. /*
  144. * PCIe requires the refclk to be stable for 100µs prior to releasing
  145. * PERST. See table 2-4 in section 2.6.2 AC Specifications of the PCI
  146. * Express Card Electromechanical Specification, 1.1. However, we don't
  147. * know if the refclk is coming from RC's PHY or external OSC. If it's
  148. * from RC, so enabling LTSSM is the just right place to release #PERST.
  149. * We need more extra time as before, rather than setting just
  150. * 100us as we don't know how long should the device need to reset.
  151. */
  152. msleep(100);
  153. gpiod_set_value_cansleep(rockchip->rst_gpio, 1);
  154. return 0;
  155. }
  156. static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
  157. {
  158. struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
  159. struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
  160. struct device *dev = rockchip->pci.dev;
  161. u32 val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE);
  162. int irq, ret;
  163. irq = of_irq_get_byname(dev->of_node, "legacy");
  164. if (irq < 0)
  165. return irq;
  166. ret = rockchip_pcie_init_irq_domain(rockchip);
  167. if (ret < 0)
  168. dev_err(dev, "failed to init irq domain\n");
  169. irq_set_chained_handler_and_data(irq, rockchip_pcie_legacy_int_handler,
  170. rockchip);
  171. /* LTSSM enable control mode */
  172. rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
  173. rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_RC_MODE,
  174. PCIE_CLIENT_GENERAL_CONTROL);
  175. return 0;
  176. }
  177. static const struct dw_pcie_host_ops rockchip_pcie_host_ops = {
  178. .host_init = rockchip_pcie_host_init,
  179. };
  180. static int rockchip_pcie_clk_init(struct rockchip_pcie *rockchip)
  181. {
  182. struct device *dev = rockchip->pci.dev;
  183. int ret;
  184. ret = devm_clk_bulk_get_all(dev, &rockchip->clks);
  185. if (ret < 0)
  186. return ret;
  187. rockchip->clk_cnt = ret;
  188. return clk_bulk_prepare_enable(rockchip->clk_cnt, rockchip->clks);
  189. }
  190. static int rockchip_pcie_resource_get(struct platform_device *pdev,
  191. struct rockchip_pcie *rockchip)
  192. {
  193. rockchip->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb");
  194. if (IS_ERR(rockchip->apb_base))
  195. return PTR_ERR(rockchip->apb_base);
  196. rockchip->rst_gpio = devm_gpiod_get_optional(&pdev->dev, "reset",
  197. GPIOD_OUT_HIGH);
  198. if (IS_ERR(rockchip->rst_gpio))
  199. return PTR_ERR(rockchip->rst_gpio);
  200. rockchip->rst = devm_reset_control_array_get_exclusive(&pdev->dev);
  201. if (IS_ERR(rockchip->rst))
  202. return dev_err_probe(&pdev->dev, PTR_ERR(rockchip->rst),
  203. "failed to get reset lines\n");
  204. return 0;
  205. }
  206. static int rockchip_pcie_phy_init(struct rockchip_pcie *rockchip)
  207. {
  208. struct device *dev = rockchip->pci.dev;
  209. int ret;
  210. rockchip->phy = devm_phy_get(dev, "pcie-phy");
  211. if (IS_ERR(rockchip->phy))
  212. return dev_err_probe(dev, PTR_ERR(rockchip->phy),
  213. "missing PHY\n");
  214. ret = phy_init(rockchip->phy);
  215. if (ret < 0)
  216. return ret;
  217. ret = phy_power_on(rockchip->phy);
  218. if (ret)
  219. phy_exit(rockchip->phy);
  220. return ret;
  221. }
  222. static void rockchip_pcie_phy_deinit(struct rockchip_pcie *rockchip)
  223. {
  224. phy_exit(rockchip->phy);
  225. phy_power_off(rockchip->phy);
  226. }
  227. static const struct dw_pcie_ops dw_pcie_ops = {
  228. .link_up = rockchip_pcie_link_up,
  229. .start_link = rockchip_pcie_start_link,
  230. };
  231. static int rockchip_pcie_probe(struct platform_device *pdev)
  232. {
  233. struct device *dev = &pdev->dev;
  234. struct rockchip_pcie *rockchip;
  235. struct dw_pcie_rp *pp;
  236. int ret;
  237. rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
  238. if (!rockchip)
  239. return -ENOMEM;
  240. platform_set_drvdata(pdev, rockchip);
  241. rockchip->pci.dev = dev;
  242. rockchip->pci.ops = &dw_pcie_ops;
  243. pp = &rockchip->pci.pp;
  244. pp->ops = &rockchip_pcie_host_ops;
  245. ret = rockchip_pcie_resource_get(pdev, rockchip);
  246. if (ret)
  247. return ret;
  248. ret = reset_control_assert(rockchip->rst);
  249. if (ret)
  250. return ret;
  251. /* DON'T MOVE ME: must be enable before PHY init */
  252. rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
  253. if (IS_ERR(rockchip->vpcie3v3)) {
  254. if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV)
  255. return dev_err_probe(dev, PTR_ERR(rockchip->vpcie3v3),
  256. "failed to get vpcie3v3 regulator\n");
  257. rockchip->vpcie3v3 = NULL;
  258. } else {
  259. ret = regulator_enable(rockchip->vpcie3v3);
  260. if (ret) {
  261. dev_err(dev, "failed to enable vpcie3v3 regulator\n");
  262. return ret;
  263. }
  264. }
  265. ret = rockchip_pcie_phy_init(rockchip);
  266. if (ret)
  267. goto disable_regulator;
  268. ret = reset_control_deassert(rockchip->rst);
  269. if (ret)
  270. goto deinit_phy;
  271. ret = rockchip_pcie_clk_init(rockchip);
  272. if (ret)
  273. goto deinit_phy;
  274. ret = dw_pcie_host_init(pp);
  275. if (!ret)
  276. return 0;
  277. clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks);
  278. deinit_phy:
  279. rockchip_pcie_phy_deinit(rockchip);
  280. disable_regulator:
  281. if (rockchip->vpcie3v3)
  282. regulator_disable(rockchip->vpcie3v3);
  283. return ret;
  284. }
  285. static const struct of_device_id rockchip_pcie_of_match[] = {
  286. { .compatible = "rockchip,rk3568-pcie", },
  287. {},
  288. };
  289. static struct platform_driver rockchip_pcie_driver = {
  290. .driver = {
  291. .name = "rockchip-dw-pcie",
  292. .of_match_table = rockchip_pcie_of_match,
  293. .suppress_bind_attrs = true,
  294. },
  295. .probe = rockchip_pcie_probe,
  296. };
  297. builtin_platform_driver(rockchip_pcie_driver);