blob: b553dc0f6eb2ec3f543695fc7c319a16e22759aa [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * rfkill power contorl for wlan/bt on ASR platform
3 *
4 * Copyright (C) 2018 ASR, Inc.
5 *
6 * Authors: Yi Zhang <yizhang@marvell.com>
7
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <linux/debugfs.h>
25#include <linux/device.h>
26#include <linux/module.h>
27#include <linux/err.h>
28#include <linux/slab.h>
29#include <linux/delay.h>
30#include <linux/platform_device.h>
31#include <linux/pm_runtime.h>
32#include <linux/suspend.h>
33#include <linux/clk.h>
34#include <linux/of_device.h>
35#include <linux/of_gpio.h>
36#include <linux/of_platform.h>
37#include <linux/edge_wakeup_mmp.h>
38
39#define ASR_DEV_NAME "asr-gnss"
40
41#define GPS_STATUS_LENS 16
42struct asr_gnss_platform_data {
43 char chip_status[GPS_STATUS_LENS];
44 int gpio_reset;
45 int gpio_edge_wakeup;
46 int gpio_wakeup_device;
47 struct clk *clk_26m_out;
48};
49
50
51
52static DEFINE_MUTEX(asr_gnss_opt_mutex);
53
54static void gnss_edge_wakeup(int irq, void *p_rsv)
55{
56 pr_debug("gnss_edge_wakeup event +++++\n");
57}
58
59static void asr_gnss_platform_data_init(
60 struct asr_gnss_platform_data *pdata)
61{
62 /* all intems are invalid just after alloc */
63
64 pdata->gpio_reset = -1;
65 pdata->gpio_edge_wakeup = -1;
66 pdata->gpio_wakeup_device = -1;
67
68 memset(pdata->chip_status, 0, GPS_STATUS_LENS);
69}
70
71static struct asr_gnss_platform_data
72 *asr_gnss_platform_data_alloc(struct platform_device *pdev)
73{
74 struct asr_gnss_platform_data *pdata;
75
76 /* create a new one and init it */
77 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
78 if (pdata) {
79 asr_gnss_platform_data_init(pdata);
80 return pdata;
81 }
82
83 return NULL;
84}
85
86/* GPS: power on/off control */
87static void gps_power_on(struct device *dev)
88{
89 struct asr_gnss_platform_data *info = dev->platform_data;
90 if (info->gpio_edge_wakeup >= 0) {
91 request_mfp_edge_wakeup(info->gpio_edge_wakeup,
92 gnss_edge_wakeup, NULL, dev);
93 }
94
95 pr_debug("gps chip powered on\n");
96 return;
97}
98
99static void gps_power_off(struct asr_gnss_platform_data *info)
100{
101 pr_debug("gps chip powered off\n");
102 return;
103}
104
105static int gps_reset(struct asr_gnss_platform_data *info, int flag)
106{
107 int gpio_reset = info->gpio_reset;
108
109 if (gpio_reset < 0)
110 return -1;
111
112 if (gpio_request(gpio_reset, "asr5311 rst")) {
113 pr_info("gpio %d request failed\n", gpio_reset);
114 return -1;
115 }
116
117 gpio_direction_output(gpio_reset, flag);
118
119 gpio_free(gpio_reset);
120
121 return 0;
122}
123
124static int gps_wakeup(struct asr_gnss_platform_data *info, int flag)
125{
126 int host_wakeup = info->gpio_wakeup_device;
127
128 if (host_wakeup < 0)
129 return -1;
130
131 if (gpio_request(host_wakeup, "host wakeup asr5311")) {
132 pr_info("gpio %d request failed\n", host_wakeup);
133 return -1;
134 }
135
136 gpio_direction_output(host_wakeup, flag);
137
138 gpio_free(host_wakeup);
139
140 return 0;
141}
142
143static ssize_t gps_ctrl(struct device *dev,
144 struct device_attribute *attr,
145 const char *buf, size_t count)
146{
147 static char msg[256];
148 int flag, ret;
149
150 struct asr_gnss_platform_data *info = dev->platform_data;
151
152 count = (count > 255) ? 255 : count;
153 memset(msg, 0, count);
154
155 sscanf(buf, "%s", info->chip_status);
156 if (!strncmp(buf, "off", 3)) {
157 strncpy(info->chip_status, "off", 4);
158 gps_power_off(info);
159 } else if (!strncmp(buf, "on", 2)) {
160 strncpy(info->chip_status, "on", 3);
161 gps_power_on(dev);
162 } else if (!strncmp(buf, "reset", 5)) {
163 strncpy(info->chip_status, "reset", 6);
164 ret = sscanf(buf, "%s %d", msg, &flag);
165 if (ret == 2)
166 gps_reset(info, flag);
167 } else if (!strncmp(buf, "wakeup", 6)) {
168 strncpy(info->chip_status, "wakeup", 7);
169 ret = sscanf(buf, "%s %d", msg, &flag);
170 if (ret == 2)
171 gps_wakeup(info, flag);
172 } else
173 pr_err("usage wrong\n");
174
175 return count;
176}
177
178static ssize_t gps_status(struct device *dev,
179 struct device_attribute *attr, char *buf)
180{
181 struct asr_gnss_platform_data *info = dev->platform_data;
182
183 return sprintf(buf, "status: %s\n", info->chip_status);
184}
185
186static DEVICE_ATTR(ctrl, S_IWUSR, gps_status, gps_ctrl);
187static DEVICE_ATTR(status, S_IRUSR, gps_status, NULL);
188
189static const struct attribute *gps_attrs[] = {
190 &dev_attr_ctrl.attr,
191 &dev_attr_status.attr,
192 NULL,
193};
194
195static const struct attribute_group gps_attr_group = {
196 .attrs = (struct attribute **)gps_attrs,
197};
198
199
200#ifdef CONFIG_OF
201static const struct of_device_id asr_gnss_of_match[] = {
202 {
203 .compatible = "asr,asr-gnss",
204 },
205 {},
206};
207
208MODULE_DEVICE_TABLE(of, asr_gnss_of_match);
209
210static int asr_gnss_probe_dt(struct platform_device *pdev)
211{
212 struct device_node *np = pdev->dev.of_node;
213 int gpio = 0;
214 struct asr_gnss_platform_data *pdata = pdev->dev.platform_data;
215
216 /* get gpios from dt */
217 gpio = of_get_named_gpio(np, "edge-wakeup-gpio", 0);
218 if (unlikely(gpio < 0)) {
219 dev_warn(&pdev->dev, "edge-wakeup-gpio undefined\n");
220 pdata->gpio_edge_wakeup = -1;
221 } else {
222 pdata->gpio_edge_wakeup = gpio;
223 }
224
225 gpio = of_get_named_gpio(np, "rst-gpio", 0);
226 if (unlikely(gpio < 0)) {
227 dev_err(&pdev->dev, "rst-gpio undefined\n");
228 pdata->gpio_reset = -1;
229 } else {
230 pdata->gpio_reset = gpio;
231 }
232
233 gpio = of_get_named_gpio(np, "host-wakeup-gnss-gpio", 0);
234 if (unlikely(gpio < 0)) {
235 dev_err(&pdev->dev, "host-wakeup-wlan-gpio undefined\n");
236 } else {
237 pdata->gpio_wakeup_device = gpio;
238 pr_info("%s: host_wakeup_wlan_gpio %d\n", __func__, gpio);
239 }
240
241 return 0;
242}
243#else
244static int asr_gnss_probe_dt(struct platform_device *pdev)
245{
246 return 0;
247}
248#endif
249
250static int asr_gnss_probe(struct platform_device *pdev)
251{
252 struct asr_gnss_platform_data *pdata = NULL;
253 /* flag: whether pdata is passed from platfrom_data */
254 int pdata_passed = 1;
255 struct device *dev = &pdev->dev;
256 const struct of_device_id *match = NULL;
257 int ret = -1;
258
259 pr_err("asr_gnss_probe!!!!");
260 /* make sure asr_rfkill_platform_data is valid */
261 pdata = pdev->dev.platform_data;
262 if (!pdata) {
263 /* if platfrom data do not pass the struct to us */
264 pdata_passed = 0;
265
266 pdata = asr_gnss_platform_data_alloc(pdev);
267 if (!pdata) {
268 pr_err("can't get asr_gnss_platform_data struct during probe\n");
269 goto err_pdata;
270 }
271
272 pdev->dev.platform_data = pdata;
273 }
274
275 pdata->clk_26m_out = devm_clk_get(dev, "gnss_26m");
276 if (!IS_ERR(pdata->clk_26m_out)) {
277 dev_info(dev, "enable 26M output for wifi\n");
278 clk_prepare_enable(pdata->clk_26m_out);
279 }
280 /* set value to asr_rfkill_platform_data if DT pass them to us */
281#ifdef CONFIG_OF
282 match = of_match_device(of_match_ptr(asr_gnss_of_match),
283 &pdev->dev);
284#endif
285 if (match) {
286 ret = asr_gnss_probe_dt(pdev);
287 if (ret)
288 goto err_dt;
289 }
290
291 ret = sysfs_create_group(&pdev->dev.kobj, &gps_attr_group);
292 if (ret) {
293 dev_err(&pdev->dev, "GPS create sysfs fail!\n");
294 return ret;
295 }
296 return 0;
297
298err_dt:
299 if (!pdata_passed)
300 pdev->dev.platform_data = NULL;
301err_pdata:
302
303 return ret;
304}
305
306static int asr_gnss_remove(struct platform_device *pdev)
307{
308 struct asr_gnss_platform_data *pdata;
309 pdata = pdev->dev.platform_data;
310
311 if (!IS_ERR(pdata->clk_26m_out))
312 clk_disable_unprepare(pdata->clk_26m_out);
313
314 return 0;
315}
316
317static int asr_gnss_suspend(struct platform_device *pdev,
318 pm_message_t pm_state)
319{
320 return 0;
321}
322
323static int asr_gnss_resume(struct platform_device *pdev)
324{
325 return 0;
326}
327
328static struct platform_driver asr_gnss_platform_driver = {
329 .probe = asr_gnss_probe,
330 .remove = asr_gnss_remove,
331 .driver = {
332 .name = ASR_DEV_NAME,
333 .owner = THIS_MODULE,
334#ifdef CONFIG_OF
335 .of_match_table = asr_gnss_of_match,
336#endif
337
338 },
339 .suspend = asr_gnss_suspend,
340 .resume = asr_gnss_resume,
341};
342
343static int __init asr_gnss_init(void)
344{
345 return platform_driver_register(&asr_gnss_platform_driver);
346}
347
348static void __exit asr_gnss_exit(void)
349{
350 platform_driver_unregister(&asr_gnss_platform_driver);
351}
352
353module_init(asr_gnss_init);
354module_exit(asr_gnss_exit);
355
356MODULE_ALIAS("platform:asr_gps");
357MODULE_DESCRIPTION("asr_gps");
358MODULE_AUTHOR("ASR");
359MODULE_LICENSE("GPL V2");