18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Mediatek GNSS receiver driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Johan Hovold <johan@kernel.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/gnss.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/serdev.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "serial.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct mtk_data { 208c2ecf20Sopenharmony_ci struct regulator *vbackup; 218c2ecf20Sopenharmony_ci struct regulator *vcc; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int mtk_set_active(struct gnss_serial *gserial) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct mtk_data *data = gnss_serial_get_drvdata(gserial); 278c2ecf20Sopenharmony_ci int ret; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci ret = regulator_enable(data->vcc); 308c2ecf20Sopenharmony_ci if (ret) 318c2ecf20Sopenharmony_ci return ret; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return 0; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int mtk_set_standby(struct gnss_serial *gserial) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct mtk_data *data = gnss_serial_get_drvdata(gserial); 398c2ecf20Sopenharmony_ci int ret; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci ret = regulator_disable(data->vcc); 428c2ecf20Sopenharmony_ci if (ret) 438c2ecf20Sopenharmony_ci return ret; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int mtk_set_power(struct gnss_serial *gserial, 498c2ecf20Sopenharmony_ci enum gnss_serial_pm_state state) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci switch (state) { 528c2ecf20Sopenharmony_ci case GNSS_SERIAL_ACTIVE: 538c2ecf20Sopenharmony_ci return mtk_set_active(gserial); 548c2ecf20Sopenharmony_ci case GNSS_SERIAL_OFF: 558c2ecf20Sopenharmony_ci case GNSS_SERIAL_STANDBY: 568c2ecf20Sopenharmony_ci return mtk_set_standby(gserial); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return -EINVAL; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const struct gnss_serial_ops mtk_gserial_ops = { 638c2ecf20Sopenharmony_ci .set_power = mtk_set_power, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int mtk_probe(struct serdev_device *serdev) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct gnss_serial *gserial; 698c2ecf20Sopenharmony_ci struct mtk_data *data; 708c2ecf20Sopenharmony_ci int ret; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci gserial = gnss_serial_allocate(serdev, sizeof(*data)); 738c2ecf20Sopenharmony_ci if (IS_ERR(gserial)) { 748c2ecf20Sopenharmony_ci ret = PTR_ERR(gserial); 758c2ecf20Sopenharmony_ci return ret; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci gserial->ops = &mtk_gserial_ops; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci gserial->gdev->type = GNSS_TYPE_MTK; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci data = gnss_serial_get_drvdata(gserial); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci data->vcc = devm_regulator_get(&serdev->dev, "vcc"); 858c2ecf20Sopenharmony_ci if (IS_ERR(data->vcc)) { 868c2ecf20Sopenharmony_ci ret = PTR_ERR(data->vcc); 878c2ecf20Sopenharmony_ci goto err_free_gserial; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci data->vbackup = devm_regulator_get_optional(&serdev->dev, "vbackup"); 918c2ecf20Sopenharmony_ci if (IS_ERR(data->vbackup)) { 928c2ecf20Sopenharmony_ci ret = PTR_ERR(data->vbackup); 938c2ecf20Sopenharmony_ci if (ret == -ENODEV) 948c2ecf20Sopenharmony_ci data->vbackup = NULL; 958c2ecf20Sopenharmony_ci else 968c2ecf20Sopenharmony_ci goto err_free_gserial; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (data->vbackup) { 1008c2ecf20Sopenharmony_ci ret = regulator_enable(data->vbackup); 1018c2ecf20Sopenharmony_ci if (ret) 1028c2ecf20Sopenharmony_ci goto err_free_gserial; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci ret = gnss_serial_register(gserial); 1068c2ecf20Sopenharmony_ci if (ret) 1078c2ecf20Sopenharmony_ci goto err_disable_vbackup; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cierr_disable_vbackup: 1128c2ecf20Sopenharmony_ci if (data->vbackup) 1138c2ecf20Sopenharmony_ci regulator_disable(data->vbackup); 1148c2ecf20Sopenharmony_cierr_free_gserial: 1158c2ecf20Sopenharmony_ci gnss_serial_free(gserial); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return ret; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void mtk_remove(struct serdev_device *serdev) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct gnss_serial *gserial = serdev_device_get_drvdata(serdev); 1238c2ecf20Sopenharmony_ci struct mtk_data *data = gnss_serial_get_drvdata(gserial); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci gnss_serial_deregister(gserial); 1268c2ecf20Sopenharmony_ci if (data->vbackup) 1278c2ecf20Sopenharmony_ci regulator_disable(data->vbackup); 1288c2ecf20Sopenharmony_ci gnss_serial_free(gserial); 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 1328c2ecf20Sopenharmony_cistatic const struct of_device_id mtk_of_match[] = { 1338c2ecf20Sopenharmony_ci { .compatible = "globaltop,pa6h" }, 1348c2ecf20Sopenharmony_ci {}, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_of_match); 1378c2ecf20Sopenharmony_ci#endif 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic struct serdev_device_driver mtk_driver = { 1408c2ecf20Sopenharmony_ci .driver = { 1418c2ecf20Sopenharmony_ci .name = "gnss-mtk", 1428c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mtk_of_match), 1438c2ecf20Sopenharmony_ci .pm = &gnss_serial_pm_ops, 1448c2ecf20Sopenharmony_ci }, 1458c2ecf20Sopenharmony_ci .probe = mtk_probe, 1468c2ecf20Sopenharmony_ci .remove = mtk_remove, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_cimodule_serdev_device_driver(mtk_driver); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciMODULE_AUTHOR("Loys Ollivier <lollivier@baylibre.com>"); 1518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mediatek GNSS receiver driver"); 1528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 153