Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Lars-Peter Clausen | 3065 | 76.78% | 6 | 30.00% |
Sean Nyekjaer | 811 | 20.32% | 1 | 5.00% |
Jonathan Cameron | 51 | 1.28% | 4 | 20.00% |
Sergiu Cuciurean | 28 | 0.70% | 1 | 5.00% |
Sachin Kamat | 25 | 0.63% | 3 | 15.00% |
Colin Ian King | 6 | 0.15% | 2 | 10.00% |
Dan Carpenter | 3 | 0.08% | 1 | 5.00% |
Thomas Gleixner | 2 | 0.05% | 1 | 5.00% |
Lee Jones | 1 | 0.03% | 1 | 5.00% |
Total | 3992 | 20 |
// SPDX-License-Identifier: GPL-2.0-only /* * AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver * * Copyright 2012 Analog Devices Inc. */ #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/property.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.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) enum ad5755_mode { AD5755_MODE_VOLTAGE_0V_5V = 0, AD5755_MODE_VOLTAGE_0V_10V = 1, AD5755_MODE_VOLTAGE_PLUSMINUS_5V = 2, AD5755_MODE_VOLTAGE_PLUSMINUS_10V = 3, AD5755_MODE_CURRENT_4mA_20mA = 4, AD5755_MODE_CURRENT_0mA_20mA = 5, AD5755_MODE_CURRENT_0mA_24mA = 6, }; enum ad5755_dc_dc_phase { AD5755_DC_DC_PHASE_ALL_SAME_EDGE = 0, AD5755_DC_DC_PHASE_A_B_SAME_EDGE_C_D_OPP_EDGE = 1, AD5755_DC_DC_PHASE_A_C_SAME_EDGE_B_D_OPP_EDGE = 2, AD5755_DC_DC_PHASE_90_DEGREE = 3, }; enum ad5755_dc_dc_freq { AD5755_DC_DC_FREQ_250kHZ = 0, AD5755_DC_DC_FREQ_410kHZ = 1, AD5755_DC_DC_FREQ_650kHZ = 2, }; enum ad5755_dc_dc_maxv { AD5755_DC_DC_MAXV_23V = 0, AD5755_DC_DC_MAXV_24V5 = 1, AD5755_DC_DC_MAXV_27V = 2, AD5755_DC_DC_MAXV_29V5 = 3, }; enum ad5755_slew_rate { AD5755_SLEW_RATE_64k = 0, AD5755_SLEW_RATE_32k = 1, AD5755_SLEW_RATE_16k = 2, AD5755_SLEW_RATE_8k = 3, AD5755_SLEW_RATE_4k = 4, AD5755_SLEW_RATE_2k = 5, AD5755_SLEW_RATE_1k = 6, AD5755_SLEW_RATE_500 = 7, AD5755_SLEW_RATE_250 = 8, AD5755_SLEW_RATE_125 = 9, AD5755_SLEW_RATE_64 = 10, AD5755_SLEW_RATE_32 = 11, AD5755_SLEW_RATE_16 = 12, AD5755_SLEW_RATE_8 = 13, AD5755_SLEW_RATE_4 = 14, AD5755_SLEW_RATE_0_5 = 15, }; enum ad5755_slew_step_size { AD5755_SLEW_STEP_SIZE_1 = 0, AD5755_SLEW_STEP_SIZE_2 = 1, AD5755_SLEW_STEP_SIZE_4 = 2, AD5755_SLEW_STEP_SIZE_8 = 3, AD5755_SLEW_STEP_SIZE_16 = 4, AD5755_SLEW_STEP_SIZE_32 = 5, AD5755_SLEW_STEP_SIZE_64 = 6, AD5755_SLEW_STEP_SIZE_128 = 7, AD5755_SLEW_STEP_SIZE_256 = 8, }; /** * struct ad5755_platform_data - AD5755 DAC driver platform data * @ext_dc_dc_compenstation_resistor: Whether an external DC-DC converter * compensation register is used. * @dc_dc_phase: DC-DC converter phase. * @dc_dc_freq: DC-DC converter frequency. * @dc_dc_maxv: DC-DC maximum allowed boost voltage. * @dac: Per DAC instance parameters. * @dac.mode: The mode to be used for the DAC output. * @dac.ext_current_sense_resistor: Whether an external current sense resistor * is used. * @dac.enable_voltage_overrange: Whether to enable 20% voltage output overrange. * @dac.slew.enable: Whether to enable digital slew. * @dac.slew.rate: Slew rate of the digital slew. * @dac.slew.step_size: Slew step size of the digital slew. **/ struct ad5755_platform_data { bool ext_dc_dc_compenstation_resistor; enum ad5755_dc_dc_phase dc_dc_phase; enum ad5755_dc_dc_freq dc_dc_freq; enum ad5755_dc_dc_maxv dc_dc_maxv; struct { enum ad5755_mode mode; bool ext_current_sense_resistor; bool enable_voltage_overrange; struct { bool enable; enum ad5755_slew_rate rate; enum ad5755_slew_step_size step_size; } slew; } dac[4]; }; /** * 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 * @lock: lock to protect the data buffer during SPI ops * @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]; struct mutex lock; /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. */ union { __be32 d32; u8 d8[4]; } data[2] __aligned(IIO_DMA_MINALIGN); }; enum ad5755_type { ID_AD5755, ID_AD5757, ID_AD5735, ID_AD5737, }; static const int ad5755_dcdc_freq_table[][2] = { { 250000, AD5755_DC_DC_FREQ_250kHZ }, { 410000, AD5755_DC_DC_FREQ_410kHZ }, { 650000, AD5755_DC_DC_FREQ_650kHZ } }; static const int ad5755_dcdc_maxv_table[][2] = { { 23000000, AD5755_DC_DC_MAXV_23V }, { 24500000, AD5755_DC_DC_MAXV_24V5 }, { 27000000, AD5755_DC_DC_MAXV_27V }, { 29500000, AD5755_DC_DC_MAXV_29V5 }, }; static const int ad5755_slew_rate_table[][2] = { { 64000, AD5755_SLEW_RATE_64k }, { 32000, AD5755_SLEW_RATE_32k }, { 16000, AD5755_SLEW_RATE_16k }, { 8000, AD5755_SLEW_RATE_8k }, { 4000, AD5755_SLEW_RATE_4k }, { 2000, AD5755_SLEW_RATE_2k }, { 1000, AD5755_SLEW_RATE_1k }, { 500, AD5755_SLEW_RATE_500 }, { 250, AD5755_SLEW_RATE_250 }, { 125, AD5755_SLEW_RATE_125 }, { 64, AD5755_SLEW_RATE_64 }, { 32, AD5755_SLEW_RATE_32 }, { 16, AD5755_SLEW_RATE_16 }, { 8, AD5755_SLEW_RATE_8 }, { 4, AD5755_SLEW_RATE_4 }, { 0, AD5755_SLEW_RATE_0_5 }, }; static const int ad5755_slew_step_table[][2] = { { 256, AD5755_SLEW_STEP_SIZE_256 }, { 128, AD5755_SLEW_STEP_SIZE_128 }, { 64, AD5755_SLEW_STEP_SIZE_64 }, { 32, AD5755_SLEW_STEP_SIZE_32 }, { 16, AD5755_SLEW_STEP_SIZE_16 }, { 4, AD5755_SLEW_STEP_SIZE_4 }, { 2, AD5755_SLEW_STEP_SIZE_2 }, { 1, AD5755_SLEW_STEP_SIZE_1 }, }; 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); } 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); } static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg, unsigned int val) { struct ad5755_state *st = iio_priv(indio_dev); int ret; mutex_lock(&st->lock); ret = ad5755_write_unlocked(indio_dev, reg, val); mutex_unlock(&st->lock); return ret; } static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel, unsigned int reg, unsigned int val) { struct ad5755_state *st = iio_priv(indio_dev); int ret; mutex_lock(&st->lock); ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val); mutex_unlock(&st->lock); return ret; } 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(&st->lock); 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(&st->lock); return ret; } 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; } 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(&st->lock); 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(&st->lock); return 0; } 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]; } 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); } 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; } 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, ®, &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; } 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, ®, &shift, &offset); if (ret) return ret; val <<= shift; val += offset; if (val < 0 || val > 0xffff) return -EINVAL; return ad5755_write(indio_dev, reg, val); } 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 sysfs_emit(buf, "%d\n", (bool)(st->pwr_down & (1 << chan->channel))); } 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 = kstrtobool(buf, &pwr_down); if (ret) return ret; ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down); return ret ? ret : len; } static const struct iio_info ad5755_info = { .read_raw = ad5755_read_raw, .write_raw = ad5755_write_raw, }; 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; } } 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; } 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; } } 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; } #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 struct ad5755_platform_data *ad5755_parse_fw(struct device *dev) { struct fwnode_handle *pp; struct ad5755_platform_data *pdata; unsigned int tmp; unsigned int tmparray[3]; int devnr, i; if (!dev_fwnode(dev)) return NULL; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; pdata->ext_dc_dc_compenstation_resistor = device_property_read_bool(dev, "adi,ext-dc-dc-compenstation-resistor"); pdata->dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE; device_property_read_u32(dev, "adi,dc-dc-phase", &pdata->dc_dc_phase); pdata->dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ; if (!device_property_read_u32(dev, "adi,dc-dc-freq-hz", &tmp)) { for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_freq_table); i++) { if (tmp == ad5755_dcdc_freq_table[i][0]) { pdata->dc_dc_freq = ad5755_dcdc_freq_table[i][1]; break; } } if (i == ARRAY_SIZE(ad5755_dcdc_freq_table)) dev_err(dev, "adi,dc-dc-freq out of range selecting 410kHz\n"); } pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V; if (!device_property_read_u32(dev, "adi,dc-dc-max-microvolt", &tmp)) { for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_maxv_table); i++) { if (tmp == ad5755_dcdc_maxv_table[i][0]) { pdata->dc_dc_maxv = ad5755_dcdc_maxv_table[i][1]; break; } } if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table)) dev_err(dev, "adi,dc-dc-maxv out of range selecting 23V\n"); } devnr = 0; device_for_each_child_node(dev, pp) { if (devnr >= AD5755_NUM_CHANNELS) { dev_err(dev, "There are too many channels defined in DT\n"); goto error_out; } pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA; fwnode_property_read_u32(pp, "adi,mode", &pdata->dac[devnr].mode); pdata->dac[devnr].ext_current_sense_resistor = fwnode_property_read_bool(pp, "adi,ext-current-sense-resistor"); pdata->dac[devnr].enable_voltage_overrange = fwnode_property_read_bool(pp, "adi,enable-voltage-overrange"); if (!fwnode_property_read_u32_array(pp, "adi,slew", tmparray, 3)) { pdata->dac[devnr].slew.enable = tmparray[0]; pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k; for (i = 0; i < ARRAY_SIZE(ad5755_slew_rate_table); i++) { if (tmparray[1] == ad5755_slew_rate_table[i][0]) { pdata->dac[devnr].slew.rate = ad5755_slew_rate_table[i][1]; break; } } if (i == ARRAY_SIZE(ad5755_slew_rate_table)) dev_err(dev, "channel %d slew rate out of range selecting 64kHz\n", devnr); pdata->dac[devnr].slew.step_size = AD5755_SLEW_STEP_SIZE_1; for (i = 0; i < ARRAY_SIZE(ad5755_slew_step_table); i++) { if (tmparray[2] == ad5755_slew_step_table[i][0]) { pdata->dac[devnr].slew.step_size = ad5755_slew_step_table[i][1]; break; } } if (i == ARRAY_SIZE(ad5755_slew_step_table)) dev_err(dev, "channel %d slew step size out of range selecting 1 LSB\n", devnr); } else { pdata->dac[devnr].slew.enable = false; pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k; pdata->dac[devnr].slew.step_size = AD5755_SLEW_STEP_SIZE_1; } devnr++; } return pdata; error_out: devm_kfree(dev, pdata); return NULL; } 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; 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->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; mutex_init(&st->lock); pdata = ad5755_parse_fw(&spi->dev); if (!pdata) { dev_warn(&spi->dev, "no firmware provided parameters? using default\n"); 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); } 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 const struct of_device_id ad5755_of_match[] = { { .compatible = "adi,ad5755" }, { .compatible = "adi,ad5755-1" }, { .compatible = "adi,ad5757" }, { .compatible = "adi,ad5735" }, { .compatible = "adi,ad5737" }, { } }; MODULE_DEVICE_TABLE(of, ad5755_of_match); 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");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1