rockchip rk3368(px5)车载开发之路4,使能GPS模块UC6226(1.驱动层)

  • Post author:
  • Post category:其他


本系列记载作者来到一个新的车载后装市场小公司,负责从新开始维护一套代码的心路过程。系统使用瑞芯微的rk3368芯片,版本是PX5_Android_8.0_release_20180726,从无到有的每个patch修改以及思考。其中着重点是驱动和系统相关,由于涉及到公司保密,所以APP部分暂时不贴过多的代码,只是针对系统bug进行记录。

2020_02_13 —- 2020_02_14

GPS芯片为外来芯片,非瑞芯微SDK原生支持,所以需要经过仔细的调试和检查。

原理图:

UC6226

需要注意控制的地方也就是i2c,reset对应gpio,一个LDO,一个LDO对应的gpio开关,还有一个主要注意的就是Safeboot引脚(此处datasheet有写,需要配置成固定电平而不是悬空,以增加GPS的稳定性。)

dts中有两部分代码,分别是rfkill和uart0的使能和配置。

diff --git a/arch/arm64/boot/dts/rockchip/rk3368-px5-evb-android.dts b/arch/arm64/boot/dts/rockchip/rk3368-px5-evb-android.dts
index 1a618d3..f139462 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368-px5-evb-android.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3368-px5-evb-android.dts
@@ -249,7 +249,17 @@

                status = "okay";
        };
+       wireless-gps {
+               compatible = "gps-platdata";

+               keep_bt_power_on;
+               GPS,power_gpio = <&gpio0 RK_PD4 GPIO_ACTIVE_HIGH>;
+               GPS,rst = <&gpio3 RK_PC6 GPIO_ACTIVE_HIGH>;
+               GPS,safeboot = <&gpio3 RK_PC1 GPIO_ACTIVE_HIGH>;
+
+               status = "okay";
+       };
+
        gpio_det: gpio-det {
                compatible = "gpio-detection";
                status = "okay";
@@ -1212,6 +1222,12 @@
        status = "okay";
 };

