bcm53344 gpio驱动分析

/*********************************************************************************
 *    1.查看代码是在vim下,使用ctags进行的。也可以使用SourceInsight
 *    2.为方便查找,使用“------->>>"加数字/字母进行标记,表示后面会进行详解。
 *      使用“<<<------"加数字/字母,表示详解或相同的地方,
 *        查看时找到相同的标记进行阅读 *    本文主要内容: *             1.总结  *         2.结构体介绍  *         3.gpio_direction_output等函数的调用过程  *         4.bcm53344初始化过程
 ******************************************************************************/1   总结
  1.1 BCM53344共有16个GPIO,是由几个寄存器分开控制的。
  1.2 GPIO 0-3来自CMICD,GPIO 4-15来自ChipcommonA的GPIO引脚0-11
  1.3 GPIO 8-15与MII或者LED共享,在设置复用功能使,需要硬件进行配置成不同的功能,软件不能配置
    参考硬件手册,了解需要将LED_MII_GPIO-SPI_SEL[1:0]进行配置,
  1.4 三个重要的结构体
      gpio_desc          : 内核中提供的公共结构体,内核中会定义一个结构提数组.
                      每一个GPIO口会对应一个.
      gpio_chip          : 内核中提供的公共结构体, 内核中会定义一个结构体数组.
                      每一个GPIO口会对应一个.启动定义了输入输出等各种处理函数
      iproc_gpio_chip : 不同的芯片,不同的平台自己定义的结构体
                      其中记录了芯片的寄存器地址,物理地址映射的虚拟地址等  1.5.内核采用面向对象的思想,定义共有的接口gpio_desc,而gpio_desc中的chip指针指向iproc_gpio_chip中的chip。    这样就可以通过共有接口访问不同芯片的数据。例如,gpio_direction_output/gpio_request等,会使用gpio_desc,    调用其中的gpio_chip,间接调用其中的  direction_input/request函数,如下所示。而request, free,direction_input,    get,direction_output等已经在初始化的时候进行了。例如上面的direction_input指向  iproc_gpiolib_input,     这样就可以访问平台的数据。    初始化函数所做的工作就是将 gpio_desc和 gpio_chip以及平台自己定义的结构提 iproc_gpio_chip结构提联系起来。    并将gpio_chip中的函数指针指向处理函数。将物理地址转化为虚拟地址,保存到iproc_gpio_chip中。

2.结构体介绍
/* 所有芯片都有的结构结构 */
struct gpio_desc {
    struct gpio_chip    *chip;       // -------------------------->>> chip
    unsigned long        flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED    0
#define FLAG_IS_OUT    1
#define FLAG_RESERVED    2
#define FLAG_EXPORT    3    /* protected by sysfs_lock */
#define FLAG_SYSFS    4    /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL    5    /* trigger on falling edge */
#define FLAG_TRIG_RISE    6    /* trigger on rising edge */
#define FLAG_ACTIVE_LOW    7    /* sysfs value has active low */
#define FLAG_OPEN_DRAIN    8    /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 9    /* Gpio is open source type */

#define ID_SHIFT    16    /* add new flags before this one */

#define GPIO_FLAGS_MASK        ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK    (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))

#ifdef CONFIG_DEBUG_FS
    const char        *label;
#endif
};

/* 通过container_of找到iproc_gpio_chip的首地址,就可以访问iproc_gpio_chip里面的数据了 */
static inline struct iproc_gpio_chip *to_iproc_gpio(struct gpio_chip *gpc)
{
    return container_of(gpc, struct iproc_gpio_chip, chip);
}

/* 不同公司芯片自己定义的结构提 */
struct iproc_gpio_chip {
    int id;
    struct gpio_chip    chip;                            // <<<----------------------- chip
    struct iproc_gpio_cfg   *config;
    void __iomem        *ioaddr;
    void __iomem        *intr_ioaddr;
    void __iomem        *dmu_ioaddr;
    spinlock_t       lock;
    int irq_base;
    struct resource * resource;
    int irq;
    struct iproc_gpio_irqcfg    *irqcfg;
    int pin_offset;
};

struct gpio_chip {
    const char      *label;
    struct device       *dev;
    struct module       *owner;

