summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-fsl.c
blob: 3b6eb219de1a714d31fc843718472efc6ab615f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
23
{ "translations": {
    "Saved" : "Salveguardate",
    "Download" : "Discargar",
    "Close" : "Clauder",
    "No articles available" : "Nulle articulos disponibile",
    "No unread articles available" : "Nulle articulos non legite disponibile",
    "Open website" : "Aperir sito web",
    "Star article" : "Favoritar articulo",
    "Unstar article" : "Disfavoritar articulo",
    "Keep article unread" : "Mantener articulo como non legite",
    "by" : "per",
    "from" : "ex",
    "Play audio" : "Reproducer audio",
    "Download audio" : "Discargar audio",
    "Download video" : "Discargar video",
    "Keyboard shortcut" : "Combination de claves",
    "Description" : "Description",
    "right" : "dextere",
    "Jump to next article" : "Saltar al articulo sequente",
    "left" : "sinistre",
    "Jump to previous article" : "Saltar al articulo previe",
    "Toggle star article" : "Alternar stato de favorito del articulo",
    "Star article and jump to next one" : "Favoritar articulo e saltar al sequente",
    "Open article in new tab" : "Aperir articulo in nove scheda",
    "Refresh" : "Refrescar",
    "Subscribe" : "Subscribe",
    "Web address" : "Adresse Web",
    "Feed exists already!" : "Syndication ja existe!",
    "Folder" : "Dossier",
    "No folder" : "Nulle dossier",
    "New folder" : "Nove dossier",
    "Folder name" : "Nomine de dossier",
    "Go back" : "Retornar",
    "Folder exists already!" : "Dossier ja existe!",
    "Credentials" : "Datos de authentication",
    "Username" : "Nomine de usator",
    "Password" : "Contrasigno",
    "New Folder" : "Nove dossier",
    "Create" : "Crear",
    "Explore" : "Explorar",
    "Update failed more than 50 times" : "Actualisation falleva plus que 50 vices",
    "Deleted feed" : "Syndication delite",
    "Undo delete feed" : "Disfacer deletion del syndication",
    "Rename" : "Renominar",
    "Menu" : "Menu",
    "Delete" : "Deler",
    "Dismiss" : "Dimitter",
    "Collapse" : "Collaber",
    "Deleted folder" : "Dossier delite",
    "Undo de
/*
 * Copyright 2005-2009 MontaVista Software, Inc.
 * Copyright 2008,2012,2015      Freescale Semiconductor, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Ported to 834x by Randy Vinson <rvinson@mvista.com> using code provided
 * by Hunter Wu.
 * Power Management support by Dave Liu <daveliu@freescale.com>,
 * Jerry Huang <Chang-Ming.Huang@freescale.com> and
 * Anton Vorontsov <avorontsov@ru.mvista.com>.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/err.h>
#include <linux/usb.h>
#include <linux/usb/ehci_def.h>
#include <linux/usb/hcd.h>
#include <linux/usb/otg.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>

#include "ehci.h"
#include "ehci-fsl.h"

#define DRIVER_DESC "Freescale EHCI Host controller driver"
#define DRV_NAME "ehci-fsl"

static struct hc_driver __read_mostly fsl_ehci_hc_driver;

/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */

/*
 * fsl_ehci_drv_probe - initialize FSL-based HCDs
 * @pdev: USB Host Controller being probed
 * Context: !in_interrupt()
 *
 * Allocates basic resources for this USB host controller.
 *
 */
static int fsl_ehci_drv_probe(struct platform_device *pdev)
{
	struct fsl_usb2_platform_data *pdata;
	struct usb_hcd *hcd;
	struct resource *res;
	int irq;
	int retval;

	pr_debug("initializing FSL-SOC USB Controller\n");

	/* Need platform data for setup */
	pdata = dev_get_platdata(&pdev->dev);
	if (!pdata) {
		dev_err(&pdev->dev,
			"No platform data for %s.\n", dev_name(&pdev->dev));
		return -ENODEV;
	}

	/*
	 * This is a host mode driver, verify that we're supposed to be
	 * in host mode.
	 */
	if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
	      (pdata->operating_mode == FSL_USB2_MPH_HOST) ||
	      (pdata->operating_mode == FSL_USB2_DR_OTG))) {
		dev_err(&pdev->dev,
			"Non Host Mode configured for %s. Wrong driver linked.\n",
			dev_name(&pdev->dev));
		return -ENODEV;
	}

	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (!res) {
		dev_err(&pdev->dev,
			"Found HC with no IRQ. Check %s setup!\n",
			dev_name(&pdev->dev));
		return -ENODEV;
	}
	irq = res->start;

	hcd = usb_create_hcd(&fsl_ehci_hc_driver, &pdev->dev,
				dev_name(&pdev->dev));
	if (!hcd) {
		retval = -ENOMEM;
		goto err1;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	hcd->regs = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(hcd->regs)) {
		retval = PTR_ERR(hcd->regs);
		goto err2;
	}

	hcd->rsrc_start = res->start;
	hcd->rsrc_len = resource_size(res);

	pdata->regs = hcd->regs;

	if (pdata->power_budget)
		hcd->power_budget = pdata->power_budget;

	/*
	 * do platform specific init: check the clock, grab/config pins, etc.
	 */
	if (pdata->init && pdata->init(pdev)) {
		retval = -ENODEV;
		goto err2;
	}

	/* Enable USB controller, 83xx or 8536 */
	if (pdata->have_sysif_regs && pdata->controller_ver < FSL_USB_VER_1_6)
		clrsetbits_be32(hcd->regs + FSL_SOC_USB_CTRL,
				CONTROL_REGISTER_W1C_MASK, 0x4);

	/*
	 * Enable UTMI phy and program PTS field in UTMI mode before asserting
	 * controller reset for USB Controller version 2.5
	 */
	if (pdata->has_fsl_erratum_a007792) {
		clrsetbits_be32(hcd->regs + FSL_SOC_USB_CTRL,
				CONTROL_REGISTER_W1C_MASK, CTRL_UTMI_PHY_EN);
		writel(PORT_PTS_UTMI, hcd->regs + FSL_SOC_USB_PORTSC1);
	}

	/* Don't need to set host mode here. It will be done by tdi_reset() */

	retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
	if (retval != 0)
		goto err2;
	device_wakeup_enable(hcd->self.controller);

#ifdef CONFIG_USB_OTG
	if (pdata->operating_mode == FSL_USB2_DR_OTG) {
		struct ehci_hcd *ehci = hcd_to_ehci(hcd);

		hcd->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
		dev_dbg(&pdev->dev, "hcd=0x%p  ehci=0x%p, phy=0x%p\n",
			hcd, ehci, hcd->usb_phy);

		if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
			retval = otg_set_host(hcd->usb_phy->otg,
					      &ehci_to_hcd(ehci)->self);
			if (retval) {
				usb_put_phy(hcd->usb_phy);
				goto err2;
			}
		} else {
			dev_err(&pdev->dev, "can't find phy\n");
			retval = -ENODEV;
			goto err2;
		}
	}
