* 'master' of git://git.denx.de/u-boot-usb: USB: efikamx: Enable USB on EfikaMX and EfikaSB USB: Add generic ULPI layer and a viewport USB: EHCI: Allow EHCI post-powerup configuration in board files USB: mx51evk: add end enable USB host support on port 1 USB: mx53loco: add end enable USB host support on port 1 USB: MX5: Add MX5 usb post-init callback USB: MX5: Abstract out mx51 USB pixmux configuration USB: MX5: add generic USB EHCI support for mx51 and mx53 USB: MX5: add helper functions to enable USB clocks usb:gadget:s5p Enable the USB Gadget framework at GONI usb:gadget:s5p USB Device Controller (UDC) implementation ehci: speed up initialization usb: add help for missing start subcommand cosmetic: remove excess whitespace from usb command help usb: align usb_endpoint_descriptor to 16-bit boundary usbtty: init endpoints prior to startup events pxa: convert pxa27x_udc to use read and write functions pxa: activate the first usb host port on pxa27x by default pxa: fix usb host register mismatch ehci-fsl: correct size of ehci caplength USB: Add usb_event_poll() to get keyboards working with EHCI USB: gadaget: add Marvell controller support USB: Fix complaints about strict aliasing in OHCI-HCD USB: Drop dead code from usb_kbd.c USB: Rework usb_kbd.c USB: Add functionality to poll the USB keyboard via control EPmaster
commit
b2eb7d9bc6
@ -0,0 +1,210 @@ |
||||
/*
|
||||
* Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> |
||||
* |
||||
* (C) Copyright 2009 Freescale Semiconductor, Inc. |
||||
* |
||||
* 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 <asm/io.h> |
||||
#include <asm/arch/imx-regs.h> |
||||
#include <asm/arch/mx5x_pins.h> |
||||
#include <asm/arch/iomux.h> |
||||
#include <asm/gpio.h> |
||||
#include <usb/ehci-fsl.h> |
||||
#include <usb/ulpi.h> |
||||
#include <errno.h> |
||||
|
||||
#include "../../drivers/usb/host/ehci.h" |
||||
|
||||
/* 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) |
||||
|
||||
/*
|
||||
* Configure the USB H1 and USB H2 IOMUX |
||||
*/ |
||||
void setup_iomux_usb(void) |
||||
{ |
||||
setup_iomux_usb_h1(); |
||||
|
||||
if (machine_is_efikasb()) |
||||
setup_iomux_usb_h2(); |
||||
|
||||
/* USB PHY reset */ |
||||
mxc_request_iomux(MX51_PIN_EIM_D27, IOMUX_CONFIG_ALT1); |
||||
mxc_iomux_set_pad(MX51_PIN_EIM_D27, PAD_CTL_PKE_ENABLE | |
||||
PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); |
||||
|
||||
/* USB HUB reset */ |
||||
mxc_request_iomux(MX51_PIN_GPIO1_5, IOMUX_CONFIG_ALT0); |
||||
mxc_iomux_set_pad(MX51_PIN_GPIO1_5, PAD_CTL_PKE_ENABLE | |
||||
PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); |
||||
|
||||
/* WIFI EN (act low) */ |
||||
mxc_request_iomux(MX51_PIN_EIM_A22, IOMUX_CONFIG_GPIO); |
||||
mxc_iomux_set_pad(MX51_PIN_EIM_A22, 0); |
||||
/* WIFI RESET */ |
||||
mxc_request_iomux(MX51_PIN_EIM_A16, IOMUX_CONFIG_GPIO); |
||||
mxc_iomux_set_pad(MX51_PIN_EIM_A16, 0); |
||||
/* BT EN (act low) */ |
||||
mxc_request_iomux(MX51_PIN_EIM_A17, IOMUX_CONFIG_GPIO); |
||||
mxc_iomux_set_pad(MX51_PIN_EIM_A17, 0); |
||||
} |
||||
|
||||
/*
|
||||
* Enable devices connected to USB BUSes |
||||
*/ |
||||
static void efika_usb_enable_devices(void) |
||||
{ |
||||
/* Enable Bluetooth */ |
||||
gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 0); |
||||
udelay(10000); |
||||
gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 1); |
||||
|
||||
/* Enable WiFi */ |
||||
gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A22), 1); |
||||
udelay(10000); |
||||
|
||||
/* Reset the WiFi chip */ |
||||
gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 0); |
||||
udelay(10000); |
||||
gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 1); |
||||
} |
||||
|
||||
/*
|
||||
* Reset USB HUB (or HUBs on EfikaSB) |
||||
*/ |
||||
static void efika_usb_hub_reset(void) |
||||
{ |
||||
/* HUB reset */ |
||||
gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); |
||||
udelay(1000); |
||||
gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 0); |
||||
udelay(1000); |
||||
gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); |
||||
} |
||||
|
||||
/*
|
||||
* Reset USB PHY (or PHYs on EfikaSB) |
||||
*/ |
||||
static void efika_usb_phy_reset(void) |
||||
{ |
||||
/* SMSC 3317 PHY reset */ |
||||
gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 0); |
||||
udelay(1000); |
||||
gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 1); |
||||
} |
||||
|
||||
static void efika_ehci_init(struct usb_ehci *ehci, uint32_t stp_gpio, |
||||
uint32_t alt0, uint32_t alt1) |
||||
{ |
||||
int ret; |
||||
struct ulpi_regs *ulpi = (struct ulpi_regs *)0; |
||||
|
||||
mxc_request_iomux(stp_gpio, alt0); |
||||
mxc_iomux_set_pad(stp_gpio, PAD_CTL_DRV_HIGH | |
||||
PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST); |
||||
gpio_direction_output(IOMUX_TO_GPIO(stp_gpio), 0); |
||||
udelay(1000); |
||||
gpio_set_value(IOMUX_TO_GPIO(stp_gpio), 1); |
||||
udelay(1000); |
||||
|
||||
mxc_request_iomux(stp_gpio, alt1); |
||||
mxc_iomux_set_pad(stp_gpio, USB_PAD_CONFIG); |
||||
udelay(10000); |
||||
|
||||
ret = ulpi_init((u32)&ehci->ulpi_viewpoint); |
||||
if (ret) { |
||||
printf("Efika USB ULPI initialization failed\n"); |
||||
return; |
||||
} |
||||
|
||||
/* ULPI set flags */ |
||||
ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl, |
||||
ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN | |
||||
ULPI_OTG_EXTVBUSIND); |
||||
ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->function_ctrl, |
||||
ULPI_FC_FULL_SPEED | ULPI_FC_OPMODE_NORMAL | |
||||
ULPI_FC_SUSPENDM); |
||||
ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->iface_ctrl, 0); |
||||
|
||||
/* Set VBus */ |
||||
ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, |
||||
ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); |
||||
|
||||
/*
|
||||
* Set VBusChrg |
||||
* |
||||
* NOTE: This violates USB specification, but otherwise, USB on Efika |
||||
* doesn't work. |
||||
*/ |
||||
ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, |
||||
ULPI_OTG_CHRGVBUS); |
||||
} |
||||
|
||||
int board_ehci_hcd_init(int port) |
||||
{ |
||||
/* Init iMX51 EHCI */ |
||||
efika_usb_phy_reset(); |
||||
efika_usb_hub_reset(); |
||||
efika_usb_enable_devices(); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) |
||||
{ |
||||
uint32_t port = OTG_BASE_ADDR + (0x200 * CONFIG_MXC_USB_PORT); |
||||
struct usb_ehci *ehci = (struct usb_ehci *)port; |
||||
struct ulpi_regs *ulpi = (struct ulpi_regs *)0; |
||||
|
||||
ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, |
||||
ULPI_OTG_CHRGVBUS); |
||||
|
||||
wait_ms(50); |
||||
|
||||
/* terminate the reset */ |
||||
*reg = ehci_readl(status_reg); |
||||
*reg |= EHCI_PS_PE; |
||||
} |
||||
|
||||
void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) |
||||
{ |
||||
uint32_t tmp; |
||||
|
||||
if (port == 0) { |
||||
/* Adjust UTMI PHY frequency to 24MHz */ |
||||
tmp = readl(OTG_BASE_ADDR + 0x80c); |
||||
tmp = (tmp & ~0x3) | 0x01; |
||||
writel(tmp, OTG_BASE_ADDR + 0x80c); |
||||
} else if (port == 1) { |
||||
efika_ehci_init(ehci, MX51_PIN_USBH1_STP, |
||||
IOMUX_CONFIG_ALT2, IOMUX_CONFIG_ALT0); |
||||
} else if ((port == 2) && machine_is_efikasb()) { |
||||
efika_ehci_init(ehci, MX51_PIN_EIM_A26, |
||||
IOMUX_CONFIG_ALT1, IOMUX_CONFIG_ALT2); |
||||
} |
||||
|
||||
if (port) |
||||
mdelay(10); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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; |
||||
} |
@ -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 |
@ -0,0 +1,892 @@ |
||||
/*
|
||||
* 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 |
||||
* |
||||
*/ |
||||
|
||||
#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/io.h> |
||||
|
||||
#include <asm/mach-types.h> |
||||
#include <asm/arch/gpio.h> |
||||
|
||||
#include "regs-otg.h" |
||||
#include <usb/s3c_udc.h> |
||||
#include <usb/lin_gadget_compat.h> |
||||
|
||||
/***********************************************************/ |
||||
|
||||
#define OTG_DMA_MODE 1 |
||||
|
||||
#undef DEBUG_S3C_UDC_SETUP |
||||
#undef DEBUG_S3C_UDC_EP0 |
||||
#undef DEBUG_S3C_UDC_ISR |
||||
#undef DEBUG_S3C_UDC_OUT_EP |
||||
#undef DEBUG_S3C_UDC_IN_EP |
||||
#undef DEBUG_S3C_UDC |
||||
|
||||
/* #define DEBUG_S3C_UDC_SETUP */ |
||||
/* #define DEBUG_S3C_UDC_EP0 */ |
||||
/* #define DEBUG_S3C_UDC_ISR */ |
||||
/* #define DEBUG_S3C_UDC_OUT_EP */ |
||||
/* #define DEBUG_S3C_UDC_IN_EP */ |
||||
/* #define DEBUG_S3C_UDC */ |
||||
|
||||
#include <usb/s3c_udc.h> |
||||
|
||||
#define EP0_CON 0 |
||||
#define EP_MASK 0xF |
||||
|
||||
#if defined(DEBUG_S3C_UDC_SETUP) || defined(DEBUG_S3C_UDC_ISR) \ |
||||
|| defined(DEBUG_S3C_UDC_OUT_EP) |
||||
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", |
||||
}; |
||||
#endif |
||||
|
||||
#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); |
||||
|
||||
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_SETUP("%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_SETUP("%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_SETUP("%s: %p\n", __func__, dev); |
||||
|
||||
otg_phy_init(dev); |
||||
reconfig_usbd(); |
||||
|
||||
DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", |
||||
readl(®->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_SETUP("%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_SETUP("%s: bind to driver --> error %d\n", |
||||
dev->gadget.name, retval); |
||||
dev->driver = 0; |
||||
return retval; |
||||
} |
||||
|
||||
enable_irq(IRQ_OTG); |
||||
|
||||
DEBUG_SETUP("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_S3C_UDC |
||||
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, ®->grstctl); |
||||
|
||||
DEBUG(2, "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**/ |
||||
®->gusbcfg); |
||||
|
||||
/* 3. Put the OTG device core in the disconnected state.*/ |
||||
uTemp = readl(®->dctl); |
||||
uTemp |= SOFT_DISCONNECT; |
||||
writel(uTemp, ®->dctl); |
||||
|
||||
udelay(20); |
||||
|
||||
/* 4. Make the OTG device core exit from the disconnected state.*/ |
||||
uTemp = readl(®->dctl); |
||||
uTemp = uTemp & ~SOFT_DISCONNECT; |
||||
writel(uTemp, ®->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, ®->dcfg); |
||||
|
||||
mdelay(1); |
||||
|
||||
/* 6. Unmask the core interrupts*/ |
||||
writel(GINTMSK_INIT, ®->gintmsk); |
||||
|
||||
/* 7. Set NAK bit of EP0, EP1, EP2*/ |
||||
writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[EP0_CON].doepctl); |
||||
writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[EP0_CON].diepctl); |
||||
|
||||
for (i = 1; i < S3C_MAX_ENDPOINTS; i++) { |
||||
writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[i].doepctl); |
||||
writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[i].diepctl); |
||||
} |
||||
|
||||
/* 8. Unmask EPO interrupts*/ |
||||
writel(((1 << EP0_CON) << DAINT_OUT_BIT) |
||||
| (1 << EP0_CON), ®->daintmsk); |
||||
|
||||
/* 9. Unmask device OUT EP common interrupts*/ |
||||
writel(DOEPMSK_INIT, ®->doepmsk); |
||||
|
||||
/* 10. Unmask device IN EP common interrupts*/ |
||||
writel(DIEPMSK_INIT, ®->diepmsk); |
||||
|
||||
/* 11. Set Rx FIFO Size (in 32-bit words) */ |
||||
writel(RX_FIFO_SIZE >> 2, ®->grxfsiz); |
||||
|
||||
/* 12. Set Non Periodic Tx FIFO Size */ |
||||
writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0, |
||||
®->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, |
||||
®->dieptxf[i-1]); |
||||
|
||||
/* Flush the RX FIFO */ |
||||
writel(RX_FIFO_FLUSH, ®->grstctl); |
||||
while (readl(®->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, ®->grstctl); |
||||
writel(TX_FIFO_FLUSH_ALL | TX_FIFO_FLUSH, ®->grstctl); |
||||
while (readl(®->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, |
||||
®->out_endp[EP0_CON].doepctl); |
||||
|
||||
/* 14. Initialize OTG Link Core.*/ |
||||
writel(GAHBCFG_INIT, ®->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(®->in_endp[EP0_CON].diepctl); |
||||
writel(ep_ctrl|(0<<0), ®->in_endp[EP0_CON].diepctl); |
||||
|
||||
/* EP0 - Control OUT (64 bytes)*/ |
||||
ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); |
||||
writel(ep_ctrl|(0<<0), ®->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(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(desc->wMaxPacketSize) != ep_maxpacket(ep)) |
||||
|| !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(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 = kmalloc(sizeof *req, gfp_flags); |
||||
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] = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL); |
||||
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(®->gintsts); |
||||
u32 gintmsk = readl(®->gintmsk); |
||||
|
||||
if (intr_status & gintmsk) |
||||
return s3c_udc_irq(1, (void *)the_controller); |
||||
return 0; |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,255 @@ |
||||
/*
|
||||
* 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; |
||||
} |
||||
|
||||
|
@ -0,0 +1,44 @@ |
||||
#
|
||||
# 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 := $(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 |
||||
|
||||
#########################################################################
|
@ -0,0 +1,118 @@ |
||||
/*
|
||||
* 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(u32 ulpi_viewport, u32 mask) |
||||
{ |
||||
int timeout = CONFIG_USB_ULPI_TIMEOUT; |
||||
|
||||
/* Wait for the bits in mask to become zero. */ |
||||
while (--timeout) { |
||||
if ((readl(ulpi_viewport) & mask) == 0) |
||||
return 0; |
||||
|
||||
udelay(1); |
||||
} |
||||
|
||||
return ULPI_ERROR; |
||||
} |
||||
|
||||
/*
|
||||
* Wake the ULPI PHY up for communication |
||||
* |
||||
* returns 0 on success. |
||||
*/ |
||||
static int ulpi_wakeup(u32 ulpi_viewport) |
||||
{ |
||||
int err; |
||||
|
||||
if (readl(ulpi_viewport) & ULPI_SS) |
||||
return 0; /* already awake */ |
||||
|
||||
writel(ULPI_WU, ulpi_viewport); |
||||
|
||||
err = ulpi_wait(ulpi_viewport, ULPI_WU); |
||||
if (err) |
||||
printf("ULPI wakeup timed out\n"); |
||||
|
||||
return err; |
||||
} |
||||
|
||||
/*
|
||||
* Issue a ULPI read/write request |
||||
* |
||||
* @value - the ULPI request |
||||
*/ |
||||
static int ulpi_request(u32 ulpi_viewport, u32 value) |
||||
{ |
||||
int err; |
||||
|
||||
err = ulpi_wakeup(ulpi_viewport); |
||||
if (err) |
||||
return err; |
||||
|
||||
writel(value, ulpi_viewport); |
||||
|
||||
err = ulpi_wait(ulpi_viewport, ULPI_RWRUN); |
||||
if (err) |
||||
printf("ULPI request timed out\n"); |
||||
|
||||
return err; |
||||
} |
||||
|
||||
u32 ulpi_write(u32 ulpi_viewport, u8 *reg, u32 value) |
||||
{ |
||||
u32 val = ULPI_RWRUN | ULPI_RWCTRL | ((u32)reg << 16) | (value & 0xff); |
||||
|
||||
return ulpi_request(ulpi_viewport, val); |
||||
} |
||||
|
||||
u32 ulpi_read(u32 ulpi_viewport, u8 *reg) |
||||
{ |
||||
u32 err; |
||||
u32 val = ULPI_RWRUN | ((u32)reg << 16); |
||||
|
||||
err = ulpi_request(ulpi_viewport, val); |
||||
if (err) |
||||
return err; |
||||
|
||||
return (readl(ulpi_viewport) >> 8) & 0xff; |
||||
} |
@ -0,0 +1,227 @@ |
||||
/*
|
||||
* 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(u32 ulpi_viewport) |
||||
{ |
||||
u32 err, val, tval = ULPI_TEST_VALUE; |
||||
int i; |
||||
|
||||
/* Use the 'special' test value to check all bits */ |
||||
for (i = 0; i < 2; i++, tval <<= 1) { |
||||
err = ulpi_write(ulpi_viewport, &ulpi->scratch, tval); |
||||
if (err) |
||||
return err; |
||||
|
||||
val = ulpi_read(ulpi_viewport, &ulpi->scratch); |
||||
if (val != tval) { |
||||
printf("ULPI integrity check failed\n"); |
||||
return val; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int ulpi_init(u32 ulpi_viewport) |
||||
{ |
||||
u32 val, id = 0; |
||||
u8 *reg = &ulpi->product_id_high; |
||||
int i; |
||||
|
||||
/* Assemble ID from four ULPI ID registers (8 bits each). */ |
||||
for (i = 0; i < ULPI_ID_REGS_COUNT; i++) { |
||||
val = ulpi_read(ulpi_viewport, reg - i); |
||||
if (val == ULPI_ERROR) |
||||
return val; |
||||
|
||||
id = (id << 8) | val; |
||||
} |
||||
|
||||
/* Split ID into vendor and product ID. */ |
||||
debug("ULPI transceiver ID 0x%04x:0x%04x\n", id >> 16, id & 0xffff); |
||||
|
||||
return ulpi_integrity_check(ulpi_viewport); |
||||
} |
||||
|
||||
int ulpi_select_transceiver(u32 ulpi_viewport, u8 speed) |
||||
{ |
||||
u8 tspeed = ULPI_FC_FULL_SPEED; |
||||
u32 val; |
||||
|
||||
switch (speed) { |
||||
case ULPI_FC_HIGH_SPEED: |
||||
case ULPI_FC_FULL_SPEED: |
||||
case ULPI_FC_LOW_SPEED: |
||||
case ULPI_FC_FS4LS: |
||||
tspeed = speed; |
||||
break; |
||||
default: |
||||
printf("ULPI: %s: wrong transceiver speed specified, " |
||||
"falling back to full speed\n", __func__); |
||||
} |
||||
|
||||
val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); |
||||
if (val == ULPI_ERROR) |
||||
return val; |
||||
|
||||
/* clear the previous speed setting */ |
||||
val = (val & ~ULPI_FC_XCVRSEL_MASK) | tspeed; |
||||
|
||||
return ulpi_write(ulpi_viewport, &ulpi->function_ctrl, val); |
||||
} |
||||
|
||||
int ulpi_set_vbus(u32 ulpi_viewport, int on, int ext_power, int ext_ind) |
||||
{ |
||||
u32 flags = ULPI_OTG_DRVVBUS; |
||||
u8 *reg = on ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; |
||||
|
||||
if (ext_power) |
||||
flags |= ULPI_OTG_DRVVBUS_EXT; |
||||
if (ext_ind) |
||||
flags |= ULPI_OTG_EXTVBUSIND; |
||||
|
||||
return ulpi_write(ulpi_viewport, reg, flags); |
||||
} |
||||
|
||||
int ulpi_set_pd(u32 ulpi_viewport, int enable) |
||||
{ |
||||
u32 val = ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN; |
||||
u8 *reg = enable ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; |
||||
|
||||
return ulpi_write(ulpi_viewport, reg, val); |
||||
} |
||||
|
||||
int ulpi_opmode_sel(u32 ulpi_viewport, u8 opmode) |
||||
{ |
||||
u8 topmode = ULPI_FC_OPMODE_NORMAL; |
||||
u32 val; |
||||
|
||||
switch (opmode) { |
||||
case ULPI_FC_OPMODE_NORMAL: |
||||
case ULPI_FC_OPMODE_NONDRIVING: |
||||
case ULPI_FC_OPMODE_DISABLE_NRZI: |
||||
case ULPI_FC_OPMODE_NOSYNC_NOEOP: |
||||
topmode = opmode; |
||||
break; |
||||
default: |
||||
printf("ULPI: %s: wrong OpMode specified, " |
||||
"falling back to OpMode Normal\n", __func__); |
||||
} |
||||
|
||||
val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); |
||||
if (val == ULPI_ERROR) |
||||
return val; |
||||
|
||||
/* clear the previous opmode setting */ |
||||
val = (val & ~ULPI_FC_OPMODE_MASK) | topmode; |
||||
|
||||
return ulpi_write(ulpi_viewport, &ulpi->function_ctrl, val); |
||||
} |
||||
|
||||
int ulpi_serial_mode_enable(u32 ulpi_viewport, u8 smode) |
||||
{ |
||||
switch (smode) { |
||||
case ULPI_IFACE_6_PIN_SERIAL_MODE: |
||||
case ULPI_IFACE_3_PIN_SERIAL_MODE: |
||||
break; |
||||
default: |
||||
printf("ULPI: %s: unrecognized Serial Mode specified\n", |
||||
__func__); |
||||
return ULPI_ERROR; |
||||
} |
||||
|
||||
return ulpi_write(ulpi_viewport, &ulpi->iface_ctrl_set, smode); |
||||
} |
||||
|
||||
int ulpi_suspend(u32 ulpi_viewport) |
||||
{ |
||||
u32 err; |
||||
|
||||
err = ulpi_write(ulpi_viewport, &ulpi->function_ctrl_clear, |
||||
ULPI_FC_SUSPENDM); |
||||
if (err) |
||||
printf("ULPI: %s: failed writing the suspend bit\n", __func__); |
||||
|
||||
return err; |
||||
} |
||||
|
||||
/*
|
||||
* Wait for ULPI PHY reset to complete. |
||||
* Actual wait for reset must be done in a view port specific way, |
||||
* because it involves checking the DIR line. |
||||
*/ |
||||
static int __ulpi_reset_wait(u32 ulpi_viewport) |
||||
{ |
||||
u32 val; |
||||
int timeout = CONFIG_USB_ULPI_TIMEOUT; |
||||
|
||||
/* Wait for the RESET bit to become zero */ |
||||
while (--timeout) { |
||||
/*
|
||||
* This function is generic and suppose to work |
||||
* with any viewport, so we cheat here and don't check |
||||
* for the error of ulpi_read(), if there is one, then |
||||
* there will be a timeout. |
||||
*/ |
||||
val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); |
||||
if (!(val & ULPI_FC_RESET)) |
||||
return 0; |
||||
|
||||
udelay(1); |
||||
} |
||||
|
||||
printf("ULPI: %s: reset timed out\n", __func__); |
||||
|
||||
return ULPI_ERROR; |
||||
} |
||||
int ulpi_reset_wait(u32) __attribute__((weak, alias("__ulpi_reset_wait"))); |
||||
|
||||
int ulpi_reset(u32 ulpi_viewport) |
||||
{ |
||||
u32 err; |
||||
|
||||
err = ulpi_write(ulpi_viewport, |
||||
&ulpi->function_ctrl_set, ULPI_FC_RESET); |
||||
if (err) { |
||||
printf("ULPI: %s: failed writing reset bit\n", __func__); |
||||
return err; |
||||
} |
||||
|
||||
return ulpi_reset_wait(ulpi_viewport); |
||||
} |
@ -0,0 +1,62 @@ |
||||
/*
|
||||
* Copyright (c) 2011 Samsung Electronics |
||||
* Lukasz Majewski <l.majewski@samsung.com> |
||||
* |
||||
* This is a Linux kernel compatibility layer for USB Gadget |
||||
* |
||||
* 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 __LIN_COMPAT_H__ |
||||
#define __LIN_COMPAT_H__ |
||||
|
||||
/* common */ |
||||
#define spin_lock_init(...) |
||||
#define spin_lock(...) |
||||
#define spin_lock_irqsave(lock, flags) do {flags = 1; } while (0) |
||||
#define spin_unlock(...) |
||||
#define spin_unlock_irqrestore(lock, flags) do {flags = 0; } while (0) |
||||
#define disable_irq(...) |
||||
#define enable_irq(...) |
||||
|
||||
#define mutex_init(...) |
||||
#define mutex_lock(...) |
||||
#define mutex_unlock(...) |
||||
|
||||
#define WARN_ON(x) if (x) {printf("WARNING in %s line %d\n" \ |
||||
, __FILE__, __LINE__); } |
||||
|
||||
#define KERN_WARNING |
||||
#define KERN_ERR |
||||
#define KERN_NOTICE |
||||
#define KERN_DEBUG |
||||
|
||||
#define GFP_KERNEL 0 |
||||
|
||||
#define IRQ_HANDLED 1 |
||||
|
||||
#define ENOTSUPP 524 /* Operation is not supported */ |
||||
|
||||
#define kmalloc(size, type) memalign(CONFIG_SYS_CACHELINE_SIZE, size) |
||||
#define kfree(addr) free(addr) |
||||
#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) |
||||
|
||||
#define __iomem |
||||
#define min_t min |
||||
#define dma_cache_maint(addr, size, mode) cache_flush() |
||||
void cache_flush(void); |
||||
|
||||
#endif /* __LIN_COMPAT_H__ */ |
@ -0,0 +1,151 @@ |
||||
/*
|
||||
* Copyright 2011, Marvell Semiconductor Inc. |
||||
* Lei Wen <leiwen@marvell.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 |
||||
*/ |
||||
|
||||
|
||||
#ifndef __MV_UDC_H__ |
||||
#define __MV_UDC_H__ |
||||
|
||||
#include <asm/byteorder.h> |
||||
#include <asm/errno.h> |
||||
#include <linux/usb/ch9.h> |
||||
#include <linux/usb/gadget.h> |
||||
|
||||
/* Endpoint 0 states */ |
||||
#define EP0_IDLE 0 |
||||
#define EP0_IN_DATA 1 |
||||
#define EP0_OUT_DATA 2 |
||||
#define EP0_XFER_COMPLETE 3 |
||||
|
||||
|
||||
/* Endpoint parameters */ |
||||
#define MAX_ENDPOINTS 4 |
||||
#define EP_MAX_PACKET_SIZE 0x200 |
||||
|
||||
#define EP0_MAX_PACKET_SIZE 64 |
||||
#define UDC_OUT_ENDPOINT 0x02 |
||||
#define UDC_OUT_PACKET_SIZE EP_MAX_PACKET_SIZE |
||||
#define UDC_IN_ENDPOINT 0x01 |
||||
#define UDC_IN_PACKET_SIZE EP_MAX_PACKET_SIZE |
||||
#define UDC_INT_ENDPOINT 0x05 |
||||
#define UDC_INT_PACKET_SIZE EP_MAX_PACKET_SIZE |
||||
#define UDC_BULK_PACKET_SIZE EP_MAX_PACKET_SIZE |
||||
|
||||
#define NUM_ENDPOINTS 6 |
||||
#define REQ_COUNT 12 |
||||
struct mv_ep { |
||||
struct usb_ep ep; |
||||
struct usb_request req; |
||||
struct list_head queue; |
||||
const struct usb_endpoint_descriptor *desc; |
||||
}; |
||||
|
||||
struct mv_udc { |
||||
u32 pad0[80]; |
||||
#define MICRO_8FRAME 0x8 |
||||
#define USBCMD_ITC(x) (((x > 0xff) ? 0xff : x) << 16) |
||||
#define USBCMD_FS2 (1 << 15) |
||||
#define USBCMD_RST (1 << 1) |
||||
#define USBCMD_RUN (1) |
||||
u32 usbcmd; /* 0x140 */ |
||||
#define STS_SLI (1 << 8) |
||||
#define STS_URI (1 << 6) |
||||
#define STS_PCI (1 << 2) |
||||
#define STS_UEI (1 << 1) |
||||
#define STS_UI (1 << 0) |
||||
u32 usbsts; /* 0x144 */ |
||||
u32 pad1[3]; |
||||
u32 devaddr; /* 0x154 */ |
||||
u32 epinitaddr; /* 0x158 */ |
||||
u32 pad2[10]; |
||||
#define PTS_ENABLE 2 |
||||
#define PTS(x) ((x & 0x3) << 30) |
||||
#define PFSC (1 << 24) |
||||
u32 portsc; /* 0x184 */ |
||||
u32 pad3[8]; |
||||
#define USBMODE_DEVICE 2 |
||||
u32 usbmode; /* 0x1a8 */ |
||||
u32 epstat; /* 0x1ac */ |
||||
#define EPT_TX(x) (1 << ((x & 0xffff) + 16)) |
||||
#define EPT_RX(x) (1 << (x & 0xffff)) |
||||
u32 epprime; /* 0x1b0 */ |
||||
u32 epflush; /* 0x1b4 */ |
||||
u32 pad4; |
||||
u32 epcomp; /* 0x1bc */ |
||||
#define CTRL_TXE (1 << 23) |
||||
#define CTRL_TXR (1 << 22) |
||||
#define CTRL_RXE (1 << 7) |
||||
#define CTRL_RXR (1 << 6) |
||||
#define CTRL_TXT_BULK (2 << 18) |
||||
#define CTRL_RXT_BULK (2 << 2) |
||||
u32 epctrl[16]; /* 0x1c0 */ |
||||
}; |
||||
|
||||
struct mv_drv { |
||||
struct usb_gadget gadget; |
||||
struct usb_gadget_driver *driver; |
||||
struct mv_udc *udc; |
||||
}; |
||||
|
||||
struct ept_queue_head { |
||||
unsigned config; |
||||
unsigned current; /* read-only */ |
||||
|
||||
unsigned next; |
||||
unsigned info; |
||||
unsigned page0; |
||||
unsigned page1; |
||||
unsigned page2; |
||||
unsigned page3; |
||||
unsigned page4; |
||||
unsigned reserved_0; |
||||
|
||||
unsigned char setup_data[8]; |
||||
|
||||
unsigned reserved_1; |
||||
unsigned reserved_2; |
||||
unsigned reserved_3; |
||||
unsigned reserved_4; |
||||
}; |
||||
|
||||
#define CONFIG_MAX_PKT(n) ((n) << 16) |
||||
#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */ |
||||
#define CONFIG_IOS (1 << 15) /* IRQ on setup */ |
||||
|
||||
struct ept_queue_item { |
||||
unsigned next; |
||||
unsigned info; |
||||
unsigned page0; |
||||
unsigned page1; |
||||
unsigned page2; |
||||
unsigned page3; |
||||
unsigned page4; |
||||
unsigned reserved; |
||||
}; |
||||
|
||||
#define TERMINATE 1 |
||||
#define INFO_BYTES(n) ((n) << 16) |
||||
#define INFO_IOC (1 << 15) |
||||
#define INFO_ACTIVE (1 << 7) |
||||
#define INFO_HALTED (1 << 6) |
||||
#define INFO_BUFFER_ERROR (1 << 5) |
||||
#define INFO_TX_ERROR (1 << 3) |
||||
|
||||
extern int usb_lowlevel_init(void); |
||||
#endif /* __MV_UDC_H__ */ |
@ -0,0 +1,175 @@ |
||||
/*
|
||||
* drivers/usb/gadget/s3c_udc.h |
||||
* Samsung S3C on-chip full/high speed USB device controllers |
||||
* Copyright (C) 2005 for Samsung Electronics |
||||
* |
||||
* 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 __S3C_USB_GADGET |
||||
#define __S3C_USB_GADGET |
||||
|
||||
#include <asm/errno.h> |
||||
#include <linux/usb/ch9.h> |
||||
#include <linux/usb/gadget.h> |
||||
#include <linux/list.h> |
||||
#include <usb/lin_gadget_compat.h> |
||||
|
||||
#define PHY0_SLEEP (1 << 5) |
||||
|
||||
/*-------------------------------------------------------------------------*/ |
||||
/* DMA bounce buffer size, 16K is enough even for mass storage */ |
||||
#define DMA_BUFFER_SIZE (4096*4) |
||||
|
||||
#define EP0_FIFO_SIZE 64 |
||||
#define EP_FIFO_SIZE 512 |
||||
#define EP_FIFO_SIZE2 1024 |
||||
/* ep0-control, ep1in-bulk, ep2out-bulk, ep3in-int */ |
||||
#define S3C_MAX_ENDPOINTS 4 |
||||
#define S3C_MAX_HW_ENDPOINTS 16 |
||||
|
||||
#define WAIT_FOR_SETUP 0 |
||||
#define DATA_STATE_XMIT 1 |
||||
#define DATA_STATE_NEED_ZLP 2 |
||||
#define WAIT_FOR_OUT_STATUS 3 |
||||
#define DATA_STATE_RECV 4 |
||||
#define WAIT_FOR_COMPLETE 5 |
||||
#define WAIT_FOR_OUT_COMPLETE 6 |
||||
#define WAIT_FOR_IN_COMPLETE 7 |
||||
#define WAIT_FOR_NULL_COMPLETE 8 |
||||
|
||||
#define TEST_J_SEL 0x1 |
||||
#define TEST_K_SEL 0x2 |
||||
#define TEST_SE0_NAK_SEL 0x3 |
||||
#define TEST_PACKET_SEL 0x4 |
||||
#define TEST_FORCE_ENABLE_SEL 0x5 |
||||
|
||||
/* ************************************************************************* */ |
||||
/* IO
|
||||
*/ |
||||
|
||||
enum ep_type { |
||||
ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt |
||||
}; |
||||
|
||||
struct s3c_ep { |
||||
struct usb_ep ep; |
||||
struct s3c_udc *dev; |
||||
|
||||
const struct usb_endpoint_descriptor *desc; |
||||
struct list_head queue; |
||||
unsigned long pio_irqs; |
||||
int len; |
||||
void *dma_buf; |
||||
|
||||
u8 stopped; |
||||
u8 bEndpointAddress; |
||||
u8 bmAttributes; |
||||
|
||||
enum ep_type ep_type; |
||||
int fifo_num; |
||||
}; |
||||
|
||||
struct s3c_request { |
||||
struct usb_request req; |
||||
struct list_head queue; |
||||
}; |
||||
|
||||
struct s3c_udc { |
||||
struct usb_gadget gadget; |
||||
struct usb_gadget_driver *driver; |
||||
|
||||
struct s3c_plat_otg_data *pdata; |
||||
|
||||
void *dma_buf[S3C_MAX_ENDPOINTS+1]; |
||||
dma_addr_t dma_addr[S3C_MAX_ENDPOINTS+1]; |
||||
|
||||
int ep0state; |
||||
struct s3c_ep ep[S3C_MAX_ENDPOINTS]; |
||||
|
||||
unsigned char usb_address; |
||||
|
||||
unsigned req_pending:1, req_std:1, req_config:1; |
||||
}; |
||||
|
||||
extern struct s3c_udc *the_controller; |
||||
|
||||
#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN) |
||||
#define ep_index(EP) ((EP)->bEndpointAddress&0xF) |
||||
#define ep_maxpacket(EP) ((EP)->ep.maxpacket) |
||||
|
||||
/*-------------------------------------------------------------------------*/ |
||||
/* #define DEBUG_UDC */ |
||||
#ifdef DEBUG_UDC |
||||
#define DBG(stuff...) printf("udc: " stuff) |
||||
#else |
||||
#define DBG(stuff...) do {} while (0) |
||||
#endif |
||||
|
||||
#ifdef DEBUG_S3C_UDC_SETUP |
||||
#define DEBUG_SETUP(fmt, args...) printk(fmt, ##args) |
||||
#else |
||||
#define DEBUG_SETUP(fmt, args...) do {} while (0) |
||||
#endif |
||||
|
||||
#ifdef DEBUG_S3C_UDC_EP0 |
||||
#define DEBUG_EP0(fmt, args...) printk(fmt, ##args) |
||||
#else |
||||
#define DEBUG_EP0(fmt, args...) do {} while (0) |
||||
#endif |
||||
|
||||
#ifdef DEBUG_S3C_UDC |
||||
#define DEBUG(fmt, args...) printk(fmt, ##args) |
||||
#else |
||||
#define DEBUG(fmt, args...) do {} while (0) |
||||
#endif |
||||
|
||||
#ifdef DEBUG_S3C_UDC_ISR |
||||
#define DEBUG_ISR(fmt, args...) printk(fmt, ##args) |
||||
#else |
||||
#define DEBUG_ISR(fmt, args...) do {} while (0) |
||||
#endif |
||||
|
||||
#ifdef DEBUG_S3C_UDC_OUT_EP |
||||
#define DEBUG_OUT_EP(fmt, args...) printk(fmt, ##args) |
||||
#else |
||||
#define DEBUG_OUT_EP(fmt, args...) do {} while (0) |
||||
#endif |
||||
|
||||
#ifdef DEBUG_S3C_UDC_IN_EP |
||||
#define DEBUG_IN_EP(fmt, args...) printk(fmt, ##args) |
||||
#else |
||||
#define DEBUG_IN_EP(fmt, args...) do {} while (0) |
||||
#endif |
||||
|
||||
#define ERR(stuff...) printf("ERR udc: " stuff) |
||||
#define WARN(stuff...) printf("WARNING udc: " stuff) |
||||
#define INFO(stuff...) printf("INFO udc: " stuff) |
||||
|
||||
extern void otg_phy_init(struct s3c_udc *dev); |
||||
extern void otg_phy_off(struct s3c_udc *dev); |
||||
|
||||
extern void s3c_udc_ep_set_stall(struct s3c_ep *ep); |
||||
extern int s3c_udc_probe(struct s3c_plat_otg_data *pdata); |
||||
|
||||
struct s3c_plat_otg_data { |
||||
int (*phy_control)(int on); |
||||
unsigned int regs_phy; |
||||
unsigned int regs_otg; |
||||
unsigned int usb_phy_ctrl; |
||||
unsigned int usb_flags; |
||||
}; |
||||
#endif |
@ -0,0 +1,298 @@ |
||||
/*
|
||||
* Generic ULPI interface. |
||||
* |
||||
* 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> |
||||
* |
||||
* Register offsets taken from: |
||||
* linux/include/linux/usb/ulpi.h |
||||
* |
||||
* Original Copyrights follow: |
||||
* Copyright (C) 2010 Nokia Corporation |
||||
* |
||||
* This software is distributed under the terms of the GNU General |
||||
* Public License ("GPL") as published by the Free Software Foundation, |
||||
* version 2 of that License. |
||||
*/ |
||||
|
||||
#ifndef __USB_ULPI_H__ |
||||
#define __USB_ULPI_H__ |
||||
|
||||
#define ULPI_ERROR (1 << 8) /* overflow from any register value */ |
||||
|
||||
#ifndef CONFIG_USB_ULPI_TIMEOUT |
||||
#define CONFIG_USB_ULPI_TIMEOUT 1000 /* timeout in us */ |
||||
#endif |
||||
|
||||
/*
|
||||
* Initialize the ULPI transciever and check the interface integrity. |
||||
* @ulpi_viewport - the address of the ULPI viewport register. |
||||
* |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
*/ |
||||
int ulpi_init(u32 ulpi_viewport); |
||||
|
||||
/*
|
||||
* Select transceiver speed. |
||||
* @speed - ULPI_FC_HIGH_SPEED, ULPI_FC_FULL_SPEED (default), |
||||
* ULPI_FC_LOW_SPEED, ULPI_FC_FS4LS |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
*/ |
||||
int ulpi_select_transceiver(u32 ulpi_viewport, u8 speed); |
||||
|
||||
/*
|
||||
* Enable/disable VBUS. |
||||
* @ext_power - external VBUS supply is used (default is false) |
||||
* @ext_indicator - external VBUS over-current indicator is used |
||||
* |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
*/ |
||||
int ulpi_enable_vbus(u32 ulpi_viewport, int on, int ext_power, int ext_ind); |
||||
|
||||
/*
|
||||
* Enable/disable pull-down resistors on D+ and D- USB lines. |
||||
* |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
*/ |
||||
int ulpi_set_pd(u32 ulpi_viewport, int enable); |
||||
|
||||
/*
|
||||
* Select OpMode. |
||||
* @opmode - ULPI_FC_OPMODE_NORMAL (default), ULPI_FC_OPMODE_NONDRIVING, |
||||
* ULPI_FC_OPMODE_DISABLE_NRZI, ULPI_FC_OPMODE_NOSYNC_NOEOP |
||||
* |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
*/ |
||||
int ulpi_opmode_sel(u32 ulpi_viewport, u8 opmode); |
||||
|
||||
/*
|
||||
* Switch to Serial Mode. |
||||
* @smode - ULPI_IFACE_6_PIN_SERIAL_MODE or ULPI_IFACE_3_PIN_SERIAL_MODE |
||||
* |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
* |
||||
* Notes: |
||||
* Switches immediately to Serial Mode. |
||||
* To return from Serial Mode, STP line needs to be asserted. |
||||
*/ |
||||
int ulpi_serial_mode_enable(u32 ulpi_viewport, u8 smode); |
||||
|
||||
/*
|
||||
* Put PHY into low power mode. |
||||
* |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
* |
||||
* Notes: |
||||
* STP line must be driven low to keep the PHY in suspend. |
||||
* To resume the PHY, STP line needs to be asserted. |
||||
*/ |
||||
int ulpi_suspend(u32 ulpi_viewport); |
||||
|
||||
/*
|
||||
* Reset the transceiver. ULPI interface and registers are not affected. |
||||
* |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
*/ |
||||
int ulpi_reset(u32 ulpi_viewport); |
||||
|
||||
|
||||
/* ULPI access methods below must be implemented for each ULPI viewport. */ |
||||
|
||||
/*
|
||||
* Write to the ULPI PHY register via the viewport. |
||||
* @reg - the ULPI register (one of the fields in struct ulpi_regs). |
||||
* @value - the value - only 8 lower bits are used, others ignored. |
||||
* |
||||
* returns 0 on success, ULPI_ERROR on failure. |
||||
*/ |
||||
u32 ulpi_write(u32 ulpi_viewport, u8 *reg, u32 value); |
||||
|
||||
/*
|
||||
* Read the ULPI PHY register content via the viewport. |
||||
* @reg - the ULPI register (one of the fields in struct ulpi_regs). |
||||
* |
||||
* returns register content on success, ULPI_ERROR on failure. |
||||
*/ |
||||
u32 ulpi_read(u32 ulpi_viewport, u8 *reg); |
||||
|
||||
/*
|
||||
* Wait for the reset to complete. |
||||
* The Link must not attempt to access the PHY until the reset has |
||||
* completed and DIR line is de-asserted. |
||||
*/ |
||||
int ulpi_reset_wait(u32 ulpi_viewport); |
||||
|
||||
/* Access Extended Register Set (indicator) */ |
||||
#define ACCESS_EXT_REGS_OFFSET 0x2f /* read-write */ |
||||
/* Vendor-specific */ |
||||
#define VENDOR_SPEC_OFFSET 0x30 |
||||
|
||||
/*
|
||||
* Extended Register Set |
||||
* |
||||
* Addresses 0x00-0x3F map directly to Immediate Register Set. |
||||
* Addresses 0x40-0x7F are reserved. |
||||
* Addresses 0x80-0xff are vendor-specific. |
||||
*/ |
||||
#define EXT_VENDOR_SPEC_OFFSET 0x80 |
||||
|
||||
/* ULPI registers, bits and offsets definitions */ |
||||
struct ulpi_regs { |
||||
/* Vendor ID and Product ID: 0x00 - 0x03 Read-only */ |
||||
u8 vendor_id_low; |
||||
u8 vendor_id_high; |
||||
u8 product_id_low; |
||||
u8 product_id_high; |
||||
/* Function Control: 0x04 - 0x06 Read */ |
||||
u8 function_ctrl; /* 0x04 Write */ |
||||
u8 function_ctrl_set; /* 0x05 Set */ |
||||
u8 function_ctrl_clear; /* 0x06 Clear */ |
||||
/* Interface Control: 0x07 - 0x09 Read */ |
||||
u8 iface_ctrl; /* 0x07 Write */ |
||||
u8 iface_ctrl_set; /* 0x08 Set */ |
||||
u8 iface_ctrl_clear; /* 0x09 Clear */ |
||||
/* OTG Control: 0x0A - 0x0C Read */ |
||||
u8 otg_ctrl; /* 0x0A Write */ |
||||
u8 otg_ctrl_set; /* 0x0B Set */ |
||||
u8 otg_ctrl_clear; /* 0x0C Clear */ |
||||
/* USB Interrupt Enable Rising: 0x0D - 0x0F Read */ |
||||
u8 usb_ie_rising; /* 0x0D Write */ |
||||
u8 usb_ie_rising_set; /* 0x0E Set */ |
||||
u8 usb_ie_rising_clear; /* 0x0F Clear */ |
||||
/* USB Interrupt Enable Falling: 0x10 - 0x12 Read */ |
||||
u8 usb_ie_falling; /* 0x10 Write */ |
||||
u8 usb_ie_falling_set; /* 0x11 Set */ |
||||
u8 usb_ie_falling_clear; /* 0x12 Clear */ |
||||
/* USB Interrupt Status: 0x13 Read-only */ |
||||
u8 usb_int_status; |
||||
/* USB Interrupt Latch: 0x14 Read-only with auto-clear */ |
||||
u8 usb_int_latch; |
||||
/* Debug: 0x15 Read-only */ |
||||
u8 debug; |
||||
/* Scratch Register: 0x16 - 0x18 Read */ |
||||
u8 scratch; /* 0x16 Write */ |
||||
u8 scratch_set; /* 0x17 Set */ |
||||
u8 scratch_clear; /* 0x18 Clear */ |
||||
/*
|
||||
* Optional Carkit registers: |
||||
* Carkit Control: 0x19 - 0x1B Read |
||||
*/ |
||||
u8 carkit_ctrl; /* 0x19 Write */ |
||||
u8 carkit_ctrl_set; /* 0x1A Set */ |
||||
u8 carkit_ctrl_clear; /* 0x1B Clear */ |
||||
/* Carkit Interrupt Delay: 0x1C Read, Write */ |
||||
u8 carkit_int_delay; |
||||
/* Carkit Interrupt Enable: 0x1D - 0x1F Read */ |
||||
u8 carkit_ie; /* 0x1D Write */ |
||||
u8 carkit_ie_set; /* 0x1E Set */ |
||||
u8 carkit_ie_clear; /* 0x1F Clear */ |
||||
/* Carkit Interrupt Status: 0x20 Read-only */ |
||||
u8 carkit_int_status; |
||||
/* Carkit Interrupt Latch: 0x21 Read-only with auto-clear */ |
||||
u8 carkit_int_latch; |
||||
/* Carkit Pulse Control: 0x22 - 0x24 Read */ |
||||
u8 carkit_pulse_ctrl; /* 0x22 Write */ |
||||
u8 carkit_pulse_ctrl_set; /* 0x23 Set */ |
||||
u8 carkit_pulse_ctrl_clear; /* 0x24 Clear */ |
||||
/*
|
||||
* Other optional registers: |
||||
* Transmit Positive Width: 0x25 Read, Write |
||||
*/ |
||||
u8 transmit_pos_width; |
||||
/* Transmit Negative Width: 0x26 Read, Write */ |
||||
u8 transmit_neg_width; |
||||
/* Receive Polarity Recovery: 0x27 Read, Write */ |
||||
u8 recv_pol_recovery; |
||||
/*
|
||||
* Addresses 0x28 - 0x2E are reserved, so we use offsets |
||||
* for immediate registers with higher addresses |
||||
*/ |
||||
}; |
||||
|
||||
/*
|
||||
* Register Bits |
||||
*/ |
||||
|
||||
/* Function Control */ |
||||
#define ULPI_FC_XCVRSEL_MASK (3 << 0) |
||||
#define ULPI_FC_HIGH_SPEED (0 << 0) |
||||
#define ULPI_FC_FULL_SPEED (1 << 0) |
||||
#define ULPI_FC_LOW_SPEED (2 << 0) |
||||
#define ULPI_FC_FS4LS (3 << 0) |
||||
#define ULPI_FC_TERMSELECT (1 << 2) |
||||
#define ULPI_FC_OPMODE_MASK (3 << 3) |
||||
#define ULPI_FC_OPMODE_NORMAL (0 << 3) |
||||
#define ULPI_FC_OPMODE_NONDRIVING (1 << 3) |
||||
#define ULPI_FC_OPMODE_DISABLE_NRZI (2 << 3) |
||||
#define ULPI_FC_OPMODE_NOSYNC_NOEOP (3 << 3) |
||||
#define ULPI_FC_RESET (1 << 5) |
||||
#define ULPI_FC_SUSPENDM (1 << 6) |
||||
|
||||
/* Interface Control */ |
||||
#define ULPI_IFACE_6_PIN_SERIAL_MODE (1 << 0) |
||||
#define ULPI_IFACE_3_PIN_SERIAL_MODE (1 << 1) |
||||
#define ULPI_IFACE_CARKITMODE (1 << 2) |
||||
#define ULPI_IFACE_CLOCKSUSPENDM (1 << 3) |
||||
#define ULPI_IFACE_AUTORESUME (1 << 4) |
||||
#define ULPI_IFACE_EXTVBUS_COMPLEMENT (1 << 5) |
||||
#define ULPI_IFACE_PASSTHRU (1 << 6) |
||||
#define ULPI_IFACE_PROTECT_IFC_DISABLE (1 << 7) |
||||
|
||||
/* OTG Control */ |
||||
#define ULPI_OTG_ID_PULLUP (1 << 0) |
||||
#define ULPI_OTG_DP_PULLDOWN (1 << 1) |
||||
#define ULPI_OTG_DM_PULLDOWN (1 << 2) |
||||
#define ULPI_OTG_DISCHRGVBUS (1 << 3) |
||||
#define ULPI_OTG_CHRGVBUS (1 << 4) |
||||
#define ULPI_OTG_DRVVBUS (1 << 5) |
||||
#define ULPI_OTG_DRVVBUS_EXT (1 << 6) |
||||
#define ULPI_OTG_EXTVBUSIND (1 << 7) |
||||
|
||||
/*
|
||||
* USB Interrupt Enable Rising, |
||||
* USB Interrupt Enable Falling, |
||||
* USB Interrupt Status and |
||||
* USB Interrupt Latch |
||||
*/ |
||||
#define ULPI_INT_HOST_DISCONNECT (1 << 0) |
||||
#define ULPI_INT_VBUS_VALID (1 << 1) |
||||
#define ULPI_INT_SESS_VALID (1 << 2) |
||||
#define ULPI_INT_SESS_END (1 << 3) |
||||
#define ULPI_INT_IDGRD (1 << 4) |
||||
|
||||
/* Debug */ |
||||
#define ULPI_DEBUG_LINESTATE0 (1 << 0) |
||||
#define ULPI_DEBUG_LINESTATE1 (1 << 1) |
||||
|
||||
/* Carkit Control */ |
||||
#define ULPI_CARKIT_CTRL_CARKITPWR (1 << 0) |
||||
#define ULPI_CARKIT_CTRL_IDGNDDRV (1 << 1) |
||||
#define ULPI_CARKIT_CTRL_TXDEN (1 << 2) |
||||
#define ULPI_CARKIT_CTRL_RXDEN (1 << 3) |
||||
#define ULPI_CARKIT_CTRL_SPKLEFTEN (1 << 4) |
||||
#define ULPI_CARKIT_CTRL_SPKRIGHTEN (1 << 5) |
||||
#define ULPI_CARKIT_CTRL_MICEN (1 << 6) |
||||
|
||||
/* Carkit Interrupt Enable */ |
||||
#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE (1 << 0) |
||||
#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL (1 << 1) |
||||
#define ULPI_CARKIT_INT_EN_CARINTDET (1 << 2) |
||||
#define ULPI_CARKIT_INT_EN_DP_RISE (1 << 3) |
||||
#define ULPI_CARKIT_INT_EN_DP_FALL (1 << 4) |
||||
|
||||
/* Carkit Interrupt Status and Latch */ |
||||
#define ULPI_CARKIT_INT_IDFLOAT (1 << 0) |
||||
#define ULPI_CARKIT_INT_CARINTDET (1 << 1) |
||||
#define ULPI_CARKIT_INT_DP (1 << 2) |
||||
|
||||
/* Carkit Pulse Control*/ |
||||
#define ULPI_CARKIT_PLS_CTRL_TXPLSEN (1 << 0) |
||||
#define ULPI_CARKIT_PLS_CTRL_RXPLSEN (1 << 1) |
||||
#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) |
||||
#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) |
||||
|
||||
|
||||
#endif /* __USB_ULPI_H__ */ |
Loading…
Reference in new issue