Merge branch '2022-08-04-assorted-fixed'
[platform/kernel/u-boot.git] / drivers / dfu / dfu_sf.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
4  */
5
6 #include <common.h>
7 #include <flash.h>
8 #include <malloc.h>
9 #include <errno.h>
10 #include <div64.h>
11 #include <dfu.h>
12 #include <spi.h>
13 #include <spi_flash.h>
14 #include <jffs2/load_kernel.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/ctype.h>
17
18 static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size)
19 {
20         *size = dfu->data.sf.size;
21
22         return 0;
23 }
24
25 static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf,
26                               long *len)
27 {
28         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;
40 }
41
42 static 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
48 static int dfu_write_medium_sf(struct dfu_entity *dfu,
49                                u64 offset, void *buf, long *len)
50 {
51         int ret;
52
53         ret = spi_flash_erase(dfu->data.sf.dev,
54                               find_sector(dfu, dfu->data.sf.start, offset),
55                               dfu->data.sf.dev->sector_size);
56         if (ret)
57                 return ret;
58
59         ret = spi_flash_write(dfu->data.sf.dev, dfu->data.sf.start + offset,
60                               *len, buf);
61         if (ret)
62                 return ret;
63
64         return 0;
65 }
66
67 static int dfu_flush_medium_sf(struct dfu_entity *dfu)
68 {
69         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
83         return 0;
84 }
85
86 static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu)
87 {
88         /*
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
96         return DFU_DEFAULT_POLL_TIMEOUT;
97 }
98
99 static void dfu_free_entity_sf(struct dfu_entity *dfu)
100 {
101         /*
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         }
118 }
119
120 static 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) {
161                 printf("Failed to create SPI flash at %u:%u:%u:%u\n",
162                        bus, cs, speed, mode);
163                 return NULL;
164         }
165
166         return dev;
167 }
168
169 int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char **argv, int argc)
170 {
171         char *s;
172         char *devstr_bkup = strdup(devstr);
173
174         dfu->data.sf.dev = parse_dev(devstr_bkup);
175         free(devstr_bkup);
176         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
182         if (argc != 3)
183                 return -EINVAL;
184         if (!strcmp(argv[0], "raw")) {
185                 dfu->layout = DFU_RAW_ADDR;
186                 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;
192         } else if (CONFIG_IS_ENABLED(DFU_SF_PART) &&
193                    (!strcmp(argv[0], "part") || !strcmp(argv[0], "partubi"))) {
194                 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
202                 dev = dectoul(argv[1], &s);
203                 if (*s)
204                         return -EINVAL;
205                 part = dectoul(argv[2], &s);
206                 if (*s)
207                         return -EINVAL;
208
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;
221                 if (!strcmp(argv[0], "partubi"))
222                         dfu->data.sf.ubi = 1;
223         } else {
224                 printf("%s: Memory layout (%s) not supported!\n", __func__, argv[0]);
225                 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 }