159d0f2daSSong Hongyan /* 259d0f2daSSong Hongyan * HID Sensors Driver 359d0f2daSSong Hongyan * Copyright (c) 2017, Intel Corporation. 459d0f2daSSong Hongyan * 559d0f2daSSong Hongyan * This program is free software; you can redistribute it and/or modify it 659d0f2daSSong Hongyan * under the terms and conditions of the GNU General Public License, 759d0f2daSSong Hongyan * version 2, as published by the Free Software Foundation. 859d0f2daSSong Hongyan * 959d0f2daSSong Hongyan * This program is distributed in the hope it will be useful, but WITHOUT 1059d0f2daSSong Hongyan * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1159d0f2daSSong Hongyan * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1259d0f2daSSong Hongyan * more details. 1359d0f2daSSong Hongyan * 1459d0f2daSSong Hongyan * You should have received a copy of the GNU General Public License along with 1559d0f2daSSong Hongyan * this program. 1659d0f2daSSong Hongyan */ 1759d0f2daSSong Hongyan #include <linux/device.h> 1859d0f2daSSong Hongyan #include <linux/hid-sensor-hub.h> 1959d0f2daSSong Hongyan #include <linux/iio/buffer.h> 2059d0f2daSSong Hongyan #include <linux/iio/iio.h> 2159d0f2daSSong Hongyan #include <linux/iio/triggered_buffer.h> 2259d0f2daSSong Hongyan #include <linux/iio/trigger_consumer.h> 2359d0f2daSSong Hongyan #include <linux/module.h> 2459d0f2daSSong Hongyan #include <linux/platform_device.h> 2559d0f2daSSong Hongyan 2659d0f2daSSong Hongyan #include "../common/hid-sensors/hid-sensor-trigger.h" 2759d0f2daSSong Hongyan 2859d0f2daSSong Hongyan struct temperature_state { 2959d0f2daSSong Hongyan struct hid_sensor_common common_attributes; 3059d0f2daSSong Hongyan struct hid_sensor_hub_attribute_info temperature_attr; 3159d0f2daSSong Hongyan s32 temperature_data; 3259d0f2daSSong Hongyan int scale_pre_decml; 3359d0f2daSSong Hongyan int scale_post_decml; 3459d0f2daSSong Hongyan int scale_precision; 3559d0f2daSSong Hongyan int value_offset; 3659d0f2daSSong Hongyan }; 3759d0f2daSSong Hongyan 3859d0f2daSSong Hongyan /* Channel definitions */ 3959d0f2daSSong Hongyan static const struct iio_chan_spec temperature_channels[] = { 4059d0f2daSSong Hongyan { 4159d0f2daSSong Hongyan .type = IIO_TEMP, 4259d0f2daSSong Hongyan .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 4359d0f2daSSong Hongyan .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | 4459d0f2daSSong Hongyan BIT(IIO_CHAN_INFO_SCALE) | 4559d0f2daSSong Hongyan BIT(IIO_CHAN_INFO_SAMP_FREQ) | 4659d0f2daSSong Hongyan BIT(IIO_CHAN_INFO_HYSTERESIS), 4759d0f2daSSong Hongyan }, 4859d0f2daSSong Hongyan IIO_CHAN_SOFT_TIMESTAMP(3), 4959d0f2daSSong Hongyan }; 5059d0f2daSSong Hongyan 5159d0f2daSSong Hongyan /* Adjust channel real bits based on report descriptor */ 5259d0f2daSSong Hongyan static void temperature_adjust_channel_bit_mask(struct iio_chan_spec *channels, 5359d0f2daSSong Hongyan int channel, int size) 5459d0f2daSSong Hongyan { 5559d0f2daSSong Hongyan channels[channel].scan_type.sign = 's'; 5659d0f2daSSong Hongyan /* Real storage bits will change based on the report desc. */ 5759d0f2daSSong Hongyan channels[channel].scan_type.realbits = size * 8; 5859d0f2daSSong Hongyan /* Maximum size of a sample to capture is s32 */ 5959d0f2daSSong Hongyan channels[channel].scan_type.storagebits = sizeof(s32) * 8; 6059d0f2daSSong Hongyan } 6159d0f2daSSong Hongyan 6259d0f2daSSong Hongyan static int temperature_read_raw(struct iio_dev *indio_dev, 6359d0f2daSSong Hongyan struct iio_chan_spec const *chan, 6459d0f2daSSong Hongyan int *val, int *val2, long mask) 6559d0f2daSSong Hongyan { 6659d0f2daSSong Hongyan struct temperature_state *temp_st = iio_priv(indio_dev); 6759d0f2daSSong Hongyan 6859d0f2daSSong Hongyan switch (mask) { 6959d0f2daSSong Hongyan case IIO_CHAN_INFO_RAW: 7059d0f2daSSong Hongyan if (chan->type != IIO_TEMP) 7159d0f2daSSong Hongyan return -EINVAL; 7259d0f2daSSong Hongyan hid_sensor_power_state( 7359d0f2daSSong Hongyan &temp_st->common_attributes, true); 7459d0f2daSSong Hongyan *val = sensor_hub_input_attr_get_raw_value( 7559d0f2daSSong Hongyan temp_st->common_attributes.hsdev, 7659d0f2daSSong Hongyan HID_USAGE_SENSOR_TEMPERATURE, 7759d0f2daSSong Hongyan HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE, 7859d0f2daSSong Hongyan temp_st->temperature_attr.report_id, 790145b505SHans de Goede SENSOR_HUB_SYNC, 800145b505SHans de Goede temp_st->temperature_attr.logical_minimum < 0); 8159d0f2daSSong Hongyan hid_sensor_power_state( 8259d0f2daSSong Hongyan &temp_st->common_attributes, 8359d0f2daSSong Hongyan false); 8459d0f2daSSong Hongyan 8559d0f2daSSong Hongyan return IIO_VAL_INT; 8659d0f2daSSong Hongyan 8759d0f2daSSong Hongyan case IIO_CHAN_INFO_SCALE: 8859d0f2daSSong Hongyan *val = temp_st->scale_pre_decml; 8959d0f2daSSong Hongyan *val2 = temp_st->scale_post_decml; 9059d0f2daSSong Hongyan return temp_st->scale_precision; 9159d0f2daSSong Hongyan 9259d0f2daSSong Hongyan case IIO_CHAN_INFO_OFFSET: 9359d0f2daSSong Hongyan *val = temp_st->value_offset; 9459d0f2daSSong Hongyan return IIO_VAL_INT; 9559d0f2daSSong Hongyan 9659d0f2daSSong Hongyan case IIO_CHAN_INFO_SAMP_FREQ: 9759d0f2daSSong Hongyan return hid_sensor_read_samp_freq_value( 9859d0f2daSSong Hongyan &temp_st->common_attributes, val, val2); 9959d0f2daSSong Hongyan 10059d0f2daSSong Hongyan case IIO_CHAN_INFO_HYSTERESIS: 10159d0f2daSSong Hongyan return hid_sensor_read_raw_hyst_value( 10259d0f2daSSong Hongyan &temp_st->common_attributes, val, val2); 10359d0f2daSSong Hongyan default: 10459d0f2daSSong Hongyan return -EINVAL; 10559d0f2daSSong Hongyan } 10659d0f2daSSong Hongyan } 10759d0f2daSSong Hongyan 10859d0f2daSSong Hongyan static int temperature_write_raw(struct iio_dev *indio_dev, 10959d0f2daSSong Hongyan struct iio_chan_spec const *chan, 11059d0f2daSSong Hongyan int val, int val2, long mask) 11159d0f2daSSong Hongyan { 11259d0f2daSSong Hongyan struct temperature_state *temp_st = iio_priv(indio_dev); 11359d0f2daSSong Hongyan 11459d0f2daSSong Hongyan switch (mask) { 11559d0f2daSSong Hongyan case IIO_CHAN_INFO_SAMP_FREQ: 11659d0f2daSSong Hongyan return hid_sensor_write_samp_freq_value( 11759d0f2daSSong Hongyan &temp_st->common_attributes, val, val2); 11859d0f2daSSong Hongyan case IIO_CHAN_INFO_HYSTERESIS: 11959d0f2daSSong Hongyan return hid_sensor_write_raw_hyst_value( 12059d0f2daSSong Hongyan &temp_st->common_attributes, val, val2); 12159d0f2daSSong Hongyan default: 12259d0f2daSSong Hongyan return -EINVAL; 12359d0f2daSSong Hongyan } 12459d0f2daSSong Hongyan } 12559d0f2daSSong Hongyan 12659d0f2daSSong Hongyan static const struct iio_info temperature_info = { 12759d0f2daSSong Hongyan .read_raw = &temperature_read_raw, 12859d0f2daSSong Hongyan .write_raw = &temperature_write_raw, 12959d0f2daSSong Hongyan }; 13059d0f2daSSong Hongyan 13159d0f2daSSong Hongyan /* Callback handler to send event after all samples are received and captured */ 13259d0f2daSSong Hongyan static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, 13359d0f2daSSong Hongyan unsigned int usage_id, void *pdev) 13459d0f2daSSong Hongyan { 13559d0f2daSSong Hongyan struct iio_dev *indio_dev = platform_get_drvdata(pdev); 13659d0f2daSSong Hongyan struct temperature_state *temp_st = iio_priv(indio_dev); 13759d0f2daSSong Hongyan 13859d0f2daSSong Hongyan if (atomic_read(&temp_st->common_attributes.data_ready)) 13959d0f2daSSong Hongyan iio_push_to_buffers_with_timestamp(indio_dev, 14059d0f2daSSong Hongyan &temp_st->temperature_data, 14159d0f2daSSong Hongyan iio_get_time_ns(indio_dev)); 14259d0f2daSSong Hongyan 14359d0f2daSSong Hongyan return 0; 14459d0f2daSSong Hongyan } 14559d0f2daSSong Hongyan 14659d0f2daSSong Hongyan /* Capture samples in local storage */ 14759d0f2daSSong Hongyan static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev, 14859d0f2daSSong Hongyan unsigned int usage_id, size_t raw_len, 14959d0f2daSSong Hongyan char *raw_data, void *pdev) 15059d0f2daSSong Hongyan { 15159d0f2daSSong Hongyan struct iio_dev *indio_dev = platform_get_drvdata(pdev); 15259d0f2daSSong Hongyan struct temperature_state *temp_st = iio_priv(indio_dev); 15359d0f2daSSong Hongyan 15459d0f2daSSong Hongyan switch (usage_id) { 15559d0f2daSSong Hongyan case HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE: 15659d0f2daSSong Hongyan temp_st->temperature_data = *(s32 *)raw_data; 15759d0f2daSSong Hongyan return 0; 15859d0f2daSSong Hongyan default: 15959d0f2daSSong Hongyan return -EINVAL; 16059d0f2daSSong Hongyan } 16159d0f2daSSong Hongyan } 16259d0f2daSSong Hongyan 16359d0f2daSSong Hongyan /* Parse report which is specific to an usage id*/ 16459d0f2daSSong Hongyan static int temperature_parse_report(struct platform_device *pdev, 16559d0f2daSSong Hongyan struct hid_sensor_hub_device *hsdev, 16659d0f2daSSong Hongyan struct iio_chan_spec *channels, 16759d0f2daSSong Hongyan unsigned int usage_id, 16859d0f2daSSong Hongyan struct temperature_state *st) 16959d0f2daSSong Hongyan { 17059d0f2daSSong Hongyan int ret; 17159d0f2daSSong Hongyan 17259d0f2daSSong Hongyan ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, 17359d0f2daSSong Hongyan usage_id, 17459d0f2daSSong Hongyan HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE, 17559d0f2daSSong Hongyan &st->temperature_attr); 17659d0f2daSSong Hongyan if (ret < 0) 17759d0f2daSSong Hongyan return ret; 17859d0f2daSSong Hongyan 17959d0f2daSSong Hongyan temperature_adjust_channel_bit_mask(channels, 0, 18059d0f2daSSong Hongyan st->temperature_attr.size); 18159d0f2daSSong Hongyan 18259d0f2daSSong Hongyan st->scale_precision = hid_sensor_format_scale( 18359d0f2daSSong Hongyan HID_USAGE_SENSOR_TEMPERATURE, 18459d0f2daSSong Hongyan &st->temperature_attr, 18559d0f2daSSong Hongyan &st->scale_pre_decml, &st->scale_post_decml); 18659d0f2daSSong Hongyan 18759d0f2daSSong Hongyan /* Set Sensitivity field ids, when there is no individual modifier */ 18859d0f2daSSong Hongyan if (st->common_attributes.sensitivity.index < 0) 18959d0f2daSSong Hongyan sensor_hub_input_get_attribute_info(hsdev, 19059d0f2daSSong Hongyan HID_FEATURE_REPORT, usage_id, 19159d0f2daSSong Hongyan HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | 19259d0f2daSSong Hongyan HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE, 19359d0f2daSSong Hongyan &st->common_attributes.sensitivity); 19459d0f2daSSong Hongyan 19559d0f2daSSong Hongyan return ret; 19659d0f2daSSong Hongyan } 19759d0f2daSSong Hongyan 19859d0f2daSSong Hongyan static struct hid_sensor_hub_callbacks temperature_callbacks = { 19959d0f2daSSong Hongyan .send_event = &temperature_proc_event, 20059d0f2daSSong Hongyan .capture_sample = &temperature_capture_sample, 20159d0f2daSSong Hongyan }; 20259d0f2daSSong Hongyan 20359d0f2daSSong Hongyan /* Function to initialize the processing for usage id */ 20459d0f2daSSong Hongyan static int hid_temperature_probe(struct platform_device *pdev) 20559d0f2daSSong Hongyan { 20659d0f2daSSong Hongyan static const char *name = "temperature"; 20759d0f2daSSong Hongyan struct iio_dev *indio_dev; 20859d0f2daSSong Hongyan struct temperature_state *temp_st; 20959d0f2daSSong Hongyan struct iio_chan_spec *temp_chans; 21059d0f2daSSong Hongyan struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); 21159d0f2daSSong Hongyan int ret; 21259d0f2daSSong Hongyan 21359d0f2daSSong Hongyan indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*temp_st)); 21459d0f2daSSong Hongyan if (!indio_dev) 21559d0f2daSSong Hongyan return -ENOMEM; 21659d0f2daSSong Hongyan 21759d0f2daSSong Hongyan temp_st = iio_priv(indio_dev); 21859d0f2daSSong Hongyan temp_st->common_attributes.hsdev = hsdev; 21959d0f2daSSong Hongyan temp_st->common_attributes.pdev = pdev; 22059d0f2daSSong Hongyan 22159d0f2daSSong Hongyan ret = hid_sensor_parse_common_attributes(hsdev, 22259d0f2daSSong Hongyan HID_USAGE_SENSOR_TEMPERATURE, 22359d0f2daSSong Hongyan &temp_st->common_attributes); 22459d0f2daSSong Hongyan if (ret) 22559d0f2daSSong Hongyan return ret; 22659d0f2daSSong Hongyan 22759d0f2daSSong Hongyan temp_chans = devm_kmemdup(&indio_dev->dev, temperature_channels, 22859d0f2daSSong Hongyan sizeof(temperature_channels), GFP_KERNEL); 22959d0f2daSSong Hongyan if (!temp_chans) 23059d0f2daSSong Hongyan return -ENOMEM; 23159d0f2daSSong Hongyan 23259d0f2daSSong Hongyan ret = temperature_parse_report(pdev, hsdev, temp_chans, 23359d0f2daSSong Hongyan HID_USAGE_SENSOR_TEMPERATURE, temp_st); 23459d0f2daSSong Hongyan if (ret) 23559d0f2daSSong Hongyan return ret; 23659d0f2daSSong Hongyan 23759d0f2daSSong Hongyan indio_dev->channels = temp_chans; 23859d0f2daSSong Hongyan indio_dev->num_channels = ARRAY_SIZE(temperature_channels); 23959d0f2daSSong Hongyan indio_dev->dev.parent = &pdev->dev; 24059d0f2daSSong Hongyan indio_dev->info = &temperature_info; 24159d0f2daSSong Hongyan indio_dev->name = name; 24259d0f2daSSong Hongyan indio_dev->modes = INDIO_DIRECT_MODE; 24359d0f2daSSong Hongyan 24459d0f2daSSong Hongyan ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, 24559d0f2daSSong Hongyan &iio_pollfunc_store_time, NULL, NULL); 24659d0f2daSSong Hongyan if (ret) 24759d0f2daSSong Hongyan return ret; 24859d0f2daSSong Hongyan 24959d0f2daSSong Hongyan atomic_set(&temp_st->common_attributes.data_ready, 0); 25059d0f2daSSong Hongyan ret = hid_sensor_setup_trigger(indio_dev, name, 25159d0f2daSSong Hongyan &temp_st->common_attributes); 25259d0f2daSSong Hongyan if (ret) 25359d0f2daSSong Hongyan return ret; 25459d0f2daSSong Hongyan 25559d0f2daSSong Hongyan platform_set_drvdata(pdev, indio_dev); 25659d0f2daSSong Hongyan 25759d0f2daSSong Hongyan temperature_callbacks.pdev = pdev; 25859d0f2daSSong Hongyan ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE, 25959d0f2daSSong Hongyan &temperature_callbacks); 26059d0f2daSSong Hongyan if (ret) 26159d0f2daSSong Hongyan goto error_remove_trigger; 26259d0f2daSSong Hongyan 26359d0f2daSSong Hongyan ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); 26459d0f2daSSong Hongyan if (ret) 26559d0f2daSSong Hongyan goto error_remove_callback; 26659d0f2daSSong Hongyan 26759d0f2daSSong Hongyan return ret; 26859d0f2daSSong Hongyan 26959d0f2daSSong Hongyan error_remove_callback: 27059d0f2daSSong Hongyan sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE); 27159d0f2daSSong Hongyan error_remove_trigger: 27259d0f2daSSong Hongyan hid_sensor_remove_trigger(&temp_st->common_attributes); 27359d0f2daSSong Hongyan return ret; 27459d0f2daSSong Hongyan } 27559d0f2daSSong Hongyan 27659d0f2daSSong Hongyan /* Function to deinitialize the processing for usage id */ 27759d0f2daSSong Hongyan static int hid_temperature_remove(struct platform_device *pdev) 27859d0f2daSSong Hongyan { 27959d0f2daSSong Hongyan struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); 28059d0f2daSSong Hongyan struct iio_dev *indio_dev = platform_get_drvdata(pdev); 28159d0f2daSSong Hongyan struct temperature_state *temp_st = iio_priv(indio_dev); 28259d0f2daSSong Hongyan 28359d0f2daSSong Hongyan sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE); 28459d0f2daSSong Hongyan hid_sensor_remove_trigger(&temp_st->common_attributes); 28559d0f2daSSong Hongyan 28659d0f2daSSong Hongyan return 0; 28759d0f2daSSong Hongyan } 28859d0f2daSSong Hongyan 28959d0f2daSSong Hongyan static const struct platform_device_id hid_temperature_ids[] = { 29059d0f2daSSong Hongyan { 29159d0f2daSSong Hongyan /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ 29259d0f2daSSong Hongyan .name = "HID-SENSOR-200033", 29359d0f2daSSong Hongyan }, 29459d0f2daSSong Hongyan { /* sentinel */ } 29559d0f2daSSong Hongyan }; 29659d0f2daSSong Hongyan MODULE_DEVICE_TABLE(platform, hid_temperature_ids); 29759d0f2daSSong Hongyan 29859d0f2daSSong Hongyan static struct platform_driver hid_temperature_platform_driver = { 29959d0f2daSSong Hongyan .id_table = hid_temperature_ids, 30059d0f2daSSong Hongyan .driver = { 30159d0f2daSSong Hongyan .name = "temperature-sensor", 30259d0f2daSSong Hongyan .pm = &hid_sensor_pm_ops, 30359d0f2daSSong Hongyan }, 30459d0f2daSSong Hongyan .probe = hid_temperature_probe, 30559d0f2daSSong Hongyan .remove = hid_temperature_remove, 30659d0f2daSSong Hongyan }; 30759d0f2daSSong Hongyan module_platform_driver(hid_temperature_platform_driver); 30859d0f2daSSong Hongyan 30959d0f2daSSong Hongyan MODULE_DESCRIPTION("HID Environmental temperature sensor"); 31059d0f2daSSong Hongyan MODULE_AUTHOR("Song Hongyan <hongyan.song@intel.com>"); 31159d0f2daSSong Hongyan MODULE_LICENSE("GPL v2"); 312