GNSS updates for 5.1-rc1

Here are the GNSS updates for 5.1-rc1, including:
 
  - a new driver for Mediatek-based receivers
  - support for SiRF receivers without a wakeup signal
  - support for a separate LNA supply for SiRF receivers
 
 Included are also various clean ups and minor fixes.
 
 All have been in linux-next with no reported issues.
 
 Signed-off-by: Johan Hovold <johan@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQHbPq+cpGvN/peuzMLxc3C7H1lCAUCXG7R0AAKCRALxc3C7H1l
 CJ5sAP9a6744LzpgHxUIYQ+GjoI69ovxd+70Ibaw6Q5IHPYFtwEAotNYYVW5l0Ic
 Uf7E94tptwP0HiR45WLK/TFarRYx1Ao=
 =omk1
 -----END PGP SIGNATURE-----

Merge tag 'gnss-5.1-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/gnss into char-misc-next

Johan writes:

GNSS updates for 5.1-rc1

Here are the GNSS updates for 5.1-rc1, including:

 - a new driver for Mediatek-based receivers
 - support for SiRF receivers without a wakeup signal
 - support for a separate LNA supply for SiRF receivers

Included are also various clean ups and minor fixes.

All have been in linux-next with no reported issues.

Signed-off-by: Johan Hovold <johan@kernel.org>

* tag 'gnss-5.1-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/gnss:
  gnss: add driver for mediatek receivers
  gnss: add mtk receiver type support
  dt-bindings: gnss: add mediatek binding
  dt-bindings: Add vendor prefix for "GlobalTop Technology, Inc."
  dt-bindings: gnss: add lna-supply property
  gnss: sirf: add a separate supply for a lna
  dt-bindings: gnss: add w2sg0004 compatible string
  gnss: sirf: add support for configurations without wakeup signal
  gnss: sirf: write data to gnss only when the gnss device is open
  gnss: sirf: drop redundant double negation
  gnss: sirf: force hibernate mode on probe
  gnss: sirf: fix premature wakeup interrupt enable
This commit is contained in:
Greg Kroah-Hartman 2019-02-21 18:19:04 +01:00
commit 6ade6e903a
10 changed files with 430 additions and 54 deletions

View file

@ -17,6 +17,7 @@ Required properties:
represents
Optional properties:
- lna-supply : Separate supply for an LNA
- enable-gpios : GPIO used to enable the device
- timepulse-gpios : Time pulse GPIO

View file

@ -0,0 +1,35 @@
Mediatek-based GNSS Receiver DT binding
Mediatek chipsets are used in GNSS-receiver modules produced by several
vendors and can use a UART interface.
Please see Documentation/devicetree/bindings/gnss/gnss.txt for generic
properties.
Required properties:
- compatible : Must be
"globaltop,pa6h"
- vcc-supply : Main voltage regulator (pin name: VCC)
Optional properties:
- current-speed : Default UART baud rate
- gnss-fix-gpios : GPIO used to determine device position fix state
(pin name: FIX, 3D_FIX)
- reset-gpios : GPIO used to reset the device (pin name: RESET, NRESET)
- timepulse-gpios : Time pulse GPIO (pin name: PPS1, 1PPS)
- vbackup-supply : Backup voltage regulator (pin name: VBAT, VBACKUP)
Example:
serial@1234 {
compatible = "ns16550a";
gnss {
compatible = "globaltop,pa6h";
vcc-supply = <&vcc_3v3>;
};
};

View file

@ -12,6 +12,7 @@ Required properties:
"fastrax,uc430"
"linx,r4"
"wi2wi,w2sg0004"
"wi2wi,w2sg0008i"
"wi2wi,w2sg0084i"

View file

@ -150,6 +150,7 @@ geniatech Geniatech, Inc.
giantec Giantec Semiconductor, Inc.
giantplus Giantplus Technology Co., Ltd.
globalscale Globalscale Technologies, Inc.
globaltop GlobalTop Technology, Inc.
gmt Global Mixed-mode Technology, Inc.
goodix Shenzhen Huiding Technology Co., Ltd.
google Google, Inc.

View file

