1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved. |
4 | */ |
5 | |
6 | #include <linux/iio/common/ssp_sensors.h> |
7 | #include <linux/iio/iio.h> |
8 | #include <linux/iio/buffer.h> |
9 | #include <linux/iio/kfifo_buf.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/slab.h> |
13 | #include "../common/ssp_sensors/ssp_iio_sensor.h" |
14 | |
15 | #define SSP_CHANNEL_COUNT 3 |
16 | |
17 | #define SSP_ACCEL_NAME "ssp-accelerometer" |
18 | static const char ssp_accel_device_name[] = SSP_ACCEL_NAME; |
19 | |
20 | enum ssp_accel_3d_channel { |
21 | SSP_CHANNEL_SCAN_INDEX_X, |
22 | SSP_CHANNEL_SCAN_INDEX_Y, |
23 | SSP_CHANNEL_SCAN_INDEX_Z, |
24 | SSP_CHANNEL_SCAN_INDEX_TIME, |
25 | }; |
26 | |
27 | static int ssp_accel_read_raw(struct iio_dev *indio_dev, |
28 | struct iio_chan_spec const *chan, int *val, |
29 | int *val2, long mask) |
30 | { |
31 | u32 t; |
32 | struct ssp_data *data = dev_get_drvdata(dev: indio_dev->dev.parent->parent); |
33 | |
34 | switch (mask) { |
35 | case IIO_CHAN_INFO_SAMP_FREQ: |
36 | t = ssp_get_sensor_delay(data, SSP_ACCELEROMETER_SENSOR); |
37 | ssp_convert_to_freq(time: t, integer_part: val, fractional: val2); |
38 | return IIO_VAL_INT_PLUS_MICRO; |
39 | default: |
40 | break; |
41 | } |
42 | |
43 | return -EINVAL; |
44 | } |
45 | |
46 | static int ssp_accel_write_raw(struct iio_dev *indio_dev, |
47 | struct iio_chan_spec const *chan, int val, |
48 | int val2, long mask) |
49 | { |
50 | int ret; |
51 | struct ssp_data *data = dev_get_drvdata(dev: indio_dev->dev.parent->parent); |
52 | |
53 | switch (mask) { |
54 | case IIO_CHAN_INFO_SAMP_FREQ: |
55 | ret = ssp_convert_to_time(integer_part: val, fractional: val2); |
56 | ret = ssp_change_delay(data, type: SSP_ACCELEROMETER_SENSOR, delay: ret); |
57 | if (ret < 0) |
58 | dev_err(&indio_dev->dev, "accel sensor enable fail\n" ); |
59 | |
60 | return ret; |
61 | default: |
62 | break; |
63 | } |
64 | |
65 | return -EINVAL; |
66 | } |
67 | |
68 | static const struct iio_info ssp_accel_iio_info = { |
69 | .read_raw = &ssp_accel_read_raw, |
70 | .write_raw = &ssp_accel_write_raw, |
71 | }; |
72 | |
73 | static const unsigned long ssp_accel_scan_mask[] = { 0x7, 0, }; |
74 | |
75 | static const struct iio_chan_spec ssp_acc_channels[] = { |
76 | SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X), |
77 | SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y), |
78 | SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z), |
79 | SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME), |
80 | }; |
81 | |
82 | static int ssp_process_accel_data(struct iio_dev *indio_dev, void *buf, |
83 | int64_t timestamp) |
84 | { |
85 | return ssp_common_process_data(indio_dev, buf, SSP_ACCELEROMETER_SIZE, |
86 | timestamp); |
87 | } |
88 | |
89 | static const struct iio_buffer_setup_ops ssp_accel_buffer_ops = { |
90 | .postenable = &ssp_common_buffer_postenable, |
91 | .postdisable = &ssp_common_buffer_postdisable, |
92 | }; |
93 | |
94 | static int ssp_accel_probe(struct platform_device *pdev) |
95 | { |
96 | int ret; |
97 | struct iio_dev *indio_dev; |
98 | struct ssp_sensor_data *spd; |
99 | |
100 | indio_dev = devm_iio_device_alloc(parent: &pdev->dev, sizeof_priv: sizeof(*spd)); |
101 | if (!indio_dev) |
102 | return -ENOMEM; |
103 | |
104 | spd = iio_priv(indio_dev); |
105 | |
106 | spd->process_data = ssp_process_accel_data; |
107 | spd->type = SSP_ACCELEROMETER_SENSOR; |
108 | |
109 | indio_dev->name = ssp_accel_device_name; |
110 | indio_dev->info = &ssp_accel_iio_info; |
111 | indio_dev->channels = ssp_acc_channels; |
112 | indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels); |
113 | indio_dev->available_scan_masks = ssp_accel_scan_mask; |
114 | |
115 | ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev, |
116 | &ssp_accel_buffer_ops); |
117 | if (ret) |
118 | return ret; |
119 | |
120 | platform_set_drvdata(pdev, data: indio_dev); |
121 | |
122 | ret = devm_iio_device_register(&pdev->dev, indio_dev); |
123 | if (ret < 0) |
124 | return ret; |
125 | |
126 | /* ssp registering should be done after all iio setup */ |
127 | ssp_register_consumer(indio_dev, type: SSP_ACCELEROMETER_SENSOR); |
128 | |
129 | return 0; |
130 | } |
131 | |
132 | static struct platform_driver ssp_accel_driver = { |
133 | .driver = { |
134 | .name = SSP_ACCEL_NAME, |
135 | }, |
136 | .probe = ssp_accel_probe, |
137 | }; |
138 | |
139 | module_platform_driver(ssp_accel_driver); |
140 | |
141 | MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>" ); |
142 | MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver" ); |
143 | MODULE_LICENSE("GPL" ); |
144 | MODULE_IMPORT_NS(IIO_SSP_SENSORS); |
145 | |