转自:http : //blog.csdn.net/liuhaoyutz

内核版本:3.10.1

 
一,s3cmci_ops分析

在上一篇文章中我们分析了Mini2440 MMC / SD驱动的探针函数s3cmci_probe。在该函数中初始化了结构mmc_host指针变量mmc,其中,设置mmc-> ops为s3cmci_ops,s3cmci_ops定义在drivers / mmc / host / s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1427static  struct  mmc_host_ops s3cmci_ops = {
  2. 1428 .request = s3cmci_request,
  3. 1429.set_ios = s3cmci_set_ios,
  4. 1430 .get_ro = s3cmci_get_ro,
  5. 1431 .get_cd = s3cmci_card_present,
  6. 1432.enable_sdio_irq = s3cmci_enable_sdio_irq,
  7. 1433};

struct mmc_host是mmc core层与主机层的接口,mmc_host.ops是控制主机完成用户请求的接口函数集,其类型是struct mmc_host_ops,该结构体定义在include / linux / mmc / host.h文件中:

[cpp] 查看纯文本
  1. 83structmmc_host_ops {
  2. 84     / *
  3. 85 *“启用”在主机被声明时被调用,并且“禁用”被调用
  4. 86 *当主机被释放时。“启用”和“禁用”已被弃用。
  5. 87 * /
  6. 88    int  (* enable)(struct  mmc_host * host);
  7. 89    int  (* disable)(struct  mmc_host * host);
  8. 90     / *
  9. 91 *主机可以实现pre_req和post_req
  10. 92 *命令来支持请求的双缓冲(准备一个
  11. 93 *请求,而另一个请求是活动的)。
  12. 94 * pre_req()必须后跟post_req()。
  13. 95 *撤消对pre_req()的调用,调用post_req()
  14. 96 *非零错误条件。
  15. 97 * /
  16. 98    void     (* post_req)(structmmc_host * host,  struct  mmc_request * req,
  17. 99                 int  err);
  18. 100    void     (* pre_req)(struct  mmc_host * host,  struct  mmc_request * req,
  19. 101                bool  is_first_req);
  20. 102    void     (* request)(struct  mmc_host * host,  struct  mmc_request * req);
  21. 103    / *
  22. 104 *避免频繁地调用这三个函数,或者在“fastpath”中,
  23. 105 *因为底层控制器可能会以昂贵的方式实施它们
  24. 106 *和/或缓慢的方式。
  25. 107 *
  26. 108 *另请注意,这些功能可能会休眠,所以不要打电话给他们
  27. 109 *在原子语境中!
  28. 110 *
  29. 111 * get_ro回调函数的返回值应为:
  30. 112 * 0用于读/写卡
  31. 113 * 1用于只读卡
  32. 114 * -ENOSYS不支持时(等于NULL回调)
  33. 115 *或错误发生错误的负errno值
  34. 116 *
  35. 117 * get_cd回调的返回值应为:
  36. 118 * 0为缺席卡
  37. 119 * 1为现在的卡
  38. 不支持120 * -ENOSYS(等于NULL回调)
  39. 121 *或错误发生错误的负errno值
  40. 122 * /
  41. 123    void     (* set_ios)(struct  mmc_host * host,  struct  mmc_ios * ios);
  42. 124    int  (* get_ro)(struct  mmc_host * host);
  43. 125    int  (* get_cd)(struct  mmc_host * host);
  44. 126
  45. 127    void     (* enable_sdio_irq)(structmmc_host * host,  int  enable);
  46. 128
  47. 129    / *可选回调HC怪癖* /
  48. 130    void     (* init_card)(struct  mmc_host * host,  struct  mmc_card * card);
  49. 131
  50. 132    int  (* start_signal_voltage_switch)(struct  mmc_host * host,  struct  mmc_ios * ios);
  51. 133
  52. 134    / *检查卡是否正在拉dat [0:3]低*
  53. 135    int  (* card_busy)(struct  mmc_host * host);
  54. 136
  55. 137    / * SD和eMMC卡的调谐命令操作码值不同* /
  56. 138    int  (* execute_tuning)(struct  mmc_host * host,u32 opcode);
  57. 139    int  (* select_drive_strength)(unsigned  int  max_dtr,  int  host_drv,intcard_drv);
  58. 140    void     (* hw_reset)(struct  mmc_host * host);
  59. 141    void     (* card_event)(structmmc_host * host);
  60. 142};

请求函数用于处理用户的请求。

set_ios函数用于设置SDI的控制参数,如时钟,总线宽度等等。

get_ro函数用于探测SD卡是否有写保护。

get_cd函数用于探测卡是否已插入插槽。

enable_sdio_irq函数用于启动或禁用SDI中断。

需要注意的是,为什么没有对MMC / SD进行读写的读取和写入函数呢?这是因为Linux的块设备的读写操作是通过请求函数完成的。

那么对于mini2440的,它的s3cmci_ops中的成员函数在什么时候会被调用呢举例如下?

在驱动器/ MMC /核心/ core.c文件中:

[cpp] 查看纯文本
  1. 194staticvoid
  2. 195mmc_start_request(struct  mmc_host * host,struct  mmc_request * mrq)
  3. 196 {
  4. 197#ifdef CONFIG_MMC_DEBUG
  5. 198 unsigned  int  i,sz;
  6. 199    struct  scatterlist * sg;
  7. 200#ENDIF
  8. 201
  9. 202    if  (mrq-> sbc){
  10. 203 pr_debug(“<%s:starting CMD%u arg%08x flags%08x> \ n” ,
  11. 204 mmc_hostname(host),mrq-> sbc-> opcode,
  12. 205 mrq-> sbc-> arg,mrq-> sbc-> flags);
  13. 206}
  14. 207
  15. 208 pr_debug(“%s:starting CMD%u arg%08x flags%08x \ n” ,
  16. 209 mmc_hostname(host),mrq-> cmd-> opcode,
  17. 210 mrq-> cmd-> arg,mrq-> cmd-> flags);
  18. 211
  19. 212    if  (mrq-> data){
  20. 213 pr_debug(“%s:blksz%dblocks%d flags%08x”
  21. 214            “tsac%d ms nsac%d \ n” ,
  22. 215 mmc_hostname(host),mrq-> data-> blksz,
  23. 216 mrq-> data-> blocks,mrq-> data-> flags,
  24. 217 mrq-> data-> timeout_ns / 1000000,
  25. 218 mrq-> data-> timeout_clks);
  26. 219}
  27. 220 221     if  (mrq-> stop){
  28. 222 pr_debug(“%s:CMD%u arg%08x flags%08x \ n” ,
  29. 223 mmc_hostname(host),mrq-> stop-> opcode,
  30. 224 mrq-> stop-> arg,mrq-> stop-> flags);
  31. 225}
  32. 226
  33. 227 WARN_ON(!host-> claim);
  34. 228
  35. 229 mrq-> cmd-> error = 0;
  36. 230 mrq-> cmd-> mrq = mrq;
  37. 231    if  (mrq-> data){
  38. 232 BUG_ON(mrq-> data-> blksz> host-> max_blk_size);
  39. 233 BUG_ON(mrq-> data-> blocks> host-> max_blk_count);
  40. 234 BUG_ON(mrq-> data-> blocks * mrq-> data-> blksz>
  41. 235 host-> max_req_size);
  42. 236
  43. 237#ifdef CONFIG_MMC_DEBUG
  44. 238 sz = 0;
  45. 239 for_each_sg(mrq-> data-> sg,sg,mrq-> data-> sg_len,i)
  46. 240 sz + = sg-> length;
  47. 241 BUG_ON(sz!= mrq-> data-> blocks * mrq-> data-> blksz);
  48. 242#ENDIF
  49. 243
  50. 244 mrq-> cmd-> data = mrq-> data;
  51. 245 mrq-> data-> error = 0;
  52. 246 mrq-> data-> mrq = mrq;
  53. 247        if  (mrq-> stop){
  54. 248 mrq-> data-> stop = mrq-> stop;
  55. 249 mrq-> stop-> error = 0;
  56. 250 mrq-> stop-> mrq = mrq;
  57. 251}
  58. 252}
  59. 253 mmc_host_clk_hold(host);
  60. 254 led_trigger_event(host-> led,LED_FULL);
  61. 255 host-> ops-> request(host,mrq);
  62. 256}

可以看到255行,调用了宿主 - > ops->请求函数,即s3cmci_request函数。

再比如,在驱动器/ MMC /核心/ core.c文件中:

[cpp] 查看纯文本
  1. 954 / *
  2. 955 *内部功能,实际的ios调用主机驱动程序,
  3. 956 *可选择打印一些调试输出。
  4. 957 * /
  5. 958static  inline void  mmc_set_ios(structmmc_host * host)
  6. 959 {
  7. 960    struct  mmc_ios * ios =&host-> ios;
  8. 961
  9. 962 pr_debug(“%s:clock%uHz busmode%u powermode%u cs%u Vdd%u”
  10. 963        “width%u timing%u \ n” ,
  11. 964 mmc_hostname(host),ios-> clock,ios-> bus_mode,
  12. 965 ios-> power_mode,ios-> chip_select,ios-> vdd,
  13. 966 ios-> bus_width,ios-> timing);
  14. 967
  15. 968    if  (ios-> clock> 0)
  16. 969 mmc_set_ungated(host);
  17. 970 host-> ops-> set_ios(host,ios);
  18. 971}

可以看到,970行,调用了宿主 - > ops-> set_ios函数,即s3cmci_set_ios函数。

下面我们就来看一下s3cmci_ops的各个成员函数的实现。

