12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
272aa29ceSVladimir Barinov /*
372aa29ceSVladimir Barinov * Holt Integrated Circuits HI-8435 threshold detector driver
472aa29ceSVladimir Barinov *
572aa29ceSVladimir Barinov * Copyright (C) 2015 Zodiac Inflight Innovations
672aa29ceSVladimir Barinov * Copyright (C) 2015 Cogent Embedded, Inc.
772aa29ceSVladimir Barinov */
872aa29ceSVladimir Barinov
972aa29ceSVladimir Barinov #include <linux/delay.h>
1072aa29ceSVladimir Barinov #include <linux/iio/events.h>
1172aa29ceSVladimir Barinov #include <linux/iio/iio.h>
1272aa29ceSVladimir Barinov #include <linux/iio/sysfs.h>
1372aa29ceSVladimir Barinov #include <linux/iio/trigger.h>
1472aa29ceSVladimir Barinov #include <linux/iio/trigger_consumer.h>
1572aa29ceSVladimir Barinov #include <linux/iio/triggered_event.h>
1672aa29ceSVladimir Barinov #include <linux/interrupt.h>
1772aa29ceSVladimir Barinov #include <linux/module.h>
18f8e6ae5aSJonathan Cameron #include <linux/mod_devicetable.h>
1972aa29ceSVladimir Barinov #include <linux/spi/spi.h>
2072aa29ceSVladimir Barinov #include <linux/gpio/consumer.h>
2172aa29ceSVladimir Barinov
2272aa29ceSVladimir Barinov #define DRV_NAME "hi8435"
2372aa29ceSVladimir Barinov
2472aa29ceSVladimir Barinov /* Register offsets for HI-8435 */
2572aa29ceSVladimir Barinov #define HI8435_CTRL_REG 0x02
2672aa29ceSVladimir Barinov #define HI8435_PSEN_REG 0x04
2772aa29ceSVladimir Barinov #define HI8435_TMDATA_REG 0x1E
2872aa29ceSVladimir Barinov #define HI8435_GOCENHYS_REG 0x3A
2972aa29ceSVladimir Barinov #define HI8435_SOCENHYS_REG 0x3C
3072aa29ceSVladimir Barinov #define HI8435_SO7_0_REG 0x10
3172aa29ceSVladimir Barinov #define HI8435_SO15_8_REG 0x12
3272aa29ceSVladimir Barinov #define HI8435_SO23_16_REG 0x14
3372aa29ceSVladimir Barinov #define HI8435_SO31_24_REG 0x16
3472aa29ceSVladimir Barinov #define HI8435_SO31_0_REG 0x78
3572aa29ceSVladimir Barinov
3672aa29ceSVladimir Barinov #define HI8435_WRITE_OPCODE 0x00
3772aa29ceSVladimir Barinov #define HI8435_READ_OPCODE 0x80
3872aa29ceSVladimir Barinov
3972aa29ceSVladimir Barinov /* CTRL register bits */
4072aa29ceSVladimir Barinov #define HI8435_CTRL_TEST 0x01
4172aa29ceSVladimir Barinov #define HI8435_CTRL_SRST 0x02
4272aa29ceSVladimir Barinov
4372aa29ceSVladimir Barinov struct hi8435_priv {
4472aa29ceSVladimir Barinov struct spi_device *spi;
4572aa29ceSVladimir Barinov struct mutex lock;
4672aa29ceSVladimir Barinov
4772aa29ceSVladimir Barinov unsigned long event_scan_mask; /* soft mask/unmask channels events */
4872aa29ceSVladimir Barinov unsigned int event_prev_val;
4972aa29ceSVladimir Barinov
5072aa29ceSVladimir Barinov unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
5172aa29ceSVladimir Barinov unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */
5248e4ae96SJonathan Cameron u8 reg_buffer[3] __aligned(IIO_DMA_MINALIGN);
5372aa29ceSVladimir Barinov };
5472aa29ceSVladimir Barinov
hi8435_readb(struct hi8435_priv * priv,u8 reg,u8 * val)5572aa29ceSVladimir Barinov static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
5672aa29ceSVladimir Barinov {
5772aa29ceSVladimir Barinov reg |= HI8435_READ_OPCODE;
5872aa29ceSVladimir Barinov return spi_write_then_read(priv->spi, ®, 1, val, 1);
5972aa29ceSVladimir Barinov }
6072aa29ceSVladimir Barinov
hi8435_readw(struct hi8435_priv * priv,u8 reg,u16 * val)6172aa29ceSVladimir Barinov static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
6272aa29ceSVladimir Barinov {
6372aa29ceSVladimir Barinov int ret;
6472aa29ceSVladimir Barinov __be16 be_val;
6572aa29ceSVladimir Barinov
6672aa29ceSVladimir Barinov reg |= HI8435_READ_OPCODE;
6772aa29ceSVladimir Barinov ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 2);
6872aa29ceSVladimir Barinov *val = be16_to_cpu(be_val);
6972aa29ceSVladimir Barinov
7072aa29ceSVladimir Barinov return ret;
7172aa29ceSVladimir Barinov }
7272aa29ceSVladimir Barinov
hi8435_readl(struct hi8435_priv * priv,u8 reg,u32 * val)7372aa29ceSVladimir Barinov static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
7472aa29ceSVladimir Barinov {
7572aa29ceSVladimir Barinov int ret;
7672aa29ceSVladimir Barinov __be32 be_val;
7772aa29ceSVladimir Barinov
7872aa29ceSVladimir Barinov reg |= HI8435_READ_OPCODE;
7972aa29ceSVladimir Barinov ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 4);
8072aa29ceSVladimir Barinov *val = be32_to_cpu(be_val);
8172aa29ceSVladimir Barinov
8272aa29ceSVladimir Barinov return ret;
8372aa29ceSVladimir Barinov }
8472aa29ceSVladimir Barinov
hi8435_writeb(struct hi8435_priv * priv,u8 reg,u8 val)8572aa29ceSVladimir Barinov static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
8672aa29ceSVladimir Barinov {
8772aa29ceSVladimir Barinov priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
8872aa29ceSVladimir Barinov priv->reg_buffer[1] = val;
8972aa29ceSVladimir Barinov
9072aa29ceSVladimir Barinov return spi_write(priv->spi, priv->reg_buffer, 2);
9172aa29ceSVladimir Barinov }
9272aa29ceSVladimir Barinov
hi8435_writew(struct hi8435_priv * priv,u8 reg,u16 val)9372aa29ceSVladimir Barinov static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
9472aa29ceSVladimir Barinov {
9572aa29ceSVladimir Barinov priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
9672aa29ceSVladimir Barinov priv->reg_buffer[1] = (val >> 8) & 0xff;
9772aa29ceSVladimir Barinov priv->reg_buffer[2] = val & 0xff;
9872aa29ceSVladimir Barinov
9972aa29ceSVladimir Barinov return spi_write(priv->spi, priv->reg_buffer, 3);
10072aa29ceSVladimir Barinov }
10172aa29ceSVladimir Barinov
hi8435_read_raw(struct iio_dev * idev,const struct iio_chan_spec * chan,int * val,int * val2,long mask)102fa7662e1SNikita Yushchenko static int hi8435_read_raw(struct iio_dev *idev,
103fa7662e1SNikita Yushchenko const struct iio_chan_spec *chan,
104fa7662e1SNikita Yushchenko int *val, int *val2, long mask)
105fa7662e1SNikita Yushchenko {
106fa7662e1SNikita Yushchenko struct hi8435_priv *priv = iio_priv(idev);
107fa7662e1SNikita Yushchenko u32 tmp;
108fa7662e1SNikita Yushchenko int ret;
109fa7662e1SNikita Yushchenko
110fa7662e1SNikita Yushchenko switch (mask) {
111fa7662e1SNikita Yushchenko case IIO_CHAN_INFO_RAW:
112fa7662e1SNikita Yushchenko ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp);
113fa7662e1SNikita Yushchenko if (ret < 0)
114fa7662e1SNikita Yushchenko return ret;
115fa7662e1SNikita Yushchenko *val = !!(tmp & BIT(chan->channel));
116fa7662e1SNikita Yushchenko return IIO_VAL_INT;
117fa7662e1SNikita Yushchenko default:
118fa7662e1SNikita Yushchenko return -EINVAL;
119fa7662e1SNikita Yushchenko }
120fa7662e1SNikita Yushchenko }
121fa7662e1SNikita Yushchenko
hi8435_read_event_config(struct iio_dev * idev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir)12272aa29ceSVladimir Barinov static int hi8435_read_event_config(struct iio_dev *idev,
12372aa29ceSVladimir Barinov const struct iio_chan_spec *chan,
12472aa29ceSVladimir Barinov enum iio_event_type type,
12572aa29ceSVladimir Barinov enum iio_event_direction dir)
12672aa29ceSVladimir Barinov {
12772aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
12872aa29ceSVladimir Barinov
12972aa29ceSVladimir Barinov return !!(priv->event_scan_mask & BIT(chan->channel));
13072aa29ceSVladimir Barinov }
13172aa29ceSVladimir Barinov
hi8435_write_event_config(struct iio_dev * idev,const struct iio_chan_spec * chan,enum iio_event_type type,enum iio_event_direction dir,int state)13272aa29ceSVladimir Barinov static int hi8435_write_event_config(struct iio_dev *idev,
13372aa29ceSVladimir Barinov const struct iio_chan_spec *chan,
13472aa29ceSVladimir Barinov enum iio_event_type type,
13572aa29ceSVladimir Barinov enum iio_event_direction dir, int state)
13672aa29ceSVladimir Barinov {
13772aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
138ee19ac34SNikita Yushchenko int ret;
139ee19ac34SNikita Yushchenko u32 tmp;
14072aa29ceSVladimir Barinov
141ee19ac34SNikita Yushchenko if (state) {
142ee19ac34SNikita Yushchenko ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp);
143ee19ac34SNikita Yushchenko if (ret < 0)
144ee19ac34SNikita Yushchenko return ret;
145ee19ac34SNikita Yushchenko if (tmp & BIT(chan->channel))
146ee19ac34SNikita Yushchenko priv->event_prev_val |= BIT(chan->channel);
147ee19ac34SNikita Yushchenko else
148ee19ac34SNikita Yushchenko priv->event_prev_val &= ~BIT(chan->channel);
149ee19ac34SNikita Yushchenko
15072aa29ceSVladimir Barinov priv->event_scan_mask |= BIT(chan->channel);
151ee19ac34SNikita Yushchenko } else
152ee19ac34SNikita Yushchenko priv->event_scan_mask &= ~BIT(chan->channel);
15372aa29ceSVladimir Barinov
15472aa29ceSVladimir Barinov return 0;
15572aa29ceSVladimir Barinov }
15672aa29ceSVladimir Barinov
hi8435_read_event_value(struct iio_dev * idev,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)15772aa29ceSVladimir Barinov static int hi8435_read_event_value(struct iio_dev *idev,
15872aa29ceSVladimir Barinov const struct iio_chan_spec *chan,
15972aa29ceSVladimir Barinov enum iio_event_type type,
16072aa29ceSVladimir Barinov enum iio_event_direction dir,
16172aa29ceSVladimir Barinov enum iio_event_info info,
16272aa29ceSVladimir Barinov int *val, int *val2)
16372aa29ceSVladimir Barinov {
16472aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
16572aa29ceSVladimir Barinov int ret;
16672aa29ceSVladimir Barinov u8 mode, psen;
16772aa29ceSVladimir Barinov u16 reg;
16872aa29ceSVladimir Barinov
16972aa29ceSVladimir Barinov ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
17072aa29ceSVladimir Barinov if (ret < 0)
17172aa29ceSVladimir Barinov return ret;
17272aa29ceSVladimir Barinov
17372aa29ceSVladimir Barinov /* Supply-Open or GND-Open sensing mode */
17472aa29ceSVladimir Barinov mode = !!(psen & BIT(chan->channel / 8));
17572aa29ceSVladimir Barinov
17672aa29ceSVladimir Barinov ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
17772aa29ceSVladimir Barinov HI8435_GOCENHYS_REG, ®);
17872aa29ceSVladimir Barinov if (ret < 0)
17972aa29ceSVladimir Barinov return ret;
18072aa29ceSVladimir Barinov
18172aa29ceSVladimir Barinov if (dir == IIO_EV_DIR_FALLING)
18272aa29ceSVladimir Barinov *val = ((reg & 0xff) - (reg >> 8)) / 2;
18372aa29ceSVladimir Barinov else if (dir == IIO_EV_DIR_RISING)
18472aa29ceSVladimir Barinov *val = ((reg & 0xff) + (reg >> 8)) / 2;
18572aa29ceSVladimir Barinov
18672aa29ceSVladimir Barinov return IIO_VAL_INT;
18772aa29ceSVladimir Barinov }
18872aa29ceSVladimir Barinov
hi8435_write_event_value(struct iio_dev * idev,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)18972aa29ceSVladimir Barinov static int hi8435_write_event_value(struct iio_dev *idev,
19072aa29ceSVladimir Barinov const struct iio_chan_spec *chan,
19172aa29ceSVladimir Barinov enum iio_event_type type,
19272aa29ceSVladimir Barinov enum iio_event_direction dir,
19372aa29ceSVladimir Barinov enum iio_event_info info,
19472aa29ceSVladimir Barinov int val, int val2)
19572aa29ceSVladimir Barinov {
19672aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
19772aa29ceSVladimir Barinov int ret;
19872aa29ceSVladimir Barinov u8 mode, psen;
19972aa29ceSVladimir Barinov u16 reg;
20072aa29ceSVladimir Barinov
20172aa29ceSVladimir Barinov ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
20272aa29ceSVladimir Barinov if (ret < 0)
20372aa29ceSVladimir Barinov return ret;
20472aa29ceSVladimir Barinov
20572aa29ceSVladimir Barinov /* Supply-Open or GND-Open sensing mode */
20672aa29ceSVladimir Barinov mode = !!(psen & BIT(chan->channel / 8));
20772aa29ceSVladimir Barinov
20872aa29ceSVladimir Barinov ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
20972aa29ceSVladimir Barinov HI8435_GOCENHYS_REG, ®);
21072aa29ceSVladimir Barinov if (ret < 0)
21172aa29ceSVladimir Barinov return ret;
21272aa29ceSVladimir Barinov
21372aa29ceSVladimir Barinov if (dir == IIO_EV_DIR_FALLING) {
21472aa29ceSVladimir Barinov /* falling threshold range 2..21V, hysteresis minimum 2V */
21572aa29ceSVladimir Barinov if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode])
21672aa29ceSVladimir Barinov return -EINVAL;
21772aa29ceSVladimir Barinov
21872aa29ceSVladimir Barinov if (val == priv->threshold_lo[mode])
21972aa29ceSVladimir Barinov return 0;
22072aa29ceSVladimir Barinov
22172aa29ceSVladimir Barinov priv->threshold_lo[mode] = val;
22272aa29ceSVladimir Barinov
22372aa29ceSVladimir Barinov /* hysteresis must not be odd */
22472aa29ceSVladimir Barinov if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
22572aa29ceSVladimir Barinov priv->threshold_hi[mode]--;
22672aa29ceSVladimir Barinov } else if (dir == IIO_EV_DIR_RISING) {
22772aa29ceSVladimir Barinov /* rising threshold range 3..22V, hysteresis minimum 2V */
22872aa29ceSVladimir Barinov if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2))
22972aa29ceSVladimir Barinov return -EINVAL;
23072aa29ceSVladimir Barinov
23172aa29ceSVladimir Barinov if (val == priv->threshold_hi[mode])
23272aa29ceSVladimir Barinov return 0;
23372aa29ceSVladimir Barinov
23472aa29ceSVladimir Barinov priv->threshold_hi[mode] = val;
23572aa29ceSVladimir Barinov
23672aa29ceSVladimir Barinov /* hysteresis must not be odd */
23772aa29ceSVladimir Barinov if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
23872aa29ceSVladimir Barinov priv->threshold_lo[mode]++;
23972aa29ceSVladimir Barinov }
24072aa29ceSVladimir Barinov
24172aa29ceSVladimir Barinov /* program thresholds */
24272aa29ceSVladimir Barinov mutex_lock(&priv->lock);
24372aa29ceSVladimir Barinov
24472aa29ceSVladimir Barinov ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
24572aa29ceSVladimir Barinov HI8435_GOCENHYS_REG, ®);
24672aa29ceSVladimir Barinov if (ret < 0) {
24772aa29ceSVladimir Barinov mutex_unlock(&priv->lock);
24872aa29ceSVladimir Barinov return ret;
24972aa29ceSVladimir Barinov }
25072aa29ceSVladimir Barinov
25172aa29ceSVladimir Barinov /* hysteresis */
25272aa29ceSVladimir Barinov reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
25372aa29ceSVladimir Barinov reg <<= 8;
25472aa29ceSVladimir Barinov /* threshold center */
25572aa29ceSVladimir Barinov reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
25672aa29ceSVladimir Barinov
25772aa29ceSVladimir Barinov ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
25872aa29ceSVladimir Barinov HI8435_GOCENHYS_REG, reg);
25972aa29ceSVladimir Barinov
26072aa29ceSVladimir Barinov mutex_unlock(&priv->lock);
26172aa29ceSVladimir Barinov
26272aa29ceSVladimir Barinov return ret;
26372aa29ceSVladimir Barinov }
26472aa29ceSVladimir Barinov
hi8435_debugfs_reg_access(struct iio_dev * idev,unsigned reg,unsigned writeval,unsigned * readval)26572aa29ceSVladimir Barinov static int hi8435_debugfs_reg_access(struct iio_dev *idev,
26672aa29ceSVladimir Barinov unsigned reg, unsigned writeval,
26772aa29ceSVladimir Barinov unsigned *readval)
26872aa29ceSVladimir Barinov {
26972aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
27072aa29ceSVladimir Barinov int ret;
27172aa29ceSVladimir Barinov u8 val;
27272aa29ceSVladimir Barinov
27372aa29ceSVladimir Barinov if (readval != NULL) {
27472aa29ceSVladimir Barinov ret = hi8435_readb(priv, reg, &val);
27572aa29ceSVladimir Barinov *readval = val;
27672aa29ceSVladimir Barinov } else {
27772aa29ceSVladimir Barinov val = (u8)writeval;
27872aa29ceSVladimir Barinov ret = hi8435_writeb(priv, reg, val);
27972aa29ceSVladimir Barinov }
28072aa29ceSVladimir Barinov
28172aa29ceSVladimir Barinov return ret;
28272aa29ceSVladimir Barinov }
28372aa29ceSVladimir Barinov
28472aa29ceSVladimir Barinov static const struct iio_event_spec hi8435_events[] = {
28572aa29ceSVladimir Barinov {
28672aa29ceSVladimir Barinov .type = IIO_EV_TYPE_THRESH,
28772aa29ceSVladimir Barinov .dir = IIO_EV_DIR_RISING,
28872aa29ceSVladimir Barinov .mask_separate = BIT(IIO_EV_INFO_VALUE),
28972aa29ceSVladimir Barinov }, {
29072aa29ceSVladimir Barinov .type = IIO_EV_TYPE_THRESH,
29172aa29ceSVladimir Barinov .dir = IIO_EV_DIR_FALLING,
29272aa29ceSVladimir Barinov .mask_separate = BIT(IIO_EV_INFO_VALUE),
29372aa29ceSVladimir Barinov }, {
29472aa29ceSVladimir Barinov .type = IIO_EV_TYPE_THRESH,
29572aa29ceSVladimir Barinov .dir = IIO_EV_DIR_EITHER,
29672aa29ceSVladimir Barinov .mask_separate = BIT(IIO_EV_INFO_ENABLE),
29772aa29ceSVladimir Barinov },
29872aa29ceSVladimir Barinov };
29972aa29ceSVladimir Barinov
hi8435_get_sensing_mode(struct iio_dev * idev,const struct iio_chan_spec * chan)30072aa29ceSVladimir Barinov static int hi8435_get_sensing_mode(struct iio_dev *idev,
30172aa29ceSVladimir Barinov const struct iio_chan_spec *chan)
30272aa29ceSVladimir Barinov {
30372aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
30472aa29ceSVladimir Barinov int ret;
30572aa29ceSVladimir Barinov u8 reg;
30672aa29ceSVladimir Barinov
30772aa29ceSVladimir Barinov ret = hi8435_readb(priv, HI8435_PSEN_REG, ®);
30872aa29ceSVladimir Barinov if (ret < 0)
30972aa29ceSVladimir Barinov return ret;
31072aa29ceSVladimir Barinov
31172aa29ceSVladimir Barinov return !!(reg & BIT(chan->channel / 8));
31272aa29ceSVladimir Barinov }
31372aa29ceSVladimir Barinov
hi8435_set_sensing_mode(struct iio_dev * idev,const struct iio_chan_spec * chan,unsigned int mode)31472aa29ceSVladimir Barinov static int hi8435_set_sensing_mode(struct iio_dev *idev,
31572aa29ceSVladimir Barinov const struct iio_chan_spec *chan,
31672aa29ceSVladimir Barinov unsigned int mode)
31772aa29ceSVladimir Barinov {
31872aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
31972aa29ceSVladimir Barinov int ret;
32072aa29ceSVladimir Barinov u8 reg;
32172aa29ceSVladimir Barinov
32272aa29ceSVladimir Barinov mutex_lock(&priv->lock);
32372aa29ceSVladimir Barinov
32472aa29ceSVladimir Barinov ret = hi8435_readb(priv, HI8435_PSEN_REG, ®);
32572aa29ceSVladimir Barinov if (ret < 0) {
32672aa29ceSVladimir Barinov mutex_unlock(&priv->lock);
32772aa29ceSVladimir Barinov return ret;
32872aa29ceSVladimir Barinov }
32972aa29ceSVladimir Barinov
33072aa29ceSVladimir Barinov reg &= ~BIT(chan->channel / 8);
33172aa29ceSVladimir Barinov if (mode)
33272aa29ceSVladimir Barinov reg |= BIT(chan->channel / 8);
33372aa29ceSVladimir Barinov
33472aa29ceSVladimir Barinov ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
33572aa29ceSVladimir Barinov
33672aa29ceSVladimir Barinov mutex_unlock(&priv->lock);
33772aa29ceSVladimir Barinov
33872aa29ceSVladimir Barinov return ret;
33972aa29ceSVladimir Barinov }
34072aa29ceSVladimir Barinov
34172aa29ceSVladimir Barinov static const char * const hi8435_sensing_modes[] = { "GND-Open",
34272aa29ceSVladimir Barinov "Supply-Open" };
34372aa29ceSVladimir Barinov
34472aa29ceSVladimir Barinov static const struct iio_enum hi8435_sensing_mode = {
34572aa29ceSVladimir Barinov .items = hi8435_sensing_modes,
34672aa29ceSVladimir Barinov .num_items = ARRAY_SIZE(hi8435_sensing_modes),
34772aa29ceSVladimir Barinov .get = hi8435_get_sensing_mode,
34872aa29ceSVladimir Barinov .set = hi8435_set_sensing_mode,
34972aa29ceSVladimir Barinov };
35072aa29ceSVladimir Barinov
35172aa29ceSVladimir Barinov static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
35272aa29ceSVladimir Barinov IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
353ffc7c517SAntoniu Miclaus IIO_ENUM_AVAILABLE("sensing_mode", IIO_SHARED_BY_TYPE, &hi8435_sensing_mode),
35472aa29ceSVladimir Barinov {},
35572aa29ceSVladimir Barinov };
35672aa29ceSVladimir Barinov
35772aa29ceSVladimir Barinov #define HI8435_VOLTAGE_CHANNEL(num) \
35872aa29ceSVladimir Barinov { \
35972aa29ceSVladimir Barinov .type = IIO_VOLTAGE, \
36072aa29ceSVladimir Barinov .indexed = 1, \
36172aa29ceSVladimir Barinov .channel = num, \
362fa7662e1SNikita Yushchenko .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
36372aa29ceSVladimir Barinov .event_spec = hi8435_events, \
36472aa29ceSVladimir Barinov .num_event_specs = ARRAY_SIZE(hi8435_events), \
36572aa29ceSVladimir Barinov .ext_info = hi8435_ext_info, \
36672aa29ceSVladimir Barinov }
36772aa29ceSVladimir Barinov
36872aa29ceSVladimir Barinov static const struct iio_chan_spec hi8435_channels[] = {
36972aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(0),
37072aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(1),
37172aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(2),
37272aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(3),
37372aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(4),
37472aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(5),
37572aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(6),
37672aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(7),
37772aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(8),
37872aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(9),
37972aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(10),
38072aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(11),
38172aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(12),
38272aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(13),
38372aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(14),
38472aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(15),
38572aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(16),
38672aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(17),
38772aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(18),
38872aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(19),
38972aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(20),
39072aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(21),
39172aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(22),
39272aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(23),
39372aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(24),
39472aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(25),
39572aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(26),
39672aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(27),
39772aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(28),
39872aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(29),
39972aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(30),
40072aa29ceSVladimir Barinov HI8435_VOLTAGE_CHANNEL(31),
40172aa29ceSVladimir Barinov IIO_CHAN_SOFT_TIMESTAMP(32),
40272aa29ceSVladimir Barinov };
40372aa29ceSVladimir Barinov
40472aa29ceSVladimir Barinov static const struct iio_info hi8435_info = {
405fa7662e1SNikita Yushchenko .read_raw = hi8435_read_raw,
406f06a0d2cSNikita Yushchenko .read_event_config = hi8435_read_event_config,
40772aa29ceSVladimir Barinov .write_event_config = hi8435_write_event_config,
408f06a0d2cSNikita Yushchenko .read_event_value = hi8435_read_event_value,
409f06a0d2cSNikita Yushchenko .write_event_value = hi8435_write_event_value,
410f06a0d2cSNikita Yushchenko .debugfs_reg_access = hi8435_debugfs_reg_access,
41172aa29ceSVladimir Barinov };
41272aa29ceSVladimir Barinov
hi8435_iio_push_event(struct iio_dev * idev,unsigned int val)41372aa29ceSVladimir Barinov static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
41472aa29ceSVladimir Barinov {
41572aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
41672aa29ceSVladimir Barinov enum iio_event_direction dir;
41772aa29ceSVladimir Barinov unsigned int i;
41872aa29ceSVladimir Barinov unsigned int status = priv->event_prev_val ^ val;
41972aa29ceSVladimir Barinov
42072aa29ceSVladimir Barinov if (!status)
42172aa29ceSVladimir Barinov return;
42272aa29ceSVladimir Barinov
42372aa29ceSVladimir Barinov for_each_set_bit(i, &priv->event_scan_mask, 32) {
42472aa29ceSVladimir Barinov if (status & BIT(i)) {
42572aa29ceSVladimir Barinov dir = val & BIT(i) ? IIO_EV_DIR_RISING :
42672aa29ceSVladimir Barinov IIO_EV_DIR_FALLING;
42772aa29ceSVladimir Barinov iio_push_event(idev,
42872aa29ceSVladimir Barinov IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
42972aa29ceSVladimir Barinov IIO_EV_TYPE_THRESH, dir),
430bc2b7dabSGregor Boirie iio_get_time_ns(idev));
43172aa29ceSVladimir Barinov }
43272aa29ceSVladimir Barinov }
43372aa29ceSVladimir Barinov
43472aa29ceSVladimir Barinov priv->event_prev_val = val;
43572aa29ceSVladimir Barinov }
43672aa29ceSVladimir Barinov
hi8435_trigger_handler(int irq,void * private)43772aa29ceSVladimir Barinov static irqreturn_t hi8435_trigger_handler(int irq, void *private)
43872aa29ceSVladimir Barinov {
43972aa29ceSVladimir Barinov struct iio_poll_func *pf = private;
44072aa29ceSVladimir Barinov struct iio_dev *idev = pf->indio_dev;
44172aa29ceSVladimir Barinov struct hi8435_priv *priv = iio_priv(idev);
44272aa29ceSVladimir Barinov u32 val;
44372aa29ceSVladimir Barinov int ret;
44472aa29ceSVladimir Barinov
44572aa29ceSVladimir Barinov ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
44672aa29ceSVladimir Barinov if (ret < 0)
44772aa29ceSVladimir Barinov goto err_read;
44872aa29ceSVladimir Barinov
44972aa29ceSVladimir Barinov hi8435_iio_push_event(idev, val);
45072aa29ceSVladimir Barinov
45172aa29ceSVladimir Barinov err_read:
45272aa29ceSVladimir Barinov iio_trigger_notify_done(idev->trig);
45372aa29ceSVladimir Barinov
45472aa29ceSVladimir Barinov return IRQ_HANDLED;
45572aa29ceSVladimir Barinov }
45672aa29ceSVladimir Barinov
hi8435_triggered_event_cleanup(void * data)457ef19ee60SAndrey Smirnov static void hi8435_triggered_event_cleanup(void *data)
458ef19ee60SAndrey Smirnov {
459ef19ee60SAndrey Smirnov iio_triggered_event_cleanup(data);
460ef19ee60SAndrey Smirnov }
461ef19ee60SAndrey Smirnov
hi8435_probe(struct spi_device * spi)46272aa29ceSVladimir Barinov static int hi8435_probe(struct spi_device *spi)
46372aa29ceSVladimir Barinov {
46472aa29ceSVladimir Barinov struct iio_dev *idev;
46572aa29ceSVladimir Barinov struct hi8435_priv *priv;
46672aa29ceSVladimir Barinov struct gpio_desc *reset_gpio;
46772aa29ceSVladimir Barinov int ret;
46872aa29ceSVladimir Barinov
46972aa29ceSVladimir Barinov idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
47072aa29ceSVladimir Barinov if (!idev)
47172aa29ceSVladimir Barinov return -ENOMEM;
47272aa29ceSVladimir Barinov
47372aa29ceSVladimir Barinov priv = iio_priv(idev);
47472aa29ceSVladimir Barinov priv->spi = spi;
47572aa29ceSVladimir Barinov
47672aa29ceSVladimir Barinov reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW);
477e18788afSJonathan Cameron if (IS_ERR(reset_gpio)) {
478e18788afSJonathan Cameron /* chip s/w reset if h/w reset failed */
47972aa29ceSVladimir Barinov hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
48072aa29ceSVladimir Barinov hi8435_writeb(priv, HI8435_CTRL_REG, 0);
481e18788afSJonathan Cameron } else {
482e18788afSJonathan Cameron udelay(5);
4836822dc9dSAndrey Smirnov gpiod_set_value_cansleep(reset_gpio, 1);
48472aa29ceSVladimir Barinov }
48572aa29ceSVladimir Barinov
48672aa29ceSVladimir Barinov mutex_init(&priv->lock);
48772aa29ceSVladimir Barinov
48872aa29ceSVladimir Barinov idev->name = spi_get_device_id(spi)->name;
48972aa29ceSVladimir Barinov idev->modes = INDIO_DIRECT_MODE;
49072aa29ceSVladimir Barinov idev->info = &hi8435_info;
49172aa29ceSVladimir Barinov idev->channels = hi8435_channels;
49272aa29ceSVladimir Barinov idev->num_channels = ARRAY_SIZE(hi8435_channels);
49372aa29ceSVladimir Barinov
49472aa29ceSVladimir Barinov /* unmask all events */
49572aa29ceSVladimir Barinov priv->event_scan_mask = ~(0);
49672aa29ceSVladimir Barinov /*
49772aa29ceSVladimir Barinov * There is a restriction in the chip - the hysteresis can not be odd.
49872aa29ceSVladimir Barinov * If the hysteresis is set to odd value then chip gets into lock state
49972aa29ceSVladimir Barinov * and not functional anymore.
50072aa29ceSVladimir Barinov * After chip reset the thresholds are in undefined state, so we need to
50172aa29ceSVladimir Barinov * initialize thresholds to some initial values and then prevent
50272aa29ceSVladimir Barinov * userspace setting odd hysteresis.
50372aa29ceSVladimir Barinov *
50472aa29ceSVladimir Barinov * Set threshold low voltage to 2V, threshold high voltage to 4V
50572aa29ceSVladimir Barinov * for both GND-Open and Supply-Open sensing modes.
50672aa29ceSVladimir Barinov */
50772aa29ceSVladimir Barinov priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
50872aa29ceSVladimir Barinov priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
50972aa29ceSVladimir Barinov hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
51072aa29ceSVladimir Barinov hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
51172aa29ceSVladimir Barinov
51272aa29ceSVladimir Barinov ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
51372aa29ceSVladimir Barinov if (ret)
51472aa29ceSVladimir Barinov return ret;
51572aa29ceSVladimir Barinov
516ef19ee60SAndrey Smirnov ret = devm_add_action_or_reset(&spi->dev,
517ef19ee60SAndrey Smirnov hi8435_triggered_event_cleanup,
518ef19ee60SAndrey Smirnov idev);
519ef19ee60SAndrey Smirnov if (ret)
52072aa29ceSVladimir Barinov return ret;
52172aa29ceSVladimir Barinov
522ef19ee60SAndrey Smirnov return devm_iio_device_register(&spi->dev, idev);
52372aa29ceSVladimir Barinov }
52472aa29ceSVladimir Barinov
52572aa29ceSVladimir Barinov static const struct of_device_id hi8435_dt_ids[] = {
52672aa29ceSVladimir Barinov { .compatible = "holt,hi8435" },
527*09e3bdfeSJonathan Cameron { }
52872aa29ceSVladimir Barinov };
52972aa29ceSVladimir Barinov MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
53072aa29ceSVladimir Barinov
53172aa29ceSVladimir Barinov static const struct spi_device_id hi8435_id[] = {
53272aa29ceSVladimir Barinov { "hi8435", 0 },
53372aa29ceSVladimir Barinov { }
53472aa29ceSVladimir Barinov };
53572aa29ceSVladimir Barinov MODULE_DEVICE_TABLE(spi, hi8435_id);
53672aa29ceSVladimir Barinov
53772aa29ceSVladimir Barinov static struct spi_driver hi8435_driver = {
53872aa29ceSVladimir Barinov .driver = {
53972aa29ceSVladimir Barinov .name = DRV_NAME,
540f8e6ae5aSJonathan Cameron .of_match_table = hi8435_dt_ids,
54172aa29ceSVladimir Barinov },
54272aa29ceSVladimir Barinov .probe = hi8435_probe,
54372aa29ceSVladimir Barinov .id_table = hi8435_id,
54472aa29ceSVladimir Barinov };
54572aa29ceSVladimir Barinov module_spi_driver(hi8435_driver);
54672aa29ceSVladimir Barinov
54772aa29ceSVladimir Barinov MODULE_LICENSE("GPL");
54872aa29ceSVladimir Barinov MODULE_AUTHOR("Vladimir Barinov");
54972aa29ceSVladimir Barinov MODULE_DESCRIPTION("HI-8435 threshold detector");
550