1dbdc025bSLars-Peter Clausen /* 2dbdc025bSLars-Peter Clausen * AD5446 SPI DAC driver 3dbdc025bSLars-Peter Clausen * 4dbdc025bSLars-Peter Clausen * Copyright 2010 Analog Devices Inc. 5dbdc025bSLars-Peter Clausen * 6dbdc025bSLars-Peter Clausen * Licensed under the GPL-2 or later. 7dbdc025bSLars-Peter Clausen */ 8dbdc025bSLars-Peter Clausen 9dbdc025bSLars-Peter Clausen #include <linux/interrupt.h> 10dbdc025bSLars-Peter Clausen #include <linux/workqueue.h> 11dbdc025bSLars-Peter Clausen #include <linux/device.h> 12dbdc025bSLars-Peter Clausen #include <linux/kernel.h> 13dbdc025bSLars-Peter Clausen #include <linux/slab.h> 14dbdc025bSLars-Peter Clausen #include <linux/sysfs.h> 15dbdc025bSLars-Peter Clausen #include <linux/list.h> 16dbdc025bSLars-Peter Clausen #include <linux/spi/spi.h> 173ec36a2cSJean-Francois Dagenais #include <linux/i2c.h> 18dbdc025bSLars-Peter Clausen #include <linux/regulator/consumer.h> 19dbdc025bSLars-Peter Clausen #include <linux/err.h> 20dbdc025bSLars-Peter Clausen #include <linux/module.h> 21dbdc025bSLars-Peter Clausen 22dbdc025bSLars-Peter Clausen #include <linux/iio/iio.h> 23dbdc025bSLars-Peter Clausen #include <linux/iio/sysfs.h> 24dbdc025bSLars-Peter Clausen 252e15c903SJean-Francois Dagenais #define MODE_PWRDWN_1k 0x1 262e15c903SJean-Francois Dagenais #define MODE_PWRDWN_100k 0x2 272e15c903SJean-Francois Dagenais #define MODE_PWRDWN_TRISTATE 0x3 282e15c903SJean-Francois Dagenais 292e15c903SJean-Francois Dagenais /** 302e15c903SJean-Francois Dagenais * struct ad5446_state - driver instance specific data 312e15c903SJean-Francois Dagenais * @spi: spi_device 322e15c903SJean-Francois Dagenais * @chip_info: chip model specific constants, available modes etc 332e15c903SJean-Francois Dagenais * @reg: supply regulator 342e15c903SJean-Francois Dagenais * @vref_mv: actual reference voltage used 352e15c903SJean-Francois Dagenais */ 362e15c903SJean-Francois Dagenais 372e15c903SJean-Francois Dagenais struct ad5446_state { 382e15c903SJean-Francois Dagenais struct device *dev; 392e15c903SJean-Francois Dagenais const struct ad5446_chip_info *chip_info; 402e15c903SJean-Francois Dagenais struct regulator *reg; 412e15c903SJean-Francois Dagenais unsigned short vref_mv; 422e15c903SJean-Francois Dagenais unsigned cached_val; 432e15c903SJean-Francois Dagenais unsigned pwr_down_mode; 442e15c903SJean-Francois Dagenais unsigned pwr_down; 452e15c903SJean-Francois Dagenais }; 462e15c903SJean-Francois Dagenais 472e15c903SJean-Francois Dagenais /** 482e15c903SJean-Francois Dagenais * struct ad5446_chip_info - chip specific information 492e15c903SJean-Francois Dagenais * @channel: channel spec for the DAC 502e15c903SJean-Francois Dagenais * @int_vref_mv: AD5620/40/60: the internal reference voltage 512e15c903SJean-Francois Dagenais * @write: chip specific helper function to write to the register 522e15c903SJean-Francois Dagenais */ 532e15c903SJean-Francois Dagenais 542e15c903SJean-Francois Dagenais struct ad5446_chip_info { 552e15c903SJean-Francois Dagenais struct iio_chan_spec channel; 562e15c903SJean-Francois Dagenais u16 int_vref_mv; 572e15c903SJean-Francois Dagenais int (*write)(struct ad5446_state *st, unsigned val); 582e15c903SJean-Francois Dagenais }; 59dbdc025bSLars-Peter Clausen 60dbdc025bSLars-Peter Clausen static const char * const ad5446_powerdown_modes[] = { 61dbdc025bSLars-Peter Clausen "1kohm_to_gnd", "100kohm_to_gnd", "three_state" 62dbdc025bSLars-Peter Clausen }; 63dbdc025bSLars-Peter Clausen 64dbdc025bSLars-Peter Clausen static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev, 65dbdc025bSLars-Peter Clausen const struct iio_chan_spec *chan, unsigned int mode) 66dbdc025bSLars-Peter Clausen { 67dbdc025bSLars-Peter Clausen struct ad5446_state *st = iio_priv(indio_dev); 68dbdc025bSLars-Peter Clausen 69dbdc025bSLars-Peter Clausen st->pwr_down_mode = mode + 1; 70dbdc025bSLars-Peter Clausen 71dbdc025bSLars-Peter Clausen return 0; 72dbdc025bSLars-Peter Clausen } 73dbdc025bSLars-Peter Clausen 74dbdc025bSLars-Peter Clausen static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev, 75dbdc025bSLars-Peter Clausen const struct iio_chan_spec *chan) 76dbdc025bSLars-Peter Clausen { 77dbdc025bSLars-Peter Clausen struct ad5446_state *st = iio_priv(indio_dev); 78dbdc025bSLars-Peter Clausen 79dbdc025bSLars-Peter Clausen return st->pwr_down_mode - 1; 80dbdc025bSLars-Peter Clausen } 81dbdc025bSLars-Peter Clausen 82dbdc025bSLars-Peter Clausen static const struct iio_enum ad5446_powerdown_mode_enum = { 83dbdc025bSLars-Peter Clausen .items = ad5446_powerdown_modes, 84dbdc025bSLars-Peter Clausen .num_items = ARRAY_SIZE(ad5446_powerdown_modes), 85dbdc025bSLars-Peter Clausen .get = ad5446_get_powerdown_mode, 86dbdc025bSLars-Peter Clausen .set = ad5446_set_powerdown_mode, 87dbdc025bSLars-Peter Clausen }; 88dbdc025bSLars-Peter Clausen 89dbdc025bSLars-Peter Clausen static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev, 90dbdc025bSLars-Peter Clausen uintptr_t private, 91dbdc025bSLars-Peter Clausen const struct iio_chan_spec *chan, 92dbdc025bSLars-Peter Clausen char *buf) 93dbdc025bSLars-Peter Clausen { 94dbdc025bSLars-Peter Clausen struct ad5446_state *st = iio_priv(indio_dev); 95dbdc025bSLars-Peter Clausen 96dbdc025bSLars-Peter Clausen return sprintf(buf, "%d\n", st->pwr_down); 97dbdc025bSLars-Peter Clausen } 98dbdc025bSLars-Peter Clausen 99dbdc025bSLars-Peter Clausen static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, 100dbdc025bSLars-Peter Clausen uintptr_t private, 101dbdc025bSLars-Peter Clausen const struct iio_chan_spec *chan, 102dbdc025bSLars-Peter Clausen const char *buf, size_t len) 103dbdc025bSLars-Peter Clausen { 104dbdc025bSLars-Peter Clausen struct ad5446_state *st = iio_priv(indio_dev); 105dbdc025bSLars-Peter Clausen unsigned int shift; 106dbdc025bSLars-Peter Clausen unsigned int val; 107dbdc025bSLars-Peter Clausen bool powerdown; 108dbdc025bSLars-Peter Clausen int ret; 109dbdc025bSLars-Peter Clausen 110dbdc025bSLars-Peter Clausen ret = strtobool(buf, &powerdown); 111dbdc025bSLars-Peter Clausen if (ret) 112dbdc025bSLars-Peter Clausen return ret; 113dbdc025bSLars-Peter Clausen 114dbdc025bSLars-Peter Clausen mutex_lock(&indio_dev->mlock); 115dbdc025bSLars-Peter Clausen st->pwr_down = powerdown; 116dbdc025bSLars-Peter Clausen 117dbdc025bSLars-Peter Clausen if (st->pwr_down) { 118dbdc025bSLars-Peter Clausen shift = chan->scan_type.realbits + chan->scan_type.shift; 119dbdc025bSLars-Peter Clausen val = st->pwr_down_mode << shift; 120dbdc025bSLars-Peter Clausen } else { 121dbdc025bSLars-Peter Clausen val = st->cached_val; 122dbdc025bSLars-Peter Clausen } 123dbdc025bSLars-Peter Clausen 124dbdc025bSLars-Peter Clausen ret = st->chip_info->write(st, val); 125dbdc025bSLars-Peter Clausen mutex_unlock(&indio_dev->mlock); 126dbdc025bSLars-Peter Clausen 127dbdc025bSLars-Peter Clausen return ret ? ret : len; 128dbdc025bSLars-Peter Clausen } 129dbdc025bSLars-Peter Clausen 1303ec36a2cSJean-Francois Dagenais static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = { 131dbdc025bSLars-Peter Clausen { 132dbdc025bSLars-Peter Clausen .name = "powerdown", 133dbdc025bSLars-Peter Clausen .read = ad5446_read_dac_powerdown, 134dbdc025bSLars-Peter Clausen .write = ad5446_write_dac_powerdown, 135dbdc025bSLars-Peter Clausen }, 136dbdc025bSLars-Peter Clausen IIO_ENUM("powerdown_mode", false, &ad5446_powerdown_mode_enum), 137dbdc025bSLars-Peter Clausen IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum), 138dbdc025bSLars-Peter Clausen { }, 139dbdc025bSLars-Peter Clausen }; 140dbdc025bSLars-Peter Clausen 141dbdc025bSLars-Peter Clausen #define _AD5446_CHANNEL(bits, storage, shift, ext) { \ 142dbdc025bSLars-Peter Clausen .type = IIO_VOLTAGE, \ 143dbdc025bSLars-Peter Clausen .indexed = 1, \ 144dbdc025bSLars-Peter Clausen .output = 1, \ 145dbdc025bSLars-Peter Clausen .channel = 0, \ 1462f6a4a44SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 1472f6a4a44SJonathan Cameron .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 148dbdc025bSLars-Peter Clausen .scan_type = IIO_ST('u', (bits), (storage), (shift)), \ 149dbdc025bSLars-Peter Clausen .ext_info = (ext), \ 150dbdc025bSLars-Peter Clausen } 151dbdc025bSLars-Peter Clausen 152dbdc025bSLars-Peter Clausen #define AD5446_CHANNEL(bits, storage, shift) \ 153dbdc025bSLars-Peter Clausen _AD5446_CHANNEL(bits, storage, shift, NULL) 154dbdc025bSLars-Peter Clausen 155dbdc025bSLars-Peter Clausen #define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \ 1563ec36a2cSJean-Francois Dagenais _AD5446_CHANNEL(bits, storage, shift, ad5446_ext_info_powerdown) 157dbdc025bSLars-Peter Clausen 1583ec36a2cSJean-Francois Dagenais static int ad5446_read_raw(struct iio_dev *indio_dev, 1593ec36a2cSJean-Francois Dagenais struct iio_chan_spec const *chan, 1603ec36a2cSJean-Francois Dagenais int *val, 1613ec36a2cSJean-Francois Dagenais int *val2, 1623ec36a2cSJean-Francois Dagenais long m) 1633ec36a2cSJean-Francois Dagenais { 1643ec36a2cSJean-Francois Dagenais struct ad5446_state *st = iio_priv(indio_dev); 1653ec36a2cSJean-Francois Dagenais unsigned long scale_uv; 1663ec36a2cSJean-Francois Dagenais 1673ec36a2cSJean-Francois Dagenais switch (m) { 1683ec36a2cSJean-Francois Dagenais case IIO_CHAN_INFO_RAW: 1693ec36a2cSJean-Francois Dagenais *val = st->cached_val; 1703ec36a2cSJean-Francois Dagenais return IIO_VAL_INT; 1713ec36a2cSJean-Francois Dagenais case IIO_CHAN_INFO_SCALE: 1723ec36a2cSJean-Francois Dagenais scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; 1733ec36a2cSJean-Francois Dagenais *val = scale_uv / 1000; 1743ec36a2cSJean-Francois Dagenais *val2 = (scale_uv % 1000) * 1000; 1753ec36a2cSJean-Francois Dagenais return IIO_VAL_INT_PLUS_MICRO; 1763ec36a2cSJean-Francois Dagenais 1773ec36a2cSJean-Francois Dagenais } 1783ec36a2cSJean-Francois Dagenais return -EINVAL; 1793ec36a2cSJean-Francois Dagenais } 1803ec36a2cSJean-Francois Dagenais 1813ec36a2cSJean-Francois Dagenais static int ad5446_write_raw(struct iio_dev *indio_dev, 1823ec36a2cSJean-Francois Dagenais struct iio_chan_spec const *chan, 1833ec36a2cSJean-Francois Dagenais int val, 1843ec36a2cSJean-Francois Dagenais int val2, 1853ec36a2cSJean-Francois Dagenais long mask) 1863ec36a2cSJean-Francois Dagenais { 1873ec36a2cSJean-Francois Dagenais struct ad5446_state *st = iio_priv(indio_dev); 1883ec36a2cSJean-Francois Dagenais int ret = 0; 1893ec36a2cSJean-Francois Dagenais 1903ec36a2cSJean-Francois Dagenais switch (mask) { 1913ec36a2cSJean-Francois Dagenais case IIO_CHAN_INFO_RAW: 1923ec36a2cSJean-Francois Dagenais if (val >= (1 << chan->scan_type.realbits) || val < 0) 1933ec36a2cSJean-Francois Dagenais return -EINVAL; 1943ec36a2cSJean-Francois Dagenais 1953ec36a2cSJean-Francois Dagenais val <<= chan->scan_type.shift; 1963ec36a2cSJean-Francois Dagenais mutex_lock(&indio_dev->mlock); 1973ec36a2cSJean-Francois Dagenais st->cached_val = val; 1983ec36a2cSJean-Francois Dagenais if (!st->pwr_down) 1993ec36a2cSJean-Francois Dagenais ret = st->chip_info->write(st, val); 2003ec36a2cSJean-Francois Dagenais mutex_unlock(&indio_dev->mlock); 2013ec36a2cSJean-Francois Dagenais break; 2023ec36a2cSJean-Francois Dagenais default: 2033ec36a2cSJean-Francois Dagenais ret = -EINVAL; 2043ec36a2cSJean-Francois Dagenais } 2053ec36a2cSJean-Francois Dagenais 2063ec36a2cSJean-Francois Dagenais return ret; 2073ec36a2cSJean-Francois Dagenais } 2083ec36a2cSJean-Francois Dagenais 2093ec36a2cSJean-Francois Dagenais static const struct iio_info ad5446_info = { 2103ec36a2cSJean-Francois Dagenais .read_raw = ad5446_read_raw, 2113ec36a2cSJean-Francois Dagenais .write_raw = ad5446_write_raw, 2123ec36a2cSJean-Francois Dagenais .driver_module = THIS_MODULE, 2133ec36a2cSJean-Francois Dagenais }; 2143ec36a2cSJean-Francois Dagenais 215fc52692cSGreg Kroah-Hartman static int ad5446_probe(struct device *dev, const char *name, 2163ec36a2cSJean-Francois Dagenais const struct ad5446_chip_info *chip_info) 2173ec36a2cSJean-Francois Dagenais { 2183ec36a2cSJean-Francois Dagenais struct ad5446_state *st; 2193ec36a2cSJean-Francois Dagenais struct iio_dev *indio_dev; 2203ec36a2cSJean-Francois Dagenais struct regulator *reg; 2213ec36a2cSJean-Francois Dagenais int ret, voltage_uv = 0; 2223ec36a2cSJean-Francois Dagenais 223ba727295SSachin Kamat reg = devm_regulator_get(dev, "vcc"); 2243ec36a2cSJean-Francois Dagenais if (!IS_ERR(reg)) { 2253ec36a2cSJean-Francois Dagenais ret = regulator_enable(reg); 2263ec36a2cSJean-Francois Dagenais if (ret) 227ba727295SSachin Kamat return ret; 2283ec36a2cSJean-Francois Dagenais 22913e57ee2SAxel Lin ret = regulator_get_voltage(reg); 23013e57ee2SAxel Lin if (ret < 0) 23113e57ee2SAxel Lin goto error_disable_reg; 23213e57ee2SAxel Lin 23313e57ee2SAxel Lin voltage_uv = ret; 2343ec36a2cSJean-Francois Dagenais } 2353ec36a2cSJean-Francois Dagenais 236ba727295SSachin Kamat indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); 2373ec36a2cSJean-Francois Dagenais if (indio_dev == NULL) { 2383ec36a2cSJean-Francois Dagenais ret = -ENOMEM; 2393ec36a2cSJean-Francois Dagenais goto error_disable_reg; 2403ec36a2cSJean-Francois Dagenais } 2413ec36a2cSJean-Francois Dagenais st = iio_priv(indio_dev); 2423ec36a2cSJean-Francois Dagenais st->chip_info = chip_info; 2433ec36a2cSJean-Francois Dagenais 2443ec36a2cSJean-Francois Dagenais dev_set_drvdata(dev, indio_dev); 2453ec36a2cSJean-Francois Dagenais st->reg = reg; 2463ec36a2cSJean-Francois Dagenais st->dev = dev; 2473ec36a2cSJean-Francois Dagenais 2483ec36a2cSJean-Francois Dagenais /* Establish that the iio_dev is a child of the device */ 2493ec36a2cSJean-Francois Dagenais indio_dev->dev.parent = dev; 2503ec36a2cSJean-Francois Dagenais indio_dev->name = name; 2513ec36a2cSJean-Francois Dagenais indio_dev->info = &ad5446_info; 2523ec36a2cSJean-Francois Dagenais indio_dev->modes = INDIO_DIRECT_MODE; 2533ec36a2cSJean-Francois Dagenais indio_dev->channels = &st->chip_info->channel; 2543ec36a2cSJean-Francois Dagenais indio_dev->num_channels = 1; 2553ec36a2cSJean-Francois Dagenais 2563ec36a2cSJean-Francois Dagenais st->pwr_down_mode = MODE_PWRDWN_1k; 2573ec36a2cSJean-Francois Dagenais 2583ec36a2cSJean-Francois Dagenais if (st->chip_info->int_vref_mv) 2593ec36a2cSJean-Francois Dagenais st->vref_mv = st->chip_info->int_vref_mv; 2603ec36a2cSJean-Francois Dagenais else if (voltage_uv) 2613ec36a2cSJean-Francois Dagenais st->vref_mv = voltage_uv / 1000; 2623ec36a2cSJean-Francois Dagenais else 2633ec36a2cSJean-Francois Dagenais dev_warn(dev, "reference voltage unspecified\n"); 2643ec36a2cSJean-Francois Dagenais 2653ec36a2cSJean-Francois Dagenais ret = iio_device_register(indio_dev); 2663ec36a2cSJean-Francois Dagenais if (ret) 267ba727295SSachin Kamat goto error_disable_reg; 2683ec36a2cSJean-Francois Dagenais 2693ec36a2cSJean-Francois Dagenais return 0; 2703ec36a2cSJean-Francois Dagenais 2713ec36a2cSJean-Francois Dagenais error_disable_reg: 2723ec36a2cSJean-Francois Dagenais if (!IS_ERR(reg)) 2733ec36a2cSJean-Francois Dagenais regulator_disable(reg); 2743ec36a2cSJean-Francois Dagenais return ret; 2753ec36a2cSJean-Francois Dagenais } 2763ec36a2cSJean-Francois Dagenais 2773ec36a2cSJean-Francois Dagenais static int ad5446_remove(struct device *dev) 2783ec36a2cSJean-Francois Dagenais { 2793ec36a2cSJean-Francois Dagenais struct iio_dev *indio_dev = dev_get_drvdata(dev); 2803ec36a2cSJean-Francois Dagenais struct ad5446_state *st = iio_priv(indio_dev); 2813ec36a2cSJean-Francois Dagenais 2823ec36a2cSJean-Francois Dagenais iio_device_unregister(indio_dev); 283ba727295SSachin Kamat if (!IS_ERR(st->reg)) 2843ec36a2cSJean-Francois Dagenais regulator_disable(st->reg); 2853ec36a2cSJean-Francois Dagenais 2863ec36a2cSJean-Francois Dagenais return 0; 2873ec36a2cSJean-Francois Dagenais } 2883ec36a2cSJean-Francois Dagenais 2893ec36a2cSJean-Francois Dagenais #if IS_ENABLED(CONFIG_SPI_MASTER) 2903ec36a2cSJean-Francois Dagenais 2913ec36a2cSJean-Francois Dagenais static int ad5446_write(struct ad5446_state *st, unsigned val) 2923ec36a2cSJean-Francois Dagenais { 2933ec36a2cSJean-Francois Dagenais struct spi_device *spi = to_spi_device(st->dev); 2943ec36a2cSJean-Francois Dagenais __be16 data = cpu_to_be16(val); 2953ec36a2cSJean-Francois Dagenais 2963ec36a2cSJean-Francois Dagenais return spi_write(spi, &data, sizeof(data)); 2973ec36a2cSJean-Francois Dagenais } 2983ec36a2cSJean-Francois Dagenais 2993ec36a2cSJean-Francois Dagenais static int ad5660_write(struct ad5446_state *st, unsigned val) 3003ec36a2cSJean-Francois Dagenais { 3013ec36a2cSJean-Francois Dagenais struct spi_device *spi = to_spi_device(st->dev); 3023ec36a2cSJean-Francois Dagenais uint8_t data[3]; 3033ec36a2cSJean-Francois Dagenais 3043ec36a2cSJean-Francois Dagenais data[0] = (val >> 16) & 0xFF; 3053ec36a2cSJean-Francois Dagenais data[1] = (val >> 8) & 0xFF; 3063ec36a2cSJean-Francois Dagenais data[2] = val & 0xFF; 3073ec36a2cSJean-Francois Dagenais 3083ec36a2cSJean-Francois Dagenais return spi_write(spi, data, sizeof(data)); 3093ec36a2cSJean-Francois Dagenais } 3103ec36a2cSJean-Francois Dagenais 3113ec36a2cSJean-Francois Dagenais /** 3123ec36a2cSJean-Francois Dagenais * ad5446_supported_spi_device_ids: 3133ec36a2cSJean-Francois Dagenais * The AD5620/40/60 parts are available in different fixed internal reference 3143ec36a2cSJean-Francois Dagenais * voltage options. The actual part numbers may look differently 3153ec36a2cSJean-Francois Dagenais * (and a bit cryptic), however this style is used to make clear which 3163ec36a2cSJean-Francois Dagenais * parts are supported here. 3173ec36a2cSJean-Francois Dagenais */ 3183ec36a2cSJean-Francois Dagenais enum ad5446_supported_spi_device_ids { 3192fafbce2SLars-Peter Clausen ID_AD5300, 3202fafbce2SLars-Peter Clausen ID_AD5310, 3212fafbce2SLars-Peter Clausen ID_AD5320, 3223ec36a2cSJean-Francois Dagenais ID_AD5444, 3233ec36a2cSJean-Francois Dagenais ID_AD5446, 3243ec36a2cSJean-Francois Dagenais ID_AD5450, 3253ec36a2cSJean-Francois Dagenais ID_AD5451, 3263ec36a2cSJean-Francois Dagenais ID_AD5541A, 3273ec36a2cSJean-Francois Dagenais ID_AD5512A, 3283ec36a2cSJean-Francois Dagenais ID_AD5553, 3293ec36a2cSJean-Francois Dagenais ID_AD5601, 3303ec36a2cSJean-Francois Dagenais ID_AD5611, 3313ec36a2cSJean-Francois Dagenais ID_AD5621, 3323ec36a2cSJean-Francois Dagenais ID_AD5620_2500, 3333ec36a2cSJean-Francois Dagenais ID_AD5620_1250, 3343ec36a2cSJean-Francois Dagenais ID_AD5640_2500, 3353ec36a2cSJean-Francois Dagenais ID_AD5640_1250, 3363ec36a2cSJean-Francois Dagenais ID_AD5660_2500, 3373ec36a2cSJean-Francois Dagenais ID_AD5660_1250, 3383ec36a2cSJean-Francois Dagenais ID_AD5662, 3393ec36a2cSJean-Francois Dagenais }; 3403ec36a2cSJean-Francois Dagenais 3413ec36a2cSJean-Francois Dagenais static const struct ad5446_chip_info ad5446_spi_chip_info[] = { 3422fafbce2SLars-Peter Clausen [ID_AD5300] = { 3432fafbce2SLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4), 3442fafbce2SLars-Peter Clausen .write = ad5446_write, 3452fafbce2SLars-Peter Clausen }, 3462fafbce2SLars-Peter Clausen [ID_AD5310] = { 3472fafbce2SLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2), 3482fafbce2SLars-Peter Clausen .write = ad5446_write, 3492fafbce2SLars-Peter Clausen }, 3502fafbce2SLars-Peter Clausen [ID_AD5320] = { 3512fafbce2SLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0), 3522fafbce2SLars-Peter Clausen .write = ad5446_write, 3532fafbce2SLars-Peter Clausen }, 354dbdc025bSLars-Peter Clausen [ID_AD5444] = { 355dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL(12, 16, 2), 356dbdc025bSLars-Peter Clausen .write = ad5446_write, 357dbdc025bSLars-Peter Clausen }, 358dbdc025bSLars-Peter Clausen [ID_AD5446] = { 359dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL(14, 16, 0), 360dbdc025bSLars-Peter Clausen .write = ad5446_write, 361dbdc025bSLars-Peter Clausen }, 362779c0c46SLars-Peter Clausen [ID_AD5450] = { 363779c0c46SLars-Peter Clausen .channel = AD5446_CHANNEL(8, 16, 6), 364779c0c46SLars-Peter Clausen .write = ad5446_write, 365779c0c46SLars-Peter Clausen }, 366779c0c46SLars-Peter Clausen [ID_AD5451] = { 367779c0c46SLars-Peter Clausen .channel = AD5446_CHANNEL(10, 16, 4), 368779c0c46SLars-Peter Clausen .write = ad5446_write, 369779c0c46SLars-Peter Clausen }, 370dbdc025bSLars-Peter Clausen [ID_AD5541A] = { 371dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL(16, 16, 0), 372dbdc025bSLars-Peter Clausen .write = ad5446_write, 373dbdc025bSLars-Peter Clausen }, 374dbdc025bSLars-Peter Clausen [ID_AD5512A] = { 375dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL(12, 16, 4), 376dbdc025bSLars-Peter Clausen .write = ad5446_write, 377dbdc025bSLars-Peter Clausen }, 378dbdc025bSLars-Peter Clausen [ID_AD5553] = { 379dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL(14, 16, 0), 380dbdc025bSLars-Peter Clausen .write = ad5446_write, 381dbdc025bSLars-Peter Clausen }, 382dbdc025bSLars-Peter Clausen [ID_AD5601] = { 383dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6), 384dbdc025bSLars-Peter Clausen .write = ad5446_write, 385dbdc025bSLars-Peter Clausen }, 386dbdc025bSLars-Peter Clausen [ID_AD5611] = { 387dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4), 388dbdc025bSLars-Peter Clausen .write = ad5446_write, 389dbdc025bSLars-Peter Clausen }, 390dbdc025bSLars-Peter Clausen [ID_AD5621] = { 391dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), 392dbdc025bSLars-Peter Clausen .write = ad5446_write, 393dbdc025bSLars-Peter Clausen }, 394dbdc025bSLars-Peter Clausen [ID_AD5620_2500] = { 395dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), 396dbdc025bSLars-Peter Clausen .int_vref_mv = 2500, 397dbdc025bSLars-Peter Clausen .write = ad5446_write, 398dbdc025bSLars-Peter Clausen }, 399dbdc025bSLars-Peter Clausen [ID_AD5620_1250] = { 400dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2), 401dbdc025bSLars-Peter Clausen .int_vref_mv = 1250, 402dbdc025bSLars-Peter Clausen .write = ad5446_write, 403dbdc025bSLars-Peter Clausen }, 404dbdc025bSLars-Peter Clausen [ID_AD5640_2500] = { 405dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), 406dbdc025bSLars-Peter Clausen .int_vref_mv = 2500, 407dbdc025bSLars-Peter Clausen .write = ad5446_write, 408dbdc025bSLars-Peter Clausen }, 409dbdc025bSLars-Peter Clausen [ID_AD5640_1250] = { 410dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), 411dbdc025bSLars-Peter Clausen .int_vref_mv = 1250, 412dbdc025bSLars-Peter Clausen .write = ad5446_write, 413dbdc025bSLars-Peter Clausen }, 414dbdc025bSLars-Peter Clausen [ID_AD5660_2500] = { 415dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), 416dbdc025bSLars-Peter Clausen .int_vref_mv = 2500, 417dbdc025bSLars-Peter Clausen .write = ad5660_write, 418dbdc025bSLars-Peter Clausen }, 419dbdc025bSLars-Peter Clausen [ID_AD5660_1250] = { 420dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), 421dbdc025bSLars-Peter Clausen .int_vref_mv = 1250, 422dbdc025bSLars-Peter Clausen .write = ad5660_write, 423dbdc025bSLars-Peter Clausen }, 424dbdc025bSLars-Peter Clausen [ID_AD5662] = { 425dbdc025bSLars-Peter Clausen .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0), 426dbdc025bSLars-Peter Clausen .write = ad5660_write, 427dbdc025bSLars-Peter Clausen }, 428dbdc025bSLars-Peter Clausen }; 429dbdc025bSLars-Peter Clausen 4303ec36a2cSJean-Francois Dagenais static const struct spi_device_id ad5446_spi_ids[] = { 4312fafbce2SLars-Peter Clausen {"ad5300", ID_AD5300}, 4322fafbce2SLars-Peter Clausen {"ad5310", ID_AD5310}, 4332fafbce2SLars-Peter Clausen {"ad5320", ID_AD5320}, 434dbdc025bSLars-Peter Clausen {"ad5444", ID_AD5444}, 435dbdc025bSLars-Peter Clausen {"ad5446", ID_AD5446}, 436779c0c46SLars-Peter Clausen {"ad5450", ID_AD5450}, 437779c0c46SLars-Peter Clausen {"ad5451", ID_AD5451}, 438779c0c46SLars-Peter Clausen {"ad5452", ID_AD5444}, /* ad5452 is compatible to the ad5444 */ 439779c0c46SLars-Peter Clausen {"ad5453", ID_AD5446}, /* ad5453 is compatible to the ad5446 */ 440dbdc025bSLars-Peter Clausen {"ad5512a", ID_AD5512A}, 441dbdc025bSLars-Peter Clausen {"ad5541a", ID_AD5541A}, 442dbdc025bSLars-Peter Clausen {"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */ 443dbdc025bSLars-Peter Clausen {"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */ 444dbdc025bSLars-Peter Clausen {"ad5553", ID_AD5553}, 445dbdc025bSLars-Peter Clausen {"ad5601", ID_AD5601}, 446dbdc025bSLars-Peter Clausen {"ad5611", ID_AD5611}, 447dbdc025bSLars-Peter Clausen {"ad5621", ID_AD5621}, 448dbdc025bSLars-Peter Clausen {"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */ 449dbdc025bSLars-Peter Clausen {"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */ 450dbdc025bSLars-Peter Clausen {"ad5640-2500", ID_AD5640_2500}, 451dbdc025bSLars-Peter Clausen {"ad5640-1250", ID_AD5640_1250}, 452dbdc025bSLars-Peter Clausen {"ad5660-2500", ID_AD5660_2500}, 453dbdc025bSLars-Peter Clausen {"ad5660-1250", ID_AD5660_1250}, 454dbdc025bSLars-Peter Clausen {"ad5662", ID_AD5662}, 455dbdc025bSLars-Peter Clausen {} 456dbdc025bSLars-Peter Clausen }; 4573ec36a2cSJean-Francois Dagenais MODULE_DEVICE_TABLE(spi, ad5446_spi_ids); 458dbdc025bSLars-Peter Clausen 459fc52692cSGreg Kroah-Hartman static int ad5446_spi_probe(struct spi_device *spi) 4603ec36a2cSJean-Francois Dagenais { 4613ec36a2cSJean-Francois Dagenais const struct spi_device_id *id = spi_get_device_id(spi); 4623ec36a2cSJean-Francois Dagenais 4633ec36a2cSJean-Francois Dagenais return ad5446_probe(&spi->dev, id->name, 4643ec36a2cSJean-Francois Dagenais &ad5446_spi_chip_info[id->driver_data]); 4653ec36a2cSJean-Francois Dagenais } 4663ec36a2cSJean-Francois Dagenais 467fc52692cSGreg Kroah-Hartman static int ad5446_spi_remove(struct spi_device *spi) 4683ec36a2cSJean-Francois Dagenais { 4693ec36a2cSJean-Francois Dagenais return ad5446_remove(&spi->dev); 4703ec36a2cSJean-Francois Dagenais } 4713ec36a2cSJean-Francois Dagenais 4723ec36a2cSJean-Francois Dagenais static struct spi_driver ad5446_spi_driver = { 473dbdc025bSLars-Peter Clausen .driver = { 474dbdc025bSLars-Peter Clausen .name = "ad5446", 475dbdc025bSLars-Peter Clausen .owner = THIS_MODULE, 476dbdc025bSLars-Peter Clausen }, 4773ec36a2cSJean-Francois Dagenais .probe = ad5446_spi_probe, 478fc52692cSGreg Kroah-Hartman .remove = ad5446_spi_remove, 4793ec36a2cSJean-Francois Dagenais .id_table = ad5446_spi_ids, 480dbdc025bSLars-Peter Clausen }; 4813ec36a2cSJean-Francois Dagenais 4823ec36a2cSJean-Francois Dagenais static int __init ad5446_spi_register_driver(void) 4833ec36a2cSJean-Francois Dagenais { 4843ec36a2cSJean-Francois Dagenais return spi_register_driver(&ad5446_spi_driver); 4853ec36a2cSJean-Francois Dagenais } 4863ec36a2cSJean-Francois Dagenais 4873ec36a2cSJean-Francois Dagenais static void ad5446_spi_unregister_driver(void) 4883ec36a2cSJean-Francois Dagenais { 4893ec36a2cSJean-Francois Dagenais spi_unregister_driver(&ad5446_spi_driver); 4903ec36a2cSJean-Francois Dagenais } 4913ec36a2cSJean-Francois Dagenais 4923ec36a2cSJean-Francois Dagenais #else 4933ec36a2cSJean-Francois Dagenais 4943ec36a2cSJean-Francois Dagenais static inline int ad5446_spi_register_driver(void) { return 0; } 4953ec36a2cSJean-Francois Dagenais static inline void ad5446_spi_unregister_driver(void) { } 4963ec36a2cSJean-Francois Dagenais 4973ec36a2cSJean-Francois Dagenais #endif 4983ec36a2cSJean-Francois Dagenais 4993ec36a2cSJean-Francois Dagenais #if IS_ENABLED(CONFIG_I2C) 5003ec36a2cSJean-Francois Dagenais 5013ec36a2cSJean-Francois Dagenais static int ad5622_write(struct ad5446_state *st, unsigned val) 5023ec36a2cSJean-Francois Dagenais { 5033ec36a2cSJean-Francois Dagenais struct i2c_client *client = to_i2c_client(st->dev); 5043ec36a2cSJean-Francois Dagenais __be16 data = cpu_to_be16(val); 5053ec36a2cSJean-Francois Dagenais 5063ec36a2cSJean-Francois Dagenais return i2c_master_send(client, (char *)&data, sizeof(data)); 5073ec36a2cSJean-Francois Dagenais } 5083ec36a2cSJean-Francois Dagenais 5093ec36a2cSJean-Francois Dagenais /** 5103ec36a2cSJean-Francois Dagenais * ad5446_supported_i2c_device_ids: 5113ec36a2cSJean-Francois Dagenais * The AD5620/40/60 parts are available in different fixed internal reference 5123ec36a2cSJean-Francois Dagenais * voltage options. The actual part numbers may look differently 5133ec36a2cSJean-Francois Dagenais * (and a bit cryptic), however this style is used to make clear which 5143ec36a2cSJean-Francois Dagenais * parts are supported here. 5153ec36a2cSJean-Francois Dagenais */ 5163ec36a2cSJean-Francois Dagenais enum ad5446_supported_i2c_device_ids { 5173ec36a2cSJean-Francois Dagenais ID_AD5602, 5183ec36a2cSJean-Francois Dagenais ID_AD5612, 5193ec36a2cSJean-Francois Dagenais ID_AD5622, 5203ec36a2cSJean-Francois Dagenais }; 5213ec36a2cSJean-Francois Dagenais 5223ec36a2cSJean-Francois Dagenais static const struct ad5446_chip_info ad5446_i2c_chip_info[] = { 5233ec36a2cSJean-Francois Dagenais [ID_AD5602] = { 5243ec36a2cSJean-Francois Dagenais .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4), 5253ec36a2cSJean-Francois Dagenais .write = ad5622_write, 5263ec36a2cSJean-Francois Dagenais }, 5273ec36a2cSJean-Francois Dagenais [ID_AD5612] = { 5283ec36a2cSJean-Francois Dagenais .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2), 5293ec36a2cSJean-Francois Dagenais .write = ad5622_write, 5303ec36a2cSJean-Francois Dagenais }, 5313ec36a2cSJean-Francois Dagenais [ID_AD5622] = { 5323ec36a2cSJean-Francois Dagenais .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0), 5333ec36a2cSJean-Francois Dagenais .write = ad5622_write, 5343ec36a2cSJean-Francois Dagenais }, 5353ec36a2cSJean-Francois Dagenais }; 5363ec36a2cSJean-Francois Dagenais 537fc52692cSGreg Kroah-Hartman static int ad5446_i2c_probe(struct i2c_client *i2c, 5383ec36a2cSJean-Francois Dagenais const struct i2c_device_id *id) 5393ec36a2cSJean-Francois Dagenais { 5403ec36a2cSJean-Francois Dagenais return ad5446_probe(&i2c->dev, id->name, 5413ec36a2cSJean-Francois Dagenais &ad5446_i2c_chip_info[id->driver_data]); 5423ec36a2cSJean-Francois Dagenais } 5433ec36a2cSJean-Francois Dagenais 544fc52692cSGreg Kroah-Hartman static int ad5446_i2c_remove(struct i2c_client *i2c) 5453ec36a2cSJean-Francois Dagenais { 5463ec36a2cSJean-Francois Dagenais return ad5446_remove(&i2c->dev); 5473ec36a2cSJean-Francois Dagenais } 5483ec36a2cSJean-Francois Dagenais 5493ec36a2cSJean-Francois Dagenais static const struct i2c_device_id ad5446_i2c_ids[] = { 550bf832380SLars-Peter Clausen {"ad5301", ID_AD5602}, 551bf832380SLars-Peter Clausen {"ad5311", ID_AD5612}, 552bf832380SLars-Peter Clausen {"ad5321", ID_AD5622}, 5533ec36a2cSJean-Francois Dagenais {"ad5602", ID_AD5602}, 5543ec36a2cSJean-Francois Dagenais {"ad5612", ID_AD5612}, 5553ec36a2cSJean-Francois Dagenais {"ad5622", ID_AD5622}, 5563ec36a2cSJean-Francois Dagenais {} 5573ec36a2cSJean-Francois Dagenais }; 5583ec36a2cSJean-Francois Dagenais MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids); 5593ec36a2cSJean-Francois Dagenais 5603ec36a2cSJean-Francois Dagenais static struct i2c_driver ad5446_i2c_driver = { 5613ec36a2cSJean-Francois Dagenais .driver = { 5623ec36a2cSJean-Francois Dagenais .name = "ad5446", 5633ec36a2cSJean-Francois Dagenais .owner = THIS_MODULE, 5643ec36a2cSJean-Francois Dagenais }, 5653ec36a2cSJean-Francois Dagenais .probe = ad5446_i2c_probe, 566fc52692cSGreg Kroah-Hartman .remove = ad5446_i2c_remove, 5673ec36a2cSJean-Francois Dagenais .id_table = ad5446_i2c_ids, 5683ec36a2cSJean-Francois Dagenais }; 5693ec36a2cSJean-Francois Dagenais 5703ec36a2cSJean-Francois Dagenais static int __init ad5446_i2c_register_driver(void) 5713ec36a2cSJean-Francois Dagenais { 5723ec36a2cSJean-Francois Dagenais return i2c_add_driver(&ad5446_i2c_driver); 5733ec36a2cSJean-Francois Dagenais } 5743ec36a2cSJean-Francois Dagenais 5753ec36a2cSJean-Francois Dagenais static void __exit ad5446_i2c_unregister_driver(void) 5763ec36a2cSJean-Francois Dagenais { 5773ec36a2cSJean-Francois Dagenais i2c_del_driver(&ad5446_i2c_driver); 5783ec36a2cSJean-Francois Dagenais } 5793ec36a2cSJean-Francois Dagenais 5803ec36a2cSJean-Francois Dagenais #else 5813ec36a2cSJean-Francois Dagenais 5823ec36a2cSJean-Francois Dagenais static inline int ad5446_i2c_register_driver(void) { return 0; } 5833ec36a2cSJean-Francois Dagenais static inline void ad5446_i2c_unregister_driver(void) { } 5843ec36a2cSJean-Francois Dagenais 5853ec36a2cSJean-Francois Dagenais #endif 5863ec36a2cSJean-Francois Dagenais 5873ec36a2cSJean-Francois Dagenais static int __init ad5446_init(void) 5883ec36a2cSJean-Francois Dagenais { 5893ec36a2cSJean-Francois Dagenais int ret; 5903ec36a2cSJean-Francois Dagenais 5913ec36a2cSJean-Francois Dagenais ret = ad5446_spi_register_driver(); 5923ec36a2cSJean-Francois Dagenais if (ret) 5933ec36a2cSJean-Francois Dagenais return ret; 5943ec36a2cSJean-Francois Dagenais 5953ec36a2cSJean-Francois Dagenais ret = ad5446_i2c_register_driver(); 5963ec36a2cSJean-Francois Dagenais if (ret) { 5973ec36a2cSJean-Francois Dagenais ad5446_spi_unregister_driver(); 5983ec36a2cSJean-Francois Dagenais return ret; 5993ec36a2cSJean-Francois Dagenais } 6003ec36a2cSJean-Francois Dagenais 6013ec36a2cSJean-Francois Dagenais return 0; 6023ec36a2cSJean-Francois Dagenais } 6033ec36a2cSJean-Francois Dagenais module_init(ad5446_init); 6043ec36a2cSJean-Francois Dagenais 6053ec36a2cSJean-Francois Dagenais static void __exit ad5446_exit(void) 6063ec36a2cSJean-Francois Dagenais { 6073ec36a2cSJean-Francois Dagenais ad5446_i2c_unregister_driver(); 6083ec36a2cSJean-Francois Dagenais ad5446_spi_unregister_driver(); 6093ec36a2cSJean-Francois Dagenais } 6103ec36a2cSJean-Francois Dagenais module_exit(ad5446_exit); 611dbdc025bSLars-Peter Clausen 612dbdc025bSLars-Peter Clausen MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 613dbdc025bSLars-Peter Clausen MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); 614dbdc025bSLars-Peter Clausen MODULE_LICENSE("GPL v2"); 615