summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ohci-lpc32xx.c
blob: 9245126ed6922392750564903d540344fe69606a (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
/*
 * Copyright (C) 2008 by NXP Semiconductors
 * @Author: Based on code by Kevin Wells
 * @Descr: USB driver - Embedded Artists LPC3250 OEM Board support functions
 *
 * Copyright (c) 2015 Tyco Fire Protection Products.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <errno.h>
#include <wait_bit.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/clk.h>
#include <usb.h>
#include <i2c.h>

/* OTG I2C controller module register structures */
struct otgi2c_regs {
	u32 otg_i2c_txrx;   /* OTG I2C Tx/Rx Data FIFO */
	u32 otg_i2c_stat;   /* OTG I2C Status Register */
	u32 otg_i2c_ctrl;   /* OTG I2C Control Register */
	u32 otg_i2c_clk_hi; /* OTG I2C Clock Divider high */
	u32 otg_i2c_clk_lo; /* OTG I2C Clock Divider low */
};

/* OTG controller module register structures */
struct otg_regs {
	u32 reserved1[64];
	u32 otg_int_sts;    /* OTG int status register */
	u32 otg_int_enab;   /* OTG int enable register */
	u32 otg_int_set;    /* OTG int set register */
	u32 otg_int_clr;    /* OTG int clear register */
	u32 otg_sts_ctrl;   /* OTG status/control register */
	u32 otg_timer;      /* OTG timer register */
	u32 reserved2[122];
	struct otgi2c_regs otg_i2c;
	u32 reserved3[824];
	u32 otg_clk_ctrl;   /* OTG clock control reg */
	u32 otg_clk_sts;    /* OTG clock status reg */
};

/* otg_sts_ctrl register definitions */
#define OTG_HOST_EN			(1 << 0) /* Enable host mode */

/* otg_clk_ctrl and otg_clk_sts register definitions */
#define OTG_CLK_AHB_EN			(1 << 4) /* Enable AHB clock */
#define OTG_CLK_OTG_EN			(1 << 3) /* Enable OTG clock */
#define OTG_CLK_I2C_EN			(1 << 2) /* Enable I2C clock */
#define OTG_CLK_HOST_EN			(1 << 0) /* Enable host clock */

/* ISP1301 USB transceiver I2C registers */
#define MC1_SPEED_REG			(1 << 0)
#define MC1_DAT_SE0			(1 << 2)
#define MC1_UART_EN			(1 << 6)

#define MC2_SPD_SUSP_CTRL		(1 << 1)
#define MC2_BI_DI			(1 << 2)
#define MC2_PSW_EN			(1 << 6)

#define OTG1_DP_PULLUP			(1 << 0)
#define OTG1_DM_PULLUP			(1 << 1)
#define OTG1_DP_PULLDOWN		(1 << 2)
#define OTG1_DM_PULLDOWN		(1 << 3)
#define OTG1_VBUS_DRV			(1 << 5)

#define ISP1301_I2C_ADDR		CONFIG_USB_ISP1301_I2C_ADDR

#define ISP1301_I2C_MODE_CONTROL_1_SET		0x04
#define ISP1301_I2C_MODE_CONTROL_1_CLR		0x05
#define ISP1301_I2C_MODE_CONTROL_2_SET		0x12
#define ISP1301_I2C_MODE_CONTROL_2_CLR		0x13
#define ISP1301_I2C_OTG_CONTROL_1_SET		0x06
#define ISP1301_I2C_OTG_CONTROL_1_CLR		0x07
#define ISP1301_I2C_INTERRUPT_LATCH_CLR		0x0B
#define ISP1301_I2C_INTERRUPT_FALLING_CLR	0x0D
#define ISP1301_I2C_INTERRUPT_RISING_CLR	0x0F

static struct otg_regs *otg = (struct otg_regs *)USB_BASE;
static struct clk_pm_regs *clk_pwr = (struct clk_pm_regs *)CLK_PM_BASE;

static int isp1301_set_value(int reg, u8 value)
{
	return i2c_write(ISP1301_I2C_ADDR, reg, 1, &value, 1);
}

static void isp1301_configure(void)
{
	i2c_set_bus_num(I2C_2);

	/*
	 * LPC32XX only supports DAT_SE0 USB mode
	 * This sequence is important
	 */

	/* Disable transparent UART mode first */
	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_CLR, MC1_UART_EN);

	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_CLR, ~MC1_SPEED_REG);
	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_SET, MC1_SPEED_REG);
	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_2_CLR, ~0);
	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_2_SET,
			  MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL);

	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_CLR, ~0);
	isp1301_set_value(ISP1301_I2C_MODE_CONTROL_1_SET, MC1_DAT_SE0);
	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET,
			  OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN);
	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_CLR,
			  OTG1_DM_PULLUP | OTG1_DP_PULLUP);
	isp1301_set_value(ISP1301_I2C_INTERRUPT_LATCH_CLR, ~0);
	isp1301_set_value(ISP1301_I2C_INTERRUPT_FALLING_CLR, ~0);
	isp1301_set_value(ISP1301_I2C_INTERRUPT_RISING_CLR, ~0);

	/* Enable usb_need_clk clock after transceiver is initialized */
	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_USBDVND_EN);
}

