cregit-Linux how code gets into the kernel

Release 4.11 drivers/iio/adc/nau7802.c

Directory: drivers/iio/adc
/*
 * Driver for the Nuvoton NAU7802 ADC
 *
 * Copyright 2013 Free Electrons
 *
 * Licensed under the GPLv2 or later.
 */

#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/wait.h>
#include <linux/log2.h>
#include <linux/of.h>

#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>


#define NAU7802_REG_PUCTRL	0x00

#define NAU7802_PUCTRL_RR(x)		(x << 0)

#define NAU7802_PUCTRL_RR_BIT		NAU7802_PUCTRL_RR(1)

#define NAU7802_PUCTRL_PUD(x)		(x << 1)

#define NAU7802_PUCTRL_PUD_BIT		NAU7802_PUCTRL_PUD(1)

#define NAU7802_PUCTRL_PUA(x)		(x << 2)

#define NAU7802_PUCTRL_PUA_BIT		NAU7802_PUCTRL_PUA(1)

#define NAU7802_PUCTRL_PUR(x)		(x << 3)

#define NAU7802_PUCTRL_PUR_BIT		NAU7802_PUCTRL_PUR(1)

#define NAU7802_PUCTRL_CS(x)		(x << 4)

#define NAU7802_PUCTRL_CS_BIT		NAU7802_PUCTRL_CS(1)

#define NAU7802_PUCTRL_CR(x)		(x << 5)

#define NAU7802_PUCTRL_CR_BIT		NAU7802_PUCTRL_CR(1)

#define NAU7802_PUCTRL_AVDDS(x)		(x << 7)

#define NAU7802_PUCTRL_AVDDS_BIT	NAU7802_PUCTRL_AVDDS(1)

#define NAU7802_REG_CTRL1	0x01

#define NAU7802_CTRL1_VLDO(x)		(x << 3)

#define NAU7802_CTRL1_GAINS(x)		(x)

#define NAU7802_CTRL1_GAINS_BITS	0x07

#define NAU7802_REG_CTRL2	0x02

#define NAU7802_CTRL2_CHS(x)		(x << 7)

#define NAU7802_CTRL2_CRS(x)		(x << 4)

#define NAU7802_SAMP_FREQ_320	0x07

#define NAU7802_CTRL2_CHS_BIT		NAU7802_CTRL2_CHS(1)

#define NAU7802_REG_ADC_B2	0x12

#define NAU7802_REG_ADC_B1	0x13

#define NAU7802_REG_ADC_B0	0x14

#define NAU7802_REG_ADC_CTRL	0x15


#define NAU7802_MIN_CONVERSIONS 6


struct nau7802_state {
	
struct i2c_client	*client;
	
s32			last_value;
	
struct mutex		lock;
	
struct mutex		data_lock;
	
u32			vref_mv;
	
u32			conversion_count;
	
u32			min_conversions;
	
u8			sample_rate;
	
u32			scale_avail[8];
	
struct completion	value_ok;
};


#define NAU7802_CHANNEL(chan) {                                       \
        .type = IIO_VOLTAGE,                                    \
        .indexed = 1,                                           \
        .channel = (chan),                                      \
        .scan_index = (chan),                                   \
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |  \
                                BIT(IIO_CHAN_INFO_SAMP_FREQ)    \
}


static const struct iio_chan_spec nau7802_chan_array[] = {
	NAU7802_CHANNEL(0),
	NAU7802_CHANNEL(1),
};


static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80,
						10, 10, 10, 320};


static ssize_t nau7802_show_scales(struct device *dev, struct device_attribute *attr, char *buf) { struct nau7802_state *st = iio_priv(dev_to_iio_dev(dev)); int i, len = 0; for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09d ", st->scale_avail[i]); buf[len-1] = '\n'; return len; }

Contributors

PersonTokensPropCommitsCommitProp
Quentin Schulz92100.00%1100.00%
Total92100.00%1100.00%

static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320"); static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, nau7802_show_scales, NULL, 0); static struct attribute *nau7802_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, NULL }; static const struct attribute_group nau7802_attribute_group = { .attrs = nau7802_attributes, };
static int nau7802_set_gain(struct nau7802_state *st, int gain) { int ret; mutex_lock(&st->lock); st->conversion_count = 0; ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1); if (ret < 0) goto nau7802_sysfs_set_gain_out; ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1, (ret & (~NAU7802_CTRL1_GAINS_BITS)) | gain); nau7802_sysfs_set_gain_out: mutex_unlock(&st->lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni86100.00%1100.00%
Total86100.00%1100.00%


static int nau7802_read_conversion(struct nau7802_state *st) { int data; mutex_lock(&st->data_lock); data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B2); if (data < 0) goto nau7802_read_conversion_out; st->last_value = data << 16; data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B1); if (data < 0) goto nau7802_read_conversion_out; st->last_value |= data << 8; data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B0); if (data < 0) goto nau7802_read_conversion_out; st->last_value |= data; st->last_value = sign_extend32(st->last_value, 23); nau7802_read_conversion_out: mutex_unlock(&st->data_lock); return data; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni130100.00%1100.00%
Total130100.00%1100.00%