    int         (*request)(struct gpio_chip *chip,
                        unsigned offset);
    void            (*free)(struct gpio_chip *chip,
                        unsigned offset);

    int         (*direction_input)(struct gpio_chip *chip,
                        unsigned offset);
    int         (*get)(struct gpio_chip *chip,
                        unsigned offset);
    int         (*direction_output)(struct gpio_chip *chip,
                        unsigned offset, int value);
    int         (*set_debounce)(struct gpio_chip *chip,
                        unsigned offset, unsigned debounce);

    void            (*set)(struct gpio_chip *chip,
                        unsigned offset, int value);

    int         (*to_irq)(struct gpio_chip *chip,
                        unsigned offset);

    void            (*dbg_show)(struct seq_file *s,
                        struct gpio_chip *chip);
    int         base;
    u16         ngpio;
    const char      *const *names;
    unsigned        can_sleep:1;
    unsigned        exported:1;

#if defined(CONFIG_OF_GPIO)
    /*
     * If CONFIG_OF is enabled, then all GPIO controllers described in the
     * device tree automatically may have an OF translation
     */
    struct device_node *of_node;
    int of_gpio_n_cells;
    int (*of_xlate)(struct gpio_chip *gc,
                const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
3.gpio_direction_output等函数的调用过程3.1 大致过程
gpio_direction_output {
    struct gpio_chip    *chip;
    gpio_desc    *desc = &gpio_desc[gpio];
    chip = desc->chip;
    chip->direction_output(chip, gpio, value);
}                |
                 |
                 V
iproc_gpiolib_output(struct gpio_chip *chip,unsigned gpio, int value)
这样就可以调用平台的自己的数据了。
3.2 详解:
int gpio_direction_output(unsigned gpio, int value)
{
    unsigned long        flags;
    struct gpio_chip    *chip;
    struct gpio_desc    *desc = &gpio_desc[gpio];
    int            status = -EINVAL;

    /* Open drain pin should not be driven to 1 */
    if (value && test_bit(FLAG_OPEN_DRAIN,  &desc->flags))
        return gpio_direction_input(gpio);

    /* Open source pin should not be driven to 0 */
    if (!value && test_bit(FLAG_OPEN_SOURCE,  &desc->flags))
        return gpio_direction_input(gpio);

    spin_lock_irqsave(&gpio_lock, flags);

    if (!gpio_is_valid(gpio))
        goto fail;
    chip = desc->chip;
    if (!chip || !chip->set || !chip->direction_output)
        goto fail;
    gpio -= chip->base;
    if (gpio >= chip->ngpio)
        goto fail;
    status = gpio_ensure_requested(desc, gpio);
    if (status < 0)
        goto fail;

    /* now we know the gpio is valid and chip won‘t vanish */

    spin_unlock_irqrestore(&gpio_lock, flags);

    might_sleep_if(chip->can_sleep);

    if (status) {
        status = chip->request(chip, gpio);
        if (status < 0) {
            pr_debug("GPIO-%d: chip request fail, %d\n",
                chip->base + gpio, status);
            /* and it‘s not available to anyone else ...
             * gpio_request() is the fully clean solution.
             */
            goto lose;
        }
    }

    /* 这里就是不同GPIO对应的函数,
     * BCM53344对应那个的接口函数就是iproc_gpiolib_output
     * 在iproc_gpiolib_add中指定 */
    /* 再通过这个函数调用不同的平台私有的数据 */
    status = chip->direction_output(chip, gpio, value);
    if (status == 0)
        set_bit(FLAG_IS_OUT, &desc->flags);
    trace_gpio_value(chip->base + gpio, 0, value);
    trace_gpio_direction(chip->base + gpio, 0, status);
lose:
    return status;
fail:
    spin_unlock_irqrestore(&gpio_lock, flags);
    if (status)
        pr_debug("%s: gpio-%d status %d\n",
            __func__, gpio, status);
    return status;
}
EXPORT_SYMBOL_GPL(gpio_direction_output);

int iproc_gpiolib_output(struct gpio_chip *chip,
                  unsigned gpio, int value)
{
    /* 找到不同平台自己定义的结构体 */
    struct iproc_gpio_chip *ourchip = to_iproc_gpio(chip);
    unsigned long flags;
    unsigned long val;
    unsigned int pin_offset = gpio + ourchip->pin_offset;
    unsigned int  nBitMask = 1 << pin_offset;

    iproc_gpio_lock(ourchip, flags);

    val = _iproc_gpio_readl(ourchip, REGOFFSET_GPIO_EN);
    /* 置1,输出使能 */
    val |= nBitMask;
    _iproc_gpio_writel(ourchip, val, REGOFFSET_GPIO_EN);

    iproc_gpio_unlock(ourchip, flags);
    return 0;
}

4.bcm53344初始化过程
static int __init gpio_init(void)
{
    iproc_gpiolib_init();

    return 0;
}

int iproc_gpiolib_init(void)
{
    /* 根据宏定义的情况直到,iproc_gpios_config
     * CONFIFG_MACH_HR
     */
    struct iproc_gpio_chip *chip = iproc_gpios_config;  // --------------------------->>>  1
    int gpn;
    int temp_base;                                                                                

    ...... (省略)

    temp_base = 0;
    /* 根据iproc_gpios_config知道for循环值执行一次 */
    for (gpn = 0; gpn < ARRAY_SIZE(iproc_gpios_config); gpn++, chip++) {
        if (gpn >= MAX_NS_GPIO){
            printk("Unavailabe to add gpiolib\n");
            return -EINVAL;
        }                                                                                        

        /* gpioA : chip.base = 0
         */
        if (chip->chip.base == -EINVAL) {
            chip->chip.base = temp_base;
        }
        /* 虚拟地址映射,设置中断 */
        iproc_gpiolib_add(chip);                     // ------------------------------>>>    2
        temp_base = chip->chip.base + chip->chip.ngpio;
    }

    return 0;
}

//                    iproc_gpios_config             // <<<------------------------------------   1
根据不同平台确定结构体,我的平台的hurricane2
/*
 * 注意下面的注释中说明
 * GPIO 0-3来自CMICD
 * GPIO 4-15来自ChipcommonA的GPIO引脚0-11
 * GPIO 8-15与MII或者LED共享
 * 因此base是4,个数使12
 */
所以在配置寄存器的时候就要去找CMICD,ChipcommonA,MII,LED相关的寄存器
/* GPIO 8-15是功能复用引脚,需要硬件进行配置成不同的功能,软件不能配置
 * 参考硬件手册,了解需要将LED_MII_GPIO-SPI_SEL[1:0]进行配置,
 * 找到对应的引脚,检查硬件是否连接正确,配置为GPIO功能 */

#elif defined(CONFIG_MACH_HR2)
/*
 * Chip level GPIO 0-3 from CMICD,
 * GPIO 4-15 are from ChipcommonA gpio pin 0 - 11
 * where GPIO 8-15 are shared with MII or LED depends on strap pin
 * Hence the base is 4 and the number is 12.
 */
//原来的
struct iproc_gpio_chip iproc_gpios_config[] = {
    [0] = {
        .id   = IPROC_GPIO_CCA_ID,
        .chip   = {
            .base           = 4,
            .label          = "GPIOA",
            .ngpio          = 12,
        },
        .irq_base = IPROC_GPIO_CCA_IRQ_BASE,
        .resource = &iproc_gpio_resources[0],
        .irq = IPROC_GPIO_CCA_INT,
        .irqcfg = &cca_gpio_irqcfg,
        .pin_offset = 0,
    },
}
//更改为
struct iproc_gpio_chip iproc_gpios_config[] = {
    [0] = {
        .id   = IPROC_GPIO_CCA_ID,
        .chip   = {
            .base           = 0,
            .label          = "GPIOA",
            .ngpio          = 4,
        },
        .resource = &iproc_gpio_resources[0],
        .pin_offset = 0,
    },
    [1] = {
        .id   = IPROC_GPIO_CCA_ID,
        .chip   = {
            .base           = 4,
            .label          = "GPIOA",
            .ngpio          = 12,
        },
        .irq_base = IPROC_GPIO_CCA_IRQ_BASE,
        .resource = &iproc_gpio_resources[1],
        .irq = IPROC_GPIO_CCA_INT,
        .irqcfg = &cca_gpio_irqcfg,
        .pin_offset = 0,
    },
};
//还需要更改resource里面的地址。
#else
static struct resource iproc_gpio_resources[] = {
    [0] = {
        .start  = IPROC_GPIO_CCA_BASE,
        .end    = IPROC_GPIO_CCA_BASE + IPROC_GPIO_REG_SIZE - 1,
        .flags  = IORESOURCE_MEM,
        .child = iproc_gpio_cca_config_resource,
    },
    [1] = {
        .start  = IPROC_GPIO_CCB_BASE,
        .end    = IPROC_GPIO_CCB_BASE + IPROC_GPIO_REG_SIZE -1,
        .flags  = IORESOURCE_MEM,
    }
};
//CMIC_GP_DATA_IN 寄存器地址    0x48002000
//CMIC_GP_DATA_OUT 寄存器地址    0x48002004
//CMIC_GP_DATA_EN 寄存器地址    0x48002008
#else
static struct resource iproc_gpio_resources[] = {
    [0] = {
        .start  = 0x48002000,
        .end    = 0x48002000 + IPROC_GPIO_REG_SIZE - 1,
        .flags  = IORESOURCE_MEM,
    },
    [1] = {

        .start  = IPROC_GPIO_CCA_BASE,
        .end    = IPROC_GPIO_CCA_BASE + IPROC_GPIO_REG_SIZE - 1,
        .flags  = IORESOURCE_MEM,
        .child = iproc_gpio_cca_config_resource,
    }
};

//iproc_gpiolib_init(void) 的最后调用   iproc_gpiolib_add(chip);  

void __init  iproc_gpiolib_add(struct iproc_gpio_chip *chip)    // <<<------------------------- 2
{
    struct resource *res;
    struct gpio_chip *gc = &chip->chip;
    int ret, i;    

    /* 系统定义的宏,用于检查lable,ngpio是否为空,空的话报错,并提示异常 */
    BUG_ON(!gc->label);
    BUG_ON(!gc->ngpio);

    spin_lock_init(&chip->lock);
    /* 指定处理函数 */
    if (!gc->direction_input)                                 // ----------------------------->>> 3
        gc->direction_input = iproc_gpiolib_input;
    if (!gc->direction_output)
        gc->direction_output = iproc_gpiolib_output;
    if (!gc->set)
        gc->set = iproc_gpiolib_set;
    if (!gc->get)
        gc->get = iproc_gpiolib_get;
    if (!gc->to_irq)
        gc->to_irq = iproc_gpiolib_to_irq;

    /* gpiochip_add() prints own failure message on error. */
    /* 给每一个gpio分配一个gpio_desc结构体 */
    /* gpio_desc是每一款芯片都有的结构体,并且其中的内容相同 */
    ret = gpiochip_add(gc);                              // ------------------------------>>> 4
    /* 同一个寄存器控制的GPIO,iproc_gpio指针指向同一个iproc_gpio_chip */
    /* iproc_gpio是bcm自己定义的结构体,里面有自己寄存器地址等信息 */
    if (ret >= 0)
        iproc_gpiolib_track(chip);

    printk(KERN_INFO "iproc gpiochip add %s\n", gc->label);
    /* io remap */
    res = chip->resource;
    /* 虚拟地址映射,GPIO的基地址开始映射 */
    chip->ioaddr = ioremap_nocache(res->start, (res->end - res->start) + 1);
    printk(KERN_INFO "%s:ioaddr %p \n", gc->label, chip->ioaddr);
    chip->intr_ioaddr = NULL;
    chip->dmu_ioaddr = NULL;
    if(res->child){
        for (i=0; i< 2; i++){
            /* 虚拟地址映射,CCA的基地址开始映射 */
            if (!strcmp("intr", res->child[i].name)){
                chip->intr_ioaddr =
                    ioremap_nocache(res->child[i].start,
                    (res->child[i].end - res->child[i].start) + 1);
            }
            if (!strcmp("dmu", res->child[i].name)){
                chip->dmu_ioaddr =
                    ioremap_nocache(res->child[i].start,
                    (res->child[i].end - res->child[i].start) + 1);
            }
        }
        printk(KERN_INFO "%s:intr_ioaddr %p dmu_ioaddr %p\n",
            gc->label, chip->intr_ioaddr,chip->dmu_ioaddr);
    }

    /* 中断 */
    if (chip->irq_base) {
        for (i = chip->irq_base; i < (chip->irq_base + gc->ngpio); i++) {
            irq_set_chip(i, &iproc_gpio_irq_chip);
            irq_set_chip_data(i,chip);
            irq_set_handler(i, handle_level_irq);
            set_irq_flags(i, IRQF_VALID);                            

        }
#if defined(IPROC_GPIO_CCA)
        if (chip->id == IPROC_GPIO_CCA_ID ){
            unsigned int val;
            /* enable the GPIO in CCA interrupt mask */
            val = readl(chip->intr_ioaddr + IPROC_CCA_INT_MASK);
            val |= IPROC_CCA_INT_F_GPIOINT;
            writel(val, chip->intr_ioaddr + IPROC_CCA_INT_MASK);
        }
#endif
    if (chip->irqcfg) {
        struct iproc_gpio_irqcfg *irqcfg = chip->irqcfg;
        if (irqcfg->handler) {
                ret = request_irq(chip->irq, irqcfg->handler, irqcfg->flags,
            gc->label, chip);
            if (ret)
            printk(KERN_ERR "Unable to request IRQ%d: %d\n",
                chip->irq, ret);
        }
        else
            printk(KERN_ERR "%s is added without isr!\n", chip->chip.label);
    }
    }
    iproc_gpio_dev[dev] = chip;
    dev++;
}

int gpiochip_add(struct gpio_chip *chip)      // <<<-----------------------------   4
{
    unsigned long    flags;
    int        status = 0;
    unsigned    id;
    int        base = chip->base;
    /* 判断GPIO的序号是否在合理的为内 */
    if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
            && base >= 0) {
        status = -EINVAL;
        goto fail;
    }

    spin_lock_irqsave(&gpio_lock, flags);

    if (base < 0) {
        base = gpiochip_find_base(chip->ngpio);
        if (base < 0) {
            status = base;
            goto unlock;
        }
        chip->base = base;
    }

    /* these GPIO numbers must not be managed by another gpio_chip */
    for (id = base; id < base + chip->ngpio; id++) {
        /* 判断结构体数组原始是否被别的gpio_chip使用,如果有使用,那么status进行标记 */
        if (gpio_desc[id].chip != NULL) {
            status = -EBUSY;
            break;
        }
    }
    /* 如果都没有使用,那么就进行初始化 */
    if (status == 0) {
        for (id = base; id < base + chip->ngpio; id++) {
            /* 都指向统一个chip结构 */
            gpio_desc[id].chip = chip;

            /* REVISIT:  most hardware initializes GPIOs as
             * inputs (often with pullups enabled) so power
             * usage is minimized.  Linux code should set the
             * gpio direction first thing; but until it does,
             * we may expose the wrong direction in sysfs.
             */
            gpio_desc[id].flags = !chip->direction_input
                ? (1 << FLAG_IS_OUT)
                : 0;
        }
    }

    of_gpiochip_add(chip);

unlock:
    spin_unlock_irqrestore(&gpio_lock, flags);

    if (status)
        goto fail;

    status = gpiochip_export(chip);
    if (status)
        goto fail;

    pr_debug("gpiochip_add: registered GPIOs %d to %d on device: %s\n",
        chip->base, chip->base + chip->ngpio - 1,
        chip->label ? : "generic");

    return 0;
fail:
    /* failures here can mean systems won‘t boot... */
    pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",
        chip->base, chip->base + chip->ngpio - 1,
        chip->label ? : "generic");
    return status;
}
EXPORT_SYMBOL_GPL(gpiochip_add);

