[U-Boot] [Drivers PATCH 01/19] mtd/st_smi: Clear error flags while initiating a fresh write

SMI controller reports an error when the code tries to write on the flash area with Write Enable command not issued or the bank has come out of the write mode.
This error is reported even with a fresh write once the ERF1 or ERF2 is set. Clear these flags while initiating a fresh write
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/mtd/st_smi.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 7507e5d..fad4420 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -392,6 +392,8 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, return -1; }
+ writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); + if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) return -EBUSY;

From: Armando Visconti armando.visconti@st.com
Signed-off-by: Armando Visconti armando.visconti@st.com --- drivers/mtd/st_smi.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index fad4420..e2d1cf9 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -75,6 +75,7 @@ static struct flash_device flash_devices[] = { FLASH_ID("st m45pe20" , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), FLASH_ID("st m45pe40" , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), FLASH_ID("st m45pe80" , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), + FLASH_ID("mcr n25q128" , 0xd8, 0x0018BA20, 0x100, 0x10000, 0x1000000), FLASH_ID("sp s25fl004" , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000), FLASH_ID("sp s25fl008" , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000), FLASH_ID("sp s25fl016" , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000),

At the start of an smi_write, if the destination address is page aligned, the Write Enable command is getting issued twice. This patch fixes it by keeping a flag.
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/mtd/st_smi.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index e2d1cf9..1b9ff0e 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -374,7 +374,7 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, u8 *src_addr8 = (u8 *)src_addr; u8 *dst_addr8 = (u8 *)dst_addr; int banknum; - int i; + int i, issue_we;
switch (bank_addr) { case SMIBANK0_BASE: @@ -394,19 +394,16 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, }
writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); - - if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) - return -EBUSY; + issue_we = 1;
/* Set SMI in Hardware Mode */ writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1);
- if (smi_write_enable(banknum)) - return -EIO; - /* Perform the write command */ for (i = 0; i < length; i += 4) { - if (((ulong) (dst_addr) % SFLASH_PAGE_SIZE) == 0) { + if (issue_we || (((ulong)(dst_addr) % SFLASH_PAGE_SIZE) == 0)) { + issue_we = 0; + if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) return -EBUSY;

The write loop is checking for dest_addr alignment with page size. This sometimes leads to smi controller coming out of write mode and eventually the next write failing with ERF1 being set.
To avoid this, write to flash in a tight loop and write bytewise to also support not word aligned data bytes to be written. Additionally, enable burst mode before this loop so that the controller does not deselect the chip if the transfer is finished.
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/mtd/st_smi.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-)
diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 1b9ff0e..5298723 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -368,13 +368,11 @@ static int smi_sector_erase(flash_info_t *info, unsigned int sector) * * Write to SMI flash */ -static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, +static int smi_write(unsigned char *src_addr, unsigned char *dst_addr, unsigned int length, ulong bank_addr) { - u8 *src_addr8 = (u8 *)src_addr; - u8 *dst_addr8 = (u8 *)dst_addr; int banknum; - int i, issue_we; + int issue_we;
switch (bank_addr) { case SMIBANK0_BASE: @@ -400,7 +398,10 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1);
/* Perform the write command */ - for (i = 0; i < length; i += 4) { + while (length) { + int k; + unsigned int wlen = min(SFLASH_PAGE_SIZE, length); + if (issue_we || (((ulong)(dst_addr) % SFLASH_PAGE_SIZE) == 0)) { issue_we = 0;
@@ -412,19 +413,14 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, return -EIO; }
- if (length < 4) { - int k; + writel(readl(&smicntl->smi_cr1) | WB_MODE, &smicntl->smi_cr1);
- /* - * Handle special case, where length < 4 (redundant env) - */ - for (k = 0; k < length; k++) - *dst_addr8++ = *src_addr8++; - } else { - /* Normal 32bit write */ + for (k = 0; k < wlen; k++) *dst_addr++ = *src_addr++; - }
+ writel(readl(&smicntl->smi_cr1) & ~WB_MODE, &smicntl->smi_cr1); + + length -= wlen; if ((readl(&smicntl->smi_sr) & (ERF1 | ERF2))) return -EIO; } @@ -448,8 +444,8 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, */ int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) { - return smi_write((unsigned int *)src, (unsigned int *)dest_addr, - length, info->start[0]); + return smi_write(src, (unsigned char *)dest_addr, length, + info->start[0]); }
/*

The page size is a flash dependent property and the driver was using a macro in place of page size. This patch uses the proper page size wrt the flash device connected on board
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/mtd/st_smi.c | 41 +++++++++++++++++++++++++++++++++-------- include/linux/mtd/st_smi.h | 1 - 2 files changed, 33 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 5298723..ac10a3d 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -96,6 +96,25 @@ static struct flash_device flash_devices[] = { };
/* + * get_flash_device - Return flash_device pointer for a particular device id + * @id: Device id + * + * Return flash_device pointer for a particular device id + */ +static struct flash_device *get_flash_device(u32 id) +{ + struct flash_device *flash_dev_p = &flash_devices[0]; + int i; + + for (i = 0; i < ARRAY_SIZE(flash_devices); i++, flash_dev_p++) { + if (flash_dev_p->device_id == id) + return flash_dev_p; + } + + return NULL; +} + +/* * smi_wait_xfer_finish - Wait until TFF is set in status register * @timeout: timeout in milliseconds * @@ -361,20 +380,27 @@ static int smi_sector_erase(flash_info_t *info, unsigned int sector)
/* * smi_write - Write to SMI flash + * @info: flash info structure * @src_addr: source buffer * @dst_addr: destination buffer * @length: length to write in bytes - * @bank: bank base address * * Write to SMI flash */ -static int smi_write(unsigned char *src_addr, unsigned char *dst_addr, - unsigned int length, ulong bank_addr) +static int smi_write(flash_info_t *info, unsigned char *src_addr, + unsigned char *dst_addr, unsigned int length) { + struct flash_device *flash_device_p = get_flash_device(info->flash_id); + u32 page_size; int banknum; int issue_we;
- switch (bank_addr) { + if (!flash_device_p) + return -EIO; + + page_size = flash_device_p->pagesize; + + switch (info->start[0]) { case SMIBANK0_BASE: banknum = BANK0; break; @@ -400,9 +426,9 @@ static int smi_write(unsigned char *src_addr, unsigned char *dst_addr, /* Perform the write command */ while (length) { int k; - unsigned int wlen = min(SFLASH_PAGE_SIZE, length); + unsigned int wlen = min(page_size, length);
- if (issue_we || (((ulong)(dst_addr) % SFLASH_PAGE_SIZE) == 0)) { + if (issue_we || (((ulong)(dst_addr) % page_size) == 0)) { issue_we = 0;
if (smi_wait_till_ready(banknum, @@ -444,8 +470,7 @@ static int smi_write(unsigned char *src_addr, unsigned char *dst_addr, */ int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) { - return smi_write(src, (unsigned char *)dest_addr, length, - info->start[0]); + return smi_write(info, src, (unsigned char *)dest_addr, length); }
/* diff --git a/include/linux/mtd/st_smi.h b/include/linux/mtd/st_smi.h index 04f81ea..5837493 100644 --- a/include/linux/mtd/st_smi.h +++ b/include/linux/mtd/st_smi.h @@ -108,7 +108,6 @@ struct flash_dev { ushort sector_count; };
-#define SFLASH_PAGE_SIZE 0x100 /* flash page size */ #define XFER_FINISH_TOUT 15 /* xfer finish timeout(in ms) */ #define WMODE_TOUT 15 /* write enable timeout(in ms) */

Few pen drives take longer than usual for enumeration. The u-boot unlike linux does not depend on interrupts and works in polling and timeout mode.
This patch increases this timeout to increase the set of usb sticks that can be enumerated by u-boot
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- common/usb_hub.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/common/usb_hub.c b/common/usb_hub.c index e4a1201..24de9b7 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -393,17 +393,34 @@ static int usb_hub_configure(struct usb_device *dev) "" : "no "); usb_hub_power_on(hub);
+ mdelay(1500); + for (i = 0; i < dev->maxchild; i++) { ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); unsigned short portstatus, portchange; + int ret; + ulong start = get_timer(0); + + do { + ret = usb_get_port_status(dev, i + 1, portsts); + if (ret < 0) { + USB_HUB_PRINTF("get_port_status failed\n"); + break; + } + + portstatus = le16_to_cpu(portsts->wPortStatus); + portchange = le16_to_cpu(portsts->wPortChange); + + if ((portchange & USB_PORT_STAT_C_CONNECTION) && + (portstatus & USB_PORT_STAT_CONNECTION)) + break;
- if (usb_get_port_status(dev, i + 1, portsts) < 0) { - USB_HUB_PRINTF("get_port_status failed\n"); + mdelay(100); + } while (get_timer(start) < CONFIG_SYS_HZ * 10); + + if (ret < 0) continue; - }
- portstatus = le16_to_cpu(portsts->wPortStatus); - portchange = le16_to_cpu(portsts->wPortChange); USB_HUB_PRINTF("Port %d Status %X Change %X\n", i + 1, portstatus, portchange);

Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/mmc/Makefile | 1 + drivers/mmc/spear_sdhci.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 drivers/mmc/spear_sdhci.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index a1dd730..01dd61d 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -45,6 +45,7 @@ COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o COBJS-$(CONFIG_SDHCI) += sdhci.o COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o +COBJS-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o COBJS-$(CONFIG_DWMMC) += dw_mmc.o
diff --git a/drivers/mmc/spear_sdhci.c b/drivers/mmc/spear_sdhci.c new file mode 100644 index 0000000..23f1f4b --- /dev/null +++ b/drivers/mmc/spear_sdhci.c @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2012 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.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 <malloc.h> +#include <sdhci.h> + +int spear_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) +{ + struct sdhci_host *host = NULL; + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("sdhci host malloc fail!\n"); + return 1; + } + + host->name = "sdhci"; + host->ioaddr = (void *)regbase; + host->quirks = quirks; + + if (quirks & SDHCI_QUIRK_REG32_RW) + host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; + else + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + add_sdhci(host, max_clk, min_clk); + return 0; +}

Do not select MIIPORT for RGMII interface
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/net/designware.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/designware.c b/drivers/net/designware.c index bf21a08..46f6601 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -113,7 +113,9 @@ static int mac_reset(struct eth_device *dev) int timeout = CONFIG_MACRESET_TIMEOUT;
writel(DMAMAC_SRST, &dma_p->busmode); - writel(MII_PORTSELECT, &mac_p->conf); + + if (priv->interface != PHY_INTERFACE_MODE_RGMII) + writel(MII_PORTSELECT, &mac_p->conf);
start = get_timer(0); while (get_timer(start) < timeout) {

From: Pratyush Anand pratyush.anand@st.com
Driver for designware otg device only implements device functionality and is meant to be used with usbtty interface. This driver will work mainly for Control and Bulk endpoints. Periodic transfer has not been verified using these drivers.
Signed-off-by: Pratyush Anand pratyush.anand@st.com Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/serial/usbtty.h | 2 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/designware_otg.c | 1063 +++++++++++++++++++++++++++++++++++ include/usb/designware_otg.h | 500 ++++++++++++++++ 4 files changed, 1566 insertions(+) create mode 100644 drivers/usb/gadget/designware_otg.c create mode 100644 include/usb/designware_otg.h
diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h index eb670da..bd3bcbc 100644 --- a/drivers/serial/usbtty.h +++ b/drivers/serial/usbtty.h @@ -35,6 +35,8 @@ #include <usb/pxa27x_udc.h> #elif defined(CONFIG_DW_UDC) #include <usb/designware_udc.h> +#elif defined(CONFIG_DW_OTG) +#include <usb/designware_otg.h> #endif
#include <version.h> diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 040eaba..30ab931 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -43,6 +43,7 @@ ifdef CONFIG_USB_DEVICE COBJS-y += core.o COBJS-y += ep0.o COBJS-$(CONFIG_DW_UDC) += designware_udc.o +COBJS-$(CONFIG_DW_OTG) += designware_otg.o COBJS-$(CONFIG_OMAP1510) += omap1510_udc.o COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o diff --git a/drivers/usb/gadget/designware_otg.c b/drivers/usb/gadget/designware_otg.c new file mode 100644 index 0000000..57f6f15 --- /dev/null +++ b/drivers/usb/gadget/designware_otg.c @@ -0,0 +1,1063 @@ +/* + * Based on drivers/usb/gadget/designware_otg.c + * Synopsys DW OTG Device bus interface driver + * + * (C) Copyright 2011 + * Pratyush Anand, ST Micoelectronics, pratyush.anand@st.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 + */ + +#include <common.h> +#include <asm/io.h> +#include <usbdevice.h> +#include "ep0.h" +#include <usb/designware_otg.h> +#include <asm/arch/hardware.h> + +#define UDC_INIT_MDELAY 80 /* Device settle delay */ + +/* Some kind of debugging output... */ +#ifndef DEBUG_DWUSBTTY +#define UDCDBG(str) +#define UDCDBGA(fmt, args...) +#else +#define UDCDBG(str) serial_printf(str "\n") +#define UDCDBGA(fmt, args...) serial_printf(fmt "\n", ##args) +#endif + +static struct urb *ep0_urb; +static struct usb_device_instance *udc_device; + +static struct device_if device_if_mem; +static struct device_if *dev_if = &device_if_mem; +#if defined(CONFIG_USBD_HS) +#define CONFIG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_HS_PACKET_SIZE +#endif + +static void udc_set_addr_ctrl(u32 address); +static void udc_set_cfg_ctrl(u32 config); + +static struct usb_endpoint_instance *dw_find_ep(int ep) +{ + int i; + + for (i = 0; i < udc_device->bus->max_endpoints; i++) { + if ((udc_device->bus->endpoint_array[i].endpoint_address & + USB_ENDPOINT_NUMBER_MASK) == ep) + return &udc_device->bus->endpoint_array[i]; + } + return NULL; +} + +/* + * This function reads a packet from the Rx FIFO into the destination buffer. + * To read SETUP data use dwc_otg_read_setup_packet. + */ +void dwc_otg_read_packet(struct dwc_ep *ep, u16 _bytes) +{ + u32 i; + int word_count = (_bytes + 3) / 4; + u32 *fifo = dev_if->data_fifo[0]; + u32 *data_buff = (u32 *) ep->xfer_buff; + u32 unaligned; + /* + * This requires reading data from the FIFO into a u32 temp buffer, + * then moving it into the data buffer. + */ + if ((_bytes < 4) && (_bytes > 0)) { + unaligned = readl(fifo); + memcpy(data_buff, &unaligned, _bytes); + } else { + for (i = 0; i < word_count; i++, data_buff++) + *data_buff = readl(fifo); + } +} + +/* Handle RX transaction on non-ISO endpoint. */ +static void dw_udc_epn_rx(struct dwc_ep *ep, int bcnt) +{ + struct urb *urb; + struct usb_endpoint_instance *endpoint = dw_find_ep(ep->num); + + if (endpoint) { + urb = endpoint->rcv_urb; + + if (urb) { + ep->xfer_buff = urb->buffer + urb->actual_length; + dwc_otg_read_packet(ep, bcnt); + usbd_rcv_complete(endpoint, bcnt, 0); + } + } +} + +/* + * This function writes a packet into the Tx FIFO associated with the EP. + * The buffer is padded to DWORD on a per packet basis in + * slave/dma mode if the MPS is not DWORD aligned. The last packet, if + * short, is also padded to a multiple of DWORD. + * + * ep->xfer_buff always starts DWORD aligned in memory and is a + * multiple of DWORD in length + * + * ep->xfer_len can be any number of bytes + * + * FIFO access is DWORD + */ +static void dwc_otg_ep_write_packet(struct dwc_ep *ep) +{ + u32 i; + u32 dword_count; + u32 *fifo; + u32 *data_buff = (u32 *) ep->xfer_buff; + u32 temp, unaligned; + struct device_in_ep_regs *in_ep_regs = dev_if->in_ep_regs[ep->num]; + struct core_global_regs *core_global_regs = dev_if->core_global_regs; + + /* + * Find the DWORD length, padded by extra bytes as neccessary if MPS + * is not a multiple of DWORD + */ + dword_count = (ep->xfer_len + 3) / 4; + fifo = dev_if->data_fifo[ep->num]; + + /* program pkt count */ + temp = ep->xfer_len; + temp |= (1 << PKTCNT_SHIFT); + writel(temp, &in_ep_regs->dieptsiz); + + /* enable EP*/ + temp = readl(&in_ep_regs->diepctl); + temp |= (EPENA | CNAK); + writel(temp, &in_ep_regs->diepctl); + + /* clear TX Fifo Empty intr*/ + writel(NPTXFEMPTY, &core_global_regs->gintsts); + + temp = readl(&core_global_regs->gintmsk); + temp |= NPTXFEMPTY; + writel(temp, &core_global_regs->gintmsk); + + while (!(readl(&core_global_regs->gintsts) & NPTXFEMPTY)) + ; + + /* write to fifo */ + if ((ep->xfer_len < 4) && (ep->xfer_len > 0)) { + memcpy(&unaligned, data_buff, ep->xfer_len); + *fifo = unaligned; + } else { + for (i = 0; i < dword_count; i++, data_buff++) + *fifo = *data_buff; + } + + writel(NPTXFEMPTY, &core_global_regs->gintsts); + + /* check for transfer completion*/ + while (!(readl(&in_ep_regs->diepint) & XFERCOMPL)) + ; + + writel(XFERCOMPL, &in_ep_regs->diepint); + + temp = readl(&core_global_regs->gintmsk); + temp &= ~NPTXFEMPTY; + writel(temp, &core_global_regs->gintmsk); +} + +/* Handle TX transaction on non-ISO endpoint. */ +static void dw_udc_epn_tx(struct dwc_ep *ep) +{ + struct usb_endpoint_instance *endpoint = dw_find_ep(ep->num); + struct urb *urb = endpoint->tx_urb; + int align; + + if (!endpoint) + return; + + /* + * We need to transmit a terminating zero-length packet now if + * we have sent all of the data in this URB and the transfer + * size was an exact multiple of the packet size. + */ + if (urb && (endpoint->last == endpoint->tx_packetSize) && + (urb->actual_length - endpoint->sent - + endpoint->last == 0)) { + /* handle zero length packet here */ + ep->xfer_len = 0; + dwc_otg_ep_write_packet(ep); + } + + if (urb && urb->actual_length) { + /* retire the data that was just sent */ + usbd_tx_complete(endpoint); + /* + * Check to see if we have more data ready to transmit + * now. + */ + if (urb && urb->actual_length) { + /* write data to FIFO */ + ep->xfer_len = MIN(urb->actual_length - endpoint->sent, + endpoint->tx_packetSize); + + if (ep->xfer_len) { + ep->xfer_buff = urb->buffer + endpoint->sent; + + /* + * This ensures that USBD packet fifo is + * accessed through word aligned pointer or + * through non word aligned pointer but only + * with a max length to make the next packet + * word aligned + */ + + align = ((ulong)ep->xfer_buff % sizeof(int)); + if (align) + ep->xfer_len = MIN(ep->xfer_len, + sizeof(int) - align); + + dwc_otg_ep_write_packet(ep); + } + endpoint->last = ep->xfer_len; + + } else if (urb && (urb->actual_length == 0)) { + /* udc_set_nak(ep); */ + } + } +} + +/* This function returns pointer to out ep struct with number num */ +struct dwc_ep *get_out_ep(u32 num) +{ + u32 i; + int num_out_eps = MAX_EPS_CHANNELS; + struct dwc_pcd *pcd = &dev_if->pcd; + + if (num == 0) { + return &pcd->ep0; + } else { + for (i = 0; i < num_out_eps; ++i) { + if (pcd->out_ep[i].num == num) + return &pcd->out_ep[i]; + } + } + return 0; +} + +/* This function returns pointer to in ep struct with number num */ +struct dwc_ep *get_in_ep(u32 num) +{ + u32 i; + int num_out_eps = MAX_EPS_CHANNELS; + struct dwc_pcd *pcd = &dev_if->pcd; + + if (num == 0) { + return &pcd->ep0; + } else { + for (i = 0; i < num_out_eps; ++i) { + if (pcd->in_ep[i].num == num) + return &pcd->in_ep[i]; + } + } + return 0; +} + +/* + * This function reads the 8 bytes of the setup packet from the Rx FIFO into the + * destination buffer. It is called from the Rx Status Queue Level (RxStsQLvl) + * interrupt routine when a SETUP packet has been received in Slave mode. + */ +static void dwc_otg_read_setup_packet(u32 *dest) +{ + dest[0] = readl(dev_if->data_fifo[0]); + dest[1] = readl(dev_if->data_fifo[0]); +} + +/* + * This function handles the Rx Status Queue Level Interrupt, which + * indicates that there is a least one packet in the Rx FIFO. The + * packets are moved from the FIFO to memory, where they will be + * processed when the Endpoint Interrupt Register indicates Transfer + * Complete or SETUP Phase Done. + * + * Repeat the following until the Rx Status Queue is empty: + * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet + * info + * -# If Receive FIFO is empty then skip to step Clear the interrupt + * and exit + * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the + * SETUP data to the buffer + * -# If OUT Data Packet call dwc_otg_read_packet to copy the data + * to the destination buffer + */ +static int dwc_otg_pcd_handle_rx_status_q_level_intr(void) +{ + struct core_global_regs *global_regs = dev_if->core_global_regs; + struct dwc_pcd *pcd = &dev_if->pcd; + u32 gintmsk; + u32 status; + struct dwc_ep *ep; + u32 bcnt; + + /* Disable the Rx Status Queue Level interrupt */ + gintmsk = readl(&global_regs->gintmsk); + gintmsk &= ~RXSTSQLVL; + writel(gintmsk, &global_regs->gintmsk); + + /* Get the Status from the top of the FIFO */ + status = readl(&global_regs->grxstsp); + /* Get pointer to EP structure */ + ep = get_out_ep((status & EPNUMMSK) >> EPNUM_SHIFT); + bcnt = (status & BCNTMSK) >> BCNT_SHIFT; + + switch ((status & PKTSTSMSK) >> PKTSTS_SHIFT) { + case DWC_DSTS_GOUT_NAK: + break; + case DWC_STS_DATA_UPDT: + if (bcnt) + dw_udc_epn_rx(ep, bcnt); + break; + case DWC_STS_XFER_COMP: + break; + case DWC_DSTS_SETUP_COMP: + break; + case DWC_DSTS_SETUP_UPDT: + dwc_otg_read_setup_packet((u32 *)pcd->req); + break; + default: + break; + } + + /* Enable the Rx Status Queue Level interrupt */ + gintmsk = readl(&global_regs->gintmsk); + gintmsk |= RXSTSQLVL; + writel(gintmsk, &global_regs->gintmsk); + + /* Clear interrupt */ + writel(RXSTSQLVL, &global_regs->gintsts); + + return 1; +} + +/* + * This function starts the Zero-Length Packet for the IN status phase + * of a 2 stage control transfer. + */ +static void do_setup_in_status_phase(struct device_if *dev_if) +{ + struct device_out_ep_regs *out_regs = + dev_if->out_ep_regs[0]; + u32 doepctl, doeptsiz; + doeptsiz = 0; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl |= (CNAK | EPENA); + writel(doepctl, &out_regs->doepctl); +} + +static void udc_set_stall(int epid, int dir) +{ + if (dir) + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SSTALL, + &dev_if->in_ep_regs[epid]->diepctl); + else + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SSTALL, + &dev_if->out_ep_regs[epid]->doepctl); +} + +/* + * This function handles EP0 Control transfers. + * + * The state of the control tranfers are tracked in ep0state + * + * A flag set indicates that it is not the first packet, so do not + * process setup data now. it has alreday been processed, just send the + * next data packet + */ +void handle_ep0(int in_flag) +{ + struct dwc_pcd *pcd = &dev_if->pcd; + struct dwc_ep *ep0 = &pcd->ep0; + struct usb_device_request *ctrl = pcd->req; + + /* handle inepint, only when more than 64 bytes to transfer*/ + if (in_flag & !ep0_urb->actual_length) + return; + + if (!ep0_urb->actual_length) { + if (ep0_recv_setup(ep0_urb)) { + udc_set_stall(0, ctrl->bmRequestType & USB_DIR_IN); + return; + } + if (ep0_urb->device->address) + udc_set_addr_ctrl(ep0_urb->device->address); + + if (ep0_urb->device->configuration) + udc_set_cfg_ctrl(ep0_urb->device->configuration); + + ep0->xfer_buff = (u8 *)ep0_urb->buffer; + } else + ep0->xfer_buff += EP0_MAX_PACKET_SIZE; + + if (ep0_urb->actual_length <= EP0_MAX_PACKET_SIZE) { + ep0->xfer_len = ep0_urb->actual_length; + ep0_urb->actual_length = 0; + } else { + ep0->xfer_len = EP0_MAX_PACKET_SIZE; + ep0_urb->actual_length -= EP0_MAX_PACKET_SIZE; + } + + if (ctrl->bmRequestType & USB_DIR_IN) { + dwc_otg_ep_write_packet(ep0); + if (!ep0_urb->actual_length) + do_setup_in_status_phase(dev_if); + } else { + if (!ctrl->wLength) + dwc_otg_ep_write_packet(ep0); + else + udc_set_stall(0, ctrl->bmRequestType & USB_DIR_OUT); + } +} + +/* + * This function reads the Device All Endpoints Interrupt register and + * returns the OUT endpoint interrupt bits. + */ +static u32 dwc_otg_read_dev_all_out_ep_intr(void) +{ + u32 v; + + v = readl(&dev_if->dev_global_regs->daint) & + readl(&dev_if->dev_global_regs->daintmsk); + return v >> 16; +} + +/* + * This function reads the Device All Endpoints Interrupt register and + * returns the IN endpoint interrupt bits. + */ +static u32 dwc_otg_read_dev_all_in_ep_intr(void) +{ + u32 v; + + v = readl(&dev_if->dev_global_regs->daint) & + readl(&dev_if->dev_global_regs->daintmsk); + return v & 0xffff; +} + +/* This function returns the Device OUT EP Interrupt register */ +static u32 dwc_otg_read_doep_intr(struct dwc_ep *ep) +{ + u32 v; + + v = readl(&dev_if->out_ep_regs[ep->num]->doepint) & + readl(&dev_if->dev_global_regs->doepmsk); + return v; +} + +/*This function returns the Device IN EP Interrupt register */ +static u32 dwc_otg_read_diep_intr(struct dwc_ep *ep) +{ + u32 v; + + v = readl(&dev_if->in_ep_regs[ep->num]->diepint) & + readl(&dev_if->dev_global_regs->diepmsk); + return v; +} + +/* + * This function configures EPO to receive SETUP packets. + * + * Program the following fields in the endpoint specific registers for Control + * OUT EP 0, in order to receive a setup packet: + * + * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back setup packets) + * + * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back to back setup + * packets) + * + * In DMA mode, DOEPDMA0 Register with a memory address to store any setup + * packets received + */ +static void ep0_out_start(void) +{ + u32 temp; + + /* program transfer size*/ + temp = 8 * 3; + /* program packet count*/ + temp |= PKTCNT; + /* program setup packet count */ + temp |= (3 << SUPCNT_SHIFT); + writel(temp, &dev_if->out_ep_regs[0]->doeptsiz); +} + +/* should be called after set address is received */ +static void udc_set_addr_ctrl(u32 address) +{ + u32 dcfg; + + dcfg = readl(&dev_if->dev_global_regs->dcfg); + dcfg &= ~DEVADDRMSK; + dcfg |= address << DEVADDR_SHIFT; + writel(dcfg, &dev_if->dev_global_regs->dcfg); + + usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0); +} + +/* should be called after set configuration is received */ +static void dwc_otg_bulk_out_activate(void) +{ + struct device_out_ep_regs *out_regs = + dev_if->out_ep_regs[UDC_OUT_ENDPOINT]; + struct device_global_regs *dev_global_regs + = dev_if->dev_global_regs; + u32 doepctl, doeptsiz, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_OUT_ENDPOINT + DAINTMASK_OUT_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl &= ~DOEPCTL_MPSMSK; + doepctl &= ~EPTYPEMSK; + doepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE | + CNAK | EPENA | USBACTEP | DATA0PID + | (EPTYPE_BULK << EPTYPE_SHIFT)); + writel(doepctl, &out_regs->doepctl); +} + +/* should be called after set configuration is received */ +static void dwc_otg_bulk_in_activate(void) +{ + struct device_in_ep_regs *in_regs = + dev_if->in_ep_regs[UDC_IN_ENDPOINT]; + struct device_global_regs *dev_global_regs + = dev_if->dev_global_regs; + u32 diepctl, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_IN_ENDPOINT + DAINTMASK_IN_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + + diepctl = readl(&in_regs->diepctl); + diepctl &= ~DIEPCTL_MPSMSK; + diepctl &= ~EPTYPEMSK; + diepctl |= (CONFIG_USBD_SERIAL_BULK_PKTSIZE + | USBACTEP | DATA0PID + | (EPTYPE_BULK << EPTYPE_SHIFT)); + writel(diepctl, &in_regs->diepctl); +} + +static void dwc_otg_int_in_activate(void) +{ + struct device_in_ep_regs *in_regs = + dev_if->in_ep_regs[UDC_INT_ENDPOINT]; + struct device_global_regs *dev_global_regs + = dev_if->dev_global_regs; + u32 diepctl, daint; + + daint = readl(&dev_global_regs->daintmsk); + daint |= 1 << (UDC_INT_ENDPOINT + DAINTMASK_IN_SHIFT); + writel(daint, &dev_global_regs->daintmsk); + + diepctl = readl(&in_regs->diepctl); + diepctl &= ~DIEPCTL_MPSMSK; + diepctl &= ~EPTYPEMSK; + diepctl |= (UDC_INT_PACKET_SIZE + | USBACTEP | DATA0PID + | (EPTYPE_INT << EPTYPE_SHIFT)); + writel(diepctl, &in_regs->diepctl); +} + +/* should be called after set configuration is received */ +static void udc_set_cfg_ctrl(u32 config) +{ + dwc_otg_bulk_out_activate(); + dwc_otg_bulk_in_activate(); + dwc_otg_int_in_activate(); + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); +} + +/* should be called to receive next packet */ +static void dwc_otg_bulk_out_enable(void) +{ + struct device_out_ep_regs *out_regs = + dev_if->out_ep_regs[UDC_OUT_ENDPOINT]; + u32 doepctl, doeptsiz; + + doeptsiz = CONFIG_USBD_SERIAL_BULK_PKTSIZE; + doeptsiz |= (1 << PKTCNT_SHIFT); + writel(doeptsiz, &out_regs->doeptsiz); + doepctl = readl(&out_regs->doepctl); + doepctl |= CNAK | EPENA; + writel(doepctl, &out_regs->doepctl); +} + +/* This interrupt indicates that an OUT EP has a pending Interrupt. */ + +static int dwc_otg_pcd_handle_out_ep_intr(void) +{ + u32 ep_intr; + u32 doepint; + u32 epnum = 0; + struct dwc_ep *dwc_ep; + struct device_out_ep_regs **out_ep_regs + = dev_if->out_ep_regs; + + /* Read in the device interrupt bits */ + ep_intr = dwc_otg_read_dev_all_out_ep_intr(); + while (ep_intr) { + if (ep_intr & 0x1) { + dwc_ep = get_out_ep(epnum); + doepint = dwc_otg_read_doep_intr(dwc_ep); + + /* Transfer complete */ + if (doepint & XFERCOMPL) { + /* Clear xfercompl */ + writel(XFERCOMPL, &out_ep_regs[epnum]->doepint); + if (!epnum) + ep0_out_start(); + else if (epnum == UDC_OUT_ENDPOINT) + dwc_otg_bulk_out_enable(); + } + /* Setup Phase Done (control EPs) */ + if (doepint & SETUP) { + writel(SETUP, &out_ep_regs[epnum]->doepint); + handle_ep0(0); + } + } + epnum++; + ep_intr >>= 1; + } + return 1; +} + +/* This interrupt indicates that an IN EP has a pending Interrupt. */ + +static int dwc_otg_pcd_handle_in_ep_intr(void) +{ + u32 ep_intr; + u32 diepint; + u32 epnum = 0; + struct dwc_ep *dwc_ep; + struct device_in_ep_regs **in_ep_regs + = dev_if->in_ep_regs; + + /* Read in the device interrupt bits */ + ep_intr = dwc_otg_read_dev_all_in_ep_intr(); + while (ep_intr) { + if (ep_intr & 0x1) { + dwc_ep = get_in_ep(epnum); + diepint = dwc_otg_read_diep_intr(dwc_ep); + + /* IN token received when txfifo empty */ + if (diepint & INTKNTXFEMP) { + /* Clear xfercompl */ + writel(INTKNTXFEMP, + &in_ep_regs[epnum]->diepint); + if (!epnum) + handle_ep0(1); + else if (epnum == UDC_IN_ENDPOINT) + dw_udc_epn_tx(dwc_ep); + } + } + epnum++; + ep_intr >>= 1; + } + return 1; +} + +static void dwc_otg_flush_tx_fifo(const int num) +{ + struct core_global_regs *global_regs = dev_if->core_global_regs; + u32 val = 0; + int count = 0; + + val = readl(&global_regs->grstctl); + val |= TXFFLSH; + val &= ~TXFNUM; + val |= (num << TXFNUM_SHIFT); + writel(val, &global_regs->grstctl); + + do { + val = readl(&global_regs->grstctl); + if (++count > 10000) + break; + udelay(1); + } while (val & TXFFLSH); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +static void dwc_otg_flush_rx_fifo(void) +{ + struct core_global_regs *global_regs = dev_if->core_global_regs; + int count = 0; + u32 val = 0; + + val = readl(&global_regs->grstctl); + val |= RXFFLSH; + writel(val, &global_regs->grstctl); + + do { + val = readl(&global_regs->grstctl); + if (++count > 10000) + break; + udelay(1); + } while (val & RXFFLSH); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +/* + * This interrupt occurs when a USB Reset is detected. When the USB Reset + * Interrupt occurs the device state is set to DEFAULT and the EP0 state is set + * to IDLE. + * + */ +static int dwc_otg_pcd_handle_usb_reset_intr(void) +{ + u32 temp; + u32 i; + u32 gintmsk; + struct device_out_ep_regs **out_ep_regs + = dev_if->out_ep_regs; + struct device_global_regs *dev_global_regs + = dev_if->dev_global_regs; + struct core_global_regs *core_global_regs + = dev_if->core_global_regs; + /* Set NAK for all OUT EPs */ + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + temp = readl(&out_ep_regs[i]->doepctl); + temp |= SNAK; + writel(temp, &out_ep_regs[i]->doepctl); + } + + /* Flush the NP Tx FIFO */ + dwc_otg_flush_tx_fifo(DWC_GRSTCTL_TXFNUM_ALL); + dwc_otg_flush_rx_fifo(); + writel((1 << (DAINTMASK_IN_SHIFT + 0)) + | (1 << (DAINTMASK_OUT_SHIFT + 0)), + &dev_global_regs->daintmsk); + + writel(SETUPMSK | XFERCOMPLMSK | AHBERRMSK | EPDISABLEDMSK, + &dev_global_regs->doepmsk); + + writel(INTKNTXFEMP, &dev_global_regs->diepmsk); + + gintmsk = readl(&core_global_regs->gintmsk); + gintmsk |= GOUTNAKEFF; + writel(gintmsk, &core_global_regs->gintmsk); + + /* program fifo size for ep0 */ + + writel(0x200, &core_global_regs->grxfsiz); + + temp = readl(&dev_if->in_ep_regs[0]->diepctl); + temp &= 0xFFC3FFFF; /* TxFNumBF = 0, bits 25:22 */ + writel(temp, &dev_if->in_ep_regs[0]->diepctl); + + temp = readl(&core_global_regs->gnptxfsiz); + + writel(0x2000000, &core_global_regs->gnptxfsiz); + + /* Reset Device Address */ + temp = readl(&dev_global_regs->dcfg); + temp &= ~DEVADDRMSK; + writel(temp, &dev_global_regs->dcfg); + + /* setup EP0 to receive SETUP packets */ + ep0_out_start(); + + /* Clear interrupt */ + writel(USBRESET, &core_global_regs->gintsts); + + UDCDBG("device reset in progess"); + usbd_device_event_irq(udc_device, DEVICE_HUB_CONFIGURED, 0); + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + + return 1; +} + +/* + * This function enables EP0 OUT to receive SETUP packets and configures EP0 + * IN for transmitting packets. It is normally called when the "Enumeration + * Done" interrupt occurs. + */ +static void dwc_otg_ep0_activate(void) +{ + u32 temp; + struct device_in_ep_regs *in_ep_regs = dev_if->in_ep_regs[0]; + struct device_out_ep_regs *out_ep_regs = dev_if->out_ep_regs[0]; + + /* Read the Device Status and Endpoint 0 Control registers */ + temp = readl(&in_ep_regs->diepctl); + temp &= ~MPSMSK0; + temp |= DWC_DEP0CTL_MPS_64; + writel(temp, &in_ep_regs->diepctl); + + temp = readl(&out_ep_regs->doepctl); + /* Enable OUT EP for receive */ + temp |= EPENA; + writel(temp, &out_ep_regs->doepctl); +} + +/* + * Read the device status register and set the device speed in the + * data structure. + * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate. + */ +static int dwc_otg_pcd_handle_enum_done_intr(void) +{ + u32 gusbcfg; + struct core_global_regs *global_regs = dev_if->core_global_regs; + dwc_otg_ep0_activate(); + + gusbcfg = readl(&global_regs->gusbcfg); + gusbcfg &= ~USBTRDTIMMSK; + gusbcfg |= PHYIF_16BIT; + gusbcfg |= (9 << USBTRDTIM_SHIFT); + writel(gusbcfg, &global_regs->gusbcfg); + /* Clear interrupt */ + writel(ENUMDONE, &global_regs->gintsts); + + return 1; +} + +static u32 dwc_otg_read_core_intr(void) +{ + return readl(&dev_if->core_global_regs->gintsts) & + readl(&dev_if->core_global_regs->gintmsk); +} + +static void dwc_otg_init(const void *reg_base) +{ + struct dwc_pcd *pcd = &dev_if->pcd; + u32 offset; + u32 i; + + dev_if->core_global_regs = (struct core_global_regs *) reg_base; + dev_if->dev_global_regs = (struct device_global_regs *) ((u32)reg_base + + DWC_DEV_GLOBAL_REG_OFFSET); + + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + offset = i * DWC_EP_REG_OFFSET; + + dev_if->in_ep_regs[i] = (struct device_in_ep_regs *) + ((u32)reg_base + DWC_DEV_IN_EP_REG_OFFSET + offset); + + dev_if->out_ep_regs[i] = (struct device_out_ep_regs *) + ((u32)reg_base + DWC_DEV_OUT_EP_REG_OFFSET + offset); + } + + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + dev_if->data_fifo[i] = + (u32 *) ((u32)reg_base + DWC_OTG_DATA_FIFO_OFFSET + + (i * DWC_OTG_DATA_FIFO_SIZE)); + } + + dev_if->speed = 0; /* unknown */ + for (i = 0; i < MAX_EPS_CHANNELS; i++) { + pcd->in_ep[i].num = i; + pcd->out_ep[i].num = i; + } +} + +/* + * This function initializes the DWC_otg controller registers and prepares the + * core for device mode + */ +static void dwc_otg_core_init(void) +{ + struct core_global_regs *global_regs = dev_if->core_global_regs; + u32 ahbcfg, gintmsk, usbcfg; + /* Step 1: Program the GAHBCFG Register. */ + ahbcfg = DWC_NPTXEMPTYLVL_EMPTY | DWC_PTXEMPTYLVL_EMPTY; + writel(ahbcfg, &global_regs->gahbcfg); + + /* Step 2: write usbcfg regs*/ + usbcfg = readl(&global_regs->gusbcfg); + usbcfg |= SRPCAP | HNPCAP; + writel(usbcfg, &global_regs->gusbcfg); + + /* step3: write int_msk reg*/ + gintmsk = USBRESET | ENUMDONE | RXSTSQLVL | OUTEPINTR | INEPINTR; + writel(gintmsk, &global_regs->gintmsk); +} + +/* Switch on the UDC */ +static void usbotg_init(void) +{ + udc_device = NULL; + dwc_otg_init((void *)CONFIG_SYS_USBD_BASE); + + /* Initialize the DWC_otg core. */ + dwc_otg_core_init(); + +} + +void udc_irq(void) +{ + u32 status; + + status = dwc_otg_read_core_intr(); + while (status) { + if (status & USBRESET) + dwc_otg_pcd_handle_usb_reset_intr(); + if (status & ENUMDONE) + dwc_otg_pcd_handle_enum_done_intr(); + if (status & RXSTSQLVL) + dwc_otg_pcd_handle_rx_status_q_level_intr(); + if (status & OUTEPINTR) + dwc_otg_pcd_handle_out_ep_intr(); + if (status & INEPINTR) + dwc_otg_pcd_handle_in_ep_intr(); + status = dwc_otg_read_core_intr(); + } + +} + +void udc_set_nak(int epid) +{ + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | SNAK, + &dev_if->out_ep_regs[epid]->doepctl); + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | SNAK, + &dev_if->in_ep_regs[epid]->diepctl); +} + +void udc_unset_nak(int epid) +{ + writel(readl(&dev_if->out_ep_regs[epid]->doepctl) | CNAK, + &dev_if->out_ep_regs[epid]->doepctl); + writel(readl(&dev_if->in_ep_regs[epid]->diepctl) | CNAK, + &dev_if->in_ep_regs[epid]->diepctl); +} + +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) +{ + udc_unset_nak(endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK); + return 0; +} + +static void udc_enable(struct usb_device_instance *device) +{ + struct dwc_pcd *pcd = &dev_if->pcd; + + UDCDBGA("enable device %p, status %d", device, device->status); + + /* Save the device structure pointer */ + udc_device = device; + + /* Setup ep0 urb */ + if (!ep0_urb) { + ep0_urb = + usbd_alloc_urb(udc_device, + udc_device->bus->endpoint_array); + pcd->req = + (struct usb_device_request *)&ep0_urb->device_request; + pcd->ep0.xfer_buff = (u8 *)ep0_urb->buffer; + } else { + serial_printf("udc_enable: ep0_urb already allocated %p\n", + ep0_urb); + } +} + +void udc_connect(void) +{ + struct device_global_regs *dev_regs = dev_if->dev_global_regs; + u32 dcfg; + + /* remove soft disconnect */ + dcfg = readl(&dev_regs->dctl); + dcfg &= ~SFTDISCON; + writel(dcfg, &dev_regs->dctl); +} + +void udc_disconnect(void) +{ + struct device_global_regs *dev_regs = dev_if->dev_global_regs; + u32 dcfg; + + /* soft disconnect */ + dcfg = readl(&dev_regs->dctl); + dcfg |= SFTDISCON; + writel(dcfg, &dev_regs->dctl); + udelay(150); +} + +void udc_startup_events(struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ + usbd_device_event_irq(device, DEVICE_INIT, 0); + + /* + * The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED. + */ + usbd_device_event_irq(device, DEVICE_CREATE, 0); + + /* + * Some USB controller driver implementations signal + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. + * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, + * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. + * The DW USB client controller has the capability to detect when the + * USB cable is connected to a powered USB bus, so we will defer the + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events until later. + */ + + udc_enable(device); + +} + +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint) +{ + /* + * Nothing to do here. Hob of this function has laready been + * done during init. + */ +} + +int is_usbd_high_speed(void) +{ + struct device_global_regs *dev_regs = dev_if->dev_global_regs; + u32 dsts; + + dsts = readl(&dev_regs->dsts); + dsts &= ENUMSPDMSK; + if (dsts == DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ) + return 1; + else + return 0; +} + +int udc_init(void) +{ +#if defined(CONFIG_DW_OTG_PHYINIT) + udc_phy_init(); +#endif + udc_disconnect(); + usbotg_init(); + return 0; +} diff --git a/include/usb/designware_otg.h b/include/usb/designware_otg.h new file mode 100644 index 0000000..98d016e --- /dev/null +++ b/include/usb/designware_otg.h @@ -0,0 +1,500 @@ +/* + * (C) Copyright 2011 + * Pratyush Anand, ST Micoelectronics, pratyush.anand@st.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 + */ + +#ifndef __DW_OTG_H +#define __DW_OTG_H + +#include "usbdevice.h" + +/* USBTTY definitions */ +#define EP0_MAX_PACKET_SIZE 64 +#define UDC_INT_ENDPOINT 1 +#define UDC_INT_PACKET_SIZE 64 +#define UDC_OUT_ENDPOINT 2 +#define UDC_BULK_PACKET_SIZE 512 +#if defined(CONFIG_USBD_HS) +#define UDC_BULK_HS_PACKET_SIZE 512 +#endif +#define UDC_IN_ENDPOINT 3 +#define UDC_OUT_PACKET_SIZE 64 +#define UDC_IN_PACKET_SIZE 64 + +/* UDC endpoint definitions */ +#define UDC_EP0 0 +#define UDC_EP1 1 +#define UDC_EP2 2 +#define UDC_EP3 3 + +/* OTG Register Definitions */ + +/* + * The application interfaces with the HS OTG core by reading from and + * writing to the Control and Status Register (CSR) space through the + * AHB Slave interface. These registers are 32 bits wide, and the + * addresses are 32-bit-block aligned. + * CSRs are classified as follows: + * - Core Global Registers + * - Device Mode Registers + * - Device Global Registers + * - Device Endpoint Specific Registers + * - Host Mode Registers + * - Host Global Registers + * - Host Port CSRs + * - Host Channel Specific Registers + * + * Only the Core Global registers can be accessed in both Device and + * Host modes. When the HS OTG core is operating in one mode, either + * Device or Host, the application must not access registers from the + * other mode. When the core switches from one mode to another, the + * registers in the new mode of operation must be reprogrammed as they + * would be after a power-on reset. + */ + +/* + * DWC_otg Core registers. The core_global_regs structure defines the + * size and relative field offsets for the Core Global registers. + */ +struct core_global_regs { + /* OTG Control and Status Register. Offset: 000h */ + u32 gotgctl; + /* OTG Interrupt Register. Offset: 004h */ + u32 gotgint; + /* Core AHB Configuration Register. Offset: 008h */ + u32 gahbcfg; + +#define DWC_GLBINTRMASK 0x0001 +#define DWC_DMAENABLE 0x0020 +#define DWC_NPTXEMPTYLVL_EMPTY 0x0080 +#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000 +#define DWC_PTXEMPTYLVL_EMPTY 0x0100 +#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000 + + /* Core USB Configuration Register. Offset: 00Ch */ + u32 gusbcfg; +#define PHYIF_16BIT (1 << 3) +#define SRPCAP (1 << 8) +#define HNPCAP (1 << 9) +#define TERM_SEL_DL_PULSE (1 << 22) +#define USBTRDTIM_SHIFT 10 +#define USBTRDTIMMSK (0xF << USBTRDTIM_SHIFT) + /* Core Reset Register. Offset: 010h */ + u32 grstctl; +#define DWC_GRSTCTL_TXFNUM_ALL 0x10 +#define CSFTRST (1 << 0) +#define INTKNQFLSH (1 << 3) +#define RXFFLSH (1 << 4) +#define TXFFLSH (1 << 5) +#define TXFNUM_SHIFT 6 +#define TXFNUM (0x1F << TXFNUM_SHIFT) +#define AHBIDLE ((u32)1 << 31) + /* Core Interrupt Register. Offset: 014h */ + u32 gintsts; +#define RXSTSQLVL (1 << 4) +#define NPTXFEMPTY (1 << 5) +#define GOUTNAKEFF (1 << 7) +#define USBRESET (1 << 12) +#define ENUMDONE (1 << 13) +#define INEPINTR (1 << 18) +#define OUTEPINTR (1 << 19) + /* Core Interrupt Mask Register. Offset: 018h */ + u32 gintmsk; + /* + * Receive Status Queue Read Register + * (Read Only) Offset: 01Ch + */ + u32 grxstsr; + /* + * Receive Status Queue Read & POP Register + * (Read Only) Offset: 020h + */ + u32 grxstsp; +#define DWC_STS_DATA_UPDT 0x2 /* OUT Data Packet */ +#define DWC_STS_XFER_COMP 0x3 /* OUT Data Transfer Complete */ +#define DWC_DSTS_GOUT_NAK 0x1 /* Global OUT NAK */ +#define DWC_DSTS_SETUP_COMP 0x4 /* Setup Phase Complete */ +#define DWC_DSTS_SETUP_UPDT 0x6 /* SETUP Packet */ +#define EPNUM_SHIFT 0 +#define EPNUMMSK (0xF << EPNUM_SHIFT) +#define BCNT_SHIFT 4 +#define BCNTMSK (0x7FF << BCNT_SHIFT) +#define PKTSTS_SHIFT 17 +#define PKTSTSMSK (0xF << PKTSTS_SHIFT) + /* Receive FIFO Size Register. Offset: 024h */ + u32 grxfsiz; +#define dwc_param_dev_rx_fifo_size_default 1064 + /* Non Periodic Transmit FIFO Size Register. Offset: 028h */ + u32 gnptxfsiz; +#define dwc_param_dev_nperio_tx_fifo_size_default 1024 + /* + * Non Periodic Transmit FIFO/Queue Status Register + * (Read Only). Offset: 02Ch + */ + u32 gnptxsts; +#define NPTXQSPCAVAIL_SHIFT 16 +#define NPTXQSPCAVAILMSK (0xFF << NPTXQSPCAVAIL_SHIFT) +#define NPTXFSPCAVAIL_SHIFT 0 +#define NPTXFSPCAVAILMSK (0xFFFF << NPTXFSPCAVAIL_SHIFT) + /* I2C Access Register. Offset: 030h */ + u32 gi2cctl; + /* PHY Vendor Control Register. Offset: 034h */ + u32 gpvndctl; + /* General Purpose Input/Output Register. Offset: 038h */ + u32 ggpio; + /* User ID Register. Offset: 03Ch */ + u32 guid; + /* Synopsys ID Register (Read Only). Offset: 040h */ + u32 gsnpsid; + /* User HW Config1 Register (Read Only). Offset: 044h */ + u32 ghwcfg1; + /* User HW Config2 Register (Read Only). Offset: 048h */ + + u32 ghwcfg2; +#define DWC_SLAVE_ONLY_ARCH 0 +#define DWC_EXT_DMA_ARCH 1 +#define DWC_INT_DMA_ARCH 2 + +#define DWC_MODE_HNP_SRP_CAPABLE 0 +#define DWC_MODE_SRP_ONLY_CAPABLE 1 +#define DWC_MODE_NO_HNP_SRP_CAPABLE 2 +#define DWC_MODE_SRP_CAPABLE_DEVICE 3 +#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4 +#define DWC_MODE_SRP_CAPABLE_HOST 5 +#define DWC_MODE_NO_SRP_CAPABLE_HOST 6 +#define DYNAMIC_FIFO (1 << 19) +#define NUM_DEV_EP_SHIFT 10 +#define NUM_DEV_EP (0xF << NUM_DEV_EP_SHIFT) +#define HSPHYTYPE_SHIFT 6 +#define HSPHYTYPEMSK (3 << HSPHYTYPE_SHIFT) +#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1 +#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2 +#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 +#define TKNQDEPTH_SHIFT 26 +#define TKNQDEPTHMSK (0x1F << TKNQDEPTH_SHIFT) + + /* User HW Config3 Register (Read Only). Offset: 04Ch */ + u32 ghwcfg3; +#define DFIFO_DEPTH_SHIFT 16 +#define DFIFO_DEPTH ((u32)0xFFFF << DFIFO_DEPTH_SHIFT) + /* User HW Config4 Register (Read Only). Offset: 050h */ + u32 ghwcfg4; +#define NUM_DEV_PERIO_IN_EP_SHIFT 0 +#define NUM_DEV_PERIO_IN_EP (0xF << NUM_DEV_PERIO_IN_EP_SHIFT) +#define DED_FIFO_EN (1 << 25) +#define NUM_IN_EPS_SHIFT 26 +#define NUM_IN_EPS (0xF << NUM_IN_EPS_SHIFT) +#define UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define UTMI_PHY_DATA_WIDTH (0x3 << UTMI_PHY_DATA_WIDTH_SHIFT) + /* Reserved Offset: 054h-0FFh */ + u32 reserved[43]; + /* Host Periodic Transmit FIFO Size Register. Offset: 100h */ + u32 hptxfsiz; + + /* + * Device Periodic Transmit FIFO#n Register, if dedicated fifos are + * disabled. Otherwise Device Transmit FIFO#n Register. + * + * Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15) + */ + u32 dptxfsiz_dieptxf[15]; +#define dwc_param_dev_tx_fifo_size_default 256 +#define dwc_param_dev_perio_tx_fifo_size_default 256 +}; + +/* + * Device Global Registers. Offsets 800h-BFFh + * + * The following structures define the size and relative field offsets for the + * Device Mode Registers. + * + * These registers are visible only in Device mode and must not be accessed in + * Host mode, as the results are unknown. + */ +struct device_global_regs { /* CONFIG_DWC_OTG_REG_LE */ + /* Device Configuration Register. Offset: 800h */ + u32 dcfg; +#define DWC_DCFG_FRAME_INTERVAL_80 0 +#define DWC_DCFG_FRAME_INTERVAL_85 1 +#define DWC_DCFG_FRAME_INTERVAL_90 2 +#define DWC_DCFG_FRAME_INTERVAL_95 3 +#define DWC_DCFG_FRAME_INTERVAL_MASK 3 +#define PERFRINT_SHIFT 11 +#define DEVSPDMSK (0x3 << 0) +#define DEVADDR_SHIFT 4 +#define DEVADDRMSK (0x7F << DEVADDR_SHIFT) +#define NZSTSOUTHSHK (1 << 2) + /* Device Control Register. Offset: 804h */ + u32 dctl; +#define RMTWKUPSIG (1 << 0) +#define SFTDISCON (1 << 1) +#define CGNPINNAK (1 << 7) + /* Device Status Register (Read Only). Offset: 808h */ + u32 dsts; +#define ENUMSPD_SHIFT 1 +#define ENUMSPDMSK (3 << ENUMSPD_SHIFT) +#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 +#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 +#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2 +#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3 + /* Reserved. Offset: 80Ch */ + u32 unused; + /* Device IN Endpoint Common Interrupt Mask Register. Offset: 810h */ + u32 diepmsk; +#define TIMEOUTMSK (1 << 3) +#define INTKNTXFEMP (1 << 4) +#define INTKNEPMISMSK (1 << 5) +#define INEPNAKEFFMSK (1 << 6) +#define TXFIFOUNDRN (1 << 8) + /* Device OUT Endpoint Common Interrupt Mask Register. Offset: 814h */ + u32 doepmsk; +#define XFERCOMPLMSK (1 << 0) +#define EPDISABLEDMSK (1 << 1) +#define AHBERRMSK (1 << 2) +#define SETUPMSK (1 << 3) +#define INTKNTXFEMPMSK (1 << 4) + /* Device All Endpoints Interrupt Register. Offset: 818h */ + u32 daint; + /* Device All Endpoints Interrupt Mask Register. Offset: 81Ch */ + u32 daintmsk; +#define DAINTMASK_IN_SHIFT 0 +#define DAINTMASK_OUT_SHIFT 16 + /* Device IN Token Queue Read Register-1 (Read Only). Offset: 820h */ + u32 dtknqr1; +#define EPTK0_5_SHIFT 8 +#define EPTK0_5MSK ((u32)0xFFFFFF << EPTK0_5_SHIFT) +#define INTKNWPTR_SHIFT 0 +#define INTKNWPTRMSK ((u32)0x1F << INTKNWPTR_SHIFT) + /* Device IN Token Queue Read Register-2 (Read Only). Offset: 824h */ + u32 dtknqr2; + /* Device VBUS discharge Register. Offset: 828h */ + u32 dvbusdis; + /* Device VBUS Pulse Register. Offset: 82Ch */ + u32 dvbuspulse; + /* Device IN Token Queue Read Register-3 (Read Only). Offset: 830h */ + u32 dtknqr3_dthrctl; + /* Device IN Token Queue Read Register-4 (Read Only). Offset: 834h */ + u32 dtknqr4_fifoemptymsk; +}; +/* + * Device Logical IN Endpoint-Specific Registers. Offsets 900h-AFCh + * + * There will be one set of endpoint registers per logical endpoint implemented. + * + * These registers are visible only in Device mode and must not be accessed in + * Host mode, as the results are unknown. + */ +struct device_in_ep_regs { + /* + * Device IN Endpoint Control Register. + * Offset:900h + (ep_num * 20h) + 00h + */ + u32 diepctl; +#define EPENA ((u32)1 << 31) +#define EPDIS (1 << 30) +#define SNAK (1 << 27) +#define CNAK (1 << 26) +#define SSTALL (1 << 21) +#define MPS_SHIFT 0 +#define MPSMSK0 (3 << MPS_SHIFT) +#define DWC_DEP0CTL_MPS_64 0 +#define DWC_DEP0CTL_MPS_32 1 +#define DWC_DEP0CTL_MPS_16 2 +#define DWC_DEP0CTL_MPS_8 3 +#define DIEPCTL_MPSMSK (0x7FF << MPS_SHIFT) + /* Reserved. Offset:900h + (ep_num * 20h) + 04h */ + u32 reserved04; + /* + * Device IN Endpoint Interrupt Register. + * Offset:900h + (ep_num * 20h) + 08h + */ + u32 diepint; +#define TXFEMP (1 << 7) +#define INTKNTXFEMP (1 << 4) +#define XFERCOMPL (1 << 0) + /* Reserved. Offset:900h + (ep_num * 20h) + 0Ch */ + u32 reserved0C; + /* Device IN Endpoint Transfer Size Register. + * Offset:900h + (ep_num * 20h) + 10h + */ + u32 dieptsiz; +#define PKTCNT_SHIFT 19 + /* + * Device IN Endpoint DMA Address Register. + * Offset:900h + (ep_num * 20h) + 14h + */ + u32 diepdma; + /* Reserved. + * Offset:900h + (ep_num * 20h) + 18h - 900h + (ep_num * 20h) + 1Ch + */ + u32 dtxfsts; + /* + * Reserved. + * Offset:900h + (ep_num * 20h) + 1Ch - 900h + (ep_num * 20h) + 1Ch + */ + u32 reserved18; +}; + +/* + * Device Logical OUT Endpoint-Specific Registers. Offsets: B00h-CFCh + * + * There will be one set of endpoint registers per logical endpoint implemented. + * + * These registers are visible only in Device mode and must not be accessed in + * Host mode, as the results are unknown. + */ +struct device_out_ep_regs { + /* + * Device OUT Endpoint Control Register. + * Offset:B00h + (ep_num * 20h) + 00h + */ + u32 doepctl; +#define DOEPCTL_MPSMSK 0x7FF +#define USBACTEP (1 << 15) +#define EPTYPE_SHIFT 18 +#define EPTYPEMSK (0x3 << EPTYPE_SHIFT) +#define EPTYPE_BULK 0x2 +#define EPTYPE_INT 0x3 +#define DATA0PID (1 << 28) +#define DATA1PID (1 << 29) +#define DPIDMSK (1 << 16) + /* + * Device OUT Endpoint Frame number Register. + * Offset: B00h + (ep_num * 20h) + 04h + */ + u32 doepfn; + /* + * Device OUT Endpoint Interrupt Register. + * Offset:B00h + (ep_num * 20h) + 08h + */ + u32 doepint; +#define XFERCOMPL (1 << 0) +#define EPDISBLD (1 << 1) +#define AHBERR (1 << 2) +#define SETUP (1 << 3) + /* Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */ + u32 reserved0C; + /* + * Device OUT Endpoint Transfer Size Register. + * Offset: B00h + (ep_num * 20h) + 10h + */ + u32 doeptsiz; +#define XFERSIZE_SHIFT 0 +#define XFERSIZEMSK 0x3F +#define PKTCNT_SHIFT 19 +#define PKTCNT (3 << 19) +#define SUPCNT_SHIFT 29 +#define SUPCNTMSK (3 << SUPCNT_SHIFT) + /* + * Device OUT Endpoint DMA Address Register. + * Offset:B00h + (ep_num * 20h) + 14h + */ + u32 doepdma; + /* + * Reserved. + * Offset:B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch + */ + u32 unused[2]; +}; +#define MAX_EPS_CHANNELS 4 + +/* + * The dwc_ep structure represents the state of a single endpoint when acting in + * device mode. It contains the data items needed for an endpoint to be + * activated and transfer packets. + */ +struct dwc_ep { + /* EP number used for register address lookup */ + u8 num; + /* pointer to the transfer buffer */ + u8 *xfer_buff; + /* Number of bytes to transfer */ + u32 xfer_len; +}; + +/* + * DWC_otg PCD Structure. + * This structure encapsulates the data for the dwc_otg PCD. + */ +struct dwc_pcd { + struct usb_device_request *req; + /* Array of EPs. */ + struct dwc_ep ep0; + /* Array of IN EPs. */ + struct dwc_ep in_ep[MAX_EPS_CHANNELS]; + /* Array of OUT EPs. */ + struct dwc_ep out_ep[MAX_EPS_CHANNELS]; +}; + +/* + * The device_if structure contains information needed to manage the DWC_otg + * controller acting in device mode. It represents the programming view of the + * device-specific aspects of the controller. + */ +struct device_if { + struct core_global_regs *core_global_regs; + /* Common configuration information */ + + /* Device Global Registers starting at offset 800h */ + struct device_global_regs *dev_global_regs; +#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 + + /* Device Logical IN Endpoint-Specific Registers 900h-AFCh */ + struct device_in_ep_regs *in_ep_regs[MAX_EPS_CHANNELS]; +#define DWC_DEV_IN_EP_REG_OFFSET 0x900 +#define DWC_EP_REG_OFFSET 0x20 + + /* Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ + struct device_out_ep_regs *out_ep_regs[MAX_EPS_CHANNELS]; +#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 + + /* Push/pop addresses for endpoints or host channels.*/ + u32 *data_fifo[MAX_EPS_CHANNELS]; +#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 +#define DWC_OTG_DATA_FIFO_SIZE 0x1000 + + struct dwc_pcd pcd; + int speed; +}; + + +/* Function declarations */ + +void phy_init(void); +void udc_irq(void); + +void udc_set_nak(int epid); +void udc_unset_nak(int epid); +int udc_endpoint_write(struct usb_endpoint_instance *endpoint); +int udc_init(void); +/* void udc_enable(struct usb_device_instance *device);*/ +void udc_disable(void); +void udc_connect(void); +void udc_disconnect(void); +#if defined(CONFIG_DW_OTG_PHYINIT) +void udc_phy_init(void); +#endif +void udc_startup_events(struct usb_device_instance *device); +void udc_setup_ep(struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint); + +#endif /* __DW_OTG_H */

Add EHCI support for spear boards
Signed-off-by: Armando Visconti armando.visconti@st.com Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/usb/host/Makefile | 1 + drivers/usb/host/ehci-spear.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 drivers/usb/host/ehci-spear.c
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6c94794..9a6f982 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -54,6 +54,7 @@ COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o COBJS-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o +COBJS-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c new file mode 100644 index 0000000..f99bd1f --- /dev/null +++ b/drivers/usb/host/ehci-spear.c @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2010 + * Armando Visconti, ST Micoelectronics, armando.visconti@st.com. + * + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar prafulla@marvell.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include "ehci.h" +#include <asm/arch/hardware.h> + + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ + *hccr = (struct ehci_hccr *)(CONFIG_SYS_UHC0_EHCI_BASE + 0x100); + *hcor = (struct ehci_hcor *)((uint32_t)*hccr + + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + debug("SPEAr-ehci: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)*hccr, (uint32_t)*hcor, + (uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +}

From: Shiraz Hashim shiraz.hashim@st.com
Increase buffer sizes at driver and tty level to accommodate kermit large packet support.
Signed-off-by: Shiraz Hashim shiraz.hashim@st.com --- drivers/serial/usbtty.c | 2 +- include/usbdevice.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index e47cb9a..148d1a6 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -63,7 +63,7 @@ /* * Buffers to hold input and output data */ -#define USBTTY_BUFFER_SIZE 256 +#define USBTTY_BUFFER_SIZE 2048 static circbuf_t usbtty_input; static circbuf_t usbtty_output;
diff --git a/include/usbdevice.h b/include/usbdevice.h index 3edaf8b..7037efd 100644 --- a/include/usbdevice.h +++ b/include/usbdevice.h @@ -475,7 +475,9 @@ typedef struct urb_link { * function driver to inform it that data has arrived. */
-#define URB_BUF_SIZE 128 /* in linux we'd malloc this, but in u-boot we prefer static data */ +/* in linux we'd malloc this, but in u-boot we prefer static data */ +#define URB_BUF_SIZE 512 + struct urb {
struct usb_endpoint_instance *endpoint;

From: Shiraz Hashim shiraz.hashim@st.com
SPEAr310 and SPEAr320 Ethernet interfaces share same MDIO lines to control their respective phys. Currently their is a fixed configuration in which only a particular MAC can use the MDIO lines.
Call an arch specific function to take control of specific mdio lines at runtime.
Signed-off-by: Shiraz Hashim shiraz.hashim@st.com Signed-off-by: Vipin Kumar vipin.kumar@st.com --- drivers/net/macb.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 0e1ced7..ac25b52 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -165,6 +165,13 @@ static u16 macb_mdio_read(struct macb_device *macb, u8 reg) return MACB_BFEXT(DATA, frame); }
+static void __def_arch_get_mdio_control(const char *name) +{ + return; +} +int arch_get_mdio_control(const char *name) + __attribute__((weak, alias("__def_arch_get_mdio_control"))); + #if defined(CONFIG_CMD_MII)
int macb_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value) @@ -175,6 +182,7 @@ int macb_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value) if ( macb->phy_addr != phy_adr ) return -1;
+ arch_get_mdio_control(devname); *value = macb_mdio_read(macb, reg);
return 0; @@ -188,6 +196,7 @@ int macb_miiphy_write(const char *devname, u8 phy_adr, u8 reg, u16 value) if ( macb->phy_addr != phy_adr ) return -1;
+ arch_get_mdio_control(devname); macb_mdio_write(macb, reg, value);
return 0; @@ -379,6 +388,7 @@ static int macb_phy_init(struct macb_device *macb) int media, speed, duplex; int i;
+ arch_get_mdio_control(netdev->name); #ifdef CONFIG_MACB_SEARCH_PHY /* Auto-detect phy_addr */ if (!macb_phy_find(macb)) {

From: Shiraz Hashim shiraz.hashim@st.com
It is observed on SPEAr320S RMII#1 interface that on transmitting packets the MAC dma hangs randomly and constantly showing busy tx-go state.
It comes out if this situation only when Transmission is disabled and enabled again.
Since it happens randomly and u-boot doesn't require high performance we disable TE and re-enable it on each transmission. We also change number of transmit descriptor to 1 as we would not require more than it, further it would not alter GMAC notion of transmit descriptor start queue as it always point to same descriptor.
Signed-off-by: Shiraz Hashim shiraz.hashim@st.com --- drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c index ac25b52..17bad33 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -55,7 +55,7 @@
#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 #define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) -#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_RING_SIZE 1 #define CONFIG_SYS_MACB_TX_TIMEOUT 1000 #define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000
@@ -226,7 +226,13 @@ static int macb_send(struct eth_device *netdev, void *packet, int length) macb->tx_ring[tx_head].ctrl = ctrl; macb->tx_ring[tx_head].addr = paddr; barrier(); - macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART)); + /* + * Due to issues on SPEAr320 RMII, disable TE first so that + * controller can come out if it is hanged during transmission + */ + macb_writel(macb, NCR, macb_readl(macb, NCR) & ~MACB_BIT(TE)); + macb_writel(macb, NCR, macb_readl(macb, NCR) | + MACB_BIT(TE) | MACB_BIT(TSTART));
/* * I guess this is necessary because the networking core may @@ -444,6 +450,31 @@ static int macb_phy_init(struct macb_device *macb) } }
+static void macb_reset_hw(struct macb_device *bp) +{ + /* Make sure we have the write buffer for ourselves */ + barrier(); + /* + * Disable RX and TX (XXX: Should we halt the transmission + * more gracefully?) and we should not close the mdio port + */ + macb_writel(bp, NCR, 0); + + /* Clear the stats registers (XXX: Update stats first?) */ + macb_writel(bp, NCR, MACB_BIT(CLRSTAT)); + + /* keep the mdio port , otherwise other eth will not work */ + macb_writel(bp, NCR, MACB_BIT(MPE)); + + /* Clear all status flags */ + macb_writel(bp, TSR, ~0UL); + macb_writel(bp, RSR, ~0UL); + + /* Disable all interrupts */ + macb_writel(bp, IDR, ~0UL); + macb_readl(bp, ISR); +} + static int macb_init(struct eth_device *netdev, bd_t *bd) { struct macb_device *macb = to_macb(netdev); @@ -520,8 +551,7 @@ static void macb_halt(struct eth_device *netdev) tsr = macb_readl(macb, TSR); } while (tsr & MACB_BIT(TGO));
- /* Disable TX and RX, and clear statistics */ - macb_writel(macb, NCR, MACB_BIT(CLRSTAT)); + macb_reset_hw(macb); }
static int macb_write_hwaddr(struct eth_device *dev)

Hi Vipin,
On Fri, 2 Nov 2012 23:09:59 +0530, Vipin Kumar vipin.kumar@st.com wrote:
From: Shiraz Hashim shiraz.hashim@st.com
It is observed on SPEAr320S RMII#1 interface that on transmitting packets the MAC dma hangs randomly and constantly showing busy tx-go state.
It comes out if this situation only when Transmission is disabled and enabled again.
Since it happens randomly and u-boot doesn't require high performance we disable TE and re-enable it on each transmission. We also change number of transmit descriptor to 1 as we would not require more than it, further it would not alter GMAC notion of transmit descriptor start queue as it always point to same descriptor.
Signed-off-by: Shiraz Hashim shiraz.hashim@st.com
drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c index ac25b52..17bad33 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -55,7 +55,7 @@
#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 #define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) -#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_RING_SIZE 1 #define CONFIG_SYS_MACB_TX_TIMEOUT 1000 #define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000
@@ -226,7 +226,13 @@ static int macb_send(struct eth_device *netdev, void *packet, int length) macb->tx_ring[tx_head].ctrl = ctrl; macb->tx_ring[tx_head].addr = paddr; barrier();
- macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
/*
* Due to issues on SPEAr320 RMII, disable TE first so that
* controller can come out if it is hanged during transmission
*/
macb_writel(macb, NCR, macb_readl(macb, NCR) & ~MACB_BIT(TE));
macb_writel(macb, NCR, macb_readl(macb, NCR) |
MACB_BIT(TE) | MACB_BIT(TSTART));
/*
- I guess this is necessary because the networking core may
@@ -444,6 +450,31 @@ static int macb_phy_init(struct macb_device *macb) } }
+static void macb_reset_hw(struct macb_device *bp) +{
- /* Make sure we have the write buffer for ourselves */
- barrier();
- /*
* Disable RX and TX (XXX: Should we halt the transmission
* more gracefully?) and we should not close the mdio port
*/
- macb_writel(bp, NCR, 0);
- /* Clear the stats registers (XXX: Update stats first?) */
- macb_writel(bp, NCR, MACB_BIT(CLRSTAT));
- /* keep the mdio port , otherwise other eth will not work */
- macb_writel(bp, NCR, MACB_BIT(MPE));
- /* Clear all status flags */
- macb_writel(bp, TSR, ~0UL);
- macb_writel(bp, RSR, ~0UL);
- /* Disable all interrupts */
- macb_writel(bp, IDR, ~0UL);
- macb_readl(bp, ISR);
+}
static int macb_init(struct eth_device *netdev, bd_t *bd) { struct macb_device *macb = to_macb(netdev); @@ -520,8 +551,7 @@ static void macb_halt(struct eth_device *netdev) tsr = macb_readl(macb, TSR); } while (tsr & MACB_BIT(TGO));
- /* Disable TX and RX, and clear statistics */
- macb_writel(macb, NCR, MACB_BIT(CLRSTAT));
- macb_reset_hw(macb);
}
static int macb_write_hwaddr(struct eth_device *dev)
This patch did not reappear in later versions of the series, and no other standalone patch seems to match it. Was it dropped?
Amicalement,

On Sun, 3 Feb 2013 12:19:26 +0100, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Vipin,
On Fri, 2 Nov 2012 23:09:59 +0530, Vipin Kumar vipin.kumar@st.com wrote:
From: Shiraz Hashim shiraz.hashim@st.com
It is observed on SPEAr320S RMII#1 interface that on transmitting packets the MAC dma hangs randomly and constantly showing busy tx-go state.
It comes out if this situation only when Transmission is disabled and enabled again.
Since it happens randomly and u-boot doesn't require high performance we disable TE and re-enable it on each transmission. We also change number of transmit descriptor to 1 as we would not require more than it, further it would not alter GMAC notion of transmit descriptor start queue as it always point to same descriptor.
Signed-off-by: Shiraz Hashim shiraz.hashim@st.com
drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c index ac25b52..17bad33 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -55,7 +55,7 @@
#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 #define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) -#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_RING_SIZE 1 #define CONFIG_SYS_MACB_TX_TIMEOUT 1000 #define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000
@@ -226,7 +226,13 @@ static int macb_send(struct eth_device *netdev, void *packet, int length) macb->tx_ring[tx_head].ctrl = ctrl; macb->tx_ring[tx_head].addr = paddr; barrier();
- macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
/*
* Due to issues on SPEAr320 RMII, disable TE first so that
* controller can come out if it is hanged during transmission
*/
macb_writel(macb, NCR, macb_readl(macb, NCR) & ~MACB_BIT(TE));
macb_writel(macb, NCR, macb_readl(macb, NCR) |
MACB_BIT(TE) | MACB_BIT(TSTART));
/*
- I guess this is necessary because the networking core may
@@ -444,6 +450,31 @@ static int macb_phy_init(struct macb_device *macb) } }
+static void macb_reset_hw(struct macb_device *bp) +{
- /* Make sure we have the write buffer for ourselves */
- barrier();
- /*
* Disable RX and TX (XXX: Should we halt the transmission
* more gracefully?) and we should not close the mdio port
*/
- macb_writel(bp, NCR, 0);
- /* Clear the stats registers (XXX: Update stats first?) */
- macb_writel(bp, NCR, MACB_BIT(CLRSTAT));
- /* keep the mdio port , otherwise other eth will not work */
- macb_writel(bp, NCR, MACB_BIT(MPE));
- /* Clear all status flags */
- macb_writel(bp, TSR, ~0UL);
- macb_writel(bp, RSR, ~0UL);
- /* Disable all interrupts */
- macb_writel(bp, IDR, ~0UL);
- macb_readl(bp, ISR);
+}
static int macb_init(struct eth_device *netdev, bd_t *bd) { struct macb_device *macb = to_macb(netdev); @@ -520,8 +551,7 @@ static void macb_halt(struct eth_device *netdev) tsr = macb_readl(macb, TSR); } while (tsr & MACB_BIT(TGO));
- /* Disable TX and RX, and clear statistics */
- macb_writel(macb, NCR, MACB_BIT(CLRSTAT));
- macb_reset_hw(macb);
}
static int macb_write_hwaddr(struct eth_device *dev)
This patch did not reappear in later versions of the series, and no other standalone patch seems to match it. Was it dropped?
Ping?
Amicalement,

Hi All,
On 02/28/2013 08:59 PM, Albert ARIBAUD wrote:
On Sun, 3 Feb 2013 12:19:26 +0100, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Vipin,
On Fri, 2 Nov 2012 23:09:59 +0530, Vipin Kumarvipin.kumar@st.com wrote:
From: Shiraz Hashimshiraz.hashim@st.com
It is observed on SPEAr320S RMII#1 interface that on transmitting packets the MAC dma hangs randomly and constantly showing busy tx-go state.
It comes out if this situation only when Transmission is disabled and enabled again.
Since it happens randomly and u-boot doesn't require high performance we disable TE and re-enable it on each transmission. We also change number of transmit descriptor to 1 as we would not require more than it, further it would not alter GMAC notion of transmit descriptor start queue as it always point to same descriptor.
Signed-off-by: Shiraz Hashimshiraz.hashim@st.com
drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
Tested on Atmel EK board. It works.
Tested-by: Bo Shen voice.shen@atmel.com
BTW, would this be implemented as a workaround only for SPEAr320S?
Best Regards, Bo Shen
diff --git a/drivers/net/macb.c b/drivers/net/macb.c index ac25b52..17bad33 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -55,7 +55,7 @@
#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 #define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) -#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_RING_SIZE 1 #define CONFIG_SYS_MACB_TX_TIMEOUT 1000 #define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000
@@ -226,7 +226,13 @@ static int macb_send(struct eth_device *netdev, void *packet, int length) macb->tx_ring[tx_head].ctrl = ctrl; macb->tx_ring[tx_head].addr = paddr; barrier();
- macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
/*
* Due to issues on SPEAr320 RMII, disable TE first so that
* controller can come out if it is hanged during transmission
*/
macb_writel(macb, NCR, macb_readl(macb, NCR)& ~MACB_BIT(TE));
macb_writel(macb, NCR, macb_readl(macb, NCR) |
MACB_BIT(TE) | MACB_BIT(TSTART));
/*
- I guess this is necessary because the networking core may
@@ -444,6 +450,31 @@ static int macb_phy_init(struct macb_device *macb) } }
+static void macb_reset_hw(struct macb_device *bp) +{
- /* Make sure we have the write buffer for ourselves */
- barrier();
- /*
* Disable RX and TX (XXX: Should we halt the transmission
* more gracefully?) and we should not close the mdio port
*/
- macb_writel(bp, NCR, 0);
- /* Clear the stats registers (XXX: Update stats first?) */
- macb_writel(bp, NCR, MACB_BIT(CLRSTAT));
- /* keep the mdio port , otherwise other eth will not work */
- macb_writel(bp, NCR, MACB_BIT(MPE));
- /* Clear all status flags */
- macb_writel(bp, TSR, ~0UL);
- macb_writel(bp, RSR, ~0UL);
- /* Disable all interrupts */
- macb_writel(bp, IDR, ~0UL);
- macb_readl(bp, ISR);
+}
- static int macb_init(struct eth_device *netdev, bd_t *bd) { struct macb_device *macb = to_macb(netdev);
@@ -520,8 +551,7 @@ static void macb_halt(struct eth_device *netdev) tsr = macb_readl(macb, TSR); } while (tsr& MACB_BIT(TGO));
- /* Disable TX and RX, and clear statistics */
- macb_writel(macb, NCR, MACB_BIT(CLRSTAT));
macb_reset_hw(macb); }
static int macb_write_hwaddr(struct eth_device *dev)
This patch did not reappear in later versions of the series, and no other standalone patch seems to match it. Was it dropped?
Ping?
Amicalement,

