Skip to content

Commit dc81be9

Browse files
embedded-essentialsjic23
authored andcommitted
iio: proximity: rfd77402: Add interrupt handling support
Add interrupt handling support to enable event-driven data acquisition instead of continuous polling. This improves responsiveness, reduces CPU overhead, and supports low-power operation by allowing the system to remain idle until an interrupt occurs. Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com> Signed-off-by: Shrikant Raskar <raskar.shree97@gmail.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
1 parent 53516b6 commit dc81be9

1 file changed

Lines changed: 113 additions & 8 deletions

File tree

drivers/iio/proximity/rfd77402.c

Lines changed: 113 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,41 @@
66
*
77
* 7-bit I2C slave address 0x4c
88
*
9-
* TODO: interrupt
109
* https://media.digikey.com/pdf/Data%20Sheets/RF%20Digital%20PDFs/RFD77402.pdf
1110
*/
1211

12+
#include <linux/bits.h>
13+
#include <linux/completion.h>
1314
#include <linux/delay.h>
15+
#include <linux/dev_printk.h>
16+
#include <linux/errno.h>
1417
#include <linux/i2c.h>
18+
#include <linux/interrupt.h>
1519
#include <linux/iopoll.h>
20+
#include <linux/jiffies.h>
1621
#include <linux/module.h>
22+
#include <linux/types.h>
1723

1824
#include <linux/iio/iio.h>
1925

2026
#define RFD77402_DRV_NAME "rfd77402"
2127

2228
#define RFD77402_ICSR 0x00 /* Interrupt Control Status Register */
29+
#define RFD77402_ICSR_CLR_CFG BIT(0)
30+
#define RFD77402_ICSR_CLR_TYPE BIT(1)
2331
#define RFD77402_ICSR_INT_MODE BIT(2)
2432
#define RFD77402_ICSR_INT_POL BIT(3)
2533
#define RFD77402_ICSR_RESULT BIT(4)
2634
#define RFD77402_ICSR_M2H_MSG BIT(5)
2735
#define RFD77402_ICSR_H2M_MSG BIT(6)
2836
#define RFD77402_ICSR_RESET BIT(7)
2937

38+
#define RFD77402_IER 0x02
39+
#define RFD77402_IER_RESULT BIT(0)
40+
#define RFD77402_IER_M2H_MSG BIT(1)
41+
#define RFD77402_IER_H2M_MSG BIT(2)
42+
#define RFD77402_IER_RESET BIT(3)
43+
3044
#define RFD77402_CMD_R 0x04
3145
#define RFD77402_CMD_SINGLE 0x01
3246
#define RFD77402_CMD_STANDBY 0x10
@@ -81,10 +95,14 @@ static const struct {
8195
* struct rfd77402_data - device-specific data for the RFD77402 sensor
8296
* @client: I2C client handle
8397
* @lock: mutex to serialize sensor reads
98+
* @completion: completion used for interrupt-driven measurements
99+
* @irq_en: indicates whether interrupt mode is enabled
84100
*/
85101
struct rfd77402_data {
86102
struct i2c_client *client;
87103
struct mutex lock;
104+
struct completion completion;
105+
bool irq_en;
88106
};
89107

90108
static const struct iio_chan_spec rfd77402_channels[] = {
@@ -95,6 +113,41 @@ static const struct iio_chan_spec rfd77402_channels[] = {
95113
},
96114
};
97115

116+
static irqreturn_t rfd77402_interrupt_handler(int irq, void *pdata)
117+
{
118+
struct rfd77402_data *data = pdata;
119+
int ret;
120+
121+
ret = i2c_smbus_read_byte_data(data->client, RFD77402_ICSR);
122+
if (ret < 0)
123+
return IRQ_NONE;
124+
125+
/* Check if the interrupt is from our device */
126+
if (!(ret & RFD77402_ICSR_RESULT))
127+
return IRQ_NONE;
128+
129+
/* Signal completion of measurement */
130+
complete(&data->completion);
131+
return IRQ_HANDLED;
132+
}
133+
134+
static int rfd77402_wait_for_irq(struct rfd77402_data *data)
135+
{
136+
int ret;
137+
138+
/*
139+
* According to RFD77402 Datasheet v1.8,
140+
* Section 3.1.1 "Single Measure" (Figure: Single Measure Flow Chart),
141+
* the suggested timeout for single measure is 100 ms.
142+
*/
143+
ret = wait_for_completion_timeout(&data->completion,
144+
msecs_to_jiffies(100));
145+
if (ret == 0)
146+
return -ETIMEDOUT;
147+
148+
return 0;
149+
}
150+
98151
static int rfd77402_set_state(struct i2c_client *client, u8 state, u16 check)
99152
{
100153
int ret;
@@ -120,6 +173,11 @@ static int rfd77402_wait_for_result(struct rfd77402_data *data)
120173
struct i2c_client *client = data->client;
121174
int val, ret;
122175

176+
if (data->irq_en) {
177+
reinit_completion(&data->completion);
178+
return rfd77402_wait_for_irq(data);
179+
}
180+
123181
/*
124182
* As per RFD77402 datasheet section '3.1.1 Single Measure', the
125183
* suggested timeout value for single measure is 100ms.
@@ -204,19 +262,47 @@ static const struct iio_info rfd77402_info = {
204262
.read_raw = rfd77402_read_raw,
205263
};
206264

207-
static int rfd77402_init(struct i2c_client *client)
265+
static int rfd77402_config_irq(struct i2c_client *client, u8 csr, u8 ier)
208266
{
267+
int ret;
268+
269+
ret = i2c_smbus_write_byte_data(client, RFD77402_ICSR, csr);
270+
if (ret)
271+
return ret;
272+
273+
return i2c_smbus_write_byte_data(client, RFD77402_IER, ier);
274+
}
275+
276+
static int rfd77402_init(struct rfd77402_data *data)
277+
{
278+
struct i2c_client *client = data->client;
209279
int ret, i;
210280

211281
ret = rfd77402_set_state(client, RFD77402_CMD_STANDBY,
212282
RFD77402_STATUS_STANDBY);
213283
if (ret < 0)
214284
return ret;
215285

216-
/* configure INT pad as push-pull, active low */
217-
ret = i2c_smbus_write_byte_data(client, RFD77402_ICSR,
218-
RFD77402_ICSR_INT_MODE);
219-
if (ret < 0)
286+
if (data->irq_en) {
287+
/*
288+
* Enable interrupt mode:
289+
* - Configure ICSR for auto-clear on read and
290+
* push-pull output
291+
* - Enable "result ready" interrupt in IER
292+
*/
293+
ret = rfd77402_config_irq(client,
294+
RFD77402_ICSR_CLR_CFG |
295+
RFD77402_ICSR_INT_MODE,
296+
RFD77402_IER_RESULT);
297+
} else {
298+
/*
299+
* Disable all interrupts:
300+
* - Clear ICSR configuration
301+
* - Disable all interrupts in IER
302+
*/
303+
ret = rfd77402_config_irq(client, 0, 0);
304+
}
305+
if (ret)
220306
return ret;
221307

222308
/* I2C configuration */
@@ -296,13 +382,29 @@ static int rfd77402_probe(struct i2c_client *client)
296382
if (ret)
297383
return ret;
298384

385+
init_completion(&data->completion);
386+
387+
if (client->irq > 0) {
388+
ret = devm_request_threaded_irq(&client->dev, client->irq,
389+
NULL, rfd77402_interrupt_handler,
390+
IRQF_ONESHOT,
391+
"rfd77402", data);
392+
if (ret)
393+
return ret;
394+
395+
data->irq_en = true;
396+
dev_dbg(&client->dev, "Using interrupt mode\n");
397+
} else {
398+
dev_dbg(&client->dev, "Using polling mode\n");
399+
}
400+
299401
indio_dev->info = &rfd77402_info;
300402
indio_dev->channels = rfd77402_channels;
301403
indio_dev->num_channels = ARRAY_SIZE(rfd77402_channels);
302404
indio_dev->name = RFD77402_DRV_NAME;
303405
indio_dev->modes = INDIO_DIRECT_MODE;
304406

305-
ret = rfd77402_init(client);
407+
ret = rfd77402_init(data);
306408
if (ret < 0)
307409
return ret;
308410

@@ -320,7 +422,10 @@ static int rfd77402_suspend(struct device *dev)
320422

321423
static int rfd77402_resume(struct device *dev)
322424
{
323-
return rfd77402_init(to_i2c_client(dev));
425+
struct iio_dev *indio_dev = dev_get_drvdata(dev);
426+
struct rfd77402_data *data = iio_priv(indio_dev);
427+
428+
return rfd77402_init(data);
324429
}
325430

326431
static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend,

0 commit comments

Comments
 (0)