]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
6f12ebf6 SW |
2 | /* |
3 | * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. | |
6f12ebf6 SW |
4 | */ |
5 | ||
6 | #include <common.h> | |
b79fdc76 | 7 | #include <flash.h> |
6f12ebf6 SW |
8 | #include <malloc.h> |
9 | #include <errno.h> | |
10 | #include <div64.h> | |
11 | #include <dfu.h> | |
843c9e87 | 12 | #include <spi.h> |
6f12ebf6 | 13 | #include <spi_flash.h> |
cb986ba0 PD |
14 | #include <jffs2/load_kernel.h> |
15 | #include <linux/mtd/mtd.h> | |
6f12ebf6 | 16 | |
15970d87 | 17 | static int dfu_get_medium_size_sf(struct dfu_entity *dfu, u64 *size) |
6f12ebf6 | 18 | { |
4de51201 PD |
19 | *size = dfu->data.sf.size; |
20 | ||
21 | return 0; | |
6f12ebf6 SW |
22 | } |
23 | ||
24 | static int dfu_read_medium_sf(struct dfu_entity *dfu, u64 offset, void *buf, | |
909b690b | 25 | long *len) |
6f12ebf6 | 26 | { |
e5f00f01 PE |
27 | return spi_flash_read(dfu->data.sf.dev, dfu->data.sf.start + offset, |
28 | *len, buf); | |
6f12ebf6 SW |
29 | } |
30 | ||
2727f3bf FE |
31 | static u64 find_sector(struct dfu_entity *dfu, u64 start, u64 offset) |
32 | { | |
33 | return (lldiv((start + offset), dfu->data.sf.dev->sector_size)) * | |
34 | dfu->data.sf.dev->sector_size; | |
35 | } | |
36 | ||
6f12ebf6 | 37 | static int dfu_write_medium_sf(struct dfu_entity *dfu, |
909b690b | 38 | u64 offset, void *buf, long *len) |
6f12ebf6 SW |
39 | { |
40 | int ret; | |
41 | ||
2727f3bf FE |
42 | ret = spi_flash_erase(dfu->data.sf.dev, |
43 | find_sector(dfu, dfu->data.sf.start, offset), | |
f4c92582 | 44 | dfu->data.sf.dev->sector_size); |
6f12ebf6 SW |
45 | if (ret) |
46 | return ret; | |
47 | ||
2727f3bf FE |
48 | ret = spi_flash_write(dfu->data.sf.dev, dfu->data.sf.start + offset, |
49 | *len, buf); | |
6f12ebf6 SW |
50 | if (ret) |
51 | return ret; | |
52 | ||
53 | return 0; | |
54 | } | |
55 | ||
56 | static int dfu_flush_medium_sf(struct dfu_entity *dfu) | |
57 | { | |
cb986ba0 PD |
58 | u64 off, length; |
59 | ||
60 | if (!CONFIG_IS_ENABLED(DFU_SF_PART) || !dfu->data.sf.ubi) | |
61 | return 0; | |
62 | ||
63 | /* in case of ubi partition, erase rest of the partition */ | |
64 | off = find_sector(dfu, dfu->data.sf.start, dfu->offset); | |
65 | /* last write ended with unaligned length jump to next */ | |
66 | if (off != dfu->data.sf.start + dfu->offset) | |
67 | off += dfu->data.sf.dev->sector_size; | |
68 | length = dfu->data.sf.start + dfu->data.sf.size - off; | |
69 | if (length) | |
70 | return spi_flash_erase(dfu->data.sf.dev, off, length); | |
71 | ||
6f12ebf6 SW |
72 | return 0; |
73 | } | |
74 | ||
75 | static unsigned int dfu_polltimeout_sf(struct dfu_entity *dfu) | |
76 | { | |
cb986ba0 PD |
77 | /* |
78 | * Currently, Poll Timeout != 0 is only needed on nand | |
79 | * ubi partition, as sectors which are not used need | |
80 | * to be erased | |
81 | */ | |
82 | if (CONFIG_IS_ENABLED(DFU_SF_PART) && dfu->data.sf.ubi) | |
83 | return DFU_MANIFEST_POLL_TIMEOUT; | |
84 | ||
6f12ebf6 SW |
85 | return DFU_DEFAULT_POLL_TIMEOUT; |
86 | } | |
87 | ||
88 | static void dfu_free_entity_sf(struct dfu_entity *dfu) | |
89 | { | |
90 | spi_flash_free(dfu->data.sf.dev); | |
91 | } | |
92 | ||
93 | static struct spi_flash *parse_dev(char *devstr) | |
94 | { | |
95 | unsigned int bus; | |
96 | unsigned int cs; | |
97 | unsigned int speed = CONFIG_SF_DEFAULT_SPEED; | |
98 | unsigned int mode = CONFIG_SF_DEFAULT_MODE; | |
99 | char *s, *endp; | |
100 | struct spi_flash *dev; | |
101 | ||
102 | s = strsep(&devstr, ":"); | |
103 | if (!s || !*s || (bus = simple_strtoul(s, &endp, 0), *endp)) { | |
104 | printf("Invalid SPI bus %s\n", s); | |
105 | return NULL; | |
106 | } | |
107 | ||
108 | s = strsep(&devstr, ":"); | |
109 | if (!s || !*s || (cs = simple_strtoul(s, &endp, 0), *endp)) { | |
110 | printf("Invalid SPI chip-select %s\n", s); | |
111 | return NULL; | |
112 | } | |
113 | ||
114 | s = strsep(&devstr, ":"); | |
115 | if (s && *s) { | |
116 | speed = simple_strtoul(s, &endp, 0); | |
117 | if (*endp || !speed) { | |
118 | printf("Invalid SPI speed %s\n", s); | |
119 | return NULL; | |
120 | } | |
121 | } | |
122 | ||
123 | s = strsep(&devstr, ":"); | |
124 | if (s && *s) { | |
125 | mode = simple_strtoul(s, &endp, 0); | |
126 | if (*endp || mode > 3) { | |
127 | printf("Invalid SPI mode %s\n", s); | |
128 | return NULL; | |
129 | } | |
130 | } | |
131 | ||
132 | dev = spi_flash_probe(bus, cs, speed, mode); | |
133 | if (!dev) { | |
134 | printf("Failed to create SPI flash at %d:%d:%d:%d\n", | |
135 | bus, cs, speed, mode); | |
136 | return NULL; | |
137 | } | |
138 | ||
139 | return dev; | |
140 | } | |
141 | ||
142 | int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr, char *s) | |
143 | { | |
144 | char *st; | |
30e3ea4c | 145 | char *devstr_bkup = strdup(devstr); |
6f12ebf6 | 146 | |
30e3ea4c V |
147 | dfu->data.sf.dev = parse_dev(devstr_bkup); |
148 | free(devstr_bkup); | |
6f12ebf6 SW |
149 | if (!dfu->data.sf.dev) |
150 | return -ENODEV; | |
151 | ||
152 | dfu->dev_type = DFU_DEV_SF; | |
153 | dfu->max_buf_size = dfu->data.sf.dev->sector_size; | |
154 | ||
155 | st = strsep(&s, " "); | |
156 | if (!strcmp(st, "raw")) { | |
157 | dfu->layout = DFU_RAW_ADDR; | |
158 | dfu->data.sf.start = simple_strtoul(s, &s, 16); | |
159 | s++; | |
160 | dfu->data.sf.size = simple_strtoul(s, &s, 16); | |
cb986ba0 PD |
161 | } else if (CONFIG_IS_ENABLED(DFU_SF_PART) && |
162 | (!strcmp(st, "part") || !strcmp(st, "partubi"))) { | |
163 | char mtd_id[32]; | |
164 | struct mtd_device *mtd_dev; | |
165 | u8 part_num; | |
166 | struct part_info *pi; | |
167 | int ret, dev, part; | |
168 | ||
169 | dfu->layout = DFU_RAW_ADDR; | |
170 | ||
171 | dev = simple_strtoul(s, &s, 10); | |
172 | s++; | |
173 | part = simple_strtoul(s, &s, 10); | |
174 | ||
175 | sprintf(mtd_id, "%s%d,%d", "nor", dev, part - 1); | |
176 | printf("using id '%s'\n", mtd_id); | |
177 | ||
178 | mtdparts_init(); | |
179 | ||
180 | ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); | |
181 | if (ret != 0) { | |
182 | printf("Could not locate '%s'\n", mtd_id); | |
183 | return -1; | |
184 | } | |
185 | dfu->data.sf.start = pi->offset; | |
186 | dfu->data.sf.size = pi->size; | |
187 | if (!strcmp(st, "partubi")) | |
188 | dfu->data.sf.ubi = 1; | |
6f12ebf6 SW |
189 | } else { |
190 | printf("%s: Memory layout (%s) not supported!\n", __func__, st); | |
191 | spi_flash_free(dfu->data.sf.dev); | |
192 | return -1; | |
193 | } | |
194 | ||
195 | dfu->get_medium_size = dfu_get_medium_size_sf; | |
196 | dfu->read_medium = dfu_read_medium_sf; | |
197 | dfu->write_medium = dfu_write_medium_sf; | |
198 | dfu->flush_medium = dfu_flush_medium_sf; | |
199 | dfu->poll_timeout = dfu_polltimeout_sf; | |
200 | dfu->free_entity = dfu_free_entity_sf; | |
201 | ||
202 | /* initial state */ | |
203 | dfu->inited = 0; | |
204 | ||
205 | return 0; | |
206 | } |