• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // SPDX-License-Identifier: GPL-2.0
2  /**
3   * dwc3-omap.c - OMAP Specific Glue layer
4   *
5   * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
6   *
7   * Authors: Felipe Balbi <balbi@ti.com>,
8   *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
9   *
10   * Taken from Linux Kernel v3.19-rc1 (drivers/usb/dwc3/dwc3-omap.c) and ported
11   * to uboot.
12   *
13   * commit 7ee2566ff5 : usb: dwc3: dwc3-omap: get rid of ->prepare()/->complete()
14   */
15  
16  #include <common.h>
17  #include <malloc.h>
18  #include <asm/io.h>
19  #include <dm.h>
20  #include <dwc3-omap-uboot.h>
21  #include <linux/usb/dwc3-omap.h>
22  #include <linux/ioport.h>
23  
24  #include <linux/usb/otg.h>
25  #include <linux/compat.h>
26  
27  #include "linux-compat.h"
28  
29  /*
30   * All these registers belong to OMAP's Wrapper around the
31   * DesignWare USB3 Core.
32   */
33  
34  #define USBOTGSS_REVISION			0x0000
35  #define USBOTGSS_SYSCONFIG			0x0010
36  #define USBOTGSS_IRQ_EOI			0x0020
37  #define USBOTGSS_EOI_OFFSET			0x0008
38  #define USBOTGSS_IRQSTATUS_RAW_0		0x0024
39  #define USBOTGSS_IRQSTATUS_0			0x0028
40  #define USBOTGSS_IRQENABLE_SET_0		0x002c
41  #define USBOTGSS_IRQENABLE_CLR_0		0x0030
42  #define USBOTGSS_IRQ0_OFFSET			0x0004
43  #define USBOTGSS_IRQSTATUS_RAW_1		0x0030
44  #define USBOTGSS_IRQSTATUS_1			0x0034
45  #define USBOTGSS_IRQENABLE_SET_1		0x0038
46  #define USBOTGSS_IRQENABLE_CLR_1		0x003c
47  #define USBOTGSS_IRQSTATUS_RAW_2		0x0040
48  #define USBOTGSS_IRQSTATUS_2			0x0044
49  #define USBOTGSS_IRQENABLE_SET_2		0x0048
50  #define USBOTGSS_IRQENABLE_CLR_2		0x004c
51  #define USBOTGSS_IRQSTATUS_RAW_3		0x0050
52  #define USBOTGSS_IRQSTATUS_3			0x0054
53  #define USBOTGSS_IRQENABLE_SET_3		0x0058
54  #define USBOTGSS_IRQENABLE_CLR_3		0x005c
55  #define USBOTGSS_IRQSTATUS_EOI_MISC		0x0030
56  #define USBOTGSS_IRQSTATUS_RAW_MISC		0x0034
57  #define USBOTGSS_IRQSTATUS_MISC			0x0038
58  #define USBOTGSS_IRQENABLE_SET_MISC		0x003c
59  #define USBOTGSS_IRQENABLE_CLR_MISC		0x0040
60  #define USBOTGSS_IRQMISC_OFFSET			0x03fc
61  #define USBOTGSS_UTMI_OTG_CTRL			0x0080
62  #define USBOTGSS_UTMI_OTG_STATUS		0x0084
63  #define USBOTGSS_UTMI_OTG_OFFSET		0x0480
64  #define USBOTGSS_TXFIFO_DEPTH			0x0508
65  #define USBOTGSS_RXFIFO_DEPTH			0x050c
66  #define USBOTGSS_MMRAM_OFFSET			0x0100
67  #define USBOTGSS_FLADJ				0x0104
68  #define USBOTGSS_DEBUG_CFG			0x0108
69  #define USBOTGSS_DEBUG_DATA			0x010c
70  #define USBOTGSS_DEV_EBC_EN			0x0110
71  #define USBOTGSS_DEBUG_OFFSET			0x0600
72  
73  /* SYSCONFIG REGISTER */
74  #define USBOTGSS_SYSCONFIG_DMADISABLE		(1 << 16)
75  
76  /* IRQ_EOI REGISTER */
77  #define USBOTGSS_IRQ_EOI_LINE_NUMBER		(1 << 0)
78  
79  /* IRQS0 BITS */
80  #define USBOTGSS_IRQO_COREIRQ_ST		(1 << 0)
81  
82  /* IRQMISC BITS */
83  #define USBOTGSS_IRQMISC_DMADISABLECLR		(1 << 17)
84  #define USBOTGSS_IRQMISC_OEVT			(1 << 16)
85  #define USBOTGSS_IRQMISC_DRVVBUS_RISE		(1 << 13)
86  #define USBOTGSS_IRQMISC_CHRGVBUS_RISE		(1 << 12)
87  #define USBOTGSS_IRQMISC_DISCHRGVBUS_RISE	(1 << 11)
88  #define USBOTGSS_IRQMISC_IDPULLUP_RISE		(1 << 8)
89  #define USBOTGSS_IRQMISC_DRVVBUS_FALL		(1 << 5)
90  #define USBOTGSS_IRQMISC_CHRGVBUS_FALL		(1 << 4)
91  #define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL		(1 << 3)
92  #define USBOTGSS_IRQMISC_IDPULLUP_FALL		(1 << 0)
93  
94  #define USBOTGSS_INTERRUPTS (USBOTGSS_IRQMISC_OEVT | \
95  			     USBOTGSS_IRQMISC_DRVVBUS_RISE | \
96  			     USBOTGSS_IRQMISC_CHRGVBUS_RISE | \
97  			     USBOTGSS_IRQMISC_DISCHRGVBUS_RISE | \
98  			     USBOTGSS_IRQMISC_IDPULLUP_RISE | \
99  			     USBOTGSS_IRQMISC_DRVVBUS_FALL | \
100  			     USBOTGSS_IRQMISC_CHRGVBUS_FALL | \
101  			     USBOTGSS_IRQMISC_DISCHRGVBUS_FALL | \
102  			     USBOTGSS_IRQMISC_IDPULLUP_FALL)
103  
104  /* UTMI_OTG_CTRL REGISTER */
105  #define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS		(1 << 5)
106  #define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS		(1 << 4)
107  #define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS	(1 << 3)
108  #define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP		(1 << 0)
109  
110  /* UTMI_OTG_STATUS REGISTER */
111  #define USBOTGSS_UTMI_OTG_STATUS_SW_MODE	(1 << 31)
112  #define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT	(1 << 9)
113  #define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8)
114  #define USBOTGSS_UTMI_OTG_STATUS_IDDIG		(1 << 4)
115  #define USBOTGSS_UTMI_OTG_STATUS_SESSEND	(1 << 3)
116  #define USBOTGSS_UTMI_OTG_STATUS_SESSVALID	(1 << 2)
117  #define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID	(1 << 1)
118  
119  struct dwc3_omap {
120  	struct device		*dev;
121  
122  	void __iomem		*base;
123  
124  	u32			utmi_otg_status;
125  	u32			utmi_otg_offset;
126  	u32			irqmisc_offset;
127  	u32			irq_eoi_offset;
128  	u32			debug_offset;
129  	u32			irq0_offset;
130  
131  	u32			dma_status:1;
132  	struct list_head	list;
133  	u32			index;
134  };
135  
136  static LIST_HEAD(dwc3_omap_list);
137  
dwc3_omap_readl(void __iomem * base,u32 offset)138  static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
139  {
140  	return readl(base + offset);
141  }
142  
dwc3_omap_writel(void __iomem * base,u32 offset,u32 value)143  static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
144  {
145  	writel(value, base + offset);
146  }
147  
dwc3_omap_read_utmi_status(struct dwc3_omap * omap)148  static u32 dwc3_omap_read_utmi_status(struct dwc3_omap *omap)
149  {
150  	return dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS +
151  							omap->utmi_otg_offset);
152  }
153  
dwc3_omap_write_utmi_status(struct dwc3_omap * omap,u32 value)154  static void dwc3_omap_write_utmi_status(struct dwc3_omap *omap, u32 value)
155  {
156  	dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS +
157  					omap->utmi_otg_offset, value);
158  
159  }
160  
dwc3_omap_read_irq0_status(struct dwc3_omap * omap)161  static u32 dwc3_omap_read_irq0_status(struct dwc3_omap *omap)
162  {
163  	return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0 -
164  						omap->irq0_offset);
165  }
166  
dwc3_omap_write_irq0_status(struct dwc3_omap * omap,u32 value)167  static void dwc3_omap_write_irq0_status(struct dwc3_omap *omap, u32 value)
168  {
169  	dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0 -
170  						omap->irq0_offset, value);
171  
172  }
173  
dwc3_omap_read_irqmisc_status(struct dwc3_omap * omap)174  static u32 dwc3_omap_read_irqmisc_status(struct dwc3_omap *omap)
175  {
176  	return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_MISC +
177  						omap->irqmisc_offset);
178  }
179  
dwc3_omap_write_irqmisc_status(struct dwc3_omap * omap,u32 value)180  static void dwc3_omap_write_irqmisc_status(struct dwc3_omap *omap, u32 value)
181  {
182  	dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_MISC +
183  					omap->irqmisc_offset, value);
184  
185  }
186  
dwc3_omap_write_irqmisc_set(struct dwc3_omap * omap,u32 value)187  static void dwc3_omap_write_irqmisc_set(struct dwc3_omap *omap, u32 value)
188  {
189  	dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_MISC +
190  						omap->irqmisc_offset, value);
191  
192  }
193  
dwc3_omap_write_irq0_set(struct dwc3_omap * omap,u32 value)194  static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value)
195  {
196  	dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0 -
197  						omap->irq0_offset, value);
198  }
199  
dwc3_omap_write_irqmisc_clr(struct dwc3_omap * omap,u32 value)200  static void dwc3_omap_write_irqmisc_clr(struct dwc3_omap *omap, u32 value)
201  {
202  	dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_MISC +
203  						omap->irqmisc_offset, value);
204  }
205  
dwc3_omap_write_irq0_clr(struct dwc3_omap * omap,u32 value)206  static void dwc3_omap_write_irq0_clr(struct dwc3_omap *omap, u32 value)
207  {
208  	dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_0 -
209  						omap->irq0_offset, value);
210  }
211  
dwc3_omap_set_mailbox(struct dwc3_omap * omap,enum omap_dwc3_vbus_id_status status)212  static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
213  	enum omap_dwc3_vbus_id_status status)
214  {
215  	u32	val;
216  
217  	switch (status) {
218  	case OMAP_DWC3_ID_GROUND:
219  		dev_dbg(omap->dev, "ID GND\n");
220  
221  		val = dwc3_omap_read_utmi_status(omap);
222  		val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
223  				| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
224  				| USBOTGSS_UTMI_OTG_STATUS_SESSEND);
225  		val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID
226  				| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
227  		dwc3_omap_write_utmi_status(omap, val);
228  		break;
229  
230  	case OMAP_DWC3_VBUS_VALID:
231  		dev_dbg(omap->dev, "VBUS Connect\n");
232  
233  		val = dwc3_omap_read_utmi_status(omap);
234  		val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND;
235  		val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG
236  				| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
237  				| USBOTGSS_UTMI_OTG_STATUS_SESSVALID
238  				| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
239  		dwc3_omap_write_utmi_status(omap, val);
240  		break;
241  
242  	case OMAP_DWC3_ID_FLOAT:
243  	case OMAP_DWC3_VBUS_OFF:
244  		dev_dbg(omap->dev, "VBUS Disconnect\n");
245  
246  		val = dwc3_omap_read_utmi_status(omap);
247  		val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID
248  				| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
249  				| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT);
250  		val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND
251  				| USBOTGSS_UTMI_OTG_STATUS_IDDIG;
252  		dwc3_omap_write_utmi_status(omap, val);
253  		break;
254  
255  	default:
256  		dev_dbg(omap->dev, "invalid state\n");
257  	}
258  }
259  
dwc3_omap_interrupt(int irq,void * _omap)260  static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
261  {
262  	struct dwc3_omap	*omap = _omap;
263  	u32			reg;
264  
265  	reg = dwc3_omap_read_irqmisc_status(omap);
266  
267  	if (reg & USBOTGSS_IRQMISC_DMADISABLECLR) {
268  		dev_dbg(omap->dev, "DMA Disable was Cleared\n");
269  		omap->dma_status = false;
270  	}
271  
272  	if (reg & USBOTGSS_IRQMISC_OEVT)
273  		dev_dbg(omap->dev, "OTG Event\n");
274  
275  	if (reg & USBOTGSS_IRQMISC_DRVVBUS_RISE)
276  		dev_dbg(omap->dev, "DRVVBUS Rise\n");
277  
278  	if (reg & USBOTGSS_IRQMISC_CHRGVBUS_RISE)
279  		dev_dbg(omap->dev, "CHRGVBUS Rise\n");
280  
281  	if (reg & USBOTGSS_IRQMISC_DISCHRGVBUS_RISE)
282  		dev_dbg(omap->dev, "DISCHRGVBUS Rise\n");
283  
284  	if (reg & USBOTGSS_IRQMISC_IDPULLUP_RISE)
285  		dev_dbg(omap->dev, "IDPULLUP Rise\n");
286  
287  	if (reg & USBOTGSS_IRQMISC_DRVVBUS_FALL)
288  		dev_dbg(omap->dev, "DRVVBUS Fall\n");
289  
290  	if (reg & USBOTGSS_IRQMISC_CHRGVBUS_FALL)
291  		dev_dbg(omap->dev, "CHRGVBUS Fall\n");
292  
293  	if (reg & USBOTGSS_IRQMISC_DISCHRGVBUS_FALL)
294  		dev_dbg(omap->dev, "DISCHRGVBUS Fall\n");
295  
296  	if (reg & USBOTGSS_IRQMISC_IDPULLUP_FALL)
297  		dev_dbg(omap->dev, "IDPULLUP Fall\n");
298  
299  	dwc3_omap_write_irqmisc_status(omap, reg);
300  
301  	reg = dwc3_omap_read_irq0_status(omap);
302  
303  	dwc3_omap_write_irq0_status(omap, reg);
304  
305  	return IRQ_HANDLED;
306  }
307  
dwc3_omap_enable_irqs(struct dwc3_omap * omap)308  static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
309  {
310  	/* enable all IRQs */
311  	dwc3_omap_write_irq0_set(omap, USBOTGSS_IRQO_COREIRQ_ST);
312  
313  	dwc3_omap_write_irqmisc_set(omap, USBOTGSS_INTERRUPTS);
314  }
315  
dwc3_omap_disable_irqs(struct dwc3_omap * omap)316  static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
317  {
318  	/* disable all IRQs */
319  	dwc3_omap_write_irq0_clr(omap, USBOTGSS_IRQO_COREIRQ_ST);
320  
321  	dwc3_omap_write_irqmisc_clr(omap, USBOTGSS_INTERRUPTS);
322  }
323  
dwc3_omap_map_offset(struct dwc3_omap * omap)324  static void dwc3_omap_map_offset(struct dwc3_omap *omap)
325  {
326  	/*
327  	 * Differentiate between OMAP5 and AM437x.
328  	 *
329  	 * For OMAP5(ES2.0) and AM437x wrapper revision is same, even
330  	 * though there are changes in wrapper register offsets.
331  	 *
332  	 * Using dt compatible to differentiate AM437x.
333  	 */
334  #ifdef CONFIG_AM43XX
335  	omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
336  	omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
337  	omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
338  	omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
339  	omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
340  #endif
341  }
342  
dwc3_omap_set_utmi_mode(struct dwc3_omap * omap,int utmi_mode)343  static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap, int utmi_mode)
344  {
345  	u32			reg;
346  
347  	reg = dwc3_omap_read_utmi_status(omap);
348  
349  	switch (utmi_mode) {
350  	case DWC3_OMAP_UTMI_MODE_SW:
351  		reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
352  		break;
353  	case DWC3_OMAP_UTMI_MODE_HW:
354  		reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
355  		break;
356  	default:
357  		dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode);
358  	}
359  
360  	dwc3_omap_write_utmi_status(omap, reg);
361  }
362  
363  /**
364   * dwc3_omap_uboot_init - dwc3 omap uboot initialization code
365   * @dev: struct dwc3_omap_device containing initialization data
366   *
367   * Entry point for dwc3 omap driver (equivalent to dwc3_omap_probe in linux
368   * kernel driver). Pointer to dwc3_omap_device should be passed containing
369   * base address and other initialization data. Returns '0' on success and
370   * a negative value on failure.
371   *
372   * Generally called from board_usb_init() implemented in board file.
373   */
dwc3_omap_uboot_init(struct dwc3_omap_device * omap_dev)374  int dwc3_omap_uboot_init(struct dwc3_omap_device *omap_dev)
375  {
376  	u32			reg;
377  	struct device		*dev = NULL;
378  	struct dwc3_omap	*omap;
379  
380  	omap = devm_kzalloc((struct udevice *)dev, sizeof(*omap), GFP_KERNEL);
381  	if (!omap)
382  		return -ENOMEM;
383  
384  	omap->base	= omap_dev->base;
385  	omap->index	= omap_dev->index;
386  
387  	dwc3_omap_map_offset(omap);
388  	dwc3_omap_set_utmi_mode(omap, omap_dev->utmi_mode);
389  
390  	/* check the DMA Status */
391  	reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
392  	omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
393  
394  	dwc3_omap_set_mailbox(omap, omap_dev->vbus_id_status);
395  
396  	dwc3_omap_enable_irqs(omap);
397  	list_add_tail(&omap->list, &dwc3_omap_list);
398  
399  	return 0;
400  }
401  
402  /**
403   * dwc3_omap_uboot_exit - dwc3 omap uboot cleanup code
404   * @index: index of this controller
405   *
406   * Performs cleanup of memory allocated in dwc3_omap_uboot_init
407   * (equivalent to dwc3_omap_remove in linux). index of _this_ controller
408   * should be passed and should match with the index passed in
409   * dwc3_omap_device during init.
410   *
411   * Generally called from board file.
412   */
dwc3_omap_uboot_exit(int index)413  void dwc3_omap_uboot_exit(int index)
414  {
415  	struct dwc3_omap *omap = NULL;
416  
417  	list_for_each_entry(omap, &dwc3_omap_list, list) {
418  		if (omap->index != index)
419  			continue;
420  
421  		dwc3_omap_disable_irqs(omap);
422  		list_del(&omap->list);
423  		kfree(omap);
424  		break;
425  	}
426  }
427  
428  /**
429   * dwc3_omap_uboot_interrupt_status - check the status of interrupt
430   * @index: index of this controller
431   *
432   * Checks the status of interrupts and returns true if an interrupt
433   * is detected or false otherwise.
434   *
435   * Generally called from board file.
436   */
dwc3_omap_uboot_interrupt_status(int index)437  int dwc3_omap_uboot_interrupt_status(int index)
438  {
439  	struct dwc3_omap *omap = NULL;
440  
441  	list_for_each_entry(omap, &dwc3_omap_list, list)
442  		if (omap->index == index)
443  			return dwc3_omap_interrupt(-1, omap);
444  
445  	return 0;
446  }
447  
448  MODULE_ALIAS("platform:omap-dwc3");
449  MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
450  MODULE_LICENSE("GPL v2");
451  MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer");
452