[PATCH] This patch add varius fix to the ehci.
- fix ehci_readl, ehci_writel - introduce new define in ehci.h - introduce the handshake function for waiting on a register - fix usb_ehci_fsl with the new HC_LENGTH macro Signed-off-by: Michael Trimarchi <trimarchimichael@yahoo.it> Signed-off-by: Remy Böhmer <linux@bohmer.net>
This commit is contained in:
parent
db63299b1d
commit
51ab142b8b
4 changed files with 133 additions and 44 deletions
|
@ -45,15 +45,24 @@ struct ehci_hccr {
|
|||
#define HC_LENGTH(p) (((p) >> 0) & 0x00ff)
|
||||
#define HC_VERSION(p) (((p) >> 16) & 0xffff)
|
||||
uint32_t cr_hcsparams;
|
||||
#define HCS_N_PORTS(p) (((p) >> 0) & 0xf)
|
||||
uint32_t cr_hccparams;
|
||||
uint8_t cr_hcsp_portrt[8];
|
||||
};
|
||||
|
||||
struct ehci_hcor {
|
||||
uint32_t or_usbcmd;
|
||||
#define CMD_ASE (1 << 5)
|
||||
#define CMD_PARK (1 << 11) /* enable "park" */
|
||||
#define CMD_PARK_CNT(c) (((c) >> 8) & 3) /* how many transfers to park */
|
||||
#define CMD_ASE (1 << 5) /* async schedule enable */
|
||||
#define CMD_LRESET (1 << 7) /* partial reset */
|
||||
#define CMD_IAAD (1 << 5) /* "doorbell" interrupt */
|
||||
#define CMD_PSE (1 << 4) /* periodic schedule enable */
|
||||
#define CMD_RESET (1 << 1) /* reset HC not bus */
|
||||
#define CMD_RUN (1 << 0) /* start/stop HC */
|
||||
uint32_t or_usbsts;
|
||||
#define STD_ASS (1 << 15)
|
||||
#define STS_HALT (1 << 12)
|
||||
uint32_t or_usbintr;
|
||||
uint32_t or_frindex;
|
||||
uint32_t or_ctrldssegment;
|
||||
|
@ -61,10 +70,17 @@ struct ehci_hcor {
|
|||
uint32_t or_asynclistaddr;
|
||||
uint32_t _reserved_[9];
|
||||
uint32_t or_configflag;
|
||||
#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
|
||||
uint32_t or_portsc[2];
|
||||
uint32_t or_systune;
|
||||
};
|
||||
|
||||
#define USBMODE 0x68 /* USB Device mode */
|
||||
#define USBMODE_SDIS (1 << 3) /* Stream disable */
|
||||
#define USBMODE_BE (1 << 2) /* BE/LE endiannes select */
|
||||
#define USBMODE_CM_HC (3 << 0) /* host controller mode */
|
||||
#define USBMODE_CM_IDLE (0 << 0) /* idle state */
|
||||
|
||||
/* Interface descriptor */
|
||||
struct usb_linux_interface_descriptor {
|
||||
unsigned char bLength;
|
||||
|
@ -91,11 +107,12 @@ struct usb_linux_config_descriptor {
|
|||
} __attribute__ ((packed));
|
||||
|
||||
#if defined CONFIG_EHCI_DESC_BIG_ENDIAN
|
||||
#define ehci_readl(x) (x)
|
||||
#define ehci_writel(a, b) (a) = (b)
|
||||
#define ehci_readl(x) (*((volatile u32 *)(x)))
|
||||
#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b))
|
||||
#else
|
||||
#define ehci_readl(x) cpu_to_le32((x))
|
||||
#define ehci_writel(a, b) (a) = cpu_to_le32((b))
|
||||
#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x))))
|
||||
#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \
|
||||
cpu_to_le32(((volatile u32)b)))
|
||||
#endif
|
||||
|
||||
#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN
|
||||
|
|
|
@ -99,8 +99,55 @@ static struct descriptor {
|
|||
},
|
||||
};
|
||||
|
||||
static void ehci_free (void *p, size_t sz)
|
||||
static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec)
|
||||
{
|
||||
uint32_t result;
|
||||
do {
|
||||
result = ehci_readl(ptr);
|
||||
debug("handshake read reg(%x)=%x\n", (uint32_t)ptr, result);
|
||||
if (result == ~(uint32_t)0)
|
||||
return -1;
|
||||
result &= mask;
|
||||
if (result == done)
|
||||
return 0;
|
||||
wait_ms(1);
|
||||
msec--;
|
||||
} while (msec > 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ehci_free(void *p, size_t sz)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static int ehci_reset(void)
|
||||
{
|
||||
uint32_t cmd;
|
||||
uint32_t tmp;
|
||||
uint32_t *reg_ptr;
|
||||
int ret = 0;
|
||||
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
cmd |= CMD_RESET;
|
||||
ehci_writel(&hcor->or_usbcmd, cmd);
|
||||
ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, 250);
|
||||
if (ret < 0) {
|
||||
printf("EHCI fail to reset\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if defined CONFIG_EHCI_IS_TDI
|
||||
reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE);
|
||||
tmp = ehci_readl(reg_ptr);
|
||||
tmp |= USBMODE_CM_HC;
|
||||
#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN
|
||||
tmp |= USBMODE_BE;
|
||||
#endif
|
||||
ehci_writel(reg_ptr, tmp);
|
||||
#endif
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *ehci_alloc(size_t sz, size_t align)
|
||||
|
@ -170,6 +217,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
uint32_t endpt, token, usbsts;
|
||||
uint32_t c, toggle;
|
||||
uint32_t cmd;
|
||||
uint32_t sts;
|
||||
|
||||
debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
|
||||
buffer, length, req);
|
||||
|
@ -277,16 +325,19 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
|
||||
qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH);
|
||||
|
||||
usbsts = ehci_readl(hcor->or_usbsts);
|
||||
ehci_writel(hcor->or_usbsts, (usbsts & 0x3f));
|
||||
usbsts = ehci_readl(&hcor->or_usbsts);
|
||||
ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f));
|
||||
|
||||
/* Enable async. schedule. */
|
||||
cmd = ehci_readl(hcor->or_usbcmd);
|
||||
hcor->or_usbcmd |= CMD_ASE;
|
||||
ehci_writel(hcor->or_usbcmd, cmd);
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
cmd |= CMD_ASE;
|
||||
ehci_writel(&hcor->or_usbcmd, cmd);
|
||||
|
||||
while ((ehci_readl(hcor->or_usbsts) & STD_ASS) == 0)
|
||||
udelay(1);
|
||||
sts = ehci_readl(&hcor->or_usbsts);
|
||||
while ((sts & STD_ASS) == 0) {
|
||||
sts = ehci_readl(&hcor->or_usbsts);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
/* Wait for TDs to be processed. */
|
||||
ts = get_timer(0);
|
||||
|
@ -298,11 +349,15 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
} while (get_timer(ts) < CONFIG_SYS_HZ);
|
||||
|
||||
/* Disable async schedule. */
|
||||
cmd = ehci_readl(hcor->or_usbcmd);
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
cmd &= ~CMD_ASE;
|
||||
ehci_writel(hcor->or_usbcmd, cmd);
|
||||
while ((ehci_readl(hcor->or_usbsts) & STD_ASS) != 0)
|
||||
udelay(1);
|
||||
ehci_writel(&hcor->or_usbcmd, cmd);
|
||||
|
||||
sts = ehci_readl(&hcor->or_usbsts);
|
||||
while ((sts & STD_ASS) != 0) {
|
||||
sts = ehci_readl(&hcor->or_usbsts);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
|
||||
|
||||
|
@ -335,9 +390,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
} else {
|
||||
dev->act_len = 0;
|
||||
debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
|
||||
dev->devnum, ehci_readl(hcor->or_usbsts),
|
||||
ehci_readl(hcor->or_portsc[0]),
|
||||
ehci_readl(hcor->or_portsc[1]));
|
||||
dev->devnum, ehci_readl(&hcor->or_usbsts),
|
||||
ehci_readl(&hcor->or_portsc[0]),
|
||||
ehci_readl(&hcor->or_portsc[1]));
|
||||
}
|
||||
|
||||
return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;
|
||||
|
@ -451,7 +506,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
break;
|
||||
case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):
|
||||
memset(tmpbuf, 0, 4);
|
||||
reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index)
|
||||
reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index)
|
||||
- 1]);
|
||||
if (reg & EHCI_PS_CS)
|
||||
tmpbuf[0] |= USB_PORT_STAT_CONNECTION;
|
||||
|
@ -479,9 +534,12 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
srclen = 4;
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
|
||||
reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]);
|
||||
reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]);
|
||||
reg &= ~EHCI_PS_CLEAR;
|
||||
switch (le16_to_cpu(req->value)) {
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
reg |= EHCI_PS_PE;
|
||||
break;
|
||||
case USB_PORT_FEAT_POWER:
|
||||
reg |= EHCI_PS_PP;
|
||||
break;
|
||||
|
@ -495,22 +553,22 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
/* Start reset sequence. */
|
||||
reg &= ~EHCI_PS_PE;
|
||||
reg |= EHCI_PS_PR;
|
||||
ehci_writel(hcor->or_portsc[
|
||||
ehci_writel(&hcor->or_portsc[
|
||||
le16_to_cpu(req->index) - 1], reg);
|
||||
/* Wait for reset to complete. */
|
||||
udelay(500000);
|
||||
wait_ms(500);
|
||||
/* Terminate reset sequence. */
|
||||
reg &= ~EHCI_PS_PR;
|
||||
/* TODO: is it only fsl chip that requires this
|
||||
* manual setting of port enable?
|
||||
*/
|
||||
reg |= EHCI_PS_PE;
|
||||
ehci_writel(hcor->or_portsc[
|
||||
ehci_writel(&hcor->or_portsc[
|
||||
le16_to_cpu(req->index) - 1], reg);
|
||||
/* Wait for HC to complete reset. */
|
||||
udelay(2000);
|
||||
wait_ms(10);
|
||||
reg =
|
||||
ehci_readl(hcor->or_portsc[le16_to_cpu(req->index)
|
||||
ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index)
|
||||
- 1]);
|
||||
reg &= ~EHCI_PS_CLEAR;
|
||||
if ((reg & EHCI_PS_PE) == 0) {
|
||||
|
@ -525,10 +583,10 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
debug("unknown feature %x\n", le16_to_cpu(req->value));
|
||||
goto unknown;
|
||||
}
|
||||
ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg);
|
||||
ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
|
||||
reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]);
|
||||
reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]);
|
||||
reg &= ~EHCI_PS_CLEAR;
|
||||
switch (le16_to_cpu(req->value)) {
|
||||
case USB_PORT_FEAT_ENABLE:
|
||||
|
@ -537,6 +595,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
case USB_PORT_FEAT_C_CONNECTION:
|
||||
reg |= EHCI_PS_CSC;
|
||||
break;
|
||||
case USB_PORT_FEAT_OVER_CURRENT:
|
||||
reg |= EHCI_PS_OCC;
|
||||
break;
|
||||
case USB_PORT_FEAT_C_RESET:
|
||||
portreset &= ~(1 << le16_to_cpu(req->index));
|
||||
break;
|
||||
|
@ -544,7 +605,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||
debug("unknown feature %x\n", le16_to_cpu(req->value));
|
||||
goto unknown;
|
||||
}
|
||||
ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg);
|
||||
ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg);
|
||||
break;
|
||||
default:
|
||||
debug("Unknown request\n");
|
||||
|
@ -585,6 +646,10 @@ int usb_lowlevel_init(void)
|
|||
if (ehci_hcd_init() != 0)
|
||||
return -1;
|
||||
|
||||
/* EHCI spec section 4.1 */
|
||||
if (ehci_reset() != 0)
|
||||
return -1;
|
||||
|
||||
/* Set head of reclaim list */
|
||||
memset(&qh_list, 0, sizeof(qh_list));
|
||||
qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH);
|
||||
|
@ -595,25 +660,31 @@ int usb_lowlevel_init(void)
|
|||
qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40);
|
||||
|
||||
/* Set async. queue head pointer. */
|
||||
ehci_writel(hcor->or_asynclistaddr, (uint32_t)&qh_list);
|
||||
ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list);
|
||||
|
||||
reg = ehci_readl(hccr->cr_hcsparams);
|
||||
descriptor.hub.bNbrPorts = reg & 0xf;
|
||||
printf("NbrPorts %x\n", descriptor.hub.bNbrPorts);
|
||||
reg = ehci_readl(&hccr->cr_hcsparams);
|
||||
descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);
|
||||
printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);
|
||||
if (reg & 0x10000) /* Port Indicators */
|
||||
descriptor.hub.wHubCharacteristics |= 0x80;
|
||||
if (reg & 0x10) /* Port Power Control */
|
||||
descriptor.hub.wHubCharacteristics |= 0x01;
|
||||
|
||||
/* take control over the ports */
|
||||
cmd = ehci_readl(hcor->or_configflag);
|
||||
cmd |= 1;
|
||||
ehci_writel(hcor->or_configflag, cmd);
|
||||
|
||||
/* Start the host controller. */
|
||||
cmd = ehci_readl(hcor->or_configflag);
|
||||
cmd |= 1;
|
||||
ehci_writel(hcor->or_usbcmd, cmd);
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
/* Philips, Intel, and maybe others need CMD_RUN before the
|
||||
* root hub will detect new devices (why?); NEC doesn't */
|
||||
cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
|
||||
cmd |= CMD_RUN;
|
||||
ehci_writel(&hcor->or_usbcmd, cmd);
|
||||
|
||||
/* take control over the ports */
|
||||
cmd = ehci_readl(&hcor->or_configflag);
|
||||
cmd |= FLAG_CF;
|
||||
ehci_writel(&hcor->or_configflag, cmd);
|
||||
/* unblock posted writes */
|
||||
cmd = ehci_readl(&hcor->or_usbcmd);
|
||||
wait_ms(5);
|
||||
|
||||
rootdev = 0;
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ int ehci_hcd_init(void)
|
|||
|
||||
addr = (uint32_t)&(im->usb[0]);
|
||||
hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI);
|
||||
hcor = (struct ehci_hcor *)((uint32_t) hccr + hccr->cr_caplength);
|
||||
hcor = (struct ehci_hcor *)((uint32_t) hccr +
|
||||
HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
|
||||
|
||||
/* Configure clock */
|
||||
clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK,
|
||||
|
|
|
@ -181,7 +181,7 @@ struct usb_device {
|
|||
*/
|
||||
|
||||
#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \
|
||||
defined(CONFI_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \
|
||||
defined(CONFIG_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \
|
||||
defined(CONFIG_USB_SL811HS) || defined(CONFIG_USB_ISP116X_HCD) || \
|
||||
defined(CONFIG_USB_R8A66597_HCD)
|
||||
|
||||
|
|
Loading…
Reference in a new issue