// SPDX-License-Identifier: GPL-2.0
/*
* AWINIC aw96103 proximity sensor driver
*
* Author : Wang Shuaijie < wangshuaijie @ awinic . com >
*
* Copyright ( c ) 2024 awinic Technology CO . , LTD
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/unaligned.h>
#define AW_DATA_PROCESS_FACTOR 1024
#define AW96103_CHIP_ID 0 xa961
#define AW96103_BIN_VALID_DATA_OFFSET 64
#define AW96103_BIN_DATA_LEN_OFFSET 16
#define AW96103_BIN_DATA_REG_NUM_SIZE 4
#define AW96103_BIN_CHIP_TYPE_SIZE 8
#define AW96103_BIN_CHIP_TYPE_OFFSET 24
#define AW96103_REG_SCANCTRL0 0 x0000
#define AW96103_REG_STAT0 0 x0090
#define AW96103_REG_BLFILT_CH0 0 x00A8
#define AW96103_REG_BLRSTRNG_CH0 0 x00B4
#define AW96103_REG_DIFF_CH0 0 x0240
#define AW96103_REG_FWVER2 0 x0410
#define AW96103_REG_CMD 0 xF008
#define AW96103_REG_IRQSRC 0 xF080
#define AW96103_REG_IRQEN 0 xF084
#define AW96103_REG_RESET 0 xFF0C
#define AW96103_REG_CHIPID 0 xFF10
#define AW96103_REG_EEDA0 0 x0408
#define AW96103_REG_EEDA1 0 x040C
#define AW96103_REG_PROXCTRL_CH0 0 x00B0
#define AW96103_REG_PROXTH0_CH0 0 x00B8
#define AW96103_PROXTH_CH_STEP 0 x3C
#define AW96103_THHYST_MASK GENMASK(13 , 12 )
#define AW96103_INDEB_MASK GENMASK(11 , 10 )
#define AW96103_OUTDEB_MASK GENMASK(9 , 8 )
#define AW96103_INITOVERIRQ_MASK BIT(0 )
#define AW96103_BLFILT_CH_STEP 0 x3C
#define AW96103_BLRSTRNG_MASK GENMASK(5 , 0 )
#define AW96103_CHIPID_MASK GENMASK(31 , 16 )
#define AW96103_BLERRTRIG_MASK BIT(25 )
#define AW96103_CHAN_EN_MASK GENMASK(5 , 0 )
#define AW96103_REG_PROXCTRL_CH(x) \
(AW96103_REG_PROXCTRL_CH0 + (x) * AW96103_PROXTH_CH_STEP)
#define AW96103_REG_PROXTH0_CH(x) \
(AW96103_REG_PROXTH0_CH0 + (x) * AW96103_PROXTH_CH_STEP)
/**
* struct aw_bin - Store the data obtained from parsing the configuration file .
* @ chip_type : Frame header information - chip type
* @ valid_data_len : Length of valid data obtained after parsing
* @ valid_data_addr : The offset address of the valid data obtained
* after parsing relative to info
* @ len : The size of the bin file obtained from the firmware
* @ data : Store the bin file obtained from the firmware
*/
struct aw_bin {
unsigned char chip_type[8 ];
unsigned int valid_data_len;
unsigned int valid_data_addr;
unsigned int len;
unsigned char data[] __counted_by(len);
};
enum aw96103_sar_vers {
AW96103 = 2 ,
AW96103A = 6 ,
AW96103B = 0 xa,
};
enum aw96103_operation_mode {
AW96103_ACTIVE_MODE = 1 ,
AW96103_SLEEP_MODE = 2 ,
AW96103_DEEPSLEEP_MODE = 3 ,
AW96103B_DEEPSLEEP_MODE = 4 ,
};
enum aw96103_sensor_type {
AW96103_VAL,
AW96105_VAL,
};
struct aw_channels_info {
bool used;
unsigned int old_irq_status;
};
struct aw_chip_info {
const char *name;
struct iio_chan_spec const *channels;
int num_channels;
};
struct aw96103 {
unsigned int hostirqen;
struct regmap *regmap;
struct device *dev;
/*
* There is one more logical channel than the actual channels ,
* and the extra logical channel is used for temperature detection
* but not for status detection . The specific channel used for
* temperature detection is determined by the register configuration .
*/
struct aw_channels_info channels_arr[6 ];
unsigned int max_channels;
unsigned int chan_en;
};
static const unsigned int aw96103_reg_default[] = {
0 x0000, 0 x00003f3f, 0 x0004, 0 x00000064, 0 x0008, 0 x0017c11e,
0 x000c, 0 x05000000, 0 x0010, 0 x00093ffd, 0 x0014, 0 x19240009,
0 x0018, 0 xd81c0207, 0 x001c, 0 xff000000, 0 x0020, 0 x00241900,
0 x0024, 0 x00093ff7, 0 x0028, 0 x58020009, 0 x002c, 0 xd81c0207,
0 x0030, 0 xff000000, 0 x0034, 0 x00025800, 0 x0038, 0 x00093fdf,
0 x003c, 0 x7d3b0009, 0 x0040, 0 xd81c0207, 0 x0044, 0 xff000000,
0 x0048, 0 x003b7d00, 0 x004c, 0 x00093f7f, 0 x0050, 0 xe9310009,
0 x0054, 0 xd81c0207, 0 x0058, 0 xff000000, 0 x005c, 0 x0031e900,
0 x0060, 0 x00093dff, 0 x0064, 0 x1a0c0009, 0 x0068, 0 xd81c0207,
0 x006c, 0 xff000000, 0 x0070, 0 x000c1a00, 0 x0074, 0 x80093fff,
0 x0078, 0 x043d0009, 0 x007c, 0 xd81c0207, 0 x0080, 0 xff000000,
0 x0084, 0 x003d0400, 0 x00a0, 0 xe6400000, 0 x00a4, 0 x00000000,
0 x00a8, 0 x010408d2, 0 x00ac, 0 x00000000, 0 x00b0, 0 x00000000,
0 x00b8, 0 x00005fff, 0 x00bc, 0 x00000000, 0 x00c0, 0 x00000000,
0 x00c4, 0 x00000000, 0 x00c8, 0 x00000000, 0 x00cc, 0 x00000000,
0 x00d0, 0 x00000000, 0 x00d4, 0 x00000000, 0 x00d8, 0 x00000000,
0 x00dc, 0 xe6447800, 0 x00e0, 0 x78000000, 0 x00e4, 0 x010408d2,
0 x00e8, 0 x00000000, 0 x00ec, 0 x00000000, 0 x00f4, 0 x00005fff,
0 x00f8, 0 x00000000, 0 x00fc, 0 x00000000, 0 x0100, 0 x00000000,
0 x0104, 0 x00000000, 0 x0108, 0 x00000000, 0 x010c, 0 x02000000,
0 x0110, 0 x00000000, 0 x0114, 0 x00000000, 0 x0118, 0 xe6447800,
0 x011c, 0 x78000000, 0 x0120, 0 x010408d2, 0 x0124, 0 x00000000,
0 x0128, 0 x00000000, 0 x0130, 0 x00005fff, 0 x0134, 0 x00000000,
0 x0138, 0 x00000000, 0 x013c, 0 x00000000, 0 x0140, 0 x00000000,
0 x0144, 0 x00000000, 0 x0148, 0 x02000000, 0 x014c, 0 x00000000,
0 x0150, 0 x00000000, 0 x0154, 0 xe6447800, 0 x0158, 0 x78000000,
0 x015c, 0 x010408d2, 0 x0160, 0 x00000000, 0 x0164, 0 x00000000,
0 x016c, 0 x00005fff, 0 x0170, 0 x00000000, 0 x0174, 0 x00000000,
0 x0178, 0 x00000000, 0 x017c, 0 x00000000, 0 x0180, 0 x00000000,
0 x0184, 0 x02000000, 0 x0188, 0 x00000000, 0 x018c, 0 x00000000,
0 x0190, 0 xe6447800, 0 x0194, 0 x78000000, 0 x0198, 0 x010408d2,
0 x019c, 0 x00000000, 0 x01a0, 0 x00000000, 0 x01a8, 0 x00005fff,
0 x01ac, 0 x00000000, 0 x01b0, 0 x00000000, 0 x01b4, 0 x00000000,
0 x01b8, 0 x00000000, 0 x01bc, 0 x00000000, 0 x01c0, 0 x02000000,
0 x01c4, 0 x00000000, 0 x01c8, 0 x00000000, 0 x01cc, 0 xe6407800,
0 x01d0, 0 x78000000, 0 x01d4, 0 x010408d2, 0 x01d8, 0 x00000000,
0 x01dc, 0 x00000000, 0 x01e4, 0 x00005fff, 0 x01e8, 0 x00000000,
0 x01ec, 0 x00000000, 0 x01f0, 0 x00000000, 0 x01f4, 0 x00000000,
0 x01f8, 0 x00000000, 0 x01fc, 0 x02000000, 0 x0200, 0 x00000000,
0 x0204, 0 x00000000, 0 x0208, 0 x00000008, 0 x020c, 0 x0000000d,
0 x41fc, 0 x00000000, 0 x4400, 0 x00000000, 0 x4410, 0 x00000000,
0 x4420, 0 x00000000, 0 x4430, 0 x00000000, 0 x4440, 0 x00000000,
0 x4450, 0 x00000000, 0 x4460, 0 x00000000, 0 x4470, 0 x00000000,
0 xf080, 0 x00003018, 0 xf084, 0 x00000fff, 0 xf800, 0 x00000000,
0 xf804, 0 x00002e00, 0 xf8d0, 0 x00000001, 0 xf8d4, 0 x00000000,
0 xff00, 0 x00000301, 0 xff0c, 0 x01000000, 0 xffe0, 0 x00000000,
0 xfff4, 0 x00004011, 0 x0090, 0 x00000000, 0 x0094, 0 x00000000,
0 x0098, 0 x00000000, 0 x009c, 0 x3f3f3f3f,
};
static const struct iio_event_spec aw_common_events[3 ] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_PERIOD),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_PERIOD),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_HYSTERESIS) |
BIT(IIO_EV_INFO_VALUE),
}
};
#define AW_IIO_CHANNEL(idx) \
{ \
.type = IIO_PROXIMITY, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.indexed = 1 , \
.channel = idx, \
.event_spec = aw_common_events, \
.num_event_specs = ARRAY_SIZE(aw_common_events), \
} \
static const struct iio_chan_spec aw96103_channels[] = {
AW_IIO_CHANNEL(0 ),
AW_IIO_CHANNEL(1 ),
AW_IIO_CHANNEL(2 ),
AW_IIO_CHANNEL(3 ),
};
static const struct iio_chan_spec aw96105_channels[] = {
AW_IIO_CHANNEL(0 ),
AW_IIO_CHANNEL(1 ),
AW_IIO_CHANNEL(2 ),
AW_IIO_CHANNEL(3 ),
AW_IIO_CHANNEL(4 ),
AW_IIO_CHANNEL(5 ),
};
static const struct aw_chip_info aw_chip_info_tbl[] = {
[AW96103_VAL] = {
.name = "aw96103_sensor" ,
.channels = aw96103_channels,
.num_channels = ARRAY_SIZE(aw96103_channels),
},
[AW96105_VAL] = {
.name = "aw96105_sensor" ,
.channels = aw96105_channels,
.num_channels = ARRAY_SIZE(aw96105_channels),
},
};
static void aw96103_parsing_bin_file(struct aw_bin *bin)
{
bin->valid_data_addr = AW96103_BIN_VALID_DATA_OFFSET;
bin->valid_data_len =
*(unsigned int *)(bin->data + AW96103_BIN_DATA_LEN_OFFSET) -
AW96103_BIN_DATA_REG_NUM_SIZE;
memcpy(bin->chip_type, bin->data + AW96103_BIN_CHIP_TYPE_OFFSET,
AW96103_BIN_CHIP_TYPE_SIZE);
}
static const struct regmap_config aw96103_regmap_confg = {
.reg_bits = 16 ,
.val_bits = 32 ,
};
static int aw96103_get_diff_raw(struct aw96103 *aw96103, unsigned int chan,
int *buf)
{
u32 data;
int ret;
ret = regmap_read(aw96103->regmap,
AW96103_REG_DIFF_CH0 + chan * 4 , &data);
if (ret)
return ret;
*buf = (int )(data / AW_DATA_PROCESS_FACTOR);
return 0 ;
}
static int aw96103_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
{
struct aw96103 *aw96103 = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = aw96103_get_diff_raw(aw96103, chan->channel, val);
if (ret)
return ret;
return IIO_VAL_INT;
default :
return -EINVAL;
}
}
static int aw96103_read_thresh(struct aw96103 *aw96103,
const struct iio_chan_spec *chan, int *val)
{
int ret;
ret = regmap_read(aw96103->regmap,
AW96103_REG_PROXTH0_CH(chan->channel), val);
if (ret)
return ret;
return IIO_VAL_INT;
}
static int aw96103_read_out_debounce(struct aw96103 *aw96103,
const struct iio_chan_spec *chan,
int *val)
{
unsigned int reg_val;
int ret;
ret = regmap_read(aw96103->regmap,
AW96103_REG_PROXCTRL_CH(chan->channel), ®_val);
if (ret)
return ret;
*val = FIELD_GET(AW96103_OUTDEB_MASK, reg_val);
return IIO_VAL_INT;
}
static int aw96103_read_in_debounce(struct aw96103 *aw96103,
const struct iio_chan_spec *chan, int *val)
{
unsigned int reg_val;
int ret;
ret = regmap_read(aw96103->regmap,
AW96103_REG_PROXCTRL_CH(chan->channel), ®_val);
if (ret)
return ret;
*val = FIELD_GET(AW96103_INDEB_MASK, reg_val);
return IIO_VAL_INT;
}
static int aw96103_read_hysteresis(struct aw96103 *aw96103,
const struct iio_chan_spec *chan, int *val)
{
unsigned int reg_val;
int ret;
ret = regmap_read(aw96103->regmap,
AW96103_REG_PROXCTRL_CH(chan->channel), ®_val);
if (ret)
return ret;
*val = FIELD_GET(AW96103_THHYST_MASK, reg_val);
return IIO_VAL_INT;
}
static int aw96103_read_event_val(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
struct aw96103 *aw96103 = iio_priv(indio_dev);
if (chan->type != IIO_PROXIMITY)
return -EINVAL;
switch (info) {
case IIO_EV_INFO_VALUE:
return aw96103_read_thresh(aw96103, chan, val);
case IIO_EV_INFO_PERIOD:
switch (dir) {
case IIO_EV_DIR_RISING:
return aw96103_read_out_debounce(aw96103, chan, val);
case IIO_EV_DIR_FALLING:
return aw96103_read_in_debounce(aw96103, chan, val);
default :
return -EINVAL;
}
case IIO_EV_INFO_HYSTERESIS:
return aw96103_read_hysteresis(aw96103, chan, val);
default :
return -EINVAL;
}
}
static int aw96103_write_event_val(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info, int val, int val2)
{
struct aw96103 *aw96103 = iio_priv(indio_dev);
if (chan->type != IIO_PROXIMITY)
return -EINVAL;
switch (info) {
case IIO_EV_INFO_VALUE:
return regmap_write(aw96103->regmap,
AW96103_REG_PROXTH0_CH(chan->channel), val);
case IIO_EV_INFO_PERIOD:
switch (dir) {
case IIO_EV_DIR_RISING:
return regmap_update_bits(aw96103->regmap,
AW96103_REG_PROXCTRL_CH(chan->channel),
AW96103_OUTDEB_MASK,
FIELD_PREP(AW96103_OUTDEB_MASK, val));
case IIO_EV_DIR_FALLING:
return regmap_update_bits(aw96103->regmap,
AW96103_REG_PROXCTRL_CH(chan->channel),
AW96103_INDEB_MASK,
FIELD_PREP(AW96103_INDEB_MASK, val));
default :
return -EINVAL;
}
case IIO_EV_INFO_HYSTERESIS:
return regmap_update_bits(aw96103->regmap,
AW96103_REG_PROXCTRL_CH(chan->channel),
AW96103_THHYST_MASK,
FIELD_PREP(AW96103_THHYST_MASK, val));
default :
return -EINVAL;
}
}
static int aw96103_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct aw96103 *aw96103 = iio_priv(indio_dev);
return aw96103->channels_arr[chan->channel].used;
}
static int aw96103_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir, bool state)
{
struct aw96103 *aw96103 = iio_priv(indio_dev);
aw96103->channels_arr[chan->channel].used = !!state;
return regmap_update_bits(aw96103->regmap, AW96103_REG_SCANCTRL0,
BIT(chan->channel),
state ? BIT(chan->channel) : 0 );
}
static const struct iio_info iio_info = {
.read_raw = aw96103_read_raw,
.read_event_value = aw96103_read_event_val,
.write_event_value = aw96103_write_event_val,
.read_event_config = aw96103_read_event_config,
.write_event_config = aw96103_write_event_config,
};
static int aw96103_channel_scan_start(struct aw96103 *aw96103)
{
int ret;
ret = regmap_write(aw96103->regmap, AW96103_REG_CMD,
AW96103_ACTIVE_MODE);
if (ret)
return ret;
return regmap_write(aw96103->regmap, AW96103_REG_IRQEN,
aw96103->hostirqen);
}
static int aw96103_reg_version_comp(struct aw96103 *aw96103,
struct aw_bin *aw_bin)
{
u32 blfilt1_data, fw_ver;
unsigned char i;
int ret;
ret = regmap_read(aw96103->regmap, AW96103_REG_FWVER2, &fw_ver);
if (ret)
return ret;
/*
* If the chip version is AW96103A and the loaded register
* configuration file is for AW96103 , special handling of the
* AW96103_REG_BLRSTRNG_CH0 register is required .
*/
if ((fw_ver != AW96103A) || (aw_bin->chip_type[7 ] != '\0' ))
return 0 ;
for (i = 0 ; i < aw96103->max_channels; i++) {
ret = regmap_read(aw96103->regmap,
AW96103_REG_BLFILT_CH0 + (AW96103_BLFILT_CH_STEP * i),
&blfilt1_data);
if (ret)
return ret;
if (FIELD_GET(AW96103_BLERRTRIG_MASK, blfilt1_data) != 1 )
return 0 ;
ret = regmap_update_bits(aw96103->regmap,
AW96103_REG_BLRSTRNG_CH0 + (AW96103_BLFILT_CH_STEP * i),
AW96103_BLRSTRNG_MASK, 1 << i);
if (ret)
return ret;
}
return 0 ;
}
static int aw96103_bin_valid_loaded(struct aw96103 *aw96103,
struct aw_bin *aw_bin_data_s)
{
unsigned int start_addr = aw_bin_data_s->valid_data_addr;
u32 i, reg_data;
u16 reg_addr;
int ret;
for (i = 0 ; i < aw_bin_data_s->valid_data_len;
i += 6 , start_addr += 6 ) {
reg_addr = get_unaligned_le16(aw_bin_data_s->data + start_addr);
reg_data = get_unaligned_le32(aw_bin_data_s->data +
start_addr + 2 );
if ((reg_addr == AW96103_REG_EEDA0) ||
(reg_addr == AW96103_REG_EEDA1))
continue ;
if (reg_addr == AW96103_REG_IRQEN) {
aw96103->hostirqen = reg_data;
continue ;
}
if (reg_addr == AW96103_REG_SCANCTRL0)
aw96103->chan_en = FIELD_GET(AW96103_CHAN_EN_MASK,
reg_data);
ret = regmap_write(aw96103->regmap, reg_addr, reg_data);
if (ret < 0 )
return ret;
}
ret = aw96103_reg_version_comp(aw96103, aw_bin_data_s);
if (ret)
return ret;
return aw96103_channel_scan_start(aw96103);
}
static int aw96103_para_loaded(struct aw96103 *aw96103)
{
int i, ret;
for (i = 0 ; i < ARRAY_SIZE(aw96103_reg_default); i += 2 ) {
ret = regmap_write(aw96103->regmap,
(u16)aw96103_reg_default[i],
(u32)aw96103_reg_default[i + 1 ]);
if (ret)
return ret;
if (aw96103_reg_default[i] == AW96103_REG_IRQEN)
aw96103->hostirqen = aw96103_reg_default[i + 1 ];
else if (aw96103_reg_default[i] == AW96103_REG_SCANCTRL0)
aw96103->chan_en = FIELD_GET(AW96103_CHAN_EN_MASK,
aw96103_reg_default[i + 1 ]);
}
return aw96103_channel_scan_start(aw96103);
}
static int aw96103_cfg_all_loaded(const struct firmware *cont,
struct aw96103 *aw96103)
{
if (!cont)
return -EINVAL;
struct aw_bin *aw_bin __free(kfree) =
kzalloc(cont->size + sizeof (*aw_bin), GFP_KERNEL);
if (!aw_bin)
return -ENOMEM;
aw_bin->len = cont->size;
memcpy(aw_bin->data, cont->data, cont->size);
release_firmware(cont);
aw96103_parsing_bin_file(aw_bin);
return aw96103_bin_valid_loaded(aw96103, aw_bin);
}
static void aw96103_cfg_update(const struct firmware *fw, void *data)
{
struct aw96103 *aw96103 = data;
int ret, i;
if (!fw || !fw->data) {
dev_err(aw96103->dev, "No firmware.\n" );
return ;
}
ret = aw96103_cfg_all_loaded(fw, aw96103);
/*
* If loading the register configuration file fails ,
* load the default register configuration in the driver to
* ensure the basic functionality of the device .
*/
if (ret) {
ret = aw96103_para_loaded(aw96103);
if (ret) {
dev_err(aw96103->dev, "load param error.\n" );
return ;
}
}
for (i = 0 ; i < aw96103->max_channels; i++) {
if ((aw96103->chan_en >> i) & 0 x01)
aw96103->channels_arr[i].used = true ;
else
aw96103->channels_arr[i].used = false ;
}
}
static int aw96103_sw_reset(struct aw96103 *aw96103)
{
int ret;
ret = regmap_write(aw96103->regmap, AW96103_REG_RESET, 0 );
/*
* After reset , the initialization process starts to perform and
* it will last for a bout 20 ms .
*/
msleep(20 );
return ret;
}
enum aw96103_irq_trigger_position {
FAR = 0 ,
TRIGGER_TH0 = 0 x01,
TRIGGER_TH1 = 0 x03,
TRIGGER_TH2 = 0 x07,
TRIGGER_TH3 = 0 x0f,
};
static irqreturn_t aw96103_irq(int irq, void *data)
{
unsigned int irq_status, curr_status_val, curr_status;
struct iio_dev *indio_dev = data;
struct aw96103 *aw96103 = iio_priv(indio_dev);
int ret, i;
ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, &irq_status);
if (ret)
return IRQ_HANDLED;
ret = regmap_read(aw96103->regmap, AW96103_REG_STAT0, &curr_status_val);
if (ret)
return IRQ_HANDLED;
/*
* Iteratively analyze the interrupt status of different channels ,
* with each channel having 4 interrupt states .
*/
for (i = 0 ; i < aw96103->max_channels; i++) {
if (!aw96103->channels_arr[i].used)
continue ;
curr_status = (((curr_status_val >> (24 + i)) & 0 x1)) |
(((curr_status_val >> (16 + i)) & 0 x1) << 1 ) |
(((curr_status_val >> (8 + i)) & 0 x1) << 2 ) |
(((curr_status_val >> i) & 0 x1) << 3 );
if (aw96103->channels_arr[i].old_irq_status == curr_status)
continue ;
switch (curr_status) {
case FAR:
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, i,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
iio_get_time_ns(indio_dev));
break ;
case TRIGGER_TH0:
case TRIGGER_TH1:
case TRIGGER_TH2:
case TRIGGER_TH3:
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, i,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
iio_get_time_ns(indio_dev));
break ;
default :
return IRQ_HANDLED;
}
aw96103->channels_arr[i].old_irq_status = curr_status;
}
return IRQ_HANDLED;
}
static int aw96103_interrupt_init(struct iio_dev *indio_dev,
struct i2c_client *i2c)
{
struct aw96103 *aw96103 = iio_priv(indio_dev);
unsigned int irq_status;
int ret;
ret = regmap_write(aw96103->regmap, AW96103_REG_IRQEN, 0 );
if (ret)
return ret;
ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC, &irq_status);
if (ret)
return ret;
ret = devm_request_threaded_irq(aw96103->dev, i2c->irq, NULL,
aw96103_irq, IRQF_ONESHOT,
"aw96103_irq" , indio_dev);
if (ret)
return ret;
return regmap_write(aw96103->regmap, AW96103_REG_IRQEN,
aw96103->hostirqen);
}
static int aw96103_wait_chip_init(struct aw96103 *aw96103)
{
unsigned int cnt = 20 ;
u32 reg_data;
int ret;
while (cnt--) {
/*
* The device should generate an initialization completion
* interrupt within 20 ms .
*/
ret = regmap_read(aw96103->regmap, AW96103_REG_IRQSRC,
®_data);
if (ret)
return ret;
if (FIELD_GET(AW96103_INITOVERIRQ_MASK, reg_data))
return 0 ;
fsleep(1000 );
}
return -ETIMEDOUT;
}
static int aw96103_read_chipid(struct aw96103 *aw96103)
{
unsigned char cnt = 0 ;
u32 reg_val = 0 ;
int ret;
while (cnt < 3 ) {
/*
* This retry mechanism and the subsequent delay are just
* attempts to read the chip ID as much as possible ,
* preventing occasional communication failures from causing
* the chip ID read to fail .
*/
ret = regmap_read(aw96103->regmap, AW96103_REG_CHIPID,
®_val);
if (ret < 0 ) {
cnt++;
fsleep(2000 );
continue ;
}
break ;
}
if (cnt == 3 )
return -ETIMEDOUT;
if (FIELD_GET(AW96103_CHIPID_MASK, reg_val) != AW96103_CHIP_ID)
dev_info(aw96103->dev,
"unexpected chipid, id=0x%08X\n" , reg_val);
return 0 ;
}
static int aw96103_i2c_probe(struct i2c_client *i2c)
{
const struct aw_chip_info *chip_info;
struct iio_dev *indio_dev;
struct aw96103 *aw96103;
int ret;
indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof (*aw96103));
if (!indio_dev)
return -ENOMEM;
aw96103 = iio_priv(indio_dev);
aw96103->dev = &i2c->dev;
chip_info = i2c_get_match_data(i2c);
aw96103->max_channels = chip_info->num_channels;
aw96103->regmap = devm_regmap_init_i2c(i2c, &aw96103_regmap_confg);
if (IS_ERR(aw96103->regmap))
return PTR_ERR(aw96103->regmap);
ret = devm_regulator_get_enable(aw96103->dev, "vcc" );
if (ret < 0 )
return ret;
ret = aw96103_read_chipid(aw96103);
if (ret)
return ret;
ret = aw96103_sw_reset(aw96103);
if (ret)
return ret;
ret = aw96103_wait_chip_init(aw96103);
if (ret)
return ret;
ret = request_firmware_nowait(THIS_MODULE, true , "aw96103_0.bin" ,
aw96103->dev, GFP_KERNEL, aw96103,
aw96103_cfg_update);
if (ret)
return ret;
ret = aw96103_interrupt_init(indio_dev, i2c);
if (ret)
return ret;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = chip_info->num_channels;
indio_dev->channels = chip_info->channels;
indio_dev->info = &iio_info;
indio_dev->name = chip_info->name;
return devm_iio_device_register(aw96103->dev, indio_dev);
}
static const struct of_device_id aw96103_dt_match[] = {
{
.compatible = "awinic,aw96103" ,
.data = &aw_chip_info_tbl[AW96103_VAL]
},
{
.compatible = "awinic,aw96105" ,
.data = &aw_chip_info_tbl[AW96105_VAL]
},
{ }
};
MODULE_DEVICE_TABLE(of, aw96103_dt_match);
static const struct i2c_device_id aw96103_i2c_id[] = {
{ "aw96103" , (kernel_ulong_t)&aw_chip_info_tbl[AW96103_VAL] },
{ "aw96105" , (kernel_ulong_t)&aw_chip_info_tbl[AW96105_VAL] },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw96103_i2c_id);
static struct i2c_driver aw96103_i2c_driver = {
.driver = {
.name = "aw96103_sensor" ,
.of_match_table = aw96103_dt_match,
},
.probe = aw96103_i2c_probe,
.id_table = aw96103_i2c_id,
};
module_i2c_driver(aw96103_i2c_driver);
MODULE_AUTHOR("Wang Shuaijie <wangshuaijie@awinic.com>" );
MODULE_DESCRIPTION("Driver for Awinic AW96103 proximity sensor" );
MODULE_LICENSE("GPL v2" );
Messung V0.5 in Prozent C=99 H=97 G=97
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland