From 82f844c22588bf47132c82faeda50b6db473162c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Jan 2019 18:22:53 +0100 Subject: gnss: sirf: fix premature wakeup interrupt enable Make sure the receiver is powered (and booted) before enabling the wakeup interrupt to avoid spurious interrupts due to a floating input. Similarly, disable the interrupt before powering off on probe errors and on unbind. Fixes: d2efbbd18b1e ("gnss: add driver for sirfstar-based receivers") Cc: stable # 4.19 Signed-off-by: Johan Hovold --- drivers/gnss/sirf.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 226f6e6fe01b..8e3f6a776e02 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -310,30 +310,26 @@ static int sirf_probe(struct serdev_device *serdev) ret = -ENODEV; goto err_put_device; } + + ret = regulator_enable(data->vcc); + if (ret) + goto err_put_device; + + /* Wait for chip to boot into hibernate mode. */ + msleep(SIRF_BOOT_DELAY); } if (data->wakeup) { ret = gpiod_to_irq(data->wakeup); if (ret < 0) - goto err_put_device; - + goto err_disable_vcc; data->irq = ret; - ret = devm_request_threaded_irq(dev, data->irq, NULL, - sirf_wakeup_handler, + ret = request_threaded_irq(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 */ - msleep(SIRF_BOOT_DELAY); + goto err_disable_vcc; } if (IS_ENABLED(CONFIG_PM)) { @@ -342,7 +338,7 @@ static int sirf_probe(struct serdev_device *serdev) } else { ret = sirf_runtime_resume(dev); if (ret < 0) - goto err_disable_vcc; + goto err_free_irq; } ret = gnss_register_device(gdev); @@ -356,6 +352,9 @@ err_disable_rpm: 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 +375,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); -- cgit From 4ceda5f654c350faa5b80f89625673a882735a54 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Jan 2019 18:22:54 +0100 Subject: gnss: sirf: force hibernate mode on probe Make sure to put the receiver in hibernate mode in case it is already active during probe in order to avoid wasting power until first open or suspend. This can happen, for example, after a reset or non-clean shutdown, and possibly also due to glitches during power-on. Reported-by: Andreas Kemnade Signed-off-by: Johan Hovold --- drivers/gnss/sirf.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 8e3f6a776e02..f9a9d00dec98 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -320,6 +320,11 @@ static int sirf_probe(struct serdev_device *serdev) } 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; @@ -332,6 +337,18 @@ static int sirf_probe(struct serdev_device *serdev) goto err_disable_vcc; } + if (data->on_off) { + /* 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); -- cgit From 9f1623fdb399e5cf677f042e3bff3beec70376f6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Jan 2019 18:22:55 +0100 Subject: gnss: sirf: drop redundant double negation The active flag is of type bool so drop the redundant double negation when storing the gpio state. Signed-off-by: Johan Hovold --- drivers/gnss/sirf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index f9a9d00dec98..59cde7e923b8 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -125,7 +125,7 @@ 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; -- cgit From 196d91846099fb49b45fb14b0f47b9af3b721b94 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Thu, 24 Jan 2019 07:34:35 +0100 Subject: gnss: sirf: write data to gnss only when the gnss device is open The api forbids writing data there otherwise. Prepare for the serdev_open()/close() being a part of sirf_set_active. Signed-off-by: Andreas Kemnade Signed-off-by: Johan Hovold --- drivers/gnss/sirf.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 59cde7e923b8..49bc021325e9 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -35,6 +35,10 @@ struct sirf_data { struct gpio_desc *wakeup; int irq; bool active; + + struct mutex gdev_mutex; + bool open; + wait_queue_head_t power_wait; }; @@ -44,9 +48,18 @@ static int sirf_open(struct gnss_device *gdev) struct serdev_device *serdev = data->serdev; int ret; + mutex_lock(&data->gdev_mutex); + data->open = true; + mutex_unlock(&data->gdev_mutex); + ret = serdev_device_open(serdev); - if (ret) + if (ret) { + mutex_lock(&data->gdev_mutex); + data->open = false; + mutex_unlock(&data->gdev_mutex); return ret; + } + serdev_device_set_baudrate(serdev, data->speed); serdev_device_set_flow_control(serdev, false); @@ -63,6 +76,10 @@ static int sirf_open(struct gnss_device *gdev) err_close: serdev_device_close(serdev); + mutex_lock(&data->gdev_mutex); + data->open = false; + mutex_unlock(&data->gdev_mutex); + return ret; } @@ -74,6 +91,10 @@ static void sirf_close(struct gnss_device *gdev) serdev_device_close(serdev); 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 +126,14 @@ 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); + 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 = { @@ -275,6 +302,7 @@ static int sirf_probe(struct serdev_device *serdev) data->serdev = serdev; data->gdev = gdev; + mutex_init(&data->gdev_mutex); init_waitqueue_head(&data->power_wait); serdev_device_set_drvdata(serdev, data); -- cgit From ccd0e4963f552213bc977be31158a153992558af Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Thu, 24 Jan 2019 07:34:36 +0100 Subject: gnss: sirf: add support for configurations without wakeup signal Some Wi2Wi devices do not have a wakeup output, so device state can only be indirectly detected by looking whether there is communication over the serial lines. This approach requires a report cycle set to a value less than 2 seconds to be reliable. Signed-off-by: Andreas Kemnade [ johan: simplify sirf_set_active(), style changes ] Signed-off-by: Johan Hovold --- drivers/gnss/sirf.c | 129 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 27 deletions(-) diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 49bc021325e9..703d9f17c583 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -25,6 +25,15 @@ #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; @@ -39,9 +48,42 @@ struct sirf_data { 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); @@ -52,7 +94,7 @@ static int sirf_open(struct gnss_device *gdev) data->open = true; mutex_unlock(&data->gdev_mutex); - ret = serdev_device_open(serdev); + ret = sirf_serdev_open(data); if (ret) { mutex_lock(&data->gdev_mutex); data->open = false; @@ -60,10 +102,6 @@ static int sirf_open(struct gnss_device *gdev) return ret; } - - serdev_device_set_baudrate(serdev, data->speed); - serdev_device_set_flow_control(serdev, false); - ret = pm_runtime_get_sync(&serdev->dev); if (ret < 0) { dev_err(&gdev->dev, "failed to runtime resume: %d\n", ret); @@ -74,7 +112,7 @@ 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; @@ -88,7 +126,7 @@ 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); @@ -128,6 +166,11 @@ static int sirf_receive_buf(struct serdev_device *serdev, struct gnss_device *gdev = data->gdev; int ret = 0; + 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); @@ -158,11 +201,39 @@ 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) @@ -195,21 +266,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; - - return ret; - } + } while (ret == -ETIMEDOUT && retries--); - break; - } while (retries--); + if (!data->wakeup) + sirf_serdev_close(data); - if (retries < 0) - return -ETIMEDOUT; + if (ret) + return ret; return 0; } @@ -303,6 +375,7 @@ static int sirf_probe(struct serdev_device *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); @@ -329,16 +402,6 @@ 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; - } - ret = regulator_enable(data->vcc); if (ret) goto err_put_device; @@ -366,6 +429,17 @@ static int sirf_probe(struct serdev_device *serdev) } 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); @@ -433,6 +507,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" }, {}, -- cgit From 176f011bda551f2bb884dc25595859c3b35ac479 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Thu, 24 Jan 2019 07:34:37 +0100 Subject: dt-bindings: gnss: add w2sg0004 compatible string Add w2sg0004 compatible string since devices without wakeup pins are now supported. Signed-off-by: Andreas Kemnade Reviewed-by: Rob Herring Signed-off-by: Johan Hovold --- Documentation/devicetree/bindings/gnss/sirfstar.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gnss/sirfstar.txt b/Documentation/devicetree/bindings/gnss/sirfstar.txt index 648d183cdb77..f4252b6b660b 100644 --- a/Documentation/devicetree/bindings/gnss/sirfstar.txt +++ b/Documentation/devicetree/bindings/gnss/sirfstar.txt @@ -12,6 +12,7 @@ Required properties: "fastrax,uc430" "linx,r4" + "wi2wi,w2sg0004" "wi2wi,w2sg0008i" "wi2wi,w2sg0084i" -- cgit From 8fafef42c799eed3afe9abbd3ad517bb8223bc37 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Thu, 24 Jan 2019 07:34:38 +0100 Subject: gnss: sirf: add a separate supply for a lna Devices might have a separate lna between antenna input of the gps chip and the antenna which might have a separate supply. Signed-off-by: Andreas Kemnade Signed-off-by: Johan Hovold --- drivers/gnss/sirf.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 703d9f17c583..effed3a8d398 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -40,6 +40,7 @@ struct sirf_data { struct serdev_device *serdev; speed_t speed; struct regulator *vcc; + struct regulator *lna; struct gpio_desc *on_off; struct gpio_desc *wakeup; int irq; @@ -289,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) + ret = sirf_set_active(data, false); + else + ret = regulator_disable(data->vcc); + + 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 (!data->on_off) - return regulator_disable(data->vcc); + if (ret2) + dev_err(dev, + "failed to reenable power on failed suspend: %d\n", + ret2); - return sirf_set_active(data, false); + 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; + + if (data->on_off) + ret = sirf_set_active(data, true); + else + ret = regulator_enable(data->vcc); + + if (ret) + goto err_disable_lna; - return sirf_set_active(data, true); + return 0; + +err_disable_lna: + regulator_disable(data->lna); + + return ret; } static int __maybe_unused sirf_suspend(struct device *dev) @@ -391,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)) -- cgit From f9eb86fddaf2e9a40c79e6ae69865eb6c8913a6e Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Thu, 24 Jan 2019 07:34:39 +0100 Subject: dt-bindings: gnss: add lna-supply property Add lna-supply property. Signed-off-by: Andreas Kemnade Reviewed-by: Rob Herring Signed-off-by: Johan Hovold --- Documentation/devicetree/bindings/gnss/gnss.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gnss/gnss.txt b/Documentation/devicetree/bindings/gnss/gnss.txt index f1e4a2ff47c5..f547bd4549fe 100644 --- a/Documentation/devicetree/bindings/gnss/gnss.txt +++ b/Documentation/devicetree/bindings/gnss/gnss.txt @@ -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 -- cgit From 27eae9d4cebeb2e2abda7579efac2ea0673e0a14 Mon Sep 17 00:00:00 2001 From: Loys Ollivier Date: Wed, 13 Feb 2019 16:09:26 +0100 Subject: dt-bindings: Add vendor prefix for "GlobalTop Technology, Inc." Add globaltop vendor definition. Signed-off-by: Loys Ollivier Reviewed-by: Rob Herring Signed-off-by: Johan Hovold --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 389508584f48..d80a70343b36 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -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. -- cgit From 3deb254d655931e72d195a5921db5af5f7b6572a Mon Sep 17 00:00:00 2001 From: Loys Ollivier Date: Wed, 13 Feb 2019 16:09:27 +0100 Subject: dt-bindings: gnss: add mediatek binding Add binding for Mediatek-based GNSS receivers. Signed-off-by: Loys Ollivier Reviewed-by: Rob Herring [ johan: rename backup supply ] Signed-off-by: Johan Hovold --- .../devicetree/bindings/gnss/mediatek.txt | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/gnss/mediatek.txt diff --git a/Documentation/devicetree/bindings/gnss/mediatek.txt b/Documentation/devicetree/bindings/gnss/mediatek.txt new file mode 100644 index 000000000000..80cb802813c5 --- /dev/null +++ b/Documentation/devicetree/bindings/gnss/mediatek.txt @@ -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>; + }; +}; -- cgit From 625239d4ad43590f6639737ee900884f7d801411 Mon Sep 17 00:00:00 2001 From: Loys Ollivier Date: Wed, 13 Feb 2019 16:09:28 +0100 Subject: gnss: add mtk receiver type support Add an MTK (Mediatek) type to the "GNSS_TYPE" attribute. Note that MTK receivers support a subset of NMEA 0183 with vendor extensions. Signed-off-by: Loys Ollivier Signed-off-by: Johan Hovold --- drivers/gnss/core.c | 1 + include/linux/gnss.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gnss/core.c b/drivers/gnss/core.c index 4291a0dd22aa..320cfca80d5f 100644 --- a/drivers/gnss/core.c +++ b/drivers/gnss/core.c @@ -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) diff --git a/include/linux/gnss.h b/include/linux/gnss.h index 43546977098c..36968a0f33e8 100644 --- a/include/linux/gnss.h +++ b/include/linux/gnss.h @@ -22,6 +22,7 @@ enum gnss_type { GNSS_TYPE_NMEA = 0, GNSS_TYPE_SIRF, GNSS_TYPE_UBX, + GNSS_TYPE_MTK, GNSS_TYPE_COUNT }; -- cgit From d4584bbfcf2a8f5b6605be9eba465bba59ce68c8 Mon Sep 17 00:00:00 2001 From: Loys Ollivier Date: Wed, 13 Feb 2019 16:09:29 +0100 Subject: gnss: add driver for mediatek receivers Add driver for serial-connected Mediatek-based GNSS receivers. These devices typically boot transmitting vendor specific NMEA output sequences. The serial port bit rate is read from the device tree "current-speed". Note that the driver uses the generic GNSS serial implementation and therefore essentially only manages power abstracted into three power states: ACTIVE, STANDBY, and OFF. For mediatek receivers with a main supply and no enable-gpios, this simply means that the main supply is disabled in STANDBY and OFF (the optional backup supply is kept enabled while the driver is bound). Note that the timepulse-support is left unimplemented. Signed-off-by: Loys Ollivier [ johan: rename backup supply ] Signed-off-by: Johan Hovold --- drivers/gnss/Kconfig | 13 +++++ drivers/gnss/Makefile | 3 + drivers/gnss/mtk.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/gnss/mtk.c diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig index 6abc88514512..6d8c8027e1cd 100644 --- a/drivers/gnss/Kconfig +++ b/drivers/gnss/Kconfig @@ -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 diff --git a/drivers/gnss/Makefile b/drivers/gnss/Makefile index 5cf0ebe0330a..451f11401ecc 100644 --- a/drivers/gnss/Makefile +++ b/drivers/gnss/Makefile @@ -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 diff --git a/drivers/gnss/mtk.c b/drivers/gnss/mtk.c new file mode 100644 index 000000000000..d1fc55560daf --- /dev/null +++ b/drivers/gnss/mtk.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mediatek GNSS receiver driver + * + * Copyright (C) 2018 Johan Hovold + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("Mediatek GNSS receiver driver"); +MODULE_LICENSE("GPL v2"); -- cgit