+&uart0 {
+        pinctrl-names = "default";
+        pinctrl-0 = <&uart0_xfer>;
+        status = "okay";
+};
+
 &uart1 {
        pinctrl-names = "default";
        pinctrl-0 = <&uart1_xfer &uart1_cts>;

还有就是自己编写的rfkill文件,对应于设备的电源管理,reset管理等等。

/*
 * Copyright (C) 2016 ROCKCHIP, Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Rock-chips rfkill driver for gps
 *
 */

#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rfkill.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <dt-bindings/gpio/gpio.h>
#include <uapi/linux/rfkill.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#endif

#define LOG(x...)   printk(KERN_INFO "[GPS_RFKILL]: "x)

struct gps_gpio {
        int     io;      //set the address of gpio
        int     enable;  //set the default value,i.e,GPIO_HIGH or GPIO_LOW
};

struct gps_rfkill_gpio_data {
        char                    *name;
        enum rfkill_type        type;
        bool                    gps_power_remain;
        struct gps_gpio         gps_power;
        struct gps_gpio                 gps_rst;
        struct gps_gpio                 gps_safeboot;

};

struct rfkill_rk_data {
        struct gps_rfkill_gpio_data    *pdata;
        struct platform_device         *pdev;
        struct rfkill                  *rfkill_dev;
};

static int gps_rfkill_gpio_set_power(void *data, bool blocked)
{
        struct rfkill_rk_data *rfkill = data;

        if (blocked) {
                if (gpio_is_valid(rfkill->pdata->gps_power.io))
                        gpio_direction_output(rfkill->pdata->gps_power.io,
                                !rfkill->pdata->gps_power.enable);
        } else {
                if (gpio_is_valid(rfkill->pdata->gps_power.io))
                        gpio_direction_output(rfkill->pdata->gps_power.io,
                                rfkill->pdata->gps_power.enable);
                        gpio_direction_output(rfkill->pdata->gps_rst.io, 0);
            		msleep(100);
                        gpio_direction_output(rfkill->pdata->gps_rst.io, 1);
        }

        return 0;
}
static const struct rfkill_ops gps_rfkill_gpio_ops = {
        .set_block = gps_rfkill_gpio_set_power,
};

#ifdef CONFIG_OF
static int gps_platdata_parse_dt(struct device *dev,
                  struct gps_rfkill_gpio_data *data)
{
        struct device_node *node = dev->of_node;
        int gpio;
        enum of_gpio_flags flags;

        if (!node)
                return -ENODEV;

        if (of_find_property(node, "keep_gps_power_on", NULL)) {
                data->gps_power_remain = true;
                LOG("%s: gps power will enabled while kernel starting and keep on.\n", __func__);
        } else {
                data->gps_power_remain = false;
                LOG("%s: enable gps power controler.\n", __func__);
        }

        gpio = of_get_named_gpio_flags(node, "GPS,power_gpio", 0, &flags);
        if (gpio_is_valid(gpio)){
                data->gps_power.io = gpio;
                data->gps_power.enable = (flags == GPIO_ACTIVE_HIGH)? 1:0;
                LOG("%s: get property: GPS,poweren_gpio = %d\n", __func__, gpio);
        } else {
                data->gps_power.io = -1;
    	}

        gpio = of_get_named_gpio_flags(node, "GPS,rst", 0, &flags);
        if (gpio_is_valid(gpio)){
                data->gps_rst.io = gpio;
                data->gps_rst.enable = (flags == GPIO_ACTIVE_HIGH)? 1:0;
                LOG("%s: get property: GPS,safeboot_gpio = %d\n", __func__, gpio);
        } else {
                data->gps_rst.io = -1;
    	}

        gpio = of_get_named_gpio_flags(node, "GPS,safeboot", 0, &flags);
        if (gpio_is_valid(gpio)){
                data->gps_safeboot.io = gpio;
                data->gps_safeboot.enable = (flags == GPIO_ACTIVE_HIGH)? 1:0;
                LOG("%s: get property: GPS,safeboot_gpio = %d\n", __func__, gpio);
        } else {
                data->gps_safeboot.io = -1;
    	}

        return 0;
}
#endif

static int gps_rfkill_gpio_probe(struct platform_device *pdev)
{
        struct rfkill_rk_data *rfkill;
        struct gps_rfkill_gpio_data *pdata = pdev->dev.platform_data;
        int ret = 0;

        if (!pdata) {
#ifdef CONFIG_OF
                pdata = devm_kzalloc(&pdev->dev, sizeof(struct gps_rfkill_gpio_data), GFP_KERNEL);
                if (!pdata)
                        return -ENOMEM;

                ret = gps_platdata_parse_dt(&pdev->dev, pdata);
                if (ret < 0) {
#endif
                        LOG("%s: No platform data specified\n", __func__);
                        return -EINVAL;
#ifdef CONFIG_OF
                }
#endif
        }

        if (gpio_is_valid(pdata->gps_power.io)) {
                ret = gpio_request(pdata->gps_power.io, "gps_poweren");
                if (ret) {
                        LOG("%s: failed to get gps_poweren.\n", __func__);
                        goto fail_gpio;
                }
        }

        if (pdata->gps_power_remain) {
                gpio_direction_output(pdata->gps_power.io, pdata->gps_power.enable);
        }

        if (gpio_is_valid(pdata->gps_safeboot.io)) {
                ret = gpio_request(pdata->gps_safeboot.io, "gps_safeboot");
                if (ret) {
                        LOG("%s: failed to get gps_safeboot.\n", __func__);
                        goto fail_rst;
                }else{
                        ret = gpio_direction_output(pdata->gps_safeboot.io, 1);
            		msleep(10);
                        if(ret){
                                LOG("%s: failed to get gps_safeboot.\n", __func__);
                                goto fail_rst;
                        }
                }
        }

        if (gpio_is_valid(pdata->gps_rst.io)) {
                ret = gpio_request(pdata->gps_rst.io, "gps_rst");
                if (ret) {
                        LOG("%s: failed to get gps_rst.\n", __func__);
                        goto fail_rst;
                }else{
                        ret = gpio_direction_output(pdata->gps_rst.io, 0);
            		msleep(100);
                        ret = gpio_direction_output(pdata->gps_rst.io, 1);
                        if(ret){
                                LOG("%s: failed to get gps_rst.\n", __func__);
                                goto fail_rst;
                        }
                }

        }

        pdata->name = (char*)"RFKILL_GPS";
        pdata->type = RFKILL_TYPE_GPS;

        rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL);
        if (!rfkill) {
                ret = -ENOMEM;
                goto fail_gpio;
        }

        rfkill->pdata = pdata;
        rfkill->pdev = pdev;

        rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type,
                                          &gps_rfkill_gpio_ops, rfkill);
        if (!rfkill->rfkill_dev) {
                ret = -ENOMEM;
                goto fail_gpio;
        }

        ret = rfkill_register(rfkill->rfkill_dev);
        if (ret < 0)
                goto fail_rfkill;

        platform_set_drvdata(pdev, rfkill);

        LOG("%s device registered.\n", pdata->name);

        return 0;

