xref: /OK3568_Linux_fs/kernel/drivers/net/lte/lte_rm310.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019, Fuzhou Rockchip Electronics Co., Ltd.
4  *
5  * Rockchip rm310 driver for 4g modem
6  */
7 
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/kthread.h>
11 #include <linux/i2c.h>
12 #include <linux/irq.h>
13 #include <linux/gpio.h>
14 #include <linux/input.h>
15 #include <linux/platform_device.h>
16 #include <linux/fs.h>
17 #include <linux/uaccess.h>
18 #include <linux/miscdevice.h>
19 #include <linux/circ_buf.h>
20 #include <linux/miscdevice.h>
21 #include <linux/gpio.h>
22 #include <dt-bindings/gpio/gpio.h>
23 #include <linux/delay.h>
24 #include <linux/poll.h>
25 #include <linux/wait.h>
26 #include <linux/wakelock.h>
27 #include <linux/workqueue.h>
28 #include <linux/lte.h>
29 #include <linux/slab.h>
30 #include <linux/of.h>
31 #include <linux/of_device.h>
32 #include <linux/of_gpio.h>
33 
34 #define LOG(x...)   pr_info("[4g_modem]: " x)
35 
36 static struct lte_data *gpdata;
37 static struct class *modem_class;
38 static int modem_status = 1;
39 
modem_poweron_off(int on_off)40 static int modem_poweron_off(int on_off)
41 {
42 	struct lte_data *pdata = gpdata;
43 
44 	if (pdata) {
45 		if (on_off) {
46 			LOG("%s: 4g modem power up.\n", __func__);
47 			if (pdata->vbat_gpio) {
48 				gpiod_direction_output(pdata->vbat_gpio, 1);
49 				msleep(2000);
50 			}
51 			if (pdata->power_gpio) {
52 				gpiod_direction_output(pdata->power_gpio, 0);
53 				msleep(50);
54 				gpiod_direction_output(pdata->power_gpio, 1);
55 				msleep(400);
56 				gpiod_direction_output(pdata->power_gpio, 0);
57 			}
58 		} else {
59 			LOG("%s: 4g modem power down.\n", __func__);
60 			if (pdata->power_gpio) {
61 				gpiod_direction_output(pdata->power_gpio, 0);
62 				msleep(100);
63 				gpiod_direction_output(pdata->power_gpio, 1);
64 				msleep(1000);
65 				gpiod_direction_output(pdata->power_gpio, 0);
66 				msleep(400);
67 			}
68 			if (pdata->vbat_gpio)
69 				gpiod_direction_output(pdata->vbat_gpio, 0);
70 		}
71 	}
72 	return 0;
73 }
74 
modem_status_store(struct class * cls,struct class_attribute * attr,const char * buf,size_t count)75 static ssize_t modem_status_store(struct class *cls,
76 				  struct class_attribute *attr,
77 				  const char *buf, size_t count)
78 {
79 	int new_state, ret;
80 
81 	ret = kstrtoint(buf, 10, &new_state);
82 	if (ret) {
83 		LOG("%s: kstrtoint error return %d\n", __func__, ret);
84 		return ret;
85 	}
86 	if (new_state == modem_status)
87 		return count;
88 	if (new_state == 1) {
89 		LOG("%s, c(%d), open modem.\n", __func__, new_state);
90 		modem_poweron_off(1);
91 	} else if (new_state == 0) {
92 		LOG("%s, c(%d), close modem.\n", __func__, new_state);
93 		modem_poweron_off(0);
94 	} else {
95 		LOG("%s, invalid parameter.\n", __func__);
96 	}
97 	modem_status = new_state;
98 	return count;
99 }
100 
modem_status_show(struct class * cls,struct class_attribute * attr,char * buf)101 static ssize_t modem_status_show(struct class *cls,
102 				 struct class_attribute *attr,
103 				 char *buf)
104 {
105 	return sprintf(buf, "%d\n", modem_status);
106 }
107 
108 static CLASS_ATTR_RW(modem_status);
109 
modem_platdata_parse_dt(struct device * dev,struct lte_data * data)110 static int modem_platdata_parse_dt(struct device *dev,
111 				   struct lte_data *data)
112 {
113 	struct device_node *node = dev->of_node;
114 	int ret;
115 
116 	if (!node)
117 		return -ENODEV;
118 	memset(data, 0, sizeof(*data));
119 	LOG("%s: LTE modem power controlled by gpio.\n", __func__);
120 	data->vbat_gpio = devm_gpiod_get_optional(dev, "4G,vbat",
121 						  GPIOD_OUT_HIGH);
122 	if (IS_ERR(data->vbat_gpio)) {
123 		ret = PTR_ERR(data->vbat_gpio);
124 		dev_err(dev, "failed to request 4G,vbat GPIO: %d\n", ret);
125 		return ret;
126 	}
127 	data->power_gpio = devm_gpiod_get_optional(dev, "4G,power",
128 						   GPIOD_OUT_HIGH);
129 	if (IS_ERR(data->power_gpio)) {
130 		ret = PTR_ERR(data->power_gpio);
131 		dev_err(dev, "failed to request 4G,power GPIO: %d\n", ret);
132 		return ret;
133 	}
134 	data->reset_gpio = devm_gpiod_get_optional(dev, "4G,reset",
135 						   GPIOD_OUT_LOW);
136 	if (IS_ERR(data->reset_gpio)) {
137 		ret = PTR_ERR(data->reset_gpio);
138 		dev_err(dev, "failed to request 4G,reset GPIO: %d\n", ret);
139 		return ret;
140 	}
141 	return 0;
142 }
143 
modem_power_on_thread(void * data)144 static int modem_power_on_thread(void *data)
145 {
146 	modem_poweron_off(1);
147 	return 0;
148 }
149 
lte_probe(struct platform_device * pdev)150 static int lte_probe(struct platform_device *pdev)
151 {
152 	struct lte_data *pdata;
153 	struct task_struct *kthread;
154 	int ret = -1;
155 
156 	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
157 	if (!pdata)
158 		return -ENOMEM;
159 	ret = modem_platdata_parse_dt(&pdev->dev, pdata);
160 	if (ret < 0) {
161 		LOG("%s: No lte platform data specified\n", __func__);
162 		goto err;
163 	}
164 	gpdata = pdata;
165 	pdata->dev = &pdev->dev;
166 
167 	if (pdata->reset_gpio)
168 		gpiod_direction_output(pdata->reset_gpio, 0);
169 	kthread = kthread_run(modem_power_on_thread, NULL,
170 			      "modem_power_on_thread");
171 	if (IS_ERR(kthread)) {
172 		LOG("%s: create modem_power_on_thread failed.\n",  __func__);
173 		ret = PTR_ERR(kthread);
174 		goto err;
175 	}
176 	return 0;
177 err:
178 	gpdata = NULL;
179 	return ret;
180 }
181 
lte_suspend(struct platform_device * pdev,pm_message_t state)182 static int lte_suspend(struct platform_device *pdev, pm_message_t state)
183 {
184 	return 0;
185 }
186 
lte_resume(struct platform_device * pdev)187 static int lte_resume(struct platform_device *pdev)
188 {
189 	return 0;
190 }
191 
lte_remove(struct platform_device * pdev)192 static int lte_remove(struct platform_device *pdev)
193 {
194 	struct lte_data *pdata = gpdata;
195 
196 	if (pdata->power_gpio) {
197 		msleep(100);
198 		gpiod_direction_output(pdata->power_gpio, 1);
199 		msleep(750);
200 		gpiod_direction_output(pdata->power_gpio, 0);
201 	}
202 	if (pdata->vbat_gpio)
203 		gpiod_direction_output(pdata->vbat_gpio, 0);
204 	gpdata = NULL;
205 	return 0;
206 }
207 
208 static const struct of_device_id modem_platdata_of_match[] = {
209 	{ .compatible = "4g-modem-platdata" },
210 	{ }
211 };
212 MODULE_DEVICE_TABLE(of, modem_platdata_of_match);
213 
214 static struct platform_driver rm310_driver = {
215 	.probe		= lte_probe,
216 	.remove		= lte_remove,
217 	.suspend	= lte_suspend,
218 	.resume		= lte_resume,
219 	.driver	= {
220 		.name	= "4g-modem-platdata",
221 		.of_match_table = of_match_ptr(modem_platdata_of_match),
222 	},
223 };
224 
rm310_init(void)225 static int __init rm310_init(void)
226 {
227 	int ret;
228 
229 	modem_class = class_create(THIS_MODULE, "rk_modem");
230 	ret =  class_create_file(modem_class, &class_attr_modem_status);
231 	if (ret)
232 		LOG("Fail to create class modem_status.\n");
233 	return platform_driver_register(&rm310_driver);
234 }
235 
rm310_exit(void)236 static void __exit rm310_exit(void)
237 {
238 	platform_driver_unregister(&rm310_driver);
239 }
240 
241 late_initcall(rm310_init);
242 module_exit(rm310_exit);
243 
244 MODULE_AUTHOR("xuxuehui <xxh@rock-chips.com>");
245 MODULE_AUTHOR("Alex Zhao <zzc@rock-chips.com>");
246 MODULE_DESCRIPTION("RM310 lte modem driver");
247 MODULE_LICENSE("GPL");
248