From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-ot1-f54.google.com (mail-ot1-f54.google.com [209.85.210.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F41692F83CB for ; Thu, 19 Mar 2026 18:32:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.54 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773945160; cv=none; b=e5mKIOsnvh+Qua4VMlo8GaRoHjXBTqp5e25EGUy+sU3q5Zd8gAA7oJpXBVzn8tFO3ccrgHWV+U3dX2hmIlB7Zr+x5T98WnlbWjC6wAATwVLOCeLe4TKox/aEY0r08M9WiRvg/9w5lRC2JvLY7E3f2lqcC6GiRTx6JkK3Avm3LtA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773945160; c=relaxed/simple; bh=hK28JWudZg3hx7b9lhnyKXE24k1Y61+j1jnme3g04fA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=cTUbLe9p5Ysx2wpIfdu33dT0fShAwPAVVIuE6tZlkrpbR1y76orVVb0Hkp8C25DP9wwJaB0M5rkJhkjIIm2Pm9KFmOclacKi+N2/DwlRSSvcC43JAoDGtSBQvcUnYDUZnahf3tNWSs/UW0BWEHhjcXzk0ci9/X4QGi38sy3aii0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=PR8MZvZh; arc=none smtp.client-ip=209.85.210.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="PR8MZvZh" Received: by mail-ot1-f54.google.com with SMTP id 46e09a7af769-7d7d096607bso571657a34.1 for ; Thu, 19 Mar 2026 11:32:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773945149; x=1774549949; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4h9IPWy9h6c3Rryt+rMRgrO9ZXsOTBW6jWrj6chYR0Y=; b=PR8MZvZhVPauM37Fl+SZBzTuSRNykDlRPF0jRmWwIQY30Fg8vuXIuv+UksYV0VOnsH wIDmylKLyMbKGMNJtP3uq8nYRhGEyUC5oGRQjfbZW42Ys3p03WrsKdl/k0TfpDKzlVEy WVhng8gR4C0lDKGD0V5KlPvodaZIM9oQXHMGjr1hQAGhi+anqFnOEuKIvU4P4hx5bNax 5PpK0BpgvQ2e8qh1wEOGym2axqvTjVCfUeC/9+OnWIvkfloKkvAHzrnnpLhCLYoPEAnB zLDrDh+se+Z2RZNgIForVuWeaofs4rgSJeRyeLmfmYHQz86TKV/DOfW51OOXGtsVkLok lpwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773945149; x=1774549949; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=4h9IPWy9h6c3Rryt+rMRgrO9ZXsOTBW6jWrj6chYR0Y=; b=LW9dDl7P15OuUdSfm4Kb5iQRsSpY9xi4kHbBIOM3lywcd03y0k4ulBlGz6dQtzOogB NaZCm8CS4axcwJcIh9BRQbF75VPF7ZIa1T4j+4hJqMzI2n+K9ILOaO6cJURf94F7jGTw 3QrliPtX7ee+NGoHC6S+05b3WzUvnhrRBq97eHt+xf5XSqBVVln+dm/c2XVJEz1Os7s+ tbjidLjTgDfclNDAXJ2FQE4GfA+tv6VANFHQNsGpZRdRcHUjiUZaFhyGLsa8kNz5OEJZ 30GvwlZ7HY0j8DlcVpMYAtDShauoXcwbOf7+IKBNX5373mQIcqzcXsnu/HWncyhoaPBM lrXg== X-Forwarded-Encrypted: i=1; AJvYcCXn9k9ZmYwczH9wwB9GCvcMu/mSuxh1EMobh9kYiezzCd1wDHFs2wWUyCAqq1FyJ1JRVXS8nOaARZ4Q@vger.kernel.org X-Gm-Message-State: AOJu0YwpX58EAr42C++A8Eeg+7XZlpLwCe0KlDaGLheN/7s+97keVqev VCRKCJHJ2fsIKdBmY26m8E9M3QJTSMFfgmhMouXJKRL5G2DI5dkxTldKp/A0kg== X-Gm-Gg: ATEYQzzx1LQWeOiIehkuH0PO6xMLKVd6kST+W65adMRoYy5mD/g9jqJIuGrVWoEf4tY qLB7U1gW5Y8hNPQ6HR5FFIwmh5D0WqXJHadIiHojeYK8U5Rq8BjaAAB9a12sVC1njQ8rSd5LDgY 7zHYhyM3gcHNIDbkKxwsnxkeJu2EFjZiB/AiGVN85p40aI0jFvwZdx4YQhTGPz32fq9cl3U5Me1 ef/NO3+61IpWNlavkJXpjVwa+nMSWd/+zrsiED3dGrTRVKabwNEr4+N4r+FQMfH97uWz+3olbeR M8reRfNWaBBNOG5ASnCnZrCCQjVp5c8EevTRLHlc9JFih7GaT+e9pnFPIN9lh8GXpcH7/wbdjln o2nVbod0IZrOXX+e1o7bybC9UD8L814KC/VmB3O4rQz3QsZVgzCRCjWvZ2wSVN1spMoOhARAbFH qe1nVNZOnzgEbWn+ef6U6D X-Received: by 2002:a05:6830:67c7:b0:7d7:d4ee:c02d with SMTP id 46e09a7af769-7d7eb0290fbmr142807a34.21.1773945148249; Thu, 19 Mar 2026 11:32:28 -0700 (PDT) Received: from localhost.localdomain ([2600:1700:fb0:1bc0::54]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7d7eadcb11csm172193a34.13.2026.03.19.11.32.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Mar 2026 11:32:27 -0700 (PDT) From: Chris Morgan To: linux-iio@vger.kernel.org Cc: andy@kernel.org, nuno.sa@analog.com, dlechner@baylibre.com, jic23@kernel.org, jean-baptiste.maneyrol@tdk.com, linux-rockchip@lists.infradead.org, devicetree@vger.kernel.org, heiko@sntech.de, conor+dt@kernel.org, krzk+dt@kernel.org, robh@kernel.org, andriy.shevchenko@intel.com, Chris Morgan Subject: [PATCH V2 4/5] iio: imu: inv_icm42600: Add support for icm42607 Date: Thu, 19 Mar 2026 13:29:40 -0500 Message-ID: <20260319182956.146976-5-macroalpha82@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260319182956.146976-1-macroalpha82@gmail.com> References: <20260319182956.146976-1-macroalpha82@gmail.com> Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Chris Morgan Add support for the icm42607 and icm42607p over both I2C and SPI. Note that at this time only the icm42607 over i2c has been tested. This driver was updated based on the existing icm42600 along with the datasheet from Invensense and out-of-tree sources included in the LineageOS kernels [1] and Rockchip kernels [2], both derived from sources provided by Invensense. [1] https://github.com/LineageOS/android_kernel_nvidia_kernel-nx/tree/lineage-23.0/drivers/iio/imu/inv_icm42607x [2] https://github.com/rockchip-linux/kernel/tree/develop-6.6/drivers/iio/imu/inv_icm42670 Signed-off-by: Chris Morgan --- drivers/iio/imu/inv_icm42600/inv_icm42600.h | 25 +- .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 482 +++++++++++++++- .../imu/inv_icm42600/inv_icm42600_buffer.c | 240 +++++++- .../imu/inv_icm42600/inv_icm42600_buffer.h | 5 + .../iio/imu/inv_icm42600/inv_icm42600_core.c | 544 +++++++++++++++++- .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 366 +++++++++++- .../iio/imu/inv_icm42600/inv_icm42600_i2c.c | 53 +- .../iio/imu/inv_icm42600/inv_icm42600_spi.c | 59 +- .../iio/imu/inv_icm42600/inv_icm42600_temp.c | 64 +++ .../iio/imu/inv_icm42600/inv_icm42600_temp.h | 4 + 10 files changed, 1799 insertions(+), 43 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index ab3616c5ee1a..9f25632f1915 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -262,7 +262,7 @@ struct inv_icm42600_sensor_state { const int *scales; size_t scales_len; enum inv_icm42600_sensor_mode power_mode; - enum inv_icm42600_filter filter; + int filter; /* either inv_icm42600_filter or inv_icm42607_filter */ struct inv_sensors_timestamp ts; }; @@ -748,6 +748,7 @@ FIELD_PREP(INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, 1) extern const struct regmap_config inv_icm42600_regmap_config; extern const struct regmap_config inv_icm42600_spi_regmap_config; +extern const struct regmap_config inv_icm42607_regmap_config; extern const struct dev_pm_ops inv_icm42600_pm_ops; const struct iio_mount_matrix * @@ -755,20 +756,32 @@ inv_icm42600_get_mount_matrix(const struct iio_dev *indio_dev, const struct iio_chan_spec *chan); u32 inv_icm42600_odr_to_period(enum inv_icm42600_odr odr); +u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr); int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st, struct inv_icm42600_sensor_conf *conf, unsigned int *sleep_ms); +int inv_icm42607_set_accel_conf(struct inv_icm42600_state *st, + struct inv_icm42600_sensor_conf *conf, + unsigned int *sleep_ms); int inv_icm42600_set_gyro_conf(struct inv_icm42600_state *st, struct inv_icm42600_sensor_conf *conf, unsigned int *sleep_ms); +int inv_icm42607_set_gyro_conf(struct inv_icm42600_state *st, + struct inv_icm42600_sensor_conf *conf, + unsigned int *sleep_ms); int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable, unsigned int *sleep_ms); +int inv_icm42607_set_temp_conf(struct inv_icm42600_state *st, bool enable, + unsigned int *sleep_ms); int inv_icm42600_enable_wom(struct inv_icm42600_state *st); +int inv_icm42607_enable_wom(struct inv_icm42600_state *st); + int inv_icm42600_disable_wom(struct inv_icm42600_state *st); +int inv_icm42607_disable_wom(struct inv_icm42600_state *st); int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval); @@ -776,11 +789,17 @@ int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, int inv_icm42600_core_probe(struct regmap *regmap, int chip, inv_icm42600_bus_setup bus_setup); -int inv_icm42600_gyro_init(struct inv_icm42600_state *st, struct iio_dev *indio_dev); +int inv_icm42600_gyro_init(struct inv_icm42600_state *st, + struct iio_dev *indio_dev); +int inv_icm42607_gyro_init(struct inv_icm42600_state *st, + struct iio_dev *indio_dev); int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev); -int inv_icm42600_accel_init(struct inv_icm42600_state *st, struct iio_dev *indio_dev); +int inv_icm42600_accel_init(struct inv_icm42600_state *st, + struct iio_dev *indio_dev); +int inv_icm42607_accel_init(struct inv_icm42600_state *st, + struct iio_dev *indio_dev); int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index cbb27c4e6b82..27066635d5f4 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -50,6 +50,31 @@ .ext_info = _ext_info, \ } +#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \ + { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = _ext_info, \ + } + #define INV_ICM42600_ACCEL_EVENT_CHAN(_modifier, _events, _events_nb) \ { \ .type = IIO_ACCEL, \ @@ -80,6 +105,10 @@ static const int inv_icm42600_accel_filter_values[] = { INV_ICM42600_FILTER_BW_ODR_DIV_2, INV_ICM42600_FILTER_AVG_16X, }; +static const int inv_icm42607_accel_filter_values[] = { + INV_ICM42607_FILTER_BW_25HZ, + INV_ICM42607_FILTER_AVG_16X, +}; static int inv_icm42600_accel_power_mode_set(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, @@ -197,6 +226,19 @@ static const struct iio_chan_spec inv_icm42600_accel_channels[] = { ARRAY_SIZE(inv_icm42600_wom_events)), }; +static const struct iio_chan_spec inv_icm42607_accel_channels[] = { + INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42600_ACCEL_SCAN_X, + inv_icm42600_accel_ext_infos), + INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42600_ACCEL_SCAN_Y, + inv_icm42600_accel_ext_infos), + INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42600_ACCEL_SCAN_Z, + inv_icm42600_accel_ext_infos), + INV_ICM42600_TEMP_CHAN(INV_ICM42600_ACCEL_SCAN_TEMP), + IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42600_ACCEL_SCAN_TIMESTAMP), + INV_ICM42600_ACCEL_EVENT_CHAN(IIO_MOD_X_OR_Y_OR_Z, inv_icm42600_wom_events, + ARRAY_SIZE(inv_icm42600_wom_events)), +}; + /* * IIO buffer data: size must be a power of 2 and timestamp aligned * 16 bytes: 6 bytes acceleration, 2 bytes temperature, 8 bytes timestamp @@ -265,6 +307,49 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev, return ret; } +static int inv_icm42607_accel_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int fifo_en = 0; + unsigned int sleep_temp = 0; + unsigned int sleep_accel = 0; + unsigned int sleep; + int ret; + + guard(mutex)(&st->lock); + + if (*scan_mask & INV_ICM42600_SCAN_MASK_TEMP) { + /* enable temp sensor */ + ret = inv_icm42607_set_temp_conf(st, true, &sleep_temp); + if (ret) + goto out_unlock; + fifo_en |= INV_ICM42600_SENSOR_TEMP; + } + + if (*scan_mask & INV_ICM42600_SCAN_MASK_ACCEL_3AXIS) { + /* enable accel sensor */ + conf.mode = accel_st->power_mode; + conf.filter = accel_st->filter; + ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_accel); + if (ret) + goto out_unlock; + fifo_en |= INV_ICM42600_SENSOR_ACCEL; + } + + /* update data FIFO write */ + ret = inv_icm42607_buffer_set_fifo_en(st, fifo_en | st->fifo.en); + +out_unlock: + /* sleep maximum required time */ + sleep = max(sleep_accel, sleep_temp); + if (sleep) + msleep(sleep); + return ret; +} + static int inv_icm42600_accel_read_sensor(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, s16 *val) @@ -319,6 +404,61 @@ static int inv_icm42600_accel_read_sensor(struct iio_dev *indio_dev, return ret; } +static int inv_icm42607_accel_read_sensor(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + s16 *val) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->map); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int reg; + __be16 *data; + int ret; + + if (chan->type != IIO_ACCEL) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + reg = INV_ICM42607_REG_ACCEL_DATA_X1; + break; + case IIO_MOD_Y: + reg = INV_ICM42607_REG_ACCEL_DATA_Y1; + break; + case IIO_MOD_Z: + reg = INV_ICM42607_REG_ACCEL_DATA_Z1; + break; + default: + return -EINVAL; + } + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; + + guard(mutex)(&st->lock); + + /* enable accel sensor */ + conf.mode = accel_st->power_mode; + conf.filter = accel_st->filter; + ret = inv_icm42607_set_accel_conf(st, &conf, NULL); + if (ret) + return ret; + + /* read accel register data */ + data = (__be16 *)&st->buffer[0]; + ret = regmap_bulk_read(st->map, reg, data, sizeof(*data)); + if (ret) + return ret; + + *val = (int16_t)be16_to_cpup(data); + if (*val == INV_ICM42600_DATA_INVALID) + ret = -EINVAL; + + return ret; +} + static unsigned int inv_icm42600_accel_convert_roc_to_wom(u64 roc, int accel_hz, int accel_uhz) { @@ -359,6 +499,7 @@ static int inv_icm42600_accel_set_wom_threshold(struct inv_icm42600_state *st, { unsigned int threshold; int ret; + u32 wom_thresh_reg; /* convert roc to wom threshold and convert back to handle clipping */ threshold = inv_icm42600_accel_convert_roc_to_wom(value, accel_hz, accel_uhz); @@ -366,11 +507,20 @@ static int inv_icm42600_accel_set_wom_threshold(struct inv_icm42600_state *st, dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold); + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + wom_thresh_reg = INV_ICM42607_REG_ACCEL_WOM_X_THR; + break; + default: + wom_thresh_reg = INV_ICM42600_REG_ACCEL_WOM_X_THR; + break; + } /* set accel WoM threshold for the 3 axes */ st->buffer[0] = threshold; st->buffer[1] = threshold; st->buffer[2] = threshold; - ret = regmap_bulk_write(st->map, INV_ICM42600_REG_ACCEL_WOM_X_THR, st->buffer, 3); + ret = regmap_bulk_write(st->map, wom_thresh_reg, st->buffer, 3); if (ret) return ret; @@ -410,6 +560,37 @@ static int _inv_icm42600_accel_enable_wom(struct iio_dev *indio_dev) return 0; } +static int _inv_icm42607_accel_enable_wom(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int sleep_ms = 0; + int ret; + + scoped_guard(mutex, &st->lock) { + /* turn on accel sensor */ + conf.mode = accel_st->power_mode; + conf.filter = accel_st->filter; + ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_ms); + if (ret) + return ret; + } + + if (sleep_ms) + msleep(sleep_ms); + + scoped_guard(mutex, &st->lock) { + ret = inv_icm42607_enable_wom(st); + if (ret) + return ret; + st->apex.on++; + st->apex.wom.enable = true; + } + + return 0; +} + static int inv_icm42600_accel_enable_wom(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); @@ -420,7 +601,15 @@ static int inv_icm42600_accel_enable_wom(struct iio_dev *indio_dev) if (ret) return ret; - ret = _inv_icm42600_accel_enable_wom(indio_dev); + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + ret = _inv_icm42607_accel_enable_wom(indio_dev); + break; + default: + ret = _inv_icm42600_accel_enable_wom(indio_dev); + break; + } if (ret) { pm_runtime_mark_last_busy(pdev); pm_runtime_put_autosuspend(pdev); @@ -464,13 +653,49 @@ static int _inv_icm42600_accel_disable_wom(struct iio_dev *indio_dev) return 0; } +static int _inv_icm42607_accel_disable_wom(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int sleep_ms = 0; + int ret; + + scoped_guard(mutex, &st->lock) { + st->apex.wom.enable = false; + st->apex.on--; + ret = inv_icm42607_disable_wom(st); + if (ret) + return ret; + /* turn off accel sensor if not used */ + if (!st->apex.on && !iio_buffer_enabled(indio_dev)) { + conf.mode = INV_ICM42600_SENSOR_MODE_OFF; + ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_ms); + if (ret) + return ret; + } + } + + if (sleep_ms) + msleep(sleep_ms); + + return 0; +} + static int inv_icm42600_accel_disable_wom(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); struct device *pdev = regmap_get_device(st->map); int ret; - ret = _inv_icm42600_accel_disable_wom(indio_dev); + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + ret = _inv_icm42607_accel_disable_wom(indio_dev); + break; + default: + ret = _inv_icm42600_accel_disable_wom(indio_dev); + break; + } pm_runtime_mark_last_busy(pdev); pm_runtime_put_autosuspend(pdev); @@ -560,12 +785,21 @@ static int inv_icm42600_accel_write_scale(struct iio_dev *indio_dev, conf.fs = idx / 2; - pm_runtime_get_sync(dev); - - scoped_guard(mutex, &st->lock) - ret = inv_icm42600_set_accel_conf(st, &conf, NULL); + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; - pm_runtime_put_autosuspend(dev); + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + scoped_guard(mutex, &st->lock) + ret = inv_icm42600_set_accel_conf(st, &conf, NULL); + break; + default: + scoped_guard(mutex, &st->lock) + ret = inv_icm42600_set_accel_conf(st, &conf, NULL); + break; + } return ret; } @@ -596,6 +830,20 @@ static const int inv_icm42600_accel_odr[] = { 4000, 0, }; +static const int inv_icm42607_accel_odr[] = { + 1, 562500, /* 1.5625Hz */ + 3, 125000, /* 3.125Hz */ + 6, 250000, /* 6.25Hz */ + 12, 500000, /* 12.5Hz */ + 25, 0, /* 25Hz */ + 50, 0, /* 50Hz */ + 100, 0, /* 100Hz */ + 200, 0, /* 200Hz */ + 400, 0, /* 400Hz */ + 800, 0, /* 800Hz */ + 1600, 0, /* 1600Hz */ +}; + static const int inv_icm42600_accel_odr_conv[] = { INV_ICM42600_ODR_1_5625HZ_LP, INV_ICM42600_ODR_3_125HZ_LP, @@ -610,6 +858,20 @@ static const int inv_icm42600_accel_odr_conv[] = { INV_ICM42600_ODR_4KHZ_LN, }; +static const int inv_icm42607_accel_odr_conv[] = { + INV_ICM42607_ODR_1_5625HZ_LP, + INV_ICM42607_ODR_3_125HZ_LP, + INV_ICM42607_ODR_6_25HZ_LP, + INV_ICM42607_ODR_12_5HZ, + INV_ICM42607_ODR_25HZ, + INV_ICM42607_ODR_50HZ, + INV_ICM42607_ODR_100HZ, + INV_ICM42607_ODR_200HZ, + INV_ICM42607_ODR_400HZ, + INV_ICM42607_ODR_800HZ, + INV_ICM42607_ODR_1600HZ, +}; + static int inv_icm42600_accel_read_odr(struct inv_icm42600_state *st, int *val, int *val2) { @@ -631,6 +893,27 @@ static int inv_icm42600_accel_read_odr(struct inv_icm42600_state *st, return IIO_VAL_INT_PLUS_MICRO; } +static int inv_icm42607_accel_read_odr(struct inv_icm42600_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + odr = st->conf.accel.odr; + + for (i = 0; i < ARRAY_SIZE(inv_icm42607_accel_odr_conv); ++i) { + if (inv_icm42607_accel_odr_conv[i] == odr) + break; + } + if (i >= ARRAY_SIZE(inv_icm42607_accel_odr_conv)) + return -EINVAL; + + *val = inv_icm42607_accel_odr[2 * i]; + *val2 = inv_icm42607_accel_odr[2 * i + 1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev, int val, int val2) { @@ -679,6 +962,50 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev, return ret; } +static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &accel_st->ts; + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + int ret; + + for (idx = 0; idx < ARRAY_SIZE(inv_icm42607_accel_odr); idx += 2) { + if (val == inv_icm42607_accel_odr[idx] && + val2 == inv_icm42607_accel_odr[idx + 1]) + break; + } + if (idx >= ARRAY_SIZE(inv_icm42607_accel_odr)) + return -EINVAL; + + conf.odr = inv_icm42607_accel_odr_conv[idx / 2]; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; + + guard(mutex)(&st->lock); + + ret = inv_sensors_timestamp_update_odr(ts, inv_icm42607_odr_to_period(conf.odr), + iio_buffer_enabled(indio_dev)); + if (ret) + return ret; + + ret = inv_icm42607_set_accel_conf(st, &conf, NULL); + if (ret) + return ret; + ret = inv_icm42600_accel_set_wom_threshold(st, st->apex.wom.value, val, val2); + if (ret) + return ret; + inv_icm42607_buffer_update_fifo_period(st); + inv_icm42600_buffer_update_watermark(st); + + return 0; +} + /* * Calibration bias values, IIO range format int + micro. * Value is limited to +/-1g coded on 12 bits signed. Step is 0.5mg. @@ -904,6 +1231,42 @@ static int inv_icm42600_accel_read_raw(struct iio_dev *indio_dev, } } +static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + s16 data; + int ret; + + switch (chan->type) { + case IIO_ACCEL: + break; + case IIO_TEMP: + return inv_icm42607_temp_read_raw(indio_dev, chan, val, val2, mask); + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = inv_icm42607_accel_read_sensor(indio_dev, chan, &data); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm42600_accel_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_accel_read_odr(st, val, val2); + default: + return -EINVAL; + } +} + static int inv_icm42600_accel_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, @@ -964,6 +1327,29 @@ static int inv_icm42600_accel_write_raw(struct iio_dev *indio_dev, } } +static int inv_icm42607_accel_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (chan->type != IIO_ACCEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = inv_icm42600_accel_write_scale(indio_dev, val, val2); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_accel_write_odr(indio_dev, val, val2); + default: + return -EINVAL; + } +} + static int inv_icm42600_accel_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) @@ -1082,7 +1468,15 @@ static int _inv_icm42600_accel_wom_value(struct inv_icm42600_state *st, guard(mutex)(&st->lock); - ret = inv_icm42600_accel_read_odr(st, &accel_hz, &accel_uhz); + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + ret = inv_icm42607_accel_read_odr(st, &accel_hz, &accel_uhz); + break; + default: + ret = inv_icm42600_accel_read_odr(st, &accel_hz, &accel_uhz); + break; + } if (ret < 0) return ret; @@ -1137,6 +1531,20 @@ static const struct iio_info inv_icm42600_accel_info = { .write_event_value = inv_icm42600_accel_write_event_value, }; +static const struct iio_info inv_icm42607_accel_info = { + .read_raw = inv_icm42607_accel_read_raw, + .write_raw = inv_icm42607_accel_write_raw, + .write_raw_get_fmt = inv_icm42600_accel_write_raw_get_fmt, + .debugfs_reg_access = inv_icm42600_debugfs_reg, + .update_scan_mode = inv_icm42607_accel_update_scan_mode, + .hwfifo_set_watermark = inv_icm42600_accel_hwfifo_set_watermark, + .hwfifo_flush_to_buffer = inv_icm42600_accel_hwfifo_flush, + .read_event_config = inv_icm42600_accel_read_event_config, + .write_event_config = inv_icm42600_accel_write_event_config, + .read_event_value = inv_icm42600_accel_read_event_value, + .write_event_value = inv_icm42600_accel_write_event_value, +}; + int inv_icm42600_accel_init(struct inv_icm42600_state *st, struct iio_dev *indio_dev) { struct device *dev = regmap_get_device(st->map); @@ -1202,6 +1610,62 @@ int inv_icm42600_accel_init(struct inv_icm42600_state *st, struct iio_dev *indio return 0; } +int inv_icm42607_accel_init(struct inv_icm42600_state *st, struct iio_dev *indio_dev) +{ + struct device *dev = regmap_get_device(st->map); + const char *name; + struct inv_icm42600_sensor_state *accel_st; + struct inv_sensors_timestamp_chip ts_chip; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name); + if (!name) + return -ENOMEM; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st)); + if (!indio_dev) + return -ENOMEM; + accel_st = iio_priv(indio_dev); + + accel_st->scales = inv_icm42600_accel_scale; + accel_st->scales_len = ARRAY_SIZE(inv_icm42600_accel_scale); + accel_st->power_mode = INV_ICM42600_SENSOR_MODE_LOW_POWER; + accel_st->filter = INV_ICM42607_FILTER_AVG_16X; + + /* + * clock period is 32kHz (31250ns) + * jitter is +/- 2% (20 per mille) + */ + ts_chip.clock_period = 31250; + ts_chip.jitter = 20; + ts_chip.init_period = inv_icm42607_odr_to_period(st->conf.accel.odr); + inv_sensors_timestamp_init(&accel_st->ts, &ts_chip); + + iio_device_set_drvdata(indio_dev, st); + indio_dev->name = name; + indio_dev->info = &inv_icm42607_accel_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = inv_icm42607_accel_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels); + indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks; + + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + &inv_icm42607_buffer_ops); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + /* accel events are wakeup capable */ + ret = devm_device_init_wakeup(&indio_dev->dev); + if (ret) + return ret; + + return 0; +} + int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 68a395758031..d9b10bf4c9df 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -116,6 +116,23 @@ void inv_icm42600_buffer_update_fifo_period(struct inv_icm42600_state *st) st->fifo.period = min(period_gyro, period_accel); } +void inv_icm42607_buffer_update_fifo_period(struct inv_icm42600_state *st) +{ + u32 period_gyro, period_accel; + + if (st->fifo.en & INV_ICM42600_SENSOR_GYRO) + period_gyro = inv_icm42607_odr_to_period(st->conf.gyro.odr); + else + period_gyro = U32_MAX; + + if (st->fifo.en & INV_ICM42600_SENSOR_ACCEL) + period_accel = inv_icm42607_odr_to_period(st->conf.accel.odr); + else + period_accel = U32_MAX; + + st->fifo.period = min(period_gyro, period_accel); +} + int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st, unsigned int fifo_en) { @@ -146,6 +163,31 @@ int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st, return 0; } +int inv_icm42607_buffer_set_fifo_en(struct inv_icm42600_state *st, + unsigned int fifo_en) +{ + unsigned int val; + int ret; + + /* update FIFO EN bits for accel and gyro */ + val = 0; + if (fifo_en & INV_ICM42600_SENSOR_GYRO) + val |= INV_ICM42607_FIFO_CONFIG1_MODE; + if (fifo_en & INV_ICM42600_SENSOR_ACCEL) + val |= INV_ICM42607_FIFO_CONFIG1_MODE; + if (fifo_en & INV_ICM42600_SENSOR_TEMP) + val |= INV_ICM42607_FIFO_CONFIG1_MODE; + + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, val); + if (ret) + return ret; + + st->fifo.en = fifo_en; + inv_icm42607_buffer_update_fifo_period(st); + + return 0; +} + static size_t inv_icm42600_get_packet_size(unsigned int fifo_en) { size_t packet_size; @@ -202,6 +244,7 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) unsigned int wm_gyro, wm_accel, watermark; u32 period_gyro, period_accel; u32 latency_gyro, latency_accel, latency; + u32 fifo_reg, int_reg; bool restore; __le16 raw_wm; int ret; @@ -249,8 +292,19 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) /* compute watermark value in bytes */ wm_size = watermark * packet_size; + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + fifo_reg = INV_ICM42607_REG_FIFO_CONFIG2; + int_reg = INV_ICM42607_REG_INT_SOURCE0; + break; + default: + fifo_reg = INV_ICM42600_REG_FIFO_WATERMARK; + int_reg = INV_ICM42600_REG_INT_SOURCE0; + break; + } /* changing FIFO watermark requires to turn off watermark interrupt */ - ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0, + ret = regmap_update_bits_check(st->map, int_reg, INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN, 0, &restore); if (ret) @@ -258,14 +312,14 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) raw_wm = INV_ICM42600_FIFO_WATERMARK_VAL(wm_size); memcpy(st->buffer, &raw_wm, sizeof(raw_wm)); - ret = regmap_bulk_write(st->map, INV_ICM42600_REG_FIFO_WATERMARK, + ret = regmap_bulk_write(st->map, fifo_reg, st->buffer, sizeof(raw_wm)); if (ret) return ret; /* restore watermark interrupt */ if (restore) { - ret = regmap_set_bits(st->map, INV_ICM42600_REG_INT_SOURCE0, + ret = regmap_set_bits(st->map, int_reg, INV_ICM42600_INT_SOURCE0_FIFO_THS_INT1_EN); if (ret) return ret; @@ -333,6 +387,47 @@ static int inv_icm42600_buffer_postenable(struct iio_dev *indio_dev) return 0; } +static int inv_icm42607_buffer_postenable(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + /* exit if FIFO is already on */ + if (st->fifo.on) { + st->fifo.on++; + return 0; + } + + /* set FIFO threshold interrupt */ + ret = regmap_set_bits(st->map, INV_ICM42607_REG_INT_SOURCE0, + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN); + if (ret) + return ret; + + /* flush FIFO data */ + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET, + INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH); + if (ret) + return ret; + + /* set FIFO in streaming mode */ + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, + INV_ICM42607_FIFO_CONFIG1_MODE); + if (ret) + return ret; + + /* workaround: first read of FIFO count after reset is always 0 */ + ret = regmap_bulk_read(st->map, INV_ICM42607_REG_FIFO_COUNTH, st->buffer, 2); + if (ret) + return ret; + + st->fifo.on++; + + return 0; +} + static int inv_icm42600_buffer_predisable(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); @@ -368,6 +463,41 @@ static int inv_icm42600_buffer_predisable(struct iio_dev *indio_dev) return 0; } +static int inv_icm42607_buffer_predisable(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + int ret; + + guard(mutex)(&st->lock); + + if (st->fifo.on > 1) { + st->fifo.on--; + return 0; + } + + /* set FIFO in bypass mode */ + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, + INV_ICM42607_FIFO_CONFIG1_BYPASS); + if (ret) + return ret; + + /* flush FIFO data */ + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET, + INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH); + if (ret) + return ret; + + /* disable FIFO threshold interrupt */ + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INT_SOURCE0, + INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN); + if (ret) + return ret; + + st->fifo.on--; + + return 0; +} + static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); @@ -430,6 +560,66 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev) return ret; } +static int inv_icm42607_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *sensor_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &sensor_st->ts; + struct device *dev = regmap_get_device(st->map); + unsigned int sensor; + unsigned int *watermark; + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int sleep_temp = 0; + unsigned int sleep_sensor = 0; + unsigned int sleep; + int ret; + + if (indio_dev == st->indio_gyro) { + sensor = INV_ICM42600_SENSOR_GYRO; + watermark = &st->fifo.watermark.gyro; + } else if (indio_dev == st->indio_accel) { + sensor = INV_ICM42600_SENSOR_ACCEL; + watermark = &st->fifo.watermark.accel; + } else { + return -EINVAL; + } + + guard(mutex)(&st->lock); + + inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); + + ret = inv_icm42607_buffer_set_fifo_en(st, st->fifo.en & ~sensor); + if (ret) + goto out_unlock; + + *watermark = 0; + ret = inv_icm42600_buffer_update_watermark(st); + if (ret) + goto out_unlock; + + conf.mode = INV_ICM42600_SENSOR_MODE_OFF; + if (sensor == INV_ICM42600_SENSOR_GYRO) + ret = inv_icm42607_set_gyro_conf(st, &conf, &sleep_sensor); + else if (!st->apex.on) + ret = inv_icm42607_set_accel_conf(st, &conf, &sleep_sensor); + if (ret) + goto out_unlock; + + /* if FIFO is off, turn temperature off */ + if (!st->fifo.on) + ret = inv_icm42607_set_temp_conf(st, false, &sleep_temp); + +out_unlock: + /* sleep maximum required time */ + sleep = max(sleep_sensor, sleep_temp); + if (sleep) + msleep(sleep); + + pm_runtime_put_autosuspend(dev); + + return ret; +} + const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = { .preenable = inv_icm42600_buffer_preenable, .postenable = inv_icm42600_buffer_postenable, @@ -437,6 +627,13 @@ const struct iio_buffer_setup_ops inv_icm42600_buffer_ops = { .postdisable = inv_icm42600_buffer_postdisable, }; +const struct iio_buffer_setup_ops inv_icm42607_buffer_ops = { + .preenable = inv_icm42600_buffer_preenable, + .postenable = inv_icm42607_buffer_postenable, + .predisable = inv_icm42607_buffer_predisable, + .postdisable = inv_icm42607_buffer_postdisable, +}; + int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, unsigned int max) { @@ -447,6 +644,7 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, const s8 *temp; unsigned int odr; int ret; + u32 fifo_cnt_reg, fifo_data_reg; /* reset all samples counters */ st->fifo.count = 0; @@ -460,9 +658,21 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, else max_count = max * inv_icm42600_get_packet_size(st->fifo.en); + + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + fifo_cnt_reg = INV_ICM42607_REG_FIFO_COUNTH; + fifo_data_reg = INV_ICM42607_REG_FIFO_DATA; + break; + default: + fifo_cnt_reg = INV_ICM42600_REG_FIFO_COUNT; + fifo_data_reg = INV_ICM42600_REG_FIFO_DATA; + break; + } /* read FIFO count value */ raw_fifo_count = (__be16 *)st->buffer; - ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT, + ret = regmap_bulk_read(st->map, fifo_cnt_reg, raw_fifo_count, sizeof(*raw_fifo_count)); if (ret) return ret; @@ -475,7 +685,7 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, st->fifo.count = max_count; /* read all FIFO data in internal buffer */ - ret = regmap_noinc_read(st->map, INV_ICM42600_REG_FIFO_DATA, + ret = regmap_noinc_read(st->map, fifo_data_reg, st->fifo.data, st->fifo.count); if (ret) return ret; @@ -596,3 +806,23 @@ int inv_icm42600_buffer_init(struct inv_icm42600_state *st) return regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1, GENMASK(6, 5) | GENMASK(3, 0), val); } + +int inv_icm42607_buffer_init(struct inv_icm42600_state *st) +{ + unsigned int val; + int ret; + + st->fifo.watermark.eff_gyro = 1; + st->fifo.watermark.eff_accel = 1; + + /* Configure FIFO_COUNT format in bytes and big endian */ + val = INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN; + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + val, val); + if (ret) + return ret; + + /* Initialize FIFO in bypass mode */ + return regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, + INV_ICM42607_FIFO_CONFIG1_BYPASS); +} diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index ffca4da1e249..900ee12fdb9d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -79,13 +79,18 @@ ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel, const void **timestamp, unsigned int *odr); extern const struct iio_buffer_setup_ops inv_icm42600_buffer_ops; +extern const struct iio_buffer_setup_ops inv_icm42607_buffer_ops; int inv_icm42600_buffer_init(struct inv_icm42600_state *st); +int inv_icm42607_buffer_init(struct inv_icm42600_state *st); void inv_icm42600_buffer_update_fifo_period(struct inv_icm42600_state *st); +void inv_icm42607_buffer_update_fifo_period(struct inv_icm42600_state *st); int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st, unsigned int fifo_en); +int inv_icm42607_buffer_set_fifo_en(struct inv_icm42600_state *st, + unsigned int fifo_en); int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 29c8c1871e06..949dbf9c2cd3 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -102,6 +102,26 @@ const struct regmap_config inv_icm42600_spi_regmap_config = { }; EXPORT_SYMBOL_NS_GPL(inv_icm42600_spi_regmap_config, "IIO_ICM42600"); +static const struct regmap_range_cfg inv_icm42607_regmap_ranges[] = { + { + .name = "user bank", + .range_min = 0x0000, + .range_max = 0x00FF, + .window_start = 0, + .window_len = 0x0100, + }, +}; + +const struct regmap_config inv_icm42607_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x00FF, + .ranges = inv_icm42607_regmap_ranges, + .num_ranges = ARRAY_SIZE(inv_icm42607_regmap_ranges), + .cache_type = REGCACHE_NONE, +}; +EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42600"); + struct inv_icm42600_hw { u8 whoami; const char *name; @@ -125,6 +145,22 @@ static const struct inv_icm42600_conf inv_icm42600_default_conf = { .temp_en = false, }; +static const struct inv_icm42600_conf inv_icm42607_default_conf = { + .gyro = { + .mode = INV_ICM42600_SENSOR_MODE_OFF, + .fs = INV_ICM42600_GYRO_FS_1000DPS, + .odr = INV_ICM42607_ODR_100HZ, + .filter = INV_ICM42607_FILTER_BW_25HZ, + }, + .accel = { + .mode = INV_ICM42600_SENSOR_MODE_OFF, + .fs = INV_ICM42600_ACCEL_FS_4G, + .odr = INV_ICM42607_ODR_100HZ, + .filter = INV_ICM42607_FILTER_BW_25HZ, + }, + .temp_en = false, +}; + static const struct inv_icm42600_conf inv_icm42686_default_conf = { .gyro = { .mode = INV_ICM42600_SENSOR_MODE_OFF, @@ -157,6 +193,16 @@ static const struct inv_icm42600_hw inv_icm42600_hw[INV_CHIP_NB] = { .name = "icm42605", .conf = &inv_icm42600_default_conf, }, + [INV_CHIP_ICM42607] = { + .whoami = INV_ICM42607_WHOAMI, + .name = "icm42607", + .conf = &inv_icm42607_default_conf, + }, + [INV_CHIP_ICM42607P] = { + .whoami = INV_ICM42607P_WHOAMI, + .name = "icm42607p", + .conf = &inv_icm42607_default_conf, + }, [INV_CHIP_ICM42686] = { .whoami = INV_ICM42600_WHOAMI_ICM42686, .name = "icm42686", @@ -224,6 +270,26 @@ u32 inv_icm42600_odr_to_period(enum inv_icm42600_odr odr) return odr_periods[odr]; } +u32 inv_icm42607_odr_to_period(enum inv_icm42607_odr odr) +{ + static u32 odr_periods[INV_ICM42607_ODR_NB] = { + 0, 0, 0, 0, 0, /* Reserved values */ + 625000, /* 1600Hz */ + 1250000, /* 800Hz */ + 2500000, /* 400Hz */ + 5000000, /* 200Hz */ + 10000000, /* 100 Hz */ + 20000000, /* 50Hz */ + 40000000, /* 25Hz */ + 80000000, /* 12.5Hz */ + 160000000, /* 6.25Hz */ + 320000000, /* 3.125Hz */ + 640000000, /* 1.5625Hz */ + }; + + return odr_periods[odr]; +} + static int inv_icm42600_set_pwr_mgmt0(struct inv_icm42600_state *st, enum inv_icm42600_sensor_mode gyro, enum inv_icm42600_sensor_mode accel, @@ -289,6 +355,62 @@ static int inv_icm42600_set_pwr_mgmt0(struct inv_icm42600_state *st, return 0; } +static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42600_state *st, + enum inv_icm42600_sensor_mode gyro, + enum inv_icm42600_sensor_mode accel, + bool temp, unsigned int *sleep_ms) +{ + enum inv_icm42600_sensor_mode oldgyro = st->conf.gyro.mode; + enum inv_icm42600_sensor_mode oldaccel = st->conf.accel.mode; + bool oldtemp = st->conf.temp_en; + unsigned int sleepval; + unsigned int val; + int ret; + + if (gyro == oldgyro && accel == oldaccel && temp == oldtemp) + return 0; + + val = INV_ICM42607_PWR_MGMT0_GYRO(gyro) | + INV_ICM42607_PWR_MGMT0_ACCEL(accel); + if (!temp) + val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL; + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val); + if (ret) + return ret; + + st->conf.gyro.mode = gyro; + st->conf.accel.mode = accel; + st->conf.temp_en = temp; + + sleepval = 0; + if (temp && !oldtemp) { + if (sleepval < INV_ICM42600_TEMP_STARTUP_TIME_MS) + sleepval = INV_ICM42600_TEMP_STARTUP_TIME_MS; + } + if (accel != oldaccel && oldaccel == INV_ICM42600_SENSOR_MODE_OFF) { + usleep_range(200, 300); + if (sleepval < INV_ICM42600_ACCEL_STARTUP_TIME_MS) + sleepval = INV_ICM42600_ACCEL_STARTUP_TIME_MS; + } + if (gyro != oldgyro) { + if (oldgyro == INV_ICM42600_SENSOR_MODE_OFF) { + usleep_range(200, 300); + if (sleepval < INV_ICM42600_GYRO_STARTUP_TIME_MS) + sleepval = INV_ICM42600_GYRO_STARTUP_TIME_MS; + } else if (gyro == INV_ICM42600_SENSOR_MODE_OFF) { + if (sleepval < INV_ICM42600_GYRO_STOP_TIME_MS) + sleepval = INV_ICM42600_GYRO_STOP_TIME_MS; + } + } + + if (sleep_ms) + *sleep_ms = sleepval; + else if (sleepval) + msleep(sleepval); + + return 0; +} + int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st, struct inv_icm42600_sensor_conf *conf, unsigned int *sleep_ms) @@ -350,6 +472,52 @@ int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st, st->conf.temp_en, sleep_ms); } +int inv_icm42607_set_accel_conf(struct inv_icm42600_state *st, + struct inv_icm42600_sensor_conf *conf, + unsigned int *sleep_ms) +{ + struct inv_icm42600_sensor_conf *oldconf = &st->conf.accel; + unsigned int val; + int ret; + + if (conf->mode < 0) + conf->mode = oldconf->mode; + if (conf->fs < 0) + conf->fs = oldconf->fs; + if (conf->odr < 0) + conf->odr = oldconf->odr; + if (conf->filter < 0) + conf->filter = oldconf->filter; + + if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) { + val = INV_ICM42607_ACCEL_CONFIG0_FS_SEL(conf->fs) | + INV_ICM42607_ACCEL_CONFIG0_ODR(conf->odr); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val); + if (ret) + return ret; + oldconf->fs = conf->fs; + oldconf->odr = conf->odr; + } + + if (conf->filter != oldconf->filter) { + if (conf->mode == INV_ICM42600_SENSOR_MODE_LOW_POWER) { + val = INV_ICM42607_ACCEL_CONFIG1_AVG(conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, + INV_ICM42607_ACCEL_CONFIG1_AVG_MASK, val); + } else { + val = INV_ICM42607_ACCEL_CONFIG1_FILTER(conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, + INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val); + } + if (ret) + return ret; + oldconf->filter = conf->filter; + } + + return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode, + st->conf.temp_en, sleep_ms); +} + int inv_icm42600_set_gyro_conf(struct inv_icm42600_state *st, struct inv_icm42600_sensor_conf *conf, unsigned int *sleep_ms) @@ -396,6 +564,46 @@ int inv_icm42600_set_gyro_conf(struct inv_icm42600_state *st, return 0; } +int inv_icm42607_set_gyro_conf(struct inv_icm42600_state *st, + struct inv_icm42600_sensor_conf *conf, + unsigned int *sleep_ms) +{ + struct inv_icm42600_sensor_conf *oldconf = &st->conf.gyro; + unsigned int val; + int ret; + + if (conf->mode < 0) + conf->mode = oldconf->mode; + if (conf->fs < 0) + conf->fs = oldconf->fs; + if (conf->odr < 0) + conf->odr = oldconf->odr; + if (conf->filter < 0) + conf->filter = oldconf->filter; + + if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) { + val = INV_ICM42607_GYRO_CONFIG0_FS_SEL(conf->fs) | + INV_ICM42607_GYRO_CONFIG0_ODR(conf->odr); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val); + if (ret) + return ret; + oldconf->fs = conf->fs; + oldconf->odr = conf->odr; + } + + if (conf->filter != oldconf->filter) { + val = INV_ICM42607_GYRO_CONFIG1_FILTER(conf->filter); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1, + INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val); + if (ret) + return ret; + oldconf->filter = conf->filter; + } + + return inv_icm42607_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode, + st->conf.temp_en, sleep_ms); +} + int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable, unsigned int *sleep_ms) { @@ -404,6 +612,23 @@ int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable, sleep_ms); } +int inv_icm42607_set_temp_conf(struct inv_icm42600_state *st, bool enable, + unsigned int *sleep_ms) +{ + unsigned int val; + int ret; + + val = INV_ICM42607_TEMP_CONFIG0_FILTER(INV_ICM42607_FILTER_BW_34HZ); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_TEMP_CONFIG0, + INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, val); + if (ret) + return ret; + + return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, + st->conf.accel.mode, enable, + sleep_ms); +} + int inv_icm42600_enable_wom(struct inv_icm42600_state *st) { int ret; @@ -420,6 +645,23 @@ int inv_icm42600_enable_wom(struct inv_icm42600_state *st) INV_ICM42600_INT_SOURCE1_WOM_INT1_EN); } +int inv_icm42607_enable_wom(struct inv_icm42600_state *st) +{ + int ret; + + /* enable WoM hardware */ + ret = regmap_write(st->map, INV_ICM42607_REG_WOM_CONFIG, + INV_ICM42607_WOM_CONFIG_INT_DUR(1) | + INV_ICM42607_WOM_CONFIG_MODE | + INV_ICM42607_WOM_CONFIG_EN); + if (ret) + return ret; + + /* enable WoM interrupt */ + return regmap_set_bits(st->map, INV_ICM42607_REG_INT_SOURCE1, + INV_ICM42607_INT_SOURCE1_WOM_INT1_EN); +} + int inv_icm42600_disable_wom(struct inv_icm42600_state *st) { int ret; @@ -435,6 +677,21 @@ int inv_icm42600_disable_wom(struct inv_icm42600_state *st) INV_ICM42600_SMD_CONFIG_SMD_MODE_OFF); } +int inv_icm42607_disable_wom(struct inv_icm42600_state *st) +{ + int ret; + + /* disable WoM interrupt */ + ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INT_SOURCE1, + INV_ICM42607_INT_SOURCE1_WOM_INT1_EN); + if (ret) + return ret; + + /* disable WoM hardware */ + return regmap_clear_bits(st->map, INV_ICM42607_REG_WOM_CONFIG, + INV_ICM42607_WOM_CONFIG_EN); +} + int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) { @@ -490,6 +747,53 @@ static int inv_icm42600_set_conf(struct inv_icm42600_state *st, return 0; } +static int inv_icm42607_set_conf(struct inv_icm42600_state *st, + const struct inv_icm42600_conf *conf) +{ + unsigned int val; + int ret; + + val = INV_ICM42607_PWR_MGMT0_GYRO(conf->gyro.mode) | + INV_ICM42607_PWR_MGMT0_ACCEL(conf->accel.mode); + /* + * No temperature enable reg in datasheet, but BSP driver + * selected RC oscillator clock in LP mode when temperature + * was disabled. + */ + if (!conf->temp_en) + val |= INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL; + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val); + if (ret) + return ret; + + val = INV_ICM42607_GYRO_CONFIG0_FS_SEL(conf->gyro.fs) | + INV_ICM42607_GYRO_CONFIG0_ODR(conf->gyro.odr); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val); + if (ret) + return ret; + + val = INV_ICM42607_ACCEL_CONFIG0_FS_SEL(conf->accel.fs) | + INV_ICM42607_ACCEL_CONFIG0_ODR(conf->accel.odr); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val); + if (ret) + return ret; + + val = INV_ICM42607_GYRO_CONFIG1_FILTER(conf->gyro.filter); + ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG1, val); + if (ret) + return ret; + + val = INV_ICM42607_ACCEL_CONFIG1_FILTER(conf->accel.filter); + ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG1, val); + if (ret) + return ret; + + /* update internal conf */ + st->conf = *conf; + + return 0; +} + /** * inv_icm42600_setup() - check and setup chip * @st: driver internal state @@ -555,6 +859,64 @@ static int inv_icm42600_setup(struct inv_icm42600_state *st, return inv_icm42600_set_conf(st, hw->conf); } +/** + * inv_icm42607_setup() - check and setup chip + * @st: driver internal state + * @bus_setup: callback for setting up bus specific registers + * + * Returns 0 on success, a negative error code otherwise. + */ +static int inv_icm42607_setup(struct inv_icm42600_state *st, + inv_icm42600_bus_setup bus_setup) +{ + const struct inv_icm42600_hw *hw = &inv_icm42600_hw[st->chip]; + const struct device *dev = regmap_get_device(st->map); + unsigned int val; + int ret; + + ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val); + if (ret) + return ret; + + if (val != hw->whoami) + return dev_err_probe(dev, -ENODEV, + "invalid whoami %#02x expected %#02x (%s)\n", + val, hw->whoami, hw->name); + + st->name = hw->name; + + ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET, + INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET); + if (ret) + return ret; + msleep(INV_ICM42600_RESET_TIME_MS); + + ret = regmap_read(st->map, INV_ICM42607_REG_INT_STATUS, &val); + if (ret) + return ret; + if (!(val & INV_ICM42607_INT_STATUS_RESET_DONE)) + return dev_err_probe(dev, -ENODEV, + "reset error, reset done bit not set\n"); + + ret = bus_setup(st); + if (ret) + return ret; + + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN, + INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN); + if (ret) + return ret; + + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK, + INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL); + if (ret) + return ret; + + return inv_icm42607_set_conf(st, hw->conf); +} + static irqreturn_t inv_icm42600_irq_timestamp(int irq, void *_data) { struct inv_icm42600_state *st = _data; @@ -571,6 +933,19 @@ static irqreturn_t inv_icm42600_irq_handler(int irq, void *_data) struct device *dev = regmap_get_device(st->map); unsigned int status; int ret; + u32 int_sts_reg, int_sts2_reg; + + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + int_sts_reg = INV_ICM42607_REG_INT_STATUS; + int_sts2_reg = INV_ICM42607_REG_INT_STATUS2; + break; + default: + int_sts_reg = INV_ICM42600_REG_INT_STATUS; + int_sts2_reg = INV_ICM42600_REG_INT_STATUS2; + break; + } mutex_lock(&st->lock); @@ -578,7 +953,7 @@ static irqreturn_t inv_icm42600_irq_handler(int irq, void *_data) unsigned int status2, status3; /* read INT_STATUS2 and INT_STATUS3 in 1 operation */ - ret = regmap_bulk_read(st->map, INV_ICM42600_REG_INT_STATUS2, st->buffer, 2); + ret = regmap_bulk_read(st->map, int_sts2_reg, st->buffer, 2); if (ret) goto out_unlock; status2 = st->buffer[0]; @@ -587,7 +962,7 @@ static irqreturn_t inv_icm42600_irq_handler(int irq, void *_data) st->timestamp.accel); } - ret = regmap_read(st->map, INV_ICM42600_REG_INT_STATUS, &status); + ret = regmap_read(st->map, int_sts_reg, &status); if (ret) goto out_unlock; @@ -651,13 +1026,19 @@ static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq, if (!open_drain) val |= INV_ICM42600_INT_CONFIG_INT1_PUSH_PULL; - ret = regmap_write(st->map, INV_ICM42600_REG_INT_CONFIG, val); - if (ret) - return ret; - - /* Deassert async reset for proper INT pin operation (cf datasheet) */ - ret = regmap_clear_bits(st->map, INV_ICM42600_REG_INT_CONFIG1, - INV_ICM42600_INT_CONFIG1_ASYNC_RESET); + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + ret = regmap_write(st->map, INV_ICM42607_REG_INT_CONFIG, val); + break; + default: + ret = regmap_write(st->map, INV_ICM42600_REG_INT_CONFIG, val); + if (ret) + return ret; + /* Deassert async reset for proper INT pin operation (cf datasheet) */ + ret = regmap_clear_bits(st->map, INV_ICM42600_REG_INT_CONFIG1, + INV_ICM42600_INT_CONFIG1_ASYNC_RESET); + } if (ret) return ret; @@ -761,6 +1142,59 @@ static int inv_icm42600_hw_suspend(struct device *dev) return 0; } +static int inv_icm42607_hw_suspend(struct device *dev) +{ + struct inv_icm42600_state *st = dev_get_drvdata(dev); + struct device *accel_dev; + bool wakeup; + int accel_conf; + int ret = 0; + + guard(mutex)(&st->lock); + + st->suspended.gyro = st->conf.gyro.mode; + st->suspended.accel = st->conf.accel.mode; + st->suspended.temp = st->conf.temp_en; + if (pm_runtime_suspended(dev)) + return 0; + + if (st->fifo.on) { + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, + INV_ICM42607_FIFO_CONFIG1_BYPASS); + if (ret) + return ret; + } + + /* keep chip on and wake-up capable if APEX and wakeup on */ + accel_dev = &st->indio_accel->dev; + wakeup = st->apex.on && device_may_wakeup(accel_dev); + if (wakeup) { + /* keep accel on and setup irq for wakeup */ + accel_conf = st->conf.accel.mode; + enable_irq_wake(st->irq); + disable_irq(st->irq); + } else { + /* disable APEX features and accel if wakeup disabled */ + if (st->apex.wom.enable) { + ret = inv_icm42607_disable_wom(st); + if (ret) + return ret; + } + accel_conf = INV_ICM42600_SENSOR_MODE_OFF; + } + + ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF, + INV_ICM42600_SENSOR_MODE_OFF, false, + NULL); + if (ret) + return ret; + + if (!wakeup) + regulator_disable(st->vddio_supply); + + return 0; +} + /* * System resume gets the system back on and restores the sensors state. * Manually put runtime power management in system active state. @@ -817,6 +1251,59 @@ static int inv_icm42600_hw_resume(struct device *dev) return 0; } +static int inv_icm42607_hw_resume(struct device *dev) +{ + struct inv_icm42600_state *st = dev_get_drvdata(dev); + struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro); + struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel); + struct device *accel_dev; + bool wakeup; + int ret; + + guard(mutex)(&st->lock); + + if (pm_runtime_suspended(dev)) + return 0; + + /* check wakeup capability */ + accel_dev = &st->indio_accel->dev; + wakeup = st->apex.on && device_may_wakeup(accel_dev); + /* restore irq state or vddio if cut off */ + if (wakeup) { + enable_irq(st->irq); + disable_irq_wake(st->irq); + } else { + ret = inv_icm42600_enable_regulator_vddio(st); + if (ret) + return ret; + } + + /* restore sensors state */ + ret = inv_icm42607_set_pwr_mgmt0(st, st->suspended.gyro, + st->suspended.accel, + st->suspended.temp, NULL); + if (ret) + return ret; + + /* restore APEX features if disabled */ + if (!wakeup && st->apex.wom.enable) { + ret = inv_icm42607_enable_wom(st); + if (ret) + return ret; + } + + if (st->fifo.on) { + inv_sensors_timestamp_reset(&gyro_st->ts); + inv_sensors_timestamp_reset(&accel_st->ts); + ret = regmap_write(st->map, INV_ICM42607_REG_FIFO_CONFIG1, + INV_ICM42607_FIFO_CONFIG1_MODE); + if (ret) + return ret; + } + + return 0; +} + /* Runtime suspend will turn off sensors that are enabled by iio devices. */ static int inv_icm42600_hw_runtime_suspend(struct device *dev) { @@ -837,6 +1324,24 @@ static int inv_icm42600_hw_runtime_suspend(struct device *dev) return 0; } +static int inv_icm42607_hw_runtime_suspend(struct device *dev) +{ + struct inv_icm42600_state *st = dev_get_drvdata(dev); + int ret; + + guard(mutex)(&st->lock); + + ret = inv_icm42607_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF, + INV_ICM42600_SENSOR_MODE_OFF, false, + NULL); + if (ret) + return ret; + + regulator_disable(st->vddio_supply); + + return 0; +} + /* Sensors are enabled by iio devices, no need to turn them back on here. */ static int inv_icm42600_hw_runtime_resume(struct device *dev) { @@ -859,6 +1364,17 @@ static struct inv_icm42600_funcs inv_icm42600_hw_funcs = { .runtime_resume = &inv_icm42600_hw_runtime_resume, }; +static struct inv_icm42600_funcs inv_icm42607_hw_funcs = { + .setup = &inv_icm42607_setup, + .buffer_init = &inv_icm42607_buffer_init, + .gyro_init = &inv_icm42607_gyro_init, + .accel_init = &inv_icm42607_accel_init, + .suspend = &inv_icm42607_hw_suspend, + .resume = &inv_icm42607_hw_resume, + .runtime_suspend = &inv_icm42607_hw_runtime_suspend, + .runtime_resume = &inv_icm42600_hw_runtime_resume, +}; + int inv_icm42600_core_probe(struct regmap *regmap, int chip, inv_icm42600_bus_setup bus_setup) { @@ -925,7 +1441,15 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, return ret; /* setup chip registers based on hardware */ - st->hw_funcs = &inv_icm42600_hw_funcs; + switch (chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + st->hw_funcs = &inv_icm42607_hw_funcs; + break; + default: + st->hw_funcs = &inv_icm42600_hw_funcs; + break; + } ret = st->hw_funcs->setup(st, bus_setup); if (ret) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 32aa2e52df2e..eb590eb4f397 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -47,6 +47,31 @@ .ext_info = _ext_info, \ } +#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \ + { \ + .type = IIO_ANGL_VEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .ext_info = _ext_info, \ + } + enum inv_icm42600_gyro_scan { INV_ICM42600_GYRO_SCAN_X, INV_ICM42600_GYRO_SCAN_Y, @@ -71,6 +96,17 @@ static const struct iio_chan_spec inv_icm42600_gyro_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42600_GYRO_SCAN_TIMESTAMP), }; +static const struct iio_chan_spec inv_icm42607_gyro_channels[] = { + INV_ICM42607_GYRO_CHAN(IIO_MOD_X, INV_ICM42600_GYRO_SCAN_X, + inv_icm42600_gyro_ext_infos), + INV_ICM42607_GYRO_CHAN(IIO_MOD_Y, INV_ICM42600_GYRO_SCAN_Y, + inv_icm42600_gyro_ext_infos), + INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42600_GYRO_SCAN_Z, + inv_icm42600_gyro_ext_infos), + INV_ICM42600_TEMP_CHAN(INV_ICM42600_GYRO_SCAN_TEMP), + IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42600_GYRO_SCAN_TIMESTAMP), +}; + /* * IIO buffer data: size must be a power of 2 and timestamp aligned * 16 bytes: 6 bytes angular velocity, 2 bytes temperature, 8 bytes timestamp @@ -137,6 +173,47 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev, return ret; } +static int inv_icm42607_gyro_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int fifo_en = 0; + unsigned int sleep_gyro = 0; + unsigned int sleep_temp = 0; + unsigned int sleep; + int ret; + + guard(mutex)(&st->lock); + + if (*scan_mask & INV_ICM42600_SCAN_MASK_TEMP) { + /* enable temp sensor */ + ret = inv_icm42607_set_temp_conf(st, true, &sleep_temp); + if (ret) + goto out_unlock; + fifo_en |= INV_ICM42600_SENSOR_TEMP; + } + + if (*scan_mask & INV_ICM42600_SCAN_MASK_GYRO_3AXIS) { + /* enable gyro sensor */ + conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE; + ret = inv_icm42607_set_gyro_conf(st, &conf, &sleep_gyro); + if (ret) + goto out_unlock; + fifo_en |= INV_ICM42600_SENSOR_GYRO; + } + + /* update data FIFO write */ + ret = inv_icm42607_buffer_set_fifo_en(st, fifo_en | st->fifo.en); + +out_unlock: + /* sleep maximum required time */ + sleep = max(sleep_gyro, sleep_temp); + if (sleep) + msleep(sleep); + return ret; +} + static int inv_icm42600_gyro_read_sensor(struct inv_icm42600_state *st, struct iio_chan_spec const *chan, s16 *val) @@ -188,6 +265,58 @@ static int inv_icm42600_gyro_read_sensor(struct inv_icm42600_state *st, return ret; } +static int inv_icm42607_gyro_read_sensor(struct inv_icm42600_state *st, + struct iio_chan_spec const *chan, + s16 *val) +{ + struct device *dev = regmap_get_device(st->map); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int reg; + __be16 *data; + int ret; + + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + reg = INV_ICM42607_REG_GYRO_DATA_X1; + break; + case IIO_MOD_Y: + reg = INV_ICM42607_REG_GYRO_DATA_Y1; + break; + case IIO_MOD_Z: + reg = INV_ICM42607_REG_GYRO_DATA_Z1; + break; + default: + return -EINVAL; + } + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; + + guard(mutex)(&st->lock); + + /* enable gyro sensor */ + conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE; + ret = inv_icm42607_set_gyro_conf(st, &conf, NULL); + if (ret) + return ret; + + /* read gyro register data */ + data = (__be16 *)&st->buffer[0]; + ret = regmap_bulk_read(st->map, reg, data, sizeof(*data)); + if (ret) + return ret; + + *val = (s16)be16_to_cpup(data); + if (*val == INV_ICM42607_DATA_INVALID) + ret = -EINVAL; + + return ret; +} + /* IIO format int + nano */ static const int inv_icm42600_gyro_scale[] = { /* +/- 2000dps => 0.001065264 rad/s */ @@ -241,6 +370,20 @@ static const int inv_icm42686_gyro_scale[] = { [2 * INV_ICM42686_GYRO_FS_31_25DPS] = 0, [2 * INV_ICM42686_GYRO_FS_31_25DPS + 1] = 16645, }; +static const int inv_icm42607_gyro_scale[] = { + /* +/- 2000dps => 0.001065264 rad/s */ + [2 * INV_ICM42607_GYRO_FS_2000DPS] = 0, + [2 * INV_ICM42607_GYRO_FS_2000DPS + 1] = 1065264, + /* +/- 1000dps => 0.000532632 rad/s */ + [2 * INV_ICM42607_GYRO_FS_1000DPS] = 0, + [2 * INV_ICM42607_GYRO_FS_1000DPS + 1] = 532632, + /* +/- 500dps => 0.000266316 rad/s */ + [2 * INV_ICM42607_GYRO_FS_500DPS] = 0, + [2 * INV_ICM42607_GYRO_FS_500DPS + 1] = 266316, + /* +/- 250dps => 0.000133158 rad/s */ + [2 * INV_ICM42607_GYRO_FS_250DPS] = 0, + [2 * INV_ICM42607_GYRO_FS_250DPS + 1] = 133158, +}; static int inv_icm42600_gyro_read_scale(struct iio_dev *indio_dev, int *val, int *val2) @@ -276,12 +419,21 @@ static int inv_icm42600_gyro_write_scale(struct iio_dev *indio_dev, conf.fs = idx / 2; - pm_runtime_get_sync(dev); - - scoped_guard(mutex, &st->lock) - ret = inv_icm42600_set_gyro_conf(st, &conf, NULL); + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; - pm_runtime_put_autosuspend(dev); + switch (st->chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + scoped_guard(mutex, &st->lock) + ret = inv_icm42607_set_gyro_conf(st, &conf, NULL); + break; + default: + scoped_guard(mutex, &st->lock) + ret = inv_icm42600_set_gyro_conf(st, &conf, NULL); + break; + } return ret; } @@ -306,6 +458,17 @@ static const int inv_icm42600_gyro_odr[] = { 4000, 0, }; +static const int inv_icm42607_gyro_odr[] = { + 12, 500000, /* 12.5Hz */ + 25, 0, /* 25Hz */ + 50, 0, /* 50Hz */ + 100, 0, /* 100Hz */ + 200, 0, /* 200Hz */ + 400, 0, /* 400Hz */ + 800, 0, /* 800Hz */ + 1600, 0, /* 1600Hz */ +}; + static const int inv_icm42600_gyro_odr_conv[] = { INV_ICM42600_ODR_12_5HZ, INV_ICM42600_ODR_25HZ, @@ -317,6 +480,17 @@ static const int inv_icm42600_gyro_odr_conv[] = { INV_ICM42600_ODR_4KHZ_LN, }; +static const int inv_icm42607_gyro_odr_conv[] = { + INV_ICM42607_ODR_12_5HZ, + INV_ICM42607_ODR_25HZ, + INV_ICM42607_ODR_50HZ, + INV_ICM42607_ODR_100HZ, + INV_ICM42607_ODR_200HZ, + INV_ICM42607_ODR_400HZ, + INV_ICM42607_ODR_800HZ, + INV_ICM42607_ODR_1600HZ, +}; + static int inv_icm42600_gyro_read_odr(struct inv_icm42600_state *st, int *val, int *val2) { @@ -338,6 +512,27 @@ static int inv_icm42600_gyro_read_odr(struct inv_icm42600_state *st, return IIO_VAL_INT_PLUS_MICRO; } +static int inv_icm42607_gyro_read_odr(struct inv_icm42600_state *st, + int *val, int *val2) +{ + unsigned int odr; + unsigned int i; + + odr = st->conf.gyro.odr; + + for (i = 0; i < ARRAY_SIZE(inv_icm42607_gyro_odr_conv); ++i) { + if (inv_icm42607_gyro_odr_conv[i] == odr) + break; + } + if (i >= ARRAY_SIZE(inv_icm42607_gyro_odr_conv)) + return -EINVAL; + + *val = inv_icm42607_gyro_odr[2 * i]; + *val2 = inv_icm42607_gyro_odr[2 * i + 1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev, int val, int val2) { @@ -382,6 +577,48 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev, return ret; } +static int inv_icm42607_gyro_write_odr(struct iio_dev *indio_dev, + int val, int val2) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &gyro_st->ts; + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + int ret; + + for (idx = 0; idx < ARRAY_SIZE(inv_icm42607_gyro_odr); idx += 2) { + if (val == inv_icm42607_gyro_odr[idx] && + val2 == inv_icm42607_gyro_odr[idx + 1]) + break; + } + if (idx >= ARRAY_SIZE(inv_icm42607_gyro_odr)) + return -EINVAL; + + conf.odr = inv_icm42607_gyro_odr_conv[idx / 2]; + if (conf.odr == st->conf.gyro.odr) + return 0; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; + guard(mutex)(&st->lock); + + ret = inv_sensors_timestamp_update_odr(ts, inv_icm42607_odr_to_period(conf.odr), + iio_buffer_enabled(indio_dev)); + if (ret) + return ret; + + ret = inv_icm42607_set_gyro_conf(st, &conf, NULL); + if (ret) + return ret; + inv_icm42607_buffer_update_fifo_period(st); + inv_icm42600_buffer_update_watermark(st); + + return 0; +} + /* * Calibration bias values, IIO range format int + nano. * Value is limited to +/-64dps coded on 12 bits signed. Step is 1/32 dps. @@ -606,6 +843,42 @@ static int inv_icm42600_gyro_read_raw(struct iio_dev *indio_dev, } } +static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + s16 data; + int ret; + + switch (chan->type) { + case IIO_ANGL_VEL: + break; + case IIO_TEMP: + return inv_icm42607_temp_read_raw(indio_dev, chan, val, val2, mask); + default: + return -EINVAL; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = inv_icm42607_gyro_read_sensor(st, chan, &data); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + *val = data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return inv_icm42600_gyro_read_scale(indio_dev, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_gyro_read_odr(st, val, val2); + default: + return -EINVAL; + } +} + static int inv_icm42600_gyro_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, @@ -666,6 +939,29 @@ static int inv_icm42600_gyro_write_raw(struct iio_dev *indio_dev, } } +static int inv_icm42607_gyro_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (chan->type != IIO_ANGL_VEL) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = inv_icm42600_gyro_write_scale(indio_dev, val, val2); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return inv_icm42607_gyro_write_odr(indio_dev, val, val2); + default: + return -EINVAL; + } +} + static int inv_icm42600_gyro_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) @@ -725,6 +1021,16 @@ static const struct iio_info inv_icm42600_gyro_info = { .hwfifo_flush_to_buffer = inv_icm42600_gyro_hwfifo_flush, }; +static const struct iio_info inv_icm42607_gyro_info = { + .read_raw = inv_icm42607_gyro_read_raw, + .write_raw = inv_icm42607_gyro_write_raw, + .write_raw_get_fmt = inv_icm42600_gyro_write_raw_get_fmt, + .debugfs_reg_access = inv_icm42600_debugfs_reg, + .update_scan_mode = inv_icm42607_gyro_update_scan_mode, + .hwfifo_set_watermark = inv_icm42600_gyro_hwfifo_set_watermark, + .hwfifo_flush_to_buffer = inv_icm42600_gyro_hwfifo_flush, +}; + int inv_icm42600_gyro_init(struct inv_icm42600_state *st, struct iio_dev *indio_dev) { struct device *dev = regmap_get_device(st->map); @@ -783,6 +1089,56 @@ int inv_icm42600_gyro_init(struct inv_icm42600_state *st, struct iio_dev *indio_ return 0; } +int inv_icm42607_gyro_init(struct inv_icm42600_state *st, struct iio_dev *indio_dev) +{ + struct device *dev = regmap_get_device(st->map); + const char *name; + struct inv_icm42600_sensor_state *gyro_st; + struct inv_sensors_timestamp_chip ts_chip; + int ret; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name); + if (!name) + return -ENOMEM; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st)); + if (!indio_dev) + return -ENOMEM; + gyro_st = iio_priv(indio_dev); + + gyro_st->scales = inv_icm42607_gyro_scale; + gyro_st->scales_len = ARRAY_SIZE(inv_icm42607_gyro_scale); + + /* + * clock period is 32kHz (31250ns) + * jitter is +/- 2% (20 per mille) + */ + ts_chip.clock_period = 31250; + ts_chip.jitter = 20; + ts_chip.init_period = inv_icm42607_odr_to_period(st->conf.accel.odr); + inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip); + + iio_device_set_drvdata(indio_dev, st); + indio_dev->name = name; + indio_dev->info = &inv_icm42607_gyro_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = inv_icm42607_gyro_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_gyro_channels); + indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks; + indio_dev->setup_ops = &inv_icm42607_buffer_ops; + + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + &inv_icm42607_buffer_ops); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + return 0; +} + int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 7e4d3ea68721..cd2071969b9a 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -49,6 +49,29 @@ static int inv_icm42600_i2c_bus_setup(struct inv_icm42600_state *st) INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS); } +static int inv_icm42607_i2c_bus_setup(struct inv_icm42600_state *st) +{ + unsigned int mask, val; + int ret; + + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN | + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN, 0); + if (ret) + return ret; + + mask = INV_ICM42607_DRIVE_CONFIG2_I2C_MASK; + val = INV_ICM42607_DRIVE_CONFIG2_I2C(INV_ICM42600_SLEW_RATE_12_36NS); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2, + mask, val); + if (ret) + return ret; + + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS); +} + static int inv_icm42600_probe(struct i2c_client *client) { const void *match; @@ -63,11 +86,23 @@ static int inv_icm42600_probe(struct i2c_client *client) return -EINVAL; chip = (uintptr_t)match; - regmap = devm_regmap_init_i2c(client, &inv_icm42600_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - return inv_icm42600_core_probe(regmap, chip, inv_icm42600_i2c_bus_setup); + switch (chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + regmap = devm_regmap_init_i2c(client, + &inv_icm42607_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + return inv_icm42600_core_probe(regmap, chip, + inv_icm42607_i2c_bus_setup); + default: + regmap = devm_regmap_init_i2c(client, + &inv_icm42600_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + return inv_icm42600_core_probe(regmap, chip, + inv_icm42600_i2c_bus_setup); + } } /* @@ -78,6 +113,8 @@ static const struct i2c_device_id inv_icm42600_id[] = { { "icm42600", INV_CHIP_ICM42600 }, { "icm42602", INV_CHIP_ICM42602 }, { "icm42605", INV_CHIP_ICM42605 }, + { "icm42607", INV_CHIP_ICM42607 }, + { "icm42607p", INV_CHIP_ICM42607P }, { "icm42686", INV_CHIP_ICM42686 }, { "icm42622", INV_CHIP_ICM42622 }, { "icm42688", INV_CHIP_ICM42688 }, @@ -96,6 +133,12 @@ static const struct of_device_id inv_icm42600_of_matches[] = { }, { .compatible = "invensense,icm42605", .data = (void *)INV_CHIP_ICM42605, + }, { + .compatible = "invensense,icm42607", + .data = (void *)INV_CHIP_ICM42607, + }, { + .compatible = "invensense,icm42607p", + .data = (void *)INV_CHIP_ICM42607P, }, { .compatible = "invensense,icm42686", .data = (void *)INV_CHIP_ICM42686, diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 13e2e7d38638..6bfd5598b262 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -48,6 +48,35 @@ static int inv_icm42600_spi_bus_setup(struct inv_icm42600_state *st) INV_ICM42600_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS); } +static int inv_icm42607_spi_bus_setup(struct inv_icm42600_state *st) +{ + unsigned int mask, val; + int ret; + + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG, + INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE, + INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE); + if (ret) + return ret; + + ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1, + INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN | + INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN, 0); + if (ret) + return ret; + + mask = INV_ICM42607_DRIVE_CONFIG3_SPI_MASK; + val = INV_ICM42607_DRIVE_CONFIG3_SPI(INV_ICM42600_SLEW_RATE_INF_2NS); + ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3, + mask, val); + if (ret) + return ret; + + return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK, + INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS); +} + static int inv_icm42600_probe(struct spi_device *spi) { const void *match; @@ -59,12 +88,22 @@ static int inv_icm42600_probe(struct spi_device *spi) return -EINVAL; chip = (uintptr_t)match; - /* use SPI specific regmap */ - regmap = devm_regmap_init_spi(spi, &inv_icm42600_spi_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - return inv_icm42600_core_probe(regmap, chip, inv_icm42600_spi_bus_setup); + switch (chip) { + case INV_CHIP_ICM42607: + case INV_CHIP_ICM42607P: + regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + return inv_icm42600_core_probe(regmap, chip, + inv_icm42607_spi_bus_setup); + default: + /* use SPI specific regmap */ + regmap = devm_regmap_init_spi(spi, &inv_icm42600_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + return inv_icm42600_core_probe(regmap, chip, + inv_icm42600_spi_bus_setup); + } } /* @@ -75,6 +114,8 @@ static const struct spi_device_id inv_icm42600_id[] = { { "icm42600", INV_CHIP_ICM42600 }, { "icm42602", INV_CHIP_ICM42602 }, { "icm42605", INV_CHIP_ICM42605 }, + { "icm42607", INV_CHIP_ICM42607 }, + { "icm42607p", INV_CHIP_ICM42607P }, { "icm42686", INV_CHIP_ICM42686 }, { "icm42622", INV_CHIP_ICM42622 }, { "icm42688", INV_CHIP_ICM42688 }, @@ -93,6 +134,12 @@ static const struct of_device_id inv_icm42600_of_matches[] = { }, { .compatible = "invensense,icm42605", .data = (void *)INV_CHIP_ICM42605, + }, { + .compatible = "invensense,icm42607", + .data = (void *)INV_CHIP_ICM42607, + }, { + .compatible = "invensense,icm42607p", + .data = (void *)INV_CHIP_ICM42607P, }, { .compatible = "invensense,icm42686", .data = (void *)INV_CHIP_ICM42686, diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c index 727b03d541a5..fdda555dbd16 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c @@ -46,6 +46,34 @@ static int inv_icm42600_temp_read(struct inv_icm42600_state *st, s16 *temp) return ret; } +static int inv_icm42607_temp_read(struct inv_icm42600_state *st, s16 *temp) +{ + struct device *dev = regmap_get_device(st->map); + __be16 *raw; + int ret; + + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; + + guard(mutex)(&st->lock); + + ret = inv_icm42607_set_temp_conf(st, true, NULL); + if (ret) + return ret; + + raw = (__be16 *)&st->buffer[0]; + ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1, raw, sizeof(*raw)); + if (ret) + return ret; + + *temp = (s16)be16_to_cpup(raw); + if (*temp == INV_ICM42600_DATA_INVALID) + ret = -EINVAL; + + return 0; +} + int inv_icm42600_temp_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -83,3 +111,39 @@ int inv_icm42600_temp_read_raw(struct iio_dev *indio_dev, return -EINVAL; } } + +int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + s16 temp; + int ret; + + if (chan->type != IIO_TEMP) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = inv_icm42607_temp_read(st, &temp); + if (ret) + return ret; + *val = temp; + return IIO_VAL_INT; + /* + * T°C = (temp / 128) + 25 + * Tm°C = 1000 * ((temp * 100 / 12800) + 25) + * scale: 100000 / 12800 ~= 7.8125 + * offset: 25000 + */ + case IIO_CHAN_INFO_SCALE: + *val = 7; + *val2 = 812500; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = 25000; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.h index 3941186512fb..b5b9e4fe3d0e 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.h @@ -27,4 +27,8 @@ int inv_icm42600_temp_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask); +int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask); + #endif -- 2.43.0