锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

linux内核添加一个压力传感器设备树驱动

时间:2022-12-02 07:30:00 04ts传感器02ts传感器0280217518传感器tftf14传感器1000ul传感器1203v2力传感器

传感器可分为iio因此使用子系统iio子系统框架

需修改的文件:

1. Kconfig

文件路径

kernel/drivers/iio/pressure/Kconfig

Kconfig文件中添加

config ST_LPS22DF_IIO  tristate "STMicroelectronics LPS22DF sensor"  depends on (I2C || SPI_MASTER) && SYSFS  select IIO_BUFFER  select IIO_KFIFO_BUF  select ST_LPS22DF_I2C_IIO if (I2C)  select ST_LPS22DF_SPI_IIO if (SPI)  help           Say yes here to build support for the ST MEMS LPS22DF pressure and    temperature  sensor.     This driver can be built as a module. The module will be called    st-lps22df.  config ST_LPS22DF_I2C_IIO  tristate  depends on ST_LPS22DF_IIO  config ST_LPS22DF_SPI_IIO  tristate  depends on ST_LPS22DF_IIO 

2. Makefile

文件路径

kernel/drivers/iio/pressure/Makefile

Makefile 添加到文件中:

obj-$(CONFIG_ST_LPS22DF_IIO)  = st_lps22df.o obj-$(CONFIG_ST_LPS22DF_I2C_IIO)  = st_lps22df_i2c.o obj-$(CONFIG_ST_LPS22DF_SPI_IIO)  = st_lps22df_spi.o  st_lps22df-y  = st_lps22df_core.o st_lps22df_buffer.o

需要添加的文件

st_lps22df.h //头文件

st_lps22df_buffer.c

st_lps22df_core.c

st_lps22df_i2c.c //走i2c总线框架

st_lps22df_spi.c //走spi总线框架

3.设备树的添加

pressure@yy {  compatible = "st,lps22df";  reg = <0xyy>;  interrupt-parent = <&gpio>;  interrupts = <1 IRQ_TYPE_LEVEL_HIGH>; };

st_lps22df.h文件内容