On 3/1/2013 8:38 AM, Bo Shen wrote:
Hi All,
On 02/28/2013 08:59 PM, Albert ARIBAUD wrote:
On Sun, 3 Feb 2013 12:19:26 +0100, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Vipin,
On Fri, 2 Nov 2012 23:09:59 +0530, Vipin Kumarvipin.kumar@st.com wrote:
From: Shiraz Hashimshiraz.hashim@st.com
It is observed on SPEAr320S RMII#1 interface that on transmitting packets the MAC dma hangs randomly and constantly showing busy tx-go state.
It comes out if this situation only when Transmission is disabled and enabled again.
Since it happens randomly and u-boot doesn't require high performance we disable TE and re-enable it on each transmission. We also change number of transmit descriptor to 1 as we would not require more than it, further it would not alter GMAC notion of transmit descriptor start queue as it always point to same descriptor.
Signed-off-by: Shiraz Hashimshiraz.hashim@st.com
drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
Tested on Atmel EK board. It works.
Tested-by: Bo Shen voice.shen@atmel.com
BTW, would this be implemented as a workaround only for SPEAr320S?
Yes, The idea was to implement this only for spear320s After your tested-by, I am thinking may be I can make a separate workaround config which can be enabled by any board using this peripheral.
Is that OK?
Regards Vipin

