linux can驱动平台设备,linux2.6.28块设备mmc_sd卡mmc控制器平台驱动注册

分类: LINUX

2016-11-07 14:58:49

//

dMYAAAAAAAAA&bo=DQKFAQ0ChQEDACU%21

///

第一条线  mmc子系统核心初始化

drivers/mmc/core/core.c

//子系统初始化 subsys_initcall(mmc_init);

第二条线mmc控制器平台驱动注册

module_init(sdhci_drv_init);

第三条线  磁盘设备驱动注册drivers/mmc/card/block.c

module_init(mmc_blk_init);

dMYAAAAAAAAA&bo=gAS2AAAAAAADABc%21

dAgBAAAAAAAA&bo=nwTFAQAAAAADAHo%21

/

static int __init sdhci_drv_init(void)

{

printk(KERN_INFO DRIVER_NAME

": Secure Digital Host Controller Interface driver\n");

printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");

return 0;

}

module_init(sdhci_drv_init);

///

的sdhci_s3c_probe函数调用sdhci_add_host

int sdhci_add_host(struct sdhci_host *host)

{

struct mmc_host *mmc;

unsigned int caps;

int ret;

WARN_ON(host == NULL);

if (host == NULL)

return -EINVAL;

mmc = host->mmc;

if (debug_quirks)

host->quirks = debug_quirks;

sdhci_reset(host, SDHCI_RESET_ALL);

host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);

host->version = (host->version & SDHCI_SPEC_VER_MASK)

>> SDHCI_SPEC_VER_SHIFT;

if (host->version > SDHCI_SPEC_200) {

printk(KERN_ERR "%s: Unknown controller version (%d). "

"You may experience problems.\n", mmc_hostname(mmc),

host->version);

}

caps = readl(host->ioaddr + SDHCI_CAPABILITIES);

if (host->quirks & SDHCI_QUIRK_FORCE_DMA)

host->flags |= SDHCI_USE_DMA;

else if (!(caps & SDHCI_CAN_DO_DMA))

DBG("Controller doesn't have DMA capability\n");

else

host->flags |= SDHCI_USE_DMA;

if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) &&

(host->flags & SDHCI_USE_DMA)) {

DBG("Disabling DMA as it is marked broken\n");

host->flags &= ~SDHCI_USE_DMA;

}

if (host->flags & SDHCI_USE_DMA) {

if ((host->version >= SDHCI_SPEC_200) &&

(caps & SDHCI_CAN_DO_ADMA2))

host->flags |= SDHCI_USE_ADMA;

}

if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&

(host->flags & SDHCI_USE_ADMA)) {

DBG("Disabling ADMA as it is marked broken\n");

host->flags &= ~SDHCI_USE_ADMA;

}

if (host->flags & SDHCI_USE_DMA) {

if (host->ops->enable_dma) {

if (host->ops->enable_dma(host)) {

printk(KERN_WARNING "%s: No suitable DMA "

"available. Falling back to PIO.\n",

mmc_hostname(mmc));

host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA);

}

}

}

if (host->flags & SDHCI_USE_ADMA) {

/*

* We need to allocate descriptors for all sg entries

* (128) and potentially one alignment transfer for

* each of those entries.

*/

host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL);

host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);

if (!host->adma_desc || !host->align_buffer) {

kfree(host->adma_desc);

kfree(host->align_buffer);

printk(KERN_WARNING "%s: Unable to allocate ADMA "

"buffers. Falling back to standard DMA.\n",

mmc_hostname(mmc));

host->flags &= ~SDHCI_USE_ADMA;

}

}

/*

* If we use DMA, then it's up to the caller to set the DMA

* mask, but PIO does not need the hw shim so we set a new

* mask here in that case.

*/

if (!(host->flags & SDHCI_USE_DMA)) {

host->dma_mask = DMA_BIT_MASK(64);

mmc_dev(host->mmc)->dma_mask = &host->dma_mask;

}

if (host->ops->get_max_clock)

host->max_clk = host->ops->get_max_clock(host);