/*  * STMicroelectronics lps22df driver  *  * Copyright 2021 STMicroelectronics Inc.  *  * Mario Tesi   *  * Licensed under the GPL-2.  */  #ifndef __ST_LPS22DF_H #define __ST_LPS22DF_H  #include  #include  #include  #include   #define ST_LPS22DF_MAX_FIFO_LENGTH  127  #define ST_LPS22DF_INTERRUPT_CFG_ADDR  0x0b #define ST_LPS22DF_LIR_MASK   BIT(2)  #define ST_LPS22DF_WHO_AM_I_ADDR  0x0f #define ST_LPS22DF_WHO_AM_I_VAL   0xb4  #define ST_LPS22DF_CTRL_REG1_ADDR  0x10 #define ST_LPS22DF_AVG_MASK   GENMASK(2, 0) #define ST_LPS22DF_ODR_MASK   GENMASK(6, 3)  #define ST_LPS22DF_CTRL_REG2_ADDR  0x11 #define ST_LPS22DF_SWRESET_MASK   BIT(2) #define ST_LPS22DF_BDU_MASK   BIT(3) #define ST_LPS22DF_EN_LPFP_MASK   BIT(4) #define ST_LPS22DF_BOOT_MASK   BIT(7)  #define ST_LPS22DF_CTRL3_ADDR   0x12 #define ST_LPS22DF_IF_ADD_INC_MASK  BIT(0) #define ST_LPS22DF_PP_OD_MASK   BIT(1) #define ST_LPS22DF_INT_H_L_MASK   BIT(3)  #define ST_LPS22DF_CTRL4_ADDR   0x13 #define ST_LPS22DF_INT_F_WTM_MASK  BIT(1)  #define ST_LPS22DF_FIFO_CTRL_ADDR  0x14 #define ST_LPS22DF_FIFO_MODE_MASK  GENMASK(1, 0)  #define ST_LPS22DF_FIFO_WTM_ADDR  0x15 #define ST_LPS22DF_FIFO_THS_MASK  GENMASK(6, 0)  #define ST_LPS22DF_FIFO_STATUS1_ADDR  0x25 #define ST_LPS22DF_FIFO_SRC_DIFF_MASK  GENMASK(7, 0)  #define ST_LPS22DF_FIFO_STATUS2_ADDR  0x26 #define ST_LPS22DF_FIFO_WTM_IA_MASK  BIT(7)  #define ST_LPS22DF_PRESS_OUT_XL_ADDR  0x28  #define ST_LPS22DF_TEMP_OUT_L_ADDR  0x2b  #define ST_LPS22DF_FIFO_DATA_OUT_PRESS_XL_ADDR 0x78  #define ST_LPS22DF_PRESS_FS_AVL_GAIN  (1000000000UL / 4096UL) #define ST_LPS22DF_TEMP_FS_AVL_GAIN  (1000000000UL / 100UL)  #define ST_LPS22DF_ODR_LIST_NUM   9  enum st_lps22df_sensor_type {  ST_LPS22DF_PRESS = 0,  ST_LPS22DF_TEMP,  ST_LPS22DF_SENSORS_NUMB, };  enum st_lps22df_fifo_mode {  ST_LPS22DF_BYPASS = 0x0,  ST_LPS22DF_STREAM = 0x2, };  #define ST_LPS22DF_PRESS_SAMPLE_LEN  3 #define ST_LPS22DF_TEMP_SAMPLE_LEN  2  #define ST_LPS22DF_TX_MAX_LENGTH  64 #define ST_LPS22DF_RX_MAX_LENGTH  ((ST_LPS22DF_MAX_FIFO_LENGTH   1) * \        ST_LPS22DF_PRESS_SAMPLE_LEN)  struct st_lps22df_transfer_buffer {  u8 rx_buf[ST_LPS22DF_RX_MAX_LENGTH];  u8 tx_buf[ST_LPS22DF_TX_MAX_LENGTH] ____cacheline_aligned; };  struct st_lps22df_transfer_function {  int (*write)(struct device *dev, u8 addr, int len, u8 *data);  int (*read)(struct device *dev, u8 addr, int len, u8 *data); };  struct st_lps22df_hw {  struct device *dev;  int irq;   struct mutex fifo_lock;  struct mutex lock;  u8 watermark;   struct iio_dev *iio_devs[ST_LPS22DF_SENSORS_NUMB];  u8 enable_mask;  u8 odr;   s64 last_fifo_ts;  s64 delta_ts;  s64 ts_irq;  s64 ts;   const struct st_lps22df_transfer_function *tf;  struct st_lps22df_transfer_buffer tb; };  struct st_lps22df_sensor {  struct st_lps22df_hw *hw;  enum st_lps22df_sensor_type type;  char name[32];   u32 gain;  u8 odr; };  int st_lps22df_common_probe(struct device *dev, int irq, const char *name,        const struct st_lps22df_transfer_function *tf_ops); int st_lps22df_write_with_mask(struct st_lps22df_hw *hw, u8 addr, u8 mask,           u8 data); int st_lps22df_allocate_buffers(struct st_lps22df_hw hw);
int st_lps22df_set_enable(struct st_lps22df_sensor *sensor, bool enable);
ssize_t st_lps22df_sysfs_set_hwfifo_watermark(struct device *dev,
					      struct device_attribute *attr,
					      const char *buf, size_t count);
ssize_t st_lps22df_sysfs_flush_fifo(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t size);

#endif /* __ST_LPS22DF_H */

st_lps22df_buffer.c 文件内容

/*
 * STMicroelectronics lps22df buffer driver
 *
 * Copyright 2021 STMicroelectronics Inc.
 *
 * Mario Tesi 
 *
 * Licensed under the GPL-2.
 */

#include 
#include 
#include 
#include 
#include 

#include "st_lps22df.h"

#define ST_LPS22DF_EWMA_LEVEL			96
#define ST_LPS22DF_EWMA_DIV			128

static inline s64 st_lps22df_ewma(s64 old, s64 new, int weight)
{
	s64 diff, incr;

	diff = new - old;
	incr = div_s64((ST_LPS22DF_EWMA_DIV - weight) * diff,
		       ST_LPS22DF_EWMA_DIV);

	return old + incr;
}

static inline s64 st_lps22df_get_time_ns(void)
{
	struct timespec ts;

	get_monotonic_boottime(&ts);

	return timespec_to_ns(&ts);
}

static int st_lps22df_set_fifo_mode(struct st_lps22df_hw *hw,
				    enum st_lps22df_fifo_mode mode)
{
	switch (mode) {
	case ST_LPS22DF_BYPASS:
	case ST_LPS22DF_STREAM:
		break;
	default:
		return -EINVAL;
	}

	return st_lps22df_write_with_mask(hw, ST_LPS22DF_FIFO_CTRL_ADDR,
					  ST_LPS22DF_FIFO_MODE_MASK, mode);
}

static int st_lps22df_update_fifo_watermark(struct st_lps22df_hw *hw, u8 val)
{
	int err;

	err = st_lps22df_write_with_mask(hw, ST_LPS22DF_FIFO_WTM_ADDR,
					 ST_LPS22DF_FIFO_THS_MASK, val);
	if (err < 0)
		return err;

	hw->watermark = val;

	return 0;
}

