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