cregit-Linux how code gets into the kernel

Release 4.11 drivers/iio/humidity/hts221_core.c

/*
 * STMicroelectronics hts221 sensor driver
 *
 * Copyright 2016 STMicroelectronics Inc.
 *
 * Lorenzo Bianconi <lorenzo.bianconi@st.com>
 *
 * Licensed under the GPL-2.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/iio/sysfs.h>
#include <linux/delay.h>
#include <asm/unaligned.h>

#include "hts221.h"


#define HTS221_REG_WHOAMI_ADDR		0x0f

#define HTS221_REG_WHOAMI_VAL		0xbc


#define HTS221_REG_CNTRL1_ADDR		0x20

#define HTS221_REG_CNTRL2_ADDR		0x21

#define HTS221_REG_CNTRL3_ADDR		0x22


#define HTS221_REG_AVG_ADDR		0x10

#define HTS221_REG_H_OUT_L		0x28

#define HTS221_REG_T_OUT_L		0x2a


#define HTS221_HUMIDITY_AVG_MASK	0x07

#define HTS221_TEMP_AVG_MASK		0x38


#define HTS221_ODR_MASK			0x87

#define HTS221_BDU_MASK			BIT(2)


#define HTS221_DRDY_MASK		BIT(2)


#define HTS221_ENABLE_SENSOR		BIT(7)


#define HTS221_HUMIDITY_AVG_4		0x00 
/* 0.4 %RH */

#define HTS221_HUMIDITY_AVG_8		0x01 
/* 0.3 %RH */

#define HTS221_HUMIDITY_AVG_16		0x02 
/* 0.2 %RH */

#define HTS221_HUMIDITY_AVG_32		0x03 
/* 0.15 %RH */

#define HTS221_HUMIDITY_AVG_64		0x04 
/* 0.1 %RH */

#define HTS221_HUMIDITY_AVG_128		0x05 
/* 0.07 %RH */

#define HTS221_HUMIDITY_AVG_256		0x06 
/* 0.05 %RH */

#define HTS221_HUMIDITY_AVG_512		0x07 
/* 0.03 %RH */


#define HTS221_TEMP_AVG_2		0x00 
/* 0.08 degC */

#define HTS221_TEMP_AVG_4		0x08 
/* 0.05 degC */

#define HTS221_TEMP_AVG_8		0x10 
/* 0.04 degC */

#define HTS221_TEMP_AVG_16		0x18 
/* 0.03 degC */

#define HTS221_TEMP_AVG_32		0x20 
/* 0.02 degC */

#define HTS221_TEMP_AVG_64		0x28 
/* 0.015 degC */

#define HTS221_TEMP_AVG_128		0x30 
/* 0.01 degC */

#define HTS221_TEMP_AVG_256		0x38 
/* 0.007 degC */

/* calibration registers */

#define HTS221_REG_0RH_CAL_X_H		0x36

#define HTS221_REG_1RH_CAL_X_H		0x3a

#define HTS221_REG_0RH_CAL_Y_H		0x30

#define HTS221_REG_1RH_CAL_Y_H		0x31

#define HTS221_REG_0T_CAL_X_L		0x3c

#define HTS221_REG_1T_CAL_X_L		0x3e

#define HTS221_REG_0T_CAL_Y_H		0x32

#define HTS221_REG_1T_CAL_Y_H		0x33

#define HTS221_REG_T1_T0_CAL_Y_H	0x35


struct hts221_odr {
	
u8 hz;
	
u8 val;
};


struct hts221_avg {
	
u8 addr;
	
u8 mask;
	
struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
};


static const struct hts221_odr hts221_odr_table[] = {
	{  1, 0x01 },	/* 1Hz */
	{  7, 0x02 },	/* 7Hz */
	{ 13, 0x03 },	/* 12.5Hz */
};


static const struct hts221_avg hts221_avg_list[] = {
	{
		.addr = HTS221_REG_AVG_ADDR,
		.mask = HTS221_HUMIDITY_AVG_MASK,
		.avg_avl = {
			{ 4, HTS221_HUMIDITY_AVG_4 },
			{ 8, HTS221_HUMIDITY_AVG_8 },
			{ 16, HTS221_HUMIDITY_AVG_16 },
			{ 32, HTS221_HUMIDITY_AVG_32 },
			{ 64, HTS221_HUMIDITY_AVG_64 },
			{ 128, HTS221_HUMIDITY_AVG_128 },
			{ 256, HTS221_HUMIDITY_AVG_256 },
			{ 512, HTS221_HUMIDITY_AVG_512 },
                },
        },
	{
		.addr = HTS221_REG_AVG_ADDR,
		.mask = HTS221_TEMP_AVG_MASK,
		.avg_avl = {
			{ 2, HTS221_TEMP_AVG_2 },
			{ 4, HTS221_TEMP_AVG_4 },
			{ 8, HTS221_TEMP_AVG_8 },
			{ 16, HTS221_TEMP_AVG_16 },
			{ 32, HTS221_TEMP_AVG_32 },
			{ 64, HTS221_TEMP_AVG_64 },
			{ 128, HTS221_TEMP_AVG_128 },
			{ 256, HTS221_TEMP_AVG_256 },
                },
        },
};


