summaryrefslogtreecommitdiffstats
path: root/drivers/usb/otg/twl4030.c
blob: 3668870b9e9747bc3a2d4afbcf822c33f8b25a6e (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
/*
 * Copyright (c) 2009 Wind River Systems, Inc.
 * Tom Rix <Tom.Rix@windriver.com>
 *
 * This is file is based on
 * repository git.gitorious.org/u-boot-omap3/mainline.git,
 * branch omap3-dev-usb, file drivers/usb/gadget/twl4030_usb.c
 *
 * This is the unique part of its copyright :
 *
 * ------------------------------------------------------------------------
 *
 *  * (C) Copyright 2009 Atin Malaviya (atin.malaviya@gmail.com)
 *
 * Based on: twl4030_usb.c in linux 2.6 (drivers/i2c/chips/twl4030_usb.c)
 * Copyright (C) 2004-2007 Texas Instruments
 * Copyright (C) 2008 Nokia Corporation
 * Contact: Felipe Balbi <felipe.balbi@nokia.com>
 *
 * Author: Atin Malaviya (atin.malaviya@gmail.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.
 *
 */

#include <mfd/twl4030.h>
#include <usb/twl4030.h>
#include <clock.h>

static int twl4030_usb_write(u8 address, u8 data)
{
	int ret;
	struct twl4030 *twl4030 = twl4030_get();

	ret = twl4030_reg_write(twl4030, address, data);
	if (ret != 0)
		printf("TWL4030:USB:Write[0x%x] Error %d\n", address, ret);

	return ret;
}

static int twl4030_usb_read(u8 address)
{
	u8 data;
	int ret;
	struct twl4030 *twl4030 = twl4030_get();

	ret = twl4030_reg_read(twl4030, address, &data);
	if (ret == 0)
		ret = data;
	else
		printf("TWL4030:USB:Read[0x%x] Error %d\n", address, ret);

	return ret;
}

static void twl4030_power_init(void)
{
	unsigned char byte;
	struct twl4030 *twl4030 = twl4030_get();

	/* set VAUX3 to 2.8V */
	byte = TWL4030_PM_RECEIVER_DEV_GRP_P1;
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VAUX3_DEV_GRP, byte);
	byte = TWL4030_PM_RECEIVER_VAUX3_VSEL_28;
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VAUX3_DEDICATED, byte);

	/* set VPLL2 to 1.8V */
	byte = TWL4030_PM_RECEIVER_DEV_GRP_ALL;
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VPLL2_DEV_GRP, byte);
	byte = TWL4030_PM_RECEIVER_VPLL2_VSEL_18;
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VPLL2_DEDICATED, byte);

	/* set VDAC to 1.8V */
	byte = TWL4030_PM_RECEIVER_DEV_GRP_P1;
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VDAC_DEV_GRP, byte);
	byte = TWL4030_PM_RECEIVER_VDAC_VSEL_18;
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VDAC_DEDICATED, byte);
}

static void twl4030_usb_ldo_init(void)
{
	struct twl4030 *twl4030 = twl4030_get();
	/* Enable writing to power configuration registers */
	twl4030_reg_write(twl4030, TWL4030_PM_MASTER_PROTECT_KEY, 0xC0);
	twl4030_reg_write(twl4030, TWL4030_PM_MASTER_PROTECT_KEY, 0x0C);

	/* put VUSB3V1 LDO in active state */
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VUSB_DEDICATED2, 0x00);

	/* input to VUSB3V1 LDO is from VBAT, not VBUS */
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VUSB_DEDICATED1, 0x14);

	/* turn on 3.1V regulator */
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VUSB3V1_DEV_GRP, 0x20);
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VUSB3V1_TYPE, 0x00);

	/* turn on 1.5V regulator */
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VUSB1V5_DEV_GRP, 0x20);
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VUSB1V5_TYPE, 0x00);

	/* turn on 1.8V regulator */
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VUSB1V8_DEV_GRP, 0x20);
	twl4030_reg_write(twl4030, TWL4030_PM_RECEIVER_VUSB1V8_TYPE, 0x00);

	/* disable access to power configuration registers */
	twl4030_reg_write(twl4030, TWL4030_PM_MASTER_PROTECT_KEY, 0x0);

	/* FIXME *need to enable LED to get USB power? */
	twl4030_reg_write(twl4030, TWL4030_BASEADD_LED, 0x33);
}

static void twl4030_phy_power(void)
{
	u8 pwr, clk;
	/* Power the PHY */
	pwr = twl4030_usb_read(TWL4030_USB_PHY_PWR_CTRL);
	pwr &= ~PHYPWD;
	twl4030_usb_write(TWL4030_USB_PHY_PWR_CTRL, pwr);
	/* Enable clocks */
	clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL);
	clk |= CLOCKGATING_EN | CLK32K_EN;
	twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk);
}

/*
 * Initiaze the ULPI interface
 * ULPI : Universal Transceiver Macrocell Low Pin Interface
 * An interface between the USB link controller like musb and the
 * the PHY or transceiver that drives the actual bus.
 */
int twl4030_usb_ulpi_init(void)
{
	u8 clk, sts, pwr;
	uint64_t start;
	long timeout = 1000 * 1000; /* 1 sec */;

	/* power up the twl4030 */
	twl4030_power_init();

	/* twl4030 ldo init */
	twl4030_usb_ldo_init();

	/* Enable the twl4030 phy */
	twl4030_phy_power();

	/* Enable DPLL to access PHY registers over I2C */
	clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL);
	clk |= REQ_PHY_DPLL_CLK;
	twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk);

	/* Check if the PHY DPLL is locked */
	sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS);
	start = get_time_ns();
	while (!(sts & PHY_DPLL_CLK)) {
		udelay(10);
		sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS);
		printf("Error:TWL4030:USB Timeout setting PHY DPLL clock\n");
		if (is_timeout(start, timeout * USECOND)) {
			return -ETIMEDOUT;
		}
	}

	/*
	 * There are two circuit blocks attached to the PHY,
	 * Carkit and USB OTG.  Disable Carkit and enable USB OTG
	 */
	twl4030_usb_write(TWL4030_USB_IFC_CTRL_CLR, CARKITMODE);
	pwr = twl4030_usb_read(TWL4030_USB_POWER_CTRL);
	pwr |= OTG_ENAB;
	twl4030_usb_write(TWL4030_USB_POWER_CTRL_SET, pwr);

	/* Clear the opmode bits to ensure normal encode */
	twl4030_usb_write(TWL4030_USB_FUNC_CTRL_CLR, OPMODE_MASK);

	/* Clear the xcvrselect bits to enable the high speed transeiver */
	twl4030_usb_write(TWL4030_USB_FUNC_CTRL_CLR, XCVRSELECT_MASK);

	/* Let ULPI control the DPLL clock */
	clk = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL);
	clk &= ~REQ_PHY_DPLL_CLK;
	twl4030_usb_write(TWL4030_USB_PHY_CLK_CTRL, clk);

	return 0;
}