// 制定的处理函数举例
int iproc_gpiolib_input(struct gpio_chip *chip, unsigned gpio)     // <<<--------------------- 3
{
    /* 由于有16个GPIO,并且是由不同的寄存器控制
     * 所以GPIO0-GPIO3的iproc_gpio_chip结构体与4-15是不同的 */
    struct iproc_gpio_chip *ourchip = to_iproc_gpio(chip);
    unsigned long flags;
    unsigned int  val;
    unsigned int pin_offset = gpio + ourchip->pin_offset;
    unsigned int  nBitMask = 1 << pin_offset;

    iproc_gpio_lock(ourchip, flags);

    val = _iproc_gpio_readl(ourchip, REGOFFSET_GPIO_EN);
    /* 输入使能,清0 */
    val &= ~nBitMask;
    _iproc_gpio_writel(ourchip, val, REGOFFSET_GPIO_EN);

    iproc_gpio_unlock(ourchip, flags);
    return 0;
}
时间: 2024-11-05 02:38:57

bcm53344 gpio驱动分析的相关文章

友坚4412开发板基于Timed_out框架的GPIO驱动分析

Timed GPIO驱动程序分析 Timed GPIO驱动程序是android系统基于linux内核新增加的一类驱动程序,这类驱动程序主要是运用了内核定时器,与内核定时器进行绑定,使得控制GPIO口的高低电平与时间打上关系,既可以实现在一定的时间实现GPIO口为高或者低电平.Timed GPIO驱动被实现为平台设备驱动,Timed GPIO驱动源码位于如下目录:\kernel\drivers\staging\android Timed GPIO驱动程序主要包括如下几个文件: Timed_gpio

