제가 사용하는 imx6 cpu 는 드라이버가 아래와 같이 있습니다.


drivers/mmc/host/sdhci-esdhc-imx.c

1265 static struct platform_driver sdhci_esdhc_imx_driver = {

1266     .driver     = {

1267         .name   = "sdhci-esdhc-imx",

1268         .owner  = THIS_MODULE,

1269         .of_match_table = imx_esdhc_dt_ids,

1270         .pm = &sdhci_esdhc_pmops,

1271     },

1272     .id_table   = imx_esdhc_devtype,

1273     .probe      = sdhci_esdhc_imx_probe,

1274     .remove     = sdhci_esdhc_imx_remove,

1275 };

1276 

1277 module_platform_driver(sdhci_esdhc_imx_driver);

심플하죠?  하지만 매크로 짜증납니다 ㅠㅠ


1277 라인은 

203 /* module_platform_driver() - Helper macro for drivers that don't do

204  * anything special in module init/exit.  This eliminates a lot of

205  * boilerplate.  Each module may only use this macro once, and

206  * calling it replaces module_init() and module_exit()

207  */  

208 #define module_platform_driver(__platform_driver) \

209     module_driver(__platform_driver, platform_driver_register, \

210             platform_driver_unregister)


1155 #define module_driver(__driver, __register, __unregister, ...) \

1156 static int __init __driver##_init(void) \

1157 { \

1158     return __register(&(__driver) , ##__VA_ARGS__); \ 

1159 } \

1160 module_init(__driver##_init); \ 

1161 static void __exit __driver##_exit(void) \

1162 { \ 

1163     __unregister(&(__driver) , ##__VA_ARGS__); \

1164 } \ 

1165 module_exit(__driver##_exit);

요딴식으로 매크로가 되어 있어서 예전에 사용하던 코딩 형식을 이렇게 간단히 쓸수 있게 해주었지만

찾아보는 사람은 이게 뭐지? 하게 되어 있으니 마음에 들지 않네요.

대충 보시면 이름만 정해주면 등록해주는 함수를 자동으로 매크로에서 만들어줍니다.(이런거 싫다면 아래와 같이 직접코딩...)


static int __init sdhci_esdhc_imx_driver_init(void)

{

return platform_driver_registster(&sdhci_esdhc_imx_driver);

}

module_init(sdhci_esdhc_imx_driver_init);

static void __exit sdhci_esdhc_imx_driver_exit(void)

{

platform_driver_unregister(&sdhci_esdhc_imx_driver);

}

module_exit(sdhci_esdhc_imx_driver_exit);

이렇게 직접 코딩을 하셔도 됩니다.


자 다시 imx 의 platform 장치가 등록을 하게 되면 probe 가 불리워지게 됩니다.

바로 등록딘 sdhci_esdhc_imx_probe 가 불리워지는 거죠.

디바이스 트리에 의해서 플랫폼 장치의 호환성을 확인합니다.

오오... imx 25, 35, 51, 53, 6sx 6sl, 6q 까지 모두 호환이군요? 당연한건가?


본격적으로 sdhci_esdhc_imx_probe 을 파헤쳐 봅니다.


1006 static int sdhci_esdhc_imx_probe(struct platform_device *pdev)

1007 {

1008     const struct of_device_id *of_id =

1009             of_match_device(imx_esdhc_dt_ids, &pdev->dev);

1010     struct sdhci_pltfm_host *pltfm_host;

1011     struct sdhci_host *host;

1012     struct esdhc_platform_data *boarddata;

1013     int err;

1014     struct pltfm_imx_data *imx_data;

1015 

1016     host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata);

1017     if (IS_ERR(host))

1018         return PTR_ERR(host);


sdhci_pltfm_init 함수의 역할은 host 의 자료구조를 초기화하고

장치의 ioremap 을 통해서 디바이스에 접근할수 있도록 만들어주고

등록된 장치의 ops 에 init 함수가 존재할 경우 불러줍니다.

즉, 장치에 접근할수 있도록 준비해주고 초기화를 수행할게 있다면 하도록 해줍니다.


1020     pltfm_host = sdhci_priv(host);

1021 

1022     imx_data = devm_kzalloc(&pdev->dev, sizeof(*imx_data), GFP_KERNEL);

1023     if (!imx_data) {

1024         err = -ENOMEM;

1025         goto free_sdhci;

1026     }

1027 

1028     imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)

1029                           pdev->id_entry->driver_data;

1030     pltfm_host->priv = imx_data;

그냥 사용할 메모리 할당 받는 함수입니다.

그리고 디바이스 트리에서 현재 플랫폼의 드라이버 데이타를 가져옵니다.

하드웨어를 제어 정보가 다를테니까요.


1032     imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");

1033     if (IS_ERR(imx_data->clk_ipg)) {

1034         err = PTR_ERR(imx_data->clk_ipg);

1035         goto free_sdhci;

1036     }

1037 

1038     imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");

1039     if (IS_ERR(imx_data->clk_ahb)) {

1040         err = PTR_ERR(imx_data->clk_ahb);

1041         goto free_sdhci;

1042     }

1043 

1044     imx_data->clk_per = devm_clk_get(&pdev->dev, "per");

1045     if (IS_ERR(imx_data->clk_per)) {

1046         err = PTR_ERR(imx_data->clk_per);

1047         goto free_sdhci;

1048     }

1049 

1050     pltfm_host->clk = imx_data->clk_per;

1051 

1052     if (imx_data->socdata->flags & ESDHC_FLAG_BUSFREQ)

1053         request_bus_freq(BUS_FREQ_HIGH);

1054 

1055     clk_prepare_enable(imx_data->clk_per);

1056     clk_prepare_enable(imx_data->clk_ipg);

1057     clk_prepare_enable(imx_data->clk_ahb);

1058 

1059     imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);

1060     if (IS_ERR(imx_data->pinctrl)) {

1061         err = PTR_ERR(imx_data->pinctrl);

1062         goto disable_clk;

1063     }

1064 

1065     imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,

1066                         PINCTRL_STATE_DEFAULT);

1067     if (IS_ERR(imx_data->pins_default)) {

1068         err = PTR_ERR(imx_data->pins_default);

1069         dev_err(mmc_dev(host->mmc), "could not get default state\n");

1070         goto disable_clk;

1071     }

클럭을 구성합니다.


1073     host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;

1074 

1075     if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207)