s3cmci_get_ro函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1372static  int  s3cmci_get_ro(structmmc_host * mmc)
  2. 1373 {
  3. 1374    struct  s3cmci_host * host = mmc_priv(mmc);
  4. 1375    struct  s3c24xx_mci_pdata * pdata = host-> pdata;
  5. 1376    int  ret;
  6. 1377
  7. 1378    if  (pdata-> no_wprotect)
  8. 1379        返回 0;
  9. 1380
  10. 1381 ret = gpio_get_value(pdata-> gpio_wprotect)?1:0;
  11. 1382 ret ^ = pdata-> wprotect_invert;
  12. 1383
  13. 1384    返回 RET;
  14. 1385}

1374行,由mmc_host取得s3cmci_host。

1375行,取得s3c24xx_mci_pdata,其它保存着SDI的平台数据。

1378行,如果s3c24xx_mci_pdata.no_wprotect为1,表明没有写保护开关,直接退出。例如MMC卡就没有写保护开关,只有SD卡才有写保护开关。

1381行,读取gpio_wprotect引脚电平,对于MINI2440,即GPH8引脚。

1382行,与pdata-> wprotect_invert执行异或操作,即反转上步得到GPH8引脚电平值。

s3cmci_card_present函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1254static  int  s3cmci_card_present(structmmc_host * mmc)
  2. 1255 {
  3. 1256    struct  s3cmci_host * host = mmc_priv(mmc);
  4. 1257    struct  s3c24xx_mci_pdata * pdata = host-> pdata;
  5. 1258    int  ret;
  6. 1259
  7. 1260    if  (pdata-> no_detect)
  8. 1261        返回 -ENOSYS;
  9. 1262
  10. 1263 ret = gpio_get_value(pdata-> gpio_detect)?0:1;
  11. 1264    return  ret ^ pdata-> detect_invert;
  12. 1265}

1256行,由mmc_host取得s3cmci_host。

1257行,取得s3c24xx_mci_pdata,其它保存着SDI的平台数据。

1260行,如果s3c24xx_mci_pdata.no_detect为1,表明没有卡探测引脚,直接退出。

1263行,读取gpio_detect引脚电平值,对于MINI2440,即GPG8引脚。

1264行,与pdata-> detect_invert进行异或操作,即反转上步得到的GPG8引脚电平值。

s3cmci_enable_sdio_irq函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1387static voids3cmci_enable_sdio_irq(struct  mmc_host * mmc,  int  enable)
  2. 1388 {
  3. 1389    struct  s3cmci_host * host = mmc_priv(mmc);
  4. 1390无符号  长 旗
  5. 1391 u32 con
  6. 1392
  7. 1393 local_irq_save(flags);
  8. 1394
  9. 1395 con = readl(host-> base + S3C2410_SDICON);
  10. 1396 host-> sdio_irqen = enable;
  11. 1397
  12. 1398    if  (enable == host-> sdio_irqen)
  13. 1399        goto  same_state;
  14. 1400
  15. 1401    if  (enable){
  16. 1402 con | = S3C2410_SDICON_SDIOIRQ;
  17. 1403 enable_imask(host,S3C2410_SDIIMSK_SDIOIRQ);
  18. 1404
  19. 1405        if  (!host-> irq_state &&!host-> irq_disabled){
  20. 1406 host-> irq_state =  true ;
  21. 1407 enable_irq(host-> irq);
  22. 1408}
  23. 1409}  else  {
  24. 1410 disable_imask(host,S3C2410_SDIIMSK_SDIOIRQ);
  25. 1411 con&=〜S3C2410_SDICON_SDIOIRQ;
  26. 1412
  27. 1413        if  (!host-> irq_enabled && host-> irq_state){
  28. 1414 disable_irq_nosync(host-> irq);
  29. 1415 host-> irq_state =  false ;
  30. 1416}
  31. 1417}
  32. 1418
  33. 1419 writel(con,host-> base + S3C2410_SDICON);
  34. 1420
  35. 1421 same_state:
  36. 1422 local_irq_restore(flags);
  37. 1423
  38. 1424 s3cmci_check_sdio_irq(host);
  39. 1425}

1389行,由mmc_host取得s3cmci_host。

1395行,读取SDICON即SDI控制寄存器的内容,保存在CON中。

1396行,我觉得这一行不应该存在,因为这一行将参数启用的值赋值给宿主>​​ sdio_irqen,但是1398行又接着判断启用与主机 - > sdio_irqen是否相等,如果相等就退出了。

1401年至1408年行,启用为1,使能SDIO中断。

1402行,S3C2410_SDICON_SDIOIRQ定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

57#define S3C2410_SDICON_SDIOIRQ(1 << 3)

对照S3C2440数据手册,可知这个宏用来设置SDICON寄存器的第3位,该位决定是否接收SDIO中断。

1403行,S3C2410_SDIIMSK_SDIOIRQ定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

105#define S3C2410_SDIIMSK_SDIOIRQ(1 << 12)

对照S3C2440数据手册,可知这个宏用来设置SDIIntMsk寄存器的第13位,该位决定当读等待请求发生时,SDI是否产生一个中断。

enable_imask函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 279staticinline u32 enable_imask(struct  s3cmci_host * host,u32 imask)
  2. 280 {
  3. 281 u32 newmask;
  4. 282
  5. 283 newmask = readl(host-> base + host-> sdiimsk);
  6. 284 newmask | = imask;
  7. 285
  8. 286 writel(newmask,host-> base + host-> sdiimsk);
  9. 287
  10. 288    返回 newmask;
  11. 289}

该函数用来设置SDIIntMsk寄存器。

1409至1416年行,使为0,禁用SDIO中断。

1419行,用新的骗子设置SDICON即SDI控制寄存器。

s3cmci_set_ios函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1306static  void  s3cmci_set_ios(structmmc_host * mmc,  struct  mmc_ios * ios)
  2. 1307 {
  3. 1308    struct  s3cmci_host * host = mmc_priv(mmc);
  4. 1309 u32 mci_con;
  5. 1310
  6. 1311    / *设置电源状态* /
  7. 1312
  8. 1313 mci_con = readl(host-> base + S3C2410_SDICON);
  9. 1314
  10. 1315    开关 (ios-> power_mode){
  11. 1316    案例 MMC_POWER_ON:
  12. 1317    箱 MMC_POWER_UP:
  13. 1318        / *在SD模式下配置GPE5 ... GPE10引脚* /
  14. 1319 s3c_gpio_cfgall_range(S3C2410_GPE(5),6,S3C_GPIO_SFN(2),
  15. 1320 S3C_GPIO_PULL_NONE);
  16. 1321
  17. 1322        if  (host-> pdata-> set_power)
  18. 1323 host-> pdata-> set_power(ios-> power_mode,ios-> vdd);
  19. 1324
  20. 1325        if  (!host-> is2440)
  21. 1326 mci_con | = S3C2410_SDICON_FIFORESET;
  22. 1327
  23. 1328        突破;
  24. 1329
  25. 1330    箱 MMC_POWER_OFF:
  26. 1331    默认值:
  27. 1332 gpio_direction_output(S3C2410_GPE(5),0);
  28. 1333
  29. 1334        if  (host-> is2440)
  30. 1335 mci_con | = S3C2440_SDICON_SDRESET;
  31. 1336
  32. 1337        if  (host-> pdata-> set_power)
  33. 1338 host-> pdata-> set_power(ios-> power_mode,ios-> vdd);
  34. 1339
  35. 1340        突破;
  36. 1341}
  37. 1342
  38. 1343 s3cmci_set_clk(host,ios);
  39. 1344
  40. 1345    / *设置CLOCK_ENABLE * /
  41. 1346    if  (ios-> clock)
  42. 1347 mci_con | = S3C2410_SDICON_CLOCKTYPE;
  43. 1348    其他
  44. 1349 mci_con&=〜S3C2410_SDICON_CLOCKTYPE;
  45. 1350
  46. 1351 writel(mci_con,host-> base + S3C2410_SDICON);
  47. 1352
  48. 1353    if  ((ios-> power_mode == MMC_POWER_ON)||
  49. 1354(ios-> power_mode == MMC_POWER_UP)){
  50. 1355 dbg(host,dbg_conf,  “以%lukHz运行(请求:%ukHz)\ n” ,
  51. 1356 host-> real_rate / 1000,ios-> clock / 1000);
  52. 1357}  else  {
  53. 1358 dbg(host,dbg_conf,  “掉电。\ n” );
  54. 1359}
  55. 1360
  56. 1361 host-> bus_width = ios-> bus_width;
  57. 1362}

1306行,参数ios是structmmc_ios类型指针.struct mmc_ios定义在include / linux / mmc / host.h文件中:

[cpp] 查看纯文本
  1. 22structmmc_ios {
  2. 23 unsigned  int     clock;          / *时钟频率* /
  3. 24 unsigned  short   vdd;
  4. 25
  5. 26 / * vdd从下方存储所选电压范围的位数。* /
  6. 27
  7. 28 unsigned  char    bus_mode;       / *命令输出模式* /
  8. 29
  9. 30#define MMC_BUSMODE_OPENDRAIN 1
  10. 31#define MMC_BUSMODE_PUSHPULL 2
  11. 32
  12. 33 unsigned  char    chip_select;        / * SPI芯片选择* /
  13. 34
  14. 35#define MMC_CS_DONTCARE 0
  15. 36#define MMC_CS_HIGH 1
  16. 37#define MMC_CS_LOW 2
  17. 38
  18. 39 unsigned  char    power_mode;     / *电源模式* /
  19. 40
  20. 41#define MMC_POWER_OFF 0
  21. 42#define MMC_POWER_UP 1
  22. 43#define MMC_POWER_ON 2
  23. 44
  24. 45 unsigned  char    bus_width;      / *数据总线宽度* /
  25. 46
  26. 47#define MMC_BUS_WIDTH_1 0
  27. 48#define MMC_BUS_WIDTH_4 2
  28. 49#define MMC_BUS_WIDTH_8 3
  29. 50
  30. 51无符号  字符   时序         / *使用时间规格* /
  31. 52
  32. 53#define MMC_TIMING_LEGACY 0
  33. 54#define MMC_TIMING_MMC_HS 1
  34. 55#define MMC_TIMING_SD_HS 2
  35. 56#define MMC_TIMING_UHS_SDR12 3
  36. 57#define MMC_TIMING_UHS_SDR25 4
  37. 58#define MMC_TIMING_UHS_SDR50 5
  38. 59#define MMC_TIMING_UHS_SDR104 6
  39. 60#define MMC_TIMING_UHS_DDR50 7
  40. 61#define MMC_TIMING_MMC_HS200 8
  41. 62
  42. 63#define MMC_SDR_MODE 0
  43. 64#define MMC_1_2V_DDR_MODE 1
  44. 65#define MMC_1_8V_DDR_MODE 2
  45. 66#define MMC_1_2V_SDR_MODE 3
  46. 67#define MMC_1_8V_SDR_MODE 4
  47. 68
  48. 69 unsigned  char   signal_voltage;     / *信号电压(1.8V或3.3V)* /
  49. 70
  50. 71#define MMC_SIGNAL_VOLTAGE_330 0
  51. 72#defineMMC_SIGNAL_VOLTAGE_180 1
  52. 73#define MMC_SIGNAL_VOLTAGE_120 2
  53. 74
  54. 75 unsigned  char    drv_type;       / *驱动程序类型(A,B,C,D)* /
  55. 76
  56. 77#define MMC_SET_DRIVER_TYPE_B 0
  57. 78#define MMC_SET_DRIVER_TYPE_A 1
  58. 79#define MMC_SET_DRIVER_TYPE_C 2
  59. 80#define MMC_SET_DRIVER_TYPE_D 3
  60. 81};

1308行,由mmc_host取得s3cmci_host。

1313行,读取SDICON即SDI控制寄存器的值,保存在mci_con中。

1316至1328年行,如果ios-> power_mode为MMC_POWER_ON或MMC_POWER_UP,则执行这个分支。

S3C_GPIO_SFN宏定义在拱/臂/高原三星/包含/高原/ GPIO-cfg.h文件中:

[cpp] 查看纯文本
  1. 66#defineS3C_GPIO_SPECIAL_MARK(0xfffffff0)
  2. 67#define S3C_GPIO_SPECIAL(x)(S3C_GPIO_SPECIAL_MARK |(x))
  3. 68
  4. 69 / *定义通用引脚配置* /
  5. 70#define S3C_GPIO_INPUT(S3C_GPIO_SPECIAL(0))
  6. 71#define S3C_GPIO_OUTPUT(S3C_GPIO_SPECIAL(1))
  7. 72#defineS3C_GPIO_SFN(x)(S3C_GPIO_SPECIAL(x))

S3C_GPIO_PULL_NONE宏定义在拱/臂/高原三星/包含/高原/ GPIO-cfg.h文件中:

[cpp] 查看纯文本
  1. 126 / *定义每个gpio引脚可用的pull-{up,down}的值。
  2. 127 *
  3. 128 *这些值控制弱上拉电阻的状态
  4. 129 *可用于S3C系列的大多数引脚。并不是所有的芯片都支持
  5. 130 *上或下设置,它可能依赖于正在使用的芯片
  6. 131 *用于特定模式是否可用。
  7. 132 * /
  8. 133#define S3C_GPIO_PULL_NONE((__force samsung_gpio_pull_t)0x00)
  9. 134#define S3C_GPIO_PULL_DOWN((__force samsung_gpio_pull_t)0x01)
  10. 135#define S3C_GPIO_PULL_UP((__force samsung_gpio_pull_t)0x02)

s3c_gpio_cfgall_range函数定义在驱动器/ GPIO / GPIO-samsung.c文件中:

[cpp] 查看纯文本
  1. 3150int s3c_gpio_cfgall_range(unsigned intstart,unsigned  int  nr,
  2. 3151 unsigned  int  cfg,samsung_gpio_pull_t pull)
  3. 3152 {
  4. 3153    int  ret;
  5. 3154
  6. 3155    for  (; nr> 0; nr--,start ++){
  7. 3156 s3c_gpio_setpull(start,pull);
  8. 3157 ret = s3c_gpio_cfgpin(start,cfg);
  9. 3158        if  (ret!= 0)
  10. 3159             返回 RET;
  11. 3160}
  12. 3161
  13. 3162    返回 0;
  14. 3163}

可以看到,s3c_gpio_cfgall_range函数设置从GPE5开始的6个GPIO,即GPE5,GPE6,GPE7,GPE8,GPE9,GPE10。使用参数CFG设置GPECON寄存器,使用参数拉设置GPEUP寄存器。

GPECON寄存器对应的位置被设置为01,即使能相关SDI功能。

一三三○年至1341年行,如果ios-> power_mode为MMC_POWER_OFF或者默认情况下,则执行这个分支。

1332行,调用gpio_direction_output(S3C2410_GPE(5),0)关闭SDI时钟。

1335行,mci_con | = S3C2440_SDICON_SDRESET,根据S3C2440数据手册,这句用于重置整个sd / mmc模块。

1343行,调用s3cmci_set_clk(主机,IOS)设置时钟,s3cmci_set_clk定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1283static  void  s3cmci_set_clk(structs3cmci_host * host,  struct  mmc_ios * ios)
  2. 1284 {
  3. 1285 u32 mci_psc;
  4. 1286
  5. 1287    / *设置时钟* /
  6. 1288    为 (mci_psc = 0; mci_psc <255; mci_psc ++){
  7. 1289 host-> real_rate = host-> clk_rate /(host-> clk_div *(mci_psc + 1));
  8. 1290
  9. 1291        if  (host-> real_rate <= ios-> clock)
  10. 1292             突破;
  11. 1293}
  12. 1294
  13. 1295    if  (mci_psc> 255)
  14. 1296 mci_psc = 255;
  15. 1297
  16. 1298 host-> prescaler = mci_psc;
  17. 1299 writel(host->预分频器,host-> base + S3C2410_SDIPRE);
  18. 1300
  19. 1301    / *如果请求的时钟为0,real_rate也将为0 *
  20. 1302    if  (ios-> clock == 0)
  21. 1303 host-> real_rate = 0;
  22. 1304}

1346至1349年行,如果ios->时钟不为0时,使能时钟,否则禁用时钟。

1351行,将mci_con写回SDICON寄存器。

1361行,用ios-> bus_width设置数据总线宽度宿主 - > bus_width。

s3cmci_request函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1267static voids3cmci_request(struct  mmc_host * mmc,  struct  mmc_request * mrq)
  2. 1268 {
  3. 1269     struct  s3cmci_host * host = mmc_priv(mmc);
  4. 1270
  5. 1271 host-> status =  “mmc request” ;
  6. 1272 host-> cmd_is_stop = 0;
  7. 1273 host-> mrq = mrq;
  8. 1274
  9. 1275     if  (s3cmci_card_present(mmc)== 0){
  10. 1276 dbg(host,dbg_err,  “%s:no mediumpresent \ n” ,__func__);
  11. 1277 host-> mrq-> cmd-> error = -ENOMEDIUM;
  12. 1278 mmc_request_done(mmc,mrq);
  13. 1279}  else
  14. 1280 s3cmci_send_request(mmc);
  15. 1281}

struct mmc_request代表一个请求,该结构体定义在include / linux / mmc / core.h文件中:

[cpp] 查看纯文本
  1. 127structmmc_request {
  2. 128     struct  mmc_command * sbc;      / * SET_BLOCK_COUNT for multiblock * /
  3. 129     struct  mmc_command * cmd;
  4. 130     struct  mmc_data * data;
  5. 131     struct  mmc_command * stop;
  6. 132
  7. 133     结构 完成;
  8. 134     void             (* done)(struct  mmc_request *); / *完成功能* /
  9. 135     struct  mmc_host * host;
  10. 136};

1271行,将宿主>状态设置为 “mmcrequest”,主机 - >状态主要用于记录请求处理所处的阶段及状态,方便调试使用。

1272行,设置宿主> cmd_is_stop为0,从字面上理解,我认为宿主> cmd_is_stop代表命令是否是停止命令(即有一个命令是停止),0表示不是停止命令。

1273行,将mmc_requestmrp保存在主机 - > MRQ中,方便以后使用。

1275年至1280年行,如果卡不存在,则调用mmc_request_done(MMC,MRQ)结束这次请求处理,否则,调用s3cmci_send_request(MMC)。

