dm: blk: Add a block-device uclass

Add a uclass for block devices. These provide block-oriented data access,
supporting reading, writing and erasing of whole blocks.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Tested-by: Stephen Warren <swarren@nvidia.com>
This commit is contained in:
Simon Glass 2016-02-29 15:25:55 -07:00
parent 9807c3b78a
commit 09d71aac7b
5 changed files with 334 additions and 0 deletions

View file

@ -1,3 +1,14 @@
config BLK
bool "Support block devices"
depends on DM
help
Enable support for block devices, such as SCSI, MMC and USB
flash sticks. These provide a block-level interface which permits
reading, writing and (in some cases) erasing blocks. Block
devices often have a partition table which allows the device to
be partitioned into several areas, called 'partitions' in U-Boot.
A filesystem can be placed in each partition.
config DISK config DISK
bool "Support disk controllers with driver model" bool "Support disk controllers with driver model"
depends on DM depends on DM

View file

@ -5,6 +5,8 @@
# SPDX-License-Identifier: GPL-2.0+ # SPDX-License-Identifier: GPL-2.0+
# #
obj-$(CONFIG_BLK) += blk-uclass.o
obj-$(CONFIG_DISK) += disk-uclass.o obj-$(CONFIG_DISK) += disk-uclass.o
obj-$(CONFIG_SCSI_AHCI) += ahci.o obj-$(CONFIG_SCSI_AHCI) += ahci.o
obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o

175
drivers/block/blk-uclass.c Normal file
View file