static const struct iio_chan_spec hts221_channels[] = {
	{
		.type = IIO_HUMIDITYRELATIVE,
		.address = HTS221_REG_H_OUT_L,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
				      BIT(IIO_CHAN_INFO_OFFSET) |
				      BIT(IIO_CHAN_INFO_SCALE) |
				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
		.scan_index = 0,
		.scan_type = {
			.sign = 's',
			.realbits = 16,
			.storagebits = 16,
			.endianness = IIO_LE,
                },
        },
	{
		.type = IIO_TEMP,
		.address = HTS221_REG_T_OUT_L,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
				      BIT(IIO_CHAN_INFO_OFFSET) |
				      BIT(IIO_CHAN_INFO_SCALE) |
				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
		.scan_index = 1,
		.scan_type = {
			.sign = 's',
			.realbits = 16,
			.storagebits = 16,
			.endianness = IIO_LE,
                },
        },
	IIO_CHAN_SOFT_TIMESTAMP(2),
};


static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val) { u8 data; int err; mutex_lock(&hw->lock); err = hw->tf->read(hw->dev, addr, sizeof(data), &data); if (err < 0) { dev_err(hw->dev, "failed to read %02x register\n", addr); goto unlock; } data = (data & ~mask) | (val & mask); err = hw->tf->write(hw->dev, addr, sizeof(data), &data); if (err < 0) dev_err(hw->dev, "failed to write %02x register\n", addr); unlock: mutex_unlock(&hw->lock); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi147100.00%1100.00%
Total147100.00%1100.00%


static int hts221_check_whoami(struct hts221_hw *hw) { u8 data; int err; err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data), &data); if (err < 0) { dev_err(hw->dev, "failed to read whoami register\n"); return err; } if (data != HTS221_REG_WHOAMI_VAL) { dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n", data, HTS221_REG_WHOAMI_VAL); return -ENODEV; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi88100.00%1100.00%
Total88100.00%1100.00%


int hts221_config_drdy(struct hts221_hw *hw, bool enable) { u8 val = enable ? BIT(2) : 0; int err; err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR, HTS221_DRDY_MASK, val); return err < 0 ? err : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi50100.00%1100.00%
Total50100.00%1100.00%


static int hts221_update_odr(struct hts221_hw *hw, u8 odr) { int i, err; u8 val; for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++) if (hts221_odr_table[i].hz == odr) break; if (i == ARRAY_SIZE(hts221_odr_table)) return -EINVAL; val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val; err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, HTS221_ODR_MASK, val); if (err < 0) return err; hw->odr = odr; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi107100.00%1100.00%
Total107100.00%1100.00%


static int hts221_update_avg(struct hts221_hw *hw, enum hts221_sensor_type type, u16 val) { int i, err; const struct hts221_avg *avg = &hts221_avg_list[type]; for (i = 0; i < HTS221_AVG_DEPTH; i++) if (avg->avg_avl[i].avg == val) break; if (i == HTS221_AVG_DEPTH) return -EINVAL; err = hts221_write_with_mask(hw, avg->addr, avg->mask, avg->avg_avl[i].val); if (err < 0) return err; hw->sensors[type].cur_avg_idx = i; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi119100.00%1100.00%
Total119100.00%1100.00%


static ssize_t hts221_sysfs_sampling_freq(struct device *dev, struct device_attribute *attr, char *buf) { int i; ssize_t len = 0; for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", hts221_odr_table[i].hz); buf[len - 1] = '\n'; return len; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi78100.00%1100.00%
Total78100.00%1100.00%


static ssize_t hts221_sysfs_rh_oversampling_avail(struct device *dev, struct device_attribute *attr, char *buf) { const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H]; ssize_t len = 0; int i; for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", avg->avg_avl[i].avg); buf[len - 1] = '\n'; return len; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi94100.00%1100.00%
Total94100.00%1100.00%


static ssize_t hts221_sysfs_temp_oversampling_avail(struct device *dev, struct device_attribute *attr, char *buf) { const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T]; ssize_t len = 0; int i; for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", avg->avg_avl[i].avg); buf[len - 1] = '\n'; return len; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi94100.00%1100.00%
Total94100.00%1100.00%


int hts221_power_on(struct hts221_hw *hw) { return hts221_update_odr(hw, hw->odr); }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi20100.00%1100.00%
Total20100.00%1100.00%


int hts221_power_off(struct hts221_hw *hw) { u8 data[] = {0x00, 0x00}; return hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data), data); }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi41100.00%1100.00%
Total41100.00%1100.00%


