CPU:F1C100S
目标系统:linux5.2
pio: pinctrl@1c20800 {
compatible = "allwinner,suniv-f1c100s-pinctrl";
reg = <0x01c20800 0x400>;
interrupts = <38>, <39>, <40>;
clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
clock-names = "apb", "hosc", "losc";
gpio-controller;
interrupt-controller;
#interrupt-cells = <3>;
#gpio-cells = <3>;
uart0_pe_pins: uart0-pe-pins {
pins = "PE0", "PE1";
function = "uart0";
};
lcd_rgb666_pins: lcd-rgb666-pins {
pins = "PD0", "PD1", "PD2", "PD3", "PD4",
"PD5", "PD6", "PD7", "PD8", "PD9",
"PD10", "PD11", "PD12", "PD13", "PD14",
"PD15", "PD16", "PD17", "PD18", "PD19",
"PD20", "PD21";
function = "lcd";
};
mmc0_pins: mmc0-pins {
pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5";
function = "mmc0";
};
};
1、pinctrl资源怎么和pinctrl驱动匹配的(linux内核是怎么做的?)
2、reg = <0x01c20800 0x400>; gpio的物理地址在内核哪里被映射成虚拟地址的?
3、每次启动映射的虚拟地址是固定的? 【注:在内核代码不变的情况下,包括驱动】
4、probe函数在获取设备相关信息(例如映射后的GPIO虚拟基地址)后怎么保存,供以后调用的?
5、既然pinctrl驱动的工作已经做完了,我们怎么去使用GPIO(gpio的操作)?
1、pinctrl资源怎么和pinctrl驱动匹配的(linux内核是怎么做的?)
首先我找到了pinctrl驱动程序所在位置(linux-nano-5.2-tf\drivers\pinctrl\sunxi\pinctrl-suniv-f1c100s.c)
pinctrl-suniv-f1c100s.c分析
.......前面代码省略,看最下面
static const struct of_device_id suniv_f1c100s_pinctrl_match[] = {
{ .compatible = "allwinner,suniv-f1c100s-pinctrl", }, //去设备设备树中匹配"allwinner,suniv-f1c100s-pinctrl"
{}
};
static struct platform_driver suniv_f1c100s_pinctrl_driver = {
.probe = suniv_pinctrl_probe,
.driver = {
.name = "suniv-f1c100s-pinctrl",
.of_match_table = suniv_f1c100s_pinctrl_match, //匹配设备树中的pinctrl
},
};
builtin_platform_driver(suniv_f1c100s_pinctrl_driver); //在这里注册了suniv_f1c100s_pinctrl_driver这个驱动
最终匹配到了pinctrl资源到驱动中。
2、reg = <0x01c20800 0x400>; gpio的物理地址在内核哪里被映射成虚拟地址的?
估计应该是驱动程序进行映射的。
好了看看驱动程序注册后要执行的代码:
.probe = suniv_pinctrl_probe
static int suniv_pinctrl_probe(struct platform_device *pdev)
{
return sunxi_pinctrl_init(pdev,
&suniv_f1c100s_pinctrl_data);
}
sunxi_pinctrl_init–>调用了sunxi_pinctrl_init_with_variant如下:
int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
const struct sunxi_pinctrl_desc *desc,
unsigned long variant)
{
struct device_node *node = pdev->dev.of_node;
struct pinctrl_desc *pctrl_desc;
struct pinctrl_pin_desc *pins;
struct sunxi_pinctrl *pctl;
struct pinmux_ops *pmxops;
struct resource *res;
int i, ret, last_pin, pin_idx;
struct clk *clk;
pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
if (!pctl)
return -ENOMEM;
platform_set_drvdata(pdev, pctl);
raw_spin_lock_init(&pctl->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pctl->membase = devm_ioremap_resource(&pdev->dev, res); //物理地址映射
.....后面代码省略
pctl->membase = devm_ioremap_resource(&pdev->dev, res); 就是这句把物理地址映射成虚拟地址的。
我们可以看看devm_ioremap_resource函数。
void __iomem *devm_ioremap_resource(struct device *dev,
const struct resource *res)
{
resource_size_t size;
void __iomem *dest_ptr;
BUG_ON(!dev);
if (!res || resource_type(res) != IORESOURCE_MEM) {
dev_err(dev, "invalid resource\n");
return IOMEM_ERR_PTR(-EINVAL);
}
size = resource_size(res);
if (!devm_request_mem_region(dev, res->start, size, dev_name(dev))) {
dev_err(dev, "can't request region for resource %pR\n", res);
return IOMEM_ERR_PTR(-EBUSY);
}
dest_ptr = devm_ioremap(dev, res->start, size);
if (!dest_ptr) {
dev_err(dev, "ioremap failed for resource %pR\n", res);
devm_release_mem_region(dev, res->start, size);
dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
}
return dest_ptr;
}
其中2个比较重点的函数devm_request_mem_region、devm_ioremap ,先申请后映射。最后返回dest_ptr。
dest_ptr:映射后的虚拟地址
3、每次启动映射的虚拟地址是固定的?
下面使用printk打印映射后的虚拟地址。
int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
const struct sunxi_pinctrl_desc *desc,
unsigned long variant)
{
struct device_node *node = pdev->dev.of_node;
struct pinctrl_desc *pctrl_desc;
struct pinctrl_pin_desc *pins;
struct sunxi_pinctrl *pctl;
struct pinmux_ops *pmxops;
struct resource *res;
int i, ret, last_pin, pin_idx;
unsigned long test_vmadr ; //++++++++++++添加一个长整形变量
struct clk *clk;
pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
if (!pctl)
return -ENOMEM;
platform_set_drvdata(pdev, pctl);
raw_spin_lock_init(&pctl->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pctl->membase = devm_ioremap_resource(&pdev->dev, res);
/*
*/
test_vmadr =*(unsigned long *)pctl->membase;//++++++++++++获取该虚拟地址的值
printk(KERN_EMERG "PinCtrl_Driver(-- %lx --)\n",test_vmadr );//++++++++++++打印该虚拟地址的值
test_vmadr =(unsigned long *)pctl->membase;//++++++++++++获取该虚拟地址
printk(KERN_EMERG "PinCtrl_Driver(-- %lx --)\n",test_vmadr );//++++++++++++打印该虚拟地址
重启开发板,多重启几次观察终端始终输出如下值:
PinCtrl_Driver(-- 7777 --)
PinCtrl_Driver(-- c284f800 --)
结论: 在驱动中每次物理地址映射后的虚拟地址和虚拟地址的值固定(在内核代码不变的情况下)。
4、probe函数在获取设备相关信息(例如映射后的GPIO虚拟基地址)后怎么保存,供以后调用的?
通常,会在驱动的probe函数中获取device的相关信息,然后使用 platform_set_drvdata进行保存。在其他函数中要用到时(比如remove),通过platform_get_drvdata来获取。
int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
const struct sunxi_pinctrl_desc *desc,
unsigned long variant)
{
struct device_node *node = pdev->dev.of_node;
struct pinctrl_desc *pctrl_desc;
struct pinctrl_pin_desc *pins;
struct sunxi_pinctrl *pctl;
struct pinmux_ops *pmxops;
struct resource *res;
int i, ret, last_pin, pin_idx;
struct clk *clk;
pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); // 为pctl 结构体申请内存(保存pinctrl 设备信息)
if (!pctl)
return -ENOMEM;
platform_set_drvdata(pdev, pctl); //保存pctl结构体在内存中的指针,供以后调用
....省略代码
5、既然pinctrl驱动的工作已经做完了,我们怎么去使用GPIO(gpio的操作)?
引用内容来自: http://blog.csdn.net/tommy_wxie/article/details/9427047
Linux内核中gpio是最简单,最常用的资源(和 interrupt ,dma,timer一样)驱动程序,应用程序都能够通过相应的接口使用gpio,gpio使用0~MAX_INT之间的整数标识,不能使用负数,gpio与硬件体系密切相关的,不过linux有一个框架处理gpio,能够使用统一的接口来操作gpio.在讲gpio核心(gpiolib.c)之前先来看看gpio是怎么使用的。
用户空间访问gpio,即通过sysfs接口访问gpio,下面是/sys/class/gpio目录下的三种文件:
--export/unexport文件
–gpioN指代具体的gpio引脚
–gpio_chipN指代gpio控制器
必须知道以上接口没有标准device文件和它们的链接。
用户空间操作GPIO
通过sysfs在用户空间使用GPIO,首先将GPIO使用export 导出到用户空间之中(方法如下)
cd /sys/class/gpio/ ;先访问/sys/class/gpio目录
echo 132 > export ;执行会创建gpio132这个目录
cd /sys/class/gpio/gpio132
设置为输入,并读值:
echo “in” > direction
cat value
设置为输出,并设置值:
cd /sys/class/gpio/gpio132
echo “out” > direction
echo 1 > value 或 echo 0 > value
内核空间操作GPIO
二 内核中gpio的使用
1 测试gpio端口是否合法 int gpio_is_valid(int number);
2 申请某个gpio端口当然在申请之前需要显示的配置该gpio端口的pinmux
int gpio_request(unsigned gpio, const char *label)
3 标记gpio的使用方向包括输入还是输出
/*成功返回零失败返回负的错误值*/
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
4 获得gpio引脚的值和设置gpio引脚的值(对于输出)
int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value);
5 gpio当作中断口使用
int gpio_to_irq(unsigned gpio);
返回的值即中断编号可以传给request_irq()和free_irq()
内核通过调用该函数将gpio端口转换为中断,在用户空间也有类似方法
6 导出gpio端口到用户空间
int gpio_export(unsigned gpio, bool direction_may_change);
内核可以对已经被gpio_request()申请的gpio端口的导出进行明确的管理,
参数direction_may_change表示用户程序是否允许修改gpio的方向,假如可以
则参数direction_may_change为真
/* 撤销GPIO的导出 */
void gpio_unexport();