diff options
Diffstat (limited to 'drivers')
27 files changed, 1341 insertions, 206 deletions
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 51b10263b8..a93810934a 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -131,6 +131,10 @@ int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num) { int ret = 0; + ret = dfu_write_buffer_drain(dfu); + if (ret) + return ret; + if (dfu->flush_medium) ret = dfu->flush_medium(dfu); diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 5e10ea7e66..63cc876612 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -18,11 +18,29 @@ static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; static long dfu_file_buf_len; +static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part) +{ + int ret; + + if (part == mmc->part_num) + return 0; + + ret = mmc_switch_part(dfu->dev_num, part); + if (ret) { + error("Cannot switch to partition %d\n", part); + return ret; + } + mmc->part_num = part; + + return 0; +} + static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, u64 offset, void *buf, long *len) { struct mmc *mmc = find_mmc_device(dfu->dev_num); u32 blk_start, blk_count, n = 0; + int ret, part_num_bkp = 0; /* * We must ensure that we work in lba_blk_size chunks, so ALIGN @@ -39,6 +57,13 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, return -EINVAL; } + if (dfu->data.mmc.hw_partition >= 0) { + part_num_bkp = mmc->part_num; + ret = mmc_access_part(dfu, mmc, dfu->data.mmc.hw_partition); + if (ret) + return ret; + } + debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__, op == DFU_OP_READ ? "MMC READ" : "MMC WRITE", dfu->dev_num, blk_start, blk_count, buf); @@ -57,9 +82,17 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, if (n != blk_count) { error("MMC operation failed"); + if (dfu->data.mmc.hw_partition >= 0) + mmc_access_part(dfu, mmc, part_num_bkp); return -EIO; } + if (dfu->data.mmc.hw_partition >= 0) { + ret = mmc_access_part(dfu, mmc, part_num_bkp); + if (ret) + return ret; + } + return 0; } @@ -194,6 +227,8 @@ int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, * 2nd and 3rd: * lba_start and lba_size, for raw write * mmc_dev and mmc_part, for filesystems and part + * 4th (optional): + * mmcpart <num> (access to HW eMMC partitions) */ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) { @@ -233,11 +268,22 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) return -ENODEV; } + dfu->data.mmc.hw_partition = -EINVAL; if (!strcmp(entity_type, "raw")) { dfu->layout = DFU_RAW_ADDR; dfu->data.mmc.lba_start = second_arg; dfu->data.mmc.lba_size = third_arg; dfu->data.mmc.lba_blk_size = mmc->read_bl_len; + + /* + * Check for an extra entry at dfu_alt_info env variable + * specifying the mmc HW defined partition number + */ + if (s) + if (!strcmp(strsep(&s, " "), "mmcpart")) + dfu->data.mmc.hw_partition = + simple_strtoul(s, NULL, 0); + } else if (!strcmp(entity_type, "part")) { disk_partition_t partinfo; block_dev_desc_t *blk_dev = &mmc->block_dev; diff --git a/drivers/dfu/dfu_nand.c b/drivers/dfu/dfu_nand.c index 2d07097e85..ccdbef6b75 100644 --- a/drivers/dfu/dfu_nand.c +++ b/drivers/dfu/dfu_nand.c @@ -163,6 +163,18 @@ static int dfu_flush_medium_nand(struct dfu_entity *dfu) return ret; } +unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu) +{ + /* + * Currently, Poll Timeout != 0 is only needed on nand + * ubi partition, as the not used sectors need an erase + */ + if (dfu->data.nand.ubi) + return DFU_MANIFEST_POLL_TIMEOUT; + + return DFU_DEFAULT_POLL_TIMEOUT; +} + int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) { char *st; @@ -211,6 +223,7 @@ int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) dfu->read_medium = dfu_read_medium_nand; dfu->write_medium = dfu_write_medium_nand; dfu->flush_medium = dfu_flush_medium_nand; + dfu->poll_timeout = dfu_polltimeout_nand; /* initial state */ dfu->inited = 0; diff --git a/drivers/fpga/fpga.c b/drivers/fpga/fpga.c index b940d9b316..37946d5e18 100644 --- a/drivers/fpga/fpga.c +++ b/drivers/fpga/fpga.c @@ -173,16 +173,45 @@ int fpga_add(fpga_type devtype, void *desc) /* * Convert bitstream data and load into the fpga */ -int __weak fpga_loadbitstream(int devnum, char *fpgadata, size_t size) +int __weak fpga_loadbitstream(int devnum, char *fpgadata, size_t size, + bitstream_type bstype) { printf("Bitstream support not implemented for this FPGA device\n"); return FPGA_FAIL; } +#if defined(CONFIG_CMD_FPGA_LOADFS) +int fpga_fsload(int devnum, const void *buf, size_t size, + fpga_fs_info *fpga_fsinfo) +{ + int ret_val = FPGA_FAIL; /* assume failure */ + const fpga_desc *desc = fpga_validate(devnum, buf, size, + (char *)__func__); + + if (desc) { + switch (desc->devtype) { + case fpga_xilinx: +#if defined(CONFIG_FPGA_XILINX) + ret_val = xilinx_loadfs(desc->devdesc, buf, size, + fpga_fsinfo); +#else + fpga_no_sup((char *)__func__, "Xilinx devices"); +#endif + break; + default: + printf("%s: Invalid or unsupported device type %d\n", + __func__, desc->devtype); + } + } + + return ret_val; +} +#endif + /* * Generic multiplexing code */ -int fpga_load(int devnum, const void *buf, size_t bsize) +int fpga_load(int devnum, const void *buf, size_t bsize, bitstream_type bstype) { int ret_val = FPGA_FAIL; /* assume failure */ const fpga_desc *desc = fpga_validate(devnum, buf, bsize, @@ -192,7 +221,8 @@ int fpga_load(int devnum, const void *buf, size_t bsize) switch (desc->devtype) { case fpga_xilinx: #if defined(CONFIG_FPGA_XILINX) - ret_val = xilinx_load(desc->devdesc, buf, bsize); + ret_val = xilinx_load(desc->devdesc, buf, bsize, + bstype); #else fpga_no_sup((char *)__func__, "Xilinx devices"); #endif diff --git a/drivers/fpga/spartan2.c b/drivers/fpga/spartan2.c index 7054056140..859fb3c778 100644 --- a/drivers/fpga/spartan2.c +++ b/drivers/fpga/spartan2.c @@ -41,7 +41,8 @@ static int spartan2_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize); /* ------------------------------------------------------------------------- */ /* Spartan-II Generic Implementation */ -static int spartan2_load(xilinx_desc *desc, const void *buf, size_t bsize) +static int spartan2_load(xilinx_desc *desc, const void *buf, size_t bsize, + bitstream_type bstype) { int ret_val = FPGA_FAIL; diff --git a/drivers/fpga/spartan3.c b/drivers/fpga/spartan3.c index 5c9412c2f6..b0213e6999 100644 --- a/drivers/fpga/spartan3.c +++ b/drivers/fpga/spartan3.c @@ -45,7 +45,8 @@ static int spartan3_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize); /* ------------------------------------------------------------------------- */ /* Spartan-II Generic Implementation */ -static int spartan3_load(xilinx_desc *desc, const void *buf, size_t bsize) +static int spartan3_load(xilinx_desc *desc, const void *buf, size_t bsize, + bitstream_type bstype) { int ret_val = FPGA_FAIL; diff --git a/drivers/fpga/virtex2.c b/drivers/fpga/virtex2.c index e092147edd..0d2d9a4693 100644 --- a/drivers/fpga/virtex2.c +++ b/drivers/fpga/virtex2.c @@ -90,7 +90,8 @@ static int virtex2_ssm_dump(xilinx_desc *desc, const void *buf, size_t bsize); static int virtex2_ss_load(xilinx_desc *desc, const void *buf, size_t bsize); static int virtex2_ss_dump(xilinx_desc *desc, const void *buf, size_t bsize); -static int virtex2_load(xilinx_desc *desc, const void *buf, size_t bsize) +static int virtex2_load(xilinx_desc *desc, const void *buf, size_t bsize, + bitstream_type bstype) { int ret_val = FPGA_FAIL; diff --git a/drivers/fpga/xilinx.c b/drivers/fpga/xilinx.c index 8837f5c122..3795c1aff6 100644 --- a/drivers/fpga/xilinx.c +++ b/drivers/fpga/xilinx.c @@ -24,7 +24,8 @@ static int xilinx_validate(xilinx_desc *desc, char *fn); /* ------------------------------------------------------------------------- */ -int fpga_loadbitstream(int devnum, char *fpgadata, size_t size) +int fpga_loadbitstream(int devnum, char *fpgadata, size_t size, + bitstream_type bstype) { unsigned int length; unsigned int swapsize; @@ -127,19 +128,36 @@ int fpga_loadbitstream(int devnum, char *fpgadata, size_t size) dataptr += 4; printf(" bytes in bitstream = %d\n", swapsize); - return fpga_load(devnum, dataptr, swapsize); + return fpga_load(devnum, dataptr, swapsize, bstype); } -int xilinx_load(xilinx_desc *desc, const void *buf, size_t bsize) +int xilinx_load(xilinx_desc *desc, const void *buf, size_t bsize, + bitstream_type bstype) { if (!xilinx_validate (desc, (char *)__FUNCTION__)) { printf ("%s: Invalid device descriptor\n", __FUNCTION__); return FPGA_FAIL; } - return desc->operations->load(desc, buf, bsize); + return desc->operations->load(desc, buf, bsize, bstype); } +#if defined(CONFIG_CMD_FPGA_LOADFS) +int xilinx_loadfs(xilinx_desc *desc, const void *buf, size_t bsize, + fpga_fs_info *fpga_fsinfo) +{ + if (!xilinx_validate(desc, (char *)__func__)) { + printf("%s: Invalid device descriptor\n", __func__); + return FPGA_FAIL; + } + + if (!desc->operations->loadfs) + return FPGA_FAIL; + + return desc->operations->loadfs(desc, buf, bsize, fpga_fsinfo); +} +#endif + int xilinx_dump(xilinx_desc *desc, const void *buf, size_t bsize) { if (!xilinx_validate (desc, (char *)__FUNCTION__)) { diff --git a/drivers/fpga/zynqpl.c b/drivers/fpga/zynqpl.c index c066f21d79..68fe0f3b03 100644 --- a/drivers/fpga/zynqpl.c +++ b/drivers/fpga/zynqpl.c @@ -9,6 +9,7 @@ #include <common.h> #include <asm/io.h> +#include <fs.h> #include <zynqpl.h> #include <linux/sizes.h> #include <asm/arch/hardware.h> @@ -194,7 +195,7 @@ static int zynq_dma_transfer(u32 srcbuf, u32 srclen, u32 dstbuf, u32 dstlen) return FPGA_SUCCESS; } -static int zynq_dma_xfer_init(u32 partialbit) +static int zynq_dma_xfer_init(bitstream_type bstype) { u32 status, control, isr_status; unsigned long ts; @@ -202,7 +203,7 @@ static int zynq_dma_xfer_init(u32 partialbit) /* Clear loopback bit */ clrbits_le32(&devcfg_base->mctrl, DEVCFG_MCTRL_PCAP_LPBK); - if (!partialbit) { + if (bstype != BIT_PARTIAL) { zynq_slcr_devcfg_disable(); /* Setting PCFG_PROG_B signal to high */ @@ -322,16 +323,11 @@ static u32 *zynq_align_dma_buffer(u32 *buf, u32 len, u32 swap) static int zynq_validate_bitstream(xilinx_desc *desc, const void *buf, size_t bsize, u32 blocksize, u32 *swap, - u32 *partialbit) + bitstream_type *bstype) { u32 *buf_start; u32 diff; - /* Detect if we are going working with partial or full bitstream */ - if (bsize != desc->size) { - printf("%s: Working with partial bitstream\n", __func__); - *partialbit = 1; - } buf_start = check_data((u8 *)buf, blocksize, swap); if (!buf_start) @@ -351,17 +347,16 @@ static int zynq_validate_bitstream(xilinx_desc *desc, const void *buf, return FPGA_FAIL; } - if (zynq_dma_xfer_init(*partialbit)) + if (zynq_dma_xfer_init(*bstype)) return FPGA_FAIL; return 0; } - -static int zynq_load(xilinx_desc *desc, const void *buf, size_t bsize) +static int zynq_load(xilinx_desc *desc, const void *buf, size_t bsize, + bitstream_type bstype) { unsigned long ts; /* Timestamp */ - u32 partialbit = 0; u32 isr_status, swap; /* @@ -369,7 +364,7 @@ static int zynq_load(xilinx_desc *desc, const void *buf, size_t bsize) * in chunks */ if (zynq_validate_bitstream(desc, buf, bsize, bsize, &swap, - &partialbit)) + &bstype)) return FPGA_FAIL; buf = zynq_align_dma_buffer((u32 *)buf, bsize, swap); @@ -398,11 +393,92 @@ static int zynq_load(xilinx_desc *desc, const void *buf, size_t bsize) debug("%s: FPGA config done\n", __func__); + if (bstype != BIT_PARTIAL) + zynq_slcr_devcfg_enable(); + + return FPGA_SUCCESS; +} + +#if defined(CONFIG_CMD_FPGA_LOADFS) +static int zynq_loadfs(xilinx_desc *desc, const void *buf, size_t bsize, + fpga_fs_info *fsinfo) +{ + unsigned long ts; /* Timestamp */ + u32 isr_status, swap; + u32 partialbit = 0; + u32 blocksize; + u32 pos = 0; + int fstype; + char *interface, *dev_part, *filename; + + blocksize = fsinfo->blocksize; + interface = fsinfo->interface; + dev_part = fsinfo->dev_part; + filename = fsinfo->filename; + fstype = fsinfo->fstype; + + if (fs_set_blk_dev(interface, dev_part, fstype)) + return FPGA_FAIL; + + if (fs_read(filename, (u32) buf, pos, blocksize) < 0) + return FPGA_FAIL; + + if (zynq_validate_bitstream(desc, buf, bsize, blocksize, &swap, + &partialbit)) + return FPGA_FAIL; + + dcache_disable(); + + do { + buf = zynq_align_dma_buffer((u32 *)buf, blocksize, swap); + + if (zynq_dma_transfer((u32)buf | 1, blocksize >> 2, + 0xffffffff, 0)) + return FPGA_FAIL; + + bsize -= blocksize; + pos += blocksize; + + if (fs_set_blk_dev(interface, dev_part, fstype)) + return FPGA_FAIL; + + if (bsize > blocksize) { + if (fs_read(filename, (u32) buf, pos, blocksize) < 0) + return FPGA_FAIL; + } else { + if (fs_read(filename, (u32) buf, pos, bsize) < 0) + return FPGA_FAIL; + } + } while (bsize > blocksize); + + buf = zynq_align_dma_buffer((u32 *)buf, blocksize, swap); + + if (zynq_dma_transfer((u32)buf | 1, bsize >> 2, 0xffffffff, 0)) + return FPGA_FAIL; + + dcache_enable(); + + isr_status = readl(&devcfg_base->int_sts); + + /* Check FPGA configuration completion */ + ts = get_timer(0); + while (!(isr_status & DEVCFG_ISR_PCFG_DONE)) { + if (get_timer(ts) > CONFIG_SYS_FPGA_WAIT) { + printf("%s: Timeout wait for FPGA to config\n", + __func__); + return FPGA_FAIL; + } + isr_status = readl(&devcfg_base->int_sts); + } + + debug("%s: FPGA config done\n", __func__); + if (!partialbit) zynq_slcr_devcfg_enable(); return FPGA_SUCCESS; } +#endif static int zynq_dump(xilinx_desc *desc, const void *buf, size_t bsize) { @@ -411,6 +487,9 @@ static int zynq_dump(xilinx_desc *desc, const void *buf, size_t bsize) struct xilinx_fpga_op zynq_op = { .load = zynq_load, +#if defined(CONFIG_CMD_FPGA_LOADFS) + .loadfs = zynq_loadfs, +#endif .dump = zynq_dump, .info = zynq_info, }; diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c index 11a0472c69..db7b673731 100644 --- a/drivers/gpio/s5p_gpio.c +++ b/drivers/gpio/s5p_gpio.c @@ -8,11 +8,9 @@ #include <common.h> #include <asm/io.h> #include <asm/gpio.h> +#include <asm/arch/gpio.h> -#define S5P_GPIO_GET_BANK(x) ((x >> S5P_GPIO_BANK_SHIFT) \ - & S5P_GPIO_BANK_MASK) - -#define S5P_GPIO_GET_PIN(x) (x & S5P_GPIO_PIN_MASK) +#define S5P_GPIO_GET_PIN(x) (x % GPIO_PER_BANK) #define CON_MASK(x) (0xf << ((x) << 2)) #define CON_SFR(x, v) ((v) << ((x) << 2)) @@ -28,7 +26,103 @@ #define RATE_MASK(x) (0x1 << (x + 16)) #define RATE_SET(x) (0x1 << (x + 16)) -void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) +#define name_to_gpio(n) s5p_name_to_gpio(n) +static inline int s5p_name_to_gpio(const char *name) +{ + unsigned num, irregular_set_number, irregular_bank_base; + const struct gpio_name_num_table *tabp; + char this_bank, bank_name, irregular_bank_name; + char *endp; + + /* + * The gpio name starts with either 'g' or 'gp' followed by the bank + * name character. Skip one or two characters depending on the prefix. + */ + if (name[0] == 'g' && name[1] == 'p') + name += 2; + else if (name[0] == 'g') + name++; + else + return -1; /* Name must start with 'g' */ + + bank_name = *name++; + if (!*name) + return -1; /* At least one digit is required/expected. */ + + /* + * On both exynos5 and exynos5420 architectures there is a bank of + * GPIOs which does not fall into the regular address pattern. Those + * banks are c4 on Exynos5 and y7 on Exynos5420. The rest of the below + * assignments help to handle these irregularities. + */ +#if defined(CONFIG_EXYNOS4) || defined(CONFIG_EXYNOS5) + if (cpu_is_exynos5()) { + if (proid_is_exynos5420()) { + tabp = exynos5420_gpio_table; + irregular_bank_name = 'y'; + irregular_set_number = '7'; + irregular_bank_base = EXYNOS5420_GPIO_Y70; + } else { + tabp = exynos5_gpio_table; + irregular_bank_name = 'c'; + irregular_set_number = '4'; + irregular_bank_base = EXYNOS5_GPIO_C40; + } + } else { + if (proid_is_exynos4412()) + tabp = exynos4x12_gpio_table; + else + tabp = exynos4_gpio_table; + irregular_bank_name = 0; + irregular_set_number = 0; + irregular_bank_base = 0; + } +#else + if (cpu_is_s5pc110()) + tabp = s5pc110_gpio_table; + else + tabp = s5pc100_gpio_table; + irregular_bank_name = 0; + irregular_set_number = 0; + irregular_bank_base = 0; +#endif + + this_bank = tabp->bank; + do { + if (bank_name == this_bank) { + unsigned pin_index; /* pin number within the bank */ + if ((bank_name == irregular_bank_name) && + (name[0] == irregular_set_number)) { + pin_index = name[1] - '0'; + /* Irregular sets have 8 pins. */ + if (pin_index >= GPIO_PER_BANK) + return -1; + num = irregular_bank_base + pin_index; + } else { + pin_index = simple_strtoul(name, &endp, 8); + pin_index -= tabp->bank_offset; + /* + * Sanity check: bunk 'z' has no set number, + * for all other banks there must be exactly + * two octal digits, and the resulting number + * should not exceed the number of pins in the + * bank. + */ + if (((bank_name != 'z') && !name[1]) || + *endp || + (pin_index >= tabp->bank_size)) + return -1; + num = tabp->base + pin_index; + } + return num; + } + this_bank = (++tabp)->bank; + } while (this_bank); + + return -1; +} + +static void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) { unsigned int value; @@ -38,18 +132,7 @@ void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) writel(value, &bank->con); } -void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en) -{ - s5p_gpio_cfg_pin(bank, gpio, GPIO_OUTPUT); - s5p_gpio_set_value(bank, gpio, en); -} - -void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio) -{ - s5p_gpio_cfg_pin(bank, gpio, GPIO_INPUT); -} - -void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) +static void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) { unsigned int value; @@ -60,7 +143,19 @@ void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) writel(value, &bank->dat); } -unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) +static void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, + int gpio, int en) +{ + s5p_gpio_cfg_pin(bank, gpio, S5P_GPIO_OUTPUT); + s5p_gpio_set_value(bank, gpio, en); +} + +static void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio) +{ + s5p_gpio_cfg_pin(bank, gpio, S5P_GPIO_INPUT); +} + +static unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) { unsigned int value; @@ -68,7 +163,7 @@ unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) return !!(value & DAT_MASK(gpio)); } -void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) +static void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) { unsigned int value; @@ -76,8 +171,8 @@ void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) value &= ~PULL_MASK(gpio); switch (mode) { - case GPIO_PULL_DOWN: - case GPIO_PULL_UP: + case S5P_GPIO_PULL_DOWN: + case S5P_GPIO_PULL_UP: value |= PULL_MODE(gpio, mode); break; default: @@ -87,7 +182,7 @@ void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) writel(value, &bank->pull); } -void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) +static void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) { unsigned int value; @@ -95,10 +190,10 @@ void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) value &= ~DRV_MASK(gpio); switch (mode) { - case GPIO_DRV_1X: - case GPIO_DRV_2X: - case GPIO_DRV_3X: - case GPIO_DRV_4X: + case S5P_GPIO_DRV_1X: + case S5P_GPIO_DRV_2X: + case S5P_GPIO_DRV_3X: + case S5P_GPIO_DRV_4X: value |= DRV_SET(gpio, mode); break; default: @@ -108,7 +203,7 @@ void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) writel(value, &bank->drv); } -void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) +static void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) { unsigned int value; @@ -116,8 +211,8 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) value &= ~RATE_MASK(gpio); switch (mode) { - case GPIO_DRV_FAST: - case GPIO_DRV_SLOW: + case S5P_GPIO_DRV_FAST: + case S5P_GPIO_DRV_SLOW: value |= RATE_SET(gpio); break; default: @@ -127,12 +222,31 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) writel(value, &bank->drv); } -struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio) +struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned int gpio) { - unsigned bank = S5P_GPIO_GET_BANK(gpio); - unsigned base = s5p_gpio_base(gpio); + const struct gpio_info *data; + unsigned int upto; + int i, count; + + data = get_gpio_data(); + count = get_bank_num(); + upto = 0; + + for (i = 0; i < count; i++) { + debug("i=%d, upto=%d\n", i, upto); + if (gpio < data->max_gpio) { + struct s5p_gpio_bank *bank; + bank = (struct s5p_gpio_bank *)data->reg_addr; + bank += (gpio - upto) / GPIO_PER_BANK; + debug("gpio=%d, bank=%p\n", gpio, bank); + return bank; + } + + upto = data->max_gpio; + data++; + } - return (struct s5p_gpio_bank *)(base + bank); + return NULL; } int s5p_gpio_get_pin(unsigned gpio) @@ -179,3 +293,27 @@ int gpio_set_value(unsigned gpio, int value) return 0; } + +void gpio_set_pull(int gpio, int mode) +{ + s5p_gpio_set_pull(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); +} + +void gpio_set_drv(int gpio, int mode) +{ + s5p_gpio_set_drv(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); +} + +void gpio_cfg_pin(int gpio, int cfg) +{ + s5p_gpio_cfg_pin(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), cfg); +} + +void gpio_set_rate(int gpio, int mode) +{ + s5p_gpio_set_rate(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), mode); +} diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c index 82b30d5ab6..fea9d17f8e 100644 --- a/drivers/gpio/tegra_gpio.c +++ b/drivers/gpio/tegra_gpio.c @@ -221,6 +221,26 @@ int gpio_set_value(unsigned gpio, int value) return 0; } +void gpio_config_table(const struct tegra_gpio_config *config, int len) +{ + int i; + + for (i = 0; i < len; i++) { + switch (config[i].init) { + case TEGRA_GPIO_INIT_IN: + gpio_direction_input(config[i].gpio); + break; + case TEGRA_GPIO_INIT_OUT0: + gpio_direction_output(config[i].gpio, 0); + break; + case TEGRA_GPIO_INIT_OUT1: + gpio_direction_output(config[i].gpio, 1); + break; + } + set_config(config[i].gpio, 1); + } +} + /* * Display Tegra GPIO information */ diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index ed67eec252..ca9c4aa15f 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -18,7 +18,7 @@ DECLARE_GLOBAL_DATA_PTR; -struct mmc_host mmc_host[MAX_HOSTS]; +struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE]; #ifndef CONFIG_OF_CONTROL #error "Please enable device tree support to use this driver" @@ -669,13 +669,14 @@ static int process_nodes(const void *blob, int node_list[], int count) void tegra_mmc_init(void) { - int node_list[MAX_HOSTS], count; + int node_list[CONFIG_SYS_MMC_MAX_DEVICE], count; const void *blob = gd->fdt_blob; debug("%s entry\n", __func__); /* See if any Tegra124 MMC controllers are present */ count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, MAX_HOSTS); + COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, + CONFIG_SYS_MMC_MAX_DEVICE); debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count); if (process_nodes(blob, node_list, count)) { printf("%s: Error processing T30 mmc node(s)!\n", __func__); @@ -684,7 +685,8 @@ void tegra_mmc_init(void) /* See if any Tegra30 MMC controllers are present */ count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, MAX_HOSTS); + COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, + CONFIG_SYS_MMC_MAX_DEVICE); debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); if (process_nodes(blob, node_list, count)) { printf("%s: Error processing T30 mmc node(s)!\n", __func__); @@ -693,7 +695,8 @@ void tegra_mmc_init(void) /* Now look for any Tegra20 MMC controllers */ count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, MAX_HOSTS); + COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, + CONFIG_SYS_MMC_MAX_DEVICE); debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); if (process_nodes(blob, node_list, count)) { printf("%s: Error processing T20 mmc node(s)!\n", __func__); diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 881a63618c..bf99b8e675 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -403,7 +403,7 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, dat[byte_pos] ^= 1 << bit_pos; printf("nand: bit-flip corrected @data=%d\n", byte_pos); } else if (byte_pos < error_max) { - read_ecc[byte_pos - SECTOR_BYTES] = 1 << bit_pos; + read_ecc[byte_pos - SECTOR_BYTES] ^= 1 << bit_pos; printf("nand: bit-flip corrected @oob=%d\n", byte_pos - SECTOR_BYTES); } else { diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 4129bdabfb..920bbdce6e 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o diff --git a/drivers/power/pmic/pmic_ltc3676.c b/drivers/power/pmic/pmic_ltc3676.c new file mode 100644 index 0000000000..9b874cb07c --- /dev/null +++ b/drivers/power/pmic/pmic_ltc3676.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Gateworks Corporation + * Tim Harvey <tharvey@gateworks.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/ltc3676_pmic.h> + +int power_ltc3676_init(unsigned char bus) +{ + static const char name[] = "LTC3676_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = LTC3676_NUM_OF_REGS; + p->hw.i2c.addr = CONFIG_POWER_LTC3676_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} diff --git a/drivers/power/pmic/pmic_pfuze100.c b/drivers/power/pmic/pmic_pfuze100.c index 22c1f15eeb..21f12d2564 100644 --- a/drivers/power/pmic/pmic_pfuze100.c +++ b/drivers/power/pmic/pmic_pfuze100.c @@ -11,7 +11,7 @@ #include <power/pmic.h> #include <power/pfuze100_pmic.h> -int pmic_init(unsigned char bus) +int power_pfuze100_init(unsigned char bus) { static const char name[] = "PFUZE100_PMIC"; struct pmic *p = pmic_alloc(); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 896c8d407e..66becdc782 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_THOR_FUNCTION) += f_thor.o obj-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o obj-$(CONFIG_DFU_FUNCTION) += f_dfu.o obj-$(CONFIG_USB_GADGET_MASS_STORAGE) += f_mass_storage.o +obj-$(CONFIG_CMD_FASTBOOT) += f_fastboot.o endif ifdef CONFIG_USB_ETHER obj-y += ether.o diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c index 02d3fdade8..9cd003636a 100644 --- a/drivers/usb/gadget/ci_udc.c +++ b/drivers/usb/gadget/ci_udc.c @@ -205,13 +205,26 @@ static void ci_invalidate_qtd(int ep_num) static struct usb_request * ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) { - struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); - return &ci_ep->req; + struct ci_req *ci_req; + + ci_req = memalign(ARCH_DMA_MINALIGN, sizeof(*ci_req)); + if (!ci_req) + return NULL; + + INIT_LIST_HEAD(&ci_req->queue); + ci_req->b_buf = 0; + + return &ci_req->req; } -static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req) +static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req) { - return; + struct ci_req *ci_req; + + ci_req = container_of(req, struct ci_req, req); + if (ci_req->b_buf) + free(ci_req->b_buf); + free(ci_req); } static void ep_enable(int num, int in, int maxpacket) @@ -267,99 +280,102 @@ static int ci_ep_disable(struct usb_ep *ep) return 0; } -static int ci_bounce(struct ci_ep *ep, int in) +static int ci_bounce(struct ci_req *ci_req, int in) { - uint32_t addr = (uint32_t)ep->req.buf; - uint32_t ba; + struct usb_request *req = &ci_req->req; + uint32_t addr = (uint32_t)req->buf; + uint32_t hwaddr; + uint32_t aligned_used_len; /* Input buffer address is not aligned. */ if (addr & (ARCH_DMA_MINALIGN - 1)) goto align; /* Input buffer length is not aligned. */ - if (ep->req.length & (ARCH_DMA_MINALIGN - 1)) + if (req->length & (ARCH_DMA_MINALIGN - 1)) goto align; /* The buffer is well aligned, only flush cache. */ - ep->b_len = ep->req.length; - ep->b_buf = ep->req.buf; + ci_req->hw_len = req->length; + ci_req->hw_buf = req->buf; goto flush; align: - /* Use internal buffer for small payloads. */ - if (ep->req.length <= 64) { - ep->b_len = 64; - ep->b_buf = ep->b_fast; - } else { - ep->b_len = roundup(ep->req.length, ARCH_DMA_MINALIGN); - ep->b_buf = memalign(ARCH_DMA_MINALIGN, ep->b_len); - if (!ep->b_buf) + if (ci_req->b_buf && req->length > ci_req->b_len) { + free(ci_req->b_buf); + ci_req->b_buf = 0; + } + if (!ci_req->b_buf) { + ci_req->b_len = roundup(req->length, ARCH_DMA_MINALIGN); + ci_req->b_buf = memalign(ARCH_DMA_MINALIGN, ci_req->b_len); + if (!ci_req->b_buf) return -ENOMEM; } + ci_req->hw_len = ci_req->b_len; + ci_req->hw_buf = ci_req->b_buf; + if (in) - memcpy(ep->b_buf, ep->req.buf, ep->req.length); + memcpy(ci_req->hw_buf, req->buf, req->length); flush: - ba = (uint32_t)ep->b_buf; - flush_dcache_range(ba, ba + ep->b_len); + hwaddr = (uint32_t)ci_req->hw_buf; + aligned_used_len = roundup(req->length, ARCH_DMA_MINALIGN); + flush_dcache_range(hwaddr, hwaddr + aligned_used_len); return 0; } -static void ci_debounce(struct ci_ep *ep, int in) +static void ci_debounce(struct ci_req *ci_req, int in) { - uint32_t addr = (uint32_t)ep->req.buf; - uint32_t ba = (uint32_t)ep->b_buf; + struct usb_request *req = &ci_req->req; + uint32_t addr = (uint32_t)req->buf; + uint32_t hwaddr = (uint32_t)ci_req->hw_buf; + uint32_t aligned_used_len; - if (in) { - if (addr == ba) - return; /* not a bounce */ - goto free; - } - invalidate_dcache_range(ba, ba + ep->b_len); + if (in) + return; + + aligned_used_len = roundup(req->actual, ARCH_DMA_MINALIGN); + invalidate_dcache_range(hwaddr, hwaddr + aligned_used_len); - if (addr == ba) - return; /* not a bounce */ + if (addr == hwaddr) + return; /* not a bounce */ - memcpy(ep->req.buf, ep->b_buf, ep->req.actual); -free: - /* Large payloads use allocated buffer, free it. */ - if (ep->b_buf != ep->b_fast) - free(ep->b_buf); + memcpy(req->buf, ci_req->hw_buf, req->actual); } -static int ci_ep_queue(struct usb_ep *ep, - struct usb_request *req, gfp_t gfp_flags) +static void ci_ep_submit_next_request(struct ci_ep *ci_ep) { - struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; struct ept_queue_item *item; struct ept_queue_head *head; - int bit, num, len, in, ret; + int bit, num, len, in; + struct ci_req *ci_req; + + ci_ep->req_primed = true; + num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; item = ci_get_qtd(num, in); head = ci_get_qh(num, in); - len = req->length; - ret = ci_bounce(ci_ep, in); - if (ret) - return ret; + ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue); + len = ci_req->req.length; item->next = TERMINATE; item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE; - item->page0 = (uint32_t)ci_ep->b_buf; - item->page1 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x1000; - item->page2 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x2000; - item->page3 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x3000; - item->page4 = ((uint32_t)ci_ep->b_buf & 0xfffff000) + 0x4000; + item->page0 = (uint32_t)ci_req->hw_buf; + item->page1 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x1000; + item->page2 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x2000; + item->page3 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x3000; + item->page4 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x4000; ci_flush_qtd(num); head->next = (unsigned) item; head->info = 0; - DBG("ept%d %s queue len %x, buffer %p\n", - num, in ? "in" : "out", len, ci_ep->b_buf); + DBG("ept%d %s queue len %x, req %p, buffer %p\n", + num, in ? "in" : "out", len, ci_req, ci_req->hw_buf); ci_flush_qh(num); if (in) @@ -368,6 +384,29 @@ static int ci_ep_queue(struct usb_ep *ep, bit = EPT_RX(num); writel(bit, &udc->epprime); +} + +static int ci_ep_queue(struct usb_ep *ep, + struct usb_request *req, gfp_t gfp_flags) +{ + struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep); + struct ci_req *ci_req = container_of(req, struct ci_req, req); + int in, ret; + int __maybe_unused num; + + num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; + + ret = ci_bounce(ci_req, in); + if (ret) + return ret; + + DBG("ept%d %s pre-queue req %p, buffer %p\n", + num, in ? "in" : "out", ci_req, ci_req->hw_buf); + list_add_tail(&ci_req->queue, &ci_ep->queue); + + if (!ci_ep->req_primed) + ci_ep_submit_next_request(ci_ep); return 0; } @@ -376,6 +415,8 @@ static void handle_ep_complete(struct ci_ep *ep) { struct ept_queue_item *item; int num, in, len; + struct ci_req *ci_req; + num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0; if (num == 0) @@ -383,20 +424,27 @@ static void handle_ep_complete(struct ci_ep *ep) item = ci_get_qtd(num, in); ci_invalidate_qtd(num); + len = (item->info >> 16) & 0x7fff; if (item->info & 0xff) printf("EP%d/%s FAIL info=%x pg0=%x\n", num, in ? "in" : "out", item->info, item->page0); - len = (item->info >> 16) & 0x7fff; - ep->req.actual = ep->req.length - len; - ci_debounce(ep, in); + ci_req = list_first_entry(&ep->queue, struct ci_req, queue); + list_del_init(&ci_req->queue); + ep->req_primed = false; + + if (!list_empty(&ep->queue)) + ci_ep_submit_next_request(ep); + + ci_req->req.actual = ci_req->req.length - len; + ci_debounce(ci_req, in); - DBG("ept%d %s complete %x\n", - num, in ? "in" : "out", len); - ep->req.complete(&ep->ep, &ep->req); + DBG("ept%d %s req %p, complete %x\n", + num, in ? "in" : "out", ci_req, len); + ci_req->req.complete(&ep->ep, &ci_req->req); if (num == 0) { - ep->req.length = 0; - usb_ep_queue(&ep->ep, &ep->req, 0); + ci_req->req.length = 0; + usb_ep_queue(&ep->ep, &ci_req->req, 0); ep->desc = &ep0_in_desc; } } @@ -405,13 +453,18 @@ static void handle_ep_complete(struct ci_ep *ep) static void handle_setup(void) { - struct usb_request *req = &controller.ep[0].req; + struct ci_ep *ci_ep = &controller.ep[0]; + struct ci_req *ci_req; + struct usb_request *req; struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor; struct ept_queue_head *head; struct usb_ctrlrequest r; int status = 0; int num, in, _num, _in, i; char *buf; + + ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue); + req = &ci_req->req; head = ci_get_qh(0, 0); /* EP0 OUT */ ci_invalidate_qh(0); @@ -424,6 +477,9 @@ static void handle_setup(void) DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest), r.bRequestType, r.bRequest, r.wIndex, r.wValue); + list_del_init(&ci_req->queue); + ci_ep->req_primed = false; + switch (SETUP(r.bRequestType, r.bRequest)) { case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE): _num = r.wIndex & 15; @@ -701,6 +757,8 @@ static int ci_udc_probe(void) /* Init EP 0 */ memcpy(&controller.ep[0].ep, &ci_ep_init[0], sizeof(*ci_ep_init)); controller.ep[0].desc = &ep0_in_desc; + INIT_LIST_HEAD(&controller.ep[0].queue); + controller.ep[0].req_primed = false; controller.gadget.ep0 = &controller.ep[0].ep; INIT_LIST_HEAD(&controller.gadget.ep0->ep_list); @@ -708,6 +766,8 @@ static int ci_udc_probe(void) for (i = 1; i < NUM_ENDPOINTS; i++) { memcpy(&controller.ep[i].ep, &ci_ep_init[1], sizeof(*ci_ep_init)); + INIT_LIST_HEAD(&controller.ep[i].queue); + controller.ep[i].req_primed = false; list_add_tail(&controller.ep[i].ep.ep_list, &controller.gadget.ep_list); } diff --git a/drivers/usb/gadget/ci_udc.h b/drivers/usb/gadget/ci_udc.h index 4425fd9345..23cff56d7e 100644 --- a/drivers/usb/gadget/ci_udc.h +++ b/drivers/usb/gadget/ci_udc.h @@ -77,15 +77,22 @@ struct ci_udc { #define CTRL_TXT_BULK (2 << 18) #define CTRL_RXT_BULK (2 << 2) +struct ci_req { + struct usb_request req; + struct list_head queue; + /* Bounce buffer allocated if needed to align the transfer */ + uint8_t *b_buf; + uint32_t b_len; + /* Buffer for the current transfer. Either req.buf/len or b_buf/len */ + uint8_t *hw_buf; + uint32_t hw_len; +}; + struct ci_ep { struct usb_ep ep; struct list_head queue; + bool req_primed; const struct usb_endpoint_descriptor *desc; - - struct usb_request req; - uint8_t *b_buf; - uint32_t b_len; - uint8_t b_fast[64] __aligned(ARCH_DMA_MINALIGN); }; struct ci_drv { diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 1b1e1793d9..859fe828de 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -175,10 +175,17 @@ static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req) req->length, f_dfu->blk_seq_num); } +static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu) +{ + return dfu->poll_timeout ? dfu->poll_timeout(dfu) : + DFU_MANIFEST_POLL_TIMEOUT; +} + static void handle_getstatus(struct usb_request *req) { struct dfu_status *dstat = (struct dfu_status *)req->buf; struct f_dfu *f_dfu = req->context; + struct dfu_entity *dfu = dfu_get_entity(f_dfu->altsetting); dfu_set_poll_timeout(dstat, 0); @@ -191,7 +198,8 @@ static void handle_getstatus(struct usb_request *req) f_dfu->dfu_state = DFU_STATE_dfuMANIFEST; break; case DFU_STATE_dfuMANIFEST: - dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT); + dfu_set_poll_timeout(dstat, dfu_get_manifest_timeout(dfu)); + break; default: break; } diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c new file mode 100644 index 0000000000..9dd85b636e --- /dev/null +++ b/drivers/usb/gadget/f_fastboot.c @@ -0,0 +1,513 @@ +/* + * (C) Copyright 2008 - 2009 + * Windriver, <www.windriver.com> + * Tom Rix <Tom.Rix@windriver.com> + * + * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de> + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <errno.h> +#include <malloc.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <linux/compiler.h> +#include <version.h> +#include <g_dnl.h> + +#define FASTBOOT_VERSION "0.4" + +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 + +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) +#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) +#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) + +/* The 64 defined bytes plus \0 */ +#define RESPONSE_LEN (64 + 1) + +#define EP_BUFFER_SIZE 4096 + +struct f_fastboot { + struct usb_function usb_function; + + /* IN/OUT EP's and correspoinding requests */ + struct usb_ep *in_ep, *out_ep; + struct usb_request *in_req, *out_req; +}; + +static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) +{ + return container_of(f, struct f_fastboot, usb_function); +} + +static struct f_fastboot *fastboot_func; +static unsigned int download_size; +static unsigned int download_bytes; + +static struct usb_endpoint_descriptor fs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = TX_ENDPOINT_MAXIMUM_PACKET_SIZE, + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor fs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1, + .bInterval = 0x00, +}; + +static struct usb_endpoint_descriptor hs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0, + .bInterval = 0x00, +}; + +static struct usb_interface_descriptor interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, +}; + +static struct usb_descriptor_header *fb_runtime_descs[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&fs_ep_in, + (struct usb_descriptor_header *)&hs_ep_out, + NULL, +}; + +/* + * static strings, in UTF-8 + */ +static const char fastboot_name[] = "Android Fastboot"; + +static struct usb_string fastboot_string_defs[] = { + [0].s = fastboot_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_fastboot = { + .language = 0x0409, /* en-us */ + .strings = fastboot_string_defs, +}; + +static struct usb_gadget_strings *fastboot_strings[] = { + &stringtab_fastboot, + NULL, +}; + +static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); + +static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + if (!status) + return; + printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual); +} + +static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) +{ + int id; + struct usb_gadget *gadget = c->cdev->gadget; + struct f_fastboot *f_fb = func_to_fastboot(f); + + /* DYNAMIC interface numbers assignments */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + interface_desc.bInterfaceNumber = id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + fastboot_string_defs[0].id = id; + interface_desc.iInterface = id; + + f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); + if (!f_fb->in_ep) + return -ENODEV; + f_fb->in_ep->driver_data = c->cdev; + + f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!f_fb->out_ep) + return -ENODEV; + f_fb->out_ep->driver_data = c->cdev; + + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + + return 0; +} + +static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) +{ + memset(fastboot_func, 0, sizeof(*fastboot_func)); +} + +static void fastboot_disable(struct usb_function *f) +{ + struct f_fastboot *f_fb = func_to_fastboot(f); + + usb_ep_disable(f_fb->out_ep); + usb_ep_disable(f_fb->in_ep); + + if (f_fb->out_req) { + free(f_fb->out_req->buf); + usb_ep_free_request(f_fb->out_ep, f_fb->out_req); + f_fb->out_req = NULL; + } + if (f_fb->in_req) { + free(f_fb->in_req->buf); + usb_ep_free_request(f_fb->in_ep, f_fb->in_req); + f_fb->in_req = NULL; + } +} + +static struct usb_request *fastboot_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (!req) + return NULL; + + req->length = EP_BUFFER_SIZE; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + memset(req->buf, 0, req->length); + return req; +} + +static int fastboot_set_alt(struct usb_function *f, + unsigned interface, unsigned alt) +{ + int ret; + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct f_fastboot *f_fb = func_to_fastboot(f); + + debug("%s: func: %s intf: %d alt: %d\n", + __func__, f->name, interface, alt); + + /* make sure we don't enable the ep twice */ + if (gadget->speed == USB_SPEED_HIGH) + ret = usb_ep_enable(f_fb->out_ep, &hs_ep_out); + else + ret = usb_ep_enable(f_fb->out_ep, &fs_ep_out); + if (ret) { + puts("failed to enable out ep\n"); + return ret; + } + + f_fb->out_req = fastboot_start_ep(f_fb->out_ep); + if (!f_fb->out_req) { + puts("failed to alloc out req\n"); + ret = -EINVAL; + goto err; + } + f_fb->out_req->complete = rx_handler_command; + + ret = usb_ep_enable(f_fb->in_ep, &fs_ep_in); + if (ret) { + puts("failed to enable in ep\n"); + goto err; + } + + f_fb->in_req = fastboot_start_ep(f_fb->in_ep); + if (!f_fb->in_req) { + puts("failed alloc req in\n"); + ret = -EINVAL; + goto err; + } + f_fb->in_req->complete = fastboot_complete; + + ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0); + if (ret) + goto err; + + return 0; +err: + fastboot_disable(f); + return ret; +} + +static int fastboot_add(struct usb_configuration *c) +{ + struct f_fastboot *f_fb = fastboot_func; + int status; + + debug("%s: cdev: 0x%p\n", __func__, c->cdev); + + if (!f_fb) { + f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb)); + if (!f_fb) + return -ENOMEM; + + fastboot_func = f_fb; + memset(f_fb, 0, sizeof(*f_fb)); + } + + f_fb->usb_function.name = "f_fastboot"; + f_fb->usb_function.hs_descriptors = fb_runtime_descs; + f_fb->usb_function.bind = fastboot_bind; + f_fb->usb_function.unbind = fastboot_unbind; + f_fb->usb_function.set_alt = fastboot_set_alt; + f_fb->usb_function.disable = fastboot_disable; + f_fb->usb_function.strings = fastboot_strings; + + status = usb_add_function(c, &f_fb->usb_function); + if (status) { + free(f_fb); + fastboot_func = f_fb; + } + + return status; +} +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add); + +int fastboot_tx_write(const char *buffer, unsigned int buffer_size) +{ + struct usb_request *in_req = fastboot_func->in_req; + int ret; + + memcpy(in_req->buf, buffer, buffer_size); + in_req->length = buffer_size; + ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0); + if (ret) + printf("Error %d on queue\n", ret); + return 0; +} + +static int fastboot_tx_write_str(const char *buffer) +{ + return fastboot_tx_write(buffer, strlen(buffer)); +} + +static void compl_do_reset(struct usb_ep *ep, struct usb_request *req) +{ + do_reset(NULL, 0, 0, NULL); +} + +static void cb_reboot(struct usb_ep *ep, struct usb_request *req) +{ + fastboot_func->in_req->complete = compl_do_reset; + fastboot_tx_write_str("OKAY"); +} + +static int strcmp_l1(const char *s1, const char *s2) +{ + if (!s1 || !s2) + return -1; + return strncmp(s1, s2, strlen(s1)); +} + +static void cb_getvar(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + const char *s; + + strcpy(response, "OKAY"); + strsep(&cmd, ":"); + if (!cmd) { + fastboot_tx_write_str("FAILmissing var"); + return; + } + + if (!strcmp_l1("version", cmd)) { + strncat(response, FASTBOOT_VERSION, sizeof(response)); + } else if (!strcmp_l1("bootloader-version", cmd)) { + strncat(response, U_BOOT_VERSION, sizeof(response)); + } else if (!strcmp_l1("downloadsize", cmd)) { + char str_num[12]; + + sprintf(str_num, "%08x", CONFIG_USB_FASTBOOT_BUF_SIZE); + strncat(response, str_num, sizeof(response)); + } else if (!strcmp_l1("serialno", cmd)) { + s = getenv("serial#"); + if (s) + strncat(response, s, sizeof(response)); + else + strcpy(response, "FAILValue not set"); + } else { + strcpy(response, "FAILVariable not implemented"); + } + fastboot_tx_write_str(response); +} + +static unsigned int rx_bytes_expected(void) +{ + int rx_remain = download_size - download_bytes; + if (rx_remain < 0) + return 0; + if (rx_remain > EP_BUFFER_SIZE) + return EP_BUFFER_SIZE; + return rx_remain; +} + +#define BYTES_PER_DOT 0x20000 +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{ + char response[RESPONSE_LEN]; + unsigned int transfer_size = download_size - download_bytes; + const unsigned char *buffer = req->buf; + unsigned int buffer_size = req->actual; + + if (req->status != 0) { + printf("Bad status: %d\n", req->status); + return; + } + + if (buffer_size < transfer_size) + transfer_size = buffer_size; + + memcpy((void *)CONFIG_USB_FASTBOOT_BUF_ADDR + download_bytes, + buffer, transfer_size); + + download_bytes += transfer_size; + + /* Check if transfer is done */ + if (download_bytes >= download_size) { + /* + * Reset global transfer variable, keep download_bytes because + * it will be used in the next possible flashing command + */ + download_size = 0; + req->complete = rx_handler_command; + req->length = EP_BUFFER_SIZE; + + sprintf(response, "OKAY"); + fastboot_tx_write_str(response); + + printf("\ndownloading of %d bytes finished\n", download_bytes); + } else { + req->length = rx_bytes_expected(); + if (req->length < ep->maxpacket) + req->length = ep->maxpacket; + } + + if (download_bytes && !(download_bytes % BYTES_PER_DOT)) { + putc('.'); + if (!(download_bytes % (74 * BYTES_PER_DOT))) + putc('\n'); + } + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +static void cb_download(struct usb_ep *ep, struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + + strsep(&cmd, ":"); + download_size = simple_strtoul(cmd, NULL, 16); + download_bytes = 0; + + printf("Starting download of %d bytes\n", download_size); + + if (0 == download_size) { + sprintf(response, "FAILdata invalid size"); + } else if (download_size > CONFIG_USB_FASTBOOT_BUF_SIZE) { + download_size = 0; + sprintf(response, "FAILdata too large"); + } else { + sprintf(response, "DATA%08x", download_size); + req->complete = rx_handler_dl_image; + req->length = rx_bytes_expected(); + if (req->length < ep->maxpacket) + req->length = ep->maxpacket; + } + fastboot_tx_write_str(response); +} + +static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req) +{ + char boot_addr_start[12]; + char *bootm_args[] = { "bootm", boot_addr_start, NULL }; + + puts("Booting kernel..\n"); + + sprintf(boot_addr_start, "0x%lx", load_addr); + do_bootm(NULL, 0, 2, bootm_args); + + /* This only happens if image is somehow faulty so we start over */ + do_reset(NULL, 0, 0, NULL); +} + +static void cb_boot(struct usb_ep *ep, struct usb_request *req) +{ + fastboot_func->in_req->complete = do_bootm_on_complete; + fastboot_tx_write_str("OKAY"); +} + +struct cmd_dispatch_info { + char *cmd; + void (*cb)(struct usb_ep *ep, struct usb_request *req); +}; + +static const struct cmd_dispatch_info cmd_dispatch_info[] = { + { + .cmd = "reboot", + .cb = cb_reboot, + }, { + .cmd = "getvar:", + .cb = cb_getvar, + }, { + .cmd = "download:", + .cb = cb_download, + }, { + .cmd = "boot", + .cb = cb_boot, + }, +}; + +static void rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + char *cmdbuf = req->buf; + void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { + if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) { + func_cb = cmd_dispatch_info[i].cb; + break; + } + } + + if (!func_cb) + fastboot_tx_write_str("FAILunknown command"); + else + func_cb(ep, req); + + if (req->status == 0) { + *cmdbuf = '\0'; + req->actual = 0; + usb_ep_queue(ep, req, 0); + } +} diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index feef9e4619..28f215e07c 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -219,21 +219,15 @@ static int download_tail(long long int left, int cnt) } /* - * To store last "packet" DFU storage backend requires dfu_write with - * size parameter equal to 0 + * To store last "packet" or write file from buffer to filesystem + * DFU storage backend requires dfu_flush * * This also frees memory malloc'ed by dfu_get_buf(), so no explicit * need fo call dfu_free_buf() is needed. */ - ret = dfu_write(dfu_entity, transfer_buffer, 0, cnt); - if (ret) - error("DFU write failed [%d] cnt: %d", ret, cnt); - ret = dfu_flush(dfu_entity, transfer_buffer, 0, cnt); - if (ret) { + if (ret) error("DFU flush failed!"); - return ret; - } return ret; } diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 74300746b9..02803df23c 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -311,11 +311,7 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ /* Number of buffers we will use. 2 is enough for double-buffering */ -#ifndef CONFIG_CI_UDC #define FSG_NUM_BUFFERS 2 -#else -#define FSG_NUM_BUFFERS 1 /* ci_udc only allows 1 req per ep at present */ -#endif /* Default size of buffer length. */ #define FSG_BUFLEN ((u32)16384) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 38db18e2c9..33e5ea9ebd 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -69,6 +69,7 @@ struct fdt_usb { unsigned enabled:1; /* 1 to enable, 0 to disable */ unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */ unsigned initialized:1; /* has this port already been initialized? */ + enum usb_init_type init_type; enum dr_mode dr_mode; /* dual role mode */ enum periph_id periph_id;/* peripheral id */ struct fdt_gpio_state vbus_gpio; /* GPIO for vbus enable */ @@ -237,29 +238,31 @@ int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) return PORTSC_PSPD(reg); } -/* Put the port into host mode */ -static void set_host_mode(struct fdt_usb *config) +/* Set up VBUS for host/device mode */ +static void set_up_vbus(struct fdt_usb *config, enum usb_init_type init) { /* - * If we are an OTG port, check if remote host is driving VBus and - * bail out in this case. + * If we are an OTG port initializing in host mode, + * check if remote host is driving VBus and bail out in this case. */ - if (config->dr_mode == DR_MODE_OTG && - (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) + if (init == USB_INIT_HOST && + config->dr_mode == DR_MODE_OTG && + (readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) { + printf("tegrausb: VBUS input active; not enabling as host\n"); return; + } - /* - * If not driving, we set the GPIO to enable VBUS. We assume - * that the pinmux is set up correctly for this. - */ if (fdt_gpio_isvalid(&config->vbus_gpio)) { + int vbus_value; + fdtdec_setup_gpio(&config->vbus_gpio); - gpio_direction_output(config->vbus_gpio.gpio, - (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? - 0 : 1); - debug("set_host_mode: GPIO %d %s\n", config->vbus_gpio.gpio, - (config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? - "low" : "high"); + + vbus_value = (init == USB_INIT_HOST) ^ + !!(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW); + gpio_direction_output(config->vbus_gpio.gpio, vbus_value); + + debug("set_up_vbus: GPIO %d %d\n", config->vbus_gpio.gpio, + vbus_value); } } @@ -293,10 +296,44 @@ static const unsigned *get_pll_timing(void) return timing; } +/* select the PHY to use with a USB controller */ +static void init_phy_mux(struct fdt_usb *config, uint pts, + enum usb_init_type init) +{ + struct usb_ctlr *usbctlr = config->reg; + +#if defined(CONFIG_TEGRA20) + if (config->periph_id == PERIPH_ID_USBD) { + clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK, + PTS_UTMI << PTS1_SHIFT); + clrbits_le32(&usbctlr->port_sc1, STS1); + } else { + clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, + PTS_UTMI << PTS_SHIFT); + clrbits_le32(&usbctlr->port_sc1, STS); + } +#else + /* Set to Host mode (if applicable) after Controller Reset was done */ + clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, + (init == USB_INIT_HOST) ? USBMODE_CM_HC : 0); + /* + * Select PHY interface after setting host mode. + * For device mode, the ordering requirement is not an issue, since + * only the first USB controller supports device mode, and that USB + * controller can only talk to a UTMI PHY, so the PHY selection is + * already made at reset time, so this write is a no-op. + */ + clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, + pts << PTS_SHIFT); + clrbits_le32(&usbctlr->hostpc1_devlc, STS); +#endif +} + /* set up the UTMI USB controller with the parameters provided */ -static int init_utmi_usb_controller(struct fdt_usb *config) +static int init_utmi_usb_controller(struct fdt_usb *config, + enum usb_init_type init) { - u32 val; + u32 b_sess_valid_mask, val; int loop_count; const unsigned *timing; struct usb_ctlr *usbctlr = config->reg; @@ -314,6 +351,10 @@ static int init_utmi_usb_controller(struct fdt_usb *config) /* Follow the crystal clock disable by >100ns delay */ udelay(1); + b_sess_valid_mask = (VBUS_B_SESS_VLD_SW_VALUE | VBUS_B_SESS_VLD_SW_EN); + clrsetbits_le32(&usbctlr->phy_vbus_sensors, b_sess_valid_mask, + (init == USB_INIT_DEVICE) ? b_sess_valid_mask : 0); + /* * To Use the A Session Valid for cable detection logic, VBUS_WAKEUP * mux must be switched to actually use a_sess_vld threshold. @@ -485,21 +526,7 @@ static int init_utmi_usb_controller(struct fdt_usb *config) clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1); /* Select UTMI parallel interface */ -#if defined(CONFIG_TEGRA20) - if (config->periph_id == PERIPH_ID_USBD) { - clrsetbits_le32(&usbctlr->port_sc1, PTS1_MASK, - PTS_UTMI << PTS1_SHIFT); - clrbits_le32(&usbctlr->port_sc1, STS1); - } else { - clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, - PTS_UTMI << PTS_SHIFT); - clrbits_le32(&usbctlr->port_sc1, STS); - } -#else - clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, - PTS_UTMI << PTS_SHIFT); - clrbits_le32(&usbctlr->hostpc1_devlc, STS); -#endif + init_phy_mux(config, PTS_UTMI, init); /* Deassert power down state */ clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN | @@ -529,7 +556,8 @@ static int init_utmi_usb_controller(struct fdt_usb *config) #endif /* set up the ULPI USB controller with the parameters provided */ -static int init_ulpi_usb_controller(struct fdt_usb *config) +static int init_ulpi_usb_controller(struct fdt_usb *config, + enum usb_init_type init) { u32 val; int loop_count; @@ -557,13 +585,7 @@ static int init_ulpi_usb_controller(struct fdt_usb *config) ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP); /* Select ULPI parallel interface */ -#if defined(CONFIG_TEGRA20) - clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, - PTS_ULPI << PTS_SHIFT); -#else - clrsetbits_le32(&usbctlr->hostpc1_devlc, PTS_MASK, - PTS_ULPI << PTS_SHIFT); -#endif + init_phy_mux(config, PTS_ULPI, init); /* enable ULPI transceiver */ setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB); @@ -612,7 +634,8 @@ static int init_ulpi_usb_controller(struct fdt_usb *config) return 0; } #else -static int init_ulpi_usb_controller(struct fdt_usb *config) +static int init_ulpi_usb_controller(struct fdt_usb *config, + enum usb_init_type init) { printf("No code to set up ULPI controller, please enable" "CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT"); @@ -765,42 +788,66 @@ int ehci_hcd_init(int index, enum usb_init_type init, config = &port[index]; + switch (init) { + case USB_INIT_HOST: + switch (config->dr_mode) { + case DR_MODE_HOST: + case DR_MODE_OTG: + break; + default: + printf("tegrausb: Invalid dr_mode %d for host mode\n", + config->dr_mode); + return -1; + } + break; + case USB_INIT_DEVICE: + if (config->periph_id != PERIPH_ID_USBD) { + printf("tegrausb: Device mode only supported on first USB controller\n"); + return -1; + } + if (!config->utmi) { + printf("tegrausb: Device mode only supported with UTMI PHY\n"); + return -1; + } + switch (config->dr_mode) { + case DR_MODE_DEVICE: + case DR_MODE_OTG: + break; + default: + printf("tegrausb: Invalid dr_mode %d for device mode\n", + config->dr_mode); + return -1; + } + break; + default: + printf("tegrausb: Unknown USB_INIT_* %d\n", init); + return -1; + } + /* skip init, if the port is already initialized */ - if (config->initialized) + if (config->initialized && config->init_type == init) goto success; - if (config->utmi && init_utmi_usb_controller(config)) { + if (config->utmi && init_utmi_usb_controller(config, init)) { printf("tegrausb: Cannot init port %d\n", index); return -1; } - if (config->ulpi && init_ulpi_usb_controller(config)) { + if (config->ulpi && init_ulpi_usb_controller(config, init)) { printf("tegrausb: Cannot init port %d\n", index); return -1; } - set_host_mode(config); + set_up_vbus(config, init); config->initialized = 1; + config->init_type = init; success: usbctlr = config->reg; *hccr = (struct ehci_hccr *)&usbctlr->cap_length; *hcor = (struct ehci_hcor *)&usbctlr->usb_cmd; - if (controller->has_hostpc) { - /* Set to Host mode after Controller Reset was done */ - clrsetbits_le32(&usbctlr->usb_mode, USBMODE_CM_HC, - USBMODE_CM_HC); - /* Select UTMI parallel interface after setting host mode */ - if (config->utmi) { - clrsetbits_le32((char *)&usbctlr->usb_cmd + - HOSTPC1_DEVLC, PTS_MASK, - PTS_UTMI << PTS_SHIFT); - clrbits_le32((char *)&usbctlr->usb_cmd + - HOSTPC1_DEVLC, STS); - } - } return 0; } diff --git a/drivers/usb/musb-new/musb_gadget_ep0.c b/drivers/usb/musb-new/musb_gadget_ep0.c index 6599d386dc..8c3b0a145a 100644 --- a/drivers/usb/musb-new/musb_gadget_ep0.c +++ b/drivers/usb/musb-new/musb_gadget_ep0.c @@ -576,6 +576,10 @@ static void ep0_txstate(struct musb *musb) } else request = NULL; + /* send it out, triggering a "txpktrdy cleared" irq */ + musb_ep_select(musb->mregs, 0); + musb_writew(regs, MUSB_CSR0, csr); + /* report completions as soon as the fifo's loaded; there's no * win in waiting till this last packet gets acked. (other than * very precise fault reporting, needed by USB TMC; possible with @@ -588,10 +592,6 @@ static void ep0_txstate(struct musb *musb) return; musb->ackpend = 0; } - - /* send it out, triggering a "txpktrdy cleared" irq */ - musb_ep_select(musb->mregs, 0); - musb_writew(regs, MUSB_CSR0, csr); } /* diff --git a/drivers/video/Makefile b/drivers/video/Makefile index c527029241..945f35dc5d 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_VIDEO_BCM2835) += bcm2835.o obj-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o obj-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o +obj-$(CONFIG_VIDEO_IMX25LCDC) += imx25lcdc.o videomodes.o obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o obj-$(CONFIG_VIDEO_MB86R0xGDC) += mb86r0xgdc.o videomodes.o obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o diff --git a/drivers/video/imx25lcdc.c b/drivers/video/imx25lcdc.c new file mode 100644 index 0000000000..ef5767baed --- /dev/null +++ b/drivers/video/imx25lcdc.c @@ -0,0 +1,121 @@ +/* + * (C) Copyright 2011 + * Matthias Weisser <weisserm@arcor.de> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * imx25lcdc.c - Graphic interface for i.MX25 lcd controller + */ + +#include <common.h> + +#include <malloc.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <video_fb.h> +#include "videomodes.h" + +/* + * 4MB (at the end of system RAM) + */ +#define VIDEO_MEM_SIZE 0x400000 + +#define FB_SYNC_CLK_INV (1<<16) /* pixel clock inverted */ + +/* + * Graphic Device + */ +static GraphicDevice imx25fb; + +void *video_hw_init(void) +{ + struct lcdc_regs *lcdc = (struct lcdc_regs *)IMX_LCDC_BASE; + struct ccm_regs *ccm = (struct ccm_regs *)IMX_CCM_BASE; + GraphicDevice *pGD = &imx25fb; + char *s; + u32 *videomem; + + memset(pGD, 0, sizeof(GraphicDevice)); + + pGD->gdfIndex = GDF_16BIT_565RGB; + pGD->gdfBytesPP = 2; + pGD->memSize = VIDEO_MEM_SIZE; + pGD->frameAdrs = PHYS_SDRAM + PHYS_SDRAM_SIZE - VIDEO_MEM_SIZE; + + videomem = (u32 *)pGD->frameAdrs; + + s = getenv("videomode"); + if (s != NULL) { + struct ctfb_res_modes var_mode; + u32 lsr, lpcr, lhcr, lvcr; + unsigned long div; + int bpp; + + /* Disable all clocks of the LCDC */ + writel(readl(&ccm->cgr0) & ~((1<<7) | (1<<24)), &ccm->cgr0); + writel(readl(&ccm->cgr1) & ~(1<<29), &ccm->cgr1); + + bpp = video_get_params(&var_mode, s); + + if (bpp == 0) { + var_mode.xres = 320; + var_mode.yres = 240; + var_mode.pixclock = 154000; + var_mode.left_margin = 68; + var_mode.right_margin = 20; + var_mode.upper_margin = 4; + var_mode.lower_margin = 18; + var_mode.hsync_len = 40; + var_mode.vsync_len = 6; + var_mode.sync = 0; + var_mode.vmode = 0; + } + + /* Fill memory with white */ + memset(videomem, 0xFF, var_mode.xres * var_mode.yres * 2); + + imx25fb.winSizeX = var_mode.xres; + imx25fb.winSizeY = var_mode.yres; + + /* LCD base clock is 66.6MHZ. We do calculations in kHz */ + div = 66000 / (1000000000L / var_mode.pixclock); + if (div > 63) + div = 63; + if (0 == div) + div = 1; + + lsr = ((var_mode.xres / 16) << 20) | + var_mode.yres; + lpcr = (1 << 31) | + (1 << 30) | + (5 << 25) | + (1 << 23) | + (1 << 22) | + (1 << 19) | + (1 << 7) | + div; + lhcr = (var_mode.right_margin << 0) | + (var_mode.left_margin << 8) | + (var_mode.hsync_len << 26); + + lvcr = (var_mode.lower_margin << 0) | + (var_mode.upper_margin << 8) | + (var_mode.vsync_len << 26); + + writel((uint32_t)videomem, &lcdc->lssar); + writel(lsr, &lcdc->lsr); + writel(var_mode.xres * 2 / 4, &lcdc->lvpwr); + writel(lpcr, &lcdc->lpcr); + writel(lhcr, &lcdc->lhcr); + writel(lvcr, &lcdc->lvcr); + writel(0x00040060, &lcdc->ldcr); + + writel(0xA90300, &lcdc->lpccr); + + /* Ensable all clocks of the LCDC */ + writel(readl(&ccm->cgr0) | ((1<<7) | (1<<24)), &ccm->cgr0); + writel(readl(&ccm->cgr1) | (1<<29), &ccm->cgr1); + } + + return pGD; +} |