| The U-Boot Driver Model Project |
| =============================== |
| MMC system analysis |
| =================== |
| Marek Vasut <marek.vasut@gmail.com> |
| 2012-02-25 |
| |
| I) Overview |
| ----------- |
| |
| The MMC subsystem is already quite dynamic in it's nature. It's only necessary |
| to flip the subsystem to properly defined API. |
| |
| The probing process of MMC drivers start by calling "mmc_initialize()", |
| implemented by MMC framework, from the architecture initialization file. The |
| "mmc_initialize()" function in turn calls "board_mmc_init()" function and if |
| this doesn't succeed, "cpu_mmc_init()" function is called. It is important to |
| note that both of the "*_mmc_init()" functions have weak aliases to functions |
| which automatically fail. |
| |
| Both of the "*_mmc_init()" functions though serve only one purpose. To call |
| driver specific probe function, which in turn actually registers the driver with |
| MMC subsystem. Each of the driver specific probe functions is currently done in |
| very ad-hoc manner. |
| |
| The registration with the MMC subsystem is done by calling "mmc_register()", |
| whose argument is a runtime configured structure of information about the MMC |
| driver. Currently, the information structure is intermixed with driver's internal |
| data. The description of the structure follows: |
| |
| struct mmc { |
| /* |
| * API: Allows this driver to be a member of the linked list of all MMC drivers |
| * registered with MMC subsystem |
| */ |
| struct list_head link; |
| |
| /* DRIVER: Name of the registered driver */ |
| char name[32]; |
| |
| /* DRIVER: Driver's private data */ |
| void *priv; |
| |
| /* DRIVER: Voltages the host bus can provide */ |
| uint voltages; |
| |
| /* API: Version of the card */ |
| uint version; |
| |
| /* API: Test if the driver was already initialized */ |
| uint has_init; |
| |
| /* DRIVER: Minimum frequency the host bus can provide */ |
| uint f_min; |
| |
| /* DRIVER: Maximum frequency the host bus can provide */ |
| uint f_max; |
| |
| /* API: Is the card SDHC */ |
| int high_capacity; |
| |
| /* API: Actual width of the bus used by the current card */ |
| uint bus_width; |
| |
| /* |
| * DRIVER: Clock frequency to be configured on the host bus, this is read-only |
| * for the driver. |
| */ |
| uint clock; |
| |
| /* API: Capabilities of the card */ |
| uint card_caps; |
| |
| /* DRIVER: MMC bus capabilities */ |
| uint host_caps; |
| |
| /* API: Configuration and ID data retrieved from the card */ |
| uint ocr; |
| uint scr[2]; |
| uint csd[4]; |
| uint cid[4]; |
| ushort rca; |
| |
| /* API: Partition configuration */ |
| char part_config; |
| |
| /* API: Number of partitions */ |
| char part_num; |
| |
| /* API: Transmission speed */ |
| uint tran_speed; |
| |
| /* API: Read block length */ |
| uint read_bl_len; |
| |
| /* API: Write block length */ |
| uint write_bl_len; |
| |
| /* API: Erase group size */ |
| uint erase_grp_size; |
| |
| /* API: Capacity of the card */ |
| u64 capacity; |
| |
| /* API: Descriptor of this block device */ |
| block_dev_desc_t block_dev; |
| |
| /* DRIVER: Function used to submit command to the card */ |
| int (*send_cmd)(struct mmc *mmc, |
| struct mmc_cmd *cmd, struct mmc_data *data); |
| |
| /* DRIVER: Function used to configure the host */ |
| void (*set_ios)(struct mmc *mmc); |
| |
| /* DRIVER: Function used to initialize the host */ |
| int (*init)(struct mmc *mmc); |
| |
| /* DRIVER: Function used to report the status of Card Detect pin */ |
| int (*getcd)(struct mmc *mmc); |
| |
| /* |
| * DRIVER: Maximum amount of blocks sent during multiblock xfer, |
| * set to 0 to autodetect. |
| */ |
| uint b_max; |
| }; |
| |
| The API above is the new API used by most of the drivers. There're still drivers |
| in the tree that use old, legacy API though. |
| |
| 2) Approach |
| ----------- |
| |
| To convert the MMC subsystem to a proper driver model, the "struct mmc" |
| structure will have to be properly split in the first place. The result will |
| consist of multiple parts, first will be the structure defining operations |
| provided by the MMC driver: |
| |
| struct mmc_driver_ops { |
| /* Function used to submit command to the card */ |
| int (*send_cmd)(struct mmc *mmc, |
| struct mmc_cmd *cmd, struct mmc_data *data); |
| /* DRIVER: Function used to configure the host */ |
| void (*set_ios)(struct mmc *mmc); |
| /* Function used to initialize the host */ |
| int (*init)(struct mmc *mmc); |
| /* Function used to report the status of Card Detect pin */ |
| int (*getcd)(struct mmc *mmc); |
| } |
| |
| The second part will define the parameters of the MMC driver: |
| |
| struct mmc_driver_params { |
| /* Voltages the host bus can provide */ |
| uint32_t voltages; |
| /* Minimum frequency the host bus can provide */ |
| uint32_t f_min; |
| /* Maximum frequency the host bus can provide */ |
| uint32_t f_max; |
| /* MMC bus capabilities */ |
| uint32_t host_caps; |
| /* |
| * Maximum amount of blocks sent during multiblock xfer, |
| * set to 0 to autodetect. |
| */ |
| uint32_t b_max; |
| } |
| |
| And finally, the internal per-card data of the MMC subsystem core: |
| |
| struct mmc_card_props { |
| /* Version of the card */ |
| uint32_t version; |
| /* Test if the driver was already initializes */ |
| bool has_init; |
| /* Is the card SDHC */ |
| bool high_capacity; |
| /* Actual width of the bus used by the current card */ |
| uint8_t bus_width; |
| /* Capabilities of the card */ |
| uint32_t card_caps; |
| /* Configuration and ID data retrieved from the card */ |
| uint32_t ocr; |
| uint32_t scr[2]; |
| uint32_t csd[4]; |
| uint32_t cid[4]; |
| uint16_t rca; |
| /* Partition configuration */ |
| uint8_t part_config; |
| /* Number of partitions */ |
| uint8_t part_num; |
| /* Transmission speed */ |
| uint32_t tran_speed; |
| /* Read block length */ |
| uint32_t read_bl_len; |
| /* Write block length */ |
| uint32_t write_bl_len; |
| /* Erase group size */ |
| uint32_t erase_grp_size; |
| /* Capacity of the card */ |
| uint64_t capacity; |
| /* Descriptor of this block device */ |
| block_dev_desc_t block_dev; |
| } |
| |
| The probe() function will then register the MMC driver by calling: |
| |
| mmc_device_register(struct instance *i, struct mmc_driver_ops *o, |
| struct mmc_driver_params *p); |
| |
| The struct mmc_driver_params will have to be dynamic in some cases, but the |
| driver shouldn't modify it's contents elsewhere than in probe() call. |
| |
| Next, since the MMC drivers will now be consistently registered into the driver |
| tree from board file, the functions "board_mmc_init()" and "cpu_mmc_init()" will |
| disappear altogether. |
| |
| As for the legacy drivers, these will either be converted or removed altogether. |
| |
| III) Analysis of in-tree drivers |
| -------------------------------- |
| |
| arm_pl180_mmci.c |
| ---------------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| atmel_mci.c |
| ----------- |
| This driver uses the legacy API and should be removed unless converted. It is |
| probably possbible to replace this driver with gen_atmel_mci.c . No conversion |
| will be done on this driver. |
| |
| bfin_sdh.c |
| ---------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| davinci_mmc.c |
| ------------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| fsl_esdhc.c |
| ----------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple, unless some problem appears due to the FDT |
| component of the driver. |
| |
| ftsdc010_esdhc.c |
| ---------------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| gen_atmel_mci.c |
| --------------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| mmc_spi.c |
| --------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| mv_sdhci.c |
| ---------- |
| This is a component of the SDHCI support, allowing it to run on Marvell |
| Kirkwood chip. It is probable the SDHCI support will have to be modified to |
| allow calling functions from this file based on information passed via |
| platform_data. |
| |
| mxcmmc.c |
| -------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| mxsmmc.c |
| -------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| omap_hsmmc.c |
| ------------ |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| pxa_mmc.c |
| --------- |
| This driver uses the legacy API and is written in a severely ad-hoc manner. |
| This driver will be removed in favor of pxa_mmc_gen.c, which is proved to work |
| better and is already well tested. No conversion will be done on this driver |
| anymore. |
| |
| pxa_mmc_gen.c |
| ------------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| s5p_mmc.c |
| --------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| sdhci.c |
| ------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple, though it'd be necessary to modify this driver |
| to also support the Kirkwood series and probably also Tegra series of CPUs. |
| See the respective parts of this section for details. |
| |
| sh_mmcif.c |
| ---------- |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |
| |
| tegra2_mmc.c |
| ------------ |
| Follows the new API and also has a good encapsulation of the whole driver. The |
| conversion here will be simple. |