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