static int hts221_parse_temp_caldata(struct hts221_hw *hw) { int err, *slope, *b_gen; s16 cal_x0, cal_x1, cal_y0, cal_y1; u8 cal0, cal1; err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H, sizeof(cal0), &cal0); if (err < 0) return err; err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H, sizeof(cal1), &cal1); if (err < 0) return err; cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0; err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H, sizeof(cal0), &cal0); if (err < 0) return err; cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0; err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0), (u8 *)&cal_x0); if (err < 0) return err; cal_x0 = le16_to_cpu(cal_x0); err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1), (u8 *)&cal_x1); if (err < 0) return err; cal_x1 = le16_to_cpu(cal_x1); slope = &hw->sensors[HTS221_SENSOR_T].slope; b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen; *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0); *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) / (cal_x1 - cal_x0); *b_gen *= 8; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi329100.00%1100.00%
Total329100.00%1100.00%


static int hts221_parse_rh_caldata(struct hts221_hw *hw) { int err, *slope, *b_gen; s16 cal_x0, cal_x1, cal_y0, cal_y1; u8 data; err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data), &data); if (err < 0) return err; cal_y0 = data; err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data), &data); if (err < 0) return err; cal_y1 = data; err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0), (u8 *)&cal_x0); if (err < 0) return err; cal_x0 = le16_to_cpu(cal_x0); err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1), (u8 *)&cal_x1); if (err < 0) return err; cal_x1 = le16_to_cpu(cal_x1); slope = &hw->sensors[HTS221_SENSOR_H].slope; b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen; *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0); *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) / (cal_x1 - cal_x0); *b_gen *= 8; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi270100.00%1100.00%
Total270100.00%1100.00%


