brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · 761a853 Raw
245 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * atlas-ezo-sensor.c - Support for Atlas Scientific EZO sensors4 *5 * Copyright (C) 2020 Konsulko Group6 * Author: Matt Ranostay <matt.ranostay@konsulko.com>7 */8 9#include <linux/init.h>10#include <linux/delay.h>11#include <linux/mod_devicetable.h>12#include <linux/module.h>13#include <linux/mutex.h>14#include <linux/property.h>15#include <linux/err.h>16#include <linux/i2c.h>17 18#include <linux/iio/iio.h>19 20#define ATLAS_EZO_DRV_NAME		"atlas-ezo-sensor"21#define ATLAS_INT_TIME_IN_MS		95022#define ATLAS_INT_HUM_TIME_IN_MS	35023 24enum {25	ATLAS_CO2_EZO,26	ATLAS_O2_EZO,27	ATLAS_HUM_EZO,28};29 30struct atlas_ezo_device {31	const struct iio_chan_spec *channels;32	int num_channels;33	int delay;34};35 36struct atlas_ezo_data {37	struct i2c_client *client;38	const struct atlas_ezo_device *chip;39 40	/* lock to avoid multiple concurrent read calls */41	struct mutex lock;42 43	u8 buffer[8];44};45 46#define ATLAS_CONCENTRATION_CHANNEL(_modifier) \47	{ \48		.type = IIO_CONCENTRATION, \49		.modified = 1,\50		.channel2 = _modifier, \51		.info_mask_separate = \52			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \53		.scan_index = 0, \54		.scan_type =  { \55			.sign = 'u', \56			.realbits = 32, \57			.storagebits = 32, \58			.endianness = IIO_CPU, \59		}, \60	}61 62static const struct iio_chan_spec atlas_co2_ezo_channels[] = {63	ATLAS_CONCENTRATION_CHANNEL(IIO_MOD_CO2),64};65 66static const struct iio_chan_spec atlas_o2_ezo_channels[] = {67	ATLAS_CONCENTRATION_CHANNEL(IIO_MOD_O2),68};69 70static const struct iio_chan_spec atlas_hum_ezo_channels[] = {71	{72		.type = IIO_HUMIDITYRELATIVE,73		.info_mask_separate =74			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),75		.scan_index = 0,76		.scan_type =  {77			.sign = 'u',78			.realbits = 32,79			.storagebits = 32,80			.endianness = IIO_CPU,81		},82	},83};84 85static struct atlas_ezo_device atlas_ezo_devices[] = {86	[ATLAS_CO2_EZO] = {87		.channels = atlas_co2_ezo_channels,88		.num_channels = 1,89		.delay = ATLAS_INT_TIME_IN_MS,90	},91	[ATLAS_O2_EZO] = {92		.channels = atlas_o2_ezo_channels,93		.num_channels = 1,94		.delay = ATLAS_INT_TIME_IN_MS,95	},96	[ATLAS_HUM_EZO] = {97		.channels = atlas_hum_ezo_channels,98		.num_channels = 1,99		.delay = ATLAS_INT_HUM_TIME_IN_MS,100	},101};102 103static void atlas_ezo_sanitize(char *buf)104{105	char *ptr = strchr(buf, '.');106 107	if (!ptr)108		return;109 110	memmove(ptr, ptr + 1, strlen(ptr));111}112 113static int atlas_ezo_read_raw(struct iio_dev *indio_dev,114			  struct iio_chan_spec const *chan,115			  int *val, int *val2, long mask)116{117	struct atlas_ezo_data *data = iio_priv(indio_dev);118	struct i2c_client *client = data->client;119 120	if (chan->type != IIO_CONCENTRATION)121		return -EINVAL;122 123	switch (mask) {124	case IIO_CHAN_INFO_RAW: {125		int ret;126		long tmp;127 128		mutex_lock(&data->lock);129 130		tmp = i2c_smbus_write_byte(client, 'R');131 132		if (tmp < 0) {133			mutex_unlock(&data->lock);134			return tmp;135		}136 137		msleep(data->chip->delay);138 139		tmp = i2c_master_recv(client, data->buffer, sizeof(data->buffer));140 141		if (tmp < 0 || data->buffer[0] != 1) {142			mutex_unlock(&data->lock);143			return -EBUSY;144		}145 146		/* removing floating point for fixed number representation */147		atlas_ezo_sanitize(data->buffer + 2);148 149		ret = kstrtol(data->buffer + 1, 10, &tmp);150 151		*val = tmp;152 153		mutex_unlock(&data->lock);154 155		return ret ? ret : IIO_VAL_INT;156	}157	case IIO_CHAN_INFO_SCALE:158		switch (chan->type) {159		case IIO_HUMIDITYRELATIVE:160			*val = 10;161			return IIO_VAL_INT;162		case IIO_CONCENTRATION:163			break;164		default:165			return -EINVAL;166		}167 168		/* IIO_CONCENTRATION modifiers */169		switch (chan->channel2) {170		case IIO_MOD_CO2:171			*val = 0;172			*val2 = 100; /* 0.0001 */173			return IIO_VAL_INT_PLUS_MICRO;174		case IIO_MOD_O2:175			*val = 100;176			return IIO_VAL_INT;177		}178		return -EINVAL;179	}180 181	return 0;182}183 184static const struct iio_info atlas_info = {185	.read_raw = atlas_ezo_read_raw,186};187 188static const struct i2c_device_id atlas_ezo_id[] = {189	{ "atlas-co2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_CO2_EZO] },190	{ "atlas-o2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_O2_EZO] },191	{ "atlas-hum-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_HUM_EZO] },192	{}193};194MODULE_DEVICE_TABLE(i2c, atlas_ezo_id);195 196static const struct of_device_id atlas_ezo_dt_ids[] = {197	{ .compatible = "atlas,co2-ezo", .data = &atlas_ezo_devices[ATLAS_CO2_EZO], },198	{ .compatible = "atlas,o2-ezo", .data = &atlas_ezo_devices[ATLAS_O2_EZO], },199	{ .compatible = "atlas,hum-ezo", .data = &atlas_ezo_devices[ATLAS_HUM_EZO], },200	{}201};202MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids);203 204static int atlas_ezo_probe(struct i2c_client *client)205{206	const struct atlas_ezo_device *chip;207	struct atlas_ezo_data *data;208	struct iio_dev *indio_dev;209 210	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));211	if (!indio_dev)212		return -ENOMEM;213 214	chip = i2c_get_match_data(client);215	if (!chip)216		return -EINVAL;217 218	indio_dev->info = &atlas_info;219	indio_dev->name = ATLAS_EZO_DRV_NAME;220	indio_dev->channels = chip->channels;221	indio_dev->num_channels = chip->num_channels;222	indio_dev->modes = INDIO_DIRECT_MODE;223 224	data = iio_priv(indio_dev);225	data->client = client;226	data->chip = chip;227	mutex_init(&data->lock);228 229	return devm_iio_device_register(&client->dev, indio_dev);230};231 232static struct i2c_driver atlas_ezo_driver = {233	.driver = {234		.name	= ATLAS_EZO_DRV_NAME,235		.of_match_table	= atlas_ezo_dt_ids,236	},237	.probe		= atlas_ezo_probe,238	.id_table	= atlas_ezo_id,239};240module_i2c_driver(atlas_ezo_driver);241 242MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");243MODULE_DESCRIPTION("Atlas Scientific EZO sensors");244MODULE_LICENSE("GPL");245