@ -0,0 +1,175 @@
/*
* Copyright (C) 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <blk.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
int blk_first_device(int if_type, struct udevice **devp)
{
struct blk_desc *desc;
int ret;
ret = uclass_first_device(UCLASS_BLK, devp);
if (ret)
return ret;
if (!*devp)
return -ENODEV;
do {
desc = dev_get_uclass_platdata(*devp);
if (desc->if_type == if_type)
return 0;
ret = uclass_next_device(devp);
if (ret)
return ret;
} while (*devp);
return -ENODEV;
}
int blk_next_device(struct udevice **devp)
{
struct blk_desc *desc;
int ret, if_type;
desc = dev_get_uclass_platdata(*devp);
if_type = desc->if_type;
do {
ret = uclass_next_device(devp);
if (ret)
return ret;
if (!*devp)
return -ENODEV;
desc = dev_get_uclass_platdata(*devp);
if (desc->if_type == if_type)
return 0;
} while (1);
}
int blk_get_device(int if_type, int devnum, struct udevice **devp)
{
struct uclass *uc;
struct udevice *dev;
int ret;
ret = uclass_get(UCLASS_BLK, &uc);
if (ret)
return ret;
uclass_foreach_dev(dev, uc) {
struct blk_desc *desc = dev_get_uclass_platdata(dev);
debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
if_type, devnum, dev->name, desc->if_type, desc->devnum);
if (desc->if_type == if_type && desc->devnum == devnum) {
*devp = dev;
return device_probe(dev);
}
}
return -ENODEV;
}
unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, void *buffer)
{
struct udevice *dev = block_dev->bdev;
const struct blk_ops *ops = blk_get_ops(dev);
if (!ops->read)
return -ENOSYS;
return ops->read(dev, start, blkcnt, buffer);
}
unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, const void *buffer)
{
struct udevice *dev = block_dev->bdev;
const struct blk_ops *ops = blk_get_ops(dev);
if (!ops->write)
return -ENOSYS;
return ops->write(dev, start, blkcnt, buffer);
}
unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt)
{
struct udevice *dev = block_dev->bdev;
const struct blk_ops *ops = blk_get_ops(dev);
if (!ops->erase)
return -ENOSYS;
return ops->erase(dev, start, blkcnt);
}
int blk_prepare_device(struct udevice *dev)
{
struct blk_desc *desc = dev_get_uclass_platdata(dev);
part_init(desc);
return 0;
}
int blk_create_device(struct udevice *parent, const char *drv_name,
const char *name, int if_type, int devnum, int blksz,
lbaint_t size, struct udevice **devp)
{
struct blk_desc *desc;
struct udevice *dev;
int ret;
ret = device_bind_driver(parent, drv_name, name, &dev);
if (ret)
return ret;
desc = dev_get_uclass_platdata(dev);
desc->if_type = if_type;
desc->blksz = blksz;
desc->lba = size / blksz;
desc->part_type = PART_TYPE_UNKNOWN;
desc->bdev = dev;
desc->devnum = devnum;
*devp = dev;
return 0;
}
int blk_unbind_all(int if_type)
{
struct uclass *uc;
struct udevice *dev, *next;
int ret;
ret = uclass_get(UCLASS_BLK, &uc);
if (ret)
return ret;
uclass_foreach_dev_safe(dev, next, uc) {
struct blk_desc *desc = dev_get_uclass_platdata(dev);
if (desc->if_type == if_type) {
ret = device_remove(dev);
if (ret)
return ret;
ret = device_unbind(dev);
if (ret)
return ret;
}
}
return 0;
}
UCLASS_DRIVER(blk) = {
.id = UCLASS_BLK,
.name = "blk",
.per_device_platdata_auto_alloc_size = sizeof(struct blk_desc),
};

View file

@ -34,7 +34,15 @@ enum if_type {
IF_TYPE_COUNT, /* Number of interface types */ IF_TYPE_COUNT, /* Number of interface types */
}; };
/*
* With driver model (CONFIG_BLK) this is uclass platform data, accessible
* with dev_get_uclass_platdata(dev)
*/
struct blk_desc { struct blk_desc {
/*
* TODO: With driver model we should be able to use the parent
* device's uclass instead.
*/
enum if_type if_type; /* type of the interface */ enum if_type if_type; /* type of the interface */
int devnum; /* device number */ int devnum; /* device number */
unsigned char part_type; /* partition type */ unsigned char part_type; /* partition type */
@ -53,6 +61,9 @@ struct blk_desc {
char vendor[40+1]; /* IDE model, SCSI Vendor */ char vendor[40+1]; /* IDE model, SCSI Vendor */
char product[20+1]; /* IDE Serial no, SCSI product */ char product[20+1]; /* IDE Serial no, SCSI product */
char revision[8+1]; /* firmware revision */ char revision[8+1]; /* firmware revision */
#ifdef CONFIG_BLK
struct udevice *bdev;
#else
unsigned long (*block_read)(struct blk_desc *block_dev, unsigned long (*block_read)(struct blk_desc *block_dev,
lbaint_t start, lbaint_t start,
lbaint_t blkcnt, lbaint_t blkcnt,
@ -65,12 +76,145 @@ struct blk_desc {
lbaint_t start, lbaint_t start,
lbaint_t blkcnt); lbaint_t blkcnt);
void *priv; /* driver private struct pointer */ void *priv; /* driver private struct pointer */
#endif
}; };
#define BLOCK_CNT(size, blk_desc) (PAD_COUNT(size, blk_desc->blksz)) #define BLOCK_CNT(size, blk_desc) (PAD_COUNT(size, blk_desc->blksz))
#define PAD_TO_BLOCKSIZE(size, blk_desc) \ #define PAD_TO_BLOCKSIZE(size, blk_desc) \
(PAD_SIZE(size, blk_desc->blksz)) (PAD_SIZE(size, blk_desc->blksz))
#ifdef CONFIG_BLK
struct udevice;
/* Operations on block devices */
struct blk_ops {
/**
* read() - read from a block device
*
* @dev: Device to read from
* @start: Start block number to read (0=first)
* @blkcnt: Number of blocks to read
* @buffer: Destination buffer for data read
* @return number of blocks read, or -ve error number (see the
* IS_ERR_VALUE() macro
*/
unsigned long (*read)(struct udevice *dev, lbaint_t start,
lbaint_t blkcnt, void *buffer);
/**
* write() - write to a block device
*
* @dev: Device to write to
* @start: Start block number to write (0=first)
* @blkcnt: Number of blocks to write
* @buffer: Source buffer for data to write
* @return number of blocks written, or -ve error number (see the
* IS_ERR_VALUE() macro
*/
unsigned long (*write)(struct udevice *dev, lbaint_t start,
lbaint_t blkcnt, const void *buffer);
/**
* erase() - erase a section of a block device
*
* @dev: Device to (partially) erase
* @start: Start block number to erase (0=first)
* @blkcnt: Number of blocks to erase
* @return number of blocks erased, or -ve error number (see the
* IS_ERR_VALUE() macro
*/
unsigned long (*erase)(struct udevice *dev, lbaint_t start,
lbaint_t blkcnt);
};
#define blk_get_ops(dev) ((struct blk_ops *)(dev)->driver->ops)
/*
* These functions should take struct udevice instead of struct blk_desc,
* but this is convenient for migration to driver model. Add a 'd' prefix
* to the function operations, so that blk_read(), etc. can be reserved for
* functions with the correct arguments.
*/
unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, void *buffer);
unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, const void *buffer);
unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt);
/**
* blk_get_device() - Find and probe a block device ready for use
*
* @if_type: Interface type (enum if_type_t)
* @devnum: Device number (specific to each interface type)
* @devp: the device, if found
* @return - if found, -ENODEV if no device found, or other -ve error value
*/
int blk_get_device(int if_type, int devnum, struct udevice **devp);
/**
* blk_first_device() - Find the first device for a given interface
*
* The device is probed ready for use
*
* @devnum: Device number (specific to each interface type)
* @devp: the device, if found
* @return 0 if found, -ENODEV if no device, or other -ve error value
*/
int blk_first_device(int if_type, struct udevice **devp);
/**
* blk_next_device() - Find the next device for a given interface
*
* This can be called repeatedly after blk_first_device() to iterate through
* all devices of the given interface type.
*
* The device is probed ready for use
*
* @devp: On entry, the previous device returned. On exit, the next
* device, if found
* @return 0 if found, -ENODEV if no device, or other -ve error value
*/
int blk_next_device(struct udevice **devp);
/**
* blk_create_device() - Create a new block device
*
* @parent: Parent of the new device
* @drv_name: Driver name to use for the block device
* @name: Name for the device
* @if_type: Interface type (enum if_type_t)
* @devnum: Device number, specific to the interface type
* @blksz: Block size of the device in bytes (typically 512)
* @size: Total size of the device in bytes
* @devp: the new device (which has not been probed)
*/
int blk_create_device(struct udevice *parent, const char *drv_name,
const char *name, int if_type, int devnum, int blksz,
lbaint_t size, struct udevice **devp);
/**
* blk_prepare_device() - Prepare a block device for use
*
* This reads partition information from the device if supported.
*
* @dev: Device to prepare
* @return 0 if ok, -ve on error
*/
int blk_prepare_device(struct udevice *dev);
/**
* blk_unbind_all() - Unbind all device of the given interface type
*
* The devices are removed and then unbound.
*
* @if_type: Interface type to unbind
* @return 0 if OK, -ve on error
*/
int blk_unbind_all(int if_type);
#else
#include <errno.h>
/* /*
* These functions should take struct udevice instead of struct blk_desc, * These functions should take struct udevice instead of struct blk_desc,
* but this is convenient for migration to driver model. Add a 'd' prefix * but this is convenient for migration to driver model. Add a 'd' prefix
@ -99,5 +243,6 @@ static inline ulong blk_derase(struct blk_desc *block_dev, lbaint_t start,
{ {
return block_dev->block_erase(block_dev, start, blkcnt); return block_dev->block_erase(block_dev, start, blkcnt);
} }
#endif /* !CONFIG_BLK */
#endif #endif

View file

@ -26,6 +26,7 @@ enum uclass_id {
/* U-Boot uclasses start here - in alphabetical order */ /* U-Boot uclasses start here - in alphabetical order */
UCLASS_ADC, /* Analog-to-digital converter */ UCLASS_ADC, /* Analog-to-digital converter */
UCLASS_BLK, /* Block device */
UCLASS_CLK, /* Clock source, e.g. used by peripherals */ UCLASS_CLK, /* Clock source, e.g. used by peripherals */
UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CPU, /* CPU, typically part of an SoC */
UCLASS_CROS_EC, /* Chrome OS EC */ UCLASS_CROS_EC, /* Chrome OS EC */