fail_rfkill:
        rfkill_destroy(rfkill->rfkill_dev);
fail_gpio:
        if (gpio_is_valid(pdata->gps_power.io))
                gpio_free(pdata->gps_power.io);
fail_rst:
        if (gpio_is_valid(pdata->gps_rst.io))
                gpio_free(pdata->gps_rst.io);

        return ret;
}

static int gps_rfkill_gpio_remove(struct platform_device *pdev)
{
        struct rfkill_rk_data *rfkill = platform_get_drvdata(pdev);

        rfkill_unregister(rfkill->rfkill_dev);
        rfkill_destroy(rfkill->rfkill_dev);
        if (gpio_is_valid(rfkill->pdata->gps_power.io))
                gpio_free(rfkill->pdata->gps_power.io);

        if (gpio_is_valid(rfkill->pdata->gps_rst.io))
                gpio_free(rfkill->pdata->gps_rst.io);

        return 0;
}

#ifdef CONFIG_OF
static struct of_device_id gps_platdata_of_match[] = {
        { .compatible = "gps-platdata" },
        { }
};
MODULE_DEVICE_TABLE(of, gps_platdata_of_match);
#endif //CONFIG_OF

static struct platform_driver gps_rfkill_gpio_driver = {
        .probe = gps_rfkill_gpio_probe,
        .remove = gps_rfkill_gpio_remove,
        .driver = {
        .name = "gps_rfkill_gpio",
                .owner = THIS_MODULE,
                .of_match_table = of_match_ptr(gps_platdata_of_match),
        },
};

module_platform_driver(gps_rfkill_gpio_driver);

MODULE_DESCRIPTION("ssnav gps rfkill");
MODULE_AUTHOR("wxz@ssnav.com");
MODULE_LICENSE("GPL");

此时,底层就可以正常上报nmea数据了

cat /dev/ttyS0

$GNGGA,003545.000,2242.069030,N,11412.200573,E,0,00,127.000,75.795,M,,M,,*69

$GNGSA,A,1,,,,,,,,,,,,,127.000,127.000,127.000*2A

$GPGSV,1,1,0,0*55
$BDGSV,1,1,0,0*44
$GNRMC,003546.000,V,2242.069030,N,11412.200573,E,0.000,0.000,,,E,N*27
$GNGGA,003546.000,2242.069030,N,11412.200573,E,0,00,127.000,75.795,M,,M,,*6A
$GNGLL,2242.069030,N,11412.200573,E,003546.000,V,N*55
$GNGSA,A,1,,,,,,,,,,,,,127.000,127.000,127.000*2A
$GNRMC,003547.000,V,2242.069030,N,11412.200573,E,0.000,0.000,,,E,N*26
$GNGGA,003547.000,2242.069030,N,11412.200573,E,0,00,127.000,75.795,M,,M,,*6B
$GNGLL,2242.069030,N,11412.200573,E,003547.000,V,N*54
$GNGSA,A,1,,,,,,,,,,,,,127.000,127.000,127.000*2A

至此,底层驱动已经打通,目前可能的问题记录如下:

1.并没有深入理解rfkill相关,可能需要后面看到上层代码后才能明了,此处存在隐患。

2.开机就工作,正常应该是打开gps才有信号输出(不过我们车机,的确是gps一直工作的,不考虑功耗),这样不利于精细化电源管理。

3.i2c驱动没有配置,怀疑上层直接使用/dev/i2c-4节点进行控制了(记得需要配置ublox对应的etc配置文件)

请看后续对gps的hal层的调试!



版权声明:本文为a10747029原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。