| /* |
| * max517.c - Support for Maxim MAX517, MAX518 and MAX519 |
| * |
| * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de> |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/slab.h> |
| #include <linux/jiffies.h> |
| #include <linux/i2c.h> |
| #include <linux/err.h> |
| |
| #include "../iio.h" |
| #include "../sysfs.h" |
| #include "dac.h" |
| |
| #include "max517.h" |
| |
| #define MAX517_DRV_NAME "max517" |
| |
| /* Commands */ |
| #define COMMAND_CHANNEL0 0x00 |
| #define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */ |
| #define COMMAND_PD 0x08 /* Power Down */ |
| |
| enum max517_device_ids { |
| ID_MAX517, |
| ID_MAX518, |
| ID_MAX519, |
| }; |
| |
| struct max517_data { |
| struct iio_dev *indio_dev; |
| struct i2c_client *client; |
| unsigned short vref_mv[2]; |
| }; |
| |
| /* |
| * channel: bit 0: channel 1 |
| * bit 1: channel 2 |
| * (this way, it's possible to set both channels at once) |
| */ |
| static ssize_t max517_set_value(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count, int channel) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct max517_data *data = iio_priv(indio_dev); |
| struct i2c_client *client = data->client; |
| u8 outbuf[4]; /* 1x or 2x command + value */ |
| int outbuf_size = 0; |
| int res; |
| long val; |
| |
| res = strict_strtol(buf, 10, &val); |
| |
| if (res) |
| return res; |
| |
| if (val < 0 || val > 255) |
| return -EINVAL; |
| |
| if (channel & 1) { |
| outbuf[outbuf_size++] = COMMAND_CHANNEL0; |
| outbuf[outbuf_size++] = val; |
| } |
| if (channel & 2) { |
| outbuf[outbuf_size++] = COMMAND_CHANNEL1; |
| outbuf[outbuf_size++] = val; |
| } |
| |
| /* |
| * At this point, there are always 1 or 2 two-byte commands in |
| * outbuf. With 2 commands, the device can set two outputs |
| * simultaneously, latching the values upon the end of the I2C |
| * transfer. |
| */ |
| |
| res = i2c_master_send(client, outbuf, outbuf_size); |
| if (res < 0) |
| return res; |
| |
| return count; |
| } |
| |
| static ssize_t max517_set_value_1(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| return max517_set_value(dev, attr, buf, count, 1); |
| } |
| static IIO_DEV_ATTR_OUT_RAW(1, max517_set_value_1, 0); |
| |
| static ssize_t max517_set_value_2(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| return max517_set_value(dev, attr, buf, count, 2); |
| } |
| static IIO_DEV_ATTR_OUT_RAW(2, max517_set_value_2, 1); |
| |
| static ssize_t max517_set_value_both(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| return max517_set_value(dev, attr, buf, count, 3); |
| } |
| static IIO_DEVICE_ATTR_NAMED(out_voltage1and2_raw, |
| out_voltage1&2_raw, S_IWUSR, NULL, |
| max517_set_value_both, -1); |
| |
| static ssize_t max517_show_scale(struct device *dev, |
| struct device_attribute *attr, |
| char *buf, int channel) |
| { |
| struct iio_dev *indio_dev = dev_get_drvdata(dev); |
| struct max517_data *data = iio_priv(indio_dev); |
| /* Corresponds to Vref / 2^(bits) */ |
| unsigned int scale_uv = (data->vref_mv[channel - 1] * 1000) >> 8; |
| |
| return sprintf(buf, "%d.%03d\n", scale_uv / 1000, scale_uv % 1000); |
| } |
| |
| static ssize_t max517_show_scale1(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| return max517_show_scale(dev, attr, buf, 1); |
| } |
| static IIO_DEVICE_ATTR(out_voltage1_scale, S_IRUGO, |
| max517_show_scale1, NULL, 0); |
| |
| static ssize_t max517_show_scale2(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| return max517_show_scale(dev, attr, buf, 2); |
| } |
| static IIO_DEVICE_ATTR(out_voltage2_scale, S_IRUGO, |
| max517_show_scale2, NULL, 0); |
| |
| /* On MAX517 variant, we have one output */ |
| static struct attribute *max517_attributes[] = { |
| &iio_dev_attr_out_voltage1_raw.dev_attr.attr, |
| &iio_dev_attr_out_voltage1_scale.dev_attr.attr, |
| NULL |
| }; |
| |
| static struct attribute_group max517_attribute_group = { |
| .attrs = max517_attributes, |
| }; |
| |
| /* On MAX518 and MAX519 variant, we have two outputs */ |
| static struct attribute *max518_attributes[] = { |
| &iio_dev_attr_out_voltage1_raw.dev_attr.attr, |
| &iio_dev_attr_out_voltage1_scale.dev_attr.attr, |
| &iio_dev_attr_out_voltage2_raw.dev_attr.attr, |
| &iio_dev_attr_out_voltage2_scale.dev_attr.attr, |
| &iio_dev_attr_out_voltage1and2_raw.dev_attr.attr, |
| NULL |
| }; |
| |
| static struct attribute_group max518_attribute_group = { |
| .attrs = max518_attributes, |
| }; |
| |
| static int max517_suspend(struct i2c_client *client, pm_message_t mesg) |
| { |
| u8 outbuf = COMMAND_PD; |
| |
| return i2c_master_send(client, &outbuf, 1); |
| } |
| |
| static int max517_resume(struct i2c_client *client) |
| { |
| u8 outbuf = 0; |
| |
| return i2c_master_send(client, &outbuf, 1); |
| } |
| |
| static const struct iio_info max517_info = { |
| .attrs = &max517_attribute_group, |
| .driver_module = THIS_MODULE, |
| }; |
| |
| static const struct iio_info max518_info = { |
| .attrs = &max518_attribute_group, |
| .driver_module = THIS_MODULE, |
| }; |
| |
| static int max517_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct max517_data *data; |
| struct iio_dev *indio_dev; |
| struct max517_platform_data *platform_data = client->dev.platform_data; |
| int err; |
| |
| indio_dev = iio_allocate_device(sizeof(*data)); |
| if (indio_dev == NULL) { |
| err = -ENOMEM; |
| goto exit; |
| } |
| data = iio_priv(indio_dev); |
| i2c_set_clientdata(client, indio_dev); |
| data->client = client; |
| |
| /* establish that the iio_dev is a child of the i2c device */ |
| indio_dev->dev.parent = &client->dev; |
| |
| /* reduced attribute set for MAX517 */ |
| if (id->driver_data == ID_MAX517) |
| indio_dev->info = &max517_info; |
| else |
| indio_dev->info = &max518_info; |
| indio_dev->modes = INDIO_DIRECT_MODE; |
| |
| /* |
| * Reference voltage on MAX518 and default is 5V, else take vref_mv |
| * from platform_data |
| */ |
| if (id->driver_data == ID_MAX518 || !platform_data) { |
| data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */ |
| } else { |
| data->vref_mv[0] = platform_data->vref_mv[0]; |
| data->vref_mv[1] = platform_data->vref_mv[1]; |
| } |
| |
| err = iio_device_register(indio_dev); |
| if (err) |
| goto exit_free_device; |
| |
| dev_info(&client->dev, "DAC registered\n"); |
| |
| return 0; |
| |
| exit_free_device: |
| iio_free_device(indio_dev); |
| exit: |
| return err; |
| } |
| |
| static int max517_remove(struct i2c_client *client) |
| { |
| iio_free_device(i2c_get_clientdata(client)); |
| |
| return 0; |
| } |
| |
| static const struct i2c_device_id max517_id[] = { |
| { "max517", ID_MAX517 }, |
| { "max518", ID_MAX518 }, |
| { "max519", ID_MAX519 }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, max517_id); |
| |
| static struct i2c_driver max517_driver = { |
| .driver = { |
| .name = MAX517_DRV_NAME, |
| }, |
| .probe = max517_probe, |
| .remove = max517_remove, |
| .suspend = max517_suspend, |
| .resume = max517_resume, |
| .id_table = max517_id, |
| }; |
| |
| static int __init max517_init(void) |
| { |
| return i2c_add_driver(&max517_driver); |
| } |
| |
| static void __exit max517_exit(void) |
| { |
| i2c_del_driver(&max517_driver); |
| } |
| |
| MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); |
| MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC"); |
| MODULE_LICENSE("GPL"); |
| |
| module_init(max517_init); |
| module_exit(max517_exit); |