cregit-Linux how code gets into the kernel

Release 4.7 drivers/iio/dac/ad5755.c

Directory: drivers/iio/dac
/*
 * AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver
 *
 * Copyright 2012 Analog Devices Inc.
 *
 * Licensed under the GPL-2.
 */

#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/platform_data/ad5755.h>


#define AD5755_NUM_CHANNELS 4


#define AD5755_ADDR(x)			((x) << 16)


#define AD5755_WRITE_REG_DATA(chan)	(chan)

#define AD5755_WRITE_REG_GAIN(chan)	(0x08 | (chan))

#define AD5755_WRITE_REG_OFFSET(chan)	(0x10 | (chan))

#define AD5755_WRITE_REG_CTRL(chan)	(0x1c | (chan))


#define AD5755_READ_REG_DATA(chan)	(chan)

#define AD5755_READ_REG_CTRL(chan)	(0x4 | (chan))

#define AD5755_READ_REG_GAIN(chan)	(0x8 | (chan))

#define AD5755_READ_REG_OFFSET(chan)	(0xc | (chan))

#define AD5755_READ_REG_CLEAR(chan)	(0x10 | (chan))

#define AD5755_READ_REG_SLEW(chan)	(0x14 | (chan))

#define AD5755_READ_REG_STATUS		0x18

#define AD5755_READ_REG_MAIN		0x19

#define AD5755_READ_REG_DC_DC		0x1a


#define AD5755_CTRL_REG_SLEW	0x0

#define AD5755_CTRL_REG_MAIN	0x1

#define AD5755_CTRL_REG_DAC	0x2

#define AD5755_CTRL_REG_DC_DC	0x3

#define AD5755_CTRL_REG_SW	0x4


#define AD5755_READ_FLAG 0x800000


#define AD5755_NOOP 0x1CE000


#define AD5755_DAC_INT_EN			BIT(8)

#define AD5755_DAC_CLR_EN			BIT(7)

#define AD5755_DAC_OUT_EN			BIT(6)

#define AD5755_DAC_INT_CURRENT_SENSE_RESISTOR	BIT(5)

#define AD5755_DAC_DC_DC_EN			BIT(4)

#define AD5755_DAC_VOLTAGE_OVERRANGE_EN		BIT(3)


#define AD5755_DC_DC_MAXV			0

#define AD5755_DC_DC_FREQ_SHIFT			2

#define AD5755_DC_DC_PHASE_SHIFT		4

#define AD5755_EXT_DC_DC_COMP_RES		BIT(6)


#define AD5755_SLEW_STEP_SIZE_SHIFT		0

#define AD5755_SLEW_RATE_SHIFT			3

#define AD5755_SLEW_ENABLE			BIT(12)

/**
 * struct ad5755_chip_info - chip specific information
 * @channel_template:   channel specification
 * @calib_shift:        shift for the calibration data registers
 * @has_voltage_out:    whether the chip has voltage outputs
 */

struct ad5755_chip_info {
	
const struct iio_chan_spec channel_template;
	
unsigned int calib_shift;
	
bool has_voltage_out;
};

/**
 * struct ad5755_state - driver instance specific data
 * @spi:        spi device the driver is attached to
 * @chip_info:  chip model specific constants, available modes etc
 * @pwr_down:   bitmask which contains  hether a channel is powered down or not
 * @ctrl:       software shadow of the channel ctrl registers
 * @channels:   iio channel spec for the device
 * @data:       spi transfer buffers
 */

struct ad5755_state {
	
struct spi_device		*spi;
	
const struct ad5755_chip_info	*chip_info;
	
unsigned int			pwr_down;
	
unsigned int			ctrl[AD5755_NUM_CHANNELS];
	
struct iio_chan_spec		channels[AD5755_NUM_CHANNELS];

	/*
         * DMA (thus cache coherency maintenance) requires the
         * transfer buffers to live in their own cache lines.
         */

	union {
		
__be32 d32;
		
u8 d8[4];
	} 
data[2] ____cacheline_aligned;
};


enum ad5755_type {
	
ID_AD5755,
	
ID_AD5757,
	
ID_AD5735,
	
ID_AD5737,
};


