blob: cf18367dee3dc5fe08737a5bdb33650cc0a51cbc [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * ASRMicro SLIC driver
3 *
4 * Copyright (C) 2020 ASR Microelectronic Ltd.
5 *
6 * Author: wen Chen@asrmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 */
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/moduleparam.h>
20#include <linux/init.h>
21#include <linux/gpio.h>
22#include <linux/of.h>
23#include <linux/of_gpio.h>
24#include <linux/delay.h>
25#include <linux/regulator/machine.h>
26#include <linux/platform_device.h>
27#include <linux/pinctrl/consumer.h>
28#include <linux/edge_wakeup_mmp.h>
29#include <linux/mfd/88pm80x.h>
30
31#define SLIC_STATUS_LENS 8
32
33static int g_reset_n_gpio = -1;
34static int g_edge_wakeup_gpio = -1;
35static int g_vdd_3v3_gpio = -1;
36
37struct maxlinear_slic_info {
38 struct device *dev;
39 char chip_status[SLIC_STATUS_LENS];
40 char reset_status[SLIC_STATUS_LENS];
41 int reset_n_gpio;
42 int edge_wakeup_gpio;
43 int vdd_3v3_gpio;
44 struct pinctrl *pinctrl;
45 struct pinctrl_state *pin_lpm_drv_low;
46 struct pinctrl_state *pin_lpm_drv_high;
47 struct regulator *vdd_3v3;
48};
49
50static void slic_power_on(struct maxlinear_slic_info *info)
51{
52 if (!strncmp(info->chip_status, "on", 2)) {
53 dev_info(info->dev, "slic chip already powered on\n");
54 return;
55 }
56
57 if (info->vdd_3v3 > 0) {
58 if (regulator_set_voltage(info->vdd_3v3, 3300000, 3300000))
59 pr_err("fail to set regulator wib_3v3 supply 2 to 3.3v\n");
60 if (regulator_enable(info->vdd_3v3))
61 pr_err("fail to enable regulator vdd_3v3\n");
62 }
63
64 if (info->vdd_3v3_gpio >= 0) {
65 gpio_direction_output(info->vdd_3v3_gpio, 1);
66 }
67
68 usleep_range(65, 70);
69
70 if (info->reset_n_gpio >= 0) {
71 gpio_direction_output(info->reset_n_gpio, 1);
72 strncpy(info->reset_status, "high", 5);
73 }
74
75 strncpy(info->chip_status, "on", 3);
76
77 if (info->vdd_3v3 > 0) {
78 buck2_ldo8_sleepmode_control_for_slic(1);
79 }
80
81 dev_info(info->dev, "slic chip powered on\n");
82}
83
84static void slic_power_off(struct maxlinear_slic_info *info)
85{
86 if (!strncmp(info->chip_status, "off", 3)) {
87 dev_info(info->dev, "slic chip already powered off\n");
88 return;
89 }
90
91 if (info->vdd_3v3 > 0) {
92 if (regulator_disable(info->vdd_3v3))
93 pr_err("fail to disable regulator vdd_3v3\n");
94 }
95
96 if (info->vdd_3v3_gpio >= 0) {
97 gpio_direction_output(info->vdd_3v3_gpio, 0);
98 }
99
100 if (info->reset_n_gpio >= 0) {
101 gpio_direction_output(info->reset_n_gpio, 0);
102 strncpy(info->reset_status, "low", 4);
103 }
104
105 strncpy(info->chip_status, "off", 4);
106
107 if (info->vdd_3v3 > 0) {
108 buck2_ldo8_sleepmode_control_for_slic(0);
109 }
110
111 dev_info(info->dev, "slic chip powered off\n");
112}
113
114static void slic_reset(struct maxlinear_slic_info *info, int flag)
115{
116 if (info->reset_n_gpio >= 0) {
117 gpio_direction_output(info->reset_n_gpio, flag);
118 sprintf(info->reset_status, "%s", flag ? "high" : "low");
119 dev_info(info->dev, "slic chip reset_n set to %s\n", flag ? "high" : "low");
120 }
121}
122
123static ssize_t slic_ctrl(struct device *dev,
124 struct device_attribute *attr,
125 const char *buf, size_t count)
126{
127 static char msg[64];
128 int flag, ret;
129
130 struct maxlinear_slic_info *info = dev_get_drvdata(dev);
131
132 count = (count > 64) ? 64 : count;
133 memset(msg, 0, count);
134
135 if (!strncmp(buf, "off", 3)) {
136 slic_power_off(info);
137 } else if (!strncmp(buf, "on", 2)) {
138 slic_power_on(info);
139 } else if (!strncmp(buf, "reset", 5)) {
140 ret = sscanf(buf, "%s %d", msg, &flag);
141 if (ret == 2)
142 slic_reset(info, flag);
143 } else
144 dev_info(info->dev, "usage wrong\n");
145
146 return count;
147}
148
149static ssize_t slic_status(struct device *dev,
150 struct device_attribute *attr, char *buf)
151{
152 struct maxlinear_slic_info *info = dev_get_drvdata(dev);
153
154 return sprintf(buf, "power: %s, reset_n: %s\n", info->chip_status,
155 info->reset_status);
156}
157
158static DEVICE_ATTR(ctrl, S_IWUSR, slic_status, slic_ctrl);
159static DEVICE_ATTR(status, S_IRUSR, slic_status, NULL);
160
161static const struct attribute *slic_attrs[] = {
162 &dev_attr_ctrl.attr,
163 &dev_attr_status.attr,
164 NULL,
165};
166
167static const struct attribute_group slic_attr_group = {
168 .attrs = (struct attribute **)slic_attrs,
169};
170
171#ifdef CONFIG_OF
172static int maxlinear_slic_dt_init(struct device_node *np,
173 struct device *dev,
174 struct maxlinear_slic_info *info)
175{
176
177 struct regulator *vdd_3v3 = NULL;
178
179 info->reset_n_gpio =
180 of_get_named_gpio(dev->of_node, "rst-gpio", 0);
181 if (info->reset_n_gpio < 0) {
182 dev_err(dev, "%s: of_get_named_gpio failed: %d\n", __func__,
183 info->reset_n_gpio);
184 info->reset_n_gpio = -1;
185 }
186
187 info->edge_wakeup_gpio =
188 of_get_named_gpio(dev->of_node, "edge-wakeup-gpio", 0);
189 if (info->edge_wakeup_gpio < 0) {
190 dev_err(dev, "%s: of_get_named_gpio failed: %d\n", __func__,
191 info->edge_wakeup_gpio);
192 info->edge_wakeup_gpio = -1;
193 goto out;
194 }
195
196 info->vdd_3v3_gpio = of_get_named_gpio(dev->of_node, "vdd-3v3-gpio", 0);
197 if (info->vdd_3v3_gpio < 0) {
198 dev_err(dev, "%s: of_get_named_gpio failed: %d\n", __func__, info->vdd_3v3_gpio);
199 info->vdd_3v3_gpio = -1;
200 }
201
202 g_reset_n_gpio = info->reset_n_gpio;
203 g_edge_wakeup_gpio = info->edge_wakeup_gpio;
204 g_vdd_3v3_gpio = info->vdd_3v3_gpio;
205
206 printk(KERN_INFO"%s: reset_n_gpio=%d, edge_wakeup_gpio=%d, vdd_3v3_gpio=%d, %s.\n",
207 __FUNCTION__, info->reset_n_gpio, info->edge_wakeup_gpio, info->vdd_3v3_gpio, (info->vdd_3v3_gpio < 0)?"ASR1826 Old DKB":"ASR1826 New DKB");
208
209 printk(KERN_INFO"%s: g_reset_n_gpio=%d, g_edge_wakeup_gpio=%d, g_vdd_3v3_gpio=%d.\n",
210 __FUNCTION__, g_reset_n_gpio, g_edge_wakeup_gpio, g_vdd_3v3_gpio);
211
212 vdd_3v3 = regulator_get(dev, "vdd33");
213 if (IS_ERR_OR_NULL(vdd_3v3)) {
214 if (PTR_ERR(vdd_3v3) < 0) {
215 pr_info("%s: the regulator for vdd_3v3 not found\n", __func__);
216 }
217 } else {
218 info->vdd_3v3 = vdd_3v3;
219 }
220
221 return 0;
222out:
223 return -EINVAL;
224}
225#else
226static int maxlinear_slic_dt_init(struct device_node *np,
227 struct device *dev,
228 struct maxlinear_slic_info *info)
229{
230 return 0;
231}
232#endif
233
234/* maxlinear_slic_ctrl */
235static struct dentry *maxlinear_slic_ctrl = NULL;
236
237static ssize_t maxlinear_slic_ctrl_read(struct file *file, char __user *user_buf,
238 size_t count, loff_t *ppos)
239{
240
241 printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
242
243 return 0;
244}
245
246/*
247 *control command:
248 *
249 *Disable VDD 3V3: echo 0 > /sys/kernel/debug/maxlinear_slic_ctrl
250 *Enable VDD 3V3: echo 1 > /sys/kernel/debug/maxlinear_slic_ctrl
251 *
252 *
253 */
254static char msg[10];
255
256static ssize_t maxlinear_slic_ctrl_write(struct file *file,
257 const char __user *user_buf,
258 size_t count, loff_t *ppos)
259{
260 int ret = 0;
261 size_t tmp_count = 0;
262
263 printk(KERN_INFO"%s/L%d.\n", __FUNCTION__, __LINE__);
264
265 memset(msg, 0x00, sizeof(msg));
266 tmp_count = count;
267
268 if (tmp_count >= sizeof(msg)){
269 tmp_count = sizeof(msg) - 1;
270 }
271
272 /* copy the content from user space to kernel space */
273 ret = copy_from_user(msg, user_buf, tmp_count);
274 if (ret){
275 printk(KERN_ALERT"copy from user fail \n");
276 return -EFAULT;
277 }
278
279 switch (msg[0]){
280 case '0':/* input command# echo 0 > /sys/kernel/debug/maxlinear_slic_ctrl */
281 printk(KERN_INFO "input %c. \n", msg[0]);
282 if (g_vdd_3v3_gpio >= 0) {
283 gpio_direction_output(g_vdd_3v3_gpio, 0);
284 }
285 printk(KERN_INFO "Disable VDD 3V3 for ASR1826 New DKB.\n");
286 break;
287
288 case '1':/* input command# echo 1 > /sys/kernel/debug/maxlinear_slic_ctrl */
289 printk(KERN_INFO "input %c. \n", msg[0]);
290 if (g_vdd_3v3_gpio >= 0) {
291 gpio_direction_output(g_vdd_3v3_gpio, 1);
292 }
293 printk(KERN_INFO "Enable VDD 3V3 for ASR1826 New DKB.\n");
294 break;
295
296 case '2':/* input command# echo 2 > /sys/kernel/debug/maxlinear_slic_ctrl */
297 printk(KERN_INFO "input %c. \n", msg[0]);
298 break;
299
300 default:/* input command# */
301 printk(KERN_INFO "input invalid. \n");
302 break;
303 }
304
305 return tmp_count;
306}
307
308static const struct file_operations maxlinear_slic_ctrl_ops = {
309 .owner = THIS_MODULE,
310 .open = simple_open,
311 .read = maxlinear_slic_ctrl_read,
312 .write = maxlinear_slic_ctrl_write,
313};
314
315static inline int maxlinear_slic_ctrl_debugfs_init(void)
316{
317
318 maxlinear_slic_ctrl = debugfs_create_file("maxlinear_slic_ctrl", S_IRUGO | S_IFREG,
319 NULL, NULL, &maxlinear_slic_ctrl_ops);
320
321 if (maxlinear_slic_ctrl == NULL) {
322 pr_err("create maxlinear_slic_ctrl debugfs error!\n");
323 return -ENOENT;
324 } else if (maxlinear_slic_ctrl == ERR_PTR(-ENODEV)) {
325 pr_err("CONFIG_DEBUG_FS is not enabled!\n");
326 return -ENOENT;
327 }
328
329 return 0;
330}
331
332static void maxlinear_slic_ctrl_debugfs_remove(void)
333{
334 if (NULL != maxlinear_slic_ctrl){
335 debugfs_remove_recursive(maxlinear_slic_ctrl);
336 }
337
338 return;
339}
340
341static int maxlinear_slic_probe(struct platform_device *pdev)
342{
343 struct maxlinear_slic_info *info;
344
345 struct device_node *np = pdev->dev.of_node;
346 int ret;
347
348 printk(KERN_INFO "enter %s. \n", __FUNCTION__);
349
350 info = devm_kzalloc(&pdev->dev, sizeof(struct maxlinear_slic_info),
351 GFP_KERNEL);
352 if (!info)
353 return -ENOMEM;
354
355 memset(info, 0x00, sizeof(struct maxlinear_slic_info));
356 info->reset_n_gpio = -1;
357 info->edge_wakeup_gpio = -1;
358 info->vdd_3v3_gpio = -1;
359
360 if (IS_ENABLED(CONFIG_OF)) {
361 ret = maxlinear_slic_dt_init(np, &pdev->dev, info);
362 if (ret) {
363 dev_err(&pdev->dev, "SLIC probe failed!\n");
364 return ret;
365 }
366 } else {
367 dev_err(&pdev->dev, "SLIC Not support DT, exit!\n");
368 return -EINVAL;
369 }
370
371 info->dev = &pdev->dev;
372
373 if (info->reset_n_gpio >= 0) {
374 ret = devm_gpio_request(info->dev,
375 info->reset_n_gpio, "slic_rst");
376 if (ret) {
377 dev_err(info->dev,
378 "request gpio %d failed\n", info->reset_n_gpio);
379 return ret;
380 }
381 }
382
383 /* use maxlinear-slic as wakeup source */
384 device_init_wakeup(&pdev->dev, 1);
385
386 if (info->edge_wakeup_gpio >= 0) {
387 ret = request_mfp_edge_wakeup(info->edge_wakeup_gpio,
388 NULL, info, info->dev);
389 if (ret) {
390 dev_err(info->dev, "failed to request edge wakeup.\n");
391 remove_mfp_edge_wakeup(info->edge_wakeup_gpio);
392 return ret;
393 }
394 }
395
396 if (info->vdd_3v3_gpio >= 0) {
397 ret = devm_gpio_request(info->dev, info->vdd_3v3_gpio, "vdd_3v3");
398 if (ret) {
399 dev_err(info->dev, "request gpio %d failed\n", info->vdd_3v3_gpio);
400 return ret;
401 }
402 }
403
404 slic_power_on(info);
405 strncpy(info->chip_status, "on", 3);
406
407 platform_set_drvdata(pdev, info);
408
409 ret = sysfs_create_group(&pdev->dev.kobj, &slic_attr_group);
410 if (ret) {
411 dev_err(&pdev->dev, "SLIC create sysfs fail!\n");
412 return ret;
413 }
414
415 /* init debug tool */
416 maxlinear_slic_ctrl_debugfs_init();
417 return 0;
418}
419
420static int maxlinear_slic_remove(struct platform_device *pdev)
421{
422 printk(KERN_INFO "enter %s. \n", __FUNCTION__);
423
424 sysfs_remove_group(&pdev->dev.kobj, &slic_attr_group);
425
426 maxlinear_slic_ctrl_debugfs_remove();
427 return 0;
428}
429
430#ifdef CONFIG_OF
431static const struct of_device_id maxlinear_slic_dt_match[] = {
432 { .compatible = "asr,maxlinear-slic", },
433 {},
434};
435#endif
436
437static struct platform_driver maxlinear_slic_driver = {
438 .driver = {
439 .name = "maxlinear-slic",
440 .owner = THIS_MODULE,
441#ifdef CONFIG_OF
442 .of_match_table = of_match_ptr(maxlinear_slic_dt_match),
443#endif
444 },
445 .probe = maxlinear_slic_probe,
446 .remove = maxlinear_slic_remove,
447};
448
449static int maxlinear_slic_init(void)
450{
451 return platform_driver_register(&maxlinear_slic_driver);
452}
453
454static void maxlinear_slic_exit(void)
455{
456 platform_driver_unregister(&maxlinear_slic_driver);
457}
458module_init(maxlinear_slic_init);
459module_exit(maxlinear_slic_exit);
460
461MODULE_AUTHOR("wen Chen@asrmicro.com>");
462MODULE_DESCRIPTION("driver for Maxlinear SLIC solution");
463MODULE_LICENSE("GPL v2");