xref: /openbmc/u-boot/drivers/usb/ulpi/ulpi.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2f93022c3SJana Rapava /*
3f93022c3SJana Rapava  * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com>
4f93022c3SJana Rapava  * Copyright (C) 2011 CompuLab, Ltd. <www.compulab.co.il>
5f93022c3SJana Rapava  *
6f93022c3SJana Rapava  * Authors: Jana Rapava <fermata7@gmail.com>
7f93022c3SJana Rapava  *	    Igor Grinberg <grinberg@compulab.co.il>
8f93022c3SJana Rapava  *
9f93022c3SJana Rapava  * Based on:
10f93022c3SJana Rapava  * linux/drivers/usb/otg/ulpi.c
11f93022c3SJana Rapava  * Generic ULPI USB transceiver support
12f93022c3SJana Rapava  *
13f93022c3SJana Rapava  * Original Copyright follow:
14f93022c3SJana Rapava  * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
15f93022c3SJana Rapava  *
16f93022c3SJana Rapava  * Based on sources from
17f93022c3SJana Rapava  *
18f93022c3SJana Rapava  *   Sascha Hauer <s.hauer@pengutronix.de>
19f93022c3SJana Rapava  *   Freescale Semiconductors
20f93022c3SJana Rapava  */
21f93022c3SJana Rapava 
22f93022c3SJana Rapava #include <common.h>
23f93022c3SJana Rapava #include <exports.h>
24f93022c3SJana Rapava #include <usb/ulpi.h>
25f93022c3SJana Rapava 
26f93022c3SJana Rapava #define ULPI_ID_REGS_COUNT	4
27f93022c3SJana Rapava #define ULPI_TEST_VALUE		0x55	/* 0x55 == 0b01010101 */
28f93022c3SJana Rapava 
29f93022c3SJana Rapava static struct ulpi_regs *ulpi = (struct ulpi_regs *)0;
30f93022c3SJana Rapava 
ulpi_integrity_check(struct ulpi_viewport * ulpi_vp)313e6e809fSGovindraj.R static int ulpi_integrity_check(struct ulpi_viewport *ulpi_vp)
32f93022c3SJana Rapava {
334256101fSIgor Grinberg 	u32 val, tval = ULPI_TEST_VALUE;
344256101fSIgor Grinberg 	int err, i;
35f93022c3SJana Rapava 
36f93022c3SJana Rapava 	/* Use the 'special' test value to check all bits */
37f93022c3SJana Rapava 	for (i = 0; i < 2; i++, tval <<= 1) {
383e6e809fSGovindraj.R 		err = ulpi_write(ulpi_vp, &ulpi->scratch, tval);
39f93022c3SJana Rapava 		if (err)
40f93022c3SJana Rapava 			return err;
41f93022c3SJana Rapava 
423e6e809fSGovindraj.R 		val = ulpi_read(ulpi_vp, &ulpi->scratch);
43f93022c3SJana Rapava 		if (val != tval) {
44f93022c3SJana Rapava 			printf("ULPI integrity check failed\n");
45f93022c3SJana Rapava 			return val;
46f93022c3SJana Rapava 		}
47f93022c3SJana Rapava 	}
48f93022c3SJana Rapava 
49f93022c3SJana Rapava 	return 0;
50f93022c3SJana Rapava }
51f93022c3SJana Rapava 
ulpi_init(struct ulpi_viewport * ulpi_vp)523e6e809fSGovindraj.R int ulpi_init(struct ulpi_viewport *ulpi_vp)
53f93022c3SJana Rapava {
54f93022c3SJana Rapava 	u32 val, id = 0;
55f93022c3SJana Rapava 	u8 *reg = &ulpi->product_id_high;
56f93022c3SJana Rapava 	int i;
57f93022c3SJana Rapava 
58f93022c3SJana Rapava 	/* Assemble ID from four ULPI ID registers (8 bits each). */
59f93022c3SJana Rapava 	for (i = 0; i < ULPI_ID_REGS_COUNT; i++) {
603e6e809fSGovindraj.R 		val = ulpi_read(ulpi_vp, reg - i);
61f93022c3SJana Rapava 		if (val == ULPI_ERROR)
62f93022c3SJana Rapava 			return val;
63f93022c3SJana Rapava 
64f93022c3SJana Rapava 		id = (id << 8) | val;
65f93022c3SJana Rapava 	}
66f93022c3SJana Rapava 
67f93022c3SJana Rapava 	/* Split ID into vendor and product ID. */
68f93022c3SJana Rapava 	debug("ULPI transceiver ID 0x%04x:0x%04x\n", id >> 16, id & 0xffff);
69f93022c3SJana Rapava 
703e6e809fSGovindraj.R 	return ulpi_integrity_check(ulpi_vp);
71f93022c3SJana Rapava }
72f93022c3SJana Rapava 
ulpi_select_transceiver(struct ulpi_viewport * ulpi_vp,unsigned speed)733e6e809fSGovindraj.R int ulpi_select_transceiver(struct ulpi_viewport *ulpi_vp, unsigned speed)
74f93022c3SJana Rapava {
751113a79bSIgor Grinberg 	u32 tspeed = ULPI_FC_FULL_SPEED;
76f93022c3SJana Rapava 	u32 val;
77f93022c3SJana Rapava 
78f93022c3SJana Rapava 	switch (speed) {
79f93022c3SJana Rapava 	case ULPI_FC_HIGH_SPEED:
80f93022c3SJana Rapava 	case ULPI_FC_FULL_SPEED:
81f93022c3SJana Rapava 	case ULPI_FC_LOW_SPEED:
82f93022c3SJana Rapava 	case ULPI_FC_FS4LS:
83f93022c3SJana Rapava 		tspeed = speed;
84f93022c3SJana Rapava 		break;
85f93022c3SJana Rapava 	default:
86cf9f95f2SIgor Grinberg 		printf("ULPI: %s: wrong transceiver speed specified: %u, "
87cf9f95f2SIgor Grinberg 			"falling back to full speed\n", __func__, speed);
88f93022c3SJana Rapava 	}
89f93022c3SJana Rapava 
903e6e809fSGovindraj.R 	val = ulpi_read(ulpi_vp, &ulpi->function_ctrl);
91f93022c3SJana Rapava 	if (val == ULPI_ERROR)
92f93022c3SJana Rapava 		return val;
93f93022c3SJana Rapava 
94f93022c3SJana Rapava 	/* clear the previous speed setting */
95f93022c3SJana Rapava 	val = (val & ~ULPI_FC_XCVRSEL_MASK) | tspeed;
96f93022c3SJana Rapava 
973e6e809fSGovindraj.R 	return ulpi_write(ulpi_vp, &ulpi->function_ctrl, val);
98f93022c3SJana Rapava }
99f93022c3SJana Rapava 
ulpi_set_vbus(struct ulpi_viewport * ulpi_vp,int on,int ext_power)100141288b3SLucas Stach int ulpi_set_vbus(struct ulpi_viewport *ulpi_vp, int on, int ext_power)
101f93022c3SJana Rapava {
102f93022c3SJana Rapava 	u32 flags = ULPI_OTG_DRVVBUS;
103f93022c3SJana Rapava 	u8 *reg = on ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear;
104f93022c3SJana Rapava 
105f93022c3SJana Rapava 	if (ext_power)
106f93022c3SJana Rapava 		flags |= ULPI_OTG_DRVVBUS_EXT;
107f93022c3SJana Rapava 
1083e6e809fSGovindraj.R 	return ulpi_write(ulpi_vp, reg, flags);
109f93022c3SJana Rapava }
110f93022c3SJana Rapava 
ulpi_set_vbus_indicator(struct ulpi_viewport * ulpi_vp,int external,int passthu,int complement)111141288b3SLucas Stach int ulpi_set_vbus_indicator(struct ulpi_viewport *ulpi_vp, int external,
112141288b3SLucas Stach 			int passthu, int complement)
113141288b3SLucas Stach {
114141288b3SLucas Stach 	u32 flags, val;
115141288b3SLucas Stach 	u8 *reg;
116141288b3SLucas Stach 
117141288b3SLucas Stach 	reg = external ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear;
118141288b3SLucas Stach 	val = ulpi_write(ulpi_vp, reg, ULPI_OTG_EXTVBUSIND);
119141288b3SLucas Stach 	if (val)
120141288b3SLucas Stach 		return val;
121141288b3SLucas Stach 
122141288b3SLucas Stach 	flags = passthu ? ULPI_IFACE_PASSTHRU : 0;
123141288b3SLucas Stach 	flags |= complement ? ULPI_IFACE_EXTVBUS_COMPLEMENT : 0;
124141288b3SLucas Stach 
125141288b3SLucas Stach 	val = ulpi_read(ulpi_vp, &ulpi->iface_ctrl);
126141288b3SLucas Stach 	if (val == ULPI_ERROR)
127141288b3SLucas Stach 		return val;
128141288b3SLucas Stach 
129141288b3SLucas Stach 	val = val & ~(ULPI_IFACE_PASSTHRU & ULPI_IFACE_EXTVBUS_COMPLEMENT);
130141288b3SLucas Stach 	val |= flags;
131141288b3SLucas Stach 	val = ulpi_write(ulpi_vp, &ulpi->iface_ctrl, val);
132141288b3SLucas Stach 	if (val)
133141288b3SLucas Stach 		return val;
134141288b3SLucas Stach 
135141288b3SLucas Stach 	return 0;
136141288b3SLucas Stach }
137141288b3SLucas Stach 
ulpi_set_pd(struct ulpi_viewport * ulpi_vp,int enable)1383e6e809fSGovindraj.R int ulpi_set_pd(struct ulpi_viewport *ulpi_vp, int enable)
139f93022c3SJana Rapava {
140f93022c3SJana Rapava 	u32 val = ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN;
141f93022c3SJana Rapava 	u8 *reg = enable ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear;
142f93022c3SJana Rapava 
1433e6e809fSGovindraj.R 	return ulpi_write(ulpi_vp, reg, val);
144f93022c3SJana Rapava }
145f93022c3SJana Rapava 
ulpi_opmode_sel(struct ulpi_viewport * ulpi_vp,unsigned opmode)1463e6e809fSGovindraj.R int ulpi_opmode_sel(struct ulpi_viewport *ulpi_vp, unsigned opmode)
147f93022c3SJana Rapava {
1481113a79bSIgor Grinberg 	u32 topmode = ULPI_FC_OPMODE_NORMAL;
149f93022c3SJana Rapava 	u32 val;
150f93022c3SJana Rapava 
151f93022c3SJana Rapava 	switch (opmode) {
152f93022c3SJana Rapava 	case ULPI_FC_OPMODE_NORMAL:
153f93022c3SJana Rapava 	case ULPI_FC_OPMODE_NONDRIVING:
154f93022c3SJana Rapava 	case ULPI_FC_OPMODE_DISABLE_NRZI:
155f93022c3SJana Rapava 	case ULPI_FC_OPMODE_NOSYNC_NOEOP:
156f93022c3SJana Rapava 		topmode = opmode;
157f93022c3SJana Rapava 		break;
158f93022c3SJana Rapava 	default:
159cf9f95f2SIgor Grinberg 		printf("ULPI: %s: wrong OpMode specified: %u, "
160cf9f95f2SIgor Grinberg 			"falling back to OpMode Normal\n", __func__, opmode);
161f93022c3SJana Rapava 	}
162f93022c3SJana Rapava 
1633e6e809fSGovindraj.R 	val = ulpi_read(ulpi_vp, &ulpi->function_ctrl);
164f93022c3SJana Rapava 	if (val == ULPI_ERROR)
165f93022c3SJana Rapava 		return val;
166f93022c3SJana Rapava 
167f93022c3SJana Rapava 	/* clear the previous opmode setting */
168f93022c3SJana Rapava 	val = (val & ~ULPI_FC_OPMODE_MASK) | topmode;
169f93022c3SJana Rapava 
1703e6e809fSGovindraj.R 	return ulpi_write(ulpi_vp, &ulpi->function_ctrl, val);
171f93022c3SJana Rapava }
172f93022c3SJana Rapava 
ulpi_serial_mode_enable(struct ulpi_viewport * ulpi_vp,unsigned smode)1733e6e809fSGovindraj.R int ulpi_serial_mode_enable(struct ulpi_viewport *ulpi_vp, unsigned smode)
174f93022c3SJana Rapava {
175f93022c3SJana Rapava 	switch (smode) {
176f93022c3SJana Rapava 	case ULPI_IFACE_6_PIN_SERIAL_MODE:
177f93022c3SJana Rapava 	case ULPI_IFACE_3_PIN_SERIAL_MODE:
178f93022c3SJana Rapava 		break;
179f93022c3SJana Rapava 	default:
180cf9f95f2SIgor Grinberg 		printf("ULPI: %s: unrecognized Serial Mode specified: %u\n",
181cf9f95f2SIgor Grinberg 			__func__, smode);
182f93022c3SJana Rapava 		return ULPI_ERROR;
183f93022c3SJana Rapava 	}
184f93022c3SJana Rapava 
1853e6e809fSGovindraj.R 	return ulpi_write(ulpi_vp, &ulpi->iface_ctrl_set, smode);
186f93022c3SJana Rapava }
187f93022c3SJana Rapava 
ulpi_suspend(struct ulpi_viewport * ulpi_vp)1883e6e809fSGovindraj.R int ulpi_suspend(struct ulpi_viewport *ulpi_vp)
189f93022c3SJana Rapava {
1904256101fSIgor Grinberg 	int err;
191f93022c3SJana Rapava 
1923e6e809fSGovindraj.R 	err = ulpi_write(ulpi_vp, &ulpi->function_ctrl_clear,
193f93022c3SJana Rapava 			ULPI_FC_SUSPENDM);
194f93022c3SJana Rapava 	if (err)
195f93022c3SJana Rapava 		printf("ULPI: %s: failed writing the suspend bit\n", __func__);
196f93022c3SJana Rapava 
197f93022c3SJana Rapava 	return err;
198f93022c3SJana Rapava }
199f93022c3SJana Rapava 
200f93022c3SJana Rapava /*
201f93022c3SJana Rapava  * Wait for ULPI PHY reset to complete.
202f93022c3SJana Rapava  * Actual wait for reset must be done in a view port specific way,
203f93022c3SJana Rapava  * because it involves checking the DIR line.
204f93022c3SJana Rapava  */
__ulpi_reset_wait(struct ulpi_viewport * ulpi_vp)2053e6e809fSGovindraj.R static int __ulpi_reset_wait(struct ulpi_viewport *ulpi_vp)
206f93022c3SJana Rapava {
207f93022c3SJana Rapava 	u32 val;
208f93022c3SJana Rapava 	int timeout = CONFIG_USB_ULPI_TIMEOUT;
209f93022c3SJana Rapava 
210f93022c3SJana Rapava 	/* Wait for the RESET bit to become zero */
211f93022c3SJana Rapava 	while (--timeout) {
212f93022c3SJana Rapava 		/*
213f93022c3SJana Rapava 		 * This function is generic and suppose to work
214f93022c3SJana Rapava 		 * with any viewport, so we cheat here and don't check
215f93022c3SJana Rapava 		 * for the error of ulpi_read(), if there is one, then
216f93022c3SJana Rapava 		 * there will be a timeout.
217f93022c3SJana Rapava 		 */
2183e6e809fSGovindraj.R 		val = ulpi_read(ulpi_vp, &ulpi->function_ctrl);
219f93022c3SJana Rapava 		if (!(val & ULPI_FC_RESET))
220f93022c3SJana Rapava 			return 0;
221f93022c3SJana Rapava 
222f93022c3SJana Rapava 		udelay(1);
223f93022c3SJana Rapava 	}
224f93022c3SJana Rapava 
225f93022c3SJana Rapava 	printf("ULPI: %s: reset timed out\n", __func__);
226f93022c3SJana Rapava 
227f93022c3SJana Rapava 	return ULPI_ERROR;
228f93022c3SJana Rapava }
2293e6e809fSGovindraj.R int ulpi_reset_wait(struct ulpi_viewport *ulpi_vp)
2303e6e809fSGovindraj.R 	__attribute__((weak, alias("__ulpi_reset_wait")));
231f93022c3SJana Rapava 
ulpi_reset(struct ulpi_viewport * ulpi_vp)2323e6e809fSGovindraj.R int ulpi_reset(struct ulpi_viewport *ulpi_vp)
233f93022c3SJana Rapava {
2344256101fSIgor Grinberg 	int err;
235f93022c3SJana Rapava 
2363e6e809fSGovindraj.R 	err = ulpi_write(ulpi_vp,
237f93022c3SJana Rapava 			&ulpi->function_ctrl_set, ULPI_FC_RESET);
238f93022c3SJana Rapava 	if (err) {
239f93022c3SJana Rapava 		printf("ULPI: %s: failed writing reset bit\n", __func__);
240f93022c3SJana Rapava 		return err;
241f93022c3SJana Rapava 	}
242f93022c3SJana Rapava 
2433e6e809fSGovindraj.R 	return ulpi_reset_wait(ulpi_vp);
244f93022c3SJana Rapava }
245