On 24/04/16 21:43, Matt Ranostay wrote: Give us a bit more description - what is it, what is it for, what interface is supplied. Err what does EC standard for? > Signed-off-by: Matt Ranostay <mranostay@xxxxxxxxx> I know next to nothing about these sensors and don't have the datasheet to hand. This is measuring 3 different types of concentration (I think) and reporting them simple as channels 0-2. What are they are should we not be distinguishing between them in some more informative fashion? Code itself is mostly fine... Jonathan > --- > .../bindings/iio/chemical/atlas,ec-sm.txt | 22 ++++ > drivers/iio/chemical/Kconfig | 8 +- > drivers/iio/chemical/atlas-ph-sensor.c | 113 ++++++++++++++++++++- > 3 files changed, 138 insertions(+), 5 deletions(-) > create mode 100644 Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt > > 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 > index 0000000..2962bd9 > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt > @@ -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>; > +}; > diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig > index f73290f..4bcc025 100644 > --- a/drivers/iio/chemical/Kconfig > +++ b/drivers/iio/chemical/Kconfig > @@ -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. > diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c > index c57f9c2..6dddd75 100644 > --- a/drivers/iio/chemical/atlas-ph-sensor.c > +++ b/drivers/iio/chemical/atlas-ph-sensor.c > @@ -50,13 +50,28 @@ > #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,38 @@ 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, \ > + .scan_type = { \ > + .sign = 'u', \ > + .realbits = 32, \ > + .storagebits = 32, \ > + .endianness = IIO_BE, \ > + }, \ > + } > + > +static const struct iio_chan_spec atlas_ec_channels[] = { > + ATLAS_EC_CHANNEL(0, ATLAS_REG_EC_DATA), > + ATLAS_EC_CHANNEL(1, ATLAS_REG_TDS_DATA), > + ATLAS_EC_CHANNEL(2, ATLAS_REG_PSS_DATA), This needs some explanation. 3 concentration channels of different types? What are they... > + 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 +190,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 +245,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, Use array_size - obviously correct that way so I don't have to go count... > + .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 +388,7 @@ static int atlas_read_raw(struct iio_dev *indio_dev, > (u8 *) ®, sizeof(reg)); I guess it's not complex enough to justify splitting read_raw for the two devices. Close though so you may want to if you support a third type in here. > break; > case IIO_PH: > + case IIO_CONCENTRATION: > mutex_lock(&indio_dev->mlock); > > if (iio_buffer_enabled(indio_dev)) > @@ -324,6 +419,18 @@ static int atlas_read_raw(struct iio_dev *indio_dev, > *val = 1; /* 0.001 */ > *val2 = 1000; > break; > + case IIO_CONCENTRATION: > + switch (chan->address) { > + case ATLAS_REG_EC_DATA: > + *val = 0; /* 0.00000000064 */ > + *val2 = 640; > + return IIO_VAL_INT_PLUS_NANO; > + default: > + *val = 0; /* 0.000000001 */ > + *val2 = 1000; > + return IIO_VAL_INT_PLUS_NANO; > + } > + break; > default: > return -EINVAL; > } > @@ -517,12 +624,14 @@ static const struct dev_pm_ops atlas_pm_ops = { > > 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); > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html