/* * Conversions are synchronised on the rising edge of NAU7802_PUCTRL_CS_BIT */
static int nau7802_sync(struct nau7802_state *st) { int ret; ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); if (ret < 0) return ret; ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, ret | NAU7802_PUCTRL_CS_BIT); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni52100.00%1100.00%
Total52100.00%1100.00%


static irqreturn_t nau7802_eoc_trigger(int irq, void *private) { struct iio_dev *indio_dev = private; struct nau7802_state *st = iio_priv(indio_dev); int status; status = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); if (status < 0) return IRQ_HANDLED; if (!(status & NAU7802_PUCTRL_CR_BIT)) return IRQ_NONE; if (nau7802_read_conversion(st) < 0) return IRQ_HANDLED; /* * Because there is actually only one ADC for both channels, we have to * wait for enough conversions to happen before getting a significant * value when changing channels and the values are far apart. */ if (st->conversion_count < NAU7802_MIN_CONVERSIONS) st->conversion_count++; if (st->conversion_count >= NAU7802_MIN_CONVERSIONS) complete(&st->value_ok); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni10999.09%150.00%
Daniel Wagner10.91%150.00%
Total110100.00%2100.00%


static int nau7802_read_irq(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct nau7802_state *st = iio_priv(indio_dev); int ret; reinit_completion(&st->value_ok); enable_irq(st->client->irq); nau7802_sync(st); /* read registers to ensure we flush everything */ ret = nau7802_read_conversion(st); if (ret < 0) goto read_chan_info_failure; /* Wait for a conversion to finish */ ret = wait_for_completion_interruptible_timeout(&st->value_ok, msecs_to_jiffies(1000)); if (ret == 0) ret = -ETIMEDOUT; if (ret < 0) goto read_chan_info_failure; disable_irq(st->client->irq); *val = st->last_value; return IIO_VAL_INT; read_chan_info_failure: disable_irq(st->client->irq); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni14098.59%150.00%
Wolfram Sang21.41%150.00%
Total142100.00%2100.00%


static int nau7802_read_poll(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct nau7802_state *st = iio_priv(indio_dev); int ret; nau7802_sync(st); /* read registers to ensure we flush everything */ ret = nau7802_read_conversion(st); if (ret < 0) return ret; /* * Because there is actually only one ADC for both channels, we have to * wait for enough conversions to happen before getting a significant * value when changing channels and the values are far appart. */ do { ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); if (ret < 0) return ret; while (!(ret & NAU7802_PUCTRL_CR_BIT)) { if (st->sample_rate != NAU7802_SAMP_FREQ_320) msleep(20); else mdelay(4); ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); if (ret < 0) return ret; } ret = nau7802_read_conversion(st); if (ret < 0) return ret; if (st->conversion_count < NAU7802_MIN_CONVERSIONS) st->conversion_count++; } while (st->conversion_count < NAU7802_MIN_CONVERSIONS); *val = st->last_value; return IIO_VAL_INT; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni178100.00%1100.00%
Total178100.00%1100.00%


static int nau7802_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct nau7802_state *st = iio_priv(indio_dev); int ret; switch (mask) { case IIO_CHAN_INFO_RAW: mutex_lock(&st->lock); /* * Select the channel to use * - Channel 1 is value 0 in the CHS register * - Channel 2 is value 1 in the CHS register */ ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL2); if (ret < 0) { mutex_unlock(&st->lock); return ret; } if (((ret & NAU7802_CTRL2_CHS_BIT) && !chan->channel) || (!(ret & NAU7802_CTRL2_CHS_BIT) && chan->channel)) { st->conversion_count = 0; ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2, NAU7802_CTRL2_CHS(chan->channel) | NAU7802_CTRL2_CRS(st->sample_rate)); if (ret < 0) { mutex_unlock(&st->lock); return ret; } } if (st->client->irq) ret = nau7802_read_irq(indio_dev, chan, val); else ret = nau7802_read_poll(indio_dev, chan, val); mutex_unlock(&st->lock); return ret; case IIO_CHAN_INFO_SCALE: ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1); if (ret < 0) return ret; /* * We have 24 bits of signed data, that means 23 bits of data * plus the sign bit */ *val = st->vref_mv; *val2 = 23 + (ret & NAU7802_CTRL1_GAINS_BITS); return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_SAMP_FREQ: *val = nau7802_sample_freq_avail[st->sample_rate]; *val2 = 0; return IIO_VAL_INT; default: break; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni283100.00%1100.00%
Total283100.00%1100.00%


static int nau7802_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct nau7802_state *st = iio_priv(indio_dev); int i, ret; switch (mask) { case IIO_CHAN_INFO_SCALE: for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) if (val2 == st->scale_avail[i]) return nau7802_set_gain(st, i); break; case IIO_CHAN_INFO_SAMP_FREQ: for (i = 0; i < ARRAY_SIZE(nau7802_sample_freq_avail); i++) if (val == nau7802_sample_freq_avail[i]) { mutex_lock(&st->lock); st->sample_rate = i; st->conversion_count = 0; ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2, NAU7802_CTRL2_CRS(st->sample_rate)); mutex_unlock(&st->lock); return ret; } break; default: break; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni174100.00%1100.00%
Total174100.00%1100.00%


