From beca04dd2446fc9ec46ca17163dadb7f82420e7c Mon Sep 17 00:00:00 2001 From: Prafulla Wadaskar Date: Thu, 24 Dec 2009 02:55:23 +0530 Subject: Kirkwood: Upgated licencing for files imported from linux source to GPLv2 or later These are few files directly imported from Linux kernel source. Those are not modifyed at all ar per strategy. These files contains source with GPLv2 only whereas u-boot expects GPLv2 or latter These files are updated for the same from prior permission from original writes Acked-by: Nicolas Pitre Signed-off-by: Prafulla Wadaskar --- drivers/gpio/kw_gpio.c | 20 +++++++++++++++++--- drivers/i2c/kirkwood_i2c.c | 23 ++++++++++++++++++----- 2 files changed, 35 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/kw_gpio.c b/drivers/gpio/kw_gpio.c index 1c2883479c..56383c2875 100644 --- a/drivers/gpio/kw_gpio.c +++ b/drivers/gpio/kw_gpio.c @@ -3,9 +3,23 @@ * * Marvell Orion SoC GPIO handling. * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA */ /* diff --git a/drivers/i2c/kirkwood_i2c.c b/drivers/i2c/kirkwood_i2c.c index 37b7d99bd2..a4409be115 100644 --- a/drivers/i2c/kirkwood_i2c.c +++ b/drivers/i2c/kirkwood_i2c.c @@ -5,16 +5,29 @@ * * Based on: * Author: Mark A. Greer + * 2005 (c) MontaVista, Software, Inc. * - * 2005 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA * * ported from Linux to u-boot * (C) Copyright 2009 * Heiko Schocher, DENX Software Engineering, hs@denx.de. - * */ #include #include -- cgit v1.2.1 From 2403f8f417b1b94701bb5949903d701f1f414a42 Mon Sep 17 00:00:00 2001 From: Vipin KUMAR Date: Fri, 15 Jan 2010 19:15:44 +0530 Subject: SPEAr : i2c driver support added for SPEAr SoCs SPEAr SoCs contain a synopsys i2c controller. This patch adds the driver for this IP. Signed-off-by: Vipin --- drivers/i2c/Makefile | 1 + drivers/i2c/spr_i2c.c | 331 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+) mode change 100644 => 100755 drivers/i2c/Makefile create mode 100755 drivers/i2c/spr_i2c.c (limited to 'drivers') diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile old mode 100644 new mode 100755 index b860e89f81..29bda85dbd --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -37,6 +37,7 @@ COBJS-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o +COBJS-$(CONFIG_SPEAR_I2C) += spr_i2c.o COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o COBJS := $(COBJS-y) diff --git a/drivers/i2c/spr_i2c.c b/drivers/i2c/spr_i2c.c new file mode 100755 index 0000000000..eabfe843f7 --- /dev/null +++ b/drivers/i2c/spr_i2c.c @@ -0,0 +1,331 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + +static struct i2c_regs *const i2c_regs_p = + (struct i2c_regs *)CONFIG_SYS_I2C_BASE; + +/* + * set_speed - Set the i2c speed mode (standard, high, fast) + * @i2c_spd: required i2c speed mode + * + * Set the i2c speed mode (standard, high, fast) + */ +static void set_speed(int i2c_spd) +{ + unsigned int cntl; + unsigned int hcnt, lcnt; + unsigned int high, low; + + cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK)); + + switch (i2c_spd) { + case IC_SPEED_MODE_MAX: + cntl |= IC_CON_SPD_HS; + high = MIN_HS_SCL_HIGHTIME; + low = MIN_HS_SCL_LOWTIME; + break; + + case IC_SPEED_MODE_STANDARD: + cntl |= IC_CON_SPD_SS; + high = MIN_SS_SCL_HIGHTIME; + low = MIN_SS_SCL_LOWTIME; + break; + + case IC_SPEED_MODE_FAST: + default: + cntl |= IC_CON_SPD_FS; + high = MIN_FS_SCL_HIGHTIME; + low = MIN_FS_SCL_LOWTIME; + break; + } + + writel(cntl, &i2c_regs_p->ic_con); + + hcnt = (IC_CLK * high) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt); + + lcnt = (IC_CLK * low) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt); +} + +/* + * i2c_set_bus_speed - Set the i2c speed + * @speed: required i2c speed + * + * Set the i2c speed. + */ +void i2c_set_bus_speed(int speed) +{ + if (speed >= I2C_MAX_SPEED) + set_speed(IC_SPEED_MODE_MAX); + else if (speed >= I2C_FAST_SPEED) + set_speed(IC_SPEED_MODE_FAST); + else + set_speed(IC_SPEED_MODE_STANDARD); +} + +/* + * i2c_get_bus_speed - Gets the i2c speed + * + * Gets the i2c speed. + */ +int i2c_get_bus_speed(void) +{ + u32 cntl; + + cntl = (readl(&i2c_regs_p->ic_con) & IC_CON_SPD_MSK); + + if (cntl == IC_CON_SPD_HS) + return I2C_MAX_SPEED; + else if (cntl == IC_CON_SPD_FS) + return I2C_FAST_SPEED; + else if (cntl == IC_CON_SPD_SS) + return I2C_STANDARD_SPEED; + + return 0; +} + +/* + * i2c_init - Init function + * @speed: required i2c speed + * @slaveadd: slave address for the spear device + * + * Initialization function. + */ +void i2c_init(int speed, int slaveadd) +{ + unsigned int enbl; + + /* Disable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl &= ~IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + + writel((IC_CON_SD | IC_CON_SPD_FS | IC_CON_MM), &i2c_regs_p->ic_con); + writel(IC_RX_TL, &i2c_regs_p->ic_rx_tl); + writel(IC_TX_TL, &i2c_regs_p->ic_tx_tl); + i2c_set_bus_speed(speed); + writel(IC_STOP_DET, &i2c_regs_p->ic_intr_mask); + writel(slaveadd, &i2c_regs_p->ic_sar); + + /* Enable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl |= IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); +} + +/* + * i2c_setaddress - Sets the target slave address + * @i2c_addr: target i2c address + * + * Sets the target slave address. + */ +static void i2c_setaddress(unsigned int i2c_addr) +{ + writel(i2c_addr, &i2c_regs_p->ic_tar); +} + +/* + * i2c_flush_rxfifo - Flushes the i2c RX FIFO + * + * Flushes the i2c RX FIFO + */ +static void i2c_flush_rxfifo(void) +{ + while (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) + readl(&i2c_regs_p->ic_cmd_data); +} + +/* + * i2c_wait_for_bb - Waits for bus busy + * + * Waits for bus busy + */ +static int i2c_wait_for_bb(void) +{ + unsigned long start_time_bb = get_timer(0); + + while ((readl(&i2c_regs_p->ic_status) & IC_STATUS_MA) || + !(readl(&i2c_regs_p->ic_status) & IC_STATUS_TFE)) { + + /* Evaluate timeout */ + if (get_timer(start_time_bb) > (unsigned long)(I2C_BYTE_TO_BB)) + return 1; + } + + return 0; +} + +/* check parameters for i2c_read and i2c_write */ +static int check_params(uint addr, int alen, uchar *buffer, int len) +{ + if (buffer == NULL) { + printf("Buffer is invalid\n"); + return 1; + } + + if (alen > 1) { + printf("addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > 256) { + printf("address out of range\n"); + return 1; + } + + return 0; +} + +static int i2c_xfer_init(uchar chip, uint addr) +{ + if (i2c_wait_for_bb()) { + printf("Timed out waiting for bus\n"); + return 1; + } + + i2c_setaddress(chip); + writel(addr, &i2c_regs_p->ic_cmd_data); + + return 0; +} + +static int i2c_xfer_finish(void) +{ + ulong start_stop_det = get_timer(0); + + while (1) { + if ((readl(&i2c_regs_p->ic_raw_intr_stat) & IC_STOP_DET)) { + readl(&i2c_regs_p->ic_clr_stop_det); + break; + } else if (get_timer(start_stop_det) > I2C_STOPDET_TO) { + break; + } + } + + if (i2c_wait_for_bb()) { + printf("Timed out waiting for bus\n"); + return 1; + } + + i2c_flush_rxfifo(); + + /* Wait for read/write operation to complete on actual memory */ + udelay(10000); + + return 0; +} + +/* + * i2c_read - Read from i2c memory + * @chip: target i2c address + * @addr: address to read from + * @alen: + * @buffer: buffer for read data + * @len: no of bytes to be read + * + * Read from i2c memory. + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + unsigned long start_time_rx; + + if (check_params(addr, alen, buffer, len)) + return 1; + + if (i2c_xfer_init(chip, addr)) + return 1; + + start_time_rx = get_timer(0); + while (len) { + writel(IC_CMD, &i2c_regs_p->ic_cmd_data); + + if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) { + *buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data); + len--; + start_time_rx = get_timer(0); + + } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { + printf("Timed out. i2c read Failed\n"); + return 1; + } + } + + return i2c_xfer_finish(); +} + +/* + * i2c_write - Write to i2c memory + * @chip: target i2c address + * @addr: address to read from + * @alen: + * @buffer: buffer for read data + * @len: no of bytes to be read + * + * Write to i2c memory. + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int nb = len; + unsigned long start_time_tx; + + if (check_params(addr, alen, buffer, len)) + return 1; + + if (i2c_xfer_init(chip, addr)) + return 1; + + start_time_tx = get_timer(0); + while (len) { + if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) { + writel(*buffer, &i2c_regs_p->ic_cmd_data); + buffer++; + len--; + start_time_tx = get_timer(0); + + } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { + printf("Timed out. i2c write Failed\n"); + return 1; + } + } + + return i2c_xfer_finish(); +} + +/* + * i2c_probe - Probe the i2c chip + */ +int i2c_probe(uchar chip) +{ + u32 tmp; + + /* + * Try to read the first location of the chip. + */ + return i2c_read(chip, 0, 1, (uchar *)&tmp, 1); +} -- cgit v1.2.1 From a6e34f76c51c8514f1b691fc60394f09ae4fb2ff Mon Sep 17 00:00:00 2001 From: Vipin KUMAR Date: Fri, 15 Jan 2010 19:15:45 +0530 Subject: SPEAr : smi driver support for SPEAr SoCs SPEAr SoCs contain a serial memory interface controller. This controller is used to interface with spi based memories. This patch adds the driver for this IP. Signed-off-by: Vipin --- drivers/mtd/Makefile | 1 + drivers/mtd/spr_smi.c | 523 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 524 insertions(+) mode change 100644 => 100755 drivers/mtd/Makefile create mode 100755 drivers/mtd/spr_smi.c (limited to 'drivers') diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile old mode 100644 new mode 100755 index 754d648a90..cbf6f156b4 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_FLASH_CFI_MTD) += cfi_mtd.o COBJS-$(CONFIG_HAS_DATAFLASH) += dataflash.o COBJS-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o COBJS-$(CONFIG_MW_EEPROM) += mw_eeprom.o +COBJS-$(CONFIG_SPEARSMI) += spr_smi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mtd/spr_smi.c b/drivers/mtd/spr_smi.c new file mode 100755 index 0000000000..9a70a199c8 --- /dev/null +++ b/drivers/mtd/spr_smi.c @@ -0,0 +1,523 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include + +#if !defined(CONFIG_SYS_NO_FLASH) + +static struct smi_regs *const smicntl = + (struct smi_regs * const)CONFIG_SYS_SMI_BASE; +static ulong bank_base[CONFIG_SYS_MAX_FLASH_BANKS] = + CONFIG_SYS_FLASH_ADDR_BASE; +flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; + +#define ST_M25Pxx_ID 0x00002020 + +static struct flash_dev flash_ids[] = { + {0x10, 0x10000, 2}, /* 64K Byte */ + {0x11, 0x20000, 4}, /* 128K Byte */ + {0x12, 0x40000, 4}, /* 256K Byte */ + {0x13, 0x80000, 8}, /* 512K Byte */ + {0x14, 0x100000, 16}, /* 1M Byte */ + {0x15, 0x200000, 32}, /* 2M Byte */ + {0x16, 0x400000, 64}, /* 4M Byte */ + {0x17, 0x800000, 128}, /* 8M Byte */ + {0x18, 0x1000000, 64}, /* 16M Byte */ + {0x00,} +}; + +/* + * smi_wait_xfer_finish - Wait until TFF is set in status register + * @timeout: timeout in milliseconds + * + * Wait until TFF is set in status register + */ +static void smi_wait_xfer_finish(int timeout) +{ + while (timeout--) { + if (readl(&smicntl->smi_sr) & TFF) + break; + udelay(1000); + } +} + +/* + * smi_read_id - Read flash id + * @info: flash_info structure pointer + * @banknum: bank number + * + * Read the flash id present at bank #banknum + */ +static unsigned int smi_read_id(flash_info_t *info, int banknum) +{ + unsigned int value; + + writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); + writel(READ_ID, &smicntl->smi_tr); + writel((banknum << BANKSEL_SHIFT) | SEND | TX_LEN_1 | RX_LEN_3, + &smicntl->smi_cr2); + smi_wait_xfer_finish(XFER_FINISH_TOUT); + + value = (readl(&smicntl->smi_rr) & 0x00FFFFFF); + + writel(readl(&smicntl->smi_sr) & ~TFF, &smicntl->smi_sr); + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + return value; +} + +/* + * flash_get_size - Detect the SMI flash by reading the ID. + * @base: Base address of the flash area bank #banknum + * @banknum: Bank number + * + * Detect the SMI flash by reading the ID. Initializes the flash_info structure + * with size, sector count etc. + */ +static ulong flash_get_size(ulong base, int banknum) +{ + flash_info_t *info = &flash_info[banknum]; + struct flash_dev *dev; + unsigned int value; + unsigned int density; + int i; + + value = smi_read_id(info, banknum); + density = (value >> 16) & 0xff; + + for (i = 0, dev = &flash_ids[0]; dev->density != 0x0; + i++, dev = &flash_ids[i]) { + if (dev->density == density) { + info->size = dev->size; + info->sector_count = dev->sector_count; + break; + } + } + + if (dev->density == 0x0) + return 0; + + info->flash_id = value & 0xffff; + info->start[0] = base; + + return info->size; +} + +/* + * smi_read_sr - Read status register of SMI + * @bank: bank number + * + * This routine will get the status register of the flash chip present at the + * given bank + */ +static unsigned int smi_read_sr(int bank) +{ + u32 ctrlreg1; + + /* store the CTRL REG1 state */ + ctrlreg1 = readl(&smicntl->smi_cr1); + + /* Program SMI in HW Mode */ + writel(readl(&smicntl->smi_cr1) & ~(SW_MODE | WB_MODE), + &smicntl->smi_cr1); + + /* Performing a RSR instruction in HW mode */ + writel((bank << BANKSEL_SHIFT) | RD_STATUS_REG, &smicntl->smi_cr2); + + smi_wait_xfer_finish(XFER_FINISH_TOUT); + + /* Restore the CTRL REG1 state */ + writel(ctrlreg1, &smicntl->smi_cr1); + + return readl(&smicntl->smi_sr); +} + +/* + * smi_wait_till_ready - Wait till last operation is over. + * @bank: bank number shifted. + * @timeout: timeout in milliseconds. + * + * This routine checks for WIP(write in progress)bit in Status register(SMSR-b0) + * The routine checks for #timeout loops, each at interval of 1 milli-second. + * If successful the routine returns 0. + */ +static int smi_wait_till_ready(int bank, int timeout) +{ + int count; + unsigned int sr; + + /* One chip guarantees max 5 msec wait here after page writes, + but potentially three seconds (!) after page erase. */ + for (count = 0; count < timeout; count++) { + + sr = smi_read_sr(bank); + if (sr < 0) + break; + else if (!(sr & WIP_BIT)) + return 0; + + /* Try again after 1m-sec */ + udelay(1000); + } + printf("SMI controller is still in wait, timeout=%d\n", timeout); + return -EIO; +} + +/* + * smi_write_enable - Enable the flash to do write operation + * @bank: bank number + * + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static int smi_write_enable(int bank) +{ + u32 ctrlreg1; + int timeout = WMODE_TOUT; + + /* Store the CTRL REG1 state */ + ctrlreg1 = readl(&smicntl->smi_cr1); + + /* Program SMI in H/W Mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + /* Give the Flash, Write Enable command */ + writel((bank << BANKSEL_SHIFT) | WE, &smicntl->smi_cr2); + + smi_wait_xfer_finish(XFER_FINISH_TOUT); + + /* Restore the CTRL REG1 state */ + writel(ctrlreg1, &smicntl->smi_cr1); + + while (timeout--) { + if (smi_read_sr(bank) & (1 << (bank + WM_SHIFT))) + break; + udelay(1000); + } + + if (timeout) + return 0; + + return -1; +} + +/* + * smi_init - SMI initialization routine + * + * SMI initialization routine. Sets SMI control register1. + */ +static void smi_init(void) +{ + /* Setting the fast mode values. SMI working at 166/4 = 41.5 MHz */ + writel(HOLD1 | FAST_MODE | BANK_EN | DSEL_TIME | PRESCAL4, + &smicntl->smi_cr1); +} + +/* + * smi_sector_erase - Erase flash sector + * @info: flash_info structure pointer + * @sector: sector number + * + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static int smi_sector_erase(flash_info_t *info, unsigned int sector) +{ + int bank; + unsigned int sect_add; + unsigned int instruction; + + switch (info->start[0]) { + case SMIBANK0_BASE: + bank = BANK0; + break; + case SMIBANK1_BASE: + bank = BANK1; + break; + case SMIBANK2_BASE: + bank = BANK2; + break; + case SMIBANK3_BASE: + bank = BANK3; + break; + default: + return -1; + } + + sect_add = sector * (info->size / info->sector_count); + instruction = ((sect_add >> 8) & 0x0000FF00) | SECTOR_ERASE; + + writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); + + if (info->flash_id == ST_M25Pxx_ID) { + /* Wait until finished previous write command. */ + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; + + /* Send write enable, before erase commands. */ + if (smi_write_enable(bank)) + return -EIO; + + /* Put SMI in SW mode */ + writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); + + /* Send Sector Erase command in SW Mode */ + writel(instruction, &smicntl->smi_tr); + writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, + &smicntl->smi_cr2); + smi_wait_xfer_finish(XFER_FINISH_TOUT); + + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; + + /* Put SMI in HW mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, + &smicntl->smi_cr1); + + return 0; + } else { + /* Put SMI in HW mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, + &smicntl->smi_cr1); + return -EINVAL; + } +} + +/* + * smi_write - Write to SMI flash + * @src_addr: source buffer + * @dst_addr: destination buffer + * @length: length to write in words + * @bank: bank base address + * + * Write to SMI flash + */ +static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, + unsigned int length, ulong bank_addr) +{ + int banknum; + unsigned int WM; + + switch (bank_addr) { + case SMIBANK0_BASE: + banknum = BANK0; + WM = WM0; + break; + case SMIBANK1_BASE: + banknum = BANK1; + WM = WM1; + break; + case SMIBANK2_BASE: + banknum = BANK2; + WM = WM2; + break; + case SMIBANK3_BASE: + banknum = BANK3; + WM = WM3; + break; + default: + return -1; + } + + if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + /* Set SMI in Hardware Mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + if (smi_write_enable(banknum)) + return -EIO; + + /* Perform the write command */ + while (length--) { + if (((ulong) (dst_addr) % SFLASH_PAGE_SIZE) == 0) { + if (smi_wait_till_ready(banknum, + CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + if (smi_write_enable(banknum)) + return -EIO; + } + + *dst_addr++ = *src_addr++; + + if ((readl(&smicntl->smi_sr) & (ERF1 | ERF2))) + return -EIO; + } + + if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + writel(readl(&smicntl->smi_sr) & ~(WCF), &smicntl->smi_sr); + + return 0; +} + +/* + * write_buff - Write to SMI flash + * @info: flash info structure + * @src: source buffer + * @dest_addr: destination buffer + * @length: length to write in words + * + * Write to SMI flash + */ +int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) +{ + return smi_write((unsigned int *)src, (unsigned int *)dest_addr, + (length + 3) / 4, info->start[0]); +} + +/* + * flash_init - SMI flash initialization + * + * SMI flash initialization + */ +unsigned long flash_init(void) +{ + unsigned long size = 0; + int i, j; + + smi_init(); + + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { + flash_info[i].flash_id = FLASH_UNKNOWN; + size += flash_info[i].size = flash_get_size(bank_base[i], i); + } + + for (j = 0; j < CONFIG_SYS_MAX_FLASH_BANKS; j++) { + for (i = 1; i < flash_info[j].sector_count; i++) + flash_info[j].start[i] = + flash_info[j].start[i - 1] + + flash_info->size / flash_info->sector_count; + + } + + return size; +} + +/* + * flash_print_info - Print SMI flash information + * + * Print SMI flash information + */ +void flash_print_info(flash_info_t *info) +{ + int i; + if (info->flash_id == FLASH_UNKNOWN) { + puts("missing or unknown FLASH type\n"); + return; + } + printf(" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + puts(" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { +#ifdef CONFIG_SYS_FLASH_EMPTY_INFO + int size; + int erased; + u32 *flash; + + /* + * Check if whole sector is erased + */ + size = (info->size) / (info->sector_count); + flash = (u32 *) info->start[i]; + size = size / sizeof(int); + + while ((size--) && (*flash++ == ~0)) + ; + + size++; + if (size) + erased = 0; + else + erased = 1; + + if ((i % 5) == 0) + printf("\n"); + + printf(" %08lX%s%s", + info->start[i], + erased ? " E" : " ", info->protect[i] ? "RO " : " "); +#else + if ((i % 5) == 0) + printf("\n "); + printf(" %08lX%s", + info->start[i], info->protect[i] ? " (RO) " : " "); +#endif + } + putc('\n'); + return; +} + +/* + * flash_erase - Erase SMI flash + * + * Erase SMI flash + */ +int flash_erase(flash_info_t *info, int s_first, int s_last) +{ + int rcode = 0; + int prot = 0; + flash_sect_t sect; + + if (info->flash_id != ST_M25Pxx_ID) { + puts("Can't erase unknown flash type - aborted\n"); + return 1; + } + + if ((s_first < 0) || (s_first > s_last)) { + puts("- no sectors to erase\n"); + return 1; + } + + for (sect = s_first; sect <= s_last; ++sect) { + if (info->protect[sect]) + prot++; + } + if (prot) { + printf("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + putc('\n'); + } + + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { + if (smi_sector_erase(info, sect)) + rcode = 1; + else + putc('.'); + } + } + puts(" done\n"); + return rcode; +} +#endif -- cgit v1.2.1 From 165fa406ad8c39c0c32a31476a8a9bda3db72851 Mon Sep 17 00:00:00 2001 From: Vipin KUMAR Date: Fri, 15 Jan 2010 19:15:46 +0530 Subject: SPEAr : nand driver support for SPEAr SoCs SPEAr SoCs contain an FSMC controller which can be used to interface with a range of memories eg. NAND, SRAM, NOR. Currently, this driver supports interfacing FSMC with NAND memories Signed-off-by: Vipin --- drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/spr_nand.c | 124 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100755 drivers/mtd/nand/spr_nand.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 02449eecb9..28f27da7a7 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -47,6 +47,7 @@ COBJS-$(CONFIG_NAND_NDFC) += ndfc.o COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o +COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o endif diff --git a/drivers/mtd/nand/spr_nand.c b/drivers/mtd/nand/spr_nand.c new file mode 100755 index 0000000000..097d0c60bf --- /dev/null +++ b/drivers/mtd/nand/spr_nand.c @@ -0,0 +1,124 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +static struct fsmc_regs *const fsmc_regs_p = + (struct fsmc_regs *)CONFIG_SPEAR_FSMCBASE; + +static struct nand_ecclayout spear_nand_ecclayout = { + .eccbytes = 24, + .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, + 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, + .oobfree = { + {.offset = 8, .length = 8}, + {.offset = 24, .length = 8}, + {.offset = 40, .length = 8}, + {.offset = 56, .length = 8}, + {.offset = 72, .length = 8}, + {.offset = 88, .length = 8}, + {.offset = 104, .length = 8}, + {.offset = 120, .length = 8} + } +}; + +static void spear_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) +{ + struct nand_chip *this = mtd->priv; + ulong IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W = (ulong)this->IO_ADDR_W; + + IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); + if (ctrl & NAND_CLE) + IO_ADDR_W |= CONFIG_SYS_NAND_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= CONFIG_SYS_NAND_ALE; + + if (ctrl & NAND_NCE) { + writel(readl(&fsmc_regs_p->genmemctrl_pc) | + FSMC_ENABLE, &fsmc_regs_p->genmemctrl_pc); + } else { + writel(readl(&fsmc_regs_p->genmemctrl_pc) & + ~FSMC_ENABLE, &fsmc_regs_p->genmemctrl_pc); + } + this->IO_ADDR_W = (void *)IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +static int spear_read_hwecc(struct mtd_info *mtd, + const u_char *data, u_char ecc[3]) +{ + u_int ecc_tmp; + + /* read the h/w ECC */ + ecc_tmp = readl(&fsmc_regs_p->genmemctrl_ecc); + + ecc[0] = (u_char) (ecc_tmp & 0xFF); + ecc[1] = (u_char) ((ecc_tmp & 0xFF00) >> 8); + ecc[2] = (u_char) ((ecc_tmp & 0xFF0000) >> 16); + + return 0; +} + +void spear_enable_hwecc(struct mtd_info *mtd, int mode) +{ + writel(readl(&fsmc_regs_p->genmemctrl_pc) & ~0x80, + &fsmc_regs_p->genmemctrl_pc); + writel(readl(&fsmc_regs_p->genmemctrl_pc) & ~FSMC_ECCEN, + &fsmc_regs_p->genmemctrl_pc); + writel(readl(&fsmc_regs_p->genmemctrl_pc) | FSMC_ECCEN, + &fsmc_regs_p->genmemctrl_pc); +} + +int spear_nand_init(struct nand_chip *nand) +{ + writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->genmemctrl_pc); + writel(readl(&fsmc_regs_p->genmemctrl_pc) | FSMC_TCLR_1 | FSMC_TAR_1, + &fsmc_regs_p->genmemctrl_pc); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->genmemctrl_comm); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->genmemctrl_attrib); + + nand->options = 0; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &spear_nand_ecclayout; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.calculate = spear_read_hwecc; + nand->ecc.hwctl = spear_enable_hwecc; + nand->ecc.correct = nand_correct_data; + nand->cmd_ctrl = spear_nand_hwcontrol; + return 0; +} -- cgit v1.2.1 From 62db1c0d79f1fd75961eec81edc8c0a1bc1f09a6 Mon Sep 17 00:00:00 2001 From: Vipin KUMAR Date: Fri, 15 Jan 2010 19:15:47 +0530 Subject: SPEAr : usbd driver support for SPEAr SoCs SPEAr SoCs contain a synopsys usb device controller. USB Device IP can work in 2 modes - DMA mode - Slave mode The driver adds support only for slave mode operation of usb device IP. This driver is used along with standard USBTTY driver to obtain a tty interface over USB on the host Signed-off-by: Vipin --- drivers/serial/usbtty.h | 2 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/spr_udc.c | 998 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1001 insertions(+) mode change 100644 => 100755 drivers/serial/usbtty.h mode change 100644 => 100755 drivers/usb/gadget/Makefile create mode 100755 drivers/usb/gadget/spr_udc.c (limited to 'drivers') diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h old mode 100644 new mode 100755 index 6b6c4a1628..a23169a29d --- a/drivers/serial/usbtty.h +++ b/drivers/serial/usbtty.h @@ -33,6 +33,8 @@ #include #elif defined(CONFIG_PXA27X) #include +#elif defined(CONFIG_SPEAR3XX) || defined(CONFIG_SPEAR600) +#include #endif #include diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile old mode 100644 new mode 100755 index 2a19b1e445..1d7362dc97 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -32,6 +32,7 @@ COBJS-$(CONFIG_OMAP1510) += omap1510_udc.o COBJS-$(CONFIG_OMAP1610) += omap1510_udc.o COBJS-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o COBJS-$(CONFIG_PXA27X) += pxa27x_udc.o +COBJS-$(CONFIG_SPEARUDC) += spr_udc.o endif COBJS := $(COBJS-y) diff --git a/drivers/usb/gadget/spr_udc.c b/drivers/usb/gadget/spr_udc.c new file mode 100755 index 0000000000..f2b06d68e2 --- /dev/null +++ b/drivers/usb/gadget/spr_udc.c @@ -0,0 +1,998 @@ +/* + * Based on drivers/usb/gadget/omap1510_udc.c + * TI OMAP1510 USB bus interface driver + * + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include + +#include +#include "ep0.h" +#include +#include +#include + +#define UDC_INIT_MDELAY 80 /* Device settle delay */ + +/* Some kind of debugging output... */ +#ifndef DEBUG_SPRUSBTTY +#define UDCDBG(str) +#define UDCDBGA(fmt, args...) +#else +#define UDCDBG(str) serial_printf(str "\n") +#define UDCDBGA(fmt, args...) serial_printf(fmt "\n", ##args) +#endif + +static struct urb *ep0_urb; +static struct usb_device_instance *udc_device; + +static struct plug_regs *const plug_regs_p = + (struct plug_regs * const)CONFIG_SYS_PLUG_BASE; +static struct udc_regs *const udc_regs_p = + (struct udc_regs * const)CONFIG_SYS_USBD_BASE; +static struct udc_endp_regs *const outep_regs_p = + &((struct udc_regs * const)CONFIG_SYS_USBD_BASE)->out_regs[0]; +static struct udc_endp_regs *const inep_regs_p = + &((struct udc_regs * const)CONFIG_SYS_USBD_BASE)->in_regs[0]; + +/* + * udc_state_transition - Write the next packet to TxFIFO. + * @initial: Initial state. + * @final: Final state. + * + * Helper function to implement device state changes. The device states and + * the events that transition between them are: + * + * STATE_ATTACHED + * || /\ + * \/ || + * DEVICE_HUB_CONFIGURED DEVICE_HUB_RESET + * || /\ + * \/ || + * STATE_POWERED + * || /\ + * \/ || + * DEVICE_RESET DEVICE_POWER_INTERRUPTION + * || /\ + * \/ || + * STATE_DEFAULT + * || /\ + * \/ || + * DEVICE_ADDRESS_ASSIGNED DEVICE_RESET + * || /\ + * \/ || + * STATE_ADDRESSED + * || /\ + * \/ || + * DEVICE_CONFIGURED DEVICE_DE_CONFIGURED + * || /\ + * \/ || + * STATE_CONFIGURED + * + * udc_state_transition transitions up (in the direction from STATE_ATTACHED + * to STATE_CONFIGURED) from the specified initial state to the specified final + * state, passing through each intermediate state on the way. If the initial + * state is at or above (i.e. nearer to STATE_CONFIGURED) the final state, then + * no state transitions will take place. + * + * udc_state_transition also transitions down (in the direction from + * STATE_CONFIGURED to STATE_ATTACHED) from the specified initial state to the + * specified final state, passing through each intermediate state on the way. + * If the initial state is at or below (i.e. nearer to STATE_ATTACHED) the final + * state, then no state transitions will take place. + * + * This function must only be called with interrupts disabled. + */ +static void udc_state_transition(usb_device_state_t initial, + usb_device_state_t final) +{ + if (initial < final) { + switch (initial) { + case STATE_ATTACHED: + usbd_device_event_irq(udc_device, + DEVICE_HUB_CONFIGURED, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq(udc_device, + DEVICE_ADDRESS_ASSIGNED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); + case STATE_CONFIGURED: + break; + default: + break; + } + } else if (initial > final) { + switch (initial) { + case STATE_CONFIGURED: + usbd_device_event_irq(udc_device, + DEVICE_DE_CONFIGURED, 0); + if (final == STATE_ADDRESSED) + break; + case STATE_ADDRESSED: + usbd_device_event_irq(udc_device, DEVICE_RESET, 0); + if (final == STATE_DEFAULT) + break; + case STATE_DEFAULT: + usbd_device_event_irq(udc_device, + DEVICE_POWER_INTERRUPTION, 0); + if (final == STATE_POWERED) + break; + case STATE_POWERED: + usbd_device_event_irq(udc_device, DEVICE_HUB_RESET, 0); + case STATE_ATTACHED: + break; + default: + break; + } + } +} + +/* Stall endpoint */ +static void udc_stall_ep(u32 ep_num) +{ + writel(readl(&inep_regs_p[ep_num].endp_cntl) | ENDP_CNTL_STALL, + &inep_regs_p[ep_num].endp_cntl); + + writel(readl(&outep_regs_p[ep_num].endp_cntl) | ENDP_CNTL_STALL, + &outep_regs_p[ep_num].endp_cntl); +} + +static void *get_fifo(int ep_num, int in) +{ + u32 *fifo_ptr = (u32 *)CONFIG_SYS_FIFO_BASE; + + switch (ep_num) { + case UDC_EP3: + fifo_ptr += readl(&inep_regs_p[1].endp_bsorfn); + /* break intentionally left out */ + + case UDC_EP1: + fifo_ptr += readl(&inep_regs_p[0].endp_bsorfn); + /* break intentionally left out */ + + case UDC_EP0: + default: + if (in) { + fifo_ptr += + readl(&outep_regs_p[2].endp_maxpacksize) >> 16; + /* break intentionally left out */ + } else { + break; + } + + case UDC_EP2: + fifo_ptr += readl(&outep_regs_p[0].endp_maxpacksize) >> 16; + /* break intentionally left out */ + } + + return (void *)fifo_ptr; +} + +static int usbgetpckfromfifo(int epNum, u8 *bufp, u32 len) +{ + u8 *fifo_ptr = (u8 *)get_fifo(epNum, 0); + u32 i, nw, nb; + u32 *wrdp; + u8 *bytp; + + if (readl(&udc_regs_p->dev_stat) & DEV_STAT_RXFIFO_EMPTY) + return -1; + + nw = len / sizeof(u32); + nb = len % sizeof(u32); + + wrdp = (u32 *)bufp; + for (i = 0; i < nw; i++) { + writel(readl(fifo_ptr), wrdp); + wrdp++; + } + + bytp = (u8 *)wrdp; + for (i = 0; i < nb; i++) { + writeb(readb(fifo_ptr), bytp); + fifo_ptr++; + bytp++; + } + readl(&outep_regs_p[epNum].write_done); + + return 0; +} + +static void usbputpcktofifo(int epNum, u8 *bufp, u32 len) +{ + u32 i, nw, nb; + u32 *wrdp; + u8 *bytp; + u8 *fifo_ptr = get_fifo(epNum, 1); + + nw = len / sizeof(int); + nb = len % sizeof(int); + wrdp = (u32 *)bufp; + for (i = 0; i < nw; i++) { + writel(*wrdp, fifo_ptr); + wrdp++; + } + + bytp = (u8 *)wrdp; + for (i = 0; i < nb; i++) { + writeb(*bytp, fifo_ptr); + fifo_ptr++; + bytp++; + } +} + +/* + * spear_write_noniso_tx_fifo - Write the next packet to TxFIFO. + * @endpoint: Endpoint pointer. + * + * If the endpoint has an active tx_urb, then the next packet of data from the + * URB is written to the tx FIFO. The total amount of data in the urb is given + * by urb->actual_length. The maximum amount of data that can be sent in any + * one packet is given by endpoint->tx_packetSize. The number of data bytes + * from this URB that have already been transmitted is given by endpoint->sent. + * endpoint->last is updated by this routine with the number of data bytes + * transmitted in this packet. + * + */ +static void spear_write_noniso_tx_fifo(struct usb_endpoint_instance + *endpoint) +{ + struct urb *urb = endpoint->tx_urb; + int align; + + if (urb) { + u32 last; + + UDCDBGA("urb->buffer %p, buffer_length %d, actual_length %d", + urb->buffer, urb->buffer_length, urb->actual_length); + + last = MIN(urb->actual_length - endpoint->sent, + endpoint->tx_packetSize); + + if (last) { + u8 *cp = urb->buffer + endpoint->sent; + + /* + * This ensures that USBD packet fifo is accessed + * - through word aligned pointer or + * - through non word aligned pointer but only + * with a max length to make the next packet + * word aligned + */ + + align = ((ulong)cp % sizeof(int)); + if (align) + last = MIN(last, sizeof(int) - align); + + UDCDBGA("endpoint->sent %d, tx_packetSize %d, last %d", + endpoint->sent, endpoint->tx_packetSize, last); + + usbputpcktofifo(endpoint->endpoint_address & + USB_ENDPOINT_NUMBER_MASK, cp, last); + } + endpoint->last = last; + } +} + +/* + * Handle SETUP USB interrupt. + * This function implements TRM Figure 14-14. + */ +static void spear_udc_setup(struct usb_endpoint_instance *endpoint) +{ + u8 *datap = (u8 *)&ep0_urb->device_request; + int ep_addr = endpoint->endpoint_address; + + UDCDBG("-> Entering device setup"); + usbgetpckfromfifo(ep_addr, datap, 8); + + /* Try to process setup packet */ + if (ep0_recv_setup(ep0_urb)) { + /* Not a setup packet, stall next EP0 transaction */ + udc_stall_ep(0); + UDCDBG("can't parse setup packet, still waiting for setup"); + return; + } + + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) + == USB_REQ_HOST2DEVICE) { + UDCDBG("control write on EP0"); + if (le16_to_cpu(ep0_urb->device_request.wLength)) { + /* Stall this request */ + UDCDBG("Stalling unsupported EP0 control write data " + "stage."); + udc_stall_ep(0); + } + } else { + + UDCDBG("control read on EP0"); + /* + * The ep0_recv_setup function has already placed our response + * packet data in ep0_urb->buffer and the packet length in + * ep0_urb->actual_length. + */ + endpoint->tx_urb = ep0_urb; + endpoint->sent = 0; + /* + * Write packet data to the FIFO. spear_write_noniso_tx_fifo + * will update endpoint->last with the number of bytes written + * to the FIFO. + */ + spear_write_noniso_tx_fifo(endpoint); + + writel(0x0, &inep_regs_p[ep_addr].write_done); + } + + udc_unset_nak(endpoint->endpoint_address); + + UDCDBG("<- Leaving device setup"); +} + +/* + * Handle endpoint 0 RX interrupt + */ +static void spear_udc_ep0_rx(struct usb_endpoint_instance *endpoint) +{ + u8 dummy[64]; + + UDCDBG("RX on EP0"); + + /* Check direction */ + if ((ep0_urb->device_request.bmRequestType + & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + /* + * This rx interrupt must be for a control write data + * stage packet. + * + * We don't support control write data stages. + * We should never end up here. + */ + + UDCDBG("Stalling unexpected EP0 control write " + "data stage packet"); + udc_stall_ep(0); + } else { + /* + * This rx interrupt must be for a control read status + * stage packet. + */ + UDCDBG("ACK on EP0 control read status stage packet"); + u32 len = (readl(&outep_regs_p[0].endp_status) >> 11) & 0xfff; + usbgetpckfromfifo(0, dummy, len); + } +} + +/* + * Handle endpoint 0 TX interrupt + */ +static void spear_udc_ep0_tx(struct usb_endpoint_instance *endpoint) +{ + struct usb_device_request *request = &ep0_urb->device_request; + int ep_addr; + + UDCDBG("TX on EP0"); + + /* Check direction */ + if ((request->bmRequestType & USB_REQ_DIRECTION_MASK) == + USB_REQ_HOST2DEVICE) { + /* + * This tx interrupt must be for a control write status + * stage packet. + */ + UDCDBG("ACK on EP0 control write status stage packet"); + } else { + /* + * This tx interrupt must be for a control read data + * stage packet. + */ + int wLength = le16_to_cpu(request->wLength); + + /* + * Update our count of bytes sent so far in this + * transfer. + */ + endpoint->sent += endpoint->last; + + /* + * We are finished with this transfer if we have sent + * all of the bytes in our tx urb (urb->actual_length) + * unless we need a zero-length terminating packet. We + * need a zero-length terminating packet if we returned + * fewer bytes than were requested (wLength) by the host, + * and the number of bytes we returned is an exact + * multiple of the packet size endpoint->tx_packetSize. + */ + if ((endpoint->sent == ep0_urb->actual_length) && + ((ep0_urb->actual_length == wLength) || + (endpoint->last != endpoint->tx_packetSize))) { + /* Done with control read data stage. */ + UDCDBG("control read data stage complete"); + } else { + /* + * We still have another packet of data to send + * in this control read data stage or else we + * need a zero-length terminating packet. + */ + UDCDBG("ACK control read data stage packet"); + spear_write_noniso_tx_fifo(endpoint); + + ep_addr = endpoint->endpoint_address; + writel(0x0, &inep_regs_p[ep_addr].write_done); + } + } +} + +static struct usb_endpoint_instance *spear_find_ep(int ep) +{ + int i; + + for (i = 0; i < udc_device->bus->max_endpoints; i++) { + if ((udc_device->bus->endpoint_array[i].endpoint_address & + USB_ENDPOINT_NUMBER_MASK) == ep) + return &udc_device->bus->endpoint_array[i]; + } + return NULL; +} + +/* + * Handle RX transaction on non-ISO endpoint. + * The ep argument is a physical endpoint number for a non-ISO IN endpoint + * in the range 1 to 15. + */ +static void spear_udc_epn_rx(int ep) +{ + int nbytes = 0; + struct urb *urb; + struct usb_endpoint_instance *endpoint = spear_find_ep(ep); + + if (endpoint) { + urb = endpoint->rcv_urb; + + if (urb) { + u8 *cp = urb->buffer + urb->actual_length; + + nbytes = (readl(&outep_regs_p[ep].endp_status) >> 11) & + 0xfff; + usbgetpckfromfifo(ep, cp, nbytes); + usbd_rcv_complete(endpoint, nbytes, 0); + } + } +} + +/* + * Handle TX transaction on non-ISO endpoint. + * The ep argument is a physical endpoint number for a non-ISO IN endpoint + * in the range 16 to 30. + */ +static void spear_udc_epn_tx(int ep) +{ + struct usb_endpoint_instance *endpoint = spear_find_ep(ep); + + /* + * We need to transmit a terminating zero-length packet now if + * we have sent all of the data in this URB and the transfer + * size was an exact multiple of the packet size. + */ + if (endpoint && endpoint->tx_urb && endpoint->tx_urb->actual_length) { + if (endpoint->last == endpoint->tx_packetSize) { + /* handle zero length packet here */ + writel(0x0, &inep_regs_p[ep].write_done); + } + /* retire the data that was just sent */ + usbd_tx_complete(endpoint); + /* + * Check to see if we have more data ready to transmit + * now. + */ + if (endpoint->tx_urb && endpoint->tx_urb->actual_length) { + /* write data to FIFO */ + spear_write_noniso_tx_fifo(endpoint); + writel(0x0, &inep_regs_p[ep].write_done); + + } else if (endpoint->tx_urb + && (endpoint->tx_urb->actual_length == 0)) { + /* udc_set_nak(ep); */ + } + } +} + +/* + * Start of public functions. + */ + +/* Called to start packet transmission. */ +int udc_endpoint_write(struct usb_endpoint_instance *endpoint) +{ + udc_unset_nak(endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK); + return 0; +} + +/* Start to initialize h/w stuff */ +int udc_init(void) +{ + int i; + u32 plug_st; + + udc_device = NULL; + + UDCDBG("starting"); + + readl(&plug_regs_p->plug_pending); + + udc_disconnect(); + + for (i = 0; i < UDC_INIT_MDELAY; i++) + udelay(1000); + + plug_st = readl(&plug_regs_p->plug_state); + writel(plug_st | PLUG_STATUS_EN, &plug_regs_p->plug_state); + + writel(~0x0, &udc_regs_p->endp_int); + writel(~0x0, &udc_regs_p->dev_int_mask); + writel(~0x0, &udc_regs_p->endp_int_mask); + + writel(DEV_CONF_FS_SPEED | DEV_CONF_REMWAKEUP | DEV_CONF_SELFPOW | + /* Dev_Conf_SYNCFRAME | */ + DEV_CONF_PHYINT_16, &udc_regs_p->dev_conf); + + writel(0x0, &udc_regs_p->dev_cntl); + + /* Clear all interrupts pending */ + writel(DEV_INT_MSK, &udc_regs_p->dev_int); + + return 0; +} + +/* + * udc_setup_ep - setup endpoint + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep(struct usb_device_instance *device, + u32 ep, struct usb_endpoint_instance *endpoint) +{ + UDCDBGA("setting up endpoint addr %x", endpoint->endpoint_address); + int ep_addr; + int ep_num, ep_type; + int packet_size; + int buffer_size; + int attributes; + char *tt; + u32 endp_intmask; + + tt = getenv("usbtty"); + if (!tt) + tt = "generic"; + + ep_addr = endpoint->endpoint_address; + ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK; + + if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* IN endpoint */ + packet_size = endpoint->tx_packetSize; + buffer_size = packet_size * 2; + attributes = endpoint->tx_attributes; + } else { + /* OUT endpoint */ + packet_size = endpoint->rcv_packetSize; + buffer_size = packet_size * 2; + attributes = endpoint->rcv_attributes; + } + + switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + ep_type = ENDP_EPTYPE_CNTL; + break; + case USB_ENDPOINT_XFER_BULK: + default: + ep_type = ENDP_EPTYPE_BULK; + break; + case USB_ENDPOINT_XFER_INT: + ep_type = ENDP_EPTYPE_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + ep_type = ENDP_EPTYPE_ISO; + break; + } + + struct udc_endp_regs *out_p = &outep_regs_p[ep_num]; + struct udc_endp_regs *in_p = &inep_regs_p[ep_num]; + + if (!ep_addr) { + /* Setup endpoint 0 */ + buffer_size = packet_size; + + writel(readl(&in_p->endp_cntl) | ENDP_CNTL_CNAK, + &in_p->endp_cntl); + + writel(readl(&out_p->endp_cntl) | ENDP_CNTL_CNAK, + &out_p->endp_cntl); + + writel(ENDP_CNTL_CONTROL | ENDP_CNTL_FLUSH, &in_p->endp_cntl); + + writel(buffer_size / sizeof(int), &in_p->endp_bsorfn); + + writel(packet_size, &in_p->endp_maxpacksize); + + writel(ENDP_CNTL_CONTROL | ENDP_CNTL_RRDY, &out_p->endp_cntl); + + writel(packet_size | ((buffer_size / sizeof(int)) << 16), + &out_p->endp_maxpacksize); + + writel((packet_size << 19) | ENDP_EPTYPE_CNTL, + &udc_regs_p->udc_endp_reg[ep_num]); + + } else if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { + /* Setup the IN endpoint */ + writel(0x0, &in_p->endp_status); + writel((ep_type << 4) | ENDP_CNTL_RRDY, &in_p->endp_cntl); + writel(buffer_size / sizeof(int), &in_p->endp_bsorfn); + writel(packet_size, &in_p->endp_maxpacksize); + + if (!strcmp(tt, "cdc_acm")) { + if (ep_type == ENDP_EPTYPE_INT) { + /* Conf no. 1 Interface no. 0 */ + writel((packet_size << 19) | + ENDP_EPDIR_IN | (1 << 7) | + (0 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } else { + /* Conf no. 1 Interface no. 1 */ + writel((packet_size << 19) | + ENDP_EPDIR_IN | (1 << 7) | + (1 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } + } else { + /* Conf no. 1 Interface no. 0 */ + writel((packet_size << 19) | + ENDP_EPDIR_IN | (1 << 7) | + (0 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } + + } else { + /* Setup the OUT endpoint */ + writel(0x0, &out_p->endp_status); + writel((ep_type << 4) | ENDP_CNTL_RRDY, &out_p->endp_cntl); + writel(packet_size | ((buffer_size / sizeof(int)) << 16), + &out_p->endp_maxpacksize); + + if (!strcmp(tt, "cdc_acm")) { + writel((packet_size << 19) | + ENDP_EPDIR_OUT | (1 << 7) | + (1 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } else { + writel((packet_size << 19) | + ENDP_EPDIR_OUT | (1 << 7) | + (0 << 11) | (ep_type << 5) | ep_num, + &udc_regs_p->udc_endp_reg[ep_num]); + } + + } + + endp_intmask = readl(&udc_regs_p->endp_int_mask); + endp_intmask &= ~((1 << ep_num) | 0x10000 << ep_num); + writel(endp_intmask, &udc_regs_p->endp_int_mask); +} + +/* Turn on the USB connection by enabling the pullup resistor */ +void udc_connect(void) +{ + u32 plug_st; + + plug_st = readl(&plug_regs_p->plug_state); + plug_st &= ~(PLUG_STATUS_PHY_RESET | PLUG_STATUS_PHY_MODE); + writel(plug_st, &plug_regs_p->plug_state); +} + +/* Turn off the USB connection by disabling the pullup resistor */ +void udc_disconnect(void) +{ + u32 plug_st; + + plug_st = readl(&plug_regs_p->plug_state); + plug_st |= (PLUG_STATUS_PHY_RESET | PLUG_STATUS_PHY_MODE); + writel(plug_st, &plug_regs_p->plug_state); +} + +/* Switch on the UDC */ +void udc_enable(struct usb_device_instance *device) +{ + UDCDBGA("enable device %p, status %d", device, device->status); + + /* Save the device structure pointer */ + udc_device = device; + + /* Setup ep0 urb */ + if (!ep0_urb) { + ep0_urb = + usbd_alloc_urb(udc_device, udc_device->bus->endpoint_array); + } else { + serial_printf("udc_enable: ep0_urb already allocated %p\n", + ep0_urb); + } + + writel(DEV_INT_SOF, &udc_regs_p->dev_int_mask); +} + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events(struct usb_device_instance *device) +{ + /* The DEVICE_INIT event puts the USB device in the state STATE_INIT. */ + usbd_device_event_irq(device, DEVICE_INIT, 0); + + /* + * The DEVICE_CREATE event puts the USB device in the state + * STATE_ATTACHED. + */ + usbd_device_event_irq(device, DEVICE_CREATE, 0); + + /* + * Some USB controller driver implementations signal + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events here. + * DEVICE_HUB_CONFIGURED causes a transition to the state STATE_POWERED, + * and DEVICE_RESET causes a transition to the state STATE_DEFAULT. + * The SPEAr USB client controller has the capability to detect when the + * USB cable is connected to a powered USB bus, so we will defer the + * DEVICE_HUB_CONFIGURED and DEVICE_RESET events until later. + */ + + udc_enable(device); +} + +/* + * Plug detection interrupt handling + */ +void spear_udc_plug_irq(void) +{ + if (readl(&plug_regs_p->plug_state) & PLUG_STATUS_ATTACHED) { + /* + * USB cable attached + * Turn off PHY reset bit (PLUG detect). + * Switch PHY opmode to normal operation (PLUG detect). + */ + udc_connect(); + writel(DEV_INT_SOF, &udc_regs_p->dev_int_mask); + + UDCDBG("device attached and powered"); + udc_state_transition(udc_device->device_state, STATE_POWERED); + } else { + /* + * USB cable detached + * Reset the PHY and switch the mode. + */ + udc_disconnect(); + writel(~0x0, &udc_regs_p->dev_int_mask); + + UDCDBG("device detached or unpowered"); + udc_state_transition(udc_device->device_state, STATE_ATTACHED); + } +} + +/* + * Device interrupt handling + */ +void spear_udc_dev_irq(void) +{ + if (readl(&udc_regs_p->dev_int) & DEV_INT_USBRESET) { + writel(~0x0, &udc_regs_p->endp_int_mask); + + udc_connect(); + + writel(readl(&inep_regs_p[0].endp_cntl) | ENDP_CNTL_FLUSH, + &inep_regs_p[0].endp_cntl); + + writel(DEV_INT_USBRESET, &udc_regs_p->dev_int); + + UDCDBG("device reset in progess"); + udc_state_transition(udc_device->device_state, STATE_DEFAULT); + } + + /* Device Enumeration completed */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_ENUM) { + writel(DEV_INT_ENUM, &udc_regs_p->dev_int); + + /* Endpoint interrupt enabled for Ctrl IN & Ctrl OUT */ + writel(readl(&udc_regs_p->endp_int_mask) & ~0x10001, + &udc_regs_p->endp_int_mask); + + UDCDBG("default -> addressed"); + udc_state_transition(udc_device->device_state, STATE_ADDRESSED); + } + + /* The USB will be in SUSPEND in 3 ms */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_INACTIVE) { + writel(DEV_INT_INACTIVE, &udc_regs_p->dev_int); + + UDCDBG("entering inactive state"); + /* usbd_device_event_irq(udc_device, DEVICE_BUS_INACTIVE, 0); */ + } + + /* SetConfiguration command received */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_SETCFG) { + writel(DEV_INT_SETCFG, &udc_regs_p->dev_int); + + UDCDBG("entering configured state"); + udc_state_transition(udc_device->device_state, + STATE_CONFIGURED); + } + + /* SetInterface command received */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_SETINTF) + writel(DEV_INT_SETINTF, &udc_regs_p->dev_int); + + /* USB Suspend detected on cable */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_SUSPUSB) { + writel(DEV_INT_SUSPUSB, &udc_regs_p->dev_int); + + UDCDBG("entering suspended state"); + usbd_device_event_irq(udc_device, DEVICE_BUS_INACTIVE, 0); + } + + /* USB Start-Of-Frame detected on cable */ + if (readl(&udc_regs_p->dev_int) & DEV_INT_SOF) + writel(DEV_INT_SOF, &udc_regs_p->dev_int); +} + +/* + * Endpoint interrupt handling + */ +void spear_udc_endpoint_irq(void) +{ + while (readl(&udc_regs_p->endp_int) & ENDP0_INT_CTRLOUT) { + + writel(ENDP0_INT_CTRLOUT, &udc_regs_p->endp_int); + + if ((readl(&outep_regs_p[0].endp_status) & ENDP_STATUS_OUTMSK) + == ENDP_STATUS_OUT_SETUP) { + spear_udc_setup(udc_device->bus->endpoint_array + 0); + writel(ENDP_STATUS_OUT_SETUP, + &outep_regs_p[0].endp_status); + + } else if ((readl(&outep_regs_p[0].endp_status) & + ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_DATA) { + spear_udc_ep0_rx(udc_device->bus->endpoint_array + 0); + writel(ENDP_STATUS_OUT_DATA, + &outep_regs_p[0].endp_status); + + } else if ((readl(&outep_regs_p[0].endp_status) & + ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_NONE) { + /* NONE received */ + } + + writel(0x0, &outep_regs_p[0].endp_status); + } + + if (readl(&udc_regs_p->endp_int) & ENDP0_INT_CTRLIN) { + spear_udc_ep0_tx(udc_device->bus->endpoint_array + 0); + + writel(ENDP_STATUS_IN, &inep_regs_p[0].endp_status); + writel(ENDP0_INT_CTRLIN, &udc_regs_p->endp_int); + } + + while (readl(&udc_regs_p->endp_int) & ENDP_INT_NONISOOUT_MSK) { + u32 epnum = 0; + u32 ep_int = readl(&udc_regs_p->endp_int) & + ENDP_INT_NONISOOUT_MSK; + + ep_int >>= 16; + while (0x0 == (ep_int & 0x1)) { + ep_int >>= 1; + epnum++; + } + + writel((1 << 16) << epnum, &udc_regs_p->endp_int); + + if ((readl(&outep_regs_p[epnum].endp_status) & + ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_DATA) { + + spear_udc_epn_rx(epnum); + writel(ENDP_STATUS_OUT_DATA, + &outep_regs_p[epnum].endp_status); + } else if ((readl(&outep_regs_p[epnum].endp_status) & + ENDP_STATUS_OUTMSK) == ENDP_STATUS_OUT_NONE) { + writel(0x0, &outep_regs_p[epnum].endp_status); + } + } + + if (readl(&udc_regs_p->endp_int) & ENDP_INT_NONISOIN_MSK) { + u32 epnum = 0; + u32 ep_int = readl(&udc_regs_p->endp_int) & + ENDP_INT_NONISOIN_MSK; + + while (0x0 == (ep_int & 0x1)) { + ep_int >>= 1; + epnum++; + } + + if (readl(&inep_regs_p[epnum].endp_status) & ENDP_STATUS_IN) { + writel(ENDP_STATUS_IN, + &outep_regs_p[epnum].endp_status); + spear_udc_epn_tx(epnum); + + writel(ENDP_STATUS_IN, + &outep_regs_p[epnum].endp_status); + } + + writel((1 << epnum), &udc_regs_p->endp_int); + } +} + +/* + * UDC interrupts + */ +void udc_irq(void) +{ + /* + * Loop while we have interrupts. + * If we don't do this, the input chain + * polling delay is likely to miss + * host requests. + */ + while (readl(&plug_regs_p->plug_pending)) + spear_udc_plug_irq(); + + while (readl(&udc_regs_p->dev_int)) + spear_udc_dev_irq(); + + if (readl(&udc_regs_p->endp_int)) + spear_udc_endpoint_irq(); +} + +/* Flow control */ +void udc_set_nak(int epid) +{ + writel(readl(&inep_regs_p[epid].endp_cntl) | ENDP_CNTL_SNAK, + &inep_regs_p[epid].endp_cntl); + + writel(readl(&outep_regs_p[epid].endp_cntl) | ENDP_CNTL_SNAK, + &outep_regs_p[epid].endp_cntl); +} + +void udc_unset_nak(int epid) +{ + u32 val; + + val = readl(&inep_regs_p[epid].endp_cntl); + val &= ~ENDP_CNTL_SNAK; + val |= ENDP_CNTL_CNAK; + writel(val, &inep_regs_p[epid].endp_cntl); + + val = readl(&outep_regs_p[epid].endp_cntl); + val &= ~ENDP_CNTL_SNAK; + val |= ENDP_CNTL_CNAK; + writel(val, &outep_regs_p[epid].endp_cntl); +} -- cgit v1.2.1