xref: /openbmc/linux/drivers/gnss/ubx.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
11ad69f10SJohan Hovold // SPDX-License-Identifier: GPL-2.0
21ad69f10SJohan Hovold /*
31ad69f10SJohan Hovold  * u-blox GNSS receiver driver
41ad69f10SJohan Hovold  *
51ad69f10SJohan Hovold  * Copyright (C) 2018 Johan Hovold <johan@kernel.org>
61ad69f10SJohan Hovold  */
71ad69f10SJohan Hovold 
81ad69f10SJohan Hovold #include <linux/errno.h>
91ad69f10SJohan Hovold #include <linux/gnss.h>
101ad69f10SJohan Hovold #include <linux/init.h>
111ad69f10SJohan Hovold #include <linux/kernel.h>
121ad69f10SJohan Hovold #include <linux/module.h>
131ad69f10SJohan Hovold #include <linux/of.h>
141ad69f10SJohan Hovold #include <linux/regulator/consumer.h>
151ad69f10SJohan Hovold #include <linux/serdev.h>
161ad69f10SJohan Hovold 
171ad69f10SJohan Hovold #include "serial.h"
181ad69f10SJohan Hovold 
191ad69f10SJohan Hovold struct ubx_data {
201ad69f10SJohan Hovold 	struct regulator *v_bckp;
211ad69f10SJohan Hovold 	struct regulator *vcc;
221ad69f10SJohan Hovold };
231ad69f10SJohan Hovold 
ubx_set_active(struct gnss_serial * gserial)241ad69f10SJohan Hovold static int ubx_set_active(struct gnss_serial *gserial)
251ad69f10SJohan Hovold {
261ad69f10SJohan Hovold 	struct ubx_data *data = gnss_serial_get_drvdata(gserial);
271ad69f10SJohan Hovold 	int ret;
281ad69f10SJohan Hovold 
291ad69f10SJohan Hovold 	ret = regulator_enable(data->vcc);
301ad69f10SJohan Hovold 	if (ret)
311ad69f10SJohan Hovold 		return ret;
321ad69f10SJohan Hovold 
331ad69f10SJohan Hovold 	return 0;
341ad69f10SJohan Hovold }
351ad69f10SJohan Hovold 
ubx_set_standby(struct gnss_serial * gserial)361ad69f10SJohan Hovold static int ubx_set_standby(struct gnss_serial *gserial)
371ad69f10SJohan Hovold {
381ad69f10SJohan Hovold 	struct ubx_data *data = gnss_serial_get_drvdata(gserial);
391ad69f10SJohan Hovold 	int ret;
401ad69f10SJohan Hovold 
411ad69f10SJohan Hovold 	ret = regulator_disable(data->vcc);
421ad69f10SJohan Hovold 	if (ret)
431ad69f10SJohan Hovold 		return ret;
441ad69f10SJohan Hovold 
451ad69f10SJohan Hovold 	return 0;
461ad69f10SJohan Hovold }
471ad69f10SJohan Hovold 
ubx_set_power(struct gnss_serial * gserial,enum gnss_serial_pm_state state)481ad69f10SJohan Hovold static int ubx_set_power(struct gnss_serial *gserial,
491ad69f10SJohan Hovold 				enum gnss_serial_pm_state state)
501ad69f10SJohan Hovold {
511ad69f10SJohan Hovold 	switch (state) {
521ad69f10SJohan Hovold 	case GNSS_SERIAL_ACTIVE:
531ad69f10SJohan Hovold 		return ubx_set_active(gserial);
541ad69f10SJohan Hovold 	case GNSS_SERIAL_OFF:
551ad69f10SJohan Hovold 	case GNSS_SERIAL_STANDBY:
561ad69f10SJohan Hovold 		return ubx_set_standby(gserial);
571ad69f10SJohan Hovold 	}
581ad69f10SJohan Hovold 
591ad69f10SJohan Hovold 	return -EINVAL;
601ad69f10SJohan Hovold }
611ad69f10SJohan Hovold 
6255570f1aSColin Ian King static const struct gnss_serial_ops ubx_gserial_ops = {
631ad69f10SJohan Hovold 	.set_power = ubx_set_power,
641ad69f10SJohan Hovold };
651ad69f10SJohan Hovold 
ubx_probe(struct serdev_device * serdev)661ad69f10SJohan Hovold static int ubx_probe(struct serdev_device *serdev)
671ad69f10SJohan Hovold {
681ad69f10SJohan Hovold 	struct gnss_serial *gserial;
691ad69f10SJohan Hovold 	struct ubx_data *data;
701ad69f10SJohan Hovold 	int ret;
711ad69f10SJohan Hovold 
721ad69f10SJohan Hovold 	gserial = gnss_serial_allocate(serdev, sizeof(*data));
731ad69f10SJohan Hovold 	if (IS_ERR(gserial)) {
741ad69f10SJohan Hovold 		ret = PTR_ERR(gserial);
751ad69f10SJohan Hovold 		return ret;
761ad69f10SJohan Hovold 	}
771ad69f10SJohan Hovold 
781ad69f10SJohan Hovold 	gserial->ops = &ubx_gserial_ops;
791ad69f10SJohan Hovold 
8010f14663SJohan Hovold 	gserial->gdev->type = GNSS_TYPE_UBX;
8110f14663SJohan Hovold 
821ad69f10SJohan Hovold 	data = gnss_serial_get_drvdata(gserial);
831ad69f10SJohan Hovold 
841ad69f10SJohan Hovold 	data->vcc = devm_regulator_get(&serdev->dev, "vcc");
851ad69f10SJohan Hovold 	if (IS_ERR(data->vcc)) {
861ad69f10SJohan Hovold 		ret = PTR_ERR(data->vcc);
871ad69f10SJohan Hovold 		goto err_free_gserial;
881ad69f10SJohan Hovold 	}
891ad69f10SJohan Hovold 
901ad69f10SJohan Hovold 	data->v_bckp = devm_regulator_get_optional(&serdev->dev, "v-bckp");
911ad69f10SJohan Hovold 	if (IS_ERR(data->v_bckp)) {
921ad69f10SJohan Hovold 		ret = PTR_ERR(data->v_bckp);
931ad69f10SJohan Hovold 		if (ret == -ENODEV)
941ad69f10SJohan Hovold 			data->v_bckp = NULL;
951ad69f10SJohan Hovold 		else
961ad69f10SJohan Hovold 			goto err_free_gserial;
971ad69f10SJohan Hovold 	}
981ad69f10SJohan Hovold 
991ad69f10SJohan Hovold 	if (data->v_bckp) {
1001ad69f10SJohan Hovold 		ret = regulator_enable(data->v_bckp);
1011ad69f10SJohan Hovold 		if (ret)
1021ad69f10SJohan Hovold 			goto err_free_gserial;
1031ad69f10SJohan Hovold 	}
1041ad69f10SJohan Hovold 
1051ad69f10SJohan Hovold 	ret = gnss_serial_register(gserial);
1061ad69f10SJohan Hovold 	if (ret)
1071ad69f10SJohan Hovold 		goto err_disable_v_bckp;
1081ad69f10SJohan Hovold 
1091ad69f10SJohan Hovold 	return 0;
1101ad69f10SJohan Hovold 
1111ad69f10SJohan Hovold err_disable_v_bckp:
1121ad69f10SJohan Hovold 	if (data->v_bckp)
1131ad69f10SJohan Hovold 		regulator_disable(data->v_bckp);
1141ad69f10SJohan Hovold err_free_gserial:
1151ad69f10SJohan Hovold 	gnss_serial_free(gserial);
1161ad69f10SJohan Hovold 
1171ad69f10SJohan Hovold 	return ret;
1181ad69f10SJohan Hovold }
1191ad69f10SJohan Hovold 
ubx_remove(struct serdev_device * serdev)1201ad69f10SJohan Hovold static void ubx_remove(struct serdev_device *serdev)
1211ad69f10SJohan Hovold {
1221ad69f10SJohan Hovold 	struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
1231ad69f10SJohan Hovold 	struct ubx_data *data = gnss_serial_get_drvdata(gserial);
1241ad69f10SJohan Hovold 
1251ad69f10SJohan Hovold 	gnss_serial_deregister(gserial);
1261ad69f10SJohan Hovold 	if (data->v_bckp)
1271ad69f10SJohan Hovold 		regulator_disable(data->v_bckp);
1281ad69f10SJohan Hovold 	gnss_serial_free(gserial);
129*b15c9015SJohan Hovold }
1301ad69f10SJohan Hovold 
1311ad69f10SJohan Hovold #ifdef CONFIG_OF
1321ad69f10SJohan Hovold static const struct of_device_id ubx_of_match[] = {
1337cc10c5cSOndrej Jirman 	{ .compatible = "u-blox,neo-6m" },
1341ad69f10SJohan Hovold 	{ .compatible = "u-blox,neo-8" },
1351ad69f10SJohan Hovold 	{ .compatible = "u-blox,neo-m8" },
1361ad69f10SJohan Hovold 	{},
1371ad69f10SJohan Hovold };
1381ad69f10SJohan Hovold MODULE_DEVICE_TABLE(of, ubx_of_match);
1391ad69f10SJohan Hovold #endif
1401ad69f10SJohan Hovold 
1411ad69f10SJohan Hovold static struct serdev_device_driver ubx_driver = {
1421ad69f10SJohan Hovold 	.driver	= {
1431ad69f10SJohan Hovold 		.name		= "gnss-ubx",
1441ad69f10SJohan Hovold 		.of_match_table	= of_match_ptr(ubx_of_match),
1451ad69f10SJohan Hovold 		.pm		= &gnss_serial_pm_ops,
1461ad69f10SJohan Hovold 	},
1471ad69f10SJohan Hovold 	.probe	= ubx_probe,
1481ad69f10SJohan Hovold 	.remove	= ubx_remove,
1491ad69f10SJohan Hovold };
1501ad69f10SJohan Hovold module_serdev_device_driver(ubx_driver);
1511ad69f10SJohan Hovold 
1521ad69f10SJohan Hovold MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
1531ad69f10SJohan Hovold MODULE_DESCRIPTION("u-blox GNSS receiver driver");
1541ad69f10SJohan Hovold MODULE_LICENSE("GPL v2");
155