ssize_t st_lps22df_sysfs_set_hwfifo_watermark(struct device *dev,
					      struct device_attribute *attr,
					      const char *buf, size_t count)
{
	struct st_lps22df_sensor *sensor = iio_priv(dev_get_drvdata(dev));
	int err, watermark;

	err = kstrtoint(buf, 10, &watermark);
	if (err < 0)
		return err;

	if (watermark < 1 || watermark > ST_LPS22DF_MAX_FIFO_LENGTH)
		return -EINVAL;

	err = st_lps22df_update_fifo_watermark(sensor->hw, watermark);

	return err < 0 ? err : count;
}

static int st_lps22df_read_fifo(struct st_lps22df_hw *hw, s64 delta_ts)
{
	u8 iio_buff[ALIGN(sizeof(u32) + sizeof(s64), sizeof(s64))];
	u8 buff[ST_LPS22DF_RX_MAX_LENGTH];
	int err, i, read_len;
	__le16 fifo_status;

	err = hw->tf->read(hw->dev, ST_LPS22DF_FIFO_STATUS1_ADDR,
			   sizeof(fifo_status), (u8 *)&fifo_status);
	if (err < 0)
		return err;

	read_len = (le16_to_cpu(fifo_status) & ST_LPS22DF_FIFO_SRC_DIFF_MASK) *
		   ST_LPS22DF_PRESS_SAMPLE_LEN;
	if (!read_len)
		return 0;

	err = hw->tf->read(hw->dev, ST_LPS22DF_FIFO_DATA_OUT_PRESS_XL_ADDR,
			   read_len, buff);
	if (err < 0)
		return err;

	for (i = 0; i < read_len; i += ST_LPS22DF_PRESS_SAMPLE_LEN) {
		memcpy(iio_buff, buff + i, ST_LPS22DF_PRESS_SAMPLE_LEN);
		iio_push_to_buffers_with_timestamp(
				hw->iio_devs[ST_LPS22DF_PRESS],
				iio_buff, hw->ts);
		hw->ts += delta_ts;
	}

	hw->last_fifo_ts = hw->ts;

	return read_len;
}

ssize_t st_lps22df_sysfs_flush_fifo(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t size)
{
	struct iio_dev *indio_dev = dev_get_drvdata(dev);
	struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
	struct st_lps22df_hw *hw = sensor->hw;
	int len, dir;
	s64 fts;

	mutex_lock(&hw->fifo_lock);
	len = st_lps22df_read_fifo(hw, hw->delta_ts);
	hw->ts = st_lps22df_get_time_ns();
	hw->ts_irq = hw->ts;

	/* flush event timestamp must match with last sample pushed in fifo */
	if (len)
		fts = hw->ts;
	else
		fts = hw->last_fifo_ts;

	mutex_unlock(&hw->fifo_lock);

	dir = len > 0 ? IIO_EV_DIR_FIFO_DATA : IIO_EV_DIR_FIFO_EMPTY;
	iio_push_event(indio_dev,
		       IIO_UNMOD_EVENT_CODE(IIO_PRESSURE, -1,
					    IIO_EV_TYPE_FIFO_FLUSH, dir),
		       fts);

	return size;
}

static irqreturn_t st_lps22df_irq_handler(int irq, void *private)
{
	struct st_lps22df_hw *hw = private;
	s64 delta_ts, ts = st_lps22df_get_time_ns();

	delta_ts = div_s64((ts - hw->ts_irq), hw->watermark);
	if (hw->odr >= 50)
		hw->delta_ts = st_lps22df_ewma(hw->delta_ts, delta_ts,
					       ST_LPS22DF_EWMA_LEVEL);
	else
		hw->delta_ts = delta_ts;

	hw->ts_irq = ts;

	return IRQ_WAKE_THREAD;
}

static irqreturn_t st_lps22df_irq_thread(int irq, void *private)
{
	struct st_lps22df_hw *hw = private;

	mutex_lock(&hw->fifo_lock);
	st_lps22df_read_fifo(hw, hw->delta_ts);
	mutex_unlock(&hw->fifo_lock);

	return IRQ_HANDLED;
}

static int st_lps22df_buffer_preenable(struct iio_dev *indio_dev)
{
	struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
	struct st_lps22df_hw *hw = sensor->hw;
	int err;

	err = st_lps22df_set_fifo_mode(sensor->hw, ST_LPS22DF_STREAM);
	if (err < 0)
		return err;

	err = st_lps22df_update_fifo_watermark(hw, hw->watermark);
	if (err < 0)
		return err;

	err = st_lps22df_write_with_mask(sensor->hw, ST_LPS22DF_CTRL4_ADDR,
					 ST_LPS22DF_INT_F_WTM_MASK, true);
	if (err < 0)
		return err;

	err = st_lps22df_set_enable(sensor, true);
	if (err < 0)
		return err;

	hw->delta_ts = div_s64(1000000000UL, hw->odr);
	hw->ts = st_lps22df_get_time_ns();
	hw->ts_irq = hw->ts;

	return 0;
}

