2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2011-12-05 09:07:00 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011 Jana Rapava <fermata7@gmail.com>
|
|
|
|
* Copyright (C) 2011 CompuLab, Ltd. <www.compulab.co.il>
|
|
|
|
*
|
|
|
|
* Authors: Jana Rapava <fermata7@gmail.com>
|
|
|
|
* Igor Grinberg <grinberg@compulab.co.il>
|
|
|
|
*
|
|
|
|
* Based on:
|
|
|
|
* linux/drivers/usb/otg/ulpi.c
|
|
|
|
* Generic ULPI USB transceiver support
|
|
|
|
*
|
|
|
|
* Original Copyright follow:
|
|
|
|
* Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
|
|
|
|
*
|
|
|
|
* Based on sources from
|
|
|
|
*
|
|
|
|
* Sascha Hauer <s.hauer@pengutronix.de>
|
|
|
|
* Freescale Semiconductors
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <exports.h>
|
|
|
|
#include <usb/ulpi.h>
|
|
|
|
|
|
|
|
#define ULPI_ID_REGS_COUNT 4
|
|
|
|
#define ULPI_TEST_VALUE 0x55 /* 0x55 == 0b01010101 */
|
|
|
|
|
|
|
|
static struct ulpi_regs *ulpi = (struct ulpi_regs *)0;
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
static int ulpi_integrity_check(struct ulpi_viewport *ulpi_vp)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
2011-12-12 10:08:33 +00:00
|
|
|
u32 val, tval = ULPI_TEST_VALUE;
|
|
|
|
int err, i;
|
2011-12-05 09:07:00 +00:00
|
|
|
|
|
|
|
/* Use the 'special' test value to check all bits */
|
|
|
|
for (i = 0; i < 2; i++, tval <<= 1) {
|
2012-02-06 03:55:31 +00:00
|
|
|
err = ulpi_write(ulpi_vp, &ulpi->scratch, tval);
|
2011-12-05 09:07:00 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
val = ulpi_read(ulpi_vp, &ulpi->scratch);
|
2011-12-05 09:07:00 +00:00
|
|
|
if (val != tval) {
|
|
|
|
printf("ULPI integrity check failed\n");
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
int ulpi_init(struct ulpi_viewport *ulpi_vp)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
|
|
|
u32 val, id = 0;
|
|
|
|
u8 *reg = &ulpi->product_id_high;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Assemble ID from four ULPI ID registers (8 bits each). */
|
|
|
|
for (i = 0; i < ULPI_ID_REGS_COUNT; i++) {
|
2012-02-06 03:55:31 +00:00
|
|
|
val = ulpi_read(ulpi_vp, reg - i);
|
2011-12-05 09:07:00 +00:00
|
|
|
if (val == ULPI_ERROR)
|
|
|
|
return val;
|
|
|
|
|
|
|
|
id = (id << 8) | val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Split ID into vendor and product ID. */
|
|
|
|
debug("ULPI transceiver ID 0x%04x:0x%04x\n", id >> 16, id & 0xffff);
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
return ulpi_integrity_check(ulpi_vp);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
int ulpi_select_transceiver(struct ulpi_viewport *ulpi_vp, unsigned speed)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
2011-12-14 06:16:03 +00:00
|
|
|
u32 tspeed = ULPI_FC_FULL_SPEED;
|
2011-12-05 09:07:00 +00:00
|
|
|
u32 val;
|
|
|
|
|
|
|
|
switch (speed) {
|
|
|
|
case ULPI_FC_HIGH_SPEED:
|
|
|
|
case ULPI_FC_FULL_SPEED:
|
|
|
|
case ULPI_FC_LOW_SPEED:
|
|
|
|
case ULPI_FC_FS4LS:
|
|
|
|
tspeed = speed;
|
|
|
|
break;
|
|
|
|
default:
|
2011-12-12 10:08:34 +00:00
|
|
|
printf("ULPI: %s: wrong transceiver speed specified: %u, "
|
|
|
|
"falling back to full speed\n", __func__, speed);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
val = ulpi_read(ulpi_vp, &ulpi->function_ctrl);
|
2011-12-05 09:07:00 +00:00
|
|
|
if (val == ULPI_ERROR)
|
|
|
|
return val;
|
|
|
|
|
|
|
|
/* clear the previous speed setting */
|
|
|
|
val = (val & ~ULPI_FC_XCVRSEL_MASK) | tspeed;
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
return ulpi_write(ulpi_vp, &ulpi->function_ctrl, val);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|
|
|
|
|
2012-09-30 22:44:34 +00:00
|
|
|
int ulpi_set_vbus(struct ulpi_viewport *ulpi_vp, int on, int ext_power)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
|
|
|
u32 flags = ULPI_OTG_DRVVBUS;
|
|
|
|
u8 *reg = on ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear;
|
|
|
|
|
|
|
|
if (ext_power)
|
|
|
|
flags |= ULPI_OTG_DRVVBUS_EXT;
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
return ulpi_write(ulpi_vp, reg, flags);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|
|
|
|
|
2012-09-30 22:44:34 +00:00
|
|
|
int ulpi_set_vbus_indicator(struct ulpi_viewport *ulpi_vp, int external,
|
|
|
|
int passthu, int complement)
|
|
|
|
{
|
|
|
|
u32 flags, val;
|
|
|
|
u8 *reg;
|
|
|
|
|
|
|
|
reg = external ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear;
|
|
|
|
val = ulpi_write(ulpi_vp, reg, ULPI_OTG_EXTVBUSIND);
|
|
|
|
if (val)
|
|
|
|
return val;
|
|
|
|
|
|
|
|
flags = passthu ? ULPI_IFACE_PASSTHRU : 0;
|
|
|
|
flags |= complement ? ULPI_IFACE_EXTVBUS_COMPLEMENT : 0;
|
|
|
|
|
|
|
|
val = ulpi_read(ulpi_vp, &ulpi->iface_ctrl);
|
|
|
|
if (val == ULPI_ERROR)
|
|
|
|
return val;
|
|
|
|
|
|
|
|
val = val & ~(ULPI_IFACE_PASSTHRU & ULPI_IFACE_EXTVBUS_COMPLEMENT);
|
|
|
|
val |= flags;
|
|
|
|
val = ulpi_write(ulpi_vp, &ulpi->iface_ctrl, val);
|
|
|
|
if (val)
|
|
|
|
return val;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
int ulpi_set_pd(struct ulpi_viewport *ulpi_vp, int enable)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
|
|
|
u32 val = ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN;
|
|
|
|
u8 *reg = enable ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear;
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
return ulpi_write(ulpi_vp, reg, val);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
int ulpi_opmode_sel(struct ulpi_viewport *ulpi_vp, unsigned opmode)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
2011-12-14 06:16:03 +00:00
|
|
|
u32 topmode = ULPI_FC_OPMODE_NORMAL;
|
2011-12-05 09:07:00 +00:00
|
|
|
u32 val;
|
|
|
|
|
|
|
|
switch (opmode) {
|
|
|
|
case ULPI_FC_OPMODE_NORMAL:
|
|
|
|
case ULPI_FC_OPMODE_NONDRIVING:
|
|
|
|
case ULPI_FC_OPMODE_DISABLE_NRZI:
|
|
|
|
case ULPI_FC_OPMODE_NOSYNC_NOEOP:
|
|
|
|
topmode = opmode;
|
|
|
|
break;
|
|
|
|
default:
|
2011-12-12 10:08:34 +00:00
|
|
|
printf("ULPI: %s: wrong OpMode specified: %u, "
|
|
|
|
"falling back to OpMode Normal\n", __func__, opmode);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
val = ulpi_read(ulpi_vp, &ulpi->function_ctrl);
|
2011-12-05 09:07:00 +00:00
|
|
|
if (val == ULPI_ERROR)
|
|
|
|
return val;
|
|
|
|
|
|
|
|
/* clear the previous opmode setting */
|
|
|
|
val = (val & ~ULPI_FC_OPMODE_MASK) | topmode;
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
return ulpi_write(ulpi_vp, &ulpi->function_ctrl, val);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
int ulpi_serial_mode_enable(struct ulpi_viewport *ulpi_vp, unsigned smode)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
|
|
|
switch (smode) {
|
|
|
|
case ULPI_IFACE_6_PIN_SERIAL_MODE:
|
|
|
|
case ULPI_IFACE_3_PIN_SERIAL_MODE:
|
|
|
|
break;
|
|
|
|
default:
|
2011-12-12 10:08:34 +00:00
|
|
|
printf("ULPI: %s: unrecognized Serial Mode specified: %u\n",
|
|
|
|
__func__, smode);
|
2011-12-05 09:07:00 +00:00
|
|
|
return ULPI_ERROR;
|
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
return ulpi_write(ulpi_vp, &ulpi->iface_ctrl_set, smode);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
int ulpi_suspend(struct ulpi_viewport *ulpi_vp)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
2011-12-12 10:08:33 +00:00
|
|
|
int err;
|
2011-12-05 09:07:00 +00:00
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
err = ulpi_write(ulpi_vp, &ulpi->function_ctrl_clear,
|
2011-12-05 09:07:00 +00:00
|
|
|
ULPI_FC_SUSPENDM);
|
|
|
|
if (err)
|
|
|
|
printf("ULPI: %s: failed writing the suspend bit\n", __func__);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for ULPI PHY reset to complete.
|
|
|
|
* Actual wait for reset must be done in a view port specific way,
|
|
|
|
* because it involves checking the DIR line.
|
|
|
|
*/
|
2012-02-06 03:55:31 +00:00
|
|
|
static int __ulpi_reset_wait(struct ulpi_viewport *ulpi_vp)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
int timeout = CONFIG_USB_ULPI_TIMEOUT;
|
|
|
|
|
|
|
|
/* Wait for the RESET bit to become zero */
|
|
|
|
while (--timeout) {
|
|
|
|
/*
|
|
|
|
* This function is generic and suppose to work
|
|
|
|
* with any viewport, so we cheat here and don't check
|
|
|
|
* for the error of ulpi_read(), if there is one, then
|
|
|
|
* there will be a timeout.
|
|
|
|
*/
|
2012-02-06 03:55:31 +00:00
|
|
|
val = ulpi_read(ulpi_vp, &ulpi->function_ctrl);
|
2011-12-05 09:07:00 +00:00
|
|
|
if (!(val & ULPI_FC_RESET))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
udelay(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("ULPI: %s: reset timed out\n", __func__);
|
|
|
|
|
|
|
|
return ULPI_ERROR;
|
|
|
|
}
|
2012-02-06 03:55:31 +00:00
|
|
|
int ulpi_reset_wait(struct ulpi_viewport *ulpi_vp)
|
|
|
|
__attribute__((weak, alias("__ulpi_reset_wait")));
|
2011-12-05 09:07:00 +00:00
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
int ulpi_reset(struct ulpi_viewport *ulpi_vp)
|
2011-12-05 09:07:00 +00:00
|
|
|
{
|
2011-12-12 10:08:33 +00:00
|
|
|
int err;
|
2011-12-05 09:07:00 +00:00
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
err = ulpi_write(ulpi_vp,
|
2011-12-05 09:07:00 +00:00
|
|
|
&ulpi->function_ctrl_set, ULPI_FC_RESET);
|
|
|
|
if (err) {
|
|
|
|
printf("ULPI: %s: failed writing reset bit\n", __func__);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-02-06 03:55:31 +00:00
|
|
|
return ulpi_reset_wait(ulpi_vp);
|
2011-12-05 09:07:00 +00:00
|
|
|
}
|