1076         /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */

1077         host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK

1078             | SDHCI_QUIRK_BROKEN_ADMA;

1079 

1080     /*

1081      * The imx6q ROM code will change the default watermark level setting

1082      * to something insane.  Change it back here.

1083      */

1084     if (esdhc_is_usdhc(imx_data)) {

1085         writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);

1086         host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN |

1087                     SDHCI_QUIRK2_NOSTD_TIMEOUT_COUNTER;

1088         host->mmc->caps |= MMC_CAP_1_8V_DDR;

1089 

1090         /*

1091          * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL

1092          * TO1.1, it's harmless for MX6SL

1093          */

1094         writel(readl(host->ioaddr + 0x6c) | BIT(7), host->ioaddr + 0x6c);

1095         sdhci_esdhc_ops.get_max_timeout_counter =

1096                     esdhc_get_max_timeout_counter;

1097     }

1098 

1099     if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)

1100         sdhci_esdhc_ops.platform_execute_tuning =

1101                     esdhc_executing_tuning;

1102 

1103     if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)

1104         host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;

장치별 특성에 따른 세팅이 이루어지는 부분이네요



1106     boarddata = &imx_data->boarddata;

1107     if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {

1108         if (!host->mmc->parent->platform_data) {

1109             dev_err(mmc_dev(host->mmc), "no board data!\n");

1110             err = -EINVAL;

1111             goto disable_clk;

1112         }

1113         imx_data->boarddata = *((struct esdhc_platform_data *)

1114                     host->mmc->parent->platform_data);

1115     }

1116 

1117     /* write_protect */

1118     if (boarddata->wp_type == ESDHC_WP_GPIO) {

1119         err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio);

1120         if (err) {

1121             dev_err(mmc_dev(host->mmc),

1122                 "failed to request write-protect gpio!\n");

1123             goto disable_clk;

1124         }

1125         host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;

1126     }

보드별 그리고 wp 상태를 어떻게 알아내는지 구성합니다.


1128     /* card_detect */

1129     switch (boarddata->cd_type) {

1130     case ESDHC_CD_GPIO:

1131         err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio);

1132         if (err) {

1133             dev_err(mmc_dev(host->mmc),

1134                 "failed to request card-detect gpio!\n");

1135             goto disable_clk;

1136         }

1137         /* fall through */

1138 

1139     case ESDHC_CD_CONTROLLER:

1140         /* we have a working card_detect back */

1141         host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;

1142         break;

1143 

1144     case ESDHC_CD_PERMANENT:

1145         host->mmc->caps |= MMC_CAP_NONREMOVABLE;

1146         break;

1147 

1148     case ESDHC_CD_NONE:

1149         break;

1150     }

card detection 이 어떻게 되는지 구성하는군요.


1152     switch (boarddata->max_bus_width) {

1153     case 8:

1154         host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA;

1155         break;

1156     case 4:

1157         host->mmc->caps |= MMC_CAP_4_BIT_DATA;

1158         break;

1159     case 1:

1160     default:

1161         host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;

1162         break;

1163     }

버스폭을 어디까지 지원하는지 구성합니다.


1165     /* sdr50 and sdr104 needs work on 1.8v signal voltage */

1166     if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) {

1167         imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,

1168                         ESDHC_PINCTRL_STATE_100MHZ);

1169         imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,

1170                         ESDHC_PINCTRL_STATE_200MHZ);

1171         if (IS_ERR(imx_data->pins_100mhz) ||

1172                 IS_ERR(imx_data->pins_200mhz)) {

1173             dev_warn(mmc_dev(host->mmc),

1174                 "could not get ultra high speed state, work on normal mode\n");

1175             /* fall back to not support uhs by specify no 1.8v quirk */

1176             host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;

1177         }

1178     } else {

1179         host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;

1180     }

하드웨어 특성에 따른 voltage 구성에 따른 세팅을 합니다.

1182     device_set_wakeup_capable(&pdev->dev, 1);

1183 

1184     err = sdhci_add_host(host);

1185     if (err)

1186         goto disable_clk;

자 이제 호스트 드라이버가 등록이 됩니다.

쫓아가보면 거대한 함수덩어리가 있습니다.

여기서 host 에는 해당 mmc 와 관련된 모든 정보와 블록 장치와의 io 요구사항에 따른

자료들도 들어 있습니다.


사실 1184라인의  sdhci_add_host(host);  이 실행되면서 많은 일이 일어납니다.
장치가 등록이 되면서 장치 rescan 이 되어 우리가 알고 있는 파티션이 보이게 되어 있거든요.
그러한 부분은 다음번에 보도록 하겠습니다.