static int st_lps22df_buffer_postdisable(struct iio_dev *indio_dev)
{
	struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
	int err;

	err = st_lps22df_set_fifo_mode(sensor->hw, ST_LPS22DF_BYPASS);
	if (err < 0)
		return err;

	err = st_lps22df_write_with_mask(sensor->hw, ST_LPS22DF_CTRL4_ADDR,
					 ST_LPS22DF_INT_F_WTM_MASK, false);
	if (err < 0)
		return err;

	return st_lps22df_set_enable(sensor, false);
}

static const struct iio_buffer_setup_ops st_lps22df_buffer_ops = {
	.preenable = st_lps22df_buffer_preenable,
	.postdisable = st_lps22df_buffer_postdisable,
};

int st_lps22df_allocate_buffers(struct st_lps22df_hw *hw)
{
	struct iio_buffer *buffer;
	unsigned long irq_type;
	u8 int_active = 0;
	int err;

	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));

	switch (irq_type) {
	case IRQF_TRIGGER_HIGH:
	case IRQF_TRIGGER_RISING:
		int_active = 0;
		break;
	case IRQF_TRIGGER_LOW:
	case IRQF_TRIGGER_FALLING:
		int_active = 1;
		break;
	default:
		dev_info(hw->dev, "mode %lx unsupported\n", irq_type);

		return -EINVAL;
	}

	/* int pin active low */
	if (device_property_read_bool(hw->dev, "int-active-low")) {
		err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL3_ADDR,
						 ST_LPS22DF_INT_H_L_MASK, 1);
		if (err < 0)
			return err;
	}

	/* int pin open drain configuration */
	if (device_property_read_bool(hw->dev, "int-open-drain")) {
		err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL3_ADDR,
						 ST_LPS22DF_PP_OD_MASK, 1);
		if (err < 0)
			return err;
	}

	err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL3_ADDR,
					 ST_LPS22DF_INT_H_L_MASK,
					 int_active);
	if (err < 0)
		return err;

	err = devm_request_threaded_irq(hw->dev, hw->irq,
					st_lps22df_irq_handler,
					st_lps22df_irq_thread,
					irq_type | IRQF_ONESHOT,
					"lps22df", hw);
	if (err)
		return err;

	buffer = devm_iio_kfifo_allocate(hw->dev);
	if (!buffer)
		return -ENOMEM;

	iio_device_attach_buffer(hw->iio_devs[ST_LPS22DF_PRESS], buffer);
	hw->iio_devs[ST_LPS22DF_PRESS]->modes |= INDIO_BUFFER_SOFTWARE;
	hw->iio_devs[ST_LPS22DF_PRESS]->setup_ops = &st_lps22df_buffer_ops;

	return 0;
}

MODULE_DESCRIPTION("STMicroelectronics lps22df buffer driver");
MODULE_AUTHOR("Mario Tesi ");
MODULE_LICENSE("GPL v2");

st_lps22df_core.c 文件内容

