550 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Holt Integrated Circuits HI-8435 threshold detector driver4 *5 * Copyright (C) 2015 Zodiac Inflight Innovations6 * Copyright (C) 2015 Cogent Embedded, Inc.7 */8 9#include <linux/delay.h>10#include <linux/iio/events.h>11#include <linux/iio/iio.h>12#include <linux/iio/sysfs.h>13#include <linux/iio/trigger.h>14#include <linux/iio/trigger_consumer.h>15#include <linux/iio/triggered_event.h>16#include <linux/interrupt.h>17#include <linux/module.h>18#include <linux/mod_devicetable.h>19#include <linux/spi/spi.h>20#include <linux/gpio/consumer.h>21 22#define DRV_NAME "hi8435"23 24/* Register offsets for HI-8435 */25#define HI8435_CTRL_REG 0x0226#define HI8435_PSEN_REG 0x0427#define HI8435_TMDATA_REG 0x1E28#define HI8435_GOCENHYS_REG 0x3A29#define HI8435_SOCENHYS_REG 0x3C30#define HI8435_SO7_0_REG 0x1031#define HI8435_SO15_8_REG 0x1232#define HI8435_SO23_16_REG 0x1433#define HI8435_SO31_24_REG 0x1634#define HI8435_SO31_0_REG 0x7835 36#define HI8435_WRITE_OPCODE 0x0037#define HI8435_READ_OPCODE 0x8038 39/* CTRL register bits */40#define HI8435_CTRL_TEST 0x0141#define HI8435_CTRL_SRST 0x0242 43struct hi8435_priv {44 struct spi_device *spi;45 struct mutex lock;46 47 unsigned long event_scan_mask; /* soft mask/unmask channels events */48 unsigned int event_prev_val;49 50 unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */51 unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */52 u8 reg_buffer[3] __aligned(IIO_DMA_MINALIGN);53};54 55static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)56{57 reg |= HI8435_READ_OPCODE;58 return spi_write_then_read(priv->spi, ®, 1, val, 1);59}60 61static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)62{63 int ret;64 __be16 be_val;65 66 reg |= HI8435_READ_OPCODE;67 ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 2);68 *val = be16_to_cpu(be_val);69 70 return ret;71}72 73static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)74{75 int ret;76 __be32 be_val;77 78 reg |= HI8435_READ_OPCODE;79 ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 4);80 *val = be32_to_cpu(be_val);81 82 return ret;83}84 85static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)86{87 priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;88 priv->reg_buffer[1] = val;89 90 return spi_write(priv->spi, priv->reg_buffer, 2);91}92 93static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)94{95 priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;96 priv->reg_buffer[1] = (val >> 8) & 0xff;97 priv->reg_buffer[2] = val & 0xff;98 99 return spi_write(priv->spi, priv->reg_buffer, 3);100}101 102static int hi8435_read_raw(struct iio_dev *idev,103 const struct iio_chan_spec *chan,104 int *val, int *val2, long mask)105{106 struct hi8435_priv *priv = iio_priv(idev);107 u32 tmp;108 int ret;109 110 switch (mask) {111 case IIO_CHAN_INFO_RAW:112 ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp);113 if (ret < 0)114 return ret;115 *val = !!(tmp & BIT(chan->channel));116 return IIO_VAL_INT;117 default:118 return -EINVAL;119 }120}121 122static int hi8435_read_event_config(struct iio_dev *idev,123 const struct iio_chan_spec *chan,124 enum iio_event_type type,125 enum iio_event_direction dir)126{127 struct hi8435_priv *priv = iio_priv(idev);128 129 return !!(priv->event_scan_mask & BIT(chan->channel));130}131 132static int hi8435_write_event_config(struct iio_dev *idev,133 const struct iio_chan_spec *chan,134 enum iio_event_type type,135 enum iio_event_direction dir, int state)136{137 struct hi8435_priv *priv = iio_priv(idev);138 int ret;139 u32 tmp;140 141 if (state) {142 ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp);143 if (ret < 0)144 return ret;145 if (tmp & BIT(chan->channel))146 priv->event_prev_val |= BIT(chan->channel);147 else148 priv->event_prev_val &= ~BIT(chan->channel);149 150 priv->event_scan_mask |= BIT(chan->channel);151 } else152 priv->event_scan_mask &= ~BIT(chan->channel);153 154 return 0;155}156 157static int hi8435_read_event_value(struct iio_dev *idev,158 const struct iio_chan_spec *chan,159 enum iio_event_type type,160 enum iio_event_direction dir,161 enum iio_event_info info,162 int *val, int *val2)163{164 struct hi8435_priv *priv = iio_priv(idev);165 int ret;166 u8 mode, psen;167 u16 reg;168 169 ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);170 if (ret < 0)171 return ret;172 173 /* Supply-Open or GND-Open sensing mode */174 mode = !!(psen & BIT(chan->channel / 8));175 176 ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :177 HI8435_GOCENHYS_REG, ®);178 if (ret < 0)179 return ret;180 181 if (dir == IIO_EV_DIR_FALLING)182 *val = ((reg & 0xff) - (reg >> 8)) / 2;183 else if (dir == IIO_EV_DIR_RISING)184 *val = ((reg & 0xff) + (reg >> 8)) / 2;185 186 return IIO_VAL_INT;187}188 189static int hi8435_write_event_value(struct iio_dev *idev,190 const struct iio_chan_spec *chan,191 enum iio_event_type type,192 enum iio_event_direction dir,193 enum iio_event_info info,194 int val, int val2)195{196 struct hi8435_priv *priv = iio_priv(idev);197 int ret;198 u8 mode, psen;199 u16 reg;200 201 ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);202 if (ret < 0)203 return ret;204 205 /* Supply-Open or GND-Open sensing mode */206 mode = !!(psen & BIT(chan->channel / 8));207 208 ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :209 HI8435_GOCENHYS_REG, ®);210 if (ret < 0)211 return ret;212 213 if (dir == IIO_EV_DIR_FALLING) {214 /* falling threshold range 2..21V, hysteresis minimum 2V */215 if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode])216 return -EINVAL;217 218 if (val == priv->threshold_lo[mode])219 return 0;220 221 priv->threshold_lo[mode] = val;222 223 /* hysteresis must not be odd */224 if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)225 priv->threshold_hi[mode]--;226 } else if (dir == IIO_EV_DIR_RISING) {227 /* rising threshold range 3..22V, hysteresis minimum 2V */228 if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2))229 return -EINVAL;230 231 if (val == priv->threshold_hi[mode])232 return 0;233 234 priv->threshold_hi[mode] = val;235 236 /* hysteresis must not be odd */237 if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)238 priv->threshold_lo[mode]++;239 }240 241 /* program thresholds */242 mutex_lock(&priv->lock);243 244 ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :245 HI8435_GOCENHYS_REG, ®);246 if (ret < 0) {247 mutex_unlock(&priv->lock);248 return ret;249 }250 251 /* hysteresis */252 reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];253 reg <<= 8;254 /* threshold center */255 reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);256 257 ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :258 HI8435_GOCENHYS_REG, reg);259 260 mutex_unlock(&priv->lock);261 262 return ret;263}264 265static int hi8435_debugfs_reg_access(struct iio_dev *idev,266 unsigned reg, unsigned writeval,267 unsigned *readval)268{269 struct hi8435_priv *priv = iio_priv(idev);270 int ret;271 u8 val;272 273 if (readval != NULL) {274 ret = hi8435_readb(priv, reg, &val);275 *readval = val;276 } else {277 val = (u8)writeval;278 ret = hi8435_writeb(priv, reg, val);279 }280 281 return ret;282}283 284static const struct iio_event_spec hi8435_events[] = {285 {286 .type = IIO_EV_TYPE_THRESH,287 .dir = IIO_EV_DIR_RISING,288 .mask_separate = BIT(IIO_EV_INFO_VALUE),289 }, {290 .type = IIO_EV_TYPE_THRESH,291 .dir = IIO_EV_DIR_FALLING,292 .mask_separate = BIT(IIO_EV_INFO_VALUE),293 }, {294 .type = IIO_EV_TYPE_THRESH,295 .dir = IIO_EV_DIR_EITHER,296 .mask_separate = BIT(IIO_EV_INFO_ENABLE),297 },298};299 300static int hi8435_get_sensing_mode(struct iio_dev *idev,301 const struct iio_chan_spec *chan)302{303 struct hi8435_priv *priv = iio_priv(idev);304 int ret;305 u8 reg;306 307 ret = hi8435_readb(priv, HI8435_PSEN_REG, ®);308 if (ret < 0)309 return ret;310 311 return !!(reg & BIT(chan->channel / 8));312}313 314static int hi8435_set_sensing_mode(struct iio_dev *idev,315 const struct iio_chan_spec *chan,316 unsigned int mode)317{318 struct hi8435_priv *priv = iio_priv(idev);319 int ret;320 u8 reg;321 322 mutex_lock(&priv->lock);323 324 ret = hi8435_readb(priv, HI8435_PSEN_REG, ®);325 if (ret < 0) {326 mutex_unlock(&priv->lock);327 return ret;328 }329 330 reg &= ~BIT(chan->channel / 8);331 if (mode)332 reg |= BIT(chan->channel / 8);333 334 ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);335 336 mutex_unlock(&priv->lock);337 338 return ret;339}340 341static const char * const hi8435_sensing_modes[] = { "GND-Open",342 "Supply-Open" };343 344static const struct iio_enum hi8435_sensing_mode = {345 .items = hi8435_sensing_modes,346 .num_items = ARRAY_SIZE(hi8435_sensing_modes),347 .get = hi8435_get_sensing_mode,348 .set = hi8435_set_sensing_mode,349};350 351static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {352 IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),353 IIO_ENUM_AVAILABLE("sensing_mode", IIO_SHARED_BY_TYPE, &hi8435_sensing_mode),354 {},355};356 357#define HI8435_VOLTAGE_CHANNEL(num) \358{ \359 .type = IIO_VOLTAGE, \360 .indexed = 1, \361 .channel = num, \362 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \363 .event_spec = hi8435_events, \364 .num_event_specs = ARRAY_SIZE(hi8435_events), \365 .ext_info = hi8435_ext_info, \366}367 368static const struct iio_chan_spec hi8435_channels[] = {369 HI8435_VOLTAGE_CHANNEL(0),370 HI8435_VOLTAGE_CHANNEL(1),371 HI8435_VOLTAGE_CHANNEL(2),372 HI8435_VOLTAGE_CHANNEL(3),373 HI8435_VOLTAGE_CHANNEL(4),374 HI8435_VOLTAGE_CHANNEL(5),375 HI8435_VOLTAGE_CHANNEL(6),376 HI8435_VOLTAGE_CHANNEL(7),377 HI8435_VOLTAGE_CHANNEL(8),378 HI8435_VOLTAGE_CHANNEL(9),379 HI8435_VOLTAGE_CHANNEL(10),380 HI8435_VOLTAGE_CHANNEL(11),381 HI8435_VOLTAGE_CHANNEL(12),382 HI8435_VOLTAGE_CHANNEL(13),383 HI8435_VOLTAGE_CHANNEL(14),384 HI8435_VOLTAGE_CHANNEL(15),385 HI8435_VOLTAGE_CHANNEL(16),386 HI8435_VOLTAGE_CHANNEL(17),387 HI8435_VOLTAGE_CHANNEL(18),388 HI8435_VOLTAGE_CHANNEL(19),389 HI8435_VOLTAGE_CHANNEL(20),390 HI8435_VOLTAGE_CHANNEL(21),391 HI8435_VOLTAGE_CHANNEL(22),392 HI8435_VOLTAGE_CHANNEL(23),393 HI8435_VOLTAGE_CHANNEL(24),394 HI8435_VOLTAGE_CHANNEL(25),395 HI8435_VOLTAGE_CHANNEL(26),396 HI8435_VOLTAGE_CHANNEL(27),397 HI8435_VOLTAGE_CHANNEL(28),398 HI8435_VOLTAGE_CHANNEL(29),399 HI8435_VOLTAGE_CHANNEL(30),400 HI8435_VOLTAGE_CHANNEL(31),401 IIO_CHAN_SOFT_TIMESTAMP(32),402};403 404static const struct iio_info hi8435_info = {405 .read_raw = hi8435_read_raw,406 .read_event_config = hi8435_read_event_config,407 .write_event_config = hi8435_write_event_config,408 .read_event_value = hi8435_read_event_value,409 .write_event_value = hi8435_write_event_value,410 .debugfs_reg_access = hi8435_debugfs_reg_access,411};412 413static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)414{415 struct hi8435_priv *priv = iio_priv(idev);416 enum iio_event_direction dir;417 unsigned int i;418 unsigned int status = priv->event_prev_val ^ val;419 420 if (!status)421 return;422 423 for_each_set_bit(i, &priv->event_scan_mask, 32) {424 if (status & BIT(i)) {425 dir = val & BIT(i) ? IIO_EV_DIR_RISING :426 IIO_EV_DIR_FALLING;427 iio_push_event(idev,428 IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,429 IIO_EV_TYPE_THRESH, dir),430 iio_get_time_ns(idev));431 }432 }433 434 priv->event_prev_val = val;435}436 437static irqreturn_t hi8435_trigger_handler(int irq, void *private)438{439 struct iio_poll_func *pf = private;440 struct iio_dev *idev = pf->indio_dev;441 struct hi8435_priv *priv = iio_priv(idev);442 u32 val;443 int ret;444 445 ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);446 if (ret < 0)447 goto err_read;448 449 hi8435_iio_push_event(idev, val);450 451err_read:452 iio_trigger_notify_done(idev->trig);453 454 return IRQ_HANDLED;455}456 457static void hi8435_triggered_event_cleanup(void *data)458{459 iio_triggered_event_cleanup(data);460}461 462static int hi8435_probe(struct spi_device *spi)463{464 struct iio_dev *idev;465 struct hi8435_priv *priv;466 struct gpio_desc *reset_gpio;467 int ret;468 469 idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));470 if (!idev)471 return -ENOMEM;472 473 priv = iio_priv(idev);474 priv->spi = spi;475 476 reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW);477 if (IS_ERR(reset_gpio)) {478 /* chip s/w reset if h/w reset failed */479 hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);480 hi8435_writeb(priv, HI8435_CTRL_REG, 0);481 } else {482 udelay(5);483 gpiod_set_value_cansleep(reset_gpio, 1);484 }485 486 mutex_init(&priv->lock);487 488 idev->name = spi_get_device_id(spi)->name;489 idev->modes = INDIO_DIRECT_MODE;490 idev->info = &hi8435_info;491 idev->channels = hi8435_channels;492 idev->num_channels = ARRAY_SIZE(hi8435_channels);493 494 /* unmask all events */495 priv->event_scan_mask = ~(0);496 /*497 * There is a restriction in the chip - the hysteresis can not be odd.498 * If the hysteresis is set to odd value then chip gets into lock state499 * and not functional anymore.500 * After chip reset the thresholds are in undefined state, so we need to501 * initialize thresholds to some initial values and then prevent502 * userspace setting odd hysteresis.503 *504 * Set threshold low voltage to 2V, threshold high voltage to 4V505 * for both GND-Open and Supply-Open sensing modes.506 */507 priv->threshold_lo[0] = priv->threshold_lo[1] = 2;508 priv->threshold_hi[0] = priv->threshold_hi[1] = 4;509 hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);510 hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);511 512 ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);513 if (ret)514 return ret;515 516 ret = devm_add_action_or_reset(&spi->dev,517 hi8435_triggered_event_cleanup,518 idev);519 if (ret)520 return ret;521 522 return devm_iio_device_register(&spi->dev, idev);523}524 525static const struct of_device_id hi8435_dt_ids[] = {526 { .compatible = "holt,hi8435" },527 { }528};529MODULE_DEVICE_TABLE(of, hi8435_dt_ids);530 531static const struct spi_device_id hi8435_id[] = {532 { "hi8435", 0 },533 { }534};535MODULE_DEVICE_TABLE(spi, hi8435_id);536 537static struct spi_driver hi8435_driver = {538 .driver = {539 .name = DRV_NAME,540 .of_match_table = hi8435_dt_ids,541 },542 .probe = hi8435_probe,543 .id_table = hi8435_id,544};545module_spi_driver(hi8435_driver);546 547MODULE_LICENSE("GPL");548MODULE_AUTHOR("Vladimir Barinov");549MODULE_DESCRIPTION("HI-8435 threshold detector");550