1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Fix for RUTX platform

This commit is contained in:
Ycarus (Yannick Chabanois) 2022-03-28 18:17:07 +02:00
parent ccdb64ad45
commit 59bc57d5d5
7254 changed files with 1810270 additions and 7 deletions

View file

@ -0,0 +1,49 @@
#
# Copyright (c) 2011 The Chromium OS Authors.
# 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., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB := $(obj)libusb_eth.o
# new USB host ethernet layer dependencies
COBJS-$(CONFIG_USB_HOST_ETHER) += usb_ether.o
ifdef CONFIG_USB_ETHER_ASIX
COBJS-y += asix.o
endif
COBJS-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.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
#########################################################################

View file

@ -0,0 +1,635 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <usb.h>
#include <linux/mii.h>
#include "usb_ether.h"
/* ASIX AX8817X based USB 2.0 Ethernet Devices */
#define AX_CMD_SET_SW_MII 0x06
#define AX_CMD_READ_MII_REG 0x07
#define AX_CMD_WRITE_MII_REG 0x08
#define AX_CMD_SET_HW_MII 0x0a
#define AX_CMD_READ_RX_CTL 0x0f
#define AX_CMD_WRITE_RX_CTL 0x10
#define AX_CMD_WRITE_IPG0 0x12
#define AX_CMD_READ_NODE_ID 0x13
#define AX_CMD_READ_PHY_ID 0x19
#define AX_CMD_WRITE_MEDIUM_MODE 0x1b
#define AX_CMD_WRITE_GPIOS 0x1f
#define AX_CMD_SW_RESET 0x20
#define AX_CMD_SW_PHY_SELECT 0x22
#define AX_SWRESET_CLEAR 0x00
#define AX_SWRESET_PRTE 0x04
#define AX_SWRESET_PRL 0x08
#define AX_SWRESET_IPRL 0x20
#define AX_SWRESET_IPPD 0x40
#define AX88772_IPG0_DEFAULT 0x15
#define AX88772_IPG1_DEFAULT 0x0c
#define AX88772_IPG2_DEFAULT 0x12
/* AX88772 & AX88178 Medium Mode Register */
#define AX_MEDIUM_PF 0x0080
#define AX_MEDIUM_JFE 0x0040
#define AX_MEDIUM_TFC 0x0020
#define AX_MEDIUM_RFC 0x0010
#define AX_MEDIUM_ENCK 0x0008
#define AX_MEDIUM_AC 0x0004
#define AX_MEDIUM_FD 0x0002
#define AX_MEDIUM_GM 0x0001
#define AX_MEDIUM_SM 0x1000
#define AX_MEDIUM_SBP 0x0800
#define AX_MEDIUM_PS 0x0200
#define AX_MEDIUM_RE 0x0100
#define AX88178_MEDIUM_DEFAULT \
(AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \
AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \
AX_MEDIUM_RE)
#define AX88772_MEDIUM_DEFAULT \
(AX_MEDIUM_FD | AX_MEDIUM_RFC | \
AX_MEDIUM_TFC | AX_MEDIUM_PS | \
AX_MEDIUM_AC | AX_MEDIUM_RE)
/* AX88772 & AX88178 RX_CTL values */
#define AX_RX_CTL_SO 0x0080
#define AX_RX_CTL_AB 0x0008
#define AX_DEFAULT_RX_CTL \
(AX_RX_CTL_SO | AX_RX_CTL_AB)
/* GPIO 2 toggles */
#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */
#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */
#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */
/* local defines */
#define ASIX_BASE_NAME "asx"
#define USB_CTRL_SET_TIMEOUT 5000
#define USB_CTRL_GET_TIMEOUT 5000
#define USB_BULK_SEND_TIMEOUT 5000
#define USB_BULK_RECV_TIMEOUT 5000
#define AX_RX_URB_SIZE 2048
#define PHY_CONNECT_TIMEOUT 5000
/* local vars */
static int curr_eth_dev; /* index for name of next device detected */
/*
* Asix infrastructure commands
*/
static int asix_write_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index,
u16 size, void *data)
{
int len;
debug("asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x "
"size=%d\n", cmd, value, index, size);
len = usb_control_msg(
dev->pusb_dev,
usb_sndctrlpipe(dev->pusb_dev, 0),
cmd,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value,
index,
data,
size,
USB_CTRL_SET_TIMEOUT);
return len == size ? 0 : -1;
}
static int asix_read_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index,
u16 size, void *data)
{
int len;
debug("asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n",
cmd, value, index, size);
len = usb_control_msg(
dev->pusb_dev,
usb_rcvctrlpipe(dev->pusb_dev, 0),
cmd,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value,
index,
data,
size,
USB_CTRL_GET_TIMEOUT);
return len == size ? 0 : -1;
}
static inline int asix_set_sw_mii(struct ueth_data *dev)
{
int ret;
ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
if (ret < 0)
debug("Failed to enable software MII access\n");
return ret;
}
static inline int asix_set_hw_mii(struct ueth_data *dev)
{
int ret;
ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL);
if (ret < 0)
debug("Failed to enable hardware MII access\n");
return ret;
}
static int asix_mdio_read(struct ueth_data *dev, int phy_id, int loc)
{
__le16 res;
asix_set_sw_mii(dev);
asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, &res);
asix_set_hw_mii(dev);
debug("asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
phy_id, loc, le16_to_cpu(res));
return le16_to_cpu(res);
}
static void
asix_mdio_write(struct ueth_data *dev, int phy_id, int loc, int val)
{
__le16 res = cpu_to_le16(val);
debug("asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
phy_id, loc, val);
asix_set_sw_mii(dev);
asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res);
asix_set_hw_mii(dev);
}
/*
* Asix "high level" commands
*/
static int asix_sw_reset(struct ueth_data *dev, u8 flags)
{
int ret;
ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL);
if (ret < 0)
debug("Failed to send software reset: %02x\n", ret);
else
udelay(150 * 1000);
return ret;
}
static inline int asix_get_phy_addr(struct ueth_data *dev)
{
u8 buf[2];
int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
debug("asix_get_phy_addr()\n");
if (ret < 0) {
debug("Error reading PHYID register: %02x\n", ret);
goto out;
}
debug("asix_get_phy_addr() returning 0x%02x%02x\n", buf[0], buf[1]);
ret = buf[1];
out:
return ret;
}
static int asix_write_medium_mode(struct ueth_data *dev, u16 mode)
{
int ret;
debug("asix_write_medium_mode() - mode = 0x%04x\n", mode);
ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode,
0, 0, NULL);
if (ret < 0) {
debug("Failed to write Medium Mode mode to 0x%04x: %02x\n",
mode, ret);
}
return ret;
}
static u16 asix_read_rx_ctl(struct ueth_data *dev)
{
__le16 v;
int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v);
if (ret < 0)
debug("Error reading RX_CTL register: %02x\n", ret);
else
ret = le16_to_cpu(v);
return ret;
}
static int asix_write_rx_ctl(struct ueth_data *dev, u16 mode)
{
int ret;
debug("asix_write_rx_ctl() - mode = 0x%04x\n", mode);
ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL);
if (ret < 0) {
debug("Failed to write RX_CTL mode to 0x%04x: %02x\n",
mode, ret);
}
return ret;
}
static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep)
{
int ret;
debug("asix_write_gpio() - value = 0x%04x\n", value);
ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL);
if (ret < 0) {
debug("Failed to write GPIO value 0x%04x: %02x\n",
value, ret);
}
if (sleep)
udelay(sleep * 1000);
return ret;
}
/*
* mii commands
*/
/*
* mii_nway_restart - restart NWay (autonegotiation) for this interface
*
* Returns 0 on success, negative on error.
*/
static int mii_nway_restart(struct ueth_data *dev)
{
int bmcr;
int r = -1;
/* if autoneg is off, it's an error */
bmcr = asix_mdio_read(dev, dev->phy_id, MII_BMCR);
if (bmcr & BMCR_ANENABLE) {
bmcr |= BMCR_ANRESTART;
asix_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr);
r = 0;
}
return r;
}
/*
* Asix callbacks
*/
static int asix_init(struct eth_device *eth, bd_t *bd)
{
int embd_phy;
unsigned char buf[ETH_ALEN];
u16 rx_ctl;
struct ueth_data *dev = (struct ueth_data *)eth->priv;
int timeout = 0;
#define TIMEOUT_RESOLUTION 50 /* ms */
int link_detected;
debug("** %s()\n", __func__);
if (asix_write_gpio(dev,
AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5) < 0)
goto out_err;
/* 0x10 is the phy id of the embedded 10/100 ethernet phy */
embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0);
if (asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
embd_phy, 0, 0, NULL) < 0) {
debug("Select PHY #1 failed\n");
goto out_err;
}
if (asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL) < 0)
goto out_err;
if (asix_sw_reset(dev, AX_SWRESET_CLEAR) < 0)
goto out_err;
if (embd_phy) {
if (asix_sw_reset(dev, AX_SWRESET_IPRL) < 0)
goto out_err;
} else {
if (asix_sw_reset(dev, AX_SWRESET_PRTE) < 0)
goto out_err;
}
rx_ctl = asix_read_rx_ctl(dev);
debug("RX_CTL is 0x%04x after software reset\n", rx_ctl);
if (asix_write_rx_ctl(dev, 0x0000) < 0)
goto out_err;
rx_ctl = asix_read_rx_ctl(dev);
debug("RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);
/* Get the MAC address */
if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
0, 0, ETH_ALEN, buf) < 0) {
debug("Failed to read MAC address.\n");
goto out_err;
}
memcpy(eth->enetaddr, buf, ETH_ALEN);
debug("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
eth->enetaddr[0], eth->enetaddr[1],
eth->enetaddr[2], eth->enetaddr[3],
eth->enetaddr[4], eth->enetaddr[5]);
dev->phy_id = asix_get_phy_addr(dev);
if (dev->phy_id < 0)
debug("Failed to read phy id\n");
if (asix_sw_reset(dev, AX_SWRESET_PRL) < 0)
goto out_err;
if (asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL) < 0)
goto out_err;
asix_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET);
asix_mdio_write(dev, dev->phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA);
mii_nway_restart(dev);
if (asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT) < 0)
goto out_err;
if (asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
AX88772_IPG2_DEFAULT, 0, NULL) < 0) {
debug("Write IPG,IPG1,IPG2 failed\n");
goto out_err;
}
if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0)
goto out_err;
do {
link_detected = asix_mdio_read(dev, dev->phy_id, MII_BMSR) &
BMSR_LSTATUS;
if (!link_detected) {
if (timeout == 0)
printf("Waiting for Ethernet connection... ");
udelay(TIMEOUT_RESOLUTION * 1000);
timeout += TIMEOUT_RESOLUTION;
}
} while (!link_detected && timeout < PHY_CONNECT_TIMEOUT);
if (link_detected) {
if (timeout != 0)
printf("done.\n");
} else {
printf("unable to connect.\n");
goto out_err;
}
return 0;
out_err:
return -1;
}
static int asix_send(struct eth_device *eth, void *packet, int length)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
int err;
u32 packet_len;
int actual_len;
unsigned char msg[PKTSIZE + sizeof(packet_len)];
debug("** %s(), len %d\n", __func__, length);
packet_len = (((length) ^ 0x0000ffff) << 16) + (length);
cpu_to_le32s(&packet_len);
memcpy(msg, &packet_len, sizeof(packet_len));
memcpy(msg + sizeof(packet_len), (void *)packet, length);
if (length & 1)
length++;
err = usb_bulk_msg(dev->pusb_dev,
usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
(void *)msg,
length + sizeof(packet_len),
&actual_len,
USB_BULK_SEND_TIMEOUT);
debug("Tx: len = %u, actual = %u, err = %d\n",
length + sizeof(packet_len), actual_len, err);
return err;
}
static int asix_recv(struct eth_device *eth)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
static unsigned char recv_buf[AX_RX_URB_SIZE];
unsigned char *buf_ptr;
int err;
int actual_len;
u32 packet_len;
debug("** %s()\n", __func__);
err = usb_bulk_msg(dev->pusb_dev,
usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
(void *)recv_buf,
AX_RX_URB_SIZE,
&actual_len,
USB_BULK_RECV_TIMEOUT);
debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE,
actual_len, err);
if (err != 0) {
debug("Rx: failed to receive\n");
return -1;
}
if (actual_len > AX_RX_URB_SIZE) {
debug("Rx: received too many bytes %d\n", actual_len);
return -1;
}
buf_ptr = recv_buf;
while (actual_len > 0) {
/*
* 1st 4 bytes contain the length of the actual data as two
* complementary 16-bit words. Extract the length of the data.
*/
if (actual_len < sizeof(packet_len)) {
debug("Rx: incomplete packet length\n");
return -1;
}
memcpy(&packet_len, buf_ptr, sizeof(packet_len));
le32_to_cpus(&packet_len);
if (((packet_len >> 16) ^ 0xffff) != (packet_len & 0xffff)) {
debug("Rx: malformed packet length: %#x (%#x:%#x)\n",
packet_len, (packet_len >> 16) ^ 0xffff,
packet_len & 0xffff);
return -1;
}
packet_len = packet_len & 0xffff;
if (packet_len > actual_len - sizeof(packet_len)) {
debug("Rx: too large packet: %d\n", packet_len);
return -1;
}
/* Notify net stack */
NetReceive(buf_ptr + sizeof(packet_len), packet_len);
/* Adjust for next iteration. Packets are padded to 16-bits */
if (packet_len & 1)
packet_len++;
actual_len -= sizeof(packet_len) + packet_len;
buf_ptr += sizeof(packet_len) + packet_len;
}
return err;
}
static void asix_halt(struct eth_device *eth)
{
debug("** %s()\n", __func__);
}
/*
* Asix probing functions
*/
void asix_eth_before_probe(void)
{
curr_eth_dev = 0;
}
struct asix_dongle {
unsigned short vendor;
unsigned short product;
};
static struct asix_dongle asix_dongles[] = {
{ 0x05ac, 0x1402 }, /* Apple USB Ethernet Adapter */
{ 0x07d1, 0x3c05 }, /* D-Link DUB-E100 H/W Ver B1 */
{ 0x0b95, 0x772a }, /* Cables-to-Go USB Ethernet Adapter */
{ 0x0b95, 0x7720 }, /* Trendnet TU2-ET100 V3.0R */
{ 0x0b95, 0x1720 }, /* SMC */
{ 0x0db0, 0xa877 }, /* MSI - ASIX 88772a */
{ 0x13b1, 0x0018 }, /* Linksys 200M v2.1 */
{ 0x1557, 0x7720 }, /* 0Q0 cable ethernet */
{ 0x2001, 0x3c05 }, /* DLink DUB-E100 H/W Ver B1 Alternate */
{ 0x0000, 0x0000 } /* END - Do not remove */
};
/* Probe to see if a new device is actually an asix device */
int asix_eth_probe(struct usb_device *dev, unsigned int ifnum,
struct ueth_data *ss)
{
struct usb_interface *iface;
struct usb_interface_descriptor *iface_desc;
int i;
/* let's examine the device now */
iface = &dev->config.if_desc[ifnum];
iface_desc = &dev->config.if_desc[ifnum].desc;
for (i = 0; asix_dongles[i].vendor != 0; i++) {
if (dev->descriptor.idVendor == asix_dongles[i].vendor &&
dev->descriptor.idProduct == asix_dongles[i].product)
/* Found a supported dongle */
break;
}
if (asix_dongles[i].vendor == 0)
return 0;
memset(ss, 0, sizeof(struct ueth_data));
/* At this point, we know we've got a live one */
debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n",
dev->descriptor.idVendor, dev->descriptor.idProduct);
/* Initialize the ueth_data structure with some useful info */
ss->ifnum = ifnum;
ss->pusb_dev = dev;
ss->subclass = iface_desc->bInterfaceSubClass;
ss->protocol = iface_desc->bInterfaceProtocol;
/*
* We are expecting a minimum of 3 endpoints - in, out (bulk), and
* int. We will ignore any others.
*/
for (i = 0; i < iface_desc->bNumEndpoints; i++) {
/* is it an BULK endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
ss->ep_in = iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
else
ss->ep_out =
iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
}
/* is it an interrupt endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
ss->ep_int = iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->irqinterval = iface->ep_desc[i].bInterval;
}
}
debug("Endpoints In %d Out %d Int %d\n",
ss->ep_in, ss->ep_out, ss->ep_int);
/* Do some basic sanity checks, and bail if we find a problem */
if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
!ss->ep_in || !ss->ep_out || !ss->ep_int) {
debug("Problems with device\n");
return 0;
}
dev->privptr = (void *)ss;
return 1;
}
int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
struct eth_device *eth)
{
if (!eth) {
debug("%s: missing parameter.\n", __func__);
return 0;
}
sprintf(eth->name, "%s%d", ASIX_BASE_NAME, curr_eth_dev++);
eth->init = asix_init;
eth->send = asix_send;
eth->recv = asix_recv;
eth->halt = asix_halt;
eth->priv = ss;
return 1;
}

View file

@ -0,0 +1,879 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* Copyright (C) 2009 NVIDIA, Corporation
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <asm/unaligned.h>
#include <common.h>
#include <usb.h>
#include <linux/mii.h>
#include "usb_ether.h"
/* SMSC LAN95xx based USB 2.0 Ethernet Devices */
/* Tx command words */
#define TX_CMD_A_FIRST_SEG_ 0x00002000
#define TX_CMD_A_LAST_SEG_ 0x00001000
/* Rx status word */
#define RX_STS_FL_ 0x3FFF0000 /* Frame Length */
#define RX_STS_ES_ 0x00008000 /* Error Summary */
/* SCSRs */
#define ID_REV 0x00
#define INT_STS 0x08
#define TX_CFG 0x10
#define TX_CFG_ON_ 0x00000004
#define HW_CFG 0x14
#define HW_CFG_BIR_ 0x00001000
#define HW_CFG_RXDOFF_ 0x00000600
#define HW_CFG_MEF_ 0x00000020
#define HW_CFG_BCE_ 0x00000002
#define HW_CFG_LRST_ 0x00000008
#define PM_CTRL 0x20
#define PM_CTL_PHY_RST_ 0x00000010
#define AFC_CFG 0x2C
/*
* Hi watermark = 15.5Kb (~10 mtu pkts)
* low watermark = 3k (~2 mtu pkts)
* backpressure duration = ~ 350us
* Apply FC on any frame.
*/
#define AFC_CFG_DEFAULT 0x00F830A1
#define E2P_CMD 0x30
#define E2P_CMD_BUSY_ 0x80000000
#define E2P_CMD_READ_ 0x00000000
#define E2P_CMD_TIMEOUT_ 0x00000400
#define E2P_CMD_LOADED_ 0x00000200
#define E2P_CMD_ADDR_ 0x000001FF
#define E2P_DATA 0x34
#define BURST_CAP 0x38
#define INT_EP_CTL 0x68
#define INT_EP_CTL_PHY_INT_ 0x00008000
#define BULK_IN_DLY 0x6C
/* MAC CSRs */
#define MAC_CR 0x100
#define MAC_CR_MCPAS_ 0x00080000
#define MAC_CR_PRMS_ 0x00040000
#define MAC_CR_HPFILT_ 0x00002000
#define MAC_CR_TXEN_ 0x00000008
#define MAC_CR_RXEN_ 0x00000004
#define ADDRH 0x104
#define ADDRL 0x108
#define MII_ADDR 0x114
#define MII_WRITE_ 0x02
#define MII_BUSY_ 0x01
#define MII_READ_ 0x00 /* ~of MII Write bit */
#define MII_DATA 0x118
#define FLOW 0x11C
#define VLAN1 0x120
#define COE_CR 0x130
#define Tx_COE_EN_ 0x00010000
#define Rx_COE_EN_ 0x00000001
/* Vendor-specific PHY Definitions */
#define PHY_INT_SRC 29
#define PHY_INT_MASK 30
#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040)
#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010)
#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \
PHY_INT_MASK_LINK_DOWN_)
/* USB Vendor Requests */
#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0
#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1
/* Some extra defines */
#define HS_USB_PKT_SIZE 512
#define FS_USB_PKT_SIZE 64
#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE)
#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE)
#define DEFAULT_BULK_IN_DELAY 0x00002000
#define MAX_SINGLE_PACKET_SIZE 2048
#define EEPROM_MAC_OFFSET 0x01
#define SMSC95XX_INTERNAL_PHY_ID 1
#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
/* local defines */
#define SMSC95XX_BASE_NAME "sms"
#define USB_CTRL_SET_TIMEOUT 5000
#define USB_CTRL_GET_TIMEOUT 5000
#define USB_BULK_SEND_TIMEOUT 5000
#define USB_BULK_RECV_TIMEOUT 5000
#define AX_RX_URB_SIZE 2048
#define PHY_CONNECT_TIMEOUT 5000
#define TURBO_MODE
/* local vars */
static int curr_eth_dev; /* index for name of next device detected */
/*
* Smsc95xx infrastructure commands
*/
static int smsc95xx_write_reg(struct ueth_data *dev, u32 index, u32 data)
{
int len;
ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
cpu_to_le32s(&data);
tmpbuf[0] = data;
len = usb_control_msg(dev->pusb_dev, usb_sndctrlpipe(dev->pusb_dev, 0),
USB_VENDOR_REQUEST_WRITE_REGISTER,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
00, index, tmpbuf, sizeof(data), USB_CTRL_SET_TIMEOUT);
if (len != sizeof(data)) {
debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d",
index, data, len);
return -1;
}
return 0;
}
static int smsc95xx_read_reg(struct ueth_data *dev, u32 index, u32 *data)
{
int len;
ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
len = usb_control_msg(dev->pusb_dev, usb_rcvctrlpipe(dev->pusb_dev, 0),
USB_VENDOR_REQUEST_READ_REGISTER,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
00, index, tmpbuf, sizeof(data), USB_CTRL_GET_TIMEOUT);
*data = tmpbuf[0];
if (len != sizeof(data)) {
debug("smsc95xx_read_reg failed: index=%d, len=%d",
index, len);
return -1;
}
le32_to_cpus(data);
return 0;
}
/* Loop until the read is completed with timeout */
static int smsc95xx_phy_wait_not_busy(struct ueth_data *dev)
{
unsigned long start_time = get_timer(0);
u32 val;
do {
smsc95xx_read_reg(dev, MII_ADDR, &val);
if (!(val & MII_BUSY_))
return 0;
} while (get_timer(start_time) < 1 * 1000 * 1000);
return -1;
}
static int smsc95xx_mdio_read(struct ueth_data *dev, int phy_id, int idx)
{
u32 val, addr;
/* confirm MII not busy */
if (smsc95xx_phy_wait_not_busy(dev)) {
debug("MII is busy in smsc95xx_mdio_read\n");
return -1;
}
/* set the address, index & direction (read from PHY) */
addr = (phy_id << 11) | (idx << 6) | MII_READ_;
smsc95xx_write_reg(dev, MII_ADDR, addr);
if (smsc95xx_phy_wait_not_busy(dev)) {
debug("Timed out reading MII reg %02X\n", idx);
return -1;
}
smsc95xx_read_reg(dev, MII_DATA, &val);
return (u16)(val & 0xFFFF);
}
static void smsc95xx_mdio_write(struct ueth_data *dev, int phy_id, int idx,
int regval)
{
u32 val, addr;
/* confirm MII not busy */
if (smsc95xx_phy_wait_not_busy(dev)) {
debug("MII is busy in smsc95xx_mdio_write\n");
return;
}
val = regval;
smsc95xx_write_reg(dev, MII_DATA, val);
/* set the address, index & direction (write to PHY) */
addr = (phy_id << 11) | (idx << 6) | MII_WRITE_;
smsc95xx_write_reg(dev, MII_ADDR, addr);
if (smsc95xx_phy_wait_not_busy(dev))
debug("Timed out writing MII reg %02X\n", idx);
}
static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev)
{
unsigned long start_time = get_timer(0);
u32 val;
do {
smsc95xx_read_reg(dev, E2P_CMD, &val);
if (!(val & E2P_CMD_LOADED_)) {
debug("No EEPROM present\n");
return -1;
}
if (!(val & E2P_CMD_BUSY_))
return 0;
udelay(40);
} while (get_timer(start_time) < 1 * 1000 * 1000);
debug("EEPROM is busy\n");
return -1;
}
static int smsc95xx_wait_eeprom(struct ueth_data *dev)
{
unsigned long start_time = get_timer(0);
u32 val;
do {
smsc95xx_read_reg(dev, E2P_CMD, &val);
if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_))
break;
udelay(40);
} while (get_timer(start_time) < 1 * 1000 * 1000);
if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) {
debug("EEPROM read operation timeout\n");
return -1;
}
return 0;
}
static int smsc95xx_read_eeprom(struct ueth_data *dev, u32 offset, u32 length,
u8 *data)
{
u32 val;
int i, ret;
ret = smsc95xx_eeprom_confirm_not_busy(dev);
if (ret)
return ret;
for (i = 0; i < length; i++) {
val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_);
smsc95xx_write_reg(dev, E2P_CMD, val);
ret = smsc95xx_wait_eeprom(dev);
if (ret < 0)
return ret;
smsc95xx_read_reg(dev, E2P_DATA, &val);
data[i] = val & 0xFF;
offset++;
}
return 0;
}
/*
* mii_nway_restart - restart NWay (autonegotiation) for this interface
*
* Returns 0 on success, negative on error.
*/
static int mii_nway_restart(struct ueth_data *dev)
{
int bmcr;
int r = -1;
/* if autoneg is off, it's an error */
bmcr = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMCR);
if (bmcr & BMCR_ANENABLE) {
bmcr |= BMCR_ANRESTART;
smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, bmcr);
r = 0;
}
return r;
}
static int smsc95xx_phy_initialize(struct ueth_data *dev)
{
smsc95xx_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET);
smsc95xx_mdio_write(dev, dev->phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP |
ADVERTISE_PAUSE_ASYM);
/* read to clear */
smsc95xx_mdio_read(dev, dev->phy_id, PHY_INT_SRC);
smsc95xx_mdio_write(dev, dev->phy_id, PHY_INT_MASK,
PHY_INT_MASK_DEFAULT_);
mii_nway_restart(dev);
debug("phy initialised succesfully\n");
return 0;
}
static int smsc95xx_init_mac_address(struct eth_device *eth,
struct ueth_data *dev)
{
/* try reading mac address from EEPROM */
if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN,
eth->enetaddr) == 0) {
if (is_valid_ether_addr(eth->enetaddr)) {
/* eeprom values are valid so use them */
debug("MAC address read from EEPROM\n");
return 0;
}
}
/*
* No eeprom, or eeprom values are invalid. Generating a random MAC
* address is not safe. Just return an error.
*/
return -1;
}
static int smsc95xx_write_hwaddr(struct eth_device *eth)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
u32 addr_lo = __get_unaligned_le32(&eth->enetaddr[0]);
u32 addr_hi = __get_unaligned_le16(&eth->enetaddr[4]);
int ret;
/* set hardware address */
debug("** %s()\n", __func__);
ret = smsc95xx_write_reg(dev, ADDRL, addr_lo);
if (ret < 0)
return ret;
ret = smsc95xx_write_reg(dev, ADDRH, addr_hi);
if (ret < 0)
return ret;
debug("MAC %pM\n", eth->enetaddr);
dev->have_hwaddr = 1;
return 0;
}
/* Enable or disable Tx & Rx checksum offload engines */
static int smsc95xx_set_csums(struct ueth_data *dev,
int use_tx_csum, int use_rx_csum)
{
u32 read_buf;
int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
if (ret < 0)
return ret;
if (use_tx_csum)
read_buf |= Tx_COE_EN_;
else
read_buf &= ~Tx_COE_EN_;
if (use_rx_csum)
read_buf |= Rx_COE_EN_;
else
read_buf &= ~Rx_COE_EN_;
ret = smsc95xx_write_reg(dev, COE_CR, read_buf);
if (ret < 0)
return ret;
debug("COE_CR = 0x%08x\n", read_buf);
return 0;
}
static void smsc95xx_set_multicast(struct ueth_data *dev)
{
/* No multicast in u-boot */
dev->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
}
/* starts the TX path */
static void smsc95xx_start_tx_path(struct ueth_data *dev)
{
u32 reg_val;
/* Enable Tx at MAC */
dev->mac_cr |= MAC_CR_TXEN_;
smsc95xx_write_reg(dev, MAC_CR, dev->mac_cr);
/* Enable Tx at SCSRs */
reg_val = TX_CFG_ON_;
smsc95xx_write_reg(dev, TX_CFG, reg_val);
}
/* Starts the Receive path */
static void smsc95xx_start_rx_path(struct ueth_data *dev)
{
dev->mac_cr |= MAC_CR_RXEN_;
smsc95xx_write_reg(dev, MAC_CR, dev->mac_cr);
}
/*
* Smsc95xx callbacks
*/
static int smsc95xx_init(struct eth_device *eth, bd_t *bd)
{
int ret;
u32 write_buf;
u32 read_buf;
u32 burst_cap;
int timeout;
struct ueth_data *dev = (struct ueth_data *)eth->priv;
#define TIMEOUT_RESOLUTION 50 /* ms */
int link_detected;
debug("** %s()\n", __func__);
dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */
write_buf = HW_CFG_LRST_;
ret = smsc95xx_write_reg(dev, HW_CFG, write_buf);
if (ret < 0)
return ret;
timeout = 0;
do {
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
udelay(10 * 1000);
timeout++;
} while ((read_buf & HW_CFG_LRST_) && (timeout < 100));
if (timeout >= 100) {
debug("timeout waiting for completion of Lite Reset\n");
return -1;
}
write_buf = PM_CTL_PHY_RST_;
ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf);
if (ret < 0)
return ret;
timeout = 0;
do {
ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf);
if (ret < 0)
return ret;
udelay(10 * 1000);
timeout++;
} while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100));
if (timeout >= 100) {
debug("timeout waiting for PHY Reset\n");
return -1;
}
if (!dev->have_hwaddr && smsc95xx_init_mac_address(eth, dev) == 0)
dev->have_hwaddr = 1;
if (!dev->have_hwaddr) {
puts("Error: SMSC95xx: No MAC address set - set usbethaddr\n");
return -1;
}
if (smsc95xx_write_hwaddr(eth) < 0)
return -1;
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from HW_CFG : 0x%08x\n", read_buf);
read_buf |= HW_CFG_BIR_;
ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from HW_CFG after writing "
"HW_CFG_BIR_: 0x%08x\n", read_buf);
#ifdef TURBO_MODE
if (dev->pusb_dev->speed == USB_SPEED_HIGH) {
burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
} else {
burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
}
#else
burst_cap = 0;
dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
#endif
debug("rx_urb_size=%ld\n", (ulong)dev->rx_urb_size);
ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf);
read_buf = DEFAULT_BULK_IN_DELAY;
ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from BULK_IN_DLY after writing: "
"0x%08x\n", read_buf);
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from HW_CFG: 0x%08x\n", read_buf);
#ifdef TURBO_MODE
read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_);
#endif
read_buf &= ~HW_CFG_RXDOFF_;
#define NET_IP_ALIGN 0
read_buf |= NET_IP_ALIGN << 9;
ret = smsc95xx_write_reg(dev, HW_CFG, read_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf);
if (ret < 0)
return ret;
debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf);
write_buf = 0xFFFFFFFF;
ret = smsc95xx_write_reg(dev, INT_STS, write_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, ID_REV, &read_buf);
if (ret < 0)
return ret;
debug("ID_REV = 0x%08x\n", read_buf);
/* Init Tx */
write_buf = 0;
ret = smsc95xx_write_reg(dev, FLOW, write_buf);
if (ret < 0)
return ret;
read_buf = AFC_CFG_DEFAULT;
ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf);
if (ret < 0)
return ret;
ret = smsc95xx_read_reg(dev, MAC_CR, &dev->mac_cr);
if (ret < 0)
return ret;
/* Init Rx. Set Vlan */
write_buf = (u32)ETH_P_8021Q;
ret = smsc95xx_write_reg(dev, VLAN1, write_buf);
if (ret < 0)
return ret;
/* Disable checksum offload engines */
ret = smsc95xx_set_csums(dev, 0, 0);
if (ret < 0) {
debug("Failed to set csum offload: %d\n", ret);
return ret;
}
smsc95xx_set_multicast(dev);
if (smsc95xx_phy_initialize(dev) < 0)
return -1;
ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf);
if (ret < 0)
return ret;
/* enable PHY interrupts */
read_buf |= INT_EP_CTL_PHY_INT_;
ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf);
if (ret < 0)
return ret;
smsc95xx_start_tx_path(dev);
smsc95xx_start_rx_path(dev);
timeout = 0;
do {
link_detected = smsc95xx_mdio_read(dev, dev->phy_id, MII_BMSR)
& BMSR_LSTATUS;
if (!link_detected) {
if (timeout == 0)
printf("Waiting for Ethernet connection... ");
udelay(TIMEOUT_RESOLUTION * 1000);
timeout += TIMEOUT_RESOLUTION;
}
} while (!link_detected && timeout < PHY_CONNECT_TIMEOUT);
if (link_detected) {
if (timeout != 0)
printf("done.\n");
} else {
printf("unable to connect.\n");
return -1;
}
return 0;
}
static int smsc95xx_send(struct eth_device *eth, void* packet, int length)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
int err;
int actual_len;
u32 tx_cmd_a;
u32 tx_cmd_b;
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg,
PKTSIZE + sizeof(tx_cmd_a) + sizeof(tx_cmd_b));
debug("** %s(), len %d, buf %#x\n", __func__, length, (int)msg);
if (length > PKTSIZE)
return -1;
tx_cmd_a = (u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
tx_cmd_b = (u32)length;
cpu_to_le32s(&tx_cmd_a);
cpu_to_le32s(&tx_cmd_b);
/* prepend cmd_a and cmd_b */
memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a));
memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b));
memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet,
length);
err = usb_bulk_msg(dev->pusb_dev,
usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
(void *)msg,
length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
&actual_len,
USB_BULK_SEND_TIMEOUT);
debug("Tx: len = %u, actual = %u, err = %d\n",
length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
actual_len, err);
return err;
}
static int smsc95xx_recv(struct eth_device *eth)
{
struct ueth_data *dev = (struct ueth_data *)eth->priv;
DEFINE_CACHE_ALIGN_BUFFER(unsigned char, recv_buf, AX_RX_URB_SIZE);
unsigned char *buf_ptr;
int err;
int actual_len;
u32 packet_len;
int cur_buf_align;
debug("** %s()\n", __func__);
err = usb_bulk_msg(dev->pusb_dev,
usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
(void *)recv_buf,
AX_RX_URB_SIZE,
&actual_len,
USB_BULK_RECV_TIMEOUT);
debug("Rx: len = %u, actual = %u, err = %d\n", AX_RX_URB_SIZE,
actual_len, err);
if (err != 0) {
debug("Rx: failed to receive\n");
return -1;
}
if (actual_len > AX_RX_URB_SIZE) {
debug("Rx: received too many bytes %d\n", actual_len);
return -1;
}
buf_ptr = recv_buf;
while (actual_len > 0) {
/*
* 1st 4 bytes contain the length of the actual data plus error
* info. Extract data length.
*/
if (actual_len < sizeof(packet_len)) {
debug("Rx: incomplete packet length\n");
return -1;
}
memcpy(&packet_len, buf_ptr, sizeof(packet_len));
le32_to_cpus(&packet_len);
if (packet_len & RX_STS_ES_) {
debug("Rx: Error header=%#x", packet_len);
return -1;
}
packet_len = ((packet_len & RX_STS_FL_) >> 16);
if (packet_len > actual_len - sizeof(packet_len)) {
debug("Rx: too large packet: %d\n", packet_len);
return -1;
}
/* Notify net stack */
NetReceive(buf_ptr + sizeof(packet_len), packet_len - 4);
/* Adjust for next iteration */
actual_len -= sizeof(packet_len) + packet_len;
buf_ptr += sizeof(packet_len) + packet_len;
cur_buf_align = (int)buf_ptr - (int)recv_buf;
if (cur_buf_align & 0x03) {
int align = 4 - (cur_buf_align & 0x03);
actual_len -= align;
buf_ptr += align;
}
}
return err;
}
static void smsc95xx_halt(struct eth_device *eth)
{
debug("** %s()\n", __func__);
}
/*
* SMSC probing functions
*/
void smsc95xx_eth_before_probe(void)
{
curr_eth_dev = 0;
}
struct smsc95xx_dongle {
unsigned short vendor;
unsigned short product;
};
static const struct smsc95xx_dongle smsc95xx_dongles[] = {
{ 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */
{ 0x0424, 0x9500 }, /* LAN9500 Ethernet */
{ 0x0000, 0x0000 } /* END - Do not remove */
};
/* Probe to see if a new device is actually an SMSC device */
int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum,
struct ueth_data *ss)
{
struct usb_interface *iface;
struct usb_interface_descriptor *iface_desc;
int i;
/* let's examine the device now */
iface = &dev->config.if_desc[ifnum];
iface_desc = &dev->config.if_desc[ifnum].desc;
for (i = 0; smsc95xx_dongles[i].vendor != 0; i++) {
if (dev->descriptor.idVendor == smsc95xx_dongles[i].vendor &&
dev->descriptor.idProduct == smsc95xx_dongles[i].product)
/* Found a supported dongle */
break;
}
if (smsc95xx_dongles[i].vendor == 0)
return 0;
/* At this point, we know we've got a live one */
debug("\n\nUSB Ethernet device detected\n");
memset(ss, '\0', sizeof(struct ueth_data));
/* Initialize the ueth_data structure with some useful info */
ss->ifnum = ifnum;
ss->pusb_dev = dev;
ss->subclass = iface_desc->bInterfaceSubClass;
ss->protocol = iface_desc->bInterfaceProtocol;
/*
* We are expecting a minimum of 3 endpoints - in, out (bulk), and int.
* We will ignore any others.
*/
for (i = 0; i < iface_desc->bNumEndpoints; i++) {
/* is it an BULK endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
ss->ep_in =
iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
else
ss->ep_out =
iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
}
/* is it an interrupt endpoint? */
if ((iface->ep_desc[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
ss->ep_int = iface->ep_desc[i].bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->irqinterval = iface->ep_desc[i].bInterval;
}
}
debug("Endpoints In %d Out %d Int %d\n",
ss->ep_in, ss->ep_out, ss->ep_int);
/* Do some basic sanity checks, and bail if we find a problem */
if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
!ss->ep_in || !ss->ep_out || !ss->ep_int) {
debug("Problems with device\n");
return 0;
}
dev->privptr = (void *)ss;
return 1;
}
int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss,
struct eth_device *eth)
{
debug("** %s()\n", __func__);
if (!eth) {
debug("%s: missing parameter.\n", __func__);
return 0;
}
sprintf(eth->name, "%s%d", SMSC95XX_BASE_NAME, curr_eth_dev++);
eth->init = smsc95xx_init;
eth->send = smsc95xx_send;
eth->recv = smsc95xx_recv;
eth->halt = smsc95xx_halt;
eth->write_hwaddr = smsc95xx_write_hwaddr;
eth->priv = ss;
return 1;
}

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <usb.h>
#include "usb_ether.h"
typedef void (*usb_eth_before_probe)(void);
typedef int (*usb_eth_probe)(struct usb_device *dev, unsigned int ifnum,
struct ueth_data *ss);
typedef int (*usb_eth_get_info)(struct usb_device *dev, struct ueth_data *ss,
struct eth_device *dev_desc);
struct usb_eth_prob_dev {
usb_eth_before_probe before_probe; /* optional */
usb_eth_probe probe;
usb_eth_get_info get_info;
};
/* driver functions go here, each bracketed by #ifdef CONFIG_USB_ETHER_xxx */
static const struct usb_eth_prob_dev prob_dev[] = {
#ifdef CONFIG_USB_ETHER_ASIX
{
.before_probe = asix_eth_before_probe,
.probe = asix_eth_probe,
.get_info = asix_eth_get_info,
},
#endif
#ifdef CONFIG_USB_ETHER_SMSC95XX
{
.before_probe = smsc95xx_eth_before_probe,
.probe = smsc95xx_eth_probe,
.get_info = smsc95xx_eth_get_info,
},
#endif
{ }, /* END */
};
static int usb_max_eth_dev; /* number of highest available usb eth device */
static struct ueth_data usb_eth[USB_MAX_ETH_DEV];
/*******************************************************************************
* tell if current ethernet device is a usb dongle
*/
int is_eth_dev_on_usb_host(void)
{
int i;
struct eth_device *dev = eth_get_dev();
if (dev) {
for (i = 0; i < usb_max_eth_dev; i++)
if (&usb_eth[i].eth_dev == dev)
return 1;
}
return 0;
}
/*
* Given a USB device, ask each driver if it can support it, and attach it
* to the first driver that says 'yes'
*/
static void probe_valid_drivers(struct usb_device *dev)
{
struct eth_device *eth;
int j;
for (j = 0; prob_dev[j].probe && prob_dev[j].get_info; j++) {
if (!prob_dev[j].probe(dev, 0, &usb_eth[usb_max_eth_dev]))
continue;
/*
* ok, it is a supported eth device. Get info and fill it in
*/
eth = &usb_eth[usb_max_eth_dev].eth_dev;
if (prob_dev[j].get_info(dev,
&usb_eth[usb_max_eth_dev],
eth)) {
/* found proper driver */
/* register with networking stack */
usb_max_eth_dev++;
/*
* usb_max_eth_dev must be incremented prior to this
* call since eth_current_changed (internally called)
* relies on it
*/
eth_register(eth);
if (eth_write_hwaddr(eth, "usbeth",
usb_max_eth_dev - 1))
puts("Warning: failed to set MAC address\n");
break;
}
}
}
/*******************************************************************************
* scan the usb and reports device info
* to the user if mode = 1
* returns current device or -1 if no
*/
int usb_host_eth_scan(int mode)
{
int i, old_async;
struct usb_device *dev;
if (mode == 1)
printf(" scanning usb for ethernet devices... ");
old_async = usb_disable_asynch(1); /* asynch transfer not allowed */
/* unregister a previously detected device */
for (i = 0; i < usb_max_eth_dev; i++)
eth_unregister(&usb_eth[i].eth_dev);
memset(usb_eth, 0, sizeof(usb_eth));
for (i = 0; prob_dev[i].probe; i++) {
if (prob_dev[i].before_probe)
prob_dev[i].before_probe();
}
usb_max_eth_dev = 0;
for (i = 0; i < USB_MAX_DEVICE; i++) {
dev = usb_get_dev_index(i); /* get device */
debug("i=%d\n", i);
if (dev == NULL)
break; /* no more devices available */
/* find valid usb_ether driver for this device, if any */
probe_valid_drivers(dev);
/* check limit */
if (usb_max_eth_dev == USB_MAX_ETH_DEV) {
printf("max USB Ethernet Device reached: %d stopping\n",
usb_max_eth_dev);
break;
}
} /* for */
usb_disable_asynch(old_async); /* restore asynch value */
printf("%d Ethernet Device(s) found\n", usb_max_eth_dev);
if (usb_max_eth_dev > 0)
return 0;
return -1;
}

View file

@ -0,0 +1,66 @@
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# 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., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB := $(obj)libusb_gadget.o
# new USB gadget layer dependencies
ifdef CONFIG_USB_GADGET
COBJS-y += epautoconf.o config.o usbstring.o
COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o
endif
ifdef CONFIG_USB_ETHER
COBJS-y += ether.o epautoconf.o config.o usbstring.o
COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o
COBJS-$(CONFIG_MV_UDC) += mv_udc.o
else
# Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
ifdef CONFIG_USB_DEVICE
COBJS-y += core.o
COBJS-y += ep0.o
COBJS-$(CONFIG_DW_UDC) += designware_udc.o
COBJS-$(CONFIG_OMAP1510) += omap1510_udc.o
COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o
COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o
COBJS-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o
endif
endif
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
#########################################################################

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,118 @@
/*
* usb/gadget/config.c -- simplify building config descriptors
*
* Copyright (C) 2003 David Brownell
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
* Remy Bohmer <linux@bohmer.net>
*/
#include <common.h>
#include <asm/errno.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/**
* usb_descriptor_fillbuf - fill buffer with descriptors
* @buf: Buffer to be filled
* @buflen: Size of buf
* @src: Array of descriptor pointers, terminated by null pointer.
*
* Copies descriptors into the buffer, returning the length or a
* negative error code if they can't all be copied. Useful when
* assembling descriptors for an associated set of interfaces used
* as part of configuring a composite device; or in other cases where
* sets of descriptors need to be marshaled.
*/
int
usb_descriptor_fillbuf(void *buf, unsigned buflen,
const struct usb_descriptor_header **src)
{
u8 *dest = buf;
if (!src)
return -EINVAL;
/* fill buffer from src[] until null descriptor ptr */
for (; NULL != *src; src++) {
unsigned len = (*src)->bLength;
if (len > buflen)
return -EINVAL;
memcpy(dest, *src, len);
buflen -= len;
dest += len;
}
return dest - (u8 *)buf;
}
/**
* usb_gadget_config_buf - builts a complete configuration descriptor
* @config: Header for the descriptor, including characteristics such
* as power requirements and number of interfaces.
* @desc: Null-terminated vector of pointers to the descriptors (interface,
* endpoint, etc) defining all functions in this device configuration.
* @buf: Buffer for the resulting configuration descriptor.
* @length: Length of buffer. If this is not big enough to hold the
* entire configuration descriptor, an error code will be returned.
*
* This copies descriptors into the response buffer, building a descriptor
* for that configuration. It returns the buffer length or a negative
* status code. The config.wTotalLength field is set to match the length
* of the result, but other descriptor fields (including power usage and
* interface count) must be set by the caller.
*
* Gadget drivers could use this when constructing a config descriptor
* in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
* resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
*/
int usb_gadget_config_buf(
const struct usb_config_descriptor *config,
void *buf,
unsigned length,
const struct usb_descriptor_header **desc
)
{
struct usb_config_descriptor *cp = buf;
int len;
/* config descriptor first */
if (length < USB_DT_CONFIG_SIZE || !desc)
return -EINVAL;
*cp = *config;
/* then interface/endpoint/class/vendor/... */
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf,
length - USB_DT_CONFIG_SIZE, desc);
if (len < 0)
return len;
len += USB_DT_CONFIG_SIZE;
if (len > 0xffff)
return -EINVAL;
/* patch up the config descriptor */
cp->bLength = USB_DT_CONFIG_SIZE;
cp->bDescriptorType = USB_DT_CONFIG;
cp->wTotalLength = cpu_to_le16(len);
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
return len;
}

View file

@ -0,0 +1,682 @@
/*
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
*
* Based on
* linux/drivers/usbd/usbd.c.c - USB Device Core Layer
*
* Copyright (c) 2000, 2001, 2002 Lineo
* Copyright (c) 2001 Hewlett Packard
*
* By:
* Stuart Lynne <sl@lineo.com>,
* Tom Rushworth <tbr@lineo.com>,
* Bruce Balden <balden@lineo.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.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <malloc.h>
#include <usbdevice.h>
#define MAX_INTERFACES 2
int maxstrings = 20;
/* Global variables ************************************************************************** */
struct usb_string_descriptor **usb_strings;
int usb_devices;
extern struct usb_function_driver ep0_driver;
int registered_functions;
int registered_devices;
char *usbd_device_events[] = {
"DEVICE_UNKNOWN",
"DEVICE_INIT",
"DEVICE_CREATE",
"DEVICE_HUB_CONFIGURED",
"DEVICE_RESET",
"DEVICE_ADDRESS_ASSIGNED",
"DEVICE_CONFIGURED",
"DEVICE_SET_INTERFACE",
"DEVICE_SET_FEATURE",
"DEVICE_CLEAR_FEATURE",
"DEVICE_DE_CONFIGURED",
"DEVICE_BUS_INACTIVE",
"DEVICE_BUS_ACTIVITY",
"DEVICE_POWER_INTERRUPTION",
"DEVICE_HUB_RESET",
"DEVICE_DESTROY",
"DEVICE_FUNCTION_PRIVATE",
};
char *usbd_device_states[] = {
"STATE_INIT",
"STATE_CREATED",
"STATE_ATTACHED",
"STATE_POWERED",
"STATE_DEFAULT",
"STATE_ADDRESSED",
"STATE_CONFIGURED",
"STATE_UNKNOWN",
};
char *usbd_device_requests[] = {
"GET STATUS", /* 0 */
"CLEAR FEATURE", /* 1 */
"RESERVED", /* 2 */
"SET FEATURE", /* 3 */
"RESERVED", /* 4 */
"SET ADDRESS", /* 5 */
"GET DESCRIPTOR", /* 6 */
"SET DESCRIPTOR", /* 7 */
"GET CONFIGURATION", /* 8 */
"SET CONFIGURATION", /* 9 */
"GET INTERFACE", /* 10 */
"SET INTERFACE", /* 11 */
"SYNC FRAME", /* 12 */
};
char *usbd_device_descriptors[] = {
"UNKNOWN", /* 0 */
"DEVICE", /* 1 */
"CONFIG", /* 2 */
"STRING", /* 3 */
"INTERFACE", /* 4 */
"ENDPOINT", /* 5 */
"DEVICE QUALIFIER", /* 6 */
"OTHER SPEED", /* 7 */
"INTERFACE POWER", /* 8 */
};
char *usbd_device_status[] = {
"USBD_OPENING",
"USBD_OK",
"USBD_SUSPENDED",
"USBD_CLOSING",
};
/* Descriptor support functions ************************************************************** */
/**
* usbd_get_string - find and return a string descriptor
* @index: string index to return
*
* Find an indexed string and return a pointer to a it.
*/
struct usb_string_descriptor *usbd_get_string (__u8 index)
{
if (index >= maxstrings) {
return NULL;
}
return usb_strings[index];
}
/* Access to device descriptor functions ***************************************************** */
/* *
* usbd_device_configuration_instance - find a configuration instance for this device
* @device:
* @configuration: index to configuration, 0 - N-1
*
* Get specifed device configuration. Index should be bConfigurationValue-1.
*/
static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device,
unsigned int port, unsigned int configuration)
{
if (configuration >= device->configurations)
return NULL;
return device->configuration_instance_array + configuration;
}
/* *
* usbd_device_interface_instance
* @device:
* @configuration: index to configuration, 0 - N-1
* @interface: index to interface
*
* Return the specified interface descriptor for the specified device.
*/
struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface)
{
struct usb_configuration_instance *configuration_instance;
if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) {
return NULL;
}
if (interface >= configuration_instance->interfaces) {
return NULL;
}
return configuration_instance->interface_instance_array + interface;
}
/* *
* usbd_device_alternate_descriptor_list
* @device:
* @configuration: index to configuration, 0 - N-1
* @interface: index to interface
* @alternate: alternate setting
*
* Return the specified alternate descriptor for the specified device.
*/
struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate)
{
struct usb_interface_instance *interface_instance;
if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) {
return NULL;
}
if (alternate >= interface_instance->alternates) {
return NULL;
}
return interface_instance->alternates_instance_array + alternate;
}
/* *
* usbd_device_device_descriptor
* @device: which device
* @configuration: index to configuration, 0 - N-1
* @port: which port
*
* Return the specified configuration descriptor for the specified device.
*/
struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port)
{
return (device->device_descriptor);
}
/**
* usbd_device_configuration_descriptor
* @device: which device
* @port: which port
* @configuration: index to configuration, 0 - N-1
*
* Return the specified configuration descriptor for the specified device.
*/
struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct
usb_device_instance
*device, int port, int configuration)
{
struct usb_configuration_instance *configuration_instance;
if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) {
return NULL;
}
return (configuration_instance->configuration_descriptor);
}
/**
* usbd_device_interface_descriptor
* @device: which device
* @port: which port
* @configuration: index to configuration, 0 - N-1
* @interface: index to interface
* @alternate: alternate setting
*
* Return the specified interface descriptor for the specified device.
*/
struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance
*device, int port, int configuration, int interface, int alternate)
{
struct usb_interface_instance *interface_instance;
if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) {
return NULL;
}
if ((alternate < 0) || (alternate >= interface_instance->alternates)) {
return NULL;
}
return (interface_instance->alternates_instance_array[alternate].interface_descriptor);
}
/**
* usbd_device_endpoint_descriptor_index
* @device: which device
* @port: which port
* @configuration: index to configuration, 0 - N-1
* @interface: index to interface
* @alternate: index setting
* @index: which index
*
* Return the specified endpoint descriptor for the specified device.
*/
struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance
*device, int port, int configuration, int interface, int alternate, int index)
{
struct usb_alternate_instance *alternate_instance;
if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
return NULL;
}
if (index >= alternate_instance->endpoints) {
return NULL;
}
return *(alternate_instance->endpoints_descriptor_array + index);
}
/**
* usbd_device_endpoint_transfersize
* @device: which device
* @port: which port
* @configuration: index to configuration, 0 - N-1
* @interface: index to interface
* @index: which index
*
* Return the specified endpoint transfer size;
*/
int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index)
{
struct usb_alternate_instance *alternate_instance;
if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) {
return 0;
}
if (index >= alternate_instance->endpoints) {
return 0;
}
return *(alternate_instance->endpoint_transfersize_array + index);
}
/**
* usbd_device_endpoint_descriptor
* @device: which device
* @port: which port
* @configuration: index to configuration, 0 - N-1
* @interface: index to interface
* @alternate: alternate setting
* @endpoint: which endpoint
*
* Return the specified endpoint descriptor for the specified device.
*/
struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint)
{
struct usb_endpoint_descriptor *endpoint_descriptor;
int i;
for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) {
if (endpoint_descriptor->bEndpointAddress == endpoint) {
return endpoint_descriptor;
}
}
return NULL;
}
/**
* usbd_endpoint_halted
* @device: point to struct usb_device_instance
* @endpoint: endpoint to check
*
* Return non-zero if endpoint is halted.
*/
int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint)
{
return (device->status == USB_STATUS_HALT);
}
/**
* usbd_rcv_complete - complete a receive
* @endpoint:
* @len:
* @urb_bad:
*
* Called from rcv interrupt to complete.
*/
void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad)
{
if (endpoint) {
struct urb *rcv_urb;
/*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */
/* if we had an urb then update actual_length, dispatch if neccessary */
if ((rcv_urb = endpoint->rcv_urb)) {
/*usbdbg("actual: %d buffer: %d\n", */
/*rcv_urb->actual_length, rcv_urb->buffer_length); */
/* check the urb is ok, are we adding data less than the packetsize */
if (!urb_bad && (len <= endpoint->rcv_packetSize)) {
/*usbdbg("updating actual_length by %d\n",len); */
/* increment the received data size */
rcv_urb->actual_length += len;
} else {
usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n",
rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad);
rcv_urb->actual_length = 0;
rcv_urb->status = RECV_ERROR;
}
} else {
usberr("no rcv_urb!");
}
} else {
usberr("no endpoint!");
}
}
/**
* usbd_tx_complete - complete a transmit
* @endpoint:
* @resetart:
*
* Called from tx interrupt to complete.
*/
void usbd_tx_complete (struct usb_endpoint_instance *endpoint)
{
if (endpoint) {
struct urb *tx_urb;
/* if we have a tx_urb advance or reset, finish if complete */
if ((tx_urb = endpoint->tx_urb)) {
int sent = endpoint->last;
endpoint->sent += sent;
endpoint->last -= sent;
if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) {
tx_urb->actual_length = 0;
endpoint->sent = 0;
endpoint->last = 0;
/* Remove from active, save for re-use */
urb_detach(tx_urb);
urb_append(&endpoint->done, tx_urb);
/*usbdbg("done->next %p, tx_urb %p, done %p", */
/* endpoint->done.next, tx_urb, &endpoint->done); */
endpoint->tx_urb = first_urb_detached(&endpoint->tx);
if( endpoint->tx_urb ) {
endpoint->tx_queue--;
usbdbg("got urb from tx list");
}
if( !endpoint->tx_urb ) {
/*usbdbg("taking urb from done list"); */
endpoint->tx_urb = first_urb_detached(&endpoint->done);
}
if( !endpoint->tx_urb ) {
usbdbg("allocating new urb for tx_urb");
endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint);
}
}
}
}
}
/* URB linked list functions ***************************************************** */
/*
* Initialize an urb_link to be a single element list.
* If the urb_link is being used as a distinguished list head
* the list is empty when the head is the only link in the list.
*/
void urb_link_init (urb_link * ul)
{
if (ul) {
ul->prev = ul->next = ul;
}
}
/*
* Detach an urb_link from a list, and set it
* up as a single element list, so no dangling
* pointers can be followed, and so it can be
* joined to another list if so desired.
*/
void urb_detach (struct urb *urb)
{
if (urb) {
urb_link *ul = &urb->link;
ul->next->prev = ul->prev;
ul->prev->next = ul->next;
urb_link_init (ul);
}
}
/*
* Return the first urb_link in a list with a distinguished
* head "hd", or NULL if the list is empty. This will also
* work as a predicate, returning NULL if empty, and non-NULL
* otherwise.
*/
urb_link *first_urb_link (urb_link * hd)
{
urb_link *nx;
if (NULL != hd && NULL != (nx = hd->next) && nx != hd) {
/* There is at least one element in the list */
/* (besides the distinguished head). */
return (nx);
}
/* The list is empty */
return (NULL);
}
/*
* Return the first urb in a list with a distinguished
* head "hd", or NULL if the list is empty.
*/
struct urb *first_urb (urb_link * hd)
{
urb_link *nx;
if (NULL == (nx = first_urb_link (hd))) {
/* The list is empty */
return (NULL);
}
return (p2surround (struct urb, link, nx));
}
/*
* Detach and return the first urb in a list with a distinguished
* head "hd", or NULL if the list is empty.
*
*/
struct urb *first_urb_detached (urb_link * hd)
{
struct urb *urb;
if ((urb = first_urb (hd))) {
urb_detach (urb);
}
return urb;
}
/*
* Append an urb_link (or a whole list of
* urb_links) to the tail of another list
* of urb_links.
*/
void urb_append (urb_link * hd, struct urb *urb)
{
if (hd && urb) {
urb_link *new = &urb->link;
/* This allows the new urb to be a list of urbs, */
/* with new pointing at the first, but the link */
/* must be initialized. */
/* Order is important here... */
urb_link *pul = hd->prev;
new->prev->next = hd;
hd->prev = new->prev;
new->prev = pul;
pul->next = new;
}
}
/* URB create/destroy functions ***************************************************** */
/**
* usbd_alloc_urb - allocate an URB appropriate for specified endpoint
* @device: device instance
* @endpoint: endpoint
*
* Allocate an urb structure. The usb device urb structure is used to
* contain all data associated with a transfer, including a setup packet for
* control transfers.
*
* NOTE: endpoint_address MUST contain a direction flag.
*/
struct urb *usbd_alloc_urb (struct usb_device_instance *device,
struct usb_endpoint_instance *endpoint)
{
struct urb *urb;
if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) {
usberr (" F A T A L: malloc(%zu) FAILED!!!!",
sizeof (struct urb));
return NULL;
}
/* Fill in known fields */
memset (urb, 0, sizeof (struct urb));
urb->endpoint = endpoint;
urb->device = device;
urb->buffer = (u8 *) urb->buffer_data;
urb->buffer_length = sizeof (urb->buffer_data);
urb_link_init (&urb->link);
return urb;
}
/**
* usbd_dealloc_urb - deallocate an URB and associated buffer
* @urb: pointer to an urb structure
*
* Deallocate an urb structure and associated data.
*/
void usbd_dealloc_urb (struct urb *urb)
{
if (urb) {
free (urb);
}
}
/* Event signaling functions ***************************************************** */
/**
* usbd_device_event - called to respond to various usb events
* @device: pointer to struct device
* @event: event to respond to
*
* Used by a Bus driver to indicate an event.
*/
void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data)
{
usb_device_state_t state;
if (!device || !device->bus) {
usberr("(%p,%d) NULL device or device->bus", device, event);
return;
}
state = device->device_state;
usbinfo("%s", usbd_device_events[event]);
switch (event) {
case DEVICE_UNKNOWN:
break;
case DEVICE_INIT:
device->device_state = STATE_INIT;
break;
case DEVICE_CREATE:
device->device_state = STATE_ATTACHED;
break;
case DEVICE_HUB_CONFIGURED:
device->device_state = STATE_POWERED;
break;
case DEVICE_RESET:
device->device_state = STATE_DEFAULT;
device->address = 0;
break;
case DEVICE_ADDRESS_ASSIGNED:
device->device_state = STATE_ADDRESSED;
break;
case DEVICE_CONFIGURED:
device->device_state = STATE_CONFIGURED;
break;
case DEVICE_DE_CONFIGURED:
device->device_state = STATE_ADDRESSED;
break;
case DEVICE_BUS_INACTIVE:
if (device->status != USBD_CLOSING) {
device->status = USBD_SUSPENDED;
}
break;
case DEVICE_BUS_ACTIVITY:
if (device->status != USBD_CLOSING) {
device->status = USBD_OK;
}
break;
case DEVICE_SET_INTERFACE:
break;
case DEVICE_SET_FEATURE:
break;
case DEVICE_CLEAR_FEATURE:
break;
case DEVICE_POWER_INTERRUPTION:
device->device_state = STATE_POWERED;
break;
case DEVICE_HUB_RESET:
device->device_state = STATE_ATTACHED;
break;
case DEVICE_DESTROY:
device->device_state = STATE_UNKNOWN;
break;
case DEVICE_FUNCTION_PRIVATE:
break;
default:
usbdbg("event %d - not handled",event);
break;
}
debug("%s event: %d oldstate: %d newstate: %d status: %d address: %d",
device->name, event, state,
device->device_state, device->status, device->address);
/* tell the bus interface driver */
if( device->event ) {
/* usbdbg("calling device->event"); */
device->event(device, event, data);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,610 @@
/*
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
*
* (C) Copyright 2006
* Bryan O'Donoghue, deckard@CodeHermit.ie
*
* Based on
* linux/drivers/usbd/ep0.c
*
* Copyright (c) 2000, 2001, 2002 Lineo
* Copyright (c) 2001 Hewlett Packard
*
* By:
* Stuart Lynne <sl@lineo.com>,
* Tom Rushworth <tbr@lineo.com>,
* Bruce Balden <balden@lineo.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.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/*
* This is the builtin ep0 control function. It implements all required functionality
* for responding to control requests (SETUP packets).
*
* XXX
*
* Currently we do not pass any SETUP packets (or other) to the configured
* function driver. This may need to change.
*
* XXX
*
* As alluded to above, a simple callback cdc_recv_setup has been implemented
* in the usb_device data structure to facilicate passing
* Common Device Class packets to a function driver.
*
* XXX
*/
#include <common.h>
#include <usbdevice.h>
#if 0
#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args)
#else
#define dbg_ep0(lvl,fmt,args...)
#endif
/* EP0 Configuration Set ********************************************************************* */
/**
* ep0_get_status - fill in URB data with appropriate status
* @device:
* @urb:
* @index:
* @requesttype:
*
*/
static int ep0_get_status (struct usb_device_instance *device,
struct urb *urb, int index, int requesttype)
{
char *cp;
urb->actual_length = 2;
cp = (char*)urb->buffer;
cp[0] = cp[1] = 0;
switch (requesttype) {
case USB_REQ_RECIPIENT_DEVICE:
cp[0] = USB_STATUS_SELFPOWERED;
break;
case USB_REQ_RECIPIENT_INTERFACE:
break;
case USB_REQ_RECIPIENT_ENDPOINT:
cp[0] = usbd_endpoint_halted (device, index);
break;
case USB_REQ_RECIPIENT_OTHER:
urb->actual_length = 0;
default:
break;
}
dbg_ep0 (2, "%02x %02x", cp[0], cp[1]);
return 0;
}
/**
* ep0_get_one
* @device:
* @urb:
* @result:
*
* Set a single byte value in the urb send buffer. Return non-zero to signal
* a request error.
*/
static int ep0_get_one (struct usb_device_instance *device, struct urb *urb,
__u8 result)
{
urb->actual_length = 1; /* XXX 2? */
((char *) urb->buffer)[0] = result;
return 0;
}
/**
* copy_config
* @urb: pointer to urb
* @data: pointer to configuration data
* @length: length of data
*
* Copy configuration data to urb transfer buffer if there is room for it.
*/
void copy_config (struct urb *urb, void *data, int max_length,
int max_buf)
{
int available;
int length;
/*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */
/* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */
if (!data) {
dbg_ep0 (1, "data is NULL");
return;
}
length = max_length;
if (length > max_length) {
dbg_ep0 (1, "length: %d >= max_length: %d", length,
max_length);
return;
}
/*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */
/* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */
if ((available =
/*urb->buffer_length */ max_buf - urb->actual_length) <= 0) {
return;
}
/*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
/* urb->actual_length, urb->buffer_length, max_buf, length, available); */
if (length > available) {
length = available;
}
/*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */
/* urb->actual_length, urb->buffer_length, max_buf, length, available); */
memcpy (urb->buffer + urb->actual_length, data, length);
urb->actual_length += length;
dbg_ep0 (3,
"copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d",
urb->actual_length, urb->buffer_length, max_buf, max_length,
available);
}
/**
* ep0_get_descriptor
* @device:
* @urb:
* @max:
* @descriptor_type:
* @index:
*
* Called by ep0_rx_process for a get descriptor device command. Determine what
* descriptor is being requested, copy to send buffer. Return zero if ok to send,
* return non-zero to signal a request error.
*/
static int ep0_get_descriptor (struct usb_device_instance *device,
struct urb *urb, int max, int descriptor_type,
int index)
{
int port = 0; /* XXX compound device */
/*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */
if (!urb || !urb->buffer || !urb->buffer_length
|| (urb->buffer_length < 255)) {
dbg_ep0 (2, "invalid urb %p", urb);
return -1L;
}
/* setup tx urb */
urb->actual_length = 0;
dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type));
switch (descriptor_type) {
case USB_DESCRIPTOR_TYPE_DEVICE:
{
struct usb_device_descriptor *device_descriptor;
if (!
(device_descriptor =
usbd_device_device_descriptor (device, port))) {
return -1;
}
/* copy descriptor for this device */
copy_config (urb, device_descriptor,
sizeof (struct usb_device_descriptor),
max);
/* correct the correct control endpoint 0 max packet size into the descriptor */
device_descriptor =
(struct usb_device_descriptor *) urb->buffer;
}
dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length);
break;
case USB_DESCRIPTOR_TYPE_CONFIGURATION:
{
struct usb_configuration_descriptor
*configuration_descriptor;
struct usb_device_descriptor *device_descriptor;
if (!
(device_descriptor =
usbd_device_device_descriptor (device, port))) {
return -1;
}
/*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */
if (index >= device_descriptor->bNumConfigurations) {
dbg_ep0 (0, "index too large: %d >= %d", index,
device_descriptor->
bNumConfigurations);
return -1;
}
if (!
(configuration_descriptor =
usbd_device_configuration_descriptor (device,
port,
index))) {
dbg_ep0 (0,
"usbd_device_configuration_descriptor failed: %d",
index);
return -1;
}
dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength));
copy_config (urb, configuration_descriptor,
cpu_to_le16(configuration_descriptor->wTotalLength),
max);
}
break;
case USB_DESCRIPTOR_TYPE_STRING:
{
struct usb_string_descriptor *string_descriptor;
if (!(string_descriptor = usbd_get_string (index))) {
serial_printf("Invalid string index %d\n", index);
return -1;
}
dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength);
copy_config (urb, string_descriptor, string_descriptor->bLength, max);
}
break;
case USB_DESCRIPTOR_TYPE_INTERFACE:
serial_printf("USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n");
return -1;
case USB_DESCRIPTOR_TYPE_ENDPOINT:
serial_printf("USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n");
return -1;
case USB_DESCRIPTOR_TYPE_HID:
{
serial_printf("USB_DESCRIPTOR_TYPE_HID - error not implemented\n");
return -1; /* unsupported at this time */
#if 0
int bNumInterface =
le16_to_cpu (urb->device_request.wIndex);
int bAlternateSetting = 0;
int class = 0;
struct usb_class_descriptor *class_descriptor;
if (!(class_descriptor =
usbd_device_class_descriptor_index (device,
port, 0,
bNumInterface,
bAlternateSetting,
class))
|| class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) {
dbg_ep0 (3, "[%d] interface is not HID",
bNumInterface);
return -1;
}
/* copy descriptor for this class */
copy_config (urb, class_descriptor,
class_descriptor->descriptor.hid.bLength,
max);
#endif
}
break;
case USB_DESCRIPTOR_TYPE_REPORT:
{
serial_printf("USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n");
return -1; /* unsupported at this time */
#if 0
int bNumInterface =
le16_to_cpu (urb->device_request.wIndex);
int bAlternateSetting = 0;
int class = 0;
struct usb_class_report_descriptor *report_descriptor;
if (!(report_descriptor =
usbd_device_class_report_descriptor_index
(device, port, 0, bNumInterface,
bAlternateSetting, class))
|| report_descriptor->bDescriptorType !=
USB_DT_REPORT) {
dbg_ep0 (3, "[%d] descriptor is not REPORT",
bNumInterface);
return -1;
}
/* copy report descriptor for this class */
/*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */
if (max - urb->actual_length > 0) {
int length =
MIN (report_descriptor->wLength,
max - urb->actual_length);
memcpy (urb->buffer + urb->actual_length,
&report_descriptor->bData[0], length);
urb->actual_length += length;
}
#endif
}
break;
case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
#if defined(CONFIG_USBD_HS)
{
struct usb_qualifier_descriptor *qualifier_descriptor =
device->qualifier_descriptor;
if (!qualifier_descriptor)
return -1;
/* copy descriptor for this device */
copy_config(urb, qualifier_descriptor,
sizeof(struct usb_qualifier_descriptor),
max);
}
dbg_ep0(3, "copied qualifier descriptor, actual_length: 0x%x",
urb->actual_length);
#else
return -1;
#endif
break;
default:
return -1;
}
dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d",
urb->buffer, urb->buffer_length, urb->actual_length,
device->bus->endpoint_array[0].tx_packetSize);
/*
if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) {
dbg_ep0(0, "adding null byte");
urb->buffer[urb->actual_length++] = 0;
dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d",
urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize);
}
*/
return 0;
}
/**
* ep0_recv_setup - called to indicate URB has been received
* @urb: pointer to struct urb
*
* Check if this is a setup packet, process the device request, put results
* back into the urb and return zero or non-zero to indicate success (DATA)
* or failure (STALL).
*
*/
int ep0_recv_setup (struct urb *urb)
{
/*struct usb_device_request *request = urb->buffer; */
/*struct usb_device_instance *device = urb->device; */
struct usb_device_request *request;
struct usb_device_instance *device;
int address;
dbg_ep0 (0, "entering ep0_recv_setup()");
if (!urb || !urb->device) {
dbg_ep0 (3, "invalid URB %p", urb);
return -1;
}
request = &urb->device_request;
device = urb->device;
dbg_ep0 (3, "urb: %p device: %p", urb, urb->device);
/*dbg_ep0(2, "- - - - - - - - - -"); */
dbg_ep0 (2,
"bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s",
request->bmRequestType, request->bRequest,
le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex),
le16_to_cpu (request->wLength),
USBD_DEVICE_REQUESTS (request->bRequest));
/* handle USB Standard Request (c.f. USB Spec table 9-2) */
if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) {
if(device->device_state <= STATE_CONFIGURED){
/* Attempt to handle a CDC specific request if we are
* in the configured state.
*/
return device->cdc_recv_setup(request,urb);
}
dbg_ep0 (1, "non standard request: %x",
request->bmRequestType & USB_REQ_TYPE_MASK);
return -1; /* Stall here */
}
switch (device->device_state) {
case STATE_CREATED:
case STATE_ATTACHED:
case STATE_POWERED:
/* It actually is important to allow requests in these states,
* Windows will request descriptors before assigning an
* address to the client.
*/
/*dbg_ep0 (1, "request %s not allowed in this state: %s", */
/* USBD_DEVICE_REQUESTS(request->bRequest), */
/* usbd_device_states[device->device_state]); */
/*return -1; */
break;
case STATE_INIT:
case STATE_DEFAULT:
switch (request->bRequest) {
case USB_REQ_GET_STATUS:
case USB_REQ_GET_INTERFACE:
case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
case USB_REQ_SET_DESCRIPTOR:
/* case USB_REQ_SET_CONFIGURATION: */
case USB_REQ_SET_INTERFACE:
dbg_ep0 (1,
"request %s not allowed in DEFAULT state: %s",
USBD_DEVICE_REQUESTS (request->bRequest),
usbd_device_states[device->device_state]);
return -1;
case USB_REQ_SET_CONFIGURATION:
case USB_REQ_SET_ADDRESS:
case USB_REQ_GET_DESCRIPTOR:
case USB_REQ_GET_CONFIGURATION:
break;
}
case STATE_ADDRESSED:
case STATE_CONFIGURED:
break;
case STATE_UNKNOWN:
dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s",
USBD_DEVICE_REQUESTS (request->bRequest),
usbd_device_states[device->device_state]);
return -1;
}
/* handle all requests that return data (direction bit set on bm RequestType) */
if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) {
dbg_ep0 (3, "Device-to-Host");
switch (request->bRequest) {
case USB_REQ_GET_STATUS:
return ep0_get_status (device, urb, request->wIndex,
request->bmRequestType &
USB_REQ_RECIPIENT_MASK);
case USB_REQ_GET_DESCRIPTOR:
return ep0_get_descriptor (device, urb,
le16_to_cpu (request->wLength),
le16_to_cpu (request->wValue) >> 8,
le16_to_cpu (request->wValue) & 0xff);
case USB_REQ_GET_CONFIGURATION:
serial_printf("get config %d\n", device->configuration);
return ep0_get_one (device, urb,
device->configuration);
case USB_REQ_GET_INTERFACE:
return ep0_get_one (device, urb, device->alternate);
case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
return -1;
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
case USB_REQ_SET_ADDRESS:
case USB_REQ_SET_DESCRIPTOR:
case USB_REQ_SET_CONFIGURATION:
case USB_REQ_SET_INTERFACE:
return -1;
}
}
/* handle the requests that do not return data */
else {
/*dbg_ep0(3, "Host-to-Device"); */
switch (request->bRequest) {
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
dbg_ep0 (0, "Host-to-Device");
switch (request->
bmRequestType & USB_REQ_RECIPIENT_MASK) {
case USB_REQ_RECIPIENT_DEVICE:
/* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */
/* XXX fall through for now as we do not support either */
case USB_REQ_RECIPIENT_INTERFACE:
case USB_REQ_RECIPIENT_OTHER:
dbg_ep0 (0, "request %s not",
USBD_DEVICE_REQUESTS (request->bRequest));
default:
return -1;
case USB_REQ_RECIPIENT_ENDPOINT:
dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue));
if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) {
/*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */
/* request->bRequest == USB_REQ_SET_FEATURE); */
/* NEED TO IMPLEMENT THIS!!! */
return -1;
} else {
dbg_ep0 (1, "request %s bad wValue: %04x",
USBD_DEVICE_REQUESTS
(request->bRequest),
le16_to_cpu (request->wValue));
return -1;
}
}
case USB_REQ_SET_ADDRESS:
/* check if this is a re-address, reset first if it is (this shouldn't be possible) */
if (device->device_state != STATE_DEFAULT) {
dbg_ep0 (1, "set_address: %02x state: %s",
le16_to_cpu (request->wValue),
usbd_device_states[device->device_state]);
return -1;
}
address = le16_to_cpu (request->wValue);
if ((address & 0x7f) != address) {
dbg_ep0 (1, "invalid address %04x %04x",
address, address & 0x7f);
return -1;
}
device->address = address;
/*dbg_ep0(2, "address: %d %d %d", */
/* request->wValue, le16_to_cpu(request->wValue), device->address); */
return 0;
case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */
dbg_ep0 (0, "set descriptor: NOT SUPPORTED");
return -1;
case USB_REQ_SET_CONFIGURATION:
/* c.f. 9.4.7 - the top half of wValue is reserved */
device->configuration = le16_to_cpu(request->wValue) & 0xff;
/* reset interface and alternate settings */
device->interface = device->alternate = 0;
/*dbg_ep0(2, "set configuration: %d", device->configuration); */
/*serial_printf("DEVICE_CONFIGURED.. event?\n"); */
return 0;
case USB_REQ_SET_INTERFACE:
device->interface = le16_to_cpu (request->wIndex);
device->alternate = le16_to_cpu (request->wValue);
/*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */
serial_printf ("DEVICE_SET_INTERFACE.. event?\n");
return 0;
case USB_REQ_GET_STATUS:
case USB_REQ_GET_DESCRIPTOR:
case USB_REQ_GET_CONFIGURATION:
case USB_REQ_GET_INTERFACE:
case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */
return -1;
}
}
return -1;
}

View file

@ -0,0 +1,39 @@
/*
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
*
* Based on
* linux/drivers/usbd/ep0.c
*
* Copyright (c) 2000, 2001, 2002 Lineo
* Copyright (c) 2001 Hewlett Packard
*
* By:
* Stuart Lynne <sl@lineo.com>,
* Tom Rushworth <tbr@lineo.com>,
* Bruce Balden <balden@lineo.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.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __USBDCORE_EP0_H__
#define __USBDCORE_EP0_H__
int ep0_recv_setup (struct urb *urb);
#endif

View file

@ -0,0 +1,306 @@
/*
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
*
* Copyright (C) 2004 David Brownell
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
* Remy Bohmer <linux@bohmer.net>
*/
#include <common.h>
#include <linux/usb/ch9.h>
#include <asm/errno.h>
#include <linux/usb/gadget.h>
#include <asm/unaligned.h>
#include "gadget_chips.h"
#define isdigit(c) ('0' <= (c) && (c) <= '9')
/* we must assign addresses for configurable endpoints (like net2280) */
static unsigned epnum;
/* #define MANY_ENDPOINTS */
#ifdef MANY_ENDPOINTS
/* more than 15 configurable endpoints */
static unsigned in_epnum;
#endif
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
*
* - ep1, ep2, ... address is fixed, not direction or type
* - ep1in, ep2out, ... address and direction are fixed, not type
* - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
* - ep1in-bulk, ep2out-iso, ... all three are fixed
* - ep-* ... no functionality restrictions
*
* Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
* Less common restrictions are implied by gadget_is_*().
*
* NOTE: each endpoint is unidirectional, as specified by its USB
* descriptor; and isn't specific to a configuration or altsetting.
*/
static int ep_matches(
struct usb_gadget *gadget,
struct usb_ep *ep,
struct usb_endpoint_descriptor *desc
)
{
u8 type;
const char *tmp;
u16 max;
/* endpoint already claimed? */
if (NULL != ep->driver_data)
return 0;
/* only support ep0 for portable CONTROL traffic */
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
/* some other naming convention */
if ('e' != ep->name[0])
return 0;
/* type-restriction: "-iso", "-bulk", or "-int".
* direction-restriction: "in", "out".
*/
if ('-' != ep->name[2]) {
tmp = strrchr(ep->name, '-');
if (tmp) {
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* bulk endpoints handle interrupt transfers,
* except the toggle-quirky iso-synch kind
*/
if ('s' == tmp[2]) /* == "-iso" */
return 0;
/* for now, avoid PXA "interrupt-in";
* it's documented as never using DATA1.
*/
if (gadget_is_pxa(gadget)
&& 'i' == tmp[1])
return 0;
break;
case USB_ENDPOINT_XFER_BULK:
if ('b' != tmp[1]) /* != "-bulk" */
return 0;
break;
case USB_ENDPOINT_XFER_ISOC:
if ('s' != tmp[2]) /* != "-iso" */
return 0;
}
} else {
tmp = ep->name + strlen(ep->name);
}
/* direction-restriction: "..in-..", "out-.." */
tmp--;
if (!isdigit(*tmp)) {
if (desc->bEndpointAddress & USB_DIR_IN) {
if ('n' != *tmp)
return 0;
} else {
if ('t' != *tmp)
return 0;
}
}
}
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
max = 0x7ff & le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high speed */
if (!gadget->is_dualspeed && max > 64)
return 0;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high speed */
if (ep->maxpacket < max)
return 0;
if (!gadget->is_dualspeed && max > 1023)
return 0;
/* BOTH: "high bandwidth" works only at high speed */
if ((get_unaligned(&desc->wMaxPacketSize) &
__constant_cpu_to_le16(3<<11))) {
if (!gadget->is_dualspeed)
return 0;
/* configure your hardware with enough buffering!! */
}
break;
}
/* MATCH!! */
/* report address */
if (isdigit(ep->name[2])) {
u8 num = simple_strtoul(&ep->name[2], NULL, 10);
desc->bEndpointAddress |= num;
#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {
if (++in_epnum > 15)
return 0;
desc->bEndpointAddress = USB_DIR_IN | in_epnum;
#endif
} else {
if (++epnum > 15)
return 0;
desc->bEndpointAddress |= epnum;
}
/* report (variable) full speed bulk maxpacket */
if (USB_ENDPOINT_XFER_BULK == type) {
int size = ep->maxpacket;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
}
return 1;
}
static struct usb_ep *
find_ep(struct usb_gadget *gadget, const char *name)
{
struct usb_ep *ep;
list_for_each_entry(ep, &gadget->ep_list, ep_list) {
if (0 == strcmp(ep->name, name))
return ep;
}
return NULL;
}
/**
* usb_ep_autoconfig - choose an endpoint matching the descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
* size must also be initialized. This is modified on success.
*
* By choosing an endpoint to use with the specified descriptor, this
* routine simplifies writing gadget drivers that work with multiple
* USB device controllers. The endpoint would be passed later to
* usb_ep_enable(), along with some descriptor.
*
* That second descriptor won't always be the same as the first one.
* For example, isochronous endpoints can be autoconfigured for high
* bandwidth, and then used in several lower bandwidth altsettings.
* Also, high and full speed descriptors will be different.
*
* Be sure to examine and test the results of autoconfiguration on your
* hardware. This code may not make the best choices about how to use the
* USB controller, and it can't know all the restrictions that may apply.
* Some combinations of driver and hardware won't be able to autoconfigure.
*
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
* is initialized as if the endpoint were used at full speed. To prevent
* the endpoint from being returned by a later autoconfig call, claim it
* by assigning ep->driver_data to some non-null value.
*
* On failure, this returns a null endpoint descriptor.
*/
struct usb_ep *usb_ep_autoconfig(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc
)
{
struct usb_ep *ep;
u8 type;
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
if (gadget_is_net2280(gadget) && type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep(gadget, "ep-e");
if (ep && ep_matches(gadget, ep, desc))
return ep;
ep = find_ep(gadget, "ep-f");
if (ep && ep_matches(gadget, ep, desc))
return ep;
} else if (gadget_is_goku(gadget)) {
if (USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough */
ep = find_ep(gadget, "ep3-bulk");
if (ep && ep_matches(gadget, ep, desc))
return ep;
} else if (USB_ENDPOINT_XFER_BULK == type
&& (USB_DIR_IN & desc->bEndpointAddress)) {
/* DMA may be available */
ep = find_ep(gadget, "ep2-bulk");
if (ep && ep_matches(gadget, ep, desc))
return ep;
}
} else if (gadget_is_sh(gadget) && USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough; maybe 8 byte fifo is too */
ep = find_ep(gadget, "ep3in-bulk");
if (ep && ep_matches(gadget, ep, desc))
return ep;
} else if (gadget_is_mq11xx(gadget) && USB_ENDPOINT_XFER_INT == type) {
ep = find_ep(gadget, "ep1-bulk");
if (ep && ep_matches(gadget, ep, desc))
return ep;
}
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry(ep, &gadget->ep_list, ep_list) {
if (ep_matches(gadget, ep, desc))
return ep;
}
/* Fail */
return NULL;
}
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
* @gadget: device for which autoconfig state will be reset
*
* Use this for devices where one configuration may need to assign
* endpoint resources very differently from the next one. It clears
* state such as ep->driver_data and the record of assigned endpoints
* used by usb_ep_autoconfig().
*/
void usb_ep_autoconfig_reset(struct usb_gadget *gadget)
{
struct usb_ep *ep;
list_for_each_entry(ep, &gadget->ep_list, ep_list) {
ep->driver_data = NULL;
}
#ifdef MANY_ENDPOINTS
in_epnum = 0;
#endif
epnum = 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,227 @@
/*
* USB device controllers have lots of quirks. Use these macros in
* gadget drivers or other code that needs to deal with them, and which
* autoconfigures instead of using early binding to the hardware.
*
* This SHOULD eventually work like the ARM mach_is_*() stuff, driven by
* some config file that gets updated as new hardware is supported.
* (And avoiding all runtime comparisons in typical one-choice configs!)
*
* NOTE: some of these controller drivers may not be available yet.
* Some are available on 2.4 kernels; several are available, but not
* yet pushed in the 2.6 mainline tree.
*
* Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
* Remy Bohmer <linux@bohmer.net>
*/
#ifdef CONFIG_USB_GADGET_NET2280
#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
#else
#define gadget_is_net2280(g) 0
#endif
#ifdef CONFIG_USB_GADGET_AMD5536UDC
#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
#else
#define gadget_is_amd5536udc(g) 0
#endif
#ifdef CONFIG_USB_GADGET_DUMMY_HCD
#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
#else
#define gadget_is_dummy(g) 0
#endif
#ifdef CONFIG_USB_GADGET_PXA2XX
#define gadget_is_pxa(g) (!strcmp("pxa2xx_udc", (g)->name))
#else
#define gadget_is_pxa(g) 0
#endif
#ifdef CONFIG_USB_GADGET_GOKU
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
#else
#define gadget_is_goku(g) 0
#endif
/* SH3 UDC -- not yet ported 2.4 --> 2.6 */
#ifdef CONFIG_USB_GADGET_SUPERH
#define gadget_is_sh(g) (!strcmp("sh_udc", (g)->name))
#else
#define gadget_is_sh(g) 0
#endif
/* not yet stable on 2.6 (would help "original Zaurus") */
#ifdef CONFIG_USB_GADGET_SA1100
#define gadget_is_sa1100(g) (!strcmp("sa1100_udc", (g)->name))
#else
#define gadget_is_sa1100(g) 0
#endif
#ifdef CONFIG_USB_GADGET_LH7A40X
#define gadget_is_lh7a40x(g) (!strcmp("lh7a40x_udc", (g)->name))
#else
#define gadget_is_lh7a40x(g) 0
#endif
/* handhelds.org tree (?) */
#ifdef CONFIG_USB_GADGET_MQ11XX
#define gadget_is_mq11xx(g) (!strcmp("mq11xx_udc", (g)->name))
#else
#define gadget_is_mq11xx(g) 0
#endif
#ifdef CONFIG_USB_GADGET_OMAP
#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
#else
#define gadget_is_omap(g) 0
#endif
/* not yet ported 2.4 --> 2.6 */
#ifdef CONFIG_USB_GADGET_N9604
#define gadget_is_n9604(g) (!strcmp("n9604_udc", (g)->name))
#else
#define gadget_is_n9604(g) 0
#endif
/* various unstable versions available */
#ifdef CONFIG_USB_GADGET_PXA27X
#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
#else
#define gadget_is_pxa27x(g) 0
#endif
#ifdef CONFIG_USB_GADGET_ATMEL_USBA
#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
#else
#define gadget_is_atmel_usba(g) 0
#endif
#ifdef CONFIG_USB_GADGET_S3C2410
#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
#else
#define gadget_is_s3c2410(g) 0
#endif
#ifdef CONFIG_USB_GADGET_AT91
#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
#else
#define gadget_is_at91(g) 0
#endif
/* status unclear */
#ifdef CONFIG_USB_GADGET_IMX
#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
#else
#define gadget_is_imx(g) 0
#endif
#ifdef CONFIG_USB_GADGET_FSL_USB2
#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
#else
#define gadget_is_fsl_usb2(g) 0
#endif
/* Mentor high speed function controller */
/* from Montavista kernel (?) */
#ifdef CONFIG_USB_GADGET_MUSBHSFC
#define gadget_is_musbhsfc(g) (!strcmp("musbhsfc_udc", (g)->name))
#else
#define gadget_is_musbhsfc(g) 0
#endif
/* Mentor high speed "dual role" controller, in peripheral role */
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
#define gadget_is_musbhdrc(g) (!strcmp("musb_hdrc", (g)->name))
#else
#define gadget_is_musbhdrc(g) 0
#endif
/* from Montavista kernel (?) */
#ifdef CONFIG_USB_GADGET_MPC8272
#define gadget_is_mpc8272(g) (!strcmp("mpc8272_udc", (g)->name))
#else
#define gadget_is_mpc8272(g) 0
#endif
#ifdef CONFIG_USB_GADGET_M66592
#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
#else
#define gadget_is_m66592(g) 0
#endif
#ifdef CONFIG_USB_GADGET_MV
#define gadget_is_mv(g) (!strcmp("mv_udc", (g)->name))
#else
#define gadget_is_mv(g) 0
#endif
/*
* CONFIG_USB_GADGET_SX2
* CONFIG_USB_GADGET_AU1X00
* ...
*/
/**
* usb_gadget_controller_number - support bcdDevice id convention
* @gadget: the controller being driven
*
* Return a 2-digit BCD value associated with the peripheral controller,
* suitable for use as part of a bcdDevice value, or a negative error code.
*
* NOTE: this convention is purely optional, and has no meaning in terms of
* any USB specification. If you want to use a different convention in your
* gadget driver firmware -- maybe a more formal revision ID -- feel free.
*
* Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
* to change their behavior accordingly. For example it might help avoiding
* some chip bug.
*/
static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
{
if (gadget_is_net2280(gadget))
return 0x01;
else if (gadget_is_dummy(gadget))
return 0x02;
else if (gadget_is_pxa(gadget))
return 0x03;
else if (gadget_is_sh(gadget))
return 0x04;
else if (gadget_is_sa1100(gadget))
return 0x05;
else if (gadget_is_goku(gadget))
return 0x06;
else if (gadget_is_mq11xx(gadget))
return 0x07;
else if (gadget_is_omap(gadget))
return 0x08;
else if (gadget_is_lh7a40x(gadget))
return 0x09;
else if (gadget_is_n9604(gadget))
return 0x10;
else if (gadget_is_pxa27x(gadget))
return 0x11;
else if (gadget_is_s3c2410(gadget))
return 0x12;
else if (gadget_is_at91(gadget))
return 0x13;
else if (gadget_is_imx(gadget))
return 0x14;
else if (gadget_is_musbhsfc(gadget))
return 0x15;
else if (gadget_is_musbhdrc(gadget))
return 0x16;
else if (gadget_is_mpc8272(gadget))
return 0x17;
else if (gadget_is_atmel_usba(gadget))
return 0x18;
else if (gadget_is_fsl_usb2(gadget))
return 0x19;
else if (gadget_is_amd5536udc(gadget))
return 0x20;
else if (gadget_is_m66592(gadget))
return 0x21;
else if (gadget_is_mv(gadget))
return 0x22;
return -ENOENT;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,499 @@
/*
* Copyright 2011, Marvell Semiconductor Inc.
* Lei Wen <leiwen@marvell.com>
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Back ported to the 8xx platform (from the 8260 platform) by
* Murray.Jensen@cmst.csiro.au, 27-Jan-01.
*/
#include <common.h>
#include <command.h>
#include <config.h>
#include <net.h>
#include <malloc.h>
#include <asm/io.h>
#include <linux/types.h>
#include <usb/mv_udc.h>
#ifndef DEBUG
#define DBG(x...) do {} while (0)
#else
#define DBG(x...) printf(x)
static const char *reqname(unsigned r)
{
switch (r) {
case USB_REQ_GET_STATUS: return "GET_STATUS";
case USB_REQ_CLEAR_FEATURE: return "CLEAR_FEATURE";
case USB_REQ_SET_FEATURE: return "SET_FEATURE";
case USB_REQ_SET_ADDRESS: return "SET_ADDRESS";
case USB_REQ_GET_DESCRIPTOR: return "GET_DESCRIPTOR";
case USB_REQ_SET_DESCRIPTOR: return "SET_DESCRIPTOR";
case USB_REQ_GET_CONFIGURATION: return "GET_CONFIGURATION";
case USB_REQ_SET_CONFIGURATION: return "SET_CONFIGURATION";
case USB_REQ_GET_INTERFACE: return "GET_INTERFACE";
case USB_REQ_SET_INTERFACE: return "SET_INTERFACE";
default: return "*UNKNOWN*";
}
}
#endif
#define PAGE_SIZE 4096
#define QH_MAXNUM 32
static struct usb_endpoint_descriptor ep0_out_desc = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
};
static struct usb_endpoint_descriptor ep0_in_desc = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_CONTROL,
};
struct ept_queue_head *epts;
struct ept_queue_item *items[2 * NUM_ENDPOINTS];
static int mv_pullup(struct usb_gadget *gadget, int is_on);
static int mv_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc);
static int mv_ep_disable(struct usb_ep *ep);
static int mv_ep_queue(struct usb_ep *ep,
struct usb_request *req, gfp_t gfp_flags);
static struct usb_request *
mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags);
static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req);
static struct usb_gadget_ops mv_udc_ops = {
.pullup = mv_pullup,
};
static struct usb_ep_ops mv_ep_ops = {
.enable = mv_ep_enable,
.disable = mv_ep_disable,
.queue = mv_ep_queue,
.alloc_request = mv_ep_alloc_request,
.free_request = mv_ep_free_request,
};
static struct mv_ep ep[2 * NUM_ENDPOINTS];
static struct mv_drv controller = {
.gadget = {
.ep0 = &ep[0].ep,
.name = "mv_udc",
},
};
static struct usb_request *
mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags)
{
struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep);
return &mv_ep->req;
}
static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
{
return;
}
static void ep_enable(int num, int in)
{
struct ept_queue_head *head;
struct mv_udc *udc = controller.udc;
unsigned n;
head = epts + 2*num + in;
n = readl(&udc->epctrl[num]);
if (in)
n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
else
n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
if (num != 0)
head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) | CONFIG_ZLT;
writel(n, &udc->epctrl[num]);
}
static int mv_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep);
int num, in;
num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
ep_enable(num, in);
mv_ep->desc = desc;
return 0;
}
static int mv_ep_disable(struct usb_ep *ep)
{
return 0;
}
static int mv_ep_queue(struct usb_ep *ep,
struct usb_request *req, gfp_t gfp_flags)
{
struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep);
struct mv_udc *udc = controller.udc;
struct ept_queue_item *item;
struct ept_queue_head *head;
unsigned phys;
int bit, num, len, in;
num = mv_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
in = (mv_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
item = items[2 * num + in];
head = epts + 2 * num + in;
phys = (unsigned)req->buf;
len = req->length;
item->next = TERMINATE;
item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE;
item->page0 = phys;
item->page1 = (phys & 0xfffff000) + 0x1000;
head->next = (unsigned) item;
head->info = 0;
DBG("ept%d %s queue len %x, buffer %x\n",
num, in ? "in" : "out", len, phys);
if (in)
bit = EPT_TX(num);
else
bit = EPT_RX(num);
flush_cache(phys, len);
flush_cache((unsigned long)item, sizeof(struct ept_queue_item));
writel(bit, &udc->epprime);
return 0;
}
static void handle_ep_complete(struct mv_ep *ep)
{
struct ept_queue_item *item;
int num, in, len;
num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
if (num == 0)
ep->desc = &ep0_out_desc;
item = items[2 * num + in];
if (item->info & 0xff)
printf("EP%d/%s FAIL nfo=%x pg0=%x\n",
num, in ? "in" : "out", item->info, item->page0);
len = (item->info >> 16) & 0x7fff;
ep->req.length -= len;
DBG("ept%d %s complete %x\n",
num, in ? "in" : "out", len);
ep->req.complete(&ep->ep, &ep->req);
if (num == 0) {
ep->req.length = 0;
usb_ep_queue(&ep->ep, &ep->req, 0);
ep->desc = &ep0_in_desc;
}
}
#define SETUP(type, request) (((type) << 8) | (request))
static void handle_setup(void)
{
struct usb_request *req = &ep[0].req;
struct mv_udc *udc = controller.udc;
struct ept_queue_head *head;
struct usb_ctrlrequest r;
int status = 0;
int num, in, _num, _in, i;
char *buf;
head = epts;
flush_cache((unsigned long)head, sizeof(struct ept_queue_head));
memcpy(&r, head->setup_data, sizeof(struct usb_ctrlrequest));
writel(EPT_RX(0), &udc->epstat);
DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest),
r.bRequestType, r.bRequest, r.wIndex, r.wValue);
switch (SETUP(r.bRequestType, r.bRequest)) {
case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE):
_num = r.wIndex & 15;
_in = !!(r.wIndex & 0x80);
if ((r.wValue == 0) && (r.wLength == 0)) {
req->length = 0;
for (i = 0; i < NUM_ENDPOINTS; i++) {
if (!ep[i].desc)
continue;
num = ep[i].desc->bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK;
in = (ep[i].desc->bEndpointAddress
& USB_DIR_IN) != 0;
if ((num == _num) && (in == _in)) {
ep_enable(num, in);
usb_ep_queue(controller.gadget.ep0,
req, 0);
break;
}
}
}
return;
case SETUP(USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS):
/*
* write address delayed (will take effect
* after the next IN txn)
*/
writel((r.wValue << 25) | (1 << 24), &udc->devaddr);
req->length = 0;
usb_ep_queue(controller.gadget.ep0, req, 0);
return;
case SETUP(USB_DIR_IN | USB_RECIP_DEVICE, USB_REQ_GET_STATUS):
req->length = 2;
buf = (char *)req->buf;
buf[0] = 1 << USB_DEVICE_SELF_POWERED;
buf[1] = 0;
usb_ep_queue(controller.gadget.ep0, req, 0);
return;
}
/* pass request up to the gadget driver */
if (controller.driver)
status = controller.driver->setup(&controller.gadget, &r);
else
status = -ENODEV;
if (!status)
return;
DBG("STALL reqname %s type %x value %x, index %x\n",
reqname(r.bRequest), r.bRequestType, r.wValue, r.wIndex);
writel((1<<16) | (1 << 0), &udc->epctrl[0]);
}
static void stop_activity(void)
{
int i, num, in;
struct ept_queue_head *head;
struct mv_udc *udc = controller.udc;
writel(readl(&udc->epcomp), &udc->epcomp);
writel(readl(&udc->epstat), &udc->epstat);
writel(0xffffffff, &udc->epflush);
/* error out any pending reqs */
for (i = 0; i < NUM_ENDPOINTS; i++) {
if (i != 0)
writel(0, &udc->epctrl[i]);
if (ep[i].desc) {
num = ep[i].desc->bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK;
in = (ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0;
head = epts + (num * 2) + (in);
head->info = INFO_ACTIVE;
}
}
}
void udc_irq(void)
{
struct mv_udc *udc = controller.udc;
unsigned n = readl(&udc->usbsts);
writel(n, &udc->usbsts);
int bit, i, num, in;
n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
if (n == 0)
return;
if (n & STS_URI) {
DBG("-- reset --\n");
stop_activity();
}
if (n & STS_SLI)
DBG("-- suspend --\n");
if (n & STS_PCI) {
DBG("-- portchange --\n");
bit = (readl(&udc->portsc) >> 26) & 3;
if (bit == 2) {
controller.gadget.speed = USB_SPEED_HIGH;
for (i = 1; i < NUM_ENDPOINTS && n; i++)
if (ep[i].desc)
ep[i].ep.maxpacket = 512;
} else {
controller.gadget.speed = USB_SPEED_FULL;
}
}
if (n & STS_UEI)
printf("<UEI %x>\n", readl(&udc->epcomp));
if ((n & STS_UI) || (n & STS_UEI)) {
n = readl(&udc->epstat);
if (n & EPT_RX(0))
handle_setup();
n = readl(&udc->epcomp);
if (n != 0)
writel(n, &udc->epcomp);
for (i = 0; i < NUM_ENDPOINTS && n; i++) {
if (ep[i].desc) {
num = ep[i].desc->bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK;
in = (ep[i].desc->bEndpointAddress
& USB_DIR_IN) != 0;
bit = (in) ? EPT_TX(num) : EPT_RX(num);
if (n & bit)
handle_ep_complete(&ep[i]);
}
}
}
}
int usb_gadget_handle_interrupts(void)
{
u32 value;
struct mv_udc *udc = controller.udc;
value = readl(&udc->usbsts);
if (value)
udc_irq();
return value;
}
static int mv_pullup(struct usb_gadget *gadget, int is_on)
{
struct mv_udc *udc = controller.udc;
if (is_on) {
/* RESET */
writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd);
udelay(200);
writel((unsigned) epts, &udc->epinitaddr);
/* select DEVICE mode */
writel(USBMODE_DEVICE, &udc->usbmode);
writel(0xffffffff, &udc->epflush);
/* Turn on the USB connection by enabling the pullup resistor */
writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RUN, &udc->usbcmd);
} else {
stop_activity();
writel(USBCMD_FS2, &udc->usbcmd);
udelay(800);
if (controller.driver)
controller.driver->disconnect(gadget);
}
return 0;
}
void udc_disconnect(void)
{
struct mv_udc *udc = controller.udc;
/* disable pullup */
stop_activity();
writel(USBCMD_FS2, &udc->usbcmd);
udelay(800);
if (controller.driver)
controller.driver->disconnect(&controller.gadget);
}
static int mvudc_probe(void)
{
struct ept_queue_head *head;
int i;
controller.gadget.ops = &mv_udc_ops;
controller.udc = (struct mv_udc *)CONFIG_USB_REG_BASE;
epts = memalign(PAGE_SIZE, QH_MAXNUM * sizeof(struct ept_queue_head));
memset(epts, 0, QH_MAXNUM * sizeof(struct ept_queue_head));
for (i = 0; i < 2 * NUM_ENDPOINTS; i++) {
/*
* For item0 and item1, they are served as ep0
* out&in seperately
*/
head = epts + i;
if (i < 2)
head->config = CONFIG_MAX_PKT(EP0_MAX_PACKET_SIZE)
| CONFIG_ZLT | CONFIG_IOS;
else
head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE)
| CONFIG_ZLT;
head->next = TERMINATE;
head->info = 0;
items[i] = memalign(PAGE_SIZE, sizeof(struct ept_queue_item));
}
INIT_LIST_HEAD(&controller.gadget.ep_list);
ep[0].ep.maxpacket = 64;
ep[0].ep.name = "ep0";
ep[0].desc = &ep0_in_desc;
INIT_LIST_HEAD(&controller.gadget.ep0->ep_list);
for (i = 0; i < 2 * NUM_ENDPOINTS; i++) {
if (i != 0) {
ep[i].ep.maxpacket = 512;
ep[i].ep.name = "ep-";
list_add_tail(&ep[i].ep.ep_list,
&controller.gadget.ep_list);
ep[i].desc = NULL;
}
ep[i].ep.ops = &mv_ep_ops;
}
return 0;
}
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
struct mv_udc *udc = controller.udc;
int retval;
if (!driver
|| driver->speed < USB_SPEED_FULL
|| !driver->bind
|| !driver->setup) {
DBG("bad parameter.\n");
return -EINVAL;
}
if (!mvudc_probe()) {
usb_lowlevel_init();
/* select ULPI phy */
writel(PTS(PTS_ENABLE) | PFSC, &udc->portsc);
}
retval = driver->bind(&controller.gadget);
if (retval) {
DBG("driver->bind() returned %d\n", retval);
return retval;
}
controller.driver = driver;
return 0;
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
return 0;
}

View file

@ -0,0 +1,217 @@
/*
* ndis.h
*
* ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
*
* Thanks to the cygwin development team,
* espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
*
* THIS SOFTWARE IS NOT COPYRIGHTED
*
* This source code is offered for use in the public domain. You may
* use, modify or distribute it freely.
*
* This code is distributed in the hope that it will be useful but
* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
* DISCLAIMED. This includes but is not limited to warranties of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef _USBGADGET_NDIS_H
#define _USBGADGET_NDIS_H
#define NDIS_STATUS_MULTICAST_FULL 0xC0010009
#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A
#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B
enum NDIS_DEVICE_POWER_STATE {
NdisDeviceStateUnspecified = 0,
NdisDeviceStateD0,
NdisDeviceStateD1,
NdisDeviceStateD2,
NdisDeviceStateD3,
NdisDeviceStateMaximum
};
struct NDIS_PM_WAKE_UP_CAPABILITIES {
enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
};
/* NDIS_PNP_CAPABILITIES.Flags constants */
#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001
#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002
#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004
struct NDIS_PNP_CAPABILITIES {
__le32 Flags;
struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
};
struct NDIS_PM_PACKET_PATTERN {
__le32 Priority;
__le32 Reserved;
__le32 MaskSize;
__le32 PatternOffset;
__le32 PatternSize;
__le32 PatternFlags;
};
/* Required Object IDs (OIDs) */
#define OID_GEN_SUPPORTED_LIST 0x00010101
#define OID_GEN_HARDWARE_STATUS 0x00010102
#define OID_GEN_MEDIA_SUPPORTED 0x00010103
#define OID_GEN_MEDIA_IN_USE 0x00010104
#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
#define OID_GEN_LINK_SPEED 0x00010107
#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
#define OID_GEN_VENDOR_ID 0x0001010C
#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D
#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
#define OID_GEN_DRIVER_VERSION 0x00010110
#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
#define OID_GEN_PROTOCOL_OPTIONS 0x00010112
#define OID_GEN_MAC_OPTIONS 0x00010113
#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
#define OID_GEN_SUPPORTED_GUIDS 0x00010117
#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
#define OID_GEN_MACHINE_NAME 0x0001021A
#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
#define OID_GEN_VLAN_ID 0x0001021C
/* Optional OIDs */
#define OID_GEN_MEDIA_CAPABILITIES 0x00010201
#define OID_GEN_PHYSICAL_MEDIUM 0x00010202
/* Required statistics OIDs */
#define OID_GEN_XMIT_OK 0x00020101
#define OID_GEN_RCV_OK 0x00020102
#define OID_GEN_XMIT_ERROR 0x00020103
#define OID_GEN_RCV_ERROR 0x00020104
#define OID_GEN_RCV_NO_BUFFER 0x00020105
/* Optional statistics OIDs */
#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207
#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209
#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
#define OID_GEN_RCV_CRC_ERROR 0x0002020D
#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
#define OID_GEN_GET_TIME_CAPS 0x0002020F
#define OID_GEN_GET_NETCARD_TIME 0x00020210
#define OID_GEN_NETCARD_LOAD 0x00020211
#define OID_GEN_DEVICE_PROFILE 0x00020212
#define OID_GEN_INIT_TIME_MS 0x00020213
#define OID_GEN_RESET_COUNTS 0x00020214
#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215
#define OID_GEN_FRIENDLY_NAME 0x00020216
#define OID_GEN_MINIPORT_INFO 0x00020217
#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218
/* IEEE 802.3 (Ethernet) OIDs */
#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
#define OID_802_3_PERMANENT_ADDRESS 0x01010101
#define OID_802_3_CURRENT_ADDRESS 0x01010102
#define OID_802_3_MULTICAST_LIST 0x01010103
#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
#define OID_802_3_MAC_OPTIONS 0x01010105
#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
#define OID_802_3_XMIT_ONE_COLLISION 0x01020102
#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
#define OID_802_3_XMIT_DEFERRED 0x01020201
#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
#define OID_802_3_RCV_OVERRUN 0x01020203
#define OID_802_3_XMIT_UNDERRUN 0x01020204
#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
/* OID_GEN_MINIPORT_INFO constants */
#define NDIS_MINIPORT_BUS_MASTER 0x00000001
#define NDIS_MINIPORT_WDM_DRIVER 0x00000002
#define NDIS_MINIPORT_SG_LIST 0x00000004
#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008
#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010
#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020
#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040
#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080
#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100
#define NDIS_MINIPORT_IS_NDIS_5 0x00000200
#define NDIS_MINIPORT_IS_CO 0x00000400
#define NDIS_MINIPORT_DESERIALIZE 0x00000800
#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000
#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000
#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000
#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000
#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000
#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000
#define NDIS_MINIPORT_HIDDEN 0x00040000
#define NDIS_MINIPORT_SWENUM 0x00080000
#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000
#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000
#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000
#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000
#define NDIS_MINIPORT_64BITS_DMA 0x01000000
#define NDIS_MEDIUM_802_3 0x00000000
#define NDIS_MEDIUM_802_5 0x00000001
#define NDIS_MEDIUM_FDDI 0x00000002
#define NDIS_MEDIUM_WAN 0x00000003
#define NDIS_MEDIUM_LOCAL_TALK 0x00000004
#define NDIS_MEDIUM_DIX 0x00000005
#define NDIS_MEDIUM_ARCENT_RAW 0x00000006
#define NDIS_MEDIUM_ARCENT_878_2 0x00000007
#define NDIS_MEDIUM_ATM 0x00000008
#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009
#define NDIS_MEDIUM_IRDA 0x0000000A
#define NDIS_MEDIUM_BPC 0x0000000B
#define NDIS_MEDIUM_CO_WAN 0x0000000C
#define NDIS_MEDIUM_1394 0x0000000D
#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
#define NDIS_PACKET_TYPE_SMT 0x00000040
#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
#define NDIS_PACKET_TYPE_GROUP 0x00000100
#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
#define NDIS_MEDIA_STATE_CONNECTED 0x00000000
#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001
#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001
#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002
#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004
#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008
#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010
#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020
#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040
#define NDIS_MAC_OPTION_RESERVED 0x80000000
#endif /* _USBGADGET_NDIS_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,718 @@
/*
* PXA27x USB device driver for u-boot.
*
* Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
* Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
* Copyright (C) 2008 Vivek Kutal <vivek.kutal@azingo.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.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*/
#include <common.h>
#include <config.h>
#include <asm/byteorder.h>
#include <usbdevice.h>
#include <asm/arch/hardware.h>
#include <asm/io.h>
#include <usb/pxa27x_udc.h>
#include "ep0.h"
/* number of endpoints on this UDC */
#define UDC_MAX_ENDPOINTS 24
static struct urb *ep0_urb;
static struct usb_device_instance *udc_device;
static int ep0state = EP0_IDLE;
#ifdef USBDDBG
static void udc_dump_buffer(char *name, u8 *buf, int len)
{
usbdbg("%s - buf %p, len %d", name, buf, len);
print_buffer(0, buf, 1, len, 0);
}
#else
#define udc_dump_buffer(name, buf, len) /* void */
#endif
static inline void udc_ack_int_UDCCR(int mask)
{
writel(readl(USIR1) | mask, USIR1);
}
/*
* If the endpoint has an active tx_urb, then the next packet of data from the
* URB is written to the tx FIFO.
* The total amount of data in the urb is given by urb->actual_length.
* The maximum amount of data that can be sent in any one packet is given by
* endpoint->tx_packetSize.
* The number of data bytes from this URB that have already been transmitted
* is given by endpoint->sent.
* endpoint->last is updated by this routine with the number of data bytes
* transmitted in this packet.
*/
static int udc_write_urb(struct usb_endpoint_instance *endpoint)
{
struct urb *urb = endpoint->tx_urb;
int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
u32 *data32 = (u32 *) urb->buffer;
u8 *data8 = (u8 *) urb->buffer;
unsigned int i, n, w, b, is_short;
int timeout = 2000; /* 2ms */
if (!urb || !urb->actual_length)
return -1;
n = MIN(urb->actual_length - endpoint->sent, endpoint->tx_packetSize);
if (n <= 0)
return -1;
usbdbg("write urb on ep %d", ep_num);
#if defined(USBDDBG) && defined(USBDPARANOIA)
usbdbg("urb: buf %p, buf_len %d, actual_len %d",
urb->buffer, urb->buffer_length, urb->actual_length);
usbdbg("endpoint: sent %d, tx_packetSize %d, last %d",
endpoint->sent, endpoint->tx_packetSize, endpoint->last);
#endif
is_short = n != endpoint->tx_packetSize;
w = n / 4;
b = n % 4;
usbdbg("n %d%s w %d b %d", n, is_short ? "-s" : "", w, b);
udc_dump_buffer("urb write", data8 + endpoint->sent, n);
/* Prepare for data send */
if (ep_num)
writel(UDCCSR_PC ,UDCCSN(ep_num));
for (i = 0; i < w; i++)
writel(data32[endpoint->sent / 4 + i], UDCDN(ep_num));
for (i = 0; i < b; i++)
writeb(data8[endpoint->sent + w * 4 + i], UDCDN(ep_num));
/* Set "Packet Complete" if less data then tx_packetSize */
if (is_short)
writel(ep_num ? UDCCSR_SP : UDCCSR0_IPR, UDCCSN(ep_num));
/* Wait for data sent */
if (ep_num) {
while (!(readl(UDCCSN(ep_num)) & UDCCSR_PC)) {
if (timeout-- == 0)
return -1;
else
udelay(1);
}
}
endpoint->last = n;
if (ep_num) {
usbd_tx_complete(endpoint);
} else {
endpoint->sent += n;
endpoint->last -= n;
}
if (endpoint->sent >= urb->actual_length) {
urb->actual_length = 0;
endpoint->sent = 0;
endpoint->last = 0;
}
if ((endpoint->sent >= urb->actual_length) && (!ep_num)) {
usbdbg("ep0 IN stage done");
if (is_short)
ep0state = EP0_IDLE;
else
ep0state = EP0_XFER_COMPLETE;
}
return 0;
}
static int udc_read_urb(struct usb_endpoint_instance *endpoint)
{
struct urb *urb = endpoint->rcv_urb;
int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
u32 *data32 = (u32 *) urb->buffer;
unsigned int i, n, is_short ;
usbdbg("read urb on ep %d", ep_num);
#if defined(USBDDBG) && defined(USBDPARANOIA)
usbdbg("urb: buf %p, buf_len %d, actual_len %d",
urb->buffer, urb->buffer_length, urb->actual_length);
usbdbg("endpoint: rcv_packetSize %d",
endpoint->rcv_packetSize);
#endif
if (readl(UDCCSN(ep_num)) & UDCCSR_BNE)
n = readl(UDCBCN(ep_num)) & 0x3ff;
else /* zlp */
n = 0;
is_short = n != endpoint->rcv_packetSize;
usbdbg("n %d%s", n, is_short ? "-s" : "");
for (i = 0; i < n; i += 4)
data32[urb->actual_length / 4 + i / 4] = readl(UDCDN(ep_num));
udc_dump_buffer("urb read", (u8 *) data32, urb->actual_length + n);
usbd_rcv_complete(endpoint, n, 0);
return 0;
}
static int udc_read_urb_ep0(void)
{
u32 *data32 = (u32 *) ep0_urb->buffer;
u8 *data8 = (u8 *) ep0_urb->buffer;
unsigned int i, n, w, b;
usbdbg("read urb on ep 0");
#if defined(USBDDBG) && defined(USBDPARANOIA)
usbdbg("urb: buf %p, buf_len %d, actual_len %d",
ep0_urb->buffer, ep0_urb->buffer_length, ep0_urb->actual_length);
#endif
n = readl(UDCBCR0);
w = n / 4;
b = n % 4;
for (i = 0; i < w; i++) {
data32[ep0_urb->actual_length / 4 + i] = readl(UDCDN(0));
/* ep0_urb->actual_length += 4; */
}
for (i = 0; i < b; i++) {
data8[ep0_urb->actual_length + w * 4 + i] = readb(UDCDN(0));
/* ep0_urb->actual_length++; */
}
ep0_urb->actual_length += n;
udc_dump_buffer("urb read", (u8 *) data32, ep0_urb->actual_length);
writel(UDCCSR0_OPC | UDCCSR0_IPR, UDCCSR0);
if (ep0_urb->actual_length == ep0_urb->device_request.wLength)
return 1;
return 0;
}
static void udc_handle_ep0(struct usb_endpoint_instance *endpoint)
{
u32 udccsr0 = readl(UDCCSR0);
u32 *data = (u32 *) &ep0_urb->device_request;
int i;
usbdbg("udccsr0 %x", udccsr0);
/* Clear stall status */
if (udccsr0 & UDCCSR0_SST) {
usberr("clear stall status");
writel(UDCCSR0_SST, UDCCSR0);
ep0state = EP0_IDLE;
}
/* previous request unfinished? non-error iff back-to-back ... */
if ((udccsr0 & UDCCSR0_SA) != 0 && ep0state != EP0_IDLE)
ep0state = EP0_IDLE;
switch (ep0state) {
case EP0_IDLE:
udccsr0 = readl(UDCCSR0);
/* Start control request? */
if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE))
== (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)) {
/* Read SETUP packet.
* SETUP packet size is 8 bytes (aka 2 words)
*/
usbdbg("try reading SETUP packet");
for (i = 0; i < 2; i++) {
if ((readl(UDCCSR0) & UDCCSR0_RNE) == 0) {
usberr("setup packet too short:%d", i);
goto stall;
}
data[i] = readl(UDCDR0);
}
writel(readl(UDCCSR0) | UDCCSR0_OPC | UDCCSR0_SA, UDCCSR0);
if ((readl(UDCCSR0) & UDCCSR0_RNE) != 0) {
usberr("setup packet too long");
goto stall;
}
udc_dump_buffer("ep0 setup read", (u8 *) data, 8);
if (ep0_urb->device_request.wLength == 0) {
usbdbg("Zero Data control Packet\n");
if (ep0_recv_setup(ep0_urb)) {
usberr("Invalid Setup Packet\n");
udc_dump_buffer("ep0 setup read",
(u8 *)data, 8);
goto stall;
}
writel(UDCCSR0_IPR, UDCCSR0);
ep0state = EP0_IDLE;
} else {
/* Check direction */
if ((ep0_urb->device_request.bmRequestType &
USB_REQ_DIRECTION_MASK)
== USB_REQ_HOST2DEVICE) {
ep0state = EP0_OUT_DATA;
ep0_urb->buffer =
(u8 *)ep0_urb->buffer_data;
ep0_urb->buffer_length =
sizeof(ep0_urb->buffer_data);
ep0_urb->actual_length = 0;
writel(UDCCSR0_IPR, UDCCSR0);
} else {
/* The ep0_recv_setup function has
* already placed our response packet
* data in ep0_urb->buffer and the
* packet length in
* ep0_urb->actual_length.
*/
if (ep0_recv_setup(ep0_urb)) {
stall:
usberr("Invalid setup packet");
udc_dump_buffer("ep0 setup read"
, (u8 *) data, 8);
ep0state = EP0_IDLE;
writel(UDCCSR0_SA |
UDCCSR0_OPC | UDCCSR0_FST |
UDCCS0_FTF, UDCCSR0);
return;
}
endpoint->tx_urb = ep0_urb;
endpoint->sent = 0;
usbdbg("EP0_IN_DATA");
ep0state = EP0_IN_DATA;
if (udc_write_urb(endpoint) < 0)
goto stall;
}
}
return;
} else if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA))
== (UDCCSR0_OPC|UDCCSR0_SA)) {
usberr("Setup Active but no data. Stalling ....\n");
goto stall;
} else {
usbdbg("random early IRQs");
/* Some random early IRQs:
* - we acked FST
* - IPR cleared
* - OPC got set, without SA (likely status stage)
*/
writel(udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC), UDCCSR0);
}
break;
case EP0_OUT_DATA:
if ((udccsr0 & UDCCSR0_OPC) && !(udccsr0 & UDCCSR0_SA)) {
if (udc_read_urb_ep0()) {
read_complete:
ep0state = EP0_IDLE;
if (ep0_recv_setup(ep0_urb)) {
/* Not a setup packet, stall next
* EP0 transaction
*/
udc_dump_buffer("ep0 setup read",
(u8 *) data, 8);
usberr("can't parse setup packet\n");
goto stall;
}
}
} else if (!(udccsr0 & UDCCSR0_OPC) &&
!(udccsr0 & UDCCSR0_IPR)) {
if (ep0_urb->device_request.wLength ==
ep0_urb->actual_length)
goto read_complete;
usberr("Premature Status\n");
ep0state = EP0_IDLE;
}
break;
case EP0_IN_DATA:
/* GET_DESCRIPTOR etc */
if (udccsr0 & UDCCSR0_OPC) {
writel(UDCCSR0_OPC | UDCCSR0_FTF, UDCCSR0);
usberr("ep0in premature status");
ep0state = EP0_IDLE;
} else {
/* irq was IPR clearing */
if (udc_write_urb(endpoint) < 0) {
usberr("ep0_write_error\n");
goto stall;
}
}
break;
case EP0_XFER_COMPLETE:
writel(UDCCSR0_IPR, UDCCSR0);
ep0state = EP0_IDLE;
break;
default:
usbdbg("Default\n");
}
writel(USIR0_IR0, USIR0);
}
static void udc_handle_ep(struct usb_endpoint_instance *endpoint)
{
int ep_addr = endpoint->endpoint_address;
int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
int ep_isout = (ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT;
u32 flags = readl(UDCCSN(ep_num)) & (UDCCSR_SST | UDCCSR_TRN);
if (flags)
writel(flags, UDCCSN(ep_num));
if (ep_isout)
udc_read_urb(endpoint);
else
udc_write_urb(endpoint);
writel(UDCCSR_PC, UDCCSN(ep_num));
}
static void udc_state_changed(void)
{
int config, interface, alternate;
writel(readl(UDCCR) | UDCCR_SMAC, UDCCR);
config = (readl(UDCCR) & UDCCR_ACN) >> UDCCR_ACN_S;
interface = (readl(UDCCR) & UDCCR_AIN) >> UDCCR_AIN_S;
alternate = (readl(UDCCR) & UDCCR_AAISN) >> UDCCR_AAISN_S;
usbdbg("New UDC settings are: conf %d - inter %d - alter %d",
config, interface, alternate);
usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0);
writel(UDCISR1_IRCC, UDCISR1);
}
void udc_irq(void)
{
int handled;
struct usb_endpoint_instance *endpoint;
int ep_num, i;
u32 udcisr0;
do {
handled = 0;
/* Suspend Interrupt Request */
if (readl(USIR1) & UDCCR_SUSIR) {
usbdbg("Suspend\n");
udc_ack_int_UDCCR(UDCCR_SUSIR);
handled = 1;
ep0state = EP0_IDLE;
}
/* Resume Interrupt Request */
if (readl(USIR1) & UDCCR_RESIR) {
udc_ack_int_UDCCR(UDCCR_RESIR);
handled = 1;
usbdbg("USB resume\n");
}
if (readl(USIR1) & (1<<31)) {
handled = 1;
udc_state_changed();
}
/* Reset Interrupt Request */
if (readl(USIR1) & UDCCR_RSTIR) {
udc_ack_int_UDCCR(UDCCR_RSTIR);
handled = 1;
usbdbg("Reset\n");
usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
} else {
if (readl(USIR0))
usbdbg("UISR0: %x \n", readl(USIR0));
if (readl(USIR0) & 0x2)
writel(0x2, USIR0);
/* Control traffic */
if (readl(USIR0) & USIR0_IR0) {
handled = 1;
writel(USIR0_IR0, USIR0);
udc_handle_ep0(udc_device->bus->endpoint_array);
}
endpoint = udc_device->bus->endpoint_array;
for (i = 0; i < udc_device->bus->max_endpoints; i++) {
ep_num = (endpoint[i].endpoint_address) &
USB_ENDPOINT_NUMBER_MASK;
if (!ep_num)
continue;
udcisr0 = readl(UDCISR0);
if (udcisr0 &
UDCISR_INT(ep_num, UDC_INT_PACKETCMP)) {
writel(UDCISR_INT(ep_num, UDC_INT_PACKETCMP),
UDCISR0);
udc_handle_ep(&endpoint[i]);
}
}
}
} while (handled);
}
/* The UDCCR reg contains mask and interrupt status bits,
* so using '|=' isn't safe as it may ack an interrupt.
*/
#define UDCCR_OEN (1 << 31) /* On-the-Go Enable */
#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_UDE)
static inline void udc_set_mask_UDCCR(int mask)
{
writel((readl(UDCCR) & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS), UDCCR);
}
static inline void udc_clear_mask_UDCCR(int mask)
{
writel((readl(UDCCR) & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS), UDCCR);
}
static void pio_irq_enable(int ep_num)
{
if (ep_num < 16)
writel(readl(UDCICR0) | 3 << (ep_num * 2), UDCICR0);
else {
ep_num -= 16;
writel(readl(UDCICR1) | 3 << (ep_num * 2), UDCICR1);
}
}
/*
* udc_set_nak
*
* Allow upper layers to signal lower layers should not accept more RX data
*/
void udc_set_nak(int ep_num)
{
/* TODO */
}
/*
* udc_unset_nak
*
* Suspend sending of NAK tokens for DATA OUT tokens on a given endpoint.
* Switch off NAKing on this endpoint to accept more data output from host.
*/
void udc_unset_nak(int ep_num)
{
/* TODO */
}
int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
{
return udc_write_urb(endpoint);
}
/* Associate a physical endpoint with endpoint instance */
void udc_setup_ep(struct usb_device_instance *device, unsigned int id,
struct usb_endpoint_instance *endpoint)
{
int ep_num, ep_addr, ep_isout, ep_type, ep_size;
int config, interface, alternate;
u32 tmp;
usbdbg("setting up endpoint id %d", id);
if (!endpoint) {
usberr("endpoint void!");
return;
}
ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
if (ep_num >= UDC_MAX_ENDPOINTS) {
usberr("unable to setup ep %d!", ep_num);
return;
}
pio_irq_enable(ep_num);
if (ep_num == 0) {
/* Done for ep0 */
return;
}
config = 1;
interface = 0;
alternate = 0;
usbdbg("config %d - interface %d - alternate %d",
config, interface, alternate);
ep_addr = endpoint->endpoint_address;
ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
ep_isout = (ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT;
ep_type = ep_isout ? endpoint->rcv_attributes : endpoint->tx_attributes;
ep_size = ep_isout ? endpoint->rcv_packetSize : endpoint->tx_packetSize;
usbdbg("addr %x, num %d, dir %s, type %s, packet size %d",
ep_addr, ep_num,
ep_isout ? "out" : "in",
ep_type == USB_ENDPOINT_XFER_ISOC ? "isoc" :
ep_type == USB_ENDPOINT_XFER_BULK ? "bulk" :
ep_type == USB_ENDPOINT_XFER_INT ? "int" : "???",
ep_size
);
/* Configure UDCCRx */
tmp = 0;
tmp |= (config << UDCCONR_CN_S) & UDCCONR_CN;
tmp |= (interface << UDCCONR_IN_S) & UDCCONR_IN;
tmp |= (alternate << UDCCONR_AISN_S) & UDCCONR_AISN;
tmp |= (ep_num << UDCCONR_EN_S) & UDCCONR_EN;
tmp |= (ep_type << UDCCONR_ET_S) & UDCCONR_ET;
tmp |= ep_isout ? 0 : UDCCONR_ED;
tmp |= (ep_size << UDCCONR_MPS_S) & UDCCONR_MPS;
tmp |= UDCCONR_EE;
writel(tmp, UDCCN(ep_num));
usbdbg("UDCCR%c = %x", 'A' + ep_num-1, readl(UDCCN(ep_num)));
usbdbg("UDCCSR%c = %x", 'A' + ep_num-1, readl(UDCCSN(ep_num)));
}
/* Connect the USB device to the bus */
void udc_connect(void)
{
usbdbg("UDC connect");
#ifdef CONFIG_USB_DEV_PULLUP_GPIO
/* Turn on the USB connection by enabling the pullup resistor */
set_GPIO_mode(CONFIG_USB_DEV_PULLUP_GPIO | GPIO_OUT);
writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPSR(CONFIG_USB_DEV_PULLUP_GPIO));
#else
/* Host port 2 transceiver D+ pull up enable */
writel(readl(UP2OCR) | UP2OCR_DPPUE, UP2OCR);
#endif
}
/* Disconnect the USB device to the bus */
void udc_disconnect(void)
{
usbdbg("UDC disconnect");
#ifdef CONFIG_USB_DEV_PULLUP_GPIO
/* Turn off the USB connection by disabling the pullup resistor */
writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPCR(CONFIG_USB_DEV_PULLUP_GPIO));
#else
/* Host port 2 transceiver D+ pull up disable */
writel(readl(UP2OCR) & ~UP2OCR_DPPUE, UP2OCR);
#endif
}
/* Switch on the UDC */
void udc_enable(struct usb_device_instance *device)
{
ep0state = EP0_IDLE;
/* enable endpoint 0, A, B's Packet Complete Interrupt. */
writel(0xffffffff, UDCICR0);
writel(0xa8000000, UDCICR1);
/* clear the interrupt status/control registers */
writel(0xffffffff, UDCISR0);
writel(0xffffffff, UDCISR1);
/* set UDC-enable */
udc_set_mask_UDCCR(UDCCR_UDE);
udc_device = device;
if (!ep0_urb)
ep0_urb = usbd_alloc_urb(udc_device,
udc_device->bus->endpoint_array);
else
usbinfo("ep0_urb %p already allocated", ep0_urb);
usbdbg("UDC Enabled\n");
}
/* Need to check this again */
void udc_disable(void)
{
usbdbg("disable UDC");
udc_clear_mask_UDCCR(UDCCR_UDE);
/* Disable clock for USB device */
writel(readl(CKEN) & ~CKEN11_USB, CKEN);
/* Free ep0 URB */
if (ep0_urb) {
usbd_dealloc_urb(ep0_urb);
ep0_urb = NULL;
}
/* Reset device pointer */
udc_device = NULL;
}
/* Allow udc code to do any additional startup */
void udc_startup_events(struct usb_device_instance *device)
{
/* The DEVICE_INIT event puts the USB device in the state STATE_INIT */
usbd_device_event_irq(device, DEVICE_INIT, 0);
/* The DEVICE_CREATE event puts the USB device in the state
* STATE_ATTACHED */
usbd_device_event_irq(device, DEVICE_CREATE, 0);
/* Some USB controller driver implementations signal
* DEVICE_HUB_CONFIGURED and DEVICE_RESET events here.
* DEVICE_HUB_CONFIGURED causes a transition to the state
* STATE_POWERED, and DEVICE_RESET causes a transition to
* the state STATE_DEFAULT.
*/
udc_enable(device);
}
/* Initialize h/w stuff */
int udc_init(void)
{
udc_device = NULL;
usbdbg("PXA27x usbd start");
/* Enable clock for USB device */
writel(readl(CKEN) | CKEN11_USB, CKEN);
/* Disable the UDC */
udc_clear_mask_UDCCR(UDCCR_UDE);
/* Disable IRQs: we don't use them */
writel(0, UDCICR0);
writel(0, UDCICR1);
return 0;
}

View file

@ -0,0 +1,271 @@
/* linux/arch/arm/plat-s3c/include/plat/regs-otg.h
*
* Copyright (C) 2004 Herbert Poetzl <herbert@13thfloor.at>
*
* Registers remapping:
* Lukasz Majewski <l.majewski@samsumg.com>
*
* This include file 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.
*/
#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H
#define __ASM_ARCH_REGS_USB_OTG_HS_H
/* USB2.0 OTG Controller register */
struct s3c_usbotg_phy {
u32 phypwr;
u32 phyclk;
u32 rstcon;
};
/* Device Logical IN Endpoint-Specific Registers */
struct s3c_dev_in_endp {
u32 diepctl;
u8 res1[4];
u32 diepint;
u8 res2[4];
u32 dieptsiz;
u32 diepdma;
u8 res3[4];
u32 diepdmab;
};
/* Device Logical OUT Endpoint-Specific Registers */
struct s3c_dev_out_endp {
u32 doepctl;
u8 res1[4];
u32 doepint;
u8 res2[4];
u32 doeptsiz;
u32 doepdma;
u8 res3[4];
u32 doepdmab;
};
struct ep_fifo {
u32 fifo;
u8 res[4092];
};
/* USB2.0 OTG Controller register */
struct s3c_usbotg_reg {
/* Core Global Registers */
u32 gotgctl; /* OTG Control & Status */
u32 gotgint; /* OTG Interrupt */
u32 gahbcfg; /* Core AHB Configuration */
u32 gusbcfg; /* Core USB Configuration */
u32 grstctl; /* Core Reset */
u32 gintsts; /* Core Interrupt */
u32 gintmsk; /* Core Interrupt Mask */
u32 grxstsr; /* Receive Status Debug Read/Status Read */
u32 grxstsp; /* Receive Status Debug Pop/Status Pop */
u32 grxfsiz; /* Receive FIFO Size */
u32 gnptxfsiz; /* Non-Periodic Transmit FIFO Size */
u8 res1[216];
u32 dieptxf[15]; /* Device Periodic Transmit FIFO size register */
u8 res2[1728];
/* Device Configuration */
u32 dcfg; /* Device Configuration Register */
u32 dctl; /* Device Control */
u32 dsts; /* Device Status */
u8 res3[4];
u32 diepmsk; /* Device IN Endpoint Common Interrupt Mask */
u32 doepmsk; /* Device OUT Endpoint Common Interrupt Mask */
u32 daint; /* Device All Endpoints Interrupt */
u32 daintmsk; /* Device All Endpoints Interrupt Mask */
u8 res4[224];
struct s3c_dev_in_endp in_endp[16];
struct s3c_dev_out_endp out_endp[16];
u8 res5[768];
struct ep_fifo ep[16];
};
/*===================================================================== */
/*definitions related to CSR setting */
/* S3C_UDC_OTG_GOTGCTL */
#define B_SESSION_VALID (0x1<<19)
#define A_SESSION_VALID (0x1<<18)
/* S3C_UDC_OTG_GAHBCFG */
#define PTXFE_HALF (0<<8)
#define PTXFE_ZERO (1<<8)
#define NPTXFE_HALF (0<<7)
#define NPTXFE_ZERO (1<<7)
#define MODE_SLAVE (0<<5)
#define MODE_DMA (1<<5)
#define BURST_SINGLE (0<<1)
#define BURST_INCR (1<<1)
#define BURST_INCR4 (3<<1)
#define BURST_INCR8 (5<<1)
#define BURST_INCR16 (7<<1)
#define GBL_INT_UNMASK (1<<0)
#define GBL_INT_MASK (0<<0)
/* S3C_UDC_OTG_GRSTCTL */
#define AHB_MASTER_IDLE (1u<<31)
#define CORE_SOFT_RESET (0x1<<0)
/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */
#define INT_RESUME (1u<<31)
#define INT_DISCONN (0x1<<29)
#define INT_CONN_ID_STS_CNG (0x1<<28)
#define INT_OUT_EP (0x1<<19)
#define INT_IN_EP (0x1<<18)
#define INT_ENUMDONE (0x1<<13)
#define INT_RESET (0x1<<12)
#define INT_SUSPEND (0x1<<11)
#define INT_EARLY_SUSPEND (0x1<<10)
#define INT_NP_TX_FIFO_EMPTY (0x1<<5)
#define INT_RX_FIFO_NOT_EMPTY (0x1<<4)
#define INT_SOF (0x1<<3)
#define INT_DEV_MODE (0x0<<0)
#define INT_HOST_MODE (0x1<<1)
#define INT_GOUTNakEff (0x01<<7)
#define INT_GINNakEff (0x01<<6)
#define FULL_SPEED_CONTROL_PKT_SIZE 8
#define FULL_SPEED_BULK_PKT_SIZE 64
#define HIGH_SPEED_CONTROL_PKT_SIZE 64
#define HIGH_SPEED_BULK_PKT_SIZE 512
#define RX_FIFO_SIZE (1024*4)
#define NPTX_FIFO_SIZE (1024*4)
#define PTX_FIFO_SIZE (1536*1)
#define DEPCTL_TXFNUM_0 (0x0<<22)
#define DEPCTL_TXFNUM_1 (0x1<<22)
#define DEPCTL_TXFNUM_2 (0x2<<22)
#define DEPCTL_TXFNUM_3 (0x3<<22)
#define DEPCTL_TXFNUM_4 (0x4<<22)
/* Enumeration speed */
#define USB_HIGH_30_60MHZ (0x0<<1)
#define USB_FULL_30_60MHZ (0x1<<1)
#define USB_LOW_6MHZ (0x2<<1)
#define USB_FULL_48MHZ (0x3<<1)
/* S3C_UDC_OTG_GRXSTSP STATUS */
#define OUT_PKT_RECEIVED (0x2<<17)
#define OUT_TRANSFER_COMPLELTED (0x3<<17)
#define SETUP_TRANSACTION_COMPLETED (0x4<<17)
#define SETUP_PKT_RECEIVED (0x6<<17)
#define GLOBAL_OUT_NAK (0x1<<17)
/* S3C_UDC_OTG_DCTL device control register */
#define NORMAL_OPERATION (0x1<<0)
#define SOFT_DISCONNECT (0x1<<1)
/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */
#define DAINT_OUT_BIT (16)
#define DAINT_MASK (0xFFFF)
/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device
control IN/OUT endpoint 0 control register */
#define DEPCTL_EPENA (0x1<<31)
#define DEPCTL_EPDIS (0x1<<30)
#define DEPCTL_SETD1PID (0x1<<29)
#define DEPCTL_SETD0PID (0x1<<28)
#define DEPCTL_SNAK (0x1<<27)
#define DEPCTL_CNAK (0x1<<26)
#define DEPCTL_STALL (0x1<<21)
#define DEPCTL_TYPE_BIT (18)
#define DEPCTL_TYPE_MASK (0x3<<18)
#define DEPCTL_CTRL_TYPE (0x0<<18)
#define DEPCTL_ISO_TYPE (0x1<<18)
#define DEPCTL_BULK_TYPE (0x2<<18)
#define DEPCTL_INTR_TYPE (0x3<<18)
#define DEPCTL_USBACTEP (0x1<<15)
#define DEPCTL_NEXT_EP_BIT (11)
#define DEPCTL_MPS_BIT (0)
#define DEPCTL_MPS_MASK (0x7FF)
#define DEPCTL0_MPS_64 (0x0<<0)
#define DEPCTL0_MPS_32 (0x1<<0)
#define DEPCTL0_MPS_16 (0x2<<0)
#define DEPCTL0_MPS_8 (0x3<<0)
#define DEPCTL_MPS_BULK_512 (512<<0)
#define DEPCTL_MPS_INT_MPS_16 (16<<0)
#define DIEPCTL0_NEXT_EP_BIT (11)
/* S3C_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint
common interrupt mask register */
/* S3C_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */
#define BACK2BACK_SETUP_RECEIVED (0x1<<6)
#define INTKNEPMIS (0x1<<5)
#define INTKN_TXFEMP (0x1<<4)
#define NON_ISO_IN_EP_TIMEOUT (0x1<<3)
#define CTRL_OUT_EP_SETUP_PHASE_DONE (0x1<<3)
#define AHB_ERROR (0x1<<2)
#define EPDISBLD (0x1<<1)
#define TRANSFER_DONE (0x1<<0)
#define USB_PHY_CTRL_EN0 (0x1 << 0)
/* OPHYPWR */
#define PHY_0_SLEEP (0x1 << 5)
#define OTG_DISABLE_0 (0x1 << 4)
#define ANALOG_PWRDOWN (0x1 << 3)
#define FORCE_SUSPEND_0 (0x1 << 0)
/* URSTCON */
#define HOST_SW_RST (0x1 << 4)
#define PHY_SW_RST1 (0x1 << 3)
#define PHYLNK_SW_RST (0x1 << 2)
#define LINK_SW_RST (0x1 << 1)
#define PHY_SW_RST0 (0x1 << 0)
/* OPHYCLK */
#define COMMON_ON_N1 (0x1 << 7)
#define COMMON_ON_N0 (0x1 << 4)
#define ID_PULLUP0 (0x1 << 2)
#define CLK_SEL_24MHZ (0x3 << 0)
#define CLK_SEL_12MHZ (0x2 << 0)
#define CLK_SEL_48MHZ (0x0 << 0)
/* Device Configuration Register DCFG */
#define DEV_SPEED_HIGH_SPEED_20 (0x0 << 0)
#define DEV_SPEED_FULL_SPEED_20 (0x1 << 0)
#define DEV_SPEED_LOW_SPEED_11 (0x2 << 0)
#define DEV_SPEED_FULL_SPEED_11 (0x3 << 0)
#define EP_MISS_CNT(x) (x << 18)
#define DEVICE_ADDRESS(x) (x << 4)
/* Core Reset Register (GRSTCTL) */
#define TX_FIFO_FLUSH (0x1 << 5)
#define RX_FIFO_FLUSH (0x1 << 4)
#define TX_FIFO_NUMBER(x) (x << 6)
#define TX_FIFO_FLUSH_ALL TX_FIFO_NUMBER(0x10)
/* Masks definitions */
#define GINTMSK_INIT (INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\
| INT_RESET | INT_SUSPEND)
#define DOEPMSK_INIT (CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE)
#define DIEPMSK_INIT (NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE)
#define GAHBCFG_INIT (PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\
| GBL_INT_UNMASK)
/* Device Endpoint X Transfer Size Register (DIEPTSIZX) */
#define DIEPT_SIZ_PKT_CNT(x) (x << 19)
#define DIEPT_SIZ_XFER_SIZE(x) (x << 0)
/* Device OUT Endpoint X Transfer Size Register (DOEPTSIZX) */
#define DOEPT_SIZ_PKT_CNT(x) (x << 19)
#define DOEPT_SIZ_XFER_SIZE(x) (x << 0)
#define DOEPT_SIZ_XFER_SIZE_MAX_EP0 (0x7F << 0)
#define DOEPT_SIZ_XFER_SIZE_MAX_EP (0x7FFF << 0)
/* Device Endpoint-N Control Register (DIEPCTLn/DOEPCTLn) */
#define DIEPCTL_TX_FIFO_NUM(x) (x << 22)
#define DIEPCTL_TX_FIFO_NUM_MASK (~DIEPCTL_TX_FIFO_NUM(0xF))
/* Device ALL Endpoints Interrupt Register (DAINT) */
#define DAINT_IN_EP_INT(x) (x << 0)
#define DAINT_OUT_EP_INT(x) (x << 16)
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,260 @@
/*
* RNDIS Definitions for Remote NDIS
*
* Authors: Benedikt Spranger, Pengutronix
* Robert Schwebel, Pengutronix
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2, as published by the Free Software Foundation.
*
* This software was originally developed in conformance with
* Microsoft's Remote NDIS Specification License Agreement.
*/
#ifndef _USBGADGET_RNDIS_H
#define _USBGADGET_RNDIS_H
#include "ndis.h"
/*
* By default rndis_signal_disconnect does not send status message about
* RNDIS disconnection to USB host (indicated as cable disconnected).
* Define RNDIS_COMPLETE_SIGNAL_DISCONNECT to send it.
* However, this will cause 1 sec delay on Ethernet device halt.
* Usually you do not need to define it. Mostly usable for debugging.
*/
#define RNDIS_MAXIMUM_FRAME_SIZE 1518
#define RNDIS_MAX_TOTAL_SIZE 1558
/* Remote NDIS Versions */
#define RNDIS_MAJOR_VERSION 1
#define RNDIS_MINOR_VERSION 0
/* Status Values */
#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */
#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */
#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */
#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */
#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */
#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */
/*
* For all not specified status messages:
* RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx
*/
/* Message Set for Connectionless (802.3) Devices */
#define REMOTE_NDIS_PACKET_MSG 0x00000001U
#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */
#define REMOTE_NDIS_HALT_MSG 0x00000003U
#define REMOTE_NDIS_QUERY_MSG 0x00000004U
#define REMOTE_NDIS_SET_MSG 0x00000005U
#define REMOTE_NDIS_RESET_MSG 0x00000006U
#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U
#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U
/* Message completion */
#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U
#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U
#define REMOTE_NDIS_SET_CMPLT 0x80000005U
#define REMOTE_NDIS_RESET_CMPLT 0x80000006U
#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U
/* Device Flags */
#define RNDIS_DF_CONNECTIONLESS 0x00000001U
#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U
#define RNDIS_MEDIUM_802_3 0x00000000U
/* from drivers/net/sk98lin/h/skgepnmi.h */
#define OID_PNP_CAPABILITIES 0xFD010100
#define OID_PNP_SET_POWER 0xFD010101
#define OID_PNP_QUERY_POWER 0xFD010102
#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103
#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104
#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
typedef struct rndis_init_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 MajorVersion;
__le32 MinorVersion;
__le32 MaxTransferSize;
} rndis_init_msg_type;
typedef struct rndis_init_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
__le32 MajorVersion;
__le32 MinorVersion;
__le32 DeviceFlags;
__le32 Medium;
__le32 MaxPacketsPerTransfer;
__le32 MaxTransferSize;
__le32 PacketAlignmentFactor;
__le32 AFListOffset;
__le32 AFListSize;
} rndis_init_cmplt_type;
typedef struct rndis_halt_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
} rndis_halt_msg_type;
typedef struct rndis_query_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 OID;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
__le32 DeviceVcHandle;
} rndis_query_msg_type;
typedef struct rndis_query_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
} rndis_query_cmplt_type;
typedef struct rndis_set_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 OID;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
__le32 DeviceVcHandle;
} rndis_set_msg_type;
typedef struct rndis_set_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
} rndis_set_cmplt_type;
typedef struct rndis_reset_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 Reserved;
} rndis_reset_msg_type;
typedef struct rndis_reset_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 Status;
__le32 AddressingReset;
} rndis_reset_cmplt_type;
typedef struct rndis_indicate_status_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 Status;
__le32 StatusBufferLength;
__le32 StatusBufferOffset;
} rndis_indicate_status_msg_type;
typedef struct rndis_keepalive_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
} rndis_keepalive_msg_type;
typedef struct rndis_keepalive_cmplt_type {
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
} rndis_keepalive_cmplt_type;
struct rndis_packet_msg_type {
__le32 MessageType;
__le32 MessageLength;
__le32 DataOffset;
__le32 DataLength;
__le32 OOBDataOffset;
__le32 OOBDataLength;
__le32 NumOOBDataElements;
__le32 PerPacketInfoOffset;
__le32 PerPacketInfoLength;
__le32 VcHandle;
__le32 Reserved;
} __attribute__ ((packed));
struct rndis_config_parameter {
__le32 ParameterNameOffset;
__le32 ParameterNameLength;
__le32 ParameterType;
__le32 ParameterValueOffset;
__le32 ParameterValueLength;
};
/* implementation specific */
enum rndis_state {
RNDIS_UNINITIALIZED,
RNDIS_INITIALIZED,
RNDIS_DATA_INITIALIZED,
};
typedef struct rndis_resp_t {
struct list_head list;
u8 *buf;
u32 length;
int send;
} rndis_resp_t;
typedef struct rndis_params {
u8 confignr;
u8 used;
u16 saved_filter;
enum rndis_state state;
u32 medium;
u32 speed;
u32 media_state;
const u8 *host_mac;
u16 *filter;
struct eth_device *dev;
struct net_device_stats *stats;
int mtu;
u32 vendorID;
const char *vendorDescr;
int (*ack)(struct eth_device *);
struct list_head resp_queue;
} rndis_params;
/* RNDIS Message parser and other useless functions */
int rndis_msg_parser(u8 configNr, u8 *buf);
enum rndis_state rndis_get_state(int configNr);
int rndis_register(int (*rndis_control_ack)(struct eth_device *));
void rndis_deregister(int configNr);
int rndis_set_param_dev(u8 configNr, struct eth_device *dev, int mtu,
struct net_device_stats *stats, u16 *cdc_filter);
int rndis_set_param_vendor(u8 configNr, u32 vendorID,
const char *vendorDescr);
int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed);
void rndis_add_hdr(void *bug, int length);
int rndis_rm_hdr(void *bug, int length);
u8 *rndis_get_next_response(int configNr, u32 *length);
void rndis_free_response(int configNr, u8 *buf);
void rndis_uninit(int configNr);
int rndis_signal_connect(int configNr);
int rndis_signal_disconnect(int configNr);
extern void rndis_set_host_mac(int configNr, const u8 *addr);
int rndis_init(void);
void rndis_exit(void);
#endif /* _USBGADGET_RNDIS_H */

View file

@ -0,0 +1,899 @@
/*
* drivers/usb/gadget/s3c_udc_otg.c
* Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers
*
* Copyright (C) 2008 for Samsung Electronics
*
* BSP Support for Samsung's UDC driver
* available at:
* git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git
*
* State machine bugfixes:
* Marek Szyprowski <m.szyprowski@samsung.com>
*
* Ported to u-boot:
* Marek Szyprowski <m.szyprowski@samsung.com>
* Lukasz Majewski <l.majewski@samsumg.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.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#undef DEBUG
#include <common.h>
#include <asm/errno.h>
#include <linux/list.h>
#include <malloc.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#include <asm/arch/gpio.h>
#include "regs-otg.h"
#include <usb/lin_gadget_compat.h>
/***********************************************************/
#define OTG_DMA_MODE 1
#define DEBUG_SETUP 0
#define DEBUG_EP0 0
#define DEBUG_ISR 0
#define DEBUG_OUT_EP 0
#define DEBUG_IN_EP 0
#include <usb/s3c_udc.h>
#define EP0_CON 0
#define EP_MASK 0xF
static char *state_names[] = {
"WAIT_FOR_SETUP",
"DATA_STATE_XMIT",
"DATA_STATE_NEED_ZLP",
"WAIT_FOR_OUT_STATUS",
"DATA_STATE_RECV",
"WAIT_FOR_COMPLETE",
"WAIT_FOR_OUT_COMPLETE",
"WAIT_FOR_IN_COMPLETE",
"WAIT_FOR_NULL_COMPLETE",
};
#define DRIVER_DESC "S3C HS USB OTG Device Driver, (c) Samsung Electronics"
#define DRIVER_VERSION "15 March 2009"
struct s3c_udc *the_controller;
static const char driver_name[] = "s3c-udc";
static const char driver_desc[] = DRIVER_DESC;
static const char ep0name[] = "ep0-control";
/* Max packet size*/
static unsigned int ep0_fifo_size = 64;
static unsigned int ep_fifo_size = 512;
static unsigned int ep_fifo_size2 = 1024;
static int reset_available = 1;
static struct usb_ctrlrequest *usb_ctrl;
static dma_addr_t usb_ctrl_dma_addr;
/*
Local declarations.
*/
static int s3c_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *);
static int s3c_ep_disable(struct usb_ep *ep);
static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags);
static void s3c_free_request(struct usb_ep *ep, struct usb_request *);
static int s3c_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags);
static int s3c_dequeue(struct usb_ep *ep, struct usb_request *);
static int s3c_fifo_status(struct usb_ep *ep);
static void s3c_fifo_flush(struct usb_ep *ep);
static void s3c_ep0_read(struct s3c_udc *dev);
static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep);
static void s3c_handle_ep0(struct s3c_udc *dev);
static int s3c_ep0_write(struct s3c_udc *dev);
static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req);
static void done(struct s3c_ep *ep, struct s3c_request *req, int status);
static void stop_activity(struct s3c_udc *dev,
struct usb_gadget_driver *driver);
static int udc_enable(struct s3c_udc *dev);
static void udc_set_address(struct s3c_udc *dev, unsigned char address);
static void reconfig_usbd(void);
static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed);
static void nuke(struct s3c_ep *ep, int status);
static int s3c_udc_set_halt(struct usb_ep *_ep, int value);
static void s3c_udc_set_nak(struct s3c_ep *ep);
void set_udc_gadget_private_data(void *p)
{
debug_cond(DEBUG_SETUP != 0,
"%s: the_controller: 0x%p, p: 0x%p\n", __func__,
the_controller, p);
the_controller->gadget.dev.device_data = p;
}
void *get_udc_gadget_private_data(struct usb_gadget *gadget)
{
return gadget->dev.device_data;
}
static struct usb_ep_ops s3c_ep_ops = {
.enable = s3c_ep_enable,
.disable = s3c_ep_disable,
.alloc_request = s3c_alloc_request,
.free_request = s3c_free_request,
.queue = s3c_queue,
.dequeue = s3c_dequeue,
.set_halt = s3c_udc_set_halt,
.fifo_status = s3c_fifo_status,
.fifo_flush = s3c_fifo_flush,
};
#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)
/***********************************************************/
void __iomem *regs_otg;
struct s3c_usbotg_reg *reg;
struct s3c_usbotg_phy *phy;
static unsigned int usb_phy_ctrl;
void otg_phy_init(struct s3c_udc *dev)
{
dev->pdata->phy_control(1);
/*USB PHY0 Enable */
printf("USB PHY0 Enable\n");
/* Enable PHY */
writel(readl(usb_phy_ctrl) | USB_PHY_CTRL_EN0, usb_phy_ctrl);
if (dev->pdata->usb_flags == PHY0_SLEEP) /* C210 Universal */
writel((readl(&phy->phypwr)
&~(PHY_0_SLEEP | OTG_DISABLE_0 | ANALOG_PWRDOWN)
&~FORCE_SUSPEND_0), &phy->phypwr);
else /* C110 GONI */
writel((readl(&phy->phypwr) &~(OTG_DISABLE_0 | ANALOG_PWRDOWN)
&~FORCE_SUSPEND_0), &phy->phypwr);
writel((readl(&phy->phyclk) &~(ID_PULLUP0 | COMMON_ON_N0)) |
CLK_SEL_24MHZ, &phy->phyclk); /* PLL 24Mhz */
writel((readl(&phy->rstcon) &~(LINK_SW_RST | PHYLNK_SW_RST))
| PHY_SW_RST0, &phy->rstcon);
udelay(10);
writel(readl(&phy->rstcon)
&~(PHY_SW_RST0 | LINK_SW_RST | PHYLNK_SW_RST), &phy->rstcon);
udelay(10);
}
void otg_phy_off(struct s3c_udc *dev)
{
/* reset controller just in case */
writel(PHY_SW_RST0, &phy->rstcon);
udelay(20);
writel(readl(&phy->phypwr) &~PHY_SW_RST0, &phy->rstcon);
udelay(20);
writel(readl(&phy->phypwr) | OTG_DISABLE_0 | ANALOG_PWRDOWN
| FORCE_SUSPEND_0, &phy->phypwr);
writel(readl(usb_phy_ctrl) &~USB_PHY_CTRL_EN0, usb_phy_ctrl);
writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)),
&phy->phyclk);
udelay(10000);
dev->pdata->phy_control(0);
}
/***********************************************************/
#include "s3c_udc_otg_xfer_dma.c"
/*
* udc_disable - disable USB device controller
*/
static void udc_disable(struct s3c_udc *dev)
{
debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
udc_set_address(dev, 0);
dev->ep0state = WAIT_FOR_SETUP;
dev->gadget.speed = USB_SPEED_UNKNOWN;
dev->usb_address = 0;
otg_phy_off(dev);
}
/*
* udc_reinit - initialize software state
*/
static void udc_reinit(struct s3c_udc *dev)
{
unsigned int i;
debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
/* device/ep0 records init */
INIT_LIST_HEAD(&dev->gadget.ep_list);
INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
dev->ep0state = WAIT_FOR_SETUP;
/* basic endpoint records init */
for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
struct s3c_ep *ep = &dev->ep[i];
if (i != 0)
list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
ep->desc = 0;
ep->stopped = 0;
INIT_LIST_HEAD(&ep->queue);
ep->pio_irqs = 0;
}
/* the rest was statically initialized, and is read-only */
}
#define BYTES2MAXP(x) (x / 8)
#define MAXP2BYTES(x) (x * 8)
/* until it's enabled, this UDC should be completely invisible
* to any USB host.
*/
static int udc_enable(struct s3c_udc *dev)
{
debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev);
otg_phy_init(dev);
reconfig_usbd();
debug_cond(DEBUG_SETUP != 0,
"S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n",
readl(&reg->gintmsk));
dev->gadget.speed = USB_SPEED_UNKNOWN;
return 0;
}
/*
Register entry point for the peripheral controller driver.
*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
struct s3c_udc *dev = the_controller;
int retval = 0;
unsigned long flags;
debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name");
if (!driver
|| (driver->speed != USB_SPEED_FULL
&& driver->speed != USB_SPEED_HIGH)
|| !driver->bind || !driver->disconnect || !driver->setup)
return -EINVAL;
if (!dev)
return -ENODEV;
if (dev->driver)
return -EBUSY;
spin_lock_irqsave(&dev->lock, flags);
/* first hook up the driver ... */
dev->driver = driver;
spin_unlock_irqrestore(&dev->lock, flags);
if (retval) { /* TODO */
printf("target device_add failed, error %d\n", retval);
return retval;
}
retval = driver->bind(&dev->gadget);
if (retval) {
debug_cond(DEBUG_SETUP != 0,
"%s: bind to driver --> error %d\n",
dev->gadget.name, retval);
dev->driver = 0;
return retval;
}
enable_irq(IRQ_OTG);
debug_cond(DEBUG_SETUP != 0,
"Registered gadget driver %s\n", dev->gadget.name);
udc_enable(dev);
return 0;
}
/*
* Unregister entry point for the peripheral controller driver.
*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct s3c_udc *dev = the_controller;
unsigned long flags;
if (!dev)
return -ENODEV;
if (!driver || driver != dev->driver)
return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
dev->driver = 0;
stop_activity(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
driver->unbind(&dev->gadget);
disable_irq(IRQ_OTG);
udc_disable(dev);
return 0;
}
/*
* done - retire a request; caller blocked irqs
*/
static void done(struct s3c_ep *ep, struct s3c_request *req, int status)
{
unsigned int stopped = ep->stopped;
debug("%s: %s %p, req = %p, stopped = %d\n",
__func__, ep->ep.name, ep, &req->req, stopped);
list_del_init(&req->queue);
if (likely(req->req.status == -EINPROGRESS))
req->req.status = status;
else
status = req->req.status;
if (status && status != -ESHUTDOWN) {
debug("complete %s req %p stat %d len %u/%u\n",
ep->ep.name, &req->req, status,
req->req.actual, req->req.length);
}
/* don't modify queue heads during completion callback */
ep->stopped = 1;
#ifdef DEBUG
printf("calling complete callback\n");
{
int i, len = req->req.length;
printf("pkt[%d] = ", req->req.length);
if (len > 64)
len = 64;
for (i = 0; i < len; i++) {
printf("%02x", ((u8 *)req->req.buf)[i]);
if ((i & 7) == 7)
printf(" ");
}
printf("\n");
}
#endif
spin_unlock(&ep->dev->lock);
req->req.complete(&ep->ep, &req->req);
spin_lock(&ep->dev->lock);
debug("callback completed\n");
ep->stopped = stopped;
}
/*
* nuke - dequeue ALL requests
*/
static void nuke(struct s3c_ep *ep, int status)
{
struct s3c_request *req;
debug("%s: %s %p\n", __func__, ep->ep.name, ep);
/* called with irqs blocked */
while (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct s3c_request, queue);
done(ep, req, status);
}
}
static void stop_activity(struct s3c_udc *dev,
struct usb_gadget_driver *driver)
{
int i;
/* don't disconnect drivers more than once */
if (dev->gadget.speed == USB_SPEED_UNKNOWN)
driver = 0;
dev->gadget.speed = USB_SPEED_UNKNOWN;
/* prevent new request submissions, kill any outstanding requests */
for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
struct s3c_ep *ep = &dev->ep[i];
ep->stopped = 1;
nuke(ep, -ESHUTDOWN);
}
/* report disconnect; the driver is already quiesced */
if (driver) {
spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
spin_lock(&dev->lock);
}
/* re-init driver-visible data structures */
udc_reinit(dev);
}
static void reconfig_usbd(void)
{
/* 2. Soft-reset OTG Core and then unreset again. */
int i;
unsigned int uTemp = writel(CORE_SOFT_RESET, &reg->grstctl);
debug("Reseting OTG controller\n");
writel(0<<15 /* PHY Low Power Clock sel*/
|1<<14 /* Non-Periodic TxFIFO Rewind Enable*/
|0x5<<10 /* Turnaround time*/
|0<<9 | 0<<8 /* [0:HNP disable,1:HNP enable][ 0:SRP disable*/
/* 1:SRP enable] H1= 1,1*/
|0<<7 /* Ulpi DDR sel*/
|0<<6 /* 0: high speed utmi+, 1: full speed serial*/
|0<<4 /* 0: utmi+, 1:ulpi*/
|1<<3 /* phy i/f 0:8bit, 1:16bit*/
|0x7<<0, /* HS/FS Timeout**/
&reg->gusbcfg);
/* 3. Put the OTG device core in the disconnected state.*/
uTemp = readl(&reg->dctl);
uTemp |= SOFT_DISCONNECT;
writel(uTemp, &reg->dctl);
udelay(20);
/* 4. Make the OTG device core exit from the disconnected state.*/
uTemp = readl(&reg->dctl);
uTemp = uTemp & ~SOFT_DISCONNECT;
writel(uTemp, &reg->dctl);
/* 5. Configure OTG Core to initial settings of device mode.*/
/* [][1: full speed(30Mhz) 0:high speed]*/
writel(EP_MISS_CNT(1) | DEV_SPEED_HIGH_SPEED_20, &reg->dcfg);
mdelay(1);
/* 6. Unmask the core interrupts*/
writel(GINTMSK_INIT, &reg->gintmsk);
/* 7. Set NAK bit of EP0, EP1, EP2*/
writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->out_endp[EP0_CON].doepctl);
writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->in_endp[EP0_CON].diepctl);
for (i = 1; i < S3C_MAX_ENDPOINTS; i++) {
writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->out_endp[i].doepctl);
writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->in_endp[i].diepctl);
}
/* 8. Unmask EPO interrupts*/
writel(((1 << EP0_CON) << DAINT_OUT_BIT)
| (1 << EP0_CON), &reg->daintmsk);
/* 9. Unmask device OUT EP common interrupts*/
writel(DOEPMSK_INIT, &reg->doepmsk);
/* 10. Unmask device IN EP common interrupts*/
writel(DIEPMSK_INIT, &reg->diepmsk);
/* 11. Set Rx FIFO Size (in 32-bit words) */
writel(RX_FIFO_SIZE >> 2, &reg->grxfsiz);
/* 12. Set Non Periodic Tx FIFO Size */
writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0,
&reg->gnptxfsiz);
for (i = 1; i < S3C_MAX_HW_ENDPOINTS; i++)
writel((PTX_FIFO_SIZE >> 2) << 16 |
((RX_FIFO_SIZE + NPTX_FIFO_SIZE +
PTX_FIFO_SIZE*(i-1)) >> 2) << 0,
&reg->dieptxf[i-1]);
/* Flush the RX FIFO */
writel(RX_FIFO_FLUSH, &reg->grstctl);
while (readl(&reg->grstctl) & RX_FIFO_FLUSH)
debug("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__);
/* Flush all the Tx FIFO's */
writel(TX_FIFO_FLUSH_ALL, &reg->grstctl);
writel(TX_FIFO_FLUSH_ALL | TX_FIFO_FLUSH, &reg->grstctl);
while (readl(&reg->grstctl) & TX_FIFO_FLUSH)
debug("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__);
/* 13. Clear NAK bit of EP0, EP1, EP2*/
/* For Slave mode*/
/* EP0: Control OUT */
writel(DEPCTL_EPDIS | DEPCTL_CNAK,
&reg->out_endp[EP0_CON].doepctl);
/* 14. Initialize OTG Link Core.*/
writel(GAHBCFG_INIT, &reg->gahbcfg);
}
static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed)
{
unsigned int ep_ctrl;
int i;
if (speed == USB_SPEED_HIGH) {
ep0_fifo_size = 64;
ep_fifo_size = 512;
ep_fifo_size2 = 1024;
dev->gadget.speed = USB_SPEED_HIGH;
} else {
ep0_fifo_size = 64;
ep_fifo_size = 64;
ep_fifo_size2 = 64;
dev->gadget.speed = USB_SPEED_FULL;
}
dev->ep[0].ep.maxpacket = ep0_fifo_size;
for (i = 1; i < S3C_MAX_ENDPOINTS; i++)
dev->ep[i].ep.maxpacket = ep_fifo_size;
/* EP0 - Control IN (64 bytes)*/
ep_ctrl = readl(&reg->in_endp[EP0_CON].diepctl);
writel(ep_ctrl|(0<<0), &reg->in_endp[EP0_CON].diepctl);
/* EP0 - Control OUT (64 bytes)*/
ep_ctrl = readl(&reg->out_endp[EP0_CON].doepctl);
writel(ep_ctrl|(0<<0), &reg->out_endp[EP0_CON].doepctl);
}
static int s3c_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct s3c_ep *ep;
struct s3c_udc *dev;
unsigned long flags;
debug("%s: %p\n", __func__, _ep);
ep = container_of(_ep, struct s3c_ep, ep);
if (!_ep || !desc || ep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| ep->bEndpointAddress != desc->bEndpointAddress
|| ep_maxpacket(ep) <
le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))) {
debug("%s: bad ep or descriptor\n", __func__);
return -EINVAL;
}
/* xfer types must match, except that interrupt ~= bulk */
if (ep->bmAttributes != desc->bmAttributes
&& ep->bmAttributes != USB_ENDPOINT_XFER_BULK
&& desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
debug("%s: %s type mismatch\n", __func__, _ep->name);
return -EINVAL;
}
/* hardware _could_ do smaller, but driver doesn't */
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
&& le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)) !=
ep_maxpacket(ep)) || !get_unaligned(&desc->wMaxPacketSize)) {
debug("%s: bad %s maxpacket\n", __func__, _ep->name);
return -ERANGE;
}
dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
debug("%s: bogus device state\n", __func__);
return -ESHUTDOWN;
}
ep->stopped = 0;
ep->desc = desc;
ep->pio_irqs = 0;
ep->ep.maxpacket = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
/* Reset halt state */
s3c_udc_set_nak(ep);
s3c_udc_set_halt(_ep, 0);
spin_lock_irqsave(&ep->dev->lock, flags);
s3c_udc_ep_activate(ep);
spin_unlock_irqrestore(&ep->dev->lock, flags);
debug("%s: enabled %s, stopped = %d, maxpacket = %d\n",
__func__, _ep->name, ep->stopped, ep->ep.maxpacket);
return 0;
}
/*
* Disable EP
*/
static int s3c_ep_disable(struct usb_ep *_ep)
{
struct s3c_ep *ep;
unsigned long flags;
debug("%s: %p\n", __func__, _ep);
ep = container_of(_ep, struct s3c_ep, ep);
if (!_ep || !ep->desc) {
debug("%s: %s not enabled\n", __func__,
_ep ? ep->ep.name : NULL);
return -EINVAL;
}
spin_lock_irqsave(&ep->dev->lock, flags);
/* Nuke all pending requests */
nuke(ep, -ESHUTDOWN);
ep->desc = 0;
ep->stopped = 1;
spin_unlock_irqrestore(&ep->dev->lock, flags);
debug("%s: disabled %s\n", __func__, _ep->name);
return 0;
}
static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
struct s3c_request *req;
debug("%s: %s %p\n", __func__, ep->name, ep);
req = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*req));
if (!req)
return 0;
memset(req, 0, sizeof *req);
INIT_LIST_HEAD(&req->queue);
return &req->req;
}
static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req)
{
struct s3c_request *req;
debug("%s: %p\n", __func__, ep);
req = container_of(_req, struct s3c_request, req);
WARN_ON(!list_empty(&req->queue));
kfree(req);
}
/* dequeue JUST ONE request */
static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct s3c_ep *ep;
struct s3c_request *req;
unsigned long flags;
debug("%s: %p\n", __func__, _ep);
ep = container_of(_ep, struct s3c_ep, ep);
if (!_ep || ep->ep.name == ep0name)
return -EINVAL;
spin_lock_irqsave(&ep->dev->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
}
if (&req->req != _req) {
spin_unlock_irqrestore(&ep->dev->lock, flags);
return -EINVAL;
}
done(ep, req, -ECONNRESET);
spin_unlock_irqrestore(&ep->dev->lock, flags);
return 0;
}
/*
* Return bytes in EP FIFO
*/
static int s3c_fifo_status(struct usb_ep *_ep)
{
int count = 0;
struct s3c_ep *ep;
ep = container_of(_ep, struct s3c_ep, ep);
if (!_ep) {
debug("%s: bad ep\n", __func__);
return -ENODEV;
}
debug("%s: %d\n", __func__, ep_index(ep));
/* LPD can't report unclaimed bytes from IN fifos */
if (ep_is_in(ep))
return -EOPNOTSUPP;
return count;
}
/*
* Flush EP FIFO
*/
static void s3c_fifo_flush(struct usb_ep *_ep)
{
struct s3c_ep *ep;
ep = container_of(_ep, struct s3c_ep, ep);
if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
debug("%s: bad ep\n", __func__);
return;
}
debug("%s: %d\n", __func__, ep_index(ep));
}
static const struct usb_gadget_ops s3c_udc_ops = {
/* current versions must always be self-powered */
};
static struct s3c_udc memory = {
.usb_address = 0,
.gadget = {
.ops = &s3c_udc_ops,
.ep0 = &memory.ep[0].ep,
.name = driver_name,
},
/* control endpoint */
.ep[0] = {
.ep = {
.name = ep0name,
.ops = &s3c_ep_ops,
.maxpacket = EP0_FIFO_SIZE,
},
.dev = &memory,
.bEndpointAddress = 0,
.bmAttributes = 0,
.ep_type = ep_control,
},
/* first group of endpoints */
.ep[1] = {
.ep = {
.name = "ep1in-bulk",
.ops = &s3c_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.bEndpointAddress = USB_DIR_IN | 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.ep_type = ep_bulk_out,
.fifo_num = 1,
},
.ep[2] = {
.ep = {
.name = "ep2out-bulk",
.ops = &s3c_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.bEndpointAddress = USB_DIR_OUT | 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.ep_type = ep_bulk_in,
.fifo_num = 2,
},
.ep[3] = {
.ep = {
.name = "ep3in-int",
.ops = &s3c_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.bEndpointAddress = USB_DIR_IN | 3,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.ep_type = ep_interrupt,
.fifo_num = 3,
},
};
/*
* probe - binds to the platform device
*/
int s3c_udc_probe(struct s3c_plat_otg_data *pdata)
{
struct s3c_udc *dev = &memory;
int retval = 0, i;
debug("%s: %p\n", __func__, pdata);
dev->pdata = pdata;
phy = (struct s3c_usbotg_phy *)pdata->regs_phy;
reg = (struct s3c_usbotg_reg *)pdata->regs_otg;
usb_phy_ctrl = pdata->usb_phy_ctrl;
/* regs_otg = (void *)pdata->regs_otg; */
dev->gadget.is_dualspeed = 1; /* Hack only*/
dev->gadget.is_otg = 0;
dev->gadget.is_a_peripheral = 0;
dev->gadget.b_hnp_enable = 0;
dev->gadget.a_hnp_support = 0;
dev->gadget.a_alt_hnp_support = 0;
the_controller = dev;
for (i = 0; i < S3C_MAX_ENDPOINTS+1; i++) {
dev->dma_buf[i] = memalign(CONFIG_SYS_CACHELINE_SIZE,
DMA_BUFFER_SIZE);
dev->dma_addr[i] = (dma_addr_t) dev->dma_buf[i];
invalidate_dcache_range((unsigned long) dev->dma_buf[i],
(unsigned long) (dev->dma_buf[i]
+ DMA_BUFFER_SIZE));
}
usb_ctrl = dev->dma_buf[0];
usb_ctrl_dma_addr = dev->dma_addr[0];
udc_reinit(dev);
return retval;
}
int usb_gadget_handle_interrupts()
{
u32 intr_status = readl(&reg->gintsts);
u32 gintmsk = readl(&reg->gintmsk);
if (intr_status & gintmsk)
return s3c_udc_irq(1, (void *)the_controller);
return 0;
}

View file

@ -0,0 +1,139 @@
/*
* Copyright (C) 2003 David Brownell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
* Remy Bohmer <linux@bohmer.net>
*/
#include <common.h>
#include <asm/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <asm/unaligned.h>
static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
{
int count = 0;
u8 c;
u16 uchar;
/*
* this insists on correct encodings, though not minimal ones.
* BUT it currently rejects legit 4-byte UTF-8 code points,
* which need surrogate pairs. (Unicode 3.1 can use them.)
*/
while (len != 0 && (c = (u8) *s++) != 0) {
if ((c & 0x80)) {
/*
* 2-byte sequence:
* 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
*/
if ((c & 0xe0) == 0xc0) {
uchar = (c & 0x1f) << 6;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c;
/*
* 3-byte sequence (most CJKV characters):
* zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
*/
} else if ((c & 0xf0) == 0xe0) {
uchar = (c & 0x0f) << 12;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c << 6;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c;
/* no bogus surrogates */
if (0xd800 <= uchar && uchar <= 0xdfff)
goto fail;
/*
* 4-byte sequence (surrogate pairs, currently rare):
* 11101110wwwwzzzzyy + 110111yyyyxxxxxx
* = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
* (uuuuu = wwww + 1)
* FIXME accept the surrogate code points (only)
*/
} else
goto fail;
} else
uchar = c;
put_unaligned_le16(uchar, cp++);
count++;
len--;
}
return count;
fail:
return -1;
}
/**
* usb_gadget_get_string - fill out a string descriptor
* @table: of c strings encoded using UTF-8
* @id: string id, from low byte of wValue in get string descriptor
* @buf: at least 256 bytes
*
* Finds the UTF-8 string matching the ID, and converts it into a
* string descriptor in utf16-le.
* Returns length of descriptor (always even) or negative errno
*
* If your driver needs stings in multiple languages, you'll probably
* "switch (wIndex) { ... }" in your ep0 string descriptor logic,
* using this routine after choosing which set of UTF-8 strings to use.
* Note that US-ASCII is a strict subset of UTF-8; any string bytes with
* the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
* characters (which are also widely used in C strings).
*/
int
usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf)
{
struct usb_string *s;
int len;
/* descriptor 0 has the language id */
if (id == 0) {
buf[0] = 4;
buf[1] = USB_DT_STRING;
buf[2] = (u8) table->language;
buf[3] = (u8) (table->language >> 8);
return 4;
}
for (s = table->strings; s && s->s; s++)
if (s->id == id)
break;
/* unrecognized: stall. */
if (!s || !s->s)
return -EINVAL;
/* string descriptors have length, tag, then UTF16-LE text */
len = min((size_t) 126, strlen(s->s));
memset(buf + 2, 0, 2 * len); /* zero all the bytes */
len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len);
if (len < 0)
return -EINVAL;
buf[0] = (len + 1) * 2;
buf[1] = USB_DT_STRING;
return buf[0];
}

View file

@ -0,0 +1,77 @@
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# 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., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB := $(obj)libusb_host.o
# ohci
COBJS-$(CONFIG_USB_OHCI_NEW) += ohci-hcd.o
COBJS-$(CONFIG_USB_ATMEL) += ohci-at91.o
COBJS-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
COBJS-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
COBJS-$(CONFIG_USB_S3C64XX) += s3c64xx-hcd.o
COBJS-$(CONFIG_USB_SL811HS) += sl811-hcd.o
# echi
COBJS-$(CONFIG_USB_EHCI) += ehci-hcd.o
COBJS-$(CONFIG_USB_EHCI_ARMADA100) += ehci-armada100.o utmi-armada100.o
COBJS-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o
ifdef CONFIG_MPC512X
COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-mpc512x.o
else
COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
endif
COBJS-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o
COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o
COBJS-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o
COBJS-$(CONFIG_USB_EHCI_MX6) += ehci-mx6.o
COBJS-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o
COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o
COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o
COBJS-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o
COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o
COBJS-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o
COBJS-$(CONFIG_IPQ806X_USB) += xhci-ipq.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
#########################################################################

View file

@ -0,0 +1,64 @@
/*
* (C) Copyright 2012
* eInfochips Ltd. <www.einfochips.com>
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
*
* This driver is based on Kirkwood echi driver
* (C) Copyright 2009
* Marvell Semiconductor <www.marvell.com>
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <asm/io.h>
#include <usb.h>
#include "ehci.h"
#include "ehci-core.h"
#include <asm/arch/cpu.h>
#include <asm/arch/armada100.h>
#include <asm/arch/utmi-armada100.h>
/*
* EHCI host controller init
*/
int ehci_hcd_init(void)
{
if (utmi_init() < 0)
return -1;
hccr = (struct ehci_hccr *)(ARMD1_USB_HOST_BASE + 0x100);
hcor = (struct ehci_hcor *)((uint32_t) hccr
+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
debug("armada100-ehci: init hccr %x and hcor %x hc_length %d\n",
(uint32_t)hccr, (uint32_t)hcor,
(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
return 0;
}
/*
* EHCI host controller stop
*/
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,89 @@
/*
* (C) Copyright 2012
* Atmel Semiconductor <www.atmel.com>
* Written-by: Bo Shen <voice.shen@atmel.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <watchdog.h>
#include <usb.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/at91_pmc.h>
#include <asm/arch/clk.h>
#include "ehci.h"
#include "ehci-core.h"
/* Enable UTMI PLL time out 500us
* 10 times as datasheet specified
*/
#define EN_UPLL_TIMEOUT 500UL
int ehci_hcd_init(void)
{
at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC;
ulong start_time, tmp_time;
start_time = get_timer(0);
/* Enable UTMI PLL */
writel(AT91_PMC_UPLLEN | AT91_PMC_BIASEN, &pmc->uckr);
while ((readl(&pmc->sr) & AT91_PMC_LOCKU) != AT91_PMC_LOCKU) {
WATCHDOG_RESET();
tmp_time = get_timer(0);
if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) {
printf("ERROR: failed to enable UPLL\n");
return -1;
}
}
/* Enable USB Host clock */
writel(1 << ATMEL_ID_UHPHS, &pmc->pcer);
hccr = (struct ehci_hccr *)ATMEL_BASE_EHCI;
hcor = (struct ehci_hcor *)((uint32_t)hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
return 0;
}
int ehci_hcd_stop(void)
{
at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC;
ulong start_time, tmp_time;
/* Disable USB Host Clock */
writel(1 << ATMEL_ID_UHPHS, &pmc->pcdr);
start_time = get_timer(0);
/* Disable UTMI PLL */
writel(readl(&pmc->uckr) & ~AT91_PMC_UPLLEN, &pmc->uckr);
while ((readl(&pmc->sr) & AT91_PMC_LOCKU) == AT91_PMC_LOCKU) {
WATCHDOG_RESET();
tmp_time = get_timer(0);
if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) {
printf("ERROR: failed to stop UPLL\n");
return -1;
}
}
return 0;
}

View file

@ -0,0 +1,29 @@
/*-
* Copyright (c) 2007-2008, Juniper Networks, Inc.
* Copyright (c) 2008, Excito Elektronik i Skåne AB
* All rights reserved.
*
* 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 version 2 of
* the License.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef USB_EHCI_CORE_H
#define USB_EHCI_CORE_H
extern int rootdev;
extern struct ehci_hccr *hccr;
extern volatile struct ehci_hcor *hcor;
#endif

View file

@ -0,0 +1,118 @@
/*
* SAMSUNG EXYNOS USB HOST EHCI Controller
*
* Copyright (C) 2012 Samsung Electronics Co.Ltd
* Vivek Gautam <gautam.vivek@samsung.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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <usb.h>
#include <asm/arch/cpu.h>
#include <asm/arch/ehci.h>
#include <asm/arch/system.h>
#include <asm/arch/power.h>
#include "ehci.h"
#include "ehci-core.h"
/* Setup the EHCI host controller. */
static void setup_usb_phy(struct exynos_usb_phy *usb)
{
set_usbhost_mode(USB20_PHY_CFG_HOST_LINK_EN);
set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_EN);
clrbits_le32(&usb->usbphyctrl0,
HOST_CTRL0_FSEL_MASK |
HOST_CTRL0_COMMONON_N |
/* HOST Phy setting */
HOST_CTRL0_PHYSWRST |
HOST_CTRL0_PHYSWRSTALL |
HOST_CTRL0_SIDDQ |
HOST_CTRL0_FORCESUSPEND |
HOST_CTRL0_FORCESLEEP);
setbits_le32(&usb->usbphyctrl0,
/* Setting up the ref freq */
(CLK_24MHZ << 16) |
/* HOST Phy setting */
HOST_CTRL0_LINKSWRST |
HOST_CTRL0_UTMISWRST);
udelay(10);
clrbits_le32(&usb->usbphyctrl0,
HOST_CTRL0_LINKSWRST |
HOST_CTRL0_UTMISWRST);
udelay(20);
/* EHCI Ctrl setting */
setbits_le32(&usb->ehcictrl,
EHCICTRL_ENAINCRXALIGN |
EHCICTRL_ENAINCR4 |
EHCICTRL_ENAINCR8 |
EHCICTRL_ENAINCR16);
}
/* Reset the EHCI host controller. */
static void reset_usb_phy(struct exynos_usb_phy *usb)
{
/* HOST_PHY reset */
setbits_le32(&usb->usbphyctrl0,
HOST_CTRL0_PHYSWRST |
HOST_CTRL0_PHYSWRSTALL |
HOST_CTRL0_SIDDQ |
HOST_CTRL0_FORCESUSPEND |
HOST_CTRL0_FORCESLEEP);
set_usbhost_phy_ctrl(POWER_USB_HOST_PHY_CTRL_DISABLE);
}
/*
* EHCI-initialization
* Create the appropriate control structures to manage
* a new EHCI host controller.
*/
int ehci_hcd_init(void)
{
struct exynos_usb_phy *usb;
usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy();
setup_usb_phy(usb);
hccr = (struct ehci_hccr *)samsung_get_base_usb_ehci();
hcor = (struct ehci_hcor *)((uint32_t) hccr
+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n",
(uint32_t)hccr, (uint32_t)hcor,
(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the EHCI host controller.
*/
int ehci_hcd_stop()
{
struct exynos_usb_phy *usb;
usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy();
reset_usb_phy(usb);
return 0;
}

View file

@ -0,0 +1,114 @@
/*
* (C) Copyright 2009, 2011 Freescale Semiconductor, Inc.
*
* (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
*
* Author: Tor Krill tor@excito.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.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <pci.h>
#include <usb.h>
#include <asm/io.h>
#include <usb/ehci-fsl.h>
#include <hwconfig.h>
#include "ehci.h"
#include "ehci-core.h"
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
*
* Excerpts from linux ehci fsl driver.
*/
int ehci_hcd_init(void)
{
struct usb_ehci *ehci;
const char *phy_type = NULL;
size_t len;
#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY
char usb_phy[5];
usb_phy[0] = '\0';
#endif
ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB_ADDR;
hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
hcor = (struct ehci_hcor *)((uint32_t) hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
/* Set to Host mode */
setbits_le32(&ehci->usbmode, CM_HOST);
out_be32(&ehci->snoop1, SNOOP_SIZE_2GB);
out_be32(&ehci->snoop2, 0x80000000 | SNOOP_SIZE_2GB);
/* Init phy */
if (hwconfig_sub("usb1", "phy_type"))
phy_type = hwconfig_subarg("usb1", "phy_type", &len);
else
phy_type = getenv("usb_phy_type");
if (!phy_type) {
#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY
/* if none specified assume internal UTMI */
strcpy(usb_phy, "utmi");
phy_type = usb_phy;
#else
printf("WARNING: USB phy type not defined !!\n");
return -1;
#endif
}
if (!strcmp(phy_type, "utmi")) {
#if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY)
setbits_be32(&ehci->control, PHY_CLK_SEL_UTMI);
setbits_be32(&ehci->control, UTMI_PHY_EN);
udelay(1000); /* delay required for PHY Clk to appear */
#endif
out_le32(&(hcor->or_portsc[0]), PORT_PTS_UTMI);
} else {
#if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY)
clrbits_be32(&ehci->control, UTMI_PHY_EN);
setbits_be32(&ehci->control, PHY_CLK_SEL_ULPI);
udelay(1000); /* delay required for PHY Clk to appear */
#endif
out_le32(&(hcor->or_portsc[0]), PORT_PTS_ULPI);
}
/* Enable interface. */
setbits_be32(&ehci->control, USB_EN);
out_be32(&ehci->prictrl, 0x0000000c);
out_be32(&ehci->age_cnt_limit, 0x00000040);
out_be32(&ehci->sictrl, 0x00000001);
in_le32(&ehci->usbmode);
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,829 @@
/*-
* Copyright (c) 2007-2008, Juniper Networks, Inc.
* Copyright (c) 2008, Excito Elektronik i Skåne AB
* Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
*
* All rights reserved.
*
* 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 version 2 of
* the License.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include <usb.h>
#include <asm/io.h>
#include <malloc.h>
#include <watchdog.h>
#include "ehci.h"
int rootdev;
struct ehci_hccr *hccr; /* R/O registers, not need for volatile */
volatile struct ehci_hcor *hcor;
static uint16_t portreset;
DEFINE_ALIGN_BUFFER(struct QH, qh_list, 1, USB_DMA_MINALIGN);
#define ALIGN_END_ADDR(type, ptr, size) \
((uint32_t)(ptr) + roundup((size) * sizeof(type), USB_DMA_MINALIGN))
static struct descriptor {
struct usb_hub_descriptor hub;
struct usb_device_descriptor device;
struct usb_linux_config_descriptor config;
struct usb_linux_interface_descriptor interface;
struct usb_endpoint_descriptor endpoint;
} __attribute__ ((packed)) descriptor = {
{
0x8, /* bDescLength */
0x29, /* bDescriptorType: hub descriptor */
2, /* bNrPorts -- runtime modified */
0, /* wHubCharacteristics */
10, /* bPwrOn2PwrGood */
0, /* bHubCntrCurrent */
{}, /* Device removable */
{} /* at most 7 ports! XXX */
},
{
0x12, /* bLength */
1, /* bDescriptorType: UDESC_DEVICE */
cpu_to_le16(0x0200), /* bcdUSB: v2.0 */
9, /* bDeviceClass: UDCLASS_HUB */
0, /* bDeviceSubClass: UDSUBCLASS_HUB */
1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */
64, /* bMaxPacketSize: 64 bytes */
0x0000, /* idVendor */
0x0000, /* idProduct */
cpu_to_le16(0x0100), /* bcdDevice */
1, /* iManufacturer */
2, /* iProduct */
0, /* iSerialNumber */
1 /* bNumConfigurations: 1 */
},
{
0x9,
2, /* bDescriptorType: UDESC_CONFIG */
cpu_to_le16(0x19),
1, /* bNumInterface */
1, /* bConfigurationValue */
0, /* iConfiguration */
0x40, /* bmAttributes: UC_SELF_POWER */
0 /* bMaxPower */
},
{
0x9, /* bLength */
4, /* bDescriptorType: UDESC_INTERFACE */
0, /* bInterfaceNumber */
0, /* bAlternateSetting */
1, /* bNumEndpoints */
9, /* bInterfaceClass: UICLASS_HUB */
0, /* bInterfaceSubClass: UISUBCLASS_HUB */
0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */
0 /* iInterface */
},
{
0x7, /* bLength */
5, /* bDescriptorType: UDESC_ENDPOINT */
0x81, /* bEndpointAddress:
* UE_DIR_IN | EHCI_INTR_ENDPT
*/
3, /* bmAttributes: UE_INTERRUPT */
8, /* wMaxPacketSize */
255 /* bInterval */
},
};
#if defined(CONFIG_EHCI_IS_TDI)
#define ehci_is_TDI() (1)
#else
#define ehci_is_TDI() (0)
#endif
void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
{
mdelay(50);
}
void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
__attribute__((weak, alias("__ehci_powerup_fixup")));
static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
{
uint32_t result;
do {
result = ehci_readl(ptr);
udelay(5);
if (result == ~(uint32_t)0)
return -1;
result &= mask;
if (result == done)
return 0;
usec--;
} while (usec > 0);
return -1;
}
static int ehci_reset(void)
{
uint32_t cmd;
uint32_t tmp;
uint32_t *reg_ptr;
int ret = 0;
cmd = ehci_readl(&hcor->or_usbcmd);
cmd = (cmd & ~CMD_RUN) | CMD_RESET;
ehci_writel(&hcor->or_usbcmd, cmd);
ret = handshake((uint32_t *)&hcor->or_usbcmd, CMD_RESET, 0, 250 * 1000);
if (ret < 0) {
printf("EHCI fail to reset\n");
goto out;
}
if (ehci_is_TDI()) {
reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE);
tmp = ehci_readl(reg_ptr);
tmp |= USBMODE_CM_HC;
#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN)
tmp |= USBMODE_BE;
#endif
ehci_writel(reg_ptr, tmp);
}
#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH
cmd = ehci_readl(&hcor->or_txfilltuning);
cmd &= ~TXFIFO_THRESH(0x3f);
cmd |= TXFIFO_THRESH(CONFIG_USB_EHCI_TXFIFO_THRESH);
ehci_writel(&hcor->or_txfilltuning, cmd);
#endif
out:
return ret;
}
static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
{
uint32_t delta, next;
uint32_t addr = (uint32_t)buf;
int idx;
if (addr != ALIGN(addr, ARCH_DMA_MINALIGN))
debug("EHCI-HCD: Misaligned buffer address (%p)\n", buf);
flush_dcache_range(addr, ALIGN(addr + sz, ARCH_DMA_MINALIGN));
idx = 0;
while (idx < 5) {
td->qt_buffer[idx] = cpu_to_hc32(addr);
td->qt_buffer_hi[idx] = 0;
next = (addr + 4096) & ~4095;
delta = next - addr;
if (delta >= sz)
break;
sz -= delta;
addr = next;
idx++;
}
if (idx == 5) {
printf("out of buffer pointers (%u bytes left)\n", sz);
return -1;
}
return 0;
}
static int
ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *req)
{
ALLOC_ALIGN_BUFFER(struct QH, qh, 1, USB_DMA_MINALIGN);
ALLOC_ALIGN_BUFFER(struct qTD, qtd, 3, USB_DMA_MINALIGN);
int qtd_counter = 0;
volatile struct qTD *vtd;
unsigned long ts;
uint32_t *tdp;
uint32_t endpt, token, usbsts;
uint32_t c, toggle;
uint32_t cmd;
int timeout;
int ret = 0;
debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
buffer, length, req);
if (req != NULL)
debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
req->request, req->request,
req->requesttype, req->requesttype,
le16_to_cpu(req->value), le16_to_cpu(req->value),
le16_to_cpu(req->index));
memset(qh, 0, sizeof(struct QH));
memset(qtd, 0, 3 * sizeof(*qtd));
toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
/*
* Setup QH (3.6 in ehci-r10.pdf)
*
* qh_link ................. 03-00 H
* qh_endpt1 ............... 07-04 H
* qh_endpt2 ............... 0B-08 H
* - qh_curtd
* qh_overlay.qt_next ...... 13-10 H
* - qh_overlay.qt_altnext
*/
qh->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH);
c = (usb_pipespeed(pipe) != USB_SPEED_HIGH &&
usb_pipeendpoint(pipe) == 0) ? 1 : 0;
endpt = (8 << 28) |
(c << 27) |
(usb_maxpacket(dev, pipe) << 16) |
(0 << 15) |
(1 << 14) |
(usb_pipespeed(pipe) << 12) |
(usb_pipeendpoint(pipe) << 8) |
(0 << 7) | (usb_pipedevice(pipe) << 0);
qh->qh_endpt1 = cpu_to_hc32(endpt);
endpt = (1 << 30) |
(dev->portnr << 23) |
(dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
qh->qh_endpt2 = cpu_to_hc32(endpt);
qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
tdp = &qh->qh_overlay.qt_next;
if (req != NULL) {
/*
* Setup request qTD (3.5 in ehci-r10.pdf)
*
* qt_next ................ 03-00 H
* qt_altnext ............. 07-04 H
* qt_token ............... 0B-08 H
*
* [ buffer, buffer_hi ] loaded with "req".
*/
qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (0 << 31) |
(sizeof(*req) << 16) |
(0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req)) != 0) {
printf("unable construct SETUP td\n");
goto fail;
}
/* Update previous qTD! */
*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
tdp = &qtd[qtd_counter++].qt_next;
toggle = 1;
}
if (length > 0 || req == NULL) {
/*
* Setup request qTD (3.5 in ehci-r10.pdf)
*
* qt_next ................ 03-00 H
* qt_altnext ............. 07-04 H
* qt_token ............... 0B-08 H
*
* [ buffer, buffer_hi ] loaded with "buffer".
*/
qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (toggle << 31) |
(length << 16) |
((req == NULL ? 1 : 0) << 15) |
(0 << 12) |
(3 << 10) |
((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
if (ehci_td_buffer(&qtd[qtd_counter], buffer, length) != 0) {
printf("unable construct DATA td\n");
goto fail;
}
/* Update previous qTD! */
*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
tdp = &qtd[qtd_counter++].qt_next;
}
if (req != NULL) {
/*
* Setup request qTD (3.5 in ehci-r10.pdf)
*
* qt_next ................ 03-00 H
* qt_altnext ............. 07-04 H
* qt_token ............... 0B-08 H
*/
qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (toggle << 31) |
(0 << 16) |
(1 << 15) |
(0 << 12) |
(3 << 10) |
((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
qtd[qtd_counter].qt_token = cpu_to_hc32(token);
/* Update previous qTD! */
*tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]);
tdp = &qtd[qtd_counter++].qt_next;
}
qh_list->qh_link = cpu_to_hc32((uint32_t)qh | QH_LINK_TYPE_QH);
/* Flush dcache */
flush_dcache_range((uint32_t)qh_list,
ALIGN_END_ADDR(struct QH, qh_list, 1));
flush_dcache_range((uint32_t)qh, ALIGN_END_ADDR(struct QH, qh, 1));
flush_dcache_range((uint32_t)qtd, ALIGN_END_ADDR(struct qTD, qtd, 3));
/* Set async. queue head pointer. */
ehci_writel(&hcor->or_asynclistaddr, (uint32_t)qh_list);
usbsts = ehci_readl(&hcor->or_usbsts);
ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f));
/* Enable async. schedule. */
cmd = ehci_readl(&hcor->or_usbcmd);
cmd |= CMD_ASE;
ehci_writel(&hcor->or_usbcmd, cmd);
ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS,
100 * 1000);
if (ret < 0) {
printf("EHCI fail timeout STD_ASS set\n");
goto fail;
}
/* Wait for TDs to be processed. */
ts = get_timer(0);
vtd = &qtd[qtd_counter - 1];
timeout = USB_TIMEOUT_MS(pipe);
do {
/* Invalidate dcache */
invalidate_dcache_range((uint32_t)qh_list,
ALIGN_END_ADDR(struct QH, qh_list, 1));
invalidate_dcache_range((uint32_t)qh,
ALIGN_END_ADDR(struct QH, qh, 1));
invalidate_dcache_range((uint32_t)qtd,
ALIGN_END_ADDR(struct qTD, qtd, 3));
token = hc32_to_cpu(vtd->qt_token);
if (!(token & 0x80))
break;
WATCHDOG_RESET();
} while (get_timer(ts) < timeout);
/*
* Invalidate the memory area occupied by buffer
* Don't try to fix the buffer alignment, if it isn't properly
* aligned it's upper layer's fault so let invalidate_dcache_range()
* vow about it. But we have to fix the length as it's actual
* transfer length and can be unaligned. This is potentially
* dangerous operation, it's responsibility of the calling
* code to make sure enough space is reserved.
*/
invalidate_dcache_range((uint32_t)buffer,
ALIGN((uint32_t)buffer + length, ARCH_DMA_MINALIGN));
/* Check that the TD processing happened */
if (token & 0x80) {
printf("EHCI timed out on TD - token=%#x\n", token);
}
/* Disable async schedule. */
cmd = ehci_readl(&hcor->or_usbcmd);
cmd &= ~CMD_ASE;
ehci_writel(&hcor->or_usbcmd, cmd);
ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0,
100 * 1000);
if (ret < 0) {
printf("EHCI fail timeout STD_ASS reset\n");
goto fail;
}
token = hc32_to_cpu(qh->qh_overlay.qt_token);
if (!(token & 0x80)) {
debug("TOKEN=%#x\n", token);
switch (token & 0xfc) {
case 0:
toggle = token >> 31;
usb_settoggle(dev, usb_pipeendpoint(pipe),
usb_pipeout(pipe), toggle);
dev->status = 0;
break;
case 0x40:
dev->status = USB_ST_STALLED;
break;
case 0xa0:
case 0x20:
dev->status = USB_ST_BUF_ERR;
break;
case 0x50:
case 0x10:
dev->status = USB_ST_BABBLE_DET;
break;
default:
dev->status = USB_ST_CRC_ERR;
if ((token & 0x40) == 0x40)
dev->status |= USB_ST_STALLED;
break;
}
dev->act_len = length - ((token >> 16) & 0x7fff);
} else {
dev->act_len = 0;
debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
dev->devnum, ehci_readl(&hcor->or_usbsts),
ehci_readl(&hcor->or_portsc[0]),
ehci_readl(&hcor->or_portsc[1]));
}
return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
fail:
return -1;
}
static inline int min3(int a, int b, int c)
{
if (b < a)
a = b;
if (c < a)
a = c;
return a;
}
int
ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *req)
{
uint8_t tmpbuf[4];
u16 typeReq;
void *srcptr = NULL;
int len, srclen;
uint32_t reg;
uint32_t *status_reg;
if (le16_to_cpu(req->index) > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
printf("The request port(%d) is not configured\n",
le16_to_cpu(req->index) - 1);
return -1;
}
status_reg = (uint32_t *)&hcor->or_portsc[
le16_to_cpu(req->index) - 1];
srclen = 0;
debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n",
req->request, req->request,
req->requesttype, req->requesttype,
le16_to_cpu(req->value), le16_to_cpu(req->index));
typeReq = req->request | req->requesttype << 8;
switch (typeReq) {
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch (le16_to_cpu(req->value) >> 8) {
case USB_DT_DEVICE:
debug("USB_DT_DEVICE request\n");
srcptr = &descriptor.device;
srclen = 0x12;
break;
case USB_DT_CONFIG:
debug("USB_DT_CONFIG config\n");
srcptr = &descriptor.config;
srclen = 0x19;
break;
case USB_DT_STRING:
debug("USB_DT_STRING config\n");
switch (le16_to_cpu(req->value) & 0xff) {
case 0: /* Language */
srcptr = "\4\3\1\0";
srclen = 4;
break;
case 1: /* Vendor */
srcptr = "\16\3u\0-\0b\0o\0o\0t\0";
srclen = 14;
break;
case 2: /* Product */
srcptr = "\52\3E\0H\0C\0I\0 "
"\0H\0o\0s\0t\0 "
"\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0";
srclen = 42;
break;
default:
debug("unknown value DT_STRING %x\n",
le16_to_cpu(req->value));
goto unknown;
}
break;
default:
debug("unknown value %x\n", le16_to_cpu(req->value));
goto unknown;
}
break;
case USB_REQ_GET_DESCRIPTOR | ((USB_DIR_IN | USB_RT_HUB) << 8):
switch (le16_to_cpu(req->value) >> 8) {
case USB_DT_HUB:
debug("USB_DT_HUB config\n");
srcptr = &descriptor.hub;
srclen = 0x8;
break;
default:
debug("unknown value %x\n", le16_to_cpu(req->value));
goto unknown;
}
break;
case USB_REQ_SET_ADDRESS | (USB_RECIP_DEVICE << 8):
debug("USB_REQ_SET_ADDRESS\n");
rootdev = le16_to_cpu(req->value);
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
debug("USB_REQ_SET_CONFIGURATION\n");
/* Nothing to do */
break;
case USB_REQ_GET_STATUS | ((USB_DIR_IN | USB_RT_HUB) << 8):
tmpbuf[0] = 1; /* USB_STATUS_SELFPOWERED */
tmpbuf[1] = 0;
srcptr = tmpbuf;
srclen = 2;
break;
case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):
memset(tmpbuf, 0, 4);
reg = ehci_readl(status_reg);
if (reg & EHCI_PS_CS)
tmpbuf[0] |= USB_PORT_STAT_CONNECTION;
if (reg & EHCI_PS_PE)
tmpbuf[0] |= USB_PORT_STAT_ENABLE;
if (reg & EHCI_PS_SUSP)
tmpbuf[0] |= USB_PORT_STAT_SUSPEND;
if (reg & EHCI_PS_OCA)
tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT;
if (reg & EHCI_PS_PR)
tmpbuf[0] |= USB_PORT_STAT_RESET;
if (reg & EHCI_PS_PP)
tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;
if (ehci_is_TDI()) {
switch ((reg >> 26) & 3) {
case 0:
break;
case 1:
tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8;
break;
case 2:
default:
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
break;
}
} else {
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
}
if (reg & EHCI_PS_CSC)
tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION;
if (reg & EHCI_PS_PEC)
tmpbuf[2] |= USB_PORT_STAT_C_ENABLE;
if (reg & EHCI_PS_OCC)
tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT;
if (portreset & (1 << le16_to_cpu(req->index)))
tmpbuf[2] |= USB_PORT_STAT_C_RESET;
srcptr = tmpbuf;
srclen = 4;
break;
case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
reg = ehci_readl(status_reg);
reg &= ~EHCI_PS_CLEAR;
switch (le16_to_cpu(req->value)) {
case USB_PORT_FEAT_ENABLE:
reg |= EHCI_PS_PE;
ehci_writel(status_reg, reg);
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams))) {
reg |= EHCI_PS_PP;
ehci_writel(status_reg, reg);
}
break;
case USB_PORT_FEAT_RESET:
if ((reg & (EHCI_PS_PE | EHCI_PS_CS)) == EHCI_PS_CS &&
!ehci_is_TDI() &&
EHCI_PS_IS_LOWSPEED(reg)) {
/* Low speed device, give up ownership. */
debug("port %d low speed --> companion\n",
req->index - 1);
reg |= EHCI_PS_PO;
ehci_writel(status_reg, reg);
break;
} else {
int ret;
reg |= EHCI_PS_PR;
reg &= ~EHCI_PS_PE;
ehci_writel(status_reg, reg);
/*
* caller must wait, then call GetPortStatus
* usb 2.0 specification say 50 ms resets on
* root
*/
ehci_powerup_fixup(status_reg, &reg);
ehci_writel(status_reg, reg & ~EHCI_PS_PR);
/*
* A host controller must terminate the reset
* and stabilize the state of the port within
* 2 milliseconds
*/
ret = handshake(status_reg, EHCI_PS_PR, 0,
2 * 1000);
if (!ret)
portreset |=
1 << le16_to_cpu(req->index);
else
printf("port(%d) reset error\n",
le16_to_cpu(req->index) - 1);
}
break;
default:
debug("unknown feature %x\n", le16_to_cpu(req->value));
goto unknown;
}
/* unblock posted writes */
(void) ehci_readl(&hcor->or_usbcmd);
break;
case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
reg = ehci_readl(status_reg);
switch (le16_to_cpu(req->value)) {
case USB_PORT_FEAT_ENABLE:
reg &= ~EHCI_PS_PE;
break;
case USB_PORT_FEAT_C_ENABLE:
reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE;
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC(ehci_readl(&hccr->cr_hcsparams)))
reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP);
case USB_PORT_FEAT_C_CONNECTION:
reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC;
break;
case USB_PORT_FEAT_OVER_CURRENT:
reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC;
break;
case USB_PORT_FEAT_C_RESET:
portreset &= ~(1 << le16_to_cpu(req->index));
break;
default:
debug("unknown feature %x\n", le16_to_cpu(req->value));
goto unknown;
}
ehci_writel(status_reg, reg);
/* unblock posted write */
(void) ehci_readl(&hcor->or_usbcmd);
break;
default:
debug("Unknown request\n");
goto unknown;
}
mdelay(1);
len = min3(srclen, le16_to_cpu(req->length), length);
if (srcptr != NULL && len > 0)
memcpy(buffer, srcptr, len);
else
debug("Len is 0\n");
dev->act_len = len;
dev->status = 0;
return 0;
unknown:
debug("requesttype=%x, request=%x, value=%x, index=%x, length=%x\n",
req->requesttype, req->request, le16_to_cpu(req->value),
le16_to_cpu(req->index), le16_to_cpu(req->length));
dev->act_len = 0;
dev->status = USB_ST_STALLED;
return -1;
}
int usb_lowlevel_stop(int index)
{
return ehci_hcd_stop();
}
int usb_lowlevel_init(int index, void **controller)
{
uint32_t reg;
uint32_t cmd;
if (ehci_hcd_init() != 0)
return -1;
/* EHCI spec section 4.1 */
if (ehci_reset() != 0)
return -1;
#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
if (ehci_hcd_init() != 0)
return -1;
#endif
/* Set head of reclaim list */
memset(qh_list, 0, sizeof(*qh_list));
qh_list->qh_link = cpu_to_hc32((uint32_t)qh_list | QH_LINK_TYPE_QH);
qh_list->qh_endpt1 = cpu_to_hc32((1 << 15) | (USB_SPEED_HIGH << 12));
qh_list->qh_curtd = cpu_to_hc32(QT_NEXT_TERMINATE);
qh_list->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qh_list->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
qh_list->qh_overlay.qt_token = cpu_to_hc32(0x40);
reg = ehci_readl(&hccr->cr_hcsparams);
descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
/* Port Indicators */
if (HCS_INDICATOR(reg))
put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
| 0x80, &descriptor.hub.wHubCharacteristics);
/* Port Power Control */
if (HCS_PPC(reg))
put_unaligned(get_unaligned(&descriptor.hub.wHubCharacteristics)
| 0x01, &descriptor.hub.wHubCharacteristics);
/* Start the host controller. */
cmd = ehci_readl(&hcor->or_usbcmd);
/*
* Philips, Intel, and maybe others need CMD_RUN before the
* root hub will detect new devices (why?); NEC doesn't
*/
cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
cmd |= CMD_RUN;
ehci_writel(&hcor->or_usbcmd, cmd);
/* take control over the ports */
cmd = ehci_readl(&hcor->or_configflag);
cmd |= FLAG_CF;
ehci_writel(&hcor->or_configflag, cmd);
/* unblock posted write */
cmd = ehci_readl(&hcor->or_usbcmd);
mdelay(5);
reg = HC_VERSION(ehci_readl(&hccr->cr_capbase));
printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
rootdev = 0;
return 0;
}
int
submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int length)
{
if (usb_pipetype(pipe) != PIPE_BULK) {
debug("non-bulk pipe (type=%lu)", usb_pipetype(pipe));
return -1;
}
return ehci_submit_async(dev, pipe, buffer, length, NULL);
}
int
submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *setup)
{
if (usb_pipetype(pipe) != PIPE_CONTROL) {
debug("non-control pipe (type=%lu)", usb_pipetype(pipe));
return -1;
}
if (usb_pipedevice(pipe) == rootdev) {
if (rootdev == 0)
dev->speed = USB_SPEED_HIGH;
return ehci_submit_root(dev, pipe, buffer, length, setup);
}
return ehci_submit_async(dev, pipe, buffer, length, setup);
}
int
submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, int interval)
{
debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
dev, pipe, buffer, length, interval);
return ehci_submit_async(dev, pipe, buffer, length, NULL);
}

View file

@ -0,0 +1,50 @@
/*
* (C) Copyright 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
*
* Author: Michael Trimarchi <trimarchimichael@yahoo.it>
* This code is based on ehci freescale driver
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <usb.h>
#include "ehci.h"
#include "ehci-core.h"
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
*/
int ehci_hcd_init(void)
{
hccr = (struct ehci_hccr *)(0xcd000100);
hcor = (struct ehci_hcor *)((uint32_t) hccr
+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
printf("IXP4XX init hccr %x and hcor %x hc_length %d\n",
(uint32_t)hccr, (uint32_t)hcor,
(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,116 @@
/*
* (C) Copyright 2009
* Marvell Semiconductor <www.marvell.com>
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <asm/io.h>
#include <usb.h>
#include "ehci.h"
#include "ehci-core.h"
#include <asm/arch/cpu.h>
#if defined(CONFIG_KIRKWOOD)
#include <asm/arch/kirkwood.h>
#elif defined(CONFIG_ORION5X)
#include <asm/arch/orion5x.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
#define rdl(off) readl(MVUSB0_BASE + (off))
#define wrl(off, val) writel((val), MVUSB0_BASE + (off))
#define USB_WINDOW_CTRL(i) (0x320 + ((i) << 4))
#define USB_WINDOW_BASE(i) (0x324 + ((i) << 4))
#define USB_TARGET_DRAM 0x0
/*
* USB 2.0 Bridge Address Decoding registers setup
*/
static void usb_brg_adrdec_setup(void)
{
int i;
u32 size, base, attrib;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
/* Enable DRAM bank */
switch (i) {
case 0:
attrib = MVUSB0_CPU_ATTR_DRAM_CS0;
break;
case 1:
attrib = MVUSB0_CPU_ATTR_DRAM_CS1;
break;
case 2:
attrib = MVUSB0_CPU_ATTR_DRAM_CS2;
break;
case 3:
attrib = MVUSB0_CPU_ATTR_DRAM_CS3;
break;
default:
/* invalide bank, disable access */
attrib = 0;
break;
}
size = gd->bd->bi_dram[i].size;
base = gd->bd->bi_dram[i].start;
if ((size) && (attrib))
wrl(USB_WINDOW_CTRL(i),
MVCPU_WIN_CTRL_DATA(size, USB_TARGET_DRAM,
attrib, MVCPU_WIN_ENABLE));
else
wrl(USB_WINDOW_CTRL(i), MVCPU_WIN_DISABLE);
wrl(USB_WINDOW_BASE(i), base);
}
}
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
*/
int ehci_hcd_init(void)
{
usb_brg_adrdec_setup();
hccr = (struct ehci_hccr *)(MVUSB0_BASE + 0x100);
hcor = (struct ehci_hcor *)((uint32_t) hccr
+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
debug("ehci-marvell: init hccr %x and hcor %x hc_length %d\n",
(uint32_t)hccr, (uint32_t)hcor,
(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,159 @@
/*
* (C) Copyright 2010, Damien Dusha, <d.dusha@gmail.com>
*
* (C) Copyright 2009, Value Team S.p.A.
* Francesco Rendine, <francesco.rendine@valueteam.com>
*
* (C) Copyright 2009 Freescale Semiconductor, Inc.
*
* (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB
*
* Author: Tor Krill tor@excito.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.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <pci.h>
#include <usb.h>
#include <asm/io.h>
#include <usb/ehci-fsl.h>
#include "ehci.h"
#include "ehci-core.h"
static void fsl_setup_phy(volatile struct ehci_hcor *);
static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci);
static int reset_usb_controller(volatile struct usb_ehci *ehci);
static void usb_platform_dr_init(volatile struct usb_ehci *ehci);
/*
* Initialize SOC FSL EHCI Controller
*
* This code is derived from EHCI FSL USB Linux driver for MPC5121
*
*/
int ehci_hcd_init(void)
{
volatile struct usb_ehci *ehci;
/* Hook the memory mapped registers for EHCI-Controller */
ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB_ADDR;
hccr = (struct ehci_hccr *)((uint32_t)&(ehci->caplength));
hcor = (struct ehci_hcor *)((uint32_t) hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
/* configure interface for UTMI_WIDE */
usb_platform_dr_init(ehci);
/* Init Phy USB0 to UTMI+ */
fsl_setup_phy(hcor);
/* Set to host mode */
fsl_platform_set_host_mode(ehci);
/*
* Setting the burst size seems to be required to prevent the
* USB from hanging when communicating with certain USB Mass
* storage devices. This was determined by analysing the
* EHCI registers under Linux vs U-Boot and burstsize was the
* major non-interrupt related difference between the two
* implementations.
*
* Some USB sticks behave better than others. In particular,
* the following USB stick is especially problematic:
* 0930:6545 Toshiba Corp
*
* The burstsize is set here to match the Linux implementation.
*/
out_be32(&ehci->burstsize, FSL_EHCI_TXPBURST(8) |
FSL_EHCI_RXPBURST(8));
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
volatile struct usb_ehci *ehci;
int exit_status = 0;
if (hcor) {
/* Unhook struct */
hccr = NULL;
hcor = NULL;
/* Reset the USB controller */
ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB_ADDR;
exit_status = reset_usb_controller(ehci);
}
return exit_status;
}
static int reset_usb_controller(volatile struct usb_ehci *ehci)
{
unsigned int i;
/* Command a reset of the USB Controller */
out_be32(&(ehci->usbcmd), EHCI_FSL_USBCMD_RST);
/* Wait for the reset process to finish */
for (i = 65535 ; i > 0 ; i--) {
/*
* The host will set this bit to zero once the
* reset process is complete
*/
if ((in_be32(&(ehci->usbcmd)) & EHCI_FSL_USBCMD_RST) == 0)
return 0;
}
/* Hub did not reset in time */
return -1;
}
static void fsl_setup_phy(volatile struct ehci_hcor *hcor)
{
uint32_t portsc;
portsc = ehci_readl(&hcor->or_portsc[0]);
portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);
/* Enable the phy mode to UTMI Wide */
portsc |= PORT_PTS_PTW;
portsc |= PORT_PTS_UTMI;
ehci_writel(&hcor->or_portsc[0], portsc);
}
static void fsl_platform_set_host_mode(volatile struct usb_ehci *ehci)
{
uint32_t temp;
temp = in_le32(&ehci->usbmode);
temp |= CM_HOST | ES_BE;
out_le32(&ehci->usbmode, temp);
}
static void usb_platform_dr_init(volatile struct usb_ehci *ehci)
{
/* Configure interface for UTMI_WIDE */
out_be32(&ehci->isiphyctrl, PHYCTRL_PHYE | PHYCTRL_PXE);
out_be32(&ehci->usbgenctrl, GC_PPP | GC_PFP );
}

View file

@ -0,0 +1,253 @@
/*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
* Copyright (C) 2010 Freescale Semiconductor, Inc.
*
* 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 <common.h>
#include <usb.h>
#include <errno.h>
#include <linux/compiler.h>
#include <usb/ehci-fsl.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
#include <asm/arch/mx5x_pins.h>
#include <asm/arch/iomux.h>
#include "ehci.h"
#include "ehci-core.h"
#define MX5_USBOTHER_REGS_OFFSET 0x800
#define MXC_OTG_OFFSET 0
#define MXC_H1_OFFSET 0x200
#define MXC_H2_OFFSET 0x400
#define MXC_USBCTRL_OFFSET 0
#define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8
#define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc
#define MXC_USB_CTRL_1_OFFSET 0x10
#define MXC_USBH2CTRL_OFFSET 0x14
/* USB_CTRL */
#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) /* OTG wakeup intr enable */
#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) /* OTG power mask */
#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) /* Host1 ULPI interrupt enable */
#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) /* HOST1 wakeup intr enable */
#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) /* HOST1 power mask */
/* USB_PHY_CTRL_FUNC */
#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) /* OTG Disable Overcurrent Event */
#define MXC_H1_OC_DIS_BIT (1 << 5) /* UH1 Disable Overcurrent Event */
/* USBH2CTRL */
#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8)
#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7)
#define MXC_H2_UCTRL_H2PM_BIT (1 << 4)
/* USB_CTRL_1 */
#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25)
/* USB pin configuration */
#define USB_PAD_CONFIG (PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \
PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | \
PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL)
#ifdef CONFIG_MX51
/*
* Configure the MX51 USB H1 IOMUX
*/
void setup_iomux_usb_h1(void)
{
mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_CLK, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DIR, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_NXT, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DATA0, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DATA1, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DATA2, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DATA3, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DATA4, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DATA5, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DATA6, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0);
mxc_iomux_set_pad(MX51_PIN_USBH1_DATA7, USB_PAD_CONFIG);
}
/*
* Configure the MX51 USB H2 IOMUX
*/
void setup_iomux_usb_h2(void)
{
mxc_request_iomux(MX51_PIN_EIM_A24, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_A24, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_A25, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_A25, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_A26, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_A27, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_A27, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_D16, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_D17, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_D18, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_D18, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_D19, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_D19, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_D20, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_D20, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_D21, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_D22, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_D22, USB_PAD_CONFIG);
mxc_request_iomux(MX51_PIN_EIM_D23, IOMUX_CONFIG_ALT2);
mxc_iomux_set_pad(MX51_PIN_EIM_D23, USB_PAD_CONFIG);
}
#endif
int mxc_set_usbcontrol(int port, unsigned int flags)
{
unsigned int v;
void __iomem *usb_base = (void __iomem *)OTG_BASE_ADDR;
void __iomem *usbother_base;
int ret = 0;
usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET;
switch (port) {
case 0: /* OTG port */
if (flags & MXC_EHCI_INTERNAL_PHY) {
v = __raw_readl(usbother_base +
MXC_USB_PHY_CTR_FUNC_OFFSET);
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
/* OC/USBPWR is not used */
v |= MXC_OTG_PHYCTRL_OC_DIS_BIT;
else
/* OC/USBPWR is used */
v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT;
__raw_writel(v, usbother_base +
MXC_USB_PHY_CTR_FUNC_OFFSET);
v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET);
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v |= MXC_OTG_UCTRL_OPM_BIT;
else
v &= ~MXC_OTG_UCTRL_OPM_BIT;
__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET);
}
break;
case 1: /* Host 1 Host ULPI */
#ifdef CONFIG_MX51
/* The clock for the USBH1 ULPI port will come externally
from the PHY. */
v = __raw_readl(usbother_base + MXC_USB_CTRL_1_OFFSET);
__raw_writel(v | MXC_USB_CTRL_UH1_EXT_CLK_EN, usbother_base +
MXC_USB_CTRL_1_OFFSET);
#endif
v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET);
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v &= ~MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */
else
v |= MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */
__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET);
v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET);
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */
else
v |= MXC_H1_OC_DIS_BIT; /* OC is not used */
__raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET);
break;
case 2: /* Host 2 ULPI */
v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET);
if (flags & MXC_EHCI_POWER_PINS_ENABLED)
v &= ~MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */
else
v |= MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */
__raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET);
break;
}
return ret;
}
void __board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
{
}
void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port)
__attribute((weak, alias("__board_ehci_hcd_postinit")));
int ehci_hcd_init(void)
{
struct usb_ehci *ehci;
#ifdef CONFIG_MX53
struct clkctl *sc_regs = (struct clkctl *)CCM_BASE_ADDR;
u32 reg;
reg = __raw_readl(&sc_regs->cscmr1) & ~(1 << 26);
/* derive USB PHY clock multiplexer from PLL3 */
reg |= 1 << 26;
__raw_writel(reg, &sc_regs->cscmr1);
#endif
set_usboh3_clk();
enable_usboh3_clk(1);
set_usb_phy2_clk();
enable_usb_phy2_clk(1);
mdelay(1);
/* Do board specific initialization */
board_ehci_hcd_init(CONFIG_MXC_USB_PORT);
ehci = (struct usb_ehci *)(OTG_BASE_ADDR +
(0x200 * CONFIG_MXC_USB_PORT));
hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
hcor = (struct ehci_hcor *)((uint32_t)hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
setbits_le32(&ehci->usbmode, CM_HOST);
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
setbits_le32(&ehci->portsc, USB_EN);
mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS);
mdelay(10);
/* Do board specific post-initialization */
board_ehci_hcd_postinit(ehci, CONFIG_MXC_USB_PORT);
return 0;
}
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
* Copyright (C) 2010 Freescale Semiconductor, Inc.
*
* 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 <common.h>
#include <usb.h>
#include <errno.h>
#include <linux/compiler.h>
#include <usb/ehci-fsl.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
#include <asm/arch/mx6x_pins.h>
#include <asm/arch/iomux-v3.h>
#include "ehci.h"
#include "ehci-core.h"
#define USB_OTGREGS_OFFSET 0x000
#define USB_H1REGS_OFFSET 0x200
#define USB_H2REGS_OFFSET 0x400
#define USB_H3REGS_OFFSET 0x600
#define USB_OTHERREGS_OFFSET 0x800
#define USB_H1_CTRL_OFFSET 0x04
#define USBPHY_CTRL 0x00000030
#define USBPHY_CTRL_SET 0x00000034
#define USBPHY_CTRL_CLR 0x00000038
#define USBPHY_CTRL_TOG 0x0000003c
#define USBPHY_PWD 0x00000000
#define USBPHY_CTRL_SFTRST 0x80000000
#define USBPHY_CTRL_CLKGATE 0x40000000
#define USBPHY_CTRL_ENUTMILEVEL3 0x00008000
#define USBPHY_CTRL_ENUTMILEVEL2 0x00004000
#define ANADIG_USB2_CHRG_DETECT_EN_B 0x00100000
#define ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B 0x00080000
#define ANADIG_USB2_PLL_480_CTRL_BYPASS 0x00010000
#define ANADIG_USB2_PLL_480_CTRL_ENABLE 0x00002000
#define ANADIG_USB2_PLL_480_CTRL_POWER 0x00001000
#define ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS 0x00000040
#define UCTRL_OVER_CUR_POL (1 << 8) /* OTG Polarity of Overcurrent */
#define UCTRL_OVER_CUR_DIS (1 << 7) /* Disable OTG Overcurrent Detection */
/* USBCMD */
#define UH1_USBCMD_OFFSET 0x140
#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
#define UCMD_RESET (1 << 1) /* controller reset */
static void usbh1_internal_phy_clock_gate(int on)
{
void __iomem *phy_reg = (void __iomem *)USB_PHY1_BASE_ADDR;
phy_reg += on ? USBPHY_CTRL_CLR : USBPHY_CTRL_SET;
__raw_writel(USBPHY_CTRL_CLKGATE, phy_reg);
}
static void usbh1_power_config(void)
{
struct anatop_regs __iomem *anatop =
(struct anatop_regs __iomem *)ANATOP_BASE_ADDR;
/*
* Some phy and power's special controls for host1
* 1. The external charger detector needs to be disabled
* or the signal at DP will be poor
* 2. The PLL's power and output to usb for host 1
* is totally controlled by IC, so the Software only needs
* to enable them at initializtion.
*/
__raw_writel(ANADIG_USB2_CHRG_DETECT_EN_B |
ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B,
&anatop->usb2_chrg_detect);
__raw_writel(ANADIG_USB2_PLL_480_CTRL_BYPASS,
&anatop->usb2_pll_480_ctrl_clr);
__raw_writel(ANADIG_USB2_PLL_480_CTRL_ENABLE |
ANADIG_USB2_PLL_480_CTRL_POWER |
ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS,
&anatop->usb2_pll_480_ctrl_set);
}
static int usbh1_phy_enable(void)
{
void __iomem *phy_reg = (void __iomem *)USB_PHY1_BASE_ADDR;
void __iomem *phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL);
void __iomem *usb_cmd = (void __iomem *)(USBOH3_USB_BASE_ADDR +
USB_H1REGS_OFFSET +
UH1_USBCMD_OFFSET);
u32 val;
/* Stop then Reset */
val = __raw_readl(usb_cmd);
val &= ~UCMD_RUN_STOP;
__raw_writel(val, usb_cmd);
while (__raw_readl(usb_cmd) & UCMD_RUN_STOP)
;
val = __raw_readl(usb_cmd);
val |= UCMD_RESET;
__raw_writel(val, usb_cmd);
while (__raw_readl(usb_cmd) & UCMD_RESET)
;
/* Reset USBPHY module */
val = __raw_readl(phy_ctrl);
val |= USBPHY_CTRL_SFTRST;
__raw_writel(val, phy_ctrl);
udelay(10);
/* Remove CLKGATE and SFTRST */
val = __raw_readl(phy_ctrl);
val &= ~(USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST);
__raw_writel(val, phy_ctrl);
udelay(10);
/* Power up the PHY */
__raw_writel(0, phy_reg + USBPHY_PWD);
/* enable FS/LS device */
val = __raw_readl(phy_reg + USBPHY_CTRL);
val |= (USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3);
__raw_writel(val, phy_reg + USBPHY_CTRL);
return 0;
}
static void usbh1_oc_config(void)
{
void __iomem *usb_base = (void __iomem *)USBOH3_USB_BASE_ADDR;
void __iomem *usbother_base = usb_base + USB_OTHERREGS_OFFSET;
u32 val;
val = __raw_readl(usbother_base + USB_H1_CTRL_OFFSET);
#if CONFIG_MACH_TYPE == MACH_TYPE_MX6Q_ARM2
/* mx6qarm2 seems to required a different setting*/
val &= ~UCTRL_OVER_CUR_POL;
#else
val |= UCTRL_OVER_CUR_POL;
#endif
__raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET);
val = __raw_readl(usbother_base + USB_H1_CTRL_OFFSET);
val |= UCTRL_OVER_CUR_DIS;
__raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET);
}
int ehci_hcd_init(void)
{
struct usb_ehci *ehci;
enable_usboh3_clk(1);
mdelay(1);
/* Do board specific initialization */
board_ehci_hcd_init(CONFIG_MXC_USB_PORT);
#if CONFIG_MXC_USB_PORT == 1
/* USB Host 1 */
usbh1_power_config();
usbh1_oc_config();
usbh1_internal_phy_clock_gate(1);
usbh1_phy_enable();
#else
#error "MXC USB port not yet supported"
#endif
ehci = (struct usb_ehci *)(USBOH3_USB_BASE_ADDR +
(0x200 * CONFIG_MXC_USB_PORT));
hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
hcor = (struct ehci_hcor *)((uint32_t)hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
setbits_le32(&ehci->usbmode, CM_HOST);
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
setbits_le32(&ehci->portsc, USB_EN);
mdelay(10);
return 0;
}
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common.h>
#include <usb.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <usb/ehci-fsl.h>
#include <errno.h>
#include "ehci.h"
#include "ehci-core.h"
#define USBCTRL_OTGBASE_OFFSET 0x600
#ifdef CONFIG_MX25
#define MX25_USB_CTRL_IP_PUE_DOWN_BIT (1<<6)
#define MX25_USB_CTRL_HSTD_BIT (1<<5)
#define MX25_USB_CTRL_USBTE_BIT (1<<4)
#define MX25_USB_CTRL_OCPOL_OTG_BIT (1<<3)
#endif
#ifdef CONFIG_MX31
#define MX31_OTG_SIC_SHIFT 29
#define MX31_OTG_SIC_MASK (0x3 << MX31_OTG_SIC_SHIFT)
#define MX31_OTG_PM_BIT (1 << 24)
#define MX31_H2_SIC_SHIFT 21
#define MX31_H2_SIC_MASK (0x3 << MX31_H2_SIC_SHIFT)
#define MX31_H2_PM_BIT (1 << 16)
#define MX31_H2_DT_BIT (1 << 5)
#define MX31_H1_SIC_SHIFT 13
#define MX31_H1_SIC_MASK (0x3 << MX31_H1_SIC_SHIFT)
#define MX31_H1_PM_BIT (1 << 8)
#define MX31_H1_DT_BIT (1 << 4)
#endif
static int mxc_set_usbcontrol(int port, unsigned int flags)
{
unsigned int v;
#ifdef CONFIG_MX25
v = MX25_USB_CTRL_IP_PUE_DOWN_BIT | MX25_USB_CTRL_HSTD_BIT |
MX25_USB_CTRL_USBTE_BIT | MX25_USB_CTRL_OCPOL_OTG_BIT;
#endif
#ifdef CONFIG_MX31
v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
switch (port) {
case 0: /* OTG port */
v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK)
<< MX31_OTG_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_OTG_PM_BIT;
break;
case 1: /* H1 port */
v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT |
MX31_H1_DT_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK)
<< MX31_H1_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_H1_PM_BIT;
if (!(flags & MXC_EHCI_TTL_ENABLED))
v |= MX31_H1_DT_BIT;
break;
case 2: /* H2 port */
v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT |
MX31_H2_DT_BIT);
v |= (flags & MXC_EHCI_INTERFACE_MASK)
<< MX31_H2_SIC_SHIFT;
if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
v |= MX31_H2_PM_BIT;
if (!(flags & MXC_EHCI_TTL_ENABLED))
v |= MX31_H2_DT_BIT;
break;
default:
return -EINVAL;
}
#endif
writel(v, IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET);
return 0;
}
int ehci_hcd_init(void)
{
struct usb_ehci *ehci;
#ifdef CONFIG_MX31
struct clock_control_regs *sc_regs =
(struct clock_control_regs *)CCM_BASE;
__raw_readl(&sc_regs->ccmr);
__raw_writel(__raw_readl(&sc_regs->ccmr) | (1 << 9), &sc_regs->ccmr) ;
#endif
udelay(80);
ehci = (struct usb_ehci *)(IMX_USB_BASE +
(0x200 * CONFIG_MXC_USB_PORT));
hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength);
hcor = (struct ehci_hcor *)((uint32_t) hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
setbits_le32(&ehci->usbmode, CM_HOST);
__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc);
mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS);
udelay(10000);
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,154 @@
/*
* Freescale i.MX28 USB Host driver
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/regs-common.h>
#include <asm/arch/regs-base.h>
#include <asm/arch/regs-clkctrl.h>
#include <asm/arch/regs-usb.h>
#include <asm/arch/regs-usbphy.h>
#include "ehci-core.h"
#include "ehci.h"
#if (CONFIG_EHCI_MXS_PORT != 0) && (CONFIG_EHCI_MXS_PORT != 1)
#error "MXS EHCI: Invalid port selected!"
#endif
#ifndef CONFIG_EHCI_MXS_PORT
#error "MXS EHCI: Please define correct port using CONFIG_EHCI_MXS_PORT!"
#endif
static struct ehci_mxs {
struct mx28_usb_regs *usb_regs;
struct mx28_usbphy_regs *phy_regs;
} ehci_mxs;
int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port)
{
uint32_t usb_base, phy_base;
switch (port) {
case 0:
usb_base = MXS_USBCTRL0_BASE;
phy_base = MXS_USBPHY0_BASE;
break;
case 1:
usb_base = MXS_USBCTRL1_BASE;
phy_base = MXS_USBPHY1_BASE;
break;
default:
printf("CONFIG_EHCI_MXS_PORT (port = %d)\n", port);
return -1;
}
mxs_usb->usb_regs = (struct mx28_usb_regs *)usb_base;
mxs_usb->phy_regs = (struct mx28_usbphy_regs *)phy_base;
return 0;
}
/* This DIGCTL register ungates clock to USB */
#define HW_DIGCTL_CTRL 0x8001c000
#define HW_DIGCTL_CTRL_USB0_CLKGATE (1 << 2)
#define HW_DIGCTL_CTRL_USB1_CLKGATE (1 << 16)
int ehci_hcd_init(void)
{
int ret;
uint32_t usb_base, cap_base;
struct mx28_register_32 *digctl_ctrl =
(struct mx28_register_32 *)HW_DIGCTL_CTRL;
struct mx28_clkctrl_regs *clkctrl_regs =
(struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT);
if (ret)
return ret;
/* Reset the PHY block */
writel(USBPHY_CTRL_SFTRST, &ehci_mxs.phy_regs->hw_usbphy_ctrl_set);
udelay(10);
writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE,
&ehci_mxs.phy_regs->hw_usbphy_ctrl_clr);
/* Enable USB clock */
writel(CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER,
&clkctrl_regs->hw_clkctrl_pll0ctrl0_set);
writel(CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER,
&clkctrl_regs->hw_clkctrl_pll1ctrl0_set);
writel(HW_DIGCTL_CTRL_USB0_CLKGATE | HW_DIGCTL_CTRL_USB1_CLKGATE,
&digctl_ctrl->reg_clr);
/* Start USB PHY */
writel(0, &ehci_mxs.phy_regs->hw_usbphy_pwd);
/* Enable UTMI+ Level 2 and Level 3 compatibility */
writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1,
&ehci_mxs.phy_regs->hw_usbphy_ctrl_set);
usb_base = ((uint32_t)ehci_mxs.usb_regs) + 0x100;
hccr = (struct ehci_hccr *)usb_base;
cap_base = ehci_readl(&hccr->cr_capbase);
hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base));
return 0;
}
int ehci_hcd_stop(void)
{
int ret;
uint32_t tmp;
struct mx28_register_32 *digctl_ctrl =
(struct mx28_register_32 *)HW_DIGCTL_CTRL;
struct mx28_clkctrl_regs *clkctrl_regs =
(struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT);
if (ret)
return ret;
/* Stop the USB port */
tmp = ehci_readl(&hcor->or_usbcmd);
tmp &= ~CMD_RUN;
ehci_writel(tmp, &hcor->or_usbcmd);
/* Disable the PHY */
tmp = USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF |
USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV |
USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS |
USBPHY_PWD_TXPWDFS;
writel(tmp, &ehci_mxs.phy_regs->hw_usbphy_pwd);
/* Disable USB clock */
writel(CLKCTRL_PLL0CTRL0_EN_USB_CLKS,
&clkctrl_regs->hw_clkctrl_pll0ctrl0_clr);
writel(CLKCTRL_PLL1CTRL0_EN_USB_CLKS,
&clkctrl_regs->hw_clkctrl_pll1ctrl0_clr);
/* Gate off the USB clock */
writel(HW_DIGCTL_CTRL_USB0_CLKGATE | HW_DIGCTL_CTRL_USB1_CLKGATE,
&digctl_ctrl->reg_set);
return 0;
}

View file

@ -0,0 +1,254 @@
/*
* (C) Copyright 2011 Ilya Yanok, Emcraft Systems
* (C) Copyright 2004-2008
* Texas Instruments, <www.ti.com>
*
* Derived from Beagle Board code by
* Sunil Kumar <sunilsaini05@gmail.com>
* Shashi Ranjan <shashiranjanmca05@gmail.com>
*
*
* 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 <common.h>
#include <usb.h>
#include <usb/ulpi.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <asm/arch/ehci.h>
#include <asm/ehci-omap.h>
#include "ehci-core.h"
static struct omap_uhh *const uhh = (struct omap_uhh *)OMAP_UHH_BASE;
static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE;
static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE;
static int omap_uhh_reset(void)
{
unsigned long init = get_timer(0);
/* perform UHH soft reset, and wait until reset is complete */
writel(OMAP_UHH_SYSCONFIG_SOFTRESET, &uhh->sysc);
/* Wait for UHH reset to complete */
while (!(readl(&uhh->syss) & OMAP_UHH_SYSSTATUS_EHCI_RESETDONE))
if (get_timer(init) > CONFIG_SYS_HZ) {
debug("OMAP UHH error: timeout resetting ehci\n");
return -EL3RST;
}
return 0;
}
static int omap_ehci_tll_reset(void)
{
unsigned long init = get_timer(0);
/* perform TLL soft reset, and wait until reset is complete */
writel(OMAP_USBTLL_SYSCONFIG_SOFTRESET, &usbtll->sysc);
/* Wait for TLL reset to complete */
while (!(readl(&usbtll->syss) & OMAP_USBTLL_SYSSTATUS_RESETDONE))
if (get_timer(init) > CONFIG_SYS_HZ) {
debug("OMAP EHCI error: timeout resetting TLL\n");
return -EL3RST;
}
return 0;
}
static void omap_usbhs_hsic_init(int port)
{
unsigned int reg;
/* Enable channels now */
reg = readl(&usbtll->channel_conf + port);
setbits_le32(&reg, (OMAP_TLL_CHANNEL_CONF_CHANMODE_TRANSPARENT_UTMI
| OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
| OMAP_TLL_CHANNEL_CONF_DRVVBUS
| OMAP_TLL_CHANNEL_CONF_CHRGVBUS
| OMAP_TLL_CHANNEL_CONF_CHANEN));
writel(reg, &usbtll->channel_conf + port);
}
static void omap_ehci_soft_phy_reset(int port)
{
struct ulpi_viewport ulpi_vp;
ulpi_vp.viewport_addr = (u32)&ehci->insreg05_utmi_ulpi;
ulpi_vp.port_num = port;
ulpi_reset(&ulpi_vp);
}
inline int __board_usb_init(void)
{
return 0;
}
int board_usb_init(void) __attribute__((weak, alias("__board_usb_init")));
#if defined(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO) || \
defined(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO)
/* controls PHY(s) reset signal(s) */
static inline void omap_ehci_phy_reset(int on, int delay)
{
/*
* Refer ISSUE1:
* Hold the PHY in RESET for enough time till
* PHY is settled and ready
*/
if (delay && !on)
udelay(delay);
#ifdef CONFIG_OMAP_EHCI_PHY1_RESET_GPIO
gpio_request(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, "USB PHY1 reset");
gpio_direction_output(CONFIG_OMAP_EHCI_PHY1_RESET_GPIO, !on);
#endif
#ifdef CONFIG_OMAP_EHCI_PHY2_RESET_GPIO
gpio_request(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, "USB PHY2 reset");
gpio_direction_output(CONFIG_OMAP_EHCI_PHY2_RESET_GPIO, !on);
#endif
/* Hold the PHY in RESET for enough time till DIR is high */
/* Refer: ISSUE1 */
if (delay && on)
udelay(delay);
}
#else
#define omap_ehci_phy_reset(on, delay) do {} while (0)
#endif
/* Reset is needed otherwise the kernel-driver will throw an error. */
int omap_ehci_hcd_stop(void)
{
debug("Resetting OMAP EHCI\n");
omap_ehci_phy_reset(1, 0);
if (omap_uhh_reset() < 0)
return -1;
if (omap_ehci_tll_reset() < 0)
return -1;
return 0;
}
/*
* Initialize the OMAP EHCI controller and PHY.
* Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1
* See there for additional Copyrights.
*/
int omap_ehci_hcd_init(struct omap_usbhs_board_data *usbhs_pdata)
{
int ret;
unsigned int i, reg = 0, rev = 0;
debug("Initializing OMAP EHCI\n");
ret = board_usb_init();
if (ret < 0)
return ret;
/* Put the PHY in RESET */
omap_ehci_phy_reset(1, 10);
ret = omap_uhh_reset();
if (ret < 0)
return ret;
ret = omap_ehci_tll_reset();
if (ret)
return ret;
writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
OMAP_USBTLL_SYSCONFIG_CACTIVITY, &usbtll->sysc);
/* Put UHH in NoIdle/NoStandby mode */
writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc);
/* setup ULPI bypass and burst configurations */
clrsetbits_le32(&reg, OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN,
(OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN |
OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN |
OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN));
rev = readl(&uhh->rev);
if (rev == OMAP_USBHS_REV1) {
if (is_ehci_phy_mode(usbhs_pdata->port_mode[0]))
clrbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS);
else
setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS);
if (is_ehci_phy_mode(usbhs_pdata->port_mode[1]))
clrbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS);
else
setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS);
if (is_ehci_phy_mode(usbhs_pdata->port_mode[2]))
clrbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
else
setbits_le32(&reg, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
} else if (rev == OMAP_USBHS_REV2) {
clrsetbits_le32(&reg, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR),
OMAP4_UHH_HOSTCONFIG_APP_START_CLK);
/* Clear port mode fields for PHY mode*/
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0]))
setbits_le32(&reg, OMAP_P1_MODE_HSIC);
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
setbits_le32(&reg, OMAP_P2_MODE_HSIC);
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2]))
setbits_le32(&reg, OMAP_P3_MODE_HSIC);
}
debug("OMAP UHH_REVISION 0x%x\n", rev);
writel(reg, &uhh->hostconfig);
for (i = 0; i < OMAP_HS_USB_PORTS; i++)
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i]))
omap_usbhs_hsic_init(i);
omap_ehci_phy_reset(0, 10);
/*
* An undocumented "feature" in the OMAP3 EHCI controller,
* causes suspended ports to be taken out of suspend when
* the USBCMD.Run/Stop bit is cleared (for example when
* we do ehci_bus_suspend).
* This breaks suspend-resume if the root-hub is allowed
* to suspend. Writing 1 to this undocumented register bit
* disables this feature and restores normal behavior.
*/
writel(EHCI_INSNREG04_DISABLE_UNSUSPEND, &ehci->insreg04);
for (i = 0; i < OMAP_HS_USB_PORTS; i++)
if (is_ehci_phy_mode(usbhs_pdata->port_mode[i]))
omap_ehci_soft_phy_reset(i);
hccr = (struct ehci_hccr *)(OMAP_EHCI_BASE);
hcor = (struct ehci_hcor *)(OMAP_EHCI_BASE + 0x10);
debug("OMAP EHCI init done\n");
return 0;
}

View file

@ -0,0 +1,71 @@
/*-
* Copyright (c) 2007-2008, Juniper Networks, Inc.
* All rights reserved.
*
* 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 version 2 of
* the License.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <pci.h>
#include <usb.h>
#include "ehci.h"
#include "ehci-core.h"
#ifdef CONFIG_PCI_EHCI_DEVICE
static struct pci_device_id ehci_pci_ids[] = {
/* Please add supported PCI EHCI controller ids here */
{0x1033, 0x00E0}, /* NEC */
{0x10B9, 0x5239}, /* ULI1575 PCI EHCI module ids */
{0x12D8, 0x400F}, /* Pericom */
{0, 0}
};
#endif
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
*/
int ehci_hcd_init(void)
{
pci_dev_t pdev;
pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE);
if (pdev == -1) {
printf("EHCI host controller not found\n");
return -1;
}
hccr = (struct ehci_hccr *)pci_map_bar(pdev,
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
hcor = (struct ehci_hcor *)((uint32_t) hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n",
(uint32_t)hccr, (uint32_t)hcor,
(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,47 @@
/*
* (C) Copyright 2010, Chris Zhang <chris@seamicro.com>
*
* Author: Chris Zhang <chris@seamicro.com>
* This code is based on ehci freescale driver
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <usb.h>
#include "ehci.h"
#include "ehci-core.h"
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
*/
int ehci_hcd_init(void)
{
hccr = (struct ehci_hccr *)(CONFIG_SYS_PPC4XX_USB_ADDR);
hcor = (struct ehci_hcor *)((uint32_t) hccr +
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2009-2012 NVIDIA Corporation
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <usb.h>
#include "ehci.h"
#include "ehci-core.h"
#include <asm/errno.h>
#include <asm/arch/usb.h>
/*
* A known hardware issue where Connect Status Change bit of PORTSC register
* of USB1 controller will be set after Port Reset.
* We have to clear it in order for later device enumeration to proceed.
* This ehci_powerup_fixup overrides the weak function ehci_powerup_fixup
* in "ehci-hcd.c".
*/
void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
{
mdelay(50);
if (((u32) status_reg & TEGRA_USB_ADDR_MASK) != TEGRA_USB1_BASE)
return;
/* For EHCI_PS_CSC to be cleared in ehci_hcd.c */
if (ehci_readl(status_reg) & EHCI_PS_CSC)
*reg |= EHCI_PS_CSC;
}
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
*/
int ehci_hcd_init(void)
{
u32 our_hccr, our_hcor;
/*
* Select the first port, as we don't have a way of selecting others
* yet
*/
if (tegrausb_start_port(0, &our_hccr, &our_hcor))
return -1;
hccr = (struct ehci_hccr *)our_hccr;
hcor = (struct ehci_hcor *)our_hcor;
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
tegrausb_stop_port();
return 0;
}

View file

@ -0,0 +1,58 @@
/*
* (C) Copyright 2009 Stefan Roese <sr@denx.de>, DENX Software Engineering
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <usb.h>
#include "ehci.h"
#include "ehci-core.h"
int vct_ehci_hcd_init(u32 *hccr, u32 *hcor);
/*
* Create the appropriate control structures to manage
* a new EHCI host controller.
*/
int ehci_hcd_init(void)
{
int ret;
u32 vct_hccr;
u32 vct_hcor;
/*
* Init VCT specific stuff
*/
ret = vct_ehci_hcd_init(&vct_hccr, &vct_hcor);
if (ret)
return ret;
hccr = (struct ehci_hccr *)vct_hccr;
hcor = (struct ehci_hcor *)vct_hcor;
return 0;
}
/*
* Destroy the appropriate control structures corresponding
* the the EHCI host controller.
*/
int ehci_hcd_stop(void)
{
return 0;
}

View file

@ -0,0 +1,191 @@
/*-
* Copyright (c) 2007-2008, Juniper Networks, Inc.
* Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
* All rights reserved.
*
* 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 version 2 of
* the License.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef USB_EHCI_H
#define USB_EHCI_H
#if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS)
#define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2
#endif
/*
* Register Space.
*/
struct ehci_hccr {
uint32_t cr_capbase;
#define HC_LENGTH(p) (((p) >> 0) & 0x00ff)
#define HC_VERSION(p) (((p) >> 16) & 0xffff)
uint32_t cr_hcsparams;
#define HCS_PPC(p) ((p) & (1 << 4))
#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* Port indicators */
#define HCS_N_PORTS(p) (((p) >> 0) & 0xf)
uint32_t cr_hccparams;
uint8_t cr_hcsp_portrt[8];
} __attribute__ ((packed, aligned(4)));
struct ehci_hcor {
uint32_t or_usbcmd;
#define CMD_PARK (1 << 11) /* enable "park" */
#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */
#define CMD_ASE (1 << 5) /* async schedule enable */
#define CMD_LRESET (1 << 7) /* partial reset */
#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */
#define CMD_PSE (1 << 4) /* periodic schedule enable */
#define CMD_RESET (1 << 1) /* reset HC not bus */
#define CMD_RUN (1 << 0) /* start/stop HC */
uint32_t or_usbsts;
#define STD_ASS (1 << 15)
#define STS_HALT (1 << 12)
uint32_t or_usbintr;
#define INTR_UE (1 << 0) /* USB interrupt enable */
#define INTR_UEE (1 << 1) /* USB error interrupt enable */
#define INTR_PCE (1 << 2) /* Port change detect enable */
#define INTR_SEE (1 << 4) /* system error enable */
#define INTR_AAE (1 << 5) /* Interrupt on async adavance enable */
uint32_t or_frindex;
uint32_t or_ctrldssegment;
uint32_t or_periodiclistbase;
uint32_t or_asynclistaddr;
uint32_t _reserved_0_;
uint32_t or_burstsize;
uint32_t or_txfilltuning;
#define TXFIFO_THRESH(p) ((p & 0x3f) << 16)
uint32_t _reserved_1_[6];
uint32_t or_configflag;
#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS];
uint32_t or_systune;
} __attribute__ ((packed, aligned(4)));
#define USBMODE 0x68 /* USB Device mode */
#define USBMODE_SDIS (1 << 3) /* Stream disable */
#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */
#define USBMODE_CM_HC (3 << 0) /* host controller mode */
#define USBMODE_CM_IDLE (0 << 0) /* idle state */
/* Interface descriptor */
struct usb_linux_interface_descriptor {
unsigned char bLength;
unsigned char bDescriptorType;
unsigned char bInterfaceNumber;
unsigned char bAlternateSetting;
unsigned char bNumEndpoints;
unsigned char bInterfaceClass;
unsigned char bInterfaceSubClass;
unsigned char bInterfaceProtocol;
unsigned char iInterface;
} __attribute__ ((packed));
/* Configuration descriptor information.. */
struct usb_linux_config_descriptor {
unsigned char bLength;
unsigned char bDescriptorType;
unsigned short wTotalLength;
unsigned char bNumInterfaces;
unsigned char bConfigurationValue;
unsigned char iConfiguration;
unsigned char bmAttributes;
unsigned char MaxPower;
} __attribute__ ((packed));
#if defined CONFIG_EHCI_DESC_BIG_ENDIAN
#define ehci_readl(x) (*((volatile u32 *)(x)))
#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b))
#else
#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x))))
#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \
cpu_to_le32(((volatile u32)b)))
#endif
#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN
#define hc32_to_cpu(x) be32_to_cpu((x))
#define cpu_to_hc32(x) cpu_to_be32((x))
#else
#define hc32_to_cpu(x) le32_to_cpu((x))
#define cpu_to_hc32(x) cpu_to_le32((x))
#endif
#define EHCI_PS_WKOC_E (1 << 22) /* RW wake on over current */
#define EHCI_PS_WKDSCNNT_E (1 << 21) /* RW wake on disconnect */
#define EHCI_PS_WKCNNT_E (1 << 20) /* RW wake on connect */
#define EHCI_PS_PO (1 << 13) /* RW port owner */
#define EHCI_PS_PP (1 << 12) /* RW,RO port power */
#define EHCI_PS_LS (3 << 10) /* RO line status */
#define EHCI_PS_PR (1 << 8) /* RW port reset */
#define EHCI_PS_SUSP (1 << 7) /* RW suspend */
#define EHCI_PS_FPR (1 << 6) /* RW force port resume */
#define EHCI_PS_OCC (1 << 5) /* RWC over current change */
#define EHCI_PS_OCA (1 << 4) /* RO over current active */
#define EHCI_PS_PEC (1 << 3) /* RWC port enable change */
#define EHCI_PS_PE (1 << 2) /* RW port enable */
#define EHCI_PS_CSC (1 << 1) /* RWC connect status change */
#define EHCI_PS_CS (1 << 0) /* RO connect status */
#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC)
#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == (1 << 10))
/*
* Schedule Interface Space.
*
* IMPORTANT: Software must ensure that no interface data structure
* reachable by the EHCI host controller spans a 4K page boundary!
*
* Periodic transfers (i.e. isochronous and interrupt transfers) are
* not supported.
*/
/* Queue Element Transfer Descriptor (qTD). */
struct qTD {
/* this part defined by EHCI spec */
uint32_t qt_next; /* see EHCI 3.5.1 */
#define QT_NEXT_TERMINATE 1
uint32_t qt_altnext; /* see EHCI 3.5.2 */
uint32_t qt_token; /* see EHCI 3.5.3 */
uint32_t qt_buffer[5]; /* see EHCI 3.5.4 */
uint32_t qt_buffer_hi[5]; /* Appendix B */
/* pad struct for 32 byte alignment */
uint32_t unused[3];
};
/* Queue Head (QH). */
struct QH {
uint32_t qh_link;
#define QH_LINK_TERMINATE 1
#define QH_LINK_TYPE_ITD 0
#define QH_LINK_TYPE_QH 2
#define QH_LINK_TYPE_SITD 4
#define QH_LINK_TYPE_FSTN 6
uint32_t qh_endpt1;
uint32_t qh_endpt2;
uint32_t qh_curtd;
struct qTD qh_overlay;
/*
* Add dummy fill value to make the size of this struct
* aligned to 32 bytes
*/
uint8_t fill[16];
};
/* Low level init functions */
int ehci_hcd_init(void);
int ehci_hcd_stop(void);
#endif /* USB_EHCI_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,489 @@
/*
* ISP116x register declarations and HCD data structures
*
* Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
* Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
* Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
* Portions:
* Copyright (C) 2004 Lothar Wassmann
* Copyright (C) 2004 Psion Teklogix
* Copyright (C) 2004 David Brownell
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifdef DEBUG
#define DBG(fmt, args...) \
printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
#else
#define DBG(fmt, args...) do {} while (0)
#endif
#ifdef VERBOSE
# define VDBG DBG
#else
# define VDBG(fmt, args...) do {} while (0)
#endif
#define ERR(fmt, args...) \
printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
#define WARN(fmt, args...) \
printf("isp116x: %s: " fmt "\n" , __FUNCTION__ , ## args)
#define INFO(fmt, args...) \
printf("isp116x: " fmt "\n" , ## args)
/* ------------------------------------------------------------------------- */
/* us of 1ms frame */
#define MAX_LOAD_LIMIT 850
/* Full speed: max # of bytes to transfer for a single urb
at a time must be < 1024 && must be multiple of 64.
832 allows transfering 4kiB within 5 frames. */
#define MAX_TRANSFER_SIZE_FULLSPEED 832
/* Low speed: there is no reason to schedule in very big
chunks; often the requested long transfers are for
string descriptors containing short strings. */
#define MAX_TRANSFER_SIZE_LOWSPEED 64
/* Bytetime (us), a rough indication of how much time it
would take to transfer a byte of useful data over USB */
#define BYTE_TIME_FULLSPEED 1
#define BYTE_TIME_LOWSPEED 20
/* Buffer sizes */
#define ISP116x_BUF_SIZE 4096
#define ISP116x_ITL_BUFSIZE 0
#define ISP116x_ATL_BUFSIZE ((ISP116x_BUF_SIZE) - 2*(ISP116x_ITL_BUFSIZE))
#define ISP116x_WRITE_OFFSET 0x80
/* --- ISP116x registers/bits ---------------------------------------------- */
#define HCREVISION 0x00
#define HCCONTROL 0x01
#define HCCONTROL_HCFS (3 << 6) /* host controller
functional state */
#define HCCONTROL_USB_RESET (0 << 6)
#define HCCONTROL_USB_RESUME (1 << 6)
#define HCCONTROL_USB_OPER (2 << 6)
#define HCCONTROL_USB_SUSPEND (3 << 6)
#define HCCONTROL_RWC (1 << 9) /* remote wakeup connected */
#define HCCONTROL_RWE (1 << 10) /* remote wakeup enable */
#define HCCMDSTAT 0x02
#define HCCMDSTAT_HCR (1 << 0) /* host controller reset */
#define HCCMDSTAT_SOC (3 << 16) /* scheduling overrun count */
#define HCINTSTAT 0x03
#define HCINT_SO (1 << 0) /* scheduling overrun */
#define HCINT_WDH (1 << 1) /* writeback of done_head */
#define HCINT_SF (1 << 2) /* start frame */
#define HCINT_RD (1 << 3) /* resume detect */
#define HCINT_UE (1 << 4) /* unrecoverable error */
#define HCINT_FNO (1 << 5) /* frame number overflow */
#define HCINT_RHSC (1 << 6) /* root hub status change */
#define HCINT_OC (1 << 30) /* ownership change */
#define HCINT_MIE (1 << 31) /* master interrupt enable */
#define HCINTENB 0x04
#define HCINTDIS 0x05
#define HCFMINTVL 0x0d
#define HCFMREM 0x0e
#define HCFMNUM 0x0f
#define HCLSTHRESH 0x11
#define HCRHDESCA 0x12
#define RH_A_NDP (0x3 << 0) /* # downstream ports */
#define RH_A_PSM (1 << 8) /* power switching mode */
#define RH_A_NPS (1 << 9) /* no power switching */
#define RH_A_DT (1 << 10) /* device type (mbz) */
#define RH_A_OCPM (1 << 11) /* overcurrent protection
mode */
#define RH_A_NOCP (1 << 12) /* no overcurrent protection */
#define RH_A_POTPGT (0xff << 24) /* power on -> power good
time */
#define HCRHDESCB 0x13
#define RH_B_DR (0xffff << 0) /* device removable flags */
#define RH_B_PPCM (0xffff << 16) /* port power control mask */
#define HCRHSTATUS 0x14
#define RH_HS_LPS (1 << 0) /* local power status */
#define RH_HS_OCI (1 << 1) /* over current indicator */
#define RH_HS_DRWE (1 << 15) /* device remote wakeup
enable */
#define RH_HS_LPSC (1 << 16) /* local power status change */
#define RH_HS_OCIC (1 << 17) /* over current indicator
change */
#define RH_HS_CRWE (1 << 31) /* clear remote wakeup
enable */
#define HCRHPORT1 0x15
#define RH_PS_CCS (1 << 0) /* current connect status */
#define RH_PS_PES (1 << 1) /* port enable status */
#define RH_PS_PSS (1 << 2) /* port suspend status */
#define RH_PS_POCI (1 << 3) /* port over current
indicator */
#define RH_PS_PRS (1 << 4) /* port reset status */
#define RH_PS_PPS (1 << 8) /* port power status */
#define RH_PS_LSDA (1 << 9) /* low speed device attached */
#define RH_PS_CSC (1 << 16) /* connect status change */
#define RH_PS_PESC (1 << 17) /* port enable status change */
#define RH_PS_PSSC (1 << 18) /* port suspend status
change */
#define RH_PS_OCIC (1 << 19) /* over current indicator
change */
#define RH_PS_PRSC (1 << 20) /* port reset status change */
#define HCRHPORT_CLRMASK (0x1f << 16)
#define HCRHPORT2 0x16
#define HCHWCFG 0x20
#define HCHWCFG_15KRSEL (1 << 12)
#define HCHWCFG_CLKNOTSTOP (1 << 11)
#define HCHWCFG_ANALOG_OC (1 << 10)
#define HCHWCFG_DACK_MODE (1 << 8)
#define HCHWCFG_EOT_POL (1 << 7)
#define HCHWCFG_DACK_POL (1 << 6)
#define HCHWCFG_DREQ_POL (1 << 5)
#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
#define HCHWCFG_INT_POL (1 << 2)
#define HCHWCFG_INT_TRIGGER (1 << 1)
#define HCHWCFG_INT_ENABLE (1 << 0)
#define HCDMACFG 0x21
#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
#define HCDMACFG_DMA_ENABLE (1 << 4)
#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
#define HCDMACFG_CTR_SEL (1 << 2)
#define HCDMACFG_ITLATL_SEL (1 << 1)
#define HCDMACFG_DMA_RW_SELECT (1 << 0)
#define HCXFERCTR 0x22
#define HCuPINT 0x24
#define HCuPINT_SOF (1 << 0)
#define HCuPINT_ATL (1 << 1)
#define HCuPINT_AIIEOT (1 << 2)
#define HCuPINT_OPR (1 << 4)
#define HCuPINT_SUSP (1 << 5)
#define HCuPINT_CLKRDY (1 << 6)
#define HCuPINTENB 0x25
#define HCCHIPID 0x27
#define HCCHIPID_MASK 0xff00
#define HCCHIPID_MAGIC 0x6100
#define HCSCRATCH 0x28
#define HCSWRES 0x29
#define HCSWRES_MAGIC 0x00f6
#define HCITLBUFLEN 0x2a
#define HCATLBUFLEN 0x2b
#define HCBUFSTAT 0x2c
#define HCBUFSTAT_ITL0_FULL (1 << 0)
#define HCBUFSTAT_ITL1_FULL (1 << 1)
#define HCBUFSTAT_ATL_FULL (1 << 2)
#define HCBUFSTAT_ITL0_DONE (1 << 3)
#define HCBUFSTAT_ITL1_DONE (1 << 4)
#define HCBUFSTAT_ATL_DONE (1 << 5)
#define HCRDITL0LEN 0x2d
#define HCRDITL1LEN 0x2e
#define HCITLPORT 0x40
#define HCATLPORT 0x41
/* PTD accessor macros. */
#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
#define PTD_GET_B5_5(p) (((p)->len & PTD_B5_5_MSK) >> 13)
#define PTD_B5_5(v) (((v) << 13) & PTD_B5_5_MSK)
#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
#define PTD_GET_FMT(p) (((p)->faddr & PTD_FMT_MSK) >> 7)
#define PTD_FMT(v) (((v) << 7) & PTD_FMT_MSK)
/* Hardware transfer status codes -- CC from ptd->count */
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
#define TD_UNEXPECTEDPID 0x07
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
/* 0x0A, 0x0B reserved for hardware */
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
/* 0x0E, 0x0F reserved for HCD */
#define TD_NOTACCESSED 0x0F
/* ------------------------------------------------------------------------- */
#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
/* Philips transfer descriptor */
struct ptd {
u16 count;
#define PTD_COUNT_MSK (0x3ff << 0)
#define PTD_TOGGLE_MSK (1 << 10)
#define PTD_ACTIVE_MSK (1 << 11)
#define PTD_CC_MSK (0xf << 12)
u16 mps;
#define PTD_MPS_MSK (0x3ff << 0)
#define PTD_SPD_MSK (1 << 10)
#define PTD_LAST_MSK (1 << 11)
#define PTD_EP_MSK (0xf << 12)
u16 len;
#define PTD_LEN_MSK (0x3ff << 0)
#define PTD_DIR_MSK (3 << 10)
#define PTD_DIR_SETUP (0)
#define PTD_DIR_OUT (1)
#define PTD_DIR_IN (2)
#define PTD_B5_5_MSK (1 << 13)
u16 faddr;
#define PTD_FA_MSK (0x7f << 0)
#define PTD_FMT_MSK (1 << 7)
} __attribute__ ((packed, aligned(2)));
struct isp116x_ep {
struct usb_device *udev;
struct ptd ptd;
u8 maxpacket;
u8 epnum;
u8 nextpid;
u16 length; /* of current packet */
unsigned char *data; /* to databuf */
u16 error_count;
};
/* URB struct */
#define N_URB_TD 48
#define URB_DEL 1
typedef struct {
struct isp116x_ep *ed;
void *transfer_buffer; /* (in) associated data buffer */
int actual_length; /* (return) actual transfer length */
unsigned long pipe; /* (in) pipe information */
#if 0
int state;
#endif
} urb_priv_t;
struct isp116x_platform_data {
/* Enable internal resistors on downstream ports */
unsigned sel15Kres:1;
/* On-chip overcurrent detection */
unsigned oc_enable:1;
/* Enable wakeup by devices on usb bus (e.g. wakeup
by attachment/detachment or by device activity
such as moving a mouse). When chosen, this option
prevents stopping internal clock, increasing
thereby power consumption in suspended state. */
unsigned remote_wakeup_enable:1;
};
struct isp116x {
u16 *addr_reg;
u16 *data_reg;
struct isp116x_platform_data *board;
struct dentry *dentry;
unsigned long stat1, stat2, stat4, stat8, stat16;
/* Status flags */
unsigned disabled:1;
unsigned sleeping:1;
/* Root hub registers */
u32 rhdesca;
u32 rhdescb;
u32 rhstatus;
u32 rhport[2];
/* Schedule for the current frame */
struct isp116x_ep *atl_active;
int atl_buflen;
int atl_bufshrt;
int atl_last_dir;
int atl_finishing;
};
/* ------------------------------------------------- */
/* Inter-io delay (ns). The chip is picky about access timings; it
* expects at least:
* 150ns delay between consecutive accesses to DATA_REG,
* 300ns delay between access to ADDR_REG and DATA_REG
* OE, WE MUST NOT be changed during these intervals
*/
#if defined(UDELAY)
#define isp116x_delay(h,d) udelay(d)
#else
#define isp116x_delay(h,d) do {} while (0)
#endif
static inline void isp116x_write_addr(struct isp116x *isp116x, unsigned reg)
{
writew(reg & 0xff, isp116x->addr_reg);
isp116x_delay(isp116x, UDELAY);
}
static inline void isp116x_write_data16(struct isp116x *isp116x, u16 val)
{
writew(val, isp116x->data_reg);
isp116x_delay(isp116x, UDELAY);
}
static inline void isp116x_raw_write_data16(struct isp116x *isp116x, u16 val)
{
__raw_writew(val, isp116x->data_reg);
isp116x_delay(isp116x, UDELAY);
}
static inline u16 isp116x_read_data16(struct isp116x *isp116x)
{
u16 val;
val = readw(isp116x->data_reg);
isp116x_delay(isp116x, UDELAY);
return val;
}
static inline u16 isp116x_raw_read_data16(struct isp116x *isp116x)
{
u16 val;
val = __raw_readw(isp116x->data_reg);
isp116x_delay(isp116x, UDELAY);
return val;
}
static inline void isp116x_write_data32(struct isp116x *isp116x, u32 val)
{
writew(val & 0xffff, isp116x->data_reg);
isp116x_delay(isp116x, UDELAY);
writew(val >> 16, isp116x->data_reg);
isp116x_delay(isp116x, UDELAY);
}
static inline u32 isp116x_read_data32(struct isp116x *isp116x)
{
u32 val;
val = (u32) readw(isp116x->data_reg);
isp116x_delay(isp116x, UDELAY);
val |= ((u32) readw(isp116x->data_reg)) << 16;
isp116x_delay(isp116x, UDELAY);
return val;
}
/* Let's keep register access functions out of line. Hint:
we wait at least 150 ns at every access.
*/
static u16 isp116x_read_reg16(struct isp116x *isp116x, unsigned reg)
{
isp116x_write_addr(isp116x, reg);
return isp116x_read_data16(isp116x);
}
static u32 isp116x_read_reg32(struct isp116x *isp116x, unsigned reg)
{
isp116x_write_addr(isp116x, reg);
return isp116x_read_data32(isp116x);
}
static void isp116x_write_reg16(struct isp116x *isp116x, unsigned reg,
unsigned val)
{
isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
isp116x_write_data16(isp116x, (u16) (val & 0xffff));
}
static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg,
unsigned val)
{
isp116x_write_addr(isp116x, reg | ISP116x_WRITE_OFFSET);
isp116x_write_data32(isp116x, (u32) val);
}
/* --- USB HUB constants (not OHCI-specific; see hub.h) -------------------- */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00

View file

@ -0,0 +1,99 @@
/*
* (C) Copyright 2006
* DENX Software Engineering <mk@denx.de>
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT)
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/at91_pmc.h>
#include <asm/arch/clk.h>
int usb_cpu_init(void)
{
at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC;
#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20) || \
defined(CONFIG_AT91SAM9261)
/* Enable PLLB */
writel(get_pllb_init(), &pmc->pllbr);
while ((readl(&pmc->sr) & AT91_PMC_LOCKB) != AT91_PMC_LOCKB)
;
#elif defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45)
/* Enable UPLL */
writel(readl(&pmc->uckr) | AT91_PMC_UPLLEN | AT91_PMC_BIASEN,
&pmc->uckr);
while ((readl(&pmc->sr) & AT91_PMC_LOCKU) != AT91_PMC_LOCKU)
;
/* Select PLLA as input clock of OHCI */
writel(AT91_PMC_USBS_USB_UPLL | AT91_PMC_USBDIV_10, &pmc->usb);
#endif
/* Enable USB host clock. */
writel(1 << ATMEL_ID_UHP, &pmc->pcer);
#ifdef CONFIG_AT91SAM9261
writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scer);
#else
writel(ATMEL_PMC_UHP, &pmc->scer);
#endif
return 0;
}
int usb_cpu_stop(void)
{
at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC;
/* Disable USB host clock. */
writel(1 << ATMEL_ID_UHP, &pmc->pcdr);
#ifdef CONFIG_AT91SAM9261
writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scdr);
#else
writel(ATMEL_PMC_UHP, &pmc->scdr);
#endif
#if defined(CONFIG_AT91CAP9) || defined(CONFIG_AT91SAM9260) || \
defined(CONFIG_AT91SAM9263) || defined(CONFIG_AT91SAM9G20)
/* Disable PLLB */
writel(0, &pmc->pllbr);
while ((readl(&pmc->sr) & AT91_PMC_LOCKB) != 0)
;
#elif defined(CONFIG_AT91SAM9G45) || defined(CONFIG_AT91SAM9M10G45)
/* Disable UPLL */
writel(readl(&pmc->uckr) & (~AT91_PMC_UPLLEN), &pmc->uckr);
while ((readl(&pmc->sr) & AT91_PMC_LOCKU) == AT91_PMC_LOCKU)
;
#endif
return 0;
}
int usb_cpu_init_fail(void)
{
return usb_cpu_stop();
}
#endif /* defined(CONFIG_USB_OHCI) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT) */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,494 @@
/*
* URB OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
*
* usb-ohci.h
*/
/*
* e.g. PCI controllers need this
*/
#ifdef CONFIG_SYS_OHCI_SWAP_REG_ACCESS
# define ohci_readl(a) __swap_32(*((volatile u32 *)(a)))
# define ohci_writel(a, b) (*((volatile u32 *)(b)) = __swap_32((volatile u32)a))
#else
# define ohci_readl(a) (*((volatile u32 *)(a)))
# define ohci_writel(a, b) (*((volatile u32 *)(b)) = ((volatile u32)a))
#endif /* CONFIG_SYS_OHCI_SWAP_REG_ACCESS */
/* functions for doing board or CPU specific setup/cleanup */
extern int usb_board_init(void);
extern int usb_board_stop(void);
extern int usb_board_init_fail(void);
extern int usb_cpu_init(void);
extern int usb_cpu_stop(void);
extern int usb_cpu_init_fail(void);
static int cc_to_error[16] = {
/* mapping of the OHCI CC status to error codes */
/* No Error */ 0,
/* CRC Error */ USB_ST_CRC_ERR,
/* Bit Stuff */ USB_ST_BIT_ERR,
/* Data Togg */ USB_ST_CRC_ERR,
/* Stall */ USB_ST_STALLED,
/* DevNotResp */ -1,
/* PIDCheck */ USB_ST_BIT_ERR,
/* UnExpPID */ USB_ST_BIT_ERR,
/* DataOver */ USB_ST_BUF_ERR,
/* DataUnder */ USB_ST_BUF_ERR,
/* reservd */ -1,
/* reservd */ -1,
/* BufferOver */ USB_ST_BUF_ERR,
/* BuffUnder */ USB_ST_BUF_ERR,
/* Not Access */ -1,
/* Not Access */ -1
};
static const char *cc_to_string[16] = {
"No Error",
"CRC: Last data packet from endpoint contained a CRC error.",
"BITSTUFFING: Last data packet from endpoint contained a bit " \
"stuffing violation",
"DATATOGGLEMISMATCH: Last packet from endpoint had data toggle PID\n" \
"that did not match the expected value.",
"STALL: TD was moved to the Done Queue because the endpoint returned" \
" a STALL PID",
"DEVICENOTRESPONDING: Device did not respond to token (IN) or did\n" \
"not provide a handshake (OUT)",
"PIDCHECKFAILURE: Check bits on PID from endpoint failed on data PID\n"\
"(IN) or handshake (OUT)",
"UNEXPECTEDPID: Receive PID was not valid when encountered or PID\n" \
"value is not defined.",
"DATAOVERRUN: The amount of data returned by the endpoint exceeded\n" \
"either the size of the maximum data packet allowed\n" \
"from the endpoint (found in MaximumPacketSize field\n" \
"of ED) or the remaining buffer size.",
"DATAUNDERRUN: The endpoint returned less than MaximumPacketSize\n" \
"and that amount was not sufficient to fill the\n" \
"specified buffer",
"reserved1",
"reserved2",
"BUFFEROVERRUN: During an IN, HC received data from endpoint faster\n" \
"than it could be written to system memory",
"BUFFERUNDERRUN: During an OUT, HC could not retrieve data from\n" \
"system memory fast enough to keep up with data USB " \
"data rate.",
"NOT ACCESSED: This code is set by software before the TD is placed" \
"on a list to be processed by the HC.(1)",
"NOT ACCESSED: This code is set by software before the TD is placed" \
"on a list to be processed by the HC.(2)",
};
/* ED States */
#define ED_NEW 0x00
#define ED_UNLINK 0x01
#define ED_OPER 0x02
#define ED_DEL 0x04
#define ED_URB_DEL 0x08
/* usb_ohci_ed */
struct ed {
__u32 hwINFO;
__u32 hwTailP;
__u32 hwHeadP;
__u32 hwNextED;
struct ed *ed_prev;
__u8 int_period;
__u8 int_branch;
__u8 int_load;
__u8 int_interval;
__u8 state;
__u8 type;
__u16 last_iso;
struct ed *ed_rm_list;
struct usb_device *usb_dev;
void *purb;
__u32 unused[2];
} __attribute__((aligned(16)));
typedef struct ed ed_t;
/* TD info field */
#define TD_CC 0xf0000000
#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
#define TD_EC 0x0C000000
#define TD_T 0x03000000
#define TD_T_DATA0 0x02000000
#define TD_T_DATA1 0x03000000
#define TD_T_TOGGLE 0x00000000
#define TD_R 0x00040000
#define TD_DI 0x00E00000
#define TD_DI_SET(X) (((X) & 0x07)<< 21)
#define TD_DP 0x00180000
#define TD_DP_SETUP 0x00000000
#define TD_DP_IN 0x00100000
#define TD_DP_OUT 0x00080000
#define TD_ISO 0x00010000
#define TD_DEL 0x00020000
/* CC Codes */
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
#define TD_UNEXPECTEDPID 0x07
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
#define TD_NOTACCESSED 0x0F
#define MAXPSW 1
struct td {
__u32 hwINFO;
__u32 hwCBP; /* Current Buffer Pointer */
__u32 hwNextTD; /* Next TD Pointer */
__u32 hwBE; /* Memory Buffer End Pointer */
/* #ifndef CONFIG_MPC5200 /\* this seems wrong *\/ */
__u16 hwPSW[MAXPSW];
/* #endif */
__u8 unused;
__u8 index;
struct ed *ed;
struct td *next_dl_td;
struct usb_device *usb_dev;
int transfer_len;
__u32 data;
__u32 unused2[2];
} __attribute__((aligned(32)));
typedef struct td td_t;
#define OHCI_ED_SKIP (1 << 14)
/*
* The HCCA (Host Controller Communications Area) is a 256 byte
* structure defined in the OHCI spec. that the host controller is
* told the base address of. It must be 256-byte aligned.
*/
#define NUM_INTS 32 /* part of the OHCI standard */
struct ohci_hcca {
__u32 int_table[NUM_INTS]; /* Interrupt ED table */
#if defined(CONFIG_MPC5200)
__u16 pad1; /* set to 0 on each frame_no change */
__u16 frame_no; /* current frame number */
#else
__u16 frame_no; /* current frame number */
__u16 pad1; /* set to 0 on each frame_no change */
#endif
__u32 done_head; /* info returned for an interrupt */
u8 reserved_for_hc[116];
} __attribute__((aligned(256)));
/*
* Maximum number of root hub ports.
*/
#ifndef CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS
# error "CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS undefined!"
#endif
/*
* This is the structure of the OHCI controller's memory mapped I/O
* region. This is Memory Mapped I/O. You must use the ohci_readl() and
* ohci_writel() macros defined in this file to access these!!
*/
struct ohci_regs {
/* control and status registers */
__u32 revision;
__u32 control;
__u32 cmdstatus;
__u32 intrstatus;
__u32 intrenable;
__u32 intrdisable;
/* memory pointers */
__u32 hcca;
__u32 ed_periodcurrent;
__u32 ed_controlhead;
__u32 ed_controlcurrent;
__u32 ed_bulkhead;
__u32 ed_bulkcurrent;
__u32 donehead;
/* frame counters */
__u32 fminterval;
__u32 fmremaining;
__u32 fmnumber;
__u32 periodicstart;
__u32 lsthresh;
/* Root hub ports */
struct ohci_roothub_regs {
__u32 a;
__u32 b;
__u32 status;
__u32 portstatus[CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS];
} roothub;
} __attribute__((aligned(32)));
/* Some EHCI controls */
#define EHCI_USBCMD_OFF 0x20
#define EHCI_USBCMD_HCRESET (1 << 1)
/* OHCI CONTROL AND STATUS REGISTER MASKS */
/*
* HcControl (control) register masks
*/
#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
/* pre-shifted values for HCFS */
# define OHCI_USB_RESET (0 << 6)
# define OHCI_USB_RESUME (1 << 6)
# define OHCI_USB_OPER (2 << 6)
# define OHCI_USB_SUSPEND (3 << 6)
/*
* HcCommandStatus (cmdstatus) register masks
*/
#define OHCI_HCR (1 << 0) /* host controller reset */
#define OHCI_CLF (1 << 1) /* control list filled */
#define OHCI_BLF (1 << 2) /* bulk list filled */
#define OHCI_OCR (1 << 3) /* ownership change request */
#define OHCI_SOC (3 << 16) /* scheduling overrun count */
/*
* masks used with interrupt registers:
* HcInterruptStatus (intrstatus)
* HcInterruptEnable (intrenable)
* HcInterruptDisable (intrdisable)
*/
#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
#define OHCI_INTR_SF (1 << 2) /* start frame */
#define OHCI_INTR_RD (1 << 3) /* resume detect */
#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
#define OHCI_INTR_OC (1 << 30) /* ownership change */
#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
/* Virtual Root HUB */
struct virt_root_hub {
int devnum; /* Address of Root Hub endpoint */
void *dev; /* was urb */
void *int_addr;
int send;
int interval;
};
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00
/* OHCI ROOT HUB REGISTER MASKS */
/* roothub.portstatus [i] bits */
#define RH_PS_CCS 0x00000001 /* current connect status */
#define RH_PS_PES 0x00000002 /* port enable status*/
#define RH_PS_PSS 0x00000004 /* port suspend status */
#define RH_PS_POCI 0x00000008 /* port over current indicator */
#define RH_PS_PRS 0x00000010 /* port reset status */
#define RH_PS_PPS 0x00000100 /* port power status */
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
#define RH_PS_CSC 0x00010000 /* connect status change */
#define RH_PS_PESC 0x00020000 /* port enable status change */
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
#define RH_PS_PRSC 0x00100000 /* port reset status change */
/* roothub.status bits */
#define RH_HS_LPS 0x00000001 /* local power status */
#define RH_HS_OCI 0x00000002 /* over current indicator */
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
#define RH_HS_LPSC 0x00010000 /* local power status change */
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
/* roothub.b masks */
#define RH_B_DR 0x0000ffff /* device removable flags */
#define RH_B_PPCM 0xffff0000 /* port power control mask */
/* roothub.a masks */
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
#define RH_A_PSM (1 << 8) /* power switching mode */
#define RH_A_NPS (1 << 9) /* no power switching */
#define RH_A_DT (1 << 10) /* device type (mbz) */
#define RH_A_OCPM (1 << 11) /* over current protection mode */
#define RH_A_NOCP (1 << 12) /* no over current protection */
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
/* urb */
#define N_URB_TD 48
typedef struct
{
ed_t *ed;
__u16 length; /* number of tds associated with this request */
__u16 td_cnt; /* number of tds already serviced */
struct usb_device *dev;
int state;
unsigned long pipe;
void *transfer_buffer;
int transfer_buffer_length;
int interval;
int actual_length;
int finished;
td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */
} urb_priv_t;
#define URB_DEL 1
/*
* This is the full ohci controller description
*
* Note how the "proper" USB information is just
* a subset of what the full implementation needs. (Linus)
*/
typedef struct ohci {
struct ohci_hcca *hcca; /* hcca */
/*dma_addr_t hcca_dma;*/
int irq;
int disabled; /* e.g. got a UE, we're hung */
int sleeping;
unsigned long flags; /* for HC bugs */
struct ohci_regs *regs; /* OHCI controller's memory */
int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/
ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */
ed_t *ed_bulktail; /* last endpoint of bulk list */
ed_t *ed_controltail; /* last endpoint of control list */
int intrstatus;
__u32 hc_control; /* copy of the hc control reg */
struct usb_device *dev[32];
struct virt_root_hub rh;
const char *slot_name;
} ohci_t;
#define NUM_EDS 8 /* num of preallocated endpoint descriptors */
struct ohci_device {
ed_t ed[NUM_EDS];
int ed_cnt;
};
/* hcd */
/* endpoint */
static int ep_link(ohci_t * ohci, ed_t * ed);
static int ep_unlink(ohci_t * ohci, ed_t * ed);
static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe,
int interval, int load);
/*-------------------------------------------------------------------------*/
/* we need more TDs than EDs */
#define NUM_TD 64
/* +1 so we can align the storage */
td_t gtd[NUM_TD+1];
/* pointers to aligned storage */
td_t *ptd;
/* TDs ... */
static inline struct td *
td_alloc (struct usb_device *usb_dev)
{
int i;
struct td *td;
td = NULL;
for (i = 0; i < NUM_TD; i++)
{
if (ptd[i].usb_dev == NULL)
{
td = &ptd[i];
td->usb_dev = usb_dev;
break;
}
}
return td;
}
static inline void
ed_free (struct ed *ed)
{
ed->usb_dev = NULL;
}

View file

@ -0,0 +1,939 @@
/*
* R8A66597 HCD (Host Controller Driver) for u-boot
*
* Copyright (C) 2008 Yoshihiro Shimoda <shimoda.yoshihiro@renesas.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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <common.h>
#include <usb.h>
#include <asm/io.h>
#include "r8a66597.h"
#ifdef R8A66597_DEBUG
#define R8A66597_DPRINT printf
#else
#define R8A66597_DPRINT(...)
#endif
static const char hcd_name[] = "r8a66597_hcd";
static unsigned short clock = CONFIG_R8A66597_XTAL;
static unsigned short vif = CONFIG_R8A66597_LDRV;
static unsigned short endian = CONFIG_R8A66597_ENDIAN;
static struct r8a66597 gr8a66597;
static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport)
{
int i;
*hub_devnum = 0;
*hubport = 0;
/* check a device connected to root_hub */
if ((dev->parent && dev->parent->devnum == 1) ||
(dev->devnum == 1))
return;
for (i = 0; i < USB_MAXCHILDREN; i++) {
if (dev->parent->children[i] == dev) {
*hub_devnum = (u8)dev->parent->devnum;
*hubport = i;
return;
}
}
printf("get_hub_data error.\n");
}
static void set_devadd(struct r8a66597 *r8a66597, u8 r8a66597_address,
struct usb_device *dev, int port)
{
u16 val, usbspd, upphub, hubport;
unsigned long devadd_reg = get_devadd_addr(r8a66597_address);
get_hub_data(dev, &upphub, &hubport);
usbspd = r8a66597->speed;
val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001);
r8a66597_write(r8a66597, val, devadd_reg);
}
static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
{
u16 tmp;
int i = 0;
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
do {
r8a66597_write(r8a66597, SCKE, SYSCFG0);
tmp = r8a66597_read(r8a66597, SYSCFG0);
if (i++ > 1000) {
printf("register access fail.\n");
return -1;
}
} while ((tmp & SCKE) != SCKE);
r8a66597_write(r8a66597, 0x04, 0x02);
#else
do {
r8a66597_write(r8a66597, USBE, SYSCFG0);
tmp = r8a66597_read(r8a66597, SYSCFG0);
if (i++ > 1000) {
printf("register access fail.\n");
return -1;
}
} while ((tmp & USBE) != USBE);
r8a66597_bclr(r8a66597, USBE, SYSCFG0);
r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0);
i = 0;
r8a66597_bset(r8a66597, XCKE, SYSCFG0);
do {
udelay(1000);
tmp = r8a66597_read(r8a66597, SYSCFG0);
if (i++ > 500) {
printf("register access fail.\n");
return -1;
}
} while ((tmp & SCKE) != SCKE);
#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */
return 0;
}
static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
{
r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
udelay(1);
#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
r8a66597_bclr(r8a66597, USBE, SYSCFG0);
#endif
}
static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port)
{
u16 val;
val = port ? DRPD : DCFM | DRPD;
r8a66597_bset(r8a66597, val, get_syscfg_reg(port));
r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port));
r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port));
}
static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port)
{
u16 val, tmp;
r8a66597_write(r8a66597, 0, get_intenb_reg(port));
r8a66597_write(r8a66597, 0, get_intsts_reg(port));
r8a66597_port_power(r8a66597, port, 0);
do {
tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS;
udelay(640);
} while (tmp == EDGESTS);
val = port ? DRPD : DCFM | DRPD;
r8a66597_bclr(r8a66597, val, get_syscfg_reg(port));
r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port));
}
static int enable_controller(struct r8a66597 *r8a66597)
{
int ret, port;
ret = r8a66597_clock_enable(r8a66597);
if (ret < 0)
return ret;
r8a66597_bset(r8a66597, vif & LDRV, PINCFG);
r8a66597_bset(r8a66597, USBE, SYSCFG0);
r8a66597_bset(r8a66597, INTL, SOFCFG);
r8a66597_write(r8a66597, 0, INTENB0);
r8a66597_write(r8a66597, 0, INTENB1);
r8a66597_write(r8a66597, 0, INTENB2);
r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL);
r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL);
r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL);
r8a66597_bset(r8a66597, TRNENSEL, SOFCFG);
for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
r8a66597_enable_port(r8a66597, port);
return 0;
}
static void disable_controller(struct r8a66597 *r8a66597)
{
int i;
if (!(r8a66597_read(r8a66597, SYSCFG0) & USBE))
return;
r8a66597_write(r8a66597, 0, INTENB0);
r8a66597_write(r8a66597, 0, INTSTS0);
r8a66597_write(r8a66597, 0, D0FIFOSEL);
r8a66597_write(r8a66597, 0, D1FIFOSEL);
r8a66597_write(r8a66597, 0, DCPCFG);
r8a66597_write(r8a66597, 0x40, DCPMAXP);
r8a66597_write(r8a66597, 0, DCPCTR);
for (i = 0; i <= 10; i++)
r8a66597_write(r8a66597, 0, get_devadd_addr(i));
for (i = 1; i <= 5; i++) {
r8a66597_write(r8a66597, 0, get_pipetre_addr(i));
r8a66597_write(r8a66597, 0, get_pipetrn_addr(i));
}
for (i = 1; i < R8A66597_MAX_NUM_PIPE; i++) {
r8a66597_write(r8a66597, 0, get_pipectr_addr(i));
r8a66597_write(r8a66597, i, PIPESEL);
r8a66597_write(r8a66597, 0, PIPECFG);
r8a66597_write(r8a66597, 0, PIPEBUF);
r8a66597_write(r8a66597, 0, PIPEMAXP);
r8a66597_write(r8a66597, 0, PIPEPERI);
}
for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++)
r8a66597_disable_port(r8a66597, i);
r8a66597_clock_disable(r8a66597);
}
static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg,
u16 mask, u16 loop)
{
u16 tmp;
int i = 0;
do {
tmp = r8a66597_read(r8a66597, reg);
if (i++ > 1000000) {
printf("register%lx, loop %x is timeout\n", reg, loop);
break;
}
} while ((tmp & mask) != loop);
}
static void pipe_buffer_setting(struct r8a66597 *r8a66597,
struct usb_device *dev, unsigned long pipe)
{
u16 val = 0;
u16 pipenum, bufnum, maxpacket;
if (usb_pipein(pipe)) {
pipenum = BULK_IN_PIPENUM;
bufnum = BULK_IN_BUFNUM;
maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)];
} else {
pipenum = BULK_OUT_PIPENUM;
bufnum = BULK_OUT_BUFNUM;
maxpacket = dev->epmaxpacketout[usb_pipeendpoint(pipe)];
}
if (r8a66597->pipe_config & (1 << pipenum))
return;
r8a66597->pipe_config |= (1 << pipenum);
r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(pipenum));
r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(pipenum));
r8a66597_write(r8a66597, pipenum, PIPESEL);
/* FIXME: This driver support bulk transfer only. */
if (!usb_pipein(pipe))
val |= R8A66597_DIR;
else
val |= R8A66597_SHTNAK;
val |= R8A66597_BULK | R8A66597_DBLB | usb_pipeendpoint(pipe);
r8a66597_write(r8a66597, val, PIPECFG);
r8a66597_write(r8a66597, (8 << 10) | bufnum, PIPEBUF);
r8a66597_write(r8a66597, make_devsel(usb_pipedevice(pipe)) |
maxpacket, PIPEMAXP);
r8a66597_write(r8a66597, 0, PIPEPERI);
r8a66597_write(r8a66597, SQCLR, get_pipectr_addr(pipenum));
}
static int send_setup_packet(struct r8a66597 *r8a66597, struct usb_device *dev,
struct devrequest *setup)
{
int i;
unsigned short *p = (unsigned short *)setup;
unsigned long setup_addr = USBREQ;
u16 intsts1;
int timeout = 3000;
u16 devsel = setup->request == USB_REQ_SET_ADDRESS ? 0 : dev->devnum;
r8a66597_write(r8a66597, make_devsel(devsel) |
(8 << dev->maxpacketsize), DCPMAXP);
r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1);
for (i = 0; i < 4; i++) {
r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr);
setup_addr += 2;
}
r8a66597_write(r8a66597, ~0x0001, BRDYSTS);
r8a66597_write(r8a66597, SUREQ, DCPCTR);
while (1) {
intsts1 = r8a66597_read(r8a66597, INTSTS1);
if (intsts1 & SACK)
break;
if (intsts1 & SIGN) {
printf("setup packet send error\n");
return -1;
}
if (timeout-- < 0) {
printf("setup packet timeout\n");
return -1;
}
udelay(500);
}
return 0;
}
static int send_bulk_packet(struct r8a66597 *r8a66597, struct usb_device *dev,
unsigned long pipe, void *buffer, int transfer_len)
{
u16 tmp, bufsize;
u16 *buf;
size_t size;
R8A66597_DPRINT("%s\n", __func__);
r8a66597_mdfy(r8a66597, MBW | BULK_OUT_PIPENUM,
MBW | CURPIPE, CFIFOSEL);
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, BULK_OUT_PIPENUM);
tmp = r8a66597_read(r8a66597, CFIFOCTR);
if ((tmp & FRDY) == 0) {
printf("%s FRDY is not set (%x)\n", __func__, tmp);
return -1;
}
/* prepare parameters */
bufsize = dev->epmaxpacketout[usb_pipeendpoint(pipe)];
buf = (u16 *)(buffer + dev->act_len);
size = min((int)bufsize, transfer_len - dev->act_len);
/* write fifo */
r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS);
if (buffer) {
r8a66597_write_fifo(r8a66597, CFIFO, buf, size);
r8a66597_write(r8a66597, BVAL, CFIFOCTR);
}
/* update parameters */
dev->act_len += size;
r8a66597_mdfy(r8a66597, PID_BUF, PID,
get_pipectr_addr(BULK_OUT_PIPENUM));
while (!(r8a66597_read(r8a66597, BEMPSTS) & (1 << BULK_OUT_PIPENUM)))
if (ctrlc())
return -1;
r8a66597_write(r8a66597, ~(1 << BULK_OUT_PIPENUM), BEMPSTS);
if (dev->act_len >= transfer_len)
r8a66597_mdfy(r8a66597, PID_NAK, PID,
get_pipectr_addr(BULK_OUT_PIPENUM));
return 0;
}
static int receive_bulk_packet(struct r8a66597 *r8a66597,
struct usb_device *dev,
unsigned long pipe,
void *buffer, int transfer_len)
{
u16 tmp;
u16 *buf;
const u16 pipenum = BULK_IN_PIPENUM;
int rcv_len;
int maxpacket = dev->epmaxpacketin[usb_pipeendpoint(pipe)];
R8A66597_DPRINT("%s\n", __func__);
/* prepare */
if (dev->act_len == 0) {
r8a66597_mdfy(r8a66597, PID_NAK, PID,
get_pipectr_addr(pipenum));
r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS);
r8a66597_write(r8a66597, TRCLR, get_pipetre_addr(pipenum));
r8a66597_write(r8a66597,
(transfer_len + maxpacket - 1) / maxpacket,
get_pipetrn_addr(pipenum));
r8a66597_bset(r8a66597, TRENB, get_pipetre_addr(pipenum));
r8a66597_mdfy(r8a66597, PID_BUF, PID,
get_pipectr_addr(pipenum));
}
r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL);
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum);
while (!(r8a66597_read(r8a66597, BRDYSTS) & (1 << pipenum)))
if (ctrlc())
return -1;
r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS);
tmp = r8a66597_read(r8a66597, CFIFOCTR);
if ((tmp & FRDY) == 0) {
printf("%s FRDY is not set. (%x)\n", __func__, tmp);
return -1;
}
buf = (u16 *)(buffer + dev->act_len);
rcv_len = tmp & DTLN;
dev->act_len += rcv_len;
if (buffer) {
if (rcv_len == 0)
r8a66597_write(r8a66597, BCLR, CFIFOCTR);
else
r8a66597_read_fifo(r8a66597, CFIFO, buf, rcv_len);
}
return 0;
}
static int receive_control_packet(struct r8a66597 *r8a66597,
struct usb_device *dev,
void *buffer, int transfer_len)
{
u16 tmp;
int rcv_len;
/* FIXME: limit transfer size : 64byte or less */
r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
r8a66597_bset(r8a66597, SQSET, DCPCTR);
r8a66597_write(r8a66597, BCLR, CFIFOCTR);
r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR);
while (!(r8a66597_read(r8a66597, BRDYSTS) & 0x0001))
if (ctrlc())
return -1;
r8a66597_write(r8a66597, ~0x0001, BRDYSTS);
r8a66597_mdfy(r8a66597, MBW, MBW | CURPIPE, CFIFOSEL);
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
tmp = r8a66597_read(r8a66597, CFIFOCTR);
if ((tmp & FRDY) == 0) {
printf("%s FRDY is not set. (%x)\n", __func__, tmp);
return -1;
}
rcv_len = tmp & DTLN;
dev->act_len += rcv_len;
r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR);
if (buffer) {
if (rcv_len == 0)
r8a66597_write(r8a66597, BCLR, DCPCTR);
else
r8a66597_read_fifo(r8a66597, CFIFO, buffer, rcv_len);
}
return 0;
}
static int send_status_packet(struct r8a66597 *r8a66597,
unsigned long pipe)
{
r8a66597_bset(r8a66597, SQSET, DCPCTR);
r8a66597_mdfy(r8a66597, PID_NAK, PID, DCPCTR);
if (usb_pipein(pipe)) {
r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG);
r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR);
} else {
r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL);
r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
r8a66597_write(r8a66597, BCLR, CFIFOCTR);
}
r8a66597_mdfy(r8a66597, PID_BUF, PID, DCPCTR);
while (!(r8a66597_read(r8a66597, BEMPSTS) & 0x0001))
if (ctrlc())
return -1;
return 0;
}
static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port)
{
int count = R8A66597_MAX_SAMPLING;
unsigned short syssts, old_syssts;
R8A66597_DPRINT("%s\n", __func__);
old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST);
while (count > 0) {
mdelay(R8A66597_RH_POLL_TIME);
syssts = r8a66597_read(r8a66597, get_syssts_reg(port) & LNST);
if (syssts == old_syssts) {
count--;
} else {
count = R8A66597_MAX_SAMPLING;
old_syssts = syssts;
}
}
}
static void r8a66597_bus_reset(struct r8a66597 *r8a66597, int port)
{
mdelay(10);
r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, get_dvstctr_reg(port));
mdelay(50);
r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, get_dvstctr_reg(port));
mdelay(50);
}
static int check_usb_device_connecting(struct r8a66597 *r8a66597)
{
int timeout = 10000; /* 100usec * 10000 = 1sec */
int i;
for (i = 0; i < 5; i++) {
/* check a usb cable connect */
while (!(r8a66597_read(r8a66597, INTSTS1) & ATTCH)) {
if (timeout-- < 0) {
printf("%s timeout.\n", __func__);
return -1;
}
udelay(100);
}
/* check a data line */
r8a66597_check_syssts(r8a66597, 0);
r8a66597_bus_reset(r8a66597, 0);
r8a66597->speed = get_rh_usb_speed(r8a66597, 0);
if (!(r8a66597_read(r8a66597, INTSTS1) & DTCH)) {
r8a66597->port_change = USB_PORT_STAT_C_CONNECTION;
r8a66597->port_status = USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_ENABLE;
return 0; /* success */
}
R8A66597_DPRINT("USB device has detached. retry = %d\n", i);
r8a66597_write(r8a66597, ~DTCH, INTSTS1);
}
return -1; /* fail */
}
/* based on usb_ohci.c */
#define min_t(type, x, y) \
({ type __x = (x); type __y = (y); __x < __y ? __x : __y; })
/*-------------------------------------------------------------------------*
* Virtual Root Hub
*-------------------------------------------------------------------------*/
/* Device descriptor */
static __u8 root_hub_dev_des[] =
{
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, /* __u16 bcdUSB; v1.1 */
0x01,
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
0x00, /* __u8 bDeviceProtocol; */
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
0x00, /* __u16 idVendor; */
0x00,
0x00, /* __u16 idProduct; */
0x00,
0x00, /* __u16 bcdDevice; */
0x00,
0x00, /* __u8 iManufacturer; */
0x01, /* __u8 iProduct; */
0x00, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
/* Configuration descriptor */
static __u8 root_hub_config_des[] =
{
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, /* __u16 wTotalLength; */
0x00,
0x01, /* __u8 bNumInterfaces; */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes; */
0x00, /* __u8 MaxPower; */
/* interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; */
0x00, /* __u8 if_iInterface; */
/* endpoint */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
0x00,
0xff /* __u8 ep_bInterval; 255 ms */
};
static unsigned char root_hub_str_index0[] =
{
0x04, /* __u8 bLength; */
0x03, /* __u8 bDescriptorType; String-descriptor */
0x09, /* __u8 lang ID */
0x04, /* __u8 lang ID */
};
static unsigned char root_hub_str_index1[] =
{
34, /* __u8 bLength; */
0x03, /* __u8 bDescriptorType; String-descriptor */
'R', /* __u8 Unicode */
0, /* __u8 Unicode */
'8', /* __u8 Unicode */
0, /* __u8 Unicode */
'A', /* __u8 Unicode */
0, /* __u8 Unicode */
'6', /* __u8 Unicode */
0, /* __u8 Unicode */
'6', /* __u8 Unicode */
0, /* __u8 Unicode */
'5', /* __u8 Unicode */
0, /* __u8 Unicode */
'9', /* __u8 Unicode */
0, /* __u8 Unicode */
'7', /* __u8 Unicode */
0, /* __u8 Unicode */
' ', /* __u8 Unicode */
0, /* __u8 Unicode */
'R', /* __u8 Unicode */
0, /* __u8 Unicode */
'o', /* __u8 Unicode */
0, /* __u8 Unicode */
'o', /* __u8 Unicode */
0, /* __u8 Unicode */
't', /* __u8 Unicode */
0, /* __u8 Unicode */
'H', /* __u8 Unicode */
0, /* __u8 Unicode */
'u', /* __u8 Unicode */
0, /* __u8 Unicode */
'b', /* __u8 Unicode */
0, /* __u8 Unicode */
};
static int r8a66597_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int transfer_len, struct devrequest *cmd)
{
struct r8a66597 *r8a66597 = &gr8a66597;
int leni = transfer_len;
int len = 0;
int stat = 0;
__u16 bmRType_bReq;
__u16 wValue;
__u16 wIndex;
__u16 wLength;
unsigned char data[32];
R8A66597_DPRINT("%s\n", __func__);
if (usb_pipeint(pipe)) {
printf("Root-Hub submit IRQ: NOT implemented");
return 0;
}
bmRType_bReq = cmd->requesttype | (cmd->request << 8);
wValue = cpu_to_le16 (cmd->value);
wIndex = cpu_to_le16 (cmd->index);
wLength = cpu_to_le16 (cmd->length);
switch (bmRType_bReq) {
case RH_GET_STATUS:
*(__u16 *)buffer = cpu_to_le16(1);
len = 2;
break;
case RH_GET_STATUS | RH_INTERFACE:
*(__u16 *)buffer = cpu_to_le16(0);
len = 2;
break;
case RH_GET_STATUS | RH_ENDPOINT:
*(__u16 *)buffer = cpu_to_le16(0);
len = 2;
break;
case RH_GET_STATUS | RH_CLASS:
*(__u32 *)buffer = cpu_to_le32(0);
len = 4;
break;
case RH_GET_STATUS | RH_OTHER | RH_CLASS:
*(__u32 *)buffer = cpu_to_le32(r8a66597->port_status |
(r8a66597->port_change << 16));
len = 4;
break;
case RH_CLEAR_FEATURE | RH_ENDPOINT:
case RH_CLEAR_FEATURE | RH_CLASS:
break;
case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
switch (wValue) {
case RH_C_PORT_CONNECTION:
r8a66597->port_change &= ~USB_PORT_STAT_C_CONNECTION;
break;
}
break;
case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
switch (wValue) {
case (RH_PORT_SUSPEND):
break;
case (RH_PORT_RESET):
r8a66597_bus_reset(r8a66597, 0);
break;
case (RH_PORT_POWER):
break;
case (RH_PORT_ENABLE):
break;
}
break;
case RH_SET_ADDRESS:
gr8a66597.rh_devnum = wValue;
break;
case RH_GET_DESCRIPTOR:
switch ((wValue & 0xff00) >> 8) {
case (0x01): /* device descriptor */
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_dev_des),
wLength));
memcpy(buffer, root_hub_dev_des, len);
break;
case (0x02): /* configuration descriptor */
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_config_des),
wLength));
memcpy(buffer, root_hub_config_des, len);
break;
case (0x03): /* string descriptors */
if (wValue == 0x0300) {
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_str_index0),
wLength));
memcpy(buffer, root_hub_str_index0, len);
}
if (wValue == 0x0301) {
len = min_t(unsigned int,
leni,
min_t(unsigned int,
sizeof(root_hub_str_index1),
wLength));
memcpy(buffer, root_hub_str_index1, len);
}
break;
default:
stat = USB_ST_STALLED;
}
break;
case RH_GET_DESCRIPTOR | RH_CLASS:
{
__u32 temp = 0x00000001;
data[0] = 9; /* min length; */
data[1] = 0x29;
data[2] = temp & RH_A_NDP;
data[3] = 0;
if (temp & RH_A_PSM)
data[3] |= 0x1;
if (temp & RH_A_NOCP)
data[3] |= 0x10;
else if (temp & RH_A_OCPM)
data[3] |= 0x8;
/* corresponds to data[4-7] */
data[5] = (temp & RH_A_POTPGT) >> 24;
data[7] = temp & RH_B_DR;
if (data[2] < 7) {
data[8] = 0xff;
} else {
data[0] += 2;
data[8] = (temp & RH_B_DR) >> 8;
data[10] = data[9] = 0xff;
}
len = min_t(unsigned int, leni,
min_t(unsigned int, data[0], wLength));
memcpy(buffer, data, len);
break;
}
case RH_GET_CONFIGURATION:
*(__u8 *) buffer = 0x01;
len = 1;
break;
case RH_SET_CONFIGURATION:
break;
default:
R8A66597_DPRINT("unsupported root hub command");
stat = USB_ST_STALLED;
}
mdelay(1);
len = min_t(int, len, leni);
dev->act_len = len;
dev->status = stat;
return stat;
}
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len)
{
struct r8a66597 *r8a66597 = &gr8a66597;
int ret = 0;
R8A66597_DPRINT("%s\n", __func__);
R8A66597_DPRINT("pipe = %08x, buffer = %p, len = %d, devnum = %d\n",
pipe, buffer, transfer_len, dev->devnum);
set_devadd(r8a66597, dev->devnum, dev, 0);
pipe_buffer_setting(r8a66597, dev, pipe);
dev->act_len = 0;
while (dev->act_len < transfer_len && ret == 0) {
if (ctrlc())
return -1;
if (usb_pipein(pipe))
ret = receive_bulk_packet(r8a66597, dev, pipe, buffer,
transfer_len);
else
ret = send_bulk_packet(r8a66597, dev, pipe, buffer,
transfer_len);
}
if (ret == 0)
dev->status = 0;
return ret;
}
int submit_control_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int transfer_len, struct devrequest *setup)
{
struct r8a66597 *r8a66597 = &gr8a66597;
u16 r8a66597_address = setup->request == USB_REQ_SET_ADDRESS ?
0 : dev->devnum;
R8A66597_DPRINT("%s\n", __func__);
if (usb_pipedevice(pipe) == r8a66597->rh_devnum)
return r8a66597_submit_rh_msg(dev, pipe, buffer, transfer_len,
setup);
R8A66597_DPRINT("%s: setup\n", __func__);
set_devadd(r8a66597, r8a66597_address, dev, 0);
if (send_setup_packet(r8a66597, dev, setup) < 0) {
printf("setup packet send error\n");
return -1;
}
dev->act_len = 0;
if (usb_pipein(pipe))
if (receive_control_packet(r8a66597, dev, buffer,
transfer_len) < 0)
return -1;
if (send_status_packet(r8a66597, pipe) < 0)
return -1;
dev->status = 0;
return 0;
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int transfer_len, int interval)
{
/* no implement */
R8A66597_DPRINT("%s\n", __func__);
return 0;
}
int usb_lowlevel_init(int index, void **controller))
{
struct r8a66597 *r8a66597 = &gr8a66597;
R8A66597_DPRINT("%s\n", __func__);
memset(r8a66597, 0, sizeof(r8a66597));
r8a66597->reg = CONFIG_R8A66597_BASE_ADDR;
disable_controller(r8a66597);
mdelay(100);
enable_controller(r8a66597);
r8a66597_port_power(r8a66597, 0 , 1);
/* check usb device */
check_usb_device_connecting(r8a66597);
mdelay(50);
return 0;
}
int usb_lowlevel_stop(int index)
{
disable_controller(&gr8a66597);
return 0;
}

View file

@ -0,0 +1,659 @@
/*
* R8A66597 HCD (Host Controller Driver) for u-boot
*
* Copyright (C) 2008 Yoshihiro Shimoda <shimoda.yoshihiro@renesas.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; version 2 of the License.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef __R8A66597_H__
#define __R8A66597_H__
#define SYSCFG0 0x00
#define SYSCFG1 0x02
#define SYSSTS0 0x04
#define SYSSTS1 0x06
#define DVSTCTR0 0x08
#define DVSTCTR1 0x0A
#define TESTMODE 0x0C
#define PINCFG 0x0E
#define DMA0CFG 0x10
#define DMA1CFG 0x12
#define CFIFO 0x14
#define D0FIFO 0x18
#define D1FIFO 0x1C
#define CFIFOSEL 0x20
#define CFIFOCTR 0x22
#define CFIFOSIE 0x24
#define D0FIFOSEL 0x28
#define D0FIFOCTR 0x2A
#define D1FIFOSEL 0x2C
#define D1FIFOCTR 0x2E
#define INTENB0 0x30
#define INTENB1 0x32
#define INTENB2 0x34
#define BRDYENB 0x36
#define NRDYENB 0x38
#define BEMPENB 0x3A
#define SOFCFG 0x3C
#define INTSTS0 0x40
#define INTSTS1 0x42
#define INTSTS2 0x44
#define BRDYSTS 0x46
#define NRDYSTS 0x48
#define BEMPSTS 0x4A
#define FRMNUM 0x4C
#define UFRMNUM 0x4E
#define USBADDR 0x50
#define USBREQ 0x54
#define USBVAL 0x56
#define USBINDX 0x58
#define USBLENG 0x5A
#define DCPCFG 0x5C
#define DCPMAXP 0x5E
#define DCPCTR 0x60
#define PIPESEL 0x64
#define PIPECFG 0x68
#define PIPEBUF 0x6A
#define PIPEMAXP 0x6C
#define PIPEPERI 0x6E
#define PIPE1CTR 0x70
#define PIPE2CTR 0x72
#define PIPE3CTR 0x74
#define PIPE4CTR 0x76
#define PIPE5CTR 0x78
#define PIPE6CTR 0x7A
#define PIPE7CTR 0x7C
#define PIPE8CTR 0x7E
#define PIPE9CTR 0x80
#define PIPE1TRE 0x90
#define PIPE1TRN 0x92
#define PIPE2TRE 0x94
#define PIPE2TRN 0x96
#define PIPE3TRE 0x98
#define PIPE3TRN 0x9A
#define PIPE4TRE 0x9C
#define PIPE4TRN 0x9E
#define PIPE5TRE 0xA0
#define PIPE5TRN 0xA2
#define DEVADD0 0xD0
#define DEVADD1 0xD2
#define DEVADD2 0xD4
#define DEVADD3 0xD6
#define DEVADD4 0xD8
#define DEVADD5 0xDA
#define DEVADD6 0xDC
#define DEVADD7 0xDE
#define DEVADD8 0xE0
#define DEVADD9 0xE2
#define DEVADDA 0xE4
/* System Configuration Control Register */
#define XTAL 0xC000 /* b15-14: Crystal selection */
#define XTAL48 0x8000 /* 48MHz */
#define XTAL24 0x4000 /* 24MHz */
#define XTAL12 0x0000 /* 12MHz */
#define XCKE 0x2000 /* b13: External clock enable */
#define PLLC 0x0800 /* b11: PLL control */
#define SCKE 0x0400 /* b10: USB clock enable */
#define PCSDIS 0x0200 /* b9: not CS wakeup */
#define LPSME 0x0100 /* b8: Low power sleep mode */
#define HSE 0x0080 /* b7: Hi-speed enable */
#define DCFM 0x0040 /* b6: Controller function select */
#define DRPD 0x0020 /* b5: D+/- pull down control */
#define DPRPU 0x0010 /* b4: D+ pull up control */
#define USBE 0x0001 /* b0: USB module operation enable */
/* System Configuration Status Register */
#define OVCBIT 0x8000 /* b15-14: Over-current bit */
#define OVCMON 0xC000 /* b15-14: Over-current monitor */
#define SOFEA 0x0020 /* b5: SOF monitor */
#define IDMON 0x0004 /* b3: ID-pin monitor */
#define LNST 0x0003 /* b1-0: D+, D- line status */
#define SE1 0x0003 /* SE1 */
#define FS_KSTS 0x0002 /* Full-Speed K State */
#define FS_JSTS 0x0001 /* Full-Speed J State */
#define LS_JSTS 0x0002 /* Low-Speed J State */
#define LS_KSTS 0x0001 /* Low-Speed K State */
#define SE0 0x0000 /* SE0 */
/* Device State Control Register */
#define EXTLP0 0x0400 /* b10: External port */
#define VBOUT 0x0200 /* b9: VBUS output */
#define WKUP 0x0100 /* b8: Remote wakeup */
#define RWUPE 0x0080 /* b7: Remote wakeup sense */
#define USBRST 0x0040 /* b6: USB reset enable */
#define RESUME 0x0020 /* b5: Resume enable */
#define UACT 0x0010 /* b4: USB bus enable */
#define RHST 0x0007 /* b1-0: Reset handshake status */
#define HSPROC 0x0004 /* HS handshake is processing */
#define HSMODE 0x0003 /* Hi-Speed mode */
#define FSMODE 0x0002 /* Full-Speed mode */
#define LSMODE 0x0001 /* Low-Speed mode */
#define UNDECID 0x0000 /* Undecided */
/* Test Mode Register */
#define UTST 0x000F /* b3-0: Test select */
#define H_TST_PACKET 0x000C /* HOST TEST Packet */
#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */
#define H_TST_K 0x000A /* HOST TEST K */
#define H_TST_J 0x0009 /* HOST TEST J */
#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */
#define P_TST_PACKET 0x0004 /* PERI TEST Packet */
#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */
#define P_TST_K 0x0002 /* PERI TEST K */
#define P_TST_J 0x0001 /* PERI TEST J */
#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */
/* Data Pin Configuration Register */
#define LDRV 0x8000 /* b15: Drive Current Adjust */
#define VIF1 0x0000 /* VIF = 1.8V */
#define VIF3 0x8000 /* VIF = 3.3V */
#define INTA 0x0001 /* b1: USB INT-pin active */
/* DMAx Pin Configuration Register */
#define DREQA 0x4000 /* b14: Dreq active select */
#define BURST 0x2000 /* b13: Burst mode */
#define DACKA 0x0400 /* b10: Dack active select */
#define DFORM 0x0380 /* b9-7: DMA mode select */
#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */
#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */
#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */
#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */
#define DENDA 0x0040 /* b6: Dend active select */
#define PKTM 0x0020 /* b5: Packet mode */
#define DENDE 0x0010 /* b4: Dend enable */
#define OBUS 0x0004 /* b2: OUTbus mode */
/* CFIFO/DxFIFO Port Select Register */
#define RCNT 0x8000 /* b15: Read count mode */
#define REW 0x4000 /* b14: Buffer rewind */
#define DCLRM 0x2000 /* b13: DMA buffer clear mode */
#define DREQE 0x1000 /* b12: DREQ output enable */
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
#define MBW 0x0800
#else
#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */
#endif
#define MBW_8 0x0000 /* 8bit */
#define MBW_16 0x0400 /* 16bit */
#define BIGEND 0x0100 /* b8: Big endian mode */
#define BYTE_LITTLE 0x0000 /* little dendian */
#define BYTE_BIG 0x0100 /* big endifan */
#define ISEL 0x0020 /* b5: DCP FIFO port direction select */
#define CURPIPE 0x000F /* b2-0: PIPE select */
/* CFIFO/DxFIFO Port Control Register */
#define BVAL 0x8000 /* b15: Buffer valid flag */
#define BCLR 0x4000 /* b14: Buffer clear */
#define FRDY 0x2000 /* b13: FIFO ready */
#define DTLN 0x0FFF /* b11-0: FIFO received data length */
/* Interrupt Enable Register 0 */
#define VBSE 0x8000 /* b15: VBUS interrupt */
#define RSME 0x4000 /* b14: Resume interrupt */
#define SOFE 0x2000 /* b13: Frame update interrupt */
#define DVSE 0x1000 /* b12: Device state transition interrupt */
#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */
#define BEMPE 0x0400 /* b10: Buffer empty interrupt */
#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */
#define BRDYE 0x0100 /* b8: Buffer ready interrupt */
/* Interrupt Enable Register 1 */
#define OVRCRE 0x8000 /* b15: Over-current interrupt */
#define BCHGE 0x4000 /* b14: USB us chenge interrupt */
#define DTCHE 0x1000 /* b12: Detach sense interrupt */
#define ATTCHE 0x0800 /* b11: Attach sense interrupt */
#define EOFERRE 0x0040 /* b6: EOF error interrupt */
#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */
#define SACKE 0x0010 /* b4: SETUP ACK interrupt */
/* BRDY Interrupt Enable/Status Register */
#define BRDY9 0x0200 /* b9: PIPE9 */
#define BRDY8 0x0100 /* b8: PIPE8 */
#define BRDY7 0x0080 /* b7: PIPE7 */
#define BRDY6 0x0040 /* b6: PIPE6 */
#define BRDY5 0x0020 /* b5: PIPE5 */
#define BRDY4 0x0010 /* b4: PIPE4 */
#define BRDY3 0x0008 /* b3: PIPE3 */
#define BRDY2 0x0004 /* b2: PIPE2 */
#define BRDY1 0x0002 /* b1: PIPE1 */
#define BRDY0 0x0001 /* b1: PIPE0 */
/* NRDY Interrupt Enable/Status Register */
#define NRDY9 0x0200 /* b9: PIPE9 */
#define NRDY8 0x0100 /* b8: PIPE8 */
#define NRDY7 0x0080 /* b7: PIPE7 */
#define NRDY6 0x0040 /* b6: PIPE6 */
#define NRDY5 0x0020 /* b5: PIPE5 */
#define NRDY4 0x0010 /* b4: PIPE4 */
#define NRDY3 0x0008 /* b3: PIPE3 */
#define NRDY2 0x0004 /* b2: PIPE2 */
#define NRDY1 0x0002 /* b1: PIPE1 */
#define NRDY0 0x0001 /* b1: PIPE0 */
/* BEMP Interrupt Enable/Status Register */
#define BEMP9 0x0200 /* b9: PIPE9 */
#define BEMP8 0x0100 /* b8: PIPE8 */
#define BEMP7 0x0080 /* b7: PIPE7 */
#define BEMP6 0x0040 /* b6: PIPE6 */
#define BEMP5 0x0020 /* b5: PIPE5 */
#define BEMP4 0x0010 /* b4: PIPE4 */
#define BEMP3 0x0008 /* b3: PIPE3 */
#define BEMP2 0x0004 /* b2: PIPE2 */
#define BEMP1 0x0002 /* b1: PIPE1 */
#define BEMP0 0x0001 /* b0: PIPE0 */
/* SOF Pin Configuration Register */
#define TRNENSEL 0x0100 /* b8: Select transaction enable period */
#define BRDYM 0x0040 /* b6: BRDY clear timing */
#define INTL 0x0020 /* b5: Interrupt sense select */
#define EDGESTS 0x0010 /* b4: */
#define SOFMODE 0x000C /* b3-2: SOF pin select */
#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */
#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */
#define SOF_DISABLE 0x0000 /* SOF OUT Disable */
/* Interrupt Status Register 0 */
#define VBINT 0x8000 /* b15: VBUS interrupt */
#define RESM 0x4000 /* b14: Resume interrupt */
#define SOFR 0x2000 /* b13: SOF frame update interrupt */
#define DVST 0x1000 /* b12: Device state transition interrupt */
#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */
#define BEMP 0x0400 /* b10: Buffer empty interrupt */
#define NRDY 0x0200 /* b9: Buffer not ready interrupt */
#define BRDY 0x0100 /* b8: Buffer ready interrupt */
#define VBSTS 0x0080 /* b7: VBUS input port */
#define DVSQ 0x0070 /* b6-4: Device state */
#define DS_SPD_CNFG 0x0070 /* Suspend Configured */
#define DS_SPD_ADDR 0x0060 /* Suspend Address */
#define DS_SPD_DFLT 0x0050 /* Suspend Default */
#define DS_SPD_POWR 0x0040 /* Suspend Powered */
#define DS_SUSP 0x0040 /* Suspend */
#define DS_CNFG 0x0030 /* Configured */
#define DS_ADDS 0x0020 /* Address */
#define DS_DFLT 0x0010 /* Default */
#define DS_POWR 0x0000 /* Powered */
#define DVSQS 0x0030 /* b5-4: Device state */
#define VALID 0x0008 /* b3: Setup packet detected flag */
#define CTSQ 0x0007 /* b2-0: Control transfer stage */
#define CS_SQER 0x0006 /* Sequence error */
#define CS_WRND 0x0005 /* Control write nodata status stage */
#define CS_WRSS 0x0004 /* Control write status stage */
#define CS_WRDS 0x0003 /* Control write data stage */
#define CS_RDSS 0x0002 /* Control read status stage */
#define CS_RDDS 0x0001 /* Control read data stage */
#define CS_IDST 0x0000 /* Idle or setup stage */
/* Interrupt Status Register 1 */
#define OVRCR 0x8000 /* b15: Over-current interrupt */
#define BCHG 0x4000 /* b14: USB bus chenge interrupt */
#define DTCH 0x1000 /* b12: Detach sense interrupt */
#define ATTCH 0x0800 /* b11: Attach sense interrupt */
#define EOFERR 0x0040 /* b6: EOF-error interrupt */
#define SIGN 0x0020 /* b5: Setup ignore interrupt */
#define SACK 0x0010 /* b4: Setup acknowledge interrupt */
/* Frame Number Register */
#define OVRN 0x8000 /* b15: Overrun error */
#define CRCE 0x4000 /* b14: Received data error */
#define FRNM 0x07FF /* b10-0: Frame number */
/* Micro Frame Number Register */
#define UFRNM 0x0007 /* b2-0: Micro frame number */
/* Default Control Pipe Maxpacket Size Register */
/* Pipe Maxpacket Size Register */
#define DEVSEL 0xF000 /* b15-14: Device address select */
#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */
/* Default Control Pipe Control Register */
#define BSTS 0x8000 /* b15: Buffer status */
#define SUREQ 0x4000 /* b14: Send USB request */
#define CSCLR 0x2000 /* b13: complete-split status clear */
#define CSSTS 0x1000 /* b12: complete-split status */
#define SUREQCLR 0x0800 /* b11: stop setup request */
#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
#define SQSET 0x0080 /* b7: Sequence toggle bit set */
#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
#define PBUSY 0x0020 /* b5: pipe busy */
#define PINGE 0x0010 /* b4: ping enable */
#define CCPL 0x0004 /* b2: Enable control transfer complete */
#define PID 0x0003 /* b1-0: Response PID */
#define PID_STALL11 0x0003 /* STALL */
#define PID_STALL 0x0002 /* STALL */
#define PID_BUF 0x0001 /* BUF */
#define PID_NAK 0x0000 /* NAK */
/* Pipe Window Select Register */
#define PIPENM 0x0007 /* b2-0: Pipe select */
/* Pipe Configuration Register */
#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */
#define R8A66597_ISO 0xC000 /* Isochronous */
#define R8A66597_INT 0x8000 /* Interrupt */
#define R8A66597_BULK 0x4000 /* Bulk */
#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */
#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */
#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */
#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */
#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */
#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */
/* Pipe Buffer Configuration Register */
#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */
#define BUFNMB 0x007F /* b6-0: Pipe buffer number */
#define PIPE0BUF 256
#define PIPExBUF 64
/* Pipe Maxpacket Size Register */
#define MXPS 0x07FF /* b10-0: Maxpacket size */
/* Pipe Cycle Configuration Register */
#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */
#define IITV 0x0007 /* b2-0: Isochronous interval */
/* Pipex Control Register */
#define BSTS 0x8000 /* b15: Buffer status */
#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */
#define CSCLR 0x2000 /* b13: complete-split status clear */
#define CSSTS 0x1000 /* b12: complete-split status */
#define ATREPM 0x0400 /* b10: Auto repeat mode */
#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */
#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */
#define SQSET 0x0080 /* b7: Sequence toggle bit set */
#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */
#define PBUSY 0x0020 /* b5: pipe busy */
#define PID 0x0003 /* b1-0: Response PID */
/* PIPExTRE */
#define TRENB 0x0200 /* b9: Transaction counter enable */
#define TRCLR 0x0100 /* b8: Transaction counter clear */
/* PIPExTRN */
#define TRNCNT 0xFFFF /* b15-0: Transaction counter */
/* DEVADDx */
#define UPPHUB 0x7800
#define HUBPORT 0x0700
#define USBSPD 0x00C0
#define RTPORT 0x0001
#define R8A66597_MAX_NUM_PIPE 10
#define R8A66597_BUF_BSIZE 8
#define R8A66597_MAX_DEVICE 10
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
#define R8A66597_MAX_ROOT_HUB 1
#else
#define R8A66597_MAX_ROOT_HUB 2
#endif
#define R8A66597_MAX_SAMPLING 5
#define R8A66597_RH_POLL_TIME 10
#define BULK_IN_PIPENUM 3
#define BULK_IN_BUFNUM 8
#define BULK_OUT_PIPENUM 4
#define BULK_OUT_BUFNUM 40
#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5))
#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9))
#define make_devsel(addr) (addr << 12)
struct r8a66597 {
unsigned long reg;
unsigned short pipe_config; /* bit field */
unsigned short port_status;
unsigned short port_change;
u16 speed; /* HSMODE or FSMODE or LSMODE */
unsigned char rh_devnum;
};
static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset)
{
return inw(r8a66597->reg + offset);
}
static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597,
unsigned long offset, void *buf,
int len)
{
int i;
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
unsigned long fifoaddr = r8a66597->reg + offset;
unsigned long count;
unsigned long *p = buf;
count = len / 4;
for (i = 0; i < count; i++)
p[i] = inl(r8a66597->reg + offset);
if (len & 0x00000003) {
unsigned long tmp = inl(fifoaddr);
memcpy((unsigned char *)buf + count * 4, &tmp, len & 0x03);
}
#else
unsigned short *p = buf;
len = (len + 1) / 2;
for (i = 0; i < len; i++)
p[i] = inw(r8a66597->reg + offset);
#endif
}
static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val,
unsigned long offset)
{
outw(val, r8a66597->reg + offset);
}
static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597,
unsigned long offset, void *buf,
int len)
{
int i;
unsigned long fifoaddr = r8a66597->reg + offset;
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
unsigned long count;
unsigned char *pb;
unsigned long *p = buf;
count = len / 4;
for (i = 0; i < count; i++)
outl(p[i], fifoaddr);
if (len & 0x00000003) {
pb = (unsigned char *)buf + count * 4;
for (i = 0; i < (len & 0x00000003); i++) {
if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND)
outb(pb[i], fifoaddr + i);
else
outb(pb[i], fifoaddr + 3 - i);
}
}
#else
int odd = len & 0x0001;
unsigned short *p = buf;
len = len / 2;
for (i = 0; i < len; i++)
outw(p[i], fifoaddr);
if (odd) {
unsigned char *pb = (unsigned char *)(buf + len);
outb(*pb, fifoaddr);
}
#endif
}
static inline void r8a66597_mdfy(struct r8a66597 *r8a66597,
u16 val, u16 pat, unsigned long offset)
{
u16 tmp;
tmp = r8a66597_read(r8a66597, offset);
tmp = tmp & (~pat);
tmp = tmp | val;
r8a66597_write(r8a66597, tmp, offset);
}
#define r8a66597_bclr(r8a66597, val, offset) \
r8a66597_mdfy(r8a66597, 0, val, offset)
#define r8a66597_bset(r8a66597, val, offset) \
r8a66597_mdfy(r8a66597, val, 0, offset)
static inline unsigned long get_syscfg_reg(int port)
{
return port == 0 ? SYSCFG0 : SYSCFG1;
}
static inline unsigned long get_syssts_reg(int port)
{
return port == 0 ? SYSSTS0 : SYSSTS1;
}
static inline unsigned long get_dvstctr_reg(int port)
{
return port == 0 ? DVSTCTR0 : DVSTCTR1;
}
static inline unsigned long get_dmacfg_reg(int port)
{
return port == 0 ? DMA0CFG : DMA1CFG;
}
static inline unsigned long get_intenb_reg(int port)
{
return port == 0 ? INTENB1 : INTENB2;
}
static inline unsigned long get_intsts_reg(int port)
{
return port == 0 ? INTSTS1 : INTSTS2;
}
static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port)
{
unsigned long dvstctr_reg = get_dvstctr_reg(port);
return r8a66597_read(r8a66597, dvstctr_reg) & RHST;
}
static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port,
int power)
{
unsigned long dvstctr_reg = get_dvstctr_reg(port);
if (power)
r8a66597_bset(r8a66597, VBOUT, dvstctr_reg);
else
r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg);
}
#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2)
#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4)
#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4)
#define get_devadd_addr(address) (DEVADD0 + address * 2)
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h, based on usb_ohci.h) */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00
/* OHCI ROOT HUB REGISTER MASKS */
/* roothub.portstatus [i] bits */
#define RH_PS_CCS 0x00000001 /* current connect status */
#define RH_PS_PES 0x00000002 /* port enable status*/
#define RH_PS_PSS 0x00000004 /* port suspend status */
#define RH_PS_POCI 0x00000008 /* port over current indicator */
#define RH_PS_PRS 0x00000010 /* port reset status */
#define RH_PS_PPS 0x00000100 /* port power status */
#define RH_PS_LSDA 0x00000200 /* low speed device attached */
#define RH_PS_CSC 0x00010000 /* connect status change */
#define RH_PS_PESC 0x00020000 /* port enable status change */
#define RH_PS_PSSC 0x00040000 /* port suspend status change */
#define RH_PS_OCIC 0x00080000 /* over current indicator change */
#define RH_PS_PRSC 0x00100000 /* port reset status change */
/* roothub.status bits */
#define RH_HS_LPS 0x00000001 /* local power status */
#define RH_HS_OCI 0x00000002 /* over current indicator */
#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
#define RH_HS_LPSC 0x00010000 /* local power status change */
#define RH_HS_OCIC 0x00020000 /* over current indicator change */
#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
/* roothub.b masks */
#define RH_B_DR 0x0000ffff /* device removable flags */
#define RH_B_PPCM 0xffff0000 /* port power control mask */
/* roothub.a masks */
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
#define RH_A_PSM (1 << 8) /* power switching mode */
#define RH_A_NPS (1 << 9) /* no power switching */
#define RH_A_DT (1 << 10) /* device type (mbz) */
#define RH_A_OCPM (1 << 11) /* over current protection mode */
#define RH_A_NOCP (1 << 12) /* no over current protection */
#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
#endif /* __R8A66597_H__ */

View file

@ -0,0 +1,45 @@
/*
* URB OHCI HCD (Host Controller Driver) initialization for USB on the S3C64XX.
*
* Copyright (C) 2008,
* Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de>
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*/
#include <common.h>
#include <asm/arch/s3c6400.h>
int usb_cpu_init(void)
{
OTHERS_REG |= 0x10000;
return 0;
}
int usb_cpu_stop(void)
{
OTHERS_REG &= ~0x10000;
return 0;
}
void usb_cpu_init_fail(void)
{
OTHERS_REG &= ~0x10000;
}

View file

@ -0,0 +1,730 @@
/*
* (C) Copyright 2004
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* This code is based on linux driver for sl811hs chip, source at
* drivers/usb/host/sl811.c:
*
* SL811 Host Controller Interface driver for USB.
*
* Copyright (c) 2003/06, Courage Co., Ltd.
*
* Based on:
* 1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
* Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
* Adam Richter, Gregory P. Smith;
* 2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
* 3.Rewrited as sl811.o by Yin Aihua <yinah:couragetech.com.cn>
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <mpc8xx.h>
#include <usb.h>
#include "sl811.h"
#include "../../../board/kup/common/kup.h"
#ifdef __PPC__
# define EIEIO __asm__ volatile ("eieio")
#else
# define EIEIO /* nothing */
#endif
#define SL811_ADR (0x50000000)
#define SL811_DAT (0x50000001)
#ifdef SL811_DEBUG
static int debug = 9;
#endif
static int root_hub_devnum = 0;
static struct usb_port_status rh_status = { 0 };/* root hub port status */
static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
void *data, int buf_len, struct devrequest *cmd);
static void sl811_write (__u8 index, __u8 data)
{
*(volatile unsigned char *) (SL811_ADR) = index;
EIEIO;
*(volatile unsigned char *) (SL811_DAT) = data;
EIEIO;
}
static __u8 sl811_read (__u8 index)
{
__u8 data;
*(volatile unsigned char *) (SL811_ADR) = index;
EIEIO;
data = *(volatile unsigned char *) (SL811_DAT);
EIEIO;
return (data);
}
/*
* Read consecutive bytes of data from the SL811H/SL11H buffer
*/
static void inline sl811_read_buf(__u8 offset, __u8 *buf, __u8 size)
{
*(volatile unsigned char *) (SL811_ADR) = offset;
EIEIO;
while (size--) {
*buf++ = *(volatile unsigned char *) (SL811_DAT);
EIEIO;
}
}
/*
* Write consecutive bytes of data to the SL811H/SL11H buffer
*/
static void inline sl811_write_buf(__u8 offset, __u8 *buf, __u8 size)
{
*(volatile unsigned char *) (SL811_ADR) = offset;
EIEIO;
while (size--) {
*(volatile unsigned char *) (SL811_DAT) = *buf++;
EIEIO;
}
}
int usb_init_kup4x (void)
{
volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR;
volatile memctl8xx_t *memctl = &immap->im_memctl;
int i;
unsigned char tmp;
memctl = &immap->im_memctl;
memctl->memc_or7 = 0xFFFF8726;
memctl->memc_br7 = 0x50000401; /* start at 0x50000000 */
/* BP 14 low = USB ON */
immap->im_cpm.cp_pbdat &= ~(BP_USB_VCC);
/* PB 14 nomal port */
immap->im_cpm.cp_pbpar &= ~(BP_USB_VCC);
/* output */
immap->im_cpm.cp_pbdir |= (BP_USB_VCC);
puts ("USB: ");
for (i = 0x10; i < 0xff; i++) {
sl811_write(i, i);
tmp = (sl811_read(i));
if (tmp != i) {
printf ("SL811 compare error index=0x%02x read=0x%02x\n", i, tmp);
return (-1);
}
}
printf ("SL811 ready\n");
return (0);
}
/*
* This function resets SL811HS controller and detects the speed of
* the connecting device
*
* Return: 0 = no device attached; 1 = USB device attached
*/
static int sl811_hc_reset(void)
{
int status ;
sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
mdelay(20);
/* Disable hardware SOF generation, clear all irq status. */
sl811_write(SL811_CTRL1, 0);
mdelay(2);
sl811_write(SL811_INTRSTS, 0xff);
status = sl811_read(SL811_INTRSTS);
if (status & SL811_INTR_NOTPRESENT) {
/* Device is not present */
PDEBUG(0, "Device not present\n");
rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
sl811_write(SL811_INTR, SL811_INTR_INSRMV);
return 0;
}
/* Send SOF to address 0, endpoint 0. */
sl811_write(SL811_LEN_B, 0);
sl811_write(SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0));
sl811_write(SL811_DEV_B, 0x00);
sl811_write(SL811_SOFLOW, SL811_12M_LOW);
if (status & SL811_INTR_SPEED_FULL) {
/* full speed device connect directly to root hub */
PDEBUG (0, "Full speed Device attached\n");
sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
mdelay(20);
sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
sl811_write(SL811_CTRL1, SL811_CTRL1_SOF);
/* start the SOF or EOP */
sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
mdelay(2);
sl811_write(SL811_INTRSTS, 0xff);
} else {
/* slow speed device connect directly to root-hub */
PDEBUG(0, "Low speed Device attached\n");
sl811_write(SL811_CTRL1, SL811_CTRL1_RESET);
mdelay(20);
sl811_write(SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
sl811_write(SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
/* start the SOF or EOP */
sl811_write(SL811_CTRL_B, SL811_USB_CTRL_ARM);
rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
mdelay(2);
sl811_write(SL811_INTRSTS, 0xff);
}
rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
sl811_write(SL811_INTR, /*SL811_INTR_INSRMV*/SL811_INTR_DONE_A);
return 1;
}
int usb_lowlevel_init(int index, void **controller)
{
root_hub_devnum = 0;
sl811_hc_reset();
return 0;
}
int usb_lowlevel_stop(int index)
{
sl811_hc_reset();
return 0;
}
static int calc_needed_buswidth(int bytes, int need_preamble)
{
return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048;
}
static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len)
{
__u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
__u16 status = 0;
int err = 0, time_start = get_timer(0);
int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
(dev->speed == USB_SPEED_LOW);
if (len > 239)
return -1;
if (usb_pipeout(pipe))
ctrl |= SL811_USB_CTRL_DIR_OUT;
if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)))
ctrl |= SL811_USB_CTRL_TOGGLE_1;
if (need_preamble)
ctrl |= SL811_USB_CTRL_PREAMBLE;
sl811_write(SL811_INTRSTS, 0xff);
while (err < 3) {
sl811_write(SL811_ADDR_A, 0x10);
sl811_write(SL811_LEN_A, len);
if (usb_pipeout(pipe) && len)
sl811_write_buf(0x10, buffer, len);
if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble))
ctrl |= SL811_USB_CTRL_SOF;
else
ctrl &= ~SL811_USB_CTRL_SOF;
sl811_write(SL811_CTRL_A, ctrl);
while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) {
if (5*CONFIG_SYS_HZ < get_timer(time_start)) {
printf("USB transmit timed out\n");
return -USB_ST_CRC_ERR;
}
}
sl811_write(SL811_INTRSTS, 0xff);
status = sl811_read(SL811_STS_A);
if (status & SL811_USB_STS_ACK) {
int remainder = sl811_read(SL811_CNT_A);
if (remainder) {
PDEBUG(0, "usb transfer remainder = %d\n", remainder);
len -= remainder;
}
if (usb_pipein(pipe) && len)
sl811_read_buf(0x10, buffer, len);
return len;
}
if ((status & SL811_USB_STS_NAK) == SL811_USB_STS_NAK)
continue;
PDEBUG(0, "usb transfer error %#x\n", (int)status);
err++;
}
err = 0;
if (status & SL811_USB_STS_ERROR)
err |= USB_ST_BUF_ERR;
if (status & SL811_USB_STS_TIMEOUT)
err |= USB_ST_CRC_ERR;
if (status & SL811_USB_STS_STALL)
err |= USB_ST_STALLED;
return -err;
}
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len)
{
int dir_out = usb_pipeout(pipe);
int ep = usb_pipeendpoint(pipe);
int max = usb_maxpacket(dev, pipe);
int done = 0;
PDEBUG(7, "dev = %ld pipe = %ld buf = %p size = %d dir_out = %d\n",
usb_pipedevice(pipe), usb_pipeendpoint(pipe), buffer, len, dir_out);
dev->status = 0;
sl811_write(SL811_DEV_A, usb_pipedevice(pipe));
sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep));
while (done < len) {
int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
max > len - done ? len - done : max);
if (res < 0) {
dev->status = -res;
return res;
}
if (!dir_out && res < max) /* short packet */
break;
done += res;
usb_dotoggle(dev, ep, dir_out);
}
dev->act_len = done;
return 0;
}
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len,struct devrequest *setup)
{
int done = 0;
int devnum = usb_pipedevice(pipe);
int ep = usb_pipeendpoint(pipe);
dev->status = 0;
if (devnum == root_hub_devnum)
return sl811_rh_submit_urb(dev, pipe, buffer, len, setup);
PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n",
devnum, ep, buffer, len, (int)setup->requesttype,
(int)setup->request, sl811_read(SL811_SOFCNTDIV)*64);
sl811_write(SL811_DEV_A, devnum);
sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep));
/* setup phase */
usb_settoggle(dev, ep, 1, 0);
if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep),
(__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
int dir_in = usb_pipein(pipe);
int max = usb_maxpacket(dev, pipe);
/* data phase */
sl811_write(SL811_PIDEP_A,
PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep));
usb_settoggle(dev, ep, usb_pipeout(pipe), 1);
while (done < len) {
int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
max > len - done ? len - done : max);
if (res < 0) {
PDEBUG(0, "status data failed!\n");
dev->status = -res;
return 0;
}
done += res;
usb_dotoggle(dev, ep, usb_pipeout(pipe));
if (dir_in && res < max) /* short packet */
break;
}
/* status phase */
sl811_write(SL811_PIDEP_A,
PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep));
usb_settoggle(dev, ep, !usb_pipeout(pipe), 1);
if (sl811_send_packet(dev,
!dir_in ? usb_rcvctrlpipe(dev, ep) :
usb_sndctrlpipe(dev, ep),
0, 0) < 0) {
PDEBUG(0, "status phase failed!\n");
dev->status = -1;
}
} else {
PDEBUG(0, "setup phase failed!\n");
dev->status = -1;
}
dev->act_len = done;
return done;
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int len, int interval)
{
PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe,
buffer, len, interval);
return -1;
}
/*
* SL811 Virtual Root Hub
*/
/* Device descriptor */
static __u8 sl811_rh_dev_des[] =
{
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, /* __u16 bcdUSB; v1.1 */
0x01,
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
0x00, /* __u8 bDeviceProtocol; */
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
0x00, /* __u16 idVendor; */
0x00,
0x00, /* __u16 idProduct; */
0x00,
0x00, /* __u16 bcdDevice; */
0x00,
0x00, /* __u8 iManufacturer; */
0x02, /* __u8 iProduct; */
0x01, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
/* Configuration descriptor */
static __u8 sl811_rh_config_des[] =
{
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, /* __u16 wTotalLength; */
0x00,
0x01, /* __u8 bNumInterfaces; */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes;
Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
/* interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; */
0x00, /* __u8 if_iInterface; */
/* endpoint */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x08, /* __u16 ep_wMaxPacketSize; */
0x00,
0xff /* __u8 ep_bInterval; 255 ms */
};
/* root hub class descriptor*/
static __u8 sl811_rh_hub_des[] =
{
0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */
0x01, /* __u8 bNbrPorts; */
0x00, /* __u16 wHubCharacteristics; */
0x00,
0x50, /* __u8 bPwrOn2pwrGood; 2ms */
0x00, /* __u8 bHubContrCurrent; 0 mA */
0xfc, /* __u8 DeviceRemovable; *** 7 Ports max *** */
0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
};
/*
* helper routine for returning string descriptors in UTF-16LE
* input can actually be ISO-8859-1; ASCII is its 7-bit subset
*/
static int ascii2utf (char *s, u8 *utf, int utfmax)
{
int retval;
for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
*utf++ = *s++;
*utf++ = 0;
}
return retval;
}
/*
* root_hub_string is used by each host controller's root hub code,
* so that they're identified consistently throughout the system.
*/
static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
{
char buf [30];
/* assert (len > (2 * (sizeof (buf) + 1)));
assert (strlen (type) <= 8);*/
/* language ids */
if (id == 0) {
*data++ = 4; *data++ = 3; /* 4 bytes data */
*data++ = 0; *data++ = 0; /* some language id */
return 4;
/* serial number */
} else if (id == 1) {
sprintf (buf, "%#x", serial);
/* product description */
} else if (id == 2) {
sprintf (buf, "USB %s Root Hub", type);
/* id 3 == vendor description */
/* unsupported IDs --> "stall" */
} else
return 0;
ascii2utf (buf, data + 2, len - 2);
data [0] = 2 + strlen(buf) * 2;
data [1] = 3;
return data [0];
}
/* helper macro */
#define OK(x) len = (x); break
/*
* This function handles all USB request to the the virtual root hub
*/
static int sl811_rh_submit_urb(struct usb_device *usb_dev, unsigned long pipe,
void *data, int buf_len, struct devrequest *cmd)
{
__u8 data_buf[16];
__u8 *bufp = data_buf;
int len = 0;
int status = 0;
__u16 bmRType_bReq;
__u16 wValue = le16_to_cpu (cmd->value);
__u16 wLength = le16_to_cpu (cmd->length);
#ifdef SL811_DEBUG
__u16 wIndex = le16_to_cpu (cmd->index);
#endif
if (usb_pipeint(pipe)) {
PDEBUG(0, "interrupt transfer unimplemented!\n");
return 0;
}
bmRType_bReq = cmd->requesttype | (cmd->request << 8);
PDEBUG(5, "submit rh urb, req = %d(%x) val = %#x index = %#x len=%d\n",
bmRType_bReq, bmRType_bReq, wValue, wIndex, wLength);
/* Request Destination:
without flags: Device,
USB_RECIP_INTERFACE: interface,
USB_RECIP_ENDPOINT: endpoint,
USB_TYPE_CLASS means HUB here,
USB_RECIP_OTHER | USB_TYPE_CLASS almost ever means HUB_PORT here
*/
switch (bmRType_bReq) {
case RH_GET_STATUS:
*(__u16 *)bufp = cpu_to_le16(1);
OK(2);
case RH_GET_STATUS | USB_RECIP_INTERFACE:
*(__u16 *)bufp = cpu_to_le16(0);
OK(2);
case RH_GET_STATUS | USB_RECIP_ENDPOINT:
*(__u16 *)bufp = cpu_to_le16(0);
OK(2);
case RH_GET_STATUS | USB_TYPE_CLASS:
*(__u32 *)bufp = cpu_to_le32(0);
OK(4);
case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
*(__u32 *)bufp = cpu_to_le32(rh_status.wPortChange<<16 | rh_status.wPortStatus);
OK(4);
case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
switch (wValue) {
case 1:
OK(0);
}
break;
case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
switch (wValue) {
case C_HUB_LOCAL_POWER:
OK(0);
case C_HUB_OVER_CURRENT:
OK(0);
}
break;
case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
OK(0);
case USB_PORT_FEAT_SUSPEND:
rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
OK(0);
case USB_PORT_FEAT_POWER:
rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
OK(0);
case USB_PORT_FEAT_C_CONNECTION:
rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
OK(0);
case USB_PORT_FEAT_C_ENABLE:
rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
OK(0);
case USB_PORT_FEAT_C_SUSPEND:
rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
OK(0);
case USB_PORT_FEAT_C_OVER_CURRENT:
rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
OK(0);
case USB_PORT_FEAT_C_RESET:
rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
OK(0);
}
break;
case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
OK(0);
case USB_PORT_FEAT_RESET:
rh_status.wPortStatus |= USB_PORT_STAT_RESET;
rh_status.wPortChange = 0;
rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
OK(0);
case USB_PORT_FEAT_POWER:
rh_status.wPortStatus |= USB_PORT_STAT_POWER;
OK(0);
case USB_PORT_FEAT_ENABLE:
rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
OK(0);
}
break;
case RH_SET_ADDRESS:
root_hub_devnum = wValue;
OK(0);
case RH_GET_DESCRIPTOR:
switch ((wValue & 0xff00) >> 8) {
case USB_DT_DEVICE:
len = sizeof(sl811_rh_dev_des);
bufp = sl811_rh_dev_des;
OK(len);
case USB_DT_CONFIG:
len = sizeof(sl811_rh_config_des);
bufp = sl811_rh_config_des;
OK(len);
case USB_DT_STRING:
len = usb_root_hub_string(wValue & 0xff, (int)(long)0, "SL811HS", data, wLength);
if (len > 0) {
bufp = data;
OK(len);
}
default:
status = -32;
}
break;
case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
len = sizeof(sl811_rh_hub_des);
bufp = sl811_rh_hub_des;
OK(len);
case RH_GET_CONFIGURATION:
bufp[0] = 0x01;
OK(1);
case RH_SET_CONFIGURATION:
OK(0);
default:
PDEBUG(1, "unsupported root hub command\n");
status = -32;
}
len = min(len, buf_len);
if (data != bufp)
memcpy(data, bufp, len);
PDEBUG(5, "len = %d, status = %d\n", len, status);
usb_dev->status = status;
usb_dev->act_len = len;
return status == 0 ? len : status;
}

View file

@ -0,0 +1,104 @@
#ifndef __UBOOT_SL811_H
#define __UBOOT_SL811_H
#undef SL811_DEBUG
#ifdef SL811_DEBUG
#define PDEBUG(level, fmt, args...) \
if (debug >= (level)) printf("[%s:%d] " fmt, \
__PRETTY_FUNCTION__, __LINE__ , ## args)
#else
#define PDEBUG(level, fmt, args...) do {} while(0)
#endif
/* Sl811 host control register */
#define SL811_CTRL_A 0x00
#define SL811_ADDR_A 0x01
#define SL811_LEN_A 0x02
#define SL811_STS_A 0x03 /* read */
#define SL811_PIDEP_A 0x03 /* write */
#define SL811_CNT_A 0x04 /* read */
#define SL811_DEV_A 0x04 /* write */
#define SL811_CTRL1 0x05
#define SL811_INTR 0x06
#define SL811_CTRL_B 0x08
#define SL811_ADDR_B 0x09
#define SL811_LEN_B 0x0A
#define SL811_STS_B 0x0B /* read */
#define SL811_PIDEP_B 0x0B /* write */
#define SL811_CNT_B 0x0C /* read */
#define SL811_DEV_B 0x0C /* write */
#define SL811_INTRSTS 0x0D /* write clears bitwise */
#define SL811_HWREV 0x0E /* read */
#define SL811_SOFLOW 0x0E /* write */
#define SL811_SOFCNTDIV 0x0F /* read */
#define SL811_CTRL2 0x0F /* write */
/* USB control register bits (addr 0x00 and addr 0x08) */
#define SL811_USB_CTRL_ARM 0x01
#define SL811_USB_CTRL_ENABLE 0x02
#define SL811_USB_CTRL_DIR_OUT 0x04
#define SL811_USB_CTRL_ISO 0x10
#define SL811_USB_CTRL_SOF 0x20
#define SL811_USB_CTRL_TOGGLE_1 0x40
#define SL811_USB_CTRL_PREAMBLE 0x80
/* USB status register bits (addr 0x03 and addr 0x0B) */
#define SL811_USB_STS_ACK 0x01
#define SL811_USB_STS_ERROR 0x02
#define SL811_USB_STS_TIMEOUT 0x04
#define SL811_USB_STS_TOGGLE_1 0x08
#define SL811_USB_STS_SETUP 0x10
#define SL811_USB_STS_OVERFLOW 0x20
#define SL811_USB_STS_NAK 0x40
#define SL811_USB_STS_STALL 0x80
/* Control register 1 bits (addr 0x05) */
#define SL811_CTRL1_SOF 0x01
#define SL811_CTRL1_RESET 0x08
#define SL811_CTRL1_JKSTATE 0x10
#define SL811_CTRL1_SPEED_LOW 0x20
#define SL811_CTRL1_SUSPEND 0x40
/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */
#define SL811_INTR_DONE_A 0x01
#define SL811_INTR_DONE_B 0x02
#define SL811_INTR_SOF 0x10
#define SL811_INTR_INSRMV 0x20
#define SL811_INTR_DETECT 0x40
#define SL811_INTR_NOTPRESENT 0x40
#define SL811_INTR_SPEED_FULL 0x80 /* only in status reg */
/* HW rev and SOF lo register bits (addr 0x0E) */
#define SL811_HWR_HWREV 0xF0
/* SOF counter and control reg 2 (addr 0x0F) */
#define SL811_CTL2_SOFHI 0x3F
#define SL811_CTL2_DSWAP 0x40
#define SL811_CTL2_HOST 0x80
/* Set up for 1-ms SOF time. */
#define SL811_12M_LOW 0xE0
#define SL811_12M_HI 0x2E
#define SL811_DATA_START 0x10
#define SL811_DATA_LIMIT 240
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep))
#endif /* __UBOOT_SL811_H */

View file

@ -0,0 +1,96 @@
/*
* (C) Copyright 2012
* eInfochips Ltd. <www.einfochips.com>
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
*
* (C) Copyright 2009
* Marvell Semiconductor <www.marvell.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <asm/io.h>
#include <usb.h>
#include <asm/arch/cpu.h>
#include <asm/arch/armada100.h>
#include <asm/arch/utmi-armada100.h>
static int utmi_phy_init(void)
{
struct armd1usb_phy_reg *phy_regs =
(struct armd1usb_phy_reg *)UTMI_PHY_BASE;
int timeout;
setbits_le32(&phy_regs->utmi_ctrl, INPKT_DELAY_SOF | PLL_PWR_UP);
udelay(1000);
setbits_le32(&phy_regs->utmi_ctrl, PHY_PWR_UP);
clrbits_le32(&phy_regs->utmi_pll, PLL_FBDIV_MASK | PLL_REFDIV_MASK);
setbits_le32(&phy_regs->utmi_pll, N_DIVIDER << PLL_FBDIV | M_DIVIDER);
setbits_le32(&phy_regs->utmi_tx, PHSEL_VAL << CK60_PHSEL);
/* Calibrate pll */
timeout = 10000;
while (--timeout && ((readl(&phy_regs->utmi_pll) & PLL_READY) == 0))
;
if (!timeout)
return -1;
udelay(200);
setbits_le32(&phy_regs->utmi_pll, VCOCAL_START);
udelay(400);
clrbits_le32(&phy_regs->utmi_pll, VCOCAL_START);
udelay(200);
setbits_le32(&phy_regs->utmi_tx, RCAL_START);
udelay(400);
clrbits_le32(&phy_regs->utmi_tx, RCAL_START);
timeout = 10000;
while (--timeout && ((readl(&phy_regs->utmi_pll) & PLL_READY) == 0))
;
if (!timeout)
return -1;
return 0;
}
/*
* Initialize USB host controller's UTMI Physical interface
*/
int utmi_init(void)
{
struct armd1mpmu_registers *mpmu_regs =
(struct armd1mpmu_registers *)ARMD1_MPMU_BASE;
struct armd1apmu_registers *apmu_regs =
(struct armd1apmu_registers *)ARMD1_APMU_BASE;
/* Turn on 26Mhz ref clock for UTMI PLL */
setbits_le32(&mpmu_regs->acgr, APB2_26M_EN | AP_26M);
/* USB Clock reset */
writel(USB_SPH_AXICLK_EN, &apmu_regs->usbcrc);
writel(USB_SPH_AXICLK_EN | USB_SPH_AXI_RST, &apmu_regs->usbcrc);
/* Initialize UTMI transceiver */
return utmi_phy_init();
}

View file

@ -0,0 +1,316 @@
/*
* SAMSUNG EXYNOS5 USB HOST XHCI Controller
*
* Copyright (C) 2012 Samsung Electronics Co.Ltd
* Vivek Gautam <gautam.vivek@samsung.com>
* Vikas Sajjan <vikas.sajjan@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
/*
* This file is a conglomeration for DWC3-init sequence and further
* exynos5 specific PHY-init sequence.
*/
#include <common.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <malloc.h>
#include <usb.h>
#include <watchdog.h>
#include <asm/arch/cpu.h>
#include <asm/arch/power.h>
#include <asm/arch/xhci-exynos.h>
#include <asm-generic/errno.h>
#include <linux/compat.h>
#include <linux/usb/dwc3.h>
#include "xhci.h"
/* Declare global data pointer */
DECLARE_GLOBAL_DATA_PTR;
/**
* Contains pointers to register base addresses
* for the usb controller.
*/
struct exynos_xhci {
struct exynos_usb3_phy *usb3_phy;
struct xhci_hccr *hcd;
struct dwc3 *dwc3_reg;
};
static struct exynos_xhci exynos;
#ifdef CONFIG_OF_CONTROL
static int exynos_usb3_parse_dt(const void *blob, struct exynos_xhci *exynos)
{
fdt_addr_t addr;
unsigned int node;
int depth;
node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_XHCI);
if (node <= 0) {
debug("XHCI: Can't get device node for xhci\n");
return -ENODEV;
}
/*
* Get the base address for XHCI controller from the device node
*/
addr = fdtdec_get_addr(blob, node, "reg");
if (addr == FDT_ADDR_T_NONE) {
debug("Can't get the XHCI register base address\n");
return -ENXIO;
}
exynos->hcd = (struct xhci_hccr *)addr;
depth = 0;
node = fdtdec_next_compatible_subnode(blob, node,
COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth);
if (node <= 0) {
debug("XHCI: Can't get device node for usb3-phy controller\n");
return -ENODEV;
}
/*
* Get the base address for usbphy from the device node
*/
exynos->usb3_phy = (struct exynos_usb3_phy *)fdtdec_get_addr(blob, node,
"reg");
if (exynos->usb3_phy == NULL) {
debug("Can't get the usbphy register address\n");
return -ENXIO;
}
return 0;
}
#endif
static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy)
{
u32 reg;
/* enabling usb_drd phy */
set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN);
/* Reset USB 3.0 PHY */
writel(0x0, &phy->phy_reg0);
clrbits_le32(&phy->phy_param0,
/* Select PHY CLK source */
PHYPARAM0_REF_USE_PAD |
/* Set Loss-of-Signal Detector sensitivity */
PHYPARAM0_REF_LOSLEVEL_MASK);
setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL);
writel(0x0, &phy->phy_resume);
/*
* Setting the Frame length Adj value[6:1] to default 0x20
* See xHCI 1.0 spec, 5.2.4
*/
setbits_le32(&phy->link_system,
LINKSYSTEM_XHCI_VERSION_CONTROL |
LINKSYSTEM_FLADJ(0x20));
/* Set Tx De-Emphasis level */
clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK);
setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH);
setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL);
/* PHYTEST POWERDOWN Control */
clrbits_le32(&phy->phy_test,
PHYTEST_POWERDOWN_SSP |
PHYTEST_POWERDOWN_HSP);
/* UTMI Power Control */
writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi);
/* Use core clock from main PLL */
reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
/* Default 24Mhz crystal clock */
PHYCLKRST_FSEL(FSEL_CLKSEL_24M) |
PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
PHYCLKRST_SSC_REFCLKSEL(0x88) |
/* Force PortReset of PHY */
PHYCLKRST_PORTRESET |
/* Digital power supply in normal operating mode */
PHYCLKRST_RETENABLEN |
/* Enable ref clock for SS function */
PHYCLKRST_REF_SSP_EN |
/* Enable spread spectrum */
PHYCLKRST_SSC_EN |
/* Power down HS Bias and PLL blocks in suspend mode */
PHYCLKRST_COMMONONN;
writel(reg, &phy->phy_clk_rst);
/* giving time to Phy clock to settle before resetting */
udelay(10);
reg &= ~PHYCLKRST_PORTRESET;
writel(reg, &phy->phy_clk_rst);
}
static void exynos5_usb3_phy_exit(struct exynos_usb3_phy *phy)
{
setbits_le32(&phy->phy_utmi,
PHYUTMI_OTGDISABLE |
PHYUTMI_FORCESUSPEND |
PHYUTMI_FORCESLEEP);
clrbits_le32(&phy->phy_clk_rst,
PHYCLKRST_REF_SSP_EN |
PHYCLKRST_SSC_EN |
PHYCLKRST_COMMONONN);
/* PHYTEST POWERDOWN Control to remove leakage current */
setbits_le32(&phy->phy_test,
PHYTEST_POWERDOWN_SSP |
PHYTEST_POWERDOWN_HSP);
/* disabling usb_drd phy */
set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_DISABLE);
}
void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode)
{
clrsetbits_le32(&dwc3_reg->g_ctl,
DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG),
DWC3_GCTL_PRTCAPDIR(mode));
}
static void dwc3_core_soft_reset(struct dwc3 *dwc3_reg)
{
/* Before Resetting PHY, put Core in Reset */
setbits_le32(&dwc3_reg->g_ctl,
DWC3_GCTL_CORESOFTRESET);
/* Assert USB3 PHY reset */
setbits_le32(&dwc3_reg->g_usb3pipectl[0],
DWC3_GUSB3PIPECTL_PHYSOFTRST);
/* Assert USB2 PHY reset */
setbits_le32(&dwc3_reg->g_usb2phycfg,
DWC3_GUSB2PHYCFG_PHYSOFTRST);
mdelay(100);
/* Clear USB3 PHY reset */
clrbits_le32(&dwc3_reg->g_usb3pipectl[0],
DWC3_GUSB3PIPECTL_PHYSOFTRST);
/* Clear USB2 PHY reset */
clrbits_le32(&dwc3_reg->g_usb2phycfg,
DWC3_GUSB2PHYCFG_PHYSOFTRST);
/* After PHYs are stable we can take Core out of reset state */
clrbits_le32(&dwc3_reg->g_ctl,
DWC3_GCTL_CORESOFTRESET);
}
static int dwc3_core_init(struct dwc3 *dwc3_reg)
{
u32 reg;
u32 revision;
unsigned int dwc3_hwparams1;
revision = readl(&dwc3_reg->g_snpsid);
/* This should read as U3 followed by revision number */
if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) {
puts("this is not a DesignWare USB3 DRD Core\n");
return -EINVAL;
}
dwc3_core_soft_reset(dwc3_reg);
dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1);
reg = readl(&dwc3_reg->g_ctl);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
reg &= ~DWC3_GCTL_DISSCRAMBLE;
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
break;
default:
debug("No power optimization available\n");
}
/*
* WORKAROUND: DWC3 revisions <1.90a have a bug
* where the device can fail to connect at SuperSpeed
* and falls back to high-speed mode which causes
* the device to enter a Connect/Disconnect loop
*/
if ((revision & DWC3_REVISION_MASK) < 0x190a)
reg |= DWC3_GCTL_U2RSTECN;
writel(reg, &dwc3_reg->g_ctl);
return 0;
}
static int exynos_xhci_core_init(struct exynos_xhci *exynos)
{
int ret;
exynos5_usb3_phy_init(exynos->usb3_phy);
ret = dwc3_core_init(exynos->dwc3_reg);
if (ret) {
debug("failed to initialize core\n");
return -EINVAL;
}
/* We are hard-coding DWC3 core to Host Mode */
dwc3_set_mode(exynos->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
return 0;
}
static void exynos_xhci_core_exit(struct exynos_xhci *exynos)
{
exynos5_usb3_phy_exit(exynos->usb3_phy);
}
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
struct exynos_xhci *ctx = &exynos;
int ret;
#ifdef CONFIG_OF_CONTROL
exynos_usb3_parse_dt(gd->fdt_blob, ctx);
#else
ctx->usb3_phy = (struct exynos_usb3_phy *)samsung_get_base_usb3_phy();
ctx->hcd = (struct xhci_hccr *)samsung_get_base_usb_xhci();
#endif
ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
ret = exynos_xhci_core_init(ctx);
if (ret) {
puts("XHCI: failed to initialize controller\n");
return -EINVAL;
}
*hccr = (ctx->hcd);
*hcor = (struct xhci_hcor *)((uint32_t) *hccr
+ HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
debug("Exynos5-xhci: init hccr %x and hcor %x hc_length %d\n",
(uint32_t)*hccr, (uint32_t)*hcor,
(uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
return 0;
}
void xhci_hcd_stop(int index)
{
struct exynos_xhci *ctx = &exynos;
exynos_xhci_core_exit(ctx);
}

View file

@ -0,0 +1,508 @@
/*
* Copyright (c) 2014 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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 <common.h>
#include <usb.h>
#include <asm-generic/errno.h>
#include <linux/compat.h>
#include <linux/usb/dwc3.h>
#include <linux/usb/msm_usb30.h>
#include "xhci.h"
#define IPQ_XHCI_BASE_1 0x11000000
#define IPQ_XHCI_BASE_2 0x10000000
#define USB30_RESET 0x00903B50
#define USB30_1_RESET 0x00903B58
#define DWC3_SSUSB_REG_GCTL 0xC110
#define DWC3_SSUSB_REG_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_SSUSB_REG_GUSB2PHYCFG(n) (0xC200 + ((n) * 0x16))
#define DWC3_SSUSB_REG_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_SSUSB_REG_GUSB3PIPECTL(n) (0xC2C0 + ((n) * 0x16))
#define DWC3_SSUSB_PHY_RTUNE_RTUNE_CTRL_REG 0x34
#define DWC3_SSUSB_PHY_RTUNE_DEBUG_REG 0x3
#define RX_TERM_VALUE 0
#define RX_EQ_VALUE 4
#define DWC3_SSUSB_PHY_RX_OVRD_IN_HI_REG 0x1006
#define DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_EN 6
#define DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_EN_OVRD 7
#define DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_MASK 0x7
#define DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ 8
#define DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_OVRD 11
#define AMPLITUDE_VALUE 110
#define TX_DEEMPH_3_5DB 23
#define DWC3_SSUSB_PHY_TX_OVRD_DRV_LO_REG 0x1002
#define DWC3_SSUSB_PHY_TX_DEEMPH_MASK 0x3F80
#define DWC3_SSUSB_PHY_AMP_MASK 0x7F
#define DWC3_SSUSB_PHY_AMP_EN (1 << 14)
#define DWC3_SSUSB_REG_GUCTL 0xC12C
#define DWC3_SSUSB_PHY_TX_ALT_BLOCK_REG 0x102D
#define DWC3_SSUSB_PHY_TX_ALT_BLOCK_EN_ALT_BUS (1 << 7)
#define DWC3_GCTL_U2EXIT_LFPS (1 << 2)
#define DWC3_GCTL_SOFITPSYNC (1 << 10)
#define DWC3_GCTL 0xc110
#define DWC3_SSUSB_REG_GUSB2PHYCFG_SUSPENDUSB20 (1 << 6)
#define DWC3_SSUSB_REG_GUSB2PHYCFG_ENBLSLPM (1 << 8)
#define DWC3_SSUSB_REG_GUSB2PHYCFG_USBTRDTIM(n) ((n) << 10)
#define DWC3_SSUSB_REG_GUSB3PIPECTL_ELASTIC_BUFFER_MODE (1 << 0)
#define DWC3_SSUSB_REG_GUSB3PIPECTL_TX_DE_EPPHASIS(n) ((n) << 1)
#define DWC3_SSUSB_REG_GUSB3PIPECTL_TX_MARGIN(n) ((n) << 3)
#define DWC3_SSUSB_REG_GUSB3PIPECTL_DELAYP1TRANS (1 << 18)
#define DWC3_SSUSB_REG_GUSB3PIPECTL_DELAYP1P2P3(n) ((n) << 19)
#define DWC3_SSUSB_REG_GUSB3PIPECTL_U1U2EXITFAIL_TO_RECOV (1 << 25)
#define DWC3_SSUSB_REG_GUSB3PIPECTL_REQUEST_P1P2P3 (1 << 24)
#define DWC3_SSUSB_PHY_TX_DEEMPH_SHIFT 7
#define DWC3_SSUSB_XHCI_REV_10 1
extern board_ipq806x_params_t board_params[];
extern board_ipq806x_params_t *gboard_param;
/* Declare global data pointer */
DECLARE_GLOBAL_DATA_PTR;
struct ipq_xhci {
struct xhci_hccr *hcd;
struct dwc3 *dwc3_reg;
};
static struct ipq_xhci ipq;
int __board_usb_init(int index, enum usb_init_type init)
{
return 0;
}
int board_usb_init(int index, enum usb_init_type init)
__attribute__((weak, alias("__board_usb_init")));
static void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode)
{
clrsetbits_le32(&dwc3_reg->g_ctl,
DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG),
DWC3_GCTL_PRTCAPDIR(mode));
}
void ipq_reset_usb_phy(struct dwc3 *dwc3_reg)
{
/* Assert USB3 PHY reset */
setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST);
/* Assert USB2 PHY reset */
setbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST);
mdelay(100);
/* Clear USB3 PHY reset */
clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST);
/* Clear USB2 PHY reset */
clrbits_le32(&dwc3_reg->g_usb2phycfg, DWC3_GUSB2PHYCFG_PHYSOFTRST);
}
static void dwc3_core_soft_reset(struct dwc3 *dwc3_reg)
{
/* Before Resetting PHY, put Core in Reset */
setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET);
ipq_reset_usb_phy(dwc3_reg);
/* After PHYs are stable we can take Core out of reset state */
clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET);
}
static int dwc3_core_init(struct dwc3 *dwc3_reg)
{
u32 reg;
u32 revision;
unsigned int dwc3_hwparams1;
revision = readl(&dwc3_reg->g_snpsid);
/* This should read as U3 followed by revision number */
if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) {
puts("this is not a DesignWare USB3 DRD Core\n");
return -1;
}
dwc3_core_soft_reset(dwc3_reg);
dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1);
reg = readl(&dwc3_reg->g_ctl);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
reg &= ~DWC3_GCTL_DISSCRAMBLE;
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
break;
default:
debug("No power optimization available\n");
}
/*
* WORKAROUND: DWC3 revisions <1.90a have a bug
* where the device can fail to connect at SuperSpeed
* and falls back to high-speed mode which causes
* the device to enter a Connect/Disconnect loop
*/
if ((revision & DWC3_REVISION_MASK) < 0x190a)
reg |= DWC3_GCTL_U2RSTECN;
writel(reg, &dwc3_reg->g_ctl);
return 0;
}
static u16 dwc3_ipq_ssusb_read_phy_reg(unsigned int addr, unsigned int ipq_base)
{
u16 tmp_phy[3], i;
do {
for (i = 0; i < 3; i++) {
writel(addr, ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_DATA_IN);
writel(0x1, ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_ADDR);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_ADDR));
writel(0x1, ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_READ);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_READ));
tmp_phy[i] = (u16)readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_DATA_OUT);
}
} while (tmp_phy[1] != tmp_phy[2]);
return tmp_phy[2];
}
static void dwc3_ipq_ssusb_write_phy_reg(u32 addr, u16 data, unsigned int ipq_base)
{
writel(addr, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_DATA_IN);
writel(0x1, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_ADDR);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_ADDR));
writel(data, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_DATA_IN);
writel(0x1, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_DATA);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_CAP_DATA));
writel(0x1, ipq_base + IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_WRITE);
while (0 != readl(ipq_base +
IPQ_SSUSB_REG_QSCRATCH_SS_CR_PROTOCOL_WRITE));
}
static void ipq_ssusb_clear_bits32(u32 offset, u32 bits, unsigned int ipq_base)
{
u32 data;
data = readl(ipq_base+offset);
data = data & ~bits;
writel(data, ipq_base + offset);
}
static void ipq_ssusb_clear_and_set_bits32(u32 offset, u32 clear_bits, u32 set_bits, unsigned int ipq_base)
{
u32 data;
data = readl(ipq_base + offset);
data = (data & ~clear_bits) | set_bits;
writel(data, ipq_base + offset);
}
static void partial_rx_reset_init(unsigned int ipq_base)
{
u32 addr = DWC3_SSUSB_PHY_TX_ALT_BLOCK_REG;
u16 data = dwc3_ipq_ssusb_read_phy_reg(addr, ipq_base);
data |= DWC3_SSUSB_PHY_TX_ALT_BLOCK_EN_ALT_BUS;
dwc3_ipq_ssusb_write_phy_reg(addr, data, ipq_base);
return;
}
static void uw_ssusb_pre_init(unsigned int ipq_base)
{
u32 set_bits, tmp;
/* GCTL Reset ON */
writel(0x800, ipq_base + DWC3_SSUSB_REG_GCTL);
/* Config SS PHY CTRL */
set_bits = 0;
writel(0x80, ipq_base + IPQ_SS_PHY_CTRL_REG);
udelay(5);
ipq_ssusb_clear_bits32(IPQ_SS_PHY_CTRL_REG, 0x80, ipq_base);
udelay(5);
/* REF_USE_PAD */
set_bits = 0x0000000; /* USE Internal clock */
set_bits |= IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_LANE0_PWR_PRESENT;
set_bits |= IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_REF_SS_PHY_EN;
writel(set_bits, ipq_base + IPQ_SS_PHY_CTRL_REG);
/* Config HS PHY CTRL */
set_bits = IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL_UTMI_OTG_VBUS_VALID;
/*
* COMMONONN forces xo, bias and pll to stay on during suspend;
* Allowing suspend (writing 1) kills Aragorn V1
*/
set_bits |= IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL_COMMONONN;
set_bits |= IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL_USE_CLKCORE;
set_bits |= IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL_FSEL_VAL;
/*
* If the configuration of clocks is not bypassed in Host mode,
* HS PHY suspend needs to be prohibited, otherwise - SS connection fails
*/
ipq_ssusb_clear_and_set_bits32(IPQ_SSUSB_REG_QSCRATCH_HS_PHY_CTRL, 0,
set_bits, ipq_base);
/* USB2 PHY Reset ON */
writel(DWC3_SSUSB_REG_GUSB2PHYCFG_PHYSOFTRST, ipq_base +
DWC3_SSUSB_REG_GUSB2PHYCFG(0));
/* USB3 PHY Reset ON */
writel(DWC3_SSUSB_REG_GUSB3PIPECTL_PHYSOFTRST, ipq_base +
DWC3_SSUSB_REG_GUSB3PIPECTL(0));
udelay(5);
/* USB3 PHY Reset OFF */
ipq_ssusb_clear_bits32(DWC3_SSUSB_REG_GUSB3PIPECTL(0),
DWC3_SSUSB_REG_GUSB3PIPECTL_PHYSOFTRST, ipq_base);
ipq_ssusb_clear_bits32(DWC3_SSUSB_REG_GUSB2PHYCFG(0),
DWC3_GUSB2PHYCFG_PHYSOFTRST, ipq_base);
udelay(5);
/* GCTL Reset OFF */
ipq_ssusb_clear_bits32(DWC3_SSUSB_REG_GCTL, DWC3_GCTL_CORESOFTRESET,
ipq_base);
udelay(5);
if (RX_TERM_VALUE) {
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_RTUNE_RTUNE_CTRL_REG,
0, ipq_base);
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_RTUNE_DEBUG_REG,
0x0448, ipq_base);
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_RTUNE_DEBUG_REG,
RX_TERM_VALUE, ipq_base);
}
if (0 != RX_EQ_VALUE) { /* Values from 1 to 7 */
tmp =0;
/*
* 1. Fixed EQ setting. This can be achieved as follows:
* LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 - address 1006 bit 6
* LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 0- address 1006 bit 7
* LANE0.RX_OVRD_IN_HI.RX_EQ set to 4 (also try setting 3 if possible) -
* address 1006 bits 10:8 - please make this a variable, if unchanged the section is not executed
* LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 - address 1006 bit 11
*/
tmp = dwc3_ipq_ssusb_read_phy_reg(DWC3_SSUSB_PHY_RX_OVRD_IN_HI_REG,
ipq_base);
tmp &= ~((u16)1 << DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_EN);
tmp |= ((u16)1 << DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_EN_OVRD);
tmp &= ~((u16) DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_MASK <<
DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ);
tmp |= RX_EQ_VALUE << DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ;
tmp |= 1 << DWC3_SSUSB_PHY_RX_OVRD_IN_HI_RX_EQ_OVRD;
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_RX_OVRD_IN_HI_REG,
tmp, ipq_base);
}
if ((113 != AMPLITUDE_VALUE) || (21 != TX_DEEMPH_3_5DB)) {
tmp = dwc3_ipq_ssusb_read_phy_reg(DWC3_SSUSB_PHY_TX_OVRD_DRV_LO_REG,
ipq_base);
tmp &= ~DWC3_SSUSB_PHY_TX_DEEMPH_MASK;
tmp |= (TX_DEEMPH_3_5DB << DWC3_SSUSB_PHY_TX_DEEMPH_SHIFT);
tmp &= ~DWC3_SSUSB_PHY_AMP_MASK;
tmp |= AMPLITUDE_VALUE;
tmp |= DWC3_SSUSB_PHY_AMP_EN;
dwc3_ipq_ssusb_write_phy_reg(DWC3_SSUSB_PHY_TX_OVRD_DRV_LO_REG,
tmp, ipq_base);
}
ipq_ssusb_clear_and_set_bits32(IPQ_SS_PHY_PARAM_CTRL_1_REG,
0x7, 0x5, ipq_base);
/* XHCI REV */
writel((1 << 2), ipq_base + IPQ_QSCRATCH_GENERAL_CFG);
writel(0x0c80c010, ipq_base + DWC3_SSUSB_REG_GUCTL);
partial_rx_reset_init(ipq_base);
set_bits = 0;
/* Test U2EXIT_LFPS */
set_bits |= IPQ_SSUSB_REG_GCTL_U2EXIT_LFPS;
ipq_ssusb_clear_and_set_bits32(DWC3_SSUSB_REG_GCTL, 0,
set_bits, ipq_base);
set_bits = 0;
set_bits |= IPQ_SSUSB_REG_GCTL_U2RSTECN;
set_bits |= IPQ_SSUSB_REG_GCTL_U2EXIT_LFPS;
ipq_ssusb_clear_and_set_bits32(DWC3_SSUSB_REG_GCTL, 0,
set_bits, ipq_base);
writel(DWC3_GCTL_U2EXIT_LFPS | DWC3_GCTL_SOFITPSYNC |
DWC3_GCTL_PRTCAPDIR(1) |
DWC3_GCTL_U2RSTECN | DWC3_GCTL_PWRDNSCALE(2),
ipq_base + DWC3_GCTL);
writel((IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_MPLL_MULTI(0x19) |
IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_REF_SS_PHY_EN |
IPQ_SSUSB_QSCRATCH_SS_PHY_CTRL_LANE0_PWR_PRESENT),
ipq_base + IPQ_SS_PHY_CTRL_REG);
writel((DWC3_SSUSB_REG_GUSB2PHYCFG_SUSPENDUSB20 |
DWC3_SSUSB_REG_GUSB2PHYCFG_ENBLSLPM |
DWC3_SSUSB_REG_GUSB2PHYCFG_USBTRDTIM(9)),
ipq_base + DWC3_SSUSB_REG_GUSB2PHYCFG(0));
writel(DWC3_SSUSB_REG_GUSB3PIPECTL_ELASTIC_BUFFER_MODE |
DWC3_SSUSB_REG_GUSB3PIPECTL_TX_DE_EPPHASIS(1) |
DWC3_SSUSB_REG_GUSB3PIPECTL_TX_MARGIN(0)|
DWC3_SSUSB_REG_GUSB3PIPECTL_DELAYP1TRANS |
DWC3_SSUSB_REG_GUSB3PIPECTL_DELAYP1P2P3(1) |
DWC3_SSUSB_REG_GUSB3PIPECTL_U1U2EXITFAIL_TO_RECOV |
DWC3_SSUSB_REG_GUSB3PIPECTL_REQUEST_P1P2P3,
ipq_base + DWC3_SSUSB_REG_GUSB3PIPECTL(0));
writel(IPQ_SSUSB_REG_QSCRATCH_SS_PHY_PARAM_CTRL_1_LOS_LEVEL(0x9) |
IPQ_SSUSB_REG_QSCRATCH_SS_PHY_PARAM_CTRL_1_TX_DEEMPH_3_5DB(0x17) |
IPQ_SSUSB_REG_QSCRATCH_SS_PHY_PARAM_CTRL_1_TX_DEEMPH_6DB(0x20) |
IPQ_SSUSB_REG_QSCRATCH_SS_PHY_PARAM_CTRL_1_TX_SWING_FULL(0x6E),
ipq_base + IPQ_SS_PHY_PARAM_CTRL_1_REG);
writel(IPQ_SSUSB_REG_QSCRATCH_GENERAL_CFG_XHCI_REV(DWC3_SSUSB_XHCI_REV_10),
ipq_base + IPQ_QSCRATCH_GENERAL_CFG);
}
static void usb30_common_pre_init(int id, unsigned int ipq_base)
{
unsigned int reg;
if (id == 0)
reg = USB30_RESET;
else
reg = USB30_1_RESET;
writel(IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
writel(IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
writel(IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
writel(IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
writel(IPQ_USB30_RESET_MASTER_ASYNC_RESET |
IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
if (id == 0) {
writel(IPQ_USB30_RESET_PORT2_HS_PHY_ASYNC_RESET |
IPQ_USB30_RESET_MASTER_ASYNC_RESET |
IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET, reg);
}
udelay(5);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
writel(IPQ_USB30_RESET_MASK & ~(IPQ_USB30_RESET_MASTER_ASYNC_RESET |
IPQ_USB30_RESET_SLEEP_ASYNC_RESET|
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
if (id == 0) {
writel(IPQ_USB30_RESET_MASK &
~(IPQ_USB30_RESET_PORT2_HS_PHY_ASYNC_RESET |
IPQ_USB30_RESET_MASTER_ASYNC_RESET |
IPQ_USB30_RESET_SLEEP_ASYNC_RESET |
IPQ_USB30_RESET_MOC_UTMI_ASYNC_RESET |
IPQ_USB30_RESET_POWERON_ASYNC_RESET |
IPQ_USB30_RESET_PHY_ASYNC_RESET), reg);
reg = IPQ_TCSR_USB_CONTROLLER_TYPE_SEL;
if (reg) {
writel(0x3, reg);
}
}
writel((IPQ_SSUSB_REG_QSCRATCH_CGCTL_RAM1112_EN |
IPQ_SSUSB_REG_QSCRATCH_CGCTL_RAM13_EN),
ipq_base + IPQ_SSUSB_REG_QSCRATCH_CGCTL);
writel((IPQ_SSUSB_REG_QSCRATCH_RAM1_RAM13_EN |
IPQ_SSUSB_REG_QSCRATCH_RAM1RAM12_EN |
IPQ_SSUSB_REG_QSCRATCH_RAM1_RAM11_EN),
ipq_base + IPQ_SSUSB_REG_QSCRATCH_RAM1);
}
static int ipq_xhci_core_init(struct ipq_xhci *ipq, unsigned int ipq_base)
{
int ret = 0;
/* Configure the usb core clock */
usb_ss_core_clock_config(0,
gboard_param->usb_core_mnd_value.m_value,
gboard_param->usb_core_mnd_value.n_value,
gboard_param->usb_core_mnd_value.d_value,
gboard_param->clk_dummy);
/* Configure the usb core clock */
usb_ss_utmi_clock_config(0,
gboard_param->usb_utmi_mnd_value.m_value,
gboard_param->usb_utmi_mnd_value.n_value,
gboard_param->usb_utmi_mnd_value.d_value,
gboard_param->clk_dummy);
usb30_common_pre_init(0, ipq_base);
uw_ssusb_pre_init(ipq_base);
ret = dwc3_core_init(ipq->dwc3_reg);
if (ret) {
debug("%s:failed to initialize core\n", __func__);
return ret;
}
/* We are hard-coding DWC3 core to Host Mode */
dwc3_set_mode(ipq->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
return ret;
}
static void ipq_xhci_core_exit(struct ipq_xhci *ipq)
{
}
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
struct ipq_xhci *ctx = &ipq;
unsigned int ipq_base;
int ret = 0;
if ( index == 0 )
ipq_base = IPQ_XHCI_BASE_1;
else
ipq_base = IPQ_XHCI_BASE_2;
ctx->hcd = (struct xhci_hccr *)ipq_base;
ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
ret = board_usb_init(index, USB_INIT_HOST);
if (ret != 0) {
puts("Failed to initialize board for USB\n");
return ret;
}
ret = ipq_xhci_core_init(ctx, ipq_base);
if (ret < 0) {
puts("Failed to initialize xhci\n");
return ret;
}
*hccr = (struct xhci_hccr *)(ipq_base);
*hcor = (struct xhci_hcor *)((uint32_t) *hccr
+ HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
debug("omap-xhci: init hccr %x and hcor %x hc_length %d\n",
(uint32_t)*hccr, (uint32_t)*hcor,
(uint32_t)HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
return ret;
}
void xhci_hcd_stop(int index)
{
struct ipq_xhci *ctx = &ipq;
ipq_xhci_core_exit(ctx);
}

View file

@ -0,0 +1,720 @@
/*
* USB HOST XHCI Controller stack
*
* Based on xHCI host controller driver in linux-kernel
* by Sarah Sharp.
*
* Copyright (C) 2008 Intel Corp.
* Author: Sarah Sharp
*
* Copyright (C) 2013 Samsung Electronics Co.Ltd
* Authors: Vivek Gautam <gautam.vivek@samsung.com>
* Vikas Sajjan <vikas.sajjan@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/byteorder.h>
#include <usb.h>
#include <malloc.h>
#include <asm/cache.h>
#include <asm-generic/errno.h>
#include "xhci.h"
#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
/**
* flushes the address passed till the length
*
* @param addr pointer to memory region to be flushed
* @param len the length of the cache line to be flushed
* @return none
*/
void xhci_flush_cache(uint32_t addr, u32 len)
{
BUG_ON((void *)addr == NULL || len == 0);
flush_dcache_range(addr & ~(CACHELINE_SIZE - 1),
ALIGN(addr + len, CACHELINE_SIZE));
}
/**
* invalidates the address passed till the length
*
* @param addr pointer to memory region to be invalidates
* @param len the length of the cache line to be invalidated
* @return none
*/
void xhci_inval_cache(uint32_t addr, u32 len)
{
BUG_ON((void *)addr == NULL || len == 0);
invalidate_dcache_range(addr & ~(CACHELINE_SIZE - 1),
ALIGN(addr + len, CACHELINE_SIZE));
}
/**
* frees the "segment" pointer passed
*
* @param ptr pointer to "segement" to be freed
* @return none
*/
static void xhci_segment_free(struct xhci_segment *seg)
{
free(seg->trbs);
seg->trbs = NULL;
free(seg);
}
/**
* frees the "ring" pointer passed
*
* @param ptr pointer to "ring" to be freed
* @return none
*/
static void xhci_ring_free(struct xhci_ring *ring)
{
struct xhci_segment *seg;
struct xhci_segment *first_seg;
BUG_ON(!ring);
first_seg = ring->first_seg;
seg = first_seg->next;
while (seg != first_seg) {
struct xhci_segment *next = seg->next;
xhci_segment_free(seg);
seg = next;
}
xhci_segment_free(first_seg);
free(ring);
}
/**
* frees the "xhci_container_ctx" pointer passed
*
* @param ptr pointer to "xhci_container_ctx" to be freed
* @return none
*/
static void xhci_free_container_ctx(struct xhci_container_ctx *ctx)
{
free(ctx->bytes);
free(ctx);
}
/**
* frees the virtual devices for "xhci_ctrl" pointer passed
*
* @param ptr pointer to "xhci_ctrl" whose virtual devices are to be freed
* @return none
*/
static void xhci_free_virt_devices(struct xhci_ctrl *ctrl)
{
int i;
int slot_id;
struct xhci_virt_device *virt_dev;
/*
* refactored here to loop through all virt_dev
* Slot ID 0 is reserved
*/
for (slot_id = 0; slot_id < MAX_HC_SLOTS; slot_id++) {
virt_dev = ctrl->devs[slot_id];
if (!virt_dev)
continue;
ctrl->dcbaa->dev_context_ptrs[slot_id] = 0;
for (i = 0; i < 31; ++i)
if (virt_dev->eps[i].ring)
xhci_ring_free(virt_dev->eps[i].ring);
if (virt_dev->in_ctx)
xhci_free_container_ctx(virt_dev->in_ctx);
if (virt_dev->out_ctx)
xhci_free_container_ctx(virt_dev->out_ctx);
free(virt_dev);
/* make sure we are pointing to NULL */
ctrl->devs[slot_id] = NULL;
}
}
/**
* frees all the memory allocated
*
* @param ptr pointer to "xhci_ctrl" to be cleaned up
* @return none
*/
void xhci_cleanup(struct xhci_ctrl *ctrl)
{
xhci_ring_free(ctrl->event_ring);
xhci_ring_free(ctrl->cmd_ring);
xhci_free_virt_devices(ctrl);
free(ctrl->erst.entries);
free(ctrl->dcbaa);
memset(ctrl, '\0', sizeof(struct xhci_ctrl));
}
/**
* Malloc the aligned memory
*
* @param size size of memory to be allocated
* @return allocates the memory and returns the aligned pointer
*/
static void *xhci_malloc(unsigned int size)
{
void *ptr;
size_t cacheline_size = max(XHCI_ALIGNMENT, CACHELINE_SIZE);
ptr = memalign(cacheline_size, ALIGN(size, cacheline_size));
BUG_ON(!ptr);
memset(ptr, '\0', size);
xhci_flush_cache((uint32_t)ptr, size);
return ptr;
}
/**
* Make the prev segment point to the next segment.
* Change the last TRB in the prev segment to be a Link TRB which points to the
* address of the next segment. The caller needs to set any Link TRB
* related flags, such as End TRB, Toggle Cycle, and no snoop.
*
* @param prev pointer to the previous segment
* @param next pointer to the next segment
* @param link_trbs flag to indicate whether to link the trbs or NOT
* @return none
*/
static void xhci_link_segments(struct xhci_segment *prev,
struct xhci_segment *next, bool link_trbs)
{
u32 val;
u64 val_64 = 0;
if (!prev || !next)
return;
prev->next = next;
if (link_trbs) {
val_64 = (uintptr_t)next->trbs;
prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = val_64;
/*
* Set the last TRB in the segment to
* have a TRB type ID of Link TRB
*/
val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
val &= ~TRB_TYPE_BITMASK;
val |= (TRB_LINK << TRB_TYPE_SHIFT);
prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
}
}
/**
* Initialises the Ring's enqueue,dequeue,enq_seg pointers
*
* @param ring pointer to the RING to be intialised
* @return none
*/
static void xhci_initialize_ring_info(struct xhci_ring *ring)
{
/*
* The ring is empty, so the enqueue pointer == dequeue pointer
*/
ring->enqueue = ring->first_seg->trbs;
ring->enq_seg = ring->first_seg;
ring->dequeue = ring->enqueue;
ring->deq_seg = ring->first_seg;
/*
* The ring is initialized to 0. The producer must write 1 to the
* cycle bit to handover ownership of the TRB, so PCS = 1.
* The consumer must compare CCS to the cycle bit to
* check ownership, so CCS = 1.
*/
ring->cycle_state = 1;
}
/**
* Allocates a generic ring segment from the ring pool, sets the dma address,
* initializes the segment to zero, and sets the private next pointer to NULL.
* Section 4.11.1.1:
* "All components of all Command and Transfer TRBs shall be initialized to '0'"
*
* @param none
* @return pointer to the newly allocated SEGMENT
*/
static struct xhci_segment *xhci_segment_alloc(void)
{
struct xhci_segment *seg;
seg = (struct xhci_segment *)malloc(sizeof(struct xhci_segment));
BUG_ON(!seg);
seg->trbs = (union xhci_trb *)xhci_malloc(SEGMENT_SIZE);
seg->next = NULL;
return seg;
}
/**
* Create a new ring with zero or more segments.
* TODO: current code only uses one-time-allocated single-segment rings
* of 1KB anyway, so we might as well get rid of all the segment and
* linking code (and maybe increase the size a bit, e.g. 4KB).
*
*
* Link each segment together into a ring.
* Set the end flag and the cycle toggle bit on the last segment.
* See section 4.9.2 and figures 15 and 16 of XHCI spec rev1.0.
*
* @param num_segs number of segments in the ring
* @param link_trbs flag to indicate whether to link the trbs or NOT
* @return pointer to the newly created RING
*/
struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs)
{
struct xhci_ring *ring;
struct xhci_segment *prev;
ring = (struct xhci_ring *)malloc(sizeof(struct xhci_ring));
BUG_ON(!ring);
if (num_segs == 0)
return ring;
ring->first_seg = xhci_segment_alloc();
BUG_ON(!ring->first_seg);
num_segs--;
prev = ring->first_seg;
while (num_segs > 0) {
struct xhci_segment *next;
next = xhci_segment_alloc();
BUG_ON(!next);
xhci_link_segments(prev, next, link_trbs);
prev = next;
num_segs--;
}
xhci_link_segments(prev, ring->first_seg, link_trbs);
if (link_trbs) {
/* See section 4.9.2.1 and 6.4.4.1 */
prev->trbs[TRBS_PER_SEGMENT-1].link.control |=
cpu_to_le32(LINK_TOGGLE);
}
xhci_initialize_ring_info(ring);
return ring;
}
/**
* Allocates the Container context
*
* @param ctrl Host controller data structure
* @param type type of XHCI Container Context
* @return NULL if failed else pointer to the context on success
*/
static struct xhci_container_ctx
*xhci_alloc_container_ctx(struct xhci_ctrl *ctrl, int type)
{
struct xhci_container_ctx *ctx;
ctx = (struct xhci_container_ctx *)
malloc(sizeof(struct xhci_container_ctx));
BUG_ON(!ctx);
BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT));
ctx->type = type;
ctx->size = (MAX_EP_CTX_NUM + 1) *
CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
if (type == XHCI_CTX_TYPE_INPUT)
ctx->size += CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
ctx->bytes = (u8 *)xhci_malloc(ctx->size);
return ctx;
}
/**
* Allocating virtual device
*
* @param udev pointer to USB deivce structure
* @return 0 on success else -1 on failure
*/
int xhci_alloc_virt_device(struct usb_device *udev)
{
u64 byte_64 = 0;
unsigned int slot_id = udev->slot_id;
struct xhci_virt_device *virt_dev;
struct xhci_ctrl *ctrl = udev->controller;
/* Slot ID 0 is reserved */
if (ctrl->devs[slot_id]) {
printf("Virt dev for slot[%d] already allocated\n", slot_id);
return -EEXIST;
}
ctrl->devs[slot_id] = (struct xhci_virt_device *)
malloc(sizeof(struct xhci_virt_device));
if (!ctrl->devs[slot_id]) {
puts("Failed to allocate virtual device\n");
return -ENOMEM;
}
memset(ctrl->devs[slot_id], 0, sizeof(struct xhci_virt_device));
virt_dev = ctrl->devs[slot_id];
/* Allocate the (output) device context that will be used in the HC. */
virt_dev->out_ctx = xhci_alloc_container_ctx(ctrl,
XHCI_CTX_TYPE_DEVICE);
if (!virt_dev->out_ctx) {
puts("Failed to allocate out context for virt dev\n");
return -ENOMEM;
}
/* Allocate the (input) device context for address device command */
virt_dev->in_ctx = xhci_alloc_container_ctx(ctrl,
XHCI_CTX_TYPE_INPUT);
if (!virt_dev->in_ctx) {
puts("Failed to allocate in context for virt dev\n");
return -ENOMEM;
}
/* Allocate endpoint 0 ring */
virt_dev->eps[0].ring = xhci_ring_alloc(1, true);
byte_64 = (uintptr_t)(virt_dev->out_ctx->bytes);
/* Point to output device context in dcbaa. */
ctrl->dcbaa->dev_context_ptrs[slot_id] = byte_64;
xhci_flush_cache((uint32_t)&ctrl->dcbaa->dev_context_ptrs[slot_id],
sizeof(__le64));
return 0;
}
/**
* Allocates the necessary data structures
* for XHCI host controller
*
* @param ctrl Host controller data structure
* @param hccr pointer to HOST Controller Control Registers
* @param hcor pointer to HOST Controller Operational Registers
* @return 0 if successful else -1 on failure
*/
int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
struct xhci_hcor *hcor)
{
uint64_t val_64;
uint64_t trb_64;
uint32_t val;
unsigned long deq;
int i;
struct xhci_segment *seg;
/* DCBAA initialization */
ctrl->dcbaa = (struct xhci_device_context_array *)
xhci_malloc(sizeof(struct xhci_device_context_array));
if (ctrl->dcbaa == NULL) {
puts("unable to allocate DCBA\n");
return -ENOMEM;
}
val_64 = (uintptr_t)ctrl->dcbaa;
/* Set the pointer in DCBAA register */
xhci_writeq(&hcor->or_dcbaap, val_64);
/* Command ring control pointer register initialization */
ctrl->cmd_ring = xhci_ring_alloc(1, true);
/* Set the address in the Command Ring Control register */
trb_64 = (uintptr_t)ctrl->cmd_ring->first_seg->trbs;
val_64 = xhci_readq(&hcor->or_crcr);
val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
(trb_64 & (u64) ~CMD_RING_RSVD_BITS) |
ctrl->cmd_ring->cycle_state;
xhci_writeq(&hcor->or_crcr, val_64);
/* write the address of db register */
val = xhci_readl(&hccr->cr_dboff);
val &= DBOFF_MASK;
ctrl->dba = (struct xhci_doorbell_array *)((char *)hccr + val);
/* write the address of runtime register */
val = xhci_readl(&hccr->cr_rtsoff);
val &= RTSOFF_MASK;
ctrl->run_regs = (struct xhci_run_regs *)((char *)hccr + val);
/* writting the address of ir_set structure */
ctrl->ir_set = &ctrl->run_regs->ir_set[0];
/* Event ring does not maintain link TRB */
ctrl->event_ring = xhci_ring_alloc(ERST_NUM_SEGS, false);
ctrl->erst.entries = (struct xhci_erst_entry *)
xhci_malloc(sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS);
ctrl->erst.num_entries = ERST_NUM_SEGS;
for (val = 0, seg = ctrl->event_ring->first_seg;
val < ERST_NUM_SEGS;
val++) {
trb_64 = 0;
trb_64 = (uintptr_t)seg->trbs;
struct xhci_erst_entry *entry = &ctrl->erst.entries[val];
xhci_writeq(&entry->seg_addr, trb_64);
entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
entry->rsvd = 0;
seg = seg->next;
}
xhci_flush_cache((uint32_t)ctrl->erst.entries,
ERST_NUM_SEGS * sizeof(struct xhci_erst_entry));
deq = (unsigned long)ctrl->event_ring->dequeue;
/* Update HC event ring dequeue pointer */
xhci_writeq(&ctrl->ir_set->erst_dequeue,
(u64)deq & (u64)~ERST_PTR_MASK);
/* set ERST count with the number of entries in the segment table */
val = xhci_readl(&ctrl->ir_set->erst_size);
val &= ERST_SIZE_MASK;
val |= ERST_NUM_SEGS;
xhci_writel(&ctrl->ir_set->erst_size, val);
/* this is the event ring segment table pointer */
val_64 = xhci_readq(&ctrl->ir_set->erst_base);
val_64 &= ERST_PTR_MASK;
val_64 |= ((u32)(ctrl->erst.entries) & ~ERST_PTR_MASK);
xhci_writeq(&ctrl->ir_set->erst_base, val_64);
/* initializing the virtual devices to NULL */
for (i = 0; i < MAX_HC_SLOTS; ++i)
ctrl->devs[i] = NULL;
/*
* Just Zero'ing this register completely,
* or some spurious Device Notification Events
* might screw things here.
*/
xhci_writel(&hcor->or_dnctrl, 0x0);
return 0;
}
/**
* Give the input control context for the passed container context
*
* @param ctx pointer to the context
* @return pointer to the Input control context data
*/
struct xhci_input_control_ctx
*xhci_get_input_control_ctx(struct xhci_container_ctx *ctx)
{
BUG_ON(ctx->type != XHCI_CTX_TYPE_INPUT);
return (struct xhci_input_control_ctx *)ctx->bytes;
}
/**
* Give the slot context for the passed container context
*
* @param ctrl Host controller data structure
* @param ctx pointer to the context
* @return pointer to the slot control context data
*/
struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl,
struct xhci_container_ctx *ctx)
{
if (ctx->type == XHCI_CTX_TYPE_DEVICE)
return (struct xhci_slot_ctx *)ctx->bytes;
return (struct xhci_slot_ctx *)
(ctx->bytes + CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)));
}
/**
* Gets the EP context from based on the ep_index
*
* @param ctrl Host controller data structure
* @param ctx context container
* @param ep_index index of the endpoint
* @return pointer to the End point context
*/
struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctrl *ctrl,
struct xhci_container_ctx *ctx,
unsigned int ep_index)
{
/* increment ep index by offset of start of ep ctx array */
ep_index++;
if (ctx->type == XHCI_CTX_TYPE_INPUT)
ep_index++;
return (struct xhci_ep_ctx *)
(ctx->bytes +
(ep_index * CTX_SIZE(readl(&ctrl->hccr->cr_hccparams))));
}
/**
* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy.
* Useful when you want to change one particular aspect of the endpoint
* and then issue a configure endpoint command.
*
* @param ctrl Host controller data structure
* @param in_ctx contains the input context
* @param out_ctx contains the input context
* @param ep_index index of the end point
* @return none
*/
void xhci_endpoint_copy(struct xhci_ctrl *ctrl,
struct xhci_container_ctx *in_ctx,
struct xhci_container_ctx *out_ctx,
unsigned int ep_index)
{
struct xhci_ep_ctx *out_ep_ctx;
struct xhci_ep_ctx *in_ep_ctx;
out_ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index);
in_ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index);
in_ep_ctx->ep_info = out_ep_ctx->ep_info;
in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2;
in_ep_ctx->deq = out_ep_ctx->deq;
in_ep_ctx->tx_info = out_ep_ctx->tx_info;
}
/**
* Copy output xhci_slot_ctx to the input xhci_slot_ctx.
* Useful when you want to change one particular aspect of the endpoint
* and then issue a configure endpoint command.
* Only the context entries field matters, but
* we'll copy the whole thing anyway.
*
* @param ctrl Host controller data structure
* @param in_ctx contains the inpout context
* @param out_ctx contains the inpout context
* @return none
*/
void xhci_slot_copy(struct xhci_ctrl *ctrl, struct xhci_container_ctx *in_ctx,
struct xhci_container_ctx *out_ctx)
{
struct xhci_slot_ctx *in_slot_ctx;
struct xhci_slot_ctx *out_slot_ctx;
in_slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx);
out_slot_ctx = xhci_get_slot_ctx(ctrl, out_ctx);
in_slot_ctx->dev_info = out_slot_ctx->dev_info;
in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2;
in_slot_ctx->tt_info = out_slot_ctx->tt_info;
in_slot_ctx->dev_state = out_slot_ctx->dev_state;
}
/**
* Setup an xHCI virtual device for a Set Address command
*
* @param udev pointer to the Device Data Structure
* @return returns negative value on failure else 0 on success
*/
void xhci_setup_addressable_virt_dev(struct usb_device *udev)
{
struct usb_device *hop = udev;
struct xhci_virt_device *virt_dev;
struct xhci_ep_ctx *ep0_ctx;
struct xhci_slot_ctx *slot_ctx;
u32 port_num = 0;
u64 trb_64 = 0;
struct xhci_ctrl *ctrl = udev->controller;
virt_dev = ctrl->devs[udev->slot_id];
BUG_ON(!virt_dev);
/* Extract the EP0 and Slot Ctrl */
ep0_ctx = xhci_get_ep_ctx(ctrl, virt_dev->in_ctx, 0);
slot_ctx = xhci_get_slot_ctx(ctrl, virt_dev->in_ctx);
/* Only the control endpoint is valid - one endpoint context */
slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | 0);
switch (udev->speed) {
case USB_SPEED_SUPER:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS);
break;
case USB_SPEED_HIGH:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS);
break;
case USB_SPEED_FULL:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS);
break;
case USB_SPEED_LOW:
slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS);
break;
default:
/* Speed was set earlier, this shouldn't happen. */
BUG();
}
/* Extract the root hub port number */
if (hop->parent)
while (hop->parent->parent)
hop = hop->parent;
port_num = hop->portnr;
debug("port_num = %d\n", port_num);
slot_ctx->dev_info2 |=
cpu_to_le32(((port_num & ROOT_HUB_PORT_MASK) <<
ROOT_HUB_PORT_SHIFT));
/* Step 4 - ring already allocated */
/* Step 5 */
ep0_ctx->ep_info2 = cpu_to_le32(CTRL_EP << EP_TYPE_SHIFT);
debug("SPEED = %d\n", udev->speed);
switch (udev->speed) {
case USB_SPEED_SUPER:
ep0_ctx->ep_info2 |= cpu_to_le32(((512 & MAX_PACKET_MASK) <<
MAX_PACKET_SHIFT));
debug("Setting Packet size = 512bytes\n");
break;
case USB_SPEED_HIGH:
/* USB core guesses at a 64-byte max packet first for FS devices */
case USB_SPEED_FULL:
ep0_ctx->ep_info2 |= cpu_to_le32(((64 & MAX_PACKET_MASK) <<
MAX_PACKET_SHIFT));
debug("Setting Packet size = 64bytes\n");
break;
case USB_SPEED_LOW:
ep0_ctx->ep_info2 |= cpu_to_le32(((8 & MAX_PACKET_MASK) <<
MAX_PACKET_SHIFT));
debug("Setting Packet size = 8bytes\n");
break;
default:
/* New speed? */
BUG();
}
/* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */
ep0_ctx->ep_info2 |=
cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) |
((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT));
trb_64 = (uintptr_t)virt_dev->eps[0].ring->first_seg->trbs;
ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state);
/* Steps 7 and 8 were done in xhci_alloc_virt_device() */
xhci_flush_cache((uint32_t)ep0_ctx, sizeof(struct xhci_ep_ctx));
xhci_flush_cache((uint32_t)slot_ctx, sizeof(struct xhci_slot_ctx));
}

View file

@ -0,0 +1,939 @@
/*
* USB HOST XHCI Controller stack
*
* Based on xHCI host controller driver in linux-kernel
* by Sarah Sharp.
*
* Copyright (C) 2008 Intel Corp.
* Author: Sarah Sharp
*
* Copyright (C) 2013 Samsung Electronics Co.Ltd
* Authors: Vivek Gautam <gautam.vivek@samsung.com>
* Vikas Sajjan <vikas.sajjan@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/byteorder.h>
#include <usb.h>
#include <asm/unaligned.h>
#include <asm-generic/errno.h>
#include "xhci.h"
/**
* Is this TRB a link TRB or was the last TRB the last TRB in this event ring
* segment? I.e. would the updated event TRB pointer step off the end of the
* event seg ?
*
* @param ctrl Host controller data structure
* @param ring pointer to the ring
* @param seg poniter to the segment to which TRB belongs
* @param trb poniter to the ring trb
* @return 1 if this TRB a link TRB else 0
*/
static int last_trb(struct xhci_ctrl *ctrl, struct xhci_ring *ring,
struct xhci_segment *seg, union xhci_trb *trb)
{
if (ring == ctrl->event_ring)
return trb == &seg->trbs[TRBS_PER_SEGMENT];
else
return TRB_TYPE_LINK_LE32(trb->link.control);
}
/**
* Does this link TRB point to the first segment in a ring,
* or was the previous TRB the last TRB on the last segment in the ERST?
*
* @param ctrl Host controller data structure
* @param ring pointer to the ring
* @param seg poniter to the segment to which TRB belongs
* @param trb poniter to the ring trb
* @return 1 if this TRB is the last TRB on the last segment else 0
*/
static bool last_trb_on_last_seg(struct xhci_ctrl *ctrl,
struct xhci_ring *ring,
struct xhci_segment *seg,
union xhci_trb *trb)
{
if (ring == ctrl->event_ring)
return ((trb == &seg->trbs[TRBS_PER_SEGMENT]) &&
(seg->next == ring->first_seg));
else
return le32_to_cpu(trb->link.control) & LINK_TOGGLE;
}
/**
* See Cycle bit rules. SW is the consumer for the event ring only.
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
*
* If we've just enqueued a TRB that is in the middle of a TD (meaning the
* chain bit is set), then set the chain bit in all the following link TRBs.
* If we've enqueued the last TRB in a TD, make sure the following link TRBs
* have their chain bit cleared (so that each Link TRB is a separate TD).
*
* Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
* set, but other sections talk about dealing with the chain bit set. This was
* fixed in the 0.96 specification errata, but we have to assume that all 0.95
* xHCI hardware can't handle the chain bit being cleared on a link TRB.
*
* @param ctrl Host controller data structure
* @param ring pointer to the ring
* @param more_trbs_coming flag to indicate whether more trbs
* are expected or NOT.
* Will you enqueue more TRBs before calling
* prepare_ring()?
* @return none
*/
static void inc_enq(struct xhci_ctrl *ctrl, struct xhci_ring *ring,
bool more_trbs_coming)
{
u32 chain;
union xhci_trb *next;
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
next = ++(ring->enqueue);
/*
* Update the dequeue pointer further if that was a link TRB or we're at
* the end of an event ring segment (which doesn't have link TRBS)
*/
while (last_trb(ctrl, ring, ring->enq_seg, next)) {
if (ring != ctrl->event_ring) {
/*
* If the caller doesn't plan on enqueueing more
* TDs before ringing the doorbell, then we
* don't want to give the link TRB to the
* hardware just yet. We'll give the link TRB
* back in prepare_ring() just before we enqueue
* the TD at the top of the ring.
*/
if (!chain && !more_trbs_coming)
break;
/*
* If we're not dealing with 0.95 hardware or
* isoc rings on AMD 0.96 host,
* carry over the chain bit of the previous TRB
* (which may mean the chain bit is cleared).
*/
next->link.control &= cpu_to_le32(~TRB_CHAIN);
next->link.control |= cpu_to_le32(chain);
next->link.control ^= cpu_to_le32(TRB_CYCLE);
xhci_flush_cache((uint32_t)next,
sizeof(union xhci_trb));
}
/* Toggle the cycle bit after the last ring segment. */
if (last_trb_on_last_seg(ctrl, ring,
ring->enq_seg, next))
ring->cycle_state = (ring->cycle_state ? 0 : 1);
ring->enq_seg = ring->enq_seg->next;
ring->enqueue = ring->enq_seg->trbs;
next = ring->enqueue;
}
}
/**
* See Cycle bit rules. SW is the consumer for the event ring only.
* Don't make a ring full of link TRBs. That would be dumb and this would loop.
*
* @param ctrl Host controller data structure
* @param ring Ring whose Dequeue TRB pointer needs to be incremented.
* return none
*/
static void inc_deq(struct xhci_ctrl *ctrl, struct xhci_ring *ring)
{
do {
/*
* Update the dequeue pointer further if that was a link TRB or
* we're at the end of an event ring segment (which doesn't have
* link TRBS)
*/
if (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue)) {
if (ring == ctrl->event_ring &&
last_trb_on_last_seg(ctrl, ring,
ring->deq_seg, ring->dequeue)) {
ring->cycle_state = (ring->cycle_state ? 0 : 1);
}
ring->deq_seg = ring->deq_seg->next;
ring->dequeue = ring->deq_seg->trbs;
} else {
ring->dequeue++;
}
} while (last_trb(ctrl, ring, ring->deq_seg, ring->dequeue));
}
/**
* Generic function for queueing a TRB on a ring.
* The caller must have checked to make sure there's room on the ring.
*
* @param more_trbs_coming: Will you enqueue more TRBs before calling
* prepare_ring()?
* @param ctrl Host controller data structure
* @param ring pointer to the ring
* @param more_trbs_coming flag to indicate whether more trbs
* @param trb_fields pointer to trb field array containing TRB contents
* @return pointer to the enqueued trb
*/
static struct xhci_generic_trb *queue_trb(struct xhci_ctrl *ctrl,
struct xhci_ring *ring,
bool more_trbs_coming,
unsigned int *trb_fields)
{
struct xhci_generic_trb *trb;
int i;
trb = &ring->enqueue->generic;
for (i = 0; i < 4; i++)
trb->field[i] = cpu_to_le32(trb_fields[i]);
xhci_flush_cache((uint32_t)trb, sizeof(struct xhci_generic_trb));
inc_enq(ctrl, ring, more_trbs_coming);
return trb;
}
/**
* Does various checks on the endpoint ring, and makes it ready
* to queue num_trbs.
*
* @param ctrl Host controller data structure
* @param ep_ring pointer to the EP Transfer Ring
* @param ep_state State of the End Point
* @return error code in case of invalid ep_state, 0 on success
*/
static int prepare_ring(struct xhci_ctrl *ctrl, struct xhci_ring *ep_ring,
u32 ep_state)
{
union xhci_trb *next = ep_ring->enqueue;
/* Make sure the endpoint has been added to xHC schedule */
switch (ep_state) {
case EP_STATE_DISABLED:
/*
* USB core changed config/interfaces without notifying us,
* or hardware is reporting the wrong state.
*/
puts("WARN urb submitted to disabled ep\n");
return -ENOENT;
case EP_STATE_ERROR:
puts("WARN waiting for error on ep to be cleared\n");
return -EINVAL;
case EP_STATE_HALTED:
puts("WARN halted endpoint, queueing URB anyway.\n");
case EP_STATE_STOPPED:
case EP_STATE_RUNNING:
debug("EP STATE RUNNING.\n");
break;
default:
puts("ERROR unknown endpoint state for ep\n");
return -EINVAL;
}
while (last_trb(ctrl, ep_ring, ep_ring->enq_seg, next)) {
/*
* If we're not dealing with 0.95 hardware or isoc rings
* on AMD 0.96 host, clear the chain bit.
*/
next->link.control &= cpu_to_le32(~TRB_CHAIN);
next->link.control ^= cpu_to_le32(TRB_CYCLE);
xhci_flush_cache((uint32_t)next, sizeof(union xhci_trb));
/* Toggle the cycle bit after the last ring segment. */
if (last_trb_on_last_seg(ctrl, ep_ring,
ep_ring->enq_seg, next))
ep_ring->cycle_state = (ep_ring->cycle_state ? 0 : 1);
ep_ring->enq_seg = ep_ring->enq_seg->next;
ep_ring->enqueue = ep_ring->enq_seg->trbs;
next = ep_ring->enqueue;
}
return 0;
}
/**
* Generic function for queueing a command TRB on the command ring.
* Check to make sure there's room on the command ring for one command TRB.
*
* @param ctrl Host controller data structure
* @param ptr Pointer address to write in the first two fields (opt.)
* @param slot_id Slot ID to encode in the flags field (opt.)
* @param ep_index Endpoint index to encode in the flags field (opt.)
* @param cmd Command type to enqueue
* @return none
*/
void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr, u32 slot_id,
u32 ep_index, trb_type cmd)
{
u32 fields[4];
u64 val_64 = (uintptr_t)ptr;
BUG_ON(prepare_ring(ctrl, ctrl->cmd_ring, EP_STATE_RUNNING));
fields[0] = lower_32_bits(val_64);
fields[1] = upper_32_bits(val_64);
fields[2] = 0;
fields[3] = TRB_TYPE(cmd) | EP_ID_FOR_TRB(ep_index) |
SLOT_ID_FOR_TRB(slot_id) | ctrl->cmd_ring->cycle_state;
queue_trb(ctrl, ctrl->cmd_ring, false, fields);
/* Ring the command ring doorbell */
xhci_writel(&ctrl->dba->doorbell[0], DB_VALUE_HOST);
}
/**
* The TD size is the number of bytes remaining in the TD (including this TRB),
* right shifted by 10.
* It must fit in bits 21:17, so it can't be bigger than 31.
*
* @param remainder remaining packets to be sent
* @return remainder if remainder is less than max else max
*/
static u32 xhci_td_remainder(unsigned int remainder)
{
u32 max = (1 << (21 - 17 + 1)) - 1;
if ((remainder >> 10) >= max)
return max << 17;
else
return (remainder >> 10) << 17;
}
/**
* Finds out the remanining packets to be sent
*
* @param running_total total size sent so far
* @param trb_buff_len length of the TRB Buffer
* @param total_packet_count total packet count
* @param maxpacketsize max packet size of current pipe
* @param num_trbs_left number of TRBs left to be processed
* @return 0 if running_total or trb_buff_len is 0, else remainder
*/
static u32 xhci_v1_0_td_remainder(int running_total,
int trb_buff_len,
unsigned int total_packet_count,
int maxpacketsize,
unsigned int num_trbs_left)
{
int packets_transferred;
/* One TRB with a zero-length data packet. */
if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0))
return 0;
/*
* All the TRB queueing functions don't count the current TRB in
* running_total.
*/
packets_transferred = (running_total + trb_buff_len) / maxpacketsize;
if ((total_packet_count - packets_transferred) > 31)
return 31 << 17;
return (total_packet_count - packets_transferred) << 17;
}
/**
* Ring the doorbell of the End Point
*
* @param udev pointer to the USB device structure
* @param ep_index index of the endpoint
* @param start_cycle cycle flag of the first TRB
* @param start_trb pionter to the first TRB
* @return none
*/
static void giveback_first_trb(struct usb_device *udev, int ep_index,
int start_cycle,
struct xhci_generic_trb *start_trb)
{
struct xhci_ctrl *ctrl = udev->controller;
/*
* Pass all the TRBs to the hardware at once and make sure this write
* isn't reordered.
*/
if (start_cycle)
start_trb->field[3] |= cpu_to_le32(start_cycle);
else
start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE);
xhci_flush_cache((uint32_t)start_trb, sizeof(struct xhci_generic_trb));
/* Ringing EP doorbell here */
xhci_writel(&ctrl->dba->doorbell[udev->slot_id],
DB_VALUE(ep_index, 0));
return;
}
/**** POLLING mechanism for XHCI ****/
/**
* Finalizes a handled event TRB by advancing our dequeue pointer and giving
* the TRB back to the hardware for recycling. Must call this exactly once at
* the end of each event handler, and not touch the TRB again afterwards.
*
* @param ctrl Host controller data structure
* @return none
*/
void xhci_acknowledge_event(struct xhci_ctrl *ctrl)
{
/* Advance our dequeue pointer to the next event */
inc_deq(ctrl, ctrl->event_ring);
/* Inform the hardware */
xhci_writeq(&ctrl->ir_set->erst_dequeue,
(uintptr_t)ctrl->event_ring->dequeue | ERST_EHB);
}
/**
* Checks if there is a new event to handle on the event ring.
*
* @param ctrl Host controller data structure
* @return 0 if failure else 1 on success
*/
static int event_ready(struct xhci_ctrl *ctrl)
{
union xhci_trb *event;
xhci_inval_cache((uint32_t)ctrl->event_ring->dequeue,
sizeof(union xhci_trb));
event = ctrl->event_ring->dequeue;
/* Does the HC or OS own the TRB? */
if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) !=
ctrl->event_ring->cycle_state)
return 0;
return 1;
}
/**
* Waits for a specific type of event and returns it. Discards unexpected
* events. Caller *must* call xhci_acknowledge_event() after it is finished
* processing the event, and must not access the returned pointer afterwards.
*
* @param ctrl Host controller data structure
* @param expected TRB type expected from Event TRB
* @return pointer to event trb
*/
union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected)
{
trb_type type;
unsigned long ts = get_timer(0);
do {
union xhci_trb *event = ctrl->event_ring->dequeue;
if (!event_ready(ctrl))
continue;
type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags));
if (type == expected)
return event;
if (type == TRB_PORT_STATUS)
/* TODO: remove this once enumeration has been reworked */
/*
* Port status change events always have a
* successful completion code
*/
BUG_ON(GET_COMP_CODE(
le32_to_cpu(event->generic.field[2])) !=
COMP_SUCCESS);
else
printf("Unexpected XHCI event TRB, skipping... "
"(%08x %08x %08x %08x)\n",
le32_to_cpu(event->generic.field[0]),
le32_to_cpu(event->generic.field[1]),
le32_to_cpu(event->generic.field[2]),
le32_to_cpu(event->generic.field[3]));
xhci_acknowledge_event(ctrl);
} while (get_timer(ts) < XHCI_TIMEOUT);
if (expected == TRB_TRANSFER)
return NULL;
printf("XHCI timeout on event type %d... cannot recover.\n", expected);
BUG();
}
/*
* Stops transfer processing for an endpoint and throws away all unprocessed
* TRBs by setting the xHC's dequeue pointer to our enqueue pointer. The next
* xhci_bulk_tx/xhci_ctrl_tx on this enpoint will add new transfers there and
* ring the doorbell, causing this endpoint to start working again.
* (Careful: This will BUG() when there was no transfer in progress. Shouldn't
* happen in practice for current uses and is too complicated to fix right now.)
*/
static void abort_td(struct usb_device *udev, int ep_index)
{
struct xhci_ctrl *ctrl = udev->controller;
struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring;
union xhci_trb *event;
u32 field;
xhci_queue_command(ctrl, NULL, udev->slot_id, ep_index, TRB_STOP_RING);
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
field = le32_to_cpu(event->trans_event.flags);
BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id);
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
BUG_ON(GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len
!= COMP_STOP)));
xhci_acknowledge_event(ctrl);
event = xhci_wait_for_event(ctrl, TRB_COMPLETION);
BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
!= udev->slot_id || GET_COMP_CODE(le32_to_cpu(
event->event_cmd.status)) != COMP_SUCCESS);
xhci_acknowledge_event(ctrl);
xhci_queue_command(ctrl, (void *)((uintptr_t)ring->enqueue |
ring->cycle_state), udev->slot_id, ep_index, TRB_SET_DEQ);
event = xhci_wait_for_event(ctrl, TRB_COMPLETION);
BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags))
!= udev->slot_id || GET_COMP_CODE(le32_to_cpu(
event->event_cmd.status)) != COMP_SUCCESS);
xhci_acknowledge_event(ctrl);
}
static void record_transfer_result(struct usb_device *udev,
union xhci_trb *event, int length)
{
udev->act_len = min(length, length -
EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len)));
switch (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))) {
case COMP_SUCCESS:
BUG_ON(udev->act_len != length);
/* fallthrough */
case COMP_SHORT_TX:
udev->status = 0;
break;
case COMP_STALL:
udev->status = USB_ST_STALLED;
break;
case COMP_DB_ERR:
case COMP_TRB_ERR:
udev->status = USB_ST_BUF_ERR;
break;
case COMP_BABBLE:
udev->status = USB_ST_BABBLE_DET;
break;
default:
udev->status = 0x80; /* USB_ST_TOO_LAZY_TO_MAKE_A_NEW_MACRO */
}
}
/**** Bulk and Control transfer methods ****/
/**
* Queues up the BULK Request
*
* @param udev pointer to the USB device structure
* @param pipe contains the DIR_IN or OUT , devnum
* @param length length of the buffer
* @param buffer buffer to be read/written based on the request
* @return returns 0 if successful else -1 on failure
*/
int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
int length, void *buffer)
{
int num_trbs = 0;
struct xhci_generic_trb *start_trb;
bool first_trb = 0;
int start_cycle;
u32 field = 0;
u32 length_field = 0;
struct xhci_ctrl *ctrl = udev->controller;
int slot_id = udev->slot_id;
int ep_index;
struct xhci_virt_device *virt_dev;
struct xhci_ep_ctx *ep_ctx;
struct xhci_ring *ring; /* EP transfer ring */
union xhci_trb *event;
int running_total, trb_buff_len;
unsigned int total_packet_count;
int maxpacketsize;
u64 addr;
int ret;
u32 trb_fields[4];
u64 val_64 = (uintptr_t)buffer;
debug("dev=%p, pipe=%lx, buffer=%p, length=%d\n",
udev, pipe, buffer, length);
ep_index = usb_pipe_ep_index(pipe);
virt_dev = ctrl->devs[slot_id];
xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes,
virt_dev->out_ctx->size);
ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index);
ring = virt_dev->eps[ep_index].ring;
/*
* How much data is (potentially) left before the 64KB boundary?
* XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec)
* that the buffer should not span 64KB boundary. if so
* we send request in more than 1 TRB by chaining them.
*/
running_total = TRB_MAX_BUFF_SIZE -
(lower_32_bits(val_64) & (TRB_MAX_BUFF_SIZE - 1));
trb_buff_len = running_total;
running_total &= TRB_MAX_BUFF_SIZE - 1;
/*
* If there's some data on this 64KB chunk, or we have to send a
* zero-length transfer, we need at least one TRB
*/
if (running_total != 0 || length == 0)
num_trbs++;
/* How many more 64KB chunks to transfer, how many more TRBs? */
while (running_total < length) {
num_trbs++;
running_total += TRB_MAX_BUFF_SIZE;
}
/*
* XXX: Calling routine prepare_ring() called in place of
* prepare_trasfer() as there in 'Linux' since we are not
* maintaining multiple TDs/transfer at the same time.
*/
ret = prepare_ring(ctrl, ring,
le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK);
if (ret < 0)
return ret;
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
* state may change as we enqueue the other TRBs, so save it too.
*/
start_trb = &ring->enqueue->generic;
start_cycle = ring->cycle_state;
running_total = 0;
maxpacketsize = usb_maxpacket(udev, pipe);
total_packet_count = DIV_ROUND_UP(length, maxpacketsize);
/* How much data is in the first TRB? */
/*
* How much data is (potentially) left before the 64KB boundary?
* XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec)
* that the buffer should not span 64KB boundary. if so
* we send request in more than 1 TRB by chaining them.
*/
addr = val_64;
if (trb_buff_len > length)
trb_buff_len = length;
first_trb = true;
/* flush the buffer before use */
xhci_flush_cache((uint32_t)buffer, length);
/* Queue the first TRB, even if it's zero-length */
do {
u32 remainder = 0;
field = 0;
/* Don't change the cycle bit of the first TRB until later */
if (first_trb) {
first_trb = false;
if (start_cycle == 0)
field |= TRB_CYCLE;
} else {
field |= ring->cycle_state;
}
/*
* Chain all the TRBs together; clear the chain bit in the last
* TRB to indicate it's the last TRB in the chain.
*/
if (num_trbs > 1)
field |= TRB_CHAIN;
else
field |= TRB_IOC;
/* Only set interrupt on short packet for IN endpoints */
if (usb_pipein(pipe))
field |= TRB_ISP;
/* Set the TRB length, TD size, and interrupter fields. */
if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) < 0x100)
remainder = xhci_td_remainder(length - running_total);
else
remainder = xhci_v1_0_td_remainder(running_total,
trb_buff_len,
total_packet_count,
maxpacketsize,
num_trbs - 1);
length_field = ((trb_buff_len & TRB_LEN_MASK) |
remainder |
((0 & TRB_INTR_TARGET_MASK) <<
TRB_INTR_TARGET_SHIFT));
trb_fields[0] = lower_32_bits(addr);
trb_fields[1] = upper_32_bits(addr);
trb_fields[2] = length_field;
trb_fields[3] = field | (TRB_NORMAL << TRB_TYPE_SHIFT);
queue_trb(ctrl, ring, (num_trbs > 1), trb_fields);
--num_trbs;
running_total += trb_buff_len;
/* Calculate length for next transfer */
addr += trb_buff_len;
trb_buff_len = min((length - running_total), TRB_MAX_BUFF_SIZE);
} while (running_total < length);
giveback_first_trb(udev, ep_index, start_cycle, start_trb);
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
if (!event) {
debug("XHCI bulk transfer timed out, aborting...\n");
abort_td(udev, ep_index);
udev->status = USB_ST_NAK_REC; /* closest thing to a timeout */
udev->act_len = 0;
return -ETIMEDOUT;
}
field = le32_to_cpu(event->trans_event.flags);
BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
BUG_ON(*(void **)(uintptr_t)le64_to_cpu(event->trans_event.buffer) -
buffer > (size_t)length);
record_transfer_result(udev, event, length);
xhci_acknowledge_event(ctrl);
xhci_inval_cache((uint32_t)buffer, length);
return (udev->status != USB_ST_NOT_PROC) ? 0 : -1;
}
/**
* Queues up the Control Transfer Request
*
* @param udev pointer to the USB device structure
* @param pipe contains the DIR_IN or OUT , devnum
* @param req request type
* @param length length of the buffer
* @param buffer buffer to be read/written based on the request
* @return returns 0 if successful else error code on failure
*/
int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
struct devrequest *req, int length,
void *buffer)
{
int ret;
int start_cycle;
int num_trbs;
u32 field;
u32 length_field;
u64 buf_64 = 0;
struct xhci_generic_trb *start_trb;
struct xhci_ctrl *ctrl = udev->controller;
int slot_id = udev->slot_id;
int ep_index;
u32 trb_fields[4];
struct xhci_virt_device *virt_dev = ctrl->devs[slot_id];
struct xhci_ring *ep_ring;
union xhci_trb *event;
debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
req->request, req->request,
req->requesttype, req->requesttype,
le16_to_cpu(req->value), le16_to_cpu(req->value),
le16_to_cpu(req->index));
ep_index = usb_pipe_ep_index(pipe);
ep_ring = virt_dev->eps[ep_index].ring;
/*
* Check to see if the max packet size for the default control
* endpoint changed during FS device enumeration
*/
if (udev->speed == USB_SPEED_FULL) {
ret = xhci_check_maxpacket(udev);
if (ret < 0)
return ret;
}
xhci_inval_cache((uint32_t)virt_dev->out_ctx->bytes,
virt_dev->out_ctx->size);
struct xhci_ep_ctx *ep_ctx = NULL;
ep_ctx = xhci_get_ep_ctx(ctrl, virt_dev->out_ctx, ep_index);
/* 1 TRB for setup, 1 for status */
num_trbs = 2;
/*
* Don't need to check if we need additional event data and normal TRBs,
* since data in control transfers will never get bigger than 16MB
* XXX: can we get a buffer that crosses 64KB boundaries?
*/
if (length > 0)
num_trbs++;
/*
* XXX: Calling routine prepare_ring() called in place of
* prepare_trasfer() as there in 'Linux' since we are not
* maintaining multiple TDs/transfer at the same time.
*/
ret = prepare_ring(ctrl, ep_ring,
le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK);
if (ret < 0)
return ret;
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
* state may change as we enqueue the other TRBs, so save it too.
*/
start_trb = &ep_ring->enqueue->generic;
start_cycle = ep_ring->cycle_state;
debug("start_trb %p, start_cycle %d\n", start_trb, start_cycle);
/* Queue setup TRB - see section 6.4.1.2.1 */
/* FIXME better way to translate setup_packet into two u32 fields? */
field = 0;
field |= TRB_IDT | (TRB_SETUP << TRB_TYPE_SHIFT);
if (start_cycle == 0)
field |= 0x1;
/* xHCI 1.0 6.4.1.2.1: Transfer Type field */
if (HC_VERSION(xhci_readl(&ctrl->hccr->cr_capbase)) == 0x100) {
if (length > 0) {
if (req->requesttype & USB_DIR_IN)
field |= (TRB_DATA_IN << TRB_TX_TYPE_SHIFT);
else
field |= (TRB_DATA_OUT << TRB_TX_TYPE_SHIFT);
}
}
debug("req->requesttype = %d, req->request = %d,"
"le16_to_cpu(req->value) = %d,"
"le16_to_cpu(req->index) = %d,"
"le16_to_cpu(req->length) = %d\n",
req->requesttype, req->request, le16_to_cpu(req->value),
le16_to_cpu(req->index), le16_to_cpu(req->length));
trb_fields[0] = req->requesttype | req->request << 8 |
le16_to_cpu(req->value) << 16;
trb_fields[1] = le16_to_cpu(req->index) |
le16_to_cpu(req->length) << 16;
/* TRB_LEN | (TRB_INTR_TARGET) */
trb_fields[2] = (8 | ((0 & TRB_INTR_TARGET_MASK) <<
TRB_INTR_TARGET_SHIFT));
/* Immediate data in pointer */
trb_fields[3] = field;
queue_trb(ctrl, ep_ring, true, trb_fields);
/* Re-initializing field to zero */
field = 0;
/* If there's data, queue data TRBs */
/* Only set interrupt on short packet for IN endpoints */
if (usb_pipein(pipe))
field = TRB_ISP | (TRB_DATA << TRB_TYPE_SHIFT);
else
field = (TRB_DATA << TRB_TYPE_SHIFT);
length_field = (length & TRB_LEN_MASK) | xhci_td_remainder(length) |
((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT);
debug("length_field = %d, length = %d,"
"xhci_td_remainder(length) = %d , TRB_INTR_TARGET(0) = %d\n",
length_field, (length & TRB_LEN_MASK),
xhci_td_remainder(length), 0);
if (length > 0) {
if (req->requesttype & USB_DIR_IN)
field |= TRB_DIR_IN;
buf_64 = (uintptr_t)buffer;
trb_fields[0] = lower_32_bits(buf_64);
trb_fields[1] = upper_32_bits(buf_64);
trb_fields[2] = length_field;
trb_fields[3] = field | ep_ring->cycle_state;
xhci_flush_cache((uint32_t)buffer, length);
queue_trb(ctrl, ep_ring, true, trb_fields);
}
/*
* Queue status TRB -
* see Table 7 and sections 4.11.2.2 and 6.4.1.2.3
*/
/* If the device sent data, the status stage is an OUT transfer */
field = 0;
if (length > 0 && req->requesttype & USB_DIR_IN)
field = 0;
else
field = TRB_DIR_IN;
trb_fields[0] = 0;
trb_fields[1] = 0;
trb_fields[2] = ((0 & TRB_INTR_TARGET_MASK) << TRB_INTR_TARGET_SHIFT);
/* Event on completion */
trb_fields[3] = field | TRB_IOC |
(TRB_STATUS << TRB_TYPE_SHIFT) |
ep_ring->cycle_state;
queue_trb(ctrl, ep_ring, false, trb_fields);
giveback_first_trb(udev, ep_index, start_cycle, start_trb);
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
if (!event)
goto abort;
field = le32_to_cpu(event->trans_event.flags);
BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
record_transfer_result(udev, event, length);
xhci_acknowledge_event(ctrl);
/* Invalidate buffer to make it available to usb-core */
if (length > 0)
xhci_inval_cache((uint32_t)buffer, length);
if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len))
== COMP_SHORT_TX) {
/* Short data stage, clear up additional status stage event */
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
if (!event)
goto abort;
BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
xhci_acknowledge_event(ctrl);
}
return (udev->status != USB_ST_NOT_PROC) ? 0 : -1;
abort:
debug("XHCI control transfer timed out, aborting...\n");
abort_td(udev, ep_index);
udev->status = USB_ST_NAK_REC;
udev->act_len = 0;
return -ETIMEDOUT;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# 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., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB := $(obj)libusb_musb.o
COBJS-$(CONFIG_MUSB_HCD) += musb_hcd.o musb_core.o
COBJS-$(CONFIG_MUSB_UDC) += musb_udc.o musb_core.o
COBJS-$(CONFIG_USB_BLACKFIN) += blackfin_usb.o
COBJS-$(CONFIG_USB_DAVINCI) += davinci.o
COBJS-$(CONFIG_USB_OMAP3) += omap3.o
COBJS-$(CONFIG_USB_DA8XX) += da8xx.o
COBJS-$(CONFIG_USB_AM35X) += am35x.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
#########################################################################

View file

@ -0,0 +1,150 @@
/*
* am35x.c - TI's AM35x platform specific usb wrapper functions.
*
* Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
*
* Based on drivers/usb/musb/da8xx.c
*
* Copyright (c) 2010 Texas Instruments Incorporated
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common.h>
#include "am35x.h"
/* MUSB platform configuration */
struct musb_config musb_cfg = {
.regs = (struct musb_regs *)AM35X_USB_OTG_CORE_BASE,
.timeout = AM35X_USB_OTG_TIMEOUT,
.musb_speed = 0,
};
/*
* Enable the USB phy
*/
static u8 phy_on(void)
{
u32 devconf2;
u32 timeout;
devconf2 = readl(&am35x_scm_general_regs->devconf2);
devconf2 &= ~(DEVCONF2_RESET | DEVCONF2_PHYPWRDN | DEVCONF2_OTGPWRDN |
DEVCONF2_OTGMODE | DEVCONF2_REFFREQ |
DEVCONF2_PHY_GPIOMODE);
devconf2 |= DEVCONF2_SESENDEN | DEVCONF2_VBDTCTEN | DEVCONF2_PHY_PLLON |
DEVCONF2_REFFREQ_13MHZ | DEVCONF2_DATPOL;
writel(devconf2, &am35x_scm_general_regs->devconf2);
/* wait until the USB phy is turned on */
timeout = musb_cfg.timeout;
while (timeout--)
if (readl(&am35x_scm_general_regs->devconf2) & DEVCONF2_PHYCKGD)
return 1;
/* USB phy was not turned on */
return 0;
}
/*
* Disable the USB phy
*/
static void phy_off(void)
{
u32 devconf2;
/*
* Power down the on-chip PHY.
*/
devconf2 = readl(&am35x_scm_general_regs->devconf2);
devconf2 &= ~DEVCONF2_PHY_PLLON;
devconf2 |= DEVCONF2_PHYPWRDN | DEVCONF2_OTGPWRDN;
writel(devconf2, &am35x_scm_general_regs->devconf2);
}
/*
* This function performs platform specific initialization for usb0.
*/
int musb_platform_init(void)
{
u32 revision;
u32 sw_reset;
/* global usb reset */
sw_reset = readl(&am35x_scm_general_regs->ip_sw_reset);
sw_reset |= (1 << 0);
writel(sw_reset, &am35x_scm_general_regs->ip_sw_reset);
sw_reset &= ~(1 << 0);
writel(sw_reset, &am35x_scm_general_regs->ip_sw_reset);
/* reset the controller */
writel(0x1, &am35x_usb_regs->control);
udelay(5000);
/* start the on-chip usb phy and its pll */
if (phy_on() == 0)
return -1;
/* Returns zero if e.g. not clocked */
revision = readl(&am35x_usb_regs->revision);
if (revision == 0)
return -1;
return 0;
}
/*
* This function performs platform specific deinitialization for usb0.
*/
void musb_platform_deinit(void)
{
/* Turn off the phy */
phy_off();
}
/*
* This function reads data from endpoint fifo for AM35x
* which supports only 32bit read operation.
*
* ep - endpoint number
* length - number of bytes to read from FIFO
* fifo_data - pointer to data buffer into which data is read
*/
__attribute__((weak))
void read_fifo(u8 ep, u32 length, void *fifo_data)
{
u8 *data = (u8 *)fifo_data;
u32 val;
int i;
/* select the endpoint index */
writeb(ep, &musbr->index);
if (length > 4) {
for (i = 0; i < (length >> 2); i++) {
val = readl(&musbr->fifox[ep]);
memcpy(data, &val, 4);
data += 4;
}
length %= 4;
}
if (length > 0) {
val = readl(&musbr->fifox[ep]);
memcpy(data, &val, length);
}
}

View file

@ -0,0 +1,94 @@
/*
* am35x.h - TI's AM35x platform specific usb wrapper definitions.
*
* Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
*
* Based on drivers/usb/musb/da8xx.h
*
* Copyright (c) 2010 Texas Instruments Incorporated
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __AM35X_USB_H__
#define __AM35X_USB_H__
#include <asm/arch/am35x_def.h>
#include "musb_core.h"
/* Base address of musb wrapper */
#define AM35X_USB_OTG_BASE 0x5C040000
/* Base address of musb core */
#define AM35X_USB_OTG_CORE_BASE (AM35X_USB_OTG_BASE + 0x400)
/* Timeout for AM35x usb module */
#define AM35X_USB_OTG_TIMEOUT 0x3FFFFFF
/*
* AM35x platform USB wrapper register overlay.
*/
struct am35x_usb_regs {
u32 revision;
u32 control;
u32 status;
u32 emulation;
u32 reserved0[1];
u32 autoreq;
u32 srpfixtime;
u32 ep_intsrc;
u32 ep_intsrcset;
u32 ep_intsrcclr;
u32 ep_intmsk;
u32 ep_intmskset;
u32 ep_intmskclr;
u32 ep_intsrcmsked;
u32 reserved1[1];
u32 core_intsrc;
u32 core_intsrcset;
u32 core_intsrcclr;
u32 core_intmsk;
u32 core_intmskset;
u32 core_intmskclr;
u32 core_intsrcmsked;
u32 reserved2[1];
u32 eoi;
u32 mop_sop_en;
u32 reserved3[2];
u32 txmode;
u32 rxmode;
u32 epcount_mode;
};
#define am35x_usb_regs ((struct am35x_usb_regs *)AM35X_USB_OTG_BASE)
/* USB 2.0 PHY Control */
#define DEVCONF2_PHY_GPIOMODE (1 << 23)
#define DEVCONF2_OTGMODE (3 << 14)
#define DEVCONF2_SESENDEN (1 << 13) /* Vsess_end comparator */
#define DEVCONF2_VBDTCTEN (1 << 12) /* Vbus comparator */
#define DEVCONF2_REFFREQ_24MHZ (2 << 8)
#define DEVCONF2_REFFREQ_26MHZ (7 << 8)
#define DEVCONF2_REFFREQ_13MHZ (6 << 8)
#define DEVCONF2_REFFREQ (0xf << 8)
#define DEVCONF2_PHYCKGD (1 << 7)
#define DEVCONF2_VBUSSENSE (1 << 6)
#define DEVCONF2_PHY_PLLON (1 << 5) /* override PLL suspend */
#define DEVCONF2_RESET (1 << 4)
#define DEVCONF2_PHYPWRDN (1 << 3)
#define DEVCONF2_OTGPWRDN (1 << 2)
#define DEVCONF2_DATPOL (1 << 1)
#endif /* __AM35X_USB_H__ */

View file

@ -0,0 +1,171 @@
/*
* Blackfin MUSB HCD (Host Controller Driver) for u-boot
*
* Copyright (c) 2008-2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <usb.h>
#include <asm/blackfin.h>
#include <asm/mach-common/bits/usb.h>
#include "musb_core.h"
#ifndef CONFIG_USB_BLACKFIN_CLKIN
#define CONFIG_USB_BLACKFIN_CLKIN 24
#endif
/* MUSB platform configuration */
struct musb_config musb_cfg = {
.regs = (struct musb_regs *)USB_FADDR,
.timeout = 0x3FFFFFF,
.musb_speed = 0,
};
/*
* This function read or write data to endpoint fifo
* Blackfin use DMA polling method to avoid buffer alignment issues
*
* ep - Endpoint number
* length - Number of bytes to write to FIFO
* fifo_data - Pointer to data buffer to be read/write
* is_write - Flag for read or write
*/
void rw_fifo(u8 ep, u32 length, void *fifo_data, int is_write)
{
struct bfin_musb_dma_regs *regs;
u32 val = (u32)fifo_data;
blackfin_dcache_flush_invalidate_range(fifo_data, fifo_data + length);
regs = (void *)USB_DMA_INTERRUPT;
regs += ep;
/* Setup DMA address register */
bfin_write16(&regs->addr_low, val);
SSYNC();
bfin_write16(&regs->addr_high, val >> 16);
SSYNC();
/* Setup DMA count register */
bfin_write16(&regs->count_low, length);
bfin_write16(&regs->count_high, 0);
SSYNC();
/* Enable the DMA */
val = (ep << 4) | DMA_ENA | INT_ENA;
if (is_write)
val |= DIRECTION;
bfin_write16(&regs->control, val);
SSYNC();
/* Wait for compelete */
while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << ep)))
continue;
/* acknowledge dma interrupt */
bfin_write_USB_DMA_INTERRUPT(1 << ep);
SSYNC();
/* Reset DMA */
bfin_write16(&regs->control, 0);
SSYNC();
}
void write_fifo(u8 ep, u32 length, void *fifo_data)
{
rw_fifo(ep, length, fifo_data, 1);
}
void read_fifo(u8 ep, u32 length, void *fifo_data)
{
rw_fifo(ep, length, fifo_data, 0);
}
/*
* CPU and board-specific MUSB initializations. Aliased function
* signals caller to move on.
*/
static void __def_musb_init(void)
{
}
void board_musb_init(void) __attribute__((weak, alias("__def_musb_init")));
static void bfin_anomaly_init(void)
{
u32 revid;
if (!ANOMALY_05000346 && !ANOMALY_05000347)
return;
revid = bfin_revid();
#ifdef __ADSPBF54x__
if (revid > 0)
return;
#endif
#ifdef __ADSPBF52x__
if (ANOMALY_BF526 && revid > 0)
return;
if (ANOMALY_BF527 && revid > 1)
return;
#endif
if (ANOMALY_05000346) {
bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value);
SSYNC();
}
if (ANOMALY_05000347) {
bfin_write_USB_APHY_CNTRL(0x0);
SSYNC();
}
}
int musb_platform_init(void)
{
/* board specific initialization */
board_musb_init();
bfin_anomaly_init();
/* Configure PLL oscillator register */
bfin_write_USB_PLLOSC_CTRL(0x3080 |
((480 / CONFIG_USB_BLACKFIN_CLKIN) << 1));
SSYNC();
bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1);
SSYNC();
bfin_write_USB_EP_NI0_RXMAXP(64);
SSYNC();
bfin_write_USB_EP_NI0_TXMAXP(64);
SSYNC();
/* Route INTRUSB/INTR_RX/INTR_TX to USB_INT0*/
bfin_write_USB_GLOBINTR(0x7);
SSYNC();
bfin_write_USB_GLOBAL_CTL(GLOBAL_ENA | EP1_TX_ENA | EP2_TX_ENA |
EP3_TX_ENA | EP4_TX_ENA | EP5_TX_ENA |
EP6_TX_ENA | EP7_TX_ENA | EP1_RX_ENA |
EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA |
EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA);
SSYNC();
return 0;
}
/*
* This function performs Blackfin platform specific deinitialization for usb.
*/
void musb_platform_deinit(void)
{
}

View file

@ -0,0 +1,99 @@
/*
* Blackfin MUSB HCD (Host Controller Driver) for u-boot
*
* Copyright (c) 2008-2009 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef __BLACKFIN_USB_H__
#define __BLACKFIN_USB_H__
#include <linux/types.h>
/* Every register is 32bit aligned, but only 16bits in size */
#define ureg(name) u16 name; u16 __pad_##name;
#define musb_regs musb_regs
struct musb_regs {
/* common registers */
ureg(faddr)
ureg(power)
ureg(intrtx)
ureg(intrrx)
ureg(intrtxe)
ureg(intrrxe)
ureg(intrusb)
ureg(intrusbe)
ureg(frame)
ureg(index)
ureg(testmode)
ureg(globintr)
ureg(global_ctl)
u32 reserved0[3];
/* indexed registers */
ureg(txmaxp)
ureg(txcsr)
ureg(rxmaxp)
ureg(rxcsr)
ureg(rxcount)
ureg(txtype)
ureg(txinterval)
ureg(rxtype)
ureg(rxinterval)
u32 reserved1;
ureg(txcount)
u32 reserved2[5];
/* fifo */
u16 fifox[32];
/* OTG, dynamic FIFO, version & vendor registers */
u32 reserved3[16];
ureg(devctl)
ureg(vbus_irq)
ureg(vbus_mask)
u32 reserved4[15];
ureg(linkinfo)
ureg(vplen)
ureg(hseof1)
ureg(fseof1)
ureg(lseof1)
u32 reserved5[41];
/* target address registers */
struct musb_tar_regs {
ureg(txmaxp)
ureg(txcsr)
ureg(rxmaxp)
ureg(rxcsr)
ureg(rxcount)
ureg(txtype)
ureg(txinternal)
ureg(rxtype)
ureg(rxinternal)
u32 reserved6;
ureg(txcount)
u32 reserved7[5];
} tar[8];
} __attribute__((packed));
struct bfin_musb_dma_regs {
ureg(interrupt);
ureg(control);
ureg(addr_low);
ureg(addr_high);
ureg(count_low);
ureg(count_high);
u32 reserved0[2];
};
#undef ureg
/* EP5-EP7 are the only ones with 1024 byte FIFOs which BULK really needs */
#define MUSB_BULK_EP 5
/* Blackfin FIFO's are static */
#define MUSB_NO_DYNAMIC_FIFO
/* No HUB support :( */
#define MUSB_NO_MULTIPOINT
#endif

View file

@ -0,0 +1,139 @@
/*
* da8xx.c - TI's DA8xx platform specific usb wrapper functions.
*
* Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
*
* Based on drivers/usb/musb/davinci.c
*
* Copyright (C) 2009 Texas Instruments Incorporated
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common.h>
#include "da8xx.h"
/* MUSB platform configuration */
struct musb_config musb_cfg = {
.regs = (struct musb_regs *)DA8XX_USB_OTG_CORE_BASE,
.timeout = DA8XX_USB_OTG_TIMEOUT,
.musb_speed = 0,
};
/*
* This function enables VBUS by driving the GPIO Bank4 Pin 15 high.
*/
static void enable_vbus(void)
{
u32 value;
/* configure GPIO bank4 pin 15 in output direction */
value = readl(&davinci_gpio_bank45->dir);
writel((value & (~DA8XX_USB_VBUS_GPIO)), &davinci_gpio_bank45->dir);
/* set GPIO bank4 pin 15 high to drive VBUS */
value = readl(&davinci_gpio_bank45->set_data);
writel((value | DA8XX_USB_VBUS_GPIO), &davinci_gpio_bank45->set_data);
}
/*
* Enable the usb0 phy. This initialization procedure is explained in
* the DA8xx USB user guide document.
*/
static u8 phy_on(void)
{
u32 timeout;
u32 cfgchip2;
cfgchip2 = readl(&davinci_syscfg_regs->cfgchip2);
cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN |
CFGCHIP2_OTGMODE | CFGCHIP2_REFFREQ);
cfgchip2 |= CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN | CFGCHIP2_PHY_PLLON |
CFGCHIP2_REFFREQ_24MHZ;
writel(cfgchip2, &davinci_syscfg_regs->cfgchip2);
/* wait until the usb phy pll locks */
timeout = musb_cfg.timeout;
while (timeout--)
if (readl(&davinci_syscfg_regs->cfgchip2) & CFGCHIP2_PHYCLKGD)
return 1;
/* USB phy was not turned on */
return 0;
}
/*
* Disable the usb phy
*/
static void phy_off(void)
{
u32 cfgchip2;
/*
* Power down the on-chip PHY.
*/
cfgchip2 = readl(&davinci_syscfg_regs->cfgchip2);
cfgchip2 &= ~CFGCHIP2_PHY_PLLON;
cfgchip2 |= CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN;
writel(cfgchip2, &davinci_syscfg_regs->cfgchip2);
}
/*
* This function performs DA8xx platform specific initialization for usb0.
*/
int musb_platform_init(void)
{
u32 revision;
/* enable psc for usb2.0 */
lpsc_on(33);
/* enable usb vbus */
enable_vbus();
/* reset the controller */
writel(0x1, &da8xx_usb_regs->control);
udelay(5000);
/* start the on-chip usb phy and its pll */
if (phy_on() == 0)
return -1;
/* Returns zero if e.g. not clocked */
revision = readl(&da8xx_usb_regs->revision);
if (revision == 0)
return -1;
/* Disable all interrupts */
writel((DA8XX_USB_USBINT_MASK | DA8XX_USB_TXINT_MASK |
DA8XX_USB_RXINT_MASK), &da8xx_usb_regs->intmsk_set);
return 0;
}
/*
* This function performs DA8xx platform specific deinitialization for usb0.
*/
void musb_platform_deinit(void)
{
/* Turn of the phy */
phy_off();
/* flush any interrupts */
writel((DA8XX_USB_USBINT_MASK | DA8XX_USB_TXINT_MASK |
DA8XX_USB_RXINT_MASK), &da8xx_usb_regs->intmsk_clr);
writel(0, &da8xx_usb_regs->eoi);
}

View file

@ -0,0 +1,102 @@
/*
* da8xx.h -- TI's DA8xx platform specific usb wrapper definitions.
*
* Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
*
* Based on drivers/usb/musb/davinci.h
*
* Copyright (C) 2009 Texas Instruments Incorporated
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __DA8XX_MUSB_H__
#define __DA8XX_MUSB_H__
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#include "musb_core.h"
/* Base address of da8xx usb0 wrapper */
#define DA8XX_USB_OTG_BASE 0x01E00000
/* Base address of da8xx musb core */
#define DA8XX_USB_OTG_CORE_BASE (DA8XX_USB_OTG_BASE + 0x400)
/* Timeout for DA8xx usb module */
#define DA8XX_USB_OTG_TIMEOUT 0x3FFFFFF
/*
* DA8xx platform USB wrapper register overlay.
*/
struct da8xx_usb_regs {
dv_reg revision;
dv_reg control;
dv_reg status;
dv_reg emulation;
dv_reg mode;
dv_reg autoreq;
dv_reg srpfixtime;
dv_reg teardown;
dv_reg intsrc;
dv_reg intsrc_set;
dv_reg intsrc_clr;
dv_reg intmsk;
dv_reg intmsk_set;
dv_reg intmsk_clr;
dv_reg intsrcmsk;
dv_reg eoi;
dv_reg intvector;
dv_reg grndis_size[4];
};
#define da8xx_usb_regs ((struct da8xx_usb_regs *)DA8XX_USB_OTG_BASE)
/* DA8XX interrupt bits definitions */
#define DA8XX_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */
#define DA8XX_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */
#define DA8XX_USB_TXINT_SHIFT 0
#define DA8XX_USB_RXINT_SHIFT 8
#define DA8XX_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */
#define DA8XX_USB_TXINT_MASK \
(DA8XX_USB_TX_ENDPTS_MASK << DA8XX_USB_TXINT_SHIFT)
#define DA8XX_USB_RXINT_MASK \
(DA8XX_USB_RX_ENDPTS_MASK << DA8XX_USB_RXINT_SHIFT)
/* DA8xx CFGCHIP2 (USB 2.0 PHY Control) register bits */
#define CFGCHIP2_PHYCLKGD (1 << 17)
#define CFGCHIP2_VBUSSENSE (1 << 16)
#define CFGCHIP2_RESET (1 << 15)
#define CFGCHIP2_OTGMODE (3 << 13)
#define CFGCHIP2_NO_OVERRIDE (0 << 13)
#define CFGCHIP2_FORCE_HOST (1 << 13)
#define CFGCHIP2_FORCE_DEVICE (2 << 13)
#define CFGCHIP2_FORCE_HOST_VBUS_LOW (3 << 13)
#define CFGCHIP2_USB1PHYCLKMUX (1 << 12)
#define CFGCHIP2_USB2PHYCLKMUX (1 << 11)
#define CFGCHIP2_PHYPWRDN (1 << 10)
#define CFGCHIP2_OTGPWRDN (1 << 9)
#define CFGCHIP2_DATPOL (1 << 8)
#define CFGCHIP2_USB1SUSPENDM (1 << 7)
#define CFGCHIP2_PHY_PLLON (1 << 6) /* override PLL suspend */
#define CFGCHIP2_SESENDEN (1 << 5) /* Vsess_end comparator */
#define CFGCHIP2_VBDTCTEN (1 << 4) /* Vbus comparator */
#define CFGCHIP2_REFFREQ (0xf << 0)
#define CFGCHIP2_REFFREQ_12MHZ (1 << 0)
#define CFGCHIP2_REFFREQ_24MHZ (2 << 0)
#define CFGCHIP2_REFFREQ_48MHZ (3 << 0)
#define DA8XX_USB_VBUS_GPIO (1 << 15)
#endif /* __DA8XX_MUSB_H__ */

View file

@ -0,0 +1,137 @@
/*
* TI's Davinci platform specific USB wrapper functions.
*
* Copyright (c) 2008 Texas Instruments
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
*/
#include <common.h>
#include <asm/io.h>
#include "davinci.h"
#include <asm/arch/hardware.h>
#if !defined(CONFIG_DV_USBPHY_CTL)
#define CONFIG_DV_USBPHY_CTL (USBPHY_SESNDEN | USBPHY_VBDTCTEN)
#endif
/* MUSB platform configuration */
struct musb_config musb_cfg = {
.regs = (struct musb_regs *)MENTOR_USB0_BASE,
.timeout = DAVINCI_USB_TIMEOUT,
.musb_speed = 0,
};
/* MUSB module register overlay */
struct davinci_usb_regs *dregs;
/*
* Enable the USB phy
*/
static u8 phy_on(void)
{
u32 timeout;
#ifdef DAVINCI_DM365EVM
u32 val;
#endif
/* Wait until the USB phy is turned on */
#ifdef DAVINCI_DM365EVM
writel(USBPHY_PHY24MHZ | USBPHY_SESNDEN |
USBPHY_VBDTCTEN, USBPHY_CTL_PADDR);
#else
writel(CONFIG_DV_USBPHY_CTL, USBPHY_CTL_PADDR);
#endif
timeout = musb_cfg.timeout;
#ifdef DAVINCI_DM365EVM
/* Set the ownership of GIO33 to USB */
val = readl(PINMUX4);
val &= ~(PINMUX4_USBDRVBUS_BITCLEAR);
val |= PINMUX4_USBDRVBUS_BITSET;
writel(val, PINMUX4);
#endif
while (timeout--)
if (readl(USBPHY_CTL_PADDR) & USBPHY_PHYCLKGD)
return 1;
/* USB phy was not turned on */
return 0;
}
/*
* Disable the USB phy
*/
static void phy_off(void)
{
/* powerdown the on-chip PHY and its oscillator */
writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, USBPHY_CTL_PADDR);
}
void __enable_vbus(void)
{
/*
* nothing to do, vbus is handled through the cpu.
* Define this function in board code, if it is
* different on your board.
*/
}
void enable_vbus(void)
__attribute__((weak, alias("__enable_vbus")));
/*
* This function performs Davinci platform specific initialization for usb0.
*/
int musb_platform_init(void)
{
u32 revision;
/* enable USB VBUS */
enable_vbus();
/* start the on-chip USB phy and its pll */
if (!phy_on())
return -1;
/* reset the controller */
dregs = (struct davinci_usb_regs *)DAVINCI_USB0_BASE;
writel(1, &dregs->ctrlr);
udelay(5000);
/* Returns zero if e.g. not clocked */
revision = readl(&dregs->version);
if (!revision)
return -1;
/* Disable all interrupts */
writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_RXINT_MASK |
DAVINCI_USB_TXINT_MASK , &dregs->intmsksetr);
return 0;
}
/*
* This function performs Davinci platform specific deinitialization for usb0.
*/
void musb_platform_deinit(void)
{
/* Turn of the phy */
phy_off();
/* flush any interrupts */
writel(DAVINCI_USB_USBINT_MASK | DAVINCI_USB_TXINT_MASK |
DAVINCI_USB_RXINT_MASK , &dregs->intclrr);
}

View file

@ -0,0 +1,87 @@
/*
* TI's Davinci platform specific USB wrapper functions.
*
* Copyright (c) 2008 Texas Instruments
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
*/
#ifndef __DAVINCI_USB_H__
#define __DAVINCI_USB_H__
#include <asm/arch/hardware.h>
#include "musb_core.h"
/* Base address of DAVINCI usb0 wrapper */
#define DAVINCI_USB0_BASE 0x01C64000
/* Base address of DAVINCI musb core */
#define MENTOR_USB0_BASE (DAVINCI_USB0_BASE+0x400)
/*
* Davinci platform USB wrapper register overlay. Note: Only the required
* registers are included in this structure. It can be expanded as required.
*/
struct davinci_usb_regs {
u32 version;
u32 ctrlr;
u32 reserved[0x20];
u32 intclrr;
u32 intmskr;
u32 intmsksetr;
};
#define DAVINCI_USB_TX_ENDPTS_MASK 0x1f /* ep0 + 4 tx */
#define DAVINCI_USB_RX_ENDPTS_MASK 0x1e /* 4 rx */
#define DAVINCI_USB_USBINT_SHIFT 16
#define DAVINCI_USB_TXINT_SHIFT 0
#define DAVINCI_USB_RXINT_SHIFT 8
#define DAVINCI_INTR_DRVVBUS 0x0100
#define DAVINCI_USB_USBINT_MASK 0x01ff0000 /* 8 Mentor, DRVVBUS */
#define DAVINCI_USB_TXINT_MASK \
(DAVINCI_USB_TX_ENDPTS_MASK << DAVINCI_USB_TXINT_SHIFT)
#define DAVINCI_USB_RXINT_MASK \
(DAVINCI_USB_RX_ENDPTS_MASK << DAVINCI_USB_RXINT_SHIFT)
#define MGC_BUSCTL_OFFSET(_bEnd, _bOffset) \
(0x80 + (8*(_bEnd)) + (_bOffset))
/* Integrated highspeed/otg PHY */
#define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34)
#define USBPHY_PHY24MHZ (1 << 13)
#define USBPHY_PHYCLKGD (1 << 8)
#define USBPHY_SESNDEN (1 << 7) /* v(sess_end) comparator */
#define USBPHY_VBDTCTEN (1 << 6) /* v(bus) comparator */
#define USBPHY_PHYPLLON (1 << 4) /* override pll suspend */
#define USBPHY_CLKO1SEL (1 << 3)
#define USBPHY_OSCPDWN (1 << 2)
#define USBPHY_PHYPDWN (1 << 0)
/* Timeout for Davinci USB module */
#define DAVINCI_USB_TIMEOUT 0x3FFFFFF
/* IO Expander I2C address and VBUS enable mask */
#define IOEXP_I2C_ADDR 0x3A
#define IOEXP_VBUSEN_MASK 1
/* extern functions */
extern void lpsc_on(unsigned int id);
extern int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len);
extern int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len);
extern void enable_vbus(void);
#endif /* __DAVINCI_USB_H__ */

View file

@ -0,0 +1,168 @@
/*
* Mentor USB OTG Core functionality common for both Host and Device
* functionality.
*
* Copyright (c) 2008 Texas Instruments
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
*/
#include <common.h>
#include "musb_core.h"
struct musb_regs *musbr;
/*
* program the mentor core to start (enable interrupts, dma, etc.)
*/
void musb_start(void)
{
#if defined(CONFIG_MUSB_HCD)
u8 devctl;
u8 busctl;
#endif
/* disable all interrupts */
writew(0, &musbr->intrtxe);
writew(0, &musbr->intrrxe);
writeb(0, &musbr->intrusbe);
writeb(0, &musbr->testmode);
/* put into basic highspeed mode and start session */
writeb(MUSB_POWER_HSENAB, &musbr->power);
#if defined(CONFIG_MUSB_HCD)
/* Program PHY to use EXT VBUS if required */
if (musb_cfg.extvbus == 1) {
busctl = musb_read_ulpi_buscontrol(musbr);
musb_write_ulpi_buscontrol(musbr, busctl | ULPI_USE_EXTVBUS);
}
devctl = readb(&musbr->devctl);
writeb(devctl | MUSB_DEVCTL_SESSION, &musbr->devctl);
#endif
}
#ifdef MUSB_NO_DYNAMIC_FIFO
# define config_fifo(dir, idx, addr)
#else
# define config_fifo(dir, idx, addr) \
do { \
writeb(idx, &musbr->dir##fifosz); \
writew(fifoaddr >> 3, &musbr->dir##fifoadd); \
} while (0)
#endif
/*
* This function configures the endpoint configuration. The musb hcd or musb
* device implementation can use this function to configure the endpoints
* and set the FIFO sizes. Note: The summation of FIFO sizes of all endpoints
* should not be more than the available FIFO size.
*
* epinfo - Pointer to EP configuration table
* cnt - Number of entries in the EP conf table.
*/
void musb_configure_ep(const struct musb_epinfo *epinfo, u8 cnt)
{
u16 csr;
u16 fifoaddr = 64; /* First 64 bytes of FIFO reserved for EP0 */
u32 fifosize;
u8 idx;
while (cnt--) {
/* prepare fifosize to write to register */
fifosize = epinfo->epsize >> 3;
idx = ffs(fifosize) - 1;
writeb(epinfo->epnum, &musbr->index);
if (epinfo->epdir) {
/* Configure fifo size and fifo base address */
config_fifo(tx, idx, fifoaddr);
csr = readw(&musbr->txcsr);
#if defined(CONFIG_MUSB_HCD)
/* clear the data toggle bit */
writew(csr | MUSB_TXCSR_CLRDATATOG, &musbr->txcsr);
#endif
/* Flush fifo if required */
if (csr & MUSB_TXCSR_TXPKTRDY)
writew(csr | MUSB_TXCSR_FLUSHFIFO,
&musbr->txcsr);
} else {
/* Configure fifo size and fifo base address */
config_fifo(rx, idx, fifoaddr);
csr = readw(&musbr->rxcsr);
#if defined(CONFIG_MUSB_HCD)
/* clear the data toggle bit */
writew(csr | MUSB_RXCSR_CLRDATATOG, &musbr->rxcsr);
#endif
/* Flush fifo if required */
if (csr & MUSB_RXCSR_RXPKTRDY)
writew(csr | MUSB_RXCSR_FLUSHFIFO,
&musbr->rxcsr);
}
fifoaddr += epinfo->epsize;
epinfo++;
}
}
/*
* This function writes data to endpoint fifo
*
* ep - endpoint number
* length - number of bytes to write to FIFO
* fifo_data - Pointer to data buffer that contains the data to write
*/
__attribute__((weak))
void write_fifo(u8 ep, u32 length, void *fifo_data)
{
u8 *data = (u8 *)fifo_data;
/* select the endpoint index */
writeb(ep, &musbr->index);
/* write the data to the fifo */
while (length--)
writeb(*data++, &musbr->fifox[ep]);
}
/*
* AM35x supports only 32bit read operations so
* use seperate read_fifo() function for it.
*/
#ifndef CONFIG_USB_AM35X
/*
* This function reads data from endpoint fifo
*
* ep - endpoint number
* length - number of bytes to read from FIFO
* fifo_data - pointer to data buffer into which data is read
*/
__attribute__((weak))
void read_fifo(u8 ep, u32 length, void *fifo_data)
{
u8 *data = (u8 *)fifo_data;
/* select the endpoint index */
writeb(ep, &musbr->index);
/* read the data to the fifo */
while (length--)
*data++ = readb(&musbr->fifox[ep]);
}
#endif /* CONFIG_USB_AM35X */

View file

@ -0,0 +1,395 @@
/******************************************************************
* Copyright 2008 Mentor Graphics Corporation
* Copyright (C) 2008 by Texas Instruments
*
* This file is part of the Inventra Controller Driver for Linux.
*
* The Inventra Controller Driver for Linux is free software; you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License version 2 as published by the Free Software
* Foundation.
*
* The Inventra Controller Driver for Linux 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 The Inventra Controller Driver for Linux ; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place,
* Suite 330, Boston, MA 02111-1307 USA
*
* ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION
* OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE
* OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS
* MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER.
* MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT
* SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR
* GRAPHICS SUPPORT CUSTOMER.
******************************************************************/
#ifndef __MUSB_HDRC_DEFS_H__
#define __MUSB_HDRC_DEFS_H__
#include <usb_defs.h>
#include <asm/io.h>
#ifdef CONFIG_USB_BLACKFIN
# include "blackfin_usb.h"
#endif
#define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */
/* EP0 */
struct musb_ep0_regs {
u16 reserved4;
u16 csr0;
u16 reserved5;
u16 reserved6;
u16 count0;
u8 host_type0;
u8 host_naklimit0;
u8 reserved7;
u8 reserved8;
u8 reserved9;
u8 configdata;
};
/* EP 1-15 */
struct musb_epN_regs {
u16 txmaxp;
u16 txcsr;
u16 rxmaxp;
u16 rxcsr;
u16 rxcount;
u8 txtype;
u8 txinterval;
u8 rxtype;
u8 rxinterval;
u8 reserved0;
u8 fifosize;
};
/* Mentor USB core register overlay structure */
#ifndef musb_regs
struct musb_regs {
/* common registers */
u8 faddr;
u8 power;
u16 intrtx;
u16 intrrx;
u16 intrtxe;
u16 intrrxe;
u8 intrusb;
u8 intrusbe;
u16 frame;
u8 index;
u8 testmode;
/* indexed registers */
u16 txmaxp;
u16 txcsr;
u16 rxmaxp;
u16 rxcsr;
u16 rxcount;
u8 txtype;
u8 txinterval;
u8 rxtype;
u8 rxinterval;
u8 reserved0;
u8 fifosize;
/* fifo */
u32 fifox[16];
/* OTG, dynamic FIFO, version & vendor registers */
u8 devctl;
u8 reserved1;
u8 txfifosz;
u8 rxfifosz;
u16 txfifoadd;
u16 rxfifoadd;
u32 vcontrol;
u16 hwvers;
u16 reserved2a[1];
u8 ulpi_busctl;
u8 reserved2b[1];
u16 reserved2[3];
u8 epinfo;
u8 raminfo;
u8 linkinfo;
u8 vplen;
u8 hseof1;
u8 fseof1;
u8 lseof1;
u8 reserved3;
/* target address registers */
struct musb_tar_regs {
u8 txfuncaddr;
u8 reserved0;
u8 txhubaddr;
u8 txhubport;
u8 rxfuncaddr;
u8 reserved1;
u8 rxhubaddr;
u8 rxhubport;
} tar[16];
/*
* endpoint registers
* ep0 elements are valid when array index is 0
* otherwise epN is valid
*/
union musb_ep_regs {
struct musb_ep0_regs ep0;
struct musb_epN_regs epN;
} ep[16];
} __attribute__((packed));
#endif
/*
* MUSB Register bits
*/
/* POWER */
#define MUSB_POWER_ISOUPDATE 0x80
#define MUSB_POWER_SOFTCONN 0x40
#define MUSB_POWER_HSENAB 0x20
#define MUSB_POWER_HSMODE 0x10
#define MUSB_POWER_RESET 0x08
#define MUSB_POWER_RESUME 0x04
#define MUSB_POWER_SUSPENDM 0x02
#define MUSB_POWER_ENSUSPEND 0x01
#define MUSB_POWER_HSMODE_SHIFT 4
/* INTRUSB */
#define MUSB_INTR_SUSPEND 0x01
#define MUSB_INTR_RESUME 0x02
#define MUSB_INTR_RESET 0x04
#define MUSB_INTR_BABBLE 0x04
#define MUSB_INTR_SOF 0x08
#define MUSB_INTR_CONNECT 0x10
#define MUSB_INTR_DISCONNECT 0x20
#define MUSB_INTR_SESSREQ 0x40
#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */
/* DEVCTL */
#define MUSB_DEVCTL_BDEVICE 0x80
#define MUSB_DEVCTL_FSDEV 0x40
#define MUSB_DEVCTL_LSDEV 0x20
#define MUSB_DEVCTL_VBUS 0x18
#define MUSB_DEVCTL_VBUS_SHIFT 3
#define MUSB_DEVCTL_HM 0x04
#define MUSB_DEVCTL_HR 0x02
#define MUSB_DEVCTL_SESSION 0x01
/* ULPI VBUSCONTROL */
#define ULPI_USE_EXTVBUS 0x01
#define ULPI_USE_EXTVBUSIND 0x02
/* TESTMODE */
#define MUSB_TEST_FORCE_HOST 0x80
#define MUSB_TEST_FIFO_ACCESS 0x40
#define MUSB_TEST_FORCE_FS 0x20
#define MUSB_TEST_FORCE_HS 0x10
#define MUSB_TEST_PACKET 0x08
#define MUSB_TEST_K 0x04
#define MUSB_TEST_J 0x02
#define MUSB_TEST_SE0_NAK 0x01
/* Allocate for double-packet buffering (effectively doubles assigned _SIZE) */
#define MUSB_FIFOSZ_DPB 0x10
/* Allocation size (8, 16, 32, ... 4096) */
#define MUSB_FIFOSZ_SIZE 0x0f
/* CSR0 */
#define MUSB_CSR0_FLUSHFIFO 0x0100
#define MUSB_CSR0_TXPKTRDY 0x0002
#define MUSB_CSR0_RXPKTRDY 0x0001
/* CSR0 in Peripheral mode */
#define MUSB_CSR0_P_SVDSETUPEND 0x0080
#define MUSB_CSR0_P_SVDRXPKTRDY 0x0040
#define MUSB_CSR0_P_SENDSTALL 0x0020
#define MUSB_CSR0_P_SETUPEND 0x0010
#define MUSB_CSR0_P_DATAEND 0x0008
#define MUSB_CSR0_P_SENTSTALL 0x0004
/* CSR0 in Host mode */
#define MUSB_CSR0_H_DIS_PING 0x0800
#define MUSB_CSR0_H_WR_DATATOGGLE 0x0400 /* Set to allow setting: */
#define MUSB_CSR0_H_DATATOGGLE 0x0200 /* Data toggle control */
#define MUSB_CSR0_H_NAKTIMEOUT 0x0080
#define MUSB_CSR0_H_STATUSPKT 0x0040
#define MUSB_CSR0_H_REQPKT 0x0020
#define MUSB_CSR0_H_ERROR 0x0010
#define MUSB_CSR0_H_SETUPPKT 0x0008
#define MUSB_CSR0_H_RXSTALL 0x0004
/* CSR0 bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_CSR0_P_WZC_BITS \
(MUSB_CSR0_P_SENTSTALL)
#define MUSB_CSR0_H_WZC_BITS \
(MUSB_CSR0_H_NAKTIMEOUT | MUSB_CSR0_H_RXSTALL \
| MUSB_CSR0_RXPKTRDY)
/* TxType/RxType */
#define MUSB_TYPE_SPEED 0xc0
#define MUSB_TYPE_SPEED_SHIFT 6
#define MUSB_TYPE_SPEED_HIGH 1
#define MUSB_TYPE_SPEED_FULL 2
#define MUSB_TYPE_SPEED_LOW 3
#define MUSB_TYPE_PROTO 0x30 /* Implicitly zero for ep0 */
#define MUSB_TYPE_PROTO_SHIFT 4
#define MUSB_TYPE_REMOTE_END 0xf /* Implicitly zero for ep0 */
#define MUSB_TYPE_PROTO_BULK 2
#define MUSB_TYPE_PROTO_INTR 3
/* CONFIGDATA */
#define MUSB_CONFIGDATA_MPRXE 0x80 /* Auto bulk pkt combining */
#define MUSB_CONFIGDATA_MPTXE 0x40 /* Auto bulk pkt splitting */
#define MUSB_CONFIGDATA_BIGENDIAN 0x20
#define MUSB_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */
#define MUSB_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */
#define MUSB_CONFIGDATA_DYNFIFO 0x04 /* Dynamic FIFO sizing */
#define MUSB_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */
#define MUSB_CONFIGDATA_UTMIDW 0x01 /* Data width 0/1 => 8/16bits */
/* TXCSR in Peripheral and Host mode */
#define MUSB_TXCSR_AUTOSET 0x8000
#define MUSB_TXCSR_MODE 0x2000
#define MUSB_TXCSR_DMAENAB 0x1000
#define MUSB_TXCSR_FRCDATATOG 0x0800
#define MUSB_TXCSR_DMAMODE 0x0400
#define MUSB_TXCSR_CLRDATATOG 0x0040
#define MUSB_TXCSR_FLUSHFIFO 0x0008
#define MUSB_TXCSR_FIFONOTEMPTY 0x0002
#define MUSB_TXCSR_TXPKTRDY 0x0001
/* TXCSR in Peripheral mode */
#define MUSB_TXCSR_P_ISO 0x4000
#define MUSB_TXCSR_P_INCOMPTX 0x0080
#define MUSB_TXCSR_P_SENTSTALL 0x0020
#define MUSB_TXCSR_P_SENDSTALL 0x0010
#define MUSB_TXCSR_P_UNDERRUN 0x0004
/* TXCSR in Host mode */
#define MUSB_TXCSR_H_WR_DATATOGGLE 0x0200
#define MUSB_TXCSR_H_DATATOGGLE 0x0100
#define MUSB_TXCSR_H_NAKTIMEOUT 0x0080
#define MUSB_TXCSR_H_RXSTALL 0x0020
#define MUSB_TXCSR_H_ERROR 0x0004
#define MUSB_TXCSR_H_DATATOGGLE_SHIFT 8
/* TXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_TXCSR_P_WZC_BITS \
(MUSB_TXCSR_P_INCOMPTX | MUSB_TXCSR_P_SENTSTALL \
| MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_FIFONOTEMPTY)
#define MUSB_TXCSR_H_WZC_BITS \
(MUSB_TXCSR_H_NAKTIMEOUT | MUSB_TXCSR_H_RXSTALL \
| MUSB_TXCSR_H_ERROR | MUSB_TXCSR_FIFONOTEMPTY)
/* RXCSR in Peripheral and Host mode */
#define MUSB_RXCSR_AUTOCLEAR 0x8000
#define MUSB_RXCSR_DMAENAB 0x2000
#define MUSB_RXCSR_DISNYET 0x1000
#define MUSB_RXCSR_PID_ERR 0x1000
#define MUSB_RXCSR_DMAMODE 0x0800
#define MUSB_RXCSR_INCOMPRX 0x0100
#define MUSB_RXCSR_CLRDATATOG 0x0080
#define MUSB_RXCSR_FLUSHFIFO 0x0010
#define MUSB_RXCSR_DATAERROR 0x0008
#define MUSB_RXCSR_FIFOFULL 0x0002
#define MUSB_RXCSR_RXPKTRDY 0x0001
/* RXCSR in Peripheral mode */
#define MUSB_RXCSR_P_ISO 0x4000
#define MUSB_RXCSR_P_SENTSTALL 0x0040
#define MUSB_RXCSR_P_SENDSTALL 0x0020
#define MUSB_RXCSR_P_OVERRUN 0x0004
/* RXCSR in Host mode */
#define MUSB_RXCSR_H_AUTOREQ 0x4000
#define MUSB_RXCSR_H_WR_DATATOGGLE 0x0400
#define MUSB_RXCSR_H_DATATOGGLE 0x0200
#define MUSB_RXCSR_H_RXSTALL 0x0040
#define MUSB_RXCSR_H_REQPKT 0x0020
#define MUSB_RXCSR_H_ERROR 0x0004
#define MUSB_S_RXCSR_H_DATATOGGLE 9
/* RXCSR bits to avoid zeroing (write zero clears, write 1 ignored) */
#define MUSB_RXCSR_P_WZC_BITS \
(MUSB_RXCSR_P_SENTSTALL | MUSB_RXCSR_P_OVERRUN \
| MUSB_RXCSR_RXPKTRDY)
#define MUSB_RXCSR_H_WZC_BITS \
(MUSB_RXCSR_H_RXSTALL | MUSB_RXCSR_H_ERROR \
| MUSB_RXCSR_DATAERROR | MUSB_RXCSR_RXPKTRDY)
/* HUBADDR */
#define MUSB_HUBADDR_MULTI_TT 0x80
/* Endpoint configuration information. Note: The value of endpoint fifo size
* element should be either 8,16,32,64,128,256,512,1024,2048 or 4096. Other
* values are not supported
*/
struct musb_epinfo {
u8 epnum; /* endpoint number */
u8 epdir; /* endpoint direction */
u16 epsize; /* endpoint FIFO size */
};
/*
* Platform specific MUSB configuration. Any platform using the musb
* functionality should create one instance of this structure in the
* platform specific file.
*/
struct musb_config {
struct musb_regs *regs;
u32 timeout;
u8 musb_speed;
u8 extvbus;
};
/* externally defined data */
extern struct musb_config musb_cfg;
extern struct musb_regs *musbr;
/* exported functions */
extern void musb_start(void);
extern void musb_configure_ep(const struct musb_epinfo *epinfo, u8 cnt);
extern void write_fifo(u8 ep, u32 length, void *fifo_data);
extern void read_fifo(u8 ep, u32 length, void *fifo_data);
#if defined(CONFIG_USB_BLACKFIN)
/* Every USB register is accessed as a 16-bit even if the value itself
* is only 8-bits in size. Fun stuff.
*/
# undef readb
# define readb(addr) (u8)bfin_read16(addr)
# undef writeb
# define writeb(b, addr) bfin_write16(addr, b)
# undef MUSB_TXCSR_MODE /* not supported */
# define MUSB_TXCSR_MODE 0
/*
* The USB PHY on current Blackfin processors is a UTMI+ level 2 PHY.
* However, it has no ULPI support - so there are no registers at all.
* That means accesses to ULPI_BUSCONTROL have to be abstracted away.
*/
static inline u8 musb_read_ulpi_buscontrol(struct musb_regs *musbr)
{
return 0;
}
static inline void musb_write_ulpi_buscontrol(struct musb_regs *musbr, u8 val)
{}
#else
static inline u8 musb_read_ulpi_buscontrol(struct musb_regs *musbr)
{
return readb(&musbr->ulpi_busctl);
}
static inline void musb_write_ulpi_buscontrol(struct musb_regs *musbr, u8 val)
{
writeb(val, &musbr->ulpi_busctl);
}
#endif
#endif /* __MUSB_HDRC_DEFS_H__ */

View file

@ -0,0 +1,205 @@
/*
* Copyright (c) 2009 Wind River Systems, Inc.
* Tom Rix <Tom.Rix@windriver.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.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/* Define MUSB_DEBUG before including this file to get debug macros */
#ifdef MUSB_DEBUG
#define MUSB_FLAGS_PRINT(v, x, y) \
if (((v) & MUSB_##x##_##y)) \
serial_printf("\t\t"#y"\n")
static inline void musb_print_pwr(u8 b)
{
serial_printf("\tpower 0x%2.2x\n", b);
MUSB_FLAGS_PRINT(b, POWER, ISOUPDATE);
MUSB_FLAGS_PRINT(b, POWER, SOFTCONN);
MUSB_FLAGS_PRINT(b, POWER, HSENAB);
MUSB_FLAGS_PRINT(b, POWER, HSMODE);
MUSB_FLAGS_PRINT(b, POWER, RESET);
MUSB_FLAGS_PRINT(b, POWER, RESUME);
MUSB_FLAGS_PRINT(b, POWER, SUSPENDM);
MUSB_FLAGS_PRINT(b, POWER, ENSUSPEND);
}
static inline void musb_print_csr0(u16 w)
{
serial_printf("\tcsr0 0x%4.4x\n", w);
MUSB_FLAGS_PRINT(w, CSR0, FLUSHFIFO);
MUSB_FLAGS_PRINT(w, CSR0_P, SVDSETUPEND);
MUSB_FLAGS_PRINT(w, CSR0_P, SVDRXPKTRDY);
MUSB_FLAGS_PRINT(w, CSR0_P, SENDSTALL);
MUSB_FLAGS_PRINT(w, CSR0_P, SETUPEND);
MUSB_FLAGS_PRINT(w, CSR0_P, DATAEND);
MUSB_FLAGS_PRINT(w, CSR0_P, SENTSTALL);
MUSB_FLAGS_PRINT(w, CSR0, TXPKTRDY);
MUSB_FLAGS_PRINT(w, CSR0, RXPKTRDY);
}
static inline void musb_print_intrusb(u8 b)
{
serial_printf("\tintrusb 0x%2.2x\n", b);
MUSB_FLAGS_PRINT(b, INTR, VBUSERROR);
MUSB_FLAGS_PRINT(b, INTR, SESSREQ);
MUSB_FLAGS_PRINT(b, INTR, DISCONNECT);
MUSB_FLAGS_PRINT(b, INTR, CONNECT);
MUSB_FLAGS_PRINT(b, INTR, SOF);
MUSB_FLAGS_PRINT(b, INTR, RESUME);
MUSB_FLAGS_PRINT(b, INTR, SUSPEND);
if (b & MUSB_INTR_BABBLE)
serial_printf("\t\tMUSB_INTR_RESET or MUSB_INTR_BABBLE\n");
}
static inline void musb_print_intrtx(u16 w)
{
serial_printf("\tintrtx 0x%4.4x\n", w);
}
static inline void musb_print_intrrx(u16 w)
{
serial_printf("\tintrx 0x%4.4x\n", w);
}
static inline void musb_print_devctl(u8 b)
{
serial_printf("\tdevctl 0x%2.2x\n", b);
if (b & MUSB_DEVCTL_BDEVICE)
serial_printf("\t\tB device\n");
else
serial_printf("\t\tA device\n");
if (b & MUSB_DEVCTL_FSDEV)
serial_printf("\t\tFast Device -(host mode)\n");
if (b & MUSB_DEVCTL_LSDEV)
serial_printf("\t\tSlow Device -(host mode)\n");
if (b & MUSB_DEVCTL_HM)
serial_printf("\t\tHost mode\n");
else
serial_printf("\t\tPeripherial mode\n");
if (b & MUSB_DEVCTL_HR)
serial_printf("\t\tHost request started(B device)\n");
else
serial_printf("\t\tHost request finished(B device)\n");
if (b & MUSB_DEVCTL_BDEVICE) {
if (b & MUSB_DEVCTL_SESSION)
serial_printf("\t\tStart of session(B device)\n");
else
serial_printf("\t\tEnd of session(B device)\n");
} else {
if (b & MUSB_DEVCTL_SESSION)
serial_printf("\t\tStart of session(A device)\n");
else
serial_printf("\t\tEnd of session(A device)\n");
}
}
static inline void musb_print_config(u8 b)
{
serial_printf("\tconfig 0x%2.2x\n", b);
if (b & MUSB_CONFIGDATA_MPRXE)
serial_printf("\t\tAuto combine rx bulk packets\n");
if (b & MUSB_CONFIGDATA_MPTXE)
serial_printf("\t\tAuto split tx bulk packets\n");
if (b & MUSB_CONFIGDATA_BIGENDIAN)
serial_printf("\t\tBig Endian ordering\n");
else
serial_printf("\t\tLittle Endian ordering\n");
if (b & MUSB_CONFIGDATA_HBRXE)
serial_printf("\t\tHigh speed rx iso endpoint\n");
if (b & MUSB_CONFIGDATA_HBTXE)
serial_printf("\t\tHigh speed tx iso endpoint\n");
if (b & MUSB_CONFIGDATA_DYNFIFO)
serial_printf("\t\tDynamic fifo sizing\n");
if (b & MUSB_CONFIGDATA_SOFTCONE)
serial_printf("\t\tSoft Connect\n");
if (b & MUSB_CONFIGDATA_UTMIDW)
serial_printf("\t\t16 bit data width\n");
else
serial_printf("\t\t8 bit data width\n");
}
static inline void musb_print_rxmaxp(u16 w)
{
serial_printf("\trxmaxp 0x%4.4x\n", w);
}
static inline void musb_print_rxcsr(u16 w)
{
serial_printf("\trxcsr 0x%4.4x\n", w);
MUSB_FLAGS_PRINT(w, RXCSR, AUTOCLEAR);
MUSB_FLAGS_PRINT(w, RXCSR, DMAENAB);
MUSB_FLAGS_PRINT(w, RXCSR, DISNYET);
MUSB_FLAGS_PRINT(w, RXCSR, PID_ERR);
MUSB_FLAGS_PRINT(w, RXCSR, DMAMODE);
MUSB_FLAGS_PRINT(w, RXCSR, CLRDATATOG);
MUSB_FLAGS_PRINT(w, RXCSR, FLUSHFIFO);
MUSB_FLAGS_PRINT(w, RXCSR, DATAERROR);
MUSB_FLAGS_PRINT(w, RXCSR, FIFOFULL);
MUSB_FLAGS_PRINT(w, RXCSR, RXPKTRDY);
MUSB_FLAGS_PRINT(w, RXCSR_P, SENTSTALL);
MUSB_FLAGS_PRINT(w, RXCSR_P, SENDSTALL);
MUSB_FLAGS_PRINT(w, RXCSR_P, OVERRUN);
if (w & MUSB_RXCSR_P_ISO)
serial_printf("\t\tiso mode\n");
else
serial_printf("\t\tbulk mode\n");
}
static inline void musb_print_txmaxp(u16 w)
{
serial_printf("\ttxmaxp 0x%4.4x\n", w);
}
static inline void musb_print_txcsr(u16 w)
{
serial_printf("\ttxcsr 0x%4.4x\n", w);
MUSB_FLAGS_PRINT(w, TXCSR, TXPKTRDY);
MUSB_FLAGS_PRINT(w, TXCSR, FIFONOTEMPTY);
MUSB_FLAGS_PRINT(w, TXCSR, FLUSHFIFO);
MUSB_FLAGS_PRINT(w, TXCSR, CLRDATATOG);
MUSB_FLAGS_PRINT(w, TXCSR_P, UNDERRUN);
MUSB_FLAGS_PRINT(w, TXCSR_P, SENTSTALL);
MUSB_FLAGS_PRINT(w, TXCSR_P, SENDSTALL);
if (w & MUSB_TXCSR_MODE)
serial_printf("\t\tTX mode\n");
else
serial_printf("\t\tRX mode\n");
}
#else
/* stubs */
#define musb_print_pwr(b)
#define musb_print_csr0(w)
#define musb_print_intrusb(b)
#define musb_print_intrtx(w)
#define musb_print_intrrx(w)
#define musb_print_devctl(b)
#define musb_print_config(b)
#define musb_print_rxmaxp(w)
#define musb_print_rxcsr(w)
#define musb_print_txmaxp(w)
#define musb_print_txcsr(w)
#endif /* MUSB_DEBUG */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
/*
* Mentor USB OTG Core host controller driver.
*
* Copyright (c) 2008 Texas Instruments
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
*/
#ifndef __MUSB_HCD_H__
#define __MUSB_HCD_H__
#include "musb_core.h"
#ifdef CONFIG_USB_KEYBOARD
#include <stdio_dev.h>
extern unsigned char new[];
#endif
#ifndef CONFIG_MUSB_TIMEOUT
# define CONFIG_MUSB_TIMEOUT 100000
#endif
/* This defines the endpoint number used for control transfers */
#define MUSB_CONTROL_EP 0
/* This defines the endpoint number used for bulk transfer */
#ifndef MUSB_BULK_EP
# define MUSB_BULK_EP 1
#endif
/* This defines the endpoint number used for interrupt transfer */
#define MUSB_INTR_EP 2
/* Determine the operating speed of MUSB core */
#define musb_ishighspeed() \
((readb(&musbr->power) & MUSB_POWER_HSMODE) \
>> MUSB_POWER_HSMODE_SHIFT)
#define min_t(type, x, y) \
({ type __x = (x); type __y = (y); __x < __y ? __x : __y; })
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00
/* extern functions */
extern int musb_platform_init(void);
extern void musb_platform_deinit(void);
#endif /* __MUSB_HCD_H__ */

View file

@ -0,0 +1,963 @@
/*
* Copyright (c) 2009 Wind River Systems, Inc.
* Tom Rix <Tom.Rix@windriver.com>
*
* This file is a rewrite of the usb device part of
* repository git.omapzoom.org/repo/u-boot.git, branch master,
* file cpu/omap3/fastboot.c
*
* This is the unique part of its copyright :
*
* -------------------------------------------------------------------------
*
* (C) Copyright 2008 - 2009
* Windriver, <www.windriver.com>
* Tom Rix <Tom.Rix@windriver.com>
*
* -------------------------------------------------------------------------
*
* The details of connecting the device to the uboot usb device subsystem
* came from the old omap3 repository www.sakoman.net/u-boot-omap3.git,
* branch omap3-dev-usb, file drivers/usb/usbdcore_musb.c
*
* This is the unique part of its copyright :
*
* -------------------------------------------------------------------------
*
* (C) Copyright 2008 Texas Instruments Incorporated.
*
* Based on
* u-boot OMAP1510 USB drivers (drivers/usbdcore_omap1510.c)
* twl4030 init based on linux (drivers/i2c/chips/twl4030_usb.c)
*
* Author: Diego Dompe (diego.dompe@ridgerun.com)
* 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.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <usb/musb_udc.h>
#include "../gadget/ep0.h"
#include "musb_core.h"
#if defined(CONFIG_USB_OMAP3)
#include "omap3.h"
#elif defined(CONFIG_USB_AM35X)
#include "am35x.h"
#elif defined(CONFIG_USB_DAVINCI)
#include "davinci.h"
#endif
/* Define MUSB_DEBUG for debugging */
/* #define MUSB_DEBUG */
#include "musb_debug.h"
#define MAX_ENDPOINT 15
#define GET_ENDPOINT(dev,ep) \
(((struct usb_device_instance *)(dev))->bus->endpoint_array + ep)
#define SET_EP0_STATE(s) \
do { \
if ((0 <= (s)) && (SET_ADDRESS >= (s))) { \
if ((s) != ep0_state) { \
if ((debug_setup) && (debug_level > 1)) \
serial_printf("INFO : Changing state " \
"from %s to %s in %s at " \
"line %d\n", \
ep0_state_strings[ep0_state],\
ep0_state_strings[s], \
__PRETTY_FUNCTION__, \
__LINE__); \
ep0_state = s; \
} \
} else { \
if (debug_level > 0) \
serial_printf("Error at %s %d with setting " \
"state %d is invalid\n", \
__PRETTY_FUNCTION__, __LINE__, s); \
} \
} while (0)
/* static implies these initialized to 0 or NULL */
static int debug_setup;
static int debug_level;
static struct musb_epinfo epinfo[MAX_ENDPOINT * 2];
static enum ep0_state_enum {
IDLE = 0,
TX,
RX,
SET_ADDRESS
} ep0_state = IDLE;
static char *ep0_state_strings[4] = {
"IDLE",
"TX",
"RX",
"SET_ADDRESS",
};
static struct urb *ep0_urb;
struct usb_endpoint_instance *ep0_endpoint;
static struct usb_device_instance *udc_device;
static int enabled;
#ifdef MUSB_DEBUG
static void musb_db_regs(void)
{
u8 b;
u16 w;
b = readb(&musbr->faddr);
serial_printf("\tfaddr 0x%2.2x\n", b);
b = readb(&musbr->power);
musb_print_pwr(b);
w = readw(&musbr->ep[0].ep0.csr0);
musb_print_csr0(w);
b = readb(&musbr->devctl);
musb_print_devctl(b);
b = readb(&musbr->ep[0].ep0.configdata);
musb_print_config(b);
w = readw(&musbr->frame);
serial_printf("\tframe 0x%4.4x\n", w);
b = readb(&musbr->index);
serial_printf("\tindex 0x%2.2x\n", b);
w = readw(&musbr->ep[1].epN.rxmaxp);
musb_print_rxmaxp(w);
w = readw(&musbr->ep[1].epN.rxcsr);
musb_print_rxcsr(w);
w = readw(&musbr->ep[1].epN.txmaxp);
musb_print_txmaxp(w);
w = readw(&musbr->ep[1].epN.txcsr);
musb_print_txcsr(w);
}
#else
#define musb_db_regs()
#endif /* DEBUG_MUSB */
static void musb_peri_softconnect(void)
{
u8 power, devctl;
/* Power off MUSB */
power = readb(&musbr->power);
power &= ~MUSB_POWER_SOFTCONN;
writeb(power, &musbr->power);
/* Read intr to clear */
readb(&musbr->intrusb);
readw(&musbr->intrrx);
readw(&musbr->intrtx);
udelay(1000 * 1000); /* 1 sec */
/* Power on MUSB */
power = readb(&musbr->power);
power |= MUSB_POWER_SOFTCONN;
/*
* The usb device interface is usb 1.1
* Disable 2.0 high speed by clearring the hsenable bit.
*/
power &= ~MUSB_POWER_HSENAB;
writeb(power, &musbr->power);
/* Check if device is in b-peripheral mode */
devctl = readb(&musbr->devctl);
if (!(devctl & MUSB_DEVCTL_BDEVICE) ||
(devctl & MUSB_DEVCTL_HM)) {
serial_printf("ERROR : Unsupport USB mode\n");
serial_printf("Check that mini-B USB cable is attached "
"to the device\n");
}
if (debug_setup && (debug_level > 1))
musb_db_regs();
}
static void musb_peri_reset(void)
{
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s reset\n", __PRETTY_FUNCTION__);
if (ep0_endpoint)
ep0_endpoint->endpoint_address = 0xff;
/* Sync sw and hw addresses */
writeb(udc_device->address, &musbr->faddr);
SET_EP0_STATE(IDLE);
}
static void musb_peri_resume(void)
{
/* noop */
}
static void musb_peri_ep0_stall(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= MUSB_CSR0_P_SENDSTALL;
writew(csr0, &musbr->ep[0].ep0.csr0);
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s stall\n", __PRETTY_FUNCTION__);
}
static void musb_peri_ep0_ack_req(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= MUSB_CSR0_P_SVDRXPKTRDY;
writew(csr0, &musbr->ep[0].ep0.csr0);
}
static void musb_ep0_tx_ready(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= MUSB_CSR0_TXPKTRDY;
writew(csr0, &musbr->ep[0].ep0.csr0);
}
static void musb_ep0_tx_ready_and_last(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= (MUSB_CSR0_TXPKTRDY | MUSB_CSR0_P_DATAEND);
writew(csr0, &musbr->ep[0].ep0.csr0);
}
static void musb_peri_ep0_last(void)
{
u16 csr0;
csr0 = readw(&musbr->ep[0].ep0.csr0);
csr0 |= MUSB_CSR0_P_DATAEND;
writew(csr0, &musbr->ep[0].ep0.csr0);
}
static void musb_peri_ep0_set_address(void)
{
u8 faddr;
writeb(udc_device->address, &musbr->faddr);
/* Verify */
faddr = readb(&musbr->faddr);
if (udc_device->address == faddr) {
SET_EP0_STATE(IDLE);
usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0);
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s Address set to %d\n",
__PRETTY_FUNCTION__, udc_device->address);
} else {
if (debug_level > 0)
serial_printf("ERROR : %s Address missmatch "
"sw %d vs hw %d\n",
__PRETTY_FUNCTION__,
udc_device->address, faddr);
}
}
static void musb_peri_rx_ack(unsigned int ep)
{
u16 peri_rxcsr;
peri_rxcsr = readw(&musbr->ep[ep].epN.rxcsr);
peri_rxcsr &= ~MUSB_RXCSR_RXPKTRDY;
writew(peri_rxcsr, &musbr->ep[ep].epN.rxcsr);
}
static void musb_peri_tx_ready(unsigned int ep)
{
u16 peri_txcsr;
peri_txcsr = readw(&musbr->ep[ep].epN.txcsr);
peri_txcsr |= MUSB_TXCSR_TXPKTRDY;
writew(peri_txcsr, &musbr->ep[ep].epN.txcsr);
}
static void musb_peri_ep0_zero_data_request(int err)
{
musb_peri_ep0_ack_req();
if (err) {
musb_peri_ep0_stall();
SET_EP0_STATE(IDLE);
} else {
musb_peri_ep0_last();
/* USBD state */
switch (ep0_urb->device_request.bRequest) {
case USB_REQ_SET_ADDRESS:
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s received set "
"address\n", __PRETTY_FUNCTION__);
break;
case USB_REQ_SET_CONFIGURATION:
if ((debug_setup) && (debug_level > 1))
serial_printf("INFO : %s Configured\n",
__PRETTY_FUNCTION__);
usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0);
break;
}
/* EP0 state */
if (USB_REQ_SET_ADDRESS == ep0_urb->device_request.bRequest) {
SET_EP0_STATE(SET_ADDRESS);
} else {
SET_EP0_STATE(IDLE);
}
}
}
static void musb_peri_ep0_rx_data_request(void)
{
/*
* This is the completion of the data OUT / RX
*
* Host is sending data to ep0 that is not
* part of setup. This comes from the cdc_recv_setup
* op that is device specific.
*
*/
musb_peri_ep0_ack_req();
ep0_endpoint->rcv_urb = ep0_urb;
ep0_urb->actual_length = 0;
SET_EP0_STATE(RX);
}
static void musb_peri_ep0_tx_data_request(int err)
{
if (err) {
musb_peri_ep0_stall();
SET_EP0_STATE(IDLE);
} else {
musb_peri_ep0_ack_req();
ep0_endpoint->tx_urb = ep0_urb;
ep0_endpoint->sent = 0;
SET_EP0_STATE(TX);
}
}
static void musb_peri_ep0_idle(void)
{
u16 count0;
int err;
u16 csr0;
/*
* Verify addresses
* A lot of confusion can be caused if the address
* in software, udc layer, does not agree with the
* hardware. Since the setting of the hardware address
* must be set after the set address request, the
* usb state machine is out of sync for a few frame.
* It is a good idea to run this check when changes
* are made to the state machine.
*/
if ((debug_level > 0) &&
(ep0_state != SET_ADDRESS)) {
u8 faddr;
faddr = readb(&musbr->faddr);
if (udc_device->address != faddr) {
serial_printf("ERROR : %s addresses do not"
"match sw %d vs hw %d\n",
__PRETTY_FUNCTION__,
udc_device->address, faddr);
udelay(1000 * 1000);
hang();
}
}
csr0 = readw(&musbr->ep[0].ep0.csr0);
if (!(MUSB_CSR0_RXPKTRDY & csr0))
goto end;
count0 = readw(&musbr->ep[0].ep0.count0);
if (count0 == 0)
goto end;
if (count0 != 8) {
if ((debug_setup) && (debug_level > 1))
serial_printf("WARN : %s SETUP incorrect size %d\n",
__PRETTY_FUNCTION__, count0);
musb_peri_ep0_stall();
goto end;
}
read_fifo(0, count0, &ep0_urb->device_request);
if (debug_level > 2)
print_usb_device_request(&ep0_urb->device_request);
if (ep0_urb->device_request.wLength == 0) {
err = ep0_recv_setup(ep0_urb);
/* Zero data request */
musb_peri_ep0_zero_data_request(err);
} else {
/* Is data coming or going ? */
u8 reqType = ep0_urb->device_request.bmRequestType;
if (USB_REQ_DEVICE2HOST == (reqType & USB_REQ_DIRECTION_MASK)) {
err = ep0_recv_setup(ep0_urb);
/* Device to host */
musb_peri_ep0_tx_data_request(err);
} else {
/*
* Host to device
*
* The RX routine will call ep0_recv_setup
* when the data packet has arrived.
*/
musb_peri_ep0_rx_data_request();
}
}
end:
return;
}
static void musb_peri_ep0_rx(void)
{
/*
* This is the completion of the data OUT / RX
*
* Host is sending data to ep0 that is not
* part of setup. This comes from the cdc_recv_setup
* op that is device specific.
*
* Pass the data back to driver ep0_recv_setup which
* should give the cdc_recv_setup the chance to handle
* the rx
*/
u16 csr0;
u16 count0;
if (debug_level > 3) {
if (0 != ep0_urb->actual_length) {
serial_printf("%s finished ? %d of %d\n",
__PRETTY_FUNCTION__,
ep0_urb->actual_length,
ep0_urb->device_request.wLength);
}
}
if (ep0_urb->device_request.wLength == ep0_urb->actual_length) {
musb_peri_ep0_last();
SET_EP0_STATE(IDLE);
ep0_recv_setup(ep0_urb);
return;
}
csr0 = readw(&musbr->ep[0].ep0.csr0);
if (!(MUSB_CSR0_RXPKTRDY & csr0))
return;
count0 = readw(&musbr->ep[0].ep0.count0);
if (count0) {
struct usb_endpoint_instance *endpoint;
u32 length;
u8 *data;
endpoint = ep0_endpoint;
if (endpoint && endpoint->rcv_urb) {
struct urb *urb = endpoint->rcv_urb;
unsigned int remaining_space = urb->buffer_length -
urb->actual_length;
if (remaining_space) {
int urb_bad = 0; /* urb is good */
if (count0 > remaining_space)
length = remaining_space;
else
length = count0;
data = (u8 *) urb->buffer_data;
data += urb->actual_length;
/* The common musb fifo reader */
read_fifo(0, length, data);
musb_peri_ep0_ack_req();
/*
* urb's actual_length is updated in
* usbd_rcv_complete
*/
usbd_rcv_complete(endpoint, length, urb_bad);
} else {
if (debug_level > 0)
serial_printf("ERROR : %s no space in "
"rcv buffer\n",
__PRETTY_FUNCTION__);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s problem with "
"endpoint\n",
__PRETTY_FUNCTION__);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s with nothing to do\n",
__PRETTY_FUNCTION__);
}
}
static void musb_peri_ep0_tx(void)
{
u16 csr0;
int transfer_size = 0;
unsigned int p, pm;
csr0 = readw(&musbr->ep[0].ep0.csr0);
/* Check for pending tx */
if (csr0 & MUSB_CSR0_TXPKTRDY)
goto end;
/* Check if this is the last packet sent */
if (ep0_endpoint->sent >= ep0_urb->actual_length) {
SET_EP0_STATE(IDLE);
goto end;
}
transfer_size = ep0_urb->actual_length - ep0_endpoint->sent;
/* Is the transfer size negative ? */
if (transfer_size <= 0) {
if (debug_level > 0)
serial_printf("ERROR : %s problem with the"
" transfer size %d\n",
__PRETTY_FUNCTION__,
transfer_size);
SET_EP0_STATE(IDLE);
goto end;
}
/* Truncate large transfers to the fifo size */
if (transfer_size > ep0_endpoint->tx_packetSize)
transfer_size = ep0_endpoint->tx_packetSize;
write_fifo(0, transfer_size, &ep0_urb->buffer[ep0_endpoint->sent]);
ep0_endpoint->sent += transfer_size;
/* Done or more to send ? */
if (ep0_endpoint->sent >= ep0_urb->actual_length)
musb_ep0_tx_ready_and_last();
else
musb_ep0_tx_ready();
/* Wait a bit */
pm = 10;
for (p = 0; p < pm; p++) {
csr0 = readw(&musbr->ep[0].ep0.csr0);
if (!(csr0 & MUSB_CSR0_TXPKTRDY))
break;
/* Double the delay. */
udelay(1 << pm);
}
if ((ep0_endpoint->sent >= ep0_urb->actual_length) && (p < pm))
SET_EP0_STATE(IDLE);
end:
return;
}
static void musb_peri_ep0(void)
{
u16 csr0;
if (SET_ADDRESS == ep0_state)
return;
csr0 = readw(&musbr->ep[0].ep0.csr0);
/* Error conditions */
if (MUSB_CSR0_P_SENTSTALL & csr0) {
csr0 &= ~MUSB_CSR0_P_SENTSTALL;
writew(csr0, &musbr->ep[0].ep0.csr0);
SET_EP0_STATE(IDLE);
}
if (MUSB_CSR0_P_SETUPEND & csr0) {
csr0 |= MUSB_CSR0_P_SVDSETUPEND;
writew(csr0, &musbr->ep[0].ep0.csr0);
SET_EP0_STATE(IDLE);
if ((debug_setup) && (debug_level > 1))
serial_printf("WARN: %s SETUPEND\n",
__PRETTY_FUNCTION__);
}
/* Normal states */
if (IDLE == ep0_state)
musb_peri_ep0_idle();
if (TX == ep0_state)
musb_peri_ep0_tx();
if (RX == ep0_state)
musb_peri_ep0_rx();
}
static void musb_peri_rx_ep(unsigned int ep)
{
u16 peri_rxcount = readw(&musbr->ep[ep].epN.rxcount);
if (peri_rxcount) {
struct usb_endpoint_instance *endpoint;
u32 length;
u8 *data;
endpoint = GET_ENDPOINT(udc_device, ep);
if (endpoint && endpoint->rcv_urb) {
struct urb *urb = endpoint->rcv_urb;
unsigned int remaining_space = urb->buffer_length -
urb->actual_length;
if (remaining_space) {
int urb_bad = 0; /* urb is good */
if (peri_rxcount > remaining_space)
length = remaining_space;
else
length = peri_rxcount;
data = (u8 *) urb->buffer_data;
data += urb->actual_length;
/* The common musb fifo reader */
read_fifo(ep, length, data);
musb_peri_rx_ack(ep);
/*
* urb's actual_length is updated in
* usbd_rcv_complete
*/
usbd_rcv_complete(endpoint, length, urb_bad);
} else {
if (debug_level > 0)
serial_printf("ERROR : %s %d no space "
"in rcv buffer\n",
__PRETTY_FUNCTION__, ep);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s %d problem with "
"endpoint\n",
__PRETTY_FUNCTION__, ep);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s %d with nothing to do\n",
__PRETTY_FUNCTION__, ep);
}
}
static void musb_peri_rx(u16 intr)
{
unsigned int ep;
/* Check for EP0 */
if (0x01 & intr)
musb_peri_ep0();
for (ep = 1; ep < 16; ep++) {
if ((1 << ep) & intr)
musb_peri_rx_ep(ep);
}
}
static void musb_peri_tx(u16 intr)
{
/* Check for EP0 */
if (0x01 & intr)
musb_peri_ep0_tx();
/*
* Use this in the future when handling epN tx
*
* u8 ep;
*
* for (ep = 1; ep < 16; ep++) {
* if ((1 << ep) & intr) {
* / * handle tx for this endpoint * /
* }
* }
*/
}
void udc_irq(void)
{
/* This is a high freq called function */
if (enabled) {
u8 intrusb;
intrusb = readb(&musbr->intrusb);
/*
* See drivers/usb/gadget/mpc8xx_udc.c for
* state diagram going from detached through
* configuration.
*/
if (MUSB_INTR_RESUME & intrusb) {
usbd_device_event_irq(udc_device,
DEVICE_BUS_ACTIVITY, 0);
musb_peri_resume();
}
musb_peri_ep0();
if (MUSB_INTR_RESET & intrusb) {
usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
musb_peri_reset();
}
if (MUSB_INTR_DISCONNECT & intrusb) {
/* cable unplugged from hub/host */
usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
musb_peri_reset();
usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0);
}
if (MUSB_INTR_SOF & intrusb) {
usbd_device_event_irq(udc_device,
DEVICE_BUS_ACTIVITY, 0);
musb_peri_resume();
}
if (MUSB_INTR_SUSPEND & intrusb) {
usbd_device_event_irq(udc_device,
DEVICE_BUS_INACTIVE, 0);
}
if (ep0_state != SET_ADDRESS) {
u16 intrrx, intrtx;
intrrx = readw(&musbr->intrrx);
intrtx = readw(&musbr->intrtx);
if (intrrx)
musb_peri_rx(intrrx);
if (intrtx)
musb_peri_tx(intrtx);
} else {
if (MUSB_INTR_SOF & intrusb) {
u8 faddr;
faddr = readb(&musbr->faddr);
/*
* Setting of the address can fail.
* Normally it succeeds the second time.
*/
if (udc_device->address != faddr)
musb_peri_ep0_set_address();
}
}
}
}
void udc_set_nak(int ep_num)
{
/* noop */
}
void udc_unset_nak(int ep_num)
{
/* noop */
}
int udc_endpoint_write(struct usb_endpoint_instance *endpoint)
{
int ret = 0;
/* Transmit only if the hardware is available */
if (endpoint->tx_urb && endpoint->state == 0) {
unsigned int ep = endpoint->endpoint_address &
USB_ENDPOINT_NUMBER_MASK;
u16 peri_txcsr = readw(&musbr->ep[ep].epN.txcsr);
/* Error conditions */
if (peri_txcsr & MUSB_TXCSR_P_UNDERRUN) {
peri_txcsr &= ~MUSB_TXCSR_P_UNDERRUN;
writew(peri_txcsr, &musbr->ep[ep].epN.txcsr);
}
if (debug_level > 1)
musb_print_txcsr(peri_txcsr);
/* Check if a packet is waiting to be sent */
if (!(peri_txcsr & MUSB_TXCSR_TXPKTRDY)) {
u32 length;
u8 *data;
struct urb *urb = endpoint->tx_urb;
unsigned int remaining_packet = urb->actual_length -
endpoint->sent;
if (endpoint->tx_packetSize < remaining_packet)
length = endpoint->tx_packetSize;
else
length = remaining_packet;
data = (u8 *) urb->buffer;
data += endpoint->sent;
/* common musb fifo function */
write_fifo(ep, length, data);
musb_peri_tx_ready(ep);
endpoint->last = length;
/* usbd_tx_complete will take care of updating 'sent' */
usbd_tx_complete(endpoint);
}
} else {
if (debug_level > 0)
serial_printf("ERROR : %s Problem with urb %p "
"or ep state %d\n",
__PRETTY_FUNCTION__,
endpoint->tx_urb, endpoint->state);
}
return ret;
}
void udc_setup_ep(struct usb_device_instance *device, unsigned int id,
struct usb_endpoint_instance *endpoint)
{
if (0 == id) {
/* EP0 */
ep0_endpoint = endpoint;
ep0_endpoint->endpoint_address = 0xff;
ep0_urb = usbd_alloc_urb(device, endpoint);
} else if (MAX_ENDPOINT >= id) {
int ep_addr;
/* Check the direction */
ep_addr = endpoint->endpoint_address;
if (USB_DIR_IN == (ep_addr & USB_ENDPOINT_DIR_MASK)) {
/* IN */
epinfo[(id * 2) + 1].epsize = endpoint->tx_packetSize;
} else {
/* OUT */
epinfo[id * 2].epsize = endpoint->rcv_packetSize;
}
musb_configure_ep(&epinfo[0],
sizeof(epinfo) / sizeof(struct musb_epinfo));
} else {
if (debug_level > 0)
serial_printf("ERROR : %s endpoint request %d "
"exceeds maximum %d\n",
__PRETTY_FUNCTION__, id, MAX_ENDPOINT);
}
}
void udc_connect(void)
{
/* noop */
}
void udc_disconnect(void)
{
/* noop */
}
void udc_enable(struct usb_device_instance *device)
{
/* Save the device structure pointer */
udc_device = device;
enabled = 1;
}
void udc_disable(void)
{
enabled = 0;
}
void udc_startup_events(struct usb_device_instance *device)
{
/* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */
usbd_device_event_irq(device, DEVICE_INIT, 0);
/*
* The DEVICE_CREATE event puts the USB device in the state
* STATE_ATTACHED.
*/
usbd_device_event_irq(device, DEVICE_CREATE, 0);
/* Resets the address to 0 */
usbd_device_event_irq(device, DEVICE_RESET, 0);
udc_enable(device);
}
int udc_init(void)
{
int ret;
int ep_loop;
ret = musb_platform_init();
if (ret < 0)
goto end;
/* Configure all the endpoint FIFO's and start usb controller */
musbr = musb_cfg.regs;
/* Initialize the endpoints */
for (ep_loop = 0; ep_loop < MAX_ENDPOINT * 2; ep_loop++) {
epinfo[ep_loop].epnum = (ep_loop / 2) + 1;
epinfo[ep_loop].epdir = ep_loop % 2; /* OUT, IN */
epinfo[ep_loop].epsize = 0;
}
musb_peri_softconnect();
ret = 0;
end:
return ret;
}

View file

@ -0,0 +1,156 @@
/*
* 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/host/omap3530_usb.c
*
* This is the unique part of its copyright :
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2009 Texas Instruments
*
* ------------------------------------------------------------------------
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <twl4030.h>
#include <twl6030.h>
#include "omap3.h"
static int platform_needs_initialization = 1;
struct musb_config musb_cfg = {
.regs = (struct musb_regs *)MENTOR_USB0_BASE,
.timeout = OMAP3_USB_TIMEOUT,
.musb_speed = 0,
};
/*
* OMAP3 USB OTG registers.
*/
struct omap3_otg_regs {
u32 revision;
u32 sysconfig;
u32 sysstatus;
u32 interfsel;
u32 simenable;
u32 forcestdby;
};
static struct omap3_otg_regs *otg;
#define OMAP3_OTG_SYSCONFIG_SMART_STANDBY_MODE 0x2000
#define OMAP3_OTG_SYSCONFIG_NO_STANDBY_MODE 0x1000
#define OMAP3_OTG_SYSCONFIG_SMART_IDLE_MODE 0x0010
#define OMAP3_OTG_SYSCONFIG_NO_IDLE_MODE 0x0008
#define OMAP3_OTG_SYSCONFIG_ENABLEWAKEUP 0x0004
#define OMAP3_OTG_SYSCONFIG_SOFTRESET 0x0002
#define OMAP3_OTG_SYSCONFIG_AUTOIDLE 0x0001
#define OMAP3_OTG_SYSSTATUS_RESETDONE 0x0001
/* OMAP4430 has an internal PHY, use it */
#ifdef CONFIG_OMAP4430
#define OMAP3_OTG_INTERFSEL_OMAP 0x0000
#else
#define OMAP3_OTG_INTERFSEL_OMAP 0x0001
#endif
#define OMAP3_OTG_FORCESTDBY_STANDBY 0x0001
#ifdef DEBUG_MUSB_OMAP3
static void musb_db_otg_regs(void)
{
u32 l;
l = readl(&otg->revision);
serial_printf("OTG_REVISION 0x%x\n", l);
l = readl(&otg->sysconfig);
serial_printf("OTG_SYSCONFIG 0x%x\n", l);
l = readl(&otg->sysstatus);
serial_printf("OTG_SYSSTATUS 0x%x\n", l);
l = readl(&otg->interfsel);
serial_printf("OTG_INTERFSEL 0x%x\n", l);
l = readl(&otg->forcestdby);
serial_printf("OTG_FORCESTDBY 0x%x\n", l);
}
#endif
int musb_platform_init(void)
{
int ret = -1;
if (platform_needs_initialization) {
u32 stdby;
/*
* OMAP3EVM uses ISP1504 phy and so
* twl4030 related init is not required.
*/
#ifdef CONFIG_TWL4030_USB
if (twl4030_usb_ulpi_init()) {
serial_printf("ERROR: %s Could not initialize PHY\n",
__PRETTY_FUNCTION__);
goto end;
}
#endif
#ifdef CONFIG_TWL6030_POWER
twl6030_usb_device_settings();
#endif
otg = (struct omap3_otg_regs *)OMAP3_OTG_BASE;
/* Set OTG to always be on */
writel(OMAP3_OTG_SYSCONFIG_NO_STANDBY_MODE |
OMAP3_OTG_SYSCONFIG_NO_IDLE_MODE, &otg->sysconfig);
/* Set the interface */
writel(OMAP3_OTG_INTERFSEL_OMAP, &otg->interfsel);
/* Clear force standby */
stdby = readl(&otg->forcestdby);
stdby &= ~OMAP3_OTG_FORCESTDBY_STANDBY;
writel(stdby, &otg->forcestdby);
#ifdef CONFIG_OMAP3_EVM
musb_cfg.extvbus = omap3_evm_need_extvbus();
#endif
#ifdef CONFIG_OMAP4430
u32 *usbotghs_control = (u32 *)(CTRL_BASE + 0x33C);
*usbotghs_control = 0x15;
#endif
platform_needs_initialization = 0;
}
ret = platform_needs_initialization;
#ifdef CONFIG_TWL4030_USB
end:
#endif
return ret;
}
void musb_platform_deinit(void)
{
/* noop */
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2009 Wind River Systems, Inc.
* Tom Rix <Tom.Rix@windriver.com>
*
* This file is based on the file drivers/usb/musb/davinci.h
*
* This is the unique part of its copyright:
*
* --------------------------------------------------------------------
*
* Copyright (c) 2008 Texas Instruments
* Author: Thomas Abraham t-abraham@ti.com, Texas Instruments
*
* --------------------------------------------------------------------
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef _MUSB_OMAP3_H_
#define _MUSB_OMAP3_H_
#include <asm/arch/cpu.h>
#include "musb_core.h"
/* Base address of MUSB registers */
#define MENTOR_USB0_BASE MUSB_BASE
/* Base address of OTG registers */
#define OMAP3_OTG_BASE (MENTOR_USB0_BASE + 0x400)
/* Timeout for USB module */
#define OMAP3_USB_TIMEOUT 0x3FFFFFF
int musb_platform_init(void);
#ifdef CONFIG_OMAP3_EVM
extern u8 omap3_evm_need_extvbus(void);
#endif
#endif /* _MUSB_OMAP3_H */

View file

@ -0,0 +1,43 @@
#
# Copyright (c) 2009 Wind River Systems, Inc.
# Tom Rix <Tom.Rix@windriver.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.
#
# 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., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
include $(TOPDIR)/config.mk
LIB := $(obj)libusb_phy.o
COBJS-$(CONFIG_TWL4030_USB) += twl4030.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
#########################################################################

View file

@ -0,0 +1,189 @@
/*
* 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.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <twl4030.h>
/* Defines for bits in registers */
#define OPMODE_MASK (3 << 3)
#define XCVRSELECT_MASK (3 << 0)
#define CARKITMODE (1 << 2)
#define OTG_ENAB (1 << 5)
#define PHYPWD (1 << 0)
#define CLOCKGATING_EN (1 << 2)
#define CLK32K_EN (1 << 1)
#define REQ_PHY_DPLL_CLK (1 << 0)
#define PHY_DPLL_CLK (1 << 0)
static int twl4030_usb_write(u8 address, u8 data)
{
int ret;
ret = twl4030_i2c_write_u8(TWL4030_CHIP_USB, data, address);
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;
ret = twl4030_i2c_read_u8(TWL4030_CHIP_USB, &data, address);
if (ret == 0)
ret = data;
else
printf("TWL4030:USB:Read[0x%x] Error %d\n", address, ret);
return ret;
}
static void twl4030_usb_ldo_init(void)
{
/* Enable writing to power configuration registers */
twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, 0xC0,
TWL4030_PM_MASTER_PROTECT_KEY);
twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, 0x0C,
TWL4030_PM_MASTER_PROTECT_KEY);
/* put VUSB3V1 LDO in active state */
twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x00,
TWL4030_PM_RECEIVER_VUSB_DEDICATED2);
/* input to VUSB3V1 LDO is from VBAT, not VBUS */
twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x14,
TWL4030_PM_RECEIVER_VUSB_DEDICATED1);
/* turn on 3.1V regulator */
twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x20,
TWL4030_PM_RECEIVER_VUSB3V1_DEV_GRP);
twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x00,
TWL4030_PM_RECEIVER_VUSB3V1_TYPE);
/* turn on 1.5V regulator */
twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x20,
TWL4030_PM_RECEIVER_VUSB1V5_DEV_GRP);
twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x00,
TWL4030_PM_RECEIVER_VUSB1V5_TYPE);
/* turn on 1.8V regulator */
twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x20,
TWL4030_PM_RECEIVER_VUSB1V8_DEV_GRP);
twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, 0x00,
TWL4030_PM_RECEIVER_VUSB1V8_TYPE);
/* disable access to power configuration registers */
twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, 0x00,
TWL4030_PM_MASTER_PROTECT_KEY);
}
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)
{
long timeout = 1000 * 1000; /* 1 sec */;
u8 clk, sts, pwr;
/* 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);
while (!(sts & PHY_DPLL_CLK) && 0 < timeout) {
udelay(10);
sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS);
timeout -= 10;
}
/* Final check */
sts = twl4030_usb_read(TWL4030_USB_PHY_CLK_CTRL_STS);
if (!(sts & PHY_DPLL_CLK)) {
printf("Error:TWL4030:USB Timeout setting PHY DPLL clock\n");
return -1;
}
/*
* 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;
}

View file

@ -0,0 +1,45 @@
#
# Copyright (C) 2011 Jana Rapava <fermata7@gmail.com>
# 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-$(CONFIG_USB_ULPI_VIEWPORT_OMAP) += omap-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
#########################################################################

View file

@ -0,0 +1,105 @@
/*
* OMAP ulpi viewport support
* Based on drivers/usb/ulpi/ulpi-viewport.c
*
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
* Author: Govindraj R <govindraj.raja@ti.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <common.h>
#include <asm/io.h>
#include <usb/ulpi.h>
#define OMAP_ULPI_WR_OPSEL (3 << 21)
#define OMAP_ULPI_ACCESS (1 << 31)
/*
* Wait for the ULPI Access to complete
*/
static int ulpi_wait(struct ulpi_viewport *ulpi_vp, u32 mask)
{
int timeout = CONFIG_USB_ULPI_TIMEOUT;
while (--timeout) {
if ((readl(ulpi_vp->viewport_addr) & mask))
return 0;
udelay(1);
}
return ULPI_ERROR;
}
/*
* Wake the ULPI PHY up for communication
*
* returns 0 on success.
*/
static int ulpi_wakeup(struct ulpi_viewport *ulpi_vp)
{
int err;
if (readl(ulpi_vp->viewport_addr) & OMAP_ULPI_ACCESS)
return 0; /* already awake */
writel(OMAP_ULPI_ACCESS, ulpi_vp->viewport_addr);
err = ulpi_wait(ulpi_vp, OMAP_ULPI_ACCESS);
if (err)
debug("ULPI wakeup timed out\n");
return err;
}
/*
* Issue a ULPI read/write request
*/
static int ulpi_request(struct ulpi_viewport *ulpi_vp, u32 value)
{
int err;
err = ulpi_wakeup(ulpi_vp);
if (err)
return err;
writel(value, ulpi_vp->viewport_addr);
err = ulpi_wait(ulpi_vp, OMAP_ULPI_ACCESS);
if (err)
debug("ULPI request timed out\n");
return err;
}
int ulpi_write(struct ulpi_viewport *ulpi_vp, u8 *reg, u32 value)
{
u32 val = ((ulpi_vp->port_num & 0xf) << 24) |
OMAP_ULPI_WR_OPSEL | ((u32)reg << 16) | (value & 0xff);
return ulpi_request(ulpi_vp, val);
}
u32 ulpi_read(struct ulpi_viewport *ulpi_vp, u8 *reg)
{
int err;
u32 val = ((ulpi_vp->port_num & 0xf) << 24) |
OMAP_ULPI_WR_OPSEL | ((u32)reg << 16);
err = ulpi_request(ulpi_vp, val);
if (err)
return err;
return readl(ulpi_vp->viewport_addr) & 0xff;
}

View file

@ -0,0 +1,120 @@
/*
* 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_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 <common.h>
#include <asm/io.h>
#include <usb/ulpi.h>
/* 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(struct ulpi_viewport *ulpi_vp, u32 mask)
{
int timeout = CONFIG_USB_ULPI_TIMEOUT;
/* Wait for the bits in mask to become zero. */
while (--timeout) {
if ((readl(ulpi_vp->viewport_addr) & mask) == 0)
return 0;
udelay(1);
}
return ULPI_ERROR;
}
/*
* Wake the ULPI PHY up for communication
*
* returns 0 on success.
*/
static int ulpi_wakeup(struct ulpi_viewport *ulpi_vp)
{
int err;
if (readl(ulpi_vp->viewport_addr) & ULPI_SS)
return 0; /* already awake */
writel(ULPI_WU, ulpi_vp->viewport_addr);
err = ulpi_wait(ulpi_vp, 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(struct ulpi_viewport *ulpi_vp, u32 value)
{
int err;
err = ulpi_wakeup(ulpi_vp);
if (err)
return err;
writel(value, ulpi_vp->viewport_addr);
err = ulpi_wait(ulpi_vp, ULPI_RWRUN);
if (err)
printf("ULPI request timed out\n");
return err;
}
int ulpi_write(struct ulpi_viewport *ulpi_vp, u8 *reg, u32 value)
{
u32 val = ULPI_RWRUN | ULPI_RWCTRL | ((u32)reg << 16) | (value & 0xff);
val |= (ulpi_vp->port_num & 0x7) << 24;
return ulpi_request(ulpi_vp, val);
}
u32 ulpi_read(struct ulpi_viewport *ulpi_vp, u8 *reg)
{
int err;
u32 val = ULPI_RWRUN | ((u32)reg << 16);
val |= (ulpi_vp->port_num & 0x7) << 24;
err = ulpi_request(ulpi_vp, val);
if (err)
return err;
return (readl(ulpi_vp->viewport_addr) >> 8) & 0xff;
}

View file

@ -0,0 +1,229 @@
/*
* 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
*
* 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 <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;
static int ulpi_integrity_check(struct ulpi_viewport *ulpi_vp)
{
u32 val, tval = ULPI_TEST_VALUE;
int err, i;
/* Use the 'special' test value to check all bits */
for (i = 0; i < 2; i++, tval <<= 1) {
err = ulpi_write(ulpi_vp, &ulpi->scratch, tval);
if (err)
return err;
val = ulpi_read(ulpi_vp, &ulpi->scratch);
if (val != tval) {
printf("ULPI integrity check failed\n");
return val;
}
}
return 0;
}
int ulpi_init(struct ulpi_viewport *ulpi_vp)
{
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_vp, 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_vp);
}
int ulpi_select_transceiver(struct ulpi_viewport *ulpi_vp, unsigned speed)
{
u32 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: %u, "
"falling back to full speed\n", __func__, speed);
}
val = ulpi_read(ulpi_vp, &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_vp, &ulpi->function_ctrl, val);
}
int ulpi_set_vbus(struct ulpi_viewport *ulpi_vp, 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_vp, reg, flags);
}
int ulpi_set_pd(struct ulpi_viewport *ulpi_vp, 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_vp, reg, val);
}
int ulpi_opmode_sel(struct ulpi_viewport *ulpi_vp, unsigned opmode)
{
u32 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: %u, "
"falling back to OpMode Normal\n", __func__, opmode);
}
val = ulpi_read(ulpi_vp, &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_vp, &ulpi->function_ctrl, val);
}
int ulpi_serial_mode_enable(struct ulpi_viewport *ulpi_vp, unsigned 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: %u\n",
__func__, smode);
return ULPI_ERROR;
}
return ulpi_write(ulpi_vp, &ulpi->iface_ctrl_set, smode);
}
int ulpi_suspend(struct ulpi_viewport *ulpi_vp)
{
int err;
err = ulpi_write(ulpi_vp, &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(struct ulpi_viewport *ulpi_vp)
{
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_vp, &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(struct ulpi_viewport *ulpi_vp)
__attribute__((weak, alias("__ulpi_reset_wait")));
int ulpi_reset(struct ulpi_viewport *ulpi_vp)
{
int err;
err = ulpi_write(ulpi_vp,
&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_vp);
}