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