On 06/06/14 13:48, Michael Welling wrote:
This patch provides an iio device driver for the Microchip MCP49x2 series DACs. Signed-off-by: Michael Welling <mwelling@xxxxxxxx> ---
Hi Michael, One small point on patch formatting. The convention is to always include a description of the change that have occurred since the previous version, here below the ---. That just makes life easy for reviewers as they can see everything they have asked about has been addressed. Anyhow, looking pretty good after the review cycles it's been through this week. A couple of bits inline. The big ones are: 1) Don't use wildcards in driver or prefix naming. So call this mcp4902 throughout (or one of the others if you prefer). The arguements for using or not using wildcards have gone back and forth for years. In IIO we almost always say don't. 2) Don't use devm_iio_device_register - it causes a race condition.
drivers/iio/dac/Kconfig | 10 +++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/mcp49x2.c | 213 +++++++++++++++++++++++++++++++++++++++++++++
Please don't use wild cards in naming. It frequently goes wrong when some clever person in marketing picks a new part that clashes with the previous naming scheme. The convention is to pick a part. Name everything after that (including all prefixes in driver) then make it clear in the kconfig text which parts are supported.
3 files changed, 224 insertions(+) create mode 100644 drivers/iio/dac/mcp49x2.c diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index f378ca8..032d678 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -163,4 +163,14 @@ config MCP4725 To compile this driver as a module, choose M here: the module will be called mcp4725. +config MCP49X2 + tristate "MCP4902, MCP4912, MCP4922 DAC driver" + depends on SPI + ---help---
I was about to say that the standard format in this file is no --- round the help, but then noticed the other MCP driver snuck in with this. Lets go with the majority view and not have them. Anyone bored can fix up the existing case of this.
+ Say yes here to build the driver for the Microchip MCP49x2 + series DAC devices. + + To compile this driver as a module, choose M here: the module + will be called mcp49x2. + endmenu diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index bb84ad6..2fdfc2e 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MCP4725) += mcp4725.o +obj-$(CONFIG_MCP49X2) += mcp49x2.o diff --git a/drivers/iio/dac/mcp49x2.c b/drivers/iio/dac/mcp49x2.c new file mode 100644 index 0000000..073af6e --- /dev/null +++ b/drivers/iio/dac/mcp49x2.c @@ -0,0 +1,213 @@ +/* + * mcp49x2.c + * + * Driver for Microchip Digital to Analog Converters. + * Supports MCP4902, MCP4912, and MCP4922. + * + * Copyright (c) 2014 EMAC Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/regulator/consumer.h> +#include <linux/bitops.h> + +#define MCP49X2_NUM_CHANNELS 2 + +enum mcp49x2_supported_device_ids { + ID_MCP4902, + ID_MCP4912, + ID_MCP4922, +}; + +struct mcp49x2_state { + struct spi_device *spi; + unsigned int value[MCP49X2_NUM_CHANNELS]; + unsigned int power_mode[MCP49X2_NUM_CHANNELS];
I can't see power_mode being used anywhere...
+ unsigned int vref_mv; + struct regulator *vref_reg; + u8 mosi[2] ___cacheline_aligned; +}; + +#define MCP49X2_CHAN(chan, bits) { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = 12 - (bits), \ + }, \ +} + +static int mcp49x2_spi_write(struct mcp49x2_state *state, u8 addr, u32 val) +{ + state->mosi[1] = val & 0xff; + state->mosi[0] = (addr == 0) ? 0x00 : 0x80; + state->mosi[0] |= 0x30 | ((val >> 8) & 0x0f); + + return spi_write(state->spi, state->mosi, 2); +} + +static int mcp49x2_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct mcp49x2_state *state = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = state->value[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = state->vref_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int mcp49x2_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct mcp49x2_state *state = iio_priv(indio_dev); +
If being really tight on checking for valid inputs, one should verify that val2 == 0 and return -EINVAL otherwise.
+ switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val > GENMASK(chan->scan_type.realbits-1, 0)) + return -EINVAL; + val <<= chan->scan_type.shift; + state->value[chan->channel] = val; + return mcp49x2_spi_write(state, chan->channel, val); + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec mcp49x2_channels[3][MCP49X2_NUM_CHANNELS] = { + [ID_MCP4902] = { MCP49X2_CHAN(0, 8), MCP49X2_CHAN(1, 8) }, + [ID_MCP4912] = { MCP49X2_CHAN(0, 10), MCP49X2_CHAN(1, 10) }, + [ID_MCP4922] = { MCP49X2_CHAN(0, 12), MCP49X2_CHAN(1, 12) }, +}; + +static const struct iio_info mcp49x2_info = { + .read_raw = &mcp49x2_read_raw, + .write_raw = &mcp49x2_write_raw, + .driver_module = THIS_MODULE, +}; + +static int mcp49x2_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp49x2_state *state; + const struct spi_device_id *id; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (indio_dev == NULL) + return -ENOMEM; + + state = iio_priv(indio_dev); + state->spi = spi; + state->vref_reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(state->vref_reg)) { + dev_err(&spi->dev, "Vref regulator not specified\n"); + return PTR_ERR(state->vref_reg); + } + + ret = regulator_enable(state->vref_reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable vref regulator: %d\n", + ret); + return ret; + } + + ret = regulator_get_voltage(state->vref_reg); + if (ret < 0) { + dev_err(&spi->dev, "Failed to read vref regulator: %d\n", + ret); + goto error_disable_reg; + } + state->vref_mv = ret / 1000; + + spi_set_drvdata(spi, indio_dev); + id = spi_get_device_id(spi); + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &mcp49x2_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mcp49x2_channels[id->driver_data]; + indio_dev->num_channels = MCP49X2_NUM_CHANNELS; + indio_dev->name = id->name; +
You shouldn't be using devm_iio_device_register unless there is nothing else to be done in the remove. Unregistering the device is responsible for tearing down the userspace interface. Until that happens, even if a remove is in progress, it is possible for new reads to come in from userspace. Jumping down to remove...
+ ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", + ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + regulator_disable(state->vref_reg); + + return ret; +} + +static int mcp49x2_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct mcp49x2_state *state; + + state = iio_priv(indio_dev); + regulator_disable(state->vref_reg);
Continuing from comment in probe... Here you disable the regulator - at this precise point it would be possible for a read to be going on, thus I presume resulting in some fairly unpredictable behaviour! Hence you need to use the unmanaged interfaces for iio_device_register and iio_device_unregister with the unregister being the first thing to happen in this remove function. We had a reasonably involved debate on whether to introduce devm_iio_device_register, precisely because this sort of issue can happen, but in the end it was decided that it would be easy enough to catch cases such as this one in review :)
+ + return 0; +} + +static const struct spi_device_id mcp49x2_id[] = { + {"mcp4902", ID_MCP4902}, + {"mcp4912", ID_MCP4912}, + {"mcp4922", ID_MCP4922}, + {} +}; +MODULE_DEVICE_TABLE(spi, mcp49x2_id); + +static struct spi_driver mcp49x2_driver = { + .driver = { + .name = "mcp49x2", + .owner = THIS_MODULE, + }, + .probe = mcp49x2_probe, + .remove = mcp49x2_remove, + .id_table = mcp49x2_id, +}; +module_spi_driver(mcp49x2_driver); + +MODULE_AUTHOR("Michael Welling <mwelling@xxxxxxxx>"); +MODULE_DESCRIPTION("Microchip MCP4902, MCP4912, MCP4922 DAC"); +MODULE_LICENSE("GPL v2");
-- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html