diff options
Diffstat (limited to 'drivers')
45 files changed, 2985 insertions, 540 deletions
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 54e9b1586fb..ef38d717252 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -18,6 +18,20 @@ * * Adapted for OMAP2420 I2C, r-woodruff2@ti.com * + * Copyright (c) 2013 Lubomir Popov <lpopov@mm-sol.com>, MM Solutions + * New i2c_read, i2c_write and i2c_probe functions, tested on OMAP4 + * (4430/60/70), OMAP5 (5430) and AM335X (3359); should work on older + * OMAPs and derivatives as well. The only anticipated exception would + * be the OMAP2420, which shall require driver modification. + * - Rewritten i2c_read to operate correctly with all types of chips + * (old function could not read consistent data from some I2C slaves). + * - Optimized i2c_write. + * - New i2c_probe, performs write access vs read. The old probe could + * hang the system under certain conditions (e.g. unconfigured pads). + * - The read/write/probe functions try to identify unconfigured bus. + * - Status functions now read irqstatus_raw as per TRM guidelines + * (except for OMAP243X and OMAP34XX). + * - Driver now supports up to I2C5 (OMAP5). */ #include <common.h> @@ -31,8 +45,11 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_TIMEOUT 1000 +/* Absolutely safe for status update at 100 kHz I2C: */ +#define I2C_WAIT 200 + static int wait_for_bb(void); -static u16 wait_for_pin(void); +static u16 wait_for_event(void); static void flush_fifo(void); /* @@ -137,10 +154,14 @@ void i2c_init(int speed, int slaveadd) /* own address */ writew(slaveadd, &i2c_base->oa); writew(I2C_CON_EN, &i2c_base->con); - - /* have to enable intrrupts or OMAP i2c module doesn't work */ +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) + /* + * Have to enable interrupts for OMAP2/3, these IPs don't have + * an 'irqstatus_raw' register and we shall have to poll 'stat' + */ writew(I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | - I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c_base->ie); + I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c_base->ie); +#endif udelay(1000); flush_fifo(); writew(0xFFFF, &i2c_base->stat); @@ -150,88 +171,6 @@ void i2c_init(int speed, int slaveadd) bus_initialized[current_bus] = 1; } -static int i2c_read_byte(u8 devaddr, u16 regoffset, u8 alen, u8 *value) -{ - int i2c_error = 0; - u16 status; - int i = 2 - alen; - u8 tmpbuf[2] = {(regoffset) >> 8, regoffset & 0xff}; - u16 w; - - /* wait until bus not busy */ - if (wait_for_bb()) - return 1; - - /* one byte only */ - writew(alen, &i2c_base->cnt); - /* set slave address */ - writew(devaddr, &i2c_base->sa); - /* no stop bit needed here */ - writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | - I2C_CON_TRX, &i2c_base->con); - - /* send register offset */ - while (1) { - status = wait_for_pin(); - if (status == 0 || status & I2C_STAT_NACK) { - i2c_error = 1; - goto read_exit; - } - if (status & I2C_STAT_XRDY) { - w = tmpbuf[i++]; -#if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ - defined(CONFIG_OMAP54XX)) - w |= tmpbuf[i++] << 8; -#endif - writew(w, &i2c_base->data); - writew(I2C_STAT_XRDY, &i2c_base->stat); - } - if (status & I2C_STAT_ARDY) { - writew(I2C_STAT_ARDY, &i2c_base->stat); - break; - } - } - - /* set slave address */ - writew(devaddr, &i2c_base->sa); - /* read one byte from slave */ - writew(1, &i2c_base->cnt); - /* need stop bit here */ - writew(I2C_CON_EN | I2C_CON_MST | - I2C_CON_STT | I2C_CON_STP, - &i2c_base->con); - - /* receive data */ - while (1) { - status = wait_for_pin(); - if (status == 0 || status & I2C_STAT_NACK) { - i2c_error = 1; - goto read_exit; - } - if (status & I2C_STAT_RRDY) { -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ - defined(CONFIG_OMAP54XX) - *value = readb(&i2c_base->data); -#else - *value = readw(&i2c_base->data); -#endif - writew(I2C_STAT_RRDY, &i2c_base->stat); - } - if (status & I2C_STAT_ARDY) { - writew(I2C_STAT_ARDY, &i2c_base->stat); - break; - } - } - -read_exit: - flush_fifo(); - writew(0xFFFF, &i2c_base->stat); - writew(0, &i2c_base->cnt); - return i2c_error; -} - static void flush_fifo(void) { u16 stat; @@ -241,13 +180,7 @@ static void flush_fifo(void) while (1) { stat = readw(&i2c_base->stat); if (stat == I2C_STAT_RRDY) { -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ - defined(CONFIG_OMAP54XX) readb(&i2c_base->data); -#else - readw(&i2c_base->data); -#endif writew(I2C_STAT_RRDY, &i2c_base->stat); udelay(1000); } else @@ -255,6 +188,10 @@ static void flush_fifo(void) } } +/* + * i2c_probe: Use write access. Allows to identify addresses that are + * write-only (like the config register of dual-port EEPROMs) + */ int i2c_probe(uchar chip) { u16 status; @@ -263,61 +200,81 @@ int i2c_probe(uchar chip) if (chip == readw(&i2c_base->oa)) return res; - /* wait until bus not busy */ + /* Wait until bus is free */ if (wait_for_bb()) return res; - /* try to read one byte */ - writew(1, &i2c_base->cnt); - /* set slave address */ + /* No data transfer, slave addr only */ + writew(0, &i2c_base->cnt); + /* Set slave address */ writew(chip, &i2c_base->sa); - /* stop bit needed here */ - writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, &i2c_base->con); - - while (1) { - status = wait_for_pin(); - if (status == 0 || status & I2C_STAT_AL) { - res = 1; - goto probe_exit; - } - if (status & I2C_STAT_NACK) { - res = 1; - writew(0xff, &i2c_base->stat); - writew (readw (&i2c_base->con) | I2C_CON_STP, &i2c_base->con); - - if (wait_for_bb()) - res = 1; - - break; - } - if (status & I2C_STAT_ARDY) { - writew(I2C_STAT_ARDY, &i2c_base->stat); - break; - } - if (status & I2C_STAT_RRDY) { - res = 0; -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ - defined(CONFIG_OMAP54XX) - readb(&i2c_base->data); -#else - readw(&i2c_base->data); -#endif - writew(I2C_STAT_RRDY, &i2c_base->stat); - } + /* Stop bit needed here */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); + + status = wait_for_event(); + + if ((status & ~I2C_STAT_XRDY) == 0 || (status & I2C_STAT_AL)) { + /* + * With current high-level command implementation, notifying + * the user shall flood the console with 127 messages. If + * silent exit is desired upon unconfigured bus, remove the + * following 'if' section: + */ + if (status == I2C_STAT_XRDY) + printf("i2c_probe: pads on bus %d probably not configured (status=0x%x)\n", + current_bus, status); + + goto pr_exit; } -probe_exit: + /* Check for ACK (!NAK) */ + if (!(status & I2C_STAT_NACK)) { + res = 0; /* Device found */ + udelay(I2C_WAIT); /* Required by AM335X in SPL */ + /* Abort transfer (force idle state) */ + writew(I2C_CON_MST | I2C_CON_TRX, &i2c_base->con); /* Reset */ + udelay(1000); + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); /* STP */ + } +pr_exit: flush_fifo(); - /* don't allow any more data in... we don't want it. */ - writew(0, &i2c_base->cnt); writew(0xFFFF, &i2c_base->stat); + writew(0, &i2c_base->cnt); return res; } +/* + * i2c_read: Function now uses a single I2C read transaction with bulk transfer + * of the requested number of bytes (note that the 'i2c md' command + * limits this to 16 bytes anyway). If CONFIG_I2C_REPEATED_START is + * defined in the board config header, this transaction shall be with + * Repeated Start (Sr) between the address and data phases; otherwise + * Stop-Start (P-S) shall be used (some I2C chips do require a P-S). + * The address (reg offset) may be 0, 1 or 2 bytes long. + * Function now reads correctly from chips that return more than one + * byte of data per addressed register (like TI temperature sensors), + * or that do not need a register address at all (such as some clock + * distributors). + */ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) { - int i; + int i2c_error = 0; + u16 status; + + if (alen < 0) { + puts("I2C read: addr len < 0\n"); + return 1; + } + if (len < 0) { + puts("I2C read: data len < 0\n"); + return 1; + } + if (buffer == NULL) { + puts("I2C read: NULL pointer passed\n"); + return 1; + } if (alen > 2) { printf("I2C read: addr len %d not supported\n", alen); @@ -329,24 +286,122 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) return 1; } - for (i = 0; i < len; i++) { - if (i2c_read_byte(chip, addr + i, alen, &buffer[i])) { - puts("I2C read: I/O error\n"); - i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); - return 1; + /* Wait until bus not busy */ + if (wait_for_bb()) + return 1; + + /* Zero, one or two bytes reg address (offset) */ + writew(alen, &i2c_base->cnt); + /* Set slave address */ + writew(chip, &i2c_base->sa); + + if (alen) { + /* Must write reg offset first */ +#ifdef CONFIG_I2C_REPEATED_START + /* No stop bit, use Repeated Start (Sr) */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | + I2C_CON_TRX, &i2c_base->con); +#else + /* Stop - Start (P-S) */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP | + I2C_CON_TRX, &i2c_base->con); +#endif + /* Send register offset */ + while (1) { + status = wait_for_event(); + /* Try to identify bus that is not padconf'd for I2C */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_read (addr phase): pads on bus %d probably not configured (status=0x%x)\n", + current_bus, status); + goto rd_exit; + } + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + printf("i2c_read: error waiting for addr ACK (status=0x%x)\n", + status); + goto rd_exit; + } + if (alen) { + if (status & I2C_STAT_XRDY) { + alen--; + /* Do we have to use byte access? */ + writeb((addr >> (8 * alen)) & 0xff, + &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } } } + /* Set slave address */ + writew(chip, &i2c_base->sa); + /* Read len bytes from slave */ + writew(len, &i2c_base->cnt); + /* Need stop bit here */ + writew(I2C_CON_EN | I2C_CON_MST | + I2C_CON_STT | I2C_CON_STP, + &i2c_base->con); - return 0; + /* Receive data */ + while (1) { + status = wait_for_event(); + /* + * Try to identify bus that is not padconf'd for I2C. This + * state could be left over from previous transactions if + * the address phase is skipped due to alen=0. + */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_read (data phase): pads on bus %d probably not configured (status=0x%x)\n", + current_bus, status); + goto rd_exit; + } + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + goto rd_exit; + } + if (status & I2C_STAT_RRDY) { + *buffer++ = readb(&i2c_base->data); + writew(I2C_STAT_RRDY, &i2c_base->stat); + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + +rd_exit: + flush_fifo(); + writew(0xFFFF, &i2c_base->stat); + writew(0, &i2c_base->cnt); + return i2c_error; } +/* i2c_write: Address (reg offset) may be 0, 1 or 2 bytes long. */ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) { int i; u16 status; int i2c_error = 0; - u16 w; - u8 tmpbuf[2] = {addr >> 8, addr & 0xff}; + + if (alen < 0) { + puts("I2C write: addr len < 0\n"); + return 1; + } + + if (len < 0) { + puts("I2C write: data len < 0\n"); + return 1; + } + + if (buffer == NULL) { + puts("I2C write: NULL pointer passed\n"); + return 1; + } if (alen > 2) { printf("I2C write: addr len %d not supported\n", alen); @@ -355,92 +410,137 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) if (addr + len > (1 << 16)) { printf("I2C write: address 0x%x + 0x%x out of range\n", - addr, len); + addr, len); return 1; } - /* wait until bus not busy */ + /* Wait until bus not busy */ if (wait_for_bb()) return 1; - /* start address phase - will write regoffset + len bytes data */ - /* TODO consider case when !CONFIG_OMAP243X/34XX/44XX */ + /* Start address phase - will write regoffset + len bytes data */ writew(alen + len, &i2c_base->cnt); - /* set slave address */ + /* Set slave address */ writew(chip, &i2c_base->sa); - /* stop bit needed here */ + /* Stop bit needed here */ writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | - I2C_CON_STP, &i2c_base->con); - - /* Send address and data */ - for (i = -alen; i < len; i++) { - status = wait_for_pin(); - + I2C_CON_STP, &i2c_base->con); + + while (alen) { + /* Must write reg offset (one or two bytes) */ + status = wait_for_event(); + /* Try to identify bus that is not padconf'd for I2C */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_write: pads on bus %d probably not configured (status=0x%x)\n", + current_bus, status); + goto wr_exit; + } if (status == 0 || status & I2C_STAT_NACK) { i2c_error = 1; - printf("i2c error waiting for data ACK (status=0x%x)\n", - status); - goto write_exit; + printf("i2c_write: error waiting for addr ACK (status=0x%x)\n", + status); + goto wr_exit; } - if (status & I2C_STAT_XRDY) { - w = (i < 0) ? tmpbuf[2+i] : buffer[i]; -#if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ - defined(CONFIG_OMAP54XX)) - w |= ((++i < 0) ? tmpbuf[2+i] : buffer[i]) << 8; -#endif - writew(w, &i2c_base->data); + alen--; + writeb((addr >> (8 * alen)) & 0xff, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { + i2c_error = 1; + printf("i2c_write: bus not ready for addr Tx (status=0x%x)\n", + status); + goto wr_exit; + } + } + /* Address phase is over, now write data */ + for (i = 0; i < len; i++) { + status = wait_for_event(); + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + printf("i2c_write: error waiting for data ACK (status=0x%x)\n", + status); + goto wr_exit; + } + if (status & I2C_STAT_XRDY) { + writeb(buffer[i], &i2c_base->data); writew(I2C_STAT_XRDY, &i2c_base->stat); } else { i2c_error = 1; - printf("i2c bus not ready for Tx (i=%d)\n", i); - goto write_exit; + printf("i2c_write: bus not ready for data Tx (i=%d)\n", + i); + goto wr_exit; } } -write_exit: +wr_exit: flush_fifo(); writew(0xFFFF, &i2c_base->stat); + writew(0, &i2c_base->cnt); return i2c_error; } +/* + * Wait for the bus to be free by checking the Bus Busy (BB) + * bit to become clear + */ static int wait_for_bb(void) { int timeout = I2C_TIMEOUT; u16 stat; writew(0xFFFF, &i2c_base->stat); /* clear current interrupts...*/ +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) { +#else + /* Read RAW status */ + while ((stat = readw(&i2c_base->irqstatus_raw) & + I2C_STAT_BB) && timeout--) { +#endif writew(stat, &i2c_base->stat); - udelay(1000); + udelay(I2C_WAIT); } if (timeout <= 0) { - printf("timed out in wait_for_bb: I2C_STAT=%x\n", - readw(&i2c_base->stat)); + printf("Timed out in wait_for_bb: status=%04x\n", + stat); return 1; } writew(0xFFFF, &i2c_base->stat); /* clear delayed stuff*/ return 0; } -static u16 wait_for_pin(void) +/* + * Wait for the I2C controller to complete current action + * and update status + */ +static u16 wait_for_event(void) { u16 status; int timeout = I2C_TIMEOUT; do { - udelay(1000); + udelay(I2C_WAIT); +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) status = readw(&i2c_base->stat); +#else + /* Read RAW status */ + status = readw(&i2c_base->irqstatus_raw); +#endif } while (!(status & (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY | I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK | I2C_STAT_AL)) && timeout--); if (timeout <= 0) { - printf("timed out in wait_for_pin: I2C_STAT=%x\n", - readw(&i2c_base->stat)); + printf("Timed out in wait_for_event: status=%04x\n", + status); + /* + * If status is still 0 here, probably the bus pads have + * not been configured for I2C, and/or pull-ups are missing. + */ + printf("Check if pads/pull-ups of bus %d are properly configured\n", + current_bus); writew(0xFFFF, &i2c_base->stat); status = 0; } @@ -450,28 +550,36 @@ static u16 wait_for_pin(void) int i2c_set_bus_num(unsigned int bus) { - if ((bus < 0) || (bus >= I2C_BUS_MAX)) { - printf("Bad bus: %d\n", bus); + if (bus >= I2C_BUS_MAX) { + printf("Bad bus: %x\n", bus); return -1; } -#if I2C_BUS_MAX == 4 - if (bus == 3) - i2c_base = (struct i2c *)I2C_BASE4; - else - if (bus == 2) + switch (bus) { + default: + bus = 0; /* Fall through */ + case 0: + i2c_base = (struct i2c *)I2C_BASE1; + break; + case 1: + i2c_base = (struct i2c *)I2C_BASE2; + break; +#if (I2C_BUS_MAX > 2) + case 2: i2c_base = (struct i2c *)I2C_BASE3; - else + break; +#if (I2C_BUS_MAX > 3) + case 3: + i2c_base = (struct i2c *)I2C_BASE4; + break; +#if (I2C_BUS_MAX > 4) + case 4: + i2c_base = (struct i2c *)I2C_BASE5; + break; #endif -#if I2C_BUS_MAX == 3 - if (bus == 2) - i2c_base = (struct i2c *)I2C_BASE3; - else #endif - if (bus == 1) - i2c_base = (struct i2c *)I2C_BASE2; - else - i2c_base = (struct i2c *)I2C_BASE1; +#endif + } current_bus = bus; diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 4070d4ea531..5da20eda558 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -129,13 +129,13 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, unsigned int timeout = 100000; u32 retry = 10000; u32 mask, ctrl; + ulong start = get_timer(0); while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { - if (timeout == 0) { + if (get_timer(start) > timeout) { printf("Timeout on data busy\n"); return TIMEOUT; } - timeout--; } dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); @@ -143,7 +143,6 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, if (data) dwmci_prepare_data(host, data); - dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); if (data) @@ -231,9 +230,8 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) int timeout = 10000; unsigned long sclk; - if (freq == host->clock) + if ((freq == host->clock) || (freq == 0)) return 0; - /* * If host->mmc_clk didn't define, * then assume that host->bus_hz is source clock value. @@ -314,7 +312,7 @@ static void dwmci_set_ios(struct mmc *mmc) static int dwmci_init(struct mmc *mmc) { struct dwmci_host *host = (struct dwmci_host *)mmc->priv; - u32 fifo_size, fifoth_val; + u32 fifo_size; dwmci_writel(host, DWMCI_PWREN, 1); @@ -323,6 +321,9 @@ static int dwmci_init(struct mmc *mmc) return -1; } + /* Enumerate at 400KHz */ + dwmci_setup_bus(host, mmc->f_min); + dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); dwmci_writel(host, DWMCI_INTMASK, 0); @@ -331,13 +332,13 @@ static int dwmci_init(struct mmc *mmc) dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1); - fifo_size = dwmci_readl(host, DWMCI_FIFOTH); - if (host->fifoth_val) - fifoth_val = host->fifoth_val; - else - fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size/2 -1) | - TX_WMARK(fifo_size/2); - dwmci_writel(host, DWMCI_FIFOTH, fifoth_val); + if (!host->fifoth_val) { + fifo_size = dwmci_readl(host, DWMCI_FIFOTH); + fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; + host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) | + TX_WMARK(fifo_size / 2); + } + dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0); diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 72a31b73fd5..4238dd933b0 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -19,39 +19,146 @@ */ #include <common.h> -#include <malloc.h> #include <dwmmc.h> +#include <fdtdec.h> +#include <libfdt.h> +#include <malloc.h> #include <asm/arch/dwmmc.h> #include <asm/arch/clk.h> +#include <asm/arch/pinmux.h> -static char *EXYNOS_NAME = "EXYNOS DWMMC"; +#define DWMMC_MAX_CH_NUM 4 +#define DWMMC_MAX_FREQ 52000000 +#define DWMMC_MIN_FREQ 400000 +#define DWMMC_MMC0_CLKSEL_VAL 0x03030001 +#define DWMMC_MMC2_CLKSEL_VAL 0x03020001 +/* + * Function used as callback function to initialise the + * CLKSEL register for every mmc channel. + */ static void exynos_dwmci_clksel(struct dwmci_host *host) { - u32 val; - val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | - DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(0); + dwmci_writel(host, DWMCI_CLKSEL, host->clksel_val); +} - dwmci_writel(host, DWMCI_CLKSEL, val); +unsigned int exynos_dwmci_get_clk(int dev_index) +{ + return get_mmc_clk(dev_index); } -int exynos_dwmci_init(u32 regbase, int bus_width, int index) +/* + * This function adds the mmc channel to be registered with mmc core. + * index - mmc channel number. + * regbase - register base address of mmc channel specified in 'index'. + * bus_width - operating bus width of mmc channel specified in 'index'. + * clksel - value to be written into CLKSEL register in case of FDT. + * NULL in case od non-FDT. + */ +int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel) { struct dwmci_host *host = NULL; + unsigned int div; + unsigned long freq, sclk; host = malloc(sizeof(struct dwmci_host)); if (!host) { printf("dwmci_host malloc fail!\n"); return 1; } + /* request mmc clock vlaue of 52MHz. */ + freq = 52000000; + sclk = get_mmc_clk(index); + div = DIV_ROUND_UP(sclk, freq); + /* set the clock divisor for mmc */ + set_mmc_clk(index, div); - host->name = EXYNOS_NAME; + host->name = "EXYNOS DWMMC"; host->ioaddr = (void *)regbase; host->buswidth = bus_width; + + if (clksel) { + host->clksel_val = clksel; + } else { + if (0 == index) + host->clksel_val = DWMMC_MMC0_CLKSEL_VAL; + if (2 == index) + host->clksel_val = DWMMC_MMC2_CLKSEL_VAL; + } + host->clksel = exynos_dwmci_clksel; host->dev_index = index; + host->mmc_clk = exynos_dwmci_get_clk; + /* Add the mmc channel to be registered with mmc core */ + if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { + debug("dwmmc%d registration failed\n", index); + return -1; + } + return 0; +} + +#ifdef CONFIG_OF_CONTROL +int exynos_dwmmc_init(const void *blob) +{ + int index, bus_width; + int node_list[DWMMC_MAX_CH_NUM]; + int err = 0, dev_id, flag, count, i; + u32 clksel_val, base, timing[3]; + + count = fdtdec_find_aliases_for_id(blob, "mmc", + COMPAT_SAMSUNG_EXYNOS5_DWMMC, node_list, + DWMMC_MAX_CH_NUM); + + for (i = 0; i < count; i++) { + int node = node_list[i]; + + if (node <= 0) + continue; - add_dwmci(host, 52000000, 400000); + /* Extract device id for each mmc channel */ + dev_id = pinmux_decode_periph_id(blob, node); + /* Get the bus width from the device node */ + bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); + if (bus_width <= 0) { + debug("DWMMC: Can't get bus-width\n"); + return -1; + } + if (8 == bus_width) + flag = PINMUX_FLAG_8BIT_MODE; + else + flag = PINMUX_FLAG_NONE; + + /* config pinmux for each mmc channel */ + err = exynos_pinmux_config(dev_id, flag); + if (err) { + debug("DWMMC not configured\n"); + return err; + } + + index = dev_id - PERIPH_ID_SDMMC0; + + /* Get the base address from the device node */ + base = fdtdec_get_addr(blob, node, "reg"); + if (!base) { + debug("DWMMC: Can't get base address\n"); + return -1; + } + /* Extract the timing info from the node */ + err = fdtdec_get_int_array(blob, node, "samsung,timing", + timing, 3); + if (err) { + debug("Can't get sdr-timings for divider\n"); + return -1; + } + + clksel_val = (DWMCI_SET_SAMPLE_CLK(timing[0]) | + DWMCI_SET_DRV_CLK(timing[1]) | + DWMCI_SET_DIV_RATIO(timing[2])); + /* Initialise each mmc channel */ + err = exynos_dwmci_add_port(index, base, bus_width, clksel_val); + if (err) + debug("dwmmc Channel-%d init failed\n", index); + } return 0; } - +#endif diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 861f4b9d6ca..973b19f337b 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -178,7 +178,7 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) int timeout; struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; -#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO uint wml_value; wml_value = data->blocksize/4; @@ -310,6 +310,9 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Figure out the transfer arguments */ xfertyp = esdhc_xfertyp(cmd, data); + /* Mask all irqs */ + esdhc_write32(®s->irqsigen, 0); + /* Send the command */ esdhc_write32(®s->cmdarg, cmd->cmdarg); #if defined(CONFIG_FSL_USDHC) @@ -320,15 +323,11 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) esdhc_write32(®s->xfertyp, xfertyp); #endif - /* Mask all irqs */ - esdhc_write32(®s->irqsigen, 0); - /* Wait for the command to complete */ while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE))) ; irqstat = esdhc_read32(®s->irqstat); - esdhc_write32(®s->irqstat, irqstat); /* Reset CMD and DATA portions on error */ if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) { diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0a2f5358e2f..83d2df77444 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -301,10 +301,12 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) return 0; } - if (blkcnt > 1) - cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; - else + if (blkcnt == 0) + return 0; + else if (blkcnt == 1) cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; if (mmc->high_capacity) cmd.cmdarg = start; @@ -700,16 +702,49 @@ static int mmc_change_freq(struct mmc *mmc) return 0; } +static int mmc_set_capacity(struct mmc *mmc, int part_num) +{ + switch (part_num) { + case 0: + mmc->capacity = mmc->capacity_user; + break; + case 1: + case 2: + mmc->capacity = mmc->capacity_boot; + break; + case 3: + mmc->capacity = mmc->capacity_rpmb; + break; + case 4: + case 5: + case 6: + case 7: + mmc->capacity = mmc->capacity_gp[part_num - 4]; + break; + default: + return -1; + } + + mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); + + return 0; +} + int mmc_switch_part(int dev_num, unsigned int part_num) { struct mmc *mmc = find_mmc_device(dev_num); + int ret; if (!mmc) return -1; - return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, - (mmc->part_config & ~PART_ACCESS_MASK) - | (part_num & PART_ACCESS_MASK)); + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, + (mmc->part_config & ~PART_ACCESS_MASK) + | (part_num & PART_ACCESS_MASK)); + if (ret) + return ret; + + return mmc_set_capacity(mmc, part_num); } int mmc_getcd(struct mmc *mmc) @@ -917,7 +952,7 @@ static void mmc_set_bus_width(struct mmc *mmc, uint width) static int mmc_startup(struct mmc *mmc) { - int err; + int err, i; uint mult, freq; u64 cmult, csize, capacity; struct mmc_cmd cmd; @@ -1035,8 +1070,12 @@ static int mmc_startup(struct mmc *mmc) cmult = (mmc->csd[2] & 0x00038000) >> 15; } - mmc->capacity = (csize + 1) << (cmult + 2); - mmc->capacity *= mmc->read_bl_len; + mmc->capacity_user = (csize + 1) << (cmult + 2); + mmc->capacity_user *= mmc->read_bl_len; + mmc->capacity_boot = 0; + mmc->capacity_rpmb = 0; + for (i = 0; i < 4; i++) + mmc->capacity_gp[i] = 0; if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN) mmc->read_bl_len = MMC_MAX_BLOCK_LEN; @@ -1075,7 +1114,7 @@ static int mmc_startup(struct mmc *mmc) | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; capacity *= MMC_MAX_BLOCK_LEN; if ((capacity >> 20) > 2 * 1024) - mmc->capacity = capacity; + mmc->capacity_user = capacity; } switch (ext_csd[EXT_CSD_REV]) { @@ -1117,8 +1156,25 @@ static int mmc_startup(struct mmc *mmc) if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || ext_csd[EXT_CSD_BOOT_MULT]) mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; + + mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; + + mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + + for (i = 0; i < 4; i++) { + int idx = EXT_CSD_GP_SIZE_MULT + i * 3; + mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) + + (ext_csd[idx + 1] << 8) + ext_csd[idx]; + mmc->capacity_gp[i] *= + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + } } + err = mmc_set_capacity(mmc, mmc->part_num); + if (err) + return err; + if (IS_SD(mmc)) err = sd_change_freq(mmc); else @@ -1447,3 +1503,137 @@ int mmc_initialize(bd_t *bis) do_preinit(); return 0; } + +#ifdef CONFIG_SUPPORT_EMMC_BOOT +/* + * This function changes the size of boot partition and the size of rpmb + * partition present on EMMC devices. + * + * Input Parameters: + * struct *mmc: pointer for the mmc device strcuture + * bootsize: size of boot partition + * rpmbsize: size of rpmb partition + * + * Returns 0 on success. + */ + +int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + int err; + struct mmc_cmd cmd; + + /* Only use this command for raw EMMC moviNAND. Enter backdoor mode */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error1 = %d\n", err); + return err; + } + + /* Boot partition changing mode */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG2; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error2 = %d\n", err); + return err; + } + /* boot partition size is multiple of 128KB */ + bootsize = (bootsize * 1024) / 128; + + /* Arg: boot partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = bootsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error3 = %d\n", err); + return err; + } + /* RPMB partition size is multiple of 128KB */ + rpmbsize = (rpmbsize * 1024) / 128; + /* Arg: RPMB partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = rpmbsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error4 = %d\n", err); + return err; + } + return 0; +} + +/* + * This function shall form and send the commands to open / close the + * boot partition specified by user. + * + * Input Parameters: + * ack: 0x0 - No boot acknowledge sent (default) + * 0x1 - Boot acknowledge sent during boot operation + * part_num: User selects boot data that will be sent to master + * 0x0 - Device not boot enabled (default) + * 0x1 - Boot partition 1 enabled for boot + * 0x2 - Boot partition 2 enabled for boot + * access: User selects partitions to access + * 0x0 : No access to boot partition (default) + * 0x1 : R/W boot partition 1 + * 0x2 : R/W boot partition 2 + * 0x3 : R/W Replay Protected Memory Block (RPMB) + * + * Returns 0 on success. + */ +int mmc_boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access) +{ + int err; + struct mmc_cmd cmd; + + /* Boot ack enable, boot partition enable , boot partition access */ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_RSP_R1b; + + cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_PART_CONF << 16) | + ((EXT_CSD_BOOT_ACK(ack) | + EXT_CSD_BOOT_PART_NUM(part_num) | + EXT_CSD_PARTITION_ACCESS(access)) << 8); + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + if (access) { + debug("mmc boot partition#%d open fail:Error1 = %d\n", + part_num, err); + } else { + debug("mmc boot partition#%d close fail:Error = %d\n", + part_num, err); + } + return err; + } + + if (access) { + /* 4bit transfer mode at booting time. */ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_RSP_R1b; + + cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_BOOT_BUS_WIDTH << 16) | + ((1 << 0) << 8); + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc boot partition#%d open fail:Error2 = %d\n", + part_num, err); + return err; + } + } + return 0; +} +#endif diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index afdfa886e8a..975b2c5ba4d 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -113,23 +113,21 @@ static void omap5_pbias_config(struct mmc *mmc) u32 value = 0; value = readl((*ctrl)->control_pbias); - value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); - value |= SDCARD_BIAS_HIZ_MODE; + value &= ~SDCARD_PWRDNZ; + writel(value, (*ctrl)->control_pbias); + udelay(10); /* wait 10 us */ + value &= ~SDCARD_BIAS_PWRDNZ; writel(value, (*ctrl)->control_pbias); palmas_mmc1_poweron_ldo(); value = readl((*ctrl)->control_pbias); - value &= ~SDCARD_BIAS_HIZ_MODE; - value |= SDCARD_PBIASLITE_VMODE | SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ; + value |= SDCARD_BIAS_PWRDNZ; writel(value, (*ctrl)->control_pbias); - - value = readl((*ctrl)->control_pbias); - if (value & (1 << 23)) { - value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); - value |= SDCARD_BIAS_HIZ_MODE; - writel(value, (*ctrl)->control_pbias); - } + udelay(150); /* wait 150 us */ + value |= SDCARD_PWRDNZ; + writel(value, (*ctrl)->control_pbias); + udelay(150); /* wait 150 us */ } #endif diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 1eaea04ad10..c5631bff672 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -486,8 +486,10 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) mmc->voltages |= host->voltages; mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; - if (caps & SDHCI_CAN_DO_8BIT) - mmc->host_caps |= MMC_MODE_8BIT; + if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { + if (caps & SDHCI_CAN_DO_8BIT) + mmc->host_caps |= MMC_MODE_8BIT; + } if (host->host_caps) mmc->host_caps |= host->host_caps; diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 8821704911b..bb81e84113c 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -83,6 +83,7 @@ COBJS-$(CONFIG_NAND_DOCG4) += docg4.o else # minimal SPL drivers COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o +COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o COBJS-$(CONFIG_NAND_MXC) += mxc_nand_spl.o endif # drivers diff --git a/drivers/mtd/nand/fsl_ifc_spl.c b/drivers/mtd/nand/fsl_ifc_spl.c new file mode 100644 index 00000000000..8537c4c6f57 --- /dev/null +++ b/drivers/mtd/nand/fsl_ifc_spl.c @@ -0,0 +1,258 @@ +/* + * NAND boot for Freescale Integrated Flash Controller, NAND FCM + * + * Copyright 2011 Freescale Semiconductor, Inc. + * Author: Dipen Dudhat <dipen.dudhat@freescale.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/fsl_ifc.h> +#include <linux/mtd/nand.h> + +static inline int is_blank(uchar *addr, int page_size) +{ + int i; + + for (i = 0; i < page_size; i++) { + if (__raw_readb(&addr[i]) != 0xff) + return 0; + } + + /* + * For the SPL, don't worry about uncorrectable errors + * where the main area is all FFs but shouldn't be. + */ + return 1; +} + +/* returns nonzero if entire page is blank */ +static inline int check_read_ecc(uchar *buf, u32 *eccstat, + unsigned int bufnum, int page_size) +{ + u32 reg = eccstat[bufnum / 4]; + int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf; + + if (errors == 0xf) { /* uncorrectable */ + /* Blank pages fail hw ECC checks */ + if (is_blank(buf, page_size)) + return 1; + + puts("ecc error\n"); + for (;;) + ; + } + + return 0; +} + +static inline void nand_wait(uchar *buf, int bufnum, int page_size) +{ + struct fsl_ifc *ifc = IFC_BASE_ADDR; + u32 status; + u32 eccstat[4]; + int bufperpage = page_size / 512; + int bufnum_end, i; + + bufnum *= bufperpage; + bufnum_end = bufnum + bufperpage - 1; + + do { + status = in_be32(&ifc->ifc_nand.nand_evter_stat); + } while (!(status & IFC_NAND_EVTER_STAT_OPC)); + + if (status & IFC_NAND_EVTER_STAT_FTOER) { + puts("flash time out error\n"); + for (;;) + ; + } + + for (i = bufnum / 4; i <= bufnum_end / 4; i++) + eccstat[i] = in_be32(&ifc->ifc_nand.nand_eccstat[i]); + + for (i = bufnum; i <= bufnum_end; i++) { + if (check_read_ecc(buf, eccstat, i, page_size)) + break; + } + + out_be32(&ifc->ifc_nand.nand_evter_stat, status); +} + +static inline int bad_block(uchar *marker, int port_size) +{ + if (port_size == 8) + return __raw_readb(marker) != 0xff; + else + return __raw_readw((u16 *)marker) != 0xffff; +} + +static void nand_load(unsigned int offs, int uboot_size, uchar *dst) +{ + struct fsl_ifc *ifc = IFC_BASE_ADDR; + uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; + int page_size; + int port_size; + int pages_per_blk; + int blk_size; + int bad_marker = 0; + int bufnum_mask, bufnum; + + int csor, cspr; + int pos = 0; + int j = 0; + + int sram_addr; + int pg_no; + + /* Get NAND Flash configuration */ + csor = CONFIG_SYS_NAND_CSOR; + cspr = CONFIG_SYS_NAND_CSPR; + + port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8; + + if (csor & CSOR_NAND_PGS_4K) { + page_size = 4096; + bufnum_mask = 0x1; + } else if (csor & CSOR_NAND_PGS_2K) { + page_size = 2048; + bufnum_mask = 0x3; + } else { + page_size = 512; + bufnum_mask = 0xf; + + if (port_size == 8) + bad_marker = 5; + } + + pages_per_blk = + 32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT); + + blk_size = pages_per_blk * page_size; + + /* Open Full SRAM mapping for spare are access */ + out_be32(&ifc->ifc_nand.ncfgr, 0x0); + + /* Clear Boot events */ + out_be32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff); + + /* Program FIR/FCR for Large/Small page */ + if (page_size > 512) { + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + + out_be32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); + } else { + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); + } + + /* Program FBCR = 0 for full page read */ + out_be32(&ifc->ifc_nand.nand_fbcr, 0); + + /* Read and copy u-boot on SDRAM from NAND device, In parallel + * check for Bad block if found skip it and read continue to + * next Block + */ + while (pos < uboot_size) { + int i = 0; + do { + pg_no = offs / page_size; + bufnum = pg_no & bufnum_mask; + sram_addr = bufnum * page_size * 2; + + out_be32(&ifc->ifc_nand.row0, pg_no); + out_be32(&ifc->ifc_nand.col0, 0); + /* start read */ + out_be32(&ifc->ifc_nand.nandseq_strt, + IFC_NAND_SEQ_STRT_FIR_STRT); + + /* wait for read to complete */ + nand_wait(&buf[sram_addr], bufnum, page_size); + + /* + * If either of the first two pages are marked bad, + * continue to the next block. + */ + if (i++ < 2 && + bad_block(&buf[sram_addr + page_size + bad_marker], + port_size)) { + puts("skipping\n"); + offs = (offs + blk_size) & ~(blk_size - 1); + pos &= ~(blk_size - 1); + break; + } + + for (j = 0; j < page_size; j++) + dst[pos + j] = __raw_readb(&buf[sram_addr + j]); + + pos += page_size; + offs += page_size; + } while ((offs & (blk_size - 1)) && (pos < uboot_size)); + } +} + +/* + * Main entrypoint for NAND Boot. It's necessary that SDRAM is already + * configured and available since this code loads the main U-boot image + * from NAND into SDRAM and starts from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + /* + * Load U-Boot image from NAND into RAM + */ + nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, + (uchar *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + nand_load(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (uchar *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_load(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + /* + * Jump to U-Boot image + */ +#ifdef CONFIG_SPL_FLUSH_IMAGE + /* + * Clean d-cache and invalidate i-cache, to + * make sure that no stale data is executed. + */ + flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); +#endif + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + uboot(); +} diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index 90f83924e2b..ecbb2108fb3 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -32,6 +32,7 @@ endif COBJS-$(CONFIG_SPI_FLASH) += spi_flash.o COBJS-$(CONFIG_SPI_FLASH_ATMEL) += atmel.o COBJS-$(CONFIG_SPI_FLASH_EON) += eon.o +COBJS-$(CONFIG_SPI_FLASH_GIGADEVICE) += gigadevice.o COBJS-$(CONFIG_SPI_FLASH_MACRONIX) += macronix.o COBJS-$(CONFIG_SPI_FLASH_SPANSION) += spansion.o COBJS-$(CONFIG_SPI_FLASH_SST) += sst.o diff --git a/drivers/mtd/spi/gigadevice.c b/drivers/mtd/spi/gigadevice.c new file mode 100644 index 00000000000..b5e1ebedf87 --- /dev/null +++ b/drivers/mtd/spi/gigadevice.c @@ -0,0 +1,81 @@ +/* + * Gigadevice SPI flash driver + * Copyright 2013, Samsung Electronics Co., Ltd. + * Author: Banajit Goswami <banajit.g@samsung.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 <malloc.h> +#include <spi_flash.h> + +#include "spi_flash_internal.h" + +struct gigadevice_spi_flash_params { + uint16_t id; + uint16_t nr_blocks; + const char *name; +}; + +static const struct gigadevice_spi_flash_params gigadevice_spi_flash_table[] = { + { + .id = 0x6016, + .nr_blocks = 64, + .name = "GD25LQ", + }, + { + .id = 0x4017, + .nr_blocks = 128, + .name = "GD25Q64B", + }, +}; + +struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode) +{ + const struct gigadevice_spi_flash_params *params; + struct spi_flash *flash; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gigadevice_spi_flash_table); i++) { + params = &gigadevice_spi_flash_table[i]; + if (params->id == ((idcode[1] << 8) | idcode[2])) + break; + } + + if (i == ARRAY_SIZE(gigadevice_spi_flash_table)) { + debug("SF: Unsupported Gigadevice ID %02x%02x\n", + idcode[1], idcode[2]); + return NULL; + } + + flash = spi_flash_alloc_base(spi, params->name); + if (!flash) { + debug("SF: Failed to allocate memory\n"); + return NULL; + } + /* page_size */ + flash->page_size = 256; + /* sector_size = page_size * pages_per_sector */ + flash->sector_size = flash->page_size * 16; + /* size = sector_size * sector_per_block * number of blocks */ + flash->size = flash->sector_size * 16 * params->nr_blocks; + + return flash; +} diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c index dad30b54c54..3ec2151b3a9 100644 --- a/drivers/mtd/spi/spansion.c +++ b/drivers/mtd/spi/spansion.c @@ -90,18 +90,32 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = { .name = "S25FL032P", }, { + .idcode1 = 0x0216, + .idcode2 = 0x4d00, + .pages_per_sector = 256, + .nr_sectors = 128, + .name = "S25FL064P", + }, + { .idcode1 = 0x2018, .idcode2 = 0x4d01, .pages_per_sector = 256, .nr_sectors = 256, - .name = "S25FL129P_64K/S25FL128S", + .name = "S25FL129P_64K/S25FL128S_64K", }, { .idcode1 = 0x0219, .idcode2 = 0x4d01, .pages_per_sector = 256, .nr_sectors = 512, - .name = "S25FL256S", + .name = "S25FL256S_64K", + }, + { + .idcode1 = 0x0220, + .idcode2 = 0x4d01, + .pages_per_sector = 256, + .nr_sectors = 1024, + .name = "S25FL512S_64K", }, }; diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 111185af175..6a6fe37e0ee 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -68,17 +68,60 @@ int spi_flash_cmd_write(struct spi_slave *spi, const u8 *cmd, size_t cmd_len, return spi_flash_read_write(spi, cmd, cmd_len, data, NULL, data_len); } -int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, - size_t len, const void *buf) +int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) { - unsigned long page_addr, byte_addr, page_size; - size_t chunk_len, actual; + struct spi_slave *spi = flash->spi; + unsigned long timebase; int ret; - u8 cmd[4]; + u8 status; + u8 check_status = 0x0; + u8 poll_bit = STATUS_WIP; + u8 cmd = flash->poll_cmd; - page_size = flash->page_size; - page_addr = offset / page_size; - byte_addr = offset % page_size; + if (cmd == CMD_FLAG_STATUS) { + poll_bit = STATUS_PEC; + check_status = poll_bit; + } + + ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); + if (ret) { + debug("SF: fail to read %s status register\n", + cmd == CMD_READ_STATUS ? "read" : "flag"); + return ret; + } + + timebase = get_timer(0); + do { + WATCHDOG_RESET(); + + ret = spi_xfer(spi, 8, NULL, &status, 0); + if (ret) + return -1; + + if ((status & poll_bit) == check_status) + break; + + } while (get_timer(timebase) < timeout); + + spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); + + if ((status & poll_bit) == check_status) + return 0; + + /* Timed out */ + debug("SF: time out!\n"); + return -1; +} + +int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, const void *buf, size_t buf_len) +{ + struct spi_slave *spi = flash->spi; + unsigned long timeout = SPI_FLASH_PROG_TIMEOUT; + int ret; + + if (buf == NULL) + timeout = SPI_FLASH_PAGE_ERASE_TIMEOUT; ret = spi_claim_bus(flash->spi); if (ret) { @@ -86,48 +129,122 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, return ret; } + ret = spi_flash_cmd_write_enable(flash); + if (ret < 0) { + debug("SF: enabling write failed\n"); + return ret; + } + + ret = spi_flash_cmd_write(spi, cmd, cmd_len, buf, buf_len); + if (ret < 0) { + debug("SF: write cmd failed\n"); + return ret; + } + + ret = spi_flash_cmd_wait_ready(flash, timeout); + if (ret < 0) { + debug("SF: write %s timed out\n", + timeout == SPI_FLASH_PROG_TIMEOUT ? + "program" : "page erase"); + return ret; + } + + spi_release_bus(spi); + + return ret; +} + +int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) +{ + u32 erase_size; + u8 cmd[4]; + int ret = -1; + + erase_size = flash->sector_size; + if (offset % erase_size || len % erase_size) { + debug("SF: Erase offset/length not multiple of erase size\n"); + return -1; + } + + if (erase_size == 4096) + cmd[0] = CMD_ERASE_4K; + else + cmd[0] = CMD_ERASE_64K; + + while (len) { +#ifdef CONFIG_SPI_FLASH_BAR + u8 bank_sel; + + bank_sel = offset / SPI_FLASH_16MB_BOUN; + + ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); + if (ret) { + debug("SF: fail to set bank%d\n", bank_sel); + return ret; + } +#endif + spi_flash_addr(offset, cmd); + + debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], + cmd[2], cmd[3], offset); + + ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); + if (ret < 0) { + debug("SF: erase failed\n"); + break; + } + + offset += erase_size; + len -= erase_size; + } + + return ret; +} + +int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, + size_t len, const void *buf) +{ + unsigned long byte_addr, page_size; + size_t chunk_len, actual; + u8 cmd[4]; + int ret = -1; + + page_size = flash->page_size; + cmd[0] = CMD_PAGE_PROGRAM; for (actual = 0; actual < len; actual += chunk_len) { +#ifdef CONFIG_SPI_FLASH_BAR + u8 bank_sel; + + bank_sel = offset / SPI_FLASH_16MB_BOUN; + + ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); + if (ret) { + debug("SF: fail to set bank%d\n", bank_sel); + return ret; + } +#endif + byte_addr = offset % page_size; chunk_len = min(len - actual, page_size - byte_addr); if (flash->spi->max_write_size) chunk_len = min(chunk_len, flash->spi->max_write_size); - cmd[1] = page_addr >> 8; - cmd[2] = page_addr; - cmd[3] = byte_addr; + spi_flash_addr(offset, cmd); debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); - ret = spi_flash_cmd_write_enable(flash); - if (ret < 0) { - debug("SF: enabling write failed\n"); - break; - } - - ret = spi_flash_cmd_write(flash->spi, cmd, 4, - buf + actual, chunk_len); + ret = spi_flash_write_common(flash, cmd, sizeof(cmd), + buf + actual, chunk_len); if (ret < 0) { debug("SF: write failed\n"); break; } - ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); - if (ret) - break; - - byte_addr += chunk_len; - if (byte_addr == page_size) { - page_addr++; - byte_addr = 0; - } + offset += chunk_len; } - debug("SF: program %s %zu bytes @ %#x\n", - ret ? "failure" : "success", len, offset); - - spi_release_bus(flash->spi); return ret; } @@ -137,8 +254,18 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, struct spi_slave *spi = flash->spi; int ret; - spi_claim_bus(spi); + ret = spi_claim_bus(flash->spi); + if (ret) { + debug("SF: unable to claim SPI bus\n"); + return ret; + } + ret = spi_flash_cmd_read(spi, cmd, cmd_len, data, data_len); + if (ret < 0) { + debug("SF: read cmd failed\n"); + return ret; + } + spi_release_bus(spi); return ret; @@ -147,140 +274,125 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, size_t len, void *data) { - u8 cmd[5]; + u8 cmd[5], bank_sel = 0; + u32 remain_len, read_len; + int ret = -1; /* Handle memory-mapped SPI */ - if (flash->memory_map) + if (flash->memory_map) { memcpy(data, flash->memory_map + offset, len); + return 0; + } cmd[0] = CMD_READ_ARRAY_FAST; - spi_flash_addr(offset, cmd); cmd[4] = 0x00; - return spi_flash_read_common(flash, cmd, sizeof(cmd), data, len); -} + while (len) { +#ifdef CONFIG_SPI_FLASH_BAR + bank_sel = offset / SPI_FLASH_16MB_BOUN; -int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout, - u8 cmd, u8 poll_bit) -{ - struct spi_slave *spi = flash->spi; - unsigned long timebase; - int ret; - u8 status; - - ret = spi_xfer(spi, 8, &cmd, NULL, SPI_XFER_BEGIN); - if (ret) { - debug("SF: Failed to send command %02x: %d\n", cmd, ret); - return ret; - } - - timebase = get_timer(0); - do { - WATCHDOG_RESET(); + ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); + if (ret) { + debug("SF: fail to set bank%d\n", bank_sel); + return ret; + } +#endif + remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset); + if (len < remain_len) + read_len = len; + else + read_len = remain_len; - ret = spi_xfer(spi, 8, NULL, &status, 0); - if (ret) - return -1; + spi_flash_addr(offset, cmd); - if ((status & poll_bit) == 0) + ret = spi_flash_read_common(flash, cmd, sizeof(cmd), + data, read_len); + if (ret < 0) { + debug("SF: read failed\n"); break; + } - } while (get_timer(timebase) < timeout); - - spi_xfer(spi, 0, NULL, NULL, SPI_XFER_END); - - if ((status & poll_bit) == 0) - return 0; - - /* Timed out */ - debug("SF: time out!\n"); - return -1; -} + offset += read_len; + len -= read_len; + data += read_len; + } -int spi_flash_cmd_wait_ready(struct spi_flash *flash, unsigned long timeout) -{ - return spi_flash_cmd_poll_bit(flash, timeout, - CMD_READ_STATUS, STATUS_WIP); + return ret; } -int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) +int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) { - u32 start, end, erase_size; + u8 cmd; int ret; - u8 cmd[4]; - - erase_size = flash->sector_size; - if (offset % erase_size || len % erase_size) { - debug("SF: Erase offset/length not multiple of erase size\n"); - return -1; - } - ret = spi_claim_bus(flash->spi); - if (ret) { - debug("SF: Unable to claim SPI bus\n"); + cmd = CMD_WRITE_STATUS; + ret = spi_flash_write_common(flash, &cmd, 1, &sr, 1); + if (ret < 0) { + debug("SF: fail to write status register\n"); return ret; } - if (erase_size == 4096) - cmd[0] = CMD_ERASE_4K; - else - cmd[0] = CMD_ERASE_64K; - start = offset; - end = start + len; - - while (offset < end) { - spi_flash_addr(offset, cmd); - offset += erase_size; - - debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], - cmd[2], cmd[3], offset); - - ret = spi_flash_cmd_write_enable(flash); - if (ret) - goto out; - - ret = spi_flash_cmd_write(flash->spi, cmd, sizeof(cmd), NULL, 0); - if (ret) - goto out; - - ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT); - if (ret) - goto out; - } - - debug("SF: Successfully erased %zu bytes @ %#x\n", len, start); - - out: - spi_release_bus(flash->spi); - return ret; + return 0; } -int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr) +#ifdef CONFIG_SPI_FLASH_BAR +int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel) { u8 cmd; int ret; - ret = spi_flash_cmd_write_enable(flash); + if (flash->bank_curr == bank_sel) { + debug("SF: not require to enable bank%d\n", bank_sel); + return 0; + } + + cmd = flash->bank_write_cmd; + ret = spi_flash_write_common(flash, &cmd, 1, &bank_sel, 1); if (ret < 0) { - debug("SF: enabling write failed\n"); + debug("SF: fail to write bank register\n"); return ret; } + flash->bank_curr = bank_sel; - cmd = CMD_WRITE_STATUS; - ret = spi_flash_cmd_write(flash->spi, &cmd, 1, &sr, 1); - if (ret) { - debug("SF: fail to write status register\n"); - return ret; + return 0; +} + +int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0) +{ + u8 cmd; + u8 curr_bank = 0; + + /* discover bank cmds */ + switch (idcode0) { + case SPI_FLASH_SPANSION_IDCODE0: + flash->bank_read_cmd = CMD_BANKADDR_BRRD; + flash->bank_write_cmd = CMD_BANKADDR_BRWR; + break; + case SPI_FLASH_STMICRO_IDCODE0: + case SPI_FLASH_WINBOND_IDCODE0: + flash->bank_read_cmd = CMD_EXTNADDR_RDEAR; + flash->bank_write_cmd = CMD_EXTNADDR_WREAR; + break; + default: + printf("SF: Unsupported bank commands %02x\n", idcode0); + return -1; } - ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT); - if (ret < 0) { - debug("SF: write status register timed out\n"); - return ret; + /* read the bank reg - on which bank the flash is in currently */ + cmd = flash->bank_read_cmd; + if (flash->size > SPI_FLASH_16MB_BOUN) { + if (spi_flash_read_common(flash, &cmd, 1, &curr_bank, 1)) { + debug("SF: fail to read bank addr register\n"); + return -1; + } + flash->bank_curr = curr_bank; + } else { + flash->bank_curr = curr_bank; } return 0; } +#endif #ifdef CONFIG_OF_CONTROL int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash) @@ -346,6 +458,9 @@ static const struct { #ifdef CONFIG_SPI_FLASH_EON { 0, 0x1c, spi_flash_probe_eon, }, #endif +#ifdef CONFIG_SPI_FLASH_GIGADEVICE + { 0, 0xc8, spi_flash_probe_gigadevice, }, +#endif #ifdef CONFIG_SPI_FLASH_MACRONIX { 0, 0xc2, spi_flash_probe_macronix, }, #endif @@ -426,6 +541,13 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, goto err_manufacturer_probe; } +#ifdef CONFIG_SPI_FLASH_BAR + /* Configure the BAR - disover bank cmds and read current bank */ + ret = spi_flash_bank_config(flash, *idp); + if (ret < 0) + goto err_manufacturer_probe; +#endif + #ifdef CONFIG_OF_CONTROL if (spi_flash_decode_fdt(gd->fdt_blob, flash)) { debug("SF: FDT decode error\n"); @@ -438,6 +560,12 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, if (flash->memory_map) printf(", mapped at %p", flash->memory_map); puts("\n"); +#ifndef CONFIG_SPI_FLASH_BAR + if (flash->size > SPI_FLASH_16MB_BOUN) { + puts("SF: Warning - Only lower 16MiB accessible,"); + puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); + } +#endif spi_release_bus(spi); @@ -468,6 +596,7 @@ void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi, /* Set up some basic fields - caller will sort out sizes */ flash->spi = spi; flash->name = name; + flash->poll_cmd = CMD_READ_STATUS; flash->read = spi_flash_cmd_read_fast; flash->write = spi_flash_cmd_write_multi; diff --git a/drivers/mtd/spi/spi_flash_internal.h b/drivers/mtd/spi/spi_flash_internal.h index 141cfa8b26d..af1afa96c9d 100644 --- a/drivers/mtd/spi/spi_flash_internal.h +++ b/drivers/mtd/spi/spi_flash_internal.h @@ -22,14 +22,31 @@ #define CMD_PAGE_PROGRAM 0x02 #define CMD_WRITE_DISABLE 0x04 #define CMD_READ_STATUS 0x05 +#define CMD_FLAG_STATUS 0x70 #define CMD_WRITE_ENABLE 0x06 #define CMD_ERASE_4K 0x20 #define CMD_ERASE_32K 0x52 #define CMD_ERASE_64K 0xd8 #define CMD_ERASE_CHIP 0xc7 +#define SPI_FLASH_16MB_BOUN 0x1000000 + +/* Manufacture ID's */ +#define SPI_FLASH_SPANSION_IDCODE0 0x01 +#define SPI_FLASH_STMICRO_IDCODE0 0x20 +#define SPI_FLASH_WINBOND_IDCODE0 0xef + +#ifdef CONFIG_SPI_FLASH_BAR +/* Bank addr access commands */ +# define CMD_BANKADDR_BRWR 0x17 +# define CMD_BANKADDR_BRRD 0x16 +# define CMD_EXTNADDR_WREAR 0xC5 +# define CMD_EXTNADDR_RDEAR 0xC8 +#endif + /* Common status */ #define STATUS_WIP 0x01 +#define STATUS_PEC 0x80 /* Send a single-byte command to the device and read the response */ int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len); @@ -77,16 +94,30 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash) /* Program the status register. */ int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr); +#ifdef CONFIG_SPI_FLASH_BAR +/* Program the bank address register */ +int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 bank_sel); + +/* Configure the BAR - discover the bank cmds */ +int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0); +#endif + /* * Same as spi_flash_cmd_read() except it also claims/releases the SPI * bus. Used as common part of the ->read() operation. */ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd, size_t cmd_len, void *data, size_t data_len); - -/* Send a command to the device and wait for some bit to clear itself. */ -int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout, - u8 cmd, u8 poll_bit); +/* + * Used for spi_flash write operation + * - SPI claim + * - spi_flash_cmd_write_enable + * - spi_flash_cmd_write + * - spi_flash_cmd_wait_ready + * - SPI release + */ +int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, + size_t cmd_len, const void *buf, size_t buf_len); /* * Send the read status command to the device and wait for the wip @@ -106,3 +137,4 @@ struct spi_flash *spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode); struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode); +struct spi_flash *spi_flash_probe_gigadevice(struct spi_slave *spi, u8 *idcode); diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c index 2a9972bd4ee..7e41ee13219 100644 --- a/drivers/mtd/spi/stmicro.c +++ b/drivers/mtd/spi/stmicro.c @@ -140,6 +140,30 @@ static const struct stmicro_spi_flash_params stmicro_spi_flash_table[] = { .nr_sectors = 512, .name = "N25Q256A", }, + { + .id = 0xba20, + .pages_per_sector = 256, + .nr_sectors = 1024, + .name = "N25Q512", + }, + { + .id = 0xbb20, + .pages_per_sector = 256, + .nr_sectors = 1024, + .name = "N25Q512A", + }, + { + .id = 0xba21, + .pages_per_sector = 256, + .nr_sectors = 2048, + .name = "N25Q1024", + }, + { + .id = 0xbb21, + .pages_per_sector = 256, + .nr_sectors = 2048, + .name = "N25Q1024A", + }, }; struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode) @@ -186,5 +210,9 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode) flash->sector_size = 256 * params->pages_per_sector; flash->size = flash->sector_size * params->nr_sectors; + /* for >= 512MiB flashes, use flag status instead of read_status */ + if (flash->size >= 0x4000000) + flash->poll_cmd = CMD_FLAG_STATUS; + return flash; } diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index 27162091c5a..c399bf14d1f 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -18,6 +18,21 @@ struct winbond_spi_flash_params { static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { { + .id = 0x2014, + .nr_blocks = 16, + .name = "W25P80", + }, + { + .id = 0x2015, + .nr_blocks = 32, + .name = "W25P16", + }, + { + .id = 0x2016, + .nr_blocks = 64, + .name = "W25P32", + }, + { .id = 0x3013, .nr_blocks = 8, .name = "W25X40", @@ -40,42 +55,57 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { { .id = 0x4014, .nr_blocks = 16, - .name = "W25Q80BL", + .name = "W25Q80BL/W25Q80BV", }, { .id = 0x4015, .nr_blocks = 32, - .name = "W25Q16", + .name = "W25Q16CL/W25Q16DV", }, { .id = 0x4016, .nr_blocks = 64, - .name = "W25Q32", + .name = "W25Q32BV/W25Q32FV_SPI", }, { .id = 0x4017, .nr_blocks = 128, - .name = "W25Q64", + .name = "W25Q64CV/W25Q64FV_SPI", }, { .id = 0x4018, .nr_blocks = 256, - .name = "W25Q128", + .name = "W25Q128BV/W25Q128FV_SPI", + }, + { + .id = 0x4019, + .nr_blocks = 512, + .name = "W25Q256", }, { .id = 0x5014, - .nr_blocks = 128, - .name = "W25Q80", + .nr_blocks = 16, + .name = "W25Q80BW", + }, + { + .id = 0x6015, + .nr_blocks = 32, + .name = "W25Q16DW", }, { .id = 0x6016, - .nr_blocks = 512, - .name = "W25Q32DW", + .nr_blocks = 64, + .name = "W25Q32DW/W25Q32FV_QPI", }, { .id = 0x6017, .nr_blocks = 128, - .name = "W25Q64DW", + .name = "W25Q64DW/W25Q64FV_QPI", + }, + { + .id = 0x6018, + .nr_blocks = 256, + .name = "W25Q128FW/W25Q128FV_QPI", }, }; @@ -104,7 +134,7 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode) } flash->page_size = 256; - flash->sector_size = 4096; + flash->sector_size = (idcode[1] == 0x20) ? 65536 : 4096; flash->size = 4096 * 16 * params->nr_blocks; return flash; diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 4dbcdca4a03..da95e285b70 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -516,9 +516,7 @@ static int fec_open(struct eth_device *edev) #ifdef FEC_QUIRK_ENET_MAC { u32 ecr = readl(&fec->eth->ecntrl) & ~FEC_ECNTRL_SPEED; - u32 rcr = (readl(&fec->eth->r_cntrl) & - ~(FEC_RCNTRL_RMII | FEC_RCNTRL_RMII_10T)) | - FEC_RCNTRL_RGMII | FEC_RCNTRL_MII_MODE; + u32 rcr = readl(&fec->eth->r_cntrl) & ~FEC_RCNTRL_RMII_10T; if (speed == _1000BASET) ecr |= FEC_ECNTRL_SPEED; else if (speed != _100BASET) diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c index 77ac1f7c7b9..621c899120a 100644 --- a/drivers/pci/fsl_pci_init.c +++ b/drivers/pci/fsl_pci_init.c @@ -211,7 +211,7 @@ static int fsl_pci_setup_inbound_windows(struct pci_controller *hose, return 1; } -#ifdef CONFIG_SYS_FSL_SRIO_PCIE_BOOT_MASTER +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER static void fsl_pcie_boot_master(pit_t *pi) { /* configure inbound window for slave's u-boot image */ @@ -388,7 +388,7 @@ void fsl_pci_init(struct pci_controller *hose, struct fsl_pci_info *pci_info) /* see if we are a PCIe or PCI controller */ pci_hose_read_config_byte(hose, dev, FSL_PCIE_CAP_ID, &pcie_cap); -#ifdef CONFIG_SYS_FSL_SRIO_PCIE_BOOT_MASTER +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER /* boot from PCIE --master */ char *s = getenv("bootmaster"); char pcie[6]; @@ -624,7 +624,7 @@ int fsl_pci_init_port(struct fsl_pci_info *pci_info, if (fsl_is_pci_agent(hose)) { fsl_pci_config_unlock(hose); hose->last_busno = hose->first_busno; -#ifdef CONFIG_SYS_FSL_SRIO_PCIE_BOOT_MASTER +#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER } else { /* boot from PCIE --master releases slave's core 0 */ char *s = getenv("bootmaster"); diff --git a/drivers/power/exynos-tmu.c b/drivers/power/exynos-tmu.c index d4b3e65a3ea..9a093a5bddd 100644 --- a/drivers/power/exynos-tmu.c +++ b/drivers/power/exynos-tmu.c @@ -50,15 +50,15 @@ /* Tmeperature threshold values for various thermal events */ struct temperature_params { /* minimum value in temperature code range */ - unsigned int min_val; + unsigned min_val; /* maximum value in temperature code range */ - unsigned int max_val; + unsigned max_val; /* temperature threshold to start warning */ - unsigned int start_warning; + unsigned start_warning; /* temperature threshold CPU tripping */ - unsigned int start_tripping; + unsigned start_tripping; /* temperature threshold for HW tripping */ - unsigned int hardware_tripping; + unsigned hardware_tripping; }; /* Pre-defined values and thresholds for calibration of current temperature */ @@ -66,25 +66,27 @@ struct tmu_data { /* pre-defined temperature thresholds */ struct temperature_params ts; /* pre-defined efuse range minimum value */ - unsigned int efuse_min_value; + unsigned efuse_min_value; /* pre-defined efuse value for temperature calibration */ - unsigned int efuse_value; + unsigned efuse_value; /* pre-defined efuse range maximum value */ - unsigned int efuse_max_value; + unsigned efuse_max_value; /* current temperature sensing slope */ - unsigned int slope; + unsigned slope; }; /* TMU device specific details and status */ struct tmu_info { /* base Address for the TMU */ - unsigned tmu_base; + struct exynos5_tmu_reg *tmu_base; + /* mux Address for the TMU */ + int tmu_mux; /* pre-defined values for calibration and thresholds */ struct tmu_data data; /* value required for triminfo_25 calibration */ - unsigned int te1; + unsigned te1; /* value required for triminfo_85 calibration */ - unsigned int te2; + unsigned te2; /* Value for measured data calibration */ int dc_value; /* enum value indicating status of the TMU */ @@ -103,17 +105,24 @@ static struct tmu_info gbl_info; */ static int get_cur_temp(struct tmu_info *info) { - int cur_temp; - struct exynos5_tmu_reg *reg = (struct exynos5_tmu_reg *)info->tmu_base; + struct exynos5_tmu_reg *reg = info->tmu_base; + ulong start; + int cur_temp = 0; /* * Temperature code range between min 25 and max 125. * May run more than once for first call as initial sensing * has not yet happened. */ - do { - cur_temp = readl(®->current_temp) & 0xff; - } while (cur_temp == 0 && info->tmu_state == TMU_STATUS_NORMAL); + if (info->tmu_state == TMU_STATUS_NORMAL) { + start = get_timer(0); + do { + cur_temp = readl(®->current_temp) & 0xff; + } while ((cur_temp == 0) || (get_timer(start) > 100)); + } + + if (cur_temp == 0) + return cur_temp; /* Calibrate current temperature */ cur_temp = cur_temp - info->te1 + info->dc_value; @@ -137,23 +146,29 @@ enum tmu_status_t tmu_monitor(int *temp) /* Read current temperature of the SOC */ cur_temp = get_cur_temp(&gbl_info); + + if (!cur_temp) + goto out; + *temp = cur_temp; /* Temperature code lies between min 25 and max 125 */ - if (cur_temp >= data->ts.start_tripping && - cur_temp <= data->ts.max_val) { + if ((cur_temp >= data->ts.start_tripping) && + (cur_temp <= data->ts.max_val)) return TMU_STATUS_TRIPPED; - } else if (cur_temp >= data->ts.start_warning) { + + if (cur_temp >= data->ts.start_warning) return TMU_STATUS_WARNING; - } else if (cur_temp < data->ts.start_warning && - cur_temp >= data->ts.min_val) { + + if ((cur_temp < data->ts.start_warning) && + (cur_temp >= data->ts.min_val)) return TMU_STATUS_NORMAL; - } else { - /* Temperature code does not lie between min 25 and max 125 */ - gbl_info.tmu_state = TMU_STATUS_INIT; - debug("EXYNOS_TMU: Thermal reading failed\n"); - return TMU_STATUS_INIT; - } + + out: + /* Temperature code does not lie between min 25 and max 125 */ + gbl_info.tmu_state = TMU_STATUS_INIT; + debug("EXYNOS_TMU: Thermal reading failed\n"); + return TMU_STATUS_INIT; } /* @@ -166,6 +181,7 @@ enum tmu_status_t tmu_monitor(int *temp) static int get_tmu_fdt_values(struct tmu_info *info, const void *blob) { #ifdef CONFIG_OF_CONTROL + fdt_addr_t addr; int node; int error = 0; @@ -183,46 +199,58 @@ static int get_tmu_fdt_values(struct tmu_info *info, const void *blob) * miscalculation of register values in tmu_setup_parameters * may result in misleading current temperature. */ - info->tmu_base = fdtdec_get_addr(blob, node, "reg"); - if (info->tmu_base == FDT_ADDR_T_NONE) { + addr = fdtdec_get_addr(blob, node, "reg"); + if (addr == FDT_ADDR_T_NONE) { debug("%s: Missing tmu-base\n", __func__); return -1; } + info->tmu_base = (struct exynos5_tmu_reg *)addr; + + /* Optional field. */ + info->tmu_mux = fdtdec_get_int(blob, + node, "samsung,mux", -1); + /* Take default value as per the user manual b(110) */ + if (info->tmu_mux == -1) + info->tmu_mux = 0x6; + info->data.ts.min_val = fdtdec_get_int(blob, node, "samsung,min-temp", -1); - error |= info->data.ts.min_val; + error |= (info->data.ts.min_val == -1); info->data.ts.max_val = fdtdec_get_int(blob, node, "samsung,max-temp", -1); - error |= info->data.ts.max_val; + error |= (info->data.ts.max_val == -1); info->data.ts.start_warning = fdtdec_get_int(blob, node, "samsung,start-warning", -1); - error |= info->data.ts.start_warning; + error |= (info->data.ts.start_warning == -1); info->data.ts.start_tripping = fdtdec_get_int(blob, node, "samsung,start-tripping", -1); - error |= info->data.ts.start_tripping; + error |= (info->data.ts.start_tripping == -1); info->data.ts.hardware_tripping = fdtdec_get_int(blob, node, "samsung,hw-tripping", -1); - error |= info->data.ts.hardware_tripping; + error |= (info->data.ts.hardware_tripping == -1); info->data.efuse_min_value = fdtdec_get_int(blob, node, "samsung,efuse-min-value", -1); - error |= info->data.efuse_min_value; + error |= (info->data.efuse_min_value == -1); info->data.efuse_value = fdtdec_get_int(blob, node, "samsung,efuse-value", -1); - error |= info->data.efuse_value; + error |= (info->data.efuse_value == -1); info->data.efuse_max_value = fdtdec_get_int(blob, node, "samsung,efuse-max-value", -1); - error |= info->data.efuse_max_value; + error |= (info->data.efuse_max_value == -1); info->data.slope = fdtdec_get_int(blob, node, "samsung,slope", -1); - error |= info->data.slope; + error |= (info->data.slope == -1); info->dc_value = fdtdec_get_int(blob, node, "samsung,dc-value", -1); - error |= info->dc_value; + error |= (info->dc_value == -1); - if (error == -1) { + if (error) { debug("fail to get tmu node properties\n"); return -1; } +#else + /* Non DT support may never be added. Just in case */ + return -1; #endif return 0; @@ -236,12 +264,12 @@ static int get_tmu_fdt_values(struct tmu_info *info, const void *blob) */ static void tmu_setup_parameters(struct tmu_info *info) { - unsigned int te_code, con; - unsigned int warning_code, trip_code, hwtrip_code; - unsigned int cooling_temp; - unsigned int rising_value; + unsigned te_code, con; + unsigned warning_code, trip_code, hwtrip_code; + unsigned cooling_temp; + unsigned rising_value; struct tmu_data *data = &info->data; - struct exynos5_tmu_reg *reg = (struct exynos5_tmu_reg *)info->tmu_base; + struct exynos5_tmu_reg *reg = info->tmu_base; /* Must reload for reading efuse value from triminfo register */ writel(TRIMINFO_RELOAD, ®->triminfo_control); @@ -288,7 +316,7 @@ static void tmu_setup_parameters(struct tmu_info *info) /* TMU core enable */ con = readl(®->tmu_control); - con |= THERM_TRIP_EN | CORE_EN; + con |= THERM_TRIP_EN | CORE_EN | (info->tmu_mux << 20); writel(con, ®->tmu_control); @@ -314,6 +342,5 @@ int tmu_init(const void *blob) tmu_setup_parameters(&gbl_info); gbl_info.tmu_state = TMU_STATUS_NORMAL; ret: - return gbl_info.tmu_state; } diff --git a/drivers/power/palmas.c b/drivers/power/palmas.c index 09c832d8b6e..2d275a76145 100644 --- a/drivers/power/palmas.c +++ b/drivers/power/palmas.c @@ -25,28 +25,137 @@ void palmas_init_settings(void) { - return; +#ifdef CONFIG_PALMAS_SMPS7_FPWM + int err; + /* + * Set SMPS7 (1.8 V I/O supply on platforms with TWL6035/37) to + * forced PWM mode. This reduces noise (but affects efficiency). + */ + u8 val = SMPS_MODE_SLP_FPWM | SMPS_MODE_ACT_FPWM; + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS7_CTRL, val); + if (err) + printf("palmas: could not force PWM for SMPS7: err = %d\n", + err); +#endif } int palmas_mmc1_poweron_ldo(void) { u8 val = 0; - /* set LDO9 TWL6035 to 3V */ - val = 0x2b; /* (3 -.9)*28 +1 */ - - if (palmas_i2c_write_u8(0x48, LDO9_VOLTAGE, val)) { - printf("twl6035: could not set LDO9 voltage.\n"); +#if defined(CONFIG_DRA7XX) + /* + * Currently valid for the dra7xx_evm board: + * Set TPS659038 LDO1 to 3.0 V + */ + val = LDO_VOLT_3V0; + if (palmas_i2c_write_u8(TPS65903X_CHIP_P1, LDO1_VOLTAGE, val)) { + printf("tps65903x: could not set LDO1 voltage.\n"); + return 1; + } + /* TURN ON LDO1 */ + val = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + if (palmas_i2c_write_u8(TPS65903X_CHIP_P1, LDO1_CTRL, val)) { + printf("tps65903x: could not turn on LDO1.\n"); return 1; } + return 0; +#else + /* + * We assume that this is a OMAP543X + TWL603X board: + * Set TWL6035/37 LDO9 to 3.0 V + */ + val = LDO_VOLT_3V0; + return twl603x_mmc1_set_ldo9(val); +#endif +} - /* TURN ON LDO9 */ - val = LDO_ON | LDO_MODE_SLEEP | LDO_MODE_ACTIVE; +/* + * On some OMAP5 + TWL603X hardware the SD card socket and LDO9_IN are + * powered by an external 3.3 V regulator, while the output of LDO9 + * supplies VDDS_SDCARD for the OMAP5 interface only. This implies that + * LDO9 could be set to 'bypass' mode when required (e.g. for 3.3 V cards). + */ +int twl603x_mmc1_set_ldo9(u8 vsel) +{ + u8 cval = 0, vval = 0; /* Off by default */ + int err; - if (palmas_i2c_write_u8(0x48, LDO9_CTRL, val)) { - printf("twl6035: could not turn on LDO9.\n"); - return 1; + if (vsel) { + /* Turn on */ + if (vsel > LDO_VOLT_3V3) { + /* Put LDO9 in bypass */ + cval = LDO9_BYP_EN | RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + vval = LDO_VOLT_3V3; + } else { + cval = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + vval = vsel & 0x3f; + } + } + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, LDO9_VOLTAGE, vval); + if (err) { + printf("twl603x: could not set LDO9 %s: err = %d\n", + vsel > LDO_VOLT_3V3 ? "bypass" : "voltage", err); + return err; } + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, LDO9_CTRL, cval); + if (err) + printf("twl603x: could not turn %s LDO9: err = %d\n", + cval ? "on" : "off", err); + return err; +} - return 0; +#ifdef CONFIG_PALMAS_AUDPWR +/* + * Turn audio codec power and 32 kHz clock on/off. Use for + * testing OMAP543X + TWL603X + TWL604X boards only. + */ +int twl603x_audio_power(u8 on) +{ + u8 cval = 0, vval = 0, c32k = 0; + int err; + + if (on) { + vval = SMPS_VOLT_2V1; + cval = SMPS_MODE_SLP_AUTO | SMPS_MODE_ACT_AUTO; + c32k = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + } + /* Set SMPS9 to 2.1 V (for TWL604x), or to 0 (off) */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS9_VOLTAGE, vval); + if (err) { + printf("twl603x: could not set SMPS9 voltage: err = %d\n", + err); + return err; + } + /* Turn on or off SMPS9 */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS9_CTRL, cval); + if (err) { + printf("twl603x: could not turn SMPS9 %s: err = %d\n", + cval ? "on" : "off", err); + return err; + } + /* Output 32 kHz clock on or off */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, CLK32KGAUDIO_CTRL, c32k); + if (err) + printf("twl603x: could not turn CLK32KGAUDIO %s: err = %d\n", + c32k ? "on" : "off", err); + return err; +} +#endif + +/* + * Enable/disable back-up battery (or super cap) charging on TWL6035/37. + * Please use defined BB_xxx values. + */ +int twl603x_enable_bb_charge(u8 bb_fields) +{ + u8 val = bb_fields & 0x0f; + int err; + + val |= (VRTC_EN_SLP | VRTC_EN_OFF | VRTC_PWEN); + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, BB_VRTC_CTRL, val); + if (err) + printf("twl603x: could not set BB_VRTC_CTRL to 0x%02x: err = %d\n", + val, err); + return err; } diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 442b7ea0df4..0f954a5f33a 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -53,6 +53,7 @@ COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o COBJS-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o COBJS-$(CONFIG_BFIN_SERIAL) += serial_bfin.o +COBJS-$(CONFIG_FSL_LPUART) += serial_lpuart.o ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 7f013ab33c5..d77c25fa9b3 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -74,13 +74,8 @@ void NS16550_init(NS16550_t com_port, int baud_divisor) defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ defined(CONFIG_TI814X) -#if defined(CONFIG_APTIX) - /* /13 mode so Aptix 6MHz can hit 115200 */ - serial_out(3, &com_port->mdr1); -#else /* /16 is proper to hit 115200 with 48MHz */ serial_out(0, &com_port->mdr1); -#endif #endif /* CONFIG_OMAP */ } diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c new file mode 100644 index 00000000000..51d56662c6f --- /dev/null +++ b/drivers/serial/serial_lpuart.c @@ -0,0 +1,132 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 <watchdog.h> +#include <asm/io.h> +#include <serial.h> +#include <linux/compiler.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> + +#define US1_TDRE (1 << 7) +#define US1_RDRF (1 << 5) +#define UC2_TE (1 << 3) +#define UC2_RE (1 << 2) + +DECLARE_GLOBAL_DATA_PTR; + +struct lpuart_fsl *base = (struct lpuart_fsl *)LPUART_BASE; + +static void lpuart_serial_setbrg(void) +{ + u32 clk = mxc_get_clock(MXC_UART_CLK); + u16 sbr; + + if (!gd->baudrate) + gd->baudrate = CONFIG_BAUDRATE; + + sbr = (u16)(clk / (16 * gd->baudrate)); + /* place adjustment later - n/32 BRFA */ + + __raw_writeb(sbr >> 8, &base->ubdh); + __raw_writeb(sbr & 0xff, &base->ubdl); +} + +static int lpuart_serial_getc(void) +{ + u8 status; + + while (!(__raw_readb(&base->us1) & US1_RDRF)) + WATCHDOG_RESET(); + + status = __raw_readb(&base->us1); + status |= US1_RDRF; + __raw_writeb(status, &base->us1); + + return __raw_readb(&base->ud); +} + +static void lpuart_serial_putc(const char c) +{ + if (c == '\n') + serial_putc('\r'); + + while (!(__raw_readb(&base->us1) & US1_TDRE)) + WATCHDOG_RESET(); + + __raw_writeb(c, &base->ud); +} + +/* + * Test whether a character is in the RX buffer + */ +static int lpuart_serial_tstc(void) +{ + if (__raw_readb(&base->urcfifo) == 0) + return 0; + + return 1; +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +static int lpuart_serial_init(void) +{ + u8 ctrl; + + ctrl = __raw_readb(&base->uc2); + ctrl &= ~UC2_RE; + ctrl &= ~UC2_TE; + __raw_writeb(ctrl, &base->uc2); + + __raw_writeb(0, &base->umodem); + __raw_writeb(0, &base->uc1); + + /* provide data bits, parity, stop bit, etc */ + + serial_setbrg(); + + __raw_writeb(UC2_RE | UC2_TE, &base->uc2); + + return 0; +} + +static struct serial_device lpuart_serial_drv = { + .name = "lpuart_serial", + .start = lpuart_serial_init, + .stop = NULL, + .setbrg = lpuart_serial_setbrg, + .putc = lpuart_serial_putc, + .puts = default_serial_puts, + .getc = lpuart_serial_getc, + .tstc = lpuart_serial_tstc, +}; + +void lpuart_serial_initialize(void) +{ + serial_register(&lpuart_serial_drv); +} + +__weak struct serial_device *default_serial_console(void) +{ + return &lpuart_serial_drv; +} diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index b92eef4db95..3c07da35976 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -151,12 +151,7 @@ static int calc_divisor (NS16550_t port) } #endif -#ifdef CONFIG_APTIX -#define MODE_X_DIV 13 -#else #define MODE_X_DIV 16 -#endif - /* Compute divisor value. Normally, we should simply return: * CONFIG_SYS_NS16550_CLK) / MODE_X_DIV / gd->baudrate * but we need to round that value by adding 0.5. diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c index 3c41242a8ec..e65125ccd74 100644 --- a/drivers/serial/serial_s5p.c +++ b/drivers/serial/serial_s5p.c @@ -30,6 +30,10 @@ DECLARE_GLOBAL_DATA_PTR; +#define RX_FIFO_COUNT_MASK 0xff +#define RX_FIFO_FULL_MASK (1 << 8) +#define TX_FIFO_FULL_MASK (1 << 24) + static inline struct s5p_uart *s5p_get_base_uart(int dev_index) { u32 offset = dev_index * sizeof(struct s5p_uart); @@ -87,8 +91,8 @@ int serial_init_dev(const int dev_index) { struct s5p_uart *const uart = s5p_get_base_uart(dev_index); - /* reset and enable FIFOs, set triggers to the maximum */ - writel(0, &uart->ufcon); + /* enable FIFOs */ + writel(0x1, &uart->ufcon); writel(0, &uart->umcon); /* 8N1 */ writel(0x3, &uart->ulcon); @@ -130,7 +134,8 @@ int serial_getc_dev(const int dev_index) struct s5p_uart *const uart = s5p_get_base_uart(dev_index); /* wait for character to arrive */ - while (!(readl(&uart->utrstat) & 0x1)) { + while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | + RX_FIFO_FULL_MASK))) { if (serial_err_check(dev_index, 0)) return 0; } @@ -146,7 +151,7 @@ void serial_putc_dev(const char c, const int dev_index) struct s5p_uart *const uart = s5p_get_base_uart(dev_index); /* wait for room in the tx FIFO */ - while (!(readl(&uart->utrstat) & 0x2)) { + while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { if (serial_err_check(dev_index, 1)) return; } diff --git a/drivers/spi/armada100_spi.c b/drivers/spi/armada100_spi.c index afdbe0508ca..b237c7c0fea 100644 --- a/drivers/spi/armada100_spi.c +++ b/drivers/spi/armada100_spi.c @@ -182,15 +182,8 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, goto done; } - if (dout) - pss->tx = dout; - else - pss->tx = NULL; - - if (din) - pss->rx = din; - else - pss->rx = NULL; + pss->tx = dout; + pss->rx = din; if (flags & SPI_XFER_BEGIN) { spi_cs_activate(slave); diff --git a/drivers/spi/cf_qspi.c b/drivers/spi/cf_qspi.c index a37ac4e5264..06bcf91a4aa 100644 --- a/drivers/spi/cf_qspi.c +++ b/drivers/spi/cf_qspi.c @@ -171,7 +171,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, volatile qspi_t *qspi = dev->regs; u8 *txbuf = (u8 *)dout; u8 *rxbuf = (u8 *)din; - u32 count = ((bitlen / 8) + (bitlen % 8 ? 1 : 0)); + u32 count = DIV_ROUND_UP(bitlen, 8); u32 n, i = 0; /* Sanitize arguments */ diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index 607e1cdec2f..01378d098e5 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -51,6 +51,7 @@ struct exynos_spi_slave { unsigned int mode; enum periph_id periph_id; /* Peripheral ID for this device */ unsigned int fifo_size; + int skip_preamble; }; static struct spi_bus *spi_get_bus(unsigned dev_index) @@ -105,6 +106,8 @@ struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, else spi_slave->fifo_size = 256; + spi_slave->skip_preamble = 0; + spi_slave->freq = bus->frequency; if (max_hz) spi_slave->freq = min(max_hz, spi_slave->freq); @@ -217,17 +220,23 @@ static void spi_request_bytes(struct exynos_spi *regs, int count) writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); } -static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, - void **dinp, void const **doutp) +static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, + void **dinp, void const **doutp, unsigned long flags) { struct exynos_spi *regs = spi_slave->regs; uchar *rxp = *dinp; const uchar *txp = *doutp; int rx_lvl, tx_lvl; uint out_bytes, in_bytes; + int toread; + unsigned start = get_timer(0); + int stopping; out_bytes = in_bytes = todo; + stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) && + !(spi_slave->mode & SPI_SLAVE); + /* * If there's something to send, do a software reset and set a * transaction size. @@ -238,6 +247,8 @@ static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, * Bytes are transmitted/received in pairs. Wait to receive all the * data because then transmission will be done as well. */ + toread = in_bytes; + while (in_bytes) { int temp; @@ -248,15 +259,43 @@ static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, writel(temp, ®s->tx_data); out_bytes--; } - if (rx_lvl > 0 && in_bytes) { + if (rx_lvl > 0) { temp = readl(®s->rx_data); - if (rxp) - *rxp++ = temp; - in_bytes--; + if (spi_slave->skip_preamble) { + if (temp == SPI_PREAMBLE_END_BYTE) { + spi_slave->skip_preamble = 0; + stopping = 0; + } + } else { + if (rxp || stopping) + *rxp++ = temp; + in_bytes--; + } + toread--; + } else if (!toread) { + /* + * We have run out of input data, but haven't read + * enough bytes after the preamble yet. Read some more, + * and make sure that we transmit dummy bytes too, to + * keep things going. + */ + assert(!out_bytes); + out_bytes = in_bytes; + toread = in_bytes; + txp = NULL; + spi_request_bytes(regs, toread); + } + if (spi_slave->skip_preamble && get_timer(start) > 100) { + printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", + in_bytes, out_bytes); + return -1; } } + *dinp = rxp; *doutp = txp; + + return 0; } /** @@ -276,6 +315,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); int upto, todo; int bytelen; + int ret = 0; /* spi core configured to do 8 bit transfers */ if (bitlen % 8) { @@ -289,16 +329,24 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, /* Exynos SPI limits each transfer to 65535 bytes */ bytelen = bitlen / 8; - for (upto = 0; upto < bytelen; upto += todo) { + for (upto = 0; !ret && upto < bytelen; upto += todo) { todo = min(bytelen - upto, (1 << 16) - 1); - spi_rx_tx(spi_slave, todo, &din, &dout); + ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags); + if (ret) + break; } /* Stop the transaction, if necessary. */ - if ((flags & SPI_XFER_END)) + if ((flags & SPI_XFER_END) && !(spi_slave->mode & SPI_SLAVE)) { spi_cs_deactivate(slave); + if (spi_slave->skip_preamble) { + assert(!spi_slave->skip_preamble); + debug("Failed to complete premable transaction\n"); + ret = -1; + } + } - return 0; + return ret; } /** @@ -325,6 +373,7 @@ void spi_cs_activate(struct spi_slave *slave) clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); debug("Activate CS, bus %d\n", spi_slave->slave.bus); + spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; } /** diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index 5bed858787f..2ea322833c2 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -224,7 +224,7 @@ int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen, const u8 *dout, u8 *din, unsigned long flags) { struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); - int nbytes = (bitlen + 7) / 8; + int nbytes = DIV_ROUND_UP(bitlen, 8); u32 data, cnt, i; struct cspi_regs *regs = (struct cspi_regs *)mxcs->base; @@ -294,7 +294,7 @@ int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen, /* Transfer completed, clear any pending request */ reg_write(®s->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF); - nbytes = (bitlen + 7) / 8; + nbytes = DIV_ROUND_UP(bitlen, 8); cnt = nbytes % 32; @@ -330,7 +330,7 @@ int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen, int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { - int n_bytes = (bitlen + 7) / 8; + int n_bytes = DIV_ROUND_UP(bitlen, 8); int n_bits; int ret; u32 blk_size; diff --git a/drivers/spi/tegra114_spi.c b/drivers/spi/tegra114_spi.c index b11a0a1ff7c..4d2af483d77 100644 --- a/drivers/spi/tegra114_spi.c +++ b/drivers/spi/tegra114_spi.c @@ -152,13 +152,11 @@ struct spi_slave *tegra114_spi_setup_slave(unsigned int bus, unsigned int cs, return NULL; } - spi = malloc(sizeof(struct tegra_spi_slave)); + spi = spi_alloc_slave(struct tegra_spi_slave, bus, cs); if (!spi) { printf("SPI error: malloc of SPI structure failed\n"); return NULL; } - spi->slave.bus = bus; - spi->slave.cs = cs; spi->ctrl = &spi_ctrls[bus]; if (!spi->ctrl) { printf("SPI error: could not find controller for bus %d\n", diff --git a/drivers/spi/tegra20_sflash.c b/drivers/spi/tegra20_sflash.c index 9322ce7f641..7c3a3fc35b1 100644 --- a/drivers/spi/tegra20_sflash.c +++ b/drivers/spi/tegra20_sflash.c @@ -132,8 +132,6 @@ struct spi_slave *tegra20_spi_setup_slave(unsigned int bus, unsigned int cs, printf("SPI error: malloc of SPI structure failed\n"); return NULL; } - spi->slave.bus = bus; - spi->slave.cs = cs; spi->ctrl = &spi_ctrls[bus]; if (!spi->ctrl) { printf("SPI error: could not find controller for bus %d\n", diff --git a/drivers/usb/eth/asix.c b/drivers/usb/eth/asix.c index 75ec8f78815..76624b92564 100644 --- a/drivers/usb/eth/asix.c +++ b/drivers/usb/eth/asix.c @@ -407,46 +407,40 @@ static int asix_basic_reset(struct ueth_data *dev) rx_ctl = asix_read_rx_ctl(dev); debug("RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl); - return 0; -} - -/* - * Asix callbacks - */ -static int asix_init(struct eth_device *eth, bd_t *bd) -{ - struct ueth_data *dev = (struct ueth_data *)eth->priv; - int timeout = 0; -#define TIMEOUT_RESOLUTION 50 /* ms */ - int link_detected; - - debug("** %s()\n", __func__); - dev->phy_id = asix_get_phy_addr(dev); if (dev->phy_id < 0) debug("Failed to read phy id\n"); - if (asix_sw_reset(dev, AX_SWRESET_PRL) < 0) - goto out_err; - - if (asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL) < 0) - goto out_err; - asix_mdio_write(dev, dev->phy_id, MII_BMCR, BMCR_RESET); asix_mdio_write(dev, dev->phy_id, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_CSMA); mii_nway_restart(dev); if (asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT) < 0) - goto out_err; + return -1; if (asix_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT, AX88772_IPG2_DEFAULT, 0, NULL) < 0) { debug("Write IPG,IPG1,IPG2 failed\n"); - goto out_err; + return -1; } + return 0; +} + +/* + * Asix callbacks + */ +static int asix_init(struct eth_device *eth, bd_t *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + int timeout = 0; +#define TIMEOUT_RESOLUTION 50 /* ms */ + int link_detected; + + debug("** %s()\n", __func__); + if (asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL) < 0) goto out_err; diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e545b6be6b3..432cf178cf4 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -35,6 +35,7 @@ endif # new USB gadget layer dependencies ifdef CONFIG_USB_GADGET COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +COBJS-$(CONFIG_USB_GADGET_FOTG210) += fotg210.o COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o endif diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 2c5600ed521..f30778a163b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1098,4 +1098,5 @@ void usb_composite_unregister(struct usb_composite_driver *driver) if (composite != driver) return; usb_gadget_unregister_driver(&composite_driver); + composite = NULL; } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index c28866f7d3c..45bc132aef6 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2261,7 +2261,8 @@ reset: if (rc) goto reset; fsg->bulk_out_enabled = 1; - common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + common->bulk_out_maxpacket = + le16_to_cpu(get_unaligned(&d->wMaxPacketSize)); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ diff --git a/drivers/usb/gadget/fotg210.c b/drivers/usb/gadget/fotg210.c new file mode 100644 index 00000000000..d003331baee --- /dev/null +++ b/drivers/usb/gadget/fotg210.c @@ -0,0 +1,948 @@ +/* + * Faraday USB 2.0 OTG Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dantesu@faraday-tech.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <net.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <linux/types.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <usb/fotg210.h> + +#define CFG_NUM_ENDPOINTS 4 +#define CFG_EP0_MAX_PACKET_SIZE 64 +#define CFG_EPX_MAX_PACKET_SIZE 512 + +#define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 2) /* 250 ms */ + +struct fotg210_chip; + +struct fotg210_ep { + struct usb_ep ep; + + uint maxpacket; + uint id; + uint stopped; + + struct list_head queue; + struct fotg210_chip *chip; + const struct usb_endpoint_descriptor *desc; +}; + +struct fotg210_request { + struct usb_request req; + struct list_head queue; + struct fotg210_ep *ep; +}; + +struct fotg210_chip { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct fotg210_regs *regs; + uint8_t irq; + uint16_t addr; + int pullup; + enum usb_device_state state; + struct fotg210_ep ep[1 + CFG_NUM_ENDPOINTS]; +}; + +static struct usb_endpoint_descriptor ep0_desc = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static inline int fifo_to_ep(struct fotg210_chip *chip, int id, int in) +{ + return (id < 0) ? 0 : ((id & 0x03) + 1); +} + +static inline int ep_to_fifo(struct fotg210_chip *chip, int id) +{ + return (id <= 0) ? -1 : ((id - 1) & 0x03); +} + +static inline int ep_reset(struct fotg210_chip *chip, uint8_t ep_addr) +{ + int ep = ep_addr & USB_ENDPOINT_NUMBER_MASK; + struct fotg210_regs *regs = chip->regs; + + if (ep_addr & USB_DIR_IN) { + /* reset endpoint */ + setbits_le32(®s->iep[ep - 1], IEP_RESET); + mdelay(1); + clrbits_le32(®s->iep[ep - 1], IEP_RESET); + /* clear endpoint stall */ + clrbits_le32(®s->iep[ep - 1], IEP_STALL); + } else { + /* reset endpoint */ + setbits_le32(®s->oep[ep - 1], OEP_RESET); + mdelay(1); + clrbits_le32(®s->oep[ep - 1], OEP_RESET); + /* clear endpoint stall */ + clrbits_le32(®s->oep[ep - 1], OEP_STALL); + } + + return 0; +} + +static int fotg210_reset(struct fotg210_chip *chip) +{ + struct fotg210_regs *regs = chip->regs; + uint32_t i; + + chip->state = USB_STATE_POWERED; + + /* chip enable */ + writel(DEVCTRL_EN, ®s->dev_ctrl); + + /* device address reset */ + chip->addr = 0; + writel(0, ®s->dev_addr); + + /* set idle counter to 7ms */ + writel(7, ®s->idle); + + /* disable all interrupts */ + writel(IMR_MASK, ®s->imr); + writel(GIMR_MASK, ®s->gimr); + writel(GIMR0_MASK, ®s->gimr0); + writel(GIMR1_MASK, ®s->gimr1); + writel(GIMR2_MASK, ®s->gimr2); + + /* clear interrupts */ + writel(ISR_MASK, ®s->isr); + writel(0, ®s->gisr); + writel(0, ®s->gisr0); + writel(0, ®s->gisr1); + writel(0, ®s->gisr2); + + /* chip reset */ + setbits_le32(®s->dev_ctrl, DEVCTRL_RESET); + mdelay(10); + if (readl(®s->dev_ctrl) & DEVCTRL_RESET) { + printf("fotg210: chip reset failed\n"); + return -1; + } + + /* CX FIFO reset */ + setbits_le32(®s->cxfifo, CXFIFO_CXFIFOCLR); + mdelay(10); + if (readl(®s->cxfifo) & CXFIFO_CXFIFOCLR) { + printf("fotg210: ep0 fifo reset failed\n"); + return -1; + } + + /* create static ep-fifo map (EP1 <-> FIFO0, EP2 <-> FIFO1 ...) */ + writel(EPMAP14_DEFAULT, ®s->epmap14); + writel(EPMAP58_DEFAULT, ®s->epmap58); + writel(FIFOMAP_DEFAULT, ®s->fifomap); + writel(0, ®s->fifocfg); + for (i = 0; i < 8; ++i) { + writel(CFG_EPX_MAX_PACKET_SIZE, ®s->iep[i]); + writel(CFG_EPX_MAX_PACKET_SIZE, ®s->oep[i]); + } + + /* FIFO reset */ + for (i = 0; i < 4; ++i) { + writel(FIFOCSR_RESET, ®s->fifocsr[i]); + mdelay(10); + if (readl(®s->fifocsr[i]) & FIFOCSR_RESET) { + printf("fotg210: fifo%d reset failed\n", i); + return -1; + } + } + + /* enable only device interrupt and triggered at level-high */ + writel(IMR_IRQLH | IMR_HOST | IMR_OTG, ®s->imr); + writel(ISR_MASK, ®s->isr); + /* disable EP0 IN/OUT interrupt */ + writel(GIMR0_CXOUT | GIMR0_CXIN, ®s->gimr0); + /* disable EPX IN+SPK+OUT interrupts */ + writel(GIMR1_MASK, ®s->gimr1); + /* disable wakeup+idle+dma+zlp interrupts */ + writel(GIMR2_WAKEUP | GIMR2_IDLE | GIMR2_DMAERR | GIMR2_DMAFIN + | GIMR2_ZLPRX | GIMR2_ZLPTX, ®s->gimr2); + /* enable all group interrupt */ + writel(0, ®s->gimr); + + /* suspend delay = 3 ms */ + writel(3, ®s->idle); + + /* turn-on device interrupts */ + setbits_le32(®s->dev_ctrl, DEVCTRL_GIRQ_EN); + + return 0; +} + +static inline int fotg210_cxwait(struct fotg210_chip *chip, uint32_t mask) +{ + struct fotg210_regs *regs = chip->regs; + int ret = -1; + ulong ts; + + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + if ((readl(®s->cxfifo) & mask) != mask) + continue; + ret = 0; + break; + } + + if (ret) + printf("fotg210: cx/ep0 timeout\n"); + + return ret; +} + +static int fotg210_dma(struct fotg210_ep *ep, struct fotg210_request *req) +{ + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + uint32_t tmp, ts; + uint8_t *buf = req->req.buf + req->req.actual; + uint32_t len = req->req.length - req->req.actual; + int fifo = ep_to_fifo(chip, ep->id); + int ret = -EBUSY; + + /* 1. init dma buffer */ + if (len > ep->maxpacket) + len = ep->maxpacket; + + /* 2. wait for dma ready (hardware) */ + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + if (!(readl(®s->dma_ctrl) & DMACTRL_START)) { + ret = 0; + break; + } + } + if (ret) { + printf("fotg210: dma busy\n"); + req->req.status = ret; + return ret; + } + + /* 3. DMA target setup */ + if (ep->desc->bEndpointAddress & USB_DIR_IN) + flush_dcache_range((ulong)buf, (ulong)buf + len); + else + invalidate_dcache_range((ulong)buf, (ulong)buf + len); + + writel(virt_to_phys(buf), ®s->dma_addr); + + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + if (ep->id == 0) { + /* Wait until cx/ep0 fifo empty */ + fotg210_cxwait(chip, CXFIFO_CXFIFOE); + writel(DMAFIFO_CX, ®s->dma_fifo); + } else { + /* Wait until epx fifo empty */ + fotg210_cxwait(chip, CXFIFO_FIFOE(fifo)); + writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo); + } + writel(DMACTRL_LEN(len) | DMACTRL_MEM2FIFO, ®s->dma_ctrl); + } else { + uint32_t blen; + + if (ep->id == 0) { + writel(DMAFIFO_CX, ®s->dma_fifo); + do { + blen = CXFIFO_BYTES(readl(®s->cxfifo)); + } while (blen < len); + } else { + writel(DMAFIFO_FIFO(fifo), ®s->dma_fifo); + blen = FIFOCSR_BYTES(readl(®s->fifocsr[fifo])); + } + len = (len < blen) ? len : blen; + writel(DMACTRL_LEN(len) | DMACTRL_FIFO2MEM, ®s->dma_ctrl); + } + + /* 4. DMA start */ + setbits_le32(®s->dma_ctrl, DMACTRL_START); + + /* 5. DMA wait */ + ret = -EBUSY; + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + tmp = readl(®s->gisr2); + /* DMA complete */ + if (tmp & GISR2_DMAFIN) { + ret = 0; + break; + } + /* DMA error */ + if (tmp & GISR2_DMAERR) { + printf("fotg210: dma error\n"); + break; + } + /* resume, suspend, reset */ + if (tmp & (GISR2_RESUME | GISR2_SUSPEND | GISR2_RESET)) { + printf("fotg210: dma reset by host\n"); + break; + } + } + + /* 7. DMA target reset */ + if (ret) + writel(DMACTRL_ABORT | DMACTRL_CLRFF, ®s->dma_ctrl); + + writel(0, ®s->gisr2); + writel(0, ®s->dma_fifo); + + req->req.status = ret; + if (!ret) + req->req.actual += len; + else + printf("fotg210: ep%d dma error(code=%d)\n", ep->id, ret); + + return len; +} + +/* + * result of setup packet + */ +#define CX_IDLE 0 +#define CX_FINISH 1 +#define CX_STALL 2 + +static void fotg210_setup(struct fotg210_chip *chip) +{ + int id, ret = CX_IDLE; + uint32_t tmp[2]; + struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)tmp; + struct fotg210_regs *regs = chip->regs; + + /* + * If this is the first Cx 8 byte command, + * we can now query USB mode (high/full speed; USB 2.0/USB 1.0) + */ + if (chip->state == USB_STATE_POWERED) { + chip->state = USB_STATE_DEFAULT; + if (readl(®s->otgcsr) & OTGCSR_DEV_B) { + /* Mini-B */ + if (readl(®s->dev_ctrl) & DEVCTRL_HS) { + puts("fotg210: HS\n"); + chip->gadget.speed = USB_SPEED_HIGH; + /* SOF mask timer = 1100 ticks */ + writel(SOFMTR_TMR(1100), ®s->sof_mtr); + } else { + puts("fotg210: FS\n"); + chip->gadget.speed = USB_SPEED_FULL; + /* SOF mask timer = 10000 ticks */ + writel(SOFMTR_TMR(10000), ®s->sof_mtr); + } + } else { + printf("fotg210: mini-A?\n"); + } + } + + /* switch data port to ep0 */ + writel(DMAFIFO_CX, ®s->dma_fifo); + /* fetch 8 bytes setup packet */ + tmp[0] = readl(®s->ep0_data); + tmp[1] = readl(®s->ep0_data); + /* release data port */ + writel(0, ®s->dma_fifo); + + if (req->bRequestType & USB_DIR_IN) + ep0_desc.bEndpointAddress = USB_DIR_IN; + else + ep0_desc.bEndpointAddress = USB_DIR_OUT; + + ret = CX_IDLE; + + if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (req->bRequest) { + case USB_REQ_SET_CONFIGURATION: + debug("fotg210: set_cfg(%d)\n", req->wValue & 0x00FF); + if (!(req->wValue & 0x00FF)) { + chip->state = USB_STATE_ADDRESS; + writel(chip->addr, ®s->dev_addr); + } else { + chip->state = USB_STATE_CONFIGURED; + writel(chip->addr | DEVADDR_CONF, + ®s->dev_addr); + } + ret = CX_IDLE; + break; + + case USB_REQ_SET_ADDRESS: + debug("fotg210: set_addr(0x%04X)\n", req->wValue); + chip->state = USB_STATE_ADDRESS; + chip->addr = req->wValue & DEVADDR_ADDR_MASK; + ret = CX_FINISH; + writel(chip->addr, ®s->dev_addr); + break; + + case USB_REQ_CLEAR_FEATURE: + debug("fotg210: clr_feature(%d, %d)\n", + req->bRequestType & 0x03, req->wValue); + switch (req->wValue) { + case 0: /* [Endpoint] halt */ + ep_reset(chip, req->wIndex); + ret = CX_FINISH; + break; + case 1: /* [Device] remote wake-up */ + case 2: /* [Device] test mode */ + default: + ret = CX_STALL; + break; + } + break; + + case USB_REQ_SET_FEATURE: + debug("fotg210: set_feature(%d, %d)\n", + req->wValue, req->wIndex & 0xf); + switch (req->wValue) { + case 0: /* Endpoint Halt */ + id = req->wIndex & 0xf; + setbits_le32(®s->iep[id - 1], IEP_STALL); + setbits_le32(®s->oep[id - 1], OEP_STALL); + ret = CX_FINISH; + break; + case 1: /* Remote Wakeup */ + case 2: /* Test Mode */ + default: + ret = CX_STALL; + break; + } + break; + + case USB_REQ_GET_STATUS: + debug("fotg210: get_status\n"); + ret = CX_STALL; + break; + + case USB_REQ_SET_DESCRIPTOR: + debug("fotg210: set_descriptor\n"); + ret = CX_STALL; + break; + + case USB_REQ_SYNCH_FRAME: + debug("fotg210: sync frame\n"); + ret = CX_STALL; + break; + } + } /* if ((req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) */ + + if (ret == CX_IDLE && chip->driver->setup) { + if (chip->driver->setup(&chip->gadget, req) < 0) + ret = CX_STALL; + else + ret = CX_FINISH; + } + + switch (ret) { + case CX_FINISH: + setbits_le32(®s->cxfifo, CXFIFO_CXFIN); + break; + + case CX_STALL: + setbits_le32(®s->cxfifo, CXFIFO_CXSTALL | CXFIFO_CXFIN); + printf("fotg210: cx_stall!\n"); + break; + + case CX_IDLE: + debug("fotg210: cx_idle?\n"); + default: + break; + } +} + +/* + * fifo - FIFO id + * zlp - zero length packet + */ +static void fotg210_recv(struct fotg210_chip *chip, int ep_id) +{ + struct fotg210_regs *regs = chip->regs; + struct fotg210_ep *ep = chip->ep + ep_id; + struct fotg210_request *req; + int len; + + if (ep->stopped || (ep->desc->bEndpointAddress & USB_DIR_IN)) { + printf("fotg210: ep%d recv, invalid!\n", ep->id); + return; + } + + if (list_empty(&ep->queue)) { + printf("fotg210: ep%d recv, drop!\n", ep->id); + return; + } + + req = list_first_entry(&ep->queue, struct fotg210_request, queue); + len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket || req->req.length <= req->req.actual) { + list_del_init(&req->queue); + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + } + + if (ep->id > 0 && list_empty(&ep->queue)) { + setbits_le32(®s->gimr1, + GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id))); + } +} + +/* + * USB Gadget Layer + */ +static int fotg210_ep_enable( + struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + int id = ep_to_fifo(chip, ep->id); + int in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + + if (!_ep || !desc + || desc->bDescriptorType != USB_DT_ENDPOINT + || le16_to_cpu(desc->wMaxPacketSize) == 0) { + printf("fotg210: bad ep or descriptor\n"); + return -EINVAL; + } + + ep->desc = desc; + ep->stopped = 0; + + if (in) + setbits_le32(®s->fifomap, FIFOMAP(id, FIFOMAP_IN)); + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + return -EINVAL; + + case USB_ENDPOINT_XFER_ISOC: + setbits_le32(®s->fifocfg, + FIFOCFG(id, FIFOCFG_EN | FIFOCFG_ISOC)); + break; + + case USB_ENDPOINT_XFER_BULK: + setbits_le32(®s->fifocfg, + FIFOCFG(id, FIFOCFG_EN | FIFOCFG_BULK)); + break; + + case USB_ENDPOINT_XFER_INT: + setbits_le32(®s->fifocfg, + FIFOCFG(id, FIFOCFG_EN | FIFOCFG_INTR)); + break; + } + + return 0; +} + +static int fotg210_ep_disable(struct usb_ep *_ep) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + int id = ep_to_fifo(chip, ep->id); + + ep->desc = NULL; + ep->stopped = 1; + + clrbits_le32(®s->fifocfg, FIFOCFG(id, FIFOCFG_CFG_MASK)); + clrbits_le32(®s->fifomap, FIFOMAP(id, FIFOMAP_DIR_MASK)); + + return 0; +} + +static struct usb_request *fotg210_ep_alloc_request( + struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct fotg210_request *req = malloc(sizeof(*req)); + + if (req) { + memset(req, 0, sizeof(*req)); + INIT_LIST_HEAD(&req->queue); + } + return &req->req; +} + +static void fotg210_ep_free_request( + struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_request *req; + + req = container_of(_req, struct fotg210_request, req); + free(req); +} + +static int fotg210_ep_queue( + struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + struct fotg210_request *req; + + req = container_of(_req, struct fotg210_request, req); + if (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue)) { + printf("fotg210: invalid request to ep%d\n", ep->id); + return -EINVAL; + } + + if (!chip || chip->state == USB_STATE_SUSPENDED) { + printf("fotg210: request while chip suspended\n"); + return -EINVAL; + } + + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (req->req.length == 0) { + req->req.status = 0; + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + return 0; + } + + if (ep->id == 0) { + do { + int len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket) + break; + if (ep->desc->bEndpointAddress & USB_DIR_IN) + udelay(100); + } while (req->req.length > req->req.actual); + } else { + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + do { + int len = fotg210_dma(ep, req); + if (len < ep->ep.maxpacket) + break; + } while (req->req.length > req->req.actual); + } else { + list_add_tail(&req->queue, &ep->queue); + clrbits_le32(®s->gimr1, + GIMR1_FIFO_RX(ep_to_fifo(chip, ep->id))); + } + } + + if (ep->id == 0 || (ep->desc->bEndpointAddress & USB_DIR_IN)) { + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + } + + return 0; +} + +static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_request *req; + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) + return -EINVAL; + + /* remove the request */ + list_del_init(&req->queue); + + /* update status & invoke complete callback */ + if (req->req.status == -EINPROGRESS) { + req->req.status = -ECONNRESET; + if (req->req.complete) + req->req.complete(_ep, &req->req); + } + + return 0; +} + +static int fotg210_ep_halt(struct usb_ep *_ep, int halt) +{ + struct fotg210_ep *ep = container_of(_ep, struct fotg210_ep, ep); + struct fotg210_chip *chip = ep->chip; + struct fotg210_regs *regs = chip->regs; + int ret = -1; + + debug("fotg210: ep%d halt=%d\n", ep->id, halt); + + /* Endpoint STALL */ + if (ep->id > 0 && ep->id <= CFG_NUM_ENDPOINTS) { + if (halt) { + /* wait until all ep fifo empty */ + fotg210_cxwait(chip, 0xf00); + /* stall */ + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + setbits_le32(®s->iep[ep->id - 1], + IEP_STALL); + } else { + setbits_le32(®s->oep[ep->id - 1], + OEP_STALL); + } + } else { + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + clrbits_le32(®s->iep[ep->id - 1], + IEP_STALL); + } else { + clrbits_le32(®s->oep[ep->id - 1], + OEP_STALL); + } + } + ret = 0; + } + + return ret; +} + +/* + * activate/deactivate link with host. + */ +static void pullup(struct fotg210_chip *chip, int is_on) +{ + struct fotg210_regs *regs = chip->regs; + + if (is_on) { + if (!chip->pullup) { + chip->state = USB_STATE_POWERED; + chip->pullup = 1; + /* enable the chip */ + setbits_le32(®s->dev_ctrl, DEVCTRL_EN); + /* clear unplug bit (BIT0) */ + clrbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG); + } + } else { + chip->state = USB_STATE_NOTATTACHED; + chip->pullup = 0; + chip->addr = 0; + writel(chip->addr, ®s->dev_addr); + /* set unplug bit (BIT0) */ + setbits_le32(®s->phy_tmsr, PHYTMSR_UNPLUG); + /* disable the chip */ + clrbits_le32(®s->dev_ctrl, DEVCTRL_EN); + } +} + +static int fotg210_pullup(struct usb_gadget *_gadget, int is_on) +{ + struct fotg210_chip *chip; + + chip = container_of(_gadget, struct fotg210_chip, gadget); + + debug("fotg210: pullup=%d\n", is_on); + + pullup(chip, is_on); + + return 0; +} + +static int fotg210_get_frame(struct usb_gadget *_gadget) +{ + struct fotg210_chip *chip; + struct fotg210_regs *regs; + + chip = container_of(_gadget, struct fotg210_chip, gadget); + regs = chip->regs; + + return SOFFNR_FNR(readl(®s->sof_fnr)); +} + +static struct usb_gadget_ops fotg210_gadget_ops = { + .get_frame = fotg210_get_frame, + .pullup = fotg210_pullup, +}; + +static struct usb_ep_ops fotg210_ep_ops = { + .enable = fotg210_ep_enable, + .disable = fotg210_ep_disable, + .queue = fotg210_ep_queue, + .dequeue = fotg210_ep_dequeue, + .set_halt = fotg210_ep_halt, + .alloc_request = fotg210_ep_alloc_request, + .free_request = fotg210_ep_free_request, +}; + +static struct fotg210_chip controller = { + .regs = (void __iomem *)CONFIG_FOTG210_BASE, + .gadget = { + .name = "fotg210_udc", + .ops = &fotg210_gadget_ops, + .ep0 = &controller.ep[0].ep, + .speed = USB_SPEED_UNKNOWN, + .is_dualspeed = 1, + .is_otg = 0, + .is_a_peripheral = 0, + .b_hnp_enable = 0, + .a_hnp_support = 0, + .a_alt_hnp_support = 0, + }, + .ep[0] = { + .id = 0, + .ep = { + .name = "ep0", + .ops = &fotg210_ep_ops, + }, + .desc = &ep0_desc, + .chip = &controller, + .maxpacket = CFG_EP0_MAX_PACKET_SIZE, + }, + .ep[1] = { + .id = 1, + .ep = { + .name = "ep1", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[2] = { + .id = 2, + .ep = { + .name = "ep2", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[3] = { + .id = 3, + .ep = { + .name = "ep3", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, + .ep[4] = { + .id = 4, + .ep = { + .name = "ep4", + .ops = &fotg210_ep_ops, + }, + .chip = &controller, + .maxpacket = CFG_EPX_MAX_PACKET_SIZE, + }, +}; + +int usb_gadget_handle_interrupts(void) +{ + struct fotg210_chip *chip = &controller; + struct fotg210_regs *regs = chip->regs; + uint32_t id, st, isr, gisr; + + isr = readl(®s->isr) & (~readl(®s->imr)); + gisr = readl(®s->gisr) & (~readl(®s->gimr)); + if (!(isr & ISR_DEV) || !gisr) + return 0; + + writel(ISR_DEV, ®s->isr); + + /* CX interrupts */ + if (gisr & GISR_GRP0) { + st = readl(®s->gisr0); + writel(0, ®s->gisr0); + + if (st & GISR0_CXERR) + printf("fotg210: cmd error\n"); + + if (st & GISR0_CXABORT) + printf("fotg210: cmd abort\n"); + + if (st & GISR0_CXSETUP) /* setup */ + fotg210_setup(chip); + else if (st & GISR0_CXEND) /* command finish */ + setbits_le32(®s->cxfifo, CXFIFO_CXFIN); + } + + /* FIFO interrupts */ + if (gisr & GISR_GRP1) { + st = readl(®s->gisr1); + for (id = 0; id < 4; ++id) { + if (st & GISR1_RX_FIFO(id)) + fotg210_recv(chip, fifo_to_ep(chip, id, 0)); + } + } + + /* Device Status Interrupts */ + if (gisr & GISR_GRP2) { + st = readl(®s->gisr2); + writel(0, ®s->gisr2); + + if (st & GISR2_RESET) + printf("fotg210: reset by host\n"); + else if (st & GISR2_SUSPEND) + printf("fotg210: suspend/removed\n"); + else if (st & GISR2_RESUME) + printf("fotg210: resume\n"); + + /* Errors */ + if (st & GISR2_ISOCERR) + printf("fotg210: iso error\n"); + if (st & GISR2_ISOCABT) + printf("fotg210: iso abort\n"); + if (st & GISR2_DMAERR) + printf("fotg210: dma error\n"); + } + + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int i, ret = 0; + struct fotg210_chip *chip = &controller; + + if (!driver || !driver->bind || !driver->setup) { + puts("fotg210: bad parameter.\n"); + return -EINVAL; + } + + INIT_LIST_HEAD(&chip->gadget.ep_list); + for (i = 0; i < CFG_NUM_ENDPOINTS + 1; ++i) { + struct fotg210_ep *ep = chip->ep + i; + + ep->ep.maxpacket = ep->maxpacket; + INIT_LIST_HEAD(&ep->queue); + + if (ep->id == 0) { + ep->stopped = 0; + } else { + ep->stopped = 1; + list_add_tail(&ep->ep.ep_list, &chip->gadget.ep_list); + } + } + + if (fotg210_reset(chip)) { + puts("fotg210: reset failed.\n"); + return -EINVAL; + } + + ret = driver->bind(&chip->gadget); + if (ret) { + debug("fotg210: driver->bind() returned %d\n", ret); + return ret; + } + chip->driver = driver; + + return ret; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct fotg210_chip *chip = &controller; + + driver->unbind(&chip->gadget); + chip->driver = NULL; + + pullup(chip, 0); + + return 0; +} diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e5701422fa3..f038747e63e 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -150,6 +150,12 @@ #define gadget_is_mv(g) 0 #endif +#ifdef CONFIG_USB_GADGET_FOTG210 +#define gadget_is_fotg210(g) (!strcmp("fotg210_udc", (g)->name)) +#else +#define gadget_is_fotg210(g) 0 +#endif + /* * CONFIG_USB_GADGET_SX2 * CONFIG_USB_GADGET_AU1X00 @@ -215,5 +221,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x20; else if (gadget_is_mv(gadget)) return 0x21; + else if (gadget_is_fotg210(gadget)) + return 0x22; return -ENOENT; } diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 9ce98f07684..085503dbe8a 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -314,7 +314,8 @@ static int pxa25x_ep_enable(struct usb_ep *_ep, if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { + || ep->fifo_size < + le16_to_cpu(get_unaligned(&desc->wMaxPacketSize))) { printf("%s, bad ep or descriptor\n", __func__); return -EINVAL; } @@ -329,9 +330,9 @@ static int pxa25x_ep_enable(struct usb_ep *_ep, /* hardware _could_ do smaller, but driver doesn't */ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu(desc->wMaxPacketSize) + && le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)) != BULK_FIFO_SIZE) - || !desc->wMaxPacketSize) { + || !get_unaligned(&desc->wMaxPacketSize)) { printf("%s, bad %s maxpacket\n", __func__, _ep->name); return -ERANGE; } @@ -345,7 +346,7 @@ static int pxa25x_ep_enable(struct usb_ep *_ep, ep->desc = desc; ep->stopped = 0; ep->pio_irqs = 0; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); /* flush fifo (mostly for OUT buffers) */ pxa25x_ep_fifo_flush(_ep); @@ -485,7 +486,7 @@ write_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) { unsigned max; - max = le16_to_cpu(ep->desc->wMaxPacketSize); + max = le16_to_cpu(get_unaligned(&ep->desc->wMaxPacketSize)); do { unsigned count; int is_last, is_short; @@ -766,7 +767,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) */ if (unlikely(ep->bmAttributes == USB_ENDPOINT_XFER_ISOC && req->req.length > - le16_to_cpu(ep->desc->wMaxPacketSize))) + le16_to_cpu(get_unaligned(&ep->desc->wMaxPacketSize)))) return -EMSGSIZE; debug_cond(NOISY, "%s queue req %p, len %d buf %p\n", diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 87a59704db7..98f2a104b7d 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -43,6 +43,7 @@ COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-mpc512x.o else COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o endif +COBJS-$(CONFIG_USB_EHCI_FARADAY) += ehci-faraday.o COBJS-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o diff --git a/drivers/usb/host/ehci-faraday.c b/drivers/usb/host/ehci-faraday.c new file mode 100644 index 00000000000..86add36ce11 --- /dev/null +++ b/drivers/usb/host/ehci-faraday.c @@ -0,0 +1,148 @@ +/* + * Faraday USB 2.0 EHCI Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dantesu@faraday-tech.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include <usb/fusbh200.h> +#include <usb/fotg210.h> + +#include "ehci.h" + +#ifndef CONFIG_USB_EHCI_BASE_LIST +#define CONFIG_USB_EHCI_BASE_LIST { CONFIG_USB_EHCI_BASE } +#endif + +union ehci_faraday_regs { + struct fusbh200_regs usb; + struct fotg210_regs otg; +}; + +static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs) +{ + return !readl(®s->usb.easstr); +} + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, struct ehci_hccr **ret_hccr, + struct ehci_hcor **ret_hcor) +{ + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + union ehci_faraday_regs *regs; + uint32_t base_list[] = CONFIG_USB_EHCI_BASE_LIST; + + if (index < 0 || index >= ARRAY_SIZE(base_list)) + return -1; + regs = (void __iomem *)base_list[index]; + hccr = (struct ehci_hccr *)®s->usb.hccr; + hcor = (struct ehci_hcor *)®s->usb.hcor; + + if (ehci_is_fotg2xx(regs)) { + /* A-device bus reset */ + /* ... Power off A-device */ + setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP); + /* ... Drop vbus and bus traffic */ + clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ); + mdelay(1); + /* ... Power on A-device */ + clrbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSDROP); + /* ... Drive vbus and bus traffic */ + setbits_le32(®s->otg.otgcsr, OTGCSR_A_BUSREQ); + mdelay(1); + /* Disable OTG & DEV interrupts, triggered at level-high */ + writel(IMR_IRQLH | IMR_OTG | IMR_DEV, ®s->otg.imr); + /* Clear all interrupt status */ + writel(ISR_HOST | ISR_OTG | ISR_DEV, ®s->otg.isr); + } else { + /* Interrupt=level-high */ + setbits_le32(®s->usb.bmcsr, BMCSR_IRQLH); + /* VBUS on */ + clrbits_le32(®s->usb.bmcsr, BMCSR_VBUS_OFF); + /* Disable all interrupts */ + writel(0x00, ®s->usb.bmier); + writel(0x1f, ®s->usb.bmisr); + } + + *ret_hccr = hccr; + *ret_hcor = hcor; + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ + return 0; +} + +/* + * This ehci_set_usbmode() overrides the weak function + * in "ehci-hcd.c". + */ +void ehci_set_usbmode(int index) +{ + /* nothing needs to be done */ +} + +/* + * This ehci_get_port_speed() overrides the weak function + * in "ehci-hcd.c". + */ +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ + int spd, ret = PORTSC_PSPD_HS; + union ehci_faraday_regs *regs = (void __iomem *)((ulong)hcor - 0x10); + + if (ehci_is_fotg2xx(regs)) + spd = OTGCSR_SPD(readl(®s->otg.otgcsr)); + else + spd = BMCSR_SPD(readl(®s->usb.bmcsr)); + + switch (spd) { + case 0: /* full speed */ + ret = PORTSC_PSPD_FS; + break; + case 1: /* low speed */ + ret = PORTSC_PSPD_LS; + break; + case 2: /* high speed */ + ret = PORTSC_PSPD_HS; + break; + default: + printf("ehci-faraday: invalid device speed\n"); + break; + } + + return ret; +} + +/* + * This ehci_get_portsc_register() overrides the weak function + * in "ehci-hcd.c". + */ +uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ + /* Faraday EHCI has one and only one portsc register */ + if (port) { + /* Printing the message would cause a scan failure! */ + debug("The request port(%d) is not configured\n", port); + return NULL; + } + + /* Faraday EHCI PORTSC register offset is 0x20 from hcor */ + return (uint32_t *)((uint8_t *)hcor + 0x20); +} diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e0f3e4b6c78..706cf0cb7db 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -589,10 +589,12 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token); } else { dev->act_len = 0; +#ifndef CONFIG_USB_EHCI_FARADAY debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", dev->devnum, ehci_readl(&ctrl->hcor->or_usbsts), ehci_readl(&ctrl->hcor->or_portsc[0]), ehci_readl(&ctrl->hcor->or_portsc[1])); +#endif } free(qtd); @@ -603,6 +605,17 @@ fail: return -1; } +__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port) +{ + if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + /* Printing the message would cause a scan failure! */ + debug("The request port(%u) is not configured\n", port); + return NULL; + } + + return (uint32_t *)&hcor->or_portsc[port]; +} + int ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) @@ -616,11 +629,6 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int port = le16_to_cpu(req->index) & 0xff; struct ehci_ctrl *ctrl = dev->controller; - if (port > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { - printf("The request port(%d) is not configured\n", port - 1); - return -1; - } - status_reg = (uint32_t *)&ctrl->hcor->or_portsc[port - 1]; srclen = 0; debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", @@ -631,6 +639,19 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, typeReq = req->request | req->requesttype << 8; switch (typeReq) { + case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8): + case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): + status_reg = ehci_get_portsc_register(ctrl->hcor, port - 1); + if (!status_reg) + return -1; + break; + default: + status_reg = NULL; + break; + } + + switch (typeReq) { case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch (le16_to_cpu(req->value) >> 8) { case USB_DT_DEVICE: @@ -809,21 +830,23 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, break; case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): reg = ehci_readl(status_reg); + reg &= ~EHCI_PS_CLEAR; switch (le16_to_cpu(req->value)) { case USB_PORT_FEAT_ENABLE: reg &= ~EHCI_PS_PE; break; case USB_PORT_FEAT_C_ENABLE: - reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE; + reg |= EHCI_PS_PE; break; case USB_PORT_FEAT_POWER: if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) - reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP); + reg &= ~EHCI_PS_PP; + break; case USB_PORT_FEAT_C_CONNECTION: - reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC; + reg |= EHCI_PS_CSC; break; case USB_PORT_FEAT_OVER_CURRENT: - reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; + reg |= EHCI_PS_OCC; break; case USB_PORT_FEAT_C_RESET: ctrl->portreset &= ~(1 << port); @@ -903,6 +926,9 @@ int usb_lowlevel_init(int index, void **controller) qh_list->qh_overlay.qt_token = cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); + flush_dcache_range((uint32_t)qh_list, + ALIGN_END_ADDR(struct QH, qh_list, 1)); + /* Set async. queue head pointer. */ ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); @@ -916,6 +942,9 @@ int usb_lowlevel_init(int index, void **controller) periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + flush_dcache_range((uint32_t)periodic, + ALIGN_END_ADDR(struct QH, periodic, 1)); + /* * Step 2: Setup frame-list: Every microframe, USB tries the same list. * In particular, device specifications on polling frequency @@ -933,6 +962,10 @@ int usb_lowlevel_init(int index, void **controller) | QH_LINK_TYPE_QH; } + flush_dcache_range((uint32_t)ehcic[index].periodic_list, + ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list, + 1024)); + /* Set periodic list base address */ ehci_writel(&ehcic[index].hcor->or_periodiclistbase, (uint32_t)ehcic[index].periodic_list); @@ -959,10 +992,13 @@ int usb_lowlevel_init(int index, void **controller) cmd |= CMD_RUN; ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd); +#ifndef CONFIG_USB_EHCI_FARADAY /* take control over the ports */ cmd = ehci_readl(&ehcic[index].hcor->or_configflag); cmd |= FLAG_CF; ehci_writel(&ehcic[index].hcor->or_configflag, cmd); +#endif + /* unblock posted write */ cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); mdelay(5); @@ -1144,6 +1180,16 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, *buf = buffer + i * elementsize; } + flush_dcache_range((uint32_t)buffer, + ALIGN_END_ADDR(char, buffer, + queuesize * elementsize)); + flush_dcache_range((uint32_t)result->first, + ALIGN_END_ADDR(struct QH, result->first, + queuesize)); + flush_dcache_range((uint32_t)result->tds, + ALIGN_END_ADDR(struct qTD, result->tds, + queuesize)); + if (disable_periodic(ctrl) < 0) { debug("FATAL: periodic should never fail, but did"); goto fail3; @@ -1154,6 +1200,11 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, result->last->qh_link = list->qh_link; list->qh_link = (uint32_t)result->first | QH_LINK_TYPE_QH; + flush_dcache_range((uint32_t)result->last, + ALIGN_END_ADDR(struct QH, result->last, 1)); + flush_dcache_range((uint32_t)list, + ALIGN_END_ADDR(struct QH, list, 1)); + if (enable_periodic(ctrl) < 0) { debug("FATAL: periodic should never fail, but did"); goto fail3; @@ -1184,6 +1235,8 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) return NULL; } /* still active */ + invalidate_dcache_range((uint32_t)cur, + ALIGN_END_ADDR(struct QH, cur, 1)); if (cur->qh_overlay.qt_token & 0x80) { debug("Exit poll_int_queue with no completed intr transfer. " "token is %x\n", cur->qh_overlay.qt_token); @@ -1290,6 +1343,9 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, return -EINVAL; } + invalidate_dcache_range((uint32_t)buffer, + ALIGN_END_ADDR(char, buffer, length)); + ret = destroy_int_queue(dev, queue); if (ret < 0) return ret; diff --git a/drivers/usb/musb/omap3.c b/drivers/usb/musb/omap3.c index c7876ed094b..a395ebcc67e 100644 --- a/drivers/usb/musb/omap3.c +++ b/drivers/usb/musb/omap3.c @@ -30,6 +30,7 @@ * MA 02111-1307 USA */ +#include <asm/omap_common.h> #include <twl4030.h> #include <twl6030.h> #include "omap3.h" @@ -135,7 +136,8 @@ int musb_platform_init(void) #endif #ifdef CONFIG_OMAP4430 - u32 *usbotghs_control = (u32 *)(CTRL_BASE + 0x33C); + u32 *usbotghs_control = + (u32 *)((*ctrl)->control_usbotghs_ctrl); *usbotghs_control = 0x15; #endif platform_needs_initialization = 0; diff --git a/drivers/video/exynos_fb.c b/drivers/video/exynos_fb.c index ed0823bf977..c3606d5b026 100644 --- a/drivers/video/exynos_fb.c +++ b/drivers/video/exynos_fb.c @@ -319,10 +319,10 @@ void lcd_ctrl_init(void *lcdbase) #ifdef CONFIG_OF_CONTROL if (exynos_fimd_parse_dt(gd->fdt_blob)) debug("Can't get proper panel info\n"); -#endif +#else /* initialize parameters which is specific to panel. */ init_panel_info(&panel_info); - +#endif panel_width = panel_info.vl_width; panel_height = panel_info.vl_height; diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 461ff6e860a..b1894193583 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -39,6 +39,11 @@ static GraphicDevice panel; * setenv videomode * video=ctfb:x:800,y:480,depth:18,mode:0,pclk:30066, * le:0,ri:256,up:0,lo:45,hs:1,vs:1,sync:100663296,vmode:0 + * + * Freescale mx23evk/mx28evk with a Seiko 4.3'' WVGA panel: + * setenv videomode + * video=ctfb:x:800,y:480,depth:24,mode:0,pclk:29851, + * le:89,ri:164,up:23,lo:10,hs:10,vs:10,sync:0,vmode:0 */ static void mxs_lcd_init(GraphicDevice *panel, diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d57578df6c2..b9bbbc63398 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -27,7 +27,7 @@ LIB := $(obj)libwatchdog.o COBJS-$(CONFIG_AT91SAM9_WATCHDOG) += at91sam9_wdt.o COBJS-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o -ifneq (,$(filter $(SOC), mx31 mx35 mx5 mx6)) +ifneq (,$(filter $(SOC), mx31 mx35 mx5 mx6 vf610)) COBJS-y += imx_watchdog.o endif COBJS-$(CONFIG_TNETV107X_WATCHDOG) += tnetv107x_wdt.o |