iio: pressure: dps310: Reset chip if MEAS_CFG is corrupt
Subsystems (2)
Eddie James
maintainerv3 cover iio: pressure: dps310: Reset chip if MEAS_CFG is corrupt
Corruption of the MEAS_CFG register has been observed soon after system boot. In order to recover this scenario, check MEAS_CFG if measurement isn't ready, and if it's incorrect, reset the DPS310 and execute the startup procedure. Include a patch to move the startup procedure into a function.
Changes since v2:
- Add some comments
- Fix the clunky control flow
Changes since v1:
- Separate into two patches
- Rename 'dps310_verify_meas_cfg' to 'dps310_check_reset_meas_cfg'
Eddie James (2):
iio: pressure: dps310: Refactor startup procedure
iio: pressure: dps310: Reset chip if MEAS_CFG is corrupt
drivers/iio/pressure/dps310.c | 280 +++++++++++++++++++++------------- 1 file changed, 176 insertions(+), 104 deletions(-)
Signature · 1 line
2.27.0
Eddie James
maintainerPATCH v3 1/2 iio: pressure: dps310: Refactor startup procedure
Move the startup procedure into a function, and correct a missing check on the return code for writing the PRS_CFG register.
--- drivers/iio/pressure/dps310.c | 192 ++++++++++++++++++---------------- 1 file changed, 103 insertions(+), 89 deletions(-)
drivers/iio/pressure/dps310.c +103 -89
@@ -159,6 +159,106 @@ static int dps310_get_coefs(struct dps310_data *data)return 0 ; } +/* +* Some verions of chip will read temperatures in the ~60C range when +* its actually ~20C. This is the manufacturer recommended workaround +* to correct the issue. The registers used below are undocumented. +*/ +static int dps310_temp_workaround (struct dps310_data *data) +{ +int rc; +int reg; + + rc =regmap_read (data->regmap,0x32 , ®); +if (rc <0 ) +return rc; + +/* +* If bit 1 is set then the device is okay, and the workaround does not +* need to be applied +*/ +if (reg &BIT (1 )) +return 0 ; + + rc =regmap_write (data->regmap,0x0e ,0xA5 ); +if (rc <0 ) +return rc; + + rc =regmap_write (data->regmap,0x0f ,0x96 ); +if (rc <0 ) +return rc; + + rc =regmap_write (data->regmap,0x62 ,0x02 ); +if (rc <0 ) +return rc; + + rc =regmap_write (data->regmap,0x0e ,0x00 ); +if (rc <0 ) +return rc; + +return regmap_write (data->regmap,0x0f ,0x00 ); +} + +static int dps310_startup (struct dps310_data *data) +{ +int rc; +int ready; + +/* +* Set up pressure sensor in single sample, one measurement per second +* mode +*/ + rc =regmap_write (data->regmap,DPS310_PRS_CFG ,0 ); +if (rc <0 ) +return rc; + +/* +* Set up external (MEMS) temperature sensor in single sample, one +* measurement per second mode +*/ + rc =regmap_write (data->regmap,DPS310_TMP_CFG ,DPS310_TMP_EXT ); +if (rc <0 ) +return rc; + +/* Temp and pressure shifts are disabled when PRC <= 8 */ + rc =regmap_write_bits (data->regmap,DPS310_CFG_REG , +DPS310_PRS_SHIFT_EN |DPS310_TMP_SHIFT_EN ,0 ); +if (rc <0 ) +return rc; + +/* MEAS_CFG doesn't update correctly unless first written with 0 */ + rc =regmap_write_bits (data->regmap,DPS310_MEAS_CFG , +DPS310_MEAS_CTRL_BITS ,0 ); +if (rc <0 ) +return rc; + +/* Turn on temperature and pressure measurement in the background */ + rc =regmap_write_bits (data->regmap,DPS310_MEAS_CFG , +DPS310_MEAS_CTRL_BITS ,DPS310_PRS_EN | +DPS310_TEMP_EN |DPS310_BACKGROUND ); +if (rc <0 ) +return rc; + +/* +* Calibration coefficients required for reporting temperature. +* They are available 40ms after the device has started +*/ + rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , ready, + ready &DPS310_COEF_RDY ,10000 ,40000 ); +if (rc <0 ) +return rc; + + rc =dps310_get_coefs (data); +if (rc <0 ) +return rc; + + rc =dps310_temp_workaround (data); +if (rc <0 ) +return rc; + +return 0 ; +} +static int dps310_get_pres_precision (struct dps310_data *data) {int rc;
@@ -677,52 +777,12 @@ static const struct iio_info dps310_info = { .write_raw = dps310_write_raw, }; -/* -* Some verions of chip will read temperatures in the ~60C range when -* its actually ~20C. This is the manufacturer recommended workaround -* to correct the issue. The registers used below are undocumented. -*/ -static int dps310_temp_workaround (struct dps310_data *data) -{ -int rc; -int reg; - - rc =regmap_read (data->regmap,0x32 , ®); -if (rc <0 ) -return rc; - -/* -* If bit 1 is set then the device is okay, and the workaround does not -* need to be applied -*/ -if (reg &BIT (1 )) -return 0 ; - - rc =regmap_write (data->regmap,0x0e ,0xA5 ); -if (rc <0 ) -return rc; - - rc =regmap_write (data->regmap,0x0f ,0x96 ); -if (rc <0 ) -return rc; - - rc =regmap_write (data->regmap,0x62 ,0x02 ); -if (rc <0 ) -return rc; - - rc =regmap_write (data->regmap,0x0e ,0x00 ); -if (rc <0 ) -return rc; - -return regmap_write (data->regmap,0x0f ,0x00 ); -} -static int dps310_probe (struct i2c_client *client,const struct i2c_device_id *id) {struct dps310_data *data;struct iio_dev *iio; -int rc, ready; +int rc; iio =devm_iio_device_alloc (&client->dev,sizeof (*data));if (!iio)
@@ -747,54 +807,8 @@ static int dps310_probe(struct i2c_client *client,if (rc)return rc; -/* -* Set up pressure sensor in single sample, one measurement per second -* mode -*/ - rc =regmap_write (data->regmap,DPS310_PRS_CFG ,0 ); - -/* -* Set up external (MEMS) temperature sensor in single sample, one -* measurement per second mode -*/ - rc =regmap_write (data->regmap,DPS310_TMP_CFG ,DPS310_TMP_EXT ); -if (rc <0 ) -return rc; - -/* Temp and pressure shifts are disabled when PRC <= 8 */ - rc =regmap_write_bits (data->regmap,DPS310_CFG_REG , -DPS310_PRS_SHIFT_EN |DPS310_TMP_SHIFT_EN ,0 ); -if (rc <0 ) -return rc; - -/* MEAS_CFG doesn't update correctly unless first written with 0 */ - rc =regmap_write_bits (data->regmap,DPS310_MEAS_CFG , -DPS310_MEAS_CTRL_BITS ,0 ); -if (rc <0 ) -return rc; - -/* Turn on temperature and pressure measurement in the background */ - rc =regmap_write_bits (data->regmap,DPS310_MEAS_CFG , -DPS310_MEAS_CTRL_BITS ,DPS310_PRS_EN | -DPS310_TEMP_EN |DPS310_BACKGROUND ); -if (rc <0 ) -return rc; - -/* -* Calibration coefficients required for reporting temperature. -* They are available 40ms after the device has started -*/ - rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , ready, - ready &DPS310_COEF_RDY ,10000 ,40000 ); -if (rc <0 ) -return rc; - - rc =dps310_get_coefs (data); -if (rc <0 ) -return rc; - - rc =dps310_temp_workaround (data); -if (rc <0 ) + rc =dps310_startup (data); +if (rc)return rc; rc =devm_iio_device_register (&client->dev, iio);
Signature · 1 line
2.27.0
Re: [PATCH v3 1/2] iio: pressure: dps310: Refactor startup procedure
Quoted 5 lines · Eddie James
Move the startup procedure into a function, and correct a missing check on the return code for writing the
PRS_CFGregister.Signed-off-by Eddie James <[email protected]>
Quoted 229 lines
--- drivers/iio/pressure/dps310.c | 192 ++++++++++++++++++---------------- 1 file changed, 103 insertions(+), 89 deletions(-)drivers/iio/pressure/dps310.c +103
-90@@ -159,6 +159,106 @@ static int dps310_get_coefs(struct dps310_data *data)return 0 ; } +/* +* Some verions of chip will read temperatures in the ~60C range when +* its actually ~20C. This is the manufacturer recommended workaround +* to correct the issue. The registers used below are undocumented. +*/ +static int dps310_temp_workaround (struct dps310_data *data) +{ +int rc; +int reg; + + rc =regmap_read (data->regmap,0x32 , ®); +if (rc <0 ) +return rc; + +/* +* If bit 1 is set then the device is okay, and the workaround does not +* need to be applied +*/ +if (reg &BIT (1 )) +return 0 ; + + rc =regmap_write (data->regmap,0x0e ,0xA5 ); +if (rc <0 ) +return rc; + + rc =regmap_write (data->regmap,0x0f ,0x96 ); +if (rc <0 ) +return rc; + + rc =regmap_write (data->regmap,0x62 ,0x02 ); +if (rc <0 ) +return rc; + + rc =regmap_write (data->regmap,0x0e ,0x00 ); +if (rc <0 ) +return rc; + +return regmap_write (data->regmap,0x0f ,0x00 ); +} + +static int dps310_startup (struct dps310_data *data) +{ +int rc; +int ready; + +/* +* Set up pressure sensor in single sample, one measurement per second +* mode +*/ + rc =regmap_write (data->regmap,DPS310_PRS_CFG ,0 ); +if (rc <0 ) +return rc; + +/* +* Set up external (MEMS) temperature sensor in single sample, one +* measurement per second mode +*/ + rc =regmap_write (data->regmap,DPS310_TMP_CFG ,DPS310_TMP_EXT ); +if (rc <0 ) +return rc; + +/* Temp and pressure shifts are disabled when PRC <= 8 */ + rc =regmap_write_bits (data->regmap,DPS310_CFG_REG , +DPS310_PRS_SHIFT_EN |DPS310_TMP_SHIFT_EN ,0 ); +if (rc <0 ) +return rc; + +/* MEAS_CFG doesn't update correctly unless first written with 0 */ + rc =regmap_write_bits (data->regmap,DPS310_MEAS_CFG , +DPS310_MEAS_CTRL_BITS ,0 ); +if (rc <0 ) +return rc; + +/* Turn on temperature and pressure measurement in the background */ + rc =regmap_write_bits (data->regmap,DPS310_MEAS_CFG , +DPS310_MEAS_CTRL_BITS ,DPS310_PRS_EN | +DPS310_TEMP_EN |DPS310_BACKGROUND ); +if (rc <0 ) +return rc; + +/* +* Calibration coefficients required for reporting temperature. +* They are available 40ms after the device has started +*/ + rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , ready, + ready &DPS310_COEF_RDY ,10000 ,40000 ); +if (rc <0 ) +return rc; + + rc =dps310_get_coefs (data); +if (rc <0 ) +return rc; + + rc =dps310_temp_workaround (data); +if (rc <0 ) +return rc; + +return 0 ; +} +static int dps310_get_pres_precision (struct dps310_data *data) {int rc;@@ -677,52 +777,12 @@ static const struct iio_info dps310_info = { .write_raw = dps310_write_raw, }; -/* -* Some verions of chip will read temperatures in the ~60C range when -* its actually ~20C. This is the manufacturer recommended workaround -* to correct the issue. The registers used below are undocumented. -*/ -static int dps310_temp_workaround (struct dps310_data *data) -{ -int rc; -int reg; - - rc =regmap_read (data->regmap,0x32 , ®); -if (rc <0 ) -return rc; - -/* -* If bit 1 is set then the device is okay, and the workaround does not -* need to be applied -*/ -if (reg &BIT (1 )) -return 0 ; - - rc =regmap_write (data->regmap,0x0e ,0xA5 ); -if (rc <0 ) -return rc; - - rc =regmap_write (data->regmap,0x0f ,0x96 ); -if (rc <0 ) -return rc; - - rc =regmap_write (data->regmap,0x62 ,0x02 ); -if (rc <0 ) -return rc; - - rc =regmap_write (data->regmap,0x0e ,0x00 ); -if (rc <0 ) -return rc; - -return regmap_write (data->regmap,0x0f ,0x00 ); -} -static int dps310_probe (struct i2c_client *client,const struct i2c_device_id *id) {struct dps310_data *data;struct iio_dev *iio; -int rc, ready; +int rc; iio =devm_iio_device_alloc (&client->dev,sizeof (*data));if (!iio)@@ -747,54 +807,8 @@ static int dps310_probe(struct i2c_client *client,if (rc)return rc; -/* -* Set up pressure sensor in single sample, one measurement per second -* mode -*/ - rc =regmap_write (data->regmap,DPS310_PRS_CFG ,0 ); - -/* -* Set up external (MEMS) temperature sensor in single sample, one -* measurement per second mode -*/ - rc =regmap_write (data->regmap,DPS310_TMP_CFG ,DPS310_TMP_EXT ); -if (rc <0 ) -return rc; - -/* Temp and pressure shifts are disabled when PRC <= 8 */ - rc =regmap_write_bits (data->regmap,DPS310_CFG_REG , -DPS310_PRS_SHIFT_EN |DPS310_TMP_SHIFT_EN ,0 ); -if (rc <0 ) -return rc; - -/* MEAS_CFG doesn't update correctly unless first written with 0 */ - rc =regmap_write_bits (data->regmap,DPS310_MEAS_CFG , -DPS310_MEAS_CTRL_BITS ,0 ); -if (rc <0 ) -return rc; - -/* Turn on temperature and pressure measurement in the background */ - rc =regmap_write_bits (data->regmap,DPS310_MEAS_CFG , -DPS310_MEAS_CTRL_BITS ,DPS310_PRS_EN | -DPS310_TEMP_EN |DPS310_BACKGROUND ); -if (rc <0 ) -return rc; - -/* -* Calibration coefficients required for reporting temperature. -* They are available 40ms after the device has started -*/ - rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , ready, - ready &DPS310_COEF_RDY ,10000 ,40000 ); -if (rc <0 ) -return rc; - - rc =dps310_get_coefs (data); -if (rc <0 ) -return rc; - - rc =dps310_temp_workaround (data); -if (rc <0 ) + rc =dps310_startup (data); +if (rc)return rc; rc =devm_iio_device_register (&client->dev, iio); --2.27.0
Re: [PATCH v3 1/2] iio: pressure: dps310: Refactor startup procedure
In reply to Eddie JamesQuoted 3 lines · Eddie James
Move the startup procedure into a function, and correct a missing check on the return code for writing the
PRS_CFGregister.
...
Quoted 2 lines
+/*
+ * Some verions of chip will read temperatures in the ~60C range when
versions
of the chip(s)
Quoted 1 line
+ * its actually ~20C. This is the manufacturer recommended workaround
it's
Quoted 2 lines
+ * to correct the issue. The registers used below are undocumented.
+ */
Yes, they are in the original code, but you may just fix while at it.
...
drivers/iio/pressure/dps310.c static int dps310_get_coefs(struct dps310_data *data) · line 256
Quoted 2 lines
drivers/iio/pressure/dps310.c +2
-0+ rc =dps310_temp_workaround (data); +if (rc <0 )
These ' < 0' parts are cargo cult, I believe.
That's why this makes code longer and confusing by "what is the meaning of the positive values here?"
drivers/iio/pressure/dps310.c static int dps310_get_coefs(struct dps310_data *data) · line 181
Quoted 3 lines
drivers/iio/pressure/dps310.c +3
-0+return rc; + +return 0 ;
That said,
return dps310_temp_workaround(data);
should suffice.
Signature · 2 lines
With Best Regards, Andy Shevchenko
Eddie James
maintainerPATCH v3 2/2 iio: pressure: dps310: Reset chip if MEAS_CFG is corrupt
In reply to Eddie JamesCorruption of the MEAS_CFG register has been observed soon after system boot. In order to recover this scenario, check MEAS_CFG if measurement isn't ready, and if it's incorrect, reset the DPS310 and execute the startup procedure.
--- drivers/iio/pressure/dps310.c | 88 +++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 15 deletions(-)
drivers/iio/pressure/dps310.c +73 -15
@@ -397,6 +397,44 @@ static int dps310_get_temp_k(struct dps310_data *data)return scale_factors[ilog2 (rc)]; } +/* +* Called with lock held. Returns a negative value on error, a positive value +* when the device is not ready, and zero when the device is ready. +*/ +static int dps310_check_reset_meas_cfg (struct dps310_data *data,int ready_bit) +{ +int meas_cfg; +int rc =regmap_read (data->regmap,DPS310_MEAS_CFG , &meas_cfg); + +if (rc <0 ) +return rc; + +/* Device is ready, proceed to measurement */ +if (meas_cfg & ready_bit) +return 0 ; + +/* Device is OK, just not ready */ +if (meas_cfg & (DPS310_PRS_EN |DPS310_TEMP_EN |DPS310_BACKGROUND )) +return 1 ; + +/* DPS310 register state corrupt, better start from scratch */ + rc =regmap_write (data->regmap,DPS310_RESET ,DPS310_RESET_MAGIC ); +if (rc <0 ) +return rc; + +/* Wait for device chip access: 2.5ms in specification */ +usleep_range (2500 ,12000 ); + +/* Reinitialize the chip */ + rc =dps310_startup (data); +if (rc) +return rc; + +dev_info (&data->client->dev, +"recovered from corrupted MEAS_CFG=%02x\n" , meas_cfg); +return 1 ; +} +static int dps310_read_pres_raw (struct dps310_data *data) {int rc;
@@ -409,16 +447,26 @@ static int dps310_read_pres_raw(struct dps310_data *data)if (mutex_lock_interruptible (&data->lock))return -EINTR ; - rate =dps310_get_pres_samp_freq (data); - timeout =DPS310_POLL_TIMEOUT_US (rate); - -/* Poll for sensor readiness; base the timeout upon the sample rate. */ - rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , ready, - ready &DPS310_PRS_RDY , -DPS310_POLL_SLEEP_US (timeout), timeout); -if (rc) + rc =dps310_check_reset_meas_cfg (data,DPS310_PRS_RDY ); +if (rc <0 ) goto done; +if (rc >0 ) { + rate =dps310_get_pres_samp_freq (data); + timeout =DPS310_POLL_TIMEOUT_US (rate); + +/* +* Poll for sensor readiness; base the timeout upon the sample +* rate. +*/ + rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , + ready, ready &DPS310_PRS_RDY , +DPS310_POLL_SLEEP_US (timeout), + timeout); +if (rc) + goto done; + } + rc =regmap_bulk_read (data->regmap,DPS310_PRS_BASE , val,sizeof (val));if (rc <0 ) goto done;
@@ -458,16 +506,26 @@ static int dps310_read_temp_raw(struct dps310_data *data)if (mutex_lock_interruptible (&data->lock))return -EINTR ; - rate =dps310_get_temp_samp_freq (data); - timeout =DPS310_POLL_TIMEOUT_US (rate); - -/* Poll for sensor readiness; base the timeout upon the sample rate. */ - rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , ready, - ready &DPS310_TMP_RDY , -DPS310_POLL_SLEEP_US (timeout), timeout); + rc =dps310_check_reset_meas_cfg (data,DPS310_TMP_RDY );if (rc <0 ) goto done; +if (rc >0 ) { + rate =dps310_get_temp_samp_freq (data); + timeout =DPS310_POLL_TIMEOUT_US (rate); + +/* +* Poll for sensor readiness; base the timeout upon the sample +* rate. +*/ + rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , + ready, ready &DPS310_TMP_RDY , +DPS310_POLL_SLEEP_US (timeout), + timeout); +if (rc <0 ) + goto done; + } + rc =dps310_read_temp_ready (data); done:
Signature · 1 line
2.27.0
Quoted 7 lines · Eddie James
Corruption of the
MEAS_CFGregister has been observed soon after system boot. In order to recover this scenario, checkMEAS_CFGif measurement isn't ready, and if it's incorrect, reset the DPS310 and execute the startup procedure.Signed-off-by Eddie James <[email protected]>
Quoted 125 lines
--- drivers/iio/pressure/dps310.c | 88 +++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 15 deletions(-)drivers/iio/pressure/dps310.c +73
-16@@ -397,6 +397,44 @@ static int dps310_get_temp_k(struct dps310_data *data)return scale_factors[ilog2 (rc)]; } +/* +* Called with lock held. Returns a negative value on error, a positive value +* when the device is not ready, and zero when the device is ready. +*/ +static int dps310_check_reset_meas_cfg (struct dps310_data *data,int ready_bit) +{ +int meas_cfg; +int rc =regmap_read (data->regmap,DPS310_MEAS_CFG , &meas_cfg); + +if (rc <0 ) +return rc; + +/* Device is ready, proceed to measurement */ +if (meas_cfg & ready_bit) +return 0 ; + +/* Device is OK, just not ready */ +if (meas_cfg & (DPS310_PRS_EN |DPS310_TEMP_EN |DPS310_BACKGROUND )) +return 1 ; + +/* DPS310 register state corrupt, better start from scratch */ + rc =regmap_write (data->regmap,DPS310_RESET ,DPS310_RESET_MAGIC ); +if (rc <0 ) +return rc; + +/* Wait for device chip access: 2.5ms in specification */ +usleep_range (2500 ,12000 ); + +/* Reinitialize the chip */ + rc =dps310_startup (data); +if (rc) +return rc; + +dev_info (&data->client->dev, +"recovered from corrupted MEAS_CFG=%02x\n" , meas_cfg); +return 1 ; +} +static int dps310_read_pres_raw (struct dps310_data *data) {int rc;@@ -409,16 +447,26 @@ static int dps310_read_pres_raw(struct dps310_data *data)if (mutex_lock_interruptible (&data->lock))return -EINTR ; - rate =dps310_get_pres_samp_freq (data); - timeout =DPS310_POLL_TIMEOUT_US (rate); - -/* Poll for sensor readiness; base the timeout upon the sample rate. */ - rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , ready, - ready &DPS310_PRS_RDY , -DPS310_POLL_SLEEP_US (timeout), timeout); -if (rc) + rc =dps310_check_reset_meas_cfg (data,DPS310_PRS_RDY ); +if (rc <0 ) goto done; +if (rc >0 ) { + rate =dps310_get_pres_samp_freq (data); + timeout =DPS310_POLL_TIMEOUT_US (rate); + +/* +* Poll for sensor readiness; base the timeout upon the sample +* rate. +*/ + rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , + ready, ready &DPS310_PRS_RDY , +DPS310_POLL_SLEEP_US (timeout), + timeout); +if (rc) + goto done; + } + rc =regmap_bulk_read (data->regmap,DPS310_PRS_BASE , val,sizeof (val));if (rc <0 ) goto done;@@ -458,16 +506,26 @@ static int dps310_read_temp_raw(struct dps310_data *data)if (mutex_lock_interruptible (&data->lock))return -EINTR ; - rate =dps310_get_temp_samp_freq (data); - timeout =DPS310_POLL_TIMEOUT_US (rate); - -/* Poll for sensor readiness; base the timeout upon the sample rate. */ - rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , ready, - ready &DPS310_TMP_RDY , -DPS310_POLL_SLEEP_US (timeout), timeout); + rc =dps310_check_reset_meas_cfg (data,DPS310_TMP_RDY );if (rc <0 ) goto done; +if (rc >0 ) { + rate =dps310_get_temp_samp_freq (data); + timeout =DPS310_POLL_TIMEOUT_US (rate); + +/* +* Poll for sensor readiness; base the timeout upon the sample +* rate. +*/ + rc =regmap_read_poll_timeout (data->regmap,DPS310_MEAS_CFG , + ready, ready &DPS310_TMP_RDY , +DPS310_POLL_SLEEP_US (timeout), + timeout); +if (rc <0 ) + goto done; + } + rc =dps310_read_temp_ready (data); done: --2.27.0