cregit-Linux how code gets into the kernel

Release 4.7 drivers/iio/adc/hi8435.c

Directory: drivers/iio/adc
/*
 * Holt Integrated Circuits HI-8435 threshold detector driver
 *
 * Copyright (C) 2015 Zodiac Inflight Innovations
 * Copyright (C) 2015 Cogent Embedded, Inc.
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#include <linux/delay.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_event.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/gpio/consumer.h>


#define DRV_NAME "hi8435"

/* Register offsets for HI-8435 */

#define HI8435_CTRL_REG		0x02

#define HI8435_PSEN_REG		0x04

#define HI8435_TMDATA_REG	0x1E

#define HI8435_GOCENHYS_REG	0x3A

#define HI8435_SOCENHYS_REG	0x3C

#define HI8435_SO7_0_REG	0x10

#define HI8435_SO15_8_REG	0x12

#define HI8435_SO23_16_REG	0x14

#define HI8435_SO31_24_REG	0x16

#define HI8435_SO31_0_REG	0x78


#define HI8435_WRITE_OPCODE	0x00

#define HI8435_READ_OPCODE	0x80

/* CTRL register bits */

#define HI8435_CTRL_TEST	0x01

#define HI8435_CTRL_SRST	0x02


struct hi8435_priv {
	
struct spi_device *spi;
	
struct mutex lock;

	
unsigned long event_scan_mask; /* soft mask/unmask channels events */
	
unsigned int event_prev_val;

	
unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
	
unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */
	
u8 reg_buffer[3] ____cacheline_aligned;
};


static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val) { reg |= HI8435_READ_OPCODE; return spi_write_then_read(priv->spi, &reg, 1, val, 1); }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov39100.00%1100.00%
Total39100.00%1100.00%


static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val) { int ret; __be16 be_val; reg |= HI8435_READ_OPCODE; ret = spi_write_then_read(priv->spi, &reg, 1, &be_val, 2); *val = be16_to_cpu(be_val); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov58100.00%1100.00%
Total58100.00%1100.00%


static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val) { int ret; __be32 be_val; reg |= HI8435_READ_OPCODE; ret = spi_write_then_read(priv->spi, &reg, 1, &be_val, 4); *val = be32_to_cpu(be_val); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov58100.00%1100.00%
Total58100.00%1100.00%


static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val) { priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE; priv->reg_buffer[1] = val; return spi_write(priv->spi, priv->reg_buffer, 2); }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov51100.00%1100.00%
Total51100.00%1100.00%


static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val) { priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE; priv->reg_buffer[1] = (val >> 8) & 0xff; priv->reg_buffer[2] = val & 0xff; return spi_write(priv->spi, priv->reg_buffer, 3); }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov68100.00%1100.00%
Total68100.00%1100.00%


static int hi8435_read_event_config(struct iio_dev *idev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir) { struct hi8435_priv *priv = iio_priv(idev); return !!(priv->event_scan_mask & BIT(chan->channel)); }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov51100.00%1100.00%
Total51100.00%1100.00%


static int 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) { struct hi8435_priv *priv = iio_priv(idev); priv->event_scan_mask &= ~BIT(chan->channel); if (state) priv->event_scan_mask |= BIT(chan->channel); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov68100.00%1100.00%
Total68100.00%1100.00%


static int 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) { struct hi8435_priv *priv = iio_priv(idev); int ret; u8 mode, psen; u16 reg; ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen); if (ret < 0) return ret; /* Supply-Open or GND-Open sensing mode */ mode = !!(psen & BIT(chan->channel / 8)); ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : HI8435_GOCENHYS_REG, &reg); if (ret < 0) return ret; if (dir == IIO_EV_DIR_FALLING) *val = ((reg & 0xff) - (reg >> 8)) / 2; else if (dir == IIO_EV_DIR_RISING) *val = ((reg & 0xff) + (reg >> 8)) / 2; return IIO_VAL_INT; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov176100.00%1100.00%
Total176100.00%1100.00%


