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>
17*0e00a84cSMasahiro Yamada #include <linux/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
getcymodem(void)3247f7bcaeSTom Rini static int getcymodem(void) {
3347f7bcaeSTom Rini if (tstc())
3447f7bcaeSTom Rini return (getc());
3547f7bcaeSTom Rini return -1;
3647f7bcaeSTom Rini }
3747f7bcaeSTom Rini
ymodem_read_fit(struct spl_load_info * load,ulong offset,ulong size,void * addr)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
spl_ymodem_load_image(struct spl_image_info * spl_image,struct spl_boot_device * bootdev)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;
106f4d7d859SSimon 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 ret = spl_parse_image_header(spl_image,
11371316c1dSSimon Glass (struct image_header *)buf);
114fa715193SLokesh Vutla if (ret)
115fa715193SLokesh Vutla return ret;
1162a2ee2acSSimon Glass addr = spl_image->load_addr;
117fa715193SLokesh Vutla memcpy((void *)addr, buf, res);
118fa715193SLokesh Vutla size += res;
119fa715193SLokesh Vutla addr += res;
120fa715193SLokesh Vutla
121fa715193SLokesh Vutla while ((res = xyzModem_stream_read(buf, BUF_SIZE, &err)) > 0) {
122fa715193SLokesh Vutla memcpy((void *)addr, buf, res);
123fa715193SLokesh Vutla size += res;
124fa715193SLokesh Vutla addr += res;
125fa715193SLokesh Vutla }
126fa715193SLokesh Vutla }
127fa715193SLokesh Vutla
128fa715193SLokesh Vutla end_stream:
12947f7bcaeSTom Rini xyzModem_stream_close(&err);
13047f7bcaeSTom Rini xyzModem_stream_terminate(false, &getcymodem);
13147f7bcaeSTom Rini
13247f7bcaeSTom Rini printf("Loaded %d bytes\n", size);
13336afd451SNikita Kiryanov return 0;
13447f7bcaeSTom Rini }
135ebc4ef61SSimon Glass SPL_LOAD_IMAGE_METHOD("UART", 0, BOOT_DEVICE_UART, spl_ymodem_load_image);
136