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/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 950 22 #define ATLAS_INT_HUM_TIME_IN_MS 350 23 24 enum { 25 ATLAS_CO2_EZO, 26 ATLAS_O2_EZO, 27 ATLAS_HUM_EZO, 28 }; 29 30 struct atlas_ezo_device { 31 const struct iio_chan_spec *channels; 32 int num_channels; 33 int delay; 34 }; 35 36 struct 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 62 static const struct iio_chan_spec atlas_co2_ezo_channels[] = { 63 ATLAS_CONCENTRATION_CHANNEL(IIO_MOD_CO2), 64 }; 65 66 static const struct iio_chan_spec atlas_o2_ezo_channels[] = { 67 ATLAS_CONCENTRATION_CHANNEL(IIO_MOD_O2), 68 }; 69 70 static 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 85 static 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 103 static 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 113 static 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 184 static const struct iio_info atlas_info = { 185 .read_raw = atlas_ezo_read_raw, 186 }; 187 188 static 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 }; 194 MODULE_DEVICE_TABLE(i2c, atlas_ezo_id); 195 196 static 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 }; 202 MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids); 203 204 static int atlas_ezo_probe(struct i2c_client *client) 205 { 206 const struct i2c_device_id *id = i2c_client_get_device_id(client); 207 const struct atlas_ezo_device *chip; 208 struct atlas_ezo_data *data; 209 struct iio_dev *indio_dev; 210 211 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 212 if (!indio_dev) 213 return -ENOMEM; 214 215 if (dev_fwnode(&client->dev)) 216 chip = device_get_match_data(&client->dev); 217 else 218 chip = (const struct atlas_ezo_device *)id->driver_data; 219 if (!chip) 220 return -EINVAL; 221 222 indio_dev->info = &atlas_info; 223 indio_dev->name = ATLAS_EZO_DRV_NAME; 224 indio_dev->channels = chip->channels; 225 indio_dev->num_channels = chip->num_channels; 226 indio_dev->modes = INDIO_DIRECT_MODE; 227 228 data = iio_priv(indio_dev); 229 data->client = client; 230 data->chip = chip; 231 mutex_init(&data->lock); 232 233 return devm_iio_device_register(&client->dev, indio_dev); 234 }; 235 236 static struct i2c_driver atlas_ezo_driver = { 237 .driver = { 238 .name = ATLAS_EZO_DRV_NAME, 239 .of_match_table = atlas_ezo_dt_ids, 240 }, 241 .probe_new = atlas_ezo_probe, 242 .id_table = atlas_ezo_id, 243 }; 244 module_i2c_driver(atlas_ezo_driver); 245 246 MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); 247 MODULE_DESCRIPTION("Atlas Scientific EZO sensors"); 248 MODULE_LICENSE("GPL"); 249