mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 12:08:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			280 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| 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, 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
 | |
| --------------------------------
 | |
| 
 | |
|   1) ahci.c
 | |
|   ---------
 | |
|     SCSI API, will be rewritten for a different API.
 | |
| 
 | |
|   2) ata_piix.c
 | |
|   -------------
 | |
|     SATA API, easy to port.
 | |
| 
 | |
|   3) fsl_sata.c
 | |
|   -------------
 | |
|     SATA API, few CONFIG macros, easy to port.
 | |
| 
 | |
|   4) ftide020.c
 | |
|   -------------
 | |
|     IDE API, defines CONFIG_IDE_AHB and ide_preinit hook functions.
 | |
| 
 | |
|   5) mg_disk.c
 | |
|   ------------
 | |
|     Single driver with mg_disk API, not much to change, easy to port.
 | |
| 
 | |
|   6) mvsata_ide.c
 | |
|   ---------------
 | |
|     IDE API, only defines ide_preinit hook function.
 | |
| 
 | |
|   7) mxc_ata.c
 | |
|   ------------
 | |
|     IDE API, only defines ide_preinit hook function.
 | |
| 
 | |
|   8) pata_bfin.c
 | |
|   --------------
 | |
|     SATA API, easy to port.
 | |
| 
 | |
|   9) sata_dwc.c
 | |
|   -------------
 | |
|     SATA API, easy to port.
 | |
| 
 | |
|   10) sata_sil3114.c
 | |
|   ------------------
 | |
|     SATA API, easy to port.
 | |
| 
 | |
|   11) sata_sil.c
 | |
|   --------------
 | |
|     SATA API, easy to port.
 | |
| 
 | |
|   12) sil680.c
 | |
|   ------------
 | |
|     IDE API, only defines ide_preinit hook function.
 | |
| 
 | |
|   13) sym53c8xx.c
 | |
|   ---------------
 | |
|     SCSI API, may be merged with code from cmd_scsi.
 | |
| 
 | |
|   14) systemace.c
 | |
|   ---------------
 | |
|     Single driver with systemace API, not much to change, easy to port.
 |