static int nau7802_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) { return IIO_VAL_INT_PLUS_NANO; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni23100.00%1100.00%
Total23100.00%1100.00%

static const struct iio_info nau7802_info = { .driver_module = THIS_MODULE, .read_raw = &nau7802_read_raw, .write_raw = &nau7802_write_raw, .write_raw_get_fmt = nau7802_write_raw_get_fmt, .attrs = &nau7802_attribute_group, };
static int nau7802_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct iio_dev *indio_dev; struct nau7802_state *st; struct device_node *np = client->dev.of_node; int i, ret; u8 data; u32 tmp = 0; if (!client->dev.of_node) { dev_err(&client->dev, "No device tree node available.\n"); return -EINVAL; } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; st = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); indio_dev->dev.parent = &client->dev; indio_dev->dev.of_node = client->dev.of_node; indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &nau7802_info; st->client = client; /* Reset the device */ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, NAU7802_PUCTRL_RR_BIT); if (ret < 0) return ret; /* Enter normal operation mode */ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, NAU7802_PUCTRL_PUD_BIT); if (ret < 0) return ret; /* * After about 200 usecs, the device should be ready and then * the Power Up bit will be set to 1. If not, wait for it. */ udelay(210); ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); if (ret < 0) return ret; if (!(ret & NAU7802_PUCTRL_PUR_BIT)) return ret; of_property_read_u32(np, "nuvoton,vldo", &tmp); st->vref_mv = tmp; data = NAU7802_PUCTRL_PUD_BIT | NAU7802_PUCTRL_PUA_BIT | NAU7802_PUCTRL_CS_BIT; if (tmp >= 2400) data |= NAU7802_PUCTRL_AVDDS_BIT; ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, data); if (ret < 0) return ret; ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_ADC_CTRL, 0x30); if (ret < 0) return ret; if (tmp >= 2400) { data = NAU7802_CTRL1_VLDO((4500 - tmp) / 300); ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1, data); if (ret < 0) return ret; } /* Populate available ADC input ranges */ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) st->scale_avail[i] = (((u64)st->vref_mv) * 1000000000ULL) >> (23 + i); init_completion(&st->value_ok); /* * The ADC fires continuously and we can't do anything about * it. So we need to have the IRQ disabled by default, and we * will enable them back when we will need them.. */ if (client->irq) { ret = request_threaded_irq(client->irq, NULL, nau7802_eoc_trigger, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, client->dev.driver->name, indio_dev); if (ret) { /* * What may happen here is that our IRQ controller is * not able to get level interrupt but this is required * by this ADC as when going over 40 sample per second, * the interrupt line may stay high between conversions. * So, we continue no matter what but we switch to * polling mode. */ dev_info(&client->dev, "Failed to allocate IRQ, using polling mode\n"); client->irq = 0; } else disable_irq(client->irq); } if (!client->irq) { /* * We are polling, use the fastest sample rate by * default */ st->sample_rate = NAU7802_SAMP_FREQ_320; ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2, NAU7802_CTRL2_CRS(st->sample_rate)); if (ret) goto error_free_irq; } /* Setup the ADC channels available on the board */ indio_dev->num_channels = ARRAY_SIZE(nau7802_chan_array); indio_dev->channels = nau7802_chan_array; mutex_init(&st->lock); mutex_init(&st->data_lock); ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, "Couldn't register the device.\n"); goto error_device_register; } return 0; error_device_register: mutex_destroy(&st->lock); mutex_destroy(&st->data_lock); error_free_irq: if (client->irq) free_irq(client->irq, indio_dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni59893.88%133.33%
Sachin Kamat274.24%133.33%
Matthew Ranostay121.88%133.33%
Total637100.00%3100.00%


static int nau7802_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct nau7802_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); mutex_destroy(&st->lock); mutex_destroy(&st->data_lock); if (client->irq) free_irq(client->irq, indio_dev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni70100.00%1100.00%
Total70100.00%1100.00%

static const struct i2c_device_id nau7802_i2c_id[] = { { "nau7802", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id); static const struct of_device_id nau7802_dt_ids[] = { { .compatible = "nuvoton,nau7802" }, {}, }; MODULE_DEVICE_TABLE(of, nau7802_dt_ids); static struct i2c_driver nau7802_driver = { .probe = nau7802_probe, .remove = nau7802_remove, .id_table = nau7802_i2c_id, .driver = { .name = "nau7802", .of_match_table = nau7802_dt_ids, }, }; module_i2c_driver(nau7802_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Nuvoton NAU7802 ADC Driver"); MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");

Overall Contributors

PersonTokensPropCommitsCommitProp
Alexandre Belloni229593.56%114.29%
Quentin Schulz1134.61%114.29%
Sachin Kamat301.22%228.57%
Matthew Ranostay120.49%114.29%
Wolfram Sang20.08%114.29%
Daniel Wagner10.04%114.29%
Total2453100.00%7100.00%
Directory: drivers/iio/adc
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.