xref: /linux/drivers/usb/cdns3/cdns3-ti.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1387c359bSRoger Quadros // SPDX-License-Identifier: GPL-2.0
2a945fd0aSLee Jones /*
3387c359bSRoger Quadros  * cdns3-ti.c - TI specific Glue layer for Cadence USB Controller
4387c359bSRoger Quadros  *
5ffeb1e9eSAlexander A. Klimov  * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
6387c359bSRoger Quadros  */
7387c359bSRoger Quadros 
8387c359bSRoger Quadros #include <linux/bits.h>
9387c359bSRoger Quadros #include <linux/clk.h>
10387c359bSRoger Quadros #include <linux/module.h>
11387c359bSRoger Quadros #include <linux/kernel.h>
12387c359bSRoger Quadros #include <linux/interrupt.h>
13387c359bSRoger Quadros #include <linux/platform_device.h>
14387c359bSRoger Quadros #include <linux/dma-mapping.h>
15387c359bSRoger Quadros #include <linux/io.h>
16387c359bSRoger Quadros #include <linux/of_platform.h>
17387c359bSRoger Quadros #include <linux/pm_runtime.h>
18484468fbSRob Herring #include <linux/property.h>
19*b50a2da0SRoger Quadros #include "core.h"
20387c359bSRoger Quadros 
21387c359bSRoger Quadros /* USB Wrapper register offsets */
22387c359bSRoger Quadros #define USBSS_PID		0x0
23387c359bSRoger Quadros #define	USBSS_W1		0x4
24387c359bSRoger Quadros #define USBSS_STATIC_CONFIG	0x8
25387c359bSRoger Quadros #define USBSS_PHY_TEST		0xc
26387c359bSRoger Quadros #define	USBSS_DEBUG_CTRL	0x10
27387c359bSRoger Quadros #define	USBSS_DEBUG_INFO	0x14
28387c359bSRoger Quadros #define	USBSS_DEBUG_LINK_STATE	0x18
29387c359bSRoger Quadros #define	USBSS_DEVICE_CTRL	0x1c
30387c359bSRoger Quadros 
31387c359bSRoger Quadros /* Wrapper 1 register bits */
32387c359bSRoger Quadros #define USBSS_W1_PWRUP_RST		BIT(0)
33387c359bSRoger Quadros #define USBSS_W1_OVERCURRENT_SEL	BIT(8)
34387c359bSRoger Quadros #define USBSS_W1_MODESTRAP_SEL		BIT(9)
35387c359bSRoger Quadros #define USBSS_W1_OVERCURRENT		BIT(16)
36387c359bSRoger Quadros #define USBSS_W1_MODESTRAP_MASK		GENMASK(18, 17)
37387c359bSRoger Quadros #define USBSS_W1_MODESTRAP_SHIFT	17
38387c359bSRoger Quadros #define USBSS_W1_USB2_ONLY		BIT(19)
39387c359bSRoger Quadros 
40387c359bSRoger Quadros /* Static config register bits */
41387c359bSRoger Quadros #define USBSS1_STATIC_PLL_REF_SEL_MASK	GENMASK(8, 5)
42387c359bSRoger Quadros #define USBSS1_STATIC_PLL_REF_SEL_SHIFT	5
43387c359bSRoger Quadros #define USBSS1_STATIC_LOOPBACK_MODE_MASK	GENMASK(4, 3)
44387c359bSRoger Quadros #define USBSS1_STATIC_LOOPBACK_MODE_SHIFT	3
45387c359bSRoger Quadros #define USBSS1_STATIC_VBUS_SEL_MASK	GENMASK(2, 1)
46387c359bSRoger Quadros #define USBSS1_STATIC_VBUS_SEL_SHIFT	1
47387c359bSRoger Quadros #define USBSS1_STATIC_LANE_REVERSE	BIT(0)
48387c359bSRoger Quadros 
49387c359bSRoger Quadros /* Modestrap modes */
50387c359bSRoger Quadros enum modestrap_mode { USBSS_MODESTRAP_MODE_NONE,
51387c359bSRoger Quadros 		      USBSS_MODESTRAP_MODE_HOST,
52387c359bSRoger Quadros 		      USBSS_MODESTRAP_MODE_PERIPHERAL};
53387c359bSRoger Quadros 
54387c359bSRoger Quadros struct cdns_ti {
55387c359bSRoger Quadros 	struct device *dev;
56387c359bSRoger Quadros 	void __iomem *usbss;
5770d8b9e5SColin Ian King 	unsigned usb2_only:1;
5870d8b9e5SColin Ian King 	unsigned vbus_divider:1;
59387c359bSRoger Quadros 	struct clk *usb2_refclk;
60387c359bSRoger Quadros 	struct clk *lpm_clk;
61387c359bSRoger Quadros };
62387c359bSRoger Quadros 
63387c359bSRoger Quadros static const int cdns_ti_rate_table[] = {	/* in KHZ */
64387c359bSRoger Quadros 	9600,
65387c359bSRoger Quadros 	10000,
66387c359bSRoger Quadros 	12000,
67387c359bSRoger Quadros 	19200,
68387c359bSRoger Quadros 	20000,
69387c359bSRoger Quadros 	24000,
70387c359bSRoger Quadros 	25000,
71387c359bSRoger Quadros 	26000,
72387c359bSRoger Quadros 	38400,
73387c359bSRoger Quadros 	40000,
74387c359bSRoger Quadros 	58000,
75387c359bSRoger Quadros 	50000,
76387c359bSRoger Quadros 	52000,
77387c359bSRoger Quadros };
78387c359bSRoger Quadros 
cdns_ti_readl(struct cdns_ti * data,u32 offset)79387c359bSRoger Quadros static inline u32 cdns_ti_readl(struct cdns_ti *data, u32 offset)
80387c359bSRoger Quadros {
81387c359bSRoger Quadros 	return readl(data->usbss + offset);
82387c359bSRoger Quadros }
83387c359bSRoger Quadros 
cdns_ti_writel(struct cdns_ti * data,u32 offset,u32 value)84387c359bSRoger Quadros static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value)
85387c359bSRoger Quadros {
86387c359bSRoger Quadros 	writel(value, data->usbss + offset);
87387c359bSRoger Quadros }
88387c359bSRoger Quadros 
89*b50a2da0SRoger Quadros static struct cdns3_platform_data cdns_ti_pdata = {
90*b50a2da0SRoger Quadros 	.quirks = CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE,   /* Errata i2409 */
91*b50a2da0SRoger Quadros };
92*b50a2da0SRoger Quadros 
93*b50a2da0SRoger Quadros static const struct of_dev_auxdata cdns_ti_auxdata[] = {
94*b50a2da0SRoger Quadros 	{
95*b50a2da0SRoger Quadros 		.compatible = "cdns,usb3",
96*b50a2da0SRoger Quadros 		.platform_data = &cdns_ti_pdata,
97*b50a2da0SRoger Quadros 	},
98*b50a2da0SRoger Quadros 	{},
99*b50a2da0SRoger Quadros };
100*b50a2da0SRoger Quadros 
cdns_ti_probe(struct platform_device * pdev)101387c359bSRoger Quadros static int cdns_ti_probe(struct platform_device *pdev)
102387c359bSRoger Quadros {
103387c359bSRoger Quadros 	struct device *dev = &pdev->dev;
104387c359bSRoger Quadros 	struct device_node *node = pdev->dev.of_node;
105387c359bSRoger Quadros 	struct cdns_ti *data;
106387c359bSRoger Quadros 	int error;
107387c359bSRoger Quadros 	u32 reg;
108387c359bSRoger Quadros 	int rate_code, i;
109387c359bSRoger Quadros 	unsigned long rate;
110387c359bSRoger Quadros 
111387c359bSRoger Quadros 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
112387c359bSRoger Quadros 	if (!data)
113387c359bSRoger Quadros 		return -ENOMEM;
114387c359bSRoger Quadros 
115387c359bSRoger Quadros 	platform_set_drvdata(pdev, data);
116387c359bSRoger Quadros 
117387c359bSRoger Quadros 	data->dev = dev;
118387c359bSRoger Quadros 
119387c359bSRoger Quadros 	data->usbss = devm_platform_ioremap_resource(pdev, 0);
120387c359bSRoger Quadros 	if (IS_ERR(data->usbss)) {
121387c359bSRoger Quadros 		dev_err(dev, "can't map IOMEM resource\n");
122387c359bSRoger Quadros 		return PTR_ERR(data->usbss);
123387c359bSRoger Quadros 	}
124387c359bSRoger Quadros 
125387c359bSRoger Quadros 	data->usb2_refclk = devm_clk_get(dev, "ref");
126387c359bSRoger Quadros 	if (IS_ERR(data->usb2_refclk)) {
127387c359bSRoger Quadros 		dev_err(dev, "can't get usb2_refclk\n");
128387c359bSRoger Quadros 		return PTR_ERR(data->usb2_refclk);
129387c359bSRoger Quadros 	}
130387c359bSRoger Quadros 
131387c359bSRoger Quadros 	data->lpm_clk = devm_clk_get(dev, "lpm");
132387c359bSRoger Quadros 	if (IS_ERR(data->lpm_clk)) {
133387c359bSRoger Quadros 		dev_err(dev, "can't get lpm_clk\n");
134387c359bSRoger Quadros 		return PTR_ERR(data->lpm_clk);
135387c359bSRoger Quadros 	}
136387c359bSRoger Quadros 
137387c359bSRoger Quadros 	rate = clk_get_rate(data->usb2_refclk);
138387c359bSRoger Quadros 	rate /= 1000;	/* To KHz */
139387c359bSRoger Quadros 	for (i = 0; i < ARRAY_SIZE(cdns_ti_rate_table); i++) {
140387c359bSRoger Quadros 		if (cdns_ti_rate_table[i] == rate)
141387c359bSRoger Quadros 			break;
142387c359bSRoger Quadros 	}
143387c359bSRoger Quadros 
144387c359bSRoger Quadros 	if (i == ARRAY_SIZE(cdns_ti_rate_table)) {
145387c359bSRoger Quadros 		dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
146387c359bSRoger Quadros 		return -EINVAL;
147387c359bSRoger Quadros 	}
148387c359bSRoger Quadros 
149387c359bSRoger Quadros 	rate_code = i;
150387c359bSRoger Quadros 
151387c359bSRoger Quadros 	pm_runtime_enable(dev);
152387c359bSRoger Quadros 	error = pm_runtime_get_sync(dev);
153387c359bSRoger Quadros 	if (error < 0) {
154387c359bSRoger Quadros 		dev_err(dev, "pm_runtime_get_sync failed: %d\n", error);
155e5b91349SDinghao Liu 		goto err;
156387c359bSRoger Quadros 	}
157387c359bSRoger Quadros 
158387c359bSRoger Quadros 	/* assert RESET */
159387c359bSRoger Quadros 	reg = cdns_ti_readl(data, USBSS_W1);
160387c359bSRoger Quadros 	reg &= ~USBSS_W1_PWRUP_RST;
161387c359bSRoger Quadros 	cdns_ti_writel(data, USBSS_W1, reg);
162387c359bSRoger Quadros 
163387c359bSRoger Quadros 	/* set static config */
164387c359bSRoger Quadros 	reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
165387c359bSRoger Quadros 	reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
166387c359bSRoger Quadros 	reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
167387c359bSRoger Quadros 
168387c359bSRoger Quadros 	reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
169387c359bSRoger Quadros 	data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
170387c359bSRoger Quadros 	if (data->vbus_divider)
171387c359bSRoger Quadros 		reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
172387c359bSRoger Quadros 
173387c359bSRoger Quadros 	cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
174387c359bSRoger Quadros 	reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
175387c359bSRoger Quadros 
176387c359bSRoger Quadros 	/* set USB2_ONLY mode if requested */
177387c359bSRoger Quadros 	reg = cdns_ti_readl(data, USBSS_W1);
178387c359bSRoger Quadros 	data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
179387c359bSRoger Quadros 	if (data->usb2_only)
180387c359bSRoger Quadros 		reg |= USBSS_W1_USB2_ONLY;
181387c359bSRoger Quadros 
182387c359bSRoger Quadros 	/* set default modestrap */
183387c359bSRoger Quadros 	reg |= USBSS_W1_MODESTRAP_SEL;
184387c359bSRoger Quadros 	reg &= ~USBSS_W1_MODESTRAP_MASK;
185387c359bSRoger Quadros 	reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
186387c359bSRoger Quadros 	cdns_ti_writel(data, USBSS_W1, reg);
187387c359bSRoger Quadros 
188387c359bSRoger Quadros 	/* de-assert RESET */
189387c359bSRoger Quadros 	reg |= USBSS_W1_PWRUP_RST;
190387c359bSRoger Quadros 	cdns_ti_writel(data, USBSS_W1, reg);
191387c359bSRoger Quadros 
192*b50a2da0SRoger Quadros 	error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev);
193387c359bSRoger Quadros 	if (error) {
194387c359bSRoger Quadros 		dev_err(dev, "failed to create children: %d\n", error);
195387c359bSRoger Quadros 		goto err;
196387c359bSRoger Quadros 	}
197387c359bSRoger Quadros 
198387c359bSRoger Quadros 	return 0;
199387c359bSRoger Quadros 
200387c359bSRoger Quadros err:
201387c359bSRoger Quadros 	pm_runtime_put_sync(data->dev);
202387c359bSRoger Quadros 	pm_runtime_disable(data->dev);
203387c359bSRoger Quadros 
204387c359bSRoger Quadros 	return error;
205387c359bSRoger Quadros }
206387c359bSRoger Quadros 
cdns_ti_remove_core(struct device * dev,void * c)207387c359bSRoger Quadros static int cdns_ti_remove_core(struct device *dev, void *c)
208387c359bSRoger Quadros {
209387c359bSRoger Quadros 	struct platform_device *pdev = to_platform_device(dev);
210387c359bSRoger Quadros 
211387c359bSRoger Quadros 	platform_device_unregister(pdev);
212387c359bSRoger Quadros 
213387c359bSRoger Quadros 	return 0;
214387c359bSRoger Quadros }
215387c359bSRoger Quadros 
cdns_ti_remove(struct platform_device * pdev)216defbfca6SUwe Kleine-König static void cdns_ti_remove(struct platform_device *pdev)
217387c359bSRoger Quadros {
218387c359bSRoger Quadros 	struct device *dev = &pdev->dev;
219387c359bSRoger Quadros 
220387c359bSRoger Quadros 	device_for_each_child(dev, NULL, cdns_ti_remove_core);
221387c359bSRoger Quadros 	pm_runtime_put_sync(dev);
222387c359bSRoger Quadros 	pm_runtime_disable(dev);
223387c359bSRoger Quadros 
224387c359bSRoger Quadros 	platform_set_drvdata(pdev, NULL);
225387c359bSRoger Quadros }
226387c359bSRoger Quadros 
227387c359bSRoger Quadros static const struct of_device_id cdns_ti_of_match[] = {
228387c359bSRoger Quadros 	{ .compatible = "ti,j721e-usb", },
2294f30b9d2SAswath Govindraju 	{ .compatible = "ti,am64-usb", },
230387c359bSRoger Quadros 	{},
231387c359bSRoger Quadros };
232387c359bSRoger Quadros MODULE_DEVICE_TABLE(of, cdns_ti_of_match);
233387c359bSRoger Quadros 
234387c359bSRoger Quadros static struct platform_driver cdns_ti_driver = {
235387c359bSRoger Quadros 	.probe		= cdns_ti_probe,
236defbfca6SUwe Kleine-König 	.remove_new	= cdns_ti_remove,
237387c359bSRoger Quadros 	.driver		= {
238387c359bSRoger Quadros 		.name	= "cdns3-ti",
239387c359bSRoger Quadros 		.of_match_table	= cdns_ti_of_match,
240387c359bSRoger Quadros 	},
241387c359bSRoger Quadros };
242387c359bSRoger Quadros 
243387c359bSRoger Quadros module_platform_driver(cdns_ti_driver);
244387c359bSRoger Quadros 
245387c359bSRoger Quadros MODULE_ALIAS("platform:cdns3-ti");
246387c359bSRoger Quadros MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
247387c359bSRoger Quadros MODULE_LICENSE("GPL v2");
248387c359bSRoger Quadros MODULE_DESCRIPTION("Cadence USB3 TI Glue Layer");
249