blob: 25a9c8185037d6888ec20a98a34f88cd75188d44 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Stephen Warren6f12ebf2014-06-11 16:03:36 -06002/*
3 * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
Stephen Warren6f12ebf2014-06-11 16:03:36 -06004 */
5
6#include <common.h>
Simon Glassb79fdc72020-05-10 11:39:54 -06007#include <flash.h>
Stephen Warren6f12ebf2014-06-11 16:03:36 -06008#include <malloc.h>
9#include <errno.h>
10#include <div64.h>
11#include <dfu.h>
Simon Glass843c9e82014-10-13 23:41:55 -060012#include <spi.h>
Stephen Warren6f12ebf2014-06-11 16:03:36 -060013#include <spi_flash.h>
Patrick Delaunaycb986ba2019-10-14 09:28:00 +020014#include <jffs2/load_kernel.h>
15#include <linux/mtd/mtd.h>
Masami Hiramatsu8db74c12022-01-31 11:52:29 +090016#include <linux/ctype.h>
Stephen Warren6f12ebf2014-06-11 16:03:36 -060017
Patrick Delaunay15970d82017-07-19 16:39:23 +020018static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size)
Stephen Warren6f12ebf2014-06-11 16:03:36 -060019{
Patrick Delaunay4de51202017-07-19 16:39:22 +020020 *size = dfu->data.sf.size;
21
22 return 0;
Stephen Warren6f12ebf2014-06-11 16:03:36 -060023}
24
25static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf,
Patrick Delaunay909b6902019-10-14 09:27:58 +020026 long *len)
Stephen Warren6f12ebf2014-06-11 16:03:36 -060027{
Marek Vasutca341e92021-09-14 05:26:51 +020028 long seglen = *len;
29 int ret;
30
31 if (seglen > (16 << 20))
32 seglen = (16 << 20);
33
34 ret = spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset,
35 seglen, buf);
36 if (!ret)
37 *len = seglen;
38
39 return ret;
Stephen Warren6f12ebf2014-06-11 16:03:36 -060040}
41
Fabio Estevam2727f3b2015-09-23 00:50:39 -030042static u64 find_sector(struct dfu_entity *dfu, u64 start, u64 offset)
43{
44 return (lldiv((start + offset), dfu->data.sf.dev->sector_size)) *
45 dfu->data.sf.dev->sector_size;
46}
47
Stephen Warren6f12ebf2014-06-11 16:03:36 -060048static int dfu_write_medium_sf(struct dfu_entity *dfu,
Patrick Delaunay909b6902019-10-14 09:27:58 +020049 u64 offset, void *buf, long *len)
Stephen Warren6f12ebf2014-06-11 16:03:36 -060050{
51 int ret;
52
Fabio Estevam2727f3b2015-09-23 00:50:39 -030053 ret = spi_flash_erase(dfu->data.sf.dev,
54 find_sector(dfu, dfu->data.sf.start, offset),
Fabio Estevamf4c92582015-09-22 00:55:00 -030055 dfu->data.sf.dev->sector_size);
Stephen Warren6f12ebf2014-06-11 16:03:36 -060056 if (ret)
57 return ret;
58
Fabio Estevam2727f3b2015-09-23 00:50:39 -030059 ret = spi_flash_write(dfu->data.sf.dev, dfu->data.sf.start + offset,
60 *len, buf);
Stephen Warren6f12ebf2014-06-11 16:03:36 -060061 if (ret)
62 return ret;
63
64 return 0;
65}
66
67static int dfu_flush_medium_sf(struct dfu_entity *dfu)
68{
Patrick Delaunaycb986ba2019-10-14 09:28:00 +020069 u64 off, length;
70
71 if (!CONFIG_IS_ENABLED(DFU_SF_PART) || !dfu->data.sf.ubi)
72 return 0;
73
74 /* in case of ubi partition, erase rest of the partition */
75 off = find_sector(dfu, dfu->data.sf.start, dfu->offset);
76 /* last write ended with unaligned length jump to next */
77 if (off != dfu->data.sf.start + dfu->offset)
78 off += dfu->data.sf.dev->sector_size;
79 length = dfu->data.sf.start + dfu->data.sf.size - off;
80 if (length)
81 return spi_flash_erase(dfu->data.sf.dev, off, length);
82
Stephen Warren6f12ebf2014-06-11 16:03:36 -060083 return 0;
84}
85
86static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu)
87{
Patrick Delaunaycb986ba2019-10-14 09:28:00 +020088 /*
89 * Currently, Poll Timeout != 0 is only needed on nand
90 * ubi partition, as sectors which are not used need
91 * to be erased
92 */
93 if (CONFIG_IS_ENABLED(DFU_SF_PART) && dfu->data.sf.ubi)
94 return DFU_MANIFEST_POLL_TIMEOUT;
95
Stephen Warren6f12ebf2014-06-11 16:03:36 -060096 return DFU_DEFAULT_POLL_TIMEOUT;
97}
98
99static void dfu_free_entity_sf(struct dfu_entity *dfu)
100{
Heinrich Schuchardt57cba222021-03-18 07:46:07 +0100101 /*
102 * In the DM case it is not necessary to free the SPI device.
103 * For the non-DM case we must ensure that the the SPI device is only
104 * freed once.
105 */
106 if (!CONFIG_IS_ENABLED(DM_SPI_FLASH)) {
107 struct spi_flash *dev = dfu->data.sf.dev;
108
109 if (!dev)
110 return;
111 dfu->data.sf.dev = NULL;
112 list_for_each_entry(dfu, &dfu_list, list) {
113 if (dfu->data.sf.dev == dev)
114 dfu->data.sf.dev = NULL;
115 }
116 spi_flash_free(dev);
117 }
Stephen Warren6f12ebf2014-06-11 16:03:36 -0600118}
119
120static struct spi_flash *parse_dev(char *devstr)
121{
122 unsigned int bus;
123 unsigned int cs;
124 unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
125 unsigned int mode = CONFIG_SF_DEFAULT_MODE;
126 char *s, *endp;
127 struct spi_flash *dev;
128
129 s = strsep(&devstr, ":");
130 if (!s || !*s || (bus = simple_strtoul(s, &endp, 0), *endp)) {
131 printf("Invalid SPI bus %s\n", s);
132 return NULL;
133 }
134
135 s = strsep(&devstr, ":");
136 if (!s || !*s || (cs = simple_strtoul(s, &endp, 0), *endp)) {
137 printf("Invalid SPI chip-select %s\n", s);
138 return NULL;
139 }
140
141 s = strsep(&devstr, ":");
142 if (s && *s) {
143 speed = simple_strtoul(s, &endp, 0);
144 if (*endp || !speed) {
145 printf("Invalid SPI speed %s\n", s);
146 return NULL;
147 }
148 }
149
150 s = strsep(&devstr, ":");
151 if (s && *s) {
152 mode = simple_strtoul(s, &endp, 0);
153 if (*endp || mode > 3) {
154 printf("Invalid SPI mode %s\n", s);
155 return NULL;
156 }
157 }
158
159 dev = spi_flash_probe(bus, cs, speed, mode);
160 if (!dev) {
Heinrich Schuchardta045bc12020-12-27 09:58:05 +0100161 printf("Failed to create SPI flash at %u:%u:%u:%u\n",
Stephen Warren6f12ebf2014-06-11 16:03:36 -0600162 bus, cs, speed, mode);
163 return NULL;
164 }
165
166 return dev;
167}
168
Masami Hiramatsu53b40632022-01-31 11:52:37 +0900169int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char **argv, int argc)
Stephen Warren6f12ebf2014-06-11 16:03:36 -0600170{
Masami Hiramatsu53b40632022-01-31 11:52:37 +0900171 char *s;
Vignesh R30e3ea42015-10-20 15:22:00 +0530172 char *devstr_bkup = strdup(devstr);
Stephen Warren6f12ebf2014-06-11 16:03:36 -0600173
Vignesh R30e3ea42015-10-20 15:22:00 +0530174 dfu->data.sf.dev = parse_dev(devstr_bkup);
175 free(devstr_bkup);
Stephen Warren6f12ebf2014-06-11 16:03:36 -0600176 if (!dfu->data.sf.dev)
177 return -ENODEV;
178
179 dfu->dev_type = DFU_DEV_SF;
180 dfu->max_buf_size = dfu->data.sf.dev->sector_size;
181
Masami Hiramatsu53b40632022-01-31 11:52:37 +0900182 if (argc != 3)
183 return -EINVAL;
184 if (!strcmp(argv[0], "raw")) {
Stephen Warren6f12ebf2014-06-11 16:03:36 -0600185 dfu->layout = DFU_RAW_ADDR;
Masami Hiramatsu53b40632022-01-31 11:52:37 +0900186 dfu->data.sf.start = hextoul(argv[1], &s);
187 if (*s)
188 return -EINVAL;
189 dfu->data.sf.size = hextoul(argv[2], &s);
190 if (*s)
191 return -EINVAL;
Patrick Delaunaycb986ba2019-10-14 09:28:00 +0200192 } else if (CONFIG_IS_ENABLED(DFU_SF_PART) &&
Masami Hiramatsu53b40632022-01-31 11:52:37 +0900193 (!strcmp(argv[0], "part") || !strcmp(argv[0], "partubi"))) {
Patrick Delaunaycb986ba2019-10-14 09:28:00 +0200194 char mtd_id[32];
195 struct mtd_device *mtd_dev;
196 u8 part_num;
197 struct part_info *pi;
198 int ret, dev, part;
199
200 dfu->layout = DFU_RAW_ADDR;
201
Masami Hiramatsu53b40632022-01-31 11:52:37 +0900202 dev = dectoul(argv[1], &s);
203 if (*s)
204 return -EINVAL;
205 part = dectoul(argv[2], &s);
206 if (*s)
207 return -EINVAL;
Patrick Delaunaycb986ba2019-10-14 09:28:00 +0200208
209 sprintf(mtd_id, "%s%d,%d", "nor", dev, part - 1);
210 printf("using id '%s'\n", mtd_id);
211
212 mtdparts_init();
213
214 ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
215 if (ret != 0) {
216 printf("Could not locate '%s'\n", mtd_id);
217 return -1;
218 }
219 dfu->data.sf.start = pi->offset;
220 dfu->data.sf.size = pi->size;
Masami Hiramatsu53b40632022-01-31 11:52:37 +0900221 if (!strcmp(argv[0], "partubi"))
Patrick Delaunaycb986ba2019-10-14 09:28:00 +0200222 dfu->data.sf.ubi = 1;
Stephen Warren6f12ebf2014-06-11 16:03:36 -0600223 } else {
Masami Hiramatsu53b40632022-01-31 11:52:37 +0900224 printf("%s: Memory layout (%s) not supported!\n", __func__, argv[0]);
Stephen Warren6f12ebf2014-06-11 16:03:36 -0600225 spi_flash_free(dfu->data.sf.dev);
226 return -1;
227 }
228
229 dfu->get_medium_size = dfu_get_medium_size_sf;
230 dfu->read_medium = dfu_read_medium_sf;
231 dfu->write_medium = dfu_write_medium_sf;
232 dfu->flush_medium = dfu_flush_medium_sf;
233 dfu->poll_timeout = dfu_polltimeout_sf;
234 dfu->free_entity = dfu_free_entity_sf;
235
236 /* initial state */
237 dfu->inited = 0;
238
239 return 0;
240}