static int hts221_get_sensor_scale(struct hts221_hw *hw, enum iio_chan_type ch_type, int *val, int *val2) { s64 tmp; s32 rem, div, data; switch (ch_type) { case IIO_HUMIDITYRELATIVE: data = hw->sensors[HTS221_SENSOR_H].slope; div = (1 << 4) * 1000; break; case IIO_TEMP: data = hw->sensors[HTS221_SENSOR_T].slope; div = (1 << 6) * 1000; break; default: return -EINVAL; } tmp = div_s64(data * 1000000000LL, div); tmp = div_s64_rem(tmp, 1000000000LL, &rem); *val = tmp; *val2 = rem; return IIO_VAL_INT_PLUS_NANO; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi130100.00%1100.00%
Total130100.00%1100.00%


static int hts221_get_sensor_offset(struct hts221_hw *hw, enum iio_chan_type ch_type, int *val, int *val2) { s64 tmp; s32 rem, div, data; switch (ch_type) { case IIO_HUMIDITYRELATIVE: data = hw->sensors[HTS221_SENSOR_H].b_gen; div = hw->sensors[HTS221_SENSOR_H].slope; break; case IIO_TEMP: data = hw->sensors[HTS221_SENSOR_T].b_gen; div = hw->sensors[HTS221_SENSOR_T].slope; break; default: return -EINVAL; } tmp = div_s64(data * 1000000000LL, div); tmp = div_s64_rem(tmp, 1000000000LL, &rem); *val = tmp; *val2 = rem; return IIO_VAL_INT_PLUS_NANO; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi132100.00%1100.00%
Total132100.00%1100.00%


static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val) { u8 data[HTS221_DATA_SIZE]; int err; err = hts221_power_on(hw); if (err < 0) return err; msleep(50); err = hw->tf->read(hw->dev, addr, sizeof(data), data); if (err < 0) return err; hts221_power_off(hw); *val = (s16)get_unaligned_le16(data); return IIO_VAL_INT; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi98100.00%1100.00%
Total98100.00%1100.00%


static int hts221_read_raw(struct iio_dev *iio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) { struct hts221_hw *hw = iio_priv(iio_dev); int ret; ret = iio_device_claim_direct_mode(iio_dev); if (ret) return ret; switch (mask) { case IIO_CHAN_INFO_RAW: ret = hts221_read_oneshot(hw, ch->address, val); break; case IIO_CHAN_INFO_SCALE: ret = hts221_get_sensor_scale(hw, ch->type, val, val2); break; case IIO_CHAN_INFO_OFFSET: ret = hts221_get_sensor_offset(hw, ch->type, val, val2); break; case IIO_CHAN_INFO_SAMP_FREQ: *val = hw->odr; ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { u8 idx; const struct hts221_avg *avg; switch (ch->type) { case IIO_HUMIDITYRELATIVE: avg = &hts221_avg_list[HTS221_SENSOR_H]; idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx; *val = avg->avg_avl[idx].avg; ret = IIO_VAL_INT; break; case IIO_TEMP: avg = &hts221_avg_list[HTS221_SENSOR_T]; idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx; *val = avg->avg_avl[idx].avg; ret = IIO_VAL_INT; break; default: ret = -EINVAL; break; } break; } default: ret = -EINVAL; break; } iio_device_release_direct_mode(iio_dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi254100.00%1100.00%
Total254100.00%1100.00%


static int hts221_write_raw(struct iio_dev *iio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct hts221_hw *hw = iio_priv(iio_dev); int ret; ret = iio_device_claim_direct_mode(iio_dev); if (ret) return ret; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: ret = hts221_update_odr(hw, val); break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: switch (chan->type) { case IIO_HUMIDITYRELATIVE: ret = hts221_update_avg(hw, HTS221_SENSOR_H, val); break; case IIO_TEMP: ret = hts221_update_avg(hw, HTS221_SENSOR_T, val); break; default: ret = -EINVAL; break; } break; default: ret = -EINVAL; break; } iio_device_release_direct_mode(iio_dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi136100.00%1100.00%
Total136100.00%1100.00%


static int hts221_validate_trigger(struct iio_dev *iio_dev, struct iio_trigger *trig) { struct hts221_hw *hw = iio_priv(iio_dev); return hw->trig == trig ? 0 : -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi38100.00%1100.00%
Total38100.00%1100.00%

static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO, hts221_sysfs_rh_oversampling_avail, NULL, 0); static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO, hts221_sysfs_temp_oversampling_avail, NULL, 0); static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq); static struct attribute *hts221_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr, &iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr, NULL, }; static const struct attribute_group hts221_attribute_group = { .attrs = hts221_attributes, }; static const struct iio_info hts221_info = { .driver_module = THIS_MODULE, .attrs = &hts221_attribute_group, .read_raw = hts221_read_raw, .write_raw = hts221_write_raw, .validate_trigger = hts221_validate_trigger, }; static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
int hts221_probe(struct iio_dev *iio_dev) { struct hts221_hw *hw = iio_priv(iio_dev); int err; u8 data; mutex_init(&hw->lock); err = hts221_check_whoami(hw); if (err < 0) return err; hw->odr = hts221_odr_table[0].hz; iio_dev->modes = INDIO_DIRECT_MODE; iio_dev->dev.parent = hw->dev; iio_dev->available_scan_masks = hts221_scan_masks; iio_dev->channels = hts221_channels; iio_dev->num_channels = ARRAY_SIZE(hts221_channels); iio_dev->name = HTS221_DEV_NAME; iio_dev->info = &hts221_info; /* configure humidity sensor */ err = hts221_parse_rh_caldata(hw); if (err < 0) { dev_err(hw->dev, "failed to get rh calibration data\n"); return err; } data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg; err = hts221_update_avg(hw, HTS221_SENSOR_H, data); if (err < 0) { dev_err(hw->dev, "failed to set rh oversampling ratio\n"); return err; } /* configure temperature sensor */ err = hts221_parse_temp_caldata(hw); if (err < 0) { dev_err(hw->dev, "failed to get temperature calibration data\n"); return err; } data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg; err = hts221_update_avg(hw, HTS221_SENSOR_T, data); if (err < 0) { dev_err(hw->dev, "failed to set temperature oversampling ratio\n"); return err; } if (hw->irq > 0) { err = hts221_allocate_buffers(hw); if (err < 0) return err; err = hts221_allocate_trigger(hw); if (err) return err; } return devm_iio_device_register(hw->dev, iio_dev); }

Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi307100.00%1100.00%
Total307100.00%1100.00%

EXPORT_SYMBOL(hts221_probe); MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>"); MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Lorenzo Bianconi3232100.00%1100.00%
Total3232100.00%1100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.