dis_chip在struct bgpio chip中什么意思

4505人阅读
mtk开发(15)
如果需要定制GPIO口呢,需要使用mediatek/dct/DrvGen.exe工具,点击Open,选择mediatek/custom/project_dir/kernel/dct/dct/codegen.dws文件,点击Edit,将出现如图所示的一个窗口:
EintMode: 指示是否当作中断引脚来使用,例如GPIO0就可以当作ENTI0,如果选中了该模式,其它模式是不可见的。
Def.Mode: GPIO默认的模式,什么意思呢,就是在系统启动过程中GPIO口的模式,GPIO口启动模式是定义在文件cust_gpio_boot.h文件中的,例如,GPIO3的启动模式就是作为普通的GPIO口来使用,即模式为GPIO_MODE_00,稍后会为大家讲解cust_gpio_boot.h文件是怎么生成的。
M0~M7: 由于GPIO口是复用的,也就说除了作为普通的GPIO口使用之外,还有其它用途,所以这里会在cust_gpio_usage.h文件中产生一些宏定义,可能某些驱动中会用到这些宏定义。
InPull En: 是指GPIO是否使能上拉或下拉。
InPull SelHigh: 上面只是只是指示是否使能上拉或下拉,但是并没有指出到底是上拉还是下拉,那么就由这里来指定,如果勾选了那么就表示上拉,否则就是下拉。
Def.Dir: 指示GPIO默认的数据传输方向,是输入还是输出,都需要设置的。
In、Out: In和Out这两个选项暂时不知道有何用处,根据字面意思呢,应该是允许输入或输出的。
OutHigh: 指示GPIO口作为普通IO时输出电平高低。
VarName1: 变量的名字,有些驱动中可能会使用到一些GPIO口,那么这里就是为GPIO口定义一个别名,那么在驱动中只需要这个别名就可以了,而这个GPIO口是可以换的,所以这里也是为了可移植性。
在GPIO口配置完成之后,首先是需要点击Save来保存这个配置,可以点击Gen Code去生成新的代码,新的代码就生成在和codegen.dws同一个目录下,但是在Android 4.4版本上我都不是这样做的,在保存以后,直接运行./mk n k也会生成新的代码,生成目录在:
out/target/product/project_dir/obj/CUSTGEN/custom/kernel/dct/,如果是其他版本,可能会不相同。
前面说了在系统启动过程中会初始化这些GPIO口,那么什么地方在做初始化呢,有两个地方preloader和lk,preloader初始化代码流程如下:
mt_gpio_set_default()-&mt_gpio_set_default_chip()
而该部分代码在mediatek/platform/mt6582/preloader/src/drivers/gpio_init.c中,在该文件中首先就是包含文件cust_gpio_boot.h。
而lk部分初始化流程都是差不多的,流程如下:
mt_gpio_set_default()-&mt_gpio_set_default_chip()
lk部分代码在mediatek/platform/mt6582/lk/mt_gpio_init.c中。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:508872次
积分:8487
积分:8487
排名:第1388名
原创:341篇
转载:43篇
评论:99条
(1)(6)(3)(5)(7)(3)(5)(3)(5)(3)(10)(4)(1)(3)(1)(6)(11)(7)(1)(9)(6)(13)(12)(4)(13)(4)(1)(23)(6)(7)(16)(5)(7)(13)(9)(1)(7)(2)(2)(4)(7)(2)(8)(4)(9)(6)(11)(15)(10)(4)(4)(22)(35)(转)i.MX53&GPIO&按键驱动
转自:http://blog.chinaunix.net/uid--id-4134510.html
硬件平台:IMX53-QSB
内核版本:LINUX-2.6.35.3
系统版本:ANDROID 2.3.4
一、GPIO 的使用&
按键的处理需要读取相应
IO 引脚的值,阅读 IMX53 处理器芯片手册,得知将 GPIO 读模式大的步骤如下:
1. 通过设置 IOMUX 将相应引脚配置为 GPIO 模式,控制的寄存器是
IOMUXC_SW_MUX_CTL_PAD_XXX
2. 配置 GPIO 的方向为输入,控制的寄存器是 GPIOx_GDIR ,0表示输入,1表示输出
3. 读取相应 GPIO 引脚的值,读取的寄存器为 GPIOx_PSR
第1步,配置为 gpio 模式(以 MX53_PAD_GPIO_2__GPIO1_2 为例)
在文件 arch/arm/mach-mx5/mx53_loco.c 中
板子初始化函数 mxc_board_init 中,调用了 io 初始化函数 mx53_loco_io_init
static void __init mxc_board_init(void)
mx53_loco_io_init();
mx53_loco_io_init 函数中调用 mxc_iomux_v3_setup_multiple_pads
函数配置引脚:
static void __init mx53_loco_io_init(void)
&&mxc_iomux_v3_setup_multiple_pads(mx53_loco_pads,ARRAY_SIZE(mx53_loco_pads));
mx53_loco_pads 为一 u64 类型的数组:
static iomux_v3_cfg_t mx53_loco_pads[] = {
&&MX53_PAD_GPIO_2__GPIO1_2,&
MX53_PAD_GPIO_2__GPIO1_2 定义在
arch/arm/plat-mxc/include/mach/iomux-mx53.h
#define MX53_PAD_GPIO_2__GPIO1_2
&(_MX53_PAD_GPIO_2__GPIO1_2 |
MUX_PAD_CTRL(NO_PAD_CTRL))
MX53_PAD_GPIO_2__GPIO1_2 由 _MX53_PAD_GPIO_2__GPIO1_2 与
MUX_PAD_CTRL(NO_PAD_CTRL) 按位或得到,
先来看 _MX53_PAD_GPIO_2__GPIO1_2 :
#define _MX53_PAD_GPIO_2__GPIO1_2
&IOMUX_PAD(0x6B8, 0x328, 1, 0x0, 0,
IOMUX_PAD 的定义在
arch/arm/plat-mxc/include/mach/iomux-v3.h
#define IOMUX_PAD(_pad_ctrl_ofs, _mux_ctrl_ofs, _mux_mode,
_sel_input_ofs,_sel_input, _pad_ctrl)
&( ((iomux_v3_cfg_t)(_mux_ctrl_ofs) &&
MUX_CTRL_OFS_SHIFT)|\
&((iomux_v3_cfg_t)(_mux_mode) &&
MUX_MODE_SHIFT)|\
&((iomux_v3_cfg_t)(_pad_ctrl_ofs) &&
MUX_PAD_CTRL_OFS_SHIFT)|\
&((iomux_v3_cfg_t)(_pad_ctrl) &&
MUX_PAD_CTRL_SHIFT)|\
&((iomux_v3_cfg_t)(_sel_input_ofs) &&
MUX_SEL_INPUT_OFS_SHIFT)|\
&((iomux_v3_cfg_t)(_sel_input) &&
MUX_SEL_INPUT_SHIFT) )
#define MUX_CTRL_OFS_SHIFT
#define MUX_PAD_CTRL_OFS_SHIFT
#define MUX_SEL_INPUT_OFS_SHIFT
#define MUX_MODE_SHIFT
#define MUX_PAD_CTRL_SHIFT
#define MUX_SEL_INPUT_SHIFT
那么 IOMUX_PAD 是这个样子:
&__________
&|61|60|59|58|
&_sel_input &
&__________________________________________
&|57|56|55|54|53|52|51|50|49|48|47|46|45|44|43|42|41|
&_pad_ctrl & &
&_____________
&|40|39|38|37|36|
& _mux_mode
&______________________________
&|35|34|33|32|31|30|29|28|27|26|25|24|
& &_sel_input_ofs
&______________________________
&|23|22|21|20|19|18|17|16|15|14|13|12|
&_pad_ctrl_ofs &
&________________________
&|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1|
& _mux_ctrl_ofs &
这样 _MX53_PAD_GPIO_2__GPIO1_2 即 IOMUX_PAD(0x6B8, 0x328, 1, 0x0, 0,
0) 是这个样子:
&__________
&|61|60|59|58|
&__________________________________________
&|57|56|55|54|53|52|51|50|49|48|47|46|45|44|43|42|41|
&____________
&|40|39|38|37|36|
&______________________________
&|35|34|33|32|31|30|29|28|27|26|25|24|
&______________________________
&|23|22|21|20|19|18|17|16|15|14|13|12|
&0x6B8 & &
&________________________
&|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1|
& & & 0x328
再来看看 MUX_PAD_CTRL(NO_PAD_CTRL)
#define MUX_PAD_CTRL(x) &((iomux_v3_cfg_t)(x)
&& MUX_PAD_CTRL_SHIFT)
#define NO_PAD_CTRL &((iomux_v3_cfg_t)1 &&
(MUX_PAD_CTRL_SHIFT + 16))
这 MUX_PAD_CTRL(NO_PAD_CTRL) 将 1 左移了 98 位,代表什么意思?
这样 MX53_PAD_GPIO_2__GPIO1_2 的值就确定了。
mxc_iomux_v3_setup_multiple_pads 定义在 arch/arm/plat-mxc/iomux-v3.c
int mxc_iomux_v3_setup_multiple_pads(iomux_v3_cfg_t *pad_list,
unsigned count)
&&iomux_v3_cfg_t *p =
& &&for (i = 0;
i & i++) {
&&mxc_iomux_v3_get_pad(p);
& &&return
mxc_iomux_v3_setup_pad 函数定义:
int mxc_iomux_v3_setup_pad(iomux_v3_cfg_t pad)
mux_ctrl_ofs = (pad & MUX_CTRL_OFS_MASK) &&
MUX_CTRL_OFS_SHIFT; & &
& // 0x328
& &&u32 mux_mode
= (pad & MUX_MODE_MASK) &&
MUX_MODE_SHIFT;
sel_input_ofs = (pad & MUX_SEL_INPUT_OFS_MASK) &&
MUX_SEL_INPUT_OFS_SHIFT;
sel_input = (pad & MUX_SEL_INPUT_MASK) &&
MUX_SEL_INPUT_SHIFT;
pad_ctrl_ofs = (pad & MUX_PAD_CTRL_OFS_MASK) &&
MUX_PAD_CTRL_OFS_SHIFT;
& &&u32 pad_ctrl
= (pad & MUX_PAD_CTRL_MASK) &&
MUX_PAD_CTRL_SHIFT;
(mux_ctrl_ofs)
&&__raw_writel(mux_mode, base +
mux_ctrl_ofs);
(sel_input_ofs)
&&__raw_writel(sel_input, base +
sel_input_ofs);
(!(pad_ctrl & NO_PAD_CTRL) &&
pad_ctrl_ofs)
&&__raw_writel(pad_ctrl, base +
pad_ctrl_ofs);
& &&return
如果需要将某 IO 引脚 XXX_GPIO_XXX 设置为 GPIO 模式,将 XXX_GPIO_XXX 添加到
mx53_loco_pads 数组即可。
第2步,将 gpio 设置为输入
使用的函数为 int gpio_direction_input(unsigned
定义在 drivers/gpio/gpiolib.c
int gpio_direction_input(unsigned gpio)
& &&unsigned
& &&struct
gpio_chip*
& &&struct
gpio_desc*desc
= &gpio_desc[gpio];
&status = -EINVAL;
&&spin_lock_irqsave(&gpio_lock,
(!gpio_is_valid(gpio))
& &&chip =
& &&if (!chip ||
!chip-&get || !chip-&direction_input)
& &&gpio -=
& &&if (gpio
&= chip-&ngpio)
& &&status =
gpio_ensure_requested(desc, gpio);
& &&if (status
&&spin_unlock_irqrestore(&gpio_lock,
&&might_sleep_if(extra_checks
&& chip-&can_sleep);
& &&if (status)
&&status = chip-&request(chip,
&&if (status & 0)
&&pr_debug("GPIO-%d: chip request
fail, %d\n",
&&chip-&base + gpio,
& &&status =
chip-&direction_input(chip, gpio);
& &&if (status
&&clear_bit(FLAG_IS_OUT,
&desc-&flags);
& &&return
&&spin_unlock_irqrestore(&gpio_lock,
&&pr_debug("%s: gpio-%d status
& &&__func__,
gpio, status);
& &&return
该函数调用了 chip-&direction_input(chip, gpio);
最终调用到的是 arch/arm/plat-mxc/gpio.c 文件中的函数 mxc_gpio_direction_input
,原型如下:
static int mxc_gpio_direction_input(struct gpio_chip *chip,
unsigned offset)
&&_set_gpio_direction(chip,
offset, 0);
& &&return
_set_gpio_direction 函数原型:
static void _set_gpio_direction(struct gpio_chip *chip, unsigned
offset,int dir)
& &&struct
mxc_gpio_port *port =
&&container_of(chip, struct
mxc_gpio_port, chip);
& &&unsigned
&&spin_lock_irqsave(&port-&lock,
__raw_readl(port-&base + GPIO_GDIR);
&&l |= 1 &&
&&l &= ~(1 &&
&&__raw_writel(l, port-&base +
GPIO_GDIR);
&&spin_unlock_irqrestore(&port-&lock,
第3步,获取 GPIO 引脚的值
使用的宏为 gpio_get_value
arch/arm/plat-mxc/include/mach/gpio.h
#define gpio_get_value & &
&__gpio_get_value
__gpio_get_value 函数原型定义在 drivers/gpio/gpiolib.c
int __gpio_get_value(unsigned gpio)
& &&struct
gpio_chip*
& &&chip =
gpio_to_chip(gpio);
&&WARN_ON(extra_checks &&
chip-&can_sleep);
& &&return
chip-&get ? chip-&get(chip, gpio - chip-&base) :
__gpio_get_value 调用 chip-&get ,实际调用到的是 arch/arm/plat-mxc/gpio.c
文件中 mxc_gpio_get 函数
static int mxc_gpio_get(struct gpio_chip *chip, unsigned
& &&struct
mxc_gpio_port *port =
container_of(chip, struct mxc_gpio_port, chip);
& &&return
(__raw_readl(port-&base + GPIO_PSR) && offset) &
二、PLATFORM DEVICE
GPIO按键作为系统的一种设备,挂载到platform总线上,相应的platform
device定义在arch/arm/mach-mx5/mx53_loco.c:
static struct platform_device loco_button_device =
& & .name &
"gpio-keys",
& & .num_resources
& & .dev &
& .platform_data =
&loco_button_data,
loco_button_data 信息如下: &
static struct gpio_keys_platform_data loco_button_data =
& & .buttons &
&= loco_buttons,
& & .nbuttons &
= ARRAY_SIZE(loco_buttons),
loco_buttons
定义了5个按键:power、back、home、volumeup、volumedown和menu。
static struct gpio_keys_button loco_buttons[] = {
& & GPIO_BUTTON(MX53_nONKEY,
KEY_POWER, & &
&1, "power", & &
& & GPIO_BUTTON(USER_UI1,
& &KEY_BACK, &
& & 1, "back", &
& & GPIO_BUTTON(USER_UI2,
& &KEY_HOME, &
& & 1, "home", &
& & GPIO_BUTTON(KEY_VOLUP,
& KEY_VOLUMEUP, & 1, "volumeup",
& & GPIO_BUTTON(KEY_VOLDOWN,
KEY_VOLUMEDOWN, 1, "volumedown", 0),
& & GPIO_BUTTON(KEY_SET,
& & KEY_MENU, &
& & 1, "menu", &
宏 GPIO_BUTTON 的内容为:
#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake)
& & .gpio &
& & = gpio_num,
& & .type &
& & = EV_KEY, &
& & .code &
& & = ev_code, &
& & .active_low = act_low,
& & .desc &
& & = "btn " descr,
& & .wakeup &
& = wake, & &
第一个参数为 gpio 引脚,第二个参数为按键的键值。
loco_buttons 使用的 gpio 定义如下:
#define MX53_nONKEY & &
& & (0*32 + 8)
#define USER_UI1 & &
&(1*32 + 14)
#define USER_UI2 & &
&(1*32 + 15)
#define KEY_VOLUP & &
& & & (6*32 +
#define KEY_VOLDOWN & &
& & (0*32 + 4)
#define KEY_SET & &
& (0*32 + 2) &
键值定义在 include/linux/input.h 文件中:
#define KEY_HOME & &
#define KEY_VOLUMEDOWN &114
#define KEY_VOLUMEUP &
#define KEY_POWER & &
#define KEY_MENU & &
#define KEY_BACK & &
在板子初始化函数 static void __init mxc_board_init(void) 中
调用了 loco_add_device_buttons()
函数将loco_button_device加入到系统中,
这样驱动即可匹配 .name 找到我们的设备loco_button_device。
loco_add_device_buttons() 函数定义如下:
static void __init loco_add_device_buttons(void)
platform_device_register(&loco_button_device);
三、PLATFORM DRIVER
GPIO按键的驱动文件为:drivers/input/keyboard/gpio_keys.c
通过 module_init ,在总线上注册 name 为 gpio-keys 的驱动,并通过 module_exit
相应的将其注销:
static int __init gpio_keys_init(void)
& & return
platform_driver_register(&gpio_keys_device_driver);
static void __exit gpio_keys_exit(void)
platform_driver_unregister(&gpio_keys_device_driver);
module_init(gpio_keys_init);
module_exit(gpio_keys_exit);
platform driver - gpio_keys_device_driver 如下:
static struct platform_driver gpio_keys_device_driver =
& & .probe &
gpio_keys_probe,
& & .remove &
__devexit_p(gpio_keys_remove),
& & .driver &
& .name & =
"gpio-keys",
& .owner &=
THIS_MODULE,
#ifdef CONFIG_PM
& .pm & & =
&gpio_keys_pm_ops,
在探测到 name 匹配的 device 之后,gpio_keys_probe 得以执行:
static int __devinit gpio_keys_probe(struct platform_device
& &&struct
gpio_keys_platform_data *pdata =
pdev-&dev.platform_
& &&struct
gpio_keys_drvdata *
& &&struct
device *dev = &pdev-&
& &&struct
input_dev *
& &&int i,
& &&int wakeup =
& &&ddata =
kzalloc(sizeof(struct gpio_keys_drvdata) +
&&pdata-&nbuttons *
sizeof(struct gpio_button_data),
&&GFP_KERNEL);
& &&input =
input_allocate_device();
& &&if (!ddata
|| !input) {
&&dev_err(dev, "failed to
allocate state\n");
&&goto fail1;
&&ddata-&input =
&&ddata-&n_buttons =
&&mutex_init(&ddata-&disable_lock);
&&platform_set_drvdata(pdev,
&&input-&name =
&&input-&phys =
"gpio-keys/input0";
&&input-&dev.parent =
&&input-&id.bustype =
&&input-&id.vendor
&= 0x0001;
&&input-&id.product =
&&input-&id.version =
(pdata-&rep)
&&__set_bit(EV_REP,
input-&evbit);
& &&for (i = 0;
i & pdata-& i++) {
&&struct gpio_keys_button *button
= &pdata-&buttons[i];
&&struct gpio_button_data *bdata
= &ddata-&data[i];
&&unsigned int type =
button-&type ?: EV_KEY;
&&bdata-&input =
&&bdata-&button =
gpio_keys_setup_key(pdev, bdata, button);
&&if (error)
&&goto fail2;
(button-&wakeup)
&&wakeup = 1;
&&input_set_capability(input,
type, button-&code);
& &&error =
sysfs_create_group(&pdev-&dev.kobj,
&gpio_keys_attr_group);
& &&if (error)
&&dev_err(dev, "Unable to export
keys/switches, error: %d\n",
& &&error =
input_register_device(input);
& &&if (error)
&&dev_err(dev, "Unable to
register input device, error: %d\n",
&&goto fail3;
& &&for (i = 0;
i & pdata-& i++)
&&gpio_keys_report_event(&ddata-&data[i]);
&&input_sync(input);
&&device_init_wakeup(&pdev-&dev,
& &&return
&&sysfs_remove_group(&pdev-&dev.kobj,
&gpio_keys_attr_group);
& &&while (--i
&&free_irq(gpio_to_irq(pdata-&buttons[i].gpio),
&ddata-&data[i]);
(pdata-&buttons[i].debounce_interval)
&&del_timer_sync(&ddata-&data[i].timer);
&&cancel_work_sync(&ddata-&data[i].work);
&&gpio_free(pdata-&buttons[i].gpio);
&&platform_set_drvdata(pdev,
&&input_free_device(input);
&&kfree(ddata);
& &&return
gpio_keys_setup_key 函数内容 :
static int __devinit gpio_keys_setup_key(struct platform_device
& &&struct
gpio_button_data *bdata,
& &&struct
gpio_keys_button *button)
& &&char *desc =
button-&desc ? button-&desc : "gpio_keys";
& &&struct
device *dev = &pdev-&
& &&unsigned
& &&int irq,
&&setup_timer(&bdata-&timer,
gpio_keys_timer, (unsigned long)bdata);
&&INIT_WORK(&bdata-&work,
gpio_keys_work_func);
& &&error =
gpio_request(button-&gpio, desc);
& &&if (error
&&dev_err(dev, "failed to request
GPIO %d, error %d\n",
&&button-&gpio,
&&goto fail2;
& &&error =
gpio_direction_input(button-&gpio);
& &&if (error
&&dev_err(dev, "failed to
configure"
&&" direction for GPIO %d, error
&&button-&gpio,
&&goto fail3;
gpio_to_irq(button-&gpio);
& &&if (irq &
&&dev_err(dev, "Unable to get irq
number for GPIO %d, error %d\n",
&&button-&gpio,
&&goto fail3;
& &&irqflags =
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
(!button-&can_disable)
&&irqflags |=
IRQF_SHARED;
& &&error =
request_irq(irq, gpio_keys_isr, irqflags, desc,
& &&if (error)
&&dev_err(dev, "Unable to claim
irq %d; error %d\n",
&&irq, error);
& &&return
&&gpio_free(button-&gpio);
& &&return
中断处理函数 gpio_keys_isr 定义如下:
static irqreturn_t gpio_keys_isr(int irq, void
& &&struct
gpio_button_data *bdata = dev_
& &&struct
gpio_keys_button *button = bdata-&
& &&BUG_ON(irq
!= gpio_to_irq(button-&gpio));
(button-&debounce_interval)
&&mod_timer(&bdata-&timer,
&&jiffies +
msecs_to_jiffies(button-&debounce_interval));
&&schedule_work(&bdata-&work);
& &&return
IRQ_HANDLED;
gpio_keys_work_func 的定义:
static void gpio_keys_work_func(struct work_struct
& &&struct
gpio_button_data *bdata =
&&container_of(work, struct
gpio_button_data, work);
&&gpio_keys_report_event(bdata);
函数 gpio_keys_report_event 内容:
static void gpio_keys_report_event(struct gpio_button_data
& &&struct
gpio_keys_button *button = bdata-&
& &&struct
input_dev *input = bdata-&
& &&unsigned int
type = button-&type ?: EV_KEY;
& &&int state =
(gpio_get_value(button-&gpio) ? 1 : 0) ^
button-&active_
&&input_event(input, type,
button-&code, !!state);
&&input_sync(input);
如果要添加或是修改 android
的按键功能,需保证&device/fsl/imx53_loco/gpio-keys.kl
文件中有相应的键值,可以参考 qwerty.kl 文件修改。
以上内容为个人对 IMX53 GPIO 按键驱动的认识,如有不妥之处,还望批评指正。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。Linux(419)
嵌入式(23)
如果是在已经适配好的linux内核上,那么相信已经有了完成的gpiochip,可以在用户空间/sys/class/gpio目录下看到,如:
gpiochip0/
gpiochip32/
gpiochip64/
gpiochip96/
然后对照手册看下需要用到哪个GPIO,举个例子:
如果使想用GPIO1_20
那么GPIO Number就是 1 x 32 + 20 = 54
使用分两种情况:
1. 如果使最终向在用户空间使用:
echo 54 & export
这样在这个/sys/class/gpio目录下就会产生gpio54文件夹
在文件夹下需要用到的有两个文件:
direction 用来配置输入(in)还是输出(out)
value 如果这个GPIO配置成了输入,那么通过cat value可以查看当前这个GPIO是什么电位;如果配置成了输出,那么可以通过echo 1/0 & value给这个GPIO口指定输出电平。
2. 在内核空间使用:
#include &linux/gpio.h&
gpio_request_one(54, GPIOF_INIT_HIGH, &gpio1_20&)
这里是配置成输出,默认高电平,别名(label)为gpio1_20——就是给你的IO口取个名字。
gpio_request_one(54, GPIOF_IN, &gpio1_20&)
这个就是配置成输入。
使用完后别忘了free
gpio_free(54);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:418874次
积分:5567
积分:5567
排名:第2912名
原创:32篇
转载:729篇
评论:18条
(1)(1)(1)(7)(8)(14)(10)(4)(5)(2)(1)(9)(2)(3)(1)(11)(5)(7)(9)(17)(32)(2)(5)(9)(6)(3)(12)(36)(32)(20)(15)(105)(4)(36)(16)(15)(52)(61)(41)(9)(12)(10)(19)(19)(1)(7)(2)(5)(3)(1)(2)(29)(22)Linux内核中的GPIO系统之(3):pin controller driver代码分析
对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datasheet会把pin controller的内容放入GPIO controller的章节中),主要功能包括:
(1)pin multiplexing。基于ARM core的嵌入式处理器一般会提供丰富的功能,例如camera interface、LCD interface、USB、I2C、SPI等等。虽然处理器有几百个pin,但是这些pin还是不够分配,因此有些pin需要复用。例如:127号GPIO可以做一个普通的GPIO控制LED,也可以配置成I2C的clock信号,也可以配置成SPI的data out信号。当然,这些功能不可能同时存在,因为硬件信号只有一个。
(2)pin configuration。这些配置参数包括:pull-up/down电阻的设定, tri-state设定,drive-strength的设定。
本文主要描述pin control subsystem中的low level driver,也就是驱动pin controller的driver。具体的硬件选用的是S3C2416的硬件平台。既然是代码分析,本文不是非常多的描述框架性的内容,关于整个pin control subsystem软件结构的描述请参考Linux内核中的GPIO系统之(2)。
阅读本文需要device tree的知识,建议首先阅读。
二、pin controller相关的DTS描述
类似其他的硬件,pin controller这个HW block需要是device tree中的一个节点。此外,各个其他的HW block在驱动之前也需要先配置其引脚复用功能,因此,这些device(我们称pin controller是host,那么这些使用pin controller进行引脚配置的device叫做client device)也需要在它自己的device tree node中描述pin control的相关内容
1、S3C2416 pin controller DTS结构
下面的伪代码描述了S3C2416 pin controller 的DTS结构:
pinctrl@ {&
&&&&&&& 定义S3C2416 pin controller自己的属性
&&&&&&& 定义属于S3C2416 pin controller的pin configurations
每个pin configuration都是pin controller的child node,描述了client device要使用到的一组pin的配置信息。具体如何定义pin configuration是和具体的pin controller相关的。
在pin controller node中定义pin configuration其目的是为了让client device引用。所谓client device其实就是使用pin control subsystem提供服务的那些设备,例如串口设备。在使用之前,我们一般会在初始化代码中配置相关的引脚功能是串口功能。有了device tree,我们可以通过device tree来传递这样的信息。也就是说,各个device可以通过自己节点的属性来指向pin controller的某个child node,也就是pin configuration了。samsung 24xx系列SOC的pin controller的pin configurations包括两类,一类是定义pin bank,另外一类是定义功能复用配置。
2、pin configuration定义
我们举两个简单的例子(当然一个是pin bank,另外一个是定义功能复用配置)来理解pin configuration第一个例子是描述pin bank:
pinctrl@ {&
&&&&&&& 定义S3C2416 pin controller自己的属性
&&&&&&& gpf {
&&&&&&&&&&& gpio-
&&&&&&&&&&& #gpio-cells = &0x2&;
&&&&&&&&&&& interrupt-
&&&&&&&&&&& #interrupt-cells = &0x2&;
&&&&&&&&&&& linux,phandle = &0xc&;
&&&&&&&&&&& phandle = &0xc&;
&&&&&&& };
其实S3C2416 pin controller定义了gpa到gpm共计11个sub node,每个sub node是描述S3C2416 GPIO controller的各个bank信息。S3C个I/O 端口(或者叫做pin、finger、pad)这些端口分成了11个bank(这里没有用group这个术语,为了和pin group这个概念区分开,pin group的概念下面会具体描述):
Port A(GPA) : 25-output port
Port B(GPB) : 9-input/output port
Port C(GPC) : 16-input/output port
Port D(GPD) : 16-input/output port
Port E(GPE) : 16-input/output port
Port F(GPF) : 8-input/output port
Port G(GPG) : 8-input/output port
Port H(GPH) : 15-input/output port
Port K(GPK) : 16-input/output port
Port L(GPL) : 7-input/output port
Port M(GPM) : 2-input port
之所以分成bank,主要是把特性相同的GPIO进行分组,方便控制。例如:这些bank中,只有GPF和GPG这两个bank上的引脚有中断功能,其他的都没有。interrupt-controller这个属性相信大家已经熟悉,就是说明该node是一个interrupt controller。gpio-controller类似,说明该device node是一个GPIO controller。#gpio-cells属性是一个GPIO controller的必须定义的属性,它描述了需要多少个cell来具体描述一个GPIO(这是和具体的GPIO controller相关的)。#interrupt-cells的概念类似,不再赘述。phandle(linux,phandle这个属性和phandle是一样的,只不过linux,phandle是old-style,多定义一个属性是为了兼容)定义了一个句柄,当其他的device node想要引用这个node的时候就可以使用该句柄。具体的例子参考下节client device的DTS的描述。
另外一个例子是uart的pin configuration,代码如下:
pinctrl@ {&
&&&&&&& 定义S3C2416 pin controller自己的属性
uart0-data {
&&& samsung,pins = "gph-0", "gph-1";
&&& samsung,pin-function = &0x2&;
&&& linux,phandle = &0x2&;
&&& phandle = &0x2&;
uart0-fctl {
&&& samsung,pins = "gph-8", "gph-9";
&&& samsung,pin-function = &0x2&;
&&& linux,phandle = &0x3&;
&&& phandle = &0x3&;
samsung,pins这个属性定义了一个pin configuration所涉及到的引脚定义。对于uart0-data这个node,该配置涉及了gph bank中的第一个和第二个GPIO pin。一旦选择了一个功能,那么samsung,pins定义的所有的引脚都需要做相应的功能设定,那么具体设定什么值呢?这就是samsung,pin-function定义的内容了。而具体设定哪个值则需要去查阅datasheet了。对于uart0-data,向gph bank中的第一个和第二个GPIO pin对应的配置寄存器中写入2就可以把这两个pin定义为uart功能。
3.client device的DTS
一个典型的device tree中的外设node定义如下:
device-node-name {&
&&&&&&& 定义该device自己的属性&&
&&&&&&& pinctrl-names = "sleep", "active";------(1)
&&&&&&& pinctrl-0 = &pin-config-0-a&;--------------(2)
&&&&&&& pinctrl-1 = &pin-config-1-a pin-config-1-b&;&&&&&&&&
(1)pinctrl-names定义了一个state列表。那么什么是state呢?具体说应该是pin state,对于一个client device,它使用了一组pin,这一组pin应该同时处于某种状态,毕竟这些pin是属于一个具体的设备功能。state的定义和电源管理关系比较紧密,例如当设备active的时候,我们需要pin controller将相关的一组pin设定为具体的设备功能,而当设备进入sleep状态的时候,需要pin controller将相关的一组pin设定为普通GPIO,并精确的控制GPIO状态以便节省系统的功耗。state有两种,标识,一种就是pinctrl-names定义的字符串列表,另外一种就是ID。ID从0开始,依次加一。根据例子中的定义,state ID等于0(名字是active)的state对应pinctrl-0属性,state ID等于1(名字是idle)的state对应pinctrl-1属性。具体设备state的定义和各个设备相关,具体参考在自己的device bind。
(2)pinctrl-x的定义。pinctrl-x是一个句柄(phandle)列表,每个句柄指向一个pin configuration。有时候,一个state对应多个pin configure。例如在active的时候,I2C功能有两种配置,一种是从pin ID{7,8}引出,另外一个是从pin ID{69,103}引出。
我们选取samsung串口的dts定义如下:
serial@ {&
&&&&&&& ……
&&&&&&& pinctrl-names = "default";
&&&&&&& pinctrl-0 = &0x2 0x3&;
该serial device只定义了一个state就是default,对应pinctrl-0属性定义。pinctrl-0是一个句柄(phandle)列表,每个句柄指向一个pin configuration。0x2对应上节中的uart0-data节点,0x03对应uart0-fctl 节点,也就是说,这个串口有两种配置,一种是从gph bank中的第一个和第二个GPIO pin引出,另外一个是从gph bank中的第8个和第9个GPIO pin引出。
三、 pin controller driver初始化
1、注册pin control device
旧的内核一般是在machine相关的代码中建立静态的platform device的数据结构,然后在machine初始化的时候,将静态定义的platform device注册到系统。不过在引入device tree之后,事情发生了变化。
根据,我们知道,在系统初始化的时候,dts描述的device node会形成一个树状结构,在machine初始化的过程中,会scan device node的树状结构,将真正的硬件device node变成一个个的设备模型中的device结构(比如struct platform_device)并加入到系统中。我们看看具体2416描述pin controller的dts code,如下:
pinctrl@ {
&&&&&&& reg = &0xx1000=""&;
&&&&&&& compatible = "samsung,s3c2416-pinctrl";
……省略wakeup的pin configuration
……省略gpb~gpm这些pink bank的pin configuration
……省略Pin groups的相关描述
reg属性描述pin controller硬件的地址信息,开始地址是0x ,地址长度是0x1000。compatible属性用来描述pin controller的programming model。该属性的值是string list,定义了一系列的modle(每个string是一个model)。这些字符串列表被操作系统用来选择用哪一个pin controller driver来驱动该设备,后面的代码会更详细的描述。 pin control subsystem要想进行控制,必须首先了解自己控制的对象,也就是说软件需要提供一个方法将各种硬件信息(total有多少可控的pin,有多少bank,pin的复用情况以及pin的配置情况)注册到pin control subsystem中,这也是pin controller driver的初始化的主要内容。这些信息当然可以通过定义静态的表格(参考linux/drivers/pinctrl目录下的pinctrl-u300.c文件,该文件定义了一个大数组u300_pads来描述每一个pin),也可以通过dts加上静态表格的方式(2416采用的方式)。
2、注册pin controller driver
当然,这个device node也会变成一个platform device加入系统。有了device,那么对应的platform driver是如何注册到系统中的呢?代码如下:
static int __init samsung_pinctrl_drv_register(void)
&&& return platform_driver_register(&samsung_pinctrl_driver);
系统初始化的时候,该函数会执行,向系统注册了samsung_pinctrl_driver:
static struct platform_driver samsung_pinctrl_driver = {
&&& .probe&&&&&&& = samsung_pinctrl_probe, ----该driver的初始化函数
&&& .driver = {
&&&&&&& .name&&& = "samsung-pinctrl",
&&&&&&& .owner&&& = THIS_MODULE,
&&&&&&& .of_match_table = samsung_pinctrl_dt_match, ----匹配列表
3、probe过程(driver初始化过程)
在linux kernel引入统一设备模型之后,bus、driver和device形成了设备模型中的铁三角。对于platform这种类型的bus,其铁三角数据是platform_bus_type(表示platform这种类型的bus)、struct platform_device(platform bus上的device)、struct platform_driver(platform bus上的driver)。统一设备模型大大降低了驱动工程师的工作量,驱动工程师只要将driver注册到系统即可,剩余的事情交给统一设备模型来完成。每次系统增加一个platform_driver,platform_bus_type都会启动scan过程,让新加入的driver扫描整个platform bus上的device的链表,看看是否有device让该driver驱动。同样的,每次系统增加一个platform_device,platform_bus_type也会启动scan过程,遍历整个platform bus上的driver的链表,看看是否有适合驱动该device的driver。具体匹配的代码是platform bus上的match函数,如下:
static int platform_match(struct device *dev, struct device_driver *drv)
&&& struct platform_device *pdev = to_platform_device(dev);
&&& struct platform_driver *pdrv = to_platform_driver(drv);
&&& /* Attempt an OF style match first */
&&& if (of_driver_match_device(dev, drv))
&&&&&&& return 1;
&&& /* Then try ACPI style match */
&&& if (acpi_driver_match_device(dev, drv))
&&&&&&& return 1;
&&& /* Then try to match against the id table */
&&& if (pdrv-&id_table)
&&&&&&& return platform_match_id(pdrv-&id_table, pdev) != NULL;
&&& /* fall-back to driver name match */
&&& return (strcmp(pdev-&name, drv-&name) == 0);
旧的的platform的匹配函数就是简单的比较device和driver的名字,多么简单,多么清晰,真是有点怀念过去单纯而美好的生活。当然,事情没有那么糟糕,我们这里只要关注OF style的匹配过程即可,思路很简单,解铃还需系铃人,把匹配过程推给device tree模块,代码如下:
const struct of_device_id *of_match_device(const struct of_device_id *matches,
&&&&&&&&&&&&&&&&&&&&&& const struct device *dev)
&&& if ((!matches) || (!dev-&of_node))
&&&&&&& return NULL;
&&& return of_match_node(matches, dev-&of_node);
platform driver提供了match table(struct device_driver 中的of_match_table的成员)。platform device提供了device tree node(struct device中的of_node成员)。对于我们这个场景,match table是samsung_pinctrl_dt_match,代码如下:
static const struct of_device_id samsung_pinctrl_dt_match[] = {
&&& { .compatible = "samsung,s3c2416-pinctrl",
&&&&&&& .data = s3c2416_pin_ctrl },
再去看看dts中pin controller的节点compatible属性的定义,你会禁不住感慨:啊,终于遇到对的人。这里还要特别说明的是data成员被设定为s3c2416_pin_ctrl ,它描述了2416的HW pin controller,我们称之samsung pin controller的描述符,初始化的过程中需要这个数据结构,后面还会详细介绍它。一旦pin controller这个device遇到了适当的driver,就会调用probe函数进行具体的driver初始化的动作了,代码如下:
static int samsung_pinctrl_probe(struct platform_device *pdev)
&&& struct samsung_pinctrl_drv_data *
&&& struct device *dev = &pdev-&
&&& struct samsung_pin_ctrl *
&&& struct resource *
&&& drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); ------(1)
&&& ctrl = samsung_pinctrl_get_soc_data(drvdata, pdev); ----------(2)
&&& drvdata-&ctrl =
&&& drvdata-&dev =
&&& res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -----分配memory资源
&&& drvdata-&virt_base = devm_ioremap_resource(&pdev-&dev, res);
&&& if (IS_ERR(drvdata-&virt_base))
&&&&&&& return PTR_ERR(drvdata-&virt_base);
&&& res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ------分配IRQ资源
&&& if (res)
&&&&&&& drvdata-&irq = res-&
&&& ret = samsung_gpiolib_register(pdev, drvdata); -------------(3)
&&& ret = samsung_pinctrl_register(pdev, drvdata); -------------(4)
&&& if (ctrl-&eint_gpio_init) ------------------(5)
&&&&&&& ctrl-&eint_gpio_init(drvdata);
&&& if (ctrl-&eint_wkup_init)
&&&&&&& ctrl-&eint_wkup_init(drvdata);
&&& platform_set_drvdata(pdev, drvdata); -设定platform device的私有数据为samsung_pinctrl_drv_data
&&& /* Add to the global list */
&&& list_add_tail(&drvdata-&node, &drvdata_list); --挂入全局链表
&&& return 0;
(1)devm_kzalloc函数是为struct samsung_pinctrl_drv_data数据结构分配内存。每当driver probe一个具体的device实例的时候,都需要建立一些私有的数据结构来保存该device的一些具体的硬件信息(本场景中,这个数据结构就是struct samsung_pinctrl_drv_data)。在过去,驱动工程师多半使用kmalloc或者kzalloc来分配内存,但这会带来一些潜在的问题。例如:在初始化过程中,有各种各样可能的失败情况,这时候就依靠driver工程师小心的撰写代码,释放之前分配的内存。当然,初始化过程中,除了memory,driver会为probe的device分配各种资源,例如IRQ 号,io memory map、DMA等等。当初始化需要管理这么多的资源分配和释放的时候,很多驱动程序都出现了资源管理的issue。而且,由于这些issue是异常路径上的issue,不是那么容易测试出来,更加重了解决这个issue的必要性。内核解决这个问题的模式(所谓解决一类问题的设计方法就叫做设计模式)是Devres,即device resource management软件模块。更细节的内容就不介绍了,其核心思想就是资源是设备的资源,那么资源的管理归于device,也就是说不需要driver过多的参与。当device和driver detach的时候,device会自动的释放其所有的资源。
(2)分配了struct samsung_pinctrl_drv_data数据结构的内存,当然下一步就是初始化这个数据结构了。我们先看看2416的pin controller driver是如何定义该数据结构的:
struct samsung_pinctrl_drv_data {
&&& struct list_head&&&&&&&---------多个pin controller的描述符可以形成链表
&&& void __iomem&&&&&&&&&&& *virt_---------访问硬件寄存器的基地址
&&& struct device&&&&&&&&&&& *-----------和platform device建立联系
&&& int&&&&&&&&&&&&&&& --------irq number,对于2416 pin control硬件而言,不需要irq资源
&&& struct samsung_pin_ctrl&&&&&&& *----samsung pin controller描述符
&&& struct pinctrl_desc&&&&&&& ------指向pin control subsystem中core driver中抽象的
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& pin controller描述符。
&&& struct pinctrl_dev&&&&&&& *pctl_ ------指向core driver的pin controller class device
&&& const struct samsung_pin_group&&& *pin_ -描述samsung pin controller中pin groups的信息
&&& unsigned int&&&&&&&&&&& nr_ --------描述samsung pin controller中pin groups的数目
&&& const struct samsung_pmx_func&&& *pmx_ --描述samsung pin controller中function信息
&&& unsigned int&&&&&&&&&&& nr_ --------描述samsung pin controller中function的数目
struct pinctrl_desc和struct pinctrl_dev 都是pin control subsystem中core driver的概念。各个具体硬件的pin controller可能会各不相同,但是可以抽取其共同的部分来形成一个HW independent的数据结构,这个数据就是pin controller描述符,在core driver中用struct pinctrl_desc表示,具体该数据结构的定义如下:
struct pinctrl_desc {
&&& const char *
&&& struct pinctrl_pin_desc const *---指向npins个pin描述符,每个描述符描述一个pin
&&&------------该pin controller中有多少个可控的pin
&&& const struct pinctrl_ops *------callback函数,是core driver和底层driver的接口
&&& const struct pinmux_ops *-----callback函数,是core driver和底层driver的接口
&&& const struct pinconf_ops *-----callback函数,是core driver和底层driver的接口
&&& struct module *
其实整个初始化过程的核心思想就是low level的driver定义一个pinctrl_desc ,设定pin相关的定义和callback函数,注册到pin control subsystem中。我们用引脚描述符(pin descriptor)来描述一个pin。在pin control subsystem中,struct pinctrl_pin_desc用来描述一个可以控制的引脚,我们称之引脚描述符,代码如下:
struct pinctrl_pin_desc {
&&&-------ID,在本pin controller中唯一标识该引脚
&&& const char *-------user friedly name
&&& void *drv_
冰冷的pin ID是给机器用的,而name是给用户使用的,是ascii字符。
struct pinctrl_dev在pin control subsystem的core driver中抽象一个pin control device。其实我们可以通过多个层面来定义一个device。在这个场景下,pin control subsystem的core driver关注的是一类pin controller的硬件设备,具体其底层是什么硬件连接方式,使用什么硬件协议它不关心,它关心的是pin controller这类设备所有的通用特性和功能。当然2416的pin controller是通过platform bus连接的,因此,在low level的层面,需要一个platform device来标识2416的pin controller(需要注意的是:pin controller class device和platform device都是基于一个驱动模型中的device派生而来的,这里struct device是基类,struct pinctrl_dev和struct platform_device都是派生类,当然c本身不支持class,但面向对象的概念是同样的)。为了充分理解class这个概念,我们再举一个例子。对于audio的硬件抽象层,它应该管理所有的audio设备,因此这个软件模块应该有一个audio class的链表,连接了所有的系统中的audio设备。但这些具体的audio设备可能是PCI接口的audio设备,也可能是usb接口的audio设备,从具体的总线层面来看,也会有PCI或者USB设备来抽象对应的声卡设备。
OK,我们再看看samsung_pinctrl_drv_data底部四个成员,要理解该数据结构底部的四个成员,还要理解什么是pin mux function,什么是pin group。对于SOC而言,其引脚除了配置成普通GPIO之外,若干个引脚还可以组成一个pin group,形成特定的功能。例如pin number是{ 0, 8, 16, 24 }这四个引脚组合形成一个pin group,提供SPI的功能。既然有了pin group的概念,为何又有function这个概念呢?什么是function呢?SPI是function,I2C也是一个function,当然GPIO也是一个function。一个function有可能对应一组或者多组pin。例如:为了设计灵活,芯片内部的SPI0的功能可能引出到pin group { A8, A7, A6, A5 },也可能引出到另外一个pin group{ G4, G3, G2, G1 },但毫无疑问,这两个pin group不能同时active,毕竟芯片内部的SPI0的逻辑功能电路只有一个。 从这个角度看,pin control subsystem要进行功能设定的时候必须要给出function以及function的pin group才能确定所有的物理pin的位置。
我们前面已经说过了,struct samsung_pinctrl_drv_data数据结构就是2416的pin controller driver要驱动2416的HW pin controller的私有数据结构。这个数据结构中最重要的就是samsung pin controller描述符了。关于pin controller有两个描述符,一个是struct pinctrl_desc,是具体硬件无关的pin controller的描述符。struct samsung_pin_ctrl描述的具体samsung pin controller硬件相关的信息,比如说:pin bank的信息,不是所有的pin controller都是分bank的,因此pin bank的信息只能封装在low level的samsung pin controller driver中。这个数据结构定义如下:
struct samsung_pin_ctrl {
&&& struct samsung_pin_bank&&& *pin_----定义具体的pin bank信息
&&& u32&&&&&&& nr_ ---------number of pin bank
&&& u32&&&&&&&----该pin controller的pin ID base。
&&& u32&&&&&&& nr_ -----总的可以控制的pin的数目
其他成员和本场景无关,和GPIO type的中断控制器驱动代码有关
关于上面的base可以多说两句。实际上,系统支持多个pin controller设备,这时候,系统要管理多个pin controller控制下的多个pin。每个pin有自己的pin ID,是唯一的,假设系统中有两个pin controller,一个是A,控制32个,另外一个是B,控制64个pin,我们可以统一编号,对A,pin ID从0~31,对于B,pin ID是从32~95。对于B,其pin base就是32。
samsung_pinctrl_probe-&samsung_pinctrl_get_soc_data函数中会根据device tree的信息和静态定义的table来初始化该描述符,具体的代码如下:
static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
&&&&&&&&&&&&&&& struct samsung_pinctrl_drv_data *d,
&&&&&&&&&&&&&&& struct platform_device *pdev)
&&& const struct of_device_id *
&&& struct device_node *node = pdev-&dev.of_ ---获取device tree中的device node指针
&&& struct device_node *
&&& struct samsung_pin_ctrl *
&&& struct samsung_pin_bank *
&&& id = of_alias_get_id(node, "pinctrl");
&&& match = of_match_node(samsung_pinctrl_dt_match, node);
&&& ctrl = (struct samsung_pin_ctrl *)match-&data + --------A
&&& bank = ctrl-&pin_
&&& for (i = 0; i & ctrl-&nr_ ++i, ++bank) {------------B&
&&&&&&& spin_lock_init(&bank-&slock);
&&&&&&& bank-&drvdata =
&&&&&&& bank-&pin_base = ctrl-&nr_ ---ctrl-&nr_pins初始的时候等于0,最后完成bank初始化后,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 该值等于total的pin number。
&&&&&&& ctrl-&nr_pins += bank-&nr_
for_each_child_of_node(node, np) {& ----------------C
&&&&&&& if (!of_find_property(np, "gpio-controller", NULL))
&&&&&&&&&&&
&&&&&&& bank = ctrl-&pin_
&&&&&&& for (i = 0; i & ctrl-&nr_ ++i, ++bank) {
&&&&&&&&&&& if (!strcmp(bank-&name, np-&name)) {
&&&&&&&&&&&&&&& bank-&of_node =
&&&&&&&&&&&&&&&
&&&&&&&&&&& }
ctrl-&base = pin_ ----------------------D
&&& pin_base += ctrl-&nr_
samsung_pinctrl_get_soc_data这个函数名字基本反应了其功能,2416是samsung的一个具体的SOC型号,调用该函数可以返回一个表示2416 SOC的samsung pin controller的描述符。
A:这段代码主要是获取具体的2416的HW pin controller的信息,该数据结构在上文中出现过(具体参考pin controller的device tree match table:samsung_pinctrl_dt_match),就是s3c2416_pin_ctrl这个变量。这个变量定义了2416的pin controller的信息(S3C2416的pin controller的pin bank信息是定义在pin controller driver的静态数据,其实最好在dts中定义)如下:
struct samsung_pin_ctrl s3c2416_pin_ctrl[] = {
&&&&&&& .pin_banks&&& = s3c2416_pin_banks,------静态定义的2416的pin bank的信息
&&&&&&& .nr_banks&&& = ARRAY_SIZE(s3c2416_pin_banks),
&&&&&&& .eint_wkup_init = s3c24xx_eint_init,
&&&&&&& .label&&&&&&& = "S3C2416-GPIO",
这个变量中包含了2416的pin bank的信息,包括:有多少个pin bank,每个bank中有多少个pin,pin bank的名字是什么,寄存器的offset是多少。这些信息用来初始化pin controller描述符数据结构。
B:初始化2416 samsung pin controller中各个bank的描述符。
C:device tree中表示pin controller的device node有若干的child node,分别表示gpa~gpl这11个bank,每个bank都是一个gpio controller。下面的代码遍历各个child node,并初始化各个bank描述符中的device tree node成员。 这里需要注意的是静态定义的pin bank的名字要和dts文件中定义的pin bank node的名字一样。
D:系统中有可能有多个pin controller,多个pin controller上的pin ID 应该是系统唯一的,ctrl-&base表示本pin controller中的pin ID的起始值。
(3)本来pin control subsystem和GPIO subsystem应该是无关的两个子系统,应该各自进行自己的初始化过程。但实际中,由于硬件的复杂性,这两个子系统耦合性非常高。这里samsung_gpiolib_register函数就是把各个bank代表的gpio chip注册到GPIO subsystem中。更具体的信息请参考GPIO subsystem软件框架文档。
(4)samsung_pinctrl_register函数的主要功能是将本pin controller注册到pin control subsystem。代码如下:
static int samsung_pinctrl_register(struct platform_device *pdev,
&&&&&&&&&&&&&&&&&&& struct samsung_pinctrl_drv_data *drvdata)
&&& struct pinctrl_desc *ctrldesc = &drvdata-&
&&& struct pinctrl_pin_desc *pindesc, *
&&& struct samsung_pin_bank *pin_
&&& char *pin_
&&& int pin, bank,
&&& ctrldesc-&name = "samsung-pinctrl";--------A
&&& ctrldesc-&owner = THIS_MODULE;
&&& ctrldesc-&pctlops = &samsung_pctrl_ ---call 函数,具体参考第四章的内容
&&& ctrldesc-&pmxops = &samsung_pinmux_
&&& ctrldesc-&confops = &samsung_pinconf_
&&& pindesc = devm_kzalloc(&pdev-&dev, sizeof(*pindesc) *-------B
&&&&&&&&&&& drvdata-&ctrl-&nr_pins, GFP_KERNEL);
&&& ctrldesc-&pins =
&&& ctrldesc-&npins = drvdata-&ctrl-&nr_&
&&& for (pin = 0, pdesc = pin & ctrldesc-& pin++, pdesc++)---C
&&&&&&& pdesc-&number = pin + drvdata-&ctrl-&
&&& pin_names = devm_kzalloc(&pdev-&dev, sizeof(char) * PIN_NAME_LENGTH *---B
&&&&&&&&&&&&&&&&&&& drvdata-&ctrl-&nr_pins, GFP_KERNEL);
&&& for (bank = 0; bank & drvdata-&ctrl-&nr_ bank++) { ---------C
&&&&&&& pin_bank = &drvdata-&ctrl-&pin_banks[bank];
&&&&&&& for (pin = 0; pin & pin_bank-&nr_ pin++) {
&&&&&&&&&&& sprintf(pin_names, "%s-%d", pin_bank-&name, pin);
&&&&&&&&&&& pdesc = pindesc + pin_bank-&pin_base +
&&&&&&&&&&& pdesc-&name = pin_
&&&&&&&&&&& pin_names += PIN_NAME_LENGTH;
&&& ret = samsung_pinctrl_parse_dt(pdev, drvdata);------D
&&& drvdata-&pctl_dev = pinctrl_register(ctrldesc, &pdev-&dev, drvdata);---E
&&& for (bank = 0; bank & drvdata-&ctrl-&nr_ ++bank) {-----F
&&&&&&& pin_bank = &drvdata-&ctrl-&pin_banks[bank];
&&&&&&& pin_bank-&grange.name = pin_bank-&
&&&&&&& pin_bank-&grange.id =
&&&&&&& pin_bank-&grange.pin_base = pin_bank-&pin_
&&&&&&& pin_bank-&grange.base = pin_bank-&gpio_chip.
&&&&&&& pin_bank-&grange.npins = pin_bank-&gpio_chip.
&&&&&&& pin_bank-&grange.gc = &pin_bank-&gpio_
&&&&&&& pinctrl_add_gpio_range(drvdata-&pctl_dev, &pin_bank-&grange);
&&& return 0;
A:初始化硬件无关的pin controller描述符(struct samsung_pinctrl_drv_data中的pctl成员)。该数据结构中还包含了所有pin的描述符的信息,这些pin descriptor所需要的内存在步骤B中分配
B:初始化过程中涉及不少内存分配,这些内存主要用于描述每一个pin(术语叫做pin descriptor)以及pin name。
C:初始化每一个pin 描述符的名字和ID。对于samsung的pin描述符,其名字使用pin-bank name + pin ID的形式。 ID的分配是从该pin controller的pin base开始分配ID的,逐个加一。
D:初始化pin group和function(具体内容在下节描述)
E:调用pinctrl_register注册到pin control subsystem 。这是pin control subsystem的核心函数,可以参考GPIO系统之2的描述。
F:在这里又不得不进行pin control subsystem和GPIO系统的耦合了。每个bank都是一个GPIO controller,但是pin bank使用的ID是pin control space中的ID,GPIO 子系统中使用的是GPIO space的ID,对于pin control subsystem而言,它需要建立这两个ID的映射关系。pinctrl_add_gpio_range就是起这个作用的。更具体的内容请参考pin control subsystem软件结构文档。 需要注意的是直接在pin controller driver中调用pinctrl_add_gpio_range是不推荐的,建议使用dts的方式在GPIO controller设备节点中描述。
(5)这里的代码是向kernel中的中断子系统注册interrupt controller。对于2416,有两个bank有中断功能,gpf和gpg,本质上gpf和gpg就是两个interrupt controller,挂接在2416真正的那个interrupt contrller之下,形成树状结构。具体的代码就不分析了,请参考GPIO类型的中断控制器代码分析。
4、pin control subsystem如何获取pin group的信息
具体的代码如下:
static int samsung_pinctrl_parse_dt(struct platform_device *pdev,
&&&&&&&&&&&&&&&&&&& struct samsung_pinctrl_drv_data *drvdata)
&&& struct device *dev = &pdev-&
&&& struct device_node *dev_np = dev-&of_
&&& struct device_node *cfg_
&&& struct samsung_pin_group *groups, *
&&& struct samsung_pmx_func *functions, *
&&& unsigned *pin_
&&& unsigned int npins, grp_cnt, func_idx = 0;
&&& char *gname, *
&&& grp_cnt = of_get_child_count(dev_np); ------(1)
&&& groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL); ----(2)
&&& functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL); ---(2)
&&& func =
&&& for_each_child_of_node(dev_np, cfg_np) { ----遍历pin control的所有的child node
&&&&&&& u32&
&&&&& if (!of_find_property(cfg_np, "samsung,pins", NULL)) -忽略掉那些没有samsung,pins属性的node
&&&&&&&&&&&
&&&&&&& ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np, --------(3)
&&&&&&&&&&&&&&&&&&& &drvdata-&pctl,&&& &pin_list, &npins);
&&&&&&& if (ret)
&&&&&&&&&&&
&&&&&&& /* derive pin group name from the node name */&
& gname = devm_kzalloc(dev, strlen(cfg_np-&name) + GSUFFIX_LEN, -分配pin group名字需要的内存
&&&&&&&&&&&&&&&&&&& GFP_KERNEL);
&&& sprintf(gname, "%s%s", cfg_np-&name, GROUP_SUFFIX);--添加“-grp”的后缀
&&&&&&& grp-&name = ----------------(4)
&&&&&&& grp-&pins = pin_
&&&&&&& grp-&num_pins =
&&&&&&& of_property_read_u32(cfg_np, "samsung,pin-function", &function);
&&&&&&& grp-&func =
&&&&&&& grp++;
&&&&&&& if (!of_find_property(cfg_np, "samsung,pin-function", NULL))&
&&&&&&&&&&& ----忽略掉那些没有samsung,pin-function属性的node
&&&&&&& /* derive function name from the node name */
&&&&&&& fname = devm_kzalloc(dev, strlen(cfg_np-&name) + FSUFFIX_LEN,
&&&&&&&&&&&&&&&&&&& GFP_KERNEL);&
&&&&&&& sprintf(fname, "%s%s", cfg_np-&name, FUNCTION_SUFFIX); -----(5)
&&&&&&& func-&name =
&&&&&&& func-&groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL); ----(6)
&&&&&&& if (!func-&groups) {
&&&&&&&&&&& dev_err(dev, "failed to alloc memory for group list "
&&&&&&&&&&&&&&&&&&& "in pin function");
&&&&&&&&&&& return -ENOMEM;
&&&&&&& func-&groups[0] =
&&&&&&& func-&num_groups = 1;
&&&&&&& func++;
&&&&&&& func_idx++;
&&& drvdata-&pin_groups = ----最终,pin group和function的信息被copy到pin controller
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& driver的私有数据结构struct samsung_pinctrl_drv_data 中
&&& drvdata-&nr_groups = grp_
&&& drvdata-&pmx_functions =
&&& drvdata-&nr_functions = func_
&&& return 0;
(1)pin controller的device node有若干个child node,每个child node都描述了一个pin configuration。of_get_child_count函数可以获取pin configuration的数目。
(2)根据pin configuration的数目分配内存。在这里共计分配了两片内存,一片保存了所有pin group的信息(struct samsung_pin_group ),一片保存了pin mux function的信息(struct samsung_pmx_func)。实际上,分配pin configuration的数目的内存有些浪费,因为不是每一个pin control的child node都是和pin group相关(例如pin bank node就是和pin group无关)。对于function,就更浪费了,因为有可能多个pin group对应一个function。
(3)samsung_pinctrl_parse_dt_pins函数主要分析samsung,pins这个属性,并根据属性值返回一个pin list,该list中每个entry是一个pin ID。
(4)初始化samsung pin group的描述符。具体的数据结构解释如下:
struct samsung_pin_group {
&&& const char&&&&&&& *---------pin group的名字,名字是device tree node name+-grp
&&& const unsigned int&&& *-------pin list的信息
&&& u8&&&&&&&&&&& num_----------pin list中的数目
&&& u8&&&&&&&&&&&------------对应samsung,pin-function属性的值,用来配置pin list中各个pin的功能设定寄存器
(5)一个pin configuration的device tree node被解析成两个描述符,一个是samsung pin group的描述符,另外一个是samsung pin mux function描述符。这两个描述符的名字都是根据dts file中的pin configuration的device node name生成,只不过pin group的名字附加-grp的后缀,而function描述符的名字后面附加-mux的后缀。
(6)对于samsung pin mux function描述符解释如下:
struct samsung_pmx_func {
&&& const char&&&&&&& *------pin function的名字,名字是device tree node name+-mux
&&& const char&&&&&&& **-----指向pin groups的指针数组
&&& u8&&&&&&&&&&& num_------属于该function的pin group的个数
在具体的代码实现中num_groups总是等于1。
四、S3C2416 pin controller driver的操作函数
1、操作函数概述
pin controller描述符中包括了三类操作函数:pctlops是一些全局的控制函数,pmxops是复用引脚相关的操作函数,confops操作函数是用来配置引脚的特性(例如:pull-up/down)。这些callback函数都是和具体的底层pin controller的操作相关。
本章节主要描述这些call back函数的逻辑,这些callback的调用时机不会在这里描述,那些内容请参考pin control subsystem的描述。
2、struct pinctrl_ops中各个callback函数的具体的解释如下:
(1)samsung_get_group_count
该函数的代码如下:
static int samsung_get_group_count(struct pinctrl_dev *pctldev)
&&& struct samsung_pinctrl_drv_data *
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& return drvdata-&nr_
该函数主要是用来获取指定pin control device的pin group的数目。逻辑很简单,通过pin control的class device的driver_data成员可以获得samsung pin control driver的私有数据(struct samsung_pinctrl_drv_data),可以nr_groups成员返回group的数目。
(2)samsung_get_group_name
该函数的代码如下:
static const char *samsung_get_group_name(struct pinctrl_dev *pctldev,
&&&&&&&&&&&&&&&&&&&&&&& unsigned selector)
&&& struct samsung_pinctrl_drv_data *
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& return drvdata-&pin_groups[selector].
该函数主要用来获取指定group selector的pin group信息。
(3)samsung_get_group_pins
该函数的代码如下:
static int samsung_get_group_pins(struct pinctrl_dev *pctldev,
&&&&&&& unsigned selector, const unsigned **pins, unsigned *num_pins)
&&& struct samsung_pinctrl_drv_data *
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& *pins = drvdata-&pin_groups[selector].
&&& *num_pins = drvdata-&pin_groups[selector].num_
&&& return 0;
该函数的主要功能是给定一个group selector(index),获取该pin group中pin的信息(该pin group包括多少个pin,每个pin的ID是什么) 。
(4)samsung_dt_node_to_map
该函数的代码如下:
static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
&&&&&&&&&&& struct device_node *np, struct pinctrl_map **maps,
&&&&&&&&&&& unsigned *nmaps)
&&& struct device *dev = pctldev-&
&&& struct pinctrl_map *
&&& unsigned long *cfg = NULL;
&&& char *gname, *
&&& int cfg_cnt = 0, map_cnt = 0, idx = 0;
&&& /* count the number of config options specfied in the node */
&&& for (idx = 0; idx & ARRAY_SIZE(pcfgs); idx++) {
&&&&&&& if (of_find_property(np, pcfgs[idx].prop_cfg, NULL))
&&&&&&&&&&& cfg_cnt++;
&&&& * Find out the number of map entries to create. All the config options
&&&& * can be accomadated into a single config map entry.
&&& if (cfg_cnt)
&&&&&&& map_cnt = 1;
&&& if (of_find_property(np, "samsung,pin-function", NULL))
&&&&&&& map_cnt++;
&&& if (!map_cnt) {
&&&&&&& dev_err(dev, "node %s does not have either config or function "
&&&&&&&&&&&&&&& "configurations\n", np-&name);
&&&&&&& return -EINVAL;
&&& /* Allocate memory for pin-map entries */
&&& map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
&&& if (!map) {
&&&&&&& dev_err(dev, "could not alloc memory for pin-maps\n");
&&&&&&& return -ENOMEM;
&&& *nmaps = 0;
&&&& * Allocate memory for pin group name. The pin group name is derived
&&&& * from the node name from which these map entries are be created.
&&& gname = kzalloc(strlen(np-&name) + GSUFFIX_LEN, GFP_KERNEL);
&&& if (!gname) {
&&&&&&& dev_err(dev, "failed to alloc memory for group name\n");
&&&&&&& goto free_
&&& sprintf(gname, "%s%s", np-&name, GROUP_SUFFIX);
&&&& * don't have config options? then skip over to creating function
&&&& * map entries.
&&& if (!cfg_cnt)
&&&&&&& goto skip_
&&& /* Allocate memory for config entries */
&&& cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
&&& if (!cfg) {
&&&&&&& dev_err(dev, "failed to alloc memory for configs\n");
&&&&&&& goto free_
&&& /* Prepare a list of config settings */
&&& for (idx = 0, cfg_cnt = 0; idx & ARRAY_SIZE(pcfgs); idx++) {
&&&&&&& u32
&&&&&&& if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value))
&&&&&&&&&&& cfg[cfg_cnt++] =
&&&&&&&&&&&&&&& PINCFG_PACK(pcfgs[idx].cfg_type, value);
&&& /* create the config map entry */
&&& map[*nmaps].data.configs.group_or_pin =
&&& map[*nmaps].data.configs.configs =
&&& map[*nmaps].data.configs.num_configs = cfg_
&&& map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
&&& *nmaps += 1;
skip_cfgs:
&&& /* create the function map entry */
&&& if (of_find_property(np, "samsung,pin-function", NULL)) {
&&&&&&& fname = kzalloc(strlen(np-&name) + FSUFFIX_LEN,&&& GFP_KERNEL);
&&&&&&& if (!fname) {
&&&&&&&&&&& dev_err(dev, "failed to alloc memory for func name\n");
&&&&&&&&&&& goto free_
&&&&&&& sprintf(fname, "%s%s", np-&name, FUNCTION_SUFFIX);
&&&&&&& map[*nmaps].data.mux.group =
&&&&&&& map[*nmaps].data.mux.function =
&&&&&&& map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
&&&&&&& *nmaps += 1;
&&& *maps =
&&& return 0;
&&& kfree(cfg);
free_gname:
&&& kfree(gname);
&&& kfree(map);
&&& return -ENOMEM;
具体分析TODO
(5)samsung_dt_free_map
该函数的代码如下:
static void samsung_dt_free_map(struct pinctrl_dev *pctldev,
&&&&&&&&&&&&&&&& struct pinctrl_map *map, unsigned num_maps)
&&& for (idx = 0; idx & num_ idx++) {
&&&&&&& if (map[idx].type == PIN_MAP_TYPE_MUX_GROUP) {
&&&&&&&&&&& kfree(map[idx].data.mux.function);
&&&&&&&&&&& if (!idx)
&&&&&&&&&&&&&&& kfree(map[idx].data.mux.group);
&&&&&&& } else if (map-&type == PIN_MAP_TYPE_CONFIGS_GROUP) {
&&&&&&&&&&& kfree(map[idx].data.configs.configs);
&&&&&&&&&&& if (!idx)
&&&&&&&&&&&&&&& kfree(map[idx].data.configs.group_or_pin);
&&& kfree(map);
具体分析TODO
3、复用引脚相关的操作函数struct pinmux_ops的具体解释如下:
(1)samsung_get_functions_count
该函数的代码如下:
static int samsung_get_functions_count(struct pinctrl_dev *pctldev)
&&& struct samsung_pinctrl_drv_data *
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& return drvdata-&nr_
该函数的主要功能是就是返回pin controller device支持的function的数目
(2)samsung_pinmux_get_fname
该函数的代码如下:
static const char *samsung_pinmux_get_fname(struct pinctrl_dev *pctldev,
&&&&&&&&&&&&&&&&&&&&&&& unsigned selector)
&&& struct samsung_pinctrl_drv_data *
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& return drvdata-&pmx_functions[selector].
该函数的主要功能是就是:给定一个function selector(index),获取指定function的name&
(3)samsung_pinmux_get_groups
该函数的代码如下:
static int samsung_pinmux_get_groups(struct pinctrl_dev *pctldev,
&&&&&&& unsigned selector, const char * const **groups,
&&&&&&& unsigned * const num_groups)
&&& struct samsung_pinctrl_drv_data *
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& *groups = drvdata-&pmx_functions[selector].
&&& *num_groups = drvdata-&pmx_functions[selector].num_
&&& return 0;
该函数的主要功能是就是:给定一个function selector(index),获取指定function的pin groups信息
(4)samsung_pinmux_enable和samsung_pinmux_disable
这个两个callback函数都是通过samsung_pinmux_setup实现,该函数的代码如下:
static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
&&&&&&&&&&&&&&&&&&& unsigned group, bool enable)
&&& struct samsung_pinctrl_drv_data *
&&& const unsigned int *
&&& struct samsung_pin_bank *
&&& void __iomem *
&&& u32 mask, shift, data, pin_offset,
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& pins = drvdata-&pin_groups[group].
&&&& * for each pin in the pin group selected, program the correspoding pin
&&&& * pin function number in the config register.
&&& for (cnt = 0; cnt & drvdata-&pin_groups[group].num_ cnt++) {
&&&&&&& struct samsung_pin_bank_type *
&&&&&&& pin_to_reg_bank(drvdata, pins[cnt] - drvdata-&ctrl-&base,
&&&&&&&&&&&&&&& (R), &pin_offset, &bank);
&&&&&&& type = bank-&
&&&&&&& mask = (1 && type-&fld_width[PINCFG_TYPE_FUNC]) - 1;
&&&&&&& shift = pin_offset * type-&fld_width[PINCFG_TYPE_FUNC];
&&&&&&& if (shift &= 32) {
&&&&&&&&&&& /* Some banks have two config registers */
&&&&&&&&&&& shift -= 32;
&&&&&&&&&&& reg += 4;
&&&&&&& spin_lock_irqsave(&bank-&slock, flags);
&&&&&&& data = readl(reg + type-&reg_offset[PINCFG_TYPE_FUNC]);
&&&&&&& data &= ~(mask && shift);
&&&&&&& if (enable)
&&&&&&&&&&& data |= drvdata-&pin_groups[group].func &&
&&&&&&& writel(data, reg + type-&reg_offset[PINCFG_TYPE_FUNC]);
&&&&&&& spin_unlock_irqrestore(&bank-&slock, flags);
该函数主要用来enable一个指定function。具体指定function的时候要给出function selector和pin group的selector 。具体的操作涉及很多底层的寄存器操作(TODO)。
(5)samsung_pinmux_gpio_set_direction
该函数的代码如下:
static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
&&&&&&& struct pinctrl_gpio_range *range, unsigned offset, bool input)
&&& struct samsung_pin_bank_type *
&&& struct samsung_pin_bank *
&&& struct samsung_pinctrl_drv_data *
&&& void __iomem *
&&& u32 data, pin_offset, mask,
&&& bank = gc_to_pin_bank(range-&gc);
&&& type = bank-&
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& pin_offset = offset - bank-&pin_
&&& reg = drvdata-&virt_base + bank-&pctl_offset +
&&&&&&&&&&&&&&&&&&& type-&reg_offset[PINCFG_TYPE_FUNC];
&&& mask = (1 && type-&fld_width[PINCFG_TYPE_FUNC]) - 1;
&&& shift = pin_offset * type-&fld_width[PINCFG_TYPE_FUNC];
&&& if (shift &= 32) {
&&&&&&& /* Some banks have two config registers */
&&&&&&& shift -= 32;
&&&&&&& reg += 4;
&&& spin_lock_irqsave(&bank-&slock, flags);
&&& data = readl(reg);
&&& data &= ~(mask && shift);
&&& if (!input)
&&&&&&& data |= FUNC_OUTPUT &&
&&& writel(data, reg);
&&& spin_unlock_irqrestore(&bank-&slock, flags);
&&& return 0;
该函数用来设定GPIO的方向。
4、配置引脚的特性的struct pinconf_ops数据结构的各个成员定义如下:
(1)samsung_pinconf_get
(2)samsung_pinconf_set
(3)samsung_pinconf_group_get
(4)samsung_pinconf_group_set
(1)和(2)是对单个pin的配置进行读取或者设定,(3)和(4)是对pin group中的所有pin进行配置进行读取或者设定。这些函数的底层都是samsung_pinconf_rw,该函数代码如下:
static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,
&&&&&&&&&&&&&&& unsigned long *config, bool set)
&&& struct samsung_pinctrl_drv_data *
&&& struct samsung_pin_bank_type *
&&& struct samsung_pin_bank *
&&& void __iomem *reg_
&&& enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config);
&&& u32 data, width, pin_offset, mask,
&&& u32 cfg_value, cfg_
&&& drvdata = pinctrl_dev_get_drvdata(pctldev);
&&& pin_to_reg_bank(drvdata, pin - drvdata-&ctrl-&base, (R)_base,
&&&&&&&&&&&&&&&&&&& &pin_offset, &bank);
&&& type = bank-&
&&& if (cfg_type &= PINCFG_TYPE_NUM || !type-&fld_width[cfg_type])
&&&&&&& return -EINVAL;
&&& width = type-&fld_width[cfg_type];
&&& cfg_reg = type-&reg_offset[cfg_type];
&&& spin_lock_irqsave(&bank-&slock, flags);
&&& mask = (1 && width) - 1;
&&& shift = pin_offset *
&&& data = readl(reg_base + cfg_reg);
&&& if (set) {
&&&&&&& cfg_value = PINCFG_UNPACK_VALUE(*config);
&&&&&&& data &= ~(mask && shift);
&&&&&&& data |= (cfg_value && shift);
&&&&&&& writel(data, reg_base + cfg_reg);
&&& } else {
&&&&&&& data &&=
&&&&&&& data &=
&&&&&&& *config = PINCFG_PACK(cfg_type, data);
&&& spin_unlock_irqrestore(&bank-&slock, flags);
&&& return 0;
具体分析TODO
原创文章,转发请注明出处。蜗窝科技。

我要回帖

更多关于 struct bgpio chip 的文章

 

随机推荐