1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
4 *
5 */
6 #include <linux/delay.h>
7 #include <linux/gpio/consumer.h>
8 #include <linux/module.h>
9 #include <linux/of_address.h>
10 #include <linux/platform_device.h>
11 #include <linux/seq_file.h>
12 #include <linux/proc_fs.h>
13
14 struct aschip_pir_drv_data {
15 struct device *dev;
16 struct gpio_desc *pulse_gpio;
17 struct proc_dir_entry *procfs;
18 };
19
pir_aschip_set_sensibility(struct aschip_pir_drv_data * drv_data,uint32_t sensibility)20 static int pir_aschip_set_sensibility(struct aschip_pir_drv_data *drv_data,
21 uint32_t sensibility)
22 {
23 if (sensibility > 100 || sensibility < 4) {
24 dev_err(drv_data->dev, "sensibilit should be [4, 100]\n");
25 return -EINVAL;
26 }
27
28 gpiod_set_value(drv_data->pulse_gpio, 1);
29 usleep_range(1000, 2000);
30 gpiod_set_value(drv_data->pulse_gpio, 0);
31 usleep_range(50000, 51000);
32 gpiod_set_value(drv_data->pulse_gpio, 1);
33 usleep_range(sensibility * 1000, sensibility * 1000 + 1);
34 gpiod_set_value(drv_data->pulse_gpio, 0);
35 usleep_range(50000, 51000);
36 gpiod_set_value(drv_data->pulse_gpio, 1);
37
38 return 0;
39 }
40
41 #ifdef CONFIG_PROC_FS
pir_aschip_sensibility_show(struct seq_file * s,void * v)42 static int pir_aschip_sensibility_show(struct seq_file *s, void *v)
43 {
44 seq_puts(s, "set sensibility [4, 100] for PIR :\n");
45 seq_puts(s, " echo [nr] > /proc/pir/sensibility\n");
46
47 return 0;
48 }
49
pir_aschip_sensibility_open(struct inode * inode,struct file * file)50 static int pir_aschip_sensibility_open(struct inode *inode, struct file *file)
51 {
52 return single_open(file, pir_aschip_sensibility_show, PDE_DATA(inode));
53 }
54
pir_aschip_sensibility_write(struct file * filp,const char __user * buf,size_t cnt,loff_t * ppos)55 static ssize_t pir_aschip_sensibility_write(struct file *filp,
56 const char __user *buf,
57 size_t cnt, loff_t *ppos)
58 {
59 struct aschip_pir_drv_data *drv_data = ((struct seq_file *)filp->private_data)->private;
60 unsigned int val;
61 int ret;
62
63 ret = kstrtouint_from_user(buf, cnt, 0, &val);
64 if (ret)
65 return ret;
66
67 ret = pir_aschip_set_sensibility(drv_data, val);
68 return ret ? ret : cnt;
69 }
70
71 static const struct file_operations pir_aschip_sensibility_fops = {
72 .open = pir_aschip_sensibility_open,
73 .read = seq_read,
74 .llseek = seq_lseek,
75 .release = single_release,
76 .write = pir_aschip_sensibility_write,
77 };
78
pir_aschip_create_procfs(struct aschip_pir_drv_data * drv_data)79 static int pir_aschip_create_procfs(struct aschip_pir_drv_data *drv_data)
80 {
81 struct proc_dir_entry *ent;
82
83 drv_data->procfs = proc_mkdir("pir", NULL);
84 if (!drv_data->procfs)
85 return -ENOMEM;
86
87 ent = proc_create_data("sensibility", 0644, drv_data->procfs,
88 &pir_aschip_sensibility_fops, drv_data);
89 if (!ent)
90 goto fail;
91
92 return 0;
93 fail:
94 proc_remove(drv_data->procfs);
95 return -ENOMEM;
96 }
97
pir_aschip_proc_release(struct aschip_pir_drv_data * drv_data)98 static void pir_aschip_proc_release(struct aschip_pir_drv_data *drv_data)
99 {
100 remove_proc_entry("sensibility", NULL);
101 proc_remove(drv_data->procfs);
102 }
103 #endif
104
pir_aschip_probe(struct platform_device * pdev)105 static int pir_aschip_probe(struct platform_device *pdev)
106 {
107 struct aschip_pir_drv_data *drv_data;
108 struct device *dev = &pdev->dev;
109
110 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
111 if (!drv_data)
112 return -ENOMEM;
113
114 drv_data->pulse_gpio = devm_gpiod_get(dev, "pulse", GPIOD_OUT_HIGH);
115
116 if (IS_ERR(drv_data->pulse_gpio)) {
117 dev_err(dev, "Failed to get pwdn-gpios\n");
118 return PTR_ERR(drv_data->pulse_gpio);
119 }
120
121 #ifdef CONFIG_PROC_FS
122 pir_aschip_create_procfs(drv_data);
123 #endif
124 drv_data->dev = dev;
125 dev_set_drvdata(dev, drv_data);
126 return 0;
127 }
128
pir_aschip_remove(struct platform_device * pdev)129 static int pir_aschip_remove(struct platform_device *pdev)
130 {
131 #ifdef CONFIG_PROC_FS
132 struct aschip_pir_drv_data *drv_data;
133
134 drv_data = dev_get_drvdata(&pdev->dev);
135 pir_aschip_proc_release(drv_data);
136 #endif
137 return 0;
138 }
139
140 static const struct of_device_id pir_aschip_rockchip_match[] = {
141 { .compatible = "aschip,pir" },
142 {},
143 };
144
145 MODULE_DEVICE_TABLE(of, pir_aschip_rockchip_match);
146
147 static struct platform_driver pir_aschip_pltfm_driver = {
148 .probe = pir_aschip_probe,
149 .remove = pir_aschip_remove,
150 .driver = {
151 .name = "pir_aschip_aschip",
152 .of_match_table = pir_aschip_rockchip_match,
153 },
154 };
155
156 module_platform_driver(pir_aschip_pltfm_driver);
157
158 MODULE_LICENSE("GPL");
159 MODULE_AUTHOR("Ziyuan Xu");
160 MODULE_DESCRIPTION("Aschip PIR sensibility control driver");
161