Chinaunix首页 | 论坛 | 博客
  • 博客访问: 307112
  • 博文数量: 53
  • 博客积分: 2510
  • 博客等级: 少校
  • 技术积分: 598
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-09 13:20
文章分类

全部博文(53)

文章存档

2011年(2)

2010年(22)

2009年(29)

我的朋友

分类: LINUX

2009-12-02 13:39:05

s3cmci的 io开关操作分析
一 s3cmci的io操作使用的数据结构
struct mmc_ios {
类型                 名称                             值
 
unsigned int        clock                           时钟速率
 
unsigned short      vdd                             电压
 
unsigned char      bus_mode                      #define MMC_BUSMODE_OPENDRAIN 1
                                                 #define MMC_BUSMODE_PUSHPULL 2
 
unsigned char      chip_select                   #define MMC_CS_DONTCARE 0
                                                 #define MMC_CS_HIGH 1
                                                 #define MMC_CS_LOW 2
 
unsigned char      power_mode                    #define MMC_POWER_OFF 0
                                                 #define MMC_POWER_UP 1
                                                 #define MMC_POWER_ON 2
 
unsigned char       bus_width                    #define MMC_BUS_WIDTH_1 0
                                                 #define MMC_BUS_WIDTH_4 2
 
unsigned char       timing                      #define MMC_TIMING_LEGACY 0
                                                #define MMC_TIMING_MMC_HS 1
                                                #define MMC_TIMING_SD_HS 2
};
二 s3cmci的io操作的处理函数
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
主要完成开电源和关电源时的各种寄存器的设置,该处理函数通过一个宿主机操作集(即一个struct mmc_host_ops结构)向MMC核心层注册自己,这样MMC核心层在需要开关电源时即通过调用注册的宿主机操作集中的.set_ios指向函数来完成.
io操作的处理函数的注册
s3cmci的宿主机操作集:
static struct mmc_host_ops s3cmci_ops = {
.request = s3cmci_request,
.set_ios = s3cmci_set_ios,
.get_ro = s3cmci_get_ro,
.get_cd = s3cmci_card_present,
};
在s3cmci平台驱动的.probe指向的函数处理中被注册
static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
{
...
mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
...
mmc->ops = &s3cmci_ops;
...
mmc_add_host(mmc);
}
关电操作
mmc_core.c
 
 
s3cmci.c
 
 
首先调用MMC核心函数 mmc_power_off(...)在此函数设置关电时设置参数,然后经过一个内部函数mmc_set_ios(...)的转换,调用宿主机自己的set_ios函数来处理.核心层只做通用的参数设置,对于不同的硬件的处理,由具体硬件的驱动根据IO设置参数来完成实际硬件的操作.
具体的操作主要完成以几件关机操作:
关闭时钟输出管脚,S3C2410_GPE5
设置电压,如果平台设置有自己的set_power处理,则调用之,
关闭时钟.
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
// 取得宿主机的私有数据结构.
struct s3cmci_host *host = mmc_priv(mmc);
u32 mci_psc, mci_con;
 
 
/* Set the power state */
// 读取当前宿主机的控制寄存器内容 .
mci_con = readl(host->base + S3C2410_SDICON);
 
 
switch (ios->power_mode) {
...
case MMC_POWER_OFF://
default:
// 关闭时钟输出管脚,S3C2410_GPE5 为0b10时用于SDCLK,现将其设为为0(输出)
s3c2410_gpio_setpin(S3C2410_GPE5, 0);
s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
......
// 如果平台设置有自己的set_power处理,则调用之.用于设置电压值.
if (host->pdata->set_power)
host->pdata->set_power(ios->power_mode, ios->vdd);
break;
}
/******************************************************************/
/* 设置时钟 */
// 设置最大的预引比例因子, 使得宿主机的时钟频率不小于ios->clock设定的值
for (mci_psc = 0; mci_psc < 255; mci_psc++) {
host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
 
 
if (host->real_rate <= ios->clock)
break;
}
if (mci_psc > 255)
mci_psc = 255;
// 保存宿主机的预比例因子.并写寄存器.
host->prescaler = mci_psc;
writel(host->prescaler, host->base + S3C2410_SDIPRE);
 
 
/* If requested clock is 0, real_rate will be 0, too */
if (ios->clock == 0)
host->real_rate = 0;
// 判断时钟使能,如果 ios->clock设定的时钟频率为0,则关闭时钟输出.写入寄存器.
if (ios->clock)
mci_con |= S3C2410_SDICON_CLOCKTYPE;
else
mci_con &= ~S3C2410_SDICON_CLOCKTYPE;//SDCLK out disable.
// 写控制寄存器
writel(mci_con, host->base + S3C2410_SDICON);
 
 
/* 设置时钟完毕*/
/**********************************************************************/
.....
// 保存总线宽度 .
host->bus_width = ios->bus_width;
}

开电操作
mmc_core.c
 
s3cmci.c
 
 
static void mmc_power_up(struct mmc_host *host)
{
// 获取电压设置的低位,这样就有方法得到最低的电压.
int bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
 
 
if (mmc_host_is_spi(host)) {
host->ios.chip_select = MMC_CS_HIGH;
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
} else {
host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
}
host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY;
// IO设置
mmc_set_ios(host);
 
 
// 等待到达最小的电压值.
// 等待两毫秒.
mmc_delay(2);
 
 
host->ios.clock = host->f_min;
host->ios.power_mode = MMC_POWER_ON;
// IO设置
mmc_set_ios(host);
// 等待至少74个时钟周期,或者一个足够的时间到达稳定电压.这里用2ms
mmc_delay(2);
}
 
 
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
// 取得宿主机的私有数据结构.
struct s3cmci_host *host = mmc_priv(mmc);
u32 mci_psc, mci_con;
 
 
/* Set the power state */
// 读取当前宿主机的控制寄存器内容 .
mci_con = readl(host->base + S3C2410_SDICON);
 
 
switch (ios->power_mode) {
case MMC_POWER_ON:
case MMC_POWER_UP:
// 设置SD卡的所有IO管脚功能
s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
// 设置电压
if (host->pdata->set_power)
host->pdata->set_power(ios->power_mode, ios->vdd);
 
 
if (!host->is2440)
mci_con |= S3C2410_SDICON_FIFORESET;
 
 
break;
......
}
/******************************************************************/
/* 设置时钟 */
// 设置最大的预引比例因子, 使得宿主机的时钟频率不小于ios->clock设定的值
for (mci_psc = 0; mci_psc < 255; mci_psc++) {
host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
 
 
if (host->real_rate <= ios->clock)
break;
}
if (mci_psc > 255)
mci_psc = 255;
// 保存宿主机的预比例因子.并写寄存器.
host->prescaler = mci_psc;
writel(host->prescaler, host->base + S3C2410_SDIPRE);
 
 
/* If requested clock is 0, real_rate will be 0, too */
if (ios->clock == 0)
host->real_rate = 0;
// 判断时钟使能,如果 ios->clock设定的时钟频率为0,则关闭时钟输出.写入寄存器.
if (ios->clock)
mci_con |= S3C2410_SDICON_CLOCKTYPE;
else
mci_con &= ~S3C2410_SDICON_CLOCKTYPE;//SDCLK out disable.
// 写控制寄存器
writel(mci_con, host->base + S3C2410_SDICON);
 
 
/* 设置时钟完毕*/
/**********************************************************************/
.....
// 保存总线宽度 .
host->bus_width = ios->bus_width;
}

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/dujie_abc/archive/2008/11/28/3401020.aspx
阅读(1741) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~