s3cmci_send_request函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1202static voids3cmci_send_request(struct  mmc_host * mmc)
  2. 1203 {
  3. 1204     struct  s3cmci_host * host = mmc_priv(mmc);
  4. 1205     struct  mmc_request * mrq = host-> mrq;
  5. 1206     struct  mmc_command * cmd = host-> cmd_is_stop?mrq-> stop:mrq-> cmd;
  6. 1207
  7. 1208 host-> ccnt ++;
  8. 1209 prepare_dbgmsg(host,cmd,host-> cmd_is_stop);
  9. 1210
  10. 1211     / *清除命令,数据和fifo状态注册
  11. 1212五十目清除只有在2440,但不会在2410受伤
  12. 1213 * /
  13. 1214 writel(0xFFFFFFFF,host-> base + S3C2410_SDICMDSTAT);
  14. 1215 writel(0xFFFFFFFF,host-> base + S3C2410_SDIDSTA);
  15. 1216 writel(0xFFFFFFFF,host-> base + S3C2410_SDIFSTA);
  16. 1217
  17. 1218     if  (cmd-> data){
  18. 1219         int  res = s3cmci_setup_data(host,cmd-> data);
  19. 1220
  20. 1221 host-> dcnt ++;
  21. 1222
  22. 1223         if  (res){
  23. 1224 dbg(host,dbg_err,  “setup dataerror%d \ n” ,res);
  24. 1225 cmd-> error = res;
  25. 1226 cmd-> data-> error = res;
  26. 1227
  27. 1228 mmc_request_done(mmc,mrq);
  28. 1229             回报;
  29. 1230}
  30. 1231
  31. 1232         if  (s3cmci_host_usedma(host))
  32. 1233 res = s3cmci_prepare_dma(host,cmd-> data);
  33. 1234         else
  34. 1235 res = s3cmci_prepare_pio(host,cmd-> data);
  35. 1236
  36. 1237         if  (res){
  37. 1238 dbg(host,dbg_err,“data prepareerror  %d \ n” ,res);
  38. 1239 cmd-> error = res;
  39. 1240 cmd-> data-> error = res;
  40. 1241
  41. 1242 mmc_request_done(mmc,mrq);
  42. 1243             回报;
  43. 1244}
  44. 1245}
  45. 1246
  46. 1247     / *发送命令* /
  47. 1248 s3cmci_send_command(host,cmd);
  48. 1249
  49. 1250     / *使能中断* /
  50. 1251 s3cmci_enable_irq(host,  true );
  51. 1252}

1204行,由structmmc_host得到结构s3cmci_host。

1205行,从宿主> MRQ取出mmc_request以便使用。

1206行,因为宿主> cmd_is_stop被设置为0,所以CMD被设置为mrq-> CMD。

1214-1216行,清空SDICmdSta寄存器,SDIDatSta寄存器和SDIFSTA寄存器。

1216至1245年行,如果CMD->数据不为0,即当前命令带有要处理的数据,则执行这个如果语句块,进行数据处理的准备工作。

1219行,调用s3cmci_setup_data(主机,CMD->数据),该函数定义在驱动/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1048static ints3cmci_setup_data(struct  s3cmci_host * host,  struct  mmc_data * data)
  2. 1049 {
  3. 1050 u32 dcon,imsk,stoptries = 3;
  4. 1051
  5. 1052     / *写DCON寄存器* /
  6. 1053
  7. 1054     if  (!data){
  8. 1055 writel(0,host-> base + S3C2410_SDIDCON);
  9. 1056         返回 0;
  10. 1057}
  11. 1058
  12. 1059     if  ((data-> blksz&3)!= 0){
  13. 1060         / *我们不能处理不对齐的块
  14. 1061 *一个块被转移。* /
  15. 1062
  16. 1063         if  (data-> blocks> 1){
  17. 1064 pr_warning(“%s:can not donon-word sizes block transfer(blksz%d)\ n” ,__func__,data-> blksz);
  18. 1065             返回 -EINVAL;
  19. 1066}
  20. 1067}
  21. 1068
  22. 1069     while  (readl(host-> base + S3C2410_SDIDSTA)&
  23. 1070(S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)){
  24. 1071
  25. 1072 dbg(host,dbg_err,
  26. 1073             “mci_setup_data()transferstillin progress。\ n” );
  27. 1074
  28. 1075 writel(S3C2410_SDIDCON_STOP,host-> base + S3C2410_SDIDCON);
  29. 1076 s3cmci_reset(host);
  30. 1077
  31. 1078         if  ((stoptries--)== 0){
  32. 1079 dbg_dumpregs(host,“DRF” );
  33. 1080             返回 -EINVAL;
  34. 1081}
  35. 1082}
  36. 1083
  37. 1084 dcon = data-> blocks&S3C2410_SDIDCON_BLKNUM_MASK;
  38. 1085
  39. 1086     if  (s3cmci_host_usedma(host))
  40. 1087 dcon | = S3C2410_SDIDCON_DMAEN;
  41. 1088
  42. 1089     if  (host-> bus_width == MMC_BUS_WIDTH_4)
  43. 1090 dcon | = S3C2410_SDIDCON_WIDEBUS;
  44. 1091
  45. 1092     if  (!(data-> flags&MMC_DATA_STREAM))
  46. 1093 dcon | = S3C2410_SDIDCON_BLOCKMODE;
  47. 1094
  48. 1095     if  (data-> flags&MMC_DATA_WRITE){
  49. 1096 dcon | = S3C2410_SDIDCON_TXAFTERRESP;
  50. 1097 dcon | = S3C2410_SDIDCON_XFER_TXSTART;
  51. 1098}
  52. 1099
  53. 1100     if  (data-> flags&MMC_DATA_READ){
  54. 1101 dcon | = S3C2410_SDIDCON_RXAFTERCMD;
  55. 1102 dcon | = S3C2410_SDIDCON_XFER_RXSTART;
  56. 1103}
  57. 1104
  58. 1105     if  (host-> is2440){
  59. 1106 dcon | = S3C2440_SDIDCON_DS_WORD;
  60. 1107 dcon | = S3C2440_SDIDCON_DATSTART;
  61. 1108}
  62. 1109
  63. 1110 writel(dcon,host-> base + S3C2410_SDIDCON);
  64. 1111
  65. 1112     / *写入BSIZE寄存器* /
  66. 1113
  67. 1114 writel(data-> blksz,host-> base + S3C2410_SDIBSIZE);
  68. 1115
  69. 1116     / *加入IMASK注册* /
  70. 1117 imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
  71. 1118 S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
  72. 1119
  73. 1120 enable_imask(host,imsk);
  74. 1121
  75. 1122     / *写入TIMER寄存器* /
  76. 1123
  77. 1124     if  (host-> is2440){
  78. 1125 writel(0x007FFFFF,host-> base + S3C2410_SDITIMER);
  79. 1126}  else  {
  80. 1127 writel(0x0000FFFF,host-> base + S3C2410_SDITIMER);
  81. 1128
  82. 1129         / * FIX:设置缓慢的时钟,以防止超时读*
  83. 1130         if  (data-> flags&MMC_DATA_READ)
  84. 1131 writel(0xFF,host-> base + S3C2410_SDIPRE);
  85. 1132}
  86. 1133
  87. 1134     返回 0;
  88. 1135}

1054-1057行,如果命令数据为空,则清零SDIDatCon寄存器。

1059至1067年行,根据数据表的描述,如果在多模块下必须分配字大小,即BLKSIZE [1:0] = 00,所以这里“与” 3来判断是不是单模块如果在单模块处理的情况下,模块数大于1,则出错退出。

1069至1082年行,循环判断是否有数据正在发送或接收,如果有,则停止传输,并复位时钟。最多循环3次。

1086年至1087年行,如果使用DMA传输,则使能SDIDatCon寄存器的第15位的DMA功能。

1089至1090年行,如果数据总线宽度为4线,则使能SDIDatCon寄存器的第16位宽总线WideBus功能。

一〇九二年至1093年行,配置SDIDatCon寄存器的第17位,数据传输模式为块传输模式。

1095至1098年行,如果是写数据,配置SDIDatCon寄存器的第20位,收到回应后开始写数据。然后配置SDIDatCon寄存器的第12,13位,设置为写模式。

一一〇〇年至1103年行,如果是读数据,配置SDIDatCon寄存器的第19位,命令发送后开始读数据。

然后配置SDIDatCon寄存器的第12,13位,设置为读模式。

1105至1108年行,如果是S3C2440,配置SDIDatCon寄存器的第22,23位为10,即传输单位为字。然后配置SDIDatCon寄存器的第14位,开始数据传输。

1110行,将DCON写入SDIDatCon寄存器。

1114行,将数据 - > blksz写入SDIBSize寄存器。

1117至1118年行,设置出现FIFO失败SDI中断使能;数据接收CRC错误SDI中断使能;数据接收超时SDI中断使能;数据计时为0SDI中断使能。

1120行,调用enable_imask使能设置的中断。

1124年至1132年行,设置SDIDTimer寄存器。

回到s3cmci_send_request函数:

1223-1230行,如果s3cmci_setup_data出错,则打印信息并退出。

1232年至1233年行,如果使用DMA,则调用s3cmci_prepare_dma函数,该函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中,关于DMA相关的函数,我们不再详细跟踪了。

1235行,如果没有使用DMA数据传输方式,则调用s3cmci_prepare_pio函数,即使用FIFO数据传输方式,具体来说,就是调用do_pio_write向FIFO中填充数据,当64字节的FIFO少于33字节时就会产生中断;或者从SD读数据,则先使能中断,当FIFO多于31字节时,则会调用中断服务程序,中断服务程序会调用do_pio_read读出FIFO的数据。

s3cmci_prepare_pio函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1139static ints3cmci_prepare_pio(struct  s3cmci_host * host,  struct  mmc_data * data)
  2. 1140 {
  3. 1141     int  rw =(data-> flags&MMC_DATA_WRITE)?1:0;
  4. 1142
  5. 1143 BUG_ON((data-> flags&BOTH_DIR)== BOTH_DIR);
  6. 1144
  7. 1145 host-> pio_sgptr = 0;
  8. 1146 host-> pio_bytes = 0;
  9. 1147 host-> pio_count = 0;
  10. 1148 host-> pio_active = rw?XFER_WRITE:XFER_READ;
  11. 1149
  12. 1150     if  (rw){
  13. 1151 do_pio_write(host);
  14. 1152 enable_imask(host,S3C2410_SDIIMSK_TXFIFOHALF);
  15. 1153}  else  {
  16. 1154 enable_imask(host,S3C2410_SDIIMSK_RXFIFOHALF
  17. 1155 | S3C2410_SDIIMSK_RXFIFOLAST);
  18. 1156}
  19. 1157
  20. 1158     返回 0;
  21. 1159}

