Merge branch '2022-08-04-Kconfig-migrations'
[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 <malloc.h>
8 #include <errno.h>
9 #include <div64.h>
10 #include <dfu.h>
11 #include <spi.h>
12 #include <spi_flash.h>
13 #include <jffs2/load_kernel.h>
14 #include <linux/mtd/mtd.h>
15 #include <linux/ctype.h>
16
17 static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size)
18 {
19         *size = dfu->data.sf.size;
20
21         return 0;
22 }
23
24 static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf,
25                               long *len)
26 {
27         long seglen = *len;
28         int ret;
29
30         if (seglen > (16 << 20))
31                 seglen = (16 << 20);
32
33         ret = spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset,
34                 seglen, buf);
35         if (!ret)
36                 *len = seglen;
37
38         return ret;
39 }
40
41 static u64 find_sector(struct dfu_entity *dfu, u64 start, u64 offset)
42 {
43         return (lldiv((start + offset), dfu->data.sf.dev->sector_size)) *
44                 dfu->data.sf.dev->sector_size;
45 }
46
47 static int dfu_write_medium_sf(struct dfu_entity *dfu,
48                                u64 offset, void *buf, long *len)
49 {
50         int ret;
51
52         ret = spi_flash_erase(dfu->data.sf.dev,
53                               find_sector(dfu, dfu->data.sf.start, offset),
54                               dfu->data.sf.dev->sector_size);
55         if (ret)
56                 return ret;
57
58         ret = spi_flash_write(dfu->data.sf.dev, dfu->data.sf.start + offset,
59                               *len, buf);
60         if (ret)
61                 return ret;
62
63         return 0;
64 }
65
66 static int dfu_flush_medium_sf(struct dfu_entity *dfu)
67 {
68         u64 off, length;
69
70         if (!CONFIG_IS_ENABLED(DFU_SF_PART) || !dfu->data.sf.ubi)
71                 return 0;
72
73         /* in case of ubi partition, erase rest of the partition */
74         off = find_sector(dfu, dfu->data.sf.start, dfu->offset);
75         /* last write ended with unaligned length jump to next */
76         if (off != dfu->data.sf.start + dfu->offset)
77                 off += dfu->data.sf.dev->sector_size;
78         length = dfu->data.sf.start + dfu->data.sf.size - off;
79         if (length)
80                 return spi_flash_erase(dfu->data.sf.dev, off, length);
81
82         return 0;
83 }
84
85 static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu)
86 {
87         /*
88          * Currently, Poll Timeout != 0 is only needed on nand
89          * ubi partition, as sectors which are not used need
90          * to be erased
91          */
92         if (CONFIG_IS_ENABLED(DFU_SF_PART) && dfu->data.sf.ubi)
93                 return DFU_MANIFEST_POLL_TIMEOUT;
94
95         return DFU_DEFAULT_POLL_TIMEOUT;
96 }
97
98 static void dfu_free_entity_sf(struct dfu_entity *dfu)
99 {
100         /*
101          * In the DM case it is not necessary to free the SPI device.
102          * For the non-DM case we must ensure that the the SPI device is only
103          * freed once.
104          */
105         if (!CONFIG_IS_ENABLED(DM_SPI_FLASH)) {
106                 struct spi_flash *dev = dfu->data.sf.dev;
107
108                 if (!dev)
109                         return;
110                 dfu->data.sf.dev = NULL;
111                 list_for_each_entry(dfu, &dfu_list, list) {
112                         if (dfu->data.sf.dev == dev)
113                                 dfu->data.sf.dev = NULL;
114                 }
115                 spi_flash_free(dev);
116         }
117 }
118
119 static struct spi_flash *parse_dev(char *devstr)
120 {
121         unsigned int bus;
122         unsigned int cs;
123         unsigned int speed = CONFIG_SF_DEFAULT_SPEED;
124         unsigned int mode = CONFIG_SF_DEFAULT_MODE;
125         char *s, *endp;
126         struct spi_flash *dev;
127
128         s = strsep(&devstr, ":");
129         if (!s || !*s || (bus = simple_strtoul(s, &endp, 0), *endp)) {
130                 printf("Invalid SPI bus %s\n", s);
131                 return NULL;
132         }
133
134         s = strsep(&devstr, ":");
135         if (!s || !*s || (cs = simple_strtoul(s, &endp, 0), *endp)) {
136                 printf("Invalid SPI chip-select %s\n", s);
137                 return NULL;
138         }
139
140         s = strsep(&devstr, ":");
141         if (s && *s) {
142                 speed = simple_strtoul(s, &endp, 0);
143                 if (*endp || !speed) {
144                         printf("Invalid SPI speed %s\n", s);
145                         return NULL;
146                 }
147         }
148
149         s = strsep(&devstr, ":");
150         if (s && *s) {
151                 mode = simple_strtoul(s, &endp, 0);
152                 if (*endp || mode > 3) {
153                         printf("Invalid SPI mode %s\n", s);
154                         return NULL;
155                 }
156         }
157
158         dev = spi_flash_probe(bus, cs, speed, mode);
159         if (!dev) {
160                 printf("Failed to create SPI flash at %u:%u:%u:%u\n",
161                        bus, cs, speed, mode);
162                 return NULL;
163         }
164
165         return dev;
166 }
167
168 int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char **argv, int argc)
169 {
170         char *s;
171         char *devstr_bkup = strdup(devstr);
172
173         dfu->data.sf.dev = parse_dev(devstr_bkup);
174         free(devstr_bkup);
175         if (!dfu->data.sf.dev)
176                 return -ENODEV;
177
178         dfu->dev_type = DFU_DEV_SF;
179         dfu->max_buf_size = dfu->data.sf.dev->sector_size;
180
181         if (argc != 3)
182                 return -EINVAL;
183         if (!strcmp(argv[0], "raw")) {
184                 dfu->layout = DFU_RAW_ADDR;
185                 dfu->data.sf.start = hextoul(argv[1], &s);
186                 if (*s)
187                         return -EINVAL;
188                 dfu->data.sf.size = hextoul(argv[2], &s);
189                 if (*s)
190                         return -EINVAL;
191         } else if (CONFIG_IS_ENABLED(DFU_SF_PART) &&
192                    (!strcmp(argv[0], "part") || !strcmp(argv[0], "partubi"))) {
193                 char mtd_id[32];
194                 struct mtd_device *mtd_dev;
195                 u8 part_num;
196                 struct part_info *pi;
197                 int ret, dev, part;
198
199                 dfu->layout = DFU_RAW_ADDR;
200
201                 dev = dectoul(argv[1], &s);
202                 if (*s)
203                         return -EINVAL;
204                 part = dectoul(argv[2], &s);
205                 if (*s)
206                         return -EINVAL;
207
208                 sprintf(mtd_id, "%s%d,%d", "nor", dev, part - 1);
209                 printf("using id '%s'\n", mtd_id);
210
211                 mtdparts_init();
212
213                 ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
214                 if (ret != 0) {
215                         printf("Could not locate '%s'\n", mtd_id);
216                         return -1;
217                 }
218                 dfu->data.sf.start = pi->offset;
219                 dfu->data.sf.size = pi->size;
220                 if (!strcmp(argv[0], "partubi"))
221                         dfu->data.sf.ubi = 1;
222         } else {
223                 printf("%s: Memory layout (%s) not supported!\n", __func__, argv[0]);
224                 spi_flash_free(dfu->data.sf.dev);
225                 return -1;
226         }
227
228         dfu->get_medium_size = dfu_get_medium_size_sf;
229         dfu->read_medium = dfu_read_medium_sf;
230         dfu->write_medium = dfu_write_medium_sf;
231         dfu->flush_medium = dfu_flush_medium_sf;
232         dfu->poll_timeout = dfu_polltimeout_sf;
233         dfu->free_entity = dfu_free_entity_sf;
234
235         /* initial state */
236         dfu->inited = 0;
237
238         return 0;
239 }