162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Mediatek GNSS receiver driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018 Johan Hovold <johan@kernel.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/gnss.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1562306a36Sopenharmony_ci#include <linux/serdev.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "serial.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct mtk_data {
2062306a36Sopenharmony_ci	struct regulator *vbackup;
2162306a36Sopenharmony_ci	struct regulator *vcc;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int mtk_set_active(struct gnss_serial *gserial)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct mtk_data *data = gnss_serial_get_drvdata(gserial);
2762306a36Sopenharmony_ci	int ret;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	ret = regulator_enable(data->vcc);
3062306a36Sopenharmony_ci	if (ret)
3162306a36Sopenharmony_ci		return ret;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	return 0;
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int mtk_set_standby(struct gnss_serial *gserial)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct mtk_data *data = gnss_serial_get_drvdata(gserial);
3962306a36Sopenharmony_ci	int ret;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	ret = regulator_disable(data->vcc);
4262306a36Sopenharmony_ci	if (ret)
4362306a36Sopenharmony_ci		return ret;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return 0;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int mtk_set_power(struct gnss_serial *gserial,
4962306a36Sopenharmony_ci			 enum gnss_serial_pm_state state)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	switch (state) {
5262306a36Sopenharmony_ci	case GNSS_SERIAL_ACTIVE:
5362306a36Sopenharmony_ci		return mtk_set_active(gserial);
5462306a36Sopenharmony_ci	case GNSS_SERIAL_OFF:
5562306a36Sopenharmony_ci	case GNSS_SERIAL_STANDBY:
5662306a36Sopenharmony_ci		return mtk_set_standby(gserial);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return -EINVAL;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const struct gnss_serial_ops mtk_gserial_ops = {
6362306a36Sopenharmony_ci	.set_power = mtk_set_power,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int mtk_probe(struct serdev_device *serdev)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct gnss_serial *gserial;
6962306a36Sopenharmony_ci	struct mtk_data *data;
7062306a36Sopenharmony_ci	int ret;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	gserial = gnss_serial_allocate(serdev, sizeof(*data));
7362306a36Sopenharmony_ci	if (IS_ERR(gserial)) {
7462306a36Sopenharmony_ci		ret = PTR_ERR(gserial);
7562306a36Sopenharmony_ci		return ret;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	gserial->ops = &mtk_gserial_ops;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	gserial->gdev->type = GNSS_TYPE_MTK;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	data = gnss_serial_get_drvdata(gserial);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	data->vcc = devm_regulator_get(&serdev->dev, "vcc");
8562306a36Sopenharmony_ci	if (IS_ERR(data->vcc)) {
8662306a36Sopenharmony_ci		ret = PTR_ERR(data->vcc);
8762306a36Sopenharmony_ci		goto err_free_gserial;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	data->vbackup = devm_regulator_get_optional(&serdev->dev, "vbackup");
9162306a36Sopenharmony_ci	if (IS_ERR(data->vbackup)) {
9262306a36Sopenharmony_ci		ret = PTR_ERR(data->vbackup);
9362306a36Sopenharmony_ci		if (ret == -ENODEV)
9462306a36Sopenharmony_ci			data->vbackup = NULL;
9562306a36Sopenharmony_ci		else
9662306a36Sopenharmony_ci			goto err_free_gserial;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (data->vbackup) {
10062306a36Sopenharmony_ci		ret = regulator_enable(data->vbackup);
10162306a36Sopenharmony_ci		if (ret)
10262306a36Sopenharmony_ci			goto err_free_gserial;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	ret = gnss_serial_register(gserial);
10662306a36Sopenharmony_ci	if (ret)
10762306a36Sopenharmony_ci		goto err_disable_vbackup;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cierr_disable_vbackup:
11262306a36Sopenharmony_ci	if (data->vbackup)
11362306a36Sopenharmony_ci		regulator_disable(data->vbackup);
11462306a36Sopenharmony_cierr_free_gserial:
11562306a36Sopenharmony_ci	gnss_serial_free(gserial);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return ret;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void mtk_remove(struct serdev_device *serdev)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct gnss_serial *gserial = serdev_device_get_drvdata(serdev);
12362306a36Sopenharmony_ci	struct mtk_data *data = gnss_serial_get_drvdata(gserial);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	gnss_serial_deregister(gserial);
12662306a36Sopenharmony_ci	if (data->vbackup)
12762306a36Sopenharmony_ci		regulator_disable(data->vbackup);
12862306a36Sopenharmony_ci	gnss_serial_free(gserial);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#ifdef CONFIG_OF
13262306a36Sopenharmony_cistatic const struct of_device_id mtk_of_match[] = {
13362306a36Sopenharmony_ci	{ .compatible = "globaltop,pa6h" },
13462306a36Sopenharmony_ci	{},
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_of_match);
13762306a36Sopenharmony_ci#endif
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic struct serdev_device_driver mtk_driver = {
14062306a36Sopenharmony_ci	.driver	= {
14162306a36Sopenharmony_ci		.name		= "gnss-mtk",
14262306a36Sopenharmony_ci		.of_match_table	= of_match_ptr(mtk_of_match),
14362306a36Sopenharmony_ci		.pm		= &gnss_serial_pm_ops,
14462306a36Sopenharmony_ci	},
14562306a36Sopenharmony_ci	.probe	= mtk_probe,
14662306a36Sopenharmony_ci	.remove	= mtk_remove,
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_cimodule_serdev_device_driver(mtk_driver);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ciMODULE_AUTHOR("Loys Ollivier <lollivier@baylibre.com>");
15162306a36Sopenharmony_ciMODULE_DESCRIPTION("Mediatek GNSS receiver driver");
15262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
153