static int 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) { struct hi8435_priv *priv = iio_priv(idev); int ret; u8 mode, psen; u16 reg; ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen); if (ret < 0) return ret; /* Supply-Open or GND-Open sensing mode */ mode = !!(psen & BIT(chan->channel / 8)); ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : HI8435_GOCENHYS_REG, &reg); if (ret < 0) return ret; if (dir == IIO_EV_DIR_FALLING) { /* falling threshold range 2..21V, hysteresis minimum 2V */ if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode]) return -EINVAL; if (val == priv->threshold_lo[mode]) return 0; priv->threshold_lo[mode] = val; /* hysteresis must not be odd */ if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2) priv->threshold_hi[mode]--; } else if (dir == IIO_EV_DIR_RISING) { /* rising threshold range 3..22V, hysteresis minimum 2V */ if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2)) return -EINVAL; if (val == priv->threshold_hi[mode]) return 0; priv->threshold_hi[mode] = val; /* hysteresis must not be odd */ if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2) priv->threshold_lo[mode]++; } /* program thresholds */ mutex_lock(&priv->lock); ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : HI8435_GOCENHYS_REG, &reg); if (ret < 0) { mutex_unlock(&priv->lock); return ret; } /* hysteresis */ reg = priv->threshold_hi[mode] - priv->threshold_lo[mode]; reg <<= 8; /* threshold center */ reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]); ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG : HI8435_GOCENHYS_REG, reg); mutex_unlock(&priv->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov407100.00%1100.00%
Total407100.00%1100.00%


static int hi8435_debugfs_reg_access(struct iio_dev *idev, unsigned reg, unsigned writeval, unsigned *readval) { struct hi8435_priv *priv = iio_priv(idev); int ret; u8 val; if (readval != NULL) { ret = hi8435_readb(priv, reg, &val); *readval = val; } else { val = (u8)writeval; ret = hi8435_writeb(priv, reg, val); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov86100.00%1100.00%
Total86100.00%1100.00%

static const struct iio_event_spec hi8435_events[] = { { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_RISING, .mask_separate = BIT(IIO_EV_INFO_VALUE), }, { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_FALLING, .mask_separate = BIT(IIO_EV_INFO_VALUE), }, { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_EITHER, .mask_separate = BIT(IIO_EV_INFO_ENABLE), }, };
static int hi8435_get_sensing_mode(struct iio_dev *idev, const struct iio_chan_spec *chan) { struct hi8435_priv *priv = iio_priv(idev); int ret; u8 reg; ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg); if (ret < 0) return ret; return !!(reg & BIT(chan->channel / 8)); }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov70100.00%1100.00%
Total70100.00%1100.00%


static int hi8435_set_sensing_mode(struct iio_dev *idev, const struct iio_chan_spec *chan, unsigned int mode) { struct hi8435_priv *priv = iio_priv(idev); int ret; u8 reg; mutex_lock(&priv->lock); ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg); if (ret < 0) { mutex_unlock(&priv->lock); return ret; } reg &= ~BIT(chan->channel / 8); if (mode) reg |= BIT(chan->channel / 8); ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg); mutex_unlock(&priv->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov125100.00%1100.00%
Total125100.00%1100.00%

static const char * const hi8435_sensing_modes[] = { "GND-Open", "Supply-Open" }; static const struct iio_enum hi8435_sensing_mode = { .items = hi8435_sensing_modes, .num_items = ARRAY_SIZE(hi8435_sensing_modes), .get = hi8435_get_sensing_mode, .set = hi8435_set_sensing_mode, }; static const struct iio_chan_spec_ext_info hi8435_ext_info[] = { IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode), {}, }; #define HI8435_VOLTAGE_CHANNEL(num) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = num, \ .event_spec = hi8435_events, \ .num_event_specs = ARRAY_SIZE(hi8435_events), \ .ext_info = hi8435_ext_info, \ } static const struct iio_chan_spec hi8435_channels[] = { HI8435_VOLTAGE_CHANNEL(0), HI8435_VOLTAGE_CHANNEL(1), HI8435_VOLTAGE_CHANNEL(2), HI8435_VOLTAGE_CHANNEL(3), HI8435_VOLTAGE_CHANNEL(4), HI8435_VOLTAGE_CHANNEL(5), HI8435_VOLTAGE_CHANNEL(6), HI8435_VOLTAGE_CHANNEL(7), HI8435_VOLTAGE_CHANNEL(8), HI8435_VOLTAGE_CHANNEL(9), HI8435_VOLTAGE_CHANNEL(10), HI8435_VOLTAGE_CHANNEL(11), HI8435_VOLTAGE_CHANNEL(12), HI8435_VOLTAGE_CHANNEL(13), HI8435_VOLTAGE_CHANNEL(14), HI8435_VOLTAGE_CHANNEL(15), HI8435_VOLTAGE_CHANNEL(16), HI8435_VOLTAGE_CHANNEL(17), HI8435_VOLTAGE_CHANNEL(18), HI8435_VOLTAGE_CHANNEL(19), HI8435_VOLTAGE_CHANNEL(20), HI8435_VOLTAGE_CHANNEL(21), HI8435_VOLTAGE_CHANNEL(22), HI8435_VOLTAGE_CHANNEL(23), HI8435_VOLTAGE_CHANNEL(24), HI8435_VOLTAGE_CHANNEL(25), HI8435_VOLTAGE_CHANNEL(26), HI8435_VOLTAGE_CHANNEL(27), HI8435_VOLTAGE_CHANNEL(28), HI8435_VOLTAGE_CHANNEL(29), HI8435_VOLTAGE_CHANNEL(30), HI8435_VOLTAGE_CHANNEL(31), IIO_CHAN_SOFT_TIMESTAMP(32), }; static const struct iio_info hi8435_info = { .driver_module = THIS_MODULE, .read_event_config = &hi8435_read_event_config, .write_event_config = hi8435_write_event_config, .read_event_value = &hi8435_read_event_value, .write_event_value = &hi8435_write_event_value, .debugfs_reg_access = &hi8435_debugfs_reg_access, };
static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val) { struct hi8435_priv *priv = iio_priv(idev); enum iio_event_direction dir; unsigned int i; unsigned int status = priv->event_prev_val ^ val; if (!status) return; for_each_set_bit(i, &priv->event_scan_mask, 32) { if (status & BIT(i)) { dir = val & BIT(i) ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; iio_push_event(idev, IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, IIO_EV_TYPE_THRESH, dir), iio_get_time_ns()); } } priv->event_prev_val = val; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov108100.00%1100.00%
Total108100.00%1100.00%