1141行,根据数据 - >标志确定是读还是写。

1151行,如果是写,则调用do_pio_write函数.do_pio_write函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 520static  void  do_pio_write(struct  s3cmci_host * host)
  2. 521 {
  3. 522    void  __iomem * to_ptr;
  4. 523    int  res
  5. 524 u32 fifo;
  6. 525 u32 * ptr;
  7. 526
  8. 527 to_ptr = host-> base + host-> sdidata;
  9. 528
  10. 529    while  ((fifo = fifo_free(host))> 3){
  11. 530        if  (!host-> pio_bytes){
  12. 531 res = get_data_buffer(host,&host-> pio_bytes,
  13. 532&host-> pio_ptr);
  14. 533            if  (res){
  15. 534 dbg(host,dbg_pio,
  16. 535                     “pio_write():complete(no more data)。\ n” );
  17. 536 host-> pio_active = XFER_NONE;
  18. 537
  19. 538                 回;
  20. 539}
  21. 540
  22. 541 dbg(host,dbg_pio,
  23. 542                 “pio_write():new source:[%i] @ [%p] \ n” ,
  24. 543 host-> pio_bytes,host-> pio_ptr);
  25. 544
  26. 545}
  27. 546
  28. 547        / *如果我们已经到了最后,我们必须
  29. 548 *写入剩余的字节数。要是我们
  30. 549 *在块的中间,我们必须写满
  31. 550 *字,所以四舍五入到4. * /
  32. 551        if  (fifo> = host-> pio_bytes)
  33. 552 fifo = host-> pio_bytes;
  34. 553        其他
  35. 554 fifo - = fifo&3;
  36. 555
  37. 556 host-> pio_bytes - = fifo;
  38. 557 host-> pio_count + = fifo;
  39. 558
  40. 559 fifo =(fifo + 3)>> 2;
  41. 560 ptr = host-> pio_ptr;
  42. 561        而 (fifo--)
  43. 562 writel(* ptr ++,to_ptr);
  44. 563 host-> pio_ptr = ptr;
  45. 564}
  46. 565
  47. 566 enable_imask(host,S3C2410_SDIIMSK_TXFIFOHALF);
  48. 567}

527行,取得SDIDAT寄存器的物理地址保存在to_ptr变量中。

529行,调用fifo_free(主机)函数取得FIFO的剩余可用空间的字节数保存在FIFO变量中,这里,FIFO变量代表这次而循环最多可写的字节个数。如果剩余空间大于3个字节,即最小写一个字,则循环条件成立。

530-532行,如果宿主> pio_bytes为0,则调用get_data_buffer从分散聚集列表中取得保存要写的数据的缓冲区,缓冲区的长度和起始地址分别保存在get_data_buffer的2个参数宿主> pio_bytes和第3个参数宿主> pio_ptr中。

533-539行,如果get_data_buffer出错,打印信息退出。

551-552行,如果FIFO大于等于宿主> pio_bytes,即FIFO的可用空间大于等于保存要写数据的缓冲区长度,则将FIFO设置为主机 - > pio_bytes。

554行,如果fifo小于主机 - > pio_bytes,即FIFO的可用空间小于要写数据的缓冲区长度,则将fifo设置为fifo - (fifo&3)。从注释可以看到,这是为了保证以字为单位进行写操作。

556行,主机 - > pio_bytes- = fifo,保存这次写操作后,剩余的要写的字节数。

557行,主机 - > pio_count + = fifo,保存已经写了多个个字节。

559行,将字节数转化为字数。

561-562行,写数据到SDIDAT寄存器。

563行,主机 - > pio_ptr = ptr,保存当前还剩余要写数据的位置。

564行,结束这次虽然循环,回到529行重新执行上述过程。

566行,使能SDIIntMsk第4位,如果Tx FIFO填充满半,就产生中断。

回到s3cmci_prepare_pio函数中:

1152行,使能SDIIntMsk第4位,如果Tx FIFO填充满半,就产生中断。

1154行,使能SDIIntMsk第0位,如果Rx FIFO填充满半,就生产中断。

1155行,使能SDIIntMsk第2位,如果Rx FIFO读取了最后的数据,就产生中断。

回到s3cmci_send_request函数:

1248行,调用s3cmci_send_command(主机,CMD)发送命令。

该函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 1016static voids3cmci_send_command(struct  s3cmci_host * host,
  2. 1017                     struct  mmc_command * cmd)
  3. 1018 {
  4. 1019 u32 ccon,imsk;
  5. 1020
  6. 1021 imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
  7. 1022 S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
  8. 1023 S3C2410_SDIIMSK_RESPONSECRC;
  9. 1024
  10. 1025 enable_imask(host,imsk);
  11. 1026
  12. 1027     if  (cmd-> data)
  13. 1028 host-> complete_what = COMPLETION_XFERFINISH_RSPFIN;
  14. 1029     else if  (cmd-> flags&MMC_RSP_PRESENT)
  15. 1030 host-> complete_what = COMPLETION_RSPFIN;
  16. 1031     其他
  17. 1032 host-> complete_what = COMPLETION_CMDSENT;
  18. 1033
  19. 1034 writel(cmd-> arg,host-> base + S3C2410_SDICMDARG);
  20. 1035
  21. 1036 ccon = cmd-> opcode&S3C2410_SDICMDCON_INDEX;
  22. 1037 ccon | = S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
  23. 1038
  24. 1039     if  (cmd-> flags&MMC_RSP_PRESENT)
  25. 1040 ccon | = S3C2410_SDICMDCON_WAITRSP;
  26. 1041
  27. 1042     if  (cmd-> flags&MMC_RSP_136)
  28. 1043 ccon | = S3C2410_SDICMDCON_LONGRSP;
  29. 1044
  30. 1045 writel(ccon,host-> base + S3C2410_SDICMDCON);
  31. 1046}

一○二一年至1025年行,出现CRC状态错误,命令响应超时,接收命令响应,命令发送,响应CRC校验失败时,将产生SDI中断。

一零二七年至1032年行,设置宿主> complete_what。

在驱动器/ MMC /主机/ s3cmci.h文件中,有如下定义:

[cpp] 查看纯文本
  1. 11enum s3cmci_waitfor {
  2. 12 COMPLETION_NONE,
  3. 13 COMPLETION_FINALIZE,
  4. 14 COMPLETION_CMDSENT,
  5. 15 COMPLETION_RSPFIN,
  6. 16 COMPLETION_XFERFINISH,
  7. 17 COMPLETION_XFERFINISH_RSPFIN,
  8. 18};

另外,在驱动器/ MMC /主机/ s3cmci.c文件的s3cmci_irq函数的注释中,有如下内容:

[cpp] 查看纯文本
  1. 603 * host-> complete_what指示请求何时被认为完成
  2. 604 * COMPLETION_CMDSENT当命令发送时
  3. 605 * COMPLETION_RSPFIN当接收到响应
  4. 606 * COMPLETION_XFERFINISH数据传输完成
  5. 607 * COMPLETION_XFERFINISH_RSPFIN上述两者。

1034行,用CMD-> ARG设置设置SDICmdArg寄存器。

1036年至1045年行,配置SDICmdCon寄存器。

1036行,取得命令索引。

1037行,命令启动。

1039年至1040年行,配置主机等待响应。

1042至1043年行,配置主机接收136位长响应。

回到s3cmci_send_request函数:

1251行,使能中断。

至此,s3cmci_send_request函数我们就分析完了。

s3cmci_request函数我们也就分析完了。

s3cmci_ops结构体我们也就分析完了。

二,中断处理函数s3cmci_irq分析

