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

iio: accel: fix ADXL355 startup race condition

There is an race-condition where device is not full working after SW reset.
Therefore it's necessary to wait some time after reset and verify shadow
registers values by reading and comparing the values before/after reset.
This mechanism is described in datasheet at least from revision D.

Fixes: 12ed27863ea3 ("iio: accel: Add driver support for ADXL355")
Signed-off-by: Valek Andrej <andrej.v@skyrain.eu>
Signed-off-by: Kessler Markus <markus.kessler@hilti.com>
Cc: <Stable@vger.kernel.org>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Valek Andrej and committed by
Jonathan Cameron
c92c1bc4 e2cc390a

+39 -5
+39 -5
drivers/iio/accel/adxl355_core.c
··· 56 56 #define ADXL355_POWER_CTL_DRDY_MSK BIT(2) 57 57 #define ADXL355_SELF_TEST_REG 0x2E 58 58 #define ADXL355_RESET_REG 0x2F 59 + #define ADXL355_BASE_ADDR_SHADOW_REG 0x50 60 + #define ADXL355_SHADOW_REG_COUNT 5 59 61 60 62 #define ADXL355_DEVID_AD_VAL 0xAD 61 63 #define ADXL355_DEVID_MST_VAL 0x1D ··· 296 294 static int adxl355_setup(struct adxl355_data *data) 297 295 { 298 296 unsigned int regval; 297 + int retries = 5; /* the number is chosen based on empirical reasons */ 299 298 int ret; 299 + u8 *shadow_regs __free(kfree) = kzalloc(ADXL355_SHADOW_REG_COUNT, GFP_KERNEL); 300 + 301 + if (!shadow_regs) 302 + return -ENOMEM; 300 303 301 304 ret = regmap_read(data->regmap, ADXL355_DEVID_AD_REG, &regval); 302 305 if (ret) ··· 328 321 if (regval != ADXL355_PARTID_VAL) 329 322 dev_warn(data->dev, "Invalid DEV ID 0x%02x\n", regval); 330 323 331 - /* 332 - * Perform a software reset to make sure the device is in a consistent 333 - * state after start-up. 334 - */ 335 - ret = regmap_write(data->regmap, ADXL355_RESET_REG, ADXL355_RESET_CODE); 324 + /* Read shadow registers to be compared after reset */ 325 + ret = regmap_bulk_read(data->regmap, 326 + ADXL355_BASE_ADDR_SHADOW_REG, 327 + shadow_regs, ADXL355_SHADOW_REG_COUNT); 336 328 if (ret) 337 329 return ret; 330 + 331 + do { 332 + if (--retries == 0) { 333 + dev_err(data->dev, "Shadow registers mismatch\n"); 334 + return -EIO; 335 + } 336 + 337 + /* 338 + * Perform a software reset to make sure the device is in a consistent 339 + * state after start-up. 340 + */ 341 + ret = regmap_write(data->regmap, ADXL355_RESET_REG, 342 + ADXL355_RESET_CODE); 343 + if (ret) 344 + return ret; 345 + 346 + /* Wait at least 5ms after software reset */ 347 + usleep_range(5000, 10000); 348 + 349 + /* Read shadow registers for comparison */ 350 + ret = regmap_bulk_read(data->regmap, 351 + ADXL355_BASE_ADDR_SHADOW_REG, 352 + data->buffer.buf, 353 + ADXL355_SHADOW_REG_COUNT); 354 + if (ret) 355 + return ret; 356 + } while (memcmp(shadow_regs, data->buffer.buf, 357 + ADXL355_SHADOW_REG_COUNT)); 338 358 339 359 ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, 340 360 ADXL355_POWER_CTL_DRDY_MSK,