forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gnss: add driver for u-blox receivers
Add driver for serial-connected u-blox GNSS receivers. 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 u-blox 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 timepulse-support is not yet implemented. Signed-off-by: Johan Hovold <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
- Loading branch information
Showing
3 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* u-blox GNSS receiver driver | ||
* | ||
* Copyright (C) 2018 Johan Hovold <[email protected]> | ||
*/ | ||
|
||
#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 ubx_data { | ||
struct regulator *v_bckp; | ||
struct regulator *vcc; | ||
}; | ||
|
||
static int ubx_set_active(struct gnss_serial *gserial) | ||
{ | ||
struct ubx_data *data = gnss_serial_get_drvdata(gserial); | ||
int ret; | ||
|
||
ret = regulator_enable(data->vcc); | ||
if (ret) | ||
return ret; | ||
|
||
return 0; | ||
} | ||
|
||
static int ubx_set_standby(struct gnss_serial *gserial) | ||
{ | ||
struct ubx_data *data = gnss_serial_get_drvdata(gserial); | ||
int ret; | ||
|
||
ret = regulator_disable(data->vcc); | ||
if (ret) | ||
return ret; | ||
|
||
return 0; | ||
} | ||
|
||
static int ubx_set_power(struct gnss_serial *gserial, | ||
enum gnss_serial_pm_state state) | ||
{ | ||
switch (state) { | ||
case GNSS_SERIAL_ACTIVE: | ||
return ubx_set_active(gserial); | ||
case GNSS_SERIAL_OFF: | ||
case GNSS_SERIAL_STANDBY: | ||
return ubx_set_standby(gserial); | ||
} | ||
|
||
return -EINVAL; | ||
} | ||
|
||
const struct gnss_serial_ops ubx_gserial_ops = { | ||
.set_power = ubx_set_power, | ||
}; | ||
|
||
static int ubx_probe(struct serdev_device *serdev) | ||
{ | ||
struct gnss_serial *gserial; | ||
struct ubx_data *data; | ||
int ret; | ||
|
||
gserial = gnss_serial_allocate(serdev, sizeof(*data)); | ||
if (IS_ERR(gserial)) { | ||
ret = PTR_ERR(gserial); | ||
return ret; | ||
} | ||
|
||
gserial->ops = &ubx_gserial_ops; | ||
|
||
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->v_bckp = devm_regulator_get_optional(&serdev->dev, "v-bckp"); | ||
if (IS_ERR(data->v_bckp)) { | ||
ret = PTR_ERR(data->v_bckp); | ||
if (ret == -ENODEV) | ||
data->v_bckp = NULL; | ||
else | ||
goto err_free_gserial; | ||
} | ||
|
||
if (data->v_bckp) { | ||
ret = regulator_enable(data->v_bckp); | ||
if (ret) | ||
goto err_free_gserial; | ||
} | ||
|
||
ret = gnss_serial_register(gserial); | ||
if (ret) | ||
goto err_disable_v_bckp; | ||
|
||
return 0; | ||
|
||
err_disable_v_bckp: | ||
if (data->v_bckp) | ||
regulator_disable(data->v_bckp); | ||
err_free_gserial: | ||
gnss_serial_free(gserial); | ||
|
||
return ret; | ||
} | ||
|
||
static void ubx_remove(struct serdev_device *serdev) | ||
{ | ||
struct gnss_serial *gserial = serdev_device_get_drvdata(serdev); | ||
struct ubx_data *data = gnss_serial_get_drvdata(gserial); | ||
|
||
gnss_serial_deregister(gserial); | ||
if (data->v_bckp) | ||
regulator_disable(data->v_bckp); | ||
gnss_serial_free(gserial); | ||
}; | ||
|
||
#ifdef CONFIG_OF | ||
static const struct of_device_id ubx_of_match[] = { | ||
{ .compatible = "u-blox,neo-8" }, | ||
{ .compatible = "u-blox,neo-m8" }, | ||
{}, | ||
}; | ||
MODULE_DEVICE_TABLE(of, ubx_of_match); | ||
#endif | ||
|
||
static struct serdev_device_driver ubx_driver = { | ||
.driver = { | ||
.name = "gnss-ubx", | ||
.of_match_table = of_match_ptr(ubx_of_match), | ||
.pm = &gnss_serial_pm_ops, | ||
}, | ||
.probe = ubx_probe, | ||
.remove = ubx_remove, | ||
}; | ||
module_serdev_device_driver(ubx_driver); | ||
|
||
MODULE_AUTHOR("Johan Hovold <[email protected]>"); | ||
MODULE_DESCRIPTION("u-blox GNSS receiver driver"); | ||
MODULE_LICENSE("GPL v2"); |