|  | The U-Boot Driver Model Project | 
|  | =============================== | 
|  | Block device subsystem analysis | 
|  | =============================== | 
|  |  | 
|  | Pavel Herrmann <morpheus.ibis@gmail.com> | 
|  | 2012-03-08 | 
|  |  | 
|  | I) Overview | 
|  | ----------- | 
|  |  | 
|  | U-Boot currently implements several distinct APIs for block devices - some | 
|  | drivers use the SATA API, some drivers use the IDE API, sym53c8xx and | 
|  | AHCI use the SCSI API, mg_disk has a separate API, and systemace also has a | 
|  | separate API. There are also MMC and USB APIs used outside of drivers/block, | 
|  | those will be detailed in their specific documents. | 
|  |  | 
|  | Block devices are described by block_dev_desc structure, that holds, among | 
|  | other things, the read/write/erase callbacks. Block device structures are | 
|  | stored in any way depending on the API, but can be accessed by | 
|  |  | 
|  | block_dev_desc_t * $api_get_dev(int dev) | 
|  |  | 
|  | function, as seen in disk/part.c. | 
|  |  | 
|  | 1) SATA interface | 
|  | ----------------- | 
|  |  | 
|  | The SATA interface drivers implement the following functions: | 
|  |  | 
|  | int   init_sata(int dev) | 
|  | int   scan_sata(int dev) | 
|  | ulong sata_read(int dev, ulong blknr, ulong blkcnt, void *buffer) | 
|  | ulong sata_write(int dev, ulong blknr, ulong blkcnt, const void *buffer) | 
|  |  | 
|  | Block devices are kept in sata_dev_desc[], which is prefilled with values | 
|  | common to all SATA devices in cmd_sata.c, and then modified in init_sata | 
|  | function in the drivers. Callbacks of the block device use SATA API | 
|  | directly. The sata_get_dev function is defined in cmd_sata.c. | 
|  |  | 
|  | 2) SCSI interface | 
|  | ----------------- | 
|  |  | 
|  | The SCSI interface drivers implement the following functions: | 
|  |  | 
|  | void scsi_print_error(ccb *pccb) | 
|  | int  scsi_exec(ccb *pccb) | 
|  | void scsi_bus_reset(void) | 
|  | void scsi_low_level_init(int busdevfunc) | 
|  |  | 
|  | The SCSI API works through the scsi_exec function, the actual operation | 
|  | requested is found in the ccb structure. | 
|  |  | 
|  | Block devices are kept in scsi_dev_desc[], which lives only in cmd_scsi.c. | 
|  | Callbacks of the block device use functions from cmd_scsi.c, which in turn | 
|  | call scsi_exec of the controller. The scsi_get_dev function is also defined | 
|  | in cmd_scsi.c. | 
|  |  | 
|  | 3) mg_disk interface | 
|  | -------------------- | 
|  |  | 
|  | The mg_disk interface drivers implement the following functions: | 
|  |  | 
|  | struct mg_drv_data* mg_get_drv_data (void) | 
|  | uint   mg_disk_init (void) | 
|  | uint   mg_disk_read (u32 addr, u8 *buff, u32 len) | 
|  | uint   mg_disk_write(u32 addr, u8 *buff, u32 len) | 
|  | uint   mg_disk_write_sects(void *buff, u32 sect_num, u32 sect_cnt) | 
|  | uint   mg_disk_read_sects(void *buff, u32 sect_num, u32 sect_cnt) | 
|  |  | 
|  | The mg_get_drv_data function is to be overridden per-board, but there are no | 
|  | board in-tree that do this. | 
|  |  | 
|  | Only one driver for this API exists, and it only supports one block device. | 
|  | Callbacks for this device are implemented in mg_disk.c and call the mg_disk | 
|  | API. The mg_disk_get_dev function is defined in mg_disk.c and ignores the | 
|  | device number, always returning the same device. | 
|  |  | 
|  | 4) systemace interface | 
|  | ---------------------- | 
|  |  | 
|  | The systemace interface does not define any driver API, and has no command | 
|  | itself. The single defined function is systemace_get_devs() from | 
|  | systemace.c, which returns a single static structure for the only supported | 
|  | block device. Callbacks for this device are also implemented in systemace.c. | 
|  |  | 
|  | 5) IDE interface | 
|  | ---------------- | 
|  |  | 
|  | The IDE interface drivers implement the following functions, but only if | 
|  | CONFIG_IDE_AHB is set: | 
|  |  | 
|  | uchar ide_read_register(int dev, unsigned int port); | 
|  | void  ide_write_register(int dev, unsigned int port, unsigned char val); | 
|  | void  ide_read_data(int dev, ulong *sect_buf, int words); | 
|  | void  ide_write_data(int dev, const ulong *sect_buf, int words); | 
|  |  | 
|  | The first two functions are called from ide_inb()/ide_outb(), and will | 
|  | default to direct memory access if CONFIG_IDE_AHB is not set, or | 
|  | ide_inb()/ide_outb() functions will get overridden by the board altogether. | 
|  |  | 
|  | The second two functions are called from input_data()/output_data() | 
|  | functions, and also default to direct memory access, but cannot be | 
|  | overridden by the board. | 
|  |  | 
|  | One function shared by IDE drivers (but not defined in ide.h) is | 
|  | int ide_preinit(void) | 
|  | This function gets called from ide_init in cmd_ide.c if CONFIG_IDE_PREINIT | 
|  | is defined, and will do the driver-specific initialization of the device. | 
|  |  | 
|  | Block devices are kept in ide_dev_desc[], which is filled in cmd_ide.c. | 
|  | Callbacks of the block device are defined in cmd_ide.c, and use the | 
|  | ide_inb()/ide_outb()/input_data()/output_data() functions mentioned above. | 
|  | The ide_get_dev function is defined in cmd_ide.c. | 
|  |  | 
|  | II) Approach | 
|  | ------------ | 
|  |  | 
|  | A new block controller core and an associated API will be created to mimic the | 
|  | current SATA API, its drivers will have the following ops: | 
|  |  | 
|  | struct block_ctrl_ops { | 
|  | int scan(instance *i); | 
|  | int reset(instance *i, int port); | 
|  | lbaint_t read(instance *i, int port, lbaint_t start, lbatin_t length, | 
|  | void *buffer); | 
|  | lbaint_t write(instance *i, int port, lbaint_t start, lbatin_t length, | 
|  | void*buffer); | 
|  | } | 
|  |  | 
|  | The current sata_init() function will be changed into the driver probe() | 
|  | function. The read() and write() functions should never be called directly, | 
|  | instead they should be called by block device driver for disks. | 
|  |  | 
|  | Other block APIs would either be transformed into this API, or be kept as | 
|  | legacy for old drivers, or be dropped altogether. | 
|  |  | 
|  | Legacy driver APIs will each have its own driver core that will contain the | 
|  | shared logic, which is currently located mostly in cmd_* files. Callbacks for | 
|  | block device drivers will then probably be implemented as a part of the core | 
|  | logic, and will use the driver ops (which will copy current state of | 
|  | respective APIs) to do the work. | 
|  |  | 
|  | All drivers will be cleaned up, most ifdefs should be converted into | 
|  | platform_data, to enable support for multiple devices with different settings. | 
|  |  | 
|  | A new block device core will also be created, and will keep track of all | 
|  | block devices on all interfaces. | 
|  |  | 
|  | Current block_dev_desc structure will be changed to fit the driver model, all | 
|  | identification and configuration will be placed in private data, and | 
|  | a single accessor and modifier will be defined, to accommodate the need for | 
|  | different sets of options for different interfaces, while keeping the | 
|  | structure small. The new block device drivers will have the following ops | 
|  | structure (lbaint_t is either 32bit or 64bit unsigned, depending on | 
|  | CONFIG_LBA48): | 
|  |  | 
|  | struct blockdev_ops { | 
|  | lbaint_t (*block_read)(struct instance *i, lbaint_t start, lbaint_t blkcnt, | 
|  | void *buffer); | 
|  | lbaint_t (*block_write)(struct instance *i, lbaint_t start, lbaint_t blkcnt, | 
|  | void *buffer); | 
|  | lbaint_t (*block_erase)(struct instance *i, lbaint_t start, lbaint_t blkcnt | 
|  | ); | 
|  | int	     (*get_option)(struct instance *i, enum blockdev_option_code op, | 
|  | struct option *res); | 
|  | int	     (*set_option)(struct instance *i, enum blockdev_option_code op, | 
|  | struct option *val); | 
|  | } | 
|  |  | 
|  | struct option { | 
|  | uint32_t flags | 
|  | union data { | 
|  | uint64_t data_u; | 
|  | char*    data_s; | 
|  | void*    data_p; | 
|  | } | 
|  | } | 
|  |  | 
|  | enum blockdev_option_code { | 
|  | BLKD_OPT_IFTYPE=0, | 
|  | BLKD_OPT_TYPE, | 
|  | BLKD_OPT_BLOCKSIZE, | 
|  | BLKD_OPT_BLOCKCOUNT, | 
|  | BLKD_OPT_REMOVABLE, | 
|  | BLKD_OPT_LBA48, | 
|  | BLKD_OPT_VENDOR, | 
|  | BLKD_OPT_PRODICT, | 
|  | BLKD_OPT_REVISION, | 
|  | BLKD_OPT_SCSILUN, | 
|  | BLKD_OPT_SCSITARGET, | 
|  | BLKD_OPT_OFFSET | 
|  | } | 
|  |  | 
|  | Flags in option above will contain the type of returned data (which should be | 
|  | checked against what is expected, even though the option requested should | 
|  | specify it), and a flag to indicate whether the returned pointer needs to be | 
|  | free()'d. | 
|  |  | 
|  | The block device core will contain the logic now located in disk/part.c and | 
|  | related files, and will be used to forward requests to block devices. The API | 
|  | for the block device core will copy the ops of a block device (with a string | 
|  | identifier instead of instance pointer). This means that partitions will also | 
|  | be handled by the block device core, and exported as block devices, making | 
|  | them transparent to the rest of the code. | 
|  |  | 
|  | Sadly, this will change how file systems can access the devices, and thus will | 
|  | affect a lot of places. However, these changes should be localized and easy to | 
|  | implement. | 
|  |  | 
|  | AHCI driver will be rewritten to fit the new unified block controller API, | 
|  | making SCSI API easy to merge with sym53c8xx, or remove it once the device | 
|  | driver has died. | 
|  |  | 
|  | Optionally, IDE core may be changed into one driver with unified block | 
|  | controller API, as most of it is already in one place and device drivers are | 
|  | just sets of hooks. Additionally, mg_disk driver is unused and may be removed | 
|  | in near future. | 
|  |  | 
|  |  | 
|  | III) Analysis of in-tree drivers | 
|  | -------------------------------- | 
|  |  | 
|  | ahci.c | 
|  | ------ | 
|  | SCSI API, will be rewritten for a different API. | 
|  |  | 
|  | ata_piix.c | 
|  | ---------- | 
|  | SATA API, easy to port. | 
|  |  | 
|  | fsl_sata.c | 
|  | ---------- | 
|  | SATA API, few CONFIG macros, easy to port. | 
|  |  | 
|  | ftide020.c | 
|  | ---------- | 
|  | IDE API, defines CONFIG_IDE_AHB and ide_preinit hook functions. | 
|  |  | 
|  | mg_disk.c | 
|  | --------- | 
|  | Single driver with mg_disk API, not much to change, easy to port. | 
|  |  | 
|  | mvsata_ide.c | 
|  | ------------ | 
|  | IDE API, only defines ide_preinit hook function. | 
|  |  | 
|  | mxc_ata.c | 
|  | --------- | 
|  | IDE API, only defines ide_preinit hook function. | 
|  |  | 
|  | pata_bfin.c | 
|  | ----------- | 
|  | SATA API, easy to port. | 
|  |  | 
|  | sata_dwc.c | 
|  | ---------- | 
|  | SATA API, easy to port. | 
|  |  | 
|  | sata_sil3114.c | 
|  | -------------- | 
|  | SATA API, easy to port. | 
|  |  | 
|  | sata_sil.c | 
|  | ---------- | 
|  | SATA API, easy to port. | 
|  |  | 
|  | sil680.c | 
|  | -------- | 
|  | IDE API, only defines ide_preinit hook function. | 
|  |  | 
|  | sym53c8xx.c | 
|  | ----------- | 
|  | SCSI API, may be merged with code from cmd_scsi. | 
|  |  | 
|  | systemace.c | 
|  | ----------- | 
|  | Single driver with systemace API, not much to change, easy to port. |