/*
 * STMicroelectronics lps22df driver
 *
 * Copyright 2021 STMicroelectronics Inc.
 *
 * Mario Tesi 
 *
 * Licensed under the GPL-2.
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "st_lps22df.h"

struct st_lps22df_odr_table_t {
	u8 addr;
	u8 mask;
	u8 odr_avl[ST_LPS22DF_ODR_LIST_NUM];
};

const static struct st_lps22df_odr_table_t st_lps22df_odr_table = {
	.addr = ST_LPS22DF_CTRL_REG1_ADDR,
	.mask = ST_LPS22DF_ODR_MASK,
	.odr_avl = { 0, 1, 4, 10, 25, 50, 75, 100, 200 },
};

const struct iio_event_spec st_lps22df_fifo_flush_event = {
	.type = IIO_EV_TYPE_FIFO_FLUSH,
	.dir = IIO_EV_DIR_EITHER,
};

static const struct iio_chan_spec st_lps22df_press_channels[] = {
	{
		.type = IIO_PRESSURE,
		.address = ST_LPS22DF_PRESS_OUT_XL_ADDR,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
				      BIT(IIO_CHAN_INFO_SCALE),
		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
		.channel2 = IIO_NO_MOD,
		.scan_index = 0,
		.scan_type = {
			.sign = 'u',
			.realbits = 24,
			.storagebits = 32,
			.endianness = IIO_LE,
		},
	},
	{
		.type = IIO_PRESSURE,
		.scan_index = -1,
		.indexed = -1,
		.event_spec = &st_lps22df_fifo_flush_event,
		.num_event_specs = 1,
	},
	IIO_CHAN_SOFT_TIMESTAMP(1)
};

static const struct iio_chan_spec st_lps22df_temp_channels[] = {
	{
		.type = IIO_TEMP,
		.address = ST_LPS22DF_TEMP_OUT_L_ADDR,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
				      BIT(IIO_CHAN_INFO_SCALE),
		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
		.channel2 = IIO_NO_MOD,
		.scan_index = 0,
		.scan_type = {
			.sign = 's',
			.realbits = 16,
			.storagebits = 16,
			.endianness = IIO_LE,
		},
	},
};

int st_lps22df_write_with_mask(struct st_lps22df_hw *hw, u8 addr, u8 mask,
			       u8 val)
{
	int err;
	u8 data;

	mutex_lock(&hw->lock);

	err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
	if (err < 0)
		goto unlock;

	data = (data & ~mask) | ((val << __ffs(mask)) & mask);
	err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
unlock:
	mutex_unlock(&hw->lock);

	return err;
}

static int st_lps22df_check_whoami(struct st_lps22df_hw *hw)
{
	int err;
	u8 data;

	err = hw->tf->read(hw->dev, ST_LPS22DF_WHO_AM_I_ADDR, sizeof(data),
			   &data);
	if (err < 0) {
		dev_err(hw->dev, "failed to read Who-Am-I register\n");

		return err;
	}

	if (data != ST_LPS22DF_WHO_AM_I_VAL) {
		dev_err(hw->dev, "Who-Am-I value not valid (%x)\n", data);

		return -ENODEV;
	}

	return 0;
}

static int st_lps22df_get_odr(struct st_lps22df_sensor *sensor, u8 odr)
{
	int i;

	for (i = 0; i < ST_LPS22DF_ODR_LIST_NUM; i++) {
		if (st_lps22df_odr_table.odr_avl[i] == odr)
			break;
	}

	return i == ST_LPS22DF_ODR_LIST_NUM ? -EINVAL : i;
}

int st_lps22df_set_enable(struct st_lps22df_sensor *sensor, bool enable)
{
	struct st_lps22df_hw *hw = sensor->hw;
	u32 max_odr = enable ? sensor->odr : 0;
	int i;

	for (i = 0; i < ST_LPS22DF_SENSORS_NUMB; i++) {
		if (sensor->type == i)
			continue;

		if (hw->enable_mask & BIT(i)) {
			struct st_lps22df_sensor *temp;

			temp = iio_priv(hw->iio_devs[i]);
			max_odr = max_t(u32, max_odr, temp->odr);
		}
	}

	if (max_odr != hw->odr) {
		int err, ret;

		ret = st_lps22df_get_odr(sensor, max_odr);
		if (ret < 0)
			return ret;

		err = st_lps22df_write_with_mask(hw, st_lps22df_odr_table.addr,
						 st_lps22df_odr_table.mask,
						 ret);
		if (err < 0)
			return err;

		hw->odr = max_odr;
	}

	if (enable)
		hw->enable_mask |= BIT(sensor->type);
	else
		hw->enable_mask &= ~BIT(sensor->type);

	return 0;
}

int st_lps22df_init_sensors(struct st_lps22df_hw *hw)
{
	int err;

	/* reboot memory content */
	err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL_REG2_ADDR,
					 ST_LPS22DF_BOOT_MASK, 1);
	if (err < 0)
		return err;

	usleep_range(8000, 10000);

	/* soft reset the device on power on */
	err = st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL_REG2_ADDR,
					 ST_LPS22DF_SWRESET_MASK, 1);
	if (err < 0)
		return err;

	usleep_range(100, 200);

	/* enable latched interrupt mode */
	err = st_lps22df_write_with_mask(hw, ST_LPS22DF_INTERRUPT_CFG_ADDR,
					 ST_LPS22DF_LIR_MASK, 1);
	if (err < 0)
		return err;

	/* enable BDU */
	return st_lps22df_write_with_mask(hw, ST_LPS22DF_CTRL_REG2_ADDR,
					  ST_LPS22DF_BDU_MASK, 1);
}

static ssize_t
st_lps22df_get_sampling_frequency_avail(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	int i, len = 0;

	for (i = 1; i < ST_LPS22DF_ODR_LIST_NUM; i++) {
		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
				 st_lps22df_odr_table.odr_avl[i]);
	}

	buf[len - 1] = '\n';

	return len;
}

