diff options
Diffstat (limited to 'drivers/hwmon')
36 files changed, 4043 insertions, 841 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5f888f7e7dc..0b62c3c6b7c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -623,7 +623,7 @@ config SENSORS_LM90 LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, - and Winbond/Nuvoton W83L771W/G/AWG/ASG sensor chips. + Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -694,14 +694,24 @@ config SENSORS_LTC4261 be called ltc4261. config SENSORS_LM95241 - tristate "National Semiconductor LM95241 sensor chip" + tristate "National Semiconductor LM95241 and compatibles" depends on I2C help - If you say yes here you get support for LM95241 sensor chip. + If you say yes here you get support for LM95231 and LM95241 sensor + chips. This driver can also be built as a module. If so, the module will be called lm95241. +config SENSORS_LM95245 + tristate "National Semiconductor LM95245 sensor chip" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for LM95245 sensor chip. + + This driver can also be built as a module. If so, the module + will be called lm95245. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip" depends on SPI_MASTER @@ -736,6 +746,16 @@ config SENSORS_MAX1619 This driver can also be built as a module. If so, the module will be called max1619. +config SENSORS_MAX1668 + tristate "Maxim MAX1668 and compatibles" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for MAX1668, MAX1989 and + MAX1805 chips. + + This driver can also be built as a module. If so, the module + will be called max1668. + config SENSORS_MAX6639 tristate "Maxim MAX6639 sensor chip" depends on I2C && EXPERIMENTAL @@ -767,6 +787,20 @@ config SENSORS_MAX6650 This driver can also be built as a module. If so, the module will be called max6650. +config SENSORS_NTC_THERMISTOR + tristate "NTC thermistor support" + depends on EXPERIMENTAL + help + This driver supports NTC thermistors sensor reading and its + interpretation. The driver can also monitor the temperature and + send notifications about the temperature. + + Currently, this driver supports + NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333. + + This driver can also be built as a module. If so, the module + will be called ntc-thermistor. + config SENSORS_PC87360 tristate "National Semiconductor PC87360 family" select HWMON_VID @@ -807,92 +841,7 @@ config SENSORS_PCF8591 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. -config PMBUS - tristate "PMBus support" - depends on I2C && EXPERIMENTAL - default n - help - Say yes here if you want to enable PMBus support. - - This driver can also be built as a module. If so, the module will - be called pmbus_core. - -if PMBUS - -config SENSORS_PMBUS - tristate "Generic PMBus devices" - default n - help - If you say yes here you get hardware monitoring support for generic - PMBus devices, including but not limited to BMR450, BMR451, BMR453, - BMR454, and LTC2978. - - This driver can also be built as a module. If so, the module will - be called pmbus. - -config SENSORS_ADM1275 - tristate "Analog Devices ADM1275" - default n - help - If you say yes here you get hardware monitoring support for Analog - Devices ADM1275 Hot-Swap Controller and Digital Power Monitor. - - This driver can also be built as a module. If so, the module will - be called adm1275. - -config SENSORS_MAX16064 - tristate "Maxim MAX16064" - default n - help - If you say yes here you get hardware monitoring support for Maxim - MAX16064. - - This driver can also be built as a module. If so, the module will - be called max16064. - -config SENSORS_MAX34440 - tristate "Maxim MAX34440/MAX34441" - default n - help - If you say yes here you get hardware monitoring support for Maxim - MAX34440 and MAX34441. - - This driver can also be built as a module. If so, the module will - be called max34440. - -config SENSORS_MAX8688 - tristate "Maxim MAX8688" - default n - help - If you say yes here you get hardware monitoring support for Maxim - MAX8688. - - This driver can also be built as a module. If so, the module will - be called max8688. - -config SENSORS_UCD9000 - tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" - default n - help - If you say yes here you get hardware monitoring support for TI - UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health - Controllers. - - This driver can also be built as a module. If so, the module will - be called ucd9000. - -config SENSORS_UCD9200 - tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" - default n - help - If you say yes here you get hardware monitoring support for TI - UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248 - Digital PWM System Controllers. - - This driver can also be built as a module. If so, the module will - be called ucd9200. - -endif # PMBUS +source drivers/hwmon/pmbus/Kconfig config SENSORS_SHT15 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." @@ -1041,8 +990,13 @@ config SENSORS_SMSC47B397 This driver can also be built as a module. If so, the module will be called smsc47b397. +config SENSORS_SCH56XX_COMMON + tristate + default n + config SENSORS_SCH5627 tristate "SMSC SCH5627" + select SENSORS_SCH56XX_COMMON help If you say yes here you get support for the hardware monitoring features of the SMSC SCH5627 Super-I/O chip. @@ -1050,6 +1004,21 @@ config SENSORS_SCH5627 This driver can also be built as a module. If so, the module will be called sch5627. +config SENSORS_SCH5636 + tristate "SMSC SCH5636" + select SENSORS_SCH56XX_COMMON + help + SMSC SCH5636 Super I/O chips include an embedded microcontroller for + hardware monitoring solutions, allowing motherboard manufacturers to + create their own custom hwmon solution based upon the SCH5636. + + Currently this driver only supports the Fujitsu Theseus SCH5636 based + hwmon solution. Say yes here if you want support for the Fujitsu + Theseus' hardware monitoring features. + + This driver can also be built as a module. If so, the module + will be called sch5636. + config SENSORS_ADS1015 tristate "Texas Instruments ADS1015" depends on I2C @@ -1142,6 +1111,7 @@ config SENSORS_TWL4030_MADC config SENSORS_VIA_CPUTEMP tristate "VIA CPU temperature sensor" depends on X86 + select HWMON_VID help If you say yes here you get support for the temperature sensor inside your CPU. Supported are all known variants of diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 28061cfa0cd..3c9ccefea79 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o +obj-$(CONFIG_SENSORS_LM95245) += lm95245.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o @@ -87,15 +88,19 @@ obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX16065) += max16065.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o +obj-$(CONFIG_SENSORS_MAX1668) += max1668.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o +obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o +obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o @@ -119,15 +124,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o -# PMBus drivers -obj-$(CONFIG_PMBUS) += pmbus_core.o -obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o -obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o -obj-$(CONFIG_SENSORS_MAX16064) += max16064.o -obj-$(CONFIG_SENSORS_MAX34440) += max34440.o -obj-$(CONFIG_SENSORS_MAX8688) += max8688.o -obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o -obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o +obj-$(CONFIG_PMBUS) += pmbus/ ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 0070d5476dd..93238378664 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -36,15 +36,25 @@ #include <linux/cpu.h> #include <linux/pci.h> #include <linux/smp.h> +#include <linux/moduleparam.h> #include <asm/msr.h> #include <asm/processor.h> #define DRVNAME "coretemp" +/* + * force_tjmax only matters when TjMax can't be read from the CPU itself. + * When set, it replaces the driver's suboptimal heuristic. + */ +static int force_tjmax; +module_param_named(tjmax, force_tjmax, int, 0444); +MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); + #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ #define NUM_REAL_CORES 16 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 17 /* String Length of attrs */ -#define MAX_ATTRS 5 /* Maximum no of per-core attrs */ +#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ +#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) #ifdef CONFIG_SMP @@ -67,6 +77,7 @@ * This value is passed as "id" field to rdmsr/wrmsr functions. * @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS, * from where the temperature values should be read. + * @attr_size: Total number of pre-core attrs displayed in the sysfs. * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data. * Otherwise, temp_data holds coretemp data. * @valid: If this is 1, the current temperature is valid. @@ -79,10 +90,11 @@ struct temp_data { unsigned int cpu; u32 cpu_core_id; u32 status_reg; + int attr_size; bool is_pkg_data; bool valid; - struct sensor_device_attribute sd_attrs[MAX_ATTRS]; - char attr_name[MAX_ATTRS][CORETEMP_NAME_LENGTH]; + struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; + char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; struct mutex update_lock; }; @@ -276,7 +288,6 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) { - /* The 100C is default for both mobile and non mobile CPUs */ int err; u32 eax, edx; u32 val; @@ -287,7 +298,8 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) */ err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); if (err) { - dev_warn(dev, "Unable to read TjMax from CPU.\n"); + if (c->x86_model > 0xe && c->x86_model != 0x1c) + dev_warn(dev, "Unable to read TjMax from CPU %u\n", id); } else { val = (eax >> 16) & 0xff; /* @@ -295,11 +307,17 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) * will be used */ if (val) { - dev_info(dev, "TjMax is %d C.\n", val); + dev_dbg(dev, "TjMax is %d degrees C\n", val); return val * 1000; } } + if (force_tjmax) { + dev_notice(dev, "TjMax forced to %d degrees C by user\n", + force_tjmax); + return force_tjmax * 1000; + } + /* * An assumption is made for early CPUs and unreadable MSR. * NOTE: the calculated value may not be correct. @@ -316,21 +334,6 @@ static void __devinit get_ucode_rev_on_cpu(void *edx) rdmsr(MSR_IA32_UCODE_REV, eax, *(u32 *)edx); } -static int get_pkg_tjmax(unsigned int cpu, struct device *dev) -{ - int err; - u32 eax, edx, val; - - err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); - if (!err) { - val = (eax >> 16) & 0xff; - if (val) - return val * 1000; - } - dev_warn(dev, "Unable to read Pkg-TjMax from CPU:%u\n", cpu); - return 100000; /* Default TjMax: 100 degree celsius */ -} - static int create_name_attr(struct platform_data *pdata, struct device *dev) { sysfs_attr_init(&pdata->name_attr.attr); @@ -344,23 +347,22 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, int attr_no) { int err, i; - static ssize_t (*rd_ptr[MAX_ATTRS]) (struct device *dev, + static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { - show_label, show_crit_alarm, show_ttarget, - show_temp, show_tjmax }; - static const char *names[MAX_ATTRS] = { + show_label, show_crit_alarm, show_temp, show_tjmax, + show_ttarget }; + static const char *const names[TOTAL_ATTRS] = { "temp%d_label", "temp%d_crit_alarm", - "temp%d_max", "temp%d_input", - "temp%d_crit" }; + "temp%d_input", "temp%d_crit", + "temp%d_max" }; - for (i = 0; i < MAX_ATTRS; i++) { + for (i = 0; i < tdata->attr_size; i++) { snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], attr_no); sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; - tdata->sd_attrs[i].dev_attr.store = NULL; tdata->sd_attrs[i].index = attr_no; err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr); if (err) @@ -374,42 +376,10 @@ exit_free: return err; } -static void update_ttarget(__u8 cpu_model, struct temp_data *tdata, - struct device *dev) -{ - int err; - u32 eax, edx; - /* - * Initialize ttarget value. Eventually this will be - * initialized with the value from MSR_IA32_THERM_INTERRUPT - * register. If IA32_TEMPERATURE_TARGET is supported, this - * value will be over written below. - * To Do: Patch to initialize ttarget from MSR_IA32_THERM_INTERRUPT - */ - tdata->ttarget = tdata->tjmax - 20000; - - /* - * Read the still undocumented IA32_TEMPERATURE_TARGET. It exists - * on older CPUs but not in this register, - * Atoms don't have it either. - */ - if (cpu_model > 0xe && cpu_model != 0x1c) { - err = rdmsr_safe_on_cpu(tdata->cpu, - MSR_IA32_TEMPERATURE_TARGET, &eax, &edx); - if (err) { - dev_warn(dev, - "Unable to read IA32_TEMPERATURE_TARGET MSR\n"); - } else { - tdata->ttarget = tdata->tjmax - - ((eax >> 8) & 0xff) * 1000; - } - } -} - -static int __devinit chk_ucode_version(struct platform_device *pdev) +static int __cpuinit chk_ucode_version(unsigned int cpu) { - struct cpuinfo_x86 *c = &cpu_data(pdev->id); + struct cpuinfo_x86 *c = &cpu_data(cpu); int err; u32 edx; @@ -420,17 +390,15 @@ static int __devinit chk_ucode_version(struct platform_device *pdev) */ if (c->x86_model == 0xe && c->x86_mask < 0xc) { /* check for microcode update */ - err = smp_call_function_single(pdev->id, get_ucode_rev_on_cpu, + err = smp_call_function_single(cpu, get_ucode_rev_on_cpu, &edx, 1); if (err) { - dev_err(&pdev->dev, - "Cannot determine microcode revision of " - "CPU#%u (%d)!\n", pdev->id, err); + pr_err("Cannot determine microcode revision of " + "CPU#%u (%d)!\n", cpu, err); return -ENODEV; } else if (edx < 0x39) { - dev_err(&pdev->dev, - "Errata AE18 not fixed, update BIOS or " - "microcode of the CPU!\n"); + pr_err("Errata AE18 not fixed, update BIOS or " + "microcode of the CPU!\n"); return -ENODEV; } } @@ -467,15 +435,16 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag) tdata->is_pkg_data = pkg_flag; tdata->cpu = cpu; tdata->cpu_core_id = TO_CORE_ID(cpu); + tdata->attr_size = MAX_CORE_ATTRS; mutex_init(&tdata->update_lock); return tdata; } -static int create_core_data(struct platform_data *pdata, - struct platform_device *pdev, +static int create_core_data(struct platform_device *pdev, unsigned int cpu, int pkg_flag) { struct temp_data *tdata; + struct platform_data *pdata = platform_get_drvdata(pdev); struct cpuinfo_x86 *c = &cpu_data(cpu); u32 eax, edx; int err, attr_no; @@ -511,12 +480,23 @@ static int create_core_data(struct platform_data *pdata, goto exit_free; /* We can access status register. Get Critical Temperature */ - if (pkg_flag) - tdata->tjmax = get_pkg_tjmax(pdev->id, &pdev->dev); - else - tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); + tdata->tjmax = get_tjmax(c, cpu, &pdev->dev); + + /* + * Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. + * The target temperature is available on older CPUs but not in this + * register. Atoms don't have the register at all. + */ + if (c->x86_model > 0xe && c->x86_model != 0x1c) { + err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, + &eax, &edx); + if (!err) { + tdata->ttarget + = tdata->tjmax - ((eax >> 8) & 0xff) * 1000; + tdata->attr_size++; + } + } - update_ttarget(c->x86_model, tdata, &pdev->dev); pdata->core_data[attr_no] = tdata; /* Create sysfs interfaces */ @@ -526,22 +506,20 @@ static int create_core_data(struct platform_data *pdata, return 0; exit_free: + pdata->core_data[attr_no] = NULL; kfree(tdata); return err; } static void coretemp_add_core(unsigned int cpu, int pkg_flag) { - struct platform_data *pdata; struct platform_device *pdev = coretemp_get_pdev(cpu); int err; if (!pdev) return; - pdata = platform_get_drvdata(pdev); - - err = create_core_data(pdata, pdev, cpu, pkg_flag); + err = create_core_data(pdev, cpu, pkg_flag); if (err) dev_err(&pdev->dev, "Adding Core %u failed\n", cpu); } @@ -553,7 +531,7 @@ static void coretemp_remove_core(struct platform_data *pdata, struct temp_data *tdata = pdata->core_data[indx]; /* Remove the sysfs attributes */ - for (i = 0; i < MAX_ATTRS; i++) + for (i = 0; i < tdata->attr_size; i++) device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); kfree(pdata->core_data[indx]); @@ -565,11 +543,6 @@ static int __devinit coretemp_probe(struct platform_device *pdev) struct platform_data *pdata; int err; - /* Check the microcode version of the CPU */ - err = chk_ucode_version(pdev); - if (err) - return err; - /* Initialize the per-package data structures */ pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL); if (!pdata) @@ -579,7 +552,7 @@ static int __devinit coretemp_probe(struct platform_device *pdev) if (err) goto exit_free; - pdata->phys_proc_id = TO_PHYS_ID(pdev->id); + pdata->phys_proc_id = pdev->id; platform_set_drvdata(pdev, pdata); pdata->hwmon_dev = hwmon_device_register(&pdev->dev); @@ -631,7 +604,7 @@ static int __cpuinit coretemp_device_add(unsigned int cpu) mutex_lock(&pdev_list_mutex); - pdev = platform_device_alloc(DRVNAME, cpu); + pdev = platform_device_alloc(DRVNAME, TO_PHYS_ID(cpu)); if (!pdev) { err = -ENOMEM; pr_err("Device allocation failed\n"); @@ -651,7 +624,7 @@ static int __cpuinit coretemp_device_add(unsigned int cpu) } pdev_entry->pdev = pdev; - pdev_entry->phys_proc_id = TO_PHYS_ID(cpu); + pdev_entry->phys_proc_id = pdev->id; list_add_tail(&pdev_entry->list, &pdev_list); mutex_unlock(&pdev_list_mutex); @@ -712,6 +685,10 @@ static void __cpuinit get_core_online(unsigned int cpu) return; if (!pdev) { + /* Check the microcode version of the CPU */ + if (chk_ucode_version(cpu)) + return; + /* * Alright, we have DTS support. * We are bringing the _first_ core in this pkg diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index 257957c69d9..4f7c3fc40a8 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -72,7 +72,7 @@ struct ds620_data { char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ - u16 temp[3]; /* Register values, word */ + s16 temp[3]; /* Register values, word */ }; /* diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index ec588026f0a..131ea8625f0 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -273,7 +273,7 @@ static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR, #define DIV_FROM_REG(val) (1 << (val)) #define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div)))) -#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255)); +#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255)) static ssize_t get_fan_input(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index c8195a077da..932da8a5aaf 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c @@ -140,7 +140,11 @@ int vid_from_reg(int val, u8 vrm) return(val & 0x10 ? 975 - (val & 0xF) * 25 : 1750 - val * 50); case 13: + case 131: val &= 0x3f; + /* Exception for Eden ULV 500 MHz */ + if (vrm == 131 && val == 0x3f) + val++; return(1708 - val * 16); case 14: /* Intel Core */ /* compute in uV, round to mV */ @@ -205,11 +209,45 @@ static struct vrm_model vrm_models[] = { {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85}, /* Nehemiah */ {X86_VENDOR_CENTAUR, 0x6, 0x9, ANY, 17}, /* C3-M, Eden-N */ {X86_VENDOR_CENTAUR, 0x6, 0xA, 0x7, 0}, /* No information */ - {X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13}, /* C7, Esther */ + {X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13}, /* C7-M, C7, Eden (Esther) */ + {X86_VENDOR_CENTAUR, 0x6, 0xD, ANY, 134}, /* C7-D, C7-M, C7, Eden (Esther) */ {X86_VENDOR_UNKNOWN, ANY, ANY, ANY, 0} /* stop here */ }; +/* + * Special case for VIA model D: there are two different possible + * VID tables, so we have to figure out first, which one must be + * used. This resolves temporary drm value 134 to 14 (Intel Core + * 7-bit VID), 13 (Pentium M 6-bit VID) or 131 (Pentium M 6-bit VID + * + quirk for Eden ULV 500 MHz). + * Note: something similar might be needed for model A, I'm not sure. + */ +static u8 get_via_model_d_vrm(void) +{ + unsigned int vid, brand, dummy; + static const char *brands[4] = { + "C7-M", "C7", "Eden", "C7-D" + }; + + rdmsr(0x198, dummy, vid); + vid &= 0xff; + + rdmsr(0x1154, brand, dummy); + brand = ((brand >> 4) ^ (brand >> 2)) & 0x03; + + if (vid > 0x3f) { + pr_info("Using %d-bit VID table for VIA %s CPU\n", + 7, brands[brand]); + return 14; + } else { + pr_info("Using %d-bit VID table for VIA %s CPU\n", + 6, brands[brand]); + /* Enable quirk for Eden */ + return brand == 2 ? 131 : 13; + } +} + static u8 find_vrm(u8 eff_family, u8 eff_model, u8 eff_stepping, u8 vendor) { int i = 0; @@ -247,6 +285,8 @@ u8 vid_which_vrm(void) eff_model += ((eax & 0x000F0000)>>16)<<4; } vrm_ret = find_vrm(eff_family, eff_model, eff_stepping, c->x86_vendor); + if (vrm_ret == 134) + vrm_ret = get_via_model_d_vrm(); if (vrm_ret == 0) pr_info("Unknown VRM version of your x86 CPU\n"); return vrm_ret; diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index c4c40be0edb..d22f241b6a6 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -114,7 +114,6 @@ struct i5k_amb_data { void __iomem *amb_mmio; struct i5k_device_attribute *attrs; unsigned int num_attrs; - unsigned long chipset_id; }; static ssize_t show_name(struct device *dev, struct device_attribute *devattr, @@ -444,8 +443,6 @@ static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data, goto out; } - data->chipset_id = devid; - res = 0; out: pci_dev_put(pcidev); @@ -478,23 +475,13 @@ out: return res; } -static unsigned long i5k_channel_pci_id(struct i5k_amb_data *data, - unsigned long channel) -{ - switch (data->chipset_id) { - case PCI_DEVICE_ID_INTEL_5000_ERR: - return PCI_DEVICE_ID_INTEL_5000_FBD0 + channel; - case PCI_DEVICE_ID_INTEL_5400_ERR: - return PCI_DEVICE_ID_INTEL_5400_FBD0 + channel; - default: - BUG(); - } -} - -static unsigned long chipset_ids[] = { - PCI_DEVICE_ID_INTEL_5000_ERR, - PCI_DEVICE_ID_INTEL_5400_ERR, - 0 +static struct { + unsigned long err; + unsigned long fbd0; +} chipset_ids[] __devinitdata = { + { PCI_DEVICE_ID_INTEL_5000_ERR, PCI_DEVICE_ID_INTEL_5000_FBD0 }, + { PCI_DEVICE_ID_INTEL_5400_ERR, PCI_DEVICE_ID_INTEL_5400_FBD0 }, + { 0, 0 } }; #ifdef MODULE @@ -510,8 +497,7 @@ static int __devinit i5k_amb_probe(struct platform_device *pdev) { struct i5k_amb_data *data; struct resource *reso; - int i; - int res = -ENODEV; + int i, res; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) @@ -520,22 +506,22 @@ static int __devinit i5k_amb_probe(struct platform_device *pdev) /* Figure out where the AMB registers live */ i = 0; do { - res = i5k_find_amb_registers(data, chipset_ids[i]); + res = i5k_find_amb_registers(data, chipset_ids[i].err); + if (res == 0) + break; i++; - } while (res && chipset_ids[i]); + } while (chipset_ids[i].err); if (res) goto err; /* Copy the DIMM presence map for the first two channels */ - res = i5k_channel_probe(&data->amb_present[0], - i5k_channel_pci_id(data, 0)); + res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0); if (res) goto err; /* Copy the DIMM presence map for the optional second two channels */ - i5k_channel_probe(&data->amb_present[2], - i5k_channel_pci_id(data, 1)); + i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1); /* Set up resource regions */ reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 1a409c5bc9b..c316294c48b 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -432,13 +432,15 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, aem_send_message(ipmi); res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); - if (!res) - return -ETIMEDOUT; + if (!res) { + res = -ETIMEDOUT; + goto out; + } if (ipmi->rx_result || ipmi->rx_msg_len != rs_size || memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) { - kfree(rs_resp); - return -ENOENT; + res = -ENOENT; + goto out; } switch (size) { @@ -463,8 +465,11 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, break; } } + res = 0; - return 0; +out: + kfree(rs_resp); + return res; } /* Update AEM energy registers */ diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 5f524775043..d912649fac5 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1172,6 +1172,32 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } + +static ssize_t clear_intrusion(struct device *dev, struct device_attribute + *attr, const char *buf, size_t count) +{ + struct it87_data *data = dev_get_drvdata(dev); + long val; + int config; + + if (strict_strtol(buf, 10, &val) < 0 || val != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + config = it87_read_value(data, IT87_REG_CONFIG); + if (config < 0) { + count = config; + } else { + config |= 1 << 5; + it87_write_value(data, IT87_REG_CONFIG, config); + /* Invalidate cache to force re-read */ + data->valid = 0; + } + mutex_unlock(&data->update_lock); + + return count; +} + static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 8); static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 9); static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 10); @@ -1188,6 +1214,8 @@ static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18); +static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR, + show_alarm, clear_intrusion, 4); static ssize_t show_beep(struct device *dev, struct device_attribute *attr, char *buf) @@ -1350,6 +1378,7 @@ static struct attribute *it87_attributes[] = { &sensor_dev_attr_temp3_alarm.dev_attr.attr, &dev_attr_alarms.attr, + &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, &dev_attr_name.attr, NULL }; diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 4cb24eafe31..6df0b468171 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -2,7 +2,7 @@ lm78.c - Part of lm_sensors, Linux kernel modules for hardware monitoring Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> - Copyright (c) 2007 Jean Delvare <khali@linux-fr.org> + Copyright (c) 2007, 2011 Jean Delvare <khali@linux-fr.org> 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 @@ -26,23 +26,21 @@ #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> -#include <linux/platform_device.h> -#include <linux/ioport.h> #include <linux/hwmon.h> #include <linux/hwmon-vid.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> -#include <linux/io.h> -/* ISA device, if found */ -static struct platform_device *pdev; +#ifdef CONFIG_ISA +#include <linux/platform_device.h> +#include <linux/ioport.h> +#include <linux/io.h> +#endif /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; -static unsigned short isa_address = 0x290; - enum chips { lm78, lm79 }; /* Many LM78 constants specified below */ @@ -143,50 +141,12 @@ struct lm78_data { }; -static int lm78_i2c_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int lm78_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int lm78_i2c_remove(struct i2c_client *client); - -static int __devinit lm78_isa_probe(struct platform_device *pdev); -static int __devexit lm78_isa_remove(struct platform_device *pdev); - static int lm78_read_value(struct lm78_data *data, u8 reg); static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value); static struct lm78_data *lm78_update_device(struct device *dev); static void lm78_init_device(struct lm78_data *data); -static const struct i2c_device_id lm78_i2c_id[] = { - { "lm78", lm78 }, - { "lm79", lm79 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); - -static struct i2c_driver lm78_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "lm78", - }, - .probe = lm78_i2c_probe, - .remove = lm78_i2c_remove, - .id_table = lm78_i2c_id, - .detect = lm78_i2c_detect, - .address_list = normal_i2c, -}; - -static struct platform_driver lm78_isa_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "lm78", - }, - .probe = lm78_isa_probe, - .remove = __devexit_p(lm78_isa_remove), -}; - - /* 7 Voltages */ static ssize_t show_in(struct device *dev, struct device_attribute *da, char *buf) @@ -514,6 +474,16 @@ static const struct attribute_group lm78_group = { .attrs = lm78_attributes, }; +/* + * ISA related code + */ +#ifdef CONFIG_ISA + +/* ISA device, if found */ +static struct platform_device *pdev; + +static unsigned short isa_address = 0x290; + /* I2C devices get this name attribute automatically, but for ISA devices we must create it by ourselves. */ static ssize_t show_name(struct device *dev, struct device_attribute @@ -525,6 +495,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static struct lm78_data *lm78_data_if_isa(void) +{ + return pdev ? platform_get_drvdata(pdev) : NULL; +} + /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) { @@ -558,12 +533,24 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) return 1; } +#else /* !CONFIG_ISA */ + +static int lm78_alias_detect(struct i2c_client *client, u8 chipid) +{ + return 0; +} + +static struct lm78_data *lm78_data_if_isa(void) +{ + return NULL; +} +#endif /* CONFIG_ISA */ static int lm78_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) { int i; - struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL; + struct lm78_data *isa = lm78_data_if_isa(); const char *client_name; struct i2c_adapter *adapter = client->adapter; int address = client->addr; @@ -663,76 +650,24 @@ static int lm78_i2c_remove(struct i2c_client *client) return 0; } -static int __devinit lm78_isa_probe(struct platform_device *pdev) -{ - int err; - struct lm78_data *data; - struct resource *res; - - /* Reserve the ISA region */ - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { - err = -EBUSY; - goto exit; - } - - if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release_region; - } - mutex_init(&data->lock); - data->isa_addr = res->start; - platform_set_drvdata(pdev, data); - - if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { - data->type = lm79; - data->name = "lm79"; - } else { - data->type = lm78; - data->name = "lm78"; - } - - /* Initialize the LM78 chip */ - lm78_init_device(data); - - /* Register sysfs hooks */ - if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group)) - || (err = device_create_file(&pdev->dev, &dev_attr_name))) - goto exit_remove_files; - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - - exit_remove_files: - sysfs_remove_group(&pdev->dev.kobj, &lm78_group); - device_remove_file(&pdev->dev, &dev_attr_name); - kfree(data); - exit_release_region: - release_region(res->start + LM78_ADDR_REG_OFFSET, 2); - exit: - return err; -} - -static int __devexit lm78_isa_remove(struct platform_device *pdev) -{ - struct lm78_data *data = platform_get_drvdata(pdev); - struct resource *res; - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &lm78_group); - device_remove_file(&pdev->dev, &dev_attr_name); - kfree(data); - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - release_region(res->start + LM78_ADDR_REG_OFFSET, 2); +static const struct i2c_device_id lm78_i2c_id[] = { + { "lm78", lm78 }, + { "lm79", lm79 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm78_i2c_id); - return 0; -} +static struct i2c_driver lm78_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm78", + }, + .probe = lm78_i2c_probe, + .remove = lm78_i2c_remove, + .id_table = lm78_i2c_id, + .detect = lm78_i2c_detect, + .address_list = normal_i2c, +}; /* The SMBus locks itself, but ISA access must be locked explicitly! We don't want to lock the whole ISA bus, so we lock each client @@ -743,6 +678,7 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) { struct i2c_client *client = data->client; +#ifdef CONFIG_ISA if (!client) { /* ISA device */ int res; mutex_lock(&data->lock); @@ -751,6 +687,7 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) mutex_unlock(&data->lock); return res; } else +#endif return i2c_smbus_read_byte_data(client, reg); } @@ -765,6 +702,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) { struct i2c_client *client = data->client; +#ifdef CONFIG_ISA if (!client) { /* ISA device */ mutex_lock(&data->lock); outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); @@ -772,6 +710,7 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) mutex_unlock(&data->lock); return 0; } else +#endif return i2c_smbus_write_byte_data(client, reg, value); } @@ -849,6 +788,88 @@ static struct lm78_data *lm78_update_device(struct device *dev) return data; } +#ifdef CONFIG_ISA +static int __devinit lm78_isa_probe(struct platform_device *pdev) +{ + int err; + struct lm78_data *data; + struct resource *res; + + /* Reserve the ISA region */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { + err = -EBUSY; + goto exit; + } + + data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit_release_region; + } + mutex_init(&data->lock); + data->isa_addr = res->start; + platform_set_drvdata(pdev, data); + + if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { + data->type = lm79; + data->name = "lm79"; + } else { + data->type = lm78; + data->name = "lm78"; + } + + /* Initialize the LM78 chip */ + lm78_init_device(data); + + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&pdev->dev.kobj, &lm78_group)) + || (err = device_create_file(&pdev->dev, &dev_attr_name))) + goto exit_remove_files; + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + + exit_remove_files: + sysfs_remove_group(&pdev->dev.kobj, &lm78_group); + device_remove_file(&pdev->dev, &dev_attr_name); + kfree(data); + exit_release_region: + release_region(res->start + LM78_ADDR_REG_OFFSET, 2); + exit: + return err; +} + +static int __devexit lm78_isa_remove(struct platform_device *pdev) +{ + struct lm78_data *data = platform_get_drvdata(pdev); + struct resource *res; + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &lm78_group); + device_remove_file(&pdev->dev, &dev_attr_name); + kfree(data); + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start + LM78_ADDR_REG_OFFSET, 2); + + return 0; +} + +static struct platform_driver lm78_isa_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "lm78", + }, + .probe = lm78_isa_probe, + .remove = __devexit_p(lm78_isa_remove), +}; + /* return 1 if a supported chip is found, 0 otherwise */ static int __init lm78_isa_found(unsigned short address) { @@ -969,12 +990,10 @@ static int __init lm78_isa_device_add(unsigned short address) return err; } -static int __init sm_lm78_init(void) +static int __init lm78_isa_register(void) { int res; - /* We register the ISA device first, so that we can skip the - * registration of an I2C interface to the same device. */ if (lm78_isa_found(isa_address)) { res = platform_driver_register(&lm78_isa_driver); if (res) @@ -986,32 +1005,62 @@ static int __init sm_lm78_init(void) goto exit_unreg_isa_driver; } - res = i2c_add_driver(&lm78_driver); - if (res) - goto exit_unreg_isa_device; - return 0; - exit_unreg_isa_device: - platform_device_unregister(pdev); exit_unreg_isa_driver: platform_driver_unregister(&lm78_isa_driver); exit: return res; } -static void __exit sm_lm78_exit(void) +static void lm78_isa_unregister(void) { if (pdev) { platform_device_unregister(pdev); platform_driver_unregister(&lm78_isa_driver); } - i2c_del_driver(&lm78_driver); } +#else /* !CONFIG_ISA */ +static int __init lm78_isa_register(void) +{ + return 0; +} + +static void lm78_isa_unregister(void) +{ +} +#endif /* CONFIG_ISA */ +static int __init sm_lm78_init(void) +{ + int res; + + /* We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. */ + res = lm78_isa_register(); + if (res) + goto exit; + + res = i2c_add_driver(&lm78_driver); + if (res) + goto exit_unreg_isa_device; + + return 0; + + exit_unreg_isa_device: + lm78_isa_unregister(); + exit: + return res; +} + +static void __exit sm_lm78_exit(void) +{ + lm78_isa_unregister(); + i2c_del_driver(&lm78_driver); +} -MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); +MODULE_AUTHOR("Frodo Looijaard, Jean Delvare <khali@linux-fr.org>"); MODULE_DESCRIPTION("LM78/LM79 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 2f94f950480..90ddb877421 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -54,6 +54,9 @@ * and extended mode. They are mostly compatible with LM90 except for a data * format difference for the temperature value registers. * + * This driver also supports the SA56004 from Philips. This device is + * pin-compatible with the LM86, the ED/EDP parts are also address-compatible. + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -96,13 +99,15 @@ * MAX6659 can have address 0x4c, 0x4d or 0x4e. * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. + * SA56004 can have address 0x48 through 0x4F. */ static const unsigned short normal_i2c[] = { - 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; + 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, - max6646, w83l771, max6696 }; + max6646, w83l771, max6696, sa56004 }; /* * The LM90 registers @@ -152,6 +157,10 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define MAX6659_REG_R_LOCAL_EMERG 0x17 #define MAX6659_REG_W_LOCAL_EMERG 0x17 +/* SA56004 registers */ + +#define SA56004_REG_R_LOCAL_TEMPL 0x22 + #define LM90_DEF_CONVRATE_RVAL 6 /* Def conversion rate register value */ #define LM90_MAX_CONVRATE_MS 16000 /* Maximum conversion rate in ms */ @@ -161,7 +170,6 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_FLAG_ADT7461_EXT (1 << 0) /* ADT7461 extended mode */ /* Device features */ #define LM90_HAVE_OFFSET (1 << 1) /* temperature offset register */ -#define LM90_HAVE_LOCAL_EXT (1 << 2) /* extended local temperature */ #define LM90_HAVE_REM_LIMIT_EXT (1 << 3) /* extended remote limit */ #define LM90_HAVE_EMERGENCY (1 << 4) /* 3rd upper (emergency) limit */ #define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ @@ -192,6 +200,7 @@ static const struct i2c_device_id lm90_id[] = { { "max6696", max6696 }, { "nct1008", adt7461 }, { "w83l771", w83l771 }, + { "sa56004", sa56004 }, { } }; MODULE_DEVICE_TABLE(i2c, lm90_id); @@ -204,6 +213,7 @@ struct lm90_params { u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate register value */ + u8 reg_local_ext; /* Extended local temp register (optional) */ }; static const struct lm90_params lm90_params[] = { @@ -235,19 +245,20 @@ static const struct lm90_params lm90_params[] = { .max_convrate = 9, }, [max6646] = { - .flags = LM90_HAVE_LOCAL_EXT, .alert_alarms = 0x7c, .max_convrate = 6, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [max6657] = { - .flags = LM90_HAVE_LOCAL_EXT, .alert_alarms = 0x7c, .max_convrate = 8, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [max6659] = { - .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY, + .flags = LM90_HAVE_EMERGENCY, .alert_alarms = 0x7c, .max_convrate = 8, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [max6680] = { .flags = LM90_HAVE_OFFSET, @@ -255,16 +266,23 @@ static const struct lm90_params lm90_params[] = { .max_convrate = 7, }, [max6696] = { - .flags = LM90_HAVE_LOCAL_EXT | LM90_HAVE_EMERGENCY + .flags = LM90_HAVE_EMERGENCY | LM90_HAVE_EMERGENCY_ALARM | LM90_HAVE_TEMP3, .alert_alarms = 0x187c, .max_convrate = 6, + .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [w83l771] = { .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, .alert_alarms = 0x7c, .max_convrate = 8, }, + [sa56004] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT, + .alert_alarms = 0x7b, + .max_convrate = 9, + .reg_local_ext = SA56004_REG_R_LOCAL_TEMPL, + }, }; /* @@ -286,6 +304,7 @@ struct lm90_data { u16 alert_alarms; /* Which alarm bits trigger ALERT# */ /* Upper 8 bits for max6695/96 */ u8 max_convrate; /* Maximum conversion rate */ + u8 reg_local_ext; /* local extension register offset */ /* registers values */ s8 temp8[8]; /* 0: local low limit @@ -452,9 +471,9 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[3]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - if (data->flags & LM90_HAVE_LOCAL_EXT) { + if (data->reg_local_ext) { lm90_read16(client, LM90_REG_R_LOCAL_TEMP, - MAX6657_REG_R_LOCAL_TEMPL, + data->reg_local_ext, &data->temp11[4]); } else { if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, @@ -1092,7 +1111,7 @@ static int lm90_detect(struct i2c_client *new_client, struct i2c_adapter *adapter = new_client->adapter; int address = new_client->addr; const char *name = NULL; - int man_id, chip_id, reg_config1, reg_convrate; + int man_id, chip_id, reg_config1, reg_config2, reg_convrate; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -1108,15 +1127,16 @@ static int lm90_detect(struct i2c_client *new_client, LM90_REG_R_CONVRATE)) < 0) return -ENODEV; - if ((address == 0x4C || address == 0x4D) - && man_id == 0x01) { /* National Semiconductor */ - int reg_config2; - + if (man_id == 0x01 || man_id == 0x5C || man_id == 0x41) { reg_config2 = i2c_smbus_read_byte_data(new_client, LM90_REG_R_CONFIG2); if (reg_config2 < 0) return -ENODEV; + } else + reg_config2 = 0; /* Make compiler happy */ + if ((address == 0x4C || address == 0x4D) + && man_id == 0x01) { /* National Semiconductor */ if ((reg_config1 & 0x2A) == 0x00 && (reg_config2 & 0xF8) == 0x00 && reg_convrate <= 0x09) { @@ -1245,13 +1265,6 @@ static int lm90_detect(struct i2c_client *new_client, } else if (address == 0x4C && man_id == 0x5C) { /* Winbond/Nuvoton */ - int reg_config2; - - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG2); - if (reg_config2 < 0) - return -ENODEV; - if ((reg_config1 & 0x2A) == 0x00 && (reg_config2 & 0xF8) == 0x00) { if (chip_id == 0x01 /* W83L771W/G */ @@ -1263,6 +1276,15 @@ static int lm90_detect(struct i2c_client *new_client, name = "w83l771"; } } + } else + if (address >= 0x48 && address <= 0x4F + && man_id == 0xA1) { /* NXP Semiconductor/Philips */ + if (chip_id == 0x00 + && (reg_config1 & 0x2A) == 0x00 + && (reg_config2 & 0xFE) == 0x00 + && reg_convrate <= 0x09) { + name = "sa56004"; + } } if (!name) { /* identification failed */ @@ -1368,6 +1390,7 @@ static int lm90_probe(struct i2c_client *new_client, /* Set chip capabilities */ data->flags = lm90_params[data->kind].flags; + data->reg_local_ext = lm90_params[data->kind].reg_local_ext; /* Set maximum conversion rate */ data->max_convrate = lm90_params[data->kind].max_convrate; diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index d3b464b74ce..513901d592a 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -74,8 +74,9 @@ static const unsigned short normal_i2c[] = { #define TT_OFF 0 #define TT_ON 1 #define TT_MASK 7 -#define MANUFACTURER_ID 0x01 -#define DEFAULT_REVISION 0xA4 +#define NATSEMI_MAN_ID 0x01 +#define LM95231_CHIP_ID 0xA1 +#define LM95241_CHIP_ID 0xA4 static const u8 lm95241_reg_address[] = { LM95241_REG_R_LOCAL_TEMPH, @@ -338,20 +339,25 @@ static int lm95241_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - int address = new_client->addr; const char *name; + int mfg_id, chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) - == MANUFACTURER_ID) - && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) - == DEFAULT_REVISION)) { - name = DEVNAME; - } else { - dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n", - address); + mfg_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID); + if (mfg_id != NATSEMI_MAN_ID) + return -ENODEV; + + chip_id = i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID); + switch (chip_id) { + case LM95231_CHIP_ID: + name = "lm95231"; + break; + case LM95241_CHIP_ID: + name = "lm95241"; + break; + default: return -ENODEV; } @@ -431,7 +437,8 @@ static int lm95241_remove(struct i2c_client *client) /* Driver data (common to all clients) */ static const struct i2c_device_id lm95241_id[] = { - { DEVNAME, 0 }, + { "lm95231", 0 }, + { "lm95241", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95241_id); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c new file mode 100644 index 00000000000..dce9e68241e --- /dev/null +++ b/drivers/hwmon/lm95245.c @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com> + * + * The LM95245 is a sensor chip made by National Semiconductors. + * It reports up to two temperatures (its own plus an external one). + * Complete datasheet can be obtained from National's website at: + * http://www.national.com/ds.cgi/LM/LM95245.pdf + * + * This driver is based on lm95241.c + * + * 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/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> + +#define DEVNAME "lm95245" + +static const unsigned short normal_i2c[] = { + 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; + +/* LM95245 registers */ +/* general registers */ +#define LM95245_REG_RW_CONFIG1 0x03 +#define LM95245_REG_RW_CONVERS_RATE 0x04 +#define LM95245_REG_W_ONE_SHOT 0x0F + +/* diode configuration */ +#define LM95245_REG_RW_CONFIG2 0xBF +#define LM95245_REG_RW_REMOTE_OFFH 0x11 +#define LM95245_REG_RW_REMOTE_OFFL 0x12 + +/* status registers */ +#define LM95245_REG_R_STATUS1 0x02 +#define LM95245_REG_R_STATUS2 0x33 + +/* limit registers */ +#define LM95245_REG_RW_REMOTE_OS_LIMIT 0x07 +#define LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT 0x20 +#define LM95245_REG_RW_REMOTE_TCRIT_LIMIT 0x19 +#define LM95245_REG_RW_COMMON_HYSTERESIS 0x21 + +/* temperature signed */ +#define LM95245_REG_R_LOCAL_TEMPH_S 0x00 +#define LM95245_REG_R_LOCAL_TEMPL_S 0x30 +#define LM95245_REG_R_REMOTE_TEMPH_S 0x01 +#define LM95245_REG_R_REMOTE_TEMPL_S 0x10 +/* temperature unsigned */ +#define LM95245_REG_R_REMOTE_TEMPH_U 0x31 +#define LM95245_REG_R_REMOTE_TEMPL_U 0x32 + +/* id registers */ +#define LM95245_REG_R_MAN_ID 0xFE +#define LM95245_REG_R_CHIP_ID 0xFF + +/* LM95245 specific bitfields */ +#define CFG_STOP 0x40 +#define CFG_REMOTE_TCRIT_MASK 0x10 +#define CFG_REMOTE_OS_MASK 0x08 +#define CFG_LOCAL_TCRIT_MASK 0x04 +#define CFG_LOCAL_OS_MASK 0x02 + +#define CFG2_OS_A0 0x40 +#define CFG2_DIODE_FAULT_OS 0x20 +#define CFG2_DIODE_FAULT_TCRIT 0x10 +#define CFG2_REMOTE_TT 0x08 +#define CFG2_REMOTE_FILTER_DIS 0x00 +#define CFG2_REMOTE_FILTER_EN 0x06 + +/* conversation rate in ms */ +#define RATE_CR0063 0x00 +#define RATE_CR0364 0x01 +#define RATE_CR1000 0x02 +#define RATE_CR2500 0x03 + +#define STATUS1_DIODE_FAULT 0x04 +#define STATUS1_RTCRIT 0x02 +#define STATUS1_LOC 0x01 + +#define MANUFACTURER_ID 0x01 +#define DEFAULT_REVISION 0xB3 + +static const u8 lm95245_reg_address[] = { + LM95245_REG_R_LOCAL_TEMPH_S, + LM95245_REG_R_LOCAL_TEMPL_S, + LM95245_REG_R_REMOTE_TEMPH_S, + LM95245_REG_R_REMOTE_TEMPL_S, + LM95245_REG_R_REMOTE_TEMPH_U, + LM95245_REG_R_REMOTE_TEMPL_U, + LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT, + LM95245_REG_RW_REMOTE_TCRIT_LIMIT, + LM95245_REG_RW_COMMON_HYSTERESIS, + LM95245_REG_R_STATUS1, +}; + +/* Client data (each client gets its own) */ +struct lm95245_data { + struct device *hwmon_dev; + struct mutex update_lock; + unsigned long last_updated; /* in jiffies */ + unsigned long interval; /* in msecs */ + bool valid; /* zero until following fields are valid */ + /* registers values */ + u8 regs[ARRAY_SIZE(lm95245_reg_address)]; + u8 config1, config2; +}; + +/* Conversions */ +static int temp_from_reg_unsigned(u8 val_h, u8 val_l) +{ + return val_h * 1000 + val_l * 1000 / 256; +} + +static int temp_from_reg_signed(u8 val_h, u8 val_l) +{ + if (val_h & 0x80) + return (val_h - 0x100) * 1000; + return temp_from_reg_unsigned(val_h, val_l); +} + +static struct lm95245_data *lm95245_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + + msecs_to_jiffies(data->interval)) || !data->valid) { + int i; + + dev_dbg(&client->dev, "Updating lm95245 data.\n"); + for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++) + data->regs[i] + = i2c_smbus_read_byte_data(client, + lm95245_reg_address[i]); + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static unsigned long lm95245_read_conversion_rate(struct i2c_client *client) +{ + int rate; + unsigned long interval; + + rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE); + + switch (rate) { + case RATE_CR0063: + interval = 63; + break; + case RATE_CR0364: + interval = 364; + break; + case RATE_CR1000: + interval = 1000; + break; + case RATE_CR2500: + default: + interval = 2500; + break; + } + + return interval; +} + +static unsigned long lm95245_set_conversion_rate(struct i2c_client *client, + unsigned long interval) +{ + int rate; + + if (interval <= 63) { + interval = 63; + rate = RATE_CR0063; + } else if (interval <= 364) { + interval = 364; + rate = RATE_CR0364; + } else if (interval <= 1000) { + interval = 1000; + rate = RATE_CR1000; + } else { + interval = 2500; + rate = RATE_CR2500; + } + + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate); + + return interval; +} + +/* Sysfs stuff */ +static ssize_t show_input(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int temp; + int index = to_sensor_dev_attr(attr)->index; + + /* + * Index 0 (Local temp) is always signed + * Index 2 (Remote temp) has both signed and unsigned data + * use signed calculation for remote if signed bit is set + */ + if (index == 0 || data->regs[index] & 0x80) + temp = temp_from_reg_signed(data->regs[index], + data->regs[index + 1]); + else + temp = temp_from_reg_unsigned(data->regs[index + 2], + data->regs[index + 3]); + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp); +} + +static ssize_t show_limit(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", + data->regs[index] * 1000); +} + +static ssize_t set_limit(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + int index = to_sensor_dev_attr(attr)->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + val /= 1000; + + val = SENSORS_LIMIT(val, 0, (index == 6 ? 127 : 255)); + + mutex_lock(&data->update_lock); + + data->valid = 0; + + i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + val /= 1000; + + val = SENSORS_LIMIT(val, 0, 31); + + mutex_lock(&data->update_lock); + + data->valid = 0; + + /* shared crit hysteresis */ + i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS, + val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + + return snprintf(buf, PAGE_SIZE - 1, + data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n"); +} + +static ssize_t set_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val != 1 && val != 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + + if (val == 1) + data->config2 |= CFG2_REMOTE_TT; + else + data->config2 &= ~CFG2_REMOTE_TT; + + data->valid = 0; + + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2, + data->config2); + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", + !!(data->regs[9] & index)); +} + +static ssize_t show_interval(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + + return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval); +} + +static ssize_t set_interval(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm95245_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val) < 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + + data->interval = lm95245_set_conversion_rate(client, val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit, + set_limit, 6); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_limit, + set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, + STATUS1_LOC); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit, + set_limit, 7); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_limit, + set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + STATUS1_RTCRIT); +static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, + set_type, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, + STATUS1_DIODE_FAULT); + +static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, + set_interval); + +static struct attribute *lm95245_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_type.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL +}; + +static const struct attribute_group lm95245_group = { + .attrs = lm95245_attributes, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm95245_detect(struct i2c_client *new_client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID) + != MANUFACTURER_ID + || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID) + != DEFAULT_REVISION) + return -ENODEV; + + strlcpy(info->type, DEVNAME, I2C_NAME_SIZE); + return 0; +} + +static void lm95245_init_client(struct i2c_client *client) +{ + struct lm95245_data *data = i2c_get_clientdata(client); + + data->valid = 0; + data->interval = lm95245_read_conversion_rate(client); + + data->config1 = i2c_smbus_read_byte_data(client, + LM95245_REG_RW_CONFIG1); + data->config2 = i2c_smbus_read_byte_data(client, + LM95245_REG_RW_CONFIG2); + + if (data->config1 & CFG_STOP) { + /* Clear the standby bit */ + data->config1 &= ~CFG_STOP; + i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1, + data->config1); + } +} + +static int lm95245_probe(struct i2c_client *new_client, + const struct i2c_device_id *id) +{ + struct lm95245_data *data; + int err; + + data = kzalloc(sizeof(struct lm95245_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(new_client, data); + mutex_init(&data->update_lock); + + /* Initialize the LM95245 chip */ + lm95245_init_client(new_client); + + /* Register sysfs hooks */ + err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&new_client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove_files; + } + + return 0; + +exit_remove_files: + sysfs_remove_group(&new_client->dev.kobj, &lm95245_group); +exit_free: + kfree(data); +exit: + return err; +} + +static int lm95245_remove(struct i2c_client *client) +{ + struct lm95245_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm95245_group); + + kfree(data); + return 0; +} + +/* Driver data (common to all clients) */ +static const struct i2c_device_id lm95245_id[] = { + { DEVNAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm95245_id); + +static struct i2c_driver lm95245_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DEVNAME, + }, + .probe = lm95245_probe, + .remove = lm95245_remove, + .id_table = lm95245_id, + .detect = lm95245_detect, + .address_list = normal_i2c, +}; + +static int __init sensors_lm95245_init(void) +{ + return i2c_add_driver(&lm95245_driver); +} + +static void __exit sensors_lm95245_exit(void) +{ + i2c_del_driver(&lm95245_driver); +} + +MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>"); +MODULE_DESCRIPTION("LM95245 sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm95245_init); +module_exit(sensors_lm95245_exit); diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index 14335bbc9bd..c97b78ef911 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c @@ -38,8 +38,8 @@ 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 */ }; @@ -131,33 +131,23 @@ static const struct attribute_group max1111_attr_group = { .attrs = max1111_attributes, }; -static int setup_transfer(struct max1111_data *data) +static int __devinit 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; @@ -192,7 +182,7 @@ 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; + goto err_free_data; } data->hwmon_dev = hwmon_device_register(&spi->dev); @@ -209,9 +199,6 @@ static int __devinit max1111_probe(struct spi_device *spi) err_remove: 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; @@ -224,8 +211,6 @@ static int __devexit max1111_remove(struct spi_device *spi) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); mutex_destroy(&data->drvdata_lock); - kfree(data->rx_buf); - kfree(data->tx_buf); kfree(data); return 0; } diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index d94a24fdf4b..dd2d7b9620c 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -124,7 +124,7 @@ static inline int MV_TO_LIMIT(int mv, int range) static inline int ADC_TO_CURR(int adc, int gain) { - return adc * 1400000 / gain * 255; + return adc * 1400000 / (gain * 255); } /* diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c new file mode 100644 index 00000000000..20d1b2ddffb --- /dev/null +++ b/drivers/hwmon/max1668.c @@ -0,0 +1,502 @@ +/* + Copyright (c) 2011 David George <david.george@ska.ac.za> + + based on adm1021.c + some credit to Christoph Scheurer, but largely a rewrite + + 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/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Addresses to scan */ +static unsigned short max1668_addr_list[] = { + 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; + +/* max1668 registers */ + +#define MAX1668_REG_TEMP(nr) (nr) +#define MAX1668_REG_STAT1 0x05 +#define MAX1668_REG_STAT2 0x06 +#define MAX1668_REG_MAN_ID 0xfe +#define MAX1668_REG_DEV_ID 0xff + +/* limits */ + +/* write high limits */ +#define MAX1668_REG_LIMH_WR(nr) (0x13 + 2 * (nr)) +/* write low limits */ +#define MAX1668_REG_LIML_WR(nr) (0x14 + 2 * (nr)) +/* read high limits */ +#define MAX1668_REG_LIMH_RD(nr) (0x08 + 2 * (nr)) +/* read low limits */ +#define MAX1668_REG_LIML_RD(nr) (0x09 + 2 * (nr)) + +/* manufacturer and device ID Constants */ +#define MAN_ID_MAXIM 0x4d +#define DEV_ID_MAX1668 0x3 +#define DEV_ID_MAX1805 0x5 +#define DEV_ID_MAX1989 0xb + +/* read only mode module parameter */ +static int read_only; +module_param(read_only, bool, 0); +MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); + +enum chips { max1668, max1805, max1989 }; + +struct max1668_data { + struct device *hwmon_dev; + enum chips type; + + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* 1x local and 4x remote */ + s8 temp_max[5]; + s8 temp_min[5]; + s8 temp[5]; + u16 alarms; +}; + +static struct max1668_data *max1668_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max1668_data *data = i2c_get_clientdata(client); + struct max1668_data *ret = data; + s32 val; + int i; + + mutex_lock(&data->update_lock); + + if (data->valid && !time_after(jiffies, + data->last_updated + HZ + HZ / 2)) + goto abort; + + for (i = 0; i < 5; i++) { + val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp[i] = (s8) val; + + val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_max[i] = (s8) val; + + val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_min[i] = (s8) val; + } + + val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->alarms = val << 8; + + val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->alarms |= val; + + data->last_updated = jiffies; + data->valid = 1; +abort: + mutex_unlock(&data->update_lock); + + return ret; +} + +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temp[index] * 1000); +} + +static ssize_t show_temp_max(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temp_max[index] * 1000); +} + +static ssize_t show_temp_min(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temp_min[index] * 1000); +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1); +} + +static ssize_t show_fault(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct max1668_data *data = max1668_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", + (data->alarms & (1 << 12)) && data->temp[index] == 127); +} + +static ssize_t set_temp_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct i2c_client *client = to_i2c_client(dev); + struct max1668_data *data = i2c_get_clientdata(client); + long temp; + int ret; + + ret = kstrtol(buf, 10, &temp); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + data->temp_max[index] = SENSORS_LIMIT(temp/1000, -128, 127); + if (i2c_smbus_write_byte_data(client, + MAX1668_REG_LIMH_WR(index), + data->temp_max[index])) + count = -EIO; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t set_temp_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct i2c_client *client = to_i2c_client(dev); + struct max1668_data *data = i2c_get_clientdata(client); + long temp; + int ret; + + ret = kstrtol(buf, 10, &temp); + if (ret < 0) + return ret; + + mutex_lock(&data->update_lock); + data->temp_min[index] = SENSORS_LIMIT(temp/1000, -128, 127); + if (i2c_smbus_write_byte_data(client, + MAX1668_REG_LIML_WR(index), + data->temp_max[index])) + count = -EIO; + mutex_unlock(&data->update_lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, + set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, + set_temp_min, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, + set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, + set_temp_min, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, + set_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, + set_temp_min, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, + set_temp_max, 3); +static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, + set_temp_min, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, + set_temp_max, 4); +static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, + set_temp_min, 4); + +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14); +static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13); +static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7); +static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0); + +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4); + +/* Attributes common to MAX1668, MAX1989 and MAX1805 */ +static struct attribute *max1668_attribute_common[] = { + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + NULL +}; + +/* Attributes not present on MAX1805 */ +static struct attribute *max1668_attribute_unique[] = { + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_min.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_min_alarm.dev_attr.attr, + + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + NULL +}; + +static mode_t max1668_attribute_mode(struct kobject *kobj, + struct attribute *attr, int index) +{ + int ret = S_IRUGO; + if (read_only) + return ret; + if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr || + attr == &sensor_dev_attr_temp2_max.dev_attr.attr || + attr == &sensor_dev_attr_temp3_max.dev_attr.attr || + attr == &sensor_dev_attr_temp4_max.dev_attr.attr || + attr == &sensor_dev_attr_temp5_max.dev_attr.attr || + attr == &sensor_dev_attr_temp1_min.dev_attr.attr || + attr == &sensor_dev_attr_temp2_min.dev_attr.attr || + attr == &sensor_dev_attr_temp3_min.dev_attr.attr || + attr == &sensor_dev_attr_temp4_min.dev_attr.attr || + attr == &sensor_dev_attr_temp5_min.dev_attr.attr) + ret |= S_IWUSR; + return ret; +} + +static const struct attribute_group max1668_group_common = { + .attrs = max1668_attribute_common, + .is_visible = max1668_attribute_mode +}; + +static const struct attribute_group max1668_group_unique = { + .attrs = max1668_attribute_unique, + .is_visible = max1668_attribute_mode +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int max1668_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + const char *type_name; + int man_id, dev_id; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + /* Check for unsupported part */ + man_id = i2c_smbus_read_byte_data(client, MAX1668_REG_MAN_ID); + if (man_id != MAN_ID_MAXIM) + return -ENODEV; + + dev_id = i2c_smbus_read_byte_data(client, MAX1668_REG_DEV_ID); + if (dev_id < 0) + return -ENODEV; + + type_name = NULL; + if (dev_id == DEV_ID_MAX1668) + type_name = "max1668"; + else if (dev_id == DEV_ID_MAX1805) + type_name = "max1805"; + else if (dev_id == DEV_ID_MAX1989) + type_name = "max1989"; + + if (!type_name) + return -ENODEV; + + strlcpy(info->type, type_name, I2C_NAME_SIZE); + + return 0; +} + +static int max1668_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct max1668_data *data; + int err; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + data = kzalloc(sizeof(struct max1668_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->type = id->driver_data; + mutex_init(&data->update_lock); + + /* Register sysfs hooks */ + err = sysfs_create_group(&client->dev.kobj, &max1668_group_common); + if (err) + goto error_free; + + if (data->type == max1668 || data->type == max1989) { + err = sysfs_create_group(&client->dev.kobj, + &max1668_group_unique); + if (err) + goto error_sysrem0; + } + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto error_sysrem1; + } + + return 0; + +error_sysrem1: + if (data->type == max1668 || data->type == max1989) + sysfs_remove_group(&client->dev.kobj, &max1668_group_unique); +error_sysrem0: + sysfs_remove_group(&client->dev.kobj, &max1668_group_common); +error_free: + kfree(data); + return err; +} + +static int max1668_remove(struct i2c_client *client) +{ + struct max1668_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + if (data->type == max1668 || data->type == max1989) + sysfs_remove_group(&client->dev.kobj, &max1668_group_unique); + + sysfs_remove_group(&client->dev.kobj, &max1668_group_common); + + kfree(data); + return 0; +} + +static const struct i2c_device_id max1668_id[] = { + { "max1668", max1668 }, + { "max1805", max1805 }, + { "max1989", max1989 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max1668_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver max1668_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max1668", + }, + .probe = max1668_probe, + .remove = max1668_remove, + .id_table = max1668_id, + .detect = max1668_detect, + .address_list = max1668_addr_list, +}; + +static int __init sensors_max1668_init(void) +{ + return i2c_add_driver(&max1668_driver); +} + +static void __exit sensors_max1668_exit(void) +{ + i2c_del_driver(&max1668_driver); +} + +MODULE_AUTHOR("David George <david.george@ska.ac.za>"); +MODULE_DESCRIPTION("MAX1668 remote temperature sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_max1668_init) +module_exit(sensors_max1668_exit) diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c new file mode 100644 index 00000000000..eab11615dce --- /dev/null +++ b/drivers/hwmon/ntc_thermistor.c @@ -0,0 +1,452 @@ +/* + * ntc_thermistor.c - NTC Thermistors + * + * Copyright (C) 2010 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@samsung.com> + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/math64.h> +#include <linux/platform_device.h> +#include <linux/err.h> + +#include <linux/platform_data/ntc_thermistor.h> + +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +struct ntc_compensation { + int temp_C; + unsigned int ohm; +}; + +/* + * A compensation table should be sorted by the values of .ohm + * in descending order. + * The following compensation tables are from the specification of Murata NTC + * Thermistors Datasheet + */ +const struct ntc_compensation ncpXXwb473[] = { + { .temp_C = -40, .ohm = 1747920 }, + { .temp_C = -35, .ohm = 1245428 }, + { .temp_C = -30, .ohm = 898485 }, + { .temp_C = -25, .ohm = 655802 }, + { .temp_C = -20, .ohm = 483954 }, + { .temp_C = -15, .ohm = 360850 }, + { .temp_C = -10, .ohm = 271697 }, + { .temp_C = -5, .ohm = 206463 }, + { .temp_C = 0, .ohm = 158214 }, + { .temp_C = 5, .ohm = 122259 }, + { .temp_C = 10, .ohm = 95227 }, + { .temp_C = 15, .ohm = 74730 }, + { .temp_C = 20, .ohm = 59065 }, + { .temp_C = 25, .ohm = 47000 }, + { .temp_C = 30, .ohm = 37643 }, + { .temp_C = 35, .ohm = 30334 }, + { .temp_C = 40, .ohm = 24591 }, + { .temp_C = 45, .ohm = 20048 }, + { .temp_C = 50, .ohm = 16433 }, + { .temp_C = 55, .ohm = 13539 }, + { .temp_C = 60, .ohm = 11209 }, + { .temp_C = 65, .ohm = 9328 }, + { .temp_C = 70, .ohm = 7798 }, + { .temp_C = 75, .ohm = 6544 }, + { .temp_C = 80, .ohm = 5518 }, + { .temp_C = 85, .ohm = 4674 }, + { .temp_C = 90, .ohm = 3972 }, + { .temp_C = 95, .ohm = 3388 }, + { .temp_C = 100, .ohm = 2902 }, + { .temp_C = 105, .ohm = 2494 }, + { .temp_C = 110, .ohm = 2150 }, + { .temp_C = 115, .ohm = 1860 }, + { .temp_C = 120, .ohm = 1615 }, + { .temp_C = 125, .ohm = 1406 }, +}; +const struct ntc_compensation ncpXXwl333[] = { + { .temp_C = -40, .ohm = 1610154 }, + { .temp_C = -35, .ohm = 1130850 }, + { .temp_C = -30, .ohm = 802609 }, + { .temp_C = -25, .ohm = 575385 }, + { .temp_C = -20, .ohm = 416464 }, + { .temp_C = -15, .ohm = 304219 }, + { .temp_C = -10, .ohm = 224193 }, + { .temp_C = -5, .ohm = 166623 }, + { .temp_C = 0, .ohm = 124850 }, + { .temp_C = 5, .ohm = 94287 }, + { .temp_C = 10, .ohm = 71747 }, + { .temp_C = 15, .ohm = 54996 }, + { .temp_C = 20, .ohm = 42455 }, + { .temp_C = 25, .ohm = 33000 }, + { .temp_C = 30, .ohm = 25822 }, + { .temp_C = 35, .ohm = 20335 }, + { .temp_C = 40, .ohm = 16115 }, + { .temp_C = 45, .ohm = 12849 }, + { .temp_C = 50, .ohm = 10306 }, + { .temp_C = 55, .ohm = 8314 }, + { .temp_C = 60, .ohm = 6746 }, + { .temp_C = 65, .ohm = 5503 }, + { .temp_C = 70, .ohm = 4513 }, + { .temp_C = 75, .ohm = 3721 }, + { .temp_C = 80, .ohm = 3084 }, + { .temp_C = 85, .ohm = 2569 }, + { .temp_C = 90, .ohm = 2151 }, + { .temp_C = 95, .ohm = 1809 }, + { .temp_C = 100, .ohm = 1529 }, + { .temp_C = 105, .ohm = 1299 }, + { .temp_C = 110, .ohm = 1108 }, + { .temp_C = 115, .ohm = 949 }, + { .temp_C = 120, .ohm = 817 }, + { .temp_C = 125, .ohm = 707 }, +}; + +struct ntc_data { + struct device *hwmon_dev; + struct ntc_thermistor_platform_data *pdata; + const struct ntc_compensation *comp; + struct device *dev; + int n_comp; + char name[PLATFORM_NAME_SIZE]; +}; + +static inline u64 div64_u64_safe(u64 dividend, u64 divisor) +{ + if (divisor == 0 && dividend == 0) + return 0; + if (divisor == 0) + return UINT_MAX; + return div64_u64(dividend, divisor); +} + +static unsigned int get_ohm_of_thermistor(struct ntc_data *data, + unsigned int uV) +{ + struct ntc_thermistor_platform_data *pdata = data->pdata; + u64 mV = uV / 1000; + u64 pmV = pdata->pullup_uV / 1000; + u64 N, puO, pdO; + puO = pdata->pullup_ohm; + pdO = pdata->pulldown_ohm; + + if (mV == 0) { + if (pdata->connect == NTC_CONNECTED_POSITIVE) + return UINT_MAX; + return 0; + } + if (mV >= pmV) + return (pdata->connect == NTC_CONNECTED_POSITIVE) ? + 0 : UINT_MAX; + + if (pdata->connect == NTC_CONNECTED_POSITIVE && puO == 0) + N = div64_u64_safe(pdO * (pmV - mV), mV); + else if (pdata->connect == NTC_CONNECTED_GROUND && pdO == 0) + N = div64_u64_safe(puO * mV, pmV - mV); + else if (pdata->connect == NTC_CONNECTED_POSITIVE) + N = div64_u64_safe(pdO * puO * (pmV - mV), + puO * mV - pdO * (pmV - mV)); + else + N = div64_u64_safe(pdO * puO * mV, pdO * (pmV - mV) - puO * mV); + + return (unsigned int) N; +} + +static int lookup_comp(struct ntc_data *data, + unsigned int ohm, int *i_low, int *i_high) +{ + int start, end, mid = -1; + + /* Do a binary search on compensation table */ + start = 0; + end = data->n_comp; + + while (end > start) { + mid = start + (end - start) / 2; + if (data->comp[mid].ohm < ohm) + end = mid; + else if (data->comp[mid].ohm > ohm) + start = mid + 1; + else + break; + } + + if (mid == 0) { + if (data->comp[mid].ohm > ohm) { + *i_high = mid; + *i_low = mid + 1; + return 0; + } else { + *i_low = mid; + *i_high = -1; + return -EINVAL; + } + } + if (mid == (data->n_comp - 1)) { + if (data->comp[mid].ohm <= ohm) { + *i_low = mid; + *i_high = mid - 1; + return 0; + } else { + *i_low = -1; + *i_high = mid; + return -EINVAL; + } + } + + if (data->comp[mid].ohm <= ohm) { + *i_low = mid; + *i_high = mid - 1; + } else { + *i_low = mid + 1; + *i_high = mid; + } + + return 0; +} + +static int get_temp_mC(struct ntc_data *data, unsigned int ohm, int *temp) +{ + int low, high; + int ret; + + ret = lookup_comp(data, ohm, &low, &high); + if (ret) { + /* Unable to use linear approximation */ + if (low != -1) + *temp = data->comp[low].temp_C * 1000; + else if (high != -1) + *temp = data->comp[high].temp_C * 1000; + else + return ret; + } else { + *temp = data->comp[low].temp_C * 1000 + + ((data->comp[high].temp_C - data->comp[low].temp_C) * + 1000 * ((int)ohm - (int)data->comp[low].ohm)) / + ((int)data->comp[high].ohm - (int)data->comp[low].ohm); + } + + return 0; +} + +static int ntc_thermistor_read(struct ntc_data *data, int *temp) +{ + int ret; + int read_ohm, read_uV; + unsigned int ohm = 0; + + if (data->pdata->read_ohm) { + read_ohm = data->pdata->read_ohm(); + if (read_ohm < 0) + return read_ohm; + ohm = (unsigned int)read_ohm; + } + + if (data->pdata->read_uV) { + read_uV = data->pdata->read_uV(); + if (read_uV < 0) + return read_uV; + ohm = get_ohm_of_thermistor(data, (unsigned int)read_uV); + } + + ret = get_temp_mC(data, ohm, temp); + if (ret) { + dev_dbg(data->dev, "Sensor reading function not available.\n"); + return ret; + } + + return 0; +} + +static ssize_t ntc_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ntc_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static ssize_t ntc_show_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "4\n"); +} + +static ssize_t ntc_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ntc_data *data = dev_get_drvdata(dev); + int temp, ret; + + ret = ntc_thermistor_read(data, &temp); + if (ret) + return ret; + return sprintf(buf, "%d\n", temp); +} + +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0); +static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL); + +static struct attribute *ntc_attributes[] = { + &dev_attr_name.attr, + &sensor_dev_attr_temp1_type.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ntc_attr_group = { + .attrs = ntc_attributes, +}; + +static int __devinit ntc_thermistor_probe(struct platform_device *pdev) +{ + struct ntc_data *data; + struct ntc_thermistor_platform_data *pdata = pdev->dev.platform_data; + int ret = 0; + + if (!pdata) { + dev_err(&pdev->dev, "No platform init data supplied.\n"); + return -ENODEV; + } + + /* Either one of the two is required. */ + if (!pdata->read_uV && !pdata->read_ohm) { + dev_err(&pdev->dev, "Both read_uV and read_ohm missing." + "Need either one of the two.\n"); + return -EINVAL; + } + + if (pdata->read_uV && pdata->read_ohm) { + dev_warn(&pdev->dev, "Only one of read_uV and read_ohm " + "is needed; ignoring read_uV.\n"); + pdata->read_uV = NULL; + } + + if (pdata->read_uV && (pdata->pullup_uV == 0 || + (pdata->pullup_ohm == 0 && pdata->connect == + NTC_CONNECTED_GROUND) || + (pdata->pulldown_ohm == 0 && pdata->connect == + NTC_CONNECTED_POSITIVE) || + (pdata->connect != NTC_CONNECTED_POSITIVE && + pdata->connect != NTC_CONNECTED_GROUND))) { + dev_err(&pdev->dev, "Required data to use read_uV not " + "supplied.\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(struct ntc_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &pdev->dev; + data->pdata = pdata; + strncpy(data->name, pdev->id_entry->name, PLATFORM_NAME_SIZE); + + switch (pdev->id_entry->driver_data) { + case TYPE_NCPXXWB473: + data->comp = ncpXXwb473; + data->n_comp = ARRAY_SIZE(ncpXXwb473); + break; + case TYPE_NCPXXWL333: + data->comp = ncpXXwl333; + data->n_comp = ARRAY_SIZE(ncpXXwl333); + break; + default: + dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n", + pdev->id_entry->driver_data, + pdev->id_entry->name); + ret = -EINVAL; + goto err; + } + + platform_set_drvdata(pdev, data); + + ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group); + if (ret) { + dev_err(data->dev, "unable to create sysfs files\n"); + goto err; + } + + data->hwmon_dev = hwmon_device_register(data->dev); + if (IS_ERR_OR_NULL(data->hwmon_dev)) { + dev_err(data->dev, "unable to register as hwmon device.\n"); + ret = -EINVAL; + goto err_after_sysfs; + } + + dev_info(&pdev->dev, "Thermistor %s:%d (type: %s/%lu) successfully probed.\n", + pdev->name, pdev->id, pdev->id_entry->name, + pdev->id_entry->driver_data); + return 0; +err_after_sysfs: + sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); +err: + kfree(data); + return ret; +} + +static int __devexit ntc_thermistor_remove(struct platform_device *pdev) +{ + struct ntc_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&data->dev->kobj, &ntc_attr_group); + platform_set_drvdata(pdev, NULL); + + kfree(data); + + return 0; +} + +static const struct platform_device_id ntc_thermistor_id[] = { + { "ncp15wb473", TYPE_NCPXXWB473 }, + { "ncp18wb473", TYPE_NCPXXWB473 }, + { "ncp21wb473", TYPE_NCPXXWB473 }, + { "ncp03wb473", TYPE_NCPXXWB473 }, + { "ncp15wl333", TYPE_NCPXXWL333 }, + { }, +}; + +static struct platform_driver ntc_thermistor_driver = { + .driver = { + .name = "ntc-thermistor", + .owner = THIS_MODULE, + }, + .probe = ntc_thermistor_probe, + .remove = __devexit_p(ntc_thermistor_remove), + .id_table = ntc_thermistor_id, +}; + +static int __init ntc_thermistor_init(void) +{ + return platform_driver_register(&ntc_thermistor_driver); +} + +module_init(ntc_thermistor_init); + +static void __exit ntc_thermistor_cleanup(void) +{ + platform_driver_unregister(&ntc_thermistor_driver); +} + +module_exit(ntc_thermistor_cleanup); + +MODULE_DESCRIPTION("NTC Thermistor Driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ntc-thermistor"); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig new file mode 100644 index 00000000000..c9237b9dcff --- /dev/null +++ b/drivers/hwmon/pmbus/Kconfig @@ -0,0 +1,100 @@ +# +# PMBus chip drivers configuration +# + +menuconfig PMBUS + tristate "PMBus support" + depends on I2C && EXPERIMENTAL + default n + help + Say yes here if you want to enable PMBus support. + + This driver can also be built as a module. If so, the module will + be called pmbus_core. + +if PMBUS + +config SENSORS_PMBUS + tristate "Generic PMBus devices" + default y + help + If you say yes here you get hardware monitoring support for generic + PMBus devices, including but not limited to ADP4000, BMR450, BMR451, + BMR453, BMR454, LTC2978, NCP4200, and NCP4208. + + This driver can also be built as a module. If so, the module will + be called pmbus. + +config SENSORS_ADM1275 + tristate "Analog Devices ADM1275" + default n + help + If you say yes here you get hardware monitoring support for Analog + Devices ADM1275 Hot-Swap Controller and Digital Power Monitor. + + This driver can also be built as a module. If so, the module will + be called adm1275. + +config SENSORS_LM25066 + tristate "National Semiconductor LM25066 and compatibles" + default n + help + If you say yes here you get hardware monitoring support for National + Semiconductor LM25066, LM5064, and LM5066. + + This driver can also be built as a module. If so, the module will + be called lm25066. + +config SENSORS_MAX16064 + tristate "Maxim MAX16064" + default n + help + If you say yes here you get hardware monitoring support for Maxim + MAX16064. + + This driver can also be built as a module. If so, the module will + be called max16064. + +config SENSORS_MAX34440 + tristate "Maxim MAX34440/MAX34441" + default n + help + If you say yes here you get hardware monitoring support for Maxim + MAX34440 and MAX34441. + + This driver can also be built as a module. If so, the module will + be called max34440. + +config SENSORS_MAX8688 + tristate "Maxim MAX8688" + default n + help + If you say yes here you get hardware monitoring support for Maxim + MAX8688. + + This driver can also be built as a module. If so, the module will + be called max8688. + +config SENSORS_UCD9000 + tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" + default n + help + If you say yes here you get hardware monitoring support for TI + UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health + Controllers. + + This driver can also be built as a module. If so, the module will + be called ucd9000. + +config SENSORS_UCD9200 + tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" + default n + help + If you say yes here you get hardware monitoring support for TI + UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248 + Digital PWM System Controllers. + + This driver can also be built as a module. If so, the module will + be called ucd9200. + +endif # PMBUS diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile new file mode 100644 index 00000000000..623eedb1ed9 --- /dev/null +++ b/drivers/hwmon/pmbus/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for PMBus chip drivers. +# + +obj-$(CONFIG_PMBUS) += pmbus_core.o +obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o +obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o +obj-$(CONFIG_SENSORS_LM25066) += lm25066.o +obj-$(CONFIG_SENSORS_MAX16064) += max16064.o +obj-$(CONFIG_SENSORS_MAX34440) += max34440.o +obj-$(CONFIG_SENSORS_MAX8688) += max8688.o +obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o +obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o diff --git a/drivers/hwmon/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 8bc1bd66372..c936e278230 100644 --- a/drivers/hwmon/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -23,11 +23,68 @@ #include <linux/i2c.h> #include "pmbus.h" +#define ADM1275_PEAK_IOUT 0xd0 +#define ADM1275_PEAK_VIN 0xd1 +#define ADM1275_PEAK_VOUT 0xd2 #define ADM1275_PMON_CONFIG 0xd4 #define ADM1275_VIN_VOUT_SELECT (1 << 6) #define ADM1275_VRANGE (1 << 5) +static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page) + return -EINVAL; + + switch (reg) { + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VOUT); + break; + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + if (page) + return -EINVAL; + + switch (reg) { + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VIN, 0); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static int adm1275_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -50,14 +107,17 @@ static int adm1275_probe(struct i2c_client *client, } info->pages = 1; - info->direct[PSC_VOLTAGE_IN] = true; - info->direct[PSC_VOLTAGE_OUT] = true; - info->direct[PSC_CURRENT_OUT] = true; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_VOLTAGE_OUT] = direct; + info->format[PSC_CURRENT_OUT] = direct; info->m[PSC_CURRENT_OUT] = 807; info->b[PSC_CURRENT_OUT] = 20475; info->R[PSC_CURRENT_OUT] = -1; info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + info->read_word_data = adm1275_read_word_data; + info->write_word_data = adm1275_write_word_data; + if (config & ADM1275_VRANGE) { info->m[PSC_VOLTAGE_IN] = 19199; info->b[PSC_VOLTAGE_IN] = 0; diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c new file mode 100644 index 00000000000..ac254fba551 --- /dev/null +++ b/drivers/hwmon/pmbus/lm25066.c @@ -0,0 +1,352 @@ +/* + * Hardware monitoring driver for LM25066 / LM5064 / LM5066 + * + * Copyright (c) 2011 Ericsson AB. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include "pmbus.h" + +enum chips { lm25066, lm5064, lm5066 }; + +#define LM25066_READ_VAUX 0xd0 +#define LM25066_MFR_READ_IIN 0xd1 +#define LM25066_MFR_READ_PIN 0xd2 +#define LM25066_MFR_IIN_OC_WARN_LIMIT 0xd3 +#define LM25066_MFR_PIN_OP_WARN_LIMIT 0xd4 +#define LM25066_READ_PIN_PEAK 0xd5 +#define LM25066_CLEAR_PIN_PEAK 0xd6 +#define LM25066_DEVICE_SETUP 0xd9 +#define LM25066_READ_AVG_VIN 0xdc +#define LM25066_READ_AVG_VOUT 0xdd +#define LM25066_READ_AVG_IIN 0xde +#define LM25066_READ_AVG_PIN 0xdf + +#define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */ + +struct lm25066_data { + int id; + struct pmbus_driver_info info; +}; + +#define to_lm25066_data(x) container_of(x, struct lm25066_data, info) + +static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct lm25066_data *data = to_lm25066_data(info); + int ret; + + if (page > 1) + return -EINVAL; + + /* Map READ_VAUX into READ_VOUT register on page 1 */ + if (page == 1) { + switch (reg) { + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, 0, + LM25066_READ_VAUX); + if (ret < 0) + break; + /* Adjust returned value to match VOUT coefficients */ + switch (data->id) { + case lm25066: + /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); + break; + case lm5064: + /* VOUT: 4.53 mV VAUX: 700 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 70, 453); + break; + case lm5066: + /* VOUT: 2.18 mV VAUX: 725 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 725, 2180); + break; + } + break; + default: + /* No other valid registers on page 1 */ + ret = -EINVAL; + break; + } + goto done; + } + + switch (reg) { + case PMBUS_READ_IIN: + ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); + break; + case PMBUS_READ_PIN: + ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25066_MFR_IIN_OC_WARN_LIMIT); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + ret = pmbus_read_word_data(client, 0, + LM25066_MFR_PIN_OP_WARN_LIMIT); + break; + case PMBUS_VIRT_READ_VIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN); + break; + case PMBUS_VIRT_READ_VOUT_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT); + break; + case PMBUS_VIRT_READ_IIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN); + break; + case PMBUS_VIRT_READ_PIN_AVG: + ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN); + break; + case PMBUS_VIRT_READ_PIN_MAX: + ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK); + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } +done: + return ret; +} + +static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + if (page > 1) + return -EINVAL; + + switch (reg) { + case PMBUS_IIN_OC_WARN_LIMIT: + ret = pmbus_write_word_data(client, 0, + LM25066_MFR_IIN_OC_WARN_LIMIT, + word); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + ret = pmbus_write_word_data(client, 0, + LM25066_MFR_PIN_OP_WARN_LIMIT, + word); + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int lm25066_write_byte(struct i2c_client *client, int page, u8 value) +{ + if (page > 1) + return -EINVAL; + + if (page == 0) + return pmbus_write_byte(client, 0, value); + + return 0; +} + +static int lm25066_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int config; + int ret; + struct lm25066_data *data; + struct pmbus_driver_info *info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -ENODEV; + + data = kzalloc(sizeof(struct lm25066_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP); + if (config < 0) { + ret = config; + goto err_mem; + } + + data->id = id->driver_data; + info = &data->info; + + info->pages = 2; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_VOLTAGE_OUT] = direct; + info->format[PSC_CURRENT_IN] = direct; + info->format[PSC_TEMPERATURE] = direct; + info->format[PSC_POWER] = direct; + + info->m[PSC_TEMPERATURE] = 16; + info->b[PSC_TEMPERATURE] = 0; + info->R[PSC_TEMPERATURE] = 0; + + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + info->func[1] = PMBUS_HAVE_VOUT; + + info->read_word_data = lm25066_read_word_data; + info->write_word_data = lm25066_write_word_data; + info->write_byte = lm25066_write_byte; + + switch (id->driver_data) { + case lm25066: + info->m[PSC_VOLTAGE_IN] = 22070; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -2; + info->m[PSC_VOLTAGE_OUT] = 22070; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -2; + + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = 6852; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 369; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -2; + } else { + info->m[PSC_CURRENT_IN] = 13661; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 736; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -2; + } + break; + case lm5064: + info->m[PSC_VOLTAGE_IN] = 22075; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -2; + info->m[PSC_VOLTAGE_OUT] = 22075; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -2; + + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = 6713; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 3619; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -3; + } else { + info->m[PSC_CURRENT_IN] = 13426; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 7238; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -3; + } + break; + case lm5066: + info->m[PSC_VOLTAGE_IN] = 4587; + info->b[PSC_VOLTAGE_IN] = 0; + info->R[PSC_VOLTAGE_IN] = -2; + info->m[PSC_VOLTAGE_OUT] = 4587; + info->b[PSC_VOLTAGE_OUT] = 0; + info->R[PSC_VOLTAGE_OUT] = -2; + + if (config & LM25066_DEV_SETUP_CL) { + info->m[PSC_CURRENT_IN] = 10753; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 1204; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -3; + } else { + info->m[PSC_CURRENT_IN] = 5405; + info->b[PSC_CURRENT_IN] = 0; + info->R[PSC_CURRENT_IN] = -2; + info->m[PSC_POWER] = 605; + info->b[PSC_POWER] = 0; + info->R[PSC_POWER] = -3; + } + break; + default: + ret = -ENODEV; + goto err_mem; + } + + ret = pmbus_do_probe(client, id, info); + if (ret) + goto err_mem; + return 0; + +err_mem: + kfree(data); + return ret; +} + +static int lm25066_remove(struct i2c_client *client) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct lm25066_data *data = to_lm25066_data(info); + int ret; + + ret = pmbus_do_remove(client); + kfree(data); + return ret; +} + +static const struct i2c_device_id lm25066_id[] = { + {"lm25066", lm25066}, + {"lm5064", lm5064}, + {"lm5066", lm5066}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, lm25066_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver lm25066_driver = { + .driver = { + .name = "lm25066", + }, + .probe = lm25066_probe, + .remove = lm25066_remove, + .id_table = lm25066_id, +}; + +static int __init lm25066_init(void) +{ + return i2c_add_driver(&lm25066_driver); +} + +static void __exit lm25066_exit(void) +{ + i2c_del_driver(&lm25066_driver); +} + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066"); +MODULE_LICENSE("GPL"); +module_init(lm25066_init); +module_exit(lm25066_exit); diff --git a/drivers/hwmon/max16064.c b/drivers/hwmon/pmbus/max16064.c index 1d6d717060d..e50b296e8db 100644 --- a/drivers/hwmon/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -25,11 +25,60 @@ #include <linux/i2c.h> #include "pmbus.h" +#define MAX16064_MFR_VOUT_PEAK 0xd4 +#define MAX16064_MFR_TEMPERATURE_PEAK 0xd6 + +static int max16064_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX16064_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + MAX16064_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max16064_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX16064_MFR_VOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX16064_MFR_TEMPERATURE_PEAK, + 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static struct pmbus_driver_info max16064_info = { .pages = 4, - .direct[PSC_VOLTAGE_IN] = true, - .direct[PSC_VOLTAGE_OUT] = true, - .direct[PSC_TEMPERATURE] = true, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, .m[PSC_VOLTAGE_IN] = 19995, .b[PSC_VOLTAGE_IN] = 0, .R[PSC_VOLTAGE_IN] = -1, @@ -44,6 +93,8 @@ static struct pmbus_driver_info max16064_info = { .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, .func[3] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT, + .read_word_data = max16064_read_word_data, + .write_word_data = max16064_write_word_data, }; static int max16064_probe(struct i2c_client *client, diff --git a/drivers/hwmon/max34440.c b/drivers/hwmon/pmbus/max34440.c index db11e1a175b..fda621d2e45 100644 --- a/drivers/hwmon/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -27,11 +27,70 @@ enum chips { max34440, max34441 }; +#define MAX34440_MFR_VOUT_PEAK 0xd4 +#define MAX34440_MFR_IOUT_PEAK 0xd5 +#define MAX34440_MFR_TEMPERATURE_PEAK 0xd6 + #define MAX34440_STATUS_OC_WARN (1 << 0) #define MAX34440_STATUS_OC_FAULT (1 << 1) #define MAX34440_STATUS_OT_FAULT (1 << 5) #define MAX34440_STATUS_OT_WARN (1 << 6) +static int max34440_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, + MAX34440_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max34440_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_VOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_IOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, page, + MAX34440_MFR_TEMPERATURE_PEAK, + 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) { int ret; @@ -72,10 +131,10 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg) static struct pmbus_driver_info max34440_info[] = { [max34440] = { .pages = 14, - .direct[PSC_VOLTAGE_IN] = true, - .direct[PSC_VOLTAGE_OUT] = true, - .direct[PSC_TEMPERATURE] = true, - .direct[PSC_CURRENT_OUT] = true, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_OUT] = direct, .m[PSC_VOLTAGE_IN] = 1, .b[PSC_VOLTAGE_IN] = 0, .R[PSC_VOLTAGE_IN] = 3, /* R = 0 in datasheet reflects mV */ @@ -109,14 +168,16 @@ static struct pmbus_driver_info max34440_info[] = { .func[12] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[13] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, }, [max34441] = { .pages = 12, - .direct[PSC_VOLTAGE_IN] = true, - .direct[PSC_VOLTAGE_OUT] = true, - .direct[PSC_TEMPERATURE] = true, - .direct[PSC_CURRENT_OUT] = true, - .direct[PSC_FAN] = true, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_FAN] = direct, .m[PSC_VOLTAGE_IN] = 1, .b[PSC_VOLTAGE_IN] = 0, .R[PSC_VOLTAGE_IN] = 3, @@ -150,6 +211,8 @@ static struct pmbus_driver_info max34440_info[] = { .func[10] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .func[11] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, .read_byte_data = max34440_read_byte_data, + .read_word_data = max34440_read_word_data, + .write_word_data = max34440_write_word_data, }, }; diff --git a/drivers/hwmon/max8688.c b/drivers/hwmon/pmbus/max8688.c index 7fb93f4e9f2..c3e72f1a3cf 100644 --- a/drivers/hwmon/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -25,6 +25,9 @@ #include <linux/i2c.h> #include "pmbus.h" +#define MAX8688_MFR_VOUT_PEAK 0xd4 +#define MAX8688_MFR_IOUT_PEAK 0xd5 +#define MAX8688_MFR_TEMPERATURE_PEAK 0xd6 #define MAX8688_MFG_STATUS 0xd8 #define MAX8688_STATUS_OC_FAULT (1 << 4) @@ -37,6 +40,62 @@ #define MAX8688_STATUS_OT_FAULT (1 << 13) #define MAX8688_STATUS_OT_WARNING (1 << 14) +static int max8688_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page) + return -EINVAL; + + switch (reg) { + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, 0, MAX8688_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, 0, MAX8688_MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, 0, + MAX8688_MFR_TEMPERATURE_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max8688_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, MAX8688_MFR_VOUT_PEAK, + 0); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, 0, MAX8688_MFR_IOUT_PEAK, + 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, 0, + MAX8688_MFR_TEMPERATURE_PEAK, + 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) { int ret = 0; @@ -91,10 +150,10 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg) static struct pmbus_driver_info max8688_info = { .pages = 1, - .direct[PSC_VOLTAGE_IN] = true, - .direct[PSC_VOLTAGE_OUT] = true, - .direct[PSC_TEMPERATURE] = true, - .direct[PSC_CURRENT_OUT] = true, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_OUT] = direct, .m[PSC_VOLTAGE_IN] = 19995, .b[PSC_VOLTAGE_IN] = 0, .R[PSC_VOLTAGE_IN] = -1, @@ -111,6 +170,8 @@ static struct pmbus_driver_info max8688_info = { | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP, .read_byte_data = max8688_read_byte_data, + .read_word_data = max8688_read_word_data, + .write_word_data = max8688_write_word_data, }; static int max8688_probe(struct i2c_client *client, diff --git a/drivers/hwmon/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 9b1f0c37ef7..73de9f1f319 100644 --- a/drivers/hwmon/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -96,6 +96,8 @@ static void pmbus_find_sensor_groups(struct i2c_client *client, static int pmbus_identify(struct i2c_client *client, struct pmbus_driver_info *info) { + int ret = 0; + if (!info->pages) { /* * Check if the PAGE command is supported. If it is, @@ -117,6 +119,27 @@ static int pmbus_identify(struct i2c_client *client, } } + if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { + int vout_mode; + + vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode != 0xff) { + switch (vout_mode >> 5) { + case 0: + break; + case 1: + info->format[PSC_VOLTAGE_OUT] = vid; + break; + case 2: + info->format[PSC_VOLTAGE_OUT] = direct; + break; + default: + ret = -ENODEV; + goto abort; + } + } + } + /* * We should check if the COEFFICIENTS register is supported. * If it is, and the chip is configured for direct mode, we can read @@ -125,13 +148,18 @@ static int pmbus_identify(struct i2c_client *client, * * To do this, we will need access to a chip which actually supports the * COEFFICIENTS command, since the command is too complex to implement - * without testing it. + * without testing it. Until then, abort if a chip configured for direct + * mode was detected. */ + if (info->format[PSC_VOLTAGE_OUT] == direct) { + ret = -ENODEV; + goto abort; + } /* Try to find sensor groups */ pmbus_find_sensor_groups(client, info); - - return 0; +abort: + return ret; } static int pmbus_probe(struct i2c_client *client, @@ -172,11 +200,14 @@ static int pmbus_remove(struct i2c_client *client) * Use driver_data to set the number of pages supported by the chip. */ static const struct i2c_device_id pmbus_id[] = { + {"adp4000", 1}, {"bmr450", 1}, {"bmr451", 1}, {"bmr453", 1}, {"bmr454", 1}, {"ltc2978", 8}, + {"ncp4200", 1}, + {"ncp4208", 1}, {"pmbus", 0}, {} }; diff --git a/drivers/hwmon/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 50647ab7235..a6ae20ffef6 100644 --- a/drivers/hwmon/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -126,6 +126,42 @@ #define PMBUS_MFR_SERIAL 0x9E /* + * Virtual registers. + * Useful to support attributes which are not supported by standard PMBus + * registers but exist as manufacturer specific registers on individual chips. + * Must be mapped to real registers in device specific code. + * + * Semantics: + * Virtual registers are all word size. + * READ registers are read-only; writes are either ignored or return an error. + * RESET registers are read/write. Reading returns zero (used for detection), + * writing any value causes the associated history to be reset. + */ +#define PMBUS_VIRT_BASE 0x100 +#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 0) +#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 1) +#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 2) +#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 3) +#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 4) +#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 5) +#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 6) +#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 7) +#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 8) +#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 9) +#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 10) +#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 11) +#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 12) +#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 13) +#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 14) +#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 15) +#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 16) +#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 17) +#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 18) +#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 19) +#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 20) +#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 21) + +/* * CAPABILITY */ #define PB_CAPABILITY_SMBALERT (1<<4) @@ -266,11 +302,11 @@ enum pmbus_sensor_classes { #define PMBUS_HAVE_STATUS_FAN12 (1 << 16) #define PMBUS_HAVE_STATUS_FAN34 (1 << 17) +enum pmbus_data_format { linear = 0, direct, vid }; + struct pmbus_driver_info { int pages; /* Total number of pages */ - bool direct[PSC_NUM_CLASSES]; - /* true if device uses direct data format - for the given sensor class */ + enum pmbus_data_format format[PSC_NUM_CLASSES]; /* * Support one set of coefficients for each sensor type * Used for chips providing data in direct mode. @@ -286,6 +322,10 @@ struct pmbus_driver_info { * necessary. */ int (*read_byte_data)(struct i2c_client *client, int page, int reg); + int (*read_word_data)(struct i2c_client *client, int page, int reg); + int (*write_word_data)(struct i2c_client *client, int page, int reg, + u16 word); + int (*write_byte)(struct i2c_client *client, int page, u8 value); /* * The identify function determines supported PMBus functionality. * This function is only necessary if a chip driver supports multiple @@ -299,6 +339,9 @@ struct pmbus_driver_info { int pmbus_set_page(struct i2c_client *client, u8 page); int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); +int pmbus_write_byte(struct i2c_client *client, int page, u8 value); void pmbus_clear_faults(struct i2c_client *client); bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); diff --git a/drivers/hwmon/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 8e31a8e2c74..397fc59b568 100644 --- a/drivers/hwmon/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -33,14 +33,18 @@ /* * Constants needed to determine number of sensors, booleans, and labels. */ -#define PMBUS_MAX_INPUT_SENSORS 11 /* 6*volt, 3*curr, 2*power */ -#define PMBUS_VOUT_SENSORS_PER_PAGE 5 /* input, min, max, lcrit, - crit */ -#define PMBUS_IOUT_SENSORS_PER_PAGE 4 /* input, min, max, crit */ +#define PMBUS_MAX_INPUT_SENSORS 22 /* 10*volt, 7*curr, 5*power */ +#define PMBUS_VOUT_SENSORS_PER_PAGE 9 /* input, min, max, lcrit, + crit, lowest, highest, avg, + reset */ +#define PMBUS_IOUT_SENSORS_PER_PAGE 8 /* input, min, max, crit, + lowest, highest, avg, + reset */ #define PMBUS_POUT_SENSORS_PER_PAGE 4 /* input, cap, max, crit */ #define PMBUS_MAX_SENSORS_PER_FAN 1 /* input */ -#define PMBUS_MAX_SENSORS_PER_TEMP 5 /* input, min, max, lcrit, - crit */ +#define PMBUS_MAX_SENSORS_PER_TEMP 8 /* input, min, max, lcrit, + crit, lowest, highest, + reset */ #define PMBUS_MAX_INPUT_BOOLEANS 7 /* v: min_alarm, max_alarm, lcrit_alarm, crit_alarm; @@ -74,11 +78,13 @@ #define PB_STATUS_INPUT_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES) #define PB_STATUS_TEMP_BASE (PB_STATUS_INPUT_BASE + 1) +#define PMBUS_NAME_SIZE 24 + struct pmbus_sensor { - char name[I2C_NAME_SIZE]; /* sysfs sensor name */ + char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ struct sensor_device_attribute attribute; u8 page; /* page number */ - u8 reg; /* register */ + u16 reg; /* register */ enum pmbus_sensor_classes class; /* sensor class */ bool update; /* runtime sensor update needed */ int data; /* Sensor data. @@ -86,14 +92,14 @@ struct pmbus_sensor { }; struct pmbus_boolean { - char name[I2C_NAME_SIZE]; /* sysfs boolean name */ + char name[PMBUS_NAME_SIZE]; /* sysfs boolean name */ struct sensor_device_attribute attribute; }; struct pmbus_label { - char name[I2C_NAME_SIZE]; /* sysfs label name */ + char name[PMBUS_NAME_SIZE]; /* sysfs label name */ struct sensor_device_attribute attribute; - char label[I2C_NAME_SIZE]; /* label */ + char label[PMBUS_NAME_SIZE]; /* label */ }; struct pmbus_data { @@ -162,19 +168,39 @@ int pmbus_set_page(struct i2c_client *client, u8 page) } EXPORT_SYMBOL_GPL(pmbus_set_page); -static int pmbus_write_byte(struct i2c_client *client, u8 page, u8 value) +int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { int rv; - rv = pmbus_set_page(client, page); - if (rv < 0) - return rv; + if (page >= 0) { + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + } return i2c_smbus_write_byte(client, value); } +EXPORT_SYMBOL_GPL(pmbus_write_byte); + +/* + * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if + * a device specific mapping funcion exists and calls it if necessary. + */ +static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_byte) { + status = info->write_byte(client, page, value); + if (status != -ENODATA) + return status; + } + return pmbus_write_byte(client, page, value); +} -static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, - u16 word) +int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word) { int rv; @@ -184,6 +210,28 @@ static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, return i2c_smbus_write_word_data(client, reg, word); } +EXPORT_SYMBOL_GPL(pmbus_write_word_data); + +/* + * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->write_word_data) { + status = info->write_word_data(client, page, reg, word); + if (status != -ENODATA) + return status; + } + if (reg >= PMBUS_VIRT_BASE) + return -EINVAL; + return pmbus_write_word_data(client, page, reg, word); +} int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg) { @@ -197,20 +245,61 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg) } EXPORT_SYMBOL_GPL(pmbus_read_word_data); -static int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg) +/* + * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_word_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_word_data) { + status = info->read_word_data(client, page, reg); + if (status != -ENODATA) + return status; + } + if (reg >= PMBUS_VIRT_BASE) + return -EINVAL; + return pmbus_read_word_data(client, page, reg); +} + +int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) { int rv; - rv = pmbus_set_page(client, page); - if (rv < 0) - return rv; + if (page >= 0) { + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + } return i2c_smbus_read_byte_data(client, reg); } +EXPORT_SYMBOL_GPL(pmbus_read_byte_data); + +/* + * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if + * a device specific mapping function exists and calls it if necessary. + */ +static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + const struct pmbus_driver_info *info = data->info; + int status; + + if (info->read_byte_data) { + status = info->read_byte_data(client, page, reg); + if (status != -ENODATA) + return status; + } + return pmbus_read_byte_data(client, page, reg); +} static void pmbus_clear_fault_page(struct i2c_client *client, int page) { - pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + _pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); } void pmbus_clear_faults(struct i2c_client *client) @@ -223,13 +312,13 @@ void pmbus_clear_faults(struct i2c_client *client) } EXPORT_SYMBOL_GPL(pmbus_clear_faults); -static int pmbus_check_status_cml(struct i2c_client *client, int page) +static int pmbus_check_status_cml(struct i2c_client *client) { int status, status2; - status = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE); + status = pmbus_read_byte_data(client, -1, PMBUS_STATUS_BYTE); if (status < 0 || (status & PB_STATUS_CML)) { - status2 = pmbus_read_byte_data(client, page, PMBUS_STATUS_CML); + status2 = pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) return -EINVAL; } @@ -241,10 +330,10 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) int rv; struct pmbus_data *data = i2c_get_clientdata(client); - rv = pmbus_read_byte_data(client, page, reg); + rv = _pmbus_read_byte_data(client, page, reg); if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) - rv = pmbus_check_status_cml(client, page); - pmbus_clear_fault_page(client, page); + rv = pmbus_check_status_cml(client); + pmbus_clear_fault_page(client, -1); return rv >= 0; } EXPORT_SYMBOL_GPL(pmbus_check_byte_register); @@ -254,10 +343,10 @@ bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) int rv; struct pmbus_data *data = i2c_get_clientdata(client); - rv = pmbus_read_word_data(client, page, reg); + rv = _pmbus_read_word_data(client, page, reg); if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) - rv = pmbus_check_status_cml(client, page); - pmbus_clear_fault_page(client, page); + rv = pmbus_check_status_cml(client); + pmbus_clear_fault_page(client, -1); return rv >= 0; } EXPORT_SYMBOL_GPL(pmbus_check_word_register); @@ -270,24 +359,6 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) } EXPORT_SYMBOL_GPL(pmbus_get_driver_info); -/* - * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if - * a device specific mapping funcion exists and calls it if necessary. - */ -static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg) -{ - struct pmbus_data *data = i2c_get_clientdata(client); - const struct pmbus_driver_info *info = data->info; - int status; - - if (info->read_byte_data) { - status = info->read_byte_data(client, page, reg); - if (status != -ENODATA) - return status; - } - return pmbus_read_byte_data(client, page, reg); -} - static struct pmbus_data *pmbus_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -347,8 +418,9 @@ static struct pmbus_data *pmbus_update_device(struct device *dev) if (!data->valid || sensor->update) sensor->data - = pmbus_read_word_data(client, sensor->page, - sensor->reg); + = _pmbus_read_word_data(client, + sensor->page, + sensor->reg); } pmbus_clear_faults(client); data->last_updated = jiffies; @@ -443,15 +515,37 @@ static long pmbus_reg2data_direct(struct pmbus_data *data, return (val - b) / m; } +/* + * Convert VID sensor values to milli- or micro-units + * depending on sensor type. + * We currently only support VR11. + */ +static long pmbus_reg2data_vid(struct pmbus_data *data, + struct pmbus_sensor *sensor) +{ + long val = sensor->data; + + if (val < 0x02 || val > 0xb2) + return 0; + return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); +} + static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) { long val; - if (data->info->direct[sensor->class]) + switch (data->info->format[sensor->class]) { + case direct: val = pmbus_reg2data_direct(data, sensor); - else + break; + case vid: + val = pmbus_reg2data_vid(data, sensor); + break; + case linear: + default: val = pmbus_reg2data_linear(data, sensor); - + break; + } return val; } @@ -561,16 +655,31 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data, return val; } +static u16 pmbus_data2reg_vid(struct pmbus_data *data, + enum pmbus_sensor_classes class, long val) +{ + val = SENSORS_LIMIT(val, 500, 1600); + + return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625); +} + static u16 pmbus_data2reg(struct pmbus_data *data, enum pmbus_sensor_classes class, long val) { u16 regval; - if (data->info->direct[class]) + switch (data->info->format[class]) { + case direct: regval = pmbus_data2reg_direct(data, class, val); - else + break; + case vid: + regval = pmbus_data2reg_vid(data, class, val); + break; + case linear: + default: regval = pmbus_data2reg_linear(data, class, val); - + break; + } return regval; } @@ -682,7 +791,7 @@ static ssize_t pmbus_set_sensor(struct device *dev, mutex_lock(&data->update_lock); regval = pmbus_data2reg(data, sensor->class, val); - ret = pmbus_write_word_data(client, sensor->page, sensor->reg, regval); + ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval); if (ret < 0) rv = ret; else @@ -867,7 +976,10 @@ static void pmbus_find_max_attr(struct i2c_client *client, * and its associated alarm attribute. */ struct pmbus_limit_attr { - u8 reg; /* Limit register */ + u16 reg; /* Limit register */ + bool update; /* True if register needs updates */ + bool low; /* True if low limit; for limits with compare + functions only */ const char *attr; /* Attribute name */ const char *alarm; /* Alarm attribute name */ u32 sbit; /* Alarm attribute status bit */ @@ -912,13 +1024,15 @@ static bool pmbus_add_limit_attrs(struct i2c_client *client, if (pmbus_check_word_register(client, page, l->reg)) { cindex = data->num_sensors; pmbus_add_sensor(data, name, l->attr, index, page, - l->reg, attr->class, attr->update, + l->reg, attr->class, + attr->update || l->update, false); - if (info->func[page] & attr->sfunc) { + if (l->sbit && (info->func[page] & attr->sfunc)) { if (attr->compare) { pmbus_add_boolean_cmp(data, name, l->alarm, index, - cbase, cindex, + l->low ? cindex : cbase, + l->low ? cbase : cindex, attr->sbase + page, l->sbit); } else { pmbus_add_boolean_reg(data, name, @@ -953,9 +1067,11 @@ static void pmbus_add_sensor_attrs_one(struct i2c_client *client, index, page, cbase, attr); /* * Add generic alarm attribute only if there are no individual - * alarm attributes, and if there is a global alarm bit. + * alarm attributes, if there is a global alarm bit, and if + * the generic status register for this page is accessible. */ - if (!have_alarm && attr->gbit) + if (!have_alarm && attr->gbit && + pmbus_check_byte_register(client, page, PMBUS_STATUS_BYTE)) pmbus_add_boolean_reg(data, name, "alarm", index, PB_STATUS_BASE + page, attr->gbit); @@ -1008,6 +1124,21 @@ static const struct pmbus_limit_attr vin_limit_attrs[] = { .attr = "crit", .alarm = "crit_alarm", .sbit = PB_VOLTAGE_OV_FAULT, + }, { + .reg = PMBUS_VIRT_READ_VIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_VIN_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_VIN_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_VIN_HISTORY, + .attr = "reset_history", }, }; @@ -1032,6 +1163,21 @@ static const struct pmbus_limit_attr vout_limit_attrs[] = { .attr = "crit", .alarm = "crit_alarm", .sbit = PB_VOLTAGE_OV_FAULT, + }, { + .reg = PMBUS_VIRT_READ_VOUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_VOUT_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_VOUT_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_VOUT_HISTORY, + .attr = "reset_history", } }; @@ -1078,6 +1224,21 @@ static const struct pmbus_limit_attr iin_limit_attrs[] = { .attr = "crit", .alarm = "crit_alarm", .sbit = PB_IIN_OC_FAULT, + }, { + .reg = PMBUS_VIRT_READ_IIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_IIN_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_IIN_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_IIN_HISTORY, + .attr = "reset_history", } }; @@ -1097,6 +1258,21 @@ static const struct pmbus_limit_attr iout_limit_attrs[] = { .attr = "crit", .alarm = "crit_alarm", .sbit = PB_IOUT_OC_FAULT, + }, { + .reg = PMBUS_VIRT_READ_IOUT_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_IOUT_MIN, + .update = true, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_IOUT_MAX, + .update = true, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_IOUT_HISTORY, + .attr = "reset_history", } }; @@ -1132,6 +1308,17 @@ static const struct pmbus_limit_attr pin_limit_attrs[] = { .attr = "max", .alarm = "alarm", .sbit = PB_PIN_OP_WARNING, + }, { + .reg = PMBUS_VIRT_READ_PIN_AVG, + .update = true, + .attr = "average", + }, { + .reg = PMBUS_VIRT_READ_PIN_MAX, + .update = true, + .attr = "input_highest", + }, { + .reg = PMBUS_VIRT_RESET_PIN_HISTORY, + .attr = "reset_history", } }; @@ -1182,11 +1369,48 @@ static const struct pmbus_sensor_attr power_attributes[] = { static const struct pmbus_limit_attr temp_limit_attrs[] = { { .reg = PMBUS_UT_WARN_LIMIT, + .low = true, + .attr = "min", + .alarm = "min_alarm", + .sbit = PB_TEMP_UT_WARNING, + }, { + .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, + .attr = "lcrit", + .alarm = "lcrit_alarm", + .sbit = PB_TEMP_UT_FAULT, + }, { + .reg = PMBUS_OT_WARN_LIMIT, + .attr = "max", + .alarm = "max_alarm", + .sbit = PB_TEMP_OT_WARNING, + }, { + .reg = PMBUS_OT_FAULT_LIMIT, + .attr = "crit", + .alarm = "crit_alarm", + .sbit = PB_TEMP_OT_FAULT, + }, { + .reg = PMBUS_VIRT_READ_TEMP_MIN, + .attr = "lowest", + }, { + .reg = PMBUS_VIRT_READ_TEMP_MAX, + .attr = "highest", + }, { + .reg = PMBUS_VIRT_RESET_TEMP_HISTORY, + .attr = "reset_history", + } +}; + +static const struct pmbus_limit_attr temp_limit_attrs23[] = { + { + .reg = PMBUS_UT_WARN_LIMIT, + .low = true, .attr = "min", .alarm = "min_alarm", .sbit = PB_TEMP_UT_WARNING, }, { .reg = PMBUS_UT_FAULT_LIMIT, + .low = true, .attr = "lcrit", .alarm = "lcrit_alarm", .sbit = PB_TEMP_UT_FAULT, @@ -1226,8 +1450,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .sfunc = PMBUS_HAVE_STATUS_TEMP, .sbase = PB_STATUS_TEMP_BASE, .gbit = PB_STATUS_TEMPERATURE, - .limit = temp_limit_attrs, - .nlimit = ARRAY_SIZE(temp_limit_attrs), + .limit = temp_limit_attrs23, + .nlimit = ARRAY_SIZE(temp_limit_attrs23), }, { .reg = PMBUS_READ_TEMPERATURE_3, .class = PSC_TEMPERATURE, @@ -1238,8 +1462,8 @@ static const struct pmbus_sensor_attr temp_attributes[] = { .sfunc = PMBUS_HAVE_STATUS_TEMP, .sbase = PB_STATUS_TEMP_BASE, .gbit = PB_STATUS_TEMPERATURE, - .limit = temp_limit_attrs, - .nlimit = ARRAY_SIZE(temp_limit_attrs), + .limit = temp_limit_attrs23, + .nlimit = ARRAY_SIZE(temp_limit_attrs23), } }; @@ -1380,7 +1604,7 @@ static int pmbus_identify_common(struct i2c_client *client, */ switch (vout_mode >> 5) { case 0: /* linear mode */ - if (data->info->direct[PSC_VOLTAGE_OUT]) + if (data->info->format[PSC_VOLTAGE_OUT] != linear) return -ENODEV; exponent = vout_mode & 0x1f; @@ -1389,8 +1613,12 @@ static int pmbus_identify_common(struct i2c_client *client, exponent |= ~0x1f; data->exponent = exponent; break; + case 1: /* VID mode */ + if (data->info->format[PSC_VOLTAGE_OUT] != vid) + return -ENODEV; + break; case 2: /* direct mode */ - if (!data->info->direct[PSC_VOLTAGE_OUT]) + if (data->info->format[PSC_VOLTAGE_OUT] != direct) return -ENODEV; break; default: @@ -1457,18 +1685,6 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, ret = -EINVAL; goto out_data; } - /* - * Bail out if more than one page was configured, but we can not - * select the highest page. This is an indication that the wrong - * chip type was selected. Better bail out now than keep - * returning errors later on. - */ - if (info->pages > 1 && pmbus_set_page(client, info->pages - 1) < 0) { - dev_err(&client->dev, "Failed to select page %d\n", - info->pages - 1); - ret = -EINVAL; - goto out_data; - } ret = pmbus_identify_common(client, data); if (ret < 0) { diff --git a/drivers/hwmon/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index ace1c731973..d0ddb60155c 100644 --- a/drivers/hwmon/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -141,13 +141,11 @@ static int ucd9000_probe(struct i2c_client *client, block_buffer[ret] = '\0'; dev_info(&client->dev, "Device ID %s\n", block_buffer); - mid = NULL; - for (i = 0; i < ARRAY_SIZE(ucd9000_id); i++) { - mid = &ucd9000_id[i]; + for (mid = ucd9000_id; mid->name[0]; mid++) { if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) break; } - if (!mid || !strlen(mid->name)) { + if (!mid->name[0]) { dev_err(&client->dev, "Unsupported device\n"); return -ENODEV; } diff --git a/drivers/hwmon/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index ffcc1cf3609..c65e9da707c 100644 --- a/drivers/hwmon/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -68,13 +68,11 @@ static int ucd9200_probe(struct i2c_client *client, block_buffer[ret] = '\0'; dev_info(&client->dev, "Device ID %s\n", block_buffer); - mid = NULL; - for (i = 0; i < ARRAY_SIZE(ucd9200_id); i++) { - mid = &ucd9200_id[i]; + for (mid = ucd9200_id; mid->name[0]; mid++) { if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) break; } - if (!mid || !strlen(mid->name)) { + if (!mid->name[0]) { dev_err(&client->dev, "Unsupported device\n"); return -ENODEV; } diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index 3494a4cce41..e3b5c6039c2 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c @@ -28,33 +28,15 @@ #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> -#include <linux/io.h> -#include <linux/acpi.h> -#include <linux/delay.h> +#include "sch56xx-common.h" #define DRVNAME "sch5627" #define DEVNAME DRVNAME /* We only support one model */ -#define SIO_SCH5627_EM_LD 0x0C /* Embedded Microcontroller LD */ -#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ -#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ - -#define SIO_REG_LDSEL 0x07 /* Logical device select */ -#define SIO_REG_DEVID 0x20 /* Device ID */ -#define SIO_REG_ENABLE 0x30 /* Logical device enable */ -#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ - -#define SIO_SCH5627_ID 0xC6 /* Chipset ID */ - -#define REGION_LENGTH 9 - #define SCH5627_HWMON_ID 0xa5 #define SCH5627_COMPANY_ID 0x5c #define SCH5627_PRIMARY_ID 0xa0 -#define SCH5627_CMD_READ 0x02 -#define SCH5627_CMD_WRITE 0x03 - #define SCH5627_REG_BUILD_CODE 0x39 #define SCH5627_REG_BUILD_ID 0x3a #define SCH5627_REG_HWMON_ID 0x3c @@ -111,182 +93,6 @@ struct sch5627_data { u16 in[SCH5627_NO_IN]; }; -static struct platform_device *sch5627_pdev; - -/* Super I/O functions */ -static inline int superio_inb(int base, int reg) -{ - outb(reg, base); - return inb(base + 1); -} - -static inline int superio_enter(int base) -{ - /* Don't step on other drivers' I/O space by accident */ - if (!request_muxed_region(base, 2, DRVNAME)) { - pr_err("I/O address 0x%04x already in use\n", base); - return -EBUSY; - } - - outb(SIO_UNLOCK_KEY, base); - - return 0; -} - -static inline void superio_select(int base, int ld) -{ - outb(SIO_REG_LDSEL, base); - outb(ld, base + 1); -} - -static inline void superio_exit(int base) -{ - outb(SIO_LOCK_KEY, base); - release_region(base, 2); -} - -static int sch5627_send_cmd(struct sch5627_data *data, u8 cmd, u16 reg, u8 v) -{ - u8 val; - int i; - /* - * According to SMSC for the commands we use the maximum time for - * the EM to respond is 15 ms, but testing shows in practice it - * responds within 15-32 reads, so we first busy poll, and if - * that fails sleep a bit and try again until we are way past - * the 15 ms maximum response time. - */ - const int max_busy_polls = 64; - const int max_lazy_polls = 32; - - /* (Optional) Write-Clear the EC to Host Mailbox Register */ - val = inb(data->addr + 1); - outb(val, data->addr + 1); - - /* Set Mailbox Address Pointer to first location in Region 1 */ - outb(0x00, data->addr + 2); - outb(0x80, data->addr + 3); - - /* Write Request Packet Header */ - outb(cmd, data->addr + 4); /* VREG Access Type read:0x02 write:0x03 */ - outb(0x01, data->addr + 5); /* # of Entries: 1 Byte (8-bit) */ - outb(0x04, data->addr + 2); /* Mailbox AP to first data entry loc. */ - - /* Write Value field */ - if (cmd == SCH5627_CMD_WRITE) - outb(v, data->addr + 4); - - /* Write Address field */ - outb(reg & 0xff, data->addr + 6); - outb(reg >> 8, data->addr + 7); - - /* Execute the Random Access Command */ - outb(0x01, data->addr); /* Write 01h to the Host-to-EC register */ - - /* EM Interface Polling "Algorithm" */ - for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { - if (i >= max_busy_polls) - msleep(1); - /* Read Interrupt source Register */ - val = inb(data->addr + 8); - /* Write Clear the interrupt source bits */ - if (val) - outb(val, data->addr + 8); - /* Command Completed ? */ - if (val & 0x01) - break; - } - if (i == max_busy_polls + max_lazy_polls) { - pr_err("Max retries exceeded reading virtual " - "register 0x%04hx (%d)\n", reg, 1); - return -EIO; - } - - /* - * According to SMSC we may need to retry this, but sofar I've always - * seen this succeed in 1 try. - */ - for (i = 0; i < max_busy_polls; i++) { - /* Read EC-to-Host Register */ - val = inb(data->addr + 1); - /* Command Completed ? */ - if (val == 0x01) - break; - - if (i == 0) - pr_warn("EC reports: 0x%02x reading virtual register " - "0x%04hx\n", (unsigned int)val, reg); - } - if (i == max_busy_polls) { - pr_err("Max retries exceeded reading virtual " - "register 0x%04hx (%d)\n", reg, 2); - return -EIO; - } - - /* - * According to the SMSC app note we should now do: - * - * Set Mailbox Address Pointer to first location in Region 1 * - * outb(0x00, data->addr + 2); - * outb(0x80, data->addr + 3); - * - * But if we do that things don't work, so let's not. - */ - - /* Read Value field */ - if (cmd == SCH5627_CMD_READ) - return inb(data->addr + 4); - - return 0; -} - -static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg) -{ - return sch5627_send_cmd(data, SCH5627_CMD_READ, reg, 0); -} - -static int sch5627_write_virtual_reg(struct sch5627_data *data, - u16 reg, u8 val) -{ - return sch5627_send_cmd(data, SCH5627_CMD_WRITE, reg, val); -} - -static int sch5627_read_virtual_reg16(struct sch5627_data *data, u16 reg) -{ - int lsb, msb; - - /* Read LSB first, this will cause the matching MSB to be latched */ - lsb = sch5627_read_virtual_reg(data, reg); - if (lsb < 0) - return lsb; - - msb = sch5627_read_virtual_reg(data, reg + 1); - if (msb < 0) - return msb; - - return lsb | (msb << 8); -} - -static int sch5627_read_virtual_reg12(struct sch5627_data *data, u16 msb_reg, - u16 lsn_reg, int high_nibble) -{ - int msb, lsn; - - /* Read MSB first, this will cause the matching LSN to be latched */ - msb = sch5627_read_virtual_reg(data, msb_reg); - if (msb < 0) - return msb; - - lsn = sch5627_read_virtual_reg(data, lsn_reg); - if (lsn < 0) - return lsn; - - if (high_nibble) - return (msb << 4) | (lsn >> 4); - else - return (msb << 4) | (lsn & 0x0f); -} - static struct sch5627_data *sch5627_update_device(struct device *dev) { struct sch5627_data *data = dev_get_drvdata(dev); @@ -297,7 +103,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev) /* Trigger a Vbat voltage measurement every 5 minutes */ if (time_after(jiffies, data->last_battery + 300 * HZ)) { - sch5627_write_virtual_reg(data, SCH5627_REG_CTRL, + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10); data->last_battery = jiffies; } @@ -305,7 +111,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev) /* Cache the values for 1 second */ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { for (i = 0; i < SCH5627_NO_TEMPS; i++) { - val = sch5627_read_virtual_reg12(data, + val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_TEMP_MSB[i], SCH5627_REG_TEMP_LSN[i], SCH5627_REG_TEMP_HIGH_NIBBLE[i]); @@ -317,7 +123,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev) } for (i = 0; i < SCH5627_NO_FANS; i++) { - val = sch5627_read_virtual_reg16(data, + val = sch56xx_read_virtual_reg16(data->addr, SCH5627_REG_FAN[i]); if (unlikely(val < 0)) { ret = ERR_PTR(val); @@ -327,7 +133,7 @@ static struct sch5627_data *sch5627_update_device(struct device *dev) } for (i = 0; i < SCH5627_NO_IN; i++) { - val = sch5627_read_virtual_reg12(data, + val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_IN_MSB[i], SCH5627_REG_IN_LSN[i], SCH5627_REG_IN_HIGH_NIBBLE[i]); @@ -355,18 +161,21 @@ static int __devinit sch5627_read_limits(struct sch5627_data *data) * Note what SMSC calls ABS, is what lm_sensors calls max * (aka high), and HIGH is what lm_sensors calls crit. */ - val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_ABS[i]); + val = sch56xx_read_virtual_reg(data->addr, + SCH5627_REG_TEMP_ABS[i]); if (val < 0) return val; data->temp_max[i] = val; - val = sch5627_read_virtual_reg(data, SCH5627_REG_TEMP_HIGH[i]); + val = sch56xx_read_virtual_reg(data->addr, + SCH5627_REG_TEMP_HIGH[i]); if (val < 0) return val; data->temp_crit[i] = val; } for (i = 0; i < SCH5627_NO_FANS; i++) { - val = sch5627_read_virtual_reg16(data, SCH5627_REG_FAN_MIN[i]); + val = sch56xx_read_virtual_reg16(data->addr, + SCH5627_REG_FAN_MIN[i]); if (val < 0) return val; data->fan_min[i] = val; @@ -667,7 +476,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); - val = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_ID); + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID); if (val < 0) { err = val; goto error; @@ -679,7 +488,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) goto error; } - val = sch5627_read_virtual_reg(data, SCH5627_REG_COMPANY_ID); + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID); if (val < 0) { err = val; goto error; @@ -691,7 +500,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) goto error; } - val = sch5627_read_virtual_reg(data, SCH5627_REG_PRIMARY_ID); + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID); if (val < 0) { err = val; goto error; @@ -703,25 +512,28 @@ static int __devinit sch5627_probe(struct platform_device *pdev) goto error; } - build_code = sch5627_read_virtual_reg(data, SCH5627_REG_BUILD_CODE); + build_code = sch56xx_read_virtual_reg(data->addr, + SCH5627_REG_BUILD_CODE); if (build_code < 0) { err = build_code; goto error; } - build_id = sch5627_read_virtual_reg16(data, SCH5627_REG_BUILD_ID); + build_id = sch56xx_read_virtual_reg16(data->addr, + SCH5627_REG_BUILD_ID); if (build_id < 0) { err = build_id; goto error; } - hwmon_rev = sch5627_read_virtual_reg(data, SCH5627_REG_HWMON_REV); + hwmon_rev = sch56xx_read_virtual_reg(data->addr, + SCH5627_REG_HWMON_REV); if (hwmon_rev < 0) { err = hwmon_rev; goto error; } - val = sch5627_read_virtual_reg(data, SCH5627_REG_CTRL); + val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL); if (val < 0) { err = val; goto error; @@ -734,7 +546,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) } /* Trigger a Vbat voltage measurement, so that we get a valid reading the first time we read Vbat */ - sch5627_write_virtual_reg(data, SCH5627_REG_CTRL, + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10); data->last_battery = jiffies; @@ -746,6 +558,7 @@ static int __devinit sch5627_probe(struct platform_device *pdev) if (err) goto error; + pr_info("found %s chip at %#hx\n", DEVNAME, data->addr); pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", build_code, build_id, hwmon_rev); @@ -768,85 +581,6 @@ error: return err; } -static int __init sch5627_find(int sioaddr, unsigned short *address) -{ - u8 devid; - int err = superio_enter(sioaddr); - if (err) - return err; - - devid = superio_inb(sioaddr, SIO_REG_DEVID); - if (devid != SIO_SCH5627_ID) { - pr_debug("Unsupported device id: 0x%02x\n", - (unsigned int)devid); - err = -ENODEV; - goto exit; - } - - superio_select(sioaddr, SIO_SCH5627_EM_LD); - - if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { - pr_warn("Device not activated\n"); - err = -ENODEV; - goto exit; - } - - /* - * Warning the order of the low / high byte is the other way around - * as on most other superio devices!! - */ - *address = superio_inb(sioaddr, SIO_REG_ADDR) | - superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; - if (*address == 0) { - pr_warn("Base address not set\n"); - err = -ENODEV; - goto exit; - } - - pr_info("Found %s chip at %#hx\n", DEVNAME, *address); -exit: - superio_exit(sioaddr); - return err; -} - -static int __init sch5627_device_add(unsigned short address) -{ - struct resource res = { - .start = address, - .end = address + REGION_LENGTH - 1, - .flags = IORESOURCE_IO, - }; - int err; - - sch5627_pdev = platform_device_alloc(DRVNAME, address); - if (!sch5627_pdev) - return -ENOMEM; - - res.name = sch5627_pdev->name; - err = acpi_check_resource_conflict(&res); - if (err) - goto exit_device_put; - - err = platform_device_add_resources(sch5627_pdev, &res, 1); - if (err) { - pr_err("Device resource addition failed\n"); - goto exit_device_put; - } - - err = platform_device_add(sch5627_pdev); - if (err) { - pr_err("Device addition failed\n"); - goto exit_device_put; - } - - return 0; - -exit_device_put: - platform_device_put(sch5627_pdev); - - return err; -} - static struct platform_driver sch5627_driver = { .driver = { .owner = THIS_MODULE, @@ -858,31 +592,11 @@ static struct platform_driver sch5627_driver = { static int __init sch5627_init(void) { - int err = -ENODEV; - unsigned short address; - - if (sch5627_find(0x4e, &address) && sch5627_find(0x2e, &address)) - goto exit; - - err = platform_driver_register(&sch5627_driver); - if (err) - goto exit; - - err = sch5627_device_add(address); - if (err) - goto exit_driver; - - return 0; - -exit_driver: - platform_driver_unregister(&sch5627_driver); -exit: - return err; + return platform_driver_register(&sch5627_driver); } static void __exit sch5627_exit(void) { - platform_device_unregister(sch5627_pdev); platform_driver_unregister(&sch5627_driver); } diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c new file mode 100644 index 00000000000..244407aa79f --- /dev/null +++ b/drivers/hwmon/sch5636.c @@ -0,0 +1,539 @@ +/*************************************************************************** + * Copyright (C) 2011 Hans de Goede <hdegoede@redhat.com> * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include "sch56xx-common.h" + +#define DRVNAME "sch5636" +#define DEVNAME "theseus" /* We only support one model for now */ + +#define SCH5636_REG_FUJITSU_ID 0x780 +#define SCH5636_REG_FUJITSU_REV 0x783 + +#define SCH5636_NO_INS 5 +#define SCH5636_NO_TEMPS 16 +#define SCH5636_NO_FANS 8 + +static const u16 SCH5636_REG_IN_VAL[SCH5636_NO_INS] = { + 0x22, 0x23, 0x24, 0x25, 0x189 }; +static const u16 SCH5636_REG_IN_FACTORS[SCH5636_NO_INS] = { + 4400, 1500, 4000, 4400, 16000 }; +static const char * const SCH5636_IN_LABELS[SCH5636_NO_INS] = { + "3.3V", "VREF", "VBAT", "3.3AUX", "12V" }; + +static const u16 SCH5636_REG_TEMP_VAL[SCH5636_NO_TEMPS] = { + 0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C }; +#define SCH5636_REG_TEMP_CTRL(i) (0x790 + (i)) +#define SCH5636_TEMP_WORKING 0x01 +#define SCH5636_TEMP_ALARM 0x02 +#define SCH5636_TEMP_DEACTIVATED 0x80 + +static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = { + 0x2C, 0x2E, 0x30, 0x32, 0x62, 0x64, 0x66, 0x68 }; +#define SCH5636_REG_FAN_CTRL(i) (0x880 + (i)) +/* FAULT in datasheet, but acts as an alarm */ +#define SCH5636_FAN_ALARM 0x04 +#define SCH5636_FAN_NOT_PRESENT 0x08 +#define SCH5636_FAN_DEACTIVATED 0x80 + + +struct sch5636_data { + unsigned short addr; + struct device *hwmon_dev; + + struct mutex update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + u8 in[SCH5636_NO_INS]; + u8 temp_val[SCH5636_NO_TEMPS]; + u8 temp_ctrl[SCH5636_NO_TEMPS]; + u16 fan_val[SCH5636_NO_FANS]; + u8 fan_ctrl[SCH5636_NO_FANS]; +}; + +static struct sch5636_data *sch5636_update_device(struct device *dev) +{ + struct sch5636_data *data = dev_get_drvdata(dev); + struct sch5636_data *ret = data; + int i, val; + + mutex_lock(&data->update_lock); + + /* Cache the values for 1 second */ + if (data->valid && !time_after(jiffies, data->last_updated + HZ)) + goto abort; + + for (i = 0; i < SCH5636_NO_INS; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_IN_VAL[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->in[i] = val; + } + + for (i = 0; i < SCH5636_NO_TEMPS; i++) { + if (data->temp_ctrl[i] & SCH5636_TEMP_DEACTIVATED) + continue; + + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_TEMP_VAL[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_val[i] = val; + + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_TEMP_CTRL(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->temp_ctrl[i] = val; + /* Alarms need to be explicitly write-cleared */ + if (val & SCH5636_TEMP_ALARM) { + sch56xx_write_virtual_reg(data->addr, + SCH5636_REG_TEMP_CTRL(i), val); + } + } + + for (i = 0; i < SCH5636_NO_FANS; i++) { + if (data->fan_ctrl[i] & SCH5636_FAN_DEACTIVATED) + continue; + + val = sch56xx_read_virtual_reg16(data->addr, + SCH5636_REG_FAN_VAL[i]); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->fan_val[i] = val; + + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_FAN_CTRL(i)); + if (unlikely(val < 0)) { + ret = ERR_PTR(val); + goto abort; + } + data->fan_ctrl[i] = val; + /* Alarms need to be explicitly write-cleared */ + if (val & SCH5636_FAN_ALARM) { + sch56xx_write_virtual_reg(data->addr, + SCH5636_REG_FAN_CTRL(i), val); + } + } + + data->last_updated = jiffies; + data->valid = 1; +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int reg_to_rpm(u16 reg) +{ + if (reg == 0) + return -EIO; + if (reg == 0xffff) + return 0; + + return 5400540 / reg; +} + +static ssize_t show_name(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); +} + +static ssize_t show_in_value(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = DIV_ROUND_CLOSEST( + data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index], + 255); + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_in_label(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + return snprintf(buf, PAGE_SIZE, "%s\n", + SCH5636_IN_LABELS[attr->index]); +} + +static ssize_t show_temp_value(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->temp_val[attr->index] - 64) * 1000; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_temp_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_value(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = reg_to_rpm(data->fan_val[attr->index]); + if (val < 0) + return val; + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct sch5636_data *data = sch5636_update_device(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0; + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static struct sensor_device_attribute sch5636_attr[] = { + SENSOR_ATTR(name, 0444, show_name, NULL, 0), + SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0), + SENSOR_ATTR(in0_label, 0444, show_in_label, NULL, 0), + SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1), + SENSOR_ATTR(in1_label, 0444, show_in_label, NULL, 1), + SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2), + SENSOR_ATTR(in2_label, 0444, show_in_label, NULL, 2), + SENSOR_ATTR(in3_input, 0444, show_in_value, NULL, 3), + SENSOR_ATTR(in3_label, 0444, show_in_label, NULL, 3), + SENSOR_ATTR(in4_input, 0444, show_in_value, NULL, 4), + SENSOR_ATTR(in4_label, 0444, show_in_label, NULL, 4), +}; + +static struct sensor_device_attribute sch5636_temp_attr[] = { + SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0), + SENSOR_ATTR(temp1_fault, 0444, show_temp_fault, NULL, 0), + SENSOR_ATTR(temp1_alarm, 0444, show_temp_alarm, NULL, 0), + SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1), + SENSOR_ATTR(temp2_fault, 0444, show_temp_fault, NULL, 1), + SENSOR_ATTR(temp2_alarm, 0444, show_temp_alarm, NULL, 1), + SENSOR_ATTR(temp3_input, 0444, show_temp_value, NULL, 2), + SENSOR_ATTR(temp3_fault, 0444, show_temp_fault, NULL, 2), + SENSOR_ATTR(temp3_alarm, 0444, show_temp_alarm, NULL, 2), + SENSOR_ATTR(temp4_input, 0444, show_temp_value, NULL, 3), + SENSOR_ATTR(temp4_fault, 0444, show_temp_fault, NULL, 3), + SENSOR_ATTR(temp4_alarm, 0444, show_temp_alarm, NULL, 3), + SENSOR_ATTR(temp5_input, 0444, show_temp_value, NULL, 4), + SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4), + SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4), + SENSOR_ATTR(temp6_input, 0444, show_temp_value, NULL, 5), + SENSOR_ATTR(temp6_fault, 0444, show_temp_fault, NULL, 5), + SENSOR_ATTR(temp6_alarm, 0444, show_temp_alarm, NULL, 5), + SENSOR_ATTR(temp7_input, 0444, show_temp_value, NULL, 6), + SENSOR_ATTR(temp7_fault, 0444, show_temp_fault, NULL, 6), + SENSOR_ATTR(temp7_alarm, 0444, show_temp_alarm, NULL, 6), + SENSOR_ATTR(temp8_input, 0444, show_temp_value, NULL, 7), + SENSOR_ATTR(temp8_fault, 0444, show_temp_fault, NULL, 7), + SENSOR_ATTR(temp8_alarm, 0444, show_temp_alarm, NULL, 7), + SENSOR_ATTR(temp9_input, 0444, show_temp_value, NULL, 8), + SENSOR_ATTR(temp9_fault, 0444, show_temp_fault, NULL, 8), + SENSOR_ATTR(temp9_alarm, 0444, show_temp_alarm, NULL, 8), + SENSOR_ATTR(temp10_input, 0444, show_temp_value, NULL, 9), + SENSOR_ATTR(temp10_fault, 0444, show_temp_fault, NULL, 9), + SENSOR_ATTR(temp10_alarm, 0444, show_temp_alarm, NULL, 9), + SENSOR_ATTR(temp11_input, 0444, show_temp_value, NULL, 10), + SENSOR_ATTR(temp11_fault, 0444, show_temp_fault, NULL, 10), + SENSOR_ATTR(temp11_alarm, 0444, show_temp_alarm, NULL, 10), + SENSOR_ATTR(temp12_input, 0444, show_temp_value, NULL, 11), + SENSOR_ATTR(temp12_fault, 0444, show_temp_fault, NULL, 11), + SENSOR_ATTR(temp12_alarm, 0444, show_temp_alarm, NULL, 11), + SENSOR_ATTR(temp13_input, 0444, show_temp_value, NULL, 12), + SENSOR_ATTR(temp13_fault, 0444, show_temp_fault, NULL, 12), + SENSOR_ATTR(temp13_alarm, 0444, show_temp_alarm, NULL, 12), + SENSOR_ATTR(temp14_input, 0444, show_temp_value, NULL, 13), + SENSOR_ATTR(temp14_fault, 0444, show_temp_fault, NULL, 13), + SENSOR_ATTR(temp14_alarm, 0444, show_temp_alarm, NULL, 13), + SENSOR_ATTR(temp15_input, 0444, show_temp_value, NULL, 14), + SENSOR_ATTR(temp15_fault, 0444, show_temp_fault, NULL, 14), + SENSOR_ATTR(temp15_alarm, 0444, show_temp_alarm, NULL, 14), + SENSOR_ATTR(temp16_input, 0444, show_temp_value, NULL, 15), + SENSOR_ATTR(temp16_fault, 0444, show_temp_fault, NULL, 15), + SENSOR_ATTR(temp16_alarm, 0444, show_temp_alarm, NULL, 15), +}; + +static struct sensor_device_attribute sch5636_fan_attr[] = { + SENSOR_ATTR(fan1_input, 0444, show_fan_value, NULL, 0), + SENSOR_ATTR(fan1_fault, 0444, show_fan_fault, NULL, 0), + SENSOR_ATTR(fan1_alarm, 0444, show_fan_alarm, NULL, 0), + SENSOR_ATTR(fan2_input, 0444, show_fan_value, NULL, 1), + SENSOR_ATTR(fan2_fault, 0444, show_fan_fault, NULL, 1), + SENSOR_ATTR(fan2_alarm, 0444, show_fan_alarm, NULL, 1), + SENSOR_ATTR(fan3_input, 0444, show_fan_value, NULL, 2), + SENSOR_ATTR(fan3_fault, 0444, show_fan_fault, NULL, 2), + SENSOR_ATTR(fan3_alarm, 0444, show_fan_alarm, NULL, 2), + SENSOR_ATTR(fan4_input, 0444, show_fan_value, NULL, 3), + SENSOR_ATTR(fan4_fault, 0444, show_fan_fault, NULL, 3), + SENSOR_ATTR(fan4_alarm, 0444, show_fan_alarm, NULL, 3), + SENSOR_ATTR(fan5_input, 0444, show_fan_value, NULL, 4), + SENSOR_ATTR(fan5_fault, 0444, show_fan_fault, NULL, 4), + SENSOR_ATTR(fan5_alarm, 0444, show_fan_alarm, NULL, 4), + SENSOR_ATTR(fan6_input, 0444, show_fan_value, NULL, 5), + SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5), + SENSOR_ATTR(fan6_alarm, 0444, show_fan_alarm, NULL, 5), + SENSOR_ATTR(fan7_input, 0444, show_fan_value, NULL, 6), + SENSOR_ATTR(fan7_fault, 0444, show_fan_fault, NULL, 6), + SENSOR_ATTR(fan7_alarm, 0444, show_fan_alarm, NULL, 6), + SENSOR_ATTR(fan8_input, 0444, show_fan_value, NULL, 7), + SENSOR_ATTR(fan8_fault, 0444, show_fan_fault, NULL, 7), + SENSOR_ATTR(fan8_alarm, 0444, show_fan_alarm, NULL, 7), +}; + +static int sch5636_remove(struct platform_device *pdev) +{ + struct sch5636_data *data = platform_get_drvdata(pdev); + int i; + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) + device_remove_file(&pdev->dev, &sch5636_attr[i].dev_attr); + + for (i = 0; i < SCH5636_NO_TEMPS * 3; i++) + device_remove_file(&pdev->dev, + &sch5636_temp_attr[i].dev_attr); + + for (i = 0; i < SCH5636_NO_FANS * 3; i++) + device_remove_file(&pdev->dev, + &sch5636_fan_attr[i].dev_attr); + + platform_set_drvdata(pdev, NULL); + kfree(data); + + return 0; +} + +static int __devinit sch5636_probe(struct platform_device *pdev) +{ + struct sch5636_data *data; + int i, err, val, revision[2]; + char id[4]; + + data = kzalloc(sizeof(struct sch5636_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); + + for (i = 0; i < 3; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_FUJITSU_ID + i); + if (val < 0) { + pr_err("Could not read Fujitsu id byte at %#x\n", + SCH5636_REG_FUJITSU_ID + i); + err = val; + goto error; + } + id[i] = val; + } + id[i] = '\0'; + + if (strcmp(id, "THS")) { + pr_err("Unknown Fujitsu id: %02x%02x%02x\n", + id[0], id[1], id[2]); + err = -ENODEV; + goto error; + } + + for (i = 0; i < 2; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_FUJITSU_REV + i); + if (val < 0) { + err = val; + goto error; + } + revision[i] = val; + } + pr_info("Found %s chip at %#hx, revison: %d.%02d\n", DEVNAME, + data->addr, revision[0], revision[1]); + + /* Read all temp + fan ctrl registers to determine which are active */ + for (i = 0; i < SCH5636_NO_TEMPS; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_TEMP_CTRL(i)); + if (unlikely(val < 0)) { + err = val; + goto error; + } + data->temp_ctrl[i] = val; + } + + for (i = 0; i < SCH5636_NO_FANS; i++) { + val = sch56xx_read_virtual_reg(data->addr, + SCH5636_REG_FAN_CTRL(i)); + if (unlikely(val < 0)) { + err = val; + goto error; + } + data->fan_ctrl[i] = val; + } + + for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) { + err = device_create_file(&pdev->dev, + &sch5636_attr[i].dev_attr); + if (err) + goto error; + } + + for (i = 0; i < (SCH5636_NO_TEMPS * 3); i++) { + if (data->temp_ctrl[i/3] & SCH5636_TEMP_DEACTIVATED) + continue; + + err = device_create_file(&pdev->dev, + &sch5636_temp_attr[i].dev_attr); + if (err) + goto error; + } + + for (i = 0; i < (SCH5636_NO_FANS * 3); i++) { + if (data->fan_ctrl[i/3] & SCH5636_FAN_DEACTIVATED) + continue; + + err = device_create_file(&pdev->dev, + &sch5636_fan_attr[i].dev_attr); + if (err) + goto error; + } + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; + goto error; + } + + return 0; + +error: + sch5636_remove(pdev); + return err; +} + +static struct platform_driver sch5636_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = sch5636_probe, + .remove = sch5636_remove, +}; + +static int __init sch5636_init(void) +{ + return platform_driver_register(&sch5636_driver); +} + +static void __exit sch5636_exit(void) +{ + platform_driver_unregister(&sch5636_driver); +} + +MODULE_DESCRIPTION("SMSC SCH5636 Hardware Monitoring Driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); + +module_init(sch5636_init); +module_exit(sch5636_exit); diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c new file mode 100644 index 00000000000..fac32ee0b10 --- /dev/null +++ b/drivers/hwmon/sch56xx-common.c @@ -0,0 +1,340 @@ +/*************************************************************************** + * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/acpi.h> +#include <linux/delay.h> +#include "sch56xx-common.h" + +#define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */ +#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x66 /* Logical device address (2 bytes) */ + +#define SIO_SCH5627_ID 0xC6 /* Chipset ID */ +#define SIO_SCH5636_ID 0xC7 /* Chipset ID */ + +#define REGION_LENGTH 9 + +#define SCH56XX_CMD_READ 0x02 +#define SCH56XX_CMD_WRITE 0x03 + +static struct platform_device *sch56xx_pdev; + +/* Super I/O functions */ +static inline int superio_inb(int base, int reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static inline int superio_enter(int base) +{ + /* Don't step on other drivers' I/O space by accident */ + if (!request_muxed_region(base, 2, "sch56xx")) { + pr_err("I/O address 0x%04x already in use\n", base); + return -EBUSY; + } + + outb(SIO_UNLOCK_KEY, base); + + return 0; +} + +static inline void superio_select(int base, int ld) +{ + outb(SIO_REG_LDSEL, base); + outb(ld, base + 1); +} + +static inline void superio_exit(int base) +{ + outb(SIO_LOCK_KEY, base); + release_region(base, 2); +} + +static int sch56xx_send_cmd(u16 addr, u8 cmd, u16 reg, u8 v) +{ + u8 val; + int i; + /* + * According to SMSC for the commands we use the maximum time for + * the EM to respond is 15 ms, but testing shows in practice it + * responds within 15-32 reads, so we first busy poll, and if + * that fails sleep a bit and try again until we are way past + * the 15 ms maximum response time. + */ + const int max_busy_polls = 64; + const int max_lazy_polls = 32; + + /* (Optional) Write-Clear the EC to Host Mailbox Register */ + val = inb(addr + 1); + outb(val, addr + 1); + + /* Set Mailbox Address Pointer to first location in Region 1 */ + outb(0x00, addr + 2); + outb(0x80, addr + 3); + + /* Write Request Packet Header */ + outb(cmd, addr + 4); /* VREG Access Type read:0x02 write:0x03 */ + outb(0x01, addr + 5); /* # of Entries: 1 Byte (8-bit) */ + outb(0x04, addr + 2); /* Mailbox AP to first data entry loc. */ + + /* Write Value field */ + if (cmd == SCH56XX_CMD_WRITE) + outb(v, addr + 4); + + /* Write Address field */ + outb(reg & 0xff, addr + 6); + outb(reg >> 8, addr + 7); + + /* Execute the Random Access Command */ + outb(0x01, addr); /* Write 01h to the Host-to-EC register */ + + /* EM Interface Polling "Algorithm" */ + for (i = 0; i < max_busy_polls + max_lazy_polls; i++) { + if (i >= max_busy_polls) + msleep(1); + /* Read Interrupt source Register */ + val = inb(addr + 8); + /* Write Clear the interrupt source bits */ + if (val) + outb(val, addr + 8); + /* Command Completed ? */ + if (val & 0x01) + break; + } + if (i == max_busy_polls + max_lazy_polls) { + pr_err("Max retries exceeded reading virtual " + "register 0x%04hx (%d)\n", reg, 1); + return -EIO; + } + + /* + * According to SMSC we may need to retry this, but sofar I've always + * seen this succeed in 1 try. + */ + for (i = 0; i < max_busy_polls; i++) { + /* Read EC-to-Host Register */ + val = inb(addr + 1); + /* Command Completed ? */ + if (val == 0x01) + break; + + if (i == 0) + pr_warn("EC reports: 0x%02x reading virtual register " + "0x%04hx\n", (unsigned int)val, reg); + } + if (i == max_busy_polls) { + pr_err("Max retries exceeded reading virtual " + "register 0x%04hx (%d)\n", reg, 2); + return -EIO; + } + + /* + * According to the SMSC app note we should now do: + * + * Set Mailbox Address Pointer to first location in Region 1 * + * outb(0x00, addr + 2); + * outb(0x80, addr + 3); + * + * But if we do that things don't work, so let's not. + */ + + /* Read Value field */ + if (cmd == SCH56XX_CMD_READ) + return inb(addr + 4); + + return 0; +} + +int sch56xx_read_virtual_reg(u16 addr, u16 reg) +{ + return sch56xx_send_cmd(addr, SCH56XX_CMD_READ, reg, 0); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg); + +int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val) +{ + return sch56xx_send_cmd(addr, SCH56XX_CMD_WRITE, reg, val); +} +EXPORT_SYMBOL(sch56xx_write_virtual_reg); + +int sch56xx_read_virtual_reg16(u16 addr, u16 reg) +{ + int lsb, msb; + + /* Read LSB first, this will cause the matching MSB to be latched */ + lsb = sch56xx_read_virtual_reg(addr, reg); + if (lsb < 0) + return lsb; + + msb = sch56xx_read_virtual_reg(addr, reg + 1); + if (msb < 0) + return msb; + + return lsb | (msb << 8); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg16); + +int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, + int high_nibble) +{ + int msb, lsn; + + /* Read MSB first, this will cause the matching LSN to be latched */ + msb = sch56xx_read_virtual_reg(addr, msb_reg); + if (msb < 0) + return msb; + + lsn = sch56xx_read_virtual_reg(addr, lsn_reg); + if (lsn < 0) + return lsn; + + if (high_nibble) + return (msb << 4) | (lsn >> 4); + else + return (msb << 4) | (lsn & 0x0f); +} +EXPORT_SYMBOL(sch56xx_read_virtual_reg12); + +static int __init sch56xx_find(int sioaddr, unsigned short *address, + const char **name) +{ + u8 devid; + int err; + + err = superio_enter(sioaddr); + if (err) + return err; + + devid = superio_inb(sioaddr, SIO_REG_DEVID); + switch (devid) { + case SIO_SCH5627_ID: + *name = "sch5627"; + break; + case SIO_SCH5636_ID: + *name = "sch5636"; + break; + default: + pr_debug("Unsupported device id: 0x%02x\n", + (unsigned int)devid); + err = -ENODEV; + goto exit; + } + + superio_select(sioaddr, SIO_SCH56XX_LD_EM); + + if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { + pr_warn("Device not activated\n"); + err = -ENODEV; + goto exit; + } + + /* + * Warning the order of the low / high byte is the other way around + * as on most other superio devices!! + */ + *address = superio_inb(sioaddr, SIO_REG_ADDR) | + superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8; + if (*address == 0) { + pr_warn("Base address not set\n"); + err = -ENODEV; + goto exit; + } + +exit: + superio_exit(sioaddr); + return err; +} + +static int __init sch56xx_device_add(unsigned short address, const char *name) +{ + struct resource res = { + .start = address, + .end = address + REGION_LENGTH - 1, + .flags = IORESOURCE_IO, + }; + int err; + + sch56xx_pdev = platform_device_alloc(name, address); + if (!sch56xx_pdev) + return -ENOMEM; + + res.name = sch56xx_pdev->name; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit_device_put; + + err = platform_device_add_resources(sch56xx_pdev, &res, 1); + if (err) { + pr_err("Device resource addition failed\n"); + goto exit_device_put; + } + + err = platform_device_add(sch56xx_pdev); + if (err) { + pr_err("Device addition failed\n"); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(sch56xx_pdev); + + return err; +} + +static int __init sch56xx_init(void) +{ + int err; + unsigned short address; + const char *name; + + err = sch56xx_find(0x4e, &address, &name); + if (err) + err = sch56xx_find(0x2e, &address, &name); + if (err) + return err; + + return sch56xx_device_add(address, name); +} + +static void __exit sch56xx_exit(void) +{ + platform_device_unregister(sch56xx_pdev); +} + +MODULE_DESCRIPTION("SMSC SCH56xx Hardware Monitoring Common Code"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); + +module_init(sch56xx_init); +module_exit(sch56xx_exit); diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h new file mode 100644 index 00000000000..d5eaf3b9ebf --- /dev/null +++ b/drivers/hwmon/sch56xx-common.h @@ -0,0 +1,24 @@ +/*************************************************************************** + * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com> * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +int sch56xx_read_virtual_reg(u16 addr, u16 reg); +int sch56xx_write_virtual_reg(u16 addr, u16 reg, u8 val); +int sch56xx_read_virtual_reg16(u16 addr, u16 reg); +int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, + int high_nibble); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index cf4330b352e..fe4104c6b76 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -32,7 +32,7 @@ #include <linux/sht15.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> -#include <asm/atomic.h> +#include <linux/atomic.h> /* Commands */ #define SHT15_MEASURE_TEMP 0x03 @@ -671,7 +671,7 @@ static ssize_t sht15_show_status(struct device *dev, * @buf: sysfs buffer to read the new heater state from. * @count: length of the data. * - * Will be called on read access to heater_enable sysfs attribute. + * Will be called on write access to heater_enable sysfs attribute. * Returns number of bytes actually decoded, negative errno on error. */ static ssize_t sht15_store_heater(struct device *dev, diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index 0d18de424c6..8eac67d769f 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -27,6 +27,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/hwmon.h> +#include <linux/hwmon-vid.h> #include <linux/sysfs.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> @@ -48,8 +49,10 @@ enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME }; struct via_cputemp_data { struct device *hwmon_dev; const char *name; + u8 vrm; u32 id; - u32 msr; + u32 msr_temp; + u32 msr_vid; }; /* @@ -77,13 +80,27 @@ static ssize_t show_temp(struct device *dev, u32 eax, edx; int err; - err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); + err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); if (err) return -EAGAIN; return sprintf(buf, "%lu\n", ((unsigned long)eax & 0xffffff) * 1000); } +static ssize_t show_cpu_vid(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct via_cputemp_data *data = dev_get_drvdata(dev); + u32 eax, edx; + int err; + + err = rdmsr_safe_on_cpu(data->id, data->msr_vid, &eax, &edx); + if (err) + return -EAGAIN; + + return sprintf(buf, "%d\n", vid_from_reg(~edx & 0x7f, data->vrm)); +} + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, SHOW_TEMP); static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL); @@ -100,6 +117,9 @@ static const struct attribute_group via_cputemp_group = { .attrs = via_cputemp_attributes, }; +/* Optional attributes */ +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_cpu_vid, NULL); + static int __devinit via_cputemp_probe(struct platform_device *pdev) { struct via_cputemp_data *data; @@ -122,11 +142,12 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) /* C7 A */ case 0xD: /* C7 D */ - data->msr = 0x1169; + data->msr_temp = 0x1169; + data->msr_vid = 0x198; break; case 0xF: /* Nano */ - data->msr = 0x1423; + data->msr_temp = 0x1423; break; default: err = -ENODEV; @@ -134,7 +155,7 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) } /* test if we can access the TEMPERATURE MSR */ - err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx); + err = rdmsr_safe_on_cpu(data->id, data->msr_temp, &eax, &edx); if (err) { dev_err(&pdev->dev, "Unable to access TEMPERATURE MSR, giving up\n"); @@ -147,6 +168,15 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) if (err) goto exit_free; + if (data->msr_vid) + data->vrm = vid_which_vrm(); + + if (data->vrm) { + err = device_create_file(&pdev->dev, &dev_attr_cpu0_vid); + if (err) + goto exit_remove; + } + data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); @@ -158,6 +188,8 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev) return 0; exit_remove: + if (data->vrm) + device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); exit_free: platform_set_drvdata(pdev, NULL); @@ -171,6 +203,8 @@ static int __devexit via_cputemp_remove(struct platform_device *pdev) struct via_cputemp_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); + if (data->vrm) + device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group); platform_set_drvdata(pdev, NULL); kfree(data); diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 17cf1ab9552..8c2844e5691 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -329,8 +329,8 @@ static int w83791d_detect(struct i2c_client *client, struct i2c_board_info *info); static int w83791d_remove(struct i2c_client *client); -static int w83791d_read(struct i2c_client *client, u8 register); -static int w83791d_write(struct i2c_client *client, u8 register, u8 value); +static int w83791d_read(struct i2c_client *client, u8 reg); +static int w83791d_write(struct i2c_client *client, u8 reg, u8 value); static struct w83791d_data *w83791d_update_device(struct device *dev); #ifdef DEBUG |