| /** |
| * 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) |
| { |
| s64 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; |
| } |
| /* |
| * 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 |
| */ |
| temp = raw_temp >> 2; |
| temp *= 3125; |
| do_div(temp, 100); |
| return sprintf(buf, "%lld\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); |