static ssize_t
st_lps22df_sysfs_get_hwfifo_watermark(struct device *dev,
				      struct device_attribute *attr, char *buf)
{
	struct st_lps22df_sensor *sensor = iio_priv(dev_get_drvdata(dev));

	return sprintf(buf, "%d\n", sensor->hw->watermark);
}

static ssize_t
st_lps22df_sysfs_get_hwfifo_watermark_max(struct device *dev,
					  struct device_attribute *attr,
					  char *buf)
{
	return sprintf(buf, "%d\n", ST_LPS22DF_MAX_FIFO_LENGTH);
}

static int st_lps22df_read_raw(struct iio_dev *indio_dev,
			       struct iio_chan_spec const *ch,
			       int *val, int *val2, long mask)
{
	struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
	struct st_lps22df_hw *hw = sensor->hw;
	int ret, delay;

	switch (mask) {
	case IIO_CHAN_INFO_RAW: {
		u8 data[4] = {};
		u8 len;

		mutex_lock(&indio_dev->mlock);
		if (iio_buffer_enabled(indio_dev)) {
			mutex_unlock(&indio_dev->mlock);
			ret = -EBUSY;
			break;
		}

		ret = st_lps22df_set_enable(sensor, true);
		if (ret < 0)
			goto unlock;

		/* wait at least 10% more than one odr */
		delay = 1100000 / sensor->odr;
		usleep_range(delay, 2 * delay);
		len = ch->scan_type.realbits >> 3;
		ret = hw->tf->read(hw->dev, ch->address, len, data);
		if (ret < 0)
			goto unlock;

		if (sensor->type == ST_LPS22DF_PRESS)
			*val = (s32)get_unaligned_le32(data);
		else if (sensor->type == ST_LPS22DF_TEMP)
			*val = (s16)get_unaligned_le16(data);

unlock:
		ret = st_lps22df_set_enable(sensor, false);
		mutex_unlock(&indio_dev->mlock);

		ret = ret < 0 ? ret : IIO_VAL_INT;
		break;
	}
	case IIO_CHAN_INFO_SCALE:
		switch (ch->type) {
		case IIO_TEMP:
			*val = 1000;
			*val2 = sensor->gain;
			ret = IIO_VAL_FRACTIONAL;
			break;
		case IIO_PRESSURE:
			*val = 0;
			*val2 = sensor->gain;
			ret = IIO_VAL_INT_PLUS_NANO;
			break;
		default:
			ret = -ENODEV;
			break;
		}
		break;
	case IIO_CHAN_INFO_SAMP_FREQ:
		*val = sensor->odr;
		ret = IIO_VAL_INT;
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static int st_lps22df_write_raw(struct iio_dev *indio_dev,
				struct iio_chan_spec const *ch,
				int val, int val2, long mask)
{
	struct st_lps22df_sensor *sensor = iio_priv(indio_dev);
	int ret;

	switch (mask) {
	case IIO_CHAN_INFO_SAMP_FREQ:
		ret = st_lps22df_get_odr(sensor, val);
		if (ret > 0)
			sensor->odr = val;
		break;
	default:
		ret = -EINVAL;
		break;
	}
	return ret;
}

static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lps22df_get_sampling_frequency_avail);
static IIO_DEVICE_ATTR(hwfifo_watermark, 0644,
		       st_lps22df_sysfs_get_hwfifo_watermark,
		       st_lps22df_sysfs_set_hwfifo_watermark, 0);
static IIO_DEVICE_ATTR(hwfifo_watermark_max, 0444,
		       st_lps22df_sysfs_get_hwfifo_watermark_max, NULL, 0);
static IIO_DEVICE_ATTR(hwfifo_flush, 0200, NULL,
		       st_lps22df_sysfs_flush_fifo, 0);

static struct attribute *st_lps22df_press_attributes[] = {
	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
	&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
	&iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
	&iio_dev_attr_hwfifo_flush.dev_attr.attr,
	NULL,
};

static struct attribute *st_lps22df_temp_attributes[] = {
	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
	NULL,
};

static const struct attribute_group st_lps22df_press_attribute_group = {
	.attrs = st_lps22df_press_attributes,
};
static const struct attribute_group st_lps22df_temp_attribute_group = {
	.attrs = st_lps22df_temp_attributes,
};

static const struct iio_info st_lps22df_press_info = {
	.driver_module = THIS_MODULE,
	.attrs = &st_lps22df_press_attribute_group,
	.read_raw = st_lps22df_read_raw,
	.write_raw = st_lps22df_write_raw,
};

static const struct iio_info st_lps22df_temp_info = {
	.driver_module = THIS_MODULE,
	.attrs = &st_lps22df_temp_attribute_group,
	.read_raw = st_lps22df_read_raw,
	.write_raw = st_lps22df_write_raw,
};

int st_lps22df_common_probe(struct device *dev, int irq, const char *name,
			    const struct st_lps22df_transfer_function *tf_ops)
{
	struct st_lps22df_sensor *sensor;
	struct st_lps22df_hw *hw;
	struct iio_dev *iio_dev;
	int err, i;

	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
	if (!hw)
		return -ENOMEM;

	dev_set_drvdata(dev, (void *)hw);
	hw->dev = dev;
	hw->tf = tf_ops;
	hw->irq = irq;

	/* set initial watermark */
	hw->watermark = 1;

	mutex_init(&hw->lock);
	mutex_init(&hw->fifo_lock);

	err = st_lps22df_check_whoami(hw);
	if (err < 0)
		return err;

	for (i = 0; i < ST_LPS22DF_SENSORS_NUMB; i++) {
		iio_dev = devm_iio_device_alloc(dev, sizeof(*sensor));
		if (!iio_dev)
			return -ENOMEM;

		hw->iio_devs[i] = iio_dev;
		sensor = iio_priv(iio_dev);
		sensor->hw = hw;
		sensor->type = i;
		sensor->odr = 1;

		switch (i) {
		case ST_LPS22DF_PRESS:
			sensor->gain = ST_LPS22DF_PRESS_FS_AVL_GAIN;
			scnprintf(sensor->name, sizeof(sensor->name),
				  "%s_press", name);
			iio_dev->channels = st_lps22df_press_channels;
			iio_dev->num_channels =
				ARRAY_SIZE(st_lps22df_press_channels);
			iio_dev->info = &st_lps22df_press_info;
			break;
		case ST_LPS22DF_TEMP:
			sensor->gain = ST_LPS22DF_TEMP_FS_AVL_GAIN;
			scnprintf(sensor->name, sizeof(sensor->name),
				  "%s_temp", name);
			iio_dev->channels = st_lps22df_temp_channels;
			iio_dev->num_channels =
				ARRAY_SIZE(st_lps22df_temp_channels);
			iio_dev->info = &st_lps22df_temp_info;
			break;
		default:
			return -EINVAL;
		}

		iio_dev->name = sensor->name;
		iio_dev->modes = INDIO_DIRECT_MODE;
	}

	err = st_lps22df_init_sensors(hw);
	if (err < 0)
		return err;

	if (irq > 0) {
		err = st_lps22df_allocate_buffers(hw);
		if (err < 0)
			return err;
	}

	for (i = 0; i < ST_LPS22DF_SENSORS_NUMB; i++) {
		err = devm_iio_device_register(dev, hw->iio_devs[i]);
		if (err)
			return err;
	}
	return 0;
}
EXPORT_SYMBOL(st_lps22df_common_probe);

MODULE_DESCRIPTION("STMicroelectronics lps22df driver");
MODULE_AUTHOR("Mario Tesi ");
MODULE_LICENSE("GPL v2");

st_lps22df_i2c.c  文件内容

/*
 * STMicroelectronics lps22df i2c driver
 *
 * Copyright 2021 STMicroelectronics Inc.
 *
 * Mario Tesi 
 *
 * Licensed under the GPL-2.
 */

#include 

#include "st_lps22df.h"

static int st_lps22df_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
{
	struct st_lps22df_hw *hw = dev_get_drvdata(dev);
	struct i2c_client *client = to_i2c_client(dev);
	struct i2c_msg msg[2];
	int ret;

	if (len >= ST_LPS22DF_RX_MAX_LENGTH)
		return -ENOMEM;

	msg[0].addr = client->addr;
	msg[0].flags = client->flags;
	msg[0].len = 1;
	msg[0].buf = &addr;

	msg[1].addr = client->addr;
	msg[1].flags = client->flags | I2C_M_RD;
	msg[1].len = len;
	msg[1].buf = hw->tb.rx_buf;

	ret = i2c_transfer(client->adapter, msg, 2);
	if (ret < 0)
		return ret;

	memcpy(data, hw->tb.rx_buf, len * sizeof(u8));

	return 0;
}

static int st_lps22df_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
{
	struct st_lps22df_hw *hw = dev_get_drvdata(dev);
	struct i2c_client *client = to_i2c_client(dev);
	struct i2c_msg msg;
	int ret;

	if (len >= ST_LPS22DF_TX_MAX_LENGTH)
		return -ENOMEM;

	hw->tb.tx_buf[0] = addr;
	memcpy(&hw->tb.tx_buf[1], data, len);

	msg.addr = client->addr;
	msg.flags = client->flags;
	msg.len = len + 1;
	msg.buf = hw->tb.tx_buf;

	ret = i2c_transfer(client->adapter, &msg, 1);

	return ret < 0 ? ret : 0;
}

static const struct st_lps22df_transfer_function st_lps22df_tf_i2c = {
	.write = st_lps22df_i2c_write,
	.read = st_lps22df_i2c_read,
};

static int st_lps22df_i2c_probe(struct i2c_client *client,
				const struct i2c_device_id *id)
{
	return st_lps22df_common_probe(&client->dev, client->irq, client->name,
				       &st_lps22df_tf_i2c);
}

static const struct i2c_device_id st_lps22df_ids[] = {
	{ "lps22df" },
	{}
};
MODULE_DEVICE_TABLE(i2c, st_lps22df_ids);

static const struct of_device_id st_lps22df_id_table[] = {
	{ .compatible = "st,lps22df" },
	{},
};
MODULE_DEVICE_TABLE(of, st_lps22df_id_table);

static struct i2c_driver st_lps22df_i2c_driver = {
	.driver = {
		   .owner = THIS_MODULE,
		   .name = "st_lps22df_i2c",
		   .of_match_table = of_match_ptr(st_lps22df_id_table),
	},
	.probe = st_lps22df_i2c_probe,
	.id_table = st_lps22df_ids,
};
module_i2c_driver(st_lps22df_i2c_driver);

MODULE_DESCRIPTION("STMicroelectronics lps22df i2c driver");
MODULE_AUTHOR("Mario Tesi ");
MODULE_LICENSE("GPL v2");

st_lps22df_spi.c  /文件内容

/*
 * STMicroelectronics lps22df spi driver
 *
 * Copyright 2021 STMicroelectronics Inc.
 *
 * Mario Tesi 
 *
 * Licensed under the GPL-2.
 */

#include 

#include "st_lps22df.h"

#define ST_SENSORS_SPI_READ			0x80

static int st_lps22df_spi_read(struct device *dev, u8 addr, int len, u8 *data)
{
	struct spi_device *spi = to_spi_device(dev);
	struct st_lps22df_hw *hw = spi_get_drvdata(spi);
	struct spi_transfer xfers[] = {
		{
			.tx_buf = hw->tb.tx_buf,
			.bits_per_word = 8,
			.len = 1,
		},
		{
			.rx_buf = hw->tb.rx_buf,
			.bits_per_word = 8,
			.len = len,
		}
	};
	int err;

	if (len >= ST_LPS22DF_RX_MAX_LENGTH)
		return -ENOMEM;

	hw->tb.tx_buf[0] = addr | ST_SENSORS_SPI_READ;
	err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
	if (err)
		return err;

	memcpy(data, hw->tb.rx_buf, len);

	return err;
}

static int st_lps22df_spi_write(struct device *dev, u8 addr, int len, u8 *data)
{
	struct st_lps22df_hw *hw;
	struct spi_device *spi;

	if (len >= ST_LPS22DF_TX_MAX_LENGTH)
		return -ENOMEM;

	spi = to_spi_device(dev);
	hw = spi_get_drvdata(spi);

	hw->tb.tx_buf[0] = addr;
	memcpy(&hw->tb.tx_buf[1], data, len);

	return spi_write(spi, hw->tb.tx_buf, len + 1);
}

static const struct st_lps22df_transfer_function st_lps22df_tf_spi = {
	.write = st_lps22df_spi_write,
	.read = st_lps22df_spi_read,
};

static int st_lps22df_spi_probe(struct spi_device *spi)
{
	return st_lps22df_common_probe(&spi->dev, spi->irq, spi->modalias,
				       &st_lps22df_tf_spi);
}

static const struct spi_device_id st_lps22df_ids[] = {
	{ "lps22df" },
	{}
};
MODULE_DEVICE_TABLE(spi, st_lps22df_ids);

static const struct of_device_id st_lps22df_id_table[] = {
	{ .compatible = "st,lps22df" },
	{},
};
MODULE_DEVICE_TABLE(of, st_lps22df_id_table);

static struct spi_driver st_lps22df_spi_driver = {
	.driver = {
		   .owner = THIS_MODULE,
		   .name = "st_lps22df_spi",
		   .of_match_table = of_match_ptr(st_lps22df_id_table),
	},
	.probe = st_lps22df_spi_probe,
	.id_table = st_lps22df_ids,
};
module_spi_driver(st_lps22df_spi_driver);

MODULE_DESCRIPTION("STMicroelectronics lps22df spi driver");
MODULE_AUTHOR("Mario Tesi ");
MODULE_LICENSE("GPL v2");

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章