blob: dfdbd8e8df698be5cc4b2e8e64bb016bfebadd85 [file] [log] [blame]
Ananda Kishore7875c502017-05-04 00:11:28 +05301/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/types.h>
14#include <linux/msm_dsps.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/slab.h>
18#include <linux/platform_device.h>
19#include <linux/cdev.h>
20#include <linux/fs.h>
21#include <linux/of_device.h>
22#include <asm/arch_timer.h>
23#include <linux/uaccess.h>
24
25#include <linux/kernel.h>
26#include <linux/err.h>
27#include <linux/delay.h>
28#include <linux/sysfs.h>
29#include <linux/workqueue.h>
30
31#include <soc/qcom/subsystem_restart.h>
32
33#define IMAGE_LOAD_CMD 1
34#define IMAGE_UNLOAD_CMD 0
Ananda Kishore686f86b2017-06-28 23:28:50 +053035#define SSR_RESET_CMD 1
Ananda Kishore7875c502017-05-04 00:11:28 +053036#define CLASS_NAME "ssc"
37#define DRV_NAME "sensors"
38#define DRV_VERSION "2.00"
39#ifdef CONFIG_COMPAT
40#define DSPS_IOCTL_READ_SLOW_TIMER32 _IOR(DSPS_IOCTL_MAGIC, 3, compat_uint_t)
41#endif
42
43#define QTICK_DIV_FACTOR 0x249F
44
45struct sns_ssc_control_s {
46 struct class *dev_class;
47 dev_t dev_num;
48 struct device *dev;
49 struct cdev *cdev;
50};
51static struct sns_ssc_control_s sns_ctl;
52
53static ssize_t slpi_boot_store(struct kobject *kobj,
54 struct kobj_attribute *attr,
55 const char *buf, size_t count);
56
Ananda Kishore686f86b2017-06-28 23:28:50 +053057static ssize_t slpi_ssr_store(struct kobject *kobj,
58 struct kobj_attribute *attr,
59 const char *buf, size_t count);
60
Ananda Kishore7875c502017-05-04 00:11:28 +053061struct slpi_loader_private {
62 void *pil_h;
63 struct kobject *boot_slpi_obj;
64 struct attribute_group *attr_group;
65};
66
67static struct kobj_attribute slpi_boot_attribute =
68 __ATTR(boot, 0220, NULL, slpi_boot_store);
69
Ananda Kishore686f86b2017-06-28 23:28:50 +053070static struct kobj_attribute slpi_ssr_attribute =
71 __ATTR(ssr, 0220, NULL, slpi_ssr_store);
72
Ananda Kishore7875c502017-05-04 00:11:28 +053073static struct attribute *attrs[] = {
74 &slpi_boot_attribute.attr,
Ananda Kishore686f86b2017-06-28 23:28:50 +053075 &slpi_ssr_attribute.attr,
Ananda Kishore7875c502017-05-04 00:11:28 +053076 NULL,
77};
78
79static struct platform_device *slpi_private;
80static struct work_struct slpi_ldr_work;
81
82static void slpi_load_fw(struct work_struct *slpi_ldr_work)
83{
84 struct platform_device *pdev = slpi_private;
85 struct slpi_loader_private *priv = NULL;
86 int ret;
87 const char *firmware_name = NULL;
88
89 if (!pdev) {
90 dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
91 goto fail;
92 }
93
94 if (!pdev->dev.of_node) {
95 dev_err(&pdev->dev,
96 "%s: Device tree information missing\n", __func__);
97 goto fail;
98 }
99
100 ret = of_property_read_string(pdev->dev.of_node,
101 "qcom,firmware-name", &firmware_name);
102 if (ret < 0) {
103 pr_err("can't get fw name.\n");
104 goto fail;
105 }
106
107 priv = platform_get_drvdata(pdev);
108 if (!priv) {
109 dev_err(&pdev->dev,
110 " %s: Private data get failed\n", __func__);
111 goto fail;
112 }
113
114 priv->pil_h = subsystem_get_with_fwname("slpi", firmware_name);
115 if (IS_ERR(priv->pil_h)) {
116 dev_err(&pdev->dev, "%s: pil get failed,\n",
117 __func__);
118 goto fail;
119 }
120
121 dev_dbg(&pdev->dev, "%s: SLPI image is loaded\n", __func__);
122 return;
123
124fail:
125 dev_err(&pdev->dev, "%s: SLPI image loading failed\n", __func__);
126}
127
128static void slpi_loader_do(struct platform_device *pdev)
129{
130 dev_dbg(&pdev->dev, "%s: scheduling work to load SLPI fw\n", __func__);
131 schedule_work(&slpi_ldr_work);
132}
133
134static void slpi_loader_unload(struct platform_device *pdev)
135{
136 struct slpi_loader_private *priv = NULL;
137
138 priv = platform_get_drvdata(pdev);
139
140 if (!priv)
141 return;
142
143 if (priv->pil_h) {
144 dev_dbg(&pdev->dev, "%s: calling subsystem put\n", __func__);
145 subsystem_put(priv->pil_h);
146 priv->pil_h = NULL;
147 }
148}
149
Ananda Kishore686f86b2017-06-28 23:28:50 +0530150static ssize_t slpi_ssr_store(struct kobject *kobj,
151 struct kobj_attribute *attr,
152 const char *buf,
153 size_t count)
154{
155 int ssr_cmd = 0;
156 struct subsys_device *sns_dev = NULL;
157 struct platform_device *pdev = slpi_private;
158 struct slpi_loader_private *priv = NULL;
159
160 pr_debug("%s: going to call slpi_ssr\n", __func__);
161
162 if (kstrtoint(buf, 10, &ssr_cmd) < 0)
163 return -EINVAL;
164
165 if (ssr_cmd != SSR_RESET_CMD)
166 return -EINVAL;
167
168 priv = platform_get_drvdata(pdev);
169 if (!priv)
170 return -EINVAL;
171
172 sns_dev = (struct subsys_device *)priv->pil_h;
173 if (!sns_dev)
174 return -EINVAL;
175
176 dev_err(&pdev->dev, "Something went wrong with SLPI, restarting\n");
177
178 /* subsystem_restart_dev has worker queue to handle */
179 if (subsystem_restart_dev(sns_dev) != 0) {
180 dev_err(&pdev->dev, "subsystem_restart_dev failed\n");
181 return -EINVAL;
182 }
183
184 dev_dbg(&pdev->dev, "SLPI restarted\n");
185 return count;
186}
187
Ananda Kishore7875c502017-05-04 00:11:28 +0530188static ssize_t slpi_boot_store(struct kobject *kobj,
189 struct kobj_attribute *attr,
190 const char *buf,
191 size_t count)
192{
193 int boot = 0;
194
195 if (sscanf(buf, "%du", &boot) != 1)
196 return -EINVAL;
197
198 if (boot == IMAGE_LOAD_CMD) {
199 pr_debug("%s: going to call slpi_loader_do\n", __func__);
200 slpi_loader_do(slpi_private);
201 } else if (boot == IMAGE_UNLOAD_CMD) {
202 pr_debug("%s: going to call slpi_unloader\n", __func__);
203 slpi_loader_unload(slpi_private);
204 }
205 return count;
206}
207
208static int slpi_loader_init_sysfs(struct platform_device *pdev)
209{
210 int ret = -EINVAL;
211 struct slpi_loader_private *priv = NULL;
212
213 slpi_private = NULL;
214
215 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
216 if (!priv) {
217 ret = -ENOMEM;
218 return ret;
219 }
220
221 platform_set_drvdata(pdev, priv);
222
223 priv->pil_h = NULL;
224 priv->boot_slpi_obj = NULL;
225 priv->attr_group = devm_kzalloc(&pdev->dev,
226 sizeof(*(priv->attr_group)),
227 GFP_KERNEL);
228 if (!priv->attr_group) {
229 dev_err(&pdev->dev, "%s: malloc attr_group failed\n",
230 __func__);
231 ret = -ENOMEM;
232 goto error_return;
233 }
234
235 priv->attr_group->attrs = attrs;
236
237 priv->boot_slpi_obj = kobject_create_and_add("boot_slpi", kernel_kobj);
238 if (!priv->boot_slpi_obj) {
239 dev_err(&pdev->dev, "%s: sysfs create and add failed\n",
240 __func__);
241 ret = -ENOMEM;
242 goto error_return;
243 }
244
245 ret = sysfs_create_group(priv->boot_slpi_obj, priv->attr_group);
246 if (ret) {
247 dev_err(&pdev->dev, "%s: sysfs create group failed %d\n",
248 __func__, ret);
249 goto error_return;
250 }
251
252 slpi_private = pdev;
253
254 return 0;
255
256error_return:
257
258 if (priv->boot_slpi_obj) {
259 kobject_del(priv->boot_slpi_obj);
260 priv->boot_slpi_obj = NULL;
261 }
262
263 return ret;
264}
265
266static int slpi_loader_remove(struct platform_device *pdev)
267{
268 struct slpi_loader_private *priv = NULL;
269
270 priv = platform_get_drvdata(pdev);
271
272 if (!priv)
273 return 0;
274
275 if (priv->pil_h) {
276 subsystem_put(priv->pil_h);
277 priv->pil_h = NULL;
278 }
279
280 if (priv->boot_slpi_obj) {
281 sysfs_remove_group(priv->boot_slpi_obj, priv->attr_group);
282 kobject_del(priv->boot_slpi_obj);
283 priv->boot_slpi_obj = NULL;
284 }
285
286 return 0;
287}
288
289/*
290 * Read virtual QTimer clock ticks and scale down to 32KHz clock as used
291 * in DSPS
292 */
293static u32 sns_read_qtimer(void)
294{
295 u64 val;
296
297 val = arch_counter_get_cntvct();
298 /*
299 * To convert ticks from 19.2 Mhz clock to 32768 Hz clock:
300 * x = (value * 32768) / 19200000
301 * This is same as first left shift the value by 4 bits, i.e. multiply
302 * by 16, and then divide by 0x249F. The latter is preferable since
303 * QTimer tick (value) is 56-bit, so (value * 32768) could overflow,
304 * while (value * 16) will never do
305 */
306 val <<= 4;
307 do_div(val, QTICK_DIV_FACTOR);
308
309 return (u32)val;
310}
311
312static int sensors_ssc_open(struct inode *ip, struct file *fp)
313{
314 return 0;
315}
316
317static int sensors_ssc_release(struct inode *inode, struct file *file)
318{
319 return 0;
320}
321
322static long sensors_ssc_ioctl(struct file *file,
323 unsigned int cmd, unsigned long arg)
324{
325 int ret = 0;
326 u32 val = 0;
327
328 switch (cmd) {
329 case DSPS_IOCTL_READ_SLOW_TIMER:
330#ifdef CONFIG_COMPAT
331 case DSPS_IOCTL_READ_SLOW_TIMER32:
332#endif
333 val = sns_read_qtimer();
334 ret = put_user(val, (u32 __user *) arg);
335 break;
336
337 default:
338 ret = -EINVAL;
339 break;
340 }
341
342 return ret;
343}
344
345const struct file_operations sensors_ssc_fops = {
346 .owner = THIS_MODULE,
347 .open = sensors_ssc_open,
348 .release = sensors_ssc_release,
349#ifdef CONFIG_COMPAT
350 .compat_ioctl = sensors_ssc_ioctl,
351#endif
352 .unlocked_ioctl = sensors_ssc_ioctl
353};
354
355static int sensors_ssc_probe(struct platform_device *pdev)
356{
357 int ret = slpi_loader_init_sysfs(pdev);
358
359 if (ret != 0) {
360 dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__);
361 return ret;
362 }
363
364 sns_ctl.dev_class = class_create(THIS_MODULE, CLASS_NAME);
365 if (sns_ctl.dev_class == NULL) {
366 pr_err("%s: class_create fail.\n", __func__);
367 goto res_err;
368 }
369
370 ret = alloc_chrdev_region(&sns_ctl.dev_num, 0, 1, DRV_NAME);
371 if (ret) {
372 pr_err("%s: alloc_chrdev_region fail.\n", __func__);
373 goto alloc_chrdev_region_err;
374 }
375
376 sns_ctl.dev = device_create(sns_ctl.dev_class, NULL,
377 sns_ctl.dev_num,
378 &sns_ctl, DRV_NAME);
379 if (IS_ERR(sns_ctl.dev)) {
380 pr_err("%s: device_create fail.\n", __func__);
381 goto device_create_err;
382 }
383
384 sns_ctl.cdev = cdev_alloc();
385 if (sns_ctl.cdev == NULL) {
386 pr_err("%s: cdev_alloc fail.\n", __func__);
387 goto cdev_alloc_err;
388 }
389 cdev_init(sns_ctl.cdev, &sensors_ssc_fops);
390 sns_ctl.cdev->owner = THIS_MODULE;
391
392 ret = cdev_add(sns_ctl.cdev, sns_ctl.dev_num, 1);
393 if (ret) {
394 pr_err("%s: cdev_add fail.\n", __func__);
395 goto cdev_add_err;
396 }
397
398 INIT_WORK(&slpi_ldr_work, slpi_load_fw);
399
400 return 0;
401
402cdev_add_err:
403 kfree(sns_ctl.cdev);
404cdev_alloc_err:
405 device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
406device_create_err:
407 unregister_chrdev_region(sns_ctl.dev_num, 1);
408alloc_chrdev_region_err:
409 class_destroy(sns_ctl.dev_class);
410res_err:
411 return -ENODEV;
412}
413
414static int sensors_ssc_remove(struct platform_device *pdev)
415{
416 slpi_loader_remove(pdev);
417 cdev_del(sns_ctl.cdev);
418 kfree(sns_ctl.cdev);
419 sns_ctl.cdev = NULL;
420 device_destroy(sns_ctl.dev_class, sns_ctl.dev_num);
421 unregister_chrdev_region(sns_ctl.dev_num, 1);
422 class_destroy(sns_ctl.dev_class);
423
424 return 0;
425}
426
427static const struct of_device_id msm_ssc_sensors_dt_match[] = {
428 {.compatible = "qcom,msm-ssc-sensors"},
429 {},
430};
431MODULE_DEVICE_TABLE(of, msm_ssc_sensors_dt_match);
432
433static struct platform_driver sensors_ssc_driver = {
434 .driver = {
435 .name = "sensors-ssc",
436 .owner = THIS_MODULE,
437 .of_match_table = msm_ssc_sensors_dt_match,
438 },
439 .probe = sensors_ssc_probe,
440 .remove = sensors_ssc_remove,
441};
442
443static int __init sensors_ssc_init(void)
444{
445 int rc;
446
447 pr_debug("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
448 rc = platform_driver_register(&sensors_ssc_driver);
449 if (rc) {
450 pr_err("%s: Failed to register sensors ssc driver\n",
451 __func__);
452 return rc;
453 }
454
455 return 0;
456}
457
458static void __exit sensors_ssc_exit(void)
459{
460 platform_driver_unregister(&sensors_ssc_driver);
461}
462
463module_init(sensors_ssc_init);
464module_exit(sensors_ssc_exit);
465MODULE_LICENSE("GPL v2");
466MODULE_DESCRIPTION("Sensors SSC driver");