1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for cros-ec proximity sensor exposed through MKBP switch
5 * Copyright 2021 Google LLC.
8 #include <linux/kernel.h>
9 #include <linux/mod_devicetable.h>
10 #include <linux/module.h>
11 #include <linux/mutex.h>
12 #include <linux/notifier.h>
13 #include <linux/platform_device.h>
14 #include <linux/slab.h>
15 #include <linux/types.h>
17 #include <linux/platform_data/cros_ec_commands.h>
18 #include <linux/platform_data/cros_ec_proto.h>
20 #include <linux/iio/events.h>
21 #include <linux/iio/iio.h>
22 #include <linux/iio/sysfs.h>
24 #include <linux/unaligned.h>
26 struct cros_ec_mkbp_proximity_data
{
27 struct cros_ec_device
*ec
;
28 struct iio_dev
*indio_dev
;
30 struct notifier_block notifier
;
35 static const struct iio_event_spec cros_ec_mkbp_proximity_events
[] = {
37 .type
= IIO_EV_TYPE_THRESH
,
38 .dir
= IIO_EV_DIR_EITHER
,
39 .mask_separate
= BIT(IIO_EV_INFO_ENABLE
),
43 static const struct iio_chan_spec cros_ec_mkbp_proximity_chan_spec
[] = {
45 .type
= IIO_PROXIMITY
,
46 .info_mask_separate
= BIT(IIO_CHAN_INFO_RAW
),
47 .event_spec
= cros_ec_mkbp_proximity_events
,
48 .num_event_specs
= ARRAY_SIZE(cros_ec_mkbp_proximity_events
),
52 static int cros_ec_mkbp_proximity_parse_state(const void *data
)
54 u32 switches
= get_unaligned_le32(data
);
56 return !!(switches
& BIT(EC_MKBP_FRONT_PROXIMITY
));
59 static int cros_ec_mkbp_proximity_query(struct cros_ec_device
*ec_dev
,
63 struct cros_ec_command msg
;
65 struct ec_params_mkbp_info params
;
69 struct ec_params_mkbp_info
*params
= &buf
.params
;
70 struct cros_ec_command
*msg
= &buf
.msg
;
71 u32
*switches
= &buf
.switches
;
72 size_t insize
= sizeof(*switches
);
75 msg
->command
= EC_CMD_MKBP_INFO
;
77 msg
->outsize
= sizeof(*params
);
80 params
->info_type
= EC_MKBP_INFO_CURRENT
;
81 params
->event_type
= EC_MKBP_EVENT_SWITCH
;
83 ret
= cros_ec_cmd_xfer_status(ec_dev
, msg
);
88 dev_warn(ec_dev
->dev
, "wrong result size: %d != %zu\n", ret
,
93 *state
= cros_ec_mkbp_proximity_parse_state(switches
);
97 static void cros_ec_mkbp_proximity_push_event(struct cros_ec_mkbp_proximity_data
*data
, int state
)
102 struct iio_dev
*indio_dev
= data
->indio_dev
;
103 struct cros_ec_device
*ec
= data
->ec
;
105 mutex_lock(&data
->lock
);
106 if (state
!= data
->last_proximity
) {
108 timestamp
= ktime_to_ns(ec
->last_event_time
);
109 if (iio_device_get_clock(indio_dev
) != CLOCK_BOOTTIME
)
110 timestamp
= iio_get_time_ns(indio_dev
);
112 dir
= state
? IIO_EV_DIR_FALLING
: IIO_EV_DIR_RISING
;
113 ev
= IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY
, 0,
114 IIO_EV_TYPE_THRESH
, dir
);
115 iio_push_event(indio_dev
, ev
, timestamp
);
117 data
->last_proximity
= state
;
119 mutex_unlock(&data
->lock
);
122 static int cros_ec_mkbp_proximity_notify(struct notifier_block
*nb
,
123 unsigned long queued_during_suspend
,
126 struct cros_ec_mkbp_proximity_data
*data
;
127 struct cros_ec_device
*ec
= _ec
;
128 u8 event_type
= ec
->event_data
.event_type
& EC_MKBP_EVENT_TYPE_MASK
;
132 if (event_type
== EC_MKBP_EVENT_SWITCH
) {
133 data
= container_of(nb
, struct cros_ec_mkbp_proximity_data
,
136 switches
= &ec
->event_data
.data
.switches
;
137 state
= cros_ec_mkbp_proximity_parse_state(switches
);
138 cros_ec_mkbp_proximity_push_event(data
, state
);
144 static int cros_ec_mkbp_proximity_read_raw(struct iio_dev
*indio_dev
,
145 const struct iio_chan_spec
*chan
, int *val
,
146 int *val2
, long mask
)
148 struct cros_ec_mkbp_proximity_data
*data
= iio_priv(indio_dev
);
149 struct cros_ec_device
*ec
= data
->ec
;
151 if (chan
->type
== IIO_PROXIMITY
&& mask
== IIO_CHAN_INFO_RAW
)
152 return cros_ec_mkbp_proximity_query(ec
, val
);
157 static int cros_ec_mkbp_proximity_read_event_config(struct iio_dev
*indio_dev
,
158 const struct iio_chan_spec
*chan
,
159 enum iio_event_type type
,
160 enum iio_event_direction dir
)
162 struct cros_ec_mkbp_proximity_data
*data
= iio_priv(indio_dev
);
164 return data
->enabled
;
167 static int cros_ec_mkbp_proximity_write_event_config(struct iio_dev
*indio_dev
,
168 const struct iio_chan_spec
*chan
,
169 enum iio_event_type type
,
170 enum iio_event_direction dir
, bool state
)
172 struct cros_ec_mkbp_proximity_data
*data
= iio_priv(indio_dev
);
174 mutex_lock(&data
->lock
);
175 data
->enabled
= state
;
176 mutex_unlock(&data
->lock
);
181 static const struct iio_info cros_ec_mkbp_proximity_info
= {
182 .read_raw
= cros_ec_mkbp_proximity_read_raw
,
183 .read_event_config
= cros_ec_mkbp_proximity_read_event_config
,
184 .write_event_config
= cros_ec_mkbp_proximity_write_event_config
,
187 static int cros_ec_mkbp_proximity_resume(struct device
*dev
)
189 struct cros_ec_mkbp_proximity_data
*data
= dev_get_drvdata(dev
);
190 struct cros_ec_device
*ec
= data
->ec
;
193 ret
= cros_ec_mkbp_proximity_query(ec
, &state
);
195 dev_warn(dev
, "failed to fetch proximity state on resume: %d\n",
198 cros_ec_mkbp_proximity_push_event(data
, state
);
204 static DEFINE_SIMPLE_DEV_PM_OPS(cros_ec_mkbp_proximity_pm_ops
, NULL
,
205 cros_ec_mkbp_proximity_resume
);
207 static int cros_ec_mkbp_proximity_probe(struct platform_device
*pdev
)
209 struct device
*dev
= &pdev
->dev
;
210 struct cros_ec_device
*ec
= dev_get_drvdata(dev
->parent
);
211 struct iio_dev
*indio_dev
;
212 struct cros_ec_mkbp_proximity_data
*data
;
215 indio_dev
= devm_iio_device_alloc(dev
, sizeof(*data
));
219 data
= iio_priv(indio_dev
);
221 data
->indio_dev
= indio_dev
;
222 data
->last_proximity
= -1; /* Unknown to start */
223 mutex_init(&data
->lock
);
224 platform_set_drvdata(pdev
, data
);
226 indio_dev
->name
= dev
->driver
->name
;
227 indio_dev
->info
= &cros_ec_mkbp_proximity_info
;
228 indio_dev
->modes
= INDIO_DIRECT_MODE
;
229 indio_dev
->channels
= cros_ec_mkbp_proximity_chan_spec
;
230 indio_dev
->num_channels
= ARRAY_SIZE(cros_ec_mkbp_proximity_chan_spec
);
232 ret
= devm_iio_device_register(dev
, indio_dev
);
236 data
->notifier
.notifier_call
= cros_ec_mkbp_proximity_notify
;
237 blocking_notifier_chain_register(&ec
->event_notifier
, &data
->notifier
);
242 static void cros_ec_mkbp_proximity_remove(struct platform_device
*pdev
)
244 struct cros_ec_mkbp_proximity_data
*data
= platform_get_drvdata(pdev
);
245 struct cros_ec_device
*ec
= data
->ec
;
247 blocking_notifier_chain_unregister(&ec
->event_notifier
,
251 static const struct of_device_id cros_ec_mkbp_proximity_of_match
[] = {
252 { .compatible
= "google,cros-ec-mkbp-proximity" },
255 MODULE_DEVICE_TABLE(of
, cros_ec_mkbp_proximity_of_match
);
257 static struct platform_driver cros_ec_mkbp_proximity_driver
= {
259 .name
= "cros-ec-mkbp-proximity",
260 .of_match_table
= cros_ec_mkbp_proximity_of_match
,
261 .pm
= pm_sleep_ptr(&cros_ec_mkbp_proximity_pm_ops
),
263 .probe
= cros_ec_mkbp_proximity_probe
,
264 .remove
= cros_ec_mkbp_proximity_remove
,
266 module_platform_driver(cros_ec_mkbp_proximity_driver
);
268 MODULE_LICENSE("GPL v2");
269 MODULE_DESCRIPTION("ChromeOS EC MKBP proximity sensor driver");