@ -15,6 +15,19 @@ if GNSS
config GNSS_SERIAL
tristate
config GNSS_MTK_SERIAL
tristate "Mediatek GNSS receiver support"
depends on SERIAL_DEV_BUS
select GNSS_SERIAL
help
Say Y here if you have a Mediatek-based GNSS receiver which uses a
serial interface.
To compile this driver as a module, choose M here: the module will
be called gnss-mtk.
If unsure, say N.
config GNSS_SIRF_SERIAL
tristate "SiRFstar GNSS receiver support"
depends on SERIAL_DEV_BUS

View file

@ -9,6 +9,9 @@ gnss-y := core.o
obj-$(CONFIG_GNSS_SERIAL) += gnss-serial.o
gnss-serial-y := serial.o
obj-$(CONFIG_GNSS_MTK_SERIAL) += gnss-mtk.o
gnss-mtk-y := mtk.o
obj-$(CONFIG_GNSS_SIRF_SERIAL) += gnss-sirf.o
gnss-sirf-y := sirf.o

View file

@ -334,6 +334,7 @@ static const char * const gnss_type_names[GNSS_TYPE_COUNT] = {
[GNSS_TYPE_NMEA] = "NMEA",
[GNSS_TYPE_SIRF] = "SiRF",
[GNSS_TYPE_UBX] = "UBX",
[GNSS_TYPE_MTK] = "MTK",
};
static const char *gnss_type_name(struct gnss_device *gdev)

152
drivers/gnss/mtk.c Normal file
View file

@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Mediatek GNSS receiver driver
*
* Copyright (C) 2018 Johan Hovold <johan@kernel.org>
*/
#include <linux/errno.h>
#include <linux/gnss.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/serdev.h>
#include "serial.h"
struct mtk_data {
struct regulator *vbackup;
struct regulator *vcc;
};
static int mtk_set_active(struct gnss_serial *gserial)
{
struct mtk_data *data = gnss_serial_get_drvdata(gserial);
int ret;
ret = regulator_enable(data->vcc);
if (ret)
return ret;
return 0;
}
static int mtk_set_standby(struct gnss_serial *gserial)
{
struct mtk_data *data = gnss_serial_get_drvdata(gserial);
int ret;
ret = regulator_disable(data->vcc);
if (ret)
return ret;
return 0;
}
static int mtk_set_power(struct gnss_serial *gserial,
enum gnss_serial_pm_state state)
{
switch (state) {
case GNSS_SERIAL_ACTIVE:
return mtk_set_active(gserial);
case GNSS_SERIAL_OFF:
case GNSS_SERIAL_STANDBY:
return mtk_set_standby(gserial);
}
return -EINVAL;
}
static const struct gnss_serial_ops mtk_gserial_ops = {
.set_power = mtk_set_power,
};
static int mtk_probe(struct serdev_device *serdev)
{
struct gnss_serial *gserial;
struct mtk_data *data;
int ret;
gserial = gnss_serial_allocate(serdev, sizeof(*data));
if (IS_ERR(gserial)) {
ret = PTR_ERR(gserial);
return ret;
}
gserial->ops = &mtk_gserial_ops;
gserial->gdev->type = GNSS_TYPE_MTK;
data = gnss_serial_get_drvdata(gserial);
data->vcc = devm_regulator_get(&serdev->dev, "vcc");
if (IS_ERR(data->vcc)) {
ret = PTR_ERR(data->vcc);
goto err_free_gserial;
}
data->vbackup = devm_regulator_get_optional(&serdev->dev, "vbackup");
if (IS_ERR(data->vbackup)) {
ret = PTR_ERR(data->vbackup);
if (ret == -ENODEV)
data->vbackup = NULL;
else
goto err_free_gserial;
}
if (data->vbackup) {
ret = regulator_enable(data->vbackup);
if (ret)
goto err_free_gserial;
}
ret = gnss_serial_register(gserial);
if (ret)
goto err_disable_vbackup;
return 0;
err_disable_vbackup:
if (data->vbackup)
regulator_disable(data->vbackup);
err_free_gserial:
gnss_serial_free(gserial);
return ret;
}
static void mtk_remove(struct serdev_device *serdev)
{
struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
struct mtk_data *data = gnss_serial_get_drvdata(gserial);
gnss_serial_deregister(gserial);
if (data->vbackup)
regulator_disable(data->vbackup);
gnss_serial_free(gserial);
};
#ifdef CONFIG_OF
static const struct of_device_id mtk_of_match[] = {
{ .compatible = "globaltop,pa6h" },
{},
};
MODULE_DEVICE_TABLE(of, mtk_of_match);
#endif
static struct serdev_device_driver mtk_driver = {
.driver = {
.name = "gnss-mtk",
.of_match_table = of_match_ptr(mtk_of_match),
.pm = &gnss_serial_pm_ops,
},
.probe = mtk_probe,
.remove = mtk_remove,
};
module_serdev_device_driver(mtk_driver);
MODULE_AUTHOR("Loys Ollivier <lollivier@baylibre.com>");
MODULE_DESCRIPTION("Mediatek GNSS receiver driver");
MODULE_LICENSE("GPL v2");

