| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * LTC2688 16 channel, 16 bit Voltage Output SoftSpan DAC driver
- *
- * Copyright 2022 Analog Devices Inc.
- */
- #include <linux/bitfield.h>
- #include <linux/bits.h>
- #include <linux/cleanup.h>
- #include <linux/clk.h>
- #include <linux/device.h>
- #include <linux/gpio/consumer.h>
- #include <linux/iio/iio.h>
- #include <linux/limits.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/mod_devicetable.h>
- #include <linux/mutex.h>
- #include <linux/of.h>
- #include <linux/property.h>
- #include <linux/regmap.h>
- #include <linux/regulator/consumer.h>
- #include <linux/spi/spi.h>
- #define LTC2688_DAC_CHANNELS 16
- #define LTC2688_CMD_CH_CODE(x) (0x00 + (x))
- #define LTC2688_CMD_CH_SETTING(x) (0x10 + (x))
- #define LTC2688_CMD_CH_OFFSET(x) (0X20 + (x))
- #define LTC2688_CMD_CH_GAIN(x) (0x30 + (x))
- #define LTC2688_CMD_CH_CODE_UPDATE(x) (0x40 + (x))
- #define LTC2688_CMD_CONFIG 0x70
- #define LTC2688_CMD_POWERDOWN 0x71
- #define LTC2688_CMD_A_B_SELECT 0x72
- #define LTC2688_CMD_SW_TOGGLE 0x73
- #define LTC2688_CMD_TOGGLE_DITHER_EN 0x74
- #define LTC2688_CMD_THERMAL_STAT 0x77
- #define LTC2688_CMD_UPDATE_ALL 0x7C
- #define LTC2688_CMD_NOOP 0xFF
- #define LTC2688_READ_OPERATION 0x80
- /* Channel Settings */
- #define LTC2688_CH_SPAN_MSK GENMASK(2, 0)
- #define LTC2688_CH_OVERRANGE_MSK BIT(3)
- #define LTC2688_CH_TD_SEL_MSK GENMASK(5, 4)
- #define LTC2688_CH_TGP_MAX 3
- #define LTC2688_CH_DIT_PER_MSK GENMASK(8, 6)
- #define LTC2688_CH_DIT_PH_MSK GENMASK(10, 9)
- #define LTC2688_CH_MODE_MSK BIT(11)
- #define LTC2688_DITHER_RAW_MASK GENMASK(15, 2)
- #define LTC2688_CH_CALIBBIAS_MASK GENMASK(15, 2)
- #define LTC2688_DITHER_RAW_MAX_VAL (BIT(14) - 1)
- #define LTC2688_CH_CALIBBIAS_MAX_VAL (BIT(14) - 1)
- /* Configuration register */
- #define LTC2688_CONFIG_RST BIT(15)
- #define LTC2688_CONFIG_EXT_REF BIT(1)
- #define LTC2688_DITHER_FREQ_AVAIL_N 5
- enum {
- LTC2688_SPAN_RANGE_0V_5V,
- LTC2688_SPAN_RANGE_0V_10V,
- LTC2688_SPAN_RANGE_M5V_5V,
- LTC2688_SPAN_RANGE_M10V_10V,
- LTC2688_SPAN_RANGE_M15V_15V,
- LTC2688_SPAN_RANGE_MAX
- };
- enum {
- LTC2688_MODE_DEFAULT,
- LTC2688_MODE_DITHER_TOGGLE,
- };
- struct ltc2688_chan {
- long dither_frequency[LTC2688_DITHER_FREQ_AVAIL_N];
- bool overrange;
- bool toggle_chan;
- u8 mode;
- };
- struct ltc2688_state {
- struct spi_device *spi;
- struct regmap *regmap;
- struct ltc2688_chan channels[LTC2688_DAC_CHANNELS];
- struct iio_chan_spec *iio_chan;
- /* lock to protect against multiple access to the device and shared data */
- struct mutex lock;
- int vref;
- /*
- * DMA (thus cache coherency maintenance) may require the
- * transfer buffers to live in their own cache lines.
- */
- u8 tx_data[6] __aligned(IIO_DMA_MINALIGN);
- u8 rx_data[3];
- };
- static int ltc2688_spi_read(void *context, const void *reg, size_t reg_size,
- void *val, size_t val_size)
- {
- struct ltc2688_state *st = context;
- struct spi_transfer xfers[] = {
- {
- .tx_buf = st->tx_data,
- .len = reg_size + val_size,
- .cs_change = 1,
- }, {
- .tx_buf = st->tx_data + 3,
- .rx_buf = st->rx_data,
- .len = reg_size + val_size,
- },
- };
- int ret;
- memcpy(st->tx_data, reg, reg_size);
- ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
- if (ret)
- return ret;
- memcpy(val, &st->rx_data[1], val_size);
- return 0;
- }
- static int ltc2688_spi_write(void *context, const void *data, size_t count)
- {
- struct ltc2688_state *st = context;
- return spi_write(st->spi, data, count);
- }
- static int ltc2688_span_get(const struct ltc2688_state *st, int c)
- {
- int ret, reg, span;
- ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(c), ®);
- if (ret)
- return ret;
- span = FIELD_GET(LTC2688_CH_SPAN_MSK, reg);
- /* sanity check to make sure we don't get any weird value from the HW */
- if (span >= LTC2688_SPAN_RANGE_MAX)
- return -EIO;
- return span;
- }
- static const int ltc2688_span_helper[LTC2688_SPAN_RANGE_MAX][2] = {
- {0, 5000}, {0, 10000}, {-5000, 5000}, {-10000, 10000}, {-15000, 15000},
- };
- static int ltc2688_scale_get(const struct ltc2688_state *st, int c, int *val)
- {
- const struct ltc2688_chan *chan = &st->channels[c];
- int span, fs;
- span = ltc2688_span_get(st, c);
- if (span < 0)
- return span;
- fs = ltc2688_span_helper[span][1] - ltc2688_span_helper[span][0];
- if (chan->overrange)
- fs = mult_frac(fs, 105, 100);
- *val = DIV_ROUND_CLOSEST(fs * st->vref, 4096);
- return 0;
- }
- static int ltc2688_offset_get(const struct ltc2688_state *st, int c, int *val)
- {
- int span;
- span = ltc2688_span_get(st, c);
- if (span < 0)
- return span;
- if (ltc2688_span_helper[span][0] < 0)
- *val = -32768;
- else
- *val = 0;
- return 0;
- }
- enum {
- LTC2688_INPUT_A,
- LTC2688_INPUT_B,
- LTC2688_INPUT_B_AVAIL,
- LTC2688_DITHER_OFF,
- LTC2688_DITHER_FREQ_AVAIL,
- };
- static int ltc2688_dac_code_write(struct ltc2688_state *st, u32 chan, u32 input,
- u16 code)
- {
- struct ltc2688_chan *c = &st->channels[chan];
- int ret, reg;
- /* 2 LSBs set to 0 if writing dither amplitude */
- if (!c->toggle_chan && input == LTC2688_INPUT_B) {
- if (code > LTC2688_DITHER_RAW_MAX_VAL)
- return -EINVAL;
- code = FIELD_PREP(LTC2688_DITHER_RAW_MASK, code);
- }
- guard(mutex)(&st->lock);
- /* select the correct input register to read from */
- ret = regmap_update_bits(st->regmap, LTC2688_CMD_A_B_SELECT, BIT(chan),
- input << chan);
- if (ret)
- return ret;
- /*
- * If in dither/toggle mode the dac should be updated by an
- * external signal (or sw toggle) and not here.
- */
- if (c->mode == LTC2688_MODE_DEFAULT)
- reg = LTC2688_CMD_CH_CODE_UPDATE(chan);
- else
- reg = LTC2688_CMD_CH_CODE(chan);
- return regmap_write(st->regmap, reg, code);
- }
- static int ltc2688_dac_code_read(struct ltc2688_state *st, u32 chan, u32 input,
- u32 *code)
- {
- struct ltc2688_chan *c = &st->channels[chan];
- int ret;
- guard(mutex)(&st->lock);
- ret = regmap_update_bits(st->regmap, LTC2688_CMD_A_B_SELECT, BIT(chan),
- input << chan);
- if (ret)
- return ret;
- ret = regmap_read(st->regmap, LTC2688_CMD_CH_CODE(chan), code);
- if (ret)
- return ret;
- if (!c->toggle_chan && input == LTC2688_INPUT_B)
- *code = FIELD_GET(LTC2688_DITHER_RAW_MASK, *code);
- return 0;
- }
- static const int ltc2688_raw_range[] = {0, 1, U16_MAX};
- static int ltc2688_read_avail(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- const int **vals, int *type, int *length,
- long info)
- {
- switch (info) {
- case IIO_CHAN_INFO_RAW:
- *vals = ltc2688_raw_range;
- *type = IIO_VAL_INT;
- return IIO_AVAIL_RANGE;
- default:
- return -EINVAL;
- }
- }
- static int ltc2688_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int *val,
- int *val2, long info)
- {
- struct ltc2688_state *st = iio_priv(indio_dev);
- int ret;
- switch (info) {
- case IIO_CHAN_INFO_RAW:
- ret = ltc2688_dac_code_read(st, chan->channel, LTC2688_INPUT_A,
- val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_OFFSET:
- ret = ltc2688_offset_get(st, chan->channel, val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_SCALE:
- ret = ltc2688_scale_get(st, chan->channel, val);
- if (ret)
- return ret;
- *val2 = 16;
- return IIO_VAL_FRACTIONAL_LOG2;
- case IIO_CHAN_INFO_CALIBBIAS:
- ret = regmap_read(st->regmap,
- LTC2688_CMD_CH_OFFSET(chan->channel), val);
- if (ret)
- return ret;
- *val = FIELD_GET(LTC2688_CH_CALIBBIAS_MASK, *val);
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_CALIBSCALE:
- ret = regmap_read(st->regmap,
- LTC2688_CMD_CH_GAIN(chan->channel), val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- }
- static int ltc2688_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int val,
- int val2, long info)
- {
- struct ltc2688_state *st = iio_priv(indio_dev);
- switch (info) {
- case IIO_CHAN_INFO_RAW:
- if (val > U16_MAX || val < 0)
- return -EINVAL;
- return ltc2688_dac_code_write(st, chan->channel,
- LTC2688_INPUT_A, val);
- case IIO_CHAN_INFO_CALIBBIAS:
- if (val > LTC2688_CH_CALIBBIAS_MAX_VAL)
- return -EINVAL;
- return regmap_write(st->regmap,
- LTC2688_CMD_CH_OFFSET(chan->channel),
- FIELD_PREP(LTC2688_CH_CALIBBIAS_MASK, val));
- case IIO_CHAN_INFO_CALIBSCALE:
- return regmap_write(st->regmap,
- LTC2688_CMD_CH_GAIN(chan->channel), val);
- default:
- return -EINVAL;
- }
- }
- static ssize_t ltc2688_dither_toggle_set(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- const char *buf, size_t len)
- {
- struct ltc2688_state *st = iio_priv(indio_dev);
- struct ltc2688_chan *c = &st->channels[chan->channel];
- int ret;
- bool en;
- ret = kstrtobool(buf, &en);
- if (ret)
- return ret;
- guard(mutex)(&st->lock);
- ret = regmap_update_bits(st->regmap, LTC2688_CMD_TOGGLE_DITHER_EN,
- BIT(chan->channel), en << chan->channel);
- if (ret)
- return ret;
- c->mode = en ? LTC2688_MODE_DITHER_TOGGLE : LTC2688_MODE_DEFAULT;
- return len;
- }
- static ssize_t ltc2688_reg_bool_get(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- char *buf)
- {
- const struct ltc2688_state *st = iio_priv(indio_dev);
- int ret;
- u32 val;
- ret = regmap_read(st->regmap, private, &val);
- if (ret)
- return ret;
- return sysfs_emit(buf, "%u\n", !!(val & BIT(chan->channel)));
- }
- static ssize_t ltc2688_reg_bool_set(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- const char *buf, size_t len)
- {
- const struct ltc2688_state *st = iio_priv(indio_dev);
- int ret;
- bool en;
- ret = kstrtobool(buf, &en);
- if (ret)
- return ret;
- ret = regmap_update_bits(st->regmap, private, BIT(chan->channel),
- en << chan->channel);
- if (ret)
- return ret;
- return len;
- }
- static ssize_t ltc2688_dither_freq_avail(const struct ltc2688_state *st,
- const struct ltc2688_chan *chan,
- char *buf)
- {
- int sz = 0;
- u32 f;
- for (f = 0; f < ARRAY_SIZE(chan->dither_frequency); f++)
- sz += sysfs_emit_at(buf, sz, "%ld ", chan->dither_frequency[f]);
- buf[sz - 1] = '\n';
- return sz;
- }
- static ssize_t ltc2688_dither_freq_get(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- char *buf)
- {
- const struct ltc2688_state *st = iio_priv(indio_dev);
- const struct ltc2688_chan *c = &st->channels[chan->channel];
- u32 reg, freq;
- int ret;
- if (private == LTC2688_DITHER_FREQ_AVAIL)
- return ltc2688_dither_freq_avail(st, c, buf);
- ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(chan->channel),
- ®);
- if (ret)
- return ret;
- freq = FIELD_GET(LTC2688_CH_DIT_PER_MSK, reg);
- if (freq >= ARRAY_SIZE(c->dither_frequency))
- return -EIO;
- return sysfs_emit(buf, "%ld\n", c->dither_frequency[freq]);
- }
- static ssize_t ltc2688_dither_freq_set(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- const char *buf, size_t len)
- {
- const struct ltc2688_state *st = iio_priv(indio_dev);
- const struct ltc2688_chan *c = &st->channels[chan->channel];
- long val;
- u32 freq;
- int ret;
- if (private == LTC2688_DITHER_FREQ_AVAIL)
- return -EINVAL;
- ret = kstrtol(buf, 10, &val);
- if (ret)
- return ret;
- for (freq = 0; freq < ARRAY_SIZE(c->dither_frequency); freq++) {
- if (val == c->dither_frequency[freq])
- break;
- }
- if (freq == ARRAY_SIZE(c->dither_frequency))
- return -EINVAL;
- ret = regmap_update_bits(st->regmap,
- LTC2688_CMD_CH_SETTING(chan->channel),
- LTC2688_CH_DIT_PER_MSK,
- FIELD_PREP(LTC2688_CH_DIT_PER_MSK, freq));
- if (ret)
- return ret;
- return len;
- }
- static ssize_t ltc2688_dac_input_read(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- char *buf)
- {
- struct ltc2688_state *st = iio_priv(indio_dev);
- int ret;
- u32 val;
- if (private == LTC2688_INPUT_B_AVAIL)
- return sysfs_emit(buf, "[%u %u %u]\n", ltc2688_raw_range[0],
- ltc2688_raw_range[1],
- ltc2688_raw_range[2] / 4);
- if (private == LTC2688_DITHER_OFF)
- return sysfs_emit(buf, "0\n");
- ret = ltc2688_dac_code_read(st, chan->channel, private, &val);
- if (ret)
- return ret;
- return sysfs_emit(buf, "%u\n", val);
- }
- static ssize_t ltc2688_dac_input_write(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- const char *buf, size_t len)
- {
- struct ltc2688_state *st = iio_priv(indio_dev);
- int ret;
- u16 val;
- if (private == LTC2688_INPUT_B_AVAIL || private == LTC2688_DITHER_OFF)
- return -EINVAL;
- ret = kstrtou16(buf, 10, &val);
- if (ret)
- return ret;
- ret = ltc2688_dac_code_write(st, chan->channel, private, val);
- if (ret)
- return ret;
- return len;
- }
- static int ltc2688_get_dither_phase(struct iio_dev *dev,
- const struct iio_chan_spec *chan)
- {
- struct ltc2688_state *st = iio_priv(dev);
- int ret, regval;
- ret = regmap_read(st->regmap, LTC2688_CMD_CH_SETTING(chan->channel),
- ®val);
- if (ret)
- return ret;
- return FIELD_GET(LTC2688_CH_DIT_PH_MSK, regval);
- }
- static int ltc2688_set_dither_phase(struct iio_dev *dev,
- const struct iio_chan_spec *chan,
- unsigned int phase)
- {
- struct ltc2688_state *st = iio_priv(dev);
- return regmap_update_bits(st->regmap,
- LTC2688_CMD_CH_SETTING(chan->channel),
- LTC2688_CH_DIT_PH_MSK,
- FIELD_PREP(LTC2688_CH_DIT_PH_MSK, phase));
- }
- static int ltc2688_reg_access(struct iio_dev *indio_dev,
- unsigned int reg,
- unsigned int writeval,
- unsigned int *readval)
- {
- struct ltc2688_state *st = iio_priv(indio_dev);
- if (readval)
- return regmap_read(st->regmap, reg, readval);
- return regmap_write(st->regmap, reg, writeval);
- }
- static const char * const ltc2688_dither_phase[] = {
- "0", "1.5708", "3.14159", "4.71239",
- };
- static const struct iio_enum ltc2688_dither_phase_enum = {
- .items = ltc2688_dither_phase,
- .num_items = ARRAY_SIZE(ltc2688_dither_phase),
- .set = ltc2688_set_dither_phase,
- .get = ltc2688_get_dither_phase,
- };
- #define LTC2688_CHAN_EXT_INFO(_name, _what, _shared, _read, _write) { \
- .name = _name, \
- .read = (_read), \
- .write = (_write), \
- .private = (_what), \
- .shared = (_shared), \
- }
- /*
- * For toggle mode we only expose the symbol attr (sw_toggle) in case a TGPx is
- * not provided in dts.
- */
- static const struct iio_chan_spec_ext_info ltc2688_toggle_sym_ext_info[] = {
- LTC2688_CHAN_EXT_INFO("raw0", LTC2688_INPUT_A, IIO_SEPARATE,
- ltc2688_dac_input_read, ltc2688_dac_input_write),
- LTC2688_CHAN_EXT_INFO("raw1", LTC2688_INPUT_B, IIO_SEPARATE,
- ltc2688_dac_input_read, ltc2688_dac_input_write),
- LTC2688_CHAN_EXT_INFO("toggle_en", LTC2688_CMD_TOGGLE_DITHER_EN,
- IIO_SEPARATE, ltc2688_reg_bool_get,
- ltc2688_dither_toggle_set),
- LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
- ltc2688_reg_bool_get, ltc2688_reg_bool_set),
- LTC2688_CHAN_EXT_INFO("symbol", LTC2688_CMD_SW_TOGGLE, IIO_SEPARATE,
- ltc2688_reg_bool_get, ltc2688_reg_bool_set),
- { }
- };
- static const struct iio_chan_spec_ext_info ltc2688_toggle_ext_info[] = {
- LTC2688_CHAN_EXT_INFO("raw0", LTC2688_INPUT_A, IIO_SEPARATE,
- ltc2688_dac_input_read, ltc2688_dac_input_write),
- LTC2688_CHAN_EXT_INFO("raw1", LTC2688_INPUT_B, IIO_SEPARATE,
- ltc2688_dac_input_read, ltc2688_dac_input_write),
- LTC2688_CHAN_EXT_INFO("toggle_en", LTC2688_CMD_TOGGLE_DITHER_EN,
- IIO_SEPARATE, ltc2688_reg_bool_get,
- ltc2688_dither_toggle_set),
- LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
- ltc2688_reg_bool_get, ltc2688_reg_bool_set),
- { }
- };
- static const struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = {
- LTC2688_CHAN_EXT_INFO("dither_raw", LTC2688_INPUT_B, IIO_SEPARATE,
- ltc2688_dac_input_read, ltc2688_dac_input_write),
- LTC2688_CHAN_EXT_INFO("dither_raw_available", LTC2688_INPUT_B_AVAIL,
- IIO_SEPARATE, ltc2688_dac_input_read,
- ltc2688_dac_input_write),
- LTC2688_CHAN_EXT_INFO("dither_offset", LTC2688_DITHER_OFF, IIO_SEPARATE,
- ltc2688_dac_input_read, ltc2688_dac_input_write),
- /*
- * Not IIO_ENUM because the available freq needs to be computed at
- * probe. We could still use it, but it didn't felt much right.
- */
- LTC2688_CHAN_EXT_INFO("dither_frequency", 0, IIO_SEPARATE,
- ltc2688_dither_freq_get, ltc2688_dither_freq_set),
- LTC2688_CHAN_EXT_INFO("dither_frequency_available",
- LTC2688_DITHER_FREQ_AVAIL, IIO_SEPARATE,
- ltc2688_dither_freq_get, ltc2688_dither_freq_set),
- IIO_ENUM("dither_phase", IIO_SEPARATE, <c2688_dither_phase_enum),
- IIO_ENUM_AVAILABLE("dither_phase", IIO_SEPARATE,
- <c2688_dither_phase_enum),
- LTC2688_CHAN_EXT_INFO("dither_en", LTC2688_CMD_TOGGLE_DITHER_EN,
- IIO_SEPARATE, ltc2688_reg_bool_get,
- ltc2688_dither_toggle_set),
- LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
- ltc2688_reg_bool_get, ltc2688_reg_bool_set),
- { }
- };
- static const struct iio_chan_spec_ext_info ltc2688_ext_info[] = {
- LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE,
- ltc2688_reg_bool_get, ltc2688_reg_bool_set),
- { }
- };
- #define LTC2688_CHANNEL(_chan) { \
- .type = IIO_VOLTAGE, \
- .indexed = 1, \
- .output = 1, \
- .channel = (_chan), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
- BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | \
- BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \
- .ext_info = ltc2688_ext_info, \
- }
- static const struct iio_chan_spec ltc2688_channels[] = {
- LTC2688_CHANNEL(0),
- LTC2688_CHANNEL(1),
- LTC2688_CHANNEL(2),
- LTC2688_CHANNEL(3),
- LTC2688_CHANNEL(4),
- LTC2688_CHANNEL(5),
- LTC2688_CHANNEL(6),
- LTC2688_CHANNEL(7),
- LTC2688_CHANNEL(8),
- LTC2688_CHANNEL(9),
- LTC2688_CHANNEL(10),
- LTC2688_CHANNEL(11),
- LTC2688_CHANNEL(12),
- LTC2688_CHANNEL(13),
- LTC2688_CHANNEL(14),
- LTC2688_CHANNEL(15),
- };
- static void ltc2688_clk_disable(void *clk)
- {
- clk_disable_unprepare(clk);
- }
- static const int ltc2688_period[LTC2688_DITHER_FREQ_AVAIL_N] = {
- 4, 8, 16, 32, 64,
- };
- static int ltc2688_tgp_clk_setup(struct ltc2688_state *st,
- struct ltc2688_chan *chan,
- struct fwnode_handle *node, int tgp)
- {
- struct device *dev = &st->spi->dev;
- unsigned long rate;
- struct clk *clk;
- int ret, f;
- clk = devm_get_clk_from_child(dev, to_of_node(node), NULL);
- if (IS_ERR(clk))
- return dev_err_probe(dev, PTR_ERR(clk), "failed to get tgp clk.\n");
- ret = clk_prepare_enable(clk);
- if (ret)
- return dev_err_probe(dev, ret, "failed to enable tgp clk.\n");
- ret = devm_add_action_or_reset(dev, ltc2688_clk_disable, clk);
- if (ret)
- return ret;
- if (chan->toggle_chan)
- return 0;
- /* calculate available dither frequencies */
- rate = clk_get_rate(clk);
- for (f = 0; f < ARRAY_SIZE(chan->dither_frequency); f++)
- chan->dither_frequency[f] = DIV_ROUND_CLOSEST(rate, ltc2688_period[f]);
- return 0;
- }
- static int ltc2688_span_lookup(const struct ltc2688_state *st, int min, int max)
- {
- u32 span;
- for (span = 0; span < ARRAY_SIZE(ltc2688_span_helper); span++) {
- if (min == ltc2688_span_helper[span][0] &&
- max == ltc2688_span_helper[span][1])
- return span;
- }
- return -EINVAL;
- }
- static int ltc2688_channel_config(struct ltc2688_state *st)
- {
- struct device *dev = &st->spi->dev;
- u32 reg, clk_input, val, tmp[2];
- int ret, span;
- device_for_each_child_node_scoped(dev, child) {
- struct ltc2688_chan *chan;
- ret = fwnode_property_read_u32(child, "reg", ®);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to get reg property\n");
- if (reg >= LTC2688_DAC_CHANNELS)
- return dev_err_probe(dev, -EINVAL,
- "reg bigger than: %d\n",
- LTC2688_DAC_CHANNELS);
- val = 0;
- chan = &st->channels[reg];
- if (fwnode_property_read_bool(child, "adi,toggle-mode")) {
- chan->toggle_chan = true;
- /* assume sw toggle ABI */
- st->iio_chan[reg].ext_info = ltc2688_toggle_sym_ext_info;
- /*
- * Clear IIO_CHAN_INFO_RAW bit as toggle channels expose
- * out_voltage_raw{0|1} files.
- */
- __clear_bit(IIO_CHAN_INFO_RAW,
- &st->iio_chan[reg].info_mask_separate);
- }
- ret = fwnode_property_read_u32_array(child, "adi,output-range-microvolt",
- tmp, ARRAY_SIZE(tmp));
- if (!ret) {
- span = ltc2688_span_lookup(st, (int)tmp[0] / 1000,
- tmp[1] / 1000);
- if (span < 0)
- return dev_err_probe(dev, span,
- "output range not valid:[%d %d]\n",
- tmp[0], tmp[1]);
- val |= FIELD_PREP(LTC2688_CH_SPAN_MSK, span);
- }
- ret = fwnode_property_read_u32(child, "adi,toggle-dither-input",
- &clk_input);
- if (!ret) {
- if (clk_input >= LTC2688_CH_TGP_MAX) {
- return dev_err_probe(dev, -EINVAL,
- "toggle-dither-input inv value(%d)\n",
- clk_input);
- }
- ret = ltc2688_tgp_clk_setup(st, chan, child, clk_input);
- if (ret)
- return ret;
- /*
- * 0 means software toggle which is the default mode.
- * Hence the +1.
- */
- val |= FIELD_PREP(LTC2688_CH_TD_SEL_MSK, clk_input + 1);
- /*
- * If a TGPx is given, we automatically assume a dither
- * capable channel (unless toggle is already enabled).
- * On top of this we just set here the dither bit in the
- * channel settings. It won't have any effect until the
- * global toggle/dither bit is enabled.
- */
- if (!chan->toggle_chan) {
- val |= FIELD_PREP(LTC2688_CH_MODE_MSK, 1);
- st->iio_chan[reg].ext_info = ltc2688_dither_ext_info;
- } else {
- /* wait, no sw toggle after all */
- st->iio_chan[reg].ext_info = ltc2688_toggle_ext_info;
- }
- }
- if (fwnode_property_read_bool(child, "adi,overrange")) {
- chan->overrange = true;
- val |= LTC2688_CH_OVERRANGE_MSK;
- }
- if (!val)
- continue;
- ret = regmap_write(st->regmap, LTC2688_CMD_CH_SETTING(reg),
- val);
- if (ret)
- return dev_err_probe(dev, ret,
- "failed to set chan settings\n");
- }
- return 0;
- }
- static int ltc2688_setup(struct ltc2688_state *st, bool has_external_vref)
- {
- struct device *dev = &st->spi->dev;
- struct gpio_desc *gpio;
- int ret;
- /*
- * If we have a reset pin, use that to reset the board, If not, use
- * the reset bit.
- */
- gpio = devm_gpiod_get_optional(dev, "clr", GPIOD_OUT_HIGH);
- if (IS_ERR(gpio))
- return dev_err_probe(dev, PTR_ERR(gpio), "Failed to get reset gpio");
- if (gpio) {
- usleep_range(1000, 1200);
- /* bring device out of reset */
- gpiod_set_value_cansleep(gpio, 0);
- } else {
- ret = regmap_set_bits(st->regmap, LTC2688_CMD_CONFIG,
- LTC2688_CONFIG_RST);
- if (ret)
- return ret;
- }
- usleep_range(10000, 12000);
- /*
- * Duplicate the default channel configuration as it can change during
- * @ltc2688_channel_config()
- */
- st->iio_chan = devm_kmemdup(dev, ltc2688_channels,
- sizeof(ltc2688_channels), GFP_KERNEL);
- if (!st->iio_chan)
- return -ENOMEM;
- ret = ltc2688_channel_config(st);
- if (ret)
- return ret;
- if (!has_external_vref)
- return 0;
- return regmap_set_bits(st->regmap, LTC2688_CMD_CONFIG,
- LTC2688_CONFIG_EXT_REF);
- }
- static bool ltc2688_reg_readable(struct device *dev, unsigned int reg)
- {
- switch (reg) {
- case LTC2688_CMD_CH_CODE(0) ... LTC2688_CMD_CH_GAIN(15):
- return true;
- case LTC2688_CMD_CONFIG ... LTC2688_CMD_THERMAL_STAT:
- return true;
- default:
- return false;
- }
- }
- static bool ltc2688_reg_writable(struct device *dev, unsigned int reg)
- {
- /*
- * There's a jump from 0x76 to 0x78 in the write codes and the thermal
- * status code is 0x77 (which is read only) so that we need to check
- * that special condition.
- */
- if (reg <= LTC2688_CMD_UPDATE_ALL && reg != LTC2688_CMD_THERMAL_STAT)
- return true;
- return false;
- }
- static const struct regmap_bus ltc2688_regmap_bus = {
- .read = ltc2688_spi_read,
- .write = ltc2688_spi_write,
- .read_flag_mask = LTC2688_READ_OPERATION,
- .reg_format_endian_default = REGMAP_ENDIAN_BIG,
- .val_format_endian_default = REGMAP_ENDIAN_BIG,
- };
- static const struct regmap_config ltc2688_regmap_config = {
- .reg_bits = 8,
- .val_bits = 16,
- .readable_reg = ltc2688_reg_readable,
- .writeable_reg = ltc2688_reg_writable,
- /* ignoring the no op command */
- .max_register = LTC2688_CMD_UPDATE_ALL,
- };
- static const struct iio_info ltc2688_info = {
- .write_raw = ltc2688_write_raw,
- .read_raw = ltc2688_read_raw,
- .read_avail = ltc2688_read_avail,
- .debugfs_reg_access = ltc2688_reg_access,
- };
- static int ltc2688_probe(struct spi_device *spi)
- {
- static const char * const regulators[] = { "vcc", "iovcc" };
- struct ltc2688_state *st;
- struct iio_dev *indio_dev;
- struct device *dev = &spi->dev;
- bool has_external_vref;
- int ret;
- indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
- if (!indio_dev)
- return -ENOMEM;
- st = iio_priv(indio_dev);
- st->spi = spi;
- /* Just write this once. No need to do it in every regmap read. */
- st->tx_data[3] = LTC2688_CMD_NOOP;
- ret = devm_mutex_init(dev, &st->lock);
- if (ret)
- return ret;
- st->regmap = devm_regmap_init(dev, <c2688_regmap_bus, st,
- <c2688_regmap_config);
- if (IS_ERR(st->regmap))
- return dev_err_probe(dev, PTR_ERR(st->regmap),
- "Failed to init regmap");
- ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators),
- regulators);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to enable regulators\n");
- ret = devm_regulator_get_enable_read_voltage(dev, "vref");
- if (ret < 0 && ret != -ENODEV)
- return dev_err_probe(dev, ret,
- "Failed to get vref regulator voltage\n");
- has_external_vref = ret != -ENODEV;
- st->vref = has_external_vref ? ret / 1000 : 0;
- ret = ltc2688_setup(st, has_external_vref);
- if (ret)
- return ret;
- indio_dev->name = "ltc2688";
- indio_dev->info = <c2688_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = st->iio_chan;
- indio_dev->num_channels = ARRAY_SIZE(ltc2688_channels);
- return devm_iio_device_register(dev, indio_dev);
- }
- static const struct of_device_id ltc2688_of_id[] = {
- { .compatible = "adi,ltc2688" },
- { }
- };
- MODULE_DEVICE_TABLE(of, ltc2688_of_id);
- static const struct spi_device_id ltc2688_id[] = {
- { "ltc2688" },
- { }
- };
- MODULE_DEVICE_TABLE(spi, ltc2688_id);
- static struct spi_driver ltc2688_driver = {
- .driver = {
- .name = "ltc2688",
- .of_match_table = ltc2688_of_id,
- },
- .probe = ltc2688_probe,
- .id_table = ltc2688_id,
- };
- module_spi_driver(ltc2688_driver);
- MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>");
- MODULE_DESCRIPTION("Analog Devices LTC2688 DAC");
- MODULE_LICENSE("GPL");
|