else {

host->max_clk =    (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;

host->max_clk *= 1000000;

}

if (host->max_clk == 0) {

printk(KERN_ERR "%s: Hardware doesn't specify base clock "

"frequency.\n", mmc_hostname(mmc));

return -ENODEV;

}

if (host->ops->get_timeout_clock)

host->timeout_clk = host->ops->get_timeout_clock(host);

else

host->timeout_clk =

(caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;

if (host->timeout_clk == 0) {

printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "

"frequency.\n", mmc_hostname(mmc));

return -ENODEV;

}

if (caps & SDHCI_TIMEOUT_CLK_UNIT)

host->timeout_clk *= 1000;

/*

* Set host parameters.

*/

//设置MMC操作接口

mmc->ops = &sdhci_ops;

mmc->f_min = host->max_clk / 256;

mmc->f_max = host->max_clk;

#if defined(CONFIG_MMC_SDHCI_S3C) || defined(CONFIG_MMC_SDHCI_MODULE)

mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;

#else

mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;

#endif

if ((caps & SDHCI_CAN_DO_HISPD) ||

(host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED))

mmc->caps |= (MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED);

mmc->ocr_avail = 0;

if (caps & SDHCI_CAN_VDD_330)

mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;

if (caps & SDHCI_CAN_VDD_300)

mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;

if (caps & SDHCI_CAN_VDD_180)

mmc->ocr_avail |= MMC_VDD_165_195;

if (mmc->ocr_avail == 0) {

printk(KERN_ERR "%s: Hardware doesn't report any "

"support voltages.\n", mmc_hostname(mmc));

return -ENODEV;

}

spin_lock_init(&host->lock);

/*

* Maximum number of segments. Depends on if the hardware

* can do scatter/gather or not.

*/

if (host->flags & SDHCI_USE_ADMA)

mmc->max_hw_segs = 128;

else if (host->flags & SDHCI_USE_DMA)

mmc->max_hw_segs = 1;

else /* PIO */

mmc->max_hw_segs = 128;

mmc->max_phys_segs = 128;

/*

* Maximum number of sectors in one transfer. Limited by DMA boundary

* size (512KiB).

*/

mmc->max_req_size = 524288;

/*

* Maximum segment size. Could be one segment with the maximum number

* of bytes. When doing hardware scatter/gather, each entry cannot

* be larger than 64 KiB though.

*/

if (host->flags & SDHCI_USE_ADMA)

mmc->max_seg_size = 65536;

else

mmc->max_seg_size = mmc->max_req_size;

/*

* Maximum block size. This varies from controller to controller and

* is specified in the capabilities register.

*/

mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;

if (mmc->max_blk_size >= 3) {

printk(KERN_WARNING "%s: Invalid maximum block size, "

"assuming 512 bytes\n", mmc_hostname(mmc));

mmc->max_blk_size = 512;

} else

mmc->max_blk_size = 512 << mmc->max_blk_size;

/*

* Maximum block count.

*/

mmc->max_blk_count = 65535;

/*

* Init tasklets.

*/

//添加两个tasklet并启动计时器

tasklet_init(&host->card_tasklet,

sdhci_tasklet_card, (unsigned long)host);

tasklet_init(&host->finish_tasklet,

sdhci_tasklet_finish, (unsigned long)host);

setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);

//中断映射,设置了可以共享标志

ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,

mmc_hostname(mmc), host);

if (ret)

goto untasklet;

//初始化MMC设备,包括复位控制器

sdhci_init(host);

#ifdef CONFIG_MMC_DEBUG

sdhci_dumpregs(host);

#endif

#ifdef CONFIG_LEDS_CLASS

host->led.name = mmc_hostname(mmc);

host->led.brightness = LED_OFF;

host->led.default_trigger = mmc_hostname(mmc);

host->led.brightness_set = sdhci_led_control;

ret = led_classdev_register(mmc_dev(mmc), &host->led);

if (ret)

goto reset;

#endif

mmiowb();

//加入MMC子系统

mmc_add_host(mmc);

printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n",

mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),

(host->flags & SDHCI_USE_ADMA)?"A":"",

(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");

return 0;

#ifdef CONFIG_LEDS_CLASS

reset:

sdhci_reset(host, SDHCI_RESET_ALL);

free_irq(host->irq, host);

#endif

untasklet:

tasklet_kill(&host->card_tasklet);

tasklet_kill(&host->finish_tasklet);

return ret;

}

/

