| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Analog Devices AD3552R |
| * Digital to Analog converter driver |
| * |
| * Copyright 2021 Analog Devices Inc. |
| */ |
| #include <linux/unaligned.h> |
| #include <linux/bitfield.h> |
| #include <linux/device.h> |
| #include <linux/iio/triggered_buffer.h> |
| #include <linux/iio/trigger_consumer.h> |
| #include <linux/iopoll.h> |
| #include <linux/kernel.h> |
| #include <linux/spi/spi.h> |
| |
| #include "ad3552r.h" |
| |
| struct ad3552r_desc { |
| const struct ad3552r_model_data *model_data; |
| /* Used to look the spi bus for atomic operations where needed */ |
| struct mutex lock; |
| struct gpio_desc *gpio_reset; |
| struct gpio_desc *gpio_ldac; |
| struct spi_device *spi; |
| struct ad3552r_ch_data ch_data[AD3552R_MAX_CH]; |
| struct iio_chan_spec channels[AD3552R_MAX_CH + 1]; |
| unsigned long enabled_ch; |
| unsigned int num_ch; |
| }; |
| |
| static u8 _ad3552r_reg_len(u8 addr) |
| { |
| switch (addr) { |
| case AD3552R_REG_ADDR_HW_LDAC_16B: |
| case AD3552R_REG_ADDR_CH_SELECT_16B: |
| case AD3552R_REG_ADDR_SW_LDAC_16B: |
| case AD3552R_REG_ADDR_HW_LDAC_24B: |
| case AD3552R_REG_ADDR_CH_SELECT_24B: |
| case AD3552R_REG_ADDR_SW_LDAC_24B: |
| return 1; |
| default: |
| break; |
| } |
| |
| if (addr > AD3552R_REG_ADDR_HW_LDAC_24B) |
| return 3; |
| if (addr > AD3552R_REG_ADDR_HW_LDAC_16B) |
| return 2; |
| |
| return 1; |
| } |
| |
| /* SPI transfer to device */ |
| static int ad3552r_transfer(struct ad3552r_desc *dac, u8 addr, u32 len, |
| u8 *data, bool is_read) |
| { |
| /* Maximum transfer: Addr (1B) + 2 * (Data Reg (3B)) + SW LDAC(1B) */ |
| u8 buf[8]; |
| |
| buf[0] = addr & AD3552R_ADDR_MASK; |
| buf[0] |= is_read ? AD3552R_READ_BIT : 0; |
| if (is_read) |
| return spi_write_then_read(dac->spi, buf, 1, data, len); |
| |
| memcpy(buf + 1, data, len); |
| return spi_write_then_read(dac->spi, buf, len + 1, NULL, 0); |
| } |
| |
| static int ad3552r_write_reg(struct ad3552r_desc *dac, u8 addr, u16 val) |
| { |
| u8 reg_len; |
| u8 buf[AD3552R_MAX_REG_SIZE] = { 0 }; |
| |
| reg_len = _ad3552r_reg_len(addr); |
| if (reg_len == 2) |
| /* Only DAC register are 2 bytes wide */ |
| val &= AD3552R_MASK_DAC_12B; |
| if (reg_len == 1) |
| buf[0] = val & 0xFF; |
| else |
| /* reg_len can be 2 or 3, but 3rd bytes needs to be set to 0 */ |
| put_unaligned_be16(val, buf); |
| |
| return ad3552r_transfer(dac, addr, reg_len, buf, false); |
| } |
| |
| static int ad3552r_read_reg(struct ad3552r_desc *dac, u8 addr, u16 *val) |
| { |
| int err; |
| u8 reg_len, buf[AD3552R_MAX_REG_SIZE] = { 0 }; |
| |
| reg_len = _ad3552r_reg_len(addr); |
| err = ad3552r_transfer(dac, addr, reg_len, buf, true); |
| if (err) |
| return err; |
| |
| if (reg_len == 1) |
| *val = buf[0]; |
| else |
| /* reg_len can be 2 or 3, but only first 2 bytes are relevant */ |
| *val = get_unaligned_be16(buf); |
| |
| return 0; |
| } |
| |
| /* Update field of a register, shift val if needed */ |
| static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask, |
| u16 val) |
| { |
| int ret; |
| u16 reg; |
| |
| ret = ad3552r_read_reg(dac, addr, ®); |
| if (ret < 0) |
| return ret; |
| |
| reg &= ~mask; |
| reg |= val; |
| |
| return ad3552r_write_reg(dac, addr, reg); |
| } |
| |
| #define AD3552R_CH_DAC(_idx) ((struct iio_chan_spec) { \ |
| .type = IIO_VOLTAGE, \ |
| .output = true, \ |
| .indexed = true, \ |
| .channel = _idx, \ |
| .scan_index = _idx, \ |
| .scan_type = { \ |
| .sign = 'u', \ |
| .realbits = 16, \ |
| .storagebits = 16, \ |
| .endianness = IIO_BE, \ |
| }, \ |
| .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
| BIT(IIO_CHAN_INFO_SCALE) | \ |
| BIT(IIO_CHAN_INFO_ENABLE) | \ |
| BIT(IIO_CHAN_INFO_OFFSET), \ |
| }) |
| |
| static int ad3552r_read_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| int *val, |
| int *val2, |
| long mask) |
| { |
| struct ad3552r_desc *dac = iio_priv(indio_dev); |
| u16 tmp_val; |
| int err; |
| u8 ch = chan->channel; |
| |
| switch (mask) { |
| case IIO_CHAN_INFO_RAW: |
| mutex_lock(&dac->lock); |
| err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_CH_DAC_24B(ch), |
| &tmp_val); |
| mutex_unlock(&dac->lock); |
| if (err < 0) |
| return err; |
| *val = tmp_val; |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_ENABLE: |
| mutex_lock(&dac->lock); |
| err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG, |
| &tmp_val); |
| mutex_unlock(&dac->lock); |
| if (err < 0) |
| return err; |
| *val = !((tmp_val & AD3552R_MASK_CH_DAC_POWERDOWN(ch)) >> |
| __ffs(AD3552R_MASK_CH_DAC_POWERDOWN(ch))); |
| return IIO_VAL_INT; |
| case IIO_CHAN_INFO_SCALE: |
| *val = dac->ch_data[ch].scale_int; |
| *val2 = dac->ch_data[ch].scale_dec; |
| return IIO_VAL_INT_PLUS_MICRO; |
| case IIO_CHAN_INFO_OFFSET: |
| *val = dac->ch_data[ch].offset_int; |
| *val2 = dac->ch_data[ch].offset_dec; |
| return IIO_VAL_INT_PLUS_MICRO; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int ad3552r_write_raw(struct iio_dev *indio_dev, |
| struct iio_chan_spec const *chan, |
| int val, |
| int val2, |
| long mask) |
| { |
| struct ad3552r_desc *dac = iio_priv(indio_dev); |
| int err; |
| |
| mutex_lock(&dac->lock); |
| switch (mask) { |
| case IIO_CHAN_INFO_RAW: |
| err = ad3552r_write_reg(dac, |
| AD3552R_REG_ADDR_CH_DAC_24B(chan->channel), |
| val); |
| break; |
| case IIO_CHAN_INFO_ENABLE: |
| if (chan->channel == 0) |
| val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(0), !val); |
| else |
| val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(1), !val); |
| |
| err = ad3552r_update_reg_field(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG, |
| AD3552R_MASK_CH_DAC_POWERDOWN(chan->channel), |
| val); |
| break; |
| default: |
| err = -EINVAL; |
| break; |
| } |
| mutex_unlock(&dac->lock); |
| |
| return err; |
| } |
| |
| static const struct iio_info ad3552r_iio_info = { |
| .read_raw = ad3552r_read_raw, |
| .write_raw = ad3552r_write_raw |
| }; |
| |
| static int32_t ad3552r_trigger_hw_ldac(struct gpio_desc *ldac) |
| { |
| gpiod_set_value_cansleep(ldac, 0); |
| usleep_range(AD3552R_LDAC_PULSE_US, AD3552R_LDAC_PULSE_US + 10); |
| gpiod_set_value_cansleep(ldac, 1); |
| |
| return 0; |
| } |
| |
| static int ad3552r_write_all_channels(struct ad3552r_desc *dac, u8 *data) |
| { |
| int err, len; |
| u8 addr, buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE + 1]; |
| |
| addr = AD3552R_REG_ADDR_CH_INPUT_24B(1); |
| /* CH1 */ |
| memcpy(buff, data + 2, 2); |
| buff[2] = 0; |
| /* CH0 */ |
| memcpy(buff + 3, data, 2); |
| buff[5] = 0; |
| len = 6; |
| if (!dac->gpio_ldac) { |
| /* Software LDAC */ |
| buff[6] = AD3552R_MASK_ALL_CH; |
| ++len; |
| } |
| err = ad3552r_transfer(dac, addr, len, buff, false); |
| if (err) |
| return err; |
| |
| if (dac->gpio_ldac) |
| return ad3552r_trigger_hw_ldac(dac->gpio_ldac); |
| |
| return 0; |
| } |
| |
| static int ad3552r_write_codes(struct ad3552r_desc *dac, u32 mask, u8 *data) |
| { |
| int err; |
| u8 addr, buff[AD3552R_MAX_REG_SIZE]; |
| |
| if (mask == AD3552R_MASK_ALL_CH) { |
| if (memcmp(data, data + 2, 2) != 0) |
| return ad3552r_write_all_channels(dac, data); |
| |
| addr = AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B; |
| } else { |
| addr = AD3552R_REG_ADDR_CH_INPUT_24B(__ffs(mask)); |
| } |
| |
| memcpy(buff, data, 2); |
| buff[2] = 0; |
| err = ad3552r_transfer(dac, addr, 3, data, false); |
| if (err) |
| return err; |
| |
| if (dac->gpio_ldac) |
| return ad3552r_trigger_hw_ldac(dac->gpio_ldac); |
| |
| return ad3552r_write_reg(dac, AD3552R_REG_ADDR_SW_LDAC_24B, mask); |
| } |
| |
| static irqreturn_t ad3552r_trigger_handler(int irq, void *p) |
| { |
| struct iio_poll_func *pf = p; |
| struct iio_dev *indio_dev = pf->indio_dev; |
| struct iio_buffer *buf = indio_dev->buffer; |
| struct ad3552r_desc *dac = iio_priv(indio_dev); |
| /* Maximum size of a scan */ |
| u8 buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE] = { }; |
| int err; |
| |
| err = iio_pop_from_buffer(buf, buff); |
| if (err) |
| goto end; |
| |
| mutex_lock(&dac->lock); |
| ad3552r_write_codes(dac, *indio_dev->active_scan_mask, buff); |
| mutex_unlock(&dac->lock); |
| end: |
| iio_trigger_notify_done(indio_dev->trig); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int ad3552r_check_scratch_pad(struct ad3552r_desc *dac) |
| { |
| const u16 val1 = AD3552R_SCRATCH_PAD_TEST_VAL1; |
| const u16 val2 = AD3552R_SCRATCH_PAD_TEST_VAL2; |
| u16 val; |
| int err; |
| |
| err = ad3552r_write_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, val1); |
| if (err < 0) |
| return err; |
| |
| err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, &val); |
| if (err < 0) |
| return err; |
| |
| if (val1 != val) |
| return -ENODEV; |
| |
| err = ad3552r_write_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, val2); |
| if (err < 0) |
| return err; |
| |
| err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, &val); |
| if (err < 0) |
| return err; |
| |
| if (val2 != val) |
| return -ENODEV; |
| |
| return 0; |
| } |
| |
| struct reg_addr_pool { |
| struct ad3552r_desc *dac; |
| u8 addr; |
| }; |
| |
| static int ad3552r_read_reg_wrapper(struct reg_addr_pool *addr) |
| { |
| int err; |
| u16 val; |
| |
| err = ad3552r_read_reg(addr->dac, addr->addr, &val); |
| if (err) |
| return err; |
| |
| return val; |
| } |
| |
| static int ad3552r_reset(struct ad3552r_desc *dac) |
| { |
| struct reg_addr_pool addr; |
| int ret; |
| int val; |
| |
| dac->gpio_reset = devm_gpiod_get_optional(&dac->spi->dev, "reset", |
| GPIOD_OUT_LOW); |
| if (IS_ERR(dac->gpio_reset)) |
| return dev_err_probe(&dac->spi->dev, PTR_ERR(dac->gpio_reset), |
| "Error while getting gpio reset"); |
| |
| if (dac->gpio_reset) { |
| /* Perform hardware reset */ |
| usleep_range(10, 20); |
| gpiod_set_value_cansleep(dac->gpio_reset, 1); |
| } else { |
| /* Perform software reset if no GPIO provided */ |
| ret = ad3552r_update_reg_field(dac, |
| AD3552R_REG_ADDR_INTERFACE_CONFIG_A, |
| AD3552R_MASK_SOFTWARE_RESET, |
| AD3552R_MASK_SOFTWARE_RESET); |
| if (ret < 0) |
| return ret; |
| |
| } |
| |
| addr.dac = dac; |
| addr.addr = AD3552R_REG_ADDR_INTERFACE_CONFIG_B; |
| ret = readx_poll_timeout(ad3552r_read_reg_wrapper, &addr, val, |
| val == AD3552R_DEFAULT_CONFIG_B_VALUE || |
| val < 0, |
| 5000, 50000); |
| if (val < 0) |
| ret = val; |
| if (ret) { |
| dev_err(&dac->spi->dev, "Error while resetting"); |
| return ret; |
| } |
| |
| ret = readx_poll_timeout(ad3552r_read_reg_wrapper, &addr, val, |
| !(val & AD3552R_MASK_INTERFACE_NOT_READY) || |
| val < 0, |
| 5000, 50000); |
| if (val < 0) |
| ret = val; |
| if (ret) { |
| dev_err(&dac->spi->dev, "Error while resetting"); |
| return ret; |
| } |
| |
| /* Clear reset error flag, see ad3552r manual, rev B table 38. */ |
| ret = ad3552r_write_reg(dac, AD3552R_REG_ADDR_ERR_STATUS, |
| AD3552R_MASK_RESET_STATUS); |
| if (ret) |
| return ret; |
| |
| return ad3552r_update_reg_field(dac, |
| AD3552R_REG_ADDR_INTERFACE_CONFIG_A, |
| AD3552R_MASK_ADDR_ASCENSION, |
| FIELD_PREP(AD3552R_MASK_ADDR_ASCENSION, val)); |
| } |
| |
| static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac, |
| struct fwnode_handle *child, |
| u32 ch) |
| { |
| struct device *dev = &dac->spi->dev; |
| int err; |
| u8 addr; |
| u16 reg; |
| |
| err = ad3552r_get_custom_gain(dev, child, |
| &dac->ch_data[ch].p, |
| &dac->ch_data[ch].n, |
| &dac->ch_data[ch].rfb, |
| &dac->ch_data[ch].gain_offset); |
| if (err) |
| return err; |
| |
| dac->ch_data[ch].range_override = 1; |
| |
| addr = AD3552R_REG_ADDR_CH_GAIN(ch); |
| err = ad3552r_write_reg(dac, addr, |
| abs((s32)dac->ch_data[ch].gain_offset) & |
| AD3552R_MASK_CH_OFFSET_BITS_0_7); |
| if (err) |
| return dev_err_probe(dev, err, "Error writing register\n"); |
| |
| reg = ad3552r_calc_custom_gain(dac->ch_data[ch].p, dac->ch_data[ch].n, |
| dac->ch_data[ch].gain_offset); |
| |
| err = ad3552r_write_reg(dac, addr, reg); |
| if (err) |
| return dev_err_probe(dev, err, "Error writing register\n"); |
| |
| return 0; |
| } |
| |
| static int ad3552r_configure_device(struct ad3552r_desc *dac) |
| { |
| struct device *dev = &dac->spi->dev; |
| int err, cnt = 0; |
| u32 val, ch; |
| |
| dac->gpio_ldac = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH); |
| if (IS_ERR(dac->gpio_ldac)) |
| return dev_err_probe(dev, PTR_ERR(dac->gpio_ldac), |
| "Error getting gpio ldac"); |
| |
| err = ad3552r_get_ref_voltage(dev, &val); |
| if (err < 0) |
| return err; |
| |
| err = ad3552r_update_reg_field(dac, |
| AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, |
| AD3552R_MASK_REFERENCE_VOLTAGE_SEL, |
| FIELD_PREP(AD3552R_MASK_REFERENCE_VOLTAGE_SEL, val)); |
| if (err) |
| return err; |
| |
| err = ad3552r_get_drive_strength(dev, &val); |
| if (!err) { |
| err = ad3552r_update_reg_field(dac, |
| AD3552R_REG_ADDR_INTERFACE_CONFIG_D, |
| AD3552R_MASK_SDO_DRIVE_STRENGTH, |
| FIELD_PREP(AD3552R_MASK_SDO_DRIVE_STRENGTH, val)); |
| if (err) |
| return err; |
| } |
| |
| dac->num_ch = device_get_child_node_count(dev); |
| if (!dac->num_ch) { |
| dev_err(dev, "No channels defined\n"); |
| return -ENODEV; |
| } |
| |
| device_for_each_child_node_scoped(dev, child) { |
| err = fwnode_property_read_u32(child, "reg", &ch); |
| if (err) |
| return dev_err_probe(dev, err, |
| "mandatory reg property missing\n"); |
| if (ch >= dac->model_data->num_hw_channels) |
| return dev_err_probe(dev, -EINVAL, |
| "reg must be less than %d\n", |
| dac->model_data->num_hw_channels); |
| |
| err = ad3552r_get_output_range(dev, dac->model_data, |
| child, &val); |
| if (err && err != -ENOENT) |
| return err; |
| |
| if (!err) { |
| if (ch == 0) |
| val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), val); |
| else |
| val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1), val); |
| |
| err = ad3552r_update_reg_field(dac, |
| AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE, |
| AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch), |
| val); |
| if (err) |
| return err; |
| |
| dac->ch_data[ch].range = val; |
| } else if (dac->model_data->requires_output_range) { |
| return dev_err_probe(dev, -EINVAL, |
| "adi,output-range-microvolt is required for %s\n", |
| dac->model_data->model_name); |
| } else { |
| err = ad3552r_configure_custom_gain(dac, child, ch); |
| if (err) |
| return err; |
| } |
| |
| ad3552r_calc_gain_and_offset(&dac->ch_data[ch], dac->model_data); |
| dac->enabled_ch |= BIT(ch); |
| |
| if (ch == 0) |
| val = FIELD_PREP(AD3552R_MASK_CH(0), 1); |
| else |
| val = FIELD_PREP(AD3552R_MASK_CH(1), 1); |
| |
| err = ad3552r_update_reg_field(dac, |
| AD3552R_REG_ADDR_CH_SELECT_16B, |
| AD3552R_MASK_CH(ch), val); |
| if (err < 0) |
| return err; |
| |
| dac->channels[cnt] = AD3552R_CH_DAC(ch); |
| ++cnt; |
| |
| } |
| |
| /* Disable unused channels */ |
| for_each_clear_bit(ch, &dac->enabled_ch, |
| dac->model_data->num_hw_channels) { |
| if (ch == 0) |
| val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0), 1); |
| else |
| val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1), 1); |
| |
| err = ad3552r_update_reg_field(dac, |
| AD3552R_REG_ADDR_POWERDOWN_CONFIG, |
| AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch), |
| val); |
| if (err) |
| return err; |
| } |
| |
| dac->num_ch = cnt; |
| |
| return 0; |
| } |
| |
| static int ad3552r_init(struct ad3552r_desc *dac) |
| { |
| int err; |
| u16 val, id; |
| |
| err = ad3552r_reset(dac); |
| if (err) { |
| dev_err(&dac->spi->dev, "Reset failed\n"); |
| return err; |
| } |
| |
| err = ad3552r_check_scratch_pad(dac); |
| if (err) { |
| dev_err(&dac->spi->dev, "Scratch pad test failed\n"); |
| return err; |
| } |
| |
| err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_PRODUCT_ID_L, &val); |
| if (err) { |
| dev_err(&dac->spi->dev, "Fail read PRODUCT_ID_L\n"); |
| return err; |
| } |
| |
| id = val; |
| err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_PRODUCT_ID_H, &val); |
| if (err) { |
| dev_err(&dac->spi->dev, "Fail read PRODUCT_ID_H\n"); |
| return err; |
| } |
| |
| id |= val << 8; |
| if (id != dac->model_data->chip_id) { |
| dev_err(&dac->spi->dev, "Product id not matching\n"); |
| return -ENODEV; |
| } |
| |
| return ad3552r_configure_device(dac); |
| } |
| |
| static int ad3552r_probe(struct spi_device *spi) |
| { |
| struct ad3552r_desc *dac; |
| struct iio_dev *indio_dev; |
| int err; |
| |
| indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*dac)); |
| if (!indio_dev) |
| return -ENOMEM; |
| |
| dac = iio_priv(indio_dev); |
| dac->spi = spi; |
| dac->model_data = spi_get_device_match_data(spi); |
| if (!dac->model_data) |
| return -EINVAL; |
| |
| mutex_init(&dac->lock); |
| |
| err = ad3552r_init(dac); |
| if (err) |
| return err; |
| |
| /* Config triggered buffer device */ |
| indio_dev->name = dac->model_data->model_name; |
| indio_dev->dev.parent = &spi->dev; |
| indio_dev->info = &ad3552r_iio_info; |
| indio_dev->num_channels = dac->num_ch; |
| indio_dev->channels = dac->channels; |
| indio_dev->modes = INDIO_DIRECT_MODE; |
| |
| err = devm_iio_triggered_buffer_setup_ext(&indio_dev->dev, indio_dev, NULL, |
| &ad3552r_trigger_handler, |
| IIO_BUFFER_DIRECTION_OUT, |
| NULL, |
| NULL); |
| if (err) |
| return err; |
| |
| return devm_iio_device_register(&spi->dev, indio_dev); |
| } |
| |
| static const struct spi_device_id ad3552r_id[] = { |
| { |
| .name = "ad3541r", |
| .driver_data = (kernel_ulong_t)&ad3541r_model_data |
| }, |
| { |
| .name = "ad3542r", |
| .driver_data = (kernel_ulong_t)&ad3542r_model_data |
| }, |
| { |
| .name = "ad3551r", |
| .driver_data = (kernel_ulong_t)&ad3551r_model_data |
| }, |
| { |
| .name = "ad3552r", |
| .driver_data = (kernel_ulong_t)&ad3552r_model_data |
| }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(spi, ad3552r_id); |
| |
| static const struct of_device_id ad3552r_of_match[] = { |
| { .compatible = "adi,ad3541r", .data = &ad3541r_model_data }, |
| { .compatible = "adi,ad3542r", .data = &ad3542r_model_data }, |
| { .compatible = "adi,ad3551r", .data = &ad3551r_model_data }, |
| { .compatible = "adi,ad3552r", .data = &ad3552r_model_data }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, ad3552r_of_match); |
| |
| static struct spi_driver ad3552r_driver = { |
| .driver = { |
| .name = "ad3552r", |
| .of_match_table = ad3552r_of_match, |
| }, |
| .probe = ad3552r_probe, |
| .id_table = ad3552r_id |
| }; |
| module_spi_driver(ad3552r_driver); |
| |
| MODULE_AUTHOR("Mihail Chindris <mihail.chindris@analog.com>"); |
| MODULE_DESCRIPTION("Analog Device AD3552R DAC"); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_IMPORT_NS("IIO_AD3552R"); |