1 /* 2 * (C) Copyright 2000-2004 3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 4 * 5 * (C) Copyright 2011 6 * Texas Instruments, <www.ti.com> 7 * 8 * Matt Porter <mporter@ti.com> 9 * 10 * SPDX-License-Identifier: GPL-2.0+ 11 */ 12 #include <common.h> 13 #include <spl.h> 14 #include <xyzModem.h> 15 #include <asm/u-boot.h> 16 #include <asm/utils.h> 17 #include <linux/libfdt.h> 18 19 #define BUF_SIZE 1024 20 21 /* 22 * Information required to load image using ymodem. 23 * 24 * @image_read: Now of bytes read from the image. 25 * @buf: pointer to the previous read block. 26 */ 27 struct ymodem_fit_info { 28 int image_read; 29 char *buf; 30 }; 31 32 static int getcymodem(void) { 33 if (tstc()) 34 return (getc()); 35 return -1; 36 } 37 38 static ulong ymodem_read_fit(struct spl_load_info *load, ulong offset, 39 ulong size, void *addr) 40 { 41 int res, err; 42 struct ymodem_fit_info *info = load->priv; 43 char *buf = info->buf; 44 45 while (info->image_read < offset) { 46 res = xyzModem_stream_read(buf, BUF_SIZE, &err); 47 if (res <= 0) 48 return res; 49 info->image_read += res; 50 } 51 52 if (info->image_read > offset) { 53 res = info->image_read - offset; 54 memcpy(addr, &buf[BUF_SIZE - res], res); 55 addr = addr + res; 56 } 57 58 while (info->image_read < offset + size) { 59 res = xyzModem_stream_read(buf, BUF_SIZE, &err); 60 if (res <= 0) 61 return res; 62 63 memcpy(addr, buf, res); 64 info->image_read += res; 65 addr += res; 66 } 67 68 return size; 69 } 70 71 static int spl_ymodem_load_image(struct spl_image_info *spl_image, 72 struct spl_boot_device *bootdev) 73 { 74 int size = 0; 75 int err; 76 int res; 77 int ret; 78 connection_info_t info; 79 char buf[BUF_SIZE]; 80 ulong addr = 0; 81 82 info.mode = xyzModem_ymodem; 83 ret = xyzModem_stream_open(&info, &err); 84 if (ret) { 85 printf("spl: ymodem err - %s\n", xyzModem_error(err)); 86 return ret; 87 } 88 89 res = xyzModem_stream_read(buf, BUF_SIZE, &err); 90 if (res <= 0) 91 goto end_stream; 92 93 if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && 94 image_get_magic((struct image_header *)buf) == FDT_MAGIC) { 95 struct spl_load_info load; 96 struct ymodem_fit_info info; 97 98 debug("Found FIT\n"); 99 load.dev = NULL; 100 load.priv = (void *)&info; 101 load.filename = NULL; 102 load.bl_len = 1; 103 info.buf = buf; 104 info.image_read = BUF_SIZE; 105 load.read = ymodem_read_fit; 106 ret = spl_load_simple_fit(spl_image, &load, 0, (void *)buf); 107 size = info.image_read; 108 109 while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) 110 size += res; 111 } else { 112 ret = spl_parse_image_header(spl_image, 113 (struct image_header *)buf); 114 if (ret) 115 return ret; 116 addr = spl_image->load_addr; 117 memcpy((void *)addr, buf, res); 118 size += res; 119 addr += res; 120 121 while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) { 122 memcpy((void *)addr, buf, res); 123 size += res; 124 addr += res; 125 } 126 } 127 128 end_stream: 129 xyzModem_stream_close(&err); 130 xyzModem_stream_terminate(false, &getcymodem); 131 132 printf("Loaded %d bytes\n", size); 133 return 0; 134 } 135 SPL_LOAD_IMAGE_METHOD("UART", 0, BOOT_DEVICE_UART, spl_ymodem_load_image); 136