From 51c2a4871c1b47255ff8d74f0a86b2a0defff319 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 12 Mar 2013 11:38:46 +0100 Subject: hwmon: (adt7410) Add support for the adt7310/adt7320 The adt7310/adt7320 is the SPI version of the adt7410/adt7420. The register map layout is a bit different, i.e. the register addresses differ between the two variants, but the bit layouts of the individual registers are identical. So both chip variants can easily be supported by the same driver. The issue of non matching register address layouts is solved by a simple look-up table which translates the I2C addresses to the SPI addresses. The patch moves the bulk of the adt7410 driver to a common module that will be shared by the adt7410 and adt7310 drivers. This common module implements the driver logic and uses a set of virtual functions to perform IO access. The adt7410 and adt7310 driver modules provide proper implementations of these IO accessor functions for I2C respective SPI. Signed-off-by: Lars-Peter Clausen Reviewed-by: Hartmut Knaack Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7x10.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 drivers/hwmon/adt7x10.h (limited to 'drivers/hwmon/adt7x10.h') diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h new file mode 100644 index 000000000000..803d9b91c5db --- /dev/null +++ b/drivers/hwmon/adt7x10.h @@ -0,0 +1,37 @@ +#ifndef __HWMON_ADT7X10_H__ +#define __HWMON_ADT7X10_H__ + +#include +#include + +/* ADT7410 registers definition */ +#define ADT7X10_TEMPERATURE 0 +#define ADT7X10_STATUS 2 +#define ADT7X10_CONFIG 3 +#define ADT7X10_T_ALARM_HIGH 4 +#define ADT7X10_T_ALARM_LOW 6 +#define ADT7X10_T_CRIT 8 +#define ADT7X10_T_HYST 0xA +#define ADT7X10_ID 0xB + +struct device; + +struct adt7x10_ops { + int (*read_byte)(struct device *, u8 reg); + int (*write_byte)(struct device *, u8 reg, u8 data); + int (*read_word)(struct device *, u8 reg); + int (*write_word)(struct device *, u8 reg, u16 data); +}; + +int adt7x10_probe(struct device *dev, const char *name, + const struct adt7x10_ops *ops); +int adt7x10_remove(struct device *dev); + +#ifdef CONFIG_PM_SLEEP +extern const struct dev_pm_ops adt7x10_dev_pm_ops; +#define ADT7X10_DEV_PM_OPS (&adt7x10_dev_pm_ops) +#else +#define ADT7X10_DEV_PM_OPS NULL +#endif + +#endif -- cgit v1.2.3 From 4b5e536b0e948b5c756aa1d57218371c242f768d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 12 Mar 2013 11:38:47 +0100 Subject: hwmon: (adt7x10) Add alarm interrupt support This allows an userspace application to poll() on the alarm files to get notified in case of a temperature threshold event. Signed-off-by: Lars-Peter Clausen Reviewed-by: Hartmut Knaack Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7310.c | 4 ++-- drivers/hwmon/adt7410.c | 4 ++-- drivers/hwmon/adt7x10.c | 41 ++++++++++++++++++++++++++++++++++++++--- drivers/hwmon/adt7x10.h | 4 ++-- 4 files changed, 44 insertions(+), 9 deletions(-) (limited to 'drivers/hwmon/adt7x10.h') diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c index f8ea6292bc74..b70a481bca17 100644 --- a/drivers/hwmon/adt7310.c +++ b/drivers/hwmon/adt7310.c @@ -90,13 +90,13 @@ static const struct adt7x10_ops adt7310_spi_ops = { static int adt7310_spi_probe(struct spi_device *spi) { - return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name, + return adt7x10_probe(&spi->dev, spi_get_device_id(spi)->name, spi->irq, &adt7310_spi_ops); } static int adt7310_spi_remove(struct spi_device *spi) { - return adt7x10_remove(&spi->dev); + return adt7x10_remove(&spi->dev, spi->irq); } static const struct spi_device_id adt7310_id[] = { diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index d294445c86dd..0dc066a939b4 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -47,12 +47,12 @@ static int adt7410_i2c_probe(struct i2c_client *client, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - return adt7x10_probe(&client->dev, NULL, &adt7410_i2c_ops); + return adt7x10_probe(&client->dev, NULL, client->irq, &adt7410_i2c_ops); } static int adt7410_i2c_remove(struct i2c_client *client) { - return adt7x10_remove(&client->dev); + return adt7x10_remove(&client->dev, client->irq); } static const struct i2c_device_id adt7410_ids[] = { diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c index 84b3dfc1c1cd..98141f483165 100644 --- a/drivers/hwmon/adt7x10.c +++ b/drivers/hwmon/adt7x10.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "adt7x10.h" @@ -112,6 +113,25 @@ static const u8 ADT7X10_REG_TEMP[4] = { ADT7X10_T_CRIT, /* critical */ }; +static irqreturn_t adt7x10_irq_handler(int irq, void *private) +{ + struct device *dev = private; + int status; + + status = adt7x10_read_byte(dev, ADT7X10_STATUS); + if (status < 0) + return IRQ_HANDLED; + + if (status & ADT7X10_STAT_T_HIGH) + sysfs_notify(&dev->kobj, NULL, "temp1_max_alarm"); + if (status & ADT7X10_STAT_T_LOW) + sysfs_notify(&dev->kobj, NULL, "temp1_min_alarm"); + if (status & ADT7X10_STAT_T_CRIT) + sysfs_notify(&dev->kobj, NULL, "temp1_crit_alarm"); + + return IRQ_HANDLED; +} + static int adt7x10_temp_ready(struct device *dev) { int i, status; @@ -359,7 +379,7 @@ static const struct attribute_group adt7x10_group = { .attrs = adt7x10_attributes, }; -int adt7x10_probe(struct device *dev, const char *name, +int adt7x10_probe(struct device *dev, const char *name, int irq, const struct adt7x10_ops *ops) { struct adt7x10_data *data; @@ -387,8 +407,10 @@ int adt7x10_probe(struct device *dev, const char *name, * Set to 16 bit resolution, continous conversion and comparator mode. */ data->config = data->oldconfig; - data->config &= ~ADT7X10_MODE_MASK; + data->config &= ~(ADT7X10_MODE_MASK | ADT7X10_CT_POLARITY | + ADT7X10_INT_POLARITY); data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE; + if (data->config != data->oldconfig) { ret = adt7x10_write_byte(dev, ADT7X10_CONFIG, data->config); if (ret) @@ -422,8 +444,18 @@ int adt7x10_probe(struct device *dev, const char *name, goto exit_remove_name; } + if (irq > 0) { + ret = request_threaded_irq(irq, NULL, adt7x10_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), dev); + if (ret) + goto exit_hwmon_device_unregister; + } + return 0; +exit_hwmon_device_unregister: + hwmon_device_unregister(data->hwmon_dev); exit_remove_name: if (name) device_remove_file(dev, &dev_attr_name); @@ -435,10 +467,13 @@ exit_restore: } EXPORT_SYMBOL_GPL(adt7x10_probe); -int adt7x10_remove(struct device *dev) +int adt7x10_remove(struct device *dev, int irq) { struct adt7x10_data *data = dev_get_drvdata(dev); + if (irq > 0) + free_irq(irq, dev); + hwmon_device_unregister(data->hwmon_dev); if (data->name) device_remove_file(dev, &dev_attr_name); diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h index 803d9b91c5db..d491c698529e 100644 --- a/drivers/hwmon/adt7x10.h +++ b/drivers/hwmon/adt7x10.h @@ -23,9 +23,9 @@ struct adt7x10_ops { int (*write_word)(struct device *, u8 reg, u16 data); }; -int adt7x10_probe(struct device *dev, const char *name, +int adt7x10_probe(struct device *dev, const char *name, int irq, const struct adt7x10_ops *ops); -int adt7x10_remove(struct device *dev); +int adt7x10_remove(struct device *dev, int irq); #ifdef CONFIG_PM_SLEEP extern const struct dev_pm_ops adt7x10_dev_pm_ops; -- cgit v1.2.3