SDI中断处理函数s3cmci_irq定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 599 / *
  2. 600 * ISR用于SDI接口IRQ
  3. 601 *驱动程序与ISRworks之间的通讯如下:
  4. 602 * host-> mrq指向电流请求
  5. 603 * host-> complete_what指示请求何时被认为完成
  6. 604 * COMPLETION_CMDSENT当命令发送时
  7. 605 * COMPLETION_RSPFIN当接收到响应
  8. 606 * COMPLETION_XFERFINISH数据传输完成
  9. 607 * COMPLETION_XFERFINISH_RSPFIN上述两者。
  10. 608 * host-> complete_request是驱动程序等待的完成对象
  11. 609 *
  12. 610 * 1)驱动程序设置host-> mrq andhost-> complete_what
  13. 611 * 2)司机准备转移
  14. 612 * 3)驱动器使能中断
  15. 613 * 4)驱动程序开始转移
  16. 614 * 5)驱动程序等待forhost-> complete_rquest
  17. 615 * 6)ISR检查请求状态(错误和成功)
  18. 616 * 6)ISR setshost-> mrq-> cmd-> error and host-> mrq-> data-> error
  19. 617 * 7)ISR completedhost-> complete_request
  20. 618 * 8)ISR禁止中断
  21. 619 * 9)司机醒来,照顾好了
  22. 620 *
  23. 621 *注意:“ - >错误”-fields在请求之前要设置为0
  24. 622 *由mmc.c发布 - 因此它们只能设置一个错误
  25. 623 *争执出现
  26. 624 * /
  27. 625
  28. 626static irqreturn_t s3cmci_irq(int  irq,  void * dev_id)
  29. 627 {
  30. 628    struct  s3cmci_host * host = dev_id;
  31. 629    struct  mmc_command * cmd;
  32. 630 u32 mci_csta,mci_dsta,mci_fsta,mci_dcnt,mci_imsk;
  33. 631 u32 mci_cclear = 0,mci_dclear;
  34. 632 unsigned  long  iflags;
  35. 633
  36. 634 mci_dsta = readl(host-> base + S3C2410_SDIDSTA);
  37. 635 mci_imsk = readl(host-> base + host-> sdiimsk);
  38. 636
  39. 637    if  (mci_dsta&S3C2410_SDIDSTA_SDIOIRQDETECT){
  40. 638        if  (mci_imsk&S3C2410_SDIIMSK_SDIOIRQ){
  41. 639 mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
  42. 640 writel(mci_dclear,host-> base + S3C2410_SDIDSTA);
  43. 641
  44. 642 mmc_signal_sdio_irq(host-> mmc);
  45. 643            返回 IRQ_HANDLED;
  46. 644}
  47. 645}
  48. 646
  49. 647 spin_lock_irqsave(&host-> complete_lock,iflags);
  50. 648
  51. 649 mci_csta = readl(host-> base + S3C2410_SDICMDSTAT);
  52. 650 mci_dcnt = readl(host-> base + S3C2410_SDIDCNT);
  53. 651 mci_fsta = readl(host-> base + S3C2410_SDIFSTA);
  54. 652 mci_dclear = 0;
  55. 653
  56. 654    if  ((host-> complete_what == COMPLETION_NONE)||
  57. 655(host-> complete_what == COMPLETION_FINALIZE)){
  58. 656 host-> status =  “nothing to complete” ;
  59. 657 clear_imask(host);
  60. 658        goto  irq_out;
  61. 659}
  62. 660
  63. 661    if  (!host-> mrq){
  64. 662 host-> status =  “no active mrq” ;
  65. 663 clear_imask(host);
  66. 664        goto  irq_out;
  67. 665}
  68. 666
  69. 667 cmd = host-> cmd_is_stop?host-> mrq-> stop:host-> mrq-> cmd;
  70. 668
  71. 669    if  (!cmd){
  72. 670 host-> status =  “no active cmd” ;
  73. 671 clear_imask(host);
  74. 672        goto  irq_out;
  75. 673}
  76. 674
  77. 675    if  (!s3cmci_host_usedma(host)){
  78. 676        if  ((host-> pio_active == XFER_WRITE)&&
  79. 677(mci_fsta&S3C2410_SDIFSTA_TFDET)){
  80. 678
  81. 679 disable_imask(host,S3C2410_SDIIMSK_TXFIFOHALF);
  82. 680 tasklet_schedule(&host-> pio_tasklet);
  83. 681 host-> status =  “pio tx” ;
  84. 682}
  85. 683
  86. 684        if  ((host-> pio_active == XFER_READ)&&
  87. 685(mci_fsta&S3C2410_SDIFSTA_RFDET)){
  88. 686
  89. 687 disable_imask(host,
  90. 688 S3C2410_SDIIMSK_RXFIFOHALF |
  91. 689 S3C2410_SDIIMSK_RXFIFOLAST);
  92. 690
  93. 691 tasklet_schedule(&host-> pio_tasklet);
  94. 692 host-> status =  “pio rx” ;
  95. 693}
  96. 694}
  97. 695
  98. 696    if  (mci_csta&S3C2410_SDICMDSTAT_CMDTIMEOUT){
  99. 697 dbg(host,dbg_err,  “CMDSTAT:error CMDTIMEOUT \ n” );
  100. 698 cmd-> error = -ETIMEDOUT;
  101. 699 host-> status =  “error:command timeout” ;
  102. 700        goto  fail_transfer;
  103. 701}
  104. 702
  105. 703    if  (mci_csta&S3C2410_SDICMDSTAT_CMDSENT){
  106. 704        if  (host-> complete_what == COMPLETION_CMDSENT){
  107. 705 host-> status =  “ok:command sent” ;
  108. 706            goto  close_transfer;
  109. 707}
  110. 708
  111. 709 mci_cclear | = S3C2410_SDICMDSTAT_CMDSENT;
  112. 710}
  113. 711
  114. 712    if  (mci_csta&S3C2410_SDICMDSTAT_CRCFAIL){
  115. 713        if  (cmd-> flags&MMC_RSP_CRC){
  116. 714            if  (host-> mrq-> cmd-> flags&MMC_RSP_136){
  117. 715 dbg(host,dbg_irq,
  118. 716                     “fixup:忽略具有长rsp \ n的CRCfail” );
  119. 717}  else  {
  120. 718                 / *注意,我们以前是转载失败
  121. 719 *在这里,但似乎这只是
  122. 720 *硬件得到它。
  123. 721 *
  124. 722 * cmd-> error = -EILSEQ;
  125. 723 * host-> status =“error:bad command crc”;
  126. 724 * goto fail_transfer;
  127. 725 * /
  128. 726}
  129. 727}
  130. 728
  131. 729 mci_cclear | = S3C2410_SDICMDSTAT_CRCFAIL;
  132. 730}
  133. 731
  134. 732     if  (mci_csta&S3C2410_SDICMDSTAT_RSPFIN){
  135. 733        if  (host-> complete_what == COMPLETION_RSPFIN){
  136. 734 host-> status =  “ok:收到命令响应” ;
  137. 735            goto  close_transfer;
  138. 736}
  139. 737
  140. 738        if  (host-> complete_what == COMPLETION_XFERFINISH_RSPFIN)
  141. 739 host-> complete_what = COMPLETION_XFERFINISH;
  142. 740
  143. 741 mci_cclear | = S3C2410_SDICMDSTAT_RSPFIN;
  144. 742}
  145. 743
  146. 744    / *之后处理的错误只是相关的
  147. 745当数据传输正在进行时* /
  148. 746
  149. 747    if  (!cmd-> data)
  150. 748        goto  clear_status_bits;
  151. 749
  152. 750    / *检查FIFO故障* /
  153. 751    if  (host-> is2440){
  154. 752        if  (mci_fsta&S3C2440_SDIFSTA_FIFOFAIL){
  155. 753 dbg(host,dbg_err,  “FIFO failure \ n” );
  156. 754 host-> mrq-> data-> error = -EILSEQ;
  157. 755 host-> status =  “error:2440 fifo failure” ;
  158. 756            goto  fail_transfer;
  159. 757}
  160. 758} else  {
  161. 759        if  (mci_dsta&S3C2410_SDIDSTA_FIFOFAIL){
  162. 760 dbg(host,dbg_err,  “FIFOfailure \ n” );
  163. 761 cmd-> data-> error = -EILSEQ;
  164. 762 host-> status =  “error:fifo failure” ;
  165. 763            goto  fail_transfer;
  166. 764}
  167. 765}
  168. 766
  169. 767    if  (mci_dsta&S3C2410_SDIDSTA_RXCRCFAIL){
  170. 768 dbg(host,dbg_err,  “bad data crc(outgoing)\ n” );
  171. 769 cmd-> data-> error = -EILSEQ;
  172. 770 host-> status =  “error:bad data crc(outgoing)” ;
  173. 771        goto  fail_transfer;
  174. 772}
  175. 773
  176. 774    if  (mci_dsta&S3C2410_SDIDSTA_CRCFAIL){
  177. 775 dbg(host,dbg_err,  “bad data crc(incoming)\ n” );
  178. 776 cmd-> data-> error = -EILSEQ;
  179. 777 host-> status =  “error:bad data crc(incoming)” ;
  180. 778        goto  fail_transfer;
  181. 779}
  182. 780
  183. 781    if  (mci_dsta&S3C2410_SDIDSTA_DATATIMEOUT){
  184. 782 dbg(host,dbg_err,  “data timeout \ n” );
  185. 783 cmd-> data-> error = -ETIMEDOUT;
  186. 784 host-> status =  “error:data timeout” ;
  187. 785        goto  fail_transfer;
  188. 786}
  189. 787
  190. 788    if  (mci_dsta&S3C2410_SDIDSTA_XFERFINISH){
  191. 789        if  (host-> complete_what == COMPLETION_XFERFINISH){
  192. 790 host-> status =  “ok:data transfer completed” ;
  193. 791            goto  close_transfer;
  194. 792}
  195. 793
  196. 794        if  (host-> complete_what == COMPLETION_XFERFINISH_RSPFIN)
  197. 795 host-> complete_what = COMPLETION_RSPFIN;
  198. 796
  199. 797 mci_dclear | = S3C2410_SDIDSTA_XFERFINISH;
  200. 798}
  201. 799
  202. 800clear_status_bits:
  203. 801 writel(mci_cclear,host-> base + S3C2410_SDICMDSTAT);
  204. 802 writel(mci_dclear,host-> base + S3C2410_SDIDSTA);
  205. 803
  206. 804    goto  irq_out;
  207. 805
  208. 806fail_transfer:
  209. 807 host-> pio_active = XFER_NONE;
  210. 808
  211. 809close_transfer:
  212. 810 host-> complete_what = COMPLETION_FINALIZE;
  213. 811
  214. 812 clear_imask(host);
  215. 813 tasklet_schedule(&host-> pio_tasklet);
  216. 814
  217. 815    goto  irq_out;
  218. 816
  219. 817irq_out:
  220. 818 dbg(host,dbg_irq,
  221. 819        “CSTA:0X%08X DSTA:0X%08X FSTA:0X%08X DCNT:0X%08xstatus:%S \ n”个,
  222. 820 mci_csta,mci_dsta,mci_fsta,mci_dcnt,host-> status);
  223. 821
  224. 822 spin_unlock_irqrestore(&host-> complete_lock,iflags);
  225. 823    返回 IRQ_HANDLED;
  226. 824
  227. 825}

在分析这个函数之前,请先看一下599-624行的注释。

628行,的dev_id是中断处理函数传递过来的structs3cmci_host指针。

634行,读取SDIDatSta寄存器,保存在mci_dsta变量中。

635行,读取SDIIntMsk寄存器,保存在mci_imsk变量中。

637行,S3C2410_SDIDSTA_SDIOIRQDETECT宏标志着SDIDatSta寄存器的第9位被置位,说明有SDIO中断被检测到。

