1e4a70e3eSLorenzo Bianconi /* 2e4a70e3eSLorenzo Bianconi * STMicroelectronics hts221 sensor driver 3e4a70e3eSLorenzo Bianconi * 4e4a70e3eSLorenzo Bianconi * Copyright 2016 STMicroelectronics Inc. 5e4a70e3eSLorenzo Bianconi * 6e4a70e3eSLorenzo Bianconi * Lorenzo Bianconi <lorenzo.bianconi@st.com> 7e4a70e3eSLorenzo Bianconi * 8e4a70e3eSLorenzo Bianconi * Licensed under the GPL-2. 9e4a70e3eSLorenzo Bianconi */ 10e4a70e3eSLorenzo Bianconi #include <linux/kernel.h> 11e4a70e3eSLorenzo Bianconi #include <linux/module.h> 12e4a70e3eSLorenzo Bianconi #include <linux/device.h> 13e4a70e3eSLorenzo Bianconi #include <linux/interrupt.h> 14e4a70e3eSLorenzo Bianconi #include <linux/irqreturn.h> 15e4a70e3eSLorenzo Bianconi 16e4a70e3eSLorenzo Bianconi #include <linux/iio/iio.h> 17e4a70e3eSLorenzo Bianconi #include <linux/iio/trigger.h> 18e4a70e3eSLorenzo Bianconi #include <linux/iio/events.h> 19e4a70e3eSLorenzo Bianconi #include <linux/iio/trigger_consumer.h> 20e4a70e3eSLorenzo Bianconi #include <linux/iio/triggered_buffer.h> 21e4a70e3eSLorenzo Bianconi #include <linux/iio/buffer.h> 22e4a70e3eSLorenzo Bianconi 239251f7aaSLorenzo Bianconi #include <linux/platform_data/st_sensors_pdata.h> 249251f7aaSLorenzo Bianconi 25e4a70e3eSLorenzo Bianconi #include "hts221.h" 26e4a70e3eSLorenzo Bianconi 27e7b4b45eSLorenzo Bianconi #define HTS221_REG_DRDY_HL_ADDR 0x22 28e7b4b45eSLorenzo Bianconi #define HTS221_REG_DRDY_HL_MASK BIT(7) 299251f7aaSLorenzo Bianconi #define HTS221_REG_DRDY_PP_OD_ADDR 0x22 309251f7aaSLorenzo Bianconi #define HTS221_REG_DRDY_PP_OD_MASK BIT(6) 31*f3f0ae16SLorenzo Bianconi #define HTS221_REG_DRDY_EN_ADDR 0x22 32*f3f0ae16SLorenzo Bianconi #define HTS221_REG_DRDY_EN_MASK BIT(2) 33e4a70e3eSLorenzo Bianconi #define HTS221_REG_STATUS_ADDR 0x27 34e4a70e3eSLorenzo Bianconi #define HTS221_RH_DRDY_MASK BIT(1) 35e4a70e3eSLorenzo Bianconi #define HTS221_TEMP_DRDY_MASK BIT(0) 36e4a70e3eSLorenzo Bianconi 37e4a70e3eSLorenzo Bianconi static int hts221_trig_set_state(struct iio_trigger *trig, bool state) 38e4a70e3eSLorenzo Bianconi { 39e4a70e3eSLorenzo Bianconi struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig); 40e4a70e3eSLorenzo Bianconi struct hts221_hw *hw = iio_priv(iio_dev); 41*f3f0ae16SLorenzo Bianconi int err; 42e4a70e3eSLorenzo Bianconi 43*f3f0ae16SLorenzo Bianconi err = hts221_write_with_mask(hw, HTS221_REG_DRDY_EN_ADDR, 44*f3f0ae16SLorenzo Bianconi HTS221_REG_DRDY_EN_MASK, state); 45*f3f0ae16SLorenzo Bianconi 46*f3f0ae16SLorenzo Bianconi return err < 0 ? err : 0; 47e4a70e3eSLorenzo Bianconi } 48e4a70e3eSLorenzo Bianconi 49e4a70e3eSLorenzo Bianconi static const struct iio_trigger_ops hts221_trigger_ops = { 50e4a70e3eSLorenzo Bianconi .owner = THIS_MODULE, 51e4a70e3eSLorenzo Bianconi .set_trigger_state = hts221_trig_set_state, 52e4a70e3eSLorenzo Bianconi }; 53e4a70e3eSLorenzo Bianconi 54e4a70e3eSLorenzo Bianconi static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) 55e4a70e3eSLorenzo Bianconi { 567ae6df68Ssimran singhal struct hts221_hw *hw = private; 57e4a70e3eSLorenzo Bianconi u8 status; 58e4a70e3eSLorenzo Bianconi int err; 59e4a70e3eSLorenzo Bianconi 60e4a70e3eSLorenzo Bianconi err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status), 61e4a70e3eSLorenzo Bianconi &status); 62e4a70e3eSLorenzo Bianconi if (err < 0) 63e4a70e3eSLorenzo Bianconi return IRQ_HANDLED; 64e4a70e3eSLorenzo Bianconi 65e4a70e3eSLorenzo Bianconi /* 66e4a70e3eSLorenzo Bianconi * H_DA bit (humidity data available) is routed to DRDY line. 67e4a70e3eSLorenzo Bianconi * Humidity sample is computed after temperature one. 68e4a70e3eSLorenzo Bianconi * Here we can assume data channels are both available if H_DA bit 69e4a70e3eSLorenzo Bianconi * is set in status register 70e4a70e3eSLorenzo Bianconi */ 71e4a70e3eSLorenzo Bianconi if (!(status & HTS221_RH_DRDY_MASK)) 72e4a70e3eSLorenzo Bianconi return IRQ_NONE; 73e4a70e3eSLorenzo Bianconi 74e4a70e3eSLorenzo Bianconi iio_trigger_poll_chained(hw->trig); 75e4a70e3eSLorenzo Bianconi 76e4a70e3eSLorenzo Bianconi return IRQ_HANDLED; 77e4a70e3eSLorenzo Bianconi } 78e4a70e3eSLorenzo Bianconi 79e4a70e3eSLorenzo Bianconi int hts221_allocate_trigger(struct hts221_hw *hw) 80e4a70e3eSLorenzo Bianconi { 81e4a70e3eSLorenzo Bianconi struct iio_dev *iio_dev = iio_priv_to_dev(hw); 829251f7aaSLorenzo Bianconi bool irq_active_low = false, open_drain = false; 839251f7aaSLorenzo Bianconi struct device_node *np = hw->dev->of_node; 849251f7aaSLorenzo Bianconi struct st_sensors_platform_data *pdata; 85e4a70e3eSLorenzo Bianconi unsigned long irq_type; 86e4a70e3eSLorenzo Bianconi int err; 87e4a70e3eSLorenzo Bianconi 88e4a70e3eSLorenzo Bianconi irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); 89e4a70e3eSLorenzo Bianconi 90e4a70e3eSLorenzo Bianconi switch (irq_type) { 91e4a70e3eSLorenzo Bianconi case IRQF_TRIGGER_HIGH: 92e4a70e3eSLorenzo Bianconi case IRQF_TRIGGER_RISING: 93e4a70e3eSLorenzo Bianconi break; 94e7b4b45eSLorenzo Bianconi case IRQF_TRIGGER_LOW: 95e7b4b45eSLorenzo Bianconi case IRQF_TRIGGER_FALLING: 96e7b4b45eSLorenzo Bianconi irq_active_low = true; 97e7b4b45eSLorenzo Bianconi break; 98e4a70e3eSLorenzo Bianconi default: 99e4a70e3eSLorenzo Bianconi dev_info(hw->dev, 100e4a70e3eSLorenzo Bianconi "mode %lx unsupported, using IRQF_TRIGGER_RISING\n", 101e4a70e3eSLorenzo Bianconi irq_type); 102e4a70e3eSLorenzo Bianconi irq_type = IRQF_TRIGGER_RISING; 103e4a70e3eSLorenzo Bianconi break; 104e4a70e3eSLorenzo Bianconi } 105e4a70e3eSLorenzo Bianconi 106e7b4b45eSLorenzo Bianconi err = hts221_write_with_mask(hw, HTS221_REG_DRDY_HL_ADDR, 107e7b4b45eSLorenzo Bianconi HTS221_REG_DRDY_HL_MASK, irq_active_low); 108e7b4b45eSLorenzo Bianconi if (err < 0) 109e7b4b45eSLorenzo Bianconi return err; 1109251f7aaSLorenzo Bianconi 1119251f7aaSLorenzo Bianconi pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; 1129251f7aaSLorenzo Bianconi if ((np && of_property_read_bool(np, "drive-open-drain")) || 1139251f7aaSLorenzo Bianconi (pdata && pdata->open_drain)) { 1149251f7aaSLorenzo Bianconi irq_type |= IRQF_SHARED; 1159251f7aaSLorenzo Bianconi open_drain = true; 1169251f7aaSLorenzo Bianconi } 1179251f7aaSLorenzo Bianconi 1189251f7aaSLorenzo Bianconi err = hts221_write_with_mask(hw, HTS221_REG_DRDY_PP_OD_ADDR, 1199251f7aaSLorenzo Bianconi HTS221_REG_DRDY_PP_OD_MASK, 1209251f7aaSLorenzo Bianconi open_drain); 1219251f7aaSLorenzo Bianconi if (err < 0) 1229251f7aaSLorenzo Bianconi return err; 1239251f7aaSLorenzo Bianconi 124e4a70e3eSLorenzo Bianconi err = devm_request_threaded_irq(hw->dev, hw->irq, NULL, 125e4a70e3eSLorenzo Bianconi hts221_trigger_handler_thread, 126e4a70e3eSLorenzo Bianconi irq_type | IRQF_ONESHOT, 127e4a70e3eSLorenzo Bianconi hw->name, hw); 128e4a70e3eSLorenzo Bianconi if (err) { 129e4a70e3eSLorenzo Bianconi dev_err(hw->dev, "failed to request trigger irq %d\n", 130e4a70e3eSLorenzo Bianconi hw->irq); 131e4a70e3eSLorenzo Bianconi return err; 132e4a70e3eSLorenzo Bianconi } 133e4a70e3eSLorenzo Bianconi 134e4a70e3eSLorenzo Bianconi hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger", 135e4a70e3eSLorenzo Bianconi iio_dev->name); 136e4a70e3eSLorenzo Bianconi if (!hw->trig) 137e4a70e3eSLorenzo Bianconi return -ENOMEM; 138e4a70e3eSLorenzo Bianconi 139e4a70e3eSLorenzo Bianconi iio_trigger_set_drvdata(hw->trig, iio_dev); 140e4a70e3eSLorenzo Bianconi hw->trig->ops = &hts221_trigger_ops; 141e4a70e3eSLorenzo Bianconi hw->trig->dev.parent = hw->dev; 142e4a70e3eSLorenzo Bianconi iio_dev->trig = iio_trigger_get(hw->trig); 143e4a70e3eSLorenzo Bianconi 144e4a70e3eSLorenzo Bianconi return devm_iio_trigger_register(hw->dev, hw->trig); 145e4a70e3eSLorenzo Bianconi } 146e4a70e3eSLorenzo Bianconi 147e4a70e3eSLorenzo Bianconi static int hts221_buffer_preenable(struct iio_dev *iio_dev) 148e4a70e3eSLorenzo Bianconi { 149e3e25446SLorenzo Bianconi return hts221_set_enable(iio_priv(iio_dev), true); 150e4a70e3eSLorenzo Bianconi } 151e4a70e3eSLorenzo Bianconi 152e4a70e3eSLorenzo Bianconi static int hts221_buffer_postdisable(struct iio_dev *iio_dev) 153e4a70e3eSLorenzo Bianconi { 154e3e25446SLorenzo Bianconi return hts221_set_enable(iio_priv(iio_dev), false); 155e4a70e3eSLorenzo Bianconi } 156e4a70e3eSLorenzo Bianconi 157e4a70e3eSLorenzo Bianconi static const struct iio_buffer_setup_ops hts221_buffer_ops = { 158e4a70e3eSLorenzo Bianconi .preenable = hts221_buffer_preenable, 159e4a70e3eSLorenzo Bianconi .postenable = iio_triggered_buffer_postenable, 160e4a70e3eSLorenzo Bianconi .predisable = iio_triggered_buffer_predisable, 161e4a70e3eSLorenzo Bianconi .postdisable = hts221_buffer_postdisable, 162e4a70e3eSLorenzo Bianconi }; 163e4a70e3eSLorenzo Bianconi 164e4a70e3eSLorenzo Bianconi static irqreturn_t hts221_buffer_handler_thread(int irq, void *p) 165e4a70e3eSLorenzo Bianconi { 166e4a70e3eSLorenzo Bianconi u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)]; 167e4a70e3eSLorenzo Bianconi struct iio_poll_func *pf = p; 168e4a70e3eSLorenzo Bianconi struct iio_dev *iio_dev = pf->indio_dev; 169e4a70e3eSLorenzo Bianconi struct hts221_hw *hw = iio_priv(iio_dev); 170e4a70e3eSLorenzo Bianconi struct iio_chan_spec const *ch; 171e4a70e3eSLorenzo Bianconi int err; 172e4a70e3eSLorenzo Bianconi 173e4a70e3eSLorenzo Bianconi /* humidity data */ 174e4a70e3eSLorenzo Bianconi ch = &iio_dev->channels[HTS221_SENSOR_H]; 175e4a70e3eSLorenzo Bianconi err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE, 176e4a70e3eSLorenzo Bianconi buffer); 177e4a70e3eSLorenzo Bianconi if (err < 0) 178e4a70e3eSLorenzo Bianconi goto out; 179e4a70e3eSLorenzo Bianconi 180e4a70e3eSLorenzo Bianconi /* temperature data */ 181e4a70e3eSLorenzo Bianconi ch = &iio_dev->channels[HTS221_SENSOR_T]; 182e4a70e3eSLorenzo Bianconi err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE, 183e4a70e3eSLorenzo Bianconi buffer + HTS221_DATA_SIZE); 184e4a70e3eSLorenzo Bianconi if (err < 0) 185e4a70e3eSLorenzo Bianconi goto out; 186e4a70e3eSLorenzo Bianconi 187e4a70e3eSLorenzo Bianconi iio_push_to_buffers_with_timestamp(iio_dev, buffer, 188e4a70e3eSLorenzo Bianconi iio_get_time_ns(iio_dev)); 189e4a70e3eSLorenzo Bianconi 190e4a70e3eSLorenzo Bianconi out: 191e4a70e3eSLorenzo Bianconi iio_trigger_notify_done(hw->trig); 192e4a70e3eSLorenzo Bianconi 193e4a70e3eSLorenzo Bianconi return IRQ_HANDLED; 194e4a70e3eSLorenzo Bianconi } 195e4a70e3eSLorenzo Bianconi 196e4a70e3eSLorenzo Bianconi int hts221_allocate_buffers(struct hts221_hw *hw) 197e4a70e3eSLorenzo Bianconi { 198e4a70e3eSLorenzo Bianconi return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw), 199e4a70e3eSLorenzo Bianconi NULL, hts221_buffer_handler_thread, 200e4a70e3eSLorenzo Bianconi &hts221_buffer_ops); 201e4a70e3eSLorenzo Bianconi } 202e4a70e3eSLorenzo Bianconi 203e4a70e3eSLorenzo Bianconi MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>"); 204e4a70e3eSLorenzo Bianconi MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver"); 205e4a70e3eSLorenzo Bianconi MODULE_LICENSE("GPL v2"); 206