blob: 4d6059038b5a50055282bc588835a4c40572d004 [file] [log] [blame]
/**
* SPI Driver for LM95071 temperature sensor device. This is implemented
* specifically in the context of the Atheros 953x GFMN board, and not meant to
* be a general solution (a more robust solution would likely be implemented
* using a parport interface).
*
* The LM95071 temperature sensor uses a 3-pin SPI interface. On this board,
* the device shares the MISO and CLK pins with the flash device.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/hwmon.h>
#include <linux/spi/spi.h>
#include <asm/delay.h>
#include <atheros.h>
#include "ath_mn_lm95071.h"
#define DRVNAME "lm95071"
struct lm95071 {
struct device *dev;
void (*lock)(void);
void (*unlock)(void);
};
static void lm_lock(void) {
ath_mn_spi_enable_cs();
lm_spi_func_clear();
ath_gpio_drive_low(ATH_MN_TEMP_SENSOR_PIN);
lm_delay();
}
static void lm_unlock(void) {
ath_gpio_drive_high(ATH_MN_TEMP_SENSOR_PIN);
lm_spi_func_restore();
ath_mn_spi_disable_cs();
}
static struct lm95071 lm_dev = {
.dev = NULL,
.lock = lm_lock,
.unlock = lm_unlock,
};
/* Enables continuous conversion mode. */
static void lm95071_enable_cc(void)
{
lm_dev.lock();
lm_spi_delay_16();
lm_spi_delay_8();
lm_spi_bit_banger(LM_MODE_CC);
lm_dev.unlock();
}
static int lm95071_detect(void)
{
/* TODO(awdavies) Implement this function to read the ID register. */
return 0;
}
static ssize_t lm95071_get_temperature(struct device *dev,
struct device_attribute *attr, char *buf)
{
s16 temp;
u16 raw_temp, sign;
lm_dev.lock();
lm_spi_delay_16();
raw_temp = lm_spi_rd_16();
lm_dev.unlock();
sign = 0x8000 & raw_temp;
if (sign) {
raw_temp = ~raw_temp + 1;
}
temp = raw_temp >> 7;
/*
* LM90571 Temp register has two unused bits at the end. After these are
* removed, every number remaining corresponds to 0.03125 C of temperature.
*
* Official docs here: http://www.ti.com/lit/ds/symlink/lm95071.pdf
*/
return sprintf(buf, "%d\n", sign ? -temp : temp);
}
static DEVICE_ATTR(temp1_input, S_IRUGO, lm95071_get_temperature, NULL);
static ssize_t lm95071_get_name(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return sprintf(buf, "%s\n", DRVNAME);
}
static DEVICE_ATTR(name, S_IRUGO, lm95071_get_name, NULL);
static int lm95071_probe(void)
{
int res;
if (lm95071_detect()) {
printk(KERN_NOTICE "hwmon_dev_detect failed (lm95071)\n");
res = -ENOENT;
goto dev_detect_failed;
}
lm95071_enable_cc();
lm_dev.dev = hwmon_device_register(NULL);
if (IS_ERR(lm_dev.dev)) {
printk(KERN_NOTICE "%x hwmon_dev_reg failed (lm95071)\n", lm_dev.dev);
res = PTR_ERR(lm_dev.dev);
goto dev_reg_failed;
}
dev_set_drvdata(lm_dev.dev, &lm_dev);
/* Here is where sysfs gets hooked. */
if ((res = device_create_file(lm_dev.dev, &dev_attr_temp1_input))
|| (res = device_create_file(lm_dev.dev, &dev_attr_name))) {
printk(KERN_NOTICE "hwmon_dev_create failed (lm95071)\n");
goto dev_create_failed;
}
/* Sanity check. */
printk(KERN_NOTICE "lm95071 temp sensor driver initialized.\n");
return 0;
dev_create_failed:
device_remove_file(lm_dev.dev, &dev_attr_temp1_input);
hwmon_device_unregister(lm_dev.dev);
dev_reg_failed:
dev_set_drvdata(lm_dev.dev, NULL);
dev_detect_failed:
return res;
}
static void lm95071_remove(void)
{
device_remove_file(lm_dev.dev, &dev_attr_temp1_input);
device_remove_file(lm_dev.dev, &dev_attr_name);
hwmon_device_unregister(lm_dev.dev);
dev_set_drvdata(lm_dev.dev, NULL);
}
static int __init init_lm95071(void)
{
return lm95071_probe();
}
static void __exit cleanup_lm95071(void)
{
lm95071_remove();
}
module_init(init_lm95071);
module_exit(cleanup_lm95071);