Android实现在用户空间拉高拉低PMIC GPIO
0、需求:
基于高通SM8550,在GNSS定位过程中,将PM8550 GPIO9管脚拉高至1.8V(用于驱动有源天线),定位结束后拉低。
PM8550相关文档可以参考高通的“80-35348-100 SM8550 PMIC Software User Guide”。
1、debug
在实现前,下面的手段是可以实现拉高pm8550 gpio9至1.8V的。拉高后可以搜到卫星,有源天线进入工作状态。
adb root
adb shell
cd d/regmap/0-01
echo 1 > count
echo 0x9040 > address
echo 0x01 > data
echo 0x9041 > address
echo 0x01 > data
echo 0x9042 > address
echo 0x05 > data
echo 0x9044 > address
# 00 output low, 0x80 output high
echo 0x80 > data
echo 0x9046 > address
echo 0x80 > data
exit
下面从软件层面去实现。
1、配置device tree
首先按照高通文档(8.3.5#Configuration examples),配置如下的dts。
在kernel_platform/qcom/proprietary/devicetree/qcom下找到该机型的dtsi。
&soc {
// ......gnss_ant_en {compatible = "qcom,gpio-en";pinctrl-names = "default";pinctrl-0 = <&gnss_ant_default>;ant-gpios = <&pm8550_gpios 9 GPIO_ACTIVE_LOW>;};
};&pm8550_gpios {gnss_ant {gnss_ant_default: gnss_ant_default {pins = "gpio9"; /* GPIO 9 */function = "normal"; /* normal output */power-source = <1>; /* VIN1 */output-low; /* digital output, no invert */input-disable; /* prevent GPIO from being set to DIO */};};
};
配置后效果如何呢?
在/sys/firmware/devicetree/base/soc树节点下,可以查找到gnss_ant_en节点。
cat name
#gnss_ant_encat cat pinctrl-names
#default
2、新增驱动
新增一个驱动,用于控制该gpio的高低。由于与gnss功能相关,我们将它放在kernel_platform/msm-kernel/drivers/gnss下。
新增一个文件pm8550_gpio9_out.c,用于实现驱动。
2.1 Makefile改动
修改Makefile,新增如下,模块名为:gnss-qcom-pm8550-gpio9-out,依赖pm8550_gpio9_out.o。
obj-$(CONFIG_GNSS_QCOM_PM8550_GPIO9_OUT) += gnss-qcom-pm8550-gpio9-out.o
gnss-qcom-pm8550-gpio9-out-y := pm8550_gpio9_out.o
2.2 Kconfig改动
在Kconfig中增加该config说明。
config GNSS_QCOM_PM8550_GPIO9_OUTtristate "GNSS QCOM PM8550 GPIO9 OUT"helpSay Y here if you need to config pm8550 gpio9 as driver-controllabledigital output HIGH or LOW.To compile this driver as a module, choose M here: the module willbe called.If unsure or no need, say N.
2.3 加入编译
为了能够在所有的版本中都能有该可加载模块,在kernel_platform/msm-kernel/arch/arm64/configs/vendor/xxx_GKI.config中配置该CONFIG。
CONFIG_GNSS_QCOM_PM8550_GPIO9_OUT=m
2.4 驱动实现
本方案中使用字符设备的ioctl来实现gpio的驱动。
// SPDX-License-Identifier: GPL-2.0#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>#define DEV_NAME "gnss_ant_en"static dev_t dev_id;
static struct cdev *gnss_ant_dev;
static struct class *gnss_ant_class;static int gnss_ant_open(struct inode *inode, struct file *file)
{printk("%s enter\n", __func__);// TODOreturn 0;
}static int gnss_ant_release(struct inode *inode, struct file *file)
{printk("%s enter\n", __func__);// TODOreturn 0;
}static void setGpio9Output(int val) {int gpio9_out;struct device_node *node;printk("%s enter. val=%d\n", __func__, val);/* 获取设备树节点的引用 */node = of_find_node_opts_by_path("/soc/gnss_ant_en", NULL);if (node == NULL) {printk("%s, Failed to find device tree node\n", __func__);return;}gpio9_out = of_get_named_gpio(node, "ant-gpios", 0);gpio_request(gpio9_out, "GPIO9");gpio_direction_output(gpio9_out, val); //1:Output HIGH;0:Output LOW
}static long gnss_ant_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{printk("%s enter\n", __func__);switch (cmd) {case _IOW('k', 0, int):setGpio9Output(0);break;case _IOW('k', 1, int):setGpio9Output(1);break;default:break;}return 0;
}static const struct file_operations gnss_ant_fops = {.owner = THIS_MODULE,.open = gnss_ant_open,.release = gnss_ant_release,.unlocked_ioctl = gnss_ant_ioctl,
};static int __init gnss_ant_init(void)
{printk("%s enter\n", __func__);/* 申请设备号 */alloc_chrdev_region(&dev_id, 1, 1, DEV_NAME);/* 分配字符设备 */gnss_ant_dev = cdev_alloc();/* 设置字符设备 */cdev_init(gnss_ant_dev, &gnss_ant_fops);/* 注册字符设备 */cdev_add(gnss_ant_dev, dev_id, 1);/* 打印申请到的主次设备号 */printk("major:%d; minor:%d\n", MAJOR(dev_id), MINOR(dev_id));gnss_ant_class = class_create(THIS_MODULE, DEV_NAME);device_create(gnss_ant_class, NULL, dev_id, NULL, DEV_NAME);return 0;
}
module_init(gnss_ant_init);static void __exit gnss_ant_exit(void)
{printk("gnss_ant_exit\n");device_destroy(gnss_ant_class, dev_id);class_destroy(gnss_ant_class);cdev_del(gnss_ant_dev);kfree(gnss_ant_dev);unregister_chrdev_region(dev_id, 1);
}
module_exit(gnss_ant_exit);MODULE_AUTHOR("阅后即奋");
MODULE_DESCRIPTION("QCOM GNSS PM8550 GPIO9 Enabler");
MODULE_LICENSE("GPL v2");
ioctl的参数cmd,魔数就选定'k',基数0表示拉低,基数1表示拉高。setGpio9Output函数也没有去润色,只是先实现个功能,可自行补充其他的check。
一开始使用of_find_node_by_name(NULL, "gnss_ant_en")函数去获取device_node,返回的是NULL,所以改成了node = of_find_node_opts_by_path("/soc/gnss_ant_en", NULL)去获取设备树节点的引用。
设备开机后,可加载模块被modprobe自动加载,字符设备创建成功。由于是动态创建的,所以主设备号可能每次开机后都不一样。
[ 8.740527] gnss_ant_init
[ 8.740559] major:488; minor:1
adb shell cat /proc/devices后,可以在看到:
Character devices:
......
488 gnss_ant_en
......
在/dev/下也能看到字符设备gnss_ant_en
crw------- 1 root root 488, 1 1970-01-02 22:59 gnss_ant_en
3、用户空间控制字符设备
编译部分就不写了。下面给出在用户空间控制字符设备"/dev/gnss_ant_en"的sample code。
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <log_util.h>#ifdef LOG_NDEBUG
#undef LOG_NDEBUG
#endif
#define LOG_NDEBUG 0
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "PM8550_GPIO9_EN"
#ifdef LOGD
#undef LOGD
#endif
#define LOGI(...) ALOGI(__VA_ARGS__)
#define LOGE(...) ALOGE(__VA_ARGS__)#define DEV_NAME "/dev/gnss_ant_en"int pm8550_gpio9_ioctl(int cmd) {int fd;int ret;fd = open(DEV_NAME, O_RDWR);if (fd < 0) {LOGE("open device %s failed, return %d", DEV_NAME, fd);return -1;}ret = ioctl(fd, _IOW('k', cmd, int));LOGI("ioctl %s, return %d", DEV_NAME, ret);close(fd);return ret;
}
然后在开始定位的函数LocApiV02::startTimeBasedTracking中调用pm8550_gpio9_ioctl(1),在结束定位的函数LocApiV02::stopTimeBasedTracking中调用pm8550_gpio9_ioctl(0)即可。
日志打印如下:
开始定位:
[ 710.940557] gnss_ant_open enter
[ 710.940576] gnss_ant_ioctl enter
[ 710.940578] setGpio9Output enter. val=1
[ 710.941423] gnss_ant_release enter
结束定位:
[ 726.021958] gnss_ant_open enter
[ 726.021980] gnss_ant_ioctl enter
[ 726.021982] setGpio9Output enter. val=0
[ 726.022581] gnss_ant_release enter
验证:
adb root
adb shell
cd /d/regmap/0-01
# 开始定位
echo 0x9044 > address
cat data
# 打印9044: 80# 结束定位
cat data
# 打印9044: 00