Lines Matching +full:startup +full:- +full:time +full:- +full:ms

1 // SPDX-License-Identifier: GPL-2.0-only
3 * mlx90614.c - Support for Melexis MLX90614/MLX90615 contactless IR temperature sensor
9 * Driver for the Melexis MLX90614/MLX90615 I2C 16-bit IR thermopile sensor
11 * MLX90614 - 17-bit ADC + MLX90302 DSP
12 * MLX90615 - 16-bit ADC + MLX90325 DSP
14 * (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
17 * for at least 33ms. This is achieved with an extra GPIO that can be connected
21 * always has a pull-up so we do not need an extra GPIO to drive it high. If
56 /* Timings (in ms) */
57 #define MLX90614_TIMING_EEPROM 20 /* time for EEPROM write/erase to complete */
58 #define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
59 #define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
61 #define MLX90615_TIMING_WAKEUP 22 /* time to hold SCL low for wake-up */
66 #define MLX90614_CONST_OFFSET_DEC -13657 /* decimal part of the Kelvin offset */
71 /* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
72 #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
73 #define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
76 /* EEPROM offsets with 16-bit data, MSB first */
80 /* RAM offsets with 16-bit data, MSB first */
102 struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
122 dev_dbg(&client->dev, "Writing 0x%x to address 0x%x", value, command); in mlx90614_write_word()
125 ret = i2c_smbus_xfer(client->adapter, client->addr, in mlx90614_write_word()
126 client->flags | I2C_CLIENT_PEC, in mlx90614_write_word()
135 ret = i2c_smbus_xfer(client->adapter, client->addr, in mlx90614_write_word()
136 client->flags | I2C_CLIENT_PEC, in mlx90614_write_word()
154 const struct mlx_chip_info *chip_info = data->chip_info; in mlx90614_iir_search()
158 for (i = chip_info->iir_valid_offset; in mlx90614_iir_search()
159 i < ARRAY_SIZE(chip_info->iir_values); in mlx90614_iir_search()
161 if (value == chip_info->iir_values[i]) in mlx90614_iir_search()
165 if (i == ARRAY_SIZE(chip_info->iir_values)) in mlx90614_iir_search()
166 return -EINVAL; in mlx90614_iir_search()
173 ret = i2c_smbus_read_word_data(client, chip_info->op_eeprom_config1); in mlx90614_iir_search()
178 if (chip_info->fir_config_mask) { in mlx90614_iir_search()
179 ret &= ~chip_info->fir_config_mask; in mlx90614_iir_search()
180 ret |= field_prep(chip_info->fir_config_mask, MLX90614_CONST_FIR); in mlx90614_iir_search()
183 ret &= ~chip_info->iir_config_mask; in mlx90614_iir_search()
184 ret |= field_prep(chip_info->iir_config_mask, i); in mlx90614_iir_search()
187 ret = mlx90614_write_word(client, chip_info->op_eeprom_config1, ret); in mlx90614_iir_search()
193 * If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
194 * the last wake-up. This is normally only needed to get a valid temperature
198 static int mlx90614_power_get(struct mlx90614_data *data, bool startup) in mlx90614_power_get() argument
203 if (!data->wakeup_gpio) in mlx90614_power_get()
206 ret = pm_runtime_resume_and_get(&data->client->dev); in mlx90614_power_get()
210 if (startup) { in mlx90614_power_get()
212 if (time_before(now, data->ready_timestamp) && in mlx90614_power_get()
214 data->ready_timestamp - now)) != 0) { in mlx90614_power_get()
215 pm_runtime_put_autosuspend(&data->client->dev); in mlx90614_power_get()
216 return -EINTR; in mlx90614_power_get()
225 if (!data->wakeup_gpio) in mlx90614_power_put()
228 pm_runtime_mark_last_busy(&data->client->dev); in mlx90614_power_put()
229 pm_runtime_put_autosuspend(&data->client->dev); in mlx90614_power_put()
232 static inline int mlx90614_power_get(struct mlx90614_data *data, bool startup) in mlx90614_power_get() argument
247 const struct mlx_chip_info *chip_info = data->chip_info; in mlx90614_read_raw()
253 switch (channel->channel2) { in mlx90614_read_raw()
255 cmd = chip_info->op_ram_ta; in mlx90614_read_raw()
258 if (chip_info->dual_channel && channel->channel) in mlx90614_read_raw()
259 return -EINVAL; in mlx90614_read_raw()
261 switch (channel->channel) { in mlx90614_read_raw()
263 cmd = chip_info->op_ram_tobj1; in mlx90614_read_raw()
266 cmd = chip_info->op_ram_tobj2; in mlx90614_read_raw()
269 return -EINVAL; in mlx90614_read_raw()
273 return -EINVAL; in mlx90614_read_raw()
279 ret = i2c_smbus_read_word_data(data->client, cmd); in mlx90614_read_raw()
287 return -EIO; in mlx90614_read_raw()
303 mutex_lock(&data->lock); in mlx90614_read_raw()
304 ret = i2c_smbus_read_word_data(data->client, in mlx90614_read_raw()
305 chip_info->op_eeprom_emissivity); in mlx90614_read_raw()
306 mutex_unlock(&data->lock); in mlx90614_read_raw()
312 if (ret == chip_info->emissivity_max) { in mlx90614_read_raw()
317 *val2 = ret * NSEC_PER_SEC / chip_info->emissivity_max; in mlx90614_read_raw()
326 mutex_lock(&data->lock); in mlx90614_read_raw()
327 ret = i2c_smbus_read_word_data(data->client, in mlx90614_read_raw()
328 chip_info->op_eeprom_config1); in mlx90614_read_raw()
329 mutex_unlock(&data->lock); in mlx90614_read_raw()
335 idx = field_get(chip_info->iir_config_mask, ret) - in mlx90614_read_raw()
336 chip_info->iir_valid_offset; in mlx90614_read_raw()
338 *val = chip_info->iir_values[idx] / 100; in mlx90614_read_raw()
339 *val2 = (chip_info->iir_values[idx] % 100) * 10000; in mlx90614_read_raw()
342 return -EINVAL; in mlx90614_read_raw()
351 const struct mlx_chip_info *chip_info = data->chip_info; in mlx90614_write_raw()
357 return -EINVAL; in mlx90614_write_raw()
358 val = val * chip_info->emissivity_max + in mlx90614_write_raw()
359 val2 * chip_info->emissivity_max / NSEC_PER_SEC; in mlx90614_write_raw()
365 mutex_lock(&data->lock); in mlx90614_write_raw()
366 ret = mlx90614_write_word(data->client, in mlx90614_write_raw()
367 chip_info->op_eeprom_emissivity, val); in mlx90614_write_raw()
368 mutex_unlock(&data->lock); in mlx90614_write_raw()
374 return -EINVAL; in mlx90614_write_raw()
380 mutex_lock(&data->lock); in mlx90614_write_raw()
381 ret = mlx90614_iir_search(data->client, in mlx90614_write_raw()
383 mutex_unlock(&data->lock); in mlx90614_write_raw()
388 return -EINVAL; in mlx90614_write_raw()
402 return -EINVAL; in mlx90614_write_raw_get_fmt()
412 const struct mlx_chip_info *chip_info = data->chip_info; in mlx90614_read_avail()
416 *vals = (int *)chip_info->iir_freqs; in mlx90614_read_avail()
418 *length = 2 * (ARRAY_SIZE(chip_info->iir_freqs) - in mlx90614_read_avail()
419 chip_info->iir_valid_offset); in mlx90614_read_avail()
422 return -EINVAL; in mlx90614_read_avail()
473 const struct mlx_chip_info *chip_info = data->chip_info; in mlx90614_sleep()
476 if (!data->wakeup_gpio) { in mlx90614_sleep()
477 dev_dbg(&data->client->dev, "Sleep disabled"); in mlx90614_sleep()
478 return -ENOSYS; in mlx90614_sleep()
481 dev_dbg(&data->client->dev, "Requesting sleep"); in mlx90614_sleep()
483 mutex_lock(&data->lock); in mlx90614_sleep()
484 ret = i2c_smbus_xfer(data->client->adapter, data->client->addr, in mlx90614_sleep()
485 data->client->flags | I2C_CLIENT_PEC, in mlx90614_sleep()
486 I2C_SMBUS_WRITE, chip_info->op_sleep, in mlx90614_sleep()
488 mutex_unlock(&data->lock); in mlx90614_sleep()
495 const struct mlx_chip_info *chip_info = data->chip_info; in mlx90614_wakeup()
497 if (!data->wakeup_gpio) { in mlx90614_wakeup()
498 dev_dbg(&data->client->dev, "Wake-up disabled"); in mlx90614_wakeup()
499 return -ENOSYS; in mlx90614_wakeup()
502 dev_dbg(&data->client->dev, "Requesting wake-up"); in mlx90614_wakeup()
504 i2c_lock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER); in mlx90614_wakeup()
505 gpiod_direction_output(data->wakeup_gpio, 0); in mlx90614_wakeup()
506 msleep(chip_info->wakeup_delay_ms); in mlx90614_wakeup()
507 gpiod_direction_input(data->wakeup_gpio); in mlx90614_wakeup()
508 i2c_unlock_bus(data->client->adapter, I2C_LOCK_ROOT_ADAPTER); in mlx90614_wakeup()
510 data->ready_timestamp = jiffies + in mlx90614_wakeup()
515 * wake-up signal has been sent. As a workaround, do a dummy read. in mlx90614_wakeup()
519 i2c_smbus_read_word_data(data->client, chip_info->op_eeprom_config1); in mlx90614_wakeup()
524 /* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
529 if (!i2c_check_functionality(client->adapter, in mlx90614_probe_wakeup()
531 dev_info(&client->dev, in mlx90614_probe_wakeup()
536 gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN); in mlx90614_probe_wakeup()
539 dev_warn(&client->dev, in mlx90614_probe_wakeup()
544 dev_info(&client->dev, in mlx90614_probe_wakeup()
545 "wakeup-gpio not found, sleep disabled"); in mlx90614_probe_wakeup()
553 return -ENOSYS; in mlx90614_sleep()
557 return -ENOSYS; in mlx90614_wakeup()
570 const struct mlx_chip_info *chip_info = data->chip_info; in mlx90614_probe_num_ir_sensors()
573 if (chip_info->dual_channel) in mlx90614_probe_num_ir_sensors()
576 ret = i2c_smbus_read_word_data(client, chip_info->op_eeprom_config1); in mlx90614_probe_num_ir_sensors()
591 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) in mlx90614_probe()
592 return -EOPNOTSUPP; in mlx90614_probe()
594 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); in mlx90614_probe()
596 return -ENOMEM; in mlx90614_probe()
600 data->client = client; in mlx90614_probe()
601 mutex_init(&data->lock); in mlx90614_probe()
602 data->wakeup_gpio = mlx90614_probe_wakeup(client); in mlx90614_probe()
603 data->chip_info = i2c_get_match_data(client); in mlx90614_probe()
607 indio_dev->name = id->name; in mlx90614_probe()
608 indio_dev->modes = INDIO_DIRECT_MODE; in mlx90614_probe()
609 indio_dev->info = &mlx90614_info; in mlx90614_probe()
614 dev_dbg(&client->dev, "Found single sensor"); in mlx90614_probe()
615 indio_dev->channels = mlx90614_channels; in mlx90614_probe()
616 indio_dev->num_channels = 2; in mlx90614_probe()
619 dev_dbg(&client->dev, "Found dual sensor"); in mlx90614_probe()
620 indio_dev->channels = mlx90614_channels; in mlx90614_probe()
621 indio_dev->num_channels = 3; in mlx90614_probe()
627 if (data->wakeup_gpio) { in mlx90614_probe()
628 pm_runtime_set_autosuspend_delay(&client->dev, in mlx90614_probe()
630 pm_runtime_use_autosuspend(&client->dev); in mlx90614_probe()
631 pm_runtime_set_active(&client->dev); in mlx90614_probe()
632 pm_runtime_enable(&client->dev); in mlx90614_probe()
645 if (data->wakeup_gpio) { in mlx90614_remove()
646 pm_runtime_disable(&client->dev); in mlx90614_remove()
647 if (!pm_runtime_status_suspended(&client->dev)) in mlx90614_remove()
649 pm_runtime_set_suspended(&client->dev); in mlx90614_remove()
724 if (data->wakeup_gpio && pm_runtime_active(dev)) in mlx90614_pm_suspend()
736 if (data->wakeup_gpio) { in mlx90614_pm_resume()
784 MODULE_AUTHOR("Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>");