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