#endif
	return retval;

      err2:
	usb_put_hcd(hcd);
      err1:
	dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
	if (pdata->exit)
		pdata->exit(pdev);
	return retval;
}

static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
			       enum fsl_usb2_phy_modes phy_mode,
			       unsigned int port_offset)
{
	u32 portsc;
	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
	void __iomem *non_ehci = hcd->regs;
	struct device *dev = hcd->self.controller;
	struct fsl_usb2_platform_data *pdata = dev_get_platdata(dev);

	if (pdata->controller_ver < 0) {
		dev_warn(hcd->self.controller, "Could not get controller version\n");
		return -ENODEV;
	}

	portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]);
	portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);

	switch (phy_mode) {
	case FSL_USB2_PHY_ULPI:
		if (pdata->have_sysif_regs && pdata->controller_ver) {
			/* controller version 1.6 or above */
			clrbits32(non_ehci + FSL_SOC_USB_CTRL,
				  CONTROL_REGISTER_W1C_MASK | UTMI_PHY_EN);
			clrsetbits_be32(non_ehci + FSL_SOC_USB_CTRL,
					CONTROL_REGISTER_W1C_MASK,
					ULPI_PHY_CLK_SEL | USB_CTRL_USB_EN);
		}
		portsc |= PORT_PTS_ULPI;
		break;
	case FSL_USB2_PHY_SERIAL:
		portsc |= PORT_PTS_SERIAL;
		break;
	case FSL_USB2_PHY_UTMI_WIDE: