162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * STMicroelectronics hts221 sensor driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2016 STMicroelectronics Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Lorenzo Bianconi <lorenzo.bianconi@st.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/irqreturn.h> 1462306a36Sopenharmony_ci#include <linux/property.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <linux/bitfield.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/iio/iio.h> 1962306a36Sopenharmony_ci#include <linux/iio/trigger.h> 2062306a36Sopenharmony_ci#include <linux/iio/events.h> 2162306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 2262306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 2362306a36Sopenharmony_ci#include <linux/iio/buffer.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/platform_data/st_sensors_pdata.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "hts221.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define HTS221_REG_DRDY_HL_ADDR 0x22 3062306a36Sopenharmony_ci#define HTS221_REG_DRDY_HL_MASK BIT(7) 3162306a36Sopenharmony_ci#define HTS221_REG_DRDY_PP_OD_ADDR 0x22 3262306a36Sopenharmony_ci#define HTS221_REG_DRDY_PP_OD_MASK BIT(6) 3362306a36Sopenharmony_ci#define HTS221_REG_DRDY_EN_ADDR 0x22 3462306a36Sopenharmony_ci#define HTS221_REG_DRDY_EN_MASK BIT(2) 3562306a36Sopenharmony_ci#define HTS221_REG_STATUS_ADDR 0x27 3662306a36Sopenharmony_ci#define HTS221_RH_DRDY_MASK BIT(1) 3762306a36Sopenharmony_ci#define HTS221_TEMP_DRDY_MASK BIT(0) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int hts221_trig_set_state(struct iio_trigger *trig, bool state) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig); 4262306a36Sopenharmony_ci struct hts221_hw *hw = iio_priv(iio_dev); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return regmap_update_bits(hw->regmap, HTS221_REG_DRDY_EN_ADDR, 4562306a36Sopenharmony_ci HTS221_REG_DRDY_EN_MASK, 4662306a36Sopenharmony_ci FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state)); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const struct iio_trigger_ops hts221_trigger_ops = { 5062306a36Sopenharmony_ci .set_trigger_state = hts221_trig_set_state, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic irqreturn_t hts221_trigger_handler_thread(int irq, void *private) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct hts221_hw *hw = private; 5662306a36Sopenharmony_ci int err, status; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci err = regmap_read(hw->regmap, HTS221_REG_STATUS_ADDR, &status); 5962306a36Sopenharmony_ci if (err < 0) 6062306a36Sopenharmony_ci return IRQ_HANDLED; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * H_DA bit (humidity data available) is routed to DRDY line. 6462306a36Sopenharmony_ci * Humidity sample is computed after temperature one. 6562306a36Sopenharmony_ci * Here we can assume data channels are both available if H_DA bit 6662306a36Sopenharmony_ci * is set in status register 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci if (!(status & HTS221_RH_DRDY_MASK)) 6962306a36Sopenharmony_ci return IRQ_NONE; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci iio_trigger_poll_nested(hw->trig); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return IRQ_HANDLED; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciint hts221_allocate_trigger(struct iio_dev *iio_dev) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct hts221_hw *hw = iio_priv(iio_dev); 7962306a36Sopenharmony_ci struct st_sensors_platform_data *pdata = dev_get_platdata(hw->dev); 8062306a36Sopenharmony_ci bool irq_active_low = false, open_drain = false; 8162306a36Sopenharmony_ci unsigned long irq_type; 8262306a36Sopenharmony_ci int err; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (irq_type) { 8762306a36Sopenharmony_ci case IRQF_TRIGGER_HIGH: 8862306a36Sopenharmony_ci case IRQF_TRIGGER_RISING: 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case IRQF_TRIGGER_LOW: 9162306a36Sopenharmony_ci case IRQF_TRIGGER_FALLING: 9262306a36Sopenharmony_ci irq_active_low = true; 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci default: 9562306a36Sopenharmony_ci dev_info(hw->dev, 9662306a36Sopenharmony_ci "mode %lx unsupported, using IRQF_TRIGGER_RISING\n", 9762306a36Sopenharmony_ci irq_type); 9862306a36Sopenharmony_ci irq_type = IRQF_TRIGGER_RISING; 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_HL_ADDR, 10362306a36Sopenharmony_ci HTS221_REG_DRDY_HL_MASK, 10462306a36Sopenharmony_ci FIELD_PREP(HTS221_REG_DRDY_HL_MASK, 10562306a36Sopenharmony_ci irq_active_low)); 10662306a36Sopenharmony_ci if (err < 0) 10762306a36Sopenharmony_ci return err; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (device_property_read_bool(hw->dev, "drive-open-drain") || 11062306a36Sopenharmony_ci (pdata && pdata->open_drain)) { 11162306a36Sopenharmony_ci irq_type |= IRQF_SHARED; 11262306a36Sopenharmony_ci open_drain = true; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_PP_OD_ADDR, 11662306a36Sopenharmony_ci HTS221_REG_DRDY_PP_OD_MASK, 11762306a36Sopenharmony_ci FIELD_PREP(HTS221_REG_DRDY_PP_OD_MASK, 11862306a36Sopenharmony_ci open_drain)); 11962306a36Sopenharmony_ci if (err < 0) 12062306a36Sopenharmony_ci return err; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci err = devm_request_threaded_irq(hw->dev, hw->irq, NULL, 12362306a36Sopenharmony_ci hts221_trigger_handler_thread, 12462306a36Sopenharmony_ci irq_type | IRQF_ONESHOT, 12562306a36Sopenharmony_ci hw->name, hw); 12662306a36Sopenharmony_ci if (err) { 12762306a36Sopenharmony_ci dev_err(hw->dev, "failed to request trigger irq %d\n", 12862306a36Sopenharmony_ci hw->irq); 12962306a36Sopenharmony_ci return err; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger", 13362306a36Sopenharmony_ci iio_dev->name); 13462306a36Sopenharmony_ci if (!hw->trig) 13562306a36Sopenharmony_ci return -ENOMEM; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci iio_trigger_set_drvdata(hw->trig, iio_dev); 13862306a36Sopenharmony_ci hw->trig->ops = &hts221_trigger_ops; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci err = devm_iio_trigger_register(hw->dev, hw->trig); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci iio_dev->trig = iio_trigger_get(hw->trig); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return err; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int hts221_buffer_preenable(struct iio_dev *iio_dev) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return hts221_set_enable(iio_priv(iio_dev), true); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int hts221_buffer_postdisable(struct iio_dev *iio_dev) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci return hts221_set_enable(iio_priv(iio_dev), false); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const struct iio_buffer_setup_ops hts221_buffer_ops = { 15862306a36Sopenharmony_ci .preenable = hts221_buffer_preenable, 15962306a36Sopenharmony_ci .postdisable = hts221_buffer_postdisable, 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic irqreturn_t hts221_buffer_handler_thread(int irq, void *p) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct iio_poll_func *pf = p; 16562306a36Sopenharmony_ci struct iio_dev *iio_dev = pf->indio_dev; 16662306a36Sopenharmony_ci struct hts221_hw *hw = iio_priv(iio_dev); 16762306a36Sopenharmony_ci struct iio_chan_spec const *ch; 16862306a36Sopenharmony_ci int err; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* humidity data */ 17162306a36Sopenharmony_ci ch = &iio_dev->channels[HTS221_SENSOR_H]; 17262306a36Sopenharmony_ci err = regmap_bulk_read(hw->regmap, ch->address, 17362306a36Sopenharmony_ci &hw->scan.channels[0], 17462306a36Sopenharmony_ci sizeof(hw->scan.channels[0])); 17562306a36Sopenharmony_ci if (err < 0) 17662306a36Sopenharmony_ci goto out; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* temperature data */ 17962306a36Sopenharmony_ci ch = &iio_dev->channels[HTS221_SENSOR_T]; 18062306a36Sopenharmony_ci err = regmap_bulk_read(hw->regmap, ch->address, 18162306a36Sopenharmony_ci &hw->scan.channels[1], 18262306a36Sopenharmony_ci sizeof(hw->scan.channels[1])); 18362306a36Sopenharmony_ci if (err < 0) 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci iio_push_to_buffers_with_timestamp(iio_dev, &hw->scan, 18762306a36Sopenharmony_ci iio_get_time_ns(iio_dev)); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciout: 19062306a36Sopenharmony_ci iio_trigger_notify_done(hw->trig); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return IRQ_HANDLED; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint hts221_allocate_buffers(struct iio_dev *iio_dev) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct hts221_hw *hw = iio_priv(iio_dev); 19862306a36Sopenharmony_ci return devm_iio_triggered_buffer_setup(hw->dev, iio_dev, 19962306a36Sopenharmony_ci NULL, hts221_buffer_handler_thread, 20062306a36Sopenharmony_ci &hts221_buffer_ops); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciMODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>"); 20462306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver"); 20562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 206