blob: c947d950232aca460daa5de7ce166bd7a6b172dc [file] [log] [blame]
Simon Glasscceee552016-02-29 15:25:55 -07001/*
2 * Copyright (C) 2016 Google, Inc
3 * Written by Simon Glass <sjg@chromium.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 */
7
8#include <common.h>
9#include <blk.h>
10#include <dm.h>
11#include <dm/device-internal.h>
12#include <dm/lists.h>
13
Simon Glass4131ad52016-05-01 11:36:08 -060014static const char *if_typename_str[IF_TYPE_COUNT] = {
15 [IF_TYPE_IDE] = "ide",
16 [IF_TYPE_SCSI] = "scsi",
17 [IF_TYPE_ATAPI] = "atapi",
18 [IF_TYPE_USB] = "usb",
19 [IF_TYPE_DOC] = "doc",
20 [IF_TYPE_MMC] = "mmc",
21 [IF_TYPE_SD] = "sd",
22 [IF_TYPE_SATA] = "sata",
23 [IF_TYPE_HOST] = "host",
24 [IF_TYPE_SYSTEMACE] = "ace",
25};
26
27static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
28 [IF_TYPE_IDE] = UCLASS_INVALID,
29 [IF_TYPE_SCSI] = UCLASS_INVALID,
30 [IF_TYPE_ATAPI] = UCLASS_INVALID,
31 [IF_TYPE_USB] = UCLASS_MASS_STORAGE,
32 [IF_TYPE_DOC] = UCLASS_INVALID,
33 [IF_TYPE_MMC] = UCLASS_MMC,
34 [IF_TYPE_SD] = UCLASS_INVALID,
35 [IF_TYPE_SATA] = UCLASS_AHCI,
36 [IF_TYPE_HOST] = UCLASS_ROOT,
37 [IF_TYPE_SYSTEMACE] = UCLASS_INVALID,
38};
39
40static enum if_type if_typename_to_iftype(const char *if_typename)
41{
42 int i;
43
44 for (i = 0; i < IF_TYPE_COUNT; i++) {
45 if (if_typename_str[i] &&
46 !strcmp(if_typename, if_typename_str[i]))
47 return i;
48 }
49
50 return IF_TYPE_UNKNOWN;
51}
52
53static enum uclass_id if_type_to_uclass_id(enum if_type if_type)
54{
55 return if_type_uclass_id[if_type];
56}
57
58struct blk_desc *blk_get_devnum_by_type(enum if_type if_type, int devnum)
59{
60 struct blk_desc *desc;
61 struct udevice *dev;
62 int ret;
63
64 ret = blk_get_device(if_type, devnum, &dev);
65 if (ret)
66 return NULL;
67 desc = dev_get_uclass_platdata(dev);
68
69 return desc;
70}
71
72/*
73 * This function is complicated with driver model. We look up the interface
74 * name in a local table. This gives us an interface type which we can match
75 * against the uclass of the block device's parent.
76 */
77struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum)
78{
79 enum uclass_id uclass_id;
80 enum if_type if_type;
81 struct udevice *dev;
82 struct uclass *uc;
83 int ret;
84
85 if_type = if_typename_to_iftype(if_typename);
86 if (if_type == IF_TYPE_UNKNOWN) {
87 debug("%s: Unknown interface type '%s'\n", __func__,
88 if_typename);
89 return NULL;
90 }
91 uclass_id = if_type_to_uclass_id(if_type);
92 if (uclass_id == UCLASS_INVALID) {
93 debug("%s: Unknown uclass for interface type'\n",
94 if_typename_str[if_type]);
95 return NULL;
96 }
97
98 ret = uclass_get(UCLASS_BLK, &uc);
99 if (ret)
100 return NULL;
101 uclass_foreach_dev(dev, uc) {
102 struct blk_desc *desc = dev_get_uclass_platdata(dev);
103
104 debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
105 if_type, devnum, dev->name, desc->if_type, desc->devnum);
106 if (desc->devnum != devnum)
107 continue;
108
109 /* Find out the parent device uclass */
110 if (device_get_uclass_id(dev->parent) != uclass_id) {
111 debug("%s: parent uclass %d, this dev %d\n", __func__,
112 device_get_uclass_id(dev->parent), uclass_id);
113 continue;
114 }
115
116 if (device_probe(dev))
117 return NULL;
118
119 debug("%s: Device desc %p\n", __func__, desc);
120 return desc;
121 }
122 debug("%s: No device found\n", __func__);
123
124 return NULL;
125}
126
127/**
128 * get_desc() - Get the block device descriptor for the given device number
129 *
130 * @if_type: Interface type
131 * @devnum: Device number (0 = first)
132 * @descp: Returns block device descriptor on success
133 * @return 0 on success, -ENODEV if there is no such device and no device
134 * with a higher device number, -ENOENT if there is no such device but there
135 * is one with a higher number, or other -ve on other error.
136 */
137static int get_desc(enum if_type if_type, int devnum, struct blk_desc **descp)
138{
139 bool found_more = false;
140 struct udevice *dev;
141 struct uclass *uc;
142 int ret;
143
144 *descp = NULL;
145 ret = uclass_get(UCLASS_BLK, &uc);
146 if (ret)
147 return ret;
148 uclass_foreach_dev(dev, uc) {
149 struct blk_desc *desc = dev_get_uclass_platdata(dev);
150
151 debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
152 if_type, devnum, dev->name, desc->if_type, desc->devnum);
153 if (desc->if_type == if_type) {
154 if (desc->devnum == devnum) {
155 ret = device_probe(dev);
156 if (ret)
157 return ret;
158
159 } else if (desc->devnum > devnum) {
160 found_more = true;
161 }
162 }
163 }
164
165 return found_more ? -ENOENT : -ENODEV;
166}
167
168int blk_list_part(enum if_type if_type)
169{
170 struct blk_desc *desc;
171 int devnum, ok;
172 int ret;
173
174 for (ok = 0, devnum = 0;; ++devnum) {
175 ret = get_desc(if_type, devnum, &desc);
176 if (ret == -ENODEV)
177 break;
178 else if (ret)
179 continue;
180 if (desc->part_type != PART_TYPE_UNKNOWN) {
181 ++ok;
182 if (devnum)
183 putc('\n');
184 part_print(desc);
185 }
186 }
187 if (!ok)
188 return -ENODEV;
189
190 return 0;
191}
192
193int blk_print_part_devnum(enum if_type if_type, int devnum)
194{
195 struct blk_desc *desc;
196 int ret;
197
198 ret = get_desc(if_type, devnum, &desc);
199 if (ret)
200 return ret;
201 if (desc->type == DEV_TYPE_UNKNOWN)
202 return -ENOENT;
203 part_print(desc);
204
205 return 0;
206}
207
208void blk_list_devices(enum if_type if_type)
209{
210 struct blk_desc *desc;
211 int ret;
212 int i;
213
214 for (i = 0;; ++i) {
215 ret = get_desc(if_type, i, &desc);
216 if (ret == -ENODEV)
217 break;
218 else if (ret)
219 continue;
220 if (desc->type == DEV_TYPE_UNKNOWN)
221 continue; /* list only known devices */
222 printf("Device %d: ", i);
223 dev_print(desc);
224 }
225}
226
227int blk_print_device_num(enum if_type if_type, int devnum)
228{
229 struct blk_desc *desc;
230 int ret;
231
232 ret = get_desc(if_type, devnum, &desc);
233 if (ret)
234 return ret;
235 printf("\nIDE device %d: ", devnum);
236 dev_print(desc);
237
238 return 0;
239}
240
241int blk_show_device(enum if_type if_type, int devnum)
242{
243 struct blk_desc *desc;
244 int ret;
245
246 printf("\nDevice %d: ", devnum);
247 ret = get_desc(if_type, devnum, &desc);
248 if (ret == -ENODEV || ret == -ENOENT) {
249 printf("unknown device\n");
250 return -ENODEV;
251 }
252 if (ret)
253 return ret;
254 dev_print(desc);
255
256 if (desc->type == DEV_TYPE_UNKNOWN)
257 return -ENOENT;
258
259 return 0;
260}
261
262ulong blk_read_devnum(enum if_type if_type, int devnum, lbaint_t start,
263 lbaint_t blkcnt, void *buffer)
264{
265 struct blk_desc *desc;
266 ulong n;
267 int ret;
268
269 ret = get_desc(if_type, devnum, &desc);
270 if (ret)
271 return ret;
272 n = blk_dread(desc, start, blkcnt, buffer);
273 if (IS_ERR_VALUE(n))
274 return n;
275
276 /* flush cache after read */
277 flush_cache((ulong)buffer, blkcnt * desc->blksz);
278
279 return n;
280}
281
282ulong blk_write_devnum(enum if_type if_type, int devnum, lbaint_t start,
283 lbaint_t blkcnt, const void *buffer)
284{
285 struct blk_desc *desc;
286 int ret;
287
288 ret = get_desc(if_type, devnum, &desc);
289 if (ret)
290 return ret;
291 return blk_dwrite(desc, start, blkcnt, buffer);
292}
293
Simon Glasscceee552016-02-29 15:25:55 -0700294int blk_first_device(int if_type, struct udevice **devp)
295{
296 struct blk_desc *desc;
297 int ret;
298
299 ret = uclass_first_device(UCLASS_BLK, devp);
300 if (ret)
301 return ret;
302 if (!*devp)
303 return -ENODEV;
304 do {
305 desc = dev_get_uclass_platdata(*devp);
306 if (desc->if_type == if_type)
307 return 0;
308 ret = uclass_next_device(devp);
309 if (ret)
310 return ret;
311 } while (*devp);
312
313 return -ENODEV;
314}
315
316int blk_next_device(struct udevice **devp)
317{
318 struct blk_desc *desc;
319 int ret, if_type;
320
321 desc = dev_get_uclass_platdata(*devp);
322 if_type = desc->if_type;
323 do {
324 ret = uclass_next_device(devp);
325 if (ret)
326 return ret;
327 if (!*devp)
328 return -ENODEV;
329 desc = dev_get_uclass_platdata(*devp);
330 if (desc->if_type == if_type)
331 return 0;
332 } while (1);
333}
334
335int blk_get_device(int if_type, int devnum, struct udevice **devp)
336{
337 struct uclass *uc;
338 struct udevice *dev;
339 int ret;
340
341 ret = uclass_get(UCLASS_BLK, &uc);
342 if (ret)
343 return ret;
344 uclass_foreach_dev(dev, uc) {
345 struct blk_desc *desc = dev_get_uclass_platdata(dev);
346
347 debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
348 if_type, devnum, dev->name, desc->if_type, desc->devnum);
349 if (desc->if_type == if_type && desc->devnum == devnum) {
350 *devp = dev;
351 return device_probe(dev);
352 }
353 }
354
355 return -ENODEV;
356}
357
358unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
359 lbaint_t blkcnt, void *buffer)
360{
361 struct udevice *dev = block_dev->bdev;
362 const struct blk_ops *ops = blk_get_ops(dev);
Eric Nelsonfaf4f052016-03-28 10:05:44 -0700363 ulong blks_read;
Simon Glasscceee552016-02-29 15:25:55 -0700364
365 if (!ops->read)
366 return -ENOSYS;
367
Eric Nelsonfaf4f052016-03-28 10:05:44 -0700368 if (blkcache_read(block_dev->if_type, block_dev->devnum,
369 start, blkcnt, block_dev->blksz, buffer))
370 return blkcnt;
371 blks_read = ops->read(dev, start, blkcnt, buffer);
372 if (blks_read == blkcnt)
373 blkcache_fill(block_dev->if_type, block_dev->devnum,
374 start, blkcnt, block_dev->blksz, buffer);
375
376 return blks_read;
Simon Glasscceee552016-02-29 15:25:55 -0700377}
378
379unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
380 lbaint_t blkcnt, const void *buffer)
381{
382 struct udevice *dev = block_dev->bdev;
383 const struct blk_ops *ops = blk_get_ops(dev);
384
385 if (!ops->write)
386 return -ENOSYS;
387
Eric Nelsonfaf4f052016-03-28 10:05:44 -0700388 blkcache_invalidate(block_dev->if_type, block_dev->devnum);
Simon Glasscceee552016-02-29 15:25:55 -0700389 return ops->write(dev, start, blkcnt, buffer);
390}
391
392unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
393 lbaint_t blkcnt)
394{
395 struct udevice *dev = block_dev->bdev;
396 const struct blk_ops *ops = blk_get_ops(dev);
397
398 if (!ops->erase)
399 return -ENOSYS;
400
Eric Nelsonfaf4f052016-03-28 10:05:44 -0700401 blkcache_invalidate(block_dev->if_type, block_dev->devnum);
Simon Glasscceee552016-02-29 15:25:55 -0700402 return ops->erase(dev, start, blkcnt);
403}
404
405int blk_prepare_device(struct udevice *dev)
406{
407 struct blk_desc *desc = dev_get_uclass_platdata(dev);
408
409 part_init(desc);
410
411 return 0;
412}
413
Simon Glassd089ba32016-05-01 11:36:28 -0600414int blk_find_max_devnum(enum if_type if_type)
415{
416 struct udevice *dev;
417 int max_devnum = -ENODEV;
418 struct uclass *uc;
419 int ret;
420
421 ret = uclass_get(UCLASS_BLK, &uc);
422 if (ret)
423 return ret;
424 uclass_foreach_dev(dev, uc) {
425 struct blk_desc *desc = dev_get_uclass_platdata(dev);
426
427 if (desc->if_type == if_type && desc->devnum > max_devnum)
428 max_devnum = desc->devnum;
429 }
430
431 return max_devnum;
432}
433
Simon Glasscceee552016-02-29 15:25:55 -0700434int blk_create_device(struct udevice *parent, const char *drv_name,
435 const char *name, int if_type, int devnum, int blksz,
436 lbaint_t size, struct udevice **devp)
437{
438 struct blk_desc *desc;
439 struct udevice *dev;
440 int ret;
441
442 ret = device_bind_driver(parent, drv_name, name, &dev);
443 if (ret)
444 return ret;
445 desc = dev_get_uclass_platdata(dev);
446 desc->if_type = if_type;
447 desc->blksz = blksz;
448 desc->lba = size / blksz;
449 desc->part_type = PART_TYPE_UNKNOWN;
450 desc->bdev = dev;
Simon Glassd089ba32016-05-01 11:36:28 -0600451 if (devnum == -1) {
452 ret = blk_find_max_devnum(if_type);
453 if (ret == -ENODEV)
454 devnum = 0;
455 else if (ret < 0)
456 return ret;
457 else
458 devnum = ret + 1;
459 }
Simon Glasscceee552016-02-29 15:25:55 -0700460 desc->devnum = devnum;
461 *devp = dev;
462
463 return 0;
464}
465
466int blk_unbind_all(int if_type)
467{
468 struct uclass *uc;
469 struct udevice *dev, *next;
470 int ret;
471
472 ret = uclass_get(UCLASS_BLK, &uc);
473 if (ret)
474 return ret;
475 uclass_foreach_dev_safe(dev, next, uc) {
476 struct blk_desc *desc = dev_get_uclass_platdata(dev);
477
478 if (desc->if_type == if_type) {
479 ret = device_remove(dev);
480 if (ret)
481 return ret;
482 ret = device_unbind(dev);
483 if (ret)
484 return ret;
485 }
486 }
487
488 return 0;
489}
490
491UCLASS_DRIVER(blk) = {
492 .id = UCLASS_BLK,
493 .name = "blk",
494 .per_device_platdata_auto_alloc_size = sizeof(struct blk_desc),
495};