Hi Vipin,
On 3/1/2013 11:40, Vipin Kumar wrote: [Snip]
Signed-off-by: Shiraz Hashimshiraz.hashim@st.com
drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
Tested on Atmel EK board. It works.
Tested-by: Bo Shen voice.shen@atmel.com
BTW, would this be implemented as a workaround only for SPEAr320S?
Yes, The idea was to implement this only for spear320s After your tested-by, I am thinking may be I can make a separate workaround config which can be enabled by any board using this peripheral.
Is that OK?
Agree.
Best Regards, Bo Shen

On 2/28/2013 6:29 PM, Albert ARIBAUD wrote:
On Sun, 3 Feb 2013 12:19:26 +0100, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Vipin,
On Fri, 2 Nov 2012 23:09:59 +0530, Vipin Kumarvipin.kumar@st.com wrote:
From: Shiraz Hashimshiraz.hashim@st.com
It is observed on SPEAr320S RMII#1 interface that on transmitting packets the MAC dma hangs randomly and constantly showing busy tx-go state.
It comes out if this situation only when Transmission is disabled and enabled again.
Since it happens randomly and u-boot doesn't require high performance we disable TE and re-enable it on each transmission. We also change number of transmit descriptor to 1 as we would not require more than it, further it would not alter GMAC notion of transmit descriptor start queue as it always point to same descriptor.
Signed-off-by: Shiraz Hashimshiraz.hashim@st.com
drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c index ac25b52..17bad33 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -55,7 +55,7 @@
#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 #define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) -#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_RING_SIZE 1 #define CONFIG_SYS_MACB_TX_TIMEOUT 1000 #define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000
@@ -226,7 +226,13 @@ static int macb_send(struct eth_device *netdev, void *packet, int length) macb->tx_ring[tx_head].ctrl = ctrl; macb->tx_ring[tx_head].addr = paddr; barrier();
- macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
/*
* Due to issues on SPEAr320 RMII, disable TE first so that
* controller can come out if it is hanged during transmission
*/
macb_writel(macb, NCR, macb_readl(macb, NCR)& ~MACB_BIT(TE));
macb_writel(macb, NCR, macb_readl(macb, NCR) |
MACB_BIT(TE) | MACB_BIT(TSTART));
/*
- I guess this is necessary because the networking core may
@@ -444,6 +450,31 @@ static int macb_phy_init(struct macb_device *macb) } }
+static void macb_reset_hw(struct macb_device *bp) +{
- /* Make sure we have the write buffer for ourselves */
- barrier();
- /*
* Disable RX and TX (XXX: Should we halt the transmission
* more gracefully?) and we should not close the mdio port
*/
- macb_writel(bp, NCR, 0);
- /* Clear the stats registers (XXX: Update stats first?) */
- macb_writel(bp, NCR, MACB_BIT(CLRSTAT));
- /* keep the mdio port , otherwise other eth will not work */
- macb_writel(bp, NCR, MACB_BIT(MPE));
- /* Clear all status flags */
- macb_writel(bp, TSR, ~0UL);
- macb_writel(bp, RSR, ~0UL);
- /* Disable all interrupts */
- macb_writel(bp, IDR, ~0UL);
- macb_readl(bp, ISR);
+}
- static int macb_init(struct eth_device *netdev, bd_t *bd) { struct macb_device *macb = to_macb(netdev);
@@ -520,8 +551,7 @@ static void macb_halt(struct eth_device *netdev) tsr = macb_readl(macb, TSR); } while (tsr& MACB_BIT(TGO));
- /* Disable TX and RX, and clear statistics */
- macb_writel(macb, NCR, MACB_BIT(CLRSTAT));
macb_reset_hw(macb); }
static int macb_write_hwaddr(struct eth_device *dev)
This patch did not reappear in later versions of the series, and no other standalone patch seems to match it. Was it dropped?
Ping?
No, I have been busy with something. I would come back to the u-boot development soon
Vipin
Amicalement,

