diff options
Diffstat (limited to 'drivers/usb/host')
| -rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hcd.c | 47 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-mx5.c | 255 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-hcd.c | 75 |
4 files changed, 342 insertions, 36 deletions
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 09abb754d..77e217f34 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -42,6 +42,7 @@ COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o endif COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o +COBJS-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o COBJS-$(CONFIG_USB_EHCI_KIRKWOOD) += ehci-kirkwood.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2197119cf..3f7bc2cef 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -26,6 +26,10 @@ #include <asm/io.h> #include <malloc.h> #include <watchdog.h> +#ifdef CONFIG_USB_KEYBOARD +#include <stdio_dev.h> +extern unsigned char new[]; +#endif #include "ehci.h" @@ -48,7 +52,7 @@ static struct descriptor { 0x29, /* bDescriptorType: hub descriptor */ 2, /* bNrPorts -- runtime modified */ 0, /* wHubCharacteristics */ - 0xff, /* bPwrOn2PwrGood */ + 10, /* bPwrOn2PwrGood */ 0, /* bHubCntrCurrent */ {}, /* Device removable */ {} /* at most 7 ports! XXX */ @@ -201,6 +205,14 @@ static inline void ehci_invalidate_dcache(struct QH *qh) } #endif /* CONFIG_EHCI_DCACHE */ +void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ + mdelay(50); +} + +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) + __attribute__((weak, alias("__ehci_powerup_fixup"))); + static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) { uint32_t result; @@ -709,8 +721,8 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, * usb 2.0 specification say 50 ms resets on * root */ - wait_ms(50); - /* terminate the reset */ + ehci_powerup_fixup(status_reg, ®); + ehci_writel(status_reg, reg & ~EHCI_PS_PR); /* * A host controller must terminate the reset @@ -895,5 +907,32 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d", dev, pipe, buffer, length, interval); - return -1; + return ehci_submit_async(dev, pipe, buffer, length, NULL); +} + +#ifdef CONFIG_SYS_USB_EVENT_POLL +/* + * This function polls for USB keyboard data. + */ +void usb_event_poll() +{ + struct stdio_dev *dev; + struct usb_device *usb_kbd_dev; + struct usb_interface *iface; + struct usb_endpoint_descriptor *ep; + int pipe; + int maxp; + + /* Get the pointer to USB Keyboard device pointer */ + dev = stdio_get_by_name("usbkbd"); + usb_kbd_dev = (struct usb_device *)dev->priv; + iface = &usb_kbd_dev->config.if_desc[0]; + ep = &iface->ep_desc[0]; + pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + + /* Submit a interrupt transfer request */ + maxp = usb_maxpacket(usb_kbd_dev, pipe); + usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], + maxp > 8 ? 8 : maxp, ep->bInterval); } +#endif /* CONFIG_SYS_USB_EVENT_POLL */ diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c new file mode 100644 index 000000000..68a673e72 --- /dev/null +++ b/drivers/usb/host/ehci-mx5.c @@ -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; +} + + diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 60c959526..cf906b47c 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1262,12 +1262,19 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, int len = 0; int stat = 0; __u32 datab[4]; - __u8 *data_buf = (__u8 *)datab; + union { + void *ptr; + __u8 *u8; + __u16 *u16; + __u32 *u32; + } databuf; __u16 bmRType_bReq; __u16 wValue; __u16 wIndex; __u16 wLength; + databuf.u32 = (__u32 *)datab; + #ifdef DEBUG pkt_print(NULL, dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); @@ -1297,20 +1304,20 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, */ case RH_GET_STATUS: - *(__u16 *) data_buf = cpu_to_le16(1); + databuf.u16[0] = cpu_to_le16(1); OK(2); case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data_buf = cpu_to_le16(0); + databuf.u16[0] = cpu_to_le16(0); OK(2); case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data_buf = cpu_to_le16(0); + databuf.u16[0] = cpu_to_le16(0); OK(2); case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32( + databuf.u32[0] = cpu_to_le32( RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); OK(4); case RH_GET_STATUS | RH_OTHER | RH_CLASS: - *(__u32 *) data_buf = cpu_to_le32(RD_RH_PORTSTAT); + databuf.u32[0] = cpu_to_le32(RD_RH_PORTSTAT); OK(4); case RH_CLEAR_FEATURE | RH_ENDPOINT: @@ -1374,14 +1381,14 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_dev_des), wLength)); - data_buf = root_hub_dev_des; OK(len); + databuf.ptr = root_hub_dev_des; OK(len); case (0x02): /* configuration descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof(root_hub_config_des), wLength)); - data_buf = root_hub_config_des; OK(len); + databuf.ptr = root_hub_config_des; OK(len); case (0x03): /* string descriptors */ if (wValue == 0x0300) { len = min_t(unsigned int, @@ -1389,7 +1396,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_str_index0), wLength)); - data_buf = root_hub_str_index0; + databuf.ptr = root_hub_str_index0; OK(len); } if (wValue == 0x0301) { @@ -1398,7 +1405,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, min_t(unsigned int, sizeof(root_hub_str_index1), wLength)); - data_buf = root_hub_str_index1; + databuf.ptr = root_hub_str_index1; OK(len); } default: @@ -1410,41 +1417,45 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, { __u32 temp = roothub_a(&gohci); - data_buf [0] = 9; /* min length; */ - data_buf [1] = 0x29; - data_buf [2] = temp & RH_A_NDP; + databuf.u8[0] = 9; /* min length; */ + databuf.u8[1] = 0x29; + databuf.u8[2] = temp & RH_A_NDP; #ifdef CONFIG_AT91C_PQFP_UHPBUG - data_buf [2] = (data_buf [2] == 2) ? 1:0; + databuf.u8[2] = (databuf.u8[2] == 2) ? 1 : 0; #endif - data_buf [3] = 0; + databuf.u8[3] = 0; if (temp & RH_A_PSM) /* per-port power switching? */ - data_buf [3] |= 0x1; + databuf.u8[3] |= 0x1; if (temp & RH_A_NOCP) /* no overcurrent reporting? */ - data_buf [3] |= 0x10; + databuf.u8[3] |= 0x10; else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ - data_buf [3] |= 0x8; + databuf.u8[3] |= 0x8; - /* corresponds to data_buf[4-7] */ - datab [1] = 0; - data_buf [5] = (temp & RH_A_POTPGT) >> 24; + /* corresponds to databuf.u8[4-7] */ + databuf.u8[1] = 0; + databuf.u8[5] = (temp & RH_A_POTPGT) >> 24; temp = roothub_b(&gohci); - data_buf [7] = temp & RH_B_DR; - if (data_buf [2] < 7) { - data_buf [8] = 0xff; + databuf.u8[7] = temp & RH_B_DR; + if (databuf.u8[2] < 7) { + databuf.u8[8] = 0xff; } else { - data_buf [0] += 2; - data_buf [8] = (temp & RH_B_DR) >> 8; - data_buf [10] = data_buf [9] = 0xff; + databuf.u8[0] += 2; + databuf.u8[8] = (temp & RH_B_DR) >> 8; + databuf.u8[10] = databuf.u8[9] = 0xff; } len = min_t(unsigned int, leni, - min_t(unsigned int, data_buf [0], wLength)); + min_t(unsigned int, databuf.u8[0], wLength)); OK(len); } - case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK(1); + case RH_GET_CONFIGURATION: + databuf.u8[0] = 0x01; + OK(1); - case RH_SET_CONFIGURATION: WR_RH_STAT(0x10000); OK(0); + case RH_SET_CONFIGURATION: + WR_RH_STAT(0x10000); + OK(0); default: dbg("unsupported root hub command"); @@ -1458,8 +1469,8 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len, #endif len = min_t(int, len, leni); - if (data != data_buf) - memcpy(data, data_buf, len); + if (data != databuf.ptr) + memcpy(data, databuf.ptr, len); dev->act_len = len; dev->status = stat; |