Linux:使用pinctrl子系统动态切换复用pin脚的功能
一、许多soc内部都包含有pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件方面,Linux内核提供了pinctrl子系统,目的是为了统一各soc厂商的pin脚管理。
二、常用API介绍
1、struct pinctrl *devm_pinctrl_get(struct device *dev)
2、struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p,const char *name)
3、int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
四、实例测试,动态切换i2c0和gpio功能
1、dts文件添加
gpio_i2c_exchage{
compatible= "gpio-i2c-exchage";
pinctrl-names= "default", "gpio";
pinctrl-0= <&i2c0_function>;
pinctrl-1= <&i2c0_gpio>;
};
i2c0_option{
i2c0_function: i2c0-function {
rockchip,pins =
<2 GPIO_D0 RK_FUNC_1 &pcfg_pull_none_smt>,
<2 GPIO_D1 RK_FUNC_1 &pcfg_pull_none_smt>;
};
i2c0_gpio: i2c0-gpio {
rockchip,pins =
<2 GPIO_D0 RK_FUNC_GPIO &pcfg_pull_none_smt>,
<2 GPIO_D1 RK_FUNC_GPIO &pcfg_pull_none_smt>;
};
};
2、测试driver代码kernel\drivers\char\pinctrl.c
#define pr_fmt(fmt) "%s:" fmt, __func__
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include<linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/io.h>
#include<linux/uaccess.h>
#include<linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
#include<linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/gpio.h>
struct pinctrl *i2c0_pinctrl;
struct pinctrl_state *i2c0_default;//uart
struct pinctrl_state *i2c0_gpio;//gpio
#define ROCKCHIP_GPIO_NR(bank, nr) (((bank)) * 32 + (nr))
#define I2C0_SCLK ROCKCHIP_GPIO_NR(2,24)
const char *I2C0_PINCTRL_STATE_DEFAULT = "default";
const char *I2C0_PINCTRL_STATE_GPIO = "gpio";
static int biada_pinctrl_request_gpios(int state)
{
int result = 0;
if(state==0)
{
result = pinctrl_select_state(i2c0_pinctrl, i2c0_default);
if (result) {
printk("%s: Can not set %s pins\n",
__func__, "default");
}
}else
{
result = pinctrl_select_state(i2c0_pinctrl, i2c0_gpio);
if (result) {
printk("%s: Can not set %s pins\n",
__func__, "gpio");
}
}
printk(KERN_ERR "%s,request state(%s) ok, result = %d\n",
__func__, state ? "default" : "gpio",result);
return result;
}
static ssize_t pinctrl_show(struct device* cd,struct device_attribute *attr, char* buf)
{
ssize_t ret = 0;
sprintf(buf, "%s\n",__func__);
ret = strlen(buf) + 1;
return ret;
}
static ssize_t pinctrl_store(struct device* cd, struct device_attribute *attr,
const char* buf, size_t len)
{
unsigned long select = simple_strtoul(buf, NULL, 10);
printk("%s: %lu\n",__func__, select);
biada_pinctrl_request_gpios(select);
if(select)
{
printk("%s: set gpio low level\n", __func__);
gpio_direction_output(I2C0_SCLK, 0);
mdelay(100);
printk("%s: set gpio low high\n", __func__);
gpio_direction_output(I2C0_SCLK, 1);
}
return len;
}
static DEVICE_ATTR(biada_pinctrl,S_IRUGO | S_IWUSR, pinctrl_show, pinctrl_store);
static int biada_pinctrl_init(struct platform_device *pdata)
{
i2c0_pinctrl = devm_pinctrl_get(&pdata->dev);
printk("\n[************%s************devm_pinctrl_get]\n", __func__);
if (IS_ERR_OR_NULL(i2c0_pinctrl)) {
printk("Failed to get pin ctrl\n");
return PTR_ERR(i2c0_pinctrl);
}
// default
printk("\n[************%s************pinctrl_lookup_state i2c0_default]\n", __func__);
i2c0_default = pinctrl_lookup_state(i2c0_pinctrl,
I2C0_PINCTRL_STATE_DEFAULT);
if (IS_ERR_OR_NULL(i2c0_default)) {
printk("Failed to lookup pinctrl default state\n");
return PTR_ERR(i2c0_default);
}
// gpio
printk("\n[************%s************pinctrl_lookup_state i2c0_gpio]\n", __func__);
i2c0_gpio = pinctrl_lookup_state(i2c0_pinctrl,
I2C0_PINCTRL_STATE_GPIO);
if (IS_ERR_OR_NULL(i2c0_gpio)) {
printk("Failed to lookup pinctrl gpio state\n");
return PTR_ERR(i2c0_gpio);
}
printk("\n[************%s************]\n", __func__);
return 0;
}
// echo 0 >/sys/bus/platform/devices/gpio_i2c_exchage.35/biada_pinctrl //i2c0 mode
// echo 1 >/sys/bus/platform/devices/gpio_i2c_exchage.35/biada_pinctrl //gpio mode
static int biada_pinctrl_probe(struct platform_device *pdev)
{
int ret = 0;
biada_pinctrl_init(pdev);
if(device_create_file(&pdev->dev, &dev_attr_biada_pinctrl))
printk(KERN_ERR "Unable to createsysfs entry: '%s'\n",
dev_attr_biada_pinctrl.attr.name);
printk("\n[************%s************]\n", __func__);
return ret;
}
static int biada_pinctrl_remove(struct platform_device *plat)
{
return 0;
}
static struct of_device_id __attribute__ ((unused)) biada_pinctrl_of_match[] = {
{ .compatible = "gpio-i2c-exchage", },
{}
};
static struct platform_driver biada_pinctrl_driver = {
.probe = biada_pinctrl_probe,
.remove = biada_pinctrl_remove,
.driver = {
.name = "gpio-exchage",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(biada_pinctrl_of_match),
},
};
static int __init biada_init(void)
{
return platform_driver_register(&biada_pinctrl_driver);
}
static void __exit biada_exit(void)
{
platform_driver_unregister(&biada_pinctrl_driver);
}
MODULE_LICENSE("GPLv2");
MODULE_AUTHOR("trump, trump@usa-chips.com");
MODULE_DESCRIPTION("Driver for Rockchip I2C Bus");
MODULE_VERSION("2.0");
module_init(biada_init);
module_exit(biada_exit);
3、运行结果效果如下,可以动态在i2c和gpio之间切换,用示波器测试gpio有波形,i2c可以work。