147f7bcaeSTom Rini /* 247f7bcaeSTom Rini * (C) Copyright 2000-2004 347f7bcaeSTom Rini * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 447f7bcaeSTom Rini * 547f7bcaeSTom Rini * (C) Copyright 2011 647f7bcaeSTom Rini * Texas Instruments, <www.ti.com> 747f7bcaeSTom Rini * 847f7bcaeSTom Rini * Matt Porter <mporter@ti.com> 947f7bcaeSTom Rini * 101a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 1147f7bcaeSTom Rini */ 1247f7bcaeSTom Rini #include <common.h> 1347f7bcaeSTom Rini #include <spl.h> 1447f7bcaeSTom Rini #include <xyzModem.h> 1547f7bcaeSTom Rini #include <asm/u-boot.h> 1647f7bcaeSTom Rini #include <asm/utils.h> 17fa715193SLokesh Vutla #include <libfdt.h> 1847f7bcaeSTom Rini 1947f7bcaeSTom Rini #define BUF_SIZE 1024 2047f7bcaeSTom Rini 21fa715193SLokesh Vutla /* 22fa715193SLokesh Vutla * Information required to load image using ymodem. 23fa715193SLokesh Vutla * 24fa715193SLokesh Vutla * @image_read: Now of bytes read from the image. 25fa715193SLokesh Vutla * @buf: pointer to the previous read block. 26fa715193SLokesh Vutla */ 27fa715193SLokesh Vutla struct ymodem_fit_info { 28fa715193SLokesh Vutla int image_read; 29fa715193SLokesh Vutla char *buf; 30fa715193SLokesh Vutla }; 31fa715193SLokesh Vutla 3247f7bcaeSTom Rini static int getcymodem(void) { 3347f7bcaeSTom Rini if (tstc()) 3447f7bcaeSTom Rini return (getc()); 3547f7bcaeSTom Rini return -1; 3647f7bcaeSTom Rini } 3747f7bcaeSTom Rini 38fa715193SLokesh Vutla static ulong ymodem_read_fit(struct spl_load_info *load, ulong offset, 39fa715193SLokesh Vutla ulong size, void *addr) 40fa715193SLokesh Vutla { 41fa715193SLokesh Vutla int res, err; 42fa715193SLokesh Vutla struct ymodem_fit_info *info = load->priv; 43fa715193SLokesh Vutla char *buf = info->buf; 44fa715193SLokesh Vutla 45fa715193SLokesh Vutla while (info->image_read < offset) { 46fa715193SLokesh Vutla res = xyzModem_stream_read(buf, BUF_SIZE, &err); 47fa715193SLokesh Vutla if (res <= 0) 48fa715193SLokesh Vutla return res; 49fa715193SLokesh Vutla info->image_read += res; 50fa715193SLokesh Vutla } 51fa715193SLokesh Vutla 52fa715193SLokesh Vutla if (info->image_read > offset) { 53fa715193SLokesh Vutla res = info->image_read - offset; 54fa715193SLokesh Vutla memcpy(addr, &buf[BUF_SIZE - res], res); 55fa715193SLokesh Vutla addr = addr + res; 56fa715193SLokesh Vutla } 57fa715193SLokesh Vutla 58fa715193SLokesh Vutla while (info->image_read < offset + size) { 59fa715193SLokesh Vutla res = xyzModem_stream_read(buf, BUF_SIZE, &err); 60fa715193SLokesh Vutla if (res <= 0) 61fa715193SLokesh Vutla return res; 62fa715193SLokesh Vutla 63fa715193SLokesh Vutla memcpy(addr, buf, res); 64fa715193SLokesh Vutla info->image_read += res; 65fa715193SLokesh Vutla addr += res; 66fa715193SLokesh Vutla } 67fa715193SLokesh Vutla 68fa715193SLokesh Vutla return size; 69fa715193SLokesh Vutla } 70fa715193SLokesh Vutla 712a2ee2acSSimon Glass static int spl_ymodem_load_image(struct spl_image_info *spl_image, 722a2ee2acSSimon Glass struct spl_boot_device *bootdev) 7347f7bcaeSTom Rini { 7447f7bcaeSTom Rini int size = 0; 7547f7bcaeSTom Rini int err; 7647f7bcaeSTom Rini int res; 7747f7bcaeSTom Rini int ret; 7847f7bcaeSTom Rini connection_info_t info; 7947f7bcaeSTom Rini char buf[BUF_SIZE]; 8047f7bcaeSTom Rini ulong addr = 0; 8147f7bcaeSTom Rini 8247f7bcaeSTom Rini info.mode = xyzModem_ymodem; 8347f7bcaeSTom Rini ret = xyzModem_stream_open(&info, &err); 84fa715193SLokesh Vutla if (ret) { 8547f7bcaeSTom Rini printf("spl: ymodem err - %s\n", xyzModem_error(err)); 8636afd451SNikita Kiryanov return ret; 8747f7bcaeSTom Rini } 8847f7bcaeSTom Rini 89fa715193SLokesh Vutla res = xyzModem_stream_read(buf, BUF_SIZE, &err); 90fa715193SLokesh Vutla if (res <= 0) 91fa715193SLokesh Vutla goto end_stream; 92fa715193SLokesh Vutla 93fa715193SLokesh Vutla if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && 94fa715193SLokesh Vutla image_get_magic((struct image_header *)buf) == FDT_MAGIC) { 95fa715193SLokesh Vutla struct spl_load_info load; 96fa715193SLokesh Vutla struct ymodem_fit_info info; 97fa715193SLokesh Vutla 98fa715193SLokesh Vutla debug("Found FIT\n"); 99fa715193SLokesh Vutla load.dev = NULL; 100fa715193SLokesh Vutla load.priv = (void *)&info; 101fa715193SLokesh Vutla load.filename = NULL; 102fa715193SLokesh Vutla load.bl_len = 1; 103fa715193SLokesh Vutla info.buf = buf; 104fa715193SLokesh Vutla info.image_read = BUF_SIZE; 105fa715193SLokesh Vutla load.read = ymodem_read_fit; 106*f4d7d859SSimon Glass ret = spl_load_simple_fit(spl_image, &load, 0, (void *)buf); 107fa715193SLokesh Vutla size = info.image_read; 108fa715193SLokesh Vutla 109fa715193SLokesh Vutla while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) 110fa715193SLokesh Vutla size += res; 111fa715193SLokesh Vutla } else { 1122a2ee2acSSimon Glass spl_parse_image_header(spl_image, (struct image_header *)buf); 1132a2ee2acSSimon Glass ret = spl_parse_image_header(spl_image, 11471316c1dSSimon Glass (struct image_header *)buf); 115fa715193SLokesh Vutla if (ret) 116fa715193SLokesh Vutla return ret; 1172a2ee2acSSimon Glass addr = spl_image->load_addr; 118fa715193SLokesh Vutla memcpy((void *)addr, buf, res); 119fa715193SLokesh Vutla size += res; 120fa715193SLokesh Vutla addr += res; 121fa715193SLokesh Vutla 122fa715193SLokesh Vutla while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) { 123fa715193SLokesh Vutla memcpy((void *)addr, buf, res); 124fa715193SLokesh Vutla size += res; 125fa715193SLokesh Vutla addr += res; 126fa715193SLokesh Vutla } 127fa715193SLokesh Vutla } 128fa715193SLokesh Vutla 129fa715193SLokesh Vutla end_stream: 13047f7bcaeSTom Rini xyzModem_stream_close(&err); 13147f7bcaeSTom Rini xyzModem_stream_terminate(false, &getcymodem); 13247f7bcaeSTom Rini 13347f7bcaeSTom Rini printf("Loaded %d bytes\n", size); 13436afd451SNikita Kiryanov return 0; 13547f7bcaeSTom Rini } 136dd6bf902SSimon Glass SPL_LOAD_IMAGE_METHOD(0, BOOT_DEVICE_UART, spl_ymodem_load_image); 137