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