内核初始化与gpio子系统
来源:互联网 发布:出去旅游哪个软件好 编辑:程序博客网 时间:2024/04/30 03:07
定义在linux/arch/arm/plat-s3c/gpio-config.c
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
{
struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); //得到对应GPIO结构体首指针,里面包含了该GPIO的各种参数
unsigned long flags;
int offset;
int ret;
offset = pin - chip->chip.base; // 否则offset等于该GPIO引脚相对于GPX(0)的偏移量,每个偏移1
ret = s3c_gpio_do_setcfg(chip, offset, config); //设置该GPIO状态寄存器的数值为config
//s3c_gpio_do_setcfg操作
static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip,unsigned int off, unsigned int config)
{
return (chip->config->set_config)(chip, off, config);
}
//这里的set_config是一个函数指针,由后面的分析知道,如果针对GPA,该函数指针指向s3c_gpio_setcfg_s3c24xx_a ,如果针对GPX应该是指向s3c_gpio_setcfg_s3c24xx——但发现,如果是其他GPX,根本没有定义set_config!!!(这个问题已经解决,见后文s3c24xx_gpiolib_init函数,事实上,其余的config的确指向s3c_gpio_do_setcfg函数)
struct s3c_gpio_cfg s3c24xx_gpiocfg_default = {
.set_config = s3c_gpio_setcfg_s3c24xx,
.get_config = s3c_gpio_getcfg_s3c24xx,
};
int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base; // GPXCON的物理基地址
unsigned int shift = off; // 每个GPA对应一位
u32 con;
if (s3c_gpio_is_cfg_special(cfg)) { //OUTPUT状态是否为(0xfffffffX),是,返回1
cfg &= 0xf; // cfg = 0xX
/* Map output to 0, and SFN2 to 1 */ 本实验不会运行到这
cfg -= 1;
if (cfg > 1)
return -EINVAL;
cfg <<= shift;
}
con = __raw_readl(reg); // 先读出该GPXCON的值,32位
con &= ~(0x1 << shift); //
con |= cfg; //
__raw_writel(con, reg); // 将新值写入GPXCON
PS:
#define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))
#define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
#define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))
#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))
return 0;
}
如果针对GPX情况
int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip,
unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base;
unsigned int shift = off * 2; // 每个GPX对应2位
u32 con;
if (s3c_gpio_is_cfg_special(cfg)) {
cfg &= 0xf;
if (cfg > 3)
return -EINVAL;
cfg <<= shift; // 将cfg的0,1两位左移offset
}
con = __raw_readl(reg); // 读对应的GPXCON值
con &= ~(0x3 << shift); // 将GPXCON(pin)的两bits请0
con |= cfg; // 设置config值
__raw_writel(con, reg); // 写入新的GPXCON
return 0;
}
return ret;
}
我们先来看s3c_gpiolib_getchip,它实现了返回对应pin值的GPIO结构体首指针的功能
#include<mach/gpio-core.h>
static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin)
{
struct s3c_gpio_chip *chip;
if (pin > S3C_GPIO_END) //如果超过GPJ(32)就return NULL
return NULL;
chip = &s3c24xx_gpios[pin/32]; //根据偏移,计算出对应pin的GPIO结构体指针
return ((pin - chip->chip.base) < chip->chip.ngpio) ? chip : NULL;
// 这里验证,如果pin偏移超过了GPIO的个数,说明出错了,否则就返回该GPIO的结构体指针
}
回想以下之前s3c2410_gpio_cfgpin中,我们传入的参数是led_table[i]和 led_cfg_table[i],
struct s3c_gpio_chip s3c24xx_gpios[] = {
[0] = {
.base = S3C2410_GPACON, // datasheet上地址为0x56000000
#define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO)
#define S3C24XX_VA_GPIO ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
S3C24XX_PA_GPIO相当于(0x15600000)
S3C24XX_PA_UART相当于(0x15000000)
#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */
#define S3C_ADDR_BASE 0xF6000000
#ifndef __ASSEMBLY__
#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))
#else
#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
#endif
0x15600000-15000000+F7000000这里的S3C2410_GPACON应该怎么算?
.pm = __gpio_pm(&s3c_gpio_pm_1bit),
.config = &s3c24xx_gpiocfg_banka, // 设置GPIO的函数指针
static struct s3c_gpio_cfg s3c24xx_gpiocfg_banka = {
.set_config = s3c_gpio_setcfg_s3c24xx_a,
.get_config = s3c_gpio_getcfg_s3c24xx_a,
};
.chip = {
.base = S3C2410_GPA(0), //基地址,也是偏移量
.owner = THIS_MODULE,
.label = "GPIOA",
.ngpio = 24,
.direction_input = s3c24xx_gpiolib_banka_input,
.direction_output = s3c24xx_gpiolib_banka_output,
},
},
[1] = {
.base = S3C2410_GPBCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPB(0),
.owner = THIS_MODULE,
.label = "GPIOB",
.ngpio = 16,
},
},
[2] = {
.base = S3C2410_GPCCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPC(0),
.owner = THIS_MODULE,
.label = "GPIOC",
.ngpio = 16,
},
},
[3] = {
.base = S3C2410_GPDCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPD(0),
.owner = THIS_MODULE,
.label = "GPIOD",
.ngpio = 16,
},
},
[4] = {
.base = S3C2410_GPECON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPE(0),
.label = "GPIOE",
.owner = THIS_MODULE,
.ngpio = 16,
},
},
[5] = {
.base = S3C2410_GPFCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPF(0),
.owner = THIS_MODULE,
.label = "GPIOF",
.ngpio = 8,
.to_irq = s3c24xx_gpiolib_bankf_toirq,
},
},
[6] = {
.base = S3C2410_GPGCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.irq_base = IRQ_EINT8,
.chip = {
.base = S3C2410_GPG(0),
.owner = THIS_MODULE,
.label = "GPIOG",
.ngpio = 16,
.to_irq = samsung_gpiolib_to_irq,
},
}, {
.base = S3C2410_GPHCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPH(0),
.owner = THIS_MODULE,
.label = "GPIOH",
.ngpio = 11,
},
},
/* GPIOS for the S3C2443 and later devices. */2440用不到
{
.base = S3C2440_GPJCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPJ(0),
.owner = THIS_MODULE,
.label = "GPIOJ",
.ngpio = 16,
},
}, {
.base = S3C2443_GPKCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPK(0),
.owner = THIS_MODULE,
.label = "GPIOK",
.ngpio = 16,
},
}, {
.base = S3C2443_GPLCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPL(0),
.owner = THIS_MODULE,
.label = "GPIOL",
.ngpio = 15,
},
}, {
.base = S3C2443_GPMCON,
.pm = __gpio_pm(&s3c_gpio_pm_2bit),
.chip = {
.base = S3C2410_GPM(0),
.owner = THIS_MODULE,
.label = "GPIOM",
.ngpio = 2,
},
},
};
***************************************************************************
下面分析第二个函数,先看一下相关结构体
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
/* do this via gpiolib until all users removed */
gpio_request(pin, "temporary");
gpio_set_value(pin, to);
gpio_free(pin);
}
又出现了三个函数,我们一一说明:
1169/* These "optional" allocation calls help prevent drivers from stomping
1170 * on each other, and help provide better diagnostics in debugfs.
1171 * They're called even less than the "set direction" calls.
1172 */
PS:static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
其中ARCH_NR_GPIOS在arch/arm/mach-s3c2410/include/mach/gpio.h中定义
#define ARCH_NR_GPIOS (32 * 9 + CONFIG_S3C24XX_GPIO_EXTRA)
因此,每个引脚都分配了一个gpio_desc数据结构
1173int gpio_request(unsigned gpio, const char *label) // 这个函数还不是很明白
1174{
1175 struct gpio_desc *desc;
1176 struct gpio_chip *chip;
1177 int status = -EINVAL;
1178 unsigned long flags;
1179
1180 spin_lock_irqsave(&gpio_lock, flags); // gpio_lock是自旋锁,上锁,保存FLAG在flags变量中
1181
1182 if (!gpio_is_valid(gpio)) // 不符合要求,跳转到done
1183 goto done;
1184 desc = &gpio_desc[gpio]; // desc = &gpio_desc[pin]
1185 chip = desc->chip;
1186 if (chip == NULL) // gpio_desc.chip指向NULL,跳转到done
1187 goto done;
1188
1189 if (!try_module_get(chip->owner)) // 该函数用于增加模块使用计数;若返回为0,表示调用失败,希望使用的模块没有被加载或正在被卸载中
1190 goto done;
1191
1192 /* NOTE: gpio_request() can be called in early boot,
1193 * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
1194 */
1195
1196 if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { // 原子操作,将flags的第FLAG_REQUESTED位置1,并返回其原值
1197 desc_set_label(desc, label ? : "?"); // 如果原来的值是0, 执行desc_set_label, 对desc->chip.label赋值,如果label有定义,直接用定义,比如上面的“temporary”,否则用“?”
static inline void desc_set_label(struct gpio_desc *d, const char *label)
{
#ifdef CONFIG_DEBUG_FS
d->label = label; // 为什么不是d->chip.label = label; ?
#endif
}
1198 status = 0;
1199 } else { // 如果flags的第FLAG_REQUESTED位原来的值是1
1200 status = -EBUSY;
1201 module_put(chip->owner); // 该函数用于减少模块使用计数
1202 goto done;
1203 }
1204
1205 if (chip->request) { // chip->request在linux初始化时是没有指向的,可以见后面s3c_gpiolib_add
1206 /* chip->request may sleep */
1207 spin_unlock_irqrestore(&gpio_lock, flags); // 如果chip->request不为0, 解锁,因为后面调用的chip->request有可能睡眠
1208 status = chip->request(chip, gpio - chip->base);
1209 spin_lock_irqsave(&gpio_lock, flags); // 执行完后,继续上锁
1210
1211 if (status < 0) { // status返回负数,说明出错
1212 desc_set_label(desc, NULL);
1213 module_put(chip->owner);
1214 clear_bit(FLAG_REQUESTED, &desc->flags);
1215 }
1216 }
1217
1218done:
1219 if (status)
1220 pr_debug("gpio_request: gpio-%d (%s) status %d/n", gpio, label ? : "?", status);
1221 // 如果状态不为0, 打印gpio-pin"****"的状态
1222 spin_unlock_irqrestore(&gpio_lock, flags); // 解锁
1223 return status; // 返回状态
1224}
***************************************************************************
下面先分析gpio_free函数
void gpio_free(unsigned gpio) // 待分析
{
unsigned long flags;
struct gpio_desc *desc;
struct gpio_chip *chip;
might_sleep();
if (!gpio_is_valid(gpio)) {
WARN_ON(extra_checks);
return;
}
gpio_unexport(gpio);
spin_lock_irqsave(&gpio_lock, flags);
desc = &gpio_desc[gpio];
chip = desc->chip;
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
if (chip->free) {
spin_unlock_irqrestore(&gpio_lock, flags);
might_sleep_if(chip->can_sleep);
chip->free(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
}
desc_set_label(desc, NULL);
module_put(desc->chip->owner);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
} else
WARN_ON(extra_checks);
spin_unlock_irqrestore(&gpio_lock, flags);
}
EXPORT_SYMBOL_GPL(gpio_free);
***************************************************************************
arch/arm/mach-s3c2410/include/mach/gpio.h
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
{
struct gpio_chip *chip;
chip = gpio_to_chip(gpio); // 返回对应于pin的gpio_desc[pin].chip指针
WARN_ON(chip->can_sleep);
chip->set(chip, gpio - chip->base, value); // 这里调用的是s3c_gpiolib_set函数!!!
}
/* caller holds gpio_lock *OR* gpio is marked as requested */
static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
{
return gpio_desc[gpio].chip;
}
看到这里,一直有个问题让我百思不得其解,这里的chip按理说应该是s3c_gpio_chip中的chip成员,但是之前都没有代码交代他们是如何联系到一起的,s3c_gpio_chip与gpio_desc结构体如何联系在一起,也没有函数交代,并且这里的chip->set函数指针也没有实现的代码,但是,经实验确认没有问题后,我开始查找plat-s3c24xx/gpiolib.c中的函数希望能有些线索,果然,找到了这么一个函数:
static __init int s3c24xx_gpiolib_init(void)
{
struct s3c_gpio_chip *chip = s3c24xx_gpios;
int gpn;
for (gpn = 0; gpn < ARRAY_SIZE(s3c24xx_gpios); gpn++, chip++) {
if (!chip->config)
chip->config = &s3c24xx_gpiocfg_default; // 原来chip->config默认函数也是在这里!!!
s3c_gpiolib_add(chip); // 之前的疑惑都在这里实现!!!
}
return 0;
}
core_initcall(s3c24xx_gpiolib_init);
但是,这个s3c24xx_gpiolib_init函数又是在什么时候执行的呢?可以看到,在该函数的下面,有一句:core_initcall(s3c24xx_gpiolib_init);查阅相关资料发现, 在linux初始化的过程中,内核采用了一种initcall的机制,它利用gcc的扩展功能以及ld的连接控制脚本实现了在内核初始化的过程中通过简单的循环就实现了相关驱动的初始化
也就是说,在linux初始化期间,就已经执行了s3c24xx_gpiolib_init,现在我们可以分析下s3c_gpiolib_add(chip);这个函数了,
__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
{
struct gpio_chip *gc = &chip->chip;
int ret;
BUG_ON(!chip->base);
BUG_ON(!gc->label);
BUG_ON(!gc->ngpio);
spin_lock_init(&chip->lock); // 初始化s3c_gpio_chip的自旋锁
if (!gc->direction_input)
gc->direction_input = s3c_gpiolib_input; // direction_input 函数指针
if (!gc->direction_output)
gc->direction_output = s3c_gpiolib_output; // direction_output 函数指针
if (!gc->set)
gc->set = s3c_gpiolib_set; // set函数指针
if (!gc->get)
gc->get = s3c_gpiolib_get; // get函数指针
#ifdef CONFIG_PM
if (chip->pm != NULL) {
if (!chip->pm->save || !chip->pm->resume)
printk(KERN_ERR "gpio: %s has missing PM functions/n", gc->label);
} else
printk(KERN_ERR "gpio: %s has no PM function/n", gc->label);
#endif
/* gpiochip_add() prints own failure message on error. */
ret = gpiochip_add(gc);
if (ret >= 0)
s3c_gpiolib_track(chip);
}
gpiochip_add函数分析:
int gpiochip_add(struct gpio_chip *chip) // 在gpio_desc[]中分配空间,并链接chip结构
{
unsigned long flags;
int status = 0;
unsigned id;
int base = chip->base;
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); // 这个函数在gpiolib.c中,在gpio_desc[]中分配chip->ngpio个空间(从最后往前分配),返回第一个index
if (base < 0) { // 分配不到
status = base;
goto unlock; // 解锁退出
}
chip->base = base; // gpio_chip *chip->base = i (i是gpio_desc[i]中的index)
}
/* these GPIO numbers must not be managed by another gpio_chip */
for (id = base; id < base + chip->ngpio; id++) {
if (gpio_desc[id].chip != NULL) {
status = -EBUSY;
break;
}
}
if (status == 0) { // 分配到空间,正常情况下
for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip; // 这里将gpio_desc与s3c_gpio_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; // 设置flags
}
} // end if
of_gpiochip_add(chip); // 没操作
unlock:
spin_unlock_irqrestore(&gpio_lock, flags); // 解锁
if (status)
goto fail;
status = gpiochip_export(chip); //×××××××××××××××待分析
if (status)
goto fail;
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; // 返回状态
}
下面是s3c_gpiolib_track函数
#ifdef CONFIG_S3C_GPIO_TRACK
struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END];
static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip) // 没完全理解,待分析
{
unsigned int gpn;
int i;
gpn = chip->chip.base;
for (i = 0; i < chip->chip.ngpio; i++, gpn++) {
BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios));
s3c_gpios[gpn] = chip;
}
}
#endif /* CONFIG_S3C_GPIO_TRACK */
***************************************************************************
好,现在我们开始分析设备注册与卸载函数,在初始化程序中,有如下语句:
ret = misc_register(&misc); //注册设备
其中的misc_register就是杂项设备的注册函数,首先关注下这里的参数misc数据结构
75 /*
76 * 把 LED 驱动注册为 MISC 设备
77 */
78 static struct miscdevice misc = {
79 .minor = MISC_DYNAMIC_MINOR, //动态设备号
80 .name = DEVICE_NAME,
81 .fops = &dev_fops,
82 };
miscdevice的数据结构如图所示:
int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list); // 初始化链表头,将misc->list的next和pre都指向自己
mutex_lock(&misc_mtx); // 获取互斥锁, or睡眠
list_for_each_entry(c, &misc_list, list) { // 遍历整个misc_list链表,所有的杂项驱动设备都有一个miscdevice数据结构,这些杂项驱动设备通过一个全局的misc_list链表连在一起, 相当一个记录
if (c->minor == misc->minor) { // 如果misc_list中已经有了这个设备(minor相同),则解锁返回,这里c是遍历时的tmp miscdevice,指向当前遍历节点
mutex_unlock(&misc_mtx);
return -EBUSY;
}
}
if (misc->minor == MISC_DYNAMIC_MINOR) { // 如果misc_list中没有该设备,判断minor是否准备动态分配,实验中如此设置
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); // misc_minors是杂项设备位图,总共有64个位DYNAMIC_MINORS=64,表示可以注册64个杂项设备,这句代码找到位图中的空闲位置(表示还能加新设备)
if (i >= DYNAMIC_MINORS) { // 如果超过总设备数,则解锁返回
mutex_unlock(&misc_mtx);
return -EBUSY;
}
misc->minor = DYNAMIC_MINORS - i - 1; // 计算子设备号,赋值到misc->minor
set_bit(i, misc_minors); // 对应的位图也置位
}
dev = MKDEV(MISC_MAJOR, misc->minor); // 生成设备号
// 在sysfs中创建并注册一个设备,可以在/dev下面看到misc->name
misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name);
1504struct device *device_create(struct class *class, struct device *parent, // 这个函数以后会详细看
1505 dev_t devt, void *drvdata, const char *fmt, ...)
1506{
1507 va_list vargs;
1508 struct device *dev;
1509
1510 va_start(vargs, fmt);
1511 dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
1512 va_end(vargs);
1513 return dev;
1514}
// this_device是在创建设备节点时指向函数device_create()返回的设备结构
if (IS_ERR(misc->this_device)) { // 如果创建节点出错,并且
int i = DYNAMIC_MINORS - misc->minor - 1; // 计算子设备号之前misc->minor的值
if (i < DYNAMIC_MINORS && i >= 0) // 计算位图位i,如果在0-64之间,说明在set_bit中置位了,则清楚位图,处理错误,准备返回
clear_bit(i, misc_minors);
err = PTR_ERR(misc->this_device);
goto out;
}
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list); // 以上操作都没有问题后,将新设备加入misc_list链表,解锁返回
out:
mutex_unlock(&misc_mtx);
return err;
}
***************************************************************************
同样,对应misc_register函数,在exit中会调用misc_deregister函数
int misc_deregister(struct miscdevice *misc)
{
int i = DYNAMIC_MINORS - misc->minor - 1;
if (WARN_ON(list_empty(&misc->list))) // 如果该misc->list的next指向自己,则出错返回
return -EINVAL;
mutex_lock(&misc_mtx); // 上锁
list_del(&misc->list); // 将misc从misc_list链表中删除
device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); // 对应device_create!
1532void device_destroy(struct class *class, dev_t devt)
1533{
1534 struct device *dev;
1535
1536 dev = class_find_device(class, NULL, &devt, __match_devt);
1537 if (dev) {
1538 put_device(dev);
1539 device_unregister(dev);
1540 }
1541}
1542EXPORT_SYMBOL_GPL(device_destroy);
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors); // 计算位图位i,如果在0-64之间,说明在set_bit中置位了,清楚位图
mutex_unlock(&misc_mtx); // 解锁返回
return 0;
}
***************************************************************************
总结杂项设备驱动的注册与卸载流程:
misc_register:找到空闲设备位图位置 -> 计算子设备号(如果动态的话),位图位置位 - > device_creat() -> miscdevice结构体加入misc_list链表中
misc_deregister: 将miscdevice结构体从misc_list链表中删除 -> device_destory() -> 位图位清零
***************************************************************************
与s3c24xx_gpiolib_init函数一样,misc也有一个初始化函数会在linux初始化时运行,下面来分析这个函数
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS //在proc文件系统下创建一个"misc"目录。 misc_proc_fops是该文件系统下文件的操作函数集
proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
misc_class = class_create(THIS_MODULE, "misc"); // 前面device_create()中的misc_class就是在这里初始化的
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class)) // 出错处理
goto fail_remove;
err = -EIO;
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) //注册一个主设备号为MISC_MAJOR(10)的字符设备,设备操作函数集为misc_fops
goto fail_printk;
misc_class->devnode = misc_devnode;
return 0;
fail_printk: // 错误处理
printk("unable to get major %d for misc devices/n", MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
return err;
}
subsys_initcall(misc_init);
内核提供了一个重要的结构体struct machine_desc ,通过machine_desc结构体来控制系统体系架构相关部分的初始化。machine_desc结构体的成员包含了体系架构相关部分的几个最重要的初始化函数,包括map_io,init_irq, init_machine以及phys_io , timer成员等。
machine_desc结构体定义如下:
struct machine_desc {
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io * page tabe entry */
const char *name; /* architecture name */
unsigned long ;boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
machine_desc结构体通过MACHINE_START宏来初始化,这里以s3c2410平台为例:
s3c2410 machine_desc结构体定义如下:
/* arch/arm/mach-s3c2410/mach-smdk2410.c */
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
* to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk2410_init,
.timer = &s3c24xx_timer,
MACHINE_END
其中的宏MACHINE_START和MACHINE_END定义如下:
#define MACHINE_START(_type,_name) \
const struct machine_desc __mach_desc_##_type \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,其所占用的内存在内核起来之后将会被释放。
这里的map_io成员即内核提供给用户的创建外设I/O资源到内核虚拟地址静态映射表的接口函数。
map_io成员函数会在系统初始化过程中被调用,流程如下:
start_kernel -> setup_arch() --> paging_init()中被调用
struct machine_desc 结构体的各个成员函数在不同时期被调用:
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall( ) 段里面,会自动按顺序被调用(另外博客分析,敬请关注)。
2. init_irq在start_kernel( ) --> init_IRQ( ) --> init_arch_irq( ) 被调用
3. map_io 在 setup_arch( ) --> paging_init( )被调用
其他主要都在 setup_arch() 中用到。
用户可以在定义machine_desc结构体时指定map_io的接口函数,我们也正是这样做的。
接下来我们继续分析smdk2410_map_io的执行过程,流程如下:
smdk2410_map_io-> s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc))
下面来看一下s3c24xx_init_io函数:
void __init s3c24xx_init_io(struct map_desc *mach_desc, int mach_size)
{
/* register our io-tables */
iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));
……
}
iotable_init内核提供,定义如下:
/*
* Create the architecture specific mappings
*/
void __init iotable_init(struct map_desc *io_desc, int nr)
{
int i;
for (i = 0; i nr; i++)
create_mapping(io_desc + i);
}
由上知道,smdk2410_map_io最终调用iotable_init建立映射表。
iotable_init函数的参数有两个:一个是map_desc类型的结构体,另一个是该结构体的数量nr。这里最关键的就是struct map_desc。map_desc结构体定义如下:
/* include/asm-arm/mach/map.h */
struct map_desc {
unsigned long virtual;
unsigned long physical;
unsigned long length;
unsigned int type;
};
create_mapping( )函数就是通过map_desc提供的信息创建线性映射表的。
这样的话我们就知道了创建I/O映射表的大致流程为:只要定义相应I/O资源的map_desc结构体,并将该结构体传给iotable_init函数执行,就可以创建相应的I/O资源到内核虚拟地址空间的映射表了。
我们来看看s3c2410是怎么定义map_desc结构体的(即上面iotable_init()函数内的s3c_iodesc)。
[arch/arm/mach-s3c2410/cpu.c]
/* minimal IO mapping */
static struct map_desc s3c_iodesc[] __initdata = {
IODESC_ENT(GPIO),
IODESC_ENT(IRQ),
IODESC_ENT(MEMCTRL),
IODESC_ENT(UART)
};
IODESC_ENT宏如下:
#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, S3C2410_PA_##x, S3C24XX_SZ_##x, MT_DEVICE }
展开后等价于:
static struct map_desc s3c_iodesc[] __initdata = {
{
.virtual = S3C24XX_VA_GPIO,
.physical = S3C24XX_PA_GPIO,
.length = S3C24XX_SZ_GPIO,
.type = MT_DEVICE
},
……
};
至此,我们可以比较清晰看到GPIO被静态映射的过程,由于我们在前面的静态映射中已经做好了GPIO的映射,也就是我们写GPIO相关驱动的时候可以如下配置引脚的原因:
s3c2410_gpio_cfgpin(xxx,xxx);
其实,s3c2410_gpio_cfgpin定义如下:
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
void __iomem *base = S3C2410_GPIO_BASE(pin);
unsigned long mask;
unsigned long con;
unsigned long flags;
if (pin < S3C2410_GPIO_BANKB) {
mask = 1 << S3C2410_GPIO_OFFSET(pin);
} else {
mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;
}
local_irq_save(flags);
con = __raw_readl(base + 0x00);
con &= ~mask;
con |= function;
__raw_writel(con, base + 0x00);
local_irq_restore(flags);
}
其中,比较关键的一个地方:
void __iomem *base = S3C2410_GPIO_BASE(pin);
这一行中,S3C2410_GPIO_BASE定义如下:
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
至此,GPIO的静态映射就看得很明白了。
下面来看其他外设的静态映射:
在s3c24xx_init_io()函数中,除了iotable_init()以为,还会在最后调用,
(cpu->map_io)(mach_desc, size);
而CPU的这个map_io在arch/arm/mach-s3c2410/cpu.c里面定义如下:
static struct cpu_table cpu_ids[] __initdata = {
{
.idcode = 0x32410000,
.idmask = 0xffffffff,
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
.init = s3c2410_init,
.name = name_s3c2410
},
...
}
再查看s3c2410_map_io(),函数代码如下:
void __init s3c2410_map_io(struct map_desc *mach_desc, int mach_size)
{
/* register our io-tables */
iotable_init(s3c2410_iodesc, ARRAY_SIZE(s3c2410_iodesc));
iotable_init(mach_desc, mach_size);
}
接下来看结构s3c2410_iodesc [arch/arm/mach-s3c2410/s3c2410.c],代码如下,
/* Initial IO mappings */
static struct map_desc s3c2410_iodesc[] __initdata = {
IODESC_ENT(USBHOST),
IODESC_ENT(USBDEV),
IODESC_ENT(CLKPWR),
IODESC_ENT(LCD),
IODESC_ENT(TIMER),
IODESC_ENT(ADC),
IODESC_ENT(WATCHDOG),
};
赫然发现IODESC_ENT(TIMER)这一行,结合之前GPIO的类似分析,IODESC_ENT宏如下:
#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, S3C2410_PA_##x, S3C24XX_SZ_##x, MT_DEVICE }
至此,TIMER, USBHOST,USBDEV,lcd,adc,watchdog等的静态映射都看得很明白了。
linux提供一个模型来让驱动统一处理GPIO,即各个板卡都有实现自己的gpio_chip控制模块:request, free, input,output, get,set,irq...然后把控制模块注册到内核中,这时会改变全局gpio数组
:gpio_desc[]. 当用户请求gpio时,就会到这个数组中找到,并调用这个GPIO对应的gpio_chip的处理函数。
gpio实现为一组可用的 gpio_chip, 由驱动传入对应 gpio的全局序号 去 request, dataout ,datain, free. 这时会调用gpio_chip中具体的实现。
寄存器读写函数: __raw_writel() __raw_writeb() __raw_readl() __raw_readb()
//****************linux 中 GPIO模型****************************************//
注册方法:
1:struct gpio_chip: 表示一个gpio controller.通过这个结构抽象化所有的 GPIO源,而让板上其它的模块可以用相同的接口调用使用这些GPIO。
2: struct gpio_desc: 表示一个gpio口,含对应的 gpio_chip.
3: ARCH_NR_GPIOS: 与板相关的GPIO口数量,即是全局GPIO数组:static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
4: 注册 gpio_chip时,就是根据 chip 的数据 修改全局 GPIO数组中 gpio_desc 字段(chip, flags)。
//***********************************************//
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //全局的gpio_chip 存放数组
static struct gpio_chip twl_gpiochip = {
.label = "twl4030",
.owner = THIS_MODULE,
.request = twl_request,
.free = twl_free,
.direction_input = twl_direction_in,
.get = twl_get,
.direction_output = twl_direction_out,
.set = twl_set,
.to_irq = twl_to_irq,
.can_sleep = 1,
};
struct gpio_desc {
struct gpio_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 */
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
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);
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;
unsigned can_sleep:1;
unsigned exported:1;
};
这里我们把目光放到gpiolib.c上,主要对外提供的接口函数,在其头文件gpio.h里可以看到:
具体的GPIO描述符:
struct gpio_chip {
int (*request)(struct gpio_chip *chip, unsigned offset);
void (*free)(struct gpio_chip *chip, unsigned offset);
int (*direction_input)(struct gpio_chip *chip, unsignedoffset);
int (*get)(struct gpio_chip *chip, unsigned offset);
int (*direction_output)(struct gpio_chip *chip, unsignedoffset, int value);
int (*set_debounce)(struct gpio_chip *chip, unsigned offset,
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;
… ...
};
externint gpio_request(unsigned gpio, const char *label);
externint gpio_direction_input(unsigned gpio);
externint gpio_direction_output(unsigned gpio, int value);
externint gpio_get_value_cansleep(unsigned gpio);
externvoid gpio_set_value_cansleep(unsigned gpio, int value)
externvoid __gpio_set_value(unsigned gpio, int value);
如果你想使用GPIO驱动,那么在配置内核的时候请把该驱动选上,即定义宏CONFIG_GENERIC_GPIO,然后在你的驱动里加入头文件linux/gpio.h,这样就可以用那些操作函数了。
在kernel/arch/arm/include/asm/mach/arch.h文件中
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
看pxa920的板文件ttc_dkb.c的最后:
MACHINE_START(TTC_DKB, "PXA910-based TTC_DKB Development Platform")
.phys_io = APB_PHYS_BASE,
.boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
.map_io = pxa_map_io,
.init_irq = pxa910_init_irq,
.timer = &pxa910_timer,
.init_machine = ttc_dkb_init,
MACHINE_END
将宏展开后得到,
static const struct machine_desc __mach_desc_TTC_DKB __used __attribute__((__section__("arch.info.init"))) = {
.nr = MACH_TYPE_TTC_DKB,
.name = "PXA910-based TTC_DKB Development Platform",
.phys_io = APB_PHYS_BASE,
.boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
.map_io = pxa_map_io,
.init_irq = pxa910_init_irq,
.timer = &pxa910_timer,
.init_machine = ttc_dkb_init,
}
在kernel/include/asm-arm/mach-types.h文件中有 “MACH_TYPE_TTC_DKB“ 的定义
#define MACH_TYPE_TTC_DKB 2045
同时在arch/arm/tools/mach-types文件中,也有“MACH_TYPE_TTC_DKB“的配置
ttc_dkb MACH_TTC_DKB TTC_DKB 2045
kernel boot 起来的时候,bootloader(uboot) 会传参数进来,其中包括 Machine Type,然后参考 arch/arm/tools/mach-types 并和 MACHINE_START() 第一个参数“.nr“对上号,即都为2045。因此,哪个MACHINE被初始化是在运行时由传入的参数决定的。
MACHINE_START()的各个成员函数在不同时期被调用:
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用 start_kernel,参考 init/main.c
2. init_irq在start_kernel() --> init_IRQ() --> init_arch_irq() 被调用
3. map_io 在 setup_arch() --> paging_init() --> devicemaps_init(),其他主要都在 setup_arch() 中用到
然后我们看init_irq成员,该成员值是 pxa910_init_irq
该函数定义如下(文件:kernel/arch/arm/mach-mmp/pxa910.c):
void __init pxa910_init_irq(void)
{
icu_init_irq(); // 初始化IRQ
pxa910_init_gpio(); // 初始化GPIO
}
static void __init pxa910_init_gpio(void)
{
int i;
/* enable GPIO clock */
__raw_writel(APBC_APBCLK | APBC_FNCLK, APBC_PXA910_GPIO);
/* unmask GPIO edge detection for all 4 banks - APMASKx */
for (i = 0; i < 4; i++)
__raw_writel(0xffffffff, APMASK(i));
pxa_init_gpio(IRQ_PXA910_AP_GPIO, 0, 127, NULL);
}
在文件 kernel/arch/arm/plat-pxa/gpio.c中,定义pxa_init_gpio函数
void __init pxa_init_gpio(int mux_irq, int start, int end, set_wake_t fn)
{
struct pxa_gpio_chip *c;
int gpio, irq;
pxa_last_gpio = end;
/* Initialize GPIO chips */
pxa_init_gpio_chip(end); // 初始化GPIO
/* clear all GPIO edge detects */
for_each_gpio_chip(gpio, c) {
__raw_writel(0, c->regbase + GFER_OFFSET);
__raw_writel(0, c->regbase + GRER_OFFSET);
__raw_writel(~0,c->regbase + GEDR_OFFSET);
}
for (irq = gpio_to_irq(start); irq <= gpio_to_irq(end); irq++) {
set_irq_chip(irq, &pxa_muxed_gpio_chip);
set_irq_handler(irq, handle_edge_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
}
/* Install handler for GPIO>=2 edge detect interrupts */
set_irq_chained_handler(mux_irq, pxa_gpio_demux_handler);
pxa_muxed_gpio_chip.set_wake = fn;
}
在pxa_init_gpio_chip函数中,GPIO chips,如下代码
static int __init pxa_init_gpio_chip(int gpio_end)
{
int i, gpio, nbanks = gpio_to_bank(gpio_end) + 1;
struct pxa_gpio_chip *chips;
chips = kzalloc(nbanks * sizeof(struct pxa_gpio_chip), GFP_KERNEL);
for (i = 0, gpio = 0; i < nbanks; i++, gpio += 32) {
struct gpio_chip *c = &chips[i].chip;
sprintf(chips[i].label, "gpio-%d", i);
chips[i].regbase = (void __iomem *)GPIO_BANK(i);
c->base = gpio;
c->label = chips[i].label;
c->direction_input = pxa_gpio_direction_input; // 将该成员指向pxa_gpio_direction_input函数
c->direction_output = pxa_gpio_direction_output; // 将该成员指向pxa_gpio_direction_output函数
c->get = pxa_gpio_get;
c->set = pxa_gpio_set;
/* number of GPIOs on last bank may be less than 32 */
c->ngpio = (gpio + 31 > gpio_end) ? (gpio_end - gpio + 1) : 32;
gpiochip_add(c);
}
pxa_gpio_chips = chips;
return 0;
}
实际上这个注册过程也就是对gpio_chip的初始化过程,有了这个初始化,后续代码对GPIO的操作将直接调用到pxa_gpio_direction_input等函数。
GPIO是与硬件体系密切相关的,linux提供一个模型来让驱动统一处理GPIO,即各个板都有实现自己的gpio_chip控制模块:request, free, input,output, get,set,irq...
然后把控制模块注册到内核中,这时会改变全局gpio数组:gpio_desc[]。当用户请求gpio时,就会到这个数组中找到,并调用这个GPIO对应的gpio_chip的处理函数。
gpio实现为一组可用的 gpio_chip, 由驱动传入对应 gpio的全局序号 去 request, dataout ,datain, free. 这时会调用gpio_chip中具体的实现。
寄存器读写函数: __raw_writel() __raw_writeb() __raw_readl() __raw_readb() ----这些函数会在特定硬件的具体GPIO操作函数中调用,如pxa_gpio_direction_input
gpio是一组可控件的脚,由多个寄存器同时控制。通过设置对应的寄存器可以达到设置GPIO口对应状态与功能。数据状态,输入输出方向,清零,中断(那个边沿触发), 一般是以组(bank)为一个单元,如920中分为4组。
以gpio_direction_output函数调用为例,
该函数定义在kernel/drivers/gpio/gpiolib.c文件中,实现如下:
int gpio_direction_output(unsigned gpio, int value)
{
unsigned long flags;
struct gpio_chip *chip;
struct gpio_desc *desc = &gpio_desc[gpio];
spin_lock_irqsave(&gpio_lock, flags);
chip = desc->chip; // 取出gpio_chip
gpio -= chip->base;
status = gpio_ensure_requested(desc, gpio);
/* now we know the gpio is valid and chip won't vanish */
spin_unlock_irqrestore(&gpio_lock, flags);
might_sleep_if(extra_checks && chip->can_sleep);
status = chip->direction_output(chip, gpio, value); // 调用该chip的成员direction_output,注意这里的成员在开机初始化时,已经初始为pxa_gpio_direction_output函数
if (status == 0)
set_bit(FLAG_IS_OUT, &desc->flags);
}
为了给不同GPIO控制器提供一个统一的编程接口,内核提供了一个可选择的实现架构。这个架构被称作"gpiolib".在这个架构下,每个GPIO控制器被抽象成“struct gpio_chip",这里GPIO控制器的所有常规信息:
-设定传输方向(输入/输出)的函数
-读写GPIO值的函数
-是否调用可睡眠的函数的flag
-可选择的用来调试的输出(dump method)
-用来诊断的标志
还有一些来自device.platform_data等与体系相关的数据,比如gpio起始号和多少可用的gpio号。
向内核注册gpio_chip是调用gpiochip_add(),注销时调用gpiochip_remove()。
通常gpio_chip是被包含在一个体系相关的结构体内,这个结构体里有一些与gpio状态相关的成员,比如如何寻址,电源管理等等。
为了支持gpio实现框架(已经说过,是可选择的),Kconfig里应该选择ARCH_REQUIRE_GPIOLIB 或 ARCH_WANT_OPTIONAL_GPIOLIB,并且让<asm/gpio.h>包含<asm-generic/gpio.h>(只要包含就可以,直接间接无所谓)并且定义三个函数:gpio_get_value(), gpio_set_value(), and gpio_cansleep().通常还需要提供ARCH_NR_GPIOS的值(代表有几个GPIO分组)。
上面3个函数实际上要调用底层函数,自己实现后就可以用gpio的架构了。
#define gpio_get_value __gpio_get_value
#define gpio_set_value __gpio_set_value
#define gpio_cansleep __gpio_cansleep
对于SOC,与平台相关的代码为每组gpio注册一个gpio_chip,里面是参照硬件原理图和数据手册定义的。通常在平台初始化时gpio也要初始化,通过调用arch_initcall或者更早的函数,他们也可作为IRQ来用。
还有对应外部GPIO控制器的描诉,比如I2C和SPI扩展器,ASICs,FPGAs等,当加载模块时,初始化函数要分配好相应的数据,然后probe()会调用gpiochip_add()注册(没详细看,具体见文档)。
GPIO是与硬件体系密切相关的,linux提供一个模型来让驱动统一处理GPIO,即各个板卡都有实现自己的gpio_chip控制模块:request, free, input,output, get,set,irq... 然后把控制模块注册到内核中,这时会改变全局gpio数组:gpio_desc[].
当用户请求gpio时,就会到这个数组中找到,并调用这个GPIO对应的gpio_chip的处理函数。
寄存器读写函数: __raw_writel() __raw_writeb() __raw_readl() __raw_readb()
gpio是一组可控件的脚,由多个寄存器同时控制。通过设置对应的寄存器可以达到设置GPIO口对应状态与功能。
数据状态,输入输出方向,清零,中断(那个边沿触发), 一般是一组(bank)一组的。
//
注册方法:
1:struct gpio_chip: 表示一个gpio controller.通过这个结构抽象化所有的 GPIO源,而让板上其它的模块可以用相同的接口调用使用这些GPIO。
2: struct gpio_desc: 表示一个gpio口,含对应的 gpio_chip.
3: ARCH_NR_GPIOS: 与板相关的GPIO口数量,即是全局GPIO数组:static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
4: 注册 gpio_chip时,就是根据 chip 的数据 修改全局 GPIO数组中 gpio_desc 字段(chip, flags)。
gpiolib架构会在/sys/class/gpio下提供用户编程接口,可以通过这个接口去控制gpio状态。(具体见文档)
struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3
#define FLAG_SYSFS 4
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
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);
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;
unsigned can_sleep:1;
unsigned exported:1;
};
批量初始化方法:
申请:
err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
释放:
gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));
导出gpio到用户空间:int gpio_export(unsigned gpio, bool direction_may_change);
创建一个sysfs连接到已导出的GPIO节点:
int gpio_export_link(struct device *dev, const char *name, unsigned gpio)
取消导出:void gpio_unexport();
Gpio设置中断:
gpio ---> irq int gpio_to_irq(unsigned gpio);
首先应该设置此gpio为输入状态,然后获取对应的中断号(或错误吗)。返回编号调用:
request_irq()和free_irq()。
Irq ---> gpio int irq_to_gpio(unsigned irq);
返回gpio编号,再调用gpio_get_value()获取相应的值。(避免使用反向映射,不支持)
----------------------------------------------------------------------------------------
gpiolib.c (gpio框架) drivers/gpio/gpiolib.c + include/asm-generic/gpio.h [对gpio chip接口]
-----------------------------------------------------------------------------------------
gpio_chip作为一个接口负责框架层与控制器层的通讯,主要关注点有:
其申请/释放/方向/获取输入/设置输出/转irq/base+ngpio[见第三部分控制器驱动]
在框架层的主要关注点在:
1. 如何分配不同chip的gpio域
2. 如何管理隶属与不同chip的gpio,并反向追溯到chip以调用控制器的具体寄存器操作
3. 统一提前管理了哪些gpio状态以及是否有必要
在gpiochip_add()中是对gpio chip的注册,并插入到框架gpio的管理中,
全局变量 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
gpio_desc作为整个系统的gpio的管理者,主要包含两个成员:chip 与 flags.
flags为框架层对gpio的整体管理标识,起MASK的作用。[有:是否已申请/是否是输出/是否保留等]
插入的规则实现在:gpiochip_find_base(int ngpio)
从后往前遍历全局gpio desc, 只要是非保留gpio且无宿主chip的连续gpio的空间起址作为base, ngpio则依次向下扩展。
简单的初始化:挂上对应的chip,检查是否有设置输入函数,没有则设置 IS_OUT位到flags.
[其中关于gpio的设备树管理暂时不予关注]
request流程: 检查对应gpio的flags是否FLAG_REQUESTED,如果未被request则调用chip的request接口实现芯片级别的调用。
free流程: 确认已经被REQUESTED, 后启用芯片级的free.
从系统gpio接口传递下来的gpio均是以base为基址,而传递到芯片上都是回归到原始gpio号
------------------------------------------------------------------------------------------
SC8810 gpio控制器驱动
-------------------------------------------------------------------------------------------
这里是整个gpio系统的核心,初步总结需要关注以下几点:
chip支持的gpio section如何划分
gpio如何配置使能即芯片如何管理众多gpio口的多个标识位的功能选项
申请/释放/方向/获取输入/设置输出/转irq/base+ngpio的处理流程及原理
gpio对应irq号的分配及映射规则
配置一个系统gpio需要的必要步骤
section:(GPIO_BASE:0xE0031000/SPRD_MISC_BASE:0xE0037000)
{ (GPIO_BASE + 0*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 1*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 2*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 3*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 4*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 5*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 6*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 7*0x80), 0x10, GPIO_SECTION_GPIO },
{ (GPIO_BASE + 8*0x80), 0x10, GPIO_SECTION_GPIO },
{ (SPRD_MISC_BASE + 0x480), 0x10, GPIO_SECTION_GPIO },
{ (SPRD_MISC_BASE + 0x4c0), 0xe, GPIO_SECTION_GPIO },
当获取一个gpio号后,需要获取的基本信息为:在哪个section/偏移量是多大/是何种芯片gpio
获取方法:
a.((gpio_id>>4) -1) * 0x80 + (u32) GPIO_BASE;
b.gpio_id & 0xF
c.gpio是在数字芯片上还是模拟芯片上:
#define NR_D_DIE_GPIOS 147
即:芯片上的gpio号小于147即位于数字芯片,否则位于模拟芯片
在一个芯片的整个寄存器内存中,采取以section + 功能的管理方式,也就是说对于同一个gpio的不同功能配置需要通过三个步骤,第一首先
需要找到段寄存器(section的基址);第二步是功能偏移的管理寄存器(功能偏移),第三步是根据段内偏移定位到某一位(bit)来配置。
注:
根据不同类型的gpio类型:
enum gpio_section_type {
GPIO_SECTION_GPI = 0x0,
GPIO_SECTION_GPO,
GPIO_SECTION_GPIO,
GPIO_SECTION_INVALID
};
其相应的功能偏移页不同。
申请:设置其功能寄存器的GPIO_DMSK功能偏移,在对应段内偏移处置位。
释放:清除对应MASK位
输出方向:三个步骤,1.设置GPIO_DIR对应位为1;2.清除GPIO_INEN对应位;3.设置GPIO_DATA对应位为需要输出的值。
输入方向:第一二步相反,无第三步。
设置及获取值:直接设置/读取GPIO_DATA功能寄存器[需要检查一些相关的方向等信息]
to irq
全局的映射数组来管理所有的gpio与irq的映射
static struct gpio_irq_map gpio_irq_table[NR_GPIO_IRQS];
映射规则是:从0-10[仅限映射10个中断号],遍历映射表找到第一个gpio offset相同的表项,返回其对应的irq值。
方向 to gpio 一般kernel不支持使用,但实现原理与上相同。
在gpio芯片管理中,最重要的一块就是irq的管理,包括irq的所有属性管理,如:irq屏蔽使能/触发条件等等。
下一篇笔记将详细描述 kernel irq的管理框架以及gpio的irq分配规则及触发原理。
- 内核初始化与gpio子系统
- Windows源码分析 - 1.初始化内核与执行体子系统
- Linux内核驱动之GPIO子系统
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动之GPIO子系统-GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动之GPIO子系统GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动之GPIO子系统-GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动之GPIO子系统-GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- Linux内核驱动 GPIO子系统 GPIO的使用
- Linux内核驱动之GPIO子系统(一)GPIO的使用
- htmlspecialchars 详解
- MKNetworkKit
- module_init与__attribute__
- android 代码取消网络保存
- hdu1018
- 内核初始化与gpio子系统
- 笔试面试题目
- 修改PMON flash芯片大小
- apache服务器下php项目访问速度慢的原因原来是因为session保存方式为files?
- linux/drivers/video/samsung/tv/s3c-tvscaler.c
- HDU 1159 子序列
- java中的乐观锁和悲观锁
- android draw9patch的使用说明
- vc 简易图形界面创建方法