1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Routines common to all CFI-type probes. |
4 | * (C) 2001-2003 Red Hat, Inc. |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/module.h> |
10 | #include <linux/mtd/mtd.h> |
11 | #include <linux/mtd/map.h> |
12 | #include <linux/mtd/cfi.h> |
13 | #include <linux/mtd/gen_probe.h> |
14 | |
15 | static struct mtd_info *check_cmd_set(struct map_info *, int); |
16 | static struct cfi_private *genprobe_ident_chips(struct map_info *map, |
17 | struct chip_probe *cp); |
18 | static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, |
19 | struct cfi_private *cfi); |
20 | |
21 | struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp) |
22 | { |
23 | struct mtd_info *mtd; |
24 | struct cfi_private *cfi; |
25 | |
26 | /* First probe the map to see if we have CFI stuff there. */ |
27 | cfi = genprobe_ident_chips(map, cp); |
28 | |
29 | if (!cfi) |
30 | return NULL; |
31 | |
32 | map->fldrv_priv = cfi; |
33 | /* OK we liked it. Now find a driver for the command set it talks */ |
34 | |
35 | mtd = check_cmd_set(map, 1); /* First the primary cmdset */ |
36 | if (!mtd) |
37 | mtd = check_cmd_set(map, 0); /* Then the secondary */ |
38 | |
39 | if (mtd) { |
40 | if (mtd->size > map->size) { |
41 | printk(KERN_WARNING "Reducing visibility of %ldKiB chip to %ldKiB\n" , |
42 | (unsigned long)mtd->size >> 10, |
43 | (unsigned long)map->size >> 10); |
44 | mtd->size = map->size; |
45 | } |
46 | return mtd; |
47 | } |
48 | |
49 | printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n" ); |
50 | |
51 | kfree(objp: cfi->cfiq); |
52 | kfree(objp: cfi); |
53 | map->fldrv_priv = NULL; |
54 | return NULL; |
55 | } |
56 | EXPORT_SYMBOL(mtd_do_chip_probe); |
57 | |
58 | |
59 | static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) |
60 | { |
61 | struct cfi_private cfi; |
62 | struct cfi_private *retcfi; |
63 | unsigned long *chip_map; |
64 | int max_chips; |
65 | int i, j; |
66 | |
67 | memset(&cfi, 0, sizeof(cfi)); |
68 | |
69 | /* Call the probetype-specific code with all permutations of |
70 | interleave and device type, etc. */ |
71 | if (!genprobe_new_chip(map, cp, cfi: &cfi)) { |
72 | /* The probe didn't like it */ |
73 | pr_debug("%s: Found no %s device at location zero\n" , |
74 | cp->name, map->name); |
75 | return NULL; |
76 | } |
77 | |
78 | #if 0 /* Let the CFI probe routine do this sanity check. The Intel and AMD |
79 | probe routines won't ever return a broken CFI structure anyway, |
80 | because they make them up themselves. |
81 | */ |
82 | if (cfi.cfiq->NumEraseRegions == 0) { |
83 | printk(KERN_WARNING "Number of erase regions is zero\n" ); |
84 | kfree(cfi.cfiq); |
85 | return NULL; |
86 | } |
87 | #endif |
88 | cfi.chipshift = cfi.cfiq->DevSize; |
89 | |
90 | if (cfi_interleave_is_1(&cfi)) { |
91 | ; |
92 | } else if (cfi_interleave_is_2(&cfi)) { |
93 | cfi.chipshift++; |
94 | } else if (cfi_interleave_is_4((&cfi))) { |
95 | cfi.chipshift += 2; |
96 | } else if (cfi_interleave_is_8(&cfi)) { |
97 | cfi.chipshift += 3; |
98 | } else { |
99 | BUG(); |
100 | } |
101 | |
102 | cfi.numchips = 1; |
103 | |
104 | /* |
105 | * Allocate memory for bitmap of valid chips. |
106 | * Align bitmap storage size to full byte. |
107 | */ |
108 | max_chips = map->size >> cfi.chipshift; |
109 | if (!max_chips) { |
110 | printk(KERN_WARNING "NOR chip too large to fit in mapping. Attempting to cope...\n" ); |
111 | max_chips = 1; |
112 | } |
113 | |
114 | chip_map = bitmap_zalloc(nbits: max_chips, GFP_KERNEL); |
115 | if (!chip_map) { |
116 | kfree(objp: cfi.cfiq); |
117 | return NULL; |
118 | } |
119 | |
120 | set_bit(nr: 0, addr: chip_map); /* Mark first chip valid */ |
121 | |
122 | /* |
123 | * Now probe for other chips, checking sensibly for aliases while |
124 | * we're at it. The new_chip probe above should have let the first |
125 | * chip in read mode. |
126 | */ |
127 | |
128 | for (i = 1; i < max_chips; i++) { |
129 | cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi); |
130 | } |
131 | |
132 | /* |
133 | * Now allocate the space for the structures we need to return to |
134 | * our caller, and copy the appropriate data into them. |
135 | */ |
136 | |
137 | retcfi = kmalloc(struct_size(retcfi, chips, cfi.numchips), GFP_KERNEL); |
138 | |
139 | if (!retcfi) { |
140 | kfree(objp: cfi.cfiq); |
141 | bitmap_free(bitmap: chip_map); |
142 | return NULL; |
143 | } |
144 | |
145 | memcpy(retcfi, &cfi, sizeof(cfi)); |
146 | memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips); |
147 | |
148 | for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) { |
149 | if(test_bit(i, chip_map)) { |
150 | struct flchip *pchip = &retcfi->chips[j++]; |
151 | |
152 | pchip->start = (i << cfi.chipshift); |
153 | pchip->state = FL_READY; |
154 | init_waitqueue_head(&pchip->wq); |
155 | mutex_init(&pchip->mutex); |
156 | } |
157 | } |
158 | |
159 | bitmap_free(bitmap: chip_map); |
160 | return retcfi; |
161 | } |
162 | |
163 | |
164 | static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, |
165 | struct cfi_private *cfi) |
166 | { |
167 | int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */ |
168 | int max_chips = map_bankwidth(map); /* And minimum 1 */ |
169 | int nr_chips, type; |
170 | |
171 | for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) { |
172 | |
173 | if (!cfi_interleave_supported(i: nr_chips)) |
174 | continue; |
175 | |
176 | cfi->interleave = nr_chips; |
177 | |
178 | /* Minimum device size. Don't look for one 8-bit device |
179 | in a 16-bit bus, etc. */ |
180 | type = map_bankwidth(map) / nr_chips; |
181 | |
182 | for (; type <= CFI_DEVICETYPE_X32; type<<=1) { |
183 | cfi->device_type = type; |
184 | |
185 | if (cp->probe_chip(map, 0, NULL, cfi)) |
186 | return 1; |
187 | } |
188 | } |
189 | return 0; |
190 | } |
191 | |
192 | typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int); |
193 | |
194 | extern cfi_cmdset_fn_t cfi_cmdset_0001; |
195 | extern cfi_cmdset_fn_t cfi_cmdset_0002; |
196 | extern cfi_cmdset_fn_t cfi_cmdset_0020; |
197 | |
198 | static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, |
199 | int primary) |
200 | { |
201 | struct cfi_private *cfi = map->fldrv_priv; |
202 | __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; |
203 | #ifdef CONFIG_MODULES |
204 | cfi_cmdset_fn_t *probe_function; |
205 | char *probename; |
206 | |
207 | probename = kasprintf(GFP_KERNEL, fmt: "cfi_cmdset_%4.4X" , type); |
208 | if (!probename) |
209 | return NULL; |
210 | |
211 | probe_function = __symbol_get(symbol: probename); |
212 | if (!probe_function) { |
213 | request_module("cfi_cmdset_%4.4X" , type); |
214 | probe_function = __symbol_get(symbol: probename); |
215 | } |
216 | kfree(objp: probename); |
217 | |
218 | if (probe_function) { |
219 | struct mtd_info *mtd; |
220 | |
221 | mtd = (*probe_function)(map, primary); |
222 | /* If it was happy, it'll have increased its own use count */ |
223 | symbol_put_addr(addr: probe_function); |
224 | return mtd; |
225 | } |
226 | #endif |
227 | printk(KERN_NOTICE "Support for command set %04X not present\n" , type); |
228 | |
229 | return NULL; |
230 | } |
231 | |
232 | static struct mtd_info *check_cmd_set(struct map_info *map, int primary) |
233 | { |
234 | struct cfi_private *cfi = map->fldrv_priv; |
235 | __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; |
236 | |
237 | if (type == P_ID_NONE || type == P_ID_RESERVED) |
238 | return NULL; |
239 | |
240 | switch(type){ |
241 | /* We need these for the !CONFIG_MODULES case, |
242 | because symbol_get() doesn't work there */ |
243 | #ifdef CONFIG_MTD_CFI_INTELEXT |
244 | case P_ID_INTEL_EXT: |
245 | case P_ID_INTEL_STD: |
246 | case P_ID_INTEL_PERFORMANCE: |
247 | return cfi_cmdset_0001(map, primary); |
248 | #endif |
249 | #ifdef CONFIG_MTD_CFI_AMDSTD |
250 | case P_ID_AMD_STD: |
251 | case P_ID_SST_OLD: |
252 | case P_ID_WINBOND: |
253 | return cfi_cmdset_0002(map, primary); |
254 | #endif |
255 | #ifdef CONFIG_MTD_CFI_STAA |
256 | case P_ID_ST_ADV: |
257 | return cfi_cmdset_0020(map, primary); |
258 | #endif |
259 | default: |
260 | return cfi_cmdset_unknown(map, primary); |
261 | } |
262 | } |
263 | |
264 | MODULE_LICENSE("GPL" ); |
265 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>" ); |
266 | MODULE_DESCRIPTION("Helper routines for flash chip probe code" ); |
267 | |