Hi Vipin,
On Fri, 1 Mar 2013 09:11:33 +0530, Vipin Kumar vipin.kumar@st.com wrote:
On 2/28/2013 6:29 PM, Albert ARIBAUD wrote:
On Sun, 3 Feb 2013 12:19:26 +0100, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Vipin,
On Fri, 2 Nov 2012 23:09:59 +0530, Vipin Kumarvipin.kumar@st.com wrote:
From: Shiraz Hashimshiraz.hashim@st.com
It is observed on SPEAr320S RMII#1 interface that on transmitting packets the MAC dma hangs randomly and constantly showing busy tx-go state.
It comes out if this situation only when Transmission is disabled and enabled again.
Since it happens randomly and u-boot doesn't require high performance we disable TE and re-enable it on each transmission. We also change number of transmit descriptor to 1 as we would not require more than it, further it would not alter GMAC notion of transmit descriptor start queue as it always point to same descriptor.
Signed-off-by: Shiraz Hashimshiraz.hashim@st.com
drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c index ac25b52..17bad33 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -55,7 +55,7 @@
#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 #define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) -#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_RING_SIZE 1 #define CONFIG_SYS_MACB_TX_TIMEOUT 1000 #define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000
@@ -226,7 +226,13 @@ static int macb_send(struct eth_device *netdev, void *packet, int length) macb->tx_ring[tx_head].ctrl = ctrl; macb->tx_ring[tx_head].addr = paddr; barrier();
- macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
/*
* Due to issues on SPEAr320 RMII, disable TE first so that
* controller can come out if it is hanged during transmission
*/
macb_writel(macb, NCR, macb_readl(macb, NCR)& ~MACB_BIT(TE));
macb_writel(macb, NCR, macb_readl(macb, NCR) |
MACB_BIT(TE) | MACB_BIT(TSTART));
/*
- I guess this is necessary because the networking core may
@@ -444,6 +450,31 @@ static int macb_phy_init(struct macb_device *macb) } }
+static void macb_reset_hw(struct macb_device *bp) +{
- /* Make sure we have the write buffer for ourselves */
- barrier();
- /*
* Disable RX and TX (XXX: Should we halt the transmission
* more gracefully?) and we should not close the mdio port
*/
- macb_writel(bp, NCR, 0);
- /* Clear the stats registers (XXX: Update stats first?) */
- macb_writel(bp, NCR, MACB_BIT(CLRSTAT));
- /* keep the mdio port , otherwise other eth will not work */
- macb_writel(bp, NCR, MACB_BIT(MPE));
- /* Clear all status flags */
- macb_writel(bp, TSR, ~0UL);
- macb_writel(bp, RSR, ~0UL);
- /* Disable all interrupts */
- macb_writel(bp, IDR, ~0UL);
- macb_readl(bp, ISR);
+}
- static int macb_init(struct eth_device *netdev, bd_t *bd) { struct macb_device *macb = to_macb(netdev);
@@ -520,8 +551,7 @@ static void macb_halt(struct eth_device *netdev) tsr = macb_readl(macb, TSR); } while (tsr& MACB_BIT(TGO));
- /* Disable TX and RX, and clear statistics */
- macb_writel(macb, NCR, MACB_BIT(CLRSTAT));
macb_reset_hw(macb); }
static int macb_write_hwaddr(struct eth_device *dev)
This patch did not reappear in later versions of the series, and no other standalone patch seems to match it. Was it dropped?
Ping?
No, I have been busy with something. I would come back to the u-boot development soon
Thanks. Following your discussion with Bo Shen, should I expect a new version of this patch in the future, or does it stand as it is?
Vipin
Amicalement,

