강좌 & 팁
제가 사용하는 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 요구사항에 따른
자료들도 들어 있습니다.