aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/dwc3/dwc3-st.c
diff options
context:
space:
mode:
authorPeter Griffin <peter.griffin@linaro.org>2014-09-05 11:36:30 -0400
committerFelipe Balbi <balbi@ti.com>2014-09-05 11:49:00 -0400
commitf83fca0707c66e36f14efef7f68702cb12de70b7 (patch)
treecf77fb9bb2d9239ae49af8f7b456e048242b239d /drivers/usb/dwc3/dwc3-st.c
parent2c4cbe6e5a9c71408b496e00a78ea9284e98af16 (diff)
usb: dwc3: add ST dwc3 glue layer to manage dwc3 HC
This patch adds the ST glue logic to manage the DWC3 HC on STiH407 SoC family. It manages the powerdown signal, and configures the internal glue logic and syscfg registers. [ balbi@ti.com : actually switch over to of_platform_depopulate() ] Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com> Signed-off-by: Peter Griffin <peter.griffin@linaro.org> Acked-by: Lee Jones <lee.jones@linaro.org> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/dwc3/dwc3-st.c')
-rw-r--r--drivers/usb/dwc3/dwc3-st.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
new file mode 100644
index 000000000000..c7602b5362ad
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -0,0 +1,367 @@
1/**
2 * dwc3-st.c Support for dwc3 platform devices on ST Microelectronics platforms
3 *
4 * This is a small driver for the dwc3 to provide the glue logic
5 * to configure the controller. Tested on STi platforms.
6 *
7 * Copyright (C) 2014 Stmicroelectronics
8 *
9 * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
10 * Contributors: Aymen Bouattay <aymen.bouattay@st.com>
11 * Peter Griffin <peter.griffin@linaro.org>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * Inspired by dwc3-omap.c and dwc3-exynos.c.
19 */
20
21#include <linux/delay.h>
22#include <linux/interrupt.h>
23#include <linux/io.h>
24#include <linux/ioport.h>
25#include <linux/kernel.h>
26#include <linux/mfd/syscon.h>
27#include <linux/module.h>
28#include <linux/of.h>
29#include <linux/of_platform.h>
30#include <linux/platform_device.h>
31#include <linux/slab.h>
32#include <linux/regmap.h>
33#include <linux/reset.h>
34#include <linux/usb/of.h>
35
36#include "core.h"
37#include "io.h"
38
39/* glue registers */
40#define CLKRST_CTRL 0x00
41#define AUX_CLK_EN BIT(0)
42#define SW_PIPEW_RESET_N BIT(4)
43#define EXT_CFG_RESET_N BIT(8)
44/*
45 * 1'b0 : The host controller complies with the xHCI revision 0.96
46 * 1'b1 : The host controller complies with the xHCI revision 1.0
47 */
48#define XHCI_REVISION BIT(12)
49
50#define USB2_VBUS_MNGMNT_SEL1 0x2C
51/*
52 * For all fields in USB2_VBUS_MNGMNT_SEL1
53 * 2’b00 : Override value from Reg 0x30 is selected
54 * 2’b01 : utmiotg_<signal_name> from usb3_top is selected
55 * 2’b10 : pipew_<signal_name> from PIPEW instance is selected
56 * 2’b11 : value is 1'b0
57 */
58#define USB2_VBUS_REG30 0x0
59#define USB2_VBUS_UTMIOTG 0x1
60#define USB2_VBUS_PIPEW 0x2
61#define USB2_VBUS_ZERO 0x3
62
63#define SEL_OVERRIDE_VBUSVALID(n) (n << 0)
64#define SEL_OVERRIDE_POWERPRESENT(n) (n << 4)
65#define SEL_OVERRIDE_BVALID(n) (n << 8)
66
67/* Static DRD configuration */
68#define USB3_CONTROL_MASK 0xf77
69
70#define USB3_DEVICE_NOT_HOST BIT(0)
71#define USB3_FORCE_VBUSVALID BIT(1)
72#define USB3_DELAY_VBUSVALID BIT(2)
73#define USB3_SEL_FORCE_OPMODE BIT(4)
74#define USB3_FORCE_OPMODE(n) (n << 5)
75#define USB3_SEL_FORCE_DPPULLDOWN2 BIT(8)
76#define USB3_FORCE_DPPULLDOWN2 BIT(9)
77#define USB3_SEL_FORCE_DMPULLDOWN2 BIT(10)
78#define USB3_FORCE_DMPULLDOWN2 BIT(11)
79
80/**
81 * struct st_dwc3 - dwc3-st driver private structure
82 * @dev: device pointer
83 * @glue_base: ioaddr for the glue registers
84 * @regmap: regmap pointer for getting syscfg
85 * @syscfg_reg_off: usb syscfg control offset
86 * @dr_mode: drd static host/device config
87 * @rstc_pwrdn: rest controller for powerdown signal
88 * @rstc_rst: reset controller for softreset signal
89 */
90
91struct st_dwc3 {
92 struct device *dev;
93 void __iomem *glue_base;
94 struct regmap *regmap;
95 int syscfg_reg_off;
96 enum usb_dr_mode dr_mode;
97 struct reset_control *rstc_pwrdn;
98 struct reset_control *rstc_rst;
99};
100
101static inline u32 st_dwc3_readl(void __iomem *base, u32 offset)
102{
103 return readl_relaxed(base + offset);
104}
105
106static inline void st_dwc3_writel(void __iomem *base, u32 offset, u32 value)
107{
108 writel_relaxed(value, base + offset);
109}
110
111/**
112 * st_dwc3_drd_init: program the port
113 * @dwc3_data: driver private structure
114 * Description: this function is to program the port as either host or device
115 * according to the static configuration passed from devicetree.
116 * OTG and dual role are not yet supported!
117 */
118static int st_dwc3_drd_init(struct st_dwc3 *dwc3_data)
119{
120 u32 val;
121 int err;
122
123 err = regmap_read(dwc3_data->regmap, dwc3_data->syscfg_reg_off, &val);
124 if (err)
125 return err;
126
127 val &= USB3_CONTROL_MASK;
128
129 switch (dwc3_data->dr_mode) {
130 case USB_DR_MODE_PERIPHERAL:
131
132 val &= ~(USB3_FORCE_VBUSVALID | USB3_DELAY_VBUSVALID
133 | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
134 | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
135 | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
136
137 val |= USB3_DEVICE_NOT_HOST;
138
139 dev_dbg(dwc3_data->dev, "Configuring as Device\n");
140 break;
141
142 case USB_DR_MODE_HOST:
143
144 val &= ~(USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID
145 | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3)
146 | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2
147 | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2);
148
149 /*
150 * USB3_DELAY_VBUSVALID is ANDed with USB_C_VBUSVALID. Thus,
151 * when set to ‘0‘, it can delay the arrival of VBUSVALID
152 * information to VBUSVLDEXT2 input of the pico PHY.
153 * We don't want to do that so we set the bit to '1'.
154 */
155
156 val |= USB3_DELAY_VBUSVALID;
157
158 dev_dbg(dwc3_data->dev, "Configuring as Host\n");
159 break;
160
161 default:
162 dev_err(dwc3_data->dev, "Unsupported mode of operation %d\n",
163 dwc3_data->dr_mode);
164 return -EINVAL;
165 }
166
167 return regmap_write(dwc3_data->regmap, dwc3_data->syscfg_reg_off, val);
168}
169
170/**
171 * st_dwc3_init: init the controller via glue logic
172 * @dwc3_data: driver private structure
173 */
174static void st_dwc3_init(struct st_dwc3 *dwc3_data)
175{
176 u32 reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL);
177
178 reg |= AUX_CLK_EN | EXT_CFG_RESET_N | XHCI_REVISION;
179 reg &= ~SW_PIPEW_RESET_N;
180 st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg);
181
182 /* configure mux for vbus, powerpresent and bvalid signals */
183 reg = st_dwc3_readl(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1);
184
185 reg |= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG) |
186 SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG) |
187 SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG);
188
189 st_dwc3_writel(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1, reg);
190
191 reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL);
192 reg |= SW_PIPEW_RESET_N;
193 st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg);
194}
195
196static int st_dwc3_probe(struct platform_device *pdev)
197{
198 struct st_dwc3 *dwc3_data;
199 struct resource *res;
200 struct device *dev = &pdev->dev;
201 struct device_node *node = dev->of_node, *child;
202 struct regmap *regmap;
203 int ret;
204
205 dwc3_data = devm_kzalloc(dev, sizeof(*dwc3_data), GFP_KERNEL);
206 if (!dwc3_data)
207 return -ENOMEM;
208
209 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg-glue");
210 dwc3_data->glue_base = devm_ioremap_resource(dev, res);
211 if (IS_ERR(dwc3_data->glue_base))
212 return PTR_ERR(dwc3_data->glue_base);
213
214 regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
215 if (IS_ERR(regmap))
216 return PTR_ERR(regmap);
217
218 dma_set_coherent_mask(dev, dev->coherent_dma_mask);
219 dwc3_data->dev = dev;
220 dwc3_data->regmap = regmap;
221
222 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "syscfg-reg");
223 if (!res) {
224 ret = -ENXIO;
225 goto undo_platform_dev_alloc;
226 }
227
228 dwc3_data->syscfg_reg_off = res->start;
229
230 dev_vdbg(&pdev->dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n",
231 dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
232
233 dwc3_data->rstc_pwrdn = devm_reset_control_get(dev, "powerdown");
234 if (IS_ERR(dwc3_data->rstc_pwrdn)) {
235 dev_err(&pdev->dev, "could not get power controller\n");
236 ret = PTR_ERR(dwc3_data->rstc_pwrdn);
237 goto undo_platform_dev_alloc;
238 }
239
240 /* Manage PowerDown */
241 reset_control_deassert(dwc3_data->rstc_pwrdn);
242
243 dwc3_data->rstc_rst = devm_reset_control_get(dev, "softreset");
244 if (IS_ERR(dwc3_data->rstc_rst)) {
245 dev_err(&pdev->dev, "could not get reset controller\n");
246 ret = PTR_ERR(dwc3_data->rstc_pwrdn);
247 goto undo_powerdown;
248 }
249
250 /* Manage SoftReset */
251 reset_control_deassert(dwc3_data->rstc_rst);
252
253 child = of_get_child_by_name(node, "dwc3");
254 if (!child) {
255 dev_err(&pdev->dev, "failed to find dwc3 core node\n");
256 ret = -ENODEV;
257 goto undo_softreset;
258 }
259
260 dwc3_data->dr_mode = of_usb_get_dr_mode(child);
261
262 /* Allocate and initialize the core */
263 ret = of_platform_populate(node, NULL, NULL, dev);
264 if (ret) {
265 dev_err(dev, "failed to add dwc3 core\n");
266 goto undo_softreset;
267 }
268
269 /*
270 * Configure the USB port as device or host according to the static
271 * configuration passed from DT.
272 * DRD is the only mode currently supported so this will be enhanced
273 * as soon as OTG is available.
274 */
275 ret = st_dwc3_drd_init(dwc3_data);
276 if (ret) {
277 dev_err(dev, "drd initialisation failed\n");
278 goto undo_softreset;
279 }
280
281 /* ST glue logic init */
282 st_dwc3_init(dwc3_data);
283
284 platform_set_drvdata(pdev, dwc3_data);
285 return 0;
286
287undo_softreset:
288 reset_control_assert(dwc3_data->rstc_rst);
289undo_powerdown:
290 reset_control_assert(dwc3_data->rstc_pwrdn);
291undo_platform_dev_alloc:
292 platform_device_put(pdev);
293 return ret;
294}
295
296static int st_dwc3_remove(struct platform_device *pdev)
297{
298 struct st_dwc3 *dwc3_data = platform_get_drvdata(pdev);
299
300 of_platform_depopulate(&pdev->dev);
301
302 reset_control_assert(dwc3_data->rstc_pwrdn);
303 reset_control_assert(dwc3_data->rstc_rst);
304
305 return 0;
306}
307
308#ifdef CONFIG_PM_SLEEP
309static int st_dwc3_suspend(struct device *dev)
310{
311 struct st_dwc3 *dwc3_data = dev_get_drvdata(dev);
312
313 reset_control_assert(dwc3_data->rstc_pwrdn);
314 reset_control_assert(dwc3_data->rstc_rst);
315
316 pinctrl_pm_select_sleep_state(dev);
317
318 return 0;
319}
320
321static int st_dwc3_resume(struct device *dev)
322{
323 struct st_dwc3 *dwc3_data = dev_get_drvdata(dev);
324 int ret;
325
326 pinctrl_pm_select_default_state(dev);
327
328 reset_control_deassert(dwc3_data->rstc_pwrdn);
329 reset_control_deassert(dwc3_data->rstc_rst);
330
331 ret = st_dwc3_drd_init(dwc3_data);
332 if (ret) {
333 dev_err(dev, "drd initialisation failed\n");
334 return ret;
335 }
336
337 /* ST glue logic init */
338 st_dwc3_init(dwc3_data);
339
340 return 0;
341}
342#endif /* CONFIG_PM_SLEEP */
343
344static SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume);
345
346static const struct of_device_id st_dwc3_match[] = {
347 { .compatible = "st,stih407-dwc3" },
348 { /* sentinel */ },
349};
350
351MODULE_DEVICE_TABLE(of, st_dwc3_match);
352
353static struct platform_driver st_dwc3_driver = {
354 .probe = st_dwc3_probe,
355 .remove = st_dwc3_remove,
356 .driver = {
357 .name = "usb-st-dwc3",
358 .of_match_table = st_dwc3_match,
359 .pm = &st_dwc3_dev_pm_ops,
360 },
361};
362
363module_platform_driver(st_dwc3_driver);
364
365MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
366MODULE_DESCRIPTION("DesignWare USB3 STi Glue Layer");
367MODULE_LICENSE("GPL v2");