20150311 NandFlash驱动分析

20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一.抽象的接口 引入MTD后,linux系统中的Flash设备驱动及接口可分为4层: 设备节点 MTD设备层 MTD原始设备层 硬件驱动层 硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读.写.擦除,Linux MTD设备的NAND型Flash驱动位于

linux input设备驱动分析

linux input设备驱动分析 工作机制 输入设备工作机制: 输入动作–>产生中断–>CPU通过总线或者IO读取数据到缓冲区 构架层次 app //-------------------- input_event_driver //-------------------- input_core //-------------------- input_device_driver //-------------------- hardware 数据结构 输入设备对象 struct input

S3C6410 LCD驱动分析(转)

一. 理论分析1. 几个概念:FIMC :    Fully Interactive Mobile Camera (完全交互式移动摄像机)FIMD:     Fully Interactive Mobile Display (完全交互式移动显示设备)2. 设置VCLK在VIDCON0中bit[3:2]-->Select the Video Clock source =00 --> HCLK=133MHZbit[13:6] --> CLKVAL_F = 13  (这个值是在驱动中计算出来的

linux的GPIO驱动的使用(s5pv210)

开发板:TQ210         内核版本:2.6.35 ######################################################################################################### 这段时间一直在学习linux的驱动,大部分的学习资料都是来自网络论坛.博客.这类资料往往不够系统,全面,且好多资料都是相互拷贝,重复的.因此,学了这么长时间,感觉好没有条理,总是东看一点西看一点,看完也说不出个所以然.不知道大家

LInux LCD驱动分析

一.让LCD显示可爱的小企鹅还是先说说环境吧,处理器为S3C2410,linux的版本当然是2.6.20的.下面先说说怎样让LCD上显示出可爱的小企鹅.最直接的步骤如下(记住不要问为什么哈-_-,一步一步跟着走就行了):1.       添加s3c2410处理器的LCD控制寄存器的初始值,具体做法为在文件arch/arm/mach-s3c2410/mach-smdk2410.c中添加struct s3c2410fb_mach_info类型的寄存器描述讯息,如下所示:static struct s

很好的linux下GPIO驱动详解文章

原文地址  http://blog.csdn.net/llxmedici/article/details/6282372 打算跟着友善之臂的<mini2440 linux移植开发指南>来做个LED驱动,虽然LED的原理简单得不能再简单了,但是要把kernel中针对于s3c24**的GPIO的一些数据结构,还有函数搞清楚也不是那么轻松的事,所以本文主要简单地说明下LED驱动中的相关数据结构以及函数/宏的定义,并对驱动加以验证 ***********************************

Linux SD/MMC/SDIO驱动分析_转

转自:Linux SD/MMC/SDIO驱动分析    https://www.cnblogs.com/cslunatic/p/3678045.html#3053341 一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(MultimediaCard) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡,而 MMC 则是较早的一种记忆卡标准,目前已经被 SD 标准所取代.在维基百科上有相当详细的 SD/MMC 规格说明:[htt

linux串口驱动分析

linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作.UART 使用系统时钟能够支持最高 115.2Kbps 的波特率.每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO. 寄存器 名称 地址 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) UART 线性控制寄存器(ULCONn) ULC