static int usbpll_setup(void)
{
	u32 ret;

	/* make sure clocks are disabled */
	clrbits_le32(&clk_pwr->usb_ctrl,
		     CLK_USBCTRL_CLK_EN1 | CLK_USBCTRL_CLK_EN2);

	/* start PLL clock input */
	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN1);

	/* Setup PLL. */
	setbits_le32(&clk_pwr->usb_ctrl,
		     CLK_USBCTRL_FDBK_PLUS1(192 - 1));
	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_POSTDIV_2POW(0x01));
	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_PWRUP);

	ret = wait_for_bit(__func__, &clk_pwr->usb_ctrl, CLK_USBCTRL_PLL_STS,
			   true, CONFIG_SYS_HZ, false);
	if (ret)
		return ret;

	/* enable PLL output */
	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_CLK_EN2);

	return 0;
}

int usb_cpu_init(void)
{
	u32 ret;

	/*
	 * USB pins routing setup is done by "lpc32xx_usb_init()" and should
	 * be call by board "board_init()" or "misc_init_r()" functions.
	 */

	/* enable AHB slave USB clock */
	setbits_le32(&clk_pwr->usb_ctrl,
		     CLK_USBCTRL_HCLK_EN | CLK_USBCTRL_BUS_KEEPER);

	/* enable I2C clock */
	writel(OTG_CLK_I2C_EN, &otg->otg_clk_ctrl);
	ret = wait_for_bit(__func__, &otg->otg_clk_sts, OTG_CLK_I2C_EN, true,
			   CONFIG_SYS_HZ, false);
	if (ret)
		return ret;

	/* Configure ISP1301 */
	isp1301_configure();

	/* setup USB clocks and PLL */
	ret = usbpll_setup();
	if (ret)
		return ret;

	/* enable usb_host_need_clk */
	setbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_USBHSTND_EN);

	/* enable all needed USB clocks */
	const u32 mask = OTG_CLK_AHB_EN | OTG_CLK_OTG_EN |
			 OTG_CLK_I2C_EN | OTG_CLK_HOST_EN;
	writel(mask, &otg->otg_clk_ctrl);

	ret = wait_for_bit(__func__, &otg->otg_clk_sts, mask, true,
			   CONFIG_SYS_HZ, false);
	if (ret)
		return ret;

	setbits_le32(&otg->otg_sts_ctrl, OTG_HOST_EN);
	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET, OTG1_VBUS_DRV);

	return 0;
}

int usb_cpu_stop(void)
{
	/* vbus off */
	isp1301_set_value(ISP1301_I2C_OTG_CONTROL_1_SET, OTG1_VBUS_DRV);

	clrbits_le32(&otg->otg_sts_ctrl, OTG_HOST_EN);

	clrbits_le32(&clk_pwr->usb_ctrl, CLK_USBCTRL_HCLK_EN);

	return 0;
}

int usb_cpu_init_fail(void)
{
	return usb_cpu_stop();
}
OpenPOWER on IntegriCloud