static int ad5755_write_unlocked(struct iio_dev *indio_dev, unsigned int reg, unsigned int val) { struct ad5755_state *st = iio_priv(indio_dev); st->data[0].d32 = cpu_to_be32((reg << 16) | val); return spi_write(st->spi, &st->data[0].d8[1], 3); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen72100.00%1100.00%
Total72100.00%1100.00%


static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev, unsigned int channel, unsigned int reg, unsigned int val) { return ad5755_write_unlocked(indio_dev, AD5755_WRITE_REG_CTRL(channel), (reg << 13) | val); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen42100.00%1100.00%
Total42100.00%1100.00%


static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg, unsigned int val) { int ret; mutex_lock(&indio_dev->mlock); ret = ad5755_write_unlocked(indio_dev, reg, val); mutex_unlock(&indio_dev->mlock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen52100.00%1100.00%
Total52100.00%1100.00%


static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel, unsigned int reg, unsigned int val) { int ret; mutex_lock(&indio_dev->mlock); ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val); mutex_unlock(&indio_dev->mlock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen58100.00%1100.00%
Total58100.00%1100.00%


static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr) { struct ad5755_state *st = iio_priv(indio_dev); int ret; struct spi_transfer t[] = { { .tx_buf = &st->data[0].d8[1], .len = 3, .cs_change = 1, }, { .tx_buf = &st->data[1].d8[1], .rx_buf = &st->data[1].d8[1], .len = 3, }, }; mutex_lock(&indio_dev->mlock); st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16)); st->data[1].d32 = cpu_to_be32(AD5755_NOOP); ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); if (ret >= 0) ret = be32_to_cpu(st->data[1].d32) & 0xffff; mutex_unlock(&indio_dev->mlock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen193100.00%2100.00%
Total193100.00%2100.00%


static int ad5755_update_dac_ctrl(struct iio_dev *indio_dev, unsigned int channel, unsigned int set, unsigned int clr) { struct ad5755_state *st = iio_priv(indio_dev); int ret; st->ctrl[channel] |= set; st->ctrl[channel] &= ~clr; ret = ad5755_write_ctrl_unlocked(indio_dev, channel, AD5755_CTRL_REG_DAC, st->ctrl[channel]); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen76100.00%1100.00%
Total76100.00%1100.00%


static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev, unsigned int channel, bool pwr_down) { struct ad5755_state *st = iio_priv(indio_dev); unsigned int mask = BIT(channel); mutex_lock(&indio_dev->mlock); if ((bool)(st->pwr_down & mask) == pwr_down) goto out_unlock; if (!pwr_down) { st->pwr_down &= ~mask; ad5755_update_dac_ctrl(indio_dev, channel, AD5755_DAC_INT_EN | AD5755_DAC_DC_DC_EN, 0); udelay(200); ad5755_update_dac_ctrl(indio_dev, channel, AD5755_DAC_OUT_EN, 0); } else { st->pwr_down |= mask; ad5755_update_dac_ctrl(indio_dev, channel, 0, AD5755_DAC_INT_EN | AD5755_DAC_OUT_EN | AD5755_DAC_DC_DC_EN); } out_unlock: mutex_unlock(&indio_dev->mlock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen143100.00%1100.00%
Total143100.00%1100.00%

static const int ad5755_min_max_table[][2] = { [AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 }, [AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 }, [AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 }, [AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 }, [AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 }, [AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 }, [AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 }, };
static void ad5755_get_min_max(struct ad5755_state *st, struct iio_chan_spec const *chan, int *min, int *max) { enum ad5755_mode mode = st->ctrl[chan->channel] & 7; *min = ad5755_min_max_table[mode][0]; *max = ad5755_min_max_table[mode][1]; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen62100.00%1100.00%
Total62100.00%1100.00%


static inline int ad5755_get_offset(struct ad5755_state *st, struct iio_chan_spec const *chan) { int min, max; ad5755_get_min_max(st, chan, &min, &max); return (min * (1 << chan->scan_type.realbits)) / (max - min); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen57100.00%1100.00%
Total57100.00%1100.00%


static int ad5755_chan_reg_info(struct ad5755_state *st, struct iio_chan_spec const *chan, long info, bool write, unsigned int *reg, unsigned int *shift, unsigned int *offset) { switch (info) { case IIO_CHAN_INFO_RAW: if (write) *reg = AD5755_WRITE_REG_DATA(chan->address); else *reg = AD5755_READ_REG_DATA(chan->address); *shift = chan->scan_type.shift; *offset = 0; break; case IIO_CHAN_INFO_CALIBBIAS: if (write) *reg = AD5755_WRITE_REG_OFFSET(chan->address); else *reg = AD5755_READ_REG_OFFSET(chan->address); *shift = st->chip_info->calib_shift; *offset = 32768; break; case IIO_CHAN_INFO_CALIBSCALE: if (write) *reg = AD5755_WRITE_REG_GAIN(chan->address); else *reg = AD5755_READ_REG_GAIN(chan->address); *shift = st->chip_info->calib_shift; *offset = 0; break; default: return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen181100.00%1100.00%
Total181100.00%1100.00%


static int ad5755_read_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long info) { struct ad5755_state *st = iio_priv(indio_dev); unsigned int reg, shift, offset; int min, max; int ret; switch (info) { case IIO_CHAN_INFO_SCALE: ad5755_get_min_max(st, chan, &min, &max); *val = max - min; *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_OFFSET: *val = ad5755_get_offset(st, chan); return IIO_VAL_INT; default: ret = ad5755_chan_reg_info(st, chan, info, false, &reg, &shift, &offset); if (ret) return ret; ret = ad5755_read(indio_dev, reg); if (ret < 0) return ret; *val = (ret - offset) >> shift; return IIO_VAL_INT; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen177100.00%2100.00%
Total177100.00%2100.00%


static int ad5755_write_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int val, int val2, long info) { struct ad5755_state *st = iio_priv(indio_dev); unsigned int shift, reg, offset; int ret; ret = ad5755_chan_reg_info(st, chan, info, true, &reg, &shift, &offset); if (ret) return ret; val <<= shift; val += offset; if (val < 0 || val > 0xffff) return -EINVAL; return ad5755_write(indio_dev, reg, val); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen108100.00%1100.00%
Total108100.00%1100.00%


static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv, const struct iio_chan_spec *chan, char *buf) { struct ad5755_state *st = iio_priv(indio_dev); return sprintf(buf, "%d\n", (bool)(st->pwr_down & (1 << chan->channel))); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen59100.00%1100.00%
Total59100.00%1100.00%


static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv, struct iio_chan_spec const *chan, const char *buf, size_t len) { bool pwr_down; int ret; ret = strtobool(buf, &pwr_down); if (ret) return ret; ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down); return ret ? ret : len; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen71100.00%1100.00%
Total71100.00%1100.00%

static const struct iio_info ad5755_info = { .read_raw = ad5755_read_raw, .write_raw = ad5755_write_raw, .driver_module = THIS_MODULE, }; static const struct iio_chan_spec_ext_info ad5755_ext_info[] = { { .name = "powerdown", .read = ad5755_read_powerdown, .write = ad5755_write_powerdown, .shared = IIO_SEPARATE, }, { }, }; #define AD5755_CHANNEL(_bits) { \ .indexed = 1, \ .output = 1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OFFSET) | \ BIT(IIO_CHAN_INFO_CALIBSCALE) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .scan_type = { \ .sign = 'u', \ .realbits = (_bits), \ .storagebits = 16, \ .shift = 16 - (_bits), \ }, \ .ext_info = ad5755_ext_info, \ } static const struct ad5755_chip_info ad5755_chip_info_tbl[] = { [ID_AD5735] = { .channel_template = AD5755_CHANNEL(14), .has_voltage_out = true, .calib_shift = 4, }, [ID_AD5737] = { .channel_template = AD5755_CHANNEL(14), .has_voltage_out = false, .calib_shift = 4, }, [ID_AD5755] = { .channel_template = AD5755_CHANNEL(16), .has_voltage_out = true, .calib_shift = 0, }, [ID_AD5757] = { .channel_template = AD5755_CHANNEL(16), .has_voltage_out = false, .calib_shift = 0, }, };
static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode) { switch (mode) { case AD5755_MODE_VOLTAGE_0V_5V: case AD5755_MODE_VOLTAGE_0V_10V: case AD5755_MODE_VOLTAGE_PLUSMINUS_5V: case AD5755_MODE_VOLTAGE_PLUSMINUS_10V: return st->chip_info->has_voltage_out; case AD5755_MODE_CURRENT_4mA_20mA: case AD5755_MODE_CURRENT_0mA_20mA: case AD5755_MODE_CURRENT_0mA_24mA: return true; default: return false; } }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen56100.00%1100.00%
Total56100.00%1100.00%


static int ad5755_setup_pdata(struct iio_dev *indio_dev, const struct ad5755_platform_data *pdata) { struct ad5755_state *st = iio_priv(indio_dev); unsigned int val; unsigned int i; int ret; if (pdata->dc_dc_phase > AD5755_DC_DC_PHASE_90_DEGREE || pdata->dc_dc_freq > AD5755_DC_DC_FREQ_650kHZ || pdata->dc_dc_maxv > AD5755_DC_DC_MAXV_29V5) return -EINVAL; val = pdata->dc_dc_maxv << AD5755_DC_DC_MAXV; val |= pdata->dc_dc_freq << AD5755_DC_DC_FREQ_SHIFT; val |= pdata->dc_dc_phase << AD5755_DC_DC_PHASE_SHIFT; if (pdata->ext_dc_dc_compenstation_resistor) val |= AD5755_EXT_DC_DC_COMP_RES; ret = ad5755_write_ctrl(indio_dev, 0, AD5755_CTRL_REG_DC_DC, val); if (ret < 0) return ret; for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) { val = pdata->dac[i].slew.step_size << AD5755_SLEW_STEP_SIZE_SHIFT; val |= pdata->dac[i].slew.rate << AD5755_SLEW_RATE_SHIFT; if (pdata->dac[i].slew.enable) val |= AD5755_SLEW_ENABLE; ret = ad5755_write_ctrl(indio_dev, i, AD5755_CTRL_REG_SLEW, val); if (ret < 0) return ret; } for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) { if (!ad5755_is_valid_mode(st, pdata->dac[i].mode)) return -EINVAL; val = 0; if (!pdata->dac[i].ext_current_sense_resistor) val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR; if (pdata->dac[i].enable_voltage_overrange) val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN; val |= pdata->dac[i].mode; ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0); if (ret < 0) return ret; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen31699.06%150.00%
dan carpenterdan carpenter30.94%150.00%
Total319100.00%2100.00%


static bool ad5755_is_voltage_mode(enum ad5755_mode mode) { switch (mode) { case AD5755_MODE_VOLTAGE_0V_5V: case AD5755_MODE_VOLTAGE_0V_10V: case AD5755_MODE_VOLTAGE_PLUSMINUS_5V: case AD5755_MODE_VOLTAGE_PLUSMINUS_10V: return true; default: return false; } }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen35100.00%1100.00%
Total35100.00%1100.00%


static int ad5755_init_channels(struct iio_dev *indio_dev, const struct ad5755_platform_data *pdata) { struct ad5755_state *st = iio_priv(indio_dev); struct iio_chan_spec *channels = st->channels; unsigned int i; for (i = 0; i < AD5755_NUM_CHANNELS; ++i) { channels[i] = st->chip_info->channel_template; channels[i].channel = i; channels[i].address = i; if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode)) channels[i].type = IIO_VOLTAGE; else channels[i].type = IIO_CURRENT; } indio_dev->channels = channels; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen128100.00%1100.00%
Total128100.00%1100.00%

#define AD5755_DEFAULT_DAC_PDATA { \ .mode = AD5755_MODE_CURRENT_4mA_20mA, \ .ext_current_sense_resistor = true, \ .enable_voltage_overrange = false, \ .slew = { \ .enable = false, \ .rate = AD5755_SLEW_RATE_64k, \ .step_size = AD5755_SLEW_STEP_SIZE_1, \ }, \ } static const struct ad5755_platform_data ad5755_default_pdata = { .ext_dc_dc_compenstation_resistor = false, .dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE, .dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ, .dc_dc_maxv = AD5755_DC_DC_MAXV_23V, .dac = { [0] = AD5755_DEFAULT_DAC_PDATA, [1] = AD5755_DEFAULT_DAC_PDATA, [2] = AD5755_DEFAULT_DAC_PDATA, [3] = AD5755_DEFAULT_DAC_PDATA, }, };
static int ad5755_probe(struct spi_device *spi) { enum ad5755_type type = spi_get_device_id(spi)->driver_data; const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev); struct iio_dev *indio_dev; struct ad5755_state *st; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (indio_dev == NULL) { dev_err(&spi->dev, "Failed to allocate iio device\n"); return -ENOMEM; } st = iio_priv(indio_dev); spi_set_drvdata(spi, indio_dev); st->chip_info = &ad5755_chip_info_tbl[type]; st->spi = spi; st->pwr_down = 0xf; indio_dev->dev.parent = &spi->dev; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &ad5755_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->num_channels = AD5755_NUM_CHANNELS; if (!pdata) pdata = &ad5755_default_pdata; ret = ad5755_init_channels(indio_dev, pdata); if (ret) return ret; ret = ad5755_setup_pdata(indio_dev, pdata); if (ret) return ret; return devm_iio_device_register(&spi->dev, indio_dev); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen19991.71%125.00%
sachin kamatsachin kamat188.29%375.00%
Total217100.00%4100.00%

static const struct spi_device_id ad5755_id[] = { { "ad5755", ID_AD5755 }, { "ad5755-1", ID_AD5755 }, { "ad5757", ID_AD5757 }, { "ad5735", ID_AD5735 }, { "ad5737", ID_AD5737 }, {} }; MODULE_DEVICE_TABLE(spi, ad5755_id); static struct spi_driver ad5755_driver = { .driver = { .name = "ad5755", }, .probe = ad5755_probe, .id_table = ad5755_id, }; module_spi_driver(ad5755_driver); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Analog Devices AD5755/55-1/57/35/37 DAC"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen277799.04%440.00%
sachin kamatsachin kamat180.64%330.00%
jonathan cameronjonathan cameron60.21%220.00%
dan carpenterdan carpenter30.11%110.00%
Total2804100.00%10100.00%
Directory: drivers/iio/dac
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}