Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: chemical: add Atlas pH-SM sensor support

Add support for the Atlas Scientific pH-SM chemical sensor that can
detect pH levels of solutions in the range of 0-14.

Signed-off-by: Matt Ranostay <mranostay@gmail.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>

authored by

Matt Ranostay and committed by
Jonathan Cameron
27dec00e ecb3a7cc

+547
+22
Documentation/devicetree/bindings/iio/chemical/atlas,ph-sm.txt
··· 1 + * Atlas Scientific pH-SM OEM sensor 2 + 3 + http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf 4 + 5 + Required properties: 6 + 7 + - compatible: must be "atlas,ph-sm" 8 + - reg: the I2C address of the sensor 9 + - interrupt-parent: should be the phandle for the interrupt controller 10 + - interrupts: the sole interrupt generated by the device 11 + 12 + Refer to interrupt-controller/interrupts.txt for generic interrupt client 13 + node bindings. 14 + 15 + Example: 16 + 17 + atlas@65 { 18 + compatible = "atlas,ph-sm"; 19 + reg = <0x65>; 20 + interrupt-parent = <&gpio1>; 21 + interrupts = <16 2>; 22 + };
+13
drivers/iio/chemical/Kconfig
··· 4 4 5 5 menu "Chemical Sensors" 6 6 7 + config ATLAS_PH_SENSOR 8 + tristate "Atlas Scientific OEM pH-SM sensor" 9 + depends on I2C 10 + select REGMAP_I2C 11 + select IIO_BUFFER 12 + select IIO_TRIGGERED_BUFFER 13 + help 14 + Say Y here to build I2C interface support for the Atlas 15 + Scientific OEM pH-SM sensor. 16 + 17 + To compile this driver as module, choose M here: the 18 + module will be called atlas-ph-sensor. 19 + 7 20 config IAQCORE 8 21 tristate "AMS iAQ-Core VOC sensors" 9 22 depends on I2C
+1
drivers/iio/chemical/Makefile
··· 3 3 # 4 4 5 5 # When adding new entries keep the list in alphabetical order 6 + obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o 6 7 obj-$(CONFIG_IAQCORE) += ams-iaq-core.o 7 8 obj-$(CONFIG_VZ89X) += vz89x.o
+511
drivers/iio/chemical/atlas-ph-sensor.c
··· 1 + /* 2 + * atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor 3 + * 4 + * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + */ 16 + 17 + #include <linux/module.h> 18 + #include <linux/init.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/delay.h> 21 + #include <linux/mutex.h> 22 + #include <linux/err.h> 23 + #include <linux/irq.h> 24 + #include <linux/irq_work.h> 25 + #include <linux/gpio.h> 26 + #include <linux/i2c.h> 27 + #include <linux/regmap.h> 28 + #include <linux/iio/iio.h> 29 + #include <linux/iio/buffer.h> 30 + #include <linux/iio/trigger.h> 31 + #include <linux/iio/trigger_consumer.h> 32 + #include <linux/iio/triggered_buffer.h> 33 + #include <linux/pm_runtime.h> 34 + 35 + #define ATLAS_REGMAP_NAME "atlas_ph_regmap" 36 + #define ATLAS_DRV_NAME "atlas_ph" 37 + 38 + #define ATLAS_REG_DEV_TYPE 0x00 39 + #define ATLAS_REG_DEV_VERSION 0x01 40 + 41 + #define ATLAS_REG_INT_CONTROL 0x04 42 + #define ATLAS_REG_INT_CONTROL_EN BIT(3) 43 + 44 + #define ATLAS_REG_PWR_CONTROL 0x06 45 + 46 + #define ATLAS_REG_CALIB_STATUS 0x0d 47 + #define ATLAS_REG_CALIB_STATUS_MASK 0x07 48 + #define ATLAS_REG_CALIB_STATUS_LOW BIT(0) 49 + #define ATLAS_REG_CALIB_STATUS_MID BIT(1) 50 + #define ATLAS_REG_CALIB_STATUS_HIGH BIT(2) 51 + 52 + #define ATLAS_REG_TEMP_DATA 0x0e 53 + #define ATLAS_REG_PH_DATA 0x16 54 + 55 + #define ATLAS_PH_INT_TIME_IN_US 450000 56 + 57 + struct atlas_data { 58 + struct i2c_client *client; 59 + struct iio_trigger *trig; 60 + struct regmap *regmap; 61 + struct irq_work work; 62 + 63 + __be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */ 64 + }; 65 + 66 + static const struct regmap_range atlas_volatile_ranges[] = { 67 + regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL), 68 + regmap_reg_range(ATLAS_REG_CALIB_STATUS, ATLAS_REG_CALIB_STATUS), 69 + regmap_reg_range(ATLAS_REG_TEMP_DATA, ATLAS_REG_TEMP_DATA + 4), 70 + regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4), 71 + }; 72 + 73 + static const struct regmap_access_table atlas_volatile_table = { 74 + .yes_ranges = atlas_volatile_ranges, 75 + .n_yes_ranges = ARRAY_SIZE(atlas_volatile_ranges), 76 + }; 77 + 78 + static const struct regmap_config atlas_regmap_config = { 79 + .name = ATLAS_REGMAP_NAME, 80 + 81 + .reg_bits = 8, 82 + .val_bits = 8, 83 + 84 + .volatile_table = &atlas_volatile_table, 85 + .max_register = ATLAS_REG_PH_DATA + 4, 86 + .cache_type = REGCACHE_FLAT, 87 + }; 88 + 89 + static const struct iio_chan_spec atlas_channels[] = { 90 + { 91 + .type = IIO_PH, 92 + .info_mask_separate = 93 + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), 94 + .scan_index = 0, 95 + .scan_type = { 96 + .sign = 'u', 97 + .realbits = 32, 98 + .storagebits = 32, 99 + .endianness = IIO_BE, 100 + }, 101 + }, 102 + IIO_CHAN_SOFT_TIMESTAMP(1), 103 + { 104 + .type = IIO_TEMP, 105 + .address = ATLAS_REG_TEMP_DATA, 106 + .info_mask_separate = 107 + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), 108 + .output = 1, 109 + .scan_index = -1 110 + }, 111 + }; 112 + 113 + static int atlas_set_powermode(struct atlas_data *data, int on) 114 + { 115 + return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on); 116 + } 117 + 118 + static int atlas_set_interrupt(struct atlas_data *data, bool state) 119 + { 120 + return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL, 121 + ATLAS_REG_INT_CONTROL_EN, 122 + state ? ATLAS_REG_INT_CONTROL_EN : 0); 123 + } 124 + 125 + static int atlas_buffer_postenable(struct iio_dev *indio_dev) 126 + { 127 + struct atlas_data *data = iio_priv(indio_dev); 128 + int ret; 129 + 130 + ret = iio_triggered_buffer_postenable(indio_dev); 131 + if (ret) 132 + return ret; 133 + 134 + ret = pm_runtime_get_sync(&data->client->dev); 135 + if (ret < 0) { 136 + pm_runtime_put_noidle(&data->client->dev); 137 + return ret; 138 + } 139 + 140 + return atlas_set_interrupt(data, true); 141 + } 142 + 143 + static int atlas_buffer_predisable(struct iio_dev *indio_dev) 144 + { 145 + struct atlas_data *data = iio_priv(indio_dev); 146 + int ret; 147 + 148 + ret = iio_triggered_buffer_predisable(indio_dev); 149 + if (ret) 150 + return ret; 151 + 152 + ret = atlas_set_interrupt(data, false); 153 + if (ret) 154 + return ret; 155 + 156 + pm_runtime_mark_last_busy(&data->client->dev); 157 + return pm_runtime_put_autosuspend(&data->client->dev); 158 + } 159 + 160 + static const struct iio_trigger_ops atlas_interrupt_trigger_ops = { 161 + .owner = THIS_MODULE, 162 + }; 163 + 164 + static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { 165 + .postenable = atlas_buffer_postenable, 166 + .predisable = atlas_buffer_predisable, 167 + }; 168 + 169 + static void atlas_work_handler(struct irq_work *work) 170 + { 171 + struct atlas_data *data = container_of(work, struct atlas_data, work); 172 + 173 + iio_trigger_poll(data->trig); 174 + } 175 + 176 + static irqreturn_t atlas_trigger_handler(int irq, void *private) 177 + { 178 + struct iio_poll_func *pf = private; 179 + struct iio_dev *indio_dev = pf->indio_dev; 180 + struct atlas_data *data = iio_priv(indio_dev); 181 + int ret; 182 + 183 + ret = i2c_smbus_read_i2c_block_data(data->client, ATLAS_REG_PH_DATA, 184 + sizeof(data->buffer[0]), (u8 *) &data->buffer); 185 + 186 + if (ret > 0) 187 + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, 188 + iio_get_time_ns()); 189 + 190 + iio_trigger_notify_done(indio_dev->trig); 191 + 192 + return IRQ_HANDLED; 193 + } 194 + 195 + static irqreturn_t atlas_interrupt_handler(int irq, void *private) 196 + { 197 + struct iio_dev *indio_dev = private; 198 + struct atlas_data *data = iio_priv(indio_dev); 199 + 200 + irq_work_queue(&data->work); 201 + 202 + return IRQ_HANDLED; 203 + } 204 + 205 + static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val) 206 + { 207 + struct device *dev = &data->client->dev; 208 + int suspended = pm_runtime_suspended(dev); 209 + int ret; 210 + 211 + ret = pm_runtime_get_sync(dev); 212 + if (ret < 0) { 213 + pm_runtime_put_noidle(dev); 214 + return ret; 215 + } 216 + 217 + if (suspended) 218 + usleep_range(ATLAS_PH_INT_TIME_IN_US, 219 + ATLAS_PH_INT_TIME_IN_US + 100000); 220 + 221 + ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA, 222 + (u8 *) val, sizeof(*val)); 223 + 224 + pm_runtime_mark_last_busy(dev); 225 + pm_runtime_put_autosuspend(dev); 226 + 227 + return ret; 228 + } 229 + 230 + static int atlas_read_raw(struct iio_dev *indio_dev, 231 + struct iio_chan_spec const *chan, 232 + int *val, int *val2, long mask) 233 + { 234 + struct atlas_data *data = iio_priv(indio_dev); 235 + 236 + switch (mask) { 237 + case IIO_CHAN_INFO_RAW: { 238 + int ret; 239 + __be32 reg; 240 + 241 + switch (chan->type) { 242 + case IIO_TEMP: 243 + ret = regmap_bulk_read(data->regmap, chan->address, 244 + (u8 *) &reg, sizeof(reg)); 245 + break; 246 + case IIO_PH: 247 + mutex_lock(&indio_dev->mlock); 248 + 249 + if (iio_buffer_enabled(indio_dev)) 250 + ret = -EBUSY; 251 + else 252 + ret = atlas_read_ph_measurement(data, &reg); 253 + 254 + mutex_unlock(&indio_dev->mlock); 255 + break; 256 + default: 257 + ret = -EINVAL; 258 + } 259 + 260 + if (!ret) { 261 + *val = be32_to_cpu(reg); 262 + ret = IIO_VAL_INT; 263 + } 264 + return ret; 265 + } 266 + case IIO_CHAN_INFO_SCALE: 267 + switch (chan->type) { 268 + case IIO_TEMP: 269 + *val = 1; /* 0.01 */ 270 + *val2 = 100; 271 + break; 272 + case IIO_PH: 273 + *val = 1; /* 0.001 */ 274 + *val2 = 1000; 275 + break; 276 + default: 277 + return -EINVAL; 278 + } 279 + return IIO_VAL_FRACTIONAL; 280 + } 281 + 282 + return -EINVAL; 283 + } 284 + 285 + static int atlas_write_raw(struct iio_dev *indio_dev, 286 + struct iio_chan_spec const *chan, 287 + int val, int val2, long mask) 288 + { 289 + struct atlas_data *data = iio_priv(indio_dev); 290 + __be32 reg = cpu_to_be32(val); 291 + 292 + if (val2 != 0 || val < 0 || val > 20000) 293 + return -EINVAL; 294 + 295 + if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_TEMP) 296 + return -EINVAL; 297 + 298 + return regmap_bulk_write(data->regmap, chan->address, 299 + &reg, sizeof(reg)); 300 + } 301 + 302 + static const struct iio_info atlas_info = { 303 + .driver_module = THIS_MODULE, 304 + .read_raw = atlas_read_raw, 305 + .write_raw = atlas_write_raw, 306 + }; 307 + 308 + static int atlas_check_calibration(struct atlas_data *data) 309 + { 310 + struct device *dev = &data->client->dev; 311 + int ret; 312 + unsigned int val; 313 + 314 + ret = regmap_read(data->regmap, ATLAS_REG_CALIB_STATUS, &val); 315 + if (ret) 316 + return ret; 317 + 318 + if (!(val & ATLAS_REG_CALIB_STATUS_MASK)) { 319 + dev_warn(dev, "device has not been calibrated\n"); 320 + return 0; 321 + } 322 + 323 + if (!(val & ATLAS_REG_CALIB_STATUS_LOW)) 324 + dev_warn(dev, "device missing low point calibration\n"); 325 + 326 + if (!(val & ATLAS_REG_CALIB_STATUS_MID)) 327 + dev_warn(dev, "device missing mid point calibration\n"); 328 + 329 + if (!(val & ATLAS_REG_CALIB_STATUS_HIGH)) 330 + dev_warn(dev, "device missing high point calibration\n"); 331 + 332 + return 0; 333 + }; 334 + 335 + static int atlas_probe(struct i2c_client *client, 336 + const struct i2c_device_id *id) 337 + { 338 + struct atlas_data *data; 339 + struct iio_trigger *trig; 340 + struct iio_dev *indio_dev; 341 + int ret; 342 + 343 + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 344 + if (!indio_dev) 345 + return -ENOMEM; 346 + 347 + indio_dev->info = &atlas_info; 348 + indio_dev->name = ATLAS_DRV_NAME; 349 + indio_dev->channels = atlas_channels; 350 + indio_dev->num_channels = ARRAY_SIZE(atlas_channels); 351 + indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE; 352 + indio_dev->dev.parent = &client->dev; 353 + 354 + trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 355 + indio_dev->name, indio_dev->id); 356 + 357 + if (!trig) 358 + return -ENOMEM; 359 + 360 + data = iio_priv(indio_dev); 361 + data->client = client; 362 + data->trig = trig; 363 + trig->dev.parent = indio_dev->dev.parent; 364 + trig->ops = &atlas_interrupt_trigger_ops; 365 + iio_trigger_set_drvdata(trig, indio_dev); 366 + 367 + i2c_set_clientdata(client, indio_dev); 368 + 369 + data->regmap = devm_regmap_init_i2c(client, &atlas_regmap_config); 370 + if (IS_ERR(data->regmap)) { 371 + dev_err(&client->dev, "regmap initialization failed\n"); 372 + return PTR_ERR(data->regmap); 373 + } 374 + 375 + ret = pm_runtime_set_active(&client->dev); 376 + if (ret) 377 + return ret; 378 + 379 + if (client->irq <= 0) { 380 + dev_err(&client->dev, "no valid irq defined\n"); 381 + return -EINVAL; 382 + } 383 + 384 + ret = atlas_check_calibration(data); 385 + if (ret) 386 + return ret; 387 + 388 + ret = iio_trigger_register(trig); 389 + if (ret) { 390 + dev_err(&client->dev, "failed to register trigger\n"); 391 + return ret; 392 + } 393 + 394 + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, 395 + &atlas_trigger_handler, &atlas_buffer_setup_ops); 396 + if (ret) { 397 + dev_err(&client->dev, "cannot setup iio trigger\n"); 398 + goto unregister_trigger; 399 + } 400 + 401 + init_irq_work(&data->work, atlas_work_handler); 402 + 403 + /* interrupt pin toggles on new conversion */ 404 + ret = devm_request_threaded_irq(&client->dev, client->irq, 405 + NULL, atlas_interrupt_handler, 406 + IRQF_TRIGGER_RISING | 407 + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 408 + "atlas_irq", 409 + indio_dev); 410 + if (ret) { 411 + dev_err(&client->dev, "request irq (%d) failed\n", client->irq); 412 + goto unregister_buffer; 413 + } 414 + 415 + ret = atlas_set_powermode(data, 1); 416 + if (ret) { 417 + dev_err(&client->dev, "cannot power device on"); 418 + goto unregister_buffer; 419 + } 420 + 421 + pm_runtime_enable(&client->dev); 422 + pm_runtime_set_autosuspend_delay(&client->dev, 2500); 423 + pm_runtime_use_autosuspend(&client->dev); 424 + 425 + ret = iio_device_register(indio_dev); 426 + if (ret) { 427 + dev_err(&client->dev, "unable to register device\n"); 428 + goto unregister_pm; 429 + } 430 + 431 + return 0; 432 + 433 + unregister_pm: 434 + pm_runtime_disable(&client->dev); 435 + atlas_set_powermode(data, 0); 436 + 437 + unregister_buffer: 438 + iio_triggered_buffer_cleanup(indio_dev); 439 + 440 + unregister_trigger: 441 + iio_trigger_unregister(data->trig); 442 + 443 + return ret; 444 + } 445 + 446 + static int atlas_remove(struct i2c_client *client) 447 + { 448 + struct iio_dev *indio_dev = i2c_get_clientdata(client); 449 + struct atlas_data *data = iio_priv(indio_dev); 450 + 451 + iio_device_unregister(indio_dev); 452 + iio_triggered_buffer_cleanup(indio_dev); 453 + iio_trigger_unregister(data->trig); 454 + 455 + pm_runtime_disable(&client->dev); 456 + pm_runtime_set_suspended(&client->dev); 457 + pm_runtime_put_noidle(&client->dev); 458 + 459 + return atlas_set_powermode(data, 0); 460 + } 461 + 462 + #ifdef CONFIG_PM 463 + static int atlas_runtime_suspend(struct device *dev) 464 + { 465 + struct atlas_data *data = 466 + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 467 + 468 + return atlas_set_powermode(data, 0); 469 + } 470 + 471 + static int atlas_runtime_resume(struct device *dev) 472 + { 473 + struct atlas_data *data = 474 + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 475 + 476 + return atlas_set_powermode(data, 1); 477 + } 478 + #endif 479 + 480 + static const struct dev_pm_ops atlas_pm_ops = { 481 + SET_RUNTIME_PM_OPS(atlas_runtime_suspend, 482 + atlas_runtime_resume, NULL) 483 + }; 484 + 485 + static const struct i2c_device_id atlas_id[] = { 486 + { "atlas-ph-sm", 0 }, 487 + {} 488 + }; 489 + MODULE_DEVICE_TABLE(i2c, atlas_id); 490 + 491 + static const struct of_device_id atlas_dt_ids[] = { 492 + { .compatible = "atlas,ph-sm" }, 493 + { } 494 + }; 495 + MODULE_DEVICE_TABLE(of, atlas_dt_ids); 496 + 497 + static struct i2c_driver atlas_driver = { 498 + .driver = { 499 + .name = ATLAS_DRV_NAME, 500 + .of_match_table = of_match_ptr(atlas_dt_ids), 501 + .pm = &atlas_pm_ops, 502 + }, 503 + .probe = atlas_probe, 504 + .remove = atlas_remove, 505 + .id_table = atlas_id, 506 + }; 507 + module_i2c_driver(atlas_driver); 508 + 509 + MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>"); 510 + MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor"); 511 + MODULE_LICENSE("GPL");