From f93022c3b11f191141c9128ee88d297cec3b9330 Mon Sep 17 00:00:00 2001 From: Jana Rapava Date: Mon, 5 Dec 2011 11:07:00 +0200 Subject: USB: Add generic ULPI layer and a viewport Add partial ULPI specification implementation that should be enough to interface the ULPI PHYs in the boot loader context. Add a viewport implementation for Chipidea/ARC based controllers. Signed-off-by: Jana Rapava Signed-off-by: Igor Grinberg Cc: Remy Bohmer Cc: Stefano Babic Cc: Wolfgang Grandegger Cc: Simon Glass --- drivers/usb/ulpi/Makefile | 44 ++++++++ drivers/usb/ulpi/ulpi-viewport.c | 118 ++++++++++++++++++++ drivers/usb/ulpi/ulpi.c | 227 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 drivers/usb/ulpi/Makefile create mode 100644 drivers/usb/ulpi/ulpi-viewport.c create mode 100644 drivers/usb/ulpi/ulpi.c (limited to 'drivers/usb/ulpi') diff --git a/drivers/usb/ulpi/Makefile b/drivers/usb/ulpi/Makefile new file mode 100644 index 0000000..d43b229 --- /dev/null +++ b/drivers/usb/ulpi/Makefile @@ -0,0 +1,44 @@ +# +# Copyright (C) 2011 Jana Rapava +# See file CREDITS for list of people who contributed to this +# project. +# +# 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. +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libusb_ulpi.o + +COBJS-$(CONFIG_USB_ULPI) += ulpi.o +COBJS-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi-viewport.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/usb/ulpi/ulpi-viewport.c b/drivers/usb/ulpi/ulpi-viewport.c new file mode 100644 index 0000000..fa2e004 --- /dev/null +++ b/drivers/usb/ulpi/ulpi-viewport.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2011 Jana Rapava + * Copyright (C) 2011 CompuLab, Ltd. + * + * Authors: Jana Rapava + * Igor Grinberg + * + * Based on: + * linux/drivers/usb/otg/ulpi_viewport.c + * + * Original Copyright follow: + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include + +/* ULPI viewport control bits */ +#define ULPI_SS (1 << 27) +#define ULPI_RWCTRL (1 << 29) +#define ULPI_RWRUN (1 << 30) +#define ULPI_WU (1 << 31) + +/* + * Wait for the ULPI request to complete + * + * @ulpi_viewport - the address of the viewport + * @mask - expected value to wait for + * + * returns 0 on mask match, ULPI_ERROR on time out. + */ +static int ulpi_wait(u32 ulpi_viewport, u32 mask) +{ + int timeout = CONFIG_USB_ULPI_TIMEOUT; + + /* Wait for the bits in mask to become zero. */ + while (--timeout) { + if ((readl(ulpi_viewport) & mask) == 0) + return 0; + + udelay(1); + } + + return ULPI_ERROR; +} + +/* + * Wake the ULPI PHY up for communication + * + * returns 0 on success. + */ +static int ulpi_wakeup(u32 ulpi_viewport) +{ + int err; + + if (readl(ulpi_viewport) & ULPI_SS) + return 0; /* already awake */ + + writel(ULPI_WU, ulpi_viewport); + + err = ulpi_wait(ulpi_viewport, ULPI_WU); + if (err) + printf("ULPI wakeup timed out\n"); + + return err; +} + +/* + * Issue a ULPI read/write request + * + * @value - the ULPI request + */ +static int ulpi_request(u32 ulpi_viewport, u32 value) +{ + int err; + + err = ulpi_wakeup(ulpi_viewport); + if (err) + return err; + + writel(value, ulpi_viewport); + + err = ulpi_wait(ulpi_viewport, ULPI_RWRUN); + if (err) + printf("ULPI request timed out\n"); + + return err; +} + +u32 ulpi_write(u32 ulpi_viewport, u8 *reg, u32 value) +{ + u32 val = ULPI_RWRUN | ULPI_RWCTRL | ((u32)reg << 16) | (value & 0xff); + + return ulpi_request(ulpi_viewport, val); +} + +u32 ulpi_read(u32 ulpi_viewport, u8 *reg) +{ + u32 err; + u32 val = ULPI_RWRUN | ((u32)reg << 16); + + err = ulpi_request(ulpi_viewport, val); + if (err) + return err; + + return (readl(ulpi_viewport) >> 8) & 0xff; +} diff --git a/drivers/usb/ulpi/ulpi.c b/drivers/usb/ulpi/ulpi.c new file mode 100644 index 0000000..805e29d --- /dev/null +++ b/drivers/usb/ulpi/ulpi.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2011 Jana Rapava + * Copyright (C) 2011 CompuLab, Ltd. + * + * Authors: Jana Rapava + * Igor Grinberg + * + * Based on: + * linux/drivers/usb/otg/ulpi.c + * Generic ULPI USB transceiver support + * + * Original Copyright follow: + * Copyright (C) 2009 Daniel Mack + * + * Based on sources from + * + * Sascha Hauer + * Freescale Semiconductors + * + * 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 +#include +#include + +#define ULPI_ID_REGS_COUNT 4 +#define ULPI_TEST_VALUE 0x55 /* 0x55 == 0b01010101 */ + +static struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + +static int ulpi_integrity_check(u32 ulpi_viewport) +{ + u32 err, val, tval = ULPI_TEST_VALUE; + int i; + + /* Use the 'special' test value to check all bits */ + for (i = 0; i < 2; i++, tval <<= 1) { + err = ulpi_write(ulpi_viewport, &ulpi->scratch, tval); + if (err) + return err; + + val = ulpi_read(ulpi_viewport, &ulpi->scratch); + if (val != tval) { + printf("ULPI integrity check failed\n"); + return val; + } + } + + return 0; +} + +int ulpi_init(u32 ulpi_viewport) +{ + 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++) { + val = ulpi_read(ulpi_viewport, reg - i); + 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); + + return ulpi_integrity_check(ulpi_viewport); +} + +int ulpi_select_transceiver(u32 ulpi_viewport, u8 speed) +{ + u8 tspeed = ULPI_FC_FULL_SPEED; + 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: + printf("ULPI: %s: wrong transceiver speed specified, " + "falling back to full speed\n", __func__); + } + + val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); + if (val == ULPI_ERROR) + return val; + + /* clear the previous speed setting */ + val = (val & ~ULPI_FC_XCVRSEL_MASK) | tspeed; + + return ulpi_write(ulpi_viewport, &ulpi->function_ctrl, val); +} + +int ulpi_set_vbus(u32 ulpi_viewport, int on, int ext_power, int ext_ind) +{ + u32 flags = ULPI_OTG_DRVVBUS; + u8 *reg = on ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + + if (ext_power) + flags |= ULPI_OTG_DRVVBUS_EXT; + if (ext_ind) + flags |= ULPI_OTG_EXTVBUSIND; + + return ulpi_write(ulpi_viewport, reg, flags); +} + +int ulpi_set_pd(u32 ulpi_viewport, int enable) +{ + u32 val = ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN; + u8 *reg = enable ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + + return ulpi_write(ulpi_viewport, reg, val); +} + +int ulpi_opmode_sel(u32 ulpi_viewport, u8 opmode) +{ + u8 topmode = ULPI_FC_OPMODE_NORMAL; + 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: + printf("ULPI: %s: wrong OpMode specified, " + "falling back to OpMode Normal\n", __func__); + } + + val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); + if (val == ULPI_ERROR) + return val; + + /* clear the previous opmode setting */ + val = (val & ~ULPI_FC_OPMODE_MASK) | topmode; + + return ulpi_write(ulpi_viewport, &ulpi->function_ctrl, val); +} + +int ulpi_serial_mode_enable(u32 ulpi_viewport, u8 smode) +{ + switch (smode) { + case ULPI_IFACE_6_PIN_SERIAL_MODE: + case ULPI_IFACE_3_PIN_SERIAL_MODE: + break; + default: + printf("ULPI: %s: unrecognized Serial Mode specified\n", + __func__); + return ULPI_ERROR; + } + + return ulpi_write(ulpi_viewport, &ulpi->iface_ctrl_set, smode); +} + +int ulpi_suspend(u32 ulpi_viewport) +{ + u32 err; + + err = ulpi_write(ulpi_viewport, &ulpi->function_ctrl_clear, + 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. + */ +static int __ulpi_reset_wait(u32 ulpi_viewport) +{ + 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. + */ + val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); + if (!(val & ULPI_FC_RESET)) + return 0; + + udelay(1); + } + + printf("ULPI: %s: reset timed out\n", __func__); + + return ULPI_ERROR; +} +int ulpi_reset_wait(u32) __attribute__((weak, alias("__ulpi_reset_wait"))); + +int ulpi_reset(u32 ulpi_viewport) +{ + u32 err; + + err = ulpi_write(ulpi_viewport, + &ulpi->function_ctrl_set, ULPI_FC_RESET); + if (err) { + printf("ULPI: %s: failed writing reset bit\n", __func__); + return err; + } + + return ulpi_reset_wait(ulpi_viewport); +} -- cgit v1.1