Pavel Herrmann | 0463574 | 2012-08-08 01:42:23 +0000 | [diff] [blame] | 1 | The U-Boot Driver Model Project |
| 2 | =============================== |
| 3 | Block device subsystem analysis |
| 4 | =============================== |
| 5 | |
| 6 | Pavel Herrmann <morpheus.ibis@gmail.com> |
| 7 | 2012-03-08 |
| 8 | |
| 9 | I) Overview |
| 10 | ----------- |
| 11 | |
| 12 | U-Boot currently implements several distinct APIs for block devices - some |
| 13 | drivers use the SATA API, some drivers use the IDE API, sym53c8xx and |
| 14 | AHCI use the SCSI API, mg_disk has a separate API, and systemace also has a |
| 15 | separate API. There are also MMC and USB APIs used outside of drivers/block, |
| 16 | those will be detailed in their specific documents. |
| 17 | |
| 18 | Block devices are described by block_dev_desc structure, that holds, among |
| 19 | other things, the read/write/erase callbacks. Block device structures are |
| 20 | stored in any way depending on the API, but can be accessed by |
| 21 | |
| 22 | block_dev_desc_t * $api_get_dev(int dev) |
| 23 | |
| 24 | function, as seen in disk/part.c. |
| 25 | |
| 26 | 1) SATA interface |
| 27 | ----------------- |
| 28 | |
| 29 | The SATA interface drivers implement the following functions: |
| 30 | |
| 31 | int init_sata(int dev) |
| 32 | int scan_sata(int dev) |
| 33 | ulong sata_read(int dev, ulong blknr, ulong blkcnt, void *buffer) |
| 34 | ulong sata_write(int dev, ulong blknr, ulong blkcnt, const void *buffer) |
| 35 | |
| 36 | Block devices are kept in sata_dev_desc[], which is prefilled with values |
| 37 | common to all SATA devices in cmd_sata.c, and then modified in init_sata |
| 38 | function in the drivers. Callbacks of the block device use SATA API |
| 39 | directly. The sata_get_dev function is defined in cmd_sata.c. |
| 40 | |
| 41 | 2) SCSI interface |
| 42 | ----------------- |
| 43 | |
| 44 | The SCSI interface drivers implement the following functions: |
| 45 | |
| 46 | void scsi_print_error(ccb *pccb) |
| 47 | int scsi_exec(ccb *pccb) |
| 48 | void scsi_bus_reset(void) |
| 49 | void scsi_low_level_init(int busdevfunc) |
| 50 | |
| 51 | The SCSI API works through the scsi_exec function, the actual operation |
| 52 | requested is found in the ccb structure. |
| 53 | |
| 54 | Block devices are kept in scsi_dev_desc[], which lives only in cmd_scsi.c. |
| 55 | Callbacks of the block device use functions from cmd_scsi.c, which in turn |
| 56 | call scsi_exec of the controller. The scsi_get_dev function is also defined |
| 57 | in cmd_scsi.c. |
| 58 | |
| 59 | 3) mg_disk interface |
| 60 | -------------------- |
| 61 | |
| 62 | The mg_disk interface drivers implement the following functions: |
| 63 | |
| 64 | struct mg_drv_data* mg_get_drv_data (void) |
| 65 | uint mg_disk_init (void) |
| 66 | uint mg_disk_read (u32 addr, u8 *buff, u32 len) |
| 67 | uint mg_disk_write(u32 addr, u8 *buff, u32 len) |
| 68 | uint mg_disk_write_sects(void *buff, u32 sect_num, u32 sect_cnt) |
| 69 | uint mg_disk_read_sects(void *buff, u32 sect_num, u32 sect_cnt) |
| 70 | |
| 71 | The mg_get_drv_data function is to be overridden per-board, but there are no |
| 72 | board in-tree that do this. |
| 73 | |
| 74 | Only one driver for this API exists, and it only supports one block device. |
| 75 | Callbacks for this device are implemented in mg_disk.c and call the mg_disk |
| 76 | API. The mg_disk_get_dev function is defined in mg_disk.c and ignores the |
| 77 | device number, always returning the same device. |
| 78 | |
| 79 | 4) systemace interface |
| 80 | ---------------------- |
| 81 | |
| 82 | The systemace interface does not define any driver API, and has no command |
| 83 | itself. The single defined function is systemace_get_devs() from |
| 84 | systemace.c, which returns a single static structure for the only supported |
| 85 | block device. Callbacks for this device are also implemented in systemace.c. |
| 86 | |
| 87 | 5) IDE interface |
| 88 | ---------------- |
| 89 | |
| 90 | The IDE interface drivers implement the following functions, but only if |
| 91 | CONFIG_IDE_AHB is set: |
| 92 | |
| 93 | uchar ide_read_register(int dev, unsigned int port); |
| 94 | void ide_write_register(int dev, unsigned int port, unsigned char val); |
| 95 | void ide_read_data(int dev, ulong *sect_buf, int words); |
| 96 | void ide_write_data(int dev, ulong *sect_buf, int words); |
| 97 | |
| 98 | The first two functions are called from ide_inb()/ide_outb(), and will |
| 99 | default to direct memory access if CONFIG_IDE_AHB is not set, or |
| 100 | ide_inb()/ide_outb() functions will get overridden by the board altogether. |
| 101 | |
| 102 | The second two functions are called from input_data()/output_data() |
| 103 | functions, and also default to direct memory access, but cannot be |
| 104 | overridden by the board. |
| 105 | |
| 106 | One function shared by IDE drivers (but not defined in ide.h) is |
| 107 | int ide_preinit(void) |
| 108 | This function gets called from ide_init in cmd_ide.c if CONFIG_IDE_PREINIT |
| 109 | is defined, and will do the driver-specific initialization of the device. |
| 110 | |
| 111 | Block devices are kept in ide_dev_desc[], which is filled in cmd_ide.c. |
| 112 | Callbacks of the block device are defined in cmd_ide.c, and use the |
| 113 | ide_inb()/ide_outb()/input_data()/output_data() functions mentioned above. |
| 114 | The ide_get_dev function is defined in cmd_ide.c. |
| 115 | |
| 116 | II) Approach |
| 117 | ------------ |
| 118 | |
| 119 | A new block controller core and an associated API will be created to mimic the |
| 120 | current SATA API, its drivers will have the following ops: |
| 121 | |
| 122 | struct block_ctrl_ops { |
| 123 | int scan(instance *i); |
| 124 | int reset(instance *i, int port); |
| 125 | lbaint_t read(instance *i, int port, lbaint_t start, lbatin_t length, |
| 126 | void *buffer); |
| 127 | lbaint_t write(instance *i, int port, lbaint_t start, lbatin_t length, |
| 128 | void*buffer); |
| 129 | } |
| 130 | |
| 131 | The current sata_init() function will be changed into the driver probe() |
| 132 | function. The read() and write() functions should never be called directly, |
| 133 | instead they should be called by block device driver for disks. |
| 134 | |
| 135 | Other block APIs would either be transformed into this API, or be kept as |
| 136 | legacy for old drivers, or be dropped altogether. |
| 137 | |
| 138 | Legacy driver APIs will each have its own driver core that will contain the |
| 139 | shared logic, which is currently located mostly in cmd_* files. Callbacks for |
| 140 | block device drivers will then probably be implemented as a part of the core |
| 141 | logic, and will use the driver ops (which will copy current state of |
| 142 | respective APIs) to do the work. |
| 143 | |
| 144 | All drivers will be cleaned up, most ifdefs should be converted into |
| 145 | platform_data, to enable support for multiple devices with different settings. |
| 146 | |
| 147 | A new block device core will also be created, and will keep track of all |
| 148 | block devices on all interfaces. |
| 149 | |
| 150 | Current block_dev_desc structure will be changed to fit the driver model, all |
| 151 | identification and configuration will be placed in private data, and |
| 152 | a single accessor and modifier will be defined, to accommodate the need for |
| 153 | different sets of options for different interfaces, while keeping the |
| 154 | structure small. The new block device drivers will have the following ops |
| 155 | structure (lbaint_t is either 32bit or 64bit unsigned, depending on |
| 156 | CONFIG_LBA48): |
| 157 | |
| 158 | struct blockdev_ops { |
| 159 | lbaint_t (*block_read)(struct instance *i, lbaint_t start, lbaint_t blkcnt, |
| 160 | void *buffer); |
| 161 | lbaint_t (*block_write)(struct instance *i, lbaint_t start, lbaint_t blkcnt, |
| 162 | void *buffer); |
| 163 | lbaint_t (*block_erase)(struct instance *i, lbaint_t start, lbaint_t blkcnt |
| 164 | ); |
| 165 | int (*get_option)(struct instance *i, enum blockdev_option_code op, |
| 166 | struct option *res); |
| 167 | int (*set_option)(struct instance *i, enum blockdev_option_code op, |
| 168 | struct option *val); |
| 169 | } |
| 170 | |
| 171 | struct option { |
| 172 | uint32_t flags |
| 173 | union data { |
| 174 | uint64_t data_u; |
| 175 | char* data_s; |
| 176 | void* data_p; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | enum blockdev_option_code { |
| 181 | BLKD_OPT_IFTYPE=0, |
| 182 | BLKD_OPT_TYPE, |
| 183 | BLKD_OPT_BLOCKSIZE, |
| 184 | BLKD_OPT_BLOCKCOUNT, |
| 185 | BLKD_OPT_REMOVABLE, |
| 186 | BLKD_OPT_LBA48, |
| 187 | BLKD_OPT_VENDOR, |
| 188 | BLKD_OPT_PRODICT, |
| 189 | BLKD_OPT_REVISION, |
| 190 | BLKD_OPT_SCSILUN, |
| 191 | BLKD_OPT_SCSITARGET, |
| 192 | BLKD_OPT_OFFSET |
| 193 | } |
| 194 | |
| 195 | Flags in option above will contain the type of returned data (which should be |
| 196 | checked against what is expected, even though the option requested should |
| 197 | specify it), and a flag to indicate whether the returned pointer needs to be |
| 198 | free()'d. |
| 199 | |
| 200 | The block device core will contain the logic now located in disk/part.c and |
| 201 | related files, and will be used to forward requests to block devices. The API |
| 202 | for the block device core will copy the ops of a block device (with a string |
| 203 | identifier instead of instance pointer). This means that partitions will also |
| 204 | be handled by the block device core, and exported as block devices, making |
| 205 | them transparent to the rest of the code. |
| 206 | |
| 207 | Sadly, this will change how file systems can access the devices, and thus will |
| 208 | affect a lot of places. However, these changes should be localized and easy to |
| 209 | implement. |
| 210 | |
| 211 | AHCI driver will be rewritten to fit the new unified block controller API, |
| 212 | making SCSI API easy to merge with sym53c8xx, or remove it once the device |
| 213 | driver has died. |
| 214 | |
| 215 | Optionally, IDE core may be changed into one driver with unified block |
| 216 | controller API, as most of it is already in one place and device drivers are |
| 217 | just sets of hooks. Additionally, mg_disk driver is unused and may be removed |
| 218 | in near future. |
| 219 | |
| 220 | |
| 221 | |
| 222 | III) Analysis of in-tree drivers |
| 223 | -------------------------------- |
| 224 | |
| 225 | 1) ahci.c |
| 226 | --------- |
| 227 | SCSI API, will be rewritten for a different API. |
| 228 | |
| 229 | 2) ata_piix.c |
| 230 | ------------- |
| 231 | SATA API, easy to port. |
| 232 | |
| 233 | 3) fsl_sata.c |
| 234 | ------------- |
| 235 | SATA API, few CONFIG macros, easy to port. |
| 236 | |
| 237 | 4) ftide020.c |
| 238 | ------------- |
| 239 | IDE API, defines CONFIG_IDE_AHB and ide_preinit hook functions. |
| 240 | |
| 241 | 5) mg_disk.c |
| 242 | ------------ |
| 243 | Single driver with mg_disk API, not much to change, easy to port. |
| 244 | |
| 245 | 6) mvsata_ide.c |
| 246 | --------------- |
| 247 | IDE API, only defines ide_preinit hook function. |
| 248 | |
| 249 | 7) mxc_ata.c |
| 250 | ------------ |
| 251 | IDE API, only defines ide_preinit hook function. |
| 252 | |
| 253 | 8) pata_bfin.c |
| 254 | -------------- |
| 255 | SATA API, easy to port. |
| 256 | |
| 257 | 9) sata_dwc.c |
| 258 | ------------- |
| 259 | SATA API, easy to port. |
| 260 | |
| 261 | 10) sata_sil3114.c |
| 262 | ------------------ |
| 263 | SATA API, easy to port. |
| 264 | |
| 265 | 11) sata_sil.c |
| 266 | -------------- |
| 267 | SATA API, easy to port. |
| 268 | |
| 269 | 12) sil680.c |
| 270 | ------------ |
| 271 | IDE API, only defines ide_preinit hook function. |
| 272 | |
| 273 | 13) sym53c8xx.c |
| 274 | --------------- |
| 275 | SCSI API, may be merged with code from cmd_scsi. |
| 276 | |
| 277 | 14) systemace.c |
| 278 | --------------- |
| 279 | Single driver with systemace API, not much to change, easy to port. |