On 3/1/2013 12:58 PM, Albert ARIBAUD wrote:
Hi Vipin,
On Fri, 1 Mar 2013 09:11:33 +0530, Vipin Kumarvipin.kumar@st.com wrote:
On 2/28/2013 6:29 PM, Albert ARIBAUD wrote:
On Sun, 3 Feb 2013 12:19:26 +0100, Albert ARIBAUD albert.u.boot@aribaud.net wrote:
Hi Vipin,
On Fri, 2 Nov 2012 23:09:59 +0530, Vipin Kumarvipin.kumar@st.com wrote:
From: Shiraz Hashimshiraz.hashim@st.com
It is observed on SPEAr320S RMII#1 interface that on transmitting packets the MAC dma hangs randomly and constantly showing busy tx-go state.
It comes out if this situation only when Transmission is disabled and enabled again.
Since it happens randomly and u-boot doesn't require high performance we disable TE and re-enable it on each transmission. We also change number of transmit descriptor to 1 as we would not require more than it, further it would not alter GMAC notion of transmit descriptor start queue as it always point to same descriptor.
Signed-off-by: Shiraz Hashimshiraz.hashim@st.com
drivers/net/macb.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/net/macb.c b/drivers/net/macb.c index ac25b52..17bad33 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -55,7 +55,7 @@
#define CONFIG_SYS_MACB_RX_BUFFER_SIZE 4096 #define CONFIG_SYS_MACB_RX_RING_SIZE (CONFIG_SYS_MACB_RX_BUFFER_SIZE / 128) -#define CONFIG_SYS_MACB_TX_RING_SIZE 16 +#define CONFIG_SYS_MACB_TX_RING_SIZE 1 #define CONFIG_SYS_MACB_TX_TIMEOUT 1000 #define CONFIG_SYS_MACB_AUTONEG_TIMEOUT 5000000
@@ -226,7 +226,13 @@ static int macb_send(struct eth_device *netdev, void *packet, int length) macb->tx_ring[tx_head].ctrl = ctrl; macb->tx_ring[tx_head].addr = paddr; barrier();
- macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART));
/*
* Due to issues on SPEAr320 RMII, disable TE first so that
* controller can come out if it is hanged during transmission
*/
macb_writel(macb, NCR, macb_readl(macb, NCR)& ~MACB_BIT(TE));
macb_writel(macb, NCR, macb_readl(macb, NCR) |
MACB_BIT(TE) | MACB_BIT(TSTART));
/*
- I guess this is necessary because the networking core may
@@ -444,6 +450,31 @@ static int macb_phy_init(struct macb_device *macb) } }
+static void macb_reset_hw(struct macb_device *bp) +{
- /* Make sure we have the write buffer for ourselves */
- barrier();
- /*
* Disable RX and TX (XXX: Should we halt the transmission
* more gracefully?) and we should not close the mdio port
*/
- macb_writel(bp, NCR, 0);
- /* Clear the stats registers (XXX: Update stats first?) */
- macb_writel(bp, NCR, MACB_BIT(CLRSTAT));
- /* keep the mdio port , otherwise other eth will not work */
- macb_writel(bp, NCR, MACB_BIT(MPE));
- /* Clear all status flags */
- macb_writel(bp, TSR, ~0UL);
- macb_writel(bp, RSR, ~0UL);
- /* Disable all interrupts */
- macb_writel(bp, IDR, ~0UL);
- macb_readl(bp, ISR);
+}
- static int macb_init(struct eth_device *netdev, bd_t *bd) { struct macb_device *macb = to_macb(netdev);
@@ -520,8 +551,7 @@ static void macb_halt(struct eth_device *netdev) tsr = macb_readl(macb, TSR); } while (tsr& MACB_BIT(TGO));
- /* Disable TX and RX, and clear statistics */
- macb_writel(macb, NCR, MACB_BIT(CLRSTAT));
macb_reset_hw(macb); }
static int macb_write_hwaddr(struct eth_device *dev)
This patch did not reappear in later versions of the series, and no other standalone patch seems to match it. Was it dropped?
Ping?
No, I have been busy with something. I would come back to the u-boot development soon
Thanks. Following your discussion with Bo Shen, should I expect a new version of this patch in the future, or does it stand as it is?
A new version
Vipin
Vipin
Amicalement,

