aboutsummaryrefslogtreecommitdiff
path: root/drivers/hwmon/max1111.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/max1111.c')
-rw-r--r--drivers/hwmon/max1111.c151
1 files changed, 98 insertions, 53 deletions
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c
index 12a54aa2977..f67d71ee838 100644
--- a/drivers/hwmon/max1111.c
+++ b/drivers/hwmon/max1111.c
@@ -6,7 +6,7 @@
* Copyright (C) 2004-2005 Richard Purdie
*
* Copyright (C) 2008 Marvell International Ltd.
- * Eric Miao <eric.miao@marvell.com>
+ * Eric Miao <eric.miao@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -22,6 +22,8 @@
#include <linux/spi/spi.h>
#include <linux/slab.h>
+enum chips { max1110, max1111, max1112, max1113 };
+
#define MAX1111_TX_BUF_SIZE 1
#define MAX1111_RX_BUF_SIZE 2
@@ -30,6 +32,7 @@
#define MAX1111_CTRL_PD1 (1u << 1)
#define MAX1111_CTRL_SGL (1u << 2)
#define MAX1111_CTRL_UNI (1u << 3)
+#define MAX1110_CTRL_SEL_SH (4)
#define MAX1111_CTRL_SEL_SH (5) /* NOTE: bit 4 is ignored */
#define MAX1111_CTRL_STR (1u << 7)
@@ -38,8 +41,12 @@ struct max1111_data {
struct device *hwmon_dev;
struct spi_message msg;
struct spi_transfer xfer[2];
- uint8_t *tx_buf;
- uint8_t *rx_buf;
+ uint8_t tx_buf[MAX1111_TX_BUF_SIZE];
+ uint8_t rx_buf[MAX1111_RX_BUF_SIZE];
+ struct mutex drvdata_lock;
+ /* protect msg, xfer and buffers from multiple access */
+ int sel_sh;
+ int lsb;
};
static int max1111_read(struct device *dev, int channel)
@@ -48,19 +55,25 @@ static int max1111_read(struct device *dev, int channel)
uint8_t v1, v2;
int err;
- data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) |
+ /* writing to drvdata struct is not thread safe, wait on mutex */
+ mutex_lock(&data->drvdata_lock);
+
+ data->tx_buf[0] = (channel << data->sel_sh) |
MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 |
MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR;
err = spi_sync(data->spi, &data->msg);
if (err < 0) {
dev_err(dev, "spi_sync failed with %d\n", err);
+ mutex_unlock(&data->drvdata_lock);
return err;
}
v1 = data->rx_buf[0];
v2 = data->rx_buf[1];
+ mutex_unlock(&data->drvdata_lock);
+
if ((v1 & 0xc0) || (v2 & 0x3f))
return -EINVAL;
@@ -85,12 +98,13 @@ EXPORT_SYMBOL(max1111_read_channel);
static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "max1111\n");
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
}
static ssize_t show_adc(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct max1111_data *data = dev_get_drvdata(dev);
int channel = to_sensor_dev_attr(attr)->index;
int ret;
@@ -98,24 +112,32 @@ static ssize_t show_adc(struct device *dev,
if (ret < 0)
return ret;
- return sprintf(buf, "%d\n", ret);
+ /*
+ * Assume the reference voltage to be 2.048V or 4.096V, with an 8-bit
+ * sample. The LSB weight is 8mV or 16mV depending on the chip type.
+ */
+ return sprintf(buf, "%d\n", ret * data->lsb);
}
#define MAX1111_ADC_ATTR(_id) \
- SENSOR_DEVICE_ATTR(adc##_id##_in, S_IRUGO, show_adc, NULL, _id)
+ SENSOR_DEVICE_ATTR(in##_id##_input, S_IRUGO, show_adc, NULL, _id)
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static MAX1111_ADC_ATTR(0);
static MAX1111_ADC_ATTR(1);
static MAX1111_ADC_ATTR(2);
static MAX1111_ADC_ATTR(3);
+static MAX1111_ADC_ATTR(4);
+static MAX1111_ADC_ATTR(5);
+static MAX1111_ADC_ATTR(6);
+static MAX1111_ADC_ATTR(7);
static struct attribute *max1111_attributes[] = {
&dev_attr_name.attr,
- &sensor_dev_attr_adc0_in.dev_attr.attr,
- &sensor_dev_attr_adc1_in.dev_attr.attr,
- &sensor_dev_attr_adc2_in.dev_attr.attr,
- &sensor_dev_attr_adc3_in.dev_attr.attr,
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
NULL,
};
@@ -123,40 +145,43 @@ static const struct attribute_group max1111_attr_group = {
.attrs = max1111_attributes,
};
+static struct attribute *max1110_attributes[] = {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max1110_attr_group = {
+ .attrs = max1110_attributes,
+};
+
static int setup_transfer(struct max1111_data *data)
{
struct spi_message *m;
struct spi_transfer *x;
- data->tx_buf = kmalloc(MAX1111_TX_BUF_SIZE, GFP_KERNEL);
- if (!data->tx_buf)
- return -ENOMEM;
-
- data->rx_buf = kmalloc(MAX1111_RX_BUF_SIZE, GFP_KERNEL);
- if (!data->rx_buf) {
- kfree(data->tx_buf);
- return -ENOMEM;
- }
-
m = &data->msg;
x = &data->xfer[0];
spi_message_init(m);
x->tx_buf = &data->tx_buf[0];
- x->len = 1;
+ x->len = MAX1111_TX_BUF_SIZE;
spi_message_add_tail(x, m);
x++;
x->rx_buf = &data->rx_buf[0];
- x->len = 2;
+ x->len = MAX1111_RX_BUF_SIZE;
spi_message_add_tail(x, m);
return 0;
}
-static int __devinit max1111_probe(struct spi_device *spi)
+static int max1111_probe(struct spi_device *spi)
{
+ enum chips chip = spi_get_device_id(spi)->driver_data;
struct max1111_data *data;
int err;
@@ -166,15 +191,33 @@ static int __devinit max1111_probe(struct spi_device *spi)
if (err < 0)
return err;
- data = kzalloc(sizeof(struct max1111_data), GFP_KERNEL);
- if (data == NULL) {
- dev_err(&spi->dev, "failed to allocate memory\n");
+ data = devm_kzalloc(&spi->dev, sizeof(struct max1111_data), GFP_KERNEL);
+ if (data == NULL)
return -ENOMEM;
- }
+ switch (chip) {
+ case max1110:
+ data->lsb = 8;
+ data->sel_sh = MAX1110_CTRL_SEL_SH;
+ break;
+ case max1111:
+ data->lsb = 8;
+ data->sel_sh = MAX1111_CTRL_SEL_SH;
+ break;
+ case max1112:
+ data->lsb = 16;
+ data->sel_sh = MAX1110_CTRL_SEL_SH;
+ break;
+ case max1113:
+ data->lsb = 16;
+ data->sel_sh = MAX1111_CTRL_SEL_SH;
+ break;
+ }
err = setup_transfer(data);
if (err)
- goto err_free_data;
+ return err;
+
+ mutex_init(&data->drvdata_lock);
data->spi = spi;
spi_set_drvdata(spi, data);
@@ -182,7 +225,15 @@ static int __devinit max1111_probe(struct spi_device *spi)
err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group);
if (err) {
dev_err(&spi->dev, "failed to create attribute group\n");
- goto err_free_all;
+ return err;
+ }
+ if (chip == max1110 || chip == max1112) {
+ err = sysfs_create_group(&spi->dev.kobj, &max1110_attr_group);
+ if (err) {
+ dev_err(&spi->dev,
+ "failed to create extended attribute group\n");
+ goto err_remove;
+ }
}
data->hwmon_dev = hwmon_device_register(&spi->dev);
@@ -198,49 +249,43 @@ static int __devinit max1111_probe(struct spi_device *spi)
return 0;
err_remove:
+ sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
-err_free_all:
- kfree(data->rx_buf);
- kfree(data->tx_buf);
-err_free_data:
- kfree(data);
return err;
}
-static int __devexit max1111_remove(struct spi_device *spi)
+static int max1111_remove(struct spi_device *spi)
{
struct max1111_data *data = spi_get_drvdata(spi);
hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
- kfree(data->rx_buf);
- kfree(data->tx_buf);
- kfree(data);
+ mutex_destroy(&data->drvdata_lock);
return 0;
}
+static const struct spi_device_id max1111_ids[] = {
+ { "max1110", max1110 },
+ { "max1111", max1111 },
+ { "max1112", max1112 },
+ { "max1113", max1113 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, max1111_ids);
+
static struct spi_driver max1111_driver = {
.driver = {
.name = "max1111",
.owner = THIS_MODULE,
},
+ .id_table = max1111_ids,
.probe = max1111_probe,
- .remove = __devexit_p(max1111_remove),
+ .remove = max1111_remove,
};
-static int __init max1111_init(void)
-{
- return spi_register_driver(&max1111_driver);
-}
-module_init(max1111_init);
-
-static void __exit max1111_exit(void)
-{
- spi_unregister_driver(&max1111_driver);
-}
-module_exit(max1111_exit);
+module_spi_driver(max1111_driver);
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
-MODULE_DESCRIPTION("MAX1111 ADC Driver");
+MODULE_DESCRIPTION("MAX1110/MAX1111/MAX1112/MAX1113 ADC Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("spi:max1111");