Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi> * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> */ #include <common.h> #include <blk.h> #include <dm.h> #include <part.h> #include <virtio_types.h> #include <virtio.h> #include <virtio_ring.h> #include "virtio_blk.h" struct virtio_blk_priv { struct virtqueue *vq; }; static ulong virtio_blk_do_req(struct udevice *dev, u64 sector, lbaint_t blkcnt, void *buffer, u32 type) { struct virtio_blk_priv *priv = dev_get_priv(dev); unsigned int num_out = 0, num_in = 0; struct virtio_sg *sgs[3]; u8 status; int ret; struct virtio_blk_outhdr out_hdr = { .type = cpu_to_virtio32(dev, type), .sector = cpu_to_virtio64(dev, sector), }; struct virtio_sg hdr_sg = { &out_hdr, sizeof(out_hdr) }; struct virtio_sg data_sg = { buffer, blkcnt * 512 }; struct virtio_sg status_sg = { &status, sizeof(status) }; sgs[num_out++] = &hdr_sg; if (type & VIRTIO_BLK_T_OUT) sgs[num_out++] = &data_sg; else sgs[num_out + num_in++] = &data_sg; sgs[num_out + num_in++] = &status_sg; ret = virtqueue_add(priv->vq, sgs, num_out, num_in); if (ret) return ret; virtqueue_kick(priv->vq); while (!virtqueue_get_buf(priv->vq, NULL)) ; return status == VIRTIO_BLK_S_OK ? blkcnt : -EIO; } static ulong virtio_blk_read(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *buffer) { return virtio_blk_do_req(dev, start, blkcnt, buffer, VIRTIO_BLK_T_IN); } static ulong virtio_blk_write(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, const void *buffer) { return virtio_blk_do_req(dev, start, blkcnt, (void *)buffer, VIRTIO_BLK_T_OUT); } static int virtio_blk_bind(struct udevice *dev) { struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent); struct blk_desc *desc = dev_get_uclass_platdata(dev); int devnum; desc->if_type = IF_TYPE_VIRTIO; /* * Initialize the devnum to -ENODEV. This is to make sure that * blk_next_free_devnum() works as expected, since the default * value 0 is a valid devnum. */ desc->devnum = -ENODEV; devnum = blk_next_free_devnum(IF_TYPE_VIRTIO); if (devnum < 0) return devnum; desc->devnum = devnum; desc->part_type = PART_TYPE_UNKNOWN; /* * virtio mmio transport supplies string identification for us, * while pci trnasport uses a 2-byte subvendor value. */ if (uc_priv->vendor >> 16) sprintf(desc->vendor, "%s", (char *)&uc_priv->vendor); else sprintf(desc->vendor, "%04x", uc_priv->vendor); desc->bdev = dev; /* Indicate what driver features we support */ virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0); return 0; } static int virtio_blk_probe(struct udevice *dev) { struct virtio_blk_priv *priv = dev_get_priv(dev); struct blk_desc *desc = dev_get_uclass_platdata(dev); u64 cap; int ret; ret = virtio_find_vqs(dev, 1, &priv->vq); if (ret) return ret; desc->blksz = 512; virtio_cread(dev, struct virtio_blk_config, capacity, &cap); desc->lba = cap; return 0; } static const struct blk_ops virtio_blk_ops = { .read = virtio_blk_read, .write = virtio_blk_write, }; U_BOOT_DRIVER(virtio_blk) = { .name = VIRTIO_BLK_DRV_NAME, .id = UCLASS_BLK, .ops = &virtio_blk_ops, .bind = virtio_blk_bind, .probe = virtio_blk_probe, .remove = virtio_reset, .priv_auto_alloc_size = sizeof(struct virtio_blk_priv), .flags = DM_FLAG_ACTIVE_DMA, }; |