638行,S3C2410_SDIIMSK_SDIOIRQ宏标志着SDIIntMsk寄存器的第12位被置位,表示使能SDI产生SDIO中断。

行639-640,根据数据表,这两句的作用是清零SDIDatSta寄存器的第9位。

642行,调用mmc_signal_sdio_irq函数处理SDIO中断,该函数定义在包括/ LINUX / MMC / host.h文件中:

[cpp] 查看纯文本
  1. 396static inlinevoid mmc_signal_sdio_irq(struct  mmc_host * host)
  2. 397 {
  3. 398 host-> ops-> enable_sdio_irq(host,0);
  4. 399 host-> sdio_irq_pending =  true ;
  5. 400 wake_up_process(host-> sdio_irq_thread);
  6. 401}

649行,读取SDICmdSta寄存器,保存在mci_csta变量中。

650行,读取SDIDatCnt寄存器,保存在mci_dcnt变量中。

651行,读取SDIFSTA寄存器,保存在mci_fsta变量中。

654-665行,做一些检查工作。

667行,设置CMD。

675-694行,如果没有使用DMA,则执行这个如果分支。

676-677行,如果宿主> pio_active为XFER_WRITE,并且SDIFSTA寄存器的第13位被置位,表明FIFO可以用于写操作。

679行,禁用TFHalf中断。

680行,调用主机 - > pio_tasklet。

681行,设置主机 - >状态为 “piotx”。

684-685行,如果宿主> pio_active为XFER_READ,并且SDIFSTA寄存器的第12位被置位,表明FIFO可以用于读操作。

687-689行,禁用Rx FIFO相关中断。

691行,调用主机 - > pio_tasklet。

692行,设置主机 - >状态为 “piorx”。

696-701行,处理命令超时。

703-710行,命令发送完成(不论是否得到应答)。

712-730行,处理CRC校验错误。

732-742行,处理收到命令应答。

750-798行,处理数据传输相关错误。

751-765行,处理FIFO相关错误。

767-772行,处理读数据CRC校验错误。

774-779行,处理发送数据时CRC校验错误。

781-786行,处理数据超时。

788-798行,处理数据传输结束。

下面我们来看宿主> pio_tasklet,在s3cmci_probe函数中,有如下语句:

1662 tasklet_init(&host-> pio_tasklet,pio_tasklet,(unsigned long)host);

可以看到,宿主> pio_tasklet对应的微进程函数为pio_tasklet,并将主机做为参数传递给该函数.pio_tasklet函数定义在驱动/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 569static  void  pio_tasklet(unsigned  long  data)
  2. 570 {
  3. 571    struct  s3cmci_host * host =(struct  s3cmci_host *)数据;
  4. 572
  5. 573 s3cmci_disable_irq(host,  true );
  6. 574
  7. 575    if  (host-> pio_active == XFER_WRITE)
  8. 576 do_pio_write(host);
  9. 577
  10. 578    if  (host-> pio_active == XFER_READ)
  11. 579 do_pio_read(host);
  12. 580
  13. 581    if  (host-> complete_what == COMPLETION_FINALIZE){
  14. 582 clear_imask(host);
  15. 583        if  (host-> pio_active!= XFER_NONE){
  16. 584 dbg(host,dbg_err,  “未完成%s”
  17. 585                 “ - pio_count:[%u] pio_bytes:[%u] \ n” ,
  18. 586(host-> pio_active == XFER_READ)? “读”  :  “写” ,
  19. 587 host-> pio_count,host-> pio_bytes);
  20. 588
  21. 589            if  (host-> mrq-> data)
  22. 590 host-> mrq-> data-> error = -EINVAL;
  23. 591}
  24. 592
  25. 593 s3cmci_enable_irq(host,  false );
  26. 594 finalize_request(host);
  27. 595} else
  28. 596 s3cmci_enable_irq(host,  true );
  29. 597}

575-576行,如果宿主> pio_active为XFER_WRITE,即写数据,则调用do_pio_write(主机)函数,该函数我们前面已经分析过了。

