Android实现在用户空间拉高拉低PMIC GPIO

7 篇文章 1 订阅
3 篇文章 0 订阅

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_OUT
	tristate "GNSS QCOM PM8550 GPIO9 OUT"
	help
	  Say Y here if you need to config pm8550 gpio9 as driver-controllable
	  digital output HIGH or LOW.

	  To compile this driver as a module, choose M here: the module will
	  be 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__);
    // TODO
    return 0;
}

static int gnss_ant_release(struct inode *inode, struct file *file)
{
    printk("%s enter\n", __func__);
    // TODO
    return 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

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阅后即奋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值