summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-mpc512x.c
blob: e98f79f9ccdd1c18bfa3ba973bfb4c0df55c9468 (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
/*
 * (C) Copyright 2010, Damien Dusha, <d.dusha@gmail.com>
 *
 * (C) Copyright 2009, Value Team S.p.A.
 * Francesco Rendine, <francesco.rendine@valueteam.com>
 *
 * (C) Copyright 2009 Freescale Semiconductor, Inc.
 *
 * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
 *
 * Author: Tor Krill tor@excito.com
 *
 * 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., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <pci.h>
#include <usb.h>
#include <asm/io.h>
#include <usb/ehci-fsl.h>

#include "ehci.h"

static void fsl_setup_phy(volatile struct ehci_hcor *);
static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci);
static int reset_usb_controller(volatile struct usb_ehci *ehci);
static void usb_platform_dr_init(volatile struct usb_ehci *ehci);

/*
 * Initialize SOC FSL EHCI Controller
 *
 * This code is derived from EHCI FSL USB Linux driver for MPC5121
 *
 */
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
{
	volatile struct usb_ehci *ehci;

	/* Hook the memory mapped registers for EHCI-Controller */
	ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB_ADDR;
	*hccr = (struct ehci_hccr *)((uint32_t)&(ehci->caplength));
	*hcor = (struct ehci_hcor *)((uint32_t) *hccr +
				HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));

	/* configure interface for UTMI_WIDE */
	usb_platform_dr_init(ehci);

	/* Init Phy USB0 to UTMI+ */
	fsl_setup_phy(*hcor);

	/* Set to host mode */
	fsl_platform_set_host_mode(ehci);

	/*
	 * Setting the burst size seems to be required to prevent the
	 * USB from hanging when communicating with certain USB Mass
	 * storage devices. This was determined by analysing the
	 * EHCI registers under Linux vs U-Boot and burstsize was the
	 * major non-interrupt related difference between the two
	 * implementations.
	 *
	 * Some USB sticks behave better than others. In particular,
	 * the following USB stick is especially problematic:
	 * 0930:6545 Toshiba Corp
	 *
	 * The burstsize is set here to match the Linux implementation.
	 */
	out_be32(&ehci->burstsize, FSL_EHCI_TXPBURST(8) |
				   FSL_EHCI_RXPBURST(8));

	return 0;
}

/*
 * Destroy the appropriate control structures corresponding
 * the the EHCI host controller.
 */
int ehci_hcd_stop(int index)
{
	volatile struct usb_ehci *ehci;
	int exit_status = 0;

	/* Reset the USB controller */
	ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB_ADDR;
	exit_status = reset_usb_controller(ehci);

	return exit_status;
}

static int reset_usb_controller(volatile struct usb_ehci *ehci)
{
	unsigned int i;

	/* Command a reset of the USB Controller */
	out_be32(&(ehci->usbcmd), EHCI_FSL_USBCMD_RST);

	/* Wait for the reset process to finish */
	for (i = 65535 ; i > 0 ; i--) {
		/*
		 * The host will set this bit to zero once the
		 * reset process is complete
		 */
		if ((in_be32(&(ehci->usbcmd)) & EHCI_FSL_USBCMD_RST) == 0)
			return 0;
	}

	/* Hub did not reset in time */
	return -1;
}

static void fsl_setup_phy(volatile struct ehci_hcor *hcor)
{
	uint32_t portsc;

	portsc  = ehci_readl(&hcor->or_portsc[0]);
	portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);

	/* Enable the phy mode to UTMI Wide */
	portsc |= PORT_PTS_PTW;
	portsc |= PORT_PTS_UTMI;

	ehci_writel(&hcor->or_portsc[0], portsc);
}

static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci)
{
	uint32_t temp;

	temp  = in_le32(&ehci->usbmode);
	temp |= CM_HOST | ES_BE;
	out_le32(&ehci->usbmode, temp);
}

static void usb_platform_dr_init(volatile struct usb_ehci *ehci)
{
	/* Configure interface for UTMI_WIDE */
	out_be32(&ehci->isiphyctrl, PHYCTRL_PHYE | PHYCTRL_PXE);
	out_be32(&ehci->usbgenctrl, GC_PPP | GC_PFP );
}
OpenPOWER on IntegriCloud