578-579行,如果宿主> pio_active为XFER_READ,即读数据,则调用do_pio_read(主机)函数,该函数定义在驱动/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 437static  void  do_pio_read(struct  s3cmci_host * host)
  2. 438 {
  3. 439    int  res
  4. 440 u32 fifo;
  5. 441 u32 * ptr;
  6. 442 u32 fifo_words;
  7. 443    void  __iomem * from_ptr;
  8. 444
  9. 445    / *写真实的预分频器到主机,它可能设置缓慢修复* /
  10. 446 writel(host->预分频器,host-> base + S3C2410_SDIPRE);
  11. 447
  12. 448 from_ptr = host-> base + host-> sdidata;
  13. 449
  14. 450    而 ((FIFO = fifo_count(主机))){
  15. 451        if  (!host-> pio_bytes){
  16. 452 res = get_data_buffer(host,&host-> pio_bytes,
  17. 453&host-> pio_ptr);
  18. 454            if  (res){
  19. 455 host-> pio_active = XFER_NONE;
  20. 456 host-> complete_what = COMPLETION_FINALIZE;
  21. 457
  22. 458 dbg(host,dbg_pio,“pio_read():”
  23. 459                     “complete(no moredata)。\ n” );
  24. 460                 回;
  25. 461}
  26. 462
  27. 463 dbg(host,dbg_pio,
  28. 464                 “pio_read():new target:[%i] @ [%p] \ n” ,
  29. 465 host-> pio_bytes,host-> pio_ptr);
  30. 466}
  31. 467
  32. 468 dbg(host,dbg_pio,
  33. 469            “pio_read():FIFO:[%02i]缓冲液:[%03i] DCNT:[%08X] \ n”个,
  34. 470 fifo,host-> pio_bytes,
  35. 471 readl(host-> base + S3C2410_SDIDCNT));
  36. 472
  37. 473        / *如果我们到了最后的块,我们可以
  38. 474 *读一个字,得到1到3个字节。如果我们在
  39. 475 *中间的块,我们必须阅读全文,
  40. 476 *否则我们会写垃圾,这样一来就可以了
  41. 477 *甚至倍数4. * /
  42. 478        if  (fifo> = host-> pio_bytes)
  43. 479 fifo = host-> pio_bytes;
  44. 480        其他
  45. 481 fifo - = fifo&3;
  46. 482
  47. 483 host-> pio_bytes - = fifo;
  48. 484 host-> pio_count + = fifo;
  49. 485
  50. 486 fifo_words = fifo >> 2;
  51. 487 ptr = host-> pio_ptr;
  52. 488        而 (fifo_words--)
  53. 489 * ptr ++ = readl(from_ptr);
  54. 490 host-> pio_ptr = ptr;
  55. 491
  56. 492        if  (fifo&3){
  57. 493 u32 n = fifo&3;
  58. 494 u32 data = readl(from_ptr);
  59. 495 u8 * p =(u8 *)host-> pio_ptr;
  60. 496
  61. 497            而 (N--){
  62. 498 * p ++ = data;
  63. 499数据>> = 8;
  64. 500}
  65. 501}
  66. 502}
  67. 503
  68. 504    if  (!host-> pio_bytes){
  69. 505 res = get_data_buffer(host,&host-> pio_bytes,&host-> pio_ptr);
  70. 506        if  (res){
  71. 507 dbg(host,dbg_pio,
  72. 508                “pio_read():complete(no more buffers)。\ n” );
  73. 509 host-> pio_active = XFER_NONE;
  74. 510 host-> complete_what = COMPLETION_FINALIZE;
  75. 511
  76. 512            回;
  77. 513}
  78. 514}
  79. 515
  80. 516 enable_imask(host,
  81. 517 S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
  82. 518}

446行,设置波特率预分频器寄存器SDIPRE。

448行,将SDI数据寄存器SDIDAT的虚拟地址保存在from_ptr变量中。

450行,调用fifo_count得到FIFO中可读取数据的字节数,保存在FIFO变量中。

451-466行,调用get_data_buffer函数,从分散聚集列表中获取用于存放被读取数据的缓冲区的相关信息,缓冲区的长度保存在主机 - > pio_bytes中,缓冲区的起始地址保存在主机 - > pio_ptr中如果get_data_buffer函数返回非0值,表示读操作完成。

478-481行,如果FIFO中可读取数据的字节数大于host-> pio_bytes(即缓冲区的大小),则将fifo设置为host-> pio_bytes,否则fifo - = fifo&3.从473- 477行的注释可以看出,这样做是为了按字来读取数据。

483行,修改主机 - > pio_bytes的值,缓冲区还有多少字节的空间。

484行,已经读取的数据的字节数保存在主机 - > pio_count变量中。

486行,以字为单位,要读取的数据个数保存在fifo_words变量中。

488-489行,循环读取数据。

490行,保存下次要读取的数据的起始位置到宿主> pio_ptr中。

492-501行,读取剩余的非字节对齐部分。

502行,结束这次而循环,回到450行,判断FIFO中是否还有可读的数据,如果有的话,继续进行读取操作。

504-514行,如果宿主> pio_bytes为0,并且get_data_buffer函数返回非0值,表示没有可用的缓冲区空间,读结束。

516-517行,便能读取中断。

至此,do_pio_read函数我们就分析完了。

回到pio_tasklet函数:

581-596行,如果命令处理结束,则调用finalize_request进行最后的处理否则,打开中断,继续监听中断.finalize_request函数定义在驱动程序/ MMC /主机/ s3cmci.c文件中:

[cpp] 查看纯文本
  1. 898static  void  finalize_request(structs3cmci_host * host)
  2. 899 {
  3. 900    struct  mmc_request * mrq = host-> mrq;
  4. 901    struct  mmc_command * cmd;
  5. 902 intdebug_as_failure = 0;
  6. 903
  7. 904    if  (host-> complete_what!= COMPLETION_FINALIZE)
  8. 905        回;
  9. 906
  10. 907    if  (!mrq)
  11. 908        回;
  12. 909 cmd = host-> cmd_is_stop?mrq-> stop:mrq-> cmd;
  13. 910
  14. 911    if  (cmd-> data &&(cmd-> error == 0)&&
  15. 912(cmd-> data-> error == 0)){
  16. 913        if  (s3cmci_host_usedma(host)&&(!host-> dma_complete)){
  17. 914 dbg(host,dbg_dma,  “DMA Missing(%d)!\ n” ,
  18. 915 host-> dma_complete);
  19. 916            回;
  20. 917}
  21. 918}
  22. 919
  23. 920    / *从控制器读取响应。* /
  24. 921 cmd-> resp [0] = readl(host-> base + S3C2410_SDIRSP0);
  25. 922 cmd-> resp [1] = readl(host-> base + S3C2410_SDIRSP1);
  26. 923 cmd-> resp [2] = readl(host-> base + S3C2410_SDIRSP2);
  27. 924 cmd-> resp [3] = readl(host-> base + S3C2410_SDIRSP3);
  28. 925
  29. 926 writel(host->预分频器,host-> base + S3C2410_SDIPRE);
  30. 927
  31. 928    if  (cmd-> error)
  32. 929 debug_as_failure = 1;
  33. 930
  34. 931    if  (cmd-> data && cmd-> data-> error)
  35. 932 debug_as_failure = 1;
  36. 933
  37. 934 dbg_dumpcmd(host,cmd,debug_as_failure);
  38. 935
  39. 936    / *清理控制器* /
  40. 937 writel(0,host-> base + S3C2410_SDICMDARG);
  41. 938 writel(S3C2410_SDIDCON_STOP,host-> base + S3C2410_SDIDCON);
  42. 939 writel(0,host-> base + S3C2410_SDICMDCON);
  43. 940 clear_imask(host);
  44. 941
  45. 942    if  (cmd-> data && cmd-> error)
  46. 943 cmd-> data-> error = cmd-> error;
  47. 944
  48. 945    if  (cmd-> data && cmd-> data-> stop &&(!host-> cmd_is_stop)){
  49. 946 host-> cmd_is_stop = 1;
  50. 947 s3cmci_send_request(host-> mmc);
  51. 948        回;
  52. 949}
  53. 950
  54. 951    / *如果我们没有数据传输,我们在这里完成* /
  55. 952    if  (!mrq-> data)
  56. 953        goto  request_done;
  57. 954
  58. 955    / *如果没有错误,则计算字节传输的数量* /
  59. 956    if  (mrq-> data-> error == 0){
  60. 957 mrq-> data-> bytes_xfered =
  61. 958(mrq-> data-> blocks * mrq-> data-> blksz);
  62. 959} else  {
  63. 960 mrq-> data-> bytes_xfered = 0;
  64. 961}
  65. 962
  66. 963    / *如果我们在传输数据时发生错误,我们刷新
  67. 964 * DMA通道和fifo清除任何垃圾。* /
  68. 965    if  (mrq-> data-> error!= 0){
  69. 966        if  (s3cmci_host_usedma(host))
  70. 967 s3c2410_dma_ctrl(host-> dma,S3C2410_DMAOP_FLUSH);
  71. 968
  72. 969        if  (host-> is2440){
  73. 970            / *清除故障寄存器并复位fifo。* /
  74. 971 writel(S3C2440_SDIFSTA_FIFORESET |
  75. 972 S3C2440_SDIFSTA_FIFOFAIL,
  76. 973 host-> base + S3C2410_SDIFSTA);
  77. 974}  else  {
  78. 975 u32 mci_con;
  79. 976
  80. 977            / * reset fifo * /
  81. 978 mci_con = readl(host-> base + S3C2410_SDICON);
  82. 979 mci_con | = S3C2410_SDICON_FIFORESET;
  83. 980
  84. 981 writel(mci_con,host-> base + S3C2410_SDICON);
  85. 982}
  86. 983}
  87. 984
  88. 985request_done:
  89. 986 host-> complete_what = COMPLETION_NONE;
  90. 987 host-> mrq = NULL;
  91. 988
  92. 989 s3cmci_check_sdio_irq(host);
  93. 990 mmc_request_done(host-> mmc,mrq);
  94. 991}

920-924行,读取SDIRSP0 -SDIRSP3寄存器,保存在cmd-> resp中。

926行,将宿主>预分频器写入SDIPRE寄存器。

937行,清零SDICmdArg寄存器。

938行,清零SDIDatCon寄存器,除了第14位设置为1,表示启动数据传输。

939行,清零SDICmdCon寄存器。

940行,清零SDIIntMsk寄存器,只允许SDIO中断。

945-949行,发送停止命令。

955-961行,计算传输的字节总数。

965-983行,如果数据传输过程出错,刷新DMA通道和FIFO,清除垃圾数据。

至此,finalize_request函数我们就分析完了,pio_tasklet函数我们也就分析完了,同时中断处理函数s3cmci_irq函数我们也就分析完了。

Linux的设备驱动程序架构分析之MMC / SD(二)相关推荐

  1. Linux设备驱动程序架构分析之一个I2C驱动实例

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 内核版本:3.10.1 编写一个I2C设备驱动程序的工作可分为两部分,一是定义和注册I2C设备,即i2c_clien ...

  2. Linux设备驱动程序架构分析之I2C架构(基于3.10.1内核)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 内核版本:3.10.1 I2C体系架构的硬件实体包括两部分: 硬件I2C Adapter:硬件I2C Adapter ...

  3. Linux设备驱动程序架构分析之SD Spec摘要

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 本文是对SDSpecifications Part 1 Physical Layer Simplified Spec ...

  4. linux设备驱动程序架构的研究,Linux设备驱动程序学习(12)-Linux设备模型(底层原理简介)...

    Linux设备驱动程序学习(12) -Linux设备模型(底层原理简介) 以<LDD3>的说法:Linux设备模型这部分内容可以认为是高级教材,对于多数程序作者来说是不必要的.但是我个人认 ...

  5. linux 卸载 usbmouse,8 Linux usbmouse设备驱动程序

    前一章节对linux内核中USB驱动程序的框架进行了分析,这一节以USB鼠标为对象,编写USB鼠标驱动程序. 实验内容:编写USB鼠标设备驱动程序.并将USB鼠标左键定义为"L"功 ...

  6. Linux字符设备驱动程序开发(1)-使用字符设备驱动

    1.编译/安装驱动 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此,编译/安装一个驱动程序,其实质就是编译/安装一个内核模块.把下面的范例代码拷贝到Linux系统中: memd ...

  7. 一张图掌握 Linux 字符设备驱动架构!【建议收藏】

    目录 一. Linux 中字符设备驱动简介 二. 字符设备驱动快速入门(超简单demo) 1. demo 2. 代码编译 3. 加载驱动模块 4. 创建设备节点文件 5. APP设备文件操作 6. 卸 ...

  8. Linux块设备驱动程序原理

    顾名思义,块设备驱动程序就是支持以块的方式进行读写的设备.块设备和字符设备最大的区别在于读写数据的基本单元不同.块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节 ...

  9. linux 查看 pci 设备驱动,如何写linux pci设备驱动程序

    PCI总线应用领域及其广泛并且令人惊奇.不同的pci设备有不同的需求以及不同的问题.因此,在linux内核中pci层支持就非常重要啦.本文档就是想为驱动程序设计开发人员解决pci处理中的各种问题. 0 ...

最新文章

  1. Python基础——PyCharm版本——第三章、数据类型和变量(超详细)
  2. Centos7下,简单DOCKER 使用.映射SSH端口到宿主主机.
  3. IDEA 热部署 仅支持jdk1.6,1.7
  4. Win7系统不能录音怎么办
  5. linux新硬盘分区及格式化和挂载分区到目录的方法
  6. ubuntu18.04+nvidia显卡安装+cuda9.0+cudnn7+pycharm2018.2专业版激活+anaconda3+tensorflow-gpu1.6.0+keras+opencv3
  7. 无需共享存储发布高性能的虚拟桌面
  8. Mandriva小记
  9. 每个人都应该具备点批判性思维
  10. 免费U盘数据恢复软件有哪些,如何免费恢复U盘的数据
  11. WP模板常用调用函数
  12. 程序员女盆友的学习日记(没内容)
  13. 阿里云-邮件推送 配置 购买域名 配置域名
  14. 常用正则表达式(手机,邮箱,身份证号,昵称,用户名,车型,车牌号)
  15. idea 使用mybatis generator生成代码时 报错 The specified target project directory src/main/java/ does not exis
  16. multiboot之ICAP
  17. zuk android系统耗电高,联想ZUK手机出现大规模卡顿耗电问题 解决办法让人忍俊不禁...
  18. Git GitHub 简明教程
  19. 推荐几本与领导力相关的书,这些书可以帮助你培养领导力
  20. 工业互联网的破局之道:存储资源盘活系统

热门文章

  1. 阿伏法机器人_深圳自动捷克钻不良QC机器人操作
  2. 一女生上厕所忘带纸了。
  3. mapreduce剖析气象站平均气温
  4. Windows: 如何配置IPv6隧道
  5. 变压器噪音分贝测试软件,一体机变压器绕组变形测试仪测试软件界面
  6. Dota2参议院(LeetCode)
  7. 如何设计一份优秀的 PPT 文档?
  8. asp怎么循环增加字段和字段对应的值_MySQL数据类型和字段属性原理与用法详解
  9. spark写入hbase任务报错:NoClassDefFoundError: com/yammer/metrics/Metrics
  10. java在圆上画刻度线_Java – 绘制标尺(带有90度角刻度线的线)