在驱动中,芯片休眠后,为了节省电流,需要设置引脚的状态,可参考如下的例子。
在引脚控制文件中,添加如下定义如kernel/arch/arm/boot/dts/qcom/msm8996-pinctrl.c
gpio_12_active: gpio_12_active {
mux {
pins ="gpio12";
function = "gpio";
};
config {
pins = "gpio12";
drive-strength = <2>;
bias-disable;
};
};
gpio_12_suspend: gpio_12_suspend {
mux {
pins ="gpio12";
function = "gpio";
};
config {
pins = "gpio12";
drive-strength = <2>;
bias-pull-down;
};
};
在具体的板级文件中,添加设备的引脚,如kernle/arch/arm/boot/dts/qcom/msm8996-mtp.c
gpio_dbg {
compatible = "gpio_dbg";
status = "okay";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&gpio_12_active>;
pinctrl-1 = <&gpio_12_suspend>;
};
其中pinctrl-n代表的一组io的配置(非一个)如
pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release";
pinctrl-0 = <&ts_int_active &ts_reset_active>;
pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
pinctrl-2 = <&ts_release>;
具体的驱动如下
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
static struct pinctrl *pinctrl;
static struct pinctrl_state *gpio_state_active;
static struct pinctrl_state *gpio_state_suspend;
static int gpio_dbg_suspend(struct device *dev)
{
int ret;
dev_dbg(dev,"%s\n",__func__);
ret = pinctrl_select_state(pinctrl,gpio_state_suspend);
if (ret){
pr_err("%s:Error selecting suspend state",__func__);
}
else{
pr_err("%s:Ok selecting suspend state",__func__);
}
return ret;
}
static int gpio_dbg_resume(struct device *dev)
{
int ret;
dev_dbg(dev,"%s\n",__func__);
ret = pinctrl_select_state(pinctrl,gpio_state_active);
if (ret){
pr_err("%s:Error selecting active state",__func__);
}
else{
pr_err("%s:OK selecting active state",__func__);
}
return ret;
}
static const struct dev_pm_ops gpio_dbg_ops = {
.suspend = gpio_dbg_suspend,
.resume = gpio_dbg_resume,
};
static ssize_t gpio_suspend_show(struct device *dev,struct device_attribute *attr, char *buf)
{
int ret;
ret=gpio_dbg_suspend(dev);
return ret;
}
static ssize_t gpio_resume_show(struct device *dev,struct device_attribute *attr, char *buf)
{
int ret;
ret=gpio_dbg_resume(dev);
return ret;
}
static DEVICE_ATTR_RO(gpio_suspend);
static DEVICE_ATTR_RO(gpio_resume);
static int gpio_dbg_probe(struct platform_device *pdev)
{
struct pinctrl_state *set_state;
int err;
pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR_OR_NULL(pinctrl)) {
pr_err("%s(): Pinctrl not defined", __func__);
} else {
pr_err("%s(): Using Pinctrl", __func__);
set_state = pinctrl_lookup_state(pinctrl,PINCTRL_STATE_DEFAULT);
if (IS_ERR_OR_NULL(set_state)) {
dev_err(&pdev->dev,"pinctrl lookup failed for default state");
goto pinctrl_fail;
}
gpio_state_active = set_state;
set_state = pinctrl_lookup_state(pinctrl,PINCTRL_STATE_SLEEP);
if (IS_ERR_OR_NULL(set_state)) {
dev_err(&pdev->dev, "pinctrl lookup failed for sleep state");
goto pinctrl_fail;
}
pr_err("%s(): Pinctrl state sleep %p\n", __func__,set_state);
gpio_state_suspend = set_state;
}
err = device_create_file(&pdev->dev, &dev_attr_gpio_suspend);
if (err)
goto err;
err = device_create_file(&pdev->dev, &dev_attr_gpio_resume);
if (err)
goto err;
return err;
pinctrl_fail:
dev_err(&pdev->dev,"pinctrl err\n");
err:
dev_err(&pdev->dev,"create file err\n");
return err;
}
static struct of_device_id gpio_dbg_match_table[] = {
{ .compatible = "gpio_dbg",},
{}
};
static struct platform_driver gpio_dbg_driver =
{
.probe = gpio_dbg_probe,
.driver = {
.name = "gpio_dbg",
.owner = THIS_MODULE,
.pm = &gpio_dbg_ops,
.of_match_table = gpio_dbg_match_table,
},
};
static int gpio_dbg_init(void)
{
platform_driver_register(&gpio_dbg_driver);
return 0;
}
static void __exit gpio_dbg_exit(void)
{
platform_driver_unregister(&gpio_dbg_driver);
}
module_init(gpio_dbg_init);
module_exit(gpio_dbg_exit);
其中pinctrl_look_state的第二个参数为引脚的名字,这个可自己定义。
附上内核中的相关宏和函数定义
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p,
const char *name)
{
struct pinctrl_state *state;
state = find_state(p, name);
if (!state) {
if (pinctrl_dummy_state) {
/* create dummy state */
dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
name);
state = create_state(p, name);
} else
state = ERR_PTR(-ENODEV);
}
return state;
}
EXPORT_SYMBOL_GPL(pinctrl_lookup_state);
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
{
struct pinctrl_setting *setting, *setting2;
struct pinctrl_state *old_state = p->state;
int ret;
if (p->state == state)
return 0;
if (p->state) {
/*
* The set of groups with a mux configuration in the old state
* may not be identical to the set of groups with a mux setting
* in the new state. While this might be unusual, it's entirely
* possible for the "user"-supplied mapping table to be written
* that way. For each group that was configured in the old state
* but not in the new state, this code puts that group into a
* safe/disabled state.
*/
list_for_each_entry(setting, &p->state->settings, node) {
bool found = false;
if (setting->type != PIN_MAP_TYPE_MUX_GROUP)
continue;
list_for_each_entry(setting2, &state->settings, node) {
if (setting2->type != PIN_MAP_TYPE_MUX_GROUP)
continue;
if (setting2->data.mux.group ==
setting->data.mux.group) {
found = true;
break;
}
}
if (!found)
pinmux_disable_setting(setting);
}
}
p->state = NULL;
/* Apply all the settings for the new state */
list_for_each_entry(setting, &state->settings, node) {
switch (setting->type) {
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_enable_setting(setting);
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_apply_setting(setting);
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0) {
goto unapply_new_state;
}
}
p->state = state;
return 0;
unapply_new_state:
dev_err(p->dev, "Error applying setting, reverse things back\n");
list_for_each_entry(setting2, &state->settings, node) {
if (&setting2->node == &setting->node)
break;
/*
* All we can do here is pinmux_disable_setting.
* That means that some pins are muxed differently now
* than they were before applying the setting (We can't
* "unmux a pin"!), but it's not a big deal since the pins
* are free to be muxed by another apply_setting.
*/
if (setting2->type == PIN_MAP_TYPE_MUX_GROUP)
pinmux_disable_setting(setting2);
}
/* There's no infinite recursive loop here because p->state is NULL */
if (old_state)
pinctrl_select_state(p, old_state);
return ret;
}
EXPORT_SYMBOL_GPL(pinctrl_select_state);