xref: /openbmc/linux/drivers/gnss/mtk.c (revision b15c9015)
1d4584bbfSLoys Ollivier // SPDX-License-Identifier: GPL-2.0
2d4584bbfSLoys Ollivier /*
3d4584bbfSLoys Ollivier  * Mediatek GNSS receiver driver
4d4584bbfSLoys Ollivier  *
5d4584bbfSLoys Ollivier  * Copyright (C) 2018 Johan Hovold <johan@kernel.org>
6d4584bbfSLoys Ollivier  */
7d4584bbfSLoys Ollivier 
8d4584bbfSLoys Ollivier #include <linux/errno.h>
9d4584bbfSLoys Ollivier #include <linux/gnss.h>
10d4584bbfSLoys Ollivier #include <linux/init.h>
11d4584bbfSLoys Ollivier #include <linux/kernel.h>
12d4584bbfSLoys Ollivier #include <linux/module.h>
13d4584bbfSLoys Ollivier #include <linux/of.h>
14d4584bbfSLoys Ollivier #include <linux/regulator/consumer.h>
15d4584bbfSLoys Ollivier #include <linux/serdev.h>
16d4584bbfSLoys Ollivier 
17d4584bbfSLoys Ollivier #include "serial.h"
18d4584bbfSLoys Ollivier 
19d4584bbfSLoys Ollivier struct mtk_data {
20d4584bbfSLoys Ollivier 	struct regulator *vbackup;
21d4584bbfSLoys Ollivier 	struct regulator *vcc;
22d4584bbfSLoys Ollivier };
23d4584bbfSLoys Ollivier 
mtk_set_active(struct gnss_serial * gserial)24d4584bbfSLoys Ollivier static int mtk_set_active(struct gnss_serial *gserial)
25d4584bbfSLoys Ollivier {
26d4584bbfSLoys Ollivier 	struct mtk_data *data = gnss_serial_get_drvdata(gserial);
27d4584bbfSLoys Ollivier 	int ret;
28d4584bbfSLoys Ollivier 
29d4584bbfSLoys Ollivier 	ret = regulator_enable(data->vcc);
30d4584bbfSLoys Ollivier 	if (ret)
31d4584bbfSLoys Ollivier 		return ret;
32d4584bbfSLoys Ollivier 
33d4584bbfSLoys Ollivier 	return 0;
34d4584bbfSLoys Ollivier }
35d4584bbfSLoys Ollivier 
mtk_set_standby(struct gnss_serial * gserial)36d4584bbfSLoys Ollivier static int mtk_set_standby(struct gnss_serial *gserial)
37d4584bbfSLoys Ollivier {
38d4584bbfSLoys Ollivier 	struct mtk_data *data = gnss_serial_get_drvdata(gserial);
39d4584bbfSLoys Ollivier 	int ret;
40d4584bbfSLoys Ollivier 
41d4584bbfSLoys Ollivier 	ret = regulator_disable(data->vcc);
42d4584bbfSLoys Ollivier 	if (ret)
43d4584bbfSLoys Ollivier 		return ret;
44d4584bbfSLoys Ollivier 
45d4584bbfSLoys Ollivier 	return 0;
46d4584bbfSLoys Ollivier }
47d4584bbfSLoys Ollivier 
mtk_set_power(struct gnss_serial * gserial,enum gnss_serial_pm_state state)48d4584bbfSLoys Ollivier static int mtk_set_power(struct gnss_serial *gserial,
49d4584bbfSLoys Ollivier 			 enum gnss_serial_pm_state state)
50d4584bbfSLoys Ollivier {
51d4584bbfSLoys Ollivier 	switch (state) {
52d4584bbfSLoys Ollivier 	case GNSS_SERIAL_ACTIVE:
53d4584bbfSLoys Ollivier 		return mtk_set_active(gserial);
54d4584bbfSLoys Ollivier 	case GNSS_SERIAL_OFF:
55d4584bbfSLoys Ollivier 	case GNSS_SERIAL_STANDBY:
56d4584bbfSLoys Ollivier 		return mtk_set_standby(gserial);
57d4584bbfSLoys Ollivier 	}
58d4584bbfSLoys Ollivier 
59d4584bbfSLoys Ollivier 	return -EINVAL;
60d4584bbfSLoys Ollivier }
61d4584bbfSLoys Ollivier 
62d4584bbfSLoys Ollivier static const struct gnss_serial_ops mtk_gserial_ops = {
63d4584bbfSLoys Ollivier 	.set_power = mtk_set_power,
64d4584bbfSLoys Ollivier };
65d4584bbfSLoys Ollivier 
mtk_probe(struct serdev_device * serdev)66d4584bbfSLoys Ollivier static int mtk_probe(struct serdev_device *serdev)
67d4584bbfSLoys Ollivier {
68d4584bbfSLoys Ollivier 	struct gnss_serial *gserial;
69d4584bbfSLoys Ollivier 	struct mtk_data *data;
70d4584bbfSLoys Ollivier 	int ret;
71d4584bbfSLoys Ollivier 
72d4584bbfSLoys Ollivier 	gserial = gnss_serial_allocate(serdev, sizeof(*data));
73d4584bbfSLoys Ollivier 	if (IS_ERR(gserial)) {
74d4584bbfSLoys Ollivier 		ret = PTR_ERR(gserial);
75d4584bbfSLoys Ollivier 		return ret;
76d4584bbfSLoys Ollivier 	}
77d4584bbfSLoys Ollivier 
78d4584bbfSLoys Ollivier 	gserial->ops = &mtk_gserial_ops;
79d4584bbfSLoys Ollivier 
80d4584bbfSLoys Ollivier 	gserial->gdev->type = GNSS_TYPE_MTK;
81d4584bbfSLoys Ollivier 
82d4584bbfSLoys Ollivier 	data = gnss_serial_get_drvdata(gserial);
83d4584bbfSLoys Ollivier 
84d4584bbfSLoys Ollivier 	data->vcc = devm_regulator_get(&serdev->dev, "vcc");
85d4584bbfSLoys Ollivier 	if (IS_ERR(data->vcc)) {
86d4584bbfSLoys Ollivier 		ret = PTR_ERR(data->vcc);
87d4584bbfSLoys Ollivier 		goto err_free_gserial;
88d4584bbfSLoys Ollivier 	}
89d4584bbfSLoys Ollivier 
90d4584bbfSLoys Ollivier 	data->vbackup = devm_regulator_get_optional(&serdev->dev, "vbackup");
91d4584bbfSLoys Ollivier 	if (IS_ERR(data->vbackup)) {
92d4584bbfSLoys Ollivier 		ret = PTR_ERR(data->vbackup);
93d4584bbfSLoys Ollivier 		if (ret == -ENODEV)
94d4584bbfSLoys Ollivier 			data->vbackup = NULL;
95d4584bbfSLoys Ollivier 		else
96d4584bbfSLoys Ollivier 			goto err_free_gserial;
97d4584bbfSLoys Ollivier 	}
98d4584bbfSLoys Ollivier 
99d4584bbfSLoys Ollivier 	if (data->vbackup) {
100d4584bbfSLoys Ollivier 		ret = regulator_enable(data->vbackup);
101d4584bbfSLoys Ollivier 		if (ret)
102d4584bbfSLoys Ollivier 			goto err_free_gserial;
103d4584bbfSLoys Ollivier 	}
104d4584bbfSLoys Ollivier 
105d4584bbfSLoys Ollivier 	ret = gnss_serial_register(gserial);
106d4584bbfSLoys Ollivier 	if (ret)
107d4584bbfSLoys Ollivier 		goto err_disable_vbackup;
108d4584bbfSLoys Ollivier 
109d4584bbfSLoys Ollivier 	return 0;
110d4584bbfSLoys Ollivier 
111d4584bbfSLoys Ollivier err_disable_vbackup:
112d4584bbfSLoys Ollivier 	if (data->vbackup)
113d4584bbfSLoys Ollivier 		regulator_disable(data->vbackup);
114d4584bbfSLoys Ollivier err_free_gserial:
115d4584bbfSLoys Ollivier 	gnss_serial_free(gserial);
116d4584bbfSLoys Ollivier 
117d4584bbfSLoys Ollivier 	return ret;
118d4584bbfSLoys Ollivier }
119d4584bbfSLoys Ollivier 
mtk_remove(struct serdev_device * serdev)120d4584bbfSLoys Ollivier static void mtk_remove(struct serdev_device *serdev)
121d4584bbfSLoys Ollivier {
122d4584bbfSLoys Ollivier 	struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
123d4584bbfSLoys Ollivier 	struct mtk_data *data = gnss_serial_get_drvdata(gserial);
124d4584bbfSLoys Ollivier 
125d4584bbfSLoys Ollivier 	gnss_serial_deregister(gserial);
126d4584bbfSLoys Ollivier 	if (data->vbackup)
127d4584bbfSLoys Ollivier 		regulator_disable(data->vbackup);
128d4584bbfSLoys Ollivier 	gnss_serial_free(gserial);
129*b15c9015SJohan Hovold }
130d4584bbfSLoys Ollivier 
131d4584bbfSLoys Ollivier #ifdef CONFIG_OF
132d4584bbfSLoys Ollivier static const struct of_device_id mtk_of_match[] = {
133d4584bbfSLoys Ollivier 	{ .compatible = "globaltop,pa6h" },
134d4584bbfSLoys Ollivier 	{},
135d4584bbfSLoys Ollivier };
136d4584bbfSLoys Ollivier MODULE_DEVICE_TABLE(of, mtk_of_match);
137d4584bbfSLoys Ollivier #endif
138d4584bbfSLoys Ollivier 
139d4584bbfSLoys Ollivier static struct serdev_device_driver mtk_driver = {
140d4584bbfSLoys Ollivier 	.driver	= {
141d4584bbfSLoys Ollivier 		.name		= "gnss-mtk",
142d4584bbfSLoys Ollivier 		.of_match_table	= of_match_ptr(mtk_of_match),
143d4584bbfSLoys Ollivier 		.pm		= &gnss_serial_pm_ops,
144d4584bbfSLoys Ollivier 	},
145d4584bbfSLoys Ollivier 	.probe	= mtk_probe,
146d4584bbfSLoys Ollivier 	.remove	= mtk_remove,
147d4584bbfSLoys Ollivier };
148d4584bbfSLoys Ollivier module_serdev_device_driver(mtk_driver);
149d4584bbfSLoys Ollivier 
150d4584bbfSLoys Ollivier MODULE_AUTHOR("Loys Ollivier <lollivier@baylibre.com>");
151d4584bbfSLoys Ollivier MODULE_DESCRIPTION("Mediatek GNSS receiver driver");
152d4584bbfSLoys Ollivier MODULE_LICENSE("GPL v2");
153