View file

@ -25,31 +25,83 @@
#define SIRF_ON_OFF_PULSE_TIME 100
#define SIRF_ACTIVATE_TIMEOUT 200
#define SIRF_HIBERNATE_TIMEOUT 200
/*
* If no data arrives for this time, we assume that the chip is off.
* REVISIT: The report cycle is configurable and can be several minutes long,
* so this will only work reliably if the report cycle is set to a reasonable
* low value. Also power saving settings (like send data only on movement)
* might things work even worse.
* Workaround might be to parse shutdown or bootup messages.
*/
#define SIRF_REPORT_CYCLE 2000
struct sirf_data {
struct gnss_device *gdev;
struct serdev_device *serdev;
speed_t speed;
struct regulator *vcc;
struct regulator *lna;
struct gpio_desc *on_off;
struct gpio_desc *wakeup;
int irq;
bool active;
struct mutex gdev_mutex;
bool open;
struct mutex serdev_mutex;
int serdev_count;
wait_queue_head_t power_wait;
};
static int sirf_serdev_open(struct sirf_data *data)
{
int ret = 0;
mutex_lock(&data->serdev_mutex);
if (++data->serdev_count == 1) {
ret = serdev_device_open(data->serdev);
if (ret) {
data->serdev_count--;
goto out_unlock;
}
serdev_device_set_baudrate(data->serdev, data->speed);
serdev_device_set_flow_control(data->serdev, false);
}
out_unlock:
mutex_unlock(&data->serdev_mutex);
return ret;
}
static void sirf_serdev_close(struct sirf_data *data)
{
mutex_lock(&data->serdev_mutex);
if (--data->serdev_count == 0)
serdev_device_close(data->serdev);
mutex_unlock(&data->serdev_mutex);
}
static int sirf_open(struct gnss_device *gdev)
{
struct sirf_data *data = gnss_get_drvdata(gdev);
struct serdev_device *serdev = data->serdev;
int ret;
ret = serdev_device_open(serdev);
if (ret)
return ret;
mutex_lock(&data->gdev_mutex);
data->open = true;
mutex_unlock(&data->gdev_mutex);
serdev_device_set_baudrate(serdev, data->speed);
serdev_device_set_flow_control(serdev, false);
ret = sirf_serdev_open(data);
if (ret) {
mutex_lock(&data->gdev_mutex);
data->open = false;
mutex_unlock(&data->gdev_mutex);
return ret;
}
ret = pm_runtime_get_sync(&serdev->dev);
if (ret < 0) {
@ -61,7 +113,11 @@ static int sirf_open(struct gnss_device *gdev)
return 0;
err_close:
serdev_device_close(serdev);
sirf_serdev_close(data);
mutex_lock(&data->gdev_mutex);
data->open = false;
mutex_unlock(&data->gdev_mutex);
return ret;
}
@ -71,9 +127,13 @@ static void sirf_close(struct gnss_device *gdev)
struct sirf_data *data = gnss_get_drvdata(gdev);
struct serdev_device *serdev = data->serdev;
serdev_device_close(serdev);
sirf_serdev_close(data);
pm_runtime_put(&serdev->dev);
mutex_lock(&data->gdev_mutex);
data->open = false;
mutex_unlock(&data->gdev_mutex);
}
static int sirf_write_raw(struct gnss_device *gdev, const unsigned char *buf,
@ -105,8 +165,19 @@ static int sirf_receive_buf(struct serdev_device *serdev,
{
struct sirf_data *data = serdev_device_get_drvdata(serdev);
struct gnss_device *gdev = data->gdev;
int ret = 0;
return gnss_insert_raw(gdev, buf, count);
if (!data->wakeup && !data->active) {
data->active = true;
wake_up_interruptible(&data->power_wait);
}
mutex_lock(&data->gdev_mutex);
if (data->open)
ret = gnss_insert_raw(gdev, buf, count);
mutex_unlock(&data->gdev_mutex);
return ret;
}
static const struct serdev_device_ops sirf_serdev_ops = {
@ -125,17 +196,45 @@ static irqreturn_t sirf_wakeup_handler(int irq, void *dev_id)
if (ret < 0)
goto out;
data->active = !!ret;
data->active = ret;
wake_up_interruptible(&data->power_wait);
out:
return IRQ_HANDLED;
}
static int sirf_wait_for_power_state_nowakeup(struct sirf_data *data,
bool active,
unsigned long timeout)
{
int ret;
/* Wait for state change (including any shutdown messages). */
msleep(timeout);
/* Wait for data reception or timeout. */
data->active = false;
ret = wait_event_interruptible_timeout(data->power_wait,
data->active, msecs_to_jiffies(SIRF_REPORT_CYCLE));
if (ret < 0)
return ret;
if (ret > 0 && !active)
return -ETIMEDOUT;
if (ret == 0 && active)
return -ETIMEDOUT;
return 0;
}
static int sirf_wait_for_power_state(struct sirf_data *data, bool active,
unsigned long timeout)
{
int ret;
if (!data->wakeup)
return sirf_wait_for_power_state_nowakeup(data, active, timeout);
ret = wait_event_interruptible_timeout(data->power_wait,
data->active == active, msecs_to_jiffies(timeout));
if (ret < 0)
@ -168,21 +267,22 @@ static int sirf_set_active(struct sirf_data *data, bool active)
else
timeout = SIRF_HIBERNATE_TIMEOUT;
if (!data->wakeup) {
ret = sirf_serdev_open(data);
if (ret)
return ret;
}
do {
sirf_pulse_on_off(data);
ret = sirf_wait_for_power_state(data, active, timeout);
if (ret < 0) {
if (ret == -ETIMEDOUT)
continue;
} while (ret == -ETIMEDOUT && retries--);
return ret;
}
if (!data->wakeup)
sirf_serdev_close(data);
break;
} while (retries--);
if (retries < 0)
return -ETIMEDOUT;
if (ret)
return ret;
return 0;
}
@ -190,21 +290,60 @@ static int sirf_set_active(struct sirf_data *data, bool active)
static int sirf_runtime_suspend(struct device *dev)
{
struct sirf_data *data = dev_get_drvdata(dev);
int ret2;
int ret;
if (!data->on_off)
return regulator_disable(data->vcc);
if (data->on_off)
ret = sirf_set_active(data, false);
else
ret = regulator_disable(data->vcc);
return sirf_set_active(data, false);
if (ret)
return ret;
ret = regulator_disable(data->lna);
if (ret)
goto err_reenable;
return 0;
err_reenable:
if (data->on_off)
ret2 = sirf_set_active(data, true);
else
ret2 = regulator_enable(data->vcc);
if (ret2)
dev_err(dev,
"failed to reenable power on failed suspend: %d\n",
ret2);
return ret;
}
static int sirf_runtime_resume(struct device *dev)
{
struct sirf_data *data = dev_get_drvdata(dev);
int ret;
if (!data->on_off)
return regulator_enable(data->vcc);
ret = regulator_enable(data->lna);
if (ret)
return ret;
return sirf_set_active(data, true);
if (data->on_off)
ret = sirf_set_active(data, true);
else
ret = regulator_enable(data->vcc);
if (ret)
goto err_disable_lna;
return 0;
err_disable_lna:
regulator_disable(data->lna);
return ret;
}
static int __maybe_unused sirf_suspend(struct device *dev)
@ -275,6 +414,8 @@ static int sirf_probe(struct serdev_device *serdev)
data->serdev = serdev;
data->gdev = gdev;
mutex_init(&data->gdev_mutex);
mutex_init(&data->serdev_mutex);
init_waitqueue_head(&data->power_wait);
serdev_device_set_drvdata(serdev, data);
@ -290,6 +431,12 @@ static int sirf_probe(struct serdev_device *serdev)
goto err_put_device;
}
data->lna = devm_regulator_get(dev, "lna");
if (IS_ERR(data->lna)) {
ret = PTR_ERR(data->lna);
goto err_put_device;
}
data->on_off = devm_gpiod_get_optional(dev, "sirf,onoff",
GPIOD_OUT_LOW);
if (IS_ERR(data->on_off))
@ -301,48 +448,62 @@ static int sirf_probe(struct serdev_device *serdev)
if (IS_ERR(data->wakeup))
goto err_put_device;
/*
* Configurations where WAKEUP has been left not connected,
* are currently not supported.
*/
if (!data->wakeup) {
dev_err(dev, "no wakeup gpio specified\n");
ret = -ENODEV;
goto err_put_device;
}
}
if (data->wakeup) {
ret = gpiod_to_irq(data->wakeup);
if (ret < 0)
goto err_put_device;
data->irq = ret;
ret = devm_request_threaded_irq(dev, data->irq, NULL,
sirf_wakeup_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"wakeup", data);
if (ret)
goto err_put_device;
}
if (data->on_off) {
ret = regulator_enable(data->vcc);
if (ret)
goto err_put_device;
/* Wait for chip to boot into hibernate mode */
/* Wait for chip to boot into hibernate mode. */
msleep(SIRF_BOOT_DELAY);
}
if (data->wakeup) {
ret = gpiod_get_value_cansleep(data->wakeup);
if (ret < 0)
goto err_disable_vcc;
data->active = ret;
ret = gpiod_to_irq(data->wakeup);
if (ret < 0)
goto err_disable_vcc;
data->irq = ret;
ret = request_threaded_irq(data->irq, NULL, sirf_wakeup_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"wakeup", data);
if (ret)
goto err_disable_vcc;
}
if (data->on_off) {
if (!data->wakeup) {
data->active = false;
ret = sirf_serdev_open(data);
if (ret)
goto err_disable_vcc;
msleep(SIRF_REPORT_CYCLE);
sirf_serdev_close(data);
}
/* Force hibernate mode if already active. */
if (data->active) {
ret = sirf_set_active(data, false);
if (ret) {
dev_err(dev, "failed to set hibernate mode: %d\n",
ret);
goto err_free_irq;
}
}
}
if (IS_ENABLED(CONFIG_PM)) {
pm_runtime_set_suspended(dev); /* clear runtime_error flag */
pm_runtime_enable(dev);
} else {
ret = sirf_runtime_resume(dev);
if (ret < 0)
goto err_disable_vcc;
goto err_free_irq;
}
ret = gnss_register_device(gdev);
@ -356,6 +517,9 @@ static int sirf_probe(struct serdev_device *serdev)
pm_runtime_disable(dev);
else
sirf_runtime_suspend(dev);
err_free_irq:
if (data->wakeup)
free_irq(data->irq, data);
err_disable_vcc:
if (data->on_off)
regulator_disable(data->vcc);
@ -376,6 +540,9 @@ static void sirf_remove(struct serdev_device *serdev)
else
sirf_runtime_suspend(&serdev->dev);
if (data->wakeup)
free_irq(data->irq, data);
if (data->on_off)
regulator_disable(data->vcc);
@ -386,6 +553,7 @@ static void sirf_remove(struct serdev_device *serdev)
static const struct of_device_id sirf_of_match[] = {
{ .compatible = "fastrax,uc430" },
{ .compatible = "linx,r4" },
{ .compatible = "wi2wi,w2sg0004" },
{ .compatible = "wi2wi,w2sg0008i" },
{ .compatible = "wi2wi,w2sg0084i" },
{},

View file

@ -22,6 +22,7 @@ enum gnss_type {
GNSS_TYPE_NMEA = 0,
GNSS_TYPE_SIRF,
GNSS_TYPE_UBX,
GNSS_TYPE_MTK,
GNSS_TYPE_COUNT
};