struct mmc_host {

struct device        *parent;

struct device        class_dev;

int            index;

const struct mmc_host_ops *ops;

unsigned int        f_min;

unsigned int        f_max;

u32            ocr_avail;

#define MMC_VDD_165_195        0x00000080    /* VDD voltage 1.65 - 1.95 */

#define MMC_VDD_20_21        0x00000100    /* VDD voltage 2.0 ~ 2.1 */

#define MMC_VDD_21_22        0x00000200    /* VDD voltage 2.1 ~ 2.2 */

#define MMC_VDD_22_23        0x00000400    /* VDD voltage 2.2 ~ 2.3 */

#define MMC_VDD_23_24        0x00000800    /* VDD voltage 2.3 ~ 2.4 */

#define MMC_VDD_24_25        0x00001000    /* VDD voltage 2.4 ~ 2.5 */

#define MMC_VDD_25_26        0x00002000    /* VDD voltage 2.5 ~ 2.6 */

#define MMC_VDD_26_27        0x00004000    /* VDD voltage 2.6 ~ 2.7 */

#define MMC_VDD_27_28        0x00008000    /* VDD voltage 2.7 ~ 2.8 */

#define MMC_VDD_28_29        0x00010000    /* VDD voltage 2.8 ~ 2.9 */

#define MMC_VDD_29_30        0x00020000    /* VDD voltage 2.9 ~ 3.0 */

#define MMC_VDD_30_31        0x00040000    /* VDD voltage 3.0 ~ 3.1 */

#define MMC_VDD_31_32        0x00080000    /* VDD voltage 3.1 ~ 3.2 */

#define MMC_VDD_32_33        0x00100000    /* VDD voltage 3.2 ~ 3.3 */

#define MMC_VDD_33_34        0x00200000    /* VDD voltage 3.3 ~ 3.4 */

#define MMC_VDD_34_35        0x00400000    /* VDD voltage 3.4 ~ 3.5 */

#define MMC_VDD_35_36        0x00800000    /* VDD voltage 3.5 ~ 3.6 */

unsigned long        caps;        /* Host capabilities */

#define MMC_CAP_4_BIT_DATA    (1 << 0)    /* Can the host do 4 bit transfers */

#define MMC_CAP_MMC_HIGHSPEED    (1 << 1)    /* Can do MMC high-speed timing */

#define MMC_CAP_SD_HIGHSPEED    (1 << 2)    /* Can do SD high-speed timing */

#define MMC_CAP_SDIO_IRQ    (1 << 3)    /* Can signal pending SDIO IRQs */

#define MMC_CAP_SPI        (1 << 4)    /* Talks only SPI protocols */

#define MMC_CAP_NEEDS_POLL    (1 << 5)    /* Needs polling for card-detection */

#define MMC_CAP_8_BIT_DATA    (1 << 6)    /* Can the host do 8 bit transfers */

#define MMC_CAP_ON_BOARD    (1 << 7)    /* Do not need to rescan after bootup */

#define MMC_CAP_BOOT_ONTHEFLY    (1 << 8)    /* Can detect device at boot time */

/* host specific block data */

unsigned int        max_seg_size;    /* see blk_queue_max_segment_size */

unsigned short        max_hw_segs;    /* see blk_queue_max_hw_segments */

unsigned short        max_phys_segs;    /* see blk_queue_max_phys_segments */

unsigned short        unused;

unsigned int        max_req_size;    /* maximum number of bytes in one req */

unsigned int        max_blk_size;    /* maximum size of one mmc block */

unsigned int        max_blk_count;    /* maximum number of blocks in one req */

/* private data */

spinlock_t        lock;        /* lock for claim and bus ops */

struct mmc_ios        ios;        /* current io bus settings */

u32            ocr;        /* the current OCR setting */

/* group bitfields together to minimize padding */

unsigned int        use_spi_crc:1;

unsigned int        claimed:1;    /* host exclusively claimed */

unsigned int        bus_dead:1;    /* bus has been released */

#ifdef CONFIG_MMC_DEBUG

unsigned int        removed:1;    /* host is being removed */

#endif

struct mmc_card        *card;        /* device attached to this host */

wait_queue_head_t    wq;

struct delayed_work    detect;

const struct mmc_bus_ops *bus_ops;    /* current bus driver */

unsigned int        bus_refs;    /* reference counter */

unsigned int        sdio_irqs;

struct task_struct    *sdio_irq_thread;

atomic_t        sdio_irq_thread_abort;

#ifdef CONFIG_LEDS_TRIGGERS

struct led_trigger    *led;        /* activity led */

#endif

struct dentry        *debugfs_root;

unsigned long        private[0] ____cacheline_aligned;

};

///

static const struct mmc_host_ops sdhci_ops = {

//request函数指针指向的函数用来处理host向从设备发送命令的请求,

.request    = sdhci_request,

//set_ios用来设置电源、时钟等等之类(需要重点关注),

.set_ios    = sdhci_set_ios,

//get_ro用来判断是否写保护

.get_ro        = sdhci_get_ro,

.enable_sdio_irq = sdhci_enable_sdio_irq,

};

