FILE
cros_ec_activity.c
File preview with syntax highlighting.
linux-6.19.14/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * cros_ec_activity - Driver for activities/gesture recognition. 4 * 5 * Copyright 2025 Google, Inc 6 * 7 * This driver uses the cros-ec interface to communicate with the ChromeOS 8 * EC about activity data. 9 */ 10 11#include <linux/bits.h> 12#include <linux/cleanup.h> 13#include <linux/kernel.h> 14#include <linux/module.h> 15#include <linux/mutex.h> 16#include <linux/platform_device.h> 17#include <linux/types.h> 18 19#include <linux/platform_data/cros_ec_commands.h> 20#include <linux/platform_data/cros_ec_proto.h> 21 22#include <linux/iio/common/cros_ec_sensors_core.h> 23#include <linux/iio/events.h> 24#include <linux/iio/iio.h> 25#include <linux/iio/trigger_consumer.h> 26 27#define DRV_NAME "cros-ec-activity" 28 29/* state data for ec_sensors iio driver. */ 30struct cros_ec_sensors_state { 31 /* Shared by all sensors */ 32 struct cros_ec_sensors_core_state core; 33 34 struct iio_chan_spec *channels; 35 36 int body_detection_channel_index; 37 int sig_motion_channel_index; 38}; 39 40static const struct iio_event_spec cros_ec_activity_single_shot[] = { 41 { 42 .type = IIO_EV_TYPE_CHANGE, 43 /* significant motion trigger when we get out of still. */ 44 .dir = IIO_EV_DIR_FALLING, 45 .mask_separate = BIT(IIO_EV_INFO_ENABLE), 46 }, 47}; 48 49static const struct iio_event_spec cros_ec_body_detect_events[] = { 50 { 51 .type = IIO_EV_TYPE_CHANGE, 52 .dir = IIO_EV_DIR_EITHER, 53 .mask_separate = BIT(IIO_EV_INFO_ENABLE), 54 }, 55}; 56 57static int cros_ec_activity_sensors_read_raw(struct iio_dev *indio_dev, 58 struct iio_chan_spec const *chan, 59 int *val, int *val2, long mask) 60{ 61 struct cros_ec_sensors_state *st = iio_priv(indio_dev); 62 int ret; 63 64 if (chan->type != IIO_PROXIMITY || mask != IIO_CHAN_INFO_RAW) 65 return -EINVAL; 66 67 guard(mutex)(&st->core.cmd_lock); 68 st->core.param.cmd = MOTIONSENSE_CMD_GET_ACTIVITY; 69 st->core.param.get_activity.activity = 70 MOTIONSENSE_ACTIVITY_BODY_DETECTION; 71 ret = cros_ec_motion_send_host_cmd(&st->core, 0); 72 if (ret) 73 return ret; 74 75 /* 76 * EC actually report if a body is near (1) or far (0). 77 * Units for proximity sensor after scale is in meter, 78 * so invert the result to return 0m when near and 1m when far. 79 */ 80 *val = !st->core.resp->get_activity.state; 81 return IIO_VAL_INT; 82} 83 84static int cros_ec_activity_read_event_config(struct iio_dev *indio_dev, 85 const struct iio_chan_spec *chan, 86 enum iio_event_type type, 87 enum iio_event_direction dir) 88{ 89 struct cros_ec_sensors_state *st = iio_priv(indio_dev); 90 int ret; 91 92 if (chan->type != IIO_ACTIVITY && chan->type != IIO_PROXIMITY) 93 return -EINVAL; 94 95 guard(mutex)(&st->core.cmd_lock); 96 st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES; 97 ret = cros_ec_motion_send_host_cmd(&st->core, 0); 98 if (ret) 99 return ret; 100 101 switch (chan->type) { 102 case IIO_PROXIMITY: 103 return !!(st->core.resp->list_activities.enabled & 104 (1 << MOTIONSENSE_ACTIVITY_BODY_DETECTION)); 105 case IIO_ACTIVITY: 106 if (chan->channel2 == IIO_MOD_STILL) { 107 return !!(st->core.resp->list_activities.enabled & 108 (1 << MOTIONSENSE_ACTIVITY_SIG_MOTION)); 109 } 110 111 dev_warn(&indio_dev->dev, "Unknown activity: %d\n", 112 chan->channel2); 113 return -EINVAL; 114 default: 115 dev_warn(&indio_dev->dev, "Unknown channel type: %d\n", 116 chan->type); 117 return -EINVAL; 118 } 119} 120 121static int cros_ec_activity_write_event_config(struct iio_dev *indio_dev, 122 const struct iio_chan_spec *chan, 123 enum iio_event_type type, 124 enum iio_event_direction dir, 125 bool state) 126{ 127 struct cros_ec_sensors_state *st = iio_priv(indio_dev); 128 129 guard(mutex)(&st->core.cmd_lock); 130 st->core.param.cmd = MOTIONSENSE_CMD_SET_ACTIVITY; 131 switch (chan->type) { 132 case IIO_PROXIMITY: 133 st->core.param.set_activity.activity = 134 MOTIONSENSE_ACTIVITY_BODY_DETECTION; 135 break; 136 case IIO_ACTIVITY: 137 if (chan->channel2 == IIO_MOD_STILL) { 138 st->core.param.set_activity.activity = 139 MOTIONSENSE_ACTIVITY_SIG_MOTION; 140 break; 141 } 142 dev_warn(&indio_dev->dev, "Unknown activity: %d\n", 143 chan->channel2); 144 return -EINVAL; 145 default: 146 dev_warn(&indio_dev->dev, "Unknown channel type: %d\n", 147 chan->type); 148 return -EINVAL; 149 } 150 st->core.param.set_activity.enable = state; 151 return cros_ec_motion_send_host_cmd(&st->core, 0); 152} 153 154static int cros_ec_activity_push_data(struct iio_dev *indio_dev, 155 s16 *data, s64 timestamp) 156{ 157 struct ec_response_activity_data *activity_data = 158 (struct ec_response_activity_data *)data; 159 enum motionsensor_activity activity = activity_data->activity; 160 u8 state = activity_data->state; 161 const struct cros_ec_sensors_state *st = iio_priv(indio_dev); 162 const struct iio_chan_spec *chan; 163 enum iio_event_direction dir; 164 int index; 165 166 switch (activity) { 167 case MOTIONSENSE_ACTIVITY_BODY_DETECTION: 168 index = st->body_detection_channel_index; 169 dir = state ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; 170 break; 171 case MOTIONSENSE_ACTIVITY_SIG_MOTION: 172 index = st->sig_motion_channel_index; 173 dir = IIO_EV_DIR_FALLING; 174 break; 175 default: 176 dev_warn(&indio_dev->dev, "Unknown activity: %d\n", activity); 177 return 0; 178 } 179 chan = &st->channels[index]; 180 iio_push_event(indio_dev, 181 IIO_UNMOD_EVENT_CODE(chan->type, index, chan->event_spec[0].type, dir), 182 timestamp); 183 return 0; 184} 185 186static irqreturn_t cros_ec_activity_capture(int irq, void *p) 187{ 188 struct iio_poll_func *pf = p; 189 struct iio_dev *indio_dev = pf->indio_dev; 190 191 /* 192 * This callback would be called when a software trigger is 193 * used. But when this virtual sensor is present, it is guaranteed 194 * the sensor hub is advanced enough to not need a software trigger. 195 */ 196 dev_warn(&indio_dev->dev, "%s: Not Expected\n", __func__); 197 return IRQ_NONE; 198} 199 200static const struct iio_info ec_sensors_info = { 201 .read_raw = &cros_ec_activity_sensors_read_raw, 202 .read_event_config = cros_ec_activity_read_event_config, 203 .write_event_config = cros_ec_activity_write_event_config, 204}; 205 206static int cros_ec_sensors_probe(struct platform_device *pdev) 207{ 208 struct device *dev = &pdev->dev; 209 struct cros_ec_device *ec_device = dev_get_drvdata(dev->parent); 210 struct iio_dev *indio_dev; 211 struct cros_ec_sensors_state *st; 212 struct iio_chan_spec *channel; 213 unsigned long activities; 214 int i, index, ret, nb_activities; 215 216 if (!ec_device) { 217 dev_warn(dev, "No CROS EC device found.\n"); 218 return -EINVAL; 219 } 220 221 indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); 222 if (!indio_dev) 223 return -ENOMEM; 224 225 ret = cros_ec_sensors_core_init(pdev, indio_dev, true, 226 cros_ec_activity_capture); 227 if (ret) 228 return ret; 229 230 indio_dev->info = &ec_sensors_info; 231 st = iio_priv(indio_dev); 232 st->core.type = st->core.resp->info.type; 233 st->core.read_ec_sensors_data = cros_ec_sensors_read_cmd; 234 235 st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES; 236 ret = cros_ec_motion_send_host_cmd(&st->core, 0); 237 if (ret) 238 return ret; 239 240 activities = st->core.resp->list_activities.enabled | 241 st->core.resp->list_activities.disabled; 242 if (!activities) 243 return -ENODEV; 244 245 /* Allocate a channel per activity and one for timestamp */ 246 nb_activities = hweight_long(activities) + 1; 247 st->channels = devm_kcalloc(dev, nb_activities, 248 sizeof(*st->channels), GFP_KERNEL); 249 if (!st->channels) 250 return -ENOMEM; 251 252 channel = &st->channels[0]; 253 index = 0; 254 for_each_set_bit(i, &activities, BITS_PER_LONG) { 255 /* List all available triggers */ 256 if (i == MOTIONSENSE_ACTIVITY_BODY_DETECTION) { 257 channel->type = IIO_PROXIMITY; 258 channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); 259 channel->event_spec = cros_ec_body_detect_events; 260 channel->num_event_specs = 261 ARRAY_SIZE(cros_ec_body_detect_events); 262 st->body_detection_channel_index = index; 263 } else { 264 channel->type = IIO_ACTIVITY; 265 channel->modified = 1; 266 channel->event_spec = cros_ec_activity_single_shot; 267 channel->num_event_specs = 268 ARRAY_SIZE(cros_ec_activity_single_shot); 269 if (i == MOTIONSENSE_ACTIVITY_SIG_MOTION) { 270 channel->channel2 = IIO_MOD_STILL; 271 st->sig_motion_channel_index = index; 272 } else { 273 dev_warn(dev, "Unknown activity: %d\n", i); 274 continue; 275 } 276 } 277 channel->ext_info = cros_ec_sensors_limited_info; 278 channel->scan_index = index++; 279 channel++; 280 } 281 282 /* Timestamp */ 283 channel->scan_index = index; 284 channel->type = IIO_TIMESTAMP; 285 channel->channel = -1; 286 channel->scan_type.sign = 's'; 287 channel->scan_type.realbits = 64; 288 channel->scan_type.storagebits = 64; 289 290 indio_dev->channels = st->channels; 291 indio_dev->num_channels = index + 1; 292 293 return cros_ec_sensors_core_register(dev, indio_dev, 294 cros_ec_activity_push_data); 295} 296 297static struct platform_driver cros_ec_sensors_platform_driver = { 298 .driver = { 299 .name = DRV_NAME, 300 }, 301 .probe = cros_ec_sensors_probe, 302}; 303module_platform_driver(cros_ec_sensors_platform_driver); 304 305MODULE_DESCRIPTION("ChromeOS EC activity sensors driver"); 306MODULE_ALIAS("platform:" DRV_NAME); 307MODULE_LICENSE("GPL v2");
~/
Read-only file preview.