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