本系列记载作者来到一个新的车载后装市场小公司,负责从新开始维护一套代码的心路过程。系统使用瑞芯微的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层的调试!