iio: chemical: atlas-ph-sensor: add EC feature
authorMatt Ranostay <mranostay@gmail.com>
Wed, 25 May 2016 04:29:20 +0000 (21:29 -0700)
committerJonathan Cameron <jic23@kernel.org>
Sun, 29 May 2016 19:03:23 +0000 (20:03 +0100)
Signed-off-by: Matt Ranostay <mranostay@gmail.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt [new file with mode: 0644]
drivers/iio/chemical/Kconfig
drivers/iio/chemical/atlas-ph-sensor.c

diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt
new file mode 100644 (file)
index 0000000..2962bd9
--- /dev/null
@@ -0,0 +1,22 @@
+* Atlas Scientific EC-SM OEM sensor
+
+http://www.atlas-scientific.com/_files/_datasheets/_oem/EC_oem_datasheet.pdf
+
+Required properties:
+
+  - compatible: must be "atlas,ec-sm"
+  - reg: the I2C address of the sensor
+  - interrupt-parent: should be the phandle for the interrupt controller
+  - interrupts: the sole interrupt generated by the device
+
+  Refer to interrupt-controller/interrupts.txt for generic interrupt client
+  node bindings.
+
+Example:
+
+atlas@64 {
+       compatible = "atlas,ec-sm";
+       reg = <0x64>;
+       interrupt-parent = <&gpio1>;
+       interrupts = <16 2>;
+};
index f73290f..4bcc025 100644 (file)
@@ -5,15 +5,17 @@
 menu "Chemical Sensors"
 
 config ATLAS_PH_SENSOR
-       tristate "Atlas Scientific OEM pH-SM sensor"
+       tristate "Atlas Scientific OEM SM sensors"
        depends on I2C
        select REGMAP_I2C
        select IIO_BUFFER
        select IIO_TRIGGERED_BUFFER
        select IRQ_WORK
        help
-        Say Y here to build I2C interface support for the Atlas
-        Scientific OEM pH-SM sensor.
+        Say Y here to build I2C interface support for the following
+        Atlas Scientific OEM SM sensors:
+           * pH SM sensor
+           * EC SM sensor
 
         To compile this driver as module, choose M here: the
         module will be called atlas-ph-sensor.
index e85477c..02e85db 100644 (file)
 #define ATLAS_REG_PH_CALIB_STATUS_MID  BIT(1)
 #define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2)
 
+#define ATLAS_REG_EC_CALIB_STATUS              0x0f
+#define ATLAS_REG_EC_CALIB_STATUS_MASK         0x0f
+#define ATLAS_REG_EC_CALIB_STATUS_DRY          BIT(0)
+#define ATLAS_REG_EC_CALIB_STATUS_SINGLE       BIT(1)
+#define ATLAS_REG_EC_CALIB_STATUS_LOW          BIT(2)
+#define ATLAS_REG_EC_CALIB_STATUS_HIGH         BIT(3)
+
 #define ATLAS_REG_PH_TEMP_DATA         0x0e
 #define ATLAS_REG_PH_DATA              0x16
 
+#define ATLAS_REG_EC_PROBE             0x08
+#define ATLAS_REG_EC_TEMP_DATA         0x10
+#define ATLAS_REG_EC_DATA              0x18
+#define ATLAS_REG_TDS_DATA             0x1c
+#define ATLAS_REG_PSS_DATA             0x20
+
 #define ATLAS_PH_INT_TIME_IN_US                450000
+#define ATLAS_EC_INT_TIME_IN_US                650000
 
 enum {
        ATLAS_PH_SM,
+       ATLAS_EC_SM,
 };
 
 struct atlas_data {
@@ -66,12 +81,13 @@ struct atlas_data {
        struct regmap *regmap;
        struct irq_work work;
 
-       __be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */
+       __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */
 };
 
 static const struct regmap_range atlas_volatile_ranges[] = {
        regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL),
        regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4),
+       regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_DATA + 4),
 };
 
 static const struct regmap_access_table atlas_volatile_table = {
@@ -86,7 +102,7 @@ static const struct regmap_config atlas_regmap_config = {
        .val_bits = 8,
 
        .volatile_table = &atlas_volatile_table,
-       .max_register = ATLAS_REG_PH_DATA + 4,
+       .max_register = ATLAS_REG_PSS_DATA + 4,
        .cache_type = REGCACHE_RBTREE,
 };
 
@@ -115,6 +131,50 @@ static const struct iio_chan_spec atlas_ph_channels[] = {
        },
 };
 
+#define ATLAS_EC_CHANNEL(_idx, _addr) \
+       {\
+               .type = IIO_CONCENTRATION, \
+               .indexed = 1, \
+               .channel = _idx, \
+               .address = _addr, \
+               .info_mask_separate = \
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
+               .scan_index = _idx + 1, \
+               .scan_type = { \
+                       .sign = 'u', \
+                       .realbits = 32, \
+                       .storagebits = 32, \
+                       .endianness = IIO_BE, \
+               }, \
+       }
+
+static const struct iio_chan_spec atlas_ec_channels[] = {
+       {
+               .type = IIO_ELECTRICALCONDUCTIVITY,
+               .address = ATLAS_REG_EC_DATA,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .scan_index = 0,
+               .scan_type = {
+                       .sign = 'u',
+                       .realbits = 32,
+                       .storagebits = 32,
+                       .endianness = IIO_BE,
+               },
+       },
+       ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA),
+       ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA),
+       IIO_CHAN_SOFT_TIMESTAMP(3),
+       {
+               .type = IIO_TEMP,
+               .address = ATLAS_REG_EC_TEMP_DATA,
+               .info_mask_separate =
+                       BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+               .output = 1,
+               .scan_index = -1
+       },
+};
+
 static int atlas_check_ph_calibration(struct atlas_data *data)
 {
        struct device *dev = &data->client->dev;
@@ -142,6 +202,44 @@ static int atlas_check_ph_calibration(struct atlas_data *data)
        return 0;
 }
 
+static int atlas_check_ec_calibration(struct atlas_data *data)
+{
+       struct device *dev = &data->client->dev;
+       int ret;
+       unsigned int val;
+
+       ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &val, 2);
+       if (ret)
+               return ret;
+
+       dev_info(dev, "probe set to K = %d.%.2d", be16_to_cpu(val) / 100,
+                                                be16_to_cpu(val) % 100);
+
+       ret = regmap_read(data->regmap, ATLAS_REG_EC_CALIB_STATUS, &val);
+       if (ret)
+               return ret;
+
+       if (!(val & ATLAS_REG_EC_CALIB_STATUS_MASK)) {
+               dev_warn(dev, "device has not been calibrated\n");
+               return 0;
+       }
+
+       if (!(val & ATLAS_REG_EC_CALIB_STATUS_DRY))
+               dev_warn(dev, "device missing dry point calibration\n");
+
+       if (val & ATLAS_REG_EC_CALIB_STATUS_SINGLE) {
+               dev_warn(dev, "device using single point calibration\n");
+       } else {
+               if (!(val & ATLAS_REG_EC_CALIB_STATUS_LOW))
+                       dev_warn(dev, "device missing low point calibration\n");
+
+               if (!(val & ATLAS_REG_EC_CALIB_STATUS_HIGH))
+                       dev_warn(dev, "device missing high point calibration\n");
+       }
+
+       return 0;
+}
+
 struct atlas_device {
        const struct iio_chan_spec *channels;
        int num_channels;
@@ -159,6 +257,14 @@ static struct atlas_device atlas_devices[] = {
                                .calibration = &atlas_check_ph_calibration,
                                .delay = ATLAS_PH_INT_TIME_IN_US,
        },
+       [ATLAS_EC_SM] = {
+                               .channels = atlas_ec_channels,
+                               .num_channels = 5,
+                               .data_reg = ATLAS_REG_EC_DATA,
+                               .calibration = &atlas_check_ec_calibration,
+                               .delay = ATLAS_EC_INT_TIME_IN_US,
+       },
+
 };
 
 static int atlas_set_powermode(struct atlas_data *data, int on)
@@ -294,6 +400,8 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
                                              (u8 *) &reg, sizeof(reg));
                        break;
                case IIO_PH:
+               case IIO_CONCENTRATION:
+               case IIO_ELECTRICALCONDUCTIVITY:
                        mutex_lock(&indio_dev->mlock);
 
                        if (iio_buffer_enabled(indio_dev))
@@ -324,6 +432,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
                        *val = 1; /* 0.001 */
                        *val2 = 1000;
                        break;
+               case IIO_ELECTRICALCONDUCTIVITY:
+                       *val = 1; /* 0.00001 */
+                       *val = 100000;
+                       break;
+               case IIO_CONCENTRATION:
+                       *val = 0; /* 0.000000001 */
+                       *val2 = 1000;
+                       return IIO_VAL_INT_PLUS_NANO;
                default:
                        return -EINVAL;
                }
@@ -358,12 +474,14 @@ static const struct iio_info atlas_info = {
 
 static const struct i2c_device_id atlas_id[] = {
        { "atlas-ph-sm", ATLAS_PH_SM},
+       { "atlas-ec-sm", ATLAS_EC_SM},
        {}
 };
 MODULE_DEVICE_TABLE(i2c, atlas_id);
 
 static const struct of_device_id atlas_dt_ids[] = {
        { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, },
+       { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, },
        { }
 };
 MODULE_DEVICE_TABLE(of, atlas_dt_ids);