//

//获取 只读

static int sdhci_get_ro(struct mmc_host *mmc)

{

struct sdhci_host *host;

unsigned long flags;

int present;

host = mmc_priv(mmc);

spin_lock_irqsave(&host->lock, flags);

#define SDHCI_PRESENT_STATE    0x24      //当前状态寄存器

if (host->flags & SDHCI_DEVICE_DEAD)

present = 0;

else

present = readl(host->ioaddr + SDHCI_PRESENT_STATE);//基地址+寄存器偏移

spin_unlock_irqrestore(&host->lock, flags);

return !(present & SDHCI_WRITE_PROTECT);

}

/*****************************************************************************\

*                                                                           *

* MMC callbacks                                                             *

*                                                                           *

\*****************************************************************************/

static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)

{

struct sdhci_host *host;

unsigned long flags;

host = mmc_priv(mmc);指定mmc_host的private域赋值给struct sdhci_host *host;

spin_lock_irqsave(&host->lock, flags);

WARN_ON(host->mrq != NULL);

#ifndef CONFIG_LEDS_CLASS

sdhci_activate_led(host);

#endif

host->mrq = mrq;

if ((mmc->caps & MMC_CAP_ON_BOARD) || (host->flags & SDHCI_DEVICE_ALIVE))

sdhci_send_command(host, mrq->cmd);

else {

if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)

|| (host->flags & SDHCI_DEVICE_DEAD)) {

host->mrq->cmd->error = -ENOMEDIUM;

tasklet_schedule(&host->finish_tasklet);

} else

sdhci_send_command(host, mrq->cmd);//发送命令

}

mmiowb();

spin_unlock_irqrestore(&host->lock, flags);

}

/

中断注册函_irq的第一个参数中断号就取自于s3c_device_hsmmc1.resource里面的irq参数,

见sdhci_irq就是中断服务程序,该中断函数一般在插卡、拔卡或者从设备反馈给host信息时会被调用数request

static irqreturn_t sdhci_irq(int irq, void *dev_id)

{

irqreturn_t result;

struct sdhci_host* host = dev_id;

u32 intmask;

int cardint = 0;

spin_lock(&host->lock);

intmask = readl(host->ioaddr + SDHCI_INT_STATUS);

if (!intmask || intmask == 0xffffffff) {

result = IRQ_NONE;

goto out;

}

DBG("*** %s got interrupt: 0x%08x\n",

mmc_hostname(host->mmc), intmask);

if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {

writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),

host->ioaddr + SDHCI_INT_STATUS);

tasklet_schedule(&host->card_tasklet);

}

intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);

if (intmask & SDHCI_INT_CMD_MASK) {

#if defined(CONFIG_MMC_SDHCI_S3C) || defined(CONFIG_MMC_SDHCI_MODULE)

/* read until all status bit is up. by scsuh */

int i;

for (i=0; i<0x1000000; i++) {

intmask = readl(host->ioaddr + SDHCI_INT_STATUS);

if (intmask & SDHCI_INT_RESPONSE)

break;

}

if (0x1000000 == i) {

printk("FAIL: waiting for status update.\n");

}

#endif

writel(intmask & SDHCI_INT_CMD_MASK,

host->ioaddr + SDHCI_INT_STATUS);

sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);

}

if (intmask & SDHCI_INT_DATA_MASK) {

writel(intmask & SDHCI_INT_DATA_MASK,

host->ioaddr + SDHCI_INT_STATUS);

sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);

}

intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);

intmask &= ~SDHCI_INT_ERROR;

if (intmask & SDHCI_INT_BUS_POWER) {

printk(KERN_ERR "%s: Card is consuming too much power!\n",

mmc_hostname(host->mmc));

writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);

}

intmask &= ~SDHCI_INT_BUS_POWER;

if (intmask & SDHCI_INT_CARD_INT)

cardint = 1;

intmask &= ~SDHCI_INT_CARD_INT;

if (intmask) {

printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",

mmc_hostname(host->mmc), intmask);

sdhci_dumpregs(host);

writel(intmask, host->ioaddr + SDHCI_INT_STATUS);

}

result = IRQ_HANDLED;

mmiowb();

out:

spin_unlock(&host->lock);

/*

* We have to delay this as it calls back into the driver.

*/

if (cardint)

mmc_signal_sdio_irq(host->mmc);

return result;

}

阅读(1066) | 评论(0) | 转发(0) |

给主人留下些什么吧!~~

评论热议

请登录后评论。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值