本系列记载作者来到一个新的车载后装市场小公司,负责从新开始维护一套代码的心路过程。系统使用瑞芯微的rk3368芯片,版本是PX5_Android_8.0_release_20180726,从无到有的每个patch修改以及思考。其中着重点是驱动和系统相关,由于涉及到公司保密,所以APP部分暂时不贴过多的代码,只是针对系统bug进行记录。
    2020_02_13 —- 2020_02_14
    
    GPS芯片为外来芯片,非瑞芯微SDK原生支持,所以需要经过仔细的调试和检查。
    
    原理图:
    
    
    
    需要注意控制的地方也就是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层的调试!