This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/ADC128D818SW-LINUX: Slow A/D reading, why?

Part Number: ADC128D818SW-LINUX
Other Parts Discussed in Thread: ADC128D818

Tool/software: Linux

Dear experts,

I have a strange issue with ADC128D818 & standard Linux driver.

The device detects and seems to work well. The problem is that it takes ~ 1 sec to get a correct reading.

When I change voltage on a input pin and read continously, changes cannot be detected more frequently than once in a second.

Read itself (from the driver's sysfs file) is quick and does not hang. But updates do not occur at the rate that I need.

Any ideas, please? Can the driver be tweaked somehow to read faster?

The machine is iMX6 Solo (arm7), Ubuntu 14.04 LTS, kernel 4.1.0

Thanks,

- Pavel A.

  • Pavel,


    I'm sorry, but even though we have links to the linux drivers on the TI website, we didn't develop them and are unable to support them.

    In this case, I would use a logic analyzer to check to see that the device is responding to commands and giving data periodically just to make sure the device is operating correctly. However, I'm not sure how the linux driver monitors for new data and how it reads it out.


    Joseph Wu
  • Hello Joseph,

    I've found the reason of slow updates:

    The Linux driver (drivers/hwmon/adc128d818.c) limits frequency of reads from the device to once per second, hard-coded:

    static struct adc128_data *adc128_update_device(struct device *dev)
    {
              .............
    	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
               // ... read from the device
             }
    

    (link)  

    The driver also leaves the device in default update speed which results in update cycle time  728 ms which is ~ 1 sec.

    All versions of this driver in the mainstream kernel behave this way.

    And there is no parameter to tweak the frequency of updates. Thought, it could be useful for the users to know.

    I'm going to fix the 1 HZ limit in the driver source and enable continuous update. Then maybe post a patch with a module or device tree parameter for this.

    With best regards, 

    Pavel A.

  • Update: confirmed that continuous mode helps to reduce the latency of signal change to 100 ms at least.

    Follows a quick variant of patch, for the early version of the driver (kernel ~ 4.1, supports only mode 0)

    with option to make scan even faster, by removing support for automatic min & max limits check.

    - Pavel A,


    --- a/drivers/hwmon/adc128d818.c
    +++ b/drivers/hwmon/adc128d818.c
    @@ -83,38 +74,42 @@
     	struct i2c_client *client = data->client;
     	struct adc128_data *ret = data;
     	int i, rv;
     
     	mutex_lock(&data->update_lock);
     
    -	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
    +	if (time_after(jiffies, data->last_updated + (HZ/11)) || !data->valid) {
     		for (i = 0; i < 7; i++) {
     			rv = i2c_smbus_read_word_swapped(client,
     							 ADC128_REG_IN(i));
     			if (rv < 0)
     				goto abort;
     			data->in[0][i] = rv >> 4;
    -
    +#if WANT_LIMITS
     			rv = i2c_smbus_read_byte_data(client,
     						      ADC128_REG_IN_MIN(i));
     			if (rv < 0)
     				goto abort;
     			data->in[1][i] = rv << 4;
     
     			rv = i2c_smbus_read_byte_data(client,
     						      ADC128_REG_IN_MAX(i));
     			if (rv < 0)
     				goto abort;
     			data->in[2][i] = rv << 4;
    +#else
    +			data->in[1][i] = 0; // dummy
    +			data->in[2][i] = 0x7FFF;
    +#endif /*WANT_LIMITS*/
     		}
     
     		rv = i2c_smbus_read_word_swapped(client, ADC128_REG_TEMP);
     		if (rv < 0)
     			goto abort;
     		data->temp[0] = rv >> 7;
    -
    +#if WANT_LIMITS
     		rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_MAX);
     		if (rv < 0)
     			goto abort;
     		data->temp[1] = rv << 1;
     
     		rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_HYST);
    @@ -123,13 +118,16 @@
     		data->temp[2] = rv << 1;
     
     		rv = i2c_smbus_read_byte_data(client, ADC128_REG_ALARM);
     		if (rv < 0)
     			goto abort;
     		data->alarms |= rv;
    -
    +#else
    +		data->temp[1] = 1; //dummy
    +		data->temp[2] = 100;
    +#endif /*WANT_LIMITS*/
     		data->last_updated = jiffies;
     		data->valid = true;
     	}
     	goto done;
     
     abort:
    @@ -384,11 +382,15 @@
     	 * This makes most other initializations unnecessary.
     	 */
     	err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x80);
     	if (err)
     		return err;
    +	err = i2c_smbus_write_byte_data(client, ADC128_REG_CONV_RATE, 0x01);
    +	if (err)
    +		return err;
     
     	/* Start monitoring */
     	err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x01);
     	if (err)
     		return err;
     
    @@ -445,13 +447,13 @@
     	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
     							   data, adc128_groups);
     	if (IS_ERR(hwmon_dev)) {
     		err = PTR_ERR(hwmon_dev);
     		goto error;
     	}
    -
    +	printk("ADC128 at %2.2X - fast scan fix\n", client->addr);
     	return 0;
     
     error:
     	if (data->regulator)
     		regulator_disable(data->regulator);
     	return err;
    @@ -485,8 +487,8 @@
     	.address_list	= normal_i2c,
     };
     
     module_i2c_driver(adc128_driver);
     
     MODULE_AUTHOR("Guenter Roeck");
    -MODULE_DESCRIPTION("Driver for ADC128D818");
    +MODULE_DESCRIPTION("Driver for ADC128D818/pa01 fast scan fix");
     MODULE_LICENSE("GPL");