forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
3.1 KiB
154 lines
3.1 KiB
// 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; |
|
} |
|
|
|
static 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; |
|
|
|
gserial->gdev->type = GNSS_TYPE_UBX; |
|
|
|
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-6m" }, |
|
{ .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");
|
|
|