xref: /linux/drivers/usb/cdns3/cdns3-imx.c (revision 67439d555f7d46349deef56886129da96bd15744)
1  // SPDX-License-Identifier: GPL-2.0
2  /**
3   * cdns3-imx.c - NXP i.MX specific Glue layer for Cadence USB Controller
4   *
5   * Copyright (C) 2019 NXP
6   */
7  
8  #include <linux/bits.h>
9  #include <linux/clk.h>
10  #include <linux/module.h>
11  #include <linux/kernel.h>
12  #include <linux/interrupt.h>
13  #include <linux/platform_device.h>
14  #include <linux/dma-mapping.h>
15  #include <linux/io.h>
16  #include <linux/of_platform.h>
17  #include <linux/iopoll.h>
18  
19  #define USB3_CORE_CTRL1    0x00
20  #define USB3_CORE_CTRL2    0x04
21  #define USB3_INT_REG       0x08
22  #define USB3_CORE_STATUS   0x0c
23  #define XHCI_DEBUG_LINK_ST 0x10
24  #define XHCI_DEBUG_BUS     0x14
25  #define USB3_SSPHY_CTRL1   0x40
26  #define USB3_SSPHY_CTRL2   0x44
27  #define USB3_SSPHY_STATUS  0x4c
28  #define USB2_PHY_CTRL1     0x50
29  #define USB2_PHY_CTRL2     0x54
30  #define USB2_PHY_STATUS    0x5c
31  
32  /* Register bits definition */
33  
34  /* USB3_CORE_CTRL1 */
35  #define SW_RESET_MASK	(0x3f << 26)
36  #define PWR_SW_RESET	BIT(31)
37  #define APB_SW_RESET	BIT(30)
38  #define AXI_SW_RESET	BIT(29)
39  #define RW_SW_RESET	BIT(28)
40  #define PHY_SW_RESET	BIT(27)
41  #define PHYAHB_SW_RESET	BIT(26)
42  #define ALL_SW_RESET	(PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \
43  		RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET)
44  #define OC_DISABLE	BIT(9)
45  #define MDCTRL_CLK_SEL	BIT(7)
46  #define MODE_STRAP_MASK	(0x7)
47  #define DEV_MODE	(1 << 2)
48  #define HOST_MODE	(1 << 1)
49  #define OTG_MODE	(1 << 0)
50  
51  /* USB3_INT_REG */
52  #define CLK_125_REQ	BIT(29)
53  #define LPM_CLK_REQ	BIT(28)
54  #define DEVU3_WAEKUP_EN	BIT(14)
55  #define OTG_WAKEUP_EN	BIT(12)
56  #define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
57  #define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
58  
59  /* USB3_CORE_STATUS */
60  #define MDCTRL_CLK_STATUS	BIT(15)
61  #define DEV_POWER_ON_READY	BIT(13)
62  #define HOST_POWER_ON_READY	BIT(12)
63  
64  /* USB3_SSPHY_STATUS */
65  #define CLK_VALID_MASK		(0x3f << 26)
66  #define CLK_VALID_COMPARE_BITS	(0xf << 28)
67  #define PHY_REFCLK_REQ		(1 << 0)
68  
69  struct cdns_imx {
70  	struct device *dev;
71  	void __iomem *noncore;
72  	struct clk_bulk_data *clks;
73  	int num_clks;
74  };
75  
76  static inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset)
77  {
78  	return readl(data->noncore + offset);
79  }
80  
81  static inline void cdns_imx_writel(struct cdns_imx *data, u32 offset, u32 value)
82  {
83  	writel(value, data->noncore + offset);
84  }
85  
86  static const struct clk_bulk_data imx_cdns3_core_clks[] = {
87  	{ .id = "usb3_lpm_clk" },
88  	{ .id = "usb3_bus_clk" },
89  	{ .id = "usb3_aclk" },
90  	{ .id = "usb3_ipg_clk" },
91  	{ .id = "usb3_core_pclk" },
92  };
93  
94  static int cdns_imx_noncore_init(struct cdns_imx *data)
95  {
96  	u32 value;
97  	int ret;
98  	struct device *dev = data->dev;
99  
100  	cdns_imx_writel(data, USB3_SSPHY_STATUS, CLK_VALID_MASK);
101  	udelay(1);
102  	ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value,
103  		(value & CLK_VALID_COMPARE_BITS) == CLK_VALID_COMPARE_BITS,
104  		10, 100000);
105  	if (ret) {
106  		dev_err(dev, "wait clkvld timeout\n");
107  		return ret;
108  	}
109  
110  	value = cdns_imx_readl(data, USB3_CORE_CTRL1);
111  	value |= ALL_SW_RESET;
112  	cdns_imx_writel(data, USB3_CORE_CTRL1, value);
113  	udelay(1);
114  
115  	value = cdns_imx_readl(data, USB3_CORE_CTRL1);
116  	value = (value & ~MODE_STRAP_MASK) | OTG_MODE | OC_DISABLE;
117  	cdns_imx_writel(data, USB3_CORE_CTRL1, value);
118  
119  	value = cdns_imx_readl(data, USB3_INT_REG);
120  	value |= HOST_INT1_EN | DEV_INT_EN;
121  	cdns_imx_writel(data, USB3_INT_REG, value);
122  
123  	value = cdns_imx_readl(data, USB3_CORE_CTRL1);
124  	value &= ~ALL_SW_RESET;
125  	cdns_imx_writel(data, USB3_CORE_CTRL1, value);
126  	return ret;
127  }
128  
129  static int cdns_imx_probe(struct platform_device *pdev)
130  {
131  	struct device *dev = &pdev->dev;
132  	struct device_node *node = dev->of_node;
133  	struct cdns_imx *data;
134  	int ret;
135  
136  	if (!node)
137  		return -ENODEV;
138  
139  	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
140  	if (!data)
141  		return -ENOMEM;
142  
143  	platform_set_drvdata(pdev, data);
144  	data->dev = dev;
145  	data->noncore = devm_platform_ioremap_resource(pdev, 0);
146  	if (IS_ERR(data->noncore)) {
147  		dev_err(dev, "can't map IOMEM resource\n");
148  		return PTR_ERR(data->noncore);
149  	}
150  
151  	data->num_clks = ARRAY_SIZE(imx_cdns3_core_clks);
152  	data->clks = (struct clk_bulk_data *)imx_cdns3_core_clks;
153  	ret = devm_clk_bulk_get(dev, data->num_clks, data->clks);
154  	if (ret)
155  		return ret;
156  
157  	ret = clk_bulk_prepare_enable(data->num_clks, data->clks);
158  	if (ret)
159  		return ret;
160  
161  	ret = cdns_imx_noncore_init(data);
162  	if (ret)
163  		goto err;
164  
165  	ret = of_platform_populate(node, NULL, NULL, dev);
166  	if (ret) {
167  		dev_err(dev, "failed to create children: %d\n", ret);
168  		goto err;
169  	}
170  
171  	return ret;
172  
173  err:
174  	clk_bulk_disable_unprepare(data->num_clks, data->clks);
175  	return ret;
176  }
177  
178  static int cdns_imx_remove_core(struct device *dev, void *data)
179  {
180  	struct platform_device *pdev = to_platform_device(dev);
181  
182  	platform_device_unregister(pdev);
183  
184  	return 0;
185  }
186  
187  static int cdns_imx_remove(struct platform_device *pdev)
188  {
189  	struct device *dev = &pdev->dev;
190  
191  	device_for_each_child(dev, NULL, cdns_imx_remove_core);
192  	platform_set_drvdata(pdev, NULL);
193  
194  	return 0;
195  }
196  
197  static const struct of_device_id cdns_imx_of_match[] = {
198  	{ .compatible = "fsl,imx8qm-usb3", },
199  	{},
200  };
201  MODULE_DEVICE_TABLE(of, cdns_imx_of_match);
202  
203  static struct platform_driver cdns_imx_driver = {
204  	.probe		= cdns_imx_probe,
205  	.remove		= cdns_imx_remove,
206  	.driver		= {
207  		.name	= "cdns3-imx",
208  		.of_match_table	= cdns_imx_of_match,
209  	},
210  };
211  module_platform_driver(cdns_imx_driver);
212  
213  MODULE_ALIAS("platform:cdns3-imx");
214  MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
215  MODULE_LICENSE("GPL v2");
216  MODULE_DESCRIPTION("Cadence USB3 i.MX Glue Layer");
217