C3 is a cryptographic controller which is used by the SPL when DDR ECC support is enabled.
Basically, the DDR ECC feature requires the initialization of ECC values before the DDR can actually be used. To accomplish this, the complete on board DDR is initialized with zeroes. This initialization can be done using * CPU * CPU (with Dcache enabled) * C3
The current SPL code uses C3 because the initialization using the CPU is slow and we do not have enough memory in SPL to initialize page tables required to enable MMU and Dcache
Signed-off-by: Vipin Kumar vipin.kumar@st.com Reviewed-by: Shiraz Hashim shiraz.hashim@st.com --- arch/arm/include/asm/arch-spear13xx/c3.h | 63 ++++++++++++++++ drivers/misc/Makefile | 1 + drivers/misc/c3.c | 122 +++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 arch/arm/include/asm/arch-spear13xx/c3.h create mode 100644 drivers/misc/c3.c
diff --git a/arch/arm/include/asm/arch-spear13xx/c3.h b/arch/arm/include/asm/arch-spear13xx/c3.h new file mode 100644 index 0000000..541d702 --- /dev/null +++ b/arch/arm/include/asm/arch-spear13xx/c3.h @@ -0,0 +1,63 @@ +/* + * (C) Copyright 2012 + * ST Micoelectronics Pvt. Ltd. + * + * 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 + */ + +#ifndef _MISC_C3_ +#define _MISC_C3_ + +#include <asm/arch/hardware.h> + +#define C3_HIF_OFF 0x400 /* master interface registers */ + +#define C3_INT_MEM_BASE_ADDR (CONFIG_SYS_C3_BASE + 0x400) +#define C3_HIF_MBAR (C3_INT_MEM_BASE_ADDR + 0x304) + #define C3_LOCAL_MEM_ADDR 0xF0000000 +#define C3_HIF_MCR (C3_INT_MEM_BASE_ADDR + 0x308) + #define C3_HIF_MCR_ENB_INT_MEM 0x01 +#define C3_HIF_MAAR (C3_INT_MEM_BASE_ADDR + 0x310) +#define C3_HIF_MADR (C3_INT_MEM_BASE_ADDR + 0x314) + +/* ID0 Registers definition */ +#define C3_ID0_SCR (CONFIG_SYS_C3_BASE + 0x1000) + #define C3_ID0_SCR_RST (1 << 16) +#define C3_ID0_IP (CONFIG_SYS_C3_BASE + 0x1000 + 0x10) + #define C3_ID0_DEF_RDY_VAL 0x80002AAA + #define C3_ID0_STATE_MASK 0xC0000000 + #define C3_ID0_STATE_RUN 0xC0000000 + #define C3_ID0_STATE_IDLE 0x80000000 + +/* C3 Register Offsets */ +#define C3_MOVE_CHANNEL_ID (CONFIG_SYS_C3_BASE + 0x2000 + 0x3FC) + #define C3_MOVE_CHANNEL_ID_VAL 0x00000102 + +#define C3_INT_MEM_SIZE 0x4000 +#define C3_MOVE_AND (1 << 21) + +/* Some Commands */ +#define C3_CMD_MOVE_INIT 0x06000000 +#define C3_CMD_MOVE_DATA 0x0A800000 +#define C3_CMD_STOP 0x00000000 + +void *c3_memset(void *s, int c, size_t count); +int c3_init(void); + +#endif diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 271463c..209291f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk LIB := $(obj)libmisc.o
COBJS-$(CONFIG_ALI152X) += ali512x.o +COBJS-$(CONFIG_C3) += c3.o COBJS-$(CONFIG_DS4510) += ds4510.o COBJS-$(CONFIG_FSL_LAW) += fsl_law.o COBJS-$(CONFIG_GPIO_LED) += gpio_led.o diff --git a/drivers/misc/c3.c b/drivers/misc/c3.c new file mode 100644 index 0000000..20dc8aa --- /dev/null +++ b/drivers/misc/c3.c @@ -0,0 +1,122 @@ +/* + * (C) Copyright 2012 + * ST Micoelectronics Pvt. Ltd. + * + * 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 <errno.h> +#include <asm/io.h> +#include <asm/arch/c3.h> +#include <asm/arch/hardware.h> + +static unsigned long c3_mem_xlate(void *addr) +{ + + if (((ulong)addr < C3_INT_MEM_BASE_ADDR) || \ + ((ulong)addr >= (C3_INT_MEM_BASE_ADDR + C3_INT_MEM_SIZE))) + return (ulong)addr; + + return (unsigned long)addr - C3_INT_MEM_BASE_ADDR + + C3_LOCAL_MEM_ADDR; +} + +int c3_init(void) +{ + if (readl(C3_ID0_SCR) != C3_ID0_DEF_RDY_VAL) + writel(C3_ID0_SCR_RST, C3_ID0_SCR); + + if (readl(C3_MOVE_CHANNEL_ID) == C3_MOVE_CHANNEL_ID_VAL) + return -EINVAL; + + writel(C3_HIF_MCR_ENB_INT_MEM, C3_HIF_MCR); + writel(C3_LOCAL_MEM_ADDR, C3_HIF_MBAR); + + return 0; +} + +static int c3_run(void *prog_start) +{ + writel(c3_mem_xlate(prog_start), C3_ID0_IP); + + while ((readl(C3_ID0_SCR) & C3_ID0_STATE_MASK) == C3_ID0_STATE_RUN) + ; + + if ((readl(C3_ID0_SCR) & C3_ID0_STATE_MASK) != C3_ID0_STATE_IDLE) { + /* If not back to idle an error occured */ + writel(C3_ID0_SCR_RST, C3_ID0_SCR); + + /* Set internal access to run c3 programs */ + writel(C3_HIF_MCR_ENB_INT_MEM, C3_HIF_MCR); + + return -EIO; + } + + return 0; +} + +static int c3_move(void *dest, void *src, int cnt, int optype, int opdata) +{ + unsigned long *c3_prog; + int ret = 0; + + /* 3.b Prepare program */ + c3_prog = (unsigned long *)C3_INT_MEM_BASE_ADDR; + + /* 3.b.i. Mov init */ + c3_prog[0] = C3_CMD_MOVE_INIT; + c3_prog[1] = opdata; + + /* 3.b.ii. Mov data */ + c3_prog[2] = C3_CMD_MOVE_DATA + cnt + optype; + c3_prog[3] = c3_mem_xlate(src); + c3_prog[4] = c3_mem_xlate(dest); + + /* 3.b.iii. Stop */ + c3_prog[5] = C3_CMD_STOP; + + /* 4. Execute and wait */ + ret = c3_run(c3_prog); + + return ret; +} + +void *c3_memset(void *s, int c, size_t count) +{ +#define DATA_SIZE (1024*4) + u32 data = C3_INT_MEM_BASE_ADDR + 0x100; + u32 size; + size_t cur = 0; + + writel(0x100, C3_HIF_MAAR); + writel(c, C3_HIF_MADR); + + for (size = 4; size < DATA_SIZE; size <<= 1) + c3_move((void *)(data + size), (void *)data, size, + C3_MOVE_AND, 0xffffffff); + + while (cur < count) { + c3_move(s + cur, (void *)data, DATA_SIZE, + C3_MOVE_AND, 0xffffffff); + cur += DATA_SIZE; + } + + return s; +}

Certain ARMV7 cpus eg. CortexA9 contains a local and a global timer within the CPU core itself. This patch adds generic support for local timer.
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- arch/arm/cpu/armv7/Makefile | 11 ++- arch/arm/cpu/armv7/ca9_ltimer.c | 154 ++++++++++++++++++++++++++++++++++++++ arch/arm/include/asm/ca9_ltimer.h | 40 ++++++++++ 3 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 arch/arm/cpu/armv7/ca9_ltimer.c create mode 100644 arch/arm/include/asm/ca9_ltimer.h
diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 4fdbee4..3ef01f6 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -27,15 +27,18 @@ LIB = $(obj)lib$(CPU).o
START := start.o
-COBJS += cache_v7.o +COBJS-y += cache_v7.o
-COBJS += cpu.o -COBJS += syslib.o +COBJS-y += cpu.o +COBJS-y += syslib.o +COBJS-$(CONFIG_ARMV7_CA9LTIMER) += ca9_ltimer.o
ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CONFIG_TEGRA20),) -SOBJS += lowlevel_init.o +SOBJS-y += lowlevel_init.o endif
+COBJS := $(sort $(COBJS-y)) +SOBJS := $(sort $(SOBJS-y)) SRCS := $(START:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS) $(SOBJS)) START := $(addprefix $(obj),$(START)) diff --git a/arch/arm/cpu/armv7/ca9_ltimer.c b/arch/arm/cpu/armv7/ca9_ltimer.c new file mode 100644 index 0000000..be04b6a --- /dev/null +++ b/arch/arm/cpu/armv7/ca9_ltimer.c @@ -0,0 +1,154 @@ +/* + * (C) Copyright 2012 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.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 + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/ca9_ltimer.h> +#include <asm/arch/hardware.h> + +#define READ_TIMER() readl(&ca9_timer_p->count) + +static struct ca9_timer_regs *const ca9_timer_p = + (struct ca9_timer_regs *)CONFIG_ARMV7_LTIMER_BASE; + +DECLARE_GLOBAL_DATA_PTR; + +#define timestamp gd->tbl +#define lastdec gd->lastinc +#define tickshz gd->timer_rate_hz +#define ticksper10usec gd->tbu + +int timer_init(void) +{ + u32 prescaler, timertickshz; + /* + * Genrally, CortexA9 MPUs are operating from 500MHz to 1500MHz which + * means that CA9 local timer clock would be in the range of 250 MHz to + * 750MHz. + * Try to find a prescaler which can perfectly divide the local timer + * clock. Take prescaler as 200 if nothing is found + */ + for (prescaler = 255; prescaler > 1; prescaler--) { + if (CONFIG_ARMV7_LTMR_CLK == + (CONFIG_ARMV7_LTMR_CLK / prescaler) * prescaler) + break; + } + + if (prescaler == 1) + prescaler = 200; + timertickshz = CONFIG_ARMV7_LTMR_CLK / prescaler; + ticksper10usec = timertickshz / (100 * 1000); + tickshz = timertickshz / CONFIG_SYS_HZ; + + /* disable timers */ + writel(((prescaler - 1) << 8) | AUTO_RELOAD, &ca9_timer_p->control); + + /* load value for free running */ + writel(FREE_RUNNING, &ca9_timer_p->load); + + /* auto reload, start timer */ + writel(readl(&ca9_timer_p->control) | TIMER_ENABLE, + &ca9_timer_p->control); + + reset_timer_masked(); + + return 0; +} + +/* + * timer without interrupts + */ + +void reset_timer(void) +{ + reset_timer_masked(); +} + +ulong get_timer(ulong base) +{ + return (get_timer_masked() / tickshz) - base; +} + +void set_timer(ulong t) +{ + timestamp = t; +} + +void __udelay(unsigned long usec) +{ + ulong tmo; + ulong start = get_timer_masked(); + ulong rndoff; + + rndoff = (usec % 10) ? 1 : 0; + tmo = ((usec / 10) + rndoff) * ticksper10usec; + + while ((ulong) (get_timer_masked() - start) < tmo) + ; +} + +void reset_timer_masked(void) +{ + /* reset time */ + lastdec = READ_TIMER(); + timestamp = 0; +} + +ulong get_timer_masked(void) +{ + ulong now = READ_TIMER(); + + if (now <= lastdec) { + /* normal mode */ + timestamp += lastdec - now; + } else { + /* we have an overflow ... */ + timestamp += lastdec + FREE_RUNNING - now; + } + lastdec = now; + + return timestamp; +} + +void udelay_masked(unsigned long usec) +{ + return udelay(usec); +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ + return CONFIG_SYS_HZ; +} diff --git a/arch/arm/include/asm/ca9_ltimer.h b/arch/arm/include/asm/ca9_ltimer.h new file mode 100644 index 0000000..8833853 --- /dev/null +++ b/arch/arm/include/asm/ca9_ltimer.h @@ -0,0 +1,40 @@ +/* + * (C) Copyright 2012 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.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 + */ + +#ifndef __ARCH_ARM_CA9TIMER_H +#define __ARCH_ARM_CA9TIMER_H + +struct ca9_timer_regs { + u32 load; + u32 count; + u32 control; +}; + +/* control related definitions */ +#define AUTO_RELOAD (1 << 1) +#define TIMER_ENABLE (1 << 0) + +/* load related definitions */ +#define FREE_RUNNING (0xFFFFFFFF) + +#endif

imls does not list the images in NAND devices. This patch implements this support for legacy type images.
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- common/cmd_bootm.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+)
diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index 83fa5d7..ca3c430 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -62,6 +62,11 @@ #include <linux/lzo.h> #endif /* CONFIG_LZO */
+#if defined(CONFIG_CMD_NAND) +#include <linux/err.h> +#include <nand.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_SYS_BOOTM_LEN @@ -1221,6 +1226,99 @@ next_sector: ; next_bank: ; }
+#if defined(CONFIG_CMD_NAND) + printf("\n"); + nand_info_t *nand; + image_header_t image_header; + image_header_t *header = &image_header; + int nand_dev = nand_curr_device; + unsigned long img_size; + size_t hdr_size, read_len; + loff_t off; + unsigned int crc; + u_char *data; + + /* the following commands operate on the current device */ + if (nand_dev < 0 || nand_dev >= CONFIG_SYS_MAX_NAND_DEVICE) { + puts("\nNo NAND devices available\n"); + return 0; + } + + for (nand_dev = 0; nand_dev < CONFIG_SYS_MAX_NAND_DEVICE; nand_dev++) { + + nand = &nand_info[nand_dev]; + if ((!nand->name) || (!nand->size)) + continue; + + data = malloc(nand->writesize); + if (!data) { + puts("No memory available to list NAND images\n"); + return 0; + } + + for (off = 0; off < nand->size; off += nand->erasesize) { + int ret; + + if (nand_block_isbad(nand, off)) + continue; + + hdr_size = sizeof(image_header_t); + ret = nand_read(nand, off, &hdr_size, (u_char *)header); + if (ret < 0 && ret != -EUCLEAN) + continue; + + if (!image_check_hcrc(header)) + continue; + + printf("Legacy Image at NAND device %d offset %08lX:\n", + nand_dev, (ulong)off); + image_print_contents(header); + + puts(" Verifying Checksum ... "); + crc = 0; + img_size = ntohl(header->ih_size); + img_size += hdr_size; + + while (img_size > 0) { + int blockoff = 0; + + while (nand_block_isbad(nand, off)) { + off += nand->erasesize; + if (off >= nand->size) + goto out; + } + + do { + read_len = min(img_size, + nand->writesize); + ret = nand_read(nand, off + blockoff, + &read_len, data); + if (ret < 0 && ret != -EUCLEAN) + break; + + crc = crc32(crc, data + hdr_size, + read_len - hdr_size); + img_size -= read_len; + blockoff += read_len; + hdr_size = 0; + } while (img_size && + (blockoff < nand->erasesize)); + + off += nand->erasesize; + if (off >= nand->size) + goto out; + } + off -= nand->erasesize; +out: + if (crc != ntohl(header->ih_dcrc)) + puts(" Bad Data CRC\n"); + else + puts("OK\n"); + } + free(data); + } + +#endif return (0); }

On 11/02/2012 12:40:02 PM, Vipin Kumar wrote:
imls does not list the images in NAND devices. This patch implements this support for legacy type images.
Signed-off-by: Vipin Kumar vipin.kumar@st.com
common/cmd_bootm.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+)
diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index 83fa5d7..ca3c430 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -62,6 +62,11 @@ #include <linux/lzo.h> #endif /* CONFIG_LZO */
+#if defined(CONFIG_CMD_NAND) +#include <linux/err.h> +#include <nand.h> +#endif
You shouldn't need to ifdef-protect header files.
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_SYS_BOOTM_LEN @@ -1221,6 +1226,99 @@ next_sector: ; next_bank: ; }
+#if defined(CONFIG_CMD_NAND)
- printf("\n");
- nand_info_t *nand;
- image_header_t image_header;
- image_header_t *header = &image_header;
- int nand_dev = nand_curr_device;
- unsigned long img_size;
- size_t hdr_size, read_len;
- loff_t off;
- unsigned int crc;
- u_char *data;
- /* the following commands operate on the current device */
- if (nand_dev < 0 || nand_dev >= CONFIG_SYS_MAX_NAND_DEVICE) {
puts("\nNo NAND devices available\n");
return 0;
- }
Please move the NAND and NOR code into their own functions.
- for (nand_dev = 0; nand_dev < CONFIG_SYS_MAX_NAND_DEVICE;
nand_dev++) {
nand = &nand_info[nand_dev];
if ((!nand->name) || (!nand->size))
continue;
Redundant parentheses.
data = malloc(nand->writesize);
if (!data) {
puts("No memory available to list NAND
images\n");
return 0;
}
for (off = 0; off < nand->size; off += nand->erasesize)
{
int ret;
if (nand_block_isbad(nand, off))
continue;
hdr_size = sizeof(image_header_t);
ret = nand_read(nand, off, &hdr_size, (u_char
*)header);
if (ret < 0 && ret != -EUCLEAN)
continue;
I guess you don't use nand_read_skip_bad() because you don't want to allocate a buffer for the whole image... How about moving this code to nand_util.c? That would at least allow some refactoring rather than duplication.
if (!image_check_hcrc(header))
continue;
printf("Legacy Image at NAND device %d offset
%08lX:\n",
nand_dev, (ulong)off);
image_print_contents(header);
Shouldn't you check for FIT images as well?
puts(" Verifying Checksum ... ");
crc = 0;
img_size = ntohl(header->ih_size);
img_size += hdr_size;
while (img_size > 0) {
int blockoff = 0;
while (nand_block_isbad(nand, off)) {
off += nand->erasesize;
if (off >= nand->size)
goto out;
}
do {
read_len = min(img_size,
nand->writesize);
ret = nand_read(nand, off +
blockoff,
&read_len,
data);
if (ret < 0 && ret != -EUCLEAN)
break;
crc = crc32(crc, data +
hdr_size,
read_len -
hdr_size);
img_size -= read_len;
blockoff += read_len;
hdr_size = 0;
} while (img_size &&
(blockoff <
nand->erasesize));
off += nand->erasesize;
if (off >= nand->size)
goto out;
}
off -= nand->erasesize;
+out:
if (crc != ntohl(header->ih_dcrc))
puts(" Bad Data CRC\n");
else
puts("OK\n");
}
Please refactor this into separate functions to improve readability. Maybe put a nand_crc_skip_bad() function into nand_util.c?
-Scott

On 11/7/2012 5:00 AM, Scott Wood wrote:
On 11/02/2012 12:40:02 PM, Vipin Kumar wrote:
imls does not list the images in NAND devices. This patch implements this support for legacy type images.
Signed-off-by: Vipin Kumar vipin.kumar@st.com
common/cmd_bootm.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+)
diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index 83fa5d7..ca3c430 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -62,6 +62,11 @@ #include <linux/lzo.h> #endif /* CONFIG_LZO */
+#if defined(CONFIG_CMD_NAND) +#include <linux/err.h> +#include <nand.h> +#endif
You shouldn't need to ifdef-protect header files.
OK. I would correct this in v2
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_SYS_BOOTM_LEN @@ -1221,6 +1226,99 @@ next_sector: ; next_bank: ; }
+#if defined(CONFIG_CMD_NAND)
- printf("\n");
- nand_info_t *nand;
- image_header_t image_header;
- image_header_t *header = &image_header;
- int nand_dev = nand_curr_device;
- unsigned long img_size;
- size_t hdr_size, read_len;
- loff_t off;
- unsigned int crc;
- u_char *data;
- /* the following commands operate on the current device */
- if (nand_dev < 0 || nand_dev >= CONFIG_SYS_MAX_NAND_DEVICE) {
- puts("\nNo NAND devices available\n");
- return 0;
- }
Please move the NAND and NOR code into their own functions.
You mean I can separate the NOR list images code in one routine and NAND in another?
- for (nand_dev = 0; nand_dev < CONFIG_SYS_MAX_NAND_DEVICE; nand_dev++) {
- nand = &nand_info[nand_dev];
- if ((!nand->name) || (!nand->size))
- continue;
Redundant parentheses.
Accepted
- data = malloc(nand->writesize);
- if (!data) {
- puts("No memory available to list NAND images\n");
- return 0;
- }
- for (off = 0; off < nand->size; off += nand->erasesize) {
- int ret;
- if (nand_block_isbad(nand, off))
- continue;
- hdr_size = sizeof(image_header_t);
- ret = nand_read(nand, off, &hdr_size, (u_char *)header);
- if (ret < 0 && ret != -EUCLEAN)
- continue;
I guess you don't use nand_read_skip_bad() because you don't want to allocate a buffer for the whole image... How about moving this code to nand_util.c? That would at least allow some refactoring rather than duplication.
- if (!image_check_hcrc(header))
- continue;
- printf("Legacy Image at NAND device %d offset %08lX:\n",
- nand_dev, (ulong)off);
- image_print_contents(header);
Shouldn't you check for FIT images as well?
Yes. I was a bit reluctant because I don't know about that format. OK, I would try to add it in v2
- puts(" Verifying Checksum ... ");
- crc = 0;
- img_size = ntohl(header->ih_size);
- img_size += hdr_size;
- while (img_size > 0) {
- int blockoff = 0;
- while (nand_block_isbad(nand, off)) {
- off += nand->erasesize;
- if (off >= nand->size)
- goto out;
- }
- do {
- read_len = min(img_size,
- nand->writesize);
- ret = nand_read(nand, off + blockoff,
- &read_len, data);
- if (ret < 0 && ret != -EUCLEAN)
- break;
- crc = crc32(crc, data + hdr_size,
- read_len - hdr_size);
- img_size -= read_len;
- blockoff += read_len;
- hdr_size = 0;
- } while (img_size &&
- (blockoff < nand->erasesize));
- off += nand->erasesize;
- if (off >= nand->size)
- goto out;
- }
- off -= nand->erasesize;
+out:
- if (crc != ntohl(header->ih_dcrc))
- puts(" Bad Data CRC\n");
- else
- puts("OK\n");
- }
Please refactor this into separate functions to improve readability. Maybe put a nand_crc_skip_bad() function into nand_util.c?
OK, I would give it a try. Please wait for v2
-Scott
btw, thanks for the review
How about other patches, Albert, Wolfgang ?
Regards Vipin

On 11/06/2012 11:15:42 PM, Vipin Kumar wrote:
On 11/7/2012 5:00 AM, Scott Wood wrote:
On 11/02/2012 12:40:02 PM, Vipin Kumar wrote:
+#if defined(CONFIG_CMD_NAND)
- printf("\n");
- nand_info_t *nand;
- image_header_t image_header;
- image_header_t *header = &image_header;
- int nand_dev = nand_curr_device;
- unsigned long img_size;
- size_t hdr_size, read_len;
- loff_t off;
- unsigned int crc;
- u_char *data;
- /* the following commands operate on the current device */
- if (nand_dev < 0 || nand_dev >= CONFIG_SYS_MAX_NAND_DEVICE) {
- puts("\nNo NAND devices available\n");
- return 0;
- }
Please move the NAND and NOR code into their own functions.
You mean I can separate the NOR list images code in one routine and NAND in another?
Yes.
-Scott

Signed-off-by: Vipin Kumar vipin.kumar@st.com --- Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/Makefile b/Makefile index 328347d..5d8d430 100644 --- a/Makefile +++ b/Makefile @@ -497,6 +497,13 @@ $(obj)u-boot.sb: $(obj)u-boot.bin $(obj)spl/u-boot-spl.bin elftosb -zdf $(ELFTOSB_TARGET-y) -c $(TOPDIR)/$(CPUDIR)/$(SOC)/u-boot-$(ELFTOSB_TARGET-y).bd \ -o $(obj)u-boot.sb
+$(obj)spl/u-boot-spl.img: $(obj)spl/u-boot-spl.bin + $(obj)tools/mkimage -A $(ARCH) -T firmware -C none \ + -a $(CONFIG_SPL_TEXT_BASE) -e $(CONFIG_SPL_TEXT_BASE) \ + -n $(shell sed -n -e 's/.*U_BOOT_SPL_VERSION//p' $(VERSION_FILE) | \ + sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \ + -d $(obj)spl/u-boot-spl.bin $(obj)spl/u-boot-spl.img + # On x600 (SPEAr600) U-Boot is appended to U-Boot SPL. # Both images are created using mkimage (crc etc), so that the ROM # bootloader can check its integrity. Padding needs to be done to the @@ -704,6 +711,9 @@ $(VERSION_FILE): "$(U_BOOT_VERSION)" "$${localvers}" ; \ printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' \ "$(U_BOOT_VERSION)" "$${localvers}" ; \ + printf '#define U_BOOT_SPL_VERSION "%s %s%s"\n' \ + $(if $(CONFIG_SPL_IMAGENAME),$(CONFIG_SPL_IMAGENAME),"U-Boot SPL") \ + "$(U_BOOT_VERSION)" "$${localvers}" ; \ ) > $@.tmp @( printf '#define CC_VERSION_STRING "%s"\n' \ '$(shell $(CC) --version | head -n 1)' )>> $@.tmp

A lot of ARM boards are using board_init routine just to initialize boot_params variable in the global data structure.
This patch lets the board config files to define a CONFIG_BOOT_PARAMS_P option which is assigned to gd->bd->bi_boot_params automatically
Consequently, many board_init routines would not be required in the respective board directories and a weak definition becomes necessary before their removal from the code.
Signed-off-by: Vipin Kumar vipin.kumar@st.com --- README | 6 ++++++ arch/arm/lib/board.c | 12 ++++++++++++ 2 files changed, 18 insertions(+)
diff --git a/README b/README index 69da2b8..444663d 100644 --- a/README +++ b/README @@ -550,6 +550,12 @@ The following options need to be configured: in a single configuration file and the machine type is runtime discoverable, do not have to use this setting.
+ CONFIG_BOOT_PARAMS_P [relevant for ARM only] + + This config option can provide a way to initialize + bi_boot_params from the u-boot infrastructure itself. The + board still has the option to override it in board_init routine + - vxWorks boot parameters:
bootvx constructs a valid bootline using the following diff --git a/arch/arm/lib/board.c b/arch/arm/lib/board.c index 92cad9a..fa161b8 100644 --- a/arch/arm/lib/board.c +++ b/arch/arm/lib/board.c @@ -399,6 +399,11 @@ void board_init_f(ulong bootflag) gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */ #endif
+#ifdef CONFIG_BOOT_PARAMS_P + /* Boot params passed to Linux */ + gd->bd->bi_boot_params = CONFIG_BOOT_PARAMS_P; +#endif + addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lx\n", @@ -468,6 +473,13 @@ void board_init_f(ulong bootflag) static char *failed = "*** failed ***\n"; #endif
+static int __def_board_init(bd_t *bis) +{ + return -1; +} + +int board_init(void) __attribute__((weak, alias("__def_board_init"))); + /* ************************************************************************ *
participants (4)
-
Albert ARIBAUD
-
Bo Shen
-
Scott Wood
-
Vipin Kumar