/* * AD7298 digital temperature sensor driver supporting AD7298 * * Copyright 2010 Analog Devices Inc. * * Licensed under the GPL-2 or later. */ #include #include #include #include #include #include #include #include #include #include #include "../iio.h" #include "../sysfs.h" /* * AD7298 command */ #define AD7298_PD 0x1 #define AD7298_T_AVG_MASK 0x2 #define AD7298_EXT_REF 0x4 #define AD7298_T_SENSE_MASK 0x20 #define AD7298_VOLTAGE_MASK 0x3fc0 #define AD7298_VOLTAGE_OFFSET 0x6 #define AD7298_VOLTAGE_LIMIT_COUNT 8 #define AD7298_REPEAT 0x40 #define AD7298_WRITE 0x80 /* * AD7298 value masks */ #define AD7298_CHANNEL_MASK 0xf000 #define AD7298_VALUE_MASK 0xfff #define AD7298_T_VALUE_SIGN 0x400 #define AD7298_T_VALUE_FLOAT_OFFSET 2 #define AD7298_T_VALUE_FLOAT_MASK 0x2 /* * struct ad7298_chip_info - chip specifc information */ struct ad7298_chip_info { const char *name; struct spi_device *spi_dev; struct iio_dev *indio_dev; u16 command; u16 busy_pin; u8 channels; /* Active voltage channels */ }; /* * ad7298 register access by SPI */ static int ad7298_spi_write(struct ad7298_chip_info *chip, u16 data) { struct spi_device *spi_dev = chip->spi_dev; int ret = 0; data |= AD7298_WRITE; data = cpu_to_be16(data); ret = spi_write(spi_dev, (u8 *)&data, sizeof(data)); if (ret < 0) dev_err(&spi_dev->dev, "SPI write error\n"); return ret; } static int ad7298_spi_read(struct ad7298_chip_info *chip, u16 mask, u16 *data) { struct spi_device *spi_dev = chip->spi_dev; int ret = 0; u8 count = chip->channels; u16 command; int i; if (mask & AD7298_T_SENSE_MASK) { command = chip->command & ~(AD7298_T_AVG_MASK | AD7298_VOLTAGE_MASK); command |= AD7298_T_SENSE_MASK; count = 1; } else if (mask & AD7298_T_AVG_MASK) { command = chip->command & ~AD7298_VOLTAGE_MASK; command |= AD7298_T_SENSE_MASK | AD7298_T_AVG_MASK; count = 2; } else if (mask & AD7298_VOLTAGE_MASK) { command = chip->command & ~(AD7298_T_AVG_MASK | AD7298_T_SENSE_MASK); count = chip->channels; } ret = ad7298_spi_write(chip, chip->command); if (ret < 0) { dev_err(&spi_dev->dev, "SPI write command error\n"); return ret; } ret = spi_read(spi_dev, (u8 *)&command, sizeof(command)); if (ret < 0) { dev_err(&spi_dev->dev, "SPI read error\n"); return ret; } i = 10000; while (i && gpio_get_value(chip->busy_pin)) { cpu_relax(); i--; } if (!i) { dev_err(&spi_dev->dev, "Always in busy convertion.\n"); return -EBUSY; } for (i = 0; i < count; i++) { ret = spi_read(spi_dev, (u8 *)&data[i], sizeof(data[i])); if (ret < 0) { dev_err(&spi_dev->dev, "SPI read error\n"); return ret; } *data = be16_to_cpu(data[i]); } return 0; } static ssize_t ad7298_show_mode(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; if (chip->command & AD7298_REPEAT) return sprintf(buf, "repeat\n"); else return sprintf(buf, "normal\n"); } static ssize_t ad7298_store_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; if (strcmp(buf, "repeat")) chip->command |= AD7298_REPEAT; else chip->command &= (~AD7298_REPEAT); return 1; } static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, ad7298_show_mode, ad7298_store_mode, 0); static ssize_t ad7298_show_available_modes(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "normal\nrepeat\n"); } static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7298_show_available_modes, NULL, 0); static ssize_t ad7298_store_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; u16 command; int ret; command = chip->command & ~AD7298_PD; ret = ad7298_spi_write(chip, command); if (ret) return -EIO; command = chip->command | AD7298_PD; ret = ad7298_spi_write(chip, command); if (ret) return -EIO; return len; } static IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, ad7298_store_reset, 0); static ssize_t ad7298_show_ext_ref(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; return sprintf(buf, "%d\n", !!(chip->command & AD7298_EXT_REF)); } static ssize_t ad7298_store_ext_ref(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; u16 command; int ret; command = chip->command & (~AD7298_EXT_REF); if (strcmp(buf, "1")) command |= AD7298_EXT_REF; ret = ad7298_spi_write(chip, command); if (ret) return -EIO; chip->command = command; return len; } static IIO_DEVICE_ATTR(ext_ref, S_IRUGO | S_IWUSR, ad7298_show_ext_ref, ad7298_store_ext_ref, 0); static ssize_t ad7298_show_t_sense(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; u16 data; char sign = ' '; int ret; ret = ad7298_spi_read(chip, AD7298_T_SENSE_MASK, &data); if (ret) return -EIO; if (data & AD7298_T_VALUE_SIGN) { /* convert supplement to positive value */ data = (AD7298_T_VALUE_SIGN << 1) - data; sign = '-'; } return sprintf(buf, "%c%d.%.2d\n", sign, (data >> AD7298_T_VALUE_FLOAT_OFFSET), (data & AD7298_T_VALUE_FLOAT_MASK) * 25); } static IIO_DEVICE_ATTR(t_sense, S_IRUGO, ad7298_show_t_sense, NULL, 0); static ssize_t ad7298_show_t_average(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; u16 data[2]; char sign = ' '; int ret; ret = ad7298_spi_read(chip, AD7298_T_AVG_MASK, data); if (ret) return -EIO; if (data[1] & AD7298_T_VALUE_SIGN) { /* convert supplement to positive value */ data[1] = (AD7298_T_VALUE_SIGN << 1) - data[1]; sign = '-'; } return sprintf(buf, "%c%d.%.2d\n", sign, (data[1] >> AD7298_T_VALUE_FLOAT_OFFSET), (data[1] & AD7298_T_VALUE_FLOAT_MASK) * 25); } static IIO_DEVICE_ATTR(t_average, S_IRUGO, ad7298_show_t_average, NULL, 0); static ssize_t ad7298_show_voltage(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; u16 data[AD7298_VOLTAGE_LIMIT_COUNT]; int i, size, ret; ret = ad7298_spi_read(chip, AD7298_VOLTAGE_MASK, data); if (ret) return -EIO; for (i = 0; i < AD7298_VOLTAGE_LIMIT_COUNT; i++) { if (chip->command & (AD7298_T_SENSE_MASK << i)) { ret = sprintf(buf, "channel[%d]=%d\n", i, data[i] & AD7298_VALUE_MASK); if (ret < 0) break; buf += ret; size += ret; } } return size; } static IIO_DEVICE_ATTR(voltage, S_IRUGO, ad7298_show_voltage, NULL, 0); static ssize_t ad7298_show_channel_mask(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; return sprintf(buf, "0x%x\n", (chip->command & AD7298_VOLTAGE_MASK) >> AD7298_VOLTAGE_OFFSET); } static ssize_t ad7298_store_channel_mask(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; unsigned long data; int i, ret; ret = strict_strtoul(buf, 16, &data); if (ret || data > 0xff) return -EINVAL; chip->command &= (~AD7298_VOLTAGE_MASK); chip->command |= data << AD7298_VOLTAGE_OFFSET; for (i = 0, chip->channels = 0; i < AD7298_VOLTAGE_LIMIT_COUNT; i++) { if (chip->command & (AD7298_T_SENSE_MASK << i)) chip->channels++; } return ret; } static IIO_DEVICE_ATTR(channel_mask, S_IRUGO | S_IWUSR, ad7298_show_channel_mask, ad7298_store_channel_mask, 0); static ssize_t ad7298_show_name(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *dev_info = dev_get_drvdata(dev); struct ad7298_chip_info *chip = dev_info->dev_data; return sprintf(buf, "%s\n", chip->name); } static IIO_DEVICE_ATTR(name, S_IRUGO, ad7298_show_name, NULL, 0); static struct attribute *ad7298_attributes[] = { &iio_dev_attr_available_modes.dev_attr.attr, &iio_dev_attr_mode.dev_attr.attr, &iio_dev_attr_reset.dev_attr.attr, &iio_dev_attr_ext_ref.dev_attr.attr, &iio_dev_attr_t_sense.dev_attr.attr, &iio_dev_attr_t_average.dev_attr.attr, &iio_dev_attr_voltage.dev_attr.attr, &iio_dev_attr_channel_mask.dev_attr.attr, &iio_dev_attr_name.dev_attr.attr, NULL, }; static const struct attribute_group ad7298_attribute_group = { .attrs = ad7298_attributes, }; /* * device probe and remove */ static int __devinit ad7298_probe(struct spi_device *spi_dev) { struct ad7298_chip_info *chip; unsigned short *pins = spi_dev->dev.platform_data; int ret = 0; chip = kzalloc(sizeof(struct ad7298_chip_info), GFP_KERNEL); if (chip == NULL) return -ENOMEM; /* this is only used for device removal purposes */ dev_set_drvdata(&spi_dev->dev, chip); chip->spi_dev = spi_dev; chip->name = spi_dev->modalias; chip->busy_pin = pins[0]; ret = gpio_request(chip->busy_pin, chip->name); if (ret) { dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n", chip->busy_pin); goto error_free_chip; } gpio_direction_input(chip->busy_pin); chip->indio_dev = iio_allocate_device(); if (chip->indio_dev == NULL) { ret = -ENOMEM; goto error_free_gpio; } chip->indio_dev->dev.parent = &spi_dev->dev; chip->indio_dev->attrs = &ad7298_attribute_group; chip->indio_dev->dev_data = (void *)chip; chip->indio_dev->driver_module = THIS_MODULE; chip->indio_dev->modes = INDIO_DIRECT_MODE; ret = iio_device_register(chip->indio_dev); if (ret) goto error_free_dev; dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n", chip->name); return 0; error_free_dev: iio_free_device(chip->indio_dev); error_free_gpio: gpio_free(chip->busy_pin); error_free_chip: kfree(chip); return ret; } static int __devexit ad7298_remove(struct spi_device *spi_dev) { struct ad7298_chip_info *chip = dev_get_drvdata(&spi_dev->dev); struct iio_dev *indio_dev = chip->indio_dev; dev_set_drvdata(&spi_dev->dev, NULL); iio_device_unregister(indio_dev); iio_free_device(chip->indio_dev); gpio_free(chip->busy_pin); kfree(chip); return 0; } static const struct spi_device_id ad7298_id[] = { { "ad7298", 0 }, {} }; MODULE_DEVICE_TABLE(spi, ad7298_id); static struct spi_driver ad7298_driver = { .driver = { .name = "ad7298", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = ad7298_probe, .remove = __devexit_p(ad7298_remove), .id_table = ad7298_id, }; static __init int ad7298_init(void) { return spi_register_driver(&ad7298_driver); } static __exit void ad7298_exit(void) { spi_unregister_driver(&ad7298_driver); } MODULE_AUTHOR("Sonic Zhang "); MODULE_DESCRIPTION("Analog Devices AD7298 digital" " temperature sensor and ADC driver"); MODULE_LICENSE("GPL v2"); module_init(ad7298_init); module_exit(ad7298_exit);