static irqreturn_t hi8435_trigger_handler(int irq, void *private) { struct iio_poll_func *pf = private; struct iio_dev *idev = pf->indio_dev; struct hi8435_priv *priv = iio_priv(idev); u32 val; int ret; ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val); if (ret < 0) goto err_read; hi8435_iio_push_event(idev, val); err_read: iio_trigger_notify_done(idev->trig); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov85100.00%1100.00%
Total85100.00%1100.00%


static int hi8435_probe(struct spi_device *spi) { struct iio_dev *idev; struct hi8435_priv *priv; struct gpio_desc *reset_gpio; int ret; idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv)); if (!idev) return -ENOMEM; priv = iio_priv(idev); priv->spi = spi; reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW); if (IS_ERR(reset_gpio)) { /* chip s/w reset if h/w reset failed */ hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST); hi8435_writeb(priv, HI8435_CTRL_REG, 0); } else { udelay(5); gpiod_set_value(reset_gpio, 1); } spi_set_drvdata(spi, idev); mutex_init(&priv->lock); idev->dev.parent = &spi->dev; idev->name = spi_get_device_id(spi)->name; idev->modes = INDIO_DIRECT_MODE; idev->info = &hi8435_info; idev->channels = hi8435_channels; idev->num_channels = ARRAY_SIZE(hi8435_channels); /* unmask all events */ priv->event_scan_mask = ~(0); /* * There is a restriction in the chip - the hysteresis can not be odd. * If the hysteresis is set to odd value then chip gets into lock state * and not functional anymore. * After chip reset the thresholds are in undefined state, so we need to * initialize thresholds to some initial values and then prevent * userspace setting odd hysteresis. * * Set threshold low voltage to 2V, threshold high voltage to 4V * for both GND-Open and Supply-Open sensing modes. */ priv->threshold_lo[0] = priv->threshold_lo[1] = 2; priv->threshold_hi[0] = priv->threshold_hi[1] = 4; hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206); hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206); ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler); if (ret) return ret; ret = iio_device_register(idev); if (ret < 0) { dev_err(&spi->dev, "unable to register device\n"); goto unregister_triggered_event; } return 0; unregister_triggered_event: iio_triggered_event_cleanup(idev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov309100.00%1100.00%
Total309100.00%1100.00%


static int hi8435_remove(struct spi_device *spi) { struct iio_dev *idev = spi_get_drvdata(spi); iio_device_unregister(idev); iio_triggered_event_cleanup(idev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov34100.00%1100.00%
Total34100.00%1100.00%

static const struct of_device_id hi8435_dt_ids[] = { { .compatible = "holt,hi8435" }, {}, }; MODULE_DEVICE_TABLE(of, hi8435_dt_ids); static const struct spi_device_id hi8435_id[] = { { "hi8435", 0}, { } }; MODULE_DEVICE_TABLE(spi, hi8435_id); static struct spi_driver hi8435_driver = { .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(hi8435_dt_ids), }, .probe = hi8435_probe, .remove = hi8435_remove, .id_table = hi8435_id, }; module_spi_driver(hi8435_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vladimir Barinov"); MODULE_DESCRIPTION("HI-8435 threshold detector");

Overall Contributors

PersonTokensPropCommitsCommitProp
vladimir barinovvladimir barinov2410100.00%1100.00%
Total2410100.00%1100.00%
Directory: drivers/iio/adc
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}