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

iio: imu: inv_mpu6050: add WoM suspend wakeup with low-power mode

Add wakeup from suspend for WoM when enabled and put accel in low-power
mode when suspended. Requires rewriting pwr_mgmt_1 register handling and
factorize out accel LPF settings. Use a low-power rate similar to the chip
sampling rate but always lower for a best match of the sampling rate while
saving power and adjust threshold to follow the required roc value.

Signed-off-by: Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
Link: https://lore.kernel.org/r/20240311160557.437337-5-inv.git-commit@tdk.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Jean-Baptiste Maneyrol and committed by
Jonathan Cameron
305914d0 5537f653

+166 -51
+152 -51
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
··· 289 289 }; 290 290 291 291 static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep, 292 - int clock, int temp_dis) 292 + bool cycle, int clock, int temp_dis) 293 293 { 294 294 u8 val; 295 295 ··· 303 303 val |= INV_MPU6050_BIT_TEMP_DIS; 304 304 if (sleep) 305 305 val |= INV_MPU6050_BIT_SLEEP; 306 + if (cycle) 307 + val |= INV_MPU6050_BIT_CYCLE; 306 308 307 309 dev_dbg(regmap_get_device(st->map), "pwr_mgmt_1: 0x%x\n", val); 308 310 return regmap_write(st->map, st->reg->pwr_mgmt_1, val); ··· 320 318 case INV_MPU6000: 321 319 case INV_MPU9150: 322 320 /* old chips: switch clock manually */ 323 - ret = inv_mpu6050_pwr_mgmt_1_write(st, false, clock, -1); 321 + ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, clock, -1); 324 322 if (ret) 325 323 return ret; 326 324 st->chip_config.clk = clock; ··· 362 360 363 361 /* turn on/off temperature sensor */ 364 362 if (mask & INV_MPU6050_SENSOR_TEMP) { 365 - ret = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, !en); 363 + ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, !en); 366 364 if (ret) 367 365 return ret; 368 366 st->chip_config.temp_en = en; ··· 469 467 { 470 468 int result; 471 469 472 - result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1); 470 + result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, false, -1, -1); 473 471 if (result) 474 472 return result; 475 473 ··· 499 497 return regmap_write(st->map, st->reg->gyro_config, data); 500 498 } 501 499 502 - /* 503 - * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent 504 - * 505 - * MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope 506 - * MPU6500 and above have a dedicated register for accelerometer 507 - */ 508 - static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, 509 - enum inv_mpu6050_filter_e val) 500 + static int inv_mpu6050_set_accel_lpf_regs(struct inv_mpu6050_state *st, 501 + enum inv_mpu6050_filter_e val) 510 502 { 511 - int result; 512 - 513 - result = regmap_write(st->map, st->reg->lpf, val); 514 - if (result) 515 - return result; 516 - 517 - /* set accel lpf */ 518 503 switch (st->chip_type) { 519 504 case INV_MPU6050: 520 505 case INV_MPU6000: ··· 518 529 } 519 530 520 531 return regmap_write(st->map, st->reg->accel_lpf, val); 532 + } 533 + 534 + /* 535 + * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent 536 + * 537 + * MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope 538 + * MPU6500 and above have a dedicated register for accelerometer 539 + */ 540 + static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, 541 + enum inv_mpu6050_filter_e val) 542 + { 543 + int result; 544 + 545 + result = regmap_write(st->map, st->reg->lpf, val); 546 + if (result) 547 + return result; 548 + 549 + /* set accel lpf */ 550 + return inv_mpu6050_set_accel_lpf_regs(st, val); 521 551 } 522 552 523 553 /* ··· 1008 1000 st->chip_config.roc_threshold = value; 1009 1001 1010 1002 return 0; 1003 + } 1004 + 1005 + static int inv_mpu6050_set_lp_odr(struct inv_mpu6050_state *st, unsigned int freq_div, 1006 + unsigned int *lp_div) 1007 + { 1008 + static const unsigned int freq_dividers[] = {2, 4, 8, 16, 32, 64, 128, 256}; 1009 + static const unsigned int reg_values[] = { 1010 + INV_MPU6050_LPOSC_500HZ, INV_MPU6050_LPOSC_250HZ, 1011 + INV_MPU6050_LPOSC_125HZ, INV_MPU6050_LPOSC_62HZ, 1012 + INV_MPU6050_LPOSC_31HZ, INV_MPU6050_LPOSC_16HZ, 1013 + INV_MPU6050_LPOSC_8HZ, INV_MPU6050_LPOSC_4HZ, 1014 + }; 1015 + unsigned int val, i; 1016 + 1017 + switch (st->chip_type) { 1018 + case INV_ICM20609: 1019 + case INV_ICM20689: 1020 + case INV_ICM20600: 1021 + case INV_ICM20602: 1022 + case INV_ICM20690: 1023 + /* nothing to do */ 1024 + *lp_div = INV_MPU6050_FREQ_DIVIDER(st); 1025 + return 0; 1026 + default: 1027 + break; 1028 + } 1029 + 1030 + /* found the nearest superior frequency divider */ 1031 + i = ARRAY_SIZE(reg_values) - 1; 1032 + val = reg_values[i]; 1033 + *lp_div = freq_dividers[i]; 1034 + for (i = 0; i < ARRAY_SIZE(freq_dividers); ++i) { 1035 + if (freq_div <= freq_dividers[i]) { 1036 + val = reg_values[i]; 1037 + *lp_div = freq_dividers[i]; 1038 + break; 1039 + } 1040 + } 1041 + 1042 + dev_dbg(regmap_get_device(st->map), "lp_odr: 0x%x\n", val); 1043 + return regmap_write(st->map, INV_MPU6500_REG_LP_ODR, val); 1044 + } 1045 + 1046 + static int inv_mpu6050_set_wom_lp(struct inv_mpu6050_state *st, bool on) 1047 + { 1048 + unsigned int lp_div; 1049 + int result; 1050 + 1051 + if (on) { 1052 + /* set low power ODR */ 1053 + result = inv_mpu6050_set_lp_odr(st, INV_MPU6050_FREQ_DIVIDER(st), &lp_div); 1054 + if (result) 1055 + return result; 1056 + /* disable accel low pass filter */ 1057 + result = inv_mpu6050_set_accel_lpf_regs(st, INV_MPU6050_FILTER_NOLPF); 1058 + if (result) 1059 + return result; 1060 + /* update wom threshold with new low-power frequency divider */ 1061 + result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, lp_div); 1062 + if (result) 1063 + return result; 1064 + /* set cycle mode */ 1065 + result = inv_mpu6050_pwr_mgmt_1_write(st, false, true, -1, -1); 1066 + } else { 1067 + /* disable cycle mode */ 1068 + result = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, -1); 1069 + if (result) 1070 + return result; 1071 + /* restore wom threshold */ 1072 + result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, 1073 + INV_MPU6050_FREQ_DIVIDER(st)); 1074 + if (result) 1075 + return result; 1076 + /* restore accel low pass filter */ 1077 + result = inv_mpu6050_set_accel_lpf_regs(st, st->chip_config.lpf); 1078 + } 1079 + 1080 + return result; 1011 1081 } 1012 1082 1013 1083 static int inv_mpu6050_enable_wom(struct inv_mpu6050_state *st, bool en) ··· 1922 1836 irq_type); 1923 1837 return -EINVAL; 1924 1838 } 1839 + device_set_wakeup_capable(dev, true); 1925 1840 1926 1841 st->vdd_supply = devm_regulator_get(dev, "vdd"); 1927 1842 if (IS_ERR(st->vdd_supply)) ··· 2088 2001 { 2089 2002 struct iio_dev *indio_dev = dev_get_drvdata(dev); 2090 2003 struct inv_mpu6050_state *st = iio_priv(indio_dev); 2004 + bool wakeup; 2091 2005 int result; 2092 2006 2093 - mutex_lock(&st->lock); 2094 - result = inv_mpu_core_enable_regulator_vddio(st); 2095 - if (result) 2096 - goto out_unlock; 2007 + guard(mutex)(&st->lock); 2097 2008 2098 - result = inv_mpu6050_set_power_itg(st, true); 2099 - if (result) 2100 - goto out_unlock; 2009 + wakeup = device_may_wakeup(dev) && st->chip_config.wom_en; 2010 + 2011 + if (wakeup) { 2012 + enable_irq(st->irq); 2013 + disable_irq_wake(st->irq); 2014 + result = inv_mpu6050_set_wom_lp(st, false); 2015 + if (result) 2016 + return result; 2017 + } else { 2018 + result = inv_mpu_core_enable_regulator_vddio(st); 2019 + if (result) 2020 + return result; 2021 + result = inv_mpu6050_set_power_itg(st, true); 2022 + if (result) 2023 + return result; 2024 + } 2101 2025 2102 2026 pm_runtime_disable(dev); 2103 2027 pm_runtime_set_active(dev); ··· 2116 2018 2117 2019 result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors); 2118 2020 if (result) 2119 - goto out_unlock; 2021 + return result; 2120 2022 2121 - if (st->chip_config.wom_en) { 2023 + if (st->chip_config.wom_en && !wakeup) { 2122 2024 result = inv_mpu6050_set_wom_int(st, true); 2123 2025 if (result) 2124 - goto out_unlock; 2026 + return result; 2125 2027 } 2126 2028 2127 2029 if (iio_buffer_enabled(indio_dev)) 2128 2030 result = inv_mpu6050_prepare_fifo(st, true); 2129 - 2130 - out_unlock: 2131 - mutex_unlock(&st->lock); 2132 2031 2133 2032 return result; 2134 2033 } ··· 2134 2039 { 2135 2040 struct iio_dev *indio_dev = dev_get_drvdata(dev); 2136 2041 struct inv_mpu6050_state *st = iio_priv(indio_dev); 2042 + bool wakeup; 2137 2043 int result; 2138 2044 2139 - mutex_lock(&st->lock); 2045 + guard(mutex)(&st->lock); 2140 2046 2141 2047 st->suspended_sensors = 0; 2142 - if (pm_runtime_suspended(dev)) { 2143 - result = 0; 2144 - goto out_unlock; 2145 - } 2048 + if (pm_runtime_suspended(dev)) 2049 + return 0; 2146 2050 2147 2051 if (iio_buffer_enabled(indio_dev)) { 2148 2052 result = inv_mpu6050_prepare_fifo(st, false); 2149 2053 if (result) 2150 - goto out_unlock; 2054 + return result; 2151 2055 } 2152 2056 2153 - if (st->chip_config.wom_en) { 2057 + wakeup = device_may_wakeup(dev) && st->chip_config.wom_en; 2058 + 2059 + if (st->chip_config.wom_en && !wakeup) { 2154 2060 result = inv_mpu6050_set_wom_int(st, false); 2155 2061 if (result) 2156 - goto out_unlock; 2062 + return result; 2157 2063 } 2158 2064 2159 - if (st->chip_config.accl_en) 2065 + if (st->chip_config.accl_en && !wakeup) 2160 2066 st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL; 2161 2067 if (st->chip_config.gyro_en) 2162 2068 st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO; ··· 2165 2069 st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP; 2166 2070 if (st->chip_config.magn_en) 2167 2071 st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN; 2168 - if (st->chip_config.wom_en) 2072 + if (st->chip_config.wom_en && !wakeup) 2169 2073 st->suspended_sensors |= INV_MPU6050_SENSOR_WOM; 2170 2074 result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors); 2171 2075 if (result) 2172 - goto out_unlock; 2076 + return result; 2173 2077 2174 - result = inv_mpu6050_set_power_itg(st, false); 2175 - if (result) 2176 - goto out_unlock; 2078 + if (wakeup) { 2079 + result = inv_mpu6050_set_wom_lp(st, true); 2080 + if (result) 2081 + return result; 2082 + enable_irq_wake(st->irq); 2083 + disable_irq(st->irq); 2084 + } else { 2085 + result = inv_mpu6050_set_power_itg(st, false); 2086 + if (result) 2087 + return result; 2088 + inv_mpu_core_disable_regulator_vddio(st); 2089 + } 2177 2090 2178 - inv_mpu_core_disable_regulator_vddio(st); 2179 - out_unlock: 2180 - mutex_unlock(&st->lock); 2181 - 2182 - return result; 2091 + return 0; 2183 2092 } 2184 2093 2185 2094 static int inv_mpu_runtime_suspend(struct device *dev)
+14
drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
··· 305 305 #define INV_MPU6050_REG_PWR_MGMT_1 0x6B 306 306 #define INV_MPU6050_BIT_H_RESET 0x80 307 307 #define INV_MPU6050_BIT_SLEEP 0x40 308 + #define INV_MPU6050_BIT_CYCLE 0x20 308 309 #define INV_MPU6050_BIT_TEMP_DIS 0x08 309 310 #define INV_MPU6050_BIT_CLK_MASK 0x7 310 311 ··· 337 336 /* mpu6500 registers */ 338 337 #define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D 339 338 #define INV_ICM20689_BITS_FIFO_SIZE_MAX 0xC0 339 + #define INV_MPU6500_REG_LP_ODR 0x1E 340 340 #define INV_MPU6500_REG_WOM_THRESHOLD 0x1F 341 341 #define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69 342 342 #define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7) ··· 452 450 INV_MPU6050_FILTER_5HZ, 453 451 INV_MPU6050_FILTER_NOLPF, 454 452 NUM_MPU6050_FILTER 453 + }; 454 + 455 + enum inv_mpu6050_lposc_e { 456 + INV_MPU6050_LPOSC_4HZ = 4, 457 + INV_MPU6050_LPOSC_8HZ, 458 + INV_MPU6050_LPOSC_16HZ, 459 + INV_MPU6050_LPOSC_31HZ, 460 + INV_MPU6050_LPOSC_62HZ, 461 + INV_MPU6050_LPOSC_125HZ, 462 + INV_MPU6050_LPOSC_250HZ, 463 + INV_MPU6050_LPOSC_500HZ, 464 + NUM_MPU6050_LPOSC, 455 465 }; 456 466 457 467 /* IIO attribute address */