From f6f11018dc7ea62482f36846e9f6eb0f27df7c3c Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 11 Aug 2008 17:04:32 +1000 Subject: powerpc/drivers: Use linux/of_device.h instead of asm/of_device.h Signed-off-by: Stephen Rothwell Acked-by: Takashi Iwai Signed-off-by: Paul Mackerras --- drivers/hwmon/ams/ams.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h index a6221e5dd98..221ef6915a5 100644 --- a/drivers/hwmon/ams/ams.h +++ b/drivers/hwmon/ams/ams.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include enum ams_irq { AMS_IRQ_FREEFALL = 0x01, -- cgit v1.2.3-70-g09d2 From e04180882faf69e896a8131ff3137788dd08b8d3 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 24 Aug 2008 20:59:49 -0700 Subject: hwmon: Add Ultra45 workstation hwmon driver. This is a PIC16F747 based controller that monitors and consolidates the hardware access to various fan and temperature values reported by adr7462 and similar devices behind an I2C bus. Signed-off-by: David S. Miller --- drivers/hwmon/Kconfig | 7 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ultra45_env.c | 320 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 drivers/hwmon/ultra45_env.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d402e8d813c..89765dd9aad 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -791,6 +791,13 @@ config SENSORS_W83627EHF This driver can also be built as a module. If so, the module will be called w83627ehf. +config SENSORS_ULTRA45 + tristate "Sun Ultra45 PIC16F747" + depends on SPARC64 + help + This driver provides support for the Ultra45 workstation environmental + sensors. + config SENSORS_HDAPS tristate "IBM Hard Drive Active Protection System (hdaps)" depends on INPUT && X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 950134ab842..ee511ecf1df 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c new file mode 100644 index 00000000000..9aec95cbf7e --- /dev/null +++ b/drivers/hwmon/ultra45_env.c @@ -0,0 +1,320 @@ +/* ultra45_env.c: Driver for Ultra45 PIC16F747 environmental monitor. + * + * Copyright (C) 2008 David S. Miller + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_MODULE_VERSION "0.1" + +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_DESCRIPTION("Ultra45 environmental monitor driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +/* PIC device registers */ +#define REG_CMD 0x00UL +#define REG_CMD_RESET 0x80 +#define REG_CMD_ESTAR 0x01 +#define REG_STAT 0x01UL +#define REG_STAT_FWVER 0xf0 +#define REG_STAT_TGOOD 0x08 +#define REG_STAT_STALE 0x04 +#define REG_STAT_BUSY 0x02 +#define REG_STAT_FAULT 0x01 +#define REG_DATA 0x40UL +#define REG_ADDR 0x41UL +#define REG_SIZE 0x42UL + +/* Registers accessed indirectly via REG_DATA/REG_ADDR */ +#define IREG_FAN0 0x00 +#define IREG_FAN1 0x01 +#define IREG_FAN2 0x02 +#define IREG_FAN3 0x03 +#define IREG_FAN4 0x04 +#define IREG_FAN5 0x05 +#define IREG_LCL_TEMP 0x06 +#define IREG_RMT1_TEMP 0x07 +#define IREG_RMT2_TEMP 0x08 +#define IREG_RMT3_TEMP 0x09 +#define IREG_LM95221_TEMP 0x0a +#define IREG_FIRE_TEMP 0x0b +#define IREG_LSI1064_TEMP 0x0c +#define IREG_FRONT_TEMP 0x0d +#define IREG_FAN_STAT 0x0e +#define IREG_VCORE0 0x0f +#define IREG_VCORE1 0x10 +#define IREG_VMEM0 0x11 +#define IREG_VMEM1 0x12 +#define IREG_PSU_TEMP 0x13 + +struct env { + void __iomem *regs; + spinlock_t lock; + + struct device *hwmon_dev; +}; + +static u8 env_read(struct env *p, u8 ireg) +{ + u8 ret; + + spin_lock(&p->lock); + writeb(ireg, p->regs + REG_ADDR); + ret = readb(p->regs + REG_DATA); + spin_unlock(&p->lock); + + return ret; +} + +static void env_write(struct env *p, u8 ireg, u8 val) +{ + spin_lock(&p->lock); + writeb(ireg, p->regs + REG_ADDR); + writeb(val, p->regs + REG_DATA); + spin_unlock(&p->lock); +} + +/* There seems to be a adr7462 providing these values, thus a lot + * of these calculations are borrowed from the adt7470 driver. + */ +#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) +#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM +#define FAN_PERIOD_INVALID (0xff << 8) +#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) + +static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf) +{ + int fan_nr = to_sensor_dev_attr(attr)->index; + struct env *p = dev_get_drvdata(dev); + int rpm, period; + u8 val; + + val = env_read(p, IREG_FAN0 + fan_nr); + period = (int) val << 8; + if (FAN_DATA_VALID(period)) + rpm = FAN_PERIOD_TO_RPM(period); + else + rpm = 0; + + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t set_fan_speed(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int fan_nr = to_sensor_dev_attr(attr)->index; + int rpm = simple_strtol(buf, NULL, 10); + struct env *p = dev_get_drvdata(dev); + int period; + u8 val; + + if (!rpm) + return -EINVAL; + + period = FAN_RPM_TO_PERIOD(rpm); + val = period >> 8; + env_write(p, IREG_FAN0 + fan_nr, val); + + return count; +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *attr, char *buf) +{ + int fan_nr = to_sensor_dev_attr(attr)->index; + struct env *p = dev_get_drvdata(dev); + u8 val = env_read(p, IREG_FAN_STAT); + return sprintf(buf, "%d\n", (val & (1 << fan_nr)) ? 1 : 0); +} + +#define fan(index) \ +static SENSOR_DEVICE_ATTR(fan##index##_speed, S_IRUGO | S_IWUSR, \ + show_fan_speed, set_fan_speed, index); \ +static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, \ + show_fan_fault, NULL, index) + +fan(0); +fan(1); +fan(2); +fan(3); +fan(4); + +static SENSOR_DEVICE_ATTR(psu_fan_fault, S_IRUGO, show_fan_fault, NULL, 6); + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + int temp_nr = to_sensor_dev_attr(attr)->index; + struct env *p = dev_get_drvdata(dev); + s8 val; + + val = env_read(p, IREG_LCL_TEMP + temp_nr); + return sprintf(buf, "%d\n", ((int) val) - 64); +} + +static SENSOR_DEVICE_ATTR(adt7462_local_temp, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(cpu0_temp, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(cpu1_temp, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(motherboard_temp, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(lm95221_local_temp, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(fire_temp, S_IRUGO, show_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(lsi1064_local_temp, S_IRUGO, show_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(front_panel_temp, S_IRUGO, show_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(psu_temp, S_IRUGO, show_temp, NULL, 13); + +static ssize_t show_stat_bit(struct device *dev, struct device_attribute *attr, char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + struct env *p = dev_get_drvdata(dev); + u8 val; + + val = readb(p->regs + REG_STAT); + return sprintf(buf, "%d\n", (val & (1 << index)) ? 1 : 0); +} + +static SENSOR_DEVICE_ATTR(fan_failure, S_IRUGO, show_stat_bit, NULL, 0); +static SENSOR_DEVICE_ATTR(env_bus_busy, S_IRUGO, show_stat_bit, NULL, 1); +static SENSOR_DEVICE_ATTR(env_data_stale, S_IRUGO, show_stat_bit, NULL, 2); +static SENSOR_DEVICE_ATTR(tpm_self_test_passed, S_IRUGO, show_stat_bit, NULL, 3); + +static ssize_t show_fwver(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct env *p = dev_get_drvdata(dev); + u8 val; + + val = readb(p->regs + REG_STAT); + return sprintf(buf, "%d\n", val >> 4); +} + +static SENSOR_DEVICE_ATTR(firmware_version, S_IRUGO, show_fwver, NULL, 0); + +static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "ultra45\n"); +} + +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); + +static struct attribute *env_attributes[] = { + &sensor_dev_attr_fan0_speed.dev_attr.attr, + &sensor_dev_attr_fan0_fault.dev_attr.attr, + &sensor_dev_attr_fan1_speed.dev_attr.attr, + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_fan2_speed.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + &sensor_dev_attr_fan3_speed.dev_attr.attr, + &sensor_dev_attr_fan3_fault.dev_attr.attr, + &sensor_dev_attr_fan4_speed.dev_attr.attr, + &sensor_dev_attr_fan4_fault.dev_attr.attr, + &sensor_dev_attr_psu_fan_fault.dev_attr.attr, + &sensor_dev_attr_adt7462_local_temp.dev_attr.attr, + &sensor_dev_attr_cpu0_temp.dev_attr.attr, + &sensor_dev_attr_cpu1_temp.dev_attr.attr, + &sensor_dev_attr_motherboard_temp.dev_attr.attr, + &sensor_dev_attr_lm95221_local_temp.dev_attr.attr, + &sensor_dev_attr_fire_temp.dev_attr.attr, + &sensor_dev_attr_lsi1064_local_temp.dev_attr.attr, + &sensor_dev_attr_front_panel_temp.dev_attr.attr, + &sensor_dev_attr_psu_temp.dev_attr.attr, + &sensor_dev_attr_fan_failure.dev_attr.attr, + &sensor_dev_attr_env_bus_busy.dev_attr.attr, + &sensor_dev_attr_env_data_stale.dev_attr.attr, + &sensor_dev_attr_tpm_self_test_passed.dev_attr.attr, + &sensor_dev_attr_firmware_version.dev_attr.attr, + &sensor_dev_attr_name.dev_attr.attr, + NULL, +}; + +static const struct attribute_group env_group = { + .attrs = env_attributes, +}; + +static int __devinit env_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct env *p = kzalloc(sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; + + if (!p) + goto out; + + spin_lock_init(&p->lock); + + p->regs = of_ioremap(&op->resource[0], 0, REG_SIZE, "pic16f747"); + if (!p->regs) + goto out_free; + + err = sysfs_create_group(&op->dev.kobj, &env_group); + if (err) + goto out_iounmap; + + p->hwmon_dev = hwmon_device_register(&op->dev); + if (IS_ERR(p->hwmon_dev)) { + err = PTR_ERR(p->hwmon_dev); + goto out_sysfs_remove_group; + } + + dev_set_drvdata(&op->dev, p); + err = 0; + +out: + return err; + +out_sysfs_remove_group: + sysfs_remove_group(&op->dev.kobj, &env_group); + +out_iounmap: + of_iounmap(&op->resource[0], p->regs, REG_SIZE); + +out_free: + kfree(p); + goto out; +} + +static int __devexit env_remove(struct of_device *op) +{ + struct env *p = dev_get_drvdata(&op->dev); + + if (p) { + sysfs_remove_group(&op->dev.kobj, &env_group); + hwmon_device_unregister(p->hwmon_dev); + of_iounmap(&op->resource[0], p->regs, REG_SIZE); + kfree(p); + } + + return 0; +} + +static struct of_device_id env_match[] = { + { + .name = "env-monitor", + .compatible = "SUNW,ebus-pic16f747-env", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, env_match); + +static struct of_platform_driver env_driver = { + .name = "ultra45_env", + .match_table = env_match, + .probe = env_probe, + .remove = __devexit_p(env_remove), +}; + +static int __init env_init(void) +{ + return of_register_driver(&env_driver, &of_bus_type); +} + +static void __exit env_exit(void) +{ + of_unregister_driver(&env_driver); +} + +module_init(env_init); +module_exit(env_exit); -- cgit v1.2.3-70-g09d2 From fd098316ef533e8441576f020ead4beab93154ce Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 31 Aug 2008 01:23:17 -0700 Subject: sparc: Annotate of_device_id arrays with const or __initdata. As suggested by Stephen Rothwell. Signed-off-by: David S. Miller --- arch/sparc/include/asm/parport.h | 2 +- arch/sparc/kernel/apc.c | 2 +- arch/sparc/kernel/pmc.c | 2 +- arch/sparc/kernel/time.c | 2 +- arch/sparc64/kernel/auxio.c | 2 +- arch/sparc64/kernel/chmc.c | 2 +- arch/sparc64/kernel/pci_fire.c | 2 +- arch/sparc64/kernel/pci_psycho.c | 2 +- arch/sparc64/kernel/pci_sabre.c | 2 +- arch/sparc64/kernel/pci_schizo.c | 2 +- arch/sparc64/kernel/pci_sun4v.c | 2 +- arch/sparc64/kernel/power.c | 2 +- arch/sparc64/kernel/time.c | 6 +++--- drivers/atm/fore200e.c | 2 +- drivers/char/hw_random/n2-drv.c | 2 +- drivers/hwmon/ultra45_env.c | 2 +- drivers/input/misc/sparcspkr.c | 4 ++-- drivers/input/serio/i8042-sparcio.h | 2 +- drivers/mtd/maps/sun_uflash.c | 2 +- drivers/net/myri_sbus.c | 2 +- drivers/net/niu.c | 2 +- drivers/net/sunbmac.c | 2 +- drivers/net/sunhme.c | 2 +- drivers/net/sunlance.c | 2 +- drivers/net/sunqe.c | 2 +- drivers/parport/parport_sunbpp.c | 2 +- drivers/sbus/char/bbc_i2c.c | 2 +- drivers/sbus/char/display7seg.c | 2 +- drivers/sbus/char/envctrl.c | 2 +- drivers/sbus/char/flash.c | 2 +- drivers/sbus/char/uctrl.c | 2 +- drivers/scsi/qlogicpti.c | 2 +- drivers/scsi/sun_esp.c | 2 +- drivers/serial/sunhv.c | 2 +- drivers/serial/sunsab.c | 2 +- drivers/serial/sunsu.c | 2 +- drivers/serial/sunzilog.c | 2 +- drivers/video/bw2.c | 2 +- drivers/video/cg14.c | 2 +- drivers/video/cg3.c | 2 +- drivers/video/cg6.c | 2 +- drivers/video/ffb.c | 2 +- drivers/video/leo.c | 2 +- drivers/video/p9100.c | 2 +- drivers/video/tcx.c | 2 +- drivers/watchdog/cpwd.c | 2 +- drivers/watchdog/riowd.c | 2 +- sound/sparc/amd7930.c | 2 +- sound/sparc/cs4231.c | 2 +- sound/sparc/dbri.c | 2 +- 50 files changed, 53 insertions(+), 53 deletions(-) (limited to 'drivers/hwmon') diff --git a/arch/sparc/include/asm/parport.h b/arch/sparc/include/asm/parport.h index 70dce0273f9..dff3f0253aa 100644 --- a/arch/sparc/include/asm/parport.h +++ b/arch/sparc/include/asm/parport.h @@ -215,7 +215,7 @@ static int __devexit ecpp_remove(struct of_device *op) return 0; } -static struct of_device_id ecpp_match[] = { +static const struct of_device_id ecpp_match[] = { { .name = "ecpp", }, diff --git a/arch/sparc/kernel/apc.c b/arch/sparc/kernel/apc.c index 0a20cd85fd3..4dd1ba752ce 100644 --- a/arch/sparc/kernel/apc.c +++ b/arch/sparc/kernel/apc.c @@ -182,7 +182,7 @@ static int __devinit apc_probe(struct of_device *op, return 0; } -static struct of_device_id apc_match[] = { +static struct of_device_id __initdata apc_match[] = { { .name = APC_OBPNAME, }, diff --git a/arch/sparc/kernel/pmc.c b/arch/sparc/kernel/pmc.c index 9976e82e323..814eb3ce039 100644 --- a/arch/sparc/kernel/pmc.c +++ b/arch/sparc/kernel/pmc.c @@ -72,7 +72,7 @@ static int __devinit pmc_probe(struct of_device *op, return 0; } -static struct of_device_id pmc_match[] = { +static struct of_device_id __initdata pmc_match[] = { { .name = PMC_OBPNAME, }, diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index a713bb43db8..339c4762fbc 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -338,7 +338,7 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id return 0; } -static struct of_device_id clock_match[] = { +static struct of_device_id __initdata clock_match[] = { { .name = "eeprom", }, diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c index dd5c7bf8761..858beda8652 100644 --- a/arch/sparc64/kernel/auxio.c +++ b/arch/sparc64/kernel/auxio.c @@ -109,7 +109,7 @@ void auxio_set_lte(int on) } } -static struct of_device_id auxio_match[] = { +static struct of_device_id __initdata auxio_match[] = { { .name = "auxio", }, diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c index 3e14952e940..2ed401087ca 100644 --- a/arch/sparc64/kernel/chmc.c +++ b/arch/sparc64/kernel/chmc.c @@ -801,7 +801,7 @@ static int __devexit us3mc_remove(struct of_device *op) return 0; } -static struct of_device_id us3mc_match[] = { +static const struct of_device_id us3mc_match[] = { { .name = "memory-controller", }, diff --git a/arch/sparc64/kernel/pci_fire.c b/arch/sparc64/kernel/pci_fire.c index adc3fe44b08..477928aad53 100644 --- a/arch/sparc64/kernel/pci_fire.c +++ b/arch/sparc64/kernel/pci_fire.c @@ -547,7 +547,7 @@ out_free: return err; } -static struct of_device_id fire_match[] = { +static struct of_device_id __initdata fire_match[] = { { .name = "pci", .compatible = "pciex108e,80f0", diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index 4e8f87aad20..708212a6e7e 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -1099,7 +1099,7 @@ out_free: return err; } -static struct of_device_id psycho_match[] = { +static struct of_device_id __initdata psycho_match[] = { { .name = "pci", .compatible = "pci108e,8000", diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index 7cce4d8f4aa..cc476e9a275 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -885,7 +885,7 @@ out_free: return err; } -static struct of_device_id sabre_match[] = { +static struct of_device_id __initdata sabre_match[] = { { .name = "pci", .compatible = "pci108e,a001", diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c index b95dd548583..18fdd887b4a 100644 --- a/arch/sparc64/kernel/pci_schizo.c +++ b/arch/sparc64/kernel/pci_schizo.c @@ -1504,7 +1504,7 @@ static int __devinit schizo_probe(struct of_device *op, * and pci108e,8001. So list the chips in reverse chronological * order. */ -static struct of_device_id schizo_match[] = { +static struct of_device_id __initdata schizo_match[] = { { .name = "pci", .compatible = "pci108e,a801", diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index c1e72beade2..fea51a054be 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c @@ -1026,7 +1026,7 @@ out_free: return -ENOMEM; } -static struct of_device_id pci_sun4v_match[] = { +static struct of_device_id __initdata pci_sun4v_match[] = { { .name = "pci", .compatible = "SUNW,sun4v-pci", diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c index 7536255ab57..7559ad395a3 100644 --- a/arch/sparc64/kernel/power.c +++ b/arch/sparc64/kernel/power.c @@ -89,7 +89,7 @@ static int __devinit power_probe(struct of_device *op, const struct of_device_id return 0; } -static struct of_device_id power_match[] = { +static struct of_device_id __initdata power_match[] = { { .name = "power", }, diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index ea05038a8c1..209e7d28c3a 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -438,7 +438,7 @@ static int __devinit rtc_probe(struct of_device *op, const struct of_device_id * return platform_device_register(&rtc_cmos_device); } -static struct of_device_id rtc_match[] = { +static struct of_device_id __initdata rtc_match[] = { { .name = "rtc", .compatible = "m5819", @@ -482,7 +482,7 @@ static int __devinit bq4802_probe(struct of_device *op, const struct of_device_i return platform_device_register(&rtc_bq4802_device); } -static struct of_device_id bq4802_match[] = { +static struct of_device_id __initdata bq4802_match[] = { { .name = "rtc", .compatible = "bq4802", @@ -566,7 +566,7 @@ static int __devinit mostek_probe(struct of_device *op, const struct of_device_i return platform_device_register(&m48t59_rtc); } -static struct of_device_id mostek_match[] = { +static struct of_device_id __initdata mostek_match[] = { { .name = "eeprom", }, diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index c2fa9fdc5d3..937c9c0ef4c 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -2685,7 +2685,7 @@ static int __devexit fore200e_sba_remove(struct of_device *op) return 0; } -static struct of_device_id fore200e_sba_match[] = { +static const struct of_device_id fore200e_sba_match[] = { { .name = SBA200E_PROM_NAME, .data = (void *) &fore200e_bus[1], diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 5220f541df2..8859aeac2d2 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -736,7 +736,7 @@ static int __devexit n2rng_remove(struct of_device *op) return 0; } -static struct of_device_id n2rng_match[] = { +static const struct of_device_id n2rng_match[] = { { .name = "random-number-generator", .compatible = "SUNW,n2-rng", diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c index 9aec95cbf7e..68e90abeba9 100644 --- a/drivers/hwmon/ultra45_env.c +++ b/drivers/hwmon/ultra45_env.c @@ -290,7 +290,7 @@ static int __devexit env_remove(struct of_device *op) return 0; } -static struct of_device_id env_match[] = { +static const struct of_device_id env_match[] = { { .name = "env-monitor", .compatible = "SUNW,ebus-pic16f747-env", diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index d8765cc93d2..c4f42311fde 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -249,7 +249,7 @@ static int bbc_remove(struct of_device *op) return 0; } -static struct of_device_id bbc_beep_match[] = { +static const struct of_device_id bbc_beep_match[] = { { .name = "beep", .compatible = "SUNW,bbc-beep", @@ -328,7 +328,7 @@ static int grover_remove(struct of_device *op) return 0; } -static struct of_device_id grover_beep_match[] = { +static const struct of_device_id grover_beep_match[] = { { .name = "beep", .compatible = "SUNW,smbus-beep", diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index 692a79ec2a2..5071af2c060 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -87,7 +87,7 @@ static int __devexit sparc_i8042_remove(struct of_device *op) return 0; } -static struct of_device_id sparc_i8042_match[] = { +static const struct of_device_id sparc_i8042_match[] = { { .name = "8042", }, diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index e931f1df0af..fd7a1017399 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -138,7 +138,7 @@ static int __devexit uflash_remove(struct of_device *op) return 0; } -static struct of_device_id uflash_match[] = { +static const struct of_device_id uflash_match[] = { { .name = UFLASH_OBPNAME, }, diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c index 03867b10f37..979d778b133 100644 --- a/drivers/net/myri_sbus.c +++ b/drivers/net/myri_sbus.c @@ -1125,7 +1125,7 @@ static int __devexit myri_sbus_remove(struct of_device *op) return 0; } -static struct of_device_id myri_sbus_match[] = { +static const struct of_device_id myri_sbus_match[] = { { .name = "MYRICOM,mlanai", }, diff --git a/drivers/net/niu.c b/drivers/net/niu.c index e4765b713ab..016d9e08692 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -9074,7 +9074,7 @@ static int __devexit niu_of_remove(struct of_device *op) return 0; } -static struct of_device_id niu_match[] = { +static const struct of_device_id niu_match[] = { { .name = "network", .compatible = "SUNW,niusl", diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index 7009a5e36c9..3f342b35a77 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -1281,7 +1281,7 @@ static int __devexit bigmac_sbus_remove(struct of_device *op) return 0; } -static struct of_device_id bigmac_sbus_match[] = { +static const struct of_device_id bigmac_sbus_match[] = { { .name = "be", }, diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index dc46e97ac12..f1ebeb5f65b 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -3252,7 +3252,7 @@ static int __devexit hme_sbus_remove(struct of_device *op) return 0; } -static struct of_device_id hme_sbus_match[] = { +static const struct of_device_id hme_sbus_match[] = { { .name = "SUNW,hme", }, diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index 292feb2154b..30cdb81853b 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -1571,7 +1571,7 @@ static int __devexit sunlance_sbus_remove(struct of_device *op) return 0; } -static struct of_device_id sunlance_sbus_match[] = { +static const struct of_device_id sunlance_sbus_match[] = { { .name = "le", }, diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index 81604cac8e3..f63644744ff 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -965,7 +965,7 @@ static int __devexit qec_sbus_remove(struct of_device *op) return 0; } -static struct of_device_id qec_sbus_match[] = { +static const struct of_device_id qec_sbus_match[] = { { .name = "qe", }, diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c index e4f00c6dfe0..065f229580d 100644 --- a/drivers/parport/parport_sunbpp.c +++ b/drivers/parport/parport_sunbpp.c @@ -372,7 +372,7 @@ static int __devexit bpp_remove(struct of_device *op) return 0; } -static struct of_device_id bpp_match[] = { +static const struct of_device_id bpp_match[] = { { .name = "SUNW,bpp", }, diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c index af7f4af6c5f..f08e169ba1b 100644 --- a/drivers/sbus/char/bbc_i2c.c +++ b/drivers/sbus/char/bbc_i2c.c @@ -404,7 +404,7 @@ static int __devexit bbc_i2c_remove(struct of_device *op) return 0; } -static struct of_device_id bbc_i2c_match[] = { +static const struct of_device_id bbc_i2c_match[] = { { .name = "i2c", .compatible = "SUNW,bbc-i2c", diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index 2f16d78e92d..2550af4ae43 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -256,7 +256,7 @@ static int __devexit d7s_remove(struct of_device *op) return 0; } -static struct of_device_id d7s_match[] = { +static const struct of_device_id d7s_match[] = { { .name = "display7seg", }, diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index ea8c35cbffd..58e583b61e6 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -1120,7 +1120,7 @@ static int __devexit envctrl_remove(struct of_device *op) return 0; } -static struct of_device_id envctrl_match[] = { +static const struct of_device_id envctrl_match[] = { { .name = "i2c", .compatible = "i2cpcf,8584", diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index 715996f5c53..41083472ff4 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -199,7 +199,7 @@ static int __devexit flash_remove(struct of_device *op) return 0; } -static struct of_device_id flash_match[] = { +static const struct of_device_id flash_match[] = { { .name = "flashprom", }, diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 6cff9777bbc..27993c37775 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -417,7 +417,7 @@ static int __devexit uctrl_remove(struct of_device *op) return 0; } -static struct of_device_id uctrl_match[] = { +static const struct of_device_id uctrl_match[] = { { .name = "uctrl", }, diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index b6ce82d3de3..42807671512 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1415,7 +1415,7 @@ static int __devexit qpti_sbus_remove(struct of_device *op) return 0; } -static struct of_device_id qpti_match[] = { +static const struct of_device_id qpti_match[] = { { .name = "ptisp", .data = &qpti_template, diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c index 97316ca28a7..3d73aad4bc8 100644 --- a/drivers/scsi/sun_esp.c +++ b/drivers/scsi/sun_esp.c @@ -617,7 +617,7 @@ static int __devexit esp_sbus_remove(struct of_device *op) return 0; } -static struct of_device_id esp_match[] = { +static const struct of_device_id esp_match[] = { { .name = "SUNW,esp", }, diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index e41766d0803..a94a2ab4b57 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c @@ -616,7 +616,7 @@ static int __devexit hv_remove(struct of_device *dev) return 0; } -static struct of_device_id hv_match[] = { +static const struct of_device_id hv_match[] = { { .name = "console", .compatible = "qcn", diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index 29b4458abf7..0355efe115d 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -1078,7 +1078,7 @@ static int __devexit sab_remove(struct of_device *op) return 0; } -static struct of_device_id sab_match[] = { +static const struct of_device_id sab_match[] = { { .name = "se", }, diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index a378464f929..a4dc79b1d7a 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -1506,7 +1506,7 @@ static int __devexit su_remove(struct of_device *op) return 0; } -static struct of_device_id su_match[] = { +static const struct of_device_id su_match[] = { { .name = "su", }, diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index 3cb4c8aee13..45a299f3561 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -1480,7 +1480,7 @@ static int __devexit zs_remove(struct of_device *op) return 0; } -static struct of_device_id zs_match[] = { +static const struct of_device_id zs_match[] = { { .name = "zs", }, diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index e721644bad7..1e35ba6f18e 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c @@ -372,7 +372,7 @@ static int __devexit bw2_remove(struct of_device *op) return 0; } -static struct of_device_id bw2_match[] = { +static const struct of_device_id bw2_match[] = { { .name = "bwtwo", }, diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c index b17e7467177..a2d1882791a 100644 --- a/drivers/video/cg14.c +++ b/drivers/video/cg14.c @@ -589,7 +589,7 @@ static int __devexit cg14_remove(struct of_device *op) return 0; } -static struct of_device_id cg14_match[] = { +static const struct of_device_id cg14_match[] = { { .name = "cgfourteen", }, diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index 3aa7b6cb026..99f87fb61d0 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c @@ -456,7 +456,7 @@ static int __devexit cg3_remove(struct of_device *op) return 0; } -static struct of_device_id cg3_match[] = { +static const struct of_device_id cg3_match[] = { { .name = "cgthree", }, diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c index 2f64bb3bd25..9eaa63ab08f 100644 --- a/drivers/video/cg6.c +++ b/drivers/video/cg6.c @@ -814,7 +814,7 @@ static int __devexit cg6_remove(struct of_device *op) return 0; } -static struct of_device_id cg6_match[] = { +static const struct of_device_id cg6_match[] = { { .name = "cgsix", }, diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c index 7992b13ee68..9dbb9646081 100644 --- a/drivers/video/ffb.c +++ b/drivers/video/ffb.c @@ -1042,7 +1042,7 @@ static int __devexit ffb_remove(struct of_device *op) return 0; } -static struct of_device_id ffb_match[] = { +static const struct of_device_id ffb_match[] = { { .name = "SUNW,ffb", }, diff --git a/drivers/video/leo.c b/drivers/video/leo.c index 13fea61d6ae..465459e5eae 100644 --- a/drivers/video/leo.c +++ b/drivers/video/leo.c @@ -641,7 +641,7 @@ static int __devexit leo_remove(struct of_device *op) return 0; } -static struct of_device_id leo_match[] = { +static const struct of_device_id leo_match[] = { { .name = "SUNW,leo", }, diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c index 9e903454ffc..7000f2cd585 100644 --- a/drivers/video/p9100.c +++ b/drivers/video/p9100.c @@ -349,7 +349,7 @@ static int __devexit p9100_remove(struct of_device *op) return 0; } -static struct of_device_id p9100_match[] = { +static const struct of_device_id p9100_match[] = { { .name = "p9100", }, diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c index 2a03f78bbb0..643afbfe827 100644 --- a/drivers/video/tcx.c +++ b/drivers/video/tcx.c @@ -505,7 +505,7 @@ static int __devexit tcx_remove(struct of_device *op) return 0; } -static struct of_device_id tcx_match[] = { +static const struct of_device_id tcx_match[] = { { .name = "SUNW,tcx", }, diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index 1f1ee2520f1..084dfe9cecf 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -666,7 +666,7 @@ static int __devexit cpwd_remove(struct of_device *op) return 0; } -static struct of_device_id cpwd_match[] = { +static const struct of_device_id cpwd_match[] = { { .name = "watchdog", }, diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index 04fd1bac2db..09cb1833ea2 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -230,7 +230,7 @@ static int __devexit riowd_remove(struct of_device *op) return 0; } -static struct of_device_id riowd_match[] = { +static const struct of_device_id riowd_match[] = { { .name = "pmc", }, diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c index 0f82c3f6cfd..5af5503edad 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -1070,7 +1070,7 @@ out_err: return err; } -static struct of_device_id amd7930_match[] = { +static const struct of_device_id amd7930_match[] = { { .name = "audio", }, diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index cdbfae96bd8..727438d276e 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -2094,7 +2094,7 @@ static int __devexit cs4231_remove(struct of_device *op) return 0; } -static struct of_device_id cs4231_match[] = { +static const struct of_device_id cs4231_match[] = { { .name = "SUNW,CS4231", }, diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 446f985b76f..2edb0ad3de7 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -2665,7 +2665,7 @@ static int __devexit dbri_remove(struct of_device *op) return 0; } -static struct of_device_id dbri_match[] = { +static const struct of_device_id dbri_match[] = { { .name = "SUNW,DBRIe", }, -- cgit v1.2.3-70-g09d2 From 55b951e7e6b9f983286c40925e340124d79bb0f7 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 29 Aug 2008 04:26:48 +0800 Subject: hwmon: add max1111 Low-power Multichannel Serial 8-bit ADCs Driver based on corgi_ssp.c and sharpsl_pm.c, previously done by Richard Purdie and many others. Now changed to generic HWMON device and expose all the ADC input value through sysfs. Signed-off-by: Eric Miao Acked-by: Jean Delvare Signed-off-by: Russell King --- drivers/hwmon/Kconfig | 9 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/max1111.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 drivers/hwmon/max1111.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d402e8d813c..3309e862f31 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -540,6 +540,15 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_MAX1111 + tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip" + depends on SPI_MASTER + help + Say y here to support Maxim's MAX1111 ADC chips. + + This driver can also be built as a module. If so, the module + will be called max1111. + config SENSORS_MAX1619 tristate "Maxim MAX1619 sensor chip" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 950134ab842..6babc801b34 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o +obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c new file mode 100644 index 00000000000..12c05774a1c --- /dev/null +++ b/drivers/hwmon/max1111.c @@ -0,0 +1,231 @@ +/* + * max1111.c - +2.7V, Low-Power, Multichannel, Serial 8-bit ADCs + * + * Based on arch/arm/mach-pxa/corgi_ssp.c + * + * Copyright (C) 2004-2005 Richard Purdie + * + * Copyright (C) 2008 Marvell International Ltd. + * Eric Miao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * publishhed by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX1111_TX_BUF_SIZE 1 +#define MAX1111_RX_BUF_SIZE 2 + +/* MAX1111 Commands */ +#define MAX1111_CTRL_PD0 (1u << 0) +#define MAX1111_CTRL_PD1 (1u << 1) +#define MAX1111_CTRL_SGL (1u << 2) +#define MAX1111_CTRL_UNI (1u << 3) +#define MAX1111_CTRL_SEL_SH (5) /* NOTE: bit 4 is ignored */ +#define MAX1111_CTRL_STR (1u << 7) + +struct max1111_data { + struct spi_device *spi; + struct device *hwmon_dev; + struct spi_message msg; + struct spi_transfer xfer[2]; + uint8_t *tx_buf; + uint8_t *rx_buf; +}; + +static int max1111_read(struct device *dev, int channel) +{ + struct max1111_data *data = dev_get_drvdata(dev); + uint8_t v1, v2; + int err; + + data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) | + MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 | + MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR; + + err = spi_sync(data->spi, &data->msg); + if (err < 0) { + dev_err(dev, "spi_sync failed with %d\n", err); + return err; + } + + v1 = data->rx_buf[0]; + v2 = data->rx_buf[1]; + + if ((v1 & 0xc0) || (v2 & 0x3f)) + return -EINVAL; + + return (v1 << 2) | (v2 >> 6); +} + +/* + * NOTE: SPI devices do not have a default 'name' attribute, which is + * likely to be used by hwmon applications to distinguish between + * different devices, explicitly add a name attribute here. + */ +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "max1111\n"); +} + +static ssize_t show_adc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int channel = to_sensor_dev_attr(attr)->index; + int ret; + + ret = max1111_read(dev, channel); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + +#define MAX1111_ADC_ATTR(_id) \ + SENSOR_DEVICE_ATTR(adc##_id##_in, S_IRUGO, show_adc, NULL, _id) + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static MAX1111_ADC_ATTR(0); +static MAX1111_ADC_ATTR(1); +static MAX1111_ADC_ATTR(2); +static MAX1111_ADC_ATTR(3); + +static struct attribute *max1111_attributes[] = { + &dev_attr_name.attr, + &sensor_dev_attr_adc0_in.dev_attr.attr, + &sensor_dev_attr_adc1_in.dev_attr.attr, + &sensor_dev_attr_adc2_in.dev_attr.attr, + &sensor_dev_attr_adc3_in.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max1111_attr_group = { + .attrs = max1111_attributes, +}; + +static int setup_transfer(struct max1111_data *data) +{ + struct spi_message *m; + struct spi_transfer *x; + + data->tx_buf = kmalloc(MAX1111_TX_BUF_SIZE, GFP_KERNEL); + if (!data->tx_buf) + return -ENOMEM; + + data->rx_buf = kmalloc(MAX1111_RX_BUF_SIZE, GFP_KERNEL); + if (!data->rx_buf) { + kfree(data->tx_buf); + return -ENOMEM; + } + + m = &data->msg; + x = &data->xfer[0]; + + spi_message_init(m); + + x->tx_buf = &data->tx_buf[0]; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &data->rx_buf[0]; + x->len = 2; + spi_message_add_tail(x, m); + + return 0; +} + +static int __devinit max1111_probe(struct spi_device *spi) +{ + struct max1111_data *data; + int err; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + err = spi_setup(spi); + if (err < 0) + return err; + + data = kzalloc(sizeof(struct max1111_data), GFP_KERNEL); + if (data == NULL) { + dev_err(&spi->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + err = setup_transfer(data); + if (err) + goto err_free_data; + + data->spi = spi; + spi_set_drvdata(spi, data); + + 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; + } + + data->hwmon_dev = hwmon_device_register(&spi->dev); + if (IS_ERR(data->hwmon_dev)) { + dev_err(&spi->dev, "failed to create hwmon device\n"); + err = PTR_ERR(data->hwmon_dev); + goto err_remove; + } + + return 0; + +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; +} + +static int __devexit max1111_remove(struct spi_device *spi) +{ + struct max1111_data *data = spi_get_drvdata(spi); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); + kfree(data->rx_buf); + kfree(data->tx_buf); + kfree(data); + return 0; +} + +static struct spi_driver max1111_driver = { + .driver = { + .name = "max1111", + .owner = THIS_MODULE, + }, + .probe = max1111_probe, + .remove = __devexit_p(max1111_remove), +}; + +static int __init max1111_init(void) +{ + return spi_register_driver(&max1111_driver); +} +module_init(max1111_init); + +static void __exit max1111_exit(void) +{ + spi_unregister_driver(&max1111_driver); +} +module_exit(max1111_exit); + +MODULE_AUTHOR("Eric Miao "); +MODULE_DESCRIPTION("MAX1111 ADC Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From f16177c20c42e1bd780b35259a995f7718986dd4 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 29 Aug 2008 06:19:32 +0800 Subject: hwmon: add max1111_read_channel() for use by sharpsl_pm This is not generic, and is added here for backward compatibility. It is made an individual commit here to make it easier for revert once the sharpsl_pm gets generic enough. Signed-off-by: Eric Miao Signed-off-by: Russell King --- arch/arm/mach-pxa/sharpsl_pm.c | 9 +++++++++ drivers/hwmon/max1111.c | 13 +++++++++++++ 2 files changed, 22 insertions(+) (limited to 'drivers/hwmon') diff --git a/arch/arm/mach-pxa/sharpsl_pm.c b/arch/arm/mach-pxa/sharpsl_pm.c index e804ae09370..9427d808839 100644 --- a/arch/arm/mach-pxa/sharpsl_pm.c +++ b/arch/arm/mach-pxa/sharpsl_pm.c @@ -132,8 +132,17 @@ int sharpsl_pm_pxa_read_max1111(int channel) if (machine_is_tosa()) // Ugly, better move this function into another module return 0; +#ifdef CONFIG_SENSORS_MAX1111 + extern int max1111_read_channel(int); + + /* max1111 accepts channels from 0-3, however, + * it is encoded from 0-7 here in the code. + */ + return max1111_read_channel(channel >> 1); +#else return corgi_ssp_max1111_get((channel << MAXCTRL_SEL_SH) | MAXCTRL_PD0 | MAXCTRL_PD1 | MAXCTRL_SGL | MAXCTRL_UNI | MAXCTRL_STR); +#endif } void sharpsl_pm_pxa_init(void) diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c index 12c05774a1c..bfaa665ccf3 100644 --- a/drivers/hwmon/max1111.c +++ b/drivers/hwmon/max1111.c @@ -66,6 +66,16 @@ static int max1111_read(struct device *dev, int channel) return (v1 << 2) | (v2 >> 6); } +#ifdef CONFIG_SHARPSL_PM +static struct max1111_data *the_max1111; + +int max1111_read_channel(int channel) +{ + return max1111_read(&the_max1111->spi->dev, channel); +} +EXPORT_SYMBOL(max1111_read_channel); +#endif + /* * NOTE: SPI devices do not have a default 'name' attribute, which is * likely to be used by hwmon applications to distinguish between @@ -181,6 +191,9 @@ static int __devinit max1111_probe(struct spi_device *spi) goto err_remove; } +#ifdef CONFIG_SHARPSL_PM + the_max1111 = data; +#endif return 0; err_remove: -- cgit v1.2.3-70-g09d2 From dbc2bc251e06c83efcc8d39f1e7de12c2b1ff591 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 14 Oct 2008 17:30:04 +0200 Subject: hwmon: (dme1737) Be less i2c-centric The dme1737 driver support both LPC (ISA) and SMBus devices. At the moment it's rather i2c-centric, and LPC variants use a fake i2c_client for some operations. In a near future, i2c_client will be allocated by i2c-core rather than by the device drivers, so non-i2c drivers will not have one. As a preparation step, change the driver code to no longer assume that an i2c_client structure is always available. No functional change. Signed-off-by: Jean Delvare Cc: Juerg Haefliger --- drivers/hwmon/dme1737.c | 226 ++++++++++++++++++++++++------------------------ 1 file changed, 111 insertions(+), 115 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index cdb8311e4ef..2bf97e1470c 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -175,11 +175,12 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23}; * Data structures and manipulation thereof * --------------------------------------------------------------------- */ -/* For ISA chips, we abuse the i2c_client addr and name fields. We also use - the driver field to differentiate between I2C and ISA chips. */ struct dme1737_data { - struct i2c_client client; + struct i2c_client _client; /* will go away soon */ + struct i2c_client *client; /* for I2C devices only */ struct device *hwmon_dev; + const char *name; + unsigned int addr; /* for ISA devices only */ struct mutex update_lock; int valid; /* !=0 if following fields are valid */ @@ -512,11 +513,12 @@ static inline int PWM_OFF_TO_REG(int val, int ix, int reg) * before calling dme1737_read or dme1737_write. * --------------------------------------------------------------------- */ -static u8 dme1737_read(struct i2c_client *client, u8 reg) +static u8 dme1737_read(const struct dme1737_data *data, u8 reg) { + struct i2c_client *client = data->client; s32 val; - if (client->driver) { /* I2C device */ + if (client) { /* I2C device */ val = i2c_smbus_read_byte_data(client, reg); if (val < 0) { @@ -525,18 +527,19 @@ static u8 dme1737_read(struct i2c_client *client, u8 reg) "maintainer.\n", reg); } } else { /* ISA device */ - outb(reg, client->addr); - val = inb(client->addr + 1); + outb(reg, data->addr); + val = inb(data->addr + 1); } return val; } -static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val) +static s32 dme1737_write(const struct dme1737_data *data, u8 reg, u8 val) { + struct i2c_client *client = data->client; s32 res = 0; - if (client->driver) { /* I2C device */ + if (client) { /* I2C device */ res = i2c_smbus_write_byte_data(client, reg, val); if (res < 0) { @@ -545,8 +548,8 @@ static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val) "maintainer.\n", reg); } } else { /* ISA device */ - outb(reg, client->addr); - outb(val, client->addr + 1); + outb(reg, data->addr); + outb(val, data->addr + 1); } return res; @@ -555,7 +558,6 @@ static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val) static struct dme1737_data *dme1737_update_device(struct device *dev) { struct dme1737_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; int ix; u8 lsb[5]; @@ -563,7 +565,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Enable a Vbat monitoring cycle every 10 mins */ if (time_after(jiffies, data->last_vbat + 600 * HZ) || !data->valid) { - dme1737_write(client, DME1737_REG_CONFIG, dme1737_read(client, + dme1737_write(data, DME1737_REG_CONFIG, dme1737_read(data, DME1737_REG_CONFIG) | 0x10); data->last_vbat = jiffies; } @@ -571,7 +573,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Sample register contents every 1 sec */ if (time_after(jiffies, data->last_update + HZ) || !data->valid) { if (data->type != sch5027) { - data->vid = dme1737_read(client, DME1737_REG_VID) & + data->vid = dme1737_read(data, DME1737_REG_VID) & 0x3f; } @@ -580,11 +582,11 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) /* Voltage inputs are stored as 16 bit values even * though they have only 12 bits resolution. This is * to make it consistent with the temp inputs. */ - data->in[ix] = dme1737_read(client, + data->in[ix] = dme1737_read(data, DME1737_REG_IN(ix)) << 8; - data->in_min[ix] = dme1737_read(client, + data->in_min[ix] = dme1737_read(data, DME1737_REG_IN_MIN(ix)); - data->in_max[ix] = dme1737_read(client, + data->in_max[ix] = dme1737_read(data, DME1737_REG_IN_MAX(ix)); } @@ -595,14 +597,14 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) * to take advantage of implicit conversions between * register values (2's complement) and temp values * (signed decimal). */ - data->temp[ix] = dme1737_read(client, + data->temp[ix] = dme1737_read(data, DME1737_REG_TEMP(ix)) << 8; - data->temp_min[ix] = dme1737_read(client, + data->temp_min[ix] = dme1737_read(data, DME1737_REG_TEMP_MIN(ix)); - data->temp_max[ix] = dme1737_read(client, + data->temp_max[ix] = dme1737_read(data, DME1737_REG_TEMP_MAX(ix)); if (data->type != sch5027) { - data->temp_offset[ix] = dme1737_read(client, + data->temp_offset[ix] = dme1737_read(data, DME1737_REG_TEMP_OFFSET(ix)); } } @@ -612,7 +614,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) * which the registers are read (MSB first, then LSB) is * important! */ for (ix = 0; ix < ARRAY_SIZE(lsb); ix++) { - lsb[ix] = dme1737_read(client, + lsb[ix] = dme1737_read(data, DME1737_REG_IN_TEMP_LSB(ix)); } for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) { @@ -631,19 +633,19 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) if (!(data->has_fan & (1 << ix))) { continue; } - data->fan[ix] = dme1737_read(client, + data->fan[ix] = dme1737_read(data, DME1737_REG_FAN(ix)); - data->fan[ix] |= dme1737_read(client, + data->fan[ix] |= dme1737_read(data, DME1737_REG_FAN(ix) + 1) << 8; - data->fan_min[ix] = dme1737_read(client, + data->fan_min[ix] = dme1737_read(data, DME1737_REG_FAN_MIN(ix)); - data->fan_min[ix] |= dme1737_read(client, + data->fan_min[ix] |= dme1737_read(data, DME1737_REG_FAN_MIN(ix) + 1) << 8; - data->fan_opt[ix] = dme1737_read(client, + data->fan_opt[ix] = dme1737_read(data, DME1737_REG_FAN_OPT(ix)); /* fan_max exists only for fan[5-6] */ if (ix > 3) { - data->fan_max[ix - 4] = dme1737_read(client, + data->fan_max[ix - 4] = dme1737_read(data, DME1737_REG_FAN_MAX(ix)); } } @@ -655,63 +657,63 @@ static struct dme1737_data *dme1737_update_device(struct device *dev) if (!(data->has_pwm & (1 << ix))) { continue; } - data->pwm[ix] = dme1737_read(client, + data->pwm[ix] = dme1737_read(data, DME1737_REG_PWM(ix)); - data->pwm_freq[ix] = dme1737_read(client, + data->pwm_freq[ix] = dme1737_read(data, DME1737_REG_PWM_FREQ(ix)); /* pwm_config and pwm_min exist only for pwm[1-3] */ if (ix < 3) { - data->pwm_config[ix] = dme1737_read(client, + data->pwm_config[ix] = dme1737_read(data, DME1737_REG_PWM_CONFIG(ix)); - data->pwm_min[ix] = dme1737_read(client, + data->pwm_min[ix] = dme1737_read(data, DME1737_REG_PWM_MIN(ix)); } } for (ix = 0; ix < ARRAY_SIZE(data->pwm_rr); ix++) { - data->pwm_rr[ix] = dme1737_read(client, + data->pwm_rr[ix] = dme1737_read(data, DME1737_REG_PWM_RR(ix)); } /* Thermal zone registers */ for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) { - data->zone_low[ix] = dme1737_read(client, + data->zone_low[ix] = dme1737_read(data, DME1737_REG_ZONE_LOW(ix)); - data->zone_abs[ix] = dme1737_read(client, + data->zone_abs[ix] = dme1737_read(data, DME1737_REG_ZONE_ABS(ix)); } if (data->type != sch5027) { for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) { - data->zone_hyst[ix] = dme1737_read(client, + data->zone_hyst[ix] = dme1737_read(data, DME1737_REG_ZONE_HYST(ix)); } } /* Alarm registers */ - data->alarms = dme1737_read(client, + data->alarms = dme1737_read(data, DME1737_REG_ALARM1); /* Bit 7 tells us if the other alarm registers are non-zero and * therefore also need to be read */ if (data->alarms & 0x80) { - data->alarms |= dme1737_read(client, + data->alarms |= dme1737_read(data, DME1737_REG_ALARM2) << 8; - data->alarms |= dme1737_read(client, + data->alarms |= dme1737_read(data, DME1737_REG_ALARM3) << 16; } /* The ISA chips require explicit clearing of alarm bits. * Don't worry, an alarm will come back if the condition * that causes it still exists */ - if (!client->driver) { + if (!data->client) { if (data->alarms & 0xff0000) { - dme1737_write(client, DME1737_REG_ALARM3, + dme1737_write(data, DME1737_REG_ALARM3, 0xff); } if (data->alarms & 0xff00) { - dme1737_write(client, DME1737_REG_ALARM2, + dme1737_write(data, DME1737_REG_ALARM2, 0xff); } if (data->alarms & 0xff) { - dme1737_write(client, DME1737_REG_ALARM1, + dme1737_write(data, DME1737_REG_ALARM1, 0xff); } } @@ -770,7 +772,6 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct dme1737_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; struct sensor_device_attribute_2 *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; @@ -781,12 +782,12 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr, switch (fn) { case SYS_IN_MIN: data->in_min[ix] = IN_TO_REG(val, data->in_nominal[ix]); - dme1737_write(client, DME1737_REG_IN_MIN(ix), + dme1737_write(data, DME1737_REG_IN_MIN(ix), data->in_min[ix]); break; case SYS_IN_MAX: data->in_max[ix] = IN_TO_REG(val, data->in_nominal[ix]); - dme1737_write(client, DME1737_REG_IN_MAX(ix), + dme1737_write(data, DME1737_REG_IN_MAX(ix), data->in_max[ix]); break; default: @@ -850,7 +851,6 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct dme1737_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; struct sensor_device_attribute_2 *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; @@ -861,17 +861,17 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr, switch (fn) { case SYS_TEMP_MIN: data->temp_min[ix] = TEMP_TO_REG(val); - dme1737_write(client, DME1737_REG_TEMP_MIN(ix), + dme1737_write(data, DME1737_REG_TEMP_MIN(ix), data->temp_min[ix]); break; case SYS_TEMP_MAX: data->temp_max[ix] = TEMP_TO_REG(val); - dme1737_write(client, DME1737_REG_TEMP_MAX(ix), + dme1737_write(data, DME1737_REG_TEMP_MAX(ix), data->temp_max[ix]); break; case SYS_TEMP_OFFSET: data->temp_offset[ix] = TEMP_TO_REG(val); - dme1737_write(client, DME1737_REG_TEMP_OFFSET(ix), + dme1737_write(data, DME1737_REG_TEMP_OFFSET(ix), data->temp_offset[ix]); break; default: @@ -939,7 +939,6 @@ static ssize_t set_zone(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct dme1737_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; struct sensor_device_attribute_2 *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; @@ -950,37 +949,37 @@ static ssize_t set_zone(struct device *dev, struct device_attribute *attr, switch (fn) { case SYS_ZONE_AUTO_POINT1_TEMP_HYST: /* Refresh the cache */ - data->zone_low[ix] = dme1737_read(client, + data->zone_low[ix] = dme1737_read(data, DME1737_REG_ZONE_LOW(ix)); /* Modify the temp hyst value */ data->zone_hyst[ix == 2] = TEMP_HYST_TO_REG( TEMP_FROM_REG(data->zone_low[ix], 8) - - val, ix, dme1737_read(client, + val, ix, dme1737_read(data, DME1737_REG_ZONE_HYST(ix == 2))); - dme1737_write(client, DME1737_REG_ZONE_HYST(ix == 2), + dme1737_write(data, DME1737_REG_ZONE_HYST(ix == 2), data->zone_hyst[ix == 2]); break; case SYS_ZONE_AUTO_POINT1_TEMP: data->zone_low[ix] = TEMP_TO_REG(val); - dme1737_write(client, DME1737_REG_ZONE_LOW(ix), + dme1737_write(data, DME1737_REG_ZONE_LOW(ix), data->zone_low[ix]); break; case SYS_ZONE_AUTO_POINT2_TEMP: /* Refresh the cache */ - data->zone_low[ix] = dme1737_read(client, + data->zone_low[ix] = dme1737_read(data, DME1737_REG_ZONE_LOW(ix)); /* Modify the temp range value (which is stored in the upper * nibble of the pwm_freq register) */ data->pwm_freq[ix] = TEMP_RANGE_TO_REG(val - TEMP_FROM_REG(data->zone_low[ix], 8), - dme1737_read(client, + dme1737_read(data, DME1737_REG_PWM_FREQ(ix))); - dme1737_write(client, DME1737_REG_PWM_FREQ(ix), + dme1737_write(data, DME1737_REG_PWM_FREQ(ix), data->pwm_freq[ix]); break; case SYS_ZONE_AUTO_POINT3_TEMP: data->zone_abs[ix] = TEMP_TO_REG(val); - dme1737_write(client, DME1737_REG_ZONE_ABS(ix), + dme1737_write(data, DME1737_REG_ZONE_ABS(ix), data->zone_abs[ix]); break; default: @@ -1046,7 +1045,6 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct dme1737_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; struct sensor_device_attribute_2 *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; @@ -1060,21 +1058,21 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, data->fan_min[ix] = FAN_TO_REG(val, 0); } else { /* Refresh the cache */ - data->fan_opt[ix] = dme1737_read(client, + data->fan_opt[ix] = dme1737_read(data, DME1737_REG_FAN_OPT(ix)); /* Modify the fan min value */ data->fan_min[ix] = FAN_TO_REG(val, FAN_TPC_FROM_REG(data->fan_opt[ix])); } - dme1737_write(client, DME1737_REG_FAN_MIN(ix), + dme1737_write(data, DME1737_REG_FAN_MIN(ix), data->fan_min[ix] & 0xff); - dme1737_write(client, DME1737_REG_FAN_MIN(ix) + 1, + dme1737_write(data, DME1737_REG_FAN_MIN(ix) + 1, data->fan_min[ix] >> 8); break; case SYS_FAN_MAX: /* Only valid for fan[5-6] */ data->fan_max[ix - 4] = FAN_MAX_TO_REG(val); - dme1737_write(client, DME1737_REG_FAN_MAX(ix), + dme1737_write(data, DME1737_REG_FAN_MAX(ix), data->fan_max[ix - 4]); break; case SYS_FAN_TYPE: @@ -1086,9 +1084,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, val); goto exit; } - data->fan_opt[ix] = FAN_TYPE_TO_REG(val, dme1737_read(client, + data->fan_opt[ix] = FAN_TYPE_TO_REG(val, dme1737_read(data, DME1737_REG_FAN_OPT(ix))); - dme1737_write(client, DME1737_REG_FAN_OPT(ix), + dme1737_write(data, DME1737_REG_FAN_OPT(ix), data->fan_opt[ix]); break; default: @@ -1185,7 +1183,6 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct dme1737_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; struct sensor_device_attribute_2 *sensor_attr_2 = to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; @@ -1196,12 +1193,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, switch (fn) { case SYS_PWM: data->pwm[ix] = SENSORS_LIMIT(val, 0, 255); - dme1737_write(client, DME1737_REG_PWM(ix), data->pwm[ix]); + dme1737_write(data, DME1737_REG_PWM(ix), data->pwm[ix]); break; case SYS_PWM_FREQ: - data->pwm_freq[ix] = PWM_FREQ_TO_REG(val, dme1737_read(client, + data->pwm_freq[ix] = PWM_FREQ_TO_REG(val, dme1737_read(data, DME1737_REG_PWM_FREQ(ix))); - dme1737_write(client, DME1737_REG_PWM_FREQ(ix), + dme1737_write(data, DME1737_REG_PWM_FREQ(ix), data->pwm_freq[ix]); break; case SYS_PWM_ENABLE: @@ -1214,7 +1211,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, goto exit; } /* Refresh the cache */ - data->pwm_config[ix] = dme1737_read(client, + data->pwm_config[ix] = dme1737_read(data, DME1737_REG_PWM_CONFIG(ix)); if (val == PWM_EN_FROM_REG(data->pwm_config[ix])) { /* Bail out if no change */ @@ -1226,14 +1223,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, data->pwm_acz[ix] = PWM_ACZ_FROM_REG( data->pwm_config[ix]); /* Save the current ramp rate state and disable it */ - data->pwm_rr[ix > 0] = dme1737_read(client, + data->pwm_rr[ix > 0] = dme1737_read(data, DME1737_REG_PWM_RR(ix > 0)); data->pwm_rr_en &= ~(1 << ix); if (PWM_RR_EN_FROM_REG(data->pwm_rr[ix > 0], ix)) { data->pwm_rr_en |= (1 << ix); data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(0, ix, data->pwm_rr[ix > 0]); - dme1737_write(client, + dme1737_write(data, DME1737_REG_PWM_RR(ix > 0), data->pwm_rr[ix > 0]); } @@ -1247,14 +1244,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, /* Turn fan fully on */ data->pwm_config[ix] = PWM_EN_TO_REG(0, data->pwm_config[ix]); - dme1737_write(client, DME1737_REG_PWM_CONFIG(ix), + dme1737_write(data, DME1737_REG_PWM_CONFIG(ix), data->pwm_config[ix]); break; case 1: /* Turn on manual mode */ data->pwm_config[ix] = PWM_EN_TO_REG(1, data->pwm_config[ix]); - dme1737_write(client, DME1737_REG_PWM_CONFIG(ix), + dme1737_write(data, DME1737_REG_PWM_CONFIG(ix), data->pwm_config[ix]); /* Change permissions of pwm[ix] to read-writeable */ dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix], @@ -1269,14 +1266,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, data->pwm_config[ix] = PWM_ACZ_TO_REG( data->pwm_acz[ix], data->pwm_config[ix]); - dme1737_write(client, DME1737_REG_PWM_CONFIG(ix), + dme1737_write(data, DME1737_REG_PWM_CONFIG(ix), data->pwm_config[ix]); /* Enable PWM ramp rate if previously enabled */ if (data->pwm_rr_en & (1 << ix)) { data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(1, ix, - dme1737_read(client, + dme1737_read(data, DME1737_REG_PWM_RR(ix > 0))); - dme1737_write(client, + dme1737_write(data, DME1737_REG_PWM_RR(ix > 0), data->pwm_rr[ix > 0]); } @@ -1286,9 +1283,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, case SYS_PWM_RAMP_RATE: /* Only valid for pwm[1-3] */ /* Refresh the cache */ - data->pwm_config[ix] = dme1737_read(client, + data->pwm_config[ix] = dme1737_read(data, DME1737_REG_PWM_CONFIG(ix)); - data->pwm_rr[ix > 0] = dme1737_read(client, + data->pwm_rr[ix > 0] = dme1737_read(data, DME1737_REG_PWM_RR(ix > 0)); /* Set the ramp rate value */ if (val > 0) { @@ -1301,7 +1298,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(val > 0, ix, data->pwm_rr[ix > 0]); } - dme1737_write(client, DME1737_REG_PWM_RR(ix > 0), + dme1737_write(data, DME1737_REG_PWM_RR(ix > 0), data->pwm_rr[ix > 0]); break; case SYS_PWM_AUTO_CHANNELS_ZONE: @@ -1315,14 +1312,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, goto exit; } /* Refresh the cache */ - data->pwm_config[ix] = dme1737_read(client, + data->pwm_config[ix] = dme1737_read(data, DME1737_REG_PWM_CONFIG(ix)); if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) { /* PWM is already in auto mode so update the temp * channel assignment */ data->pwm_config[ix] = PWM_ACZ_TO_REG(val, data->pwm_config[ix]); - dme1737_write(client, DME1737_REG_PWM_CONFIG(ix), + dme1737_write(data, DME1737_REG_PWM_CONFIG(ix), data->pwm_config[ix]); } else { /* PWM is not in auto mode so we save the temp @@ -1333,7 +1330,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, case SYS_PWM_AUTO_PWM_MIN: /* Only valid for pwm[1-3] */ /* Refresh the cache */ - data->pwm_min[ix] = dme1737_read(client, + data->pwm_min[ix] = dme1737_read(data, DME1737_REG_PWM_MIN(ix)); /* There are only 2 values supported for the auto_pwm_min * value: 0 or auto_point1_pwm. So if the temperature drops @@ -1341,20 +1338,20 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, * off or runs at auto_point1_pwm duty-cycle. */ if (val > ((data->pwm_min[ix] + 1) / 2)) { data->pwm_rr[0] = PWM_OFF_TO_REG(1, ix, - dme1737_read(client, + dme1737_read(data, DME1737_REG_PWM_RR(0))); } else { data->pwm_rr[0] = PWM_OFF_TO_REG(0, ix, - dme1737_read(client, + dme1737_read(data, DME1737_REG_PWM_RR(0))); } - dme1737_write(client, DME1737_REG_PWM_RR(0), + dme1737_write(data, DME1737_REG_PWM_RR(0), data->pwm_rr[0]); break; case SYS_PWM_AUTO_POINT1_PWM: /* Only valid for pwm[1-3] */ data->pwm_min[ix] = SENSORS_LIMIT(val, 0, 255); - dme1737_write(client, DME1737_REG_PWM_MIN(ix), + dme1737_write(data, DME1737_REG_PWM_MIN(ix), data->pwm_min[ix]); break; default: @@ -1402,7 +1399,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute *attr, { struct dme1737_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); + return sprintf(buf, "%s\n", data->name); } /* --------------------------------------------------------------------- @@ -1908,7 +1905,7 @@ static void dme1737_remove_files(struct device *dev) sysfs_remove_group(&dev->kobj, &dme1737_group); - if (!data->client.driver) { + if (!data->client) { sysfs_remove_file(&dev->kobj, &dev_attr_name.attr); } } @@ -1919,7 +1916,7 @@ static int dme1737_create_files(struct device *dev) int err, ix; /* Create a name attribute for ISA devices */ - if (!data->client.driver && + if (!data->client && (err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr))) { goto exit; } @@ -2013,14 +2010,14 @@ exit: static int dme1737_init_device(struct device *dev) { struct dme1737_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int ix; u8 reg; /* Point to the right nominal voltages array */ data->in_nominal = IN_NOMINAL(data->type); - data->config = dme1737_read(client, DME1737_REG_CONFIG); + data->config = dme1737_read(data, DME1737_REG_CONFIG); /* Inform if part is not monitoring/started */ if (!(data->config & 0x01)) { if (!force_start) { @@ -2032,7 +2029,7 @@ static int dme1737_init_device(struct device *dev) /* Force monitoring */ data->config |= 0x01; - dme1737_write(client, DME1737_REG_CONFIG, data->config); + dme1737_write(data, DME1737_REG_CONFIG, data->config); } /* Inform if part is not ready */ if (!(data->config & 0x04)) { @@ -2041,8 +2038,8 @@ static int dme1737_init_device(struct device *dev) } /* Determine which optional fan and pwm features are enabled/present */ - if (client->driver) { /* I2C chip */ - data->config2 = dme1737_read(client, DME1737_REG_CONFIG2); + if (client) { /* I2C chip */ + data->config2 = dme1737_read(data, DME1737_REG_CONFIG2); /* Check if optional fan3 input is enabled */ if (data->config2 & 0x04) { data->has_fan |= (1 << 2); @@ -2051,7 +2048,7 @@ static int dme1737_init_device(struct device *dev) /* Fan4 and pwm3 are only available if the client's I2C address * is the default 0x2e. Otherwise the I/Os associated with * these functions are used for addr enable/select. */ - if (data->client.addr == 0x2e) { + if (client->addr == 0x2e) { data->has_fan |= (1 << 3); data->has_pwm |= (1 << 2); } @@ -2086,16 +2083,16 @@ static int dme1737_init_device(struct device *dev) (data->has_fan & (1 << 4)) ? "yes" : "no", (data->has_fan & (1 << 5)) ? "yes" : "no"); - reg = dme1737_read(client, DME1737_REG_TACH_PWM); + reg = dme1737_read(data, DME1737_REG_TACH_PWM); /* Inform if fan-to-pwm mapping differs from the default */ - if (client->driver && reg != 0xa4) { /* I2C chip */ + if (client && reg != 0xa4) { /* I2C chip */ dev_warn(dev, "Non-standard fan to pwm mapping: " "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, " "fan4->pwm%d. Please report to the driver " "maintainer.\n", (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1, ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1); - } else if (!client->driver && reg != 0x24) { /* ISA chip */ + } else if (!client && reg != 0x24) { /* ISA chip */ dev_warn(dev, "Non-standard fan to pwm mapping: " "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. " "Please report to the driver maintainer.\n", @@ -2108,7 +2105,7 @@ static int dme1737_init_device(struct device *dev) * disabled). */ if (!(data->config & 0x02)) { for (ix = 0; ix < 3; ix++) { - data->pwm_config[ix] = dme1737_read(client, + data->pwm_config[ix] = dme1737_read(data, DME1737_REG_PWM_CONFIG(ix)); if ((data->has_pwm & (1 << ix)) && (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) { @@ -2116,8 +2113,8 @@ static int dme1737_init_device(struct device *dev) "manual mode.\n", ix + 1); data->pwm_config[ix] = PWM_EN_TO_REG(1, data->pwm_config[ix]); - dme1737_write(client, DME1737_REG_PWM(ix), 0); - dme1737_write(client, + dme1737_write(data, DME1737_REG_PWM(ix), 0); + dme1737_write(data, DME1737_REG_PWM_CONFIG(ix), data->pwm_config[ix]); } @@ -2210,7 +2207,8 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address, goto exit; } - client = &data->client; + client = &data->_client; + data->client = client; i2c_set_clientdata(client, data); client->addr = address; client->adapter = adapter; @@ -2220,8 +2218,8 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address, /* A negative kind means that the driver was loaded with no force * parameter (default), so we must identify the chip. */ if (kind < 0) { - company = dme1737_read(client, DME1737_REG_COMPANY); - verstep = dme1737_read(client, DME1737_REG_VERSTEP); + company = dme1737_read(data, DME1737_REG_COMPANY); + verstep = dme1737_read(data, DME1737_REG_VERSTEP); if (company == DME1737_COMPANY_SMSC && (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) { @@ -2246,6 +2244,7 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address, /* Fill in the remaining client fields and put it into the global * list */ strlcpy(client->name, name, I2C_NAME_SIZE); + data->name = client->name; mutex_init(&data->update_lock); /* Tell the I2C layer a new client has arrived */ @@ -2403,7 +2402,6 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev) { u8 company, device; struct resource *res; - struct i2c_client *client; struct dme1737_data *data; struct device *dev = &pdev->dev; int err; @@ -2422,15 +2420,13 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev) goto exit_release_region; } - client = &data->client; - i2c_set_clientdata(client, data); - client->addr = res->start; + data->addr = res->start; platform_set_drvdata(pdev, data); /* Skip chip detection if module is loaded with force_id parameter */ if (!force_id) { - company = dme1737_read(client, DME1737_REG_COMPANY); - device = dme1737_read(client, DME1737_REG_DEVICE); + company = dme1737_read(data, DME1737_REG_COMPANY); + device = dme1737_read(data, DME1737_REG_DEVICE); if (!((company == DME1737_COMPANY_SMSC) && (device == SCH311X_DEVICE))) { @@ -2441,10 +2437,10 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev) data->type = sch311x; /* Fill in the remaining client fields and initialize the mutex */ - strlcpy(client->name, "sch311x", I2C_NAME_SIZE); + data->name = "sch311x"; mutex_init(&data->update_lock); - dev_info(dev, "Found a SCH311x chip at 0x%04x\n", client->addr); + dev_info(dev, "Found a SCH311x chip at 0x%04x\n", data->addr); /* Initialize the chip */ if ((err = dme1737_init_device(dev))) { @@ -2485,7 +2481,7 @@ static int __devexit dme1737_isa_remove(struct platform_device *pdev) hwmon_device_unregister(data->hwmon_dev); dme1737_remove_files(&pdev->dev); - release_region(data->client.addr, DME1737_EXTENT); + release_region(data->addr, DME1737_EXTENT); platform_set_drvdata(pdev, NULL); kfree(data); -- cgit v1.2.3-70-g09d2 From 67a37308ae37f8948d3c26f75a18f0ddb77ac198 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 14 Oct 2008 17:30:04 +0200 Subject: hwmon: (dme1737) Convert to a new-style i2c driver The new-style dme1737 driver implements the optional detect() callback to cover the use cases of the legacy driver. I don't actually expect any new-style device for that driver, but as the old i2c API is going away soon, we have to switch to the new one. Signed-off-by: Jean Delvare Cc: Juerg Haefliger --- drivers/hwmon/dme1737.c | 106 ++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 58 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 2bf97e1470c..27a5d397f9a 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -176,7 +176,6 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23}; * --------------------------------------------------------------------- */ struct dme1737_data { - struct i2c_client _client; /* will go away soon */ struct i2c_client *client; /* for I2C devices only */ struct device *hwmon_dev; const char *name; @@ -2188,38 +2187,24 @@ exit: return err; } -static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address, - int kind) +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int dme1737_i2c_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &adapter->dev; u8 company, verstep = 0; - struct i2c_client *client; - struct dme1737_data *data; - struct device *dev; - int err = 0; const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - goto exit; - } - - if (!(data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; + return -ENODEV; } - client = &data->_client; - data->client = client; - i2c_set_clientdata(client, data); - client->addr = address; - client->adapter = adapter; - client->driver = &dme1737_i2c_driver; - dev = &client->dev; - /* A negative kind means that the driver was loaded with no force * parameter (default), so we must identify the chip. */ if (kind < 0) { - company = dme1737_read(data, DME1737_REG_COMPANY); - verstep = dme1737_read(data, DME1737_REG_VERSTEP); + company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY); + verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP); if (company == DME1737_COMPANY_SMSC && (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) { @@ -2228,8 +2213,7 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address, verstep == SCH5027_VERSTEP) { kind = sch5027; } else { - err = -ENODEV; - goto exit_kfree; + return -ENODEV; } } @@ -2239,33 +2223,44 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address, kind = dme1737; name = "dme1737"; } - data->type = kind; - - /* Fill in the remaining client fields and put it into the global - * list */ - strlcpy(client->name, name, I2C_NAME_SIZE); - data->name = client->name; - mutex_init(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(client))) { - goto exit_kfree; - } dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n", kind == sch5027 ? "SCH5027" : "DME1737", client->addr, verstep); + strlcpy(info->type, name, I2C_NAME_SIZE); + + return 0; +} + +static int dme1737_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct dme1737_data *data; + struct device *dev = &client->dev; + int err; + + data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + data->type = id->driver_data; + data->client = client; + data->name = client->name; + mutex_init(&data->update_lock); /* Initialize the DME1737 chip */ if ((err = dme1737_init_device(dev))) { dev_err(dev, "Failed to initialize device.\n"); - goto exit_detach; + goto exit_kfree; } /* Create sysfs files */ if ((err = dme1737_create_files(dev))) { dev_err(dev, "Failed to create sysfs files.\n"); - goto exit_detach; + goto exit_kfree; } /* Register device */ @@ -2280,45 +2275,40 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address, exit_remove: dme1737_remove_files(dev); -exit_detach: - i2c_detach_client(client); exit_kfree: kfree(data); exit: return err; } -static int dme1737_i2c_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) { - return 0; - } - - return i2c_probe(adapter, &addr_data, dme1737_i2c_detect); -} - -static int dme1737_i2c_detach_client(struct i2c_client *client) +static int dme1737_i2c_remove(struct i2c_client *client) { struct dme1737_data *data = i2c_get_clientdata(client); - int err; hwmon_device_unregister(data->hwmon_dev); dme1737_remove_files(&client->dev); - if ((err = i2c_detach_client(client))) { - return err; - } - kfree(data); return 0; } +static const struct i2c_device_id dme1737_id[] = { + { "dme1737", dme1737 }, + { "sch5027", sch5027 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dme1737_id); + static struct i2c_driver dme1737_i2c_driver = { + .class = I2C_CLASS_HWMON, .driver = { .name = "dme1737", }, - .attach_adapter = dme1737_i2c_attach_adapter, - .detach_client = dme1737_i2c_detach_client, + .probe = dme1737_i2c_probe, + .remove = dme1737_i2c_remove, + .id_table = dme1737_id, + .detect = dme1737_i2c_detect, + .address_data = &addr_data, }; /* --------------------------------------------------------------------- -- cgit v1.2.3-70-g09d2 From a9b12619f7b6f19c871437ec24a088787a04b1de Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 21 Jul 2008 20:03:34 -0700 Subject: device create: misc: convert device_create_drvdata to device_create Now that device_create() has been audited, rename things back to the original call to be sane. Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/rtlx.c | 4 ++-- arch/mips/sibyte/common/sb_tbprof.c | 3 +-- arch/x86/kernel/cpuid.c | 4 ++-- arch/x86/kernel/msr.c | 4 ++-- drivers/dca/dca-sysfs.c | 8 +++----- drivers/hid/hidraw.c | 5 ++--- drivers/hwmon/hwmon.c | 4 ++-- drivers/i2c/i2c-dev.c | 6 +++--- drivers/isdn/capi/capi.c | 3 +-- drivers/leds/led-class.c | 4 ++-- drivers/macintosh/adb.c | 3 +-- drivers/media/dvb/dvb-core/dvbdev.c | 2 +- drivers/misc/phantom.c | 6 +++--- drivers/mtd/mtdchar.c | 10 ++++------ drivers/power/power_supply_core.c | 4 ++-- drivers/spi/spidev.c | 7 +++---- drivers/uio/uio.c | 6 +++--- fs/coda/psdev.c | 5 ++--- 18 files changed, 39 insertions(+), 49 deletions(-) (limited to 'drivers/hwmon') diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c index dfd868b6836..4ce93aa7b37 100644 --- a/arch/mips/kernel/rtlx.c +++ b/arch/mips/kernel/rtlx.c @@ -522,8 +522,8 @@ static int __init rtlx_module_init(void) atomic_set(&channel_wqs[i].in_open, 0); mutex_init(&channel_wqs[i].mutex); - dev = device_create_drvdata(mt_class, NULL, MKDEV(major, i), - NULL, "%s%d", module_name, i); + dev = device_create(mt_class, NULL, MKDEV(major, i), NULL, + "%s%d", module_name, i); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_chrdev; diff --git a/arch/mips/sibyte/common/sb_tbprof.c b/arch/mips/sibyte/common/sb_tbprof.c index 66e3e3fb311..637a194e5cd 100644 --- a/arch/mips/sibyte/common/sb_tbprof.c +++ b/arch/mips/sibyte/common/sb_tbprof.c @@ -576,8 +576,7 @@ static int __init sbprof_tb_init(void) tb_class = tbc; - dev = device_create_drvdata(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), - NULL, "tb"); + dev = device_create(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), NULL, "tb"); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_class; diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index 6a44d646599..72cefd1e649 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -147,8 +147,8 @@ static __cpuinit int cpuid_device_create(int cpu) { struct device *dev; - dev = device_create_drvdata(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), - NULL, "cpu%d", cpu); + dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, + "cpu%d", cpu); return IS_ERR(dev) ? PTR_ERR(dev) : 0; } diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c index 2e2af5d1819..82a7c7ed6d4 100644 --- a/arch/x86/kernel/msr.c +++ b/arch/x86/kernel/msr.c @@ -163,8 +163,8 @@ static int __cpuinit msr_device_create(int cpu) { struct device *dev; - dev = device_create_drvdata(msr_class, NULL, MKDEV(MSR_MAJOR, cpu), - NULL, "msr%d", cpu); + dev = device_create(msr_class, NULL, MKDEV(MSR_MAJOR, cpu), NULL, + "msr%d", cpu); return IS_ERR(dev) ? PTR_ERR(dev) : 0; } diff --git a/drivers/dca/dca-sysfs.c b/drivers/dca/dca-sysfs.c index 7af4b403bd2..bb538b9690e 100644 --- a/drivers/dca/dca-sysfs.c +++ b/drivers/dca/dca-sysfs.c @@ -15,9 +15,8 @@ int dca_sysfs_add_req(struct dca_provider *dca, struct device *dev, int slot) struct device *cd; static int req_count; - cd = device_create_drvdata(dca_class, dca->cd, - MKDEV(0, slot + 1), NULL, - "requester%d", req_count++); + cd = device_create(dca_class, dca->cd, MKDEV(0, slot + 1), NULL, + "requester%d", req_count++); if (IS_ERR(cd)) return PTR_ERR(cd); return 0; @@ -48,8 +47,7 @@ idr_try_again: return err; } - cd = device_create_drvdata(dca_class, dev, MKDEV(0, 0), NULL, - "dca%d", dca->id); + cd = device_create(dca_class, dev, MKDEV(0, 0), NULL, "dca%d", dca->id); if (IS_ERR(cd)) { spin_lock(&dca_idr_lock); idr_remove(&dca_idr, dca->id); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 497e0d1dd3c..af3edb98df4 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -326,9 +326,8 @@ int hidraw_connect(struct hid_device *hid) goto out; } - dev->dev = device_create_drvdata(hidraw_class, NULL, - MKDEV(hidraw_major, minor), NULL, - "%s%d", "hidraw", minor); + dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor), + NULL, "%s%d", "hidraw", minor); if (IS_ERR(dev->dev)) { spin_lock(&minors_lock); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 7321a88a511..076a59cdabe 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -55,8 +55,8 @@ again: return ERR_PTR(err); id = id & MAX_ID_MASK; - hwdev = device_create_drvdata(hwmon_class, dev, MKDEV(0, 0), NULL, - HWMON_ID_FORMAT, id); + hwdev = device_create(hwmon_class, dev, MKDEV(0, 0), NULL, + HWMON_ID_FORMAT, id); if (IS_ERR(hwdev)) { spin_lock(&idr_lock); diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 307d976c9b6..c171988a9f5 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -521,9 +521,9 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap) return PTR_ERR(i2c_dev); /* register this i2c device with the driver core */ - i2c_dev->dev = device_create_drvdata(i2c_dev_class, &adap->dev, - MKDEV(I2C_MAJOR, adap->nr), - NULL, "i2c-%d", adap->nr); + i2c_dev->dev = device_create(i2c_dev_class, &adap->dev, + MKDEV(I2C_MAJOR, adap->nr), NULL, + "i2c-%d", adap->nr); if (IS_ERR(i2c_dev->dev)) { res = PTR_ERR(i2c_dev->dev); goto error; diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 798d7f3e42e..1b5bf87c4cf 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1553,8 +1553,7 @@ static int __init capi_init(void) return PTR_ERR(capi_class); } - device_create_drvdata(capi_class, NULL, MKDEV(capi_major, 0), NULL, - "capi"); + device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi"); #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE if (capinc_tty_init() < 0) { diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 559a40861c3..ee74ee7b2ac 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -103,8 +103,8 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { int rc; - led_cdev->dev = device_create_drvdata(leds_class, parent, 0, led_cdev, - "%s", led_cdev->name); + led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, + "%s", led_cdev->name); if (IS_ERR(led_cdev->dev)) return PTR_ERR(led_cdev->dev); diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index cae52485208..23741cec45e 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -862,8 +862,7 @@ adbdev_init(void) adb_dev_class = class_create(THIS_MODULE, "adb"); if (IS_ERR(adb_dev_class)) return; - device_create_drvdata(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, - "adb"); + device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, "adb"); platform_device_register(&adb_pfdev); platform_driver_probe(&adb_pfdrv, adb_dummy_probe); diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index e7132770a3b..665776d72a4 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -233,7 +233,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, mutex_unlock(&dvbdev_register_lock); - clsdev = device_create_drvdata(dvb_class, adap->device, + clsdev = device_create(dvb_class, adap->device, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)), NULL, "dvb%d.%s%d", adap->num, dnames[type], id); if (IS_ERR(clsdev)) { diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index daf585689ce..abdebe34738 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -399,9 +399,9 @@ static int __devinit phantom_probe(struct pci_dev *pdev, goto err_irq; } - if (IS_ERR(device_create_drvdata(phantom_class, &pdev->dev, - MKDEV(phantom_major, minor), - NULL, "phantom%u", minor))) + if (IS_ERR(device_create(phantom_class, &pdev->dev, + MKDEV(phantom_major, minor), NULL, + "phantom%u", minor))) dev_err(&pdev->dev, "can't create device\n"); pci_set_drvdata(pdev, pht); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index e00d424e657..1c74762dec8 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -26,13 +26,11 @@ static void mtd_notify_add(struct mtd_info* mtd) if (!mtd) return; - device_create_drvdata(mtd_class, NULL, - MKDEV(MTD_CHAR_MAJOR, mtd->index*2), - NULL, "mtd%d", mtd->index); + device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + NULL, "mtd%d", mtd->index); - device_create_drvdata(mtd_class, NULL, - MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), - NULL, "mtd%dro", mtd->index); + device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + NULL, "mtd%dro", mtd->index); } static void mtd_notify_remove(struct mtd_info* mtd) diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index cb1ccb47292..3007695f90c 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -91,8 +91,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) { int rc = 0; - psy->dev = device_create_drvdata(power_supply_class, parent, 0, - psy, "%s", psy->name); + psy->dev = device_create(power_supply_class, parent, 0, psy, + "%s", psy->name); if (IS_ERR(psy->dev)) { rc = PTR_ERR(psy->dev); goto dev_create_failed; diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index e5e0cfed5e3..89a43755a45 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -583,10 +583,9 @@ static int spidev_probe(struct spi_device *spi) struct device *dev; spidev->devt = MKDEV(SPIDEV_MAJOR, minor); - dev = device_create_drvdata(spidev_class, &spi->dev, - spidev->devt, spidev, - "spidev%d.%d", - spi->master->bus_num, spi->chip_select); + dev = device_create(spidev_class, &spi->dev, spidev->devt, + spidev, "spidev%d.%d", + spi->master->bus_num, spi->chip_select); status = IS_ERR(dev) ? PTR_ERR(dev) : 0; } else { dev_dbg(&spi->dev, "no minor number available!\n"); diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 3a6934bf713..9ac22c7c385 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -682,9 +682,9 @@ int __uio_register_device(struct module *owner, if (ret) goto err_get_minor; - idev->dev = device_create_drvdata(uio_class->class, parent, - MKDEV(uio_major, idev->minor), idev, - "uio%d", idev->minor); + idev->dev = device_create(uio_class->class, parent, + MKDEV(uio_major, idev->minor), idev, + "uio%d", idev->minor); if (IS_ERR(idev->dev)) { printk(KERN_ERR "UIO: device register failed\n"); ret = PTR_ERR(idev->dev); diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 0d9b80ec689..cfd29da714d 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -362,9 +362,8 @@ static int init_coda_psdev(void) goto out_chrdev; } for (i = 0; i < MAX_CODADEVS; i++) - device_create_drvdata(coda_psdev_class, NULL, - MKDEV(CODA_PSDEV_MAJOR, i), - NULL, "cfs%d", i); + device_create(coda_psdev_class, NULL, + MKDEV(CODA_PSDEV_MAJOR, i), NULL, "cfs%d", i); coda_sysctl_init(); goto out; -- cgit v1.2.3-70-g09d2 From 6388a388ffb720f40fc8046c261252ea2be9c12f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:09 +0200 Subject: hwmon: (lm90) Move 16-bit value read to a separate function Move the code which aggregates two 8-bit register values into a 16-bit value to a separate function. We'll need to do it a second time soon and I don't want to duplicate the code. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- drivers/hwmon/lm90.c | 72 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 29 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c24fe36ac78..73a1c622fb7 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1,7 +1,7 @@ /* * lm90.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2006 Jean Delvare + * Copyright (C) 2003-2008 Jean Delvare * * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to @@ -736,6 +736,38 @@ static int lm90_remove(struct i2c_client *client) return 0; } +static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl, u16 *value) +{ + int err; + u8 oldh, newh, l; + + /* + * There is a trick here. We have to read two registers to have the + * sensor temperature, but we have to beware a conversion could occur + * inbetween the readings. The datasheet says we should either use + * the one-shot conversion register, which we don't want to do + * (disables hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit at the + * exact same time). So the solution used here is to read the high + * byte once, then the low byte, then the high byte again. If the new + * high byte matches the old one, then we have a valid reading. Else + * we have to read the low byte again, and now we believe we have a + * correct reading. + */ + if ((err = lm90_read_reg(client, regh, &oldh)) + || (err = lm90_read_reg(client, regl, &l)) + || (err = lm90_read_reg(client, regh, &newh))) + return err; + if (oldh != newh) { + err = lm90_read_reg(client, regl, &l); + if (err) + return err; + } + *value = (newh << 8) | l; + + return 0; +} + static struct lm90_data *lm90_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -744,7 +776,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - u8 oldh, newh, l; + u8 h, l; dev_dbg(&client->dev, "Updating lm90 data.\n"); lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]); @@ -754,39 +786,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]); lm90_read_reg(client, LM90_REG_R_TCRIT_HYST, &data->temp_hyst); - /* - * There is a trick here. We have to read two registers to - * have the remote sensor temperature, but we have to beware - * a conversion could occur inbetween the readings. The - * datasheet says we should either use the one-shot - * conversion register, which we don't want to do (disables - * hardware monitoring) or monitor the busy bit, which is - * impossible (we can't read the values and monitor that bit - * at the exact same time). So the solution used here is to - * read the high byte once, then the low byte, then the high - * byte again. If the new high byte matches the old one, - * then we have a valid reading. Else we have to read the low - * byte again, and now we believe we have a correct reading. - */ - if (lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &oldh) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPH, &newh) == 0 - && (newh == oldh - || lm90_read_reg(client, LM90_REG_R_REMOTE_TEMPL, &l) == 0)) - data->temp11[0] = (newh << 8) | l; - - if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &newh) == 0 + lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, + LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); + + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) - data->temp11[1] = (newh << 8) | l; - if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &newh) == 0 + data->temp11[1] = (h << 8) | l; + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) - data->temp11[2] = (newh << 8) | l; + data->temp11[2] = (h << 8) | l; if (data->kind != max6657) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, - &newh) == 0 + &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, &l) == 0) - data->temp11[3] = (newh << 8) | l; + data->temp11[3] = (h << 8) | l; } lm90_read_reg(client, LM90_REG_R_STATUS, &data->alarms); -- cgit v1.2.3-70-g09d2 From f65e17086fc141bee1592bbf6e709e9c7a43541b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:09 +0200 Subject: hwmon: (lm90) Support the extra resolution bits of MAX6657 The Maxim MAX6657, MAX6658 and MAX6659 have extra resolution bits for the local temperature measurement. Let the lm90 driver read them and export them to user-space. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- Documentation/hwmon/lm90 | 10 +++++---- drivers/hwmon/lm90.c | 54 +++++++++++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 25 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index aa4a0ec2008..0b3e8bb7c1f 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -86,9 +86,8 @@ family is that it features critical limits with hysteresis, and an increased resolution of the remote temperature measurement. The different chipsets of the family are not strictly identical, although -very similar. This driver doesn't handle any specific feature for now, -with the exception of SMBus PEC. For reference, here comes a non-exhaustive -list of specific features: +very similar. For reference, here comes a non-exhaustive list of specific +features: LM90: * Filter and alert configuration register at 0xBF. @@ -114,9 +113,11 @@ ADT7461: * Lower resolution for remote temperature MAX6657 and MAX6658: + * Better local resolution * Remote sensor type selection MAX6659: + * Better local resolution * Selectable address * Second critical temperature limit * Remote sensor type selection @@ -127,7 +128,8 @@ MAX6680 and MAX6681: All temperature values are given in degrees Celsius. Resolution is 1.0 degree for the local temperature, 0.125 degree for the remote -temperature. +temperature, except for the MAX6657, MAX6658 and MAX6659 which have a +resolution of 0.125 degree for both temperatures. Each sensor has its own high and low limits, plus a critical limit. Additionally, there is a relative hysteresis value common to both critical diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 73a1c622fb7..16b99e0bdff 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -149,6 +149,10 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 +/* MAX6657-specific registers */ + +#define MAX6657_REG_R_LOCAL_TEMPL 0x11 + /* * Conversions and various macros * For local temperatures and limits, critical limits and the hysteresis @@ -239,15 +243,15 @@ struct lm90_data { int kind; /* registers values */ - s8 temp8[5]; /* 0: local input - 1: local low limit - 2: local high limit - 3: local critical limit - 4: remote critical limit */ - s16 temp11[4]; /* 0: remote input + s8 temp8[4]; /* 0: local low limit + 1: local high limit + 2: local critical limit + 3: remote critical limit */ + s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6657) */ + 3: remote offset (except max6657) + 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ }; @@ -285,7 +289,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); else data->temp8[nr] = TEMP1_TO_REG(val); - i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]); + i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); mutex_unlock(&data->update_lock); return count; } @@ -347,7 +351,7 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, long hyst; mutex_lock(&data->update_lock); - hyst = TEMP1_FROM_REG(data->temp8[3]) - val; + hyst = TEMP1_FROM_REG(data->temp8[2]) - val; i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, HYST_TO_REG(hyst)); mutex_unlock(&data->update_lock); @@ -371,23 +375,23 @@ static ssize_t show_alarm(struct device *dev, struct device_attribute return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp11, NULL, 4); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 1); + set_temp8, 0); static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 1); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 2); + set_temp8, 1); static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 2); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 3); + set_temp8, 2); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 4); + set_temp8, 3); static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, - set_temphyst, 3); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4); + set_temphyst, 2); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 3); static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11, set_temp11, 3); @@ -779,13 +783,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) u8 h, l; dev_dbg(&client->dev, "Updating lm90 data.\n"); - lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, &data->temp8[0]); - lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[1]); - lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[2]); - lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[3]); - lm90_read_reg(client, LM90_REG_R_REMOTE_CRIT, &data->temp8[4]); + lm90_read_reg(client, LM90_REG_R_LOCAL_LOW, &data->temp8[0]); + lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH, &data->temp8[1]); + lm90_read_reg(client, LM90_REG_R_LOCAL_CRIT, &data->temp8[2]); + 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->kind == max6657) { + lm90_read16(client, LM90_REG_R_LOCAL_TEMP, + MAX6657_REG_R_LOCAL_TEMPL, + &data->temp11[4]); + } else { + if (lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP, + &h) == 0) + data->temp11[4] = h << 8; + } lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); -- cgit v1.2.3-70-g09d2 From 5f502a834a6471dc3cc456ccef66292e9e3a152e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:09 +0200 Subject: hwmon: (lm90) Don't access nonexistent registers on Maxim chips The Maxim chips supported by the lm90 driver have 8-bit high and low remote limit values, not 11-bit as the other chips have. So stop reading from and writing to registers that do not exist on these chips. Also round the limit values set by the user properly. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- drivers/hwmon/lm90.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 16b99e0bdff..90489b8f5c8 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -323,12 +323,16 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) data->temp11[nr] = TEMP2_TO_REG_ADT7461(val); + else if (data->kind == max6657 || data->kind == max6680) + data->temp11[nr] = TEMP1_TO_REG(val) << 8; else data->temp11[nr] = TEMP2_TO_REG(val); + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], - data->temp11[nr] & 0xff); + if (data->kind != max6657 && data->kind != max6680) + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], + data->temp11[nr] & 0xff); mutex_unlock(&data->update_lock); return count; } @@ -801,12 +805,21 @@ static struct lm90_data *lm90_update_device(struct device *dev) lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, LM90_REG_R_REMOTE_TEMPL, &data->temp11[0]); - if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) - data->temp11[1] = (h << 8) | l; - if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0 - && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) - data->temp11[2] = (h << 8) | l; + if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { + data->temp11[1] = h << 8; + if (data->kind != max6657 && data->kind != max6680 + && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, + &l) == 0) + data->temp11[1] |= l; + } + if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { + data->temp11[2] = h << 8; + if (data->kind != max6657 && data->kind != max6680 + && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, + &l) == 0) + data->temp11[2] |= l; + } + if (data->kind != max6657) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, &h) == 0 -- cgit v1.2.3-70-g09d2 From a874a10cf0b7105ae5eeb98b4860eae0fc78fcdd Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Update datasheet links Update the links to the datasheet of some of the devices supported by the lm90 driver. Also remove the links from the driver itself, so that we don't have to update them twice each time they change. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- Documentation/hwmon/lm90 | 12 ++++++------ drivers/hwmon/lm90.c | 30 +++++++----------------------- 2 files changed, 13 insertions(+), 29 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 0b3e8bb7c1f..0c08c5c5637 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -11,7 +11,7 @@ Supported chips: Prefix: 'lm99' Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM89.html + http://www.national.com/mpf/LM/LM89.html * National Semiconductor LM99 Prefix: 'lm99' Addresses scanned: I2C 0x4c and 0x4d @@ -21,17 +21,17 @@ Supported chips: Prefix: 'lm86' Addresses scanned: I2C 0x4c Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM86.html + http://www.national.com/mpf/LM/LM86.html * Analog Devices ADM1032 Prefix: 'adm1032' Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the Analog Devices website - http://www.analog.com/en/prod/0,2877,ADM1032,00.html + Datasheet: Publicly available at the ON Semiconductor website + http://www.onsemi.com/PowerSolutions/product.do?id=ADM1032 * Analog Devices ADT7461 Prefix: 'adt7461' Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the Analog Devices website - http://www.analog.com/en/prod/0,2877,ADT7461,00.html + Datasheet: Publicly available at the ON Semiconductor website + http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 Note: Only if in ADM1032 compatibility mode * Maxim MAX6657 Prefix: 'max6657' diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 90489b8f5c8..d96e403d59e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -6,9 +6,7 @@ * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to * one external one) with a 0.125 deg resolution (1 deg for local - * temperature) and a 3-4 deg accuracy. Complete datasheet can be - * obtained from National's website at: - * http://www.national.com/pf/LM/LM90.html + * temperature) and a 3-4 deg accuracy. * * This driver also supports the LM89 and LM99, two other sensor chips * made by National Semiconductor. Both have an increased remote @@ -16,29 +14,19 @@ * additionally shifts remote temperatures (measured and limits) by 16 * degrees, which allows for higher temperatures measurement. The * driver doesn't handle it since it can be done easily in user-space. - * Complete datasheets can be obtained from National's website at: - * http://www.national.com/pf/LM/LM89.html - * http://www.national.com/pf/LM/LM99.html * Note that there is no way to differentiate between both chips. * * This driver also supports the LM86, another sensor chip made by * National Semiconductor. It is exactly similar to the LM90 except it * has a higher accuracy. - * Complete datasheet can be obtained from National's website at: - * http://www.national.com/pf/LM/LM86.html * * This driver also supports the ADM1032, a sensor chip made by Analog * Devices. That chip is similar to the LM90, with a few differences - * that are not handled by this driver. Complete datasheet can be - * obtained from Analog's website at: - * http://www.analog.com/en/prod/0,2877,ADM1032,00.html - * Among others, it has a higher accuracy than the LM90, much like the - * LM86 does. + * that are not handled by this driver. Among others, it has a higher + * accuracy than the LM90, much like the LM86 does. * * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor - * chips made by Maxim. These chips are similar to the LM86. Complete - * datasheet can be obtained at Maxim's website at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + * chips made by Maxim. These chips are similar to the LM86. * Note that there is no easy way to differentiate between the three * variants. The extra address and features of the MAX6659 are not * supported by this driver. These chips lack the remote temperature @@ -46,18 +34,14 @@ * * This driver also supports the MAX6680 and MAX6681, two other sensor * chips made by Maxim. These are quite similar to the other Maxim - * chips. Complete datasheet can be obtained at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370 - * The MAX6680 and MAX6681 only differ in the pinout so they can be - * treated identically. + * chips. The MAX6680 and MAX6681 only differ in the pinout so they can + * be treated identically. * * This driver also supports the ADT7461 chip from Analog Devices but * only in its "compatability mode". If an ADT7461 chip is found but * is configured in non-compatible mode (where its temperature * register values are decoded differently) it is ignored by this - * driver. Complete datasheet can be obtained from Analog's website - * at: - * http://www.analog.com/en/prod/0,2877,ADT7461,00.html + * driver. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and -- cgit v1.2.3-70-g09d2 From cea50fe2fdea36174aa24b58c69c4eb9770e7c49 Mon Sep 17 00:00:00 2001 From: Nate Case Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Convert some macros to static functions Use static functions instead of the TEMPx_FROM_REG* and TEMPx_TO_REG* macros. This will ensure type safety and eliminate any side effects from arguments passed in since the macros referenced 'val' multiple times. This change should not affect functionality. Signed-off-by: Nate Case Signed-off-by: Jean Delvare Tested-by: Martyn Welch --- drivers/hwmon/lm90.c | 128 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 45 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index d96e403d59e..b2d9b3f0946 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -137,40 +137,6 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define MAX6657_REG_R_LOCAL_TEMPL 0x11 -/* - * Conversions and various macros - * For local temperatures and limits, critical limits and the hysteresis - * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. - * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. - */ - -#define TEMP1_FROM_REG(val) ((val) * 1000) -#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \ - (val) >= 127000 ? 127 : \ - (val) < 0 ? ((val) - 500) / 1000 : \ - ((val) + 500) / 1000) -#define TEMP2_FROM_REG(val) ((val) / 32 * 125) -#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ - (val) >= 127875 ? 0x7FE0 : \ - (val) < 0 ? ((val) - 62) / 125 * 32 : \ - ((val) + 62) / 125 * 32) -#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \ - ((val) + 500) / 1000) - -/* - * ADT7461 is almost identical to LM90 except that attempts to write - * values that are outside the range 0 < temp < 127 are treated as - * the boundary value. - */ - -#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ - (val) >= 127000 ? 127 : \ - ((val) + 500) / 1000) -#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ - (val) >= 127750 ? 0x7FC0 : \ - ((val) + 125) / 250 * 64) - /* * Functions declaration */ @@ -240,6 +206,78 @@ struct lm90_data { u8 alarms; /* bitvector */ }; +/* + * Conversions + * For local temperatures and limits, critical limits and the hysteresis + * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. + * For remote temperatures and limits, it uses signed 11-bit values with + * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. + */ + +static inline int temp1_from_reg(s8 val) +{ + return val * 1000; +} + +static inline int temp2_from_reg(s16 val) +{ + return val / 32 * 125; +} + +static s8 temp1_to_reg(long val) +{ + if (val <= -128000) + return -128; + if (val >= 127000) + return 127; + if (val < 0) + return (val - 500) / 1000; + return (val + 500) / 1000; +} + +static s16 temp2_to_reg(long val) +{ + if (val <= -128000) + return 0x8000; + if (val >= 127875) + return 0x7FE0; + if (val < 0) + return (val - 62) / 125 * 32; + return (val + 62) / 125 * 32; +} + +static u8 hyst_to_reg(long val) +{ + if (val <= 0) + return 0; + if (val >= 30500) + return 31; + return (val + 500) / 1000; +} + +/* + * ADT7461 is almost identical to LM90 except that attempts to write + * values that are outside the range 0 < temp < 127 are treated as + * the boundary value. + */ +static u8 temp1_to_reg_adt7461(long val) +{ + if (val <= 0) + return 0; + if (val >= 127000) + return 127; + return (val + 500) / 1000; +} + +static u16 temp2_to_reg_adt7461(long val) +{ + if (val <= 0) + return 0; + if (val >= 127750) + return 0x7FC0; + return (val + 125) / 250 * 64; +} + /* * Sysfs stuff */ @@ -249,7 +287,7 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])); + return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index])); } static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, @@ -270,9 +308,9 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); + data->temp8[nr] = temp1_to_reg_adt7461(val); else - data->temp8[nr] = TEMP1_TO_REG(val); + data->temp8[nr] = temp1_to_reg(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); mutex_unlock(&data->update_lock); return count; @@ -283,7 +321,7 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index])); + return sprintf(buf, "%d\n", temp2_from_reg(data->temp11[attr->index])); } static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, @@ -306,11 +344,11 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = TEMP2_TO_REG_ADT7461(val); + data->temp11[nr] = temp2_to_reg_adt7461(val); else if (data->kind == max6657 || data->kind == max6680) - data->temp11[nr] = TEMP1_TO_REG(val) << 8; + data->temp11[nr] = temp1_to_reg(val) << 8; else - data->temp11[nr] = TEMP2_TO_REG(val); + data->temp11[nr] = temp2_to_reg(val); i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); @@ -326,8 +364,8 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]) - - TEMP1_FROM_REG(data->temp_hyst)); + return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index]) + - temp1_from_reg(data->temp_hyst)); } static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, @@ -339,9 +377,9 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, long hyst; mutex_lock(&data->update_lock); - hyst = TEMP1_FROM_REG(data->temp8[2]) - val; + hyst = temp1_from_reg(data->temp8[2]) - val; i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, - HYST_TO_REG(hyst)); + hyst_to_reg(hyst)); mutex_unlock(&data->update_lock); return count; } -- cgit v1.2.3-70-g09d2 From 23b2d4778ad33ee6bfe60439fb73c16580f204f2 Mon Sep 17 00:00:00 2001 From: Nate Case Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Support ADT7461 in extended mode Support ADT7461 in extended temperature range mode, which will change the range of readings from 0..127 to -64..191 degC. Adjust the register conversion functions accordingly. Signed-off-by: Nate Case Signed-off-by: Jean Delvare Tested-by: Martyn Welch --- Documentation/hwmon/lm90 | 8 +--- drivers/hwmon/Kconfig | 7 +-- drivers/hwmon/lm90.c | 120 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 97 insertions(+), 38 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 0c08c5c5637..53cd829f3f4 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -32,7 +32,6 @@ Supported chips: Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the ON Semiconductor website http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 - Note: Only if in ADM1032 compatibility mode * Maxim MAX6657 Prefix: 'max6657' Addresses scanned: I2C 0x4c @@ -70,16 +69,13 @@ Description The LM90 is a digital temperature sensor. It senses its own temperature as well as the temperature of up to one external diode. It is compatible -with many other devices such as the LM86, the LM89, the LM99, the ADM1032, -the MAX6657, MAX6658, MAX6659, MAX6680 and the MAX6681 all of which are -supported by this driver. +with many other devices, many of which are supported by this driver. Note that there is no easy way to differentiate between the MAX6657, MAX6658 and MAX6659 variants. The extra address and features of the MAX6659 are not supported by this driver. The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously can't (and don't need to) -be distinguished. Additionally, the ADT7461 is supported if found in -ADM1032 compatibility mode. +be distinguished. The specificity of this family of chipsets over the ADM1021/LM84 family is that it features critical limits with hysteresis, and an diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ebacc0af40f..96701e099e8 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -510,11 +510,8 @@ config SENSORS_LM90 depends on I2C help If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657, - MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. - - The Analog Devices ADT7461 sensor chip is also supported, but only - if found in ADM1032 compatibility mode. + LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim + MAX6657, MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index b2d9b3f0946..fe5d860fc83 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -37,11 +37,10 @@ * chips. The MAX6680 and MAX6681 only differ in the pinout so they can * be treated identically. * - * This driver also supports the ADT7461 chip from Analog Devices but - * only in its "compatability mode". If an ADT7461 chip is found but - * is configured in non-compatible mode (where its temperature - * register values are decoded differently) it is ignored by this - * driver. + * This driver also supports the ADT7461 chip from Analog Devices. + * It's supported in both compatibility and extended mode. It is mostly + * compatible with LM90 except for a data format difference for the + * temperature value registers. * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and @@ -137,6 +136,11 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define MAX6657_REG_R_LOCAL_TEMPL 0x11 +/* + * Device flags + */ +#define LM90_FLAG_ADT7461_EXT 0x01 /* ADT7461 extended mode */ + /* * Functions declaration */ @@ -191,6 +195,7 @@ struct lm90_data { char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ int kind; + int flags; /* registers values */ s8 temp8[4]; /* 0: local low limit @@ -256,26 +261,61 @@ static u8 hyst_to_reg(long val) } /* - * ADT7461 is almost identical to LM90 except that attempts to write - * values that are outside the range 0 < temp < 127 are treated as - * the boundary value. + * ADT7461 in compatibility mode is almost identical to LM90 except that + * attempts to write values that are outside the range 0 < temp < 127 are + * treated as the boundary value. + * + * ADT7461 in "extended mode" operation uses unsigned integers offset by + * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. */ -static u8 temp1_to_reg_adt7461(long val) +static inline int temp1_from_reg_adt7461(struct lm90_data *data, u8 val) { - if (val <= 0) - return 0; - if (val >= 127000) - return 127; - return (val + 500) / 1000; + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 64) * 1000; + else + return temp1_from_reg(val); } -static u16 temp2_to_reg_adt7461(long val) +static inline int temp2_from_reg_adt7461(struct lm90_data *data, u16 val) { - if (val <= 0) - return 0; - if (val >= 127750) - return 0x7FC0; - return (val + 125) / 250 * 64; + if (data->flags & LM90_FLAG_ADT7461_EXT) + return (val - 0x4000) / 64 * 250; + else + return temp2_from_reg(val); +} + +static u8 temp1_to_reg_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191000) + return 0xFF; + return (val + 500 + 64000) / 1000; + } else { + if (val <= 0) + return 0; + if (val >= 127000) + return 127; + return (val + 500) / 1000; + } +} + +static u16 temp2_to_reg_adt7461(struct lm90_data *data, long val) +{ + if (data->flags & LM90_FLAG_ADT7461_EXT) { + if (val <= -64000) + return 0; + if (val >= 191750) + return 0xFFC0; + return (val + 64000 + 125) / 250 * 64; + } else { + if (val <= 0) + return 0; + if (val >= 127750) + return 0x7FC0; + return (val + 125) / 250 * 64; + } } /* @@ -287,7 +327,14 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index])); + int temp; + + if (data->kind == adt7461) + temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + else + temp = temp1_from_reg(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, @@ -308,7 +355,7 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp8[nr] = temp1_to_reg_adt7461(val); + data->temp8[nr] = temp1_to_reg_adt7461(data, val); else data->temp8[nr] = temp1_to_reg(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); @@ -321,7 +368,14 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp2_from_reg(data->temp11[attr->index])); + int temp; + + if (data->kind == adt7461) + temp = temp2_from_reg_adt7461(data, data->temp11[attr->index]); + else + temp = temp2_from_reg(data->temp11[attr->index]); + + return sprintf(buf, "%d\n", temp); } static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, @@ -344,7 +398,7 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = temp2_to_reg_adt7461(val); + data->temp11[nr] = temp2_to_reg_adt7461(data, val); else if (data->kind == max6657 || data->kind == max6680) data->temp11[nr] = temp1_to_reg(val) << 8; else @@ -364,8 +418,14 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", temp1_from_reg(data->temp8[attr->index]) - - temp1_from_reg(data->temp_hyst)); + int temp; + + if (data->kind == adt7461) + temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + else + temp = temp1_from_reg(data->temp8[attr->index]); + + return sprintf(buf, "%d\n", temp - temp1_from_reg(data->temp_hyst)); } static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, @@ -598,7 +658,7 @@ static int lm90_detect(struct i2c_client *new_client, int kind, kind = adm1032; } else if (chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1F) == 0x00 /* check compat mode */ + && (reg_config1 & 0x1B) == 0x00 && reg_convrate <= 0x0A) { kind = adt7461; } @@ -737,6 +797,12 @@ static void lm90_init_client(struct i2c_client *client) } config_orig = config; + /* Check Temperature Range Select */ + if (data->kind == adt7461) { + if (config & 0x04) + data->flags |= LM90_FLAG_ADT7461_EXT; + } + /* * Put MAX6680/MAX8881 into extended resolution (bit 0x10, * 0.125 degree resolution) and range (0x08, extend range -- cgit v1.2.3-70-g09d2 From 9d4d3834229e9949c066c2d0f73ed5d4b4965761 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:10 +0200 Subject: hwmon: (lm90) Rename temperature conversion functions to match usage The encoding of temperatures varies between chips and modes. So do not use "temp1" or "temp2" in the names of the conversion functions, but specify the encoding. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare Tested-by: Martyn Welch --- drivers/hwmon/lm90.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index fe5d860fc83..85ba2c4feb4 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -219,17 +219,17 @@ struct lm90_data { * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. */ -static inline int temp1_from_reg(s8 val) +static inline int temp_from_s8(s8 val) { return val * 1000; } -static inline int temp2_from_reg(s16 val) +static inline int temp_from_s16(s16 val) { return val / 32 * 125; } -static s8 temp1_to_reg(long val) +static s8 temp_to_s8(long val) { if (val <= -128000) return -128; @@ -240,7 +240,7 @@ static s8 temp1_to_reg(long val) return (val + 500) / 1000; } -static s16 temp2_to_reg(long val) +static s16 temp_to_s16(long val) { if (val <= -128000) return 0x8000; @@ -268,23 +268,23 @@ static u8 hyst_to_reg(long val) * ADT7461 in "extended mode" operation uses unsigned integers offset by * 64 (e.g., 0 -> -64 degC). The range is restricted to -64..191 degC. */ -static inline int temp1_from_reg_adt7461(struct lm90_data *data, u8 val) +static inline int temp_from_u8_adt7461(struct lm90_data *data, u8 val) { if (data->flags & LM90_FLAG_ADT7461_EXT) return (val - 64) * 1000; else - return temp1_from_reg(val); + return temp_from_s8(val); } -static inline int temp2_from_reg_adt7461(struct lm90_data *data, u16 val) +static inline int temp_from_u16_adt7461(struct lm90_data *data, u16 val) { if (data->flags & LM90_FLAG_ADT7461_EXT) return (val - 0x4000) / 64 * 250; else - return temp2_from_reg(val); + return temp_from_s16(val); } -static u8 temp1_to_reg_adt7461(struct lm90_data *data, long val) +static u8 temp_to_u8_adt7461(struct lm90_data *data, long val) { if (data->flags & LM90_FLAG_ADT7461_EXT) { if (val <= -64000) @@ -301,7 +301,7 @@ static u8 temp1_to_reg_adt7461(struct lm90_data *data, long val) } } -static u16 temp2_to_reg_adt7461(struct lm90_data *data, long val) +static u16 temp_to_u16_adt7461(struct lm90_data *data, long val) { if (data->flags & LM90_FLAG_ADT7461_EXT) { if (val <= -64000) @@ -330,9 +330,9 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, int temp; if (data->kind == adt7461) - temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); else - temp = temp1_from_reg(data->temp8[attr->index]); + temp = temp_from_s8(data->temp8[attr->index]); return sprintf(buf, "%d\n", temp); } @@ -355,9 +355,9 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp8[nr] = temp1_to_reg_adt7461(data, val); + data->temp8[nr] = temp_to_u8_adt7461(data, val); else - data->temp8[nr] = temp1_to_reg(val); + data->temp8[nr] = temp_to_s8(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); mutex_unlock(&data->update_lock); return count; @@ -371,9 +371,9 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, int temp; if (data->kind == adt7461) - temp = temp2_from_reg_adt7461(data, data->temp11[attr->index]); + temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); else - temp = temp2_from_reg(data->temp11[attr->index]); + temp = temp_from_s16(data->temp11[attr->index]); return sprintf(buf, "%d\n", temp); } @@ -398,11 +398,11 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) - data->temp11[nr] = temp2_to_reg_adt7461(data, val); + data->temp11[nr] = temp_to_u16_adt7461(data, val); else if (data->kind == max6657 || data->kind == max6680) - data->temp11[nr] = temp1_to_reg(val) << 8; + data->temp11[nr] = temp_to_s8(val) << 8; else - data->temp11[nr] = temp2_to_reg(val); + data->temp11[nr] = temp_to_s16(val); i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); @@ -421,11 +421,11 @@ static ssize_t show_temphyst(struct device *dev, struct device_attribute *devatt int temp; if (data->kind == adt7461) - temp = temp1_from_reg_adt7461(data, data->temp8[attr->index]); + temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); else - temp = temp1_from_reg(data->temp8[attr->index]); + temp = temp_from_s8(data->temp8[attr->index]); - return sprintf(buf, "%d\n", temp - temp1_from_reg(data->temp_hyst)); + return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst)); } static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, @@ -437,7 +437,7 @@ static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, long hyst; mutex_lock(&data->update_lock); - hyst = temp1_from_reg(data->temp8[2]) - val; + hyst = temp_from_s8(data->temp8[2]) - val; i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, hyst_to_reg(hyst)); mutex_unlock(&data->update_lock); -- cgit v1.2.3-70-g09d2 From 271dabf5bbf6ae6e2792cd5cf6f0434230e5c18c Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm90) Support MAX6646, MAX6647 and MAX6649 These Maxim chips are similar to MAX6657 but use unsigned temperature values to allow for readings up to 145 degrees. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- Documentation/hwmon/lm90 | 15 ++++++++++ drivers/hwmon/Kconfig | 3 +- drivers/hwmon/lm90.c | 77 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 82 insertions(+), 13 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 index 53cd829f3f4..e0d5206d1de 100644 --- a/Documentation/hwmon/lm90 +++ b/Documentation/hwmon/lm90 @@ -32,6 +32,21 @@ Supported chips: Addresses scanned: I2C 0x4c and 0x4d Datasheet: Publicly available at the ON Semiconductor website http://www.onsemi.com/PowerSolutions/product.do?id=ADT7461 + * Maxim MAX6646 + Prefix: 'max6646' + Addresses scanned: I2C 0x4d + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + * Maxim MAX6647 + Prefix: 'max6646' + Addresses scanned: I2C 0x4e + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 + * Maxim MAX6649 + Prefix: 'max6646' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497 * Maxim MAX6657 Prefix: 'max6657' Addresses scanned: I2C 0x4c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 96701e099e8..6de1e0ffd39 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -511,7 +511,8 @@ config SENSORS_LM90 help If you say yes here you get support for National Semiconductor LM90, LM86, LM89 and LM99, Analog Devices ADM1032 and ADT7461, and Maxim - MAX6657, MAX6658, MAX6659, MAX6680 and MAX6681 sensor chips. + MAX6646, MAX6647, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680 and + MAX6681 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 85ba2c4feb4..fe09f82c42e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -32,6 +32,11 @@ * supported by this driver. These chips lack the remote temperature * offset feature. * + * This driver also supports the MAX6646, MAX6647 and MAX6649 chips + * made by Maxim. These are again similar to the LM86, but they use + * unsigned temperature values and can report temperatures from 0 to + * 145 degrees. + * * This driver also supports the MAX6680 and MAX6681, two other sensor * chips made by Maxim. These are quite similar to the other Maxim * chips. The MAX6680 and MAX6681 only differ in the pinout so they can @@ -76,9 +81,10 @@ * Addresses to scan * Address is fully defined internally and cannot be changed except for * MAX6659, MAX6680 and MAX6681. - * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6657 and MAX6658 - * have address 0x4c. - * ADM1032-2, ADT7461-2, LM89-1, and LM99-1 have address 0x4d. + * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, MAX6649, MAX6657 + * and MAX6658 have address 0x4c. + * ADM1032-2, ADT7461-2, LM89-1, LM99-1 and MAX6646 have address 0x4d. + * MAX6647 has address 0x4e. * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). * MAX6680 and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, * 0x4c, 0x4d or 0x4e. @@ -91,7 +97,8 @@ static const unsigned short normal_i2c[] = { * Insmod parameters */ -I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); +I2C_CLIENT_INSMOD_8(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680, + max6646); /* * The LM90 registers @@ -132,7 +139,7 @@ I2C_CLIENT_INSMOD_7(lm90, adm1032, lm99, lm86, max6657, adt7461, max6680); #define LM90_REG_R_TCRIT_HYST 0x21 #define LM90_REG_W_TCRIT_HYST 0x21 -/* MAX6657-specific registers */ +/* MAX6646/6647/6649/6657/6658/6659 registers */ #define MAX6657_REG_R_LOCAL_TEMPL 0x11 @@ -164,6 +171,9 @@ static const struct i2c_device_id lm90_id[] = { { "lm86", lm86 }, { "lm89", lm99 }, { "lm99", lm99 }, /* Missing temperature offset */ + { "max6646", max6646 }, + { "max6647", max6646 }, + { "max6649", max6646 }, { "max6657", max6657 }, { "max6658", max6657 }, { "max6659", max6657 }, @@ -205,7 +215,7 @@ struct lm90_data { s16 temp11[5]; /* 0: remote input 1: remote low limit 2: remote high limit - 3: remote offset (except max6657) + 3: remote offset (except max6646 and max6657) 4: local input */ u8 temp_hyst; u8 alarms; /* bitvector */ @@ -216,7 +226,8 @@ struct lm90_data { * For local temperatures and limits, critical limits and the hysteresis * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. + * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. Some + * Maxim chips use unsigned values. */ static inline int temp_from_s8(s8 val) @@ -224,11 +235,21 @@ static inline int temp_from_s8(s8 val) return val * 1000; } +static inline int temp_from_u8(u8 val) +{ + return val * 1000; +} + static inline int temp_from_s16(s16 val) { return val / 32 * 125; } +static inline int temp_from_u16(u16 val) +{ + return val / 32 * 125; +} + static s8 temp_to_s8(long val) { if (val <= -128000) @@ -240,6 +261,15 @@ static s8 temp_to_s8(long val) return (val + 500) / 1000; } +static u8 temp_to_u8(long val) +{ + if (val <= 0) + return 0; + if (val >= 255000) + return 255; + return (val + 500) / 1000; +} + static s16 temp_to_s16(long val) { if (val <= -128000) @@ -331,6 +361,8 @@ static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, if (data->kind == adt7461) temp = temp_from_u8_adt7461(data, data->temp8[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u8(data->temp8[attr->index]); else temp = temp_from_s8(data->temp8[attr->index]); @@ -356,6 +388,8 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, mutex_lock(&data->update_lock); if (data->kind == adt7461) data->temp8[nr] = temp_to_u8_adt7461(data, val); + else if (data->kind == max6646) + data->temp8[nr] = temp_to_u8(val); else data->temp8[nr] = temp_to_s8(val); i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]); @@ -372,6 +406,8 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, if (data->kind == adt7461) temp = temp_from_u16_adt7461(data, data->temp11[attr->index]); + else if (data->kind == max6646) + temp = temp_from_u16(data->temp11[attr->index]); else temp = temp_from_s16(data->temp11[attr->index]); @@ -401,12 +437,15 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, data->temp11[nr] = temp_to_u16_adt7461(data, val); else if (data->kind == max6657 || data->kind == max6680) data->temp11[nr] = temp_to_s8(val) << 8; + else if (data->kind == max6646) + data->temp11[nr] = temp_to_u8(val) << 8; else data->temp11[nr] = temp_to_s16(val); i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], data->temp11[nr] >> 8); - if (data->kind != max6657 && data->kind != max6680) + if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646) i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], data->temp11[nr] & 0xff); mutex_unlock(&data->update_lock); @@ -689,6 +728,16 @@ static int lm90_detect(struct i2c_client *new_client, int kind, && (reg_config1 & 0x03) == 0x00 && reg_convrate <= 0x07) { kind = max6680; + } else + /* The chip_id register of the MAX6646/6647/6649 + * holds the revision of the chip. + * The lowest 6 bits of the config1 register are + * unused and should return zero when read. + */ + if (chip_id == 0x59 + && (reg_config1 & 0x3f) == 0x00 + && reg_convrate <= 0x07) { + kind = max6646; } } @@ -719,6 +768,8 @@ static int lm90_detect(struct i2c_client *new_client, int kind, name = "max6680"; } else if (kind == adt7461) { name = "adt7461"; + } else if (kind == max6646) { + name = "max6646"; } strlcpy(info->type, name, I2C_NAME_SIZE); @@ -758,7 +809,7 @@ static int lm90_probe(struct i2c_client *new_client, &dev_attr_pec))) goto exit_remove_files; } - if (data->kind != max6657) { + if (data->kind != max6657 && data->kind != max6646) { if ((err = device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr))) goto exit_remove_files; @@ -824,7 +875,7 @@ static int lm90_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm90_group); device_remove_file(&client->dev, &dev_attr_pec); - if (data->kind != max6657) + if (data->kind != max6657 && data->kind != max6646) device_remove_file(&client->dev, &sensor_dev_attr_temp2_offset.dev_attr); @@ -881,7 +932,7 @@ 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->kind == max6657) { + if (data->kind == max6657 || data->kind == max6646) { lm90_read16(client, LM90_REG_R_LOCAL_TEMP, MAX6657_REG_R_LOCAL_TEMPL, &data->temp11[4]); @@ -896,6 +947,7 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (lm90_read_reg(client, LM90_REG_R_REMOTE_LOWH, &h) == 0) { data->temp11[1] = h << 8; if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 && lm90_read_reg(client, LM90_REG_R_REMOTE_LOWL, &l) == 0) data->temp11[1] |= l; @@ -903,12 +955,13 @@ static struct lm90_data *lm90_update_device(struct device *dev) if (lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHH, &h) == 0) { data->temp11[2] = h << 8; if (data->kind != max6657 && data->kind != max6680 + && data->kind != max6646 && lm90_read_reg(client, LM90_REG_R_REMOTE_HIGHL, &l) == 0) data->temp11[2] |= l; } - if (data->kind != max6657) { + if (data->kind != max6657 && data->kind != max6646) { if (lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSH, &h) == 0 && lm90_read_reg(client, LM90_REG_R_REMOTE_OFFSL, -- cgit v1.2.3-70-g09d2 From 4b4e7a72fda549e309919931d8a39dea4e5b4be1 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm90) Don't spam the kernel log Degrade the "Unsupported chip" message from info to debug level. There's nothing wrong with this, so no need to bother the user. Also make the message slightly more descriptive. Signed-off-by: Jean Delvare Acked-by: Martyn Welch --- drivers/hwmon/lm90.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index fe09f82c42e..3edeebc0b83 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -742,9 +742,9 @@ static int lm90_detect(struct i2c_client *new_client, int kind, } if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); + dev_dbg(&adapter->dev, + "Unsupported chip at 0x%02x (man_id=0x%02X, " + "chip_id=0x%02X)\n", address, man_id, chip_id); return -ENODEV; } } -- cgit v1.2.3-70-g09d2 From 49ae6cc8005f45edadf237089c31885834f2608e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm87) Fix masking of config register in lm87_init_client() lm87_init_client() conditionally sets the Start bit and clears the INT#_Clear bit in the Config 1 register. The condition should be that either of these bits needs changing, but currently it checks the (self-clearing) Initialization bit instead of INT#_Clear. Fix the condition and also ensure we never set the Initialization bit. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- drivers/hwmon/lm87.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 21970f0d53a..0fecbfd2ba5 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -867,11 +867,11 @@ static void lm87_init_client(struct i2c_client *client) lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); } } - if ((config & 0x81) != 0x01) { - /* Start monitoring */ + + /* Make sure Start is set and INT#_Clear is clear */ + if ((config & 0x09) != 0x01) lm87_write_value(client, LM87_REG_CONFIG, - (config & 0xF7) | 0x01); - } + (config & 0x77) | 0x01); } static int lm87_remove(struct i2c_client *client) -- cgit v1.2.3-70-g09d2 From d2cac802feae6f0c246a9251eefc482bf5ec0f0f Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:11 +0200 Subject: hwmon: (lm87) Restore original configuration register on removal This means that if we have to start the monitor when probed, we also stop it on removal. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- drivers/hwmon/lm87.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 0fecbfd2ba5..fa0e3794d9a 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -199,6 +199,7 @@ struct lm87_data { unsigned long last_updated; /* In jiffies */ u8 channel; /* register value */ + u8 config; /* original register value */ u8 in[8]; /* register value */ u8 in_max[8]; /* register value */ @@ -832,6 +833,7 @@ exit_remove: sysfs_remove_group(&new_client->dev.kobj, &lm87_group); sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt); exit_free: + lm87_write_value(new_client, LM87_REG_CONFIG, data->config); kfree(data); exit: return err; @@ -840,12 +842,11 @@ exit: static void lm87_init_client(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); - u8 config; data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; - config = lm87_read_value(client, LM87_REG_CONFIG); - if (!(config & 0x01)) { + if (!(data->config & 0x01)) { int i; /* Limits are left uninitialized after power-up */ @@ -869,9 +870,9 @@ static void lm87_init_client(struct i2c_client *client) } /* Make sure Start is set and INT#_Clear is clear */ - if ((config & 0x09) != 0x01) + if ((data->config & 0x09) != 0x01) lm87_write_value(client, LM87_REG_CONFIG, - (config & 0x77) | 0x01); + (data->config & 0x77) | 0x01); } static int lm87_remove(struct i2c_client *client) @@ -882,6 +883,7 @@ static int lm87_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &lm87_group); sysfs_remove_group(&client->dev.kobj, &lm87_group_opt); + lm87_write_value(client, LM87_REG_CONFIG, data->config); kfree(data); return 0; } -- cgit v1.2.3-70-g09d2 From 47064d645bc55863c7887a7c96cde39c9a37ee5f Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (lm87) Add support for configuration through platform_data The lm87 driver normally assumes that firmware configured the chip correctly. Since this is not always the case, alllow platform code to set the channel register value via platform_data. All other configuration registers can be changed after driver initialisation. Signed-off-by: Ben Hutchings Signed-off-by: Jean Delvare --- Documentation/hwmon/lm87 | 9 ++++----- drivers/hwmon/lm87.c | 17 +++++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/lm87 b/Documentation/hwmon/lm87 index ec27aa1b94c..6b47b67fd96 100644 --- a/Documentation/hwmon/lm87 +++ b/Documentation/hwmon/lm87 @@ -65,11 +65,10 @@ The LM87 has four pins which can serve one of two possible functions, depending on the hardware configuration. Some functions share pins, so not all functions are available at the same -time. Which are depends on the hardware setup. This driver assumes that -the BIOS configured the chip correctly. In that respect, it differs from -the original driver (from lm_sensors for Linux 2.4), which would force the -LM87 to an arbitrary, compile-time chosen mode, regardless of the actual -chipset wiring. +time. Which are depends on the hardware setup. This driver normally +assumes that firmware configured the chip correctly. Where this is not +the case, platform code must set the I2C client's platform_data to point +to a u8 value to be written to the channel register. For reference, here is the list of exclusive functions: - in0+in5 (default) or temp3 diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index fa0e3794d9a..2e4a3cea95f 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -21,11 +21,10 @@ * http://www.national.com/pf/LM/LM87.html * * Some functions share pins, so not all functions are available at the same - * time. Which are depends on the hardware setup. This driver assumes that - * the BIOS configured the chip correctly. In that respect, it differs from - * the original driver (from lm_sensors for Linux 2.4), which would force the - * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual - * chipset wiring. + * time. Which are depends on the hardware setup. This driver normally + * assumes that firmware configured the chip correctly. Where this is not + * the case, platform code must set the I2C client's platform_data to point + * to a u8 value to be written to the channel register. * For reference, here is the list of exclusive functions: * - in0+in5 (default) or temp3 * - fan1 (default) or in6 @@ -843,7 +842,13 @@ static void lm87_init_client(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); - data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + if (client->dev.platform_data) { + data->channel = *(u8 *)client->dev.platform_data; + lm87_write_value(client, + LM87_REG_CHANNEL_MODE, data->channel); + } else { + data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + } data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; if (!(data->config & 0x01)) { -- cgit v1.2.3-70-g09d2 From 810ad7b62c0f075dc44ecc781b24c7f6ba388da5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (ams) Convert to a new-style i2c driver The legacy i2c binding model is phasing out, so the ams driver needs to be converted to a new-style i2c driver. Here is a naive approach of this conversion. Basically it is moving the i2c device creation from the ams driver to the i2c-powermac driver. This should work, but I suspect we could come up with something cleaner by declaring the i2c device as part of the platform setup. This could be done later by someone more familiar with openfirmware-based platforms than I am myself. One nice thing brought by this conversion is that the ams driver should be loaded automatically on systems where is is needed (at least when the I2C interface to the chip is used) providing coldplug-aware user-space environment. Signed-off-by: Jean Delvare Acked-by: Johannes Berg Cc: Stelian Pop Cc: Michael Hanselmann Cc: Benjamin Herrenschmidt --- drivers/hwmon/ams/ams-i2c.c | 56 +++++++++++++-------------------------- drivers/hwmon/ams/ams.h | 4 +-- drivers/i2c/busses/i2c-powermac.c | 29 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 41 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c index 957760536a4..da26de01068 100644 --- a/drivers/hwmon/ams/ams-i2c.c +++ b/drivers/hwmon/ams/ams-i2c.c @@ -60,26 +60,34 @@ enum ams_i2c_cmd { AMS_CMD_START, }; -static int ams_i2c_attach(struct i2c_adapter *adapter); -static int ams_i2c_detach(struct i2c_adapter *adapter); +static int ams_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int ams_i2c_remove(struct i2c_client *client); + +static const struct i2c_device_id ams_id[] = { + { "ams", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ams_id); static struct i2c_driver ams_i2c_driver = { .driver = { .name = "ams", .owner = THIS_MODULE, }, - .attach_adapter = ams_i2c_attach, - .detach_adapter = ams_i2c_detach, + .probe = ams_i2c_probe, + .remove = ams_i2c_remove, + .id_table = ams_id, }; static s32 ams_i2c_read(u8 reg) { - return i2c_smbus_read_byte_data(&ams_info.i2c_client, reg); + return i2c_smbus_read_byte_data(ams_info.i2c_client, reg); } static int ams_i2c_write(u8 reg, u8 value) { - return i2c_smbus_write_byte_data(&ams_info.i2c_client, reg, value); + return i2c_smbus_write_byte_data(ams_info.i2c_client, reg, value); } static int ams_i2c_cmd(enum ams_i2c_cmd cmd) @@ -152,9 +160,9 @@ static void ams_i2c_get_xyz(s8 *x, s8 *y, s8 *z) *z = ams_i2c_read(AMS_DATAZ); } -static int ams_i2c_attach(struct i2c_adapter *adapter) +static int ams_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - unsigned long bus; int vmaj, vmin; int result; @@ -162,17 +170,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter) if (unlikely(ams_info.has_device)) return -ENODEV; - if (strncmp(adapter->name, "uni-n", 5)) - return -ENODEV; - - bus = simple_strtoul(adapter->name + 6, NULL, 10); - if (bus != ams_info.i2c_bus) - return -ENODEV; - - ams_info.i2c_client.addr = ams_info.i2c_address; - ams_info.i2c_client.adapter = adapter; - ams_info.i2c_client.driver = &ams_i2c_driver; - strcpy(ams_info.i2c_client.name, "Apple Motion Sensor"); + ams_info.i2c_client = client; if (ams_i2c_cmd(AMS_CMD_RESET)) { printk(KERN_INFO "ams: Failed to reset the device\n"); @@ -237,7 +235,7 @@ static int ams_i2c_attach(struct i2c_adapter *adapter) return 0; } -static int ams_i2c_detach(struct i2c_adapter *adapter) +static int ams_i2c_remove(struct i2c_client *client) { if (ams_info.has_device) { /* Disable interrupts */ @@ -261,9 +259,7 @@ static void ams_i2c_exit(void) int __init ams_i2c_init(struct device_node *np) { - char *tmp_bus; int result; - const u32 *prop; mutex_lock(&ams_info.lock); @@ -275,24 +271,8 @@ int __init ams_i2c_init(struct device_node *np) ams_info.clear_irq = ams_i2c_clear_irq; ams_info.bustype = BUS_I2C; - /* look for bus either using "reg" or by path */ - prop = of_get_property(ams_info.of_node, "reg", NULL); - if (!prop) { - result = -ENODEV; - - goto exit; - } - - tmp_bus = strstr(ams_info.of_node->full_name, "/i2c-bus@"); - if (tmp_bus) - ams_info.i2c_bus = *(tmp_bus + 9) - '0'; - else - ams_info.i2c_bus = ((*prop) >> 8) & 0x0f; - ams_info.i2c_address = ((*prop) & 0xff) >> 1; - result = i2c_add_driver(&ams_i2c_driver); -exit: mutex_unlock(&ams_info.lock); return result; diff --git a/drivers/hwmon/ams/ams.h b/drivers/hwmon/ams/ams.h index 221ef6915a5..5ed387b0bd9 100644 --- a/drivers/hwmon/ams/ams.h +++ b/drivers/hwmon/ams/ams.h @@ -46,9 +46,7 @@ struct ams { #ifdef CONFIG_SENSORS_AMS_I2C /* I2C properties */ - int i2c_bus; - int i2c_address; - struct i2c_client i2c_client; + struct i2c_client *i2c_client; #endif /* Joystick emulation */ diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 0e7b1c6724a..60ca91745e5 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -259,6 +259,35 @@ static int __devinit i2c_powermac_probe(struct platform_device *dev) } printk(KERN_INFO "PowerMac i2c bus %s registered\n", name); + + if (!strncmp(basename, "uni-n", 5)) { + struct device_node *np; + const u32 *prop; + struct i2c_board_info info; + + /* Instantiate I2C motion sensor if present */ + np = of_find_node_by_name(NULL, "accelerometer"); + if (np && of_device_is_compatible(np, "AAPL,accelerometer_1") && + (prop = of_get_property(np, "reg", NULL))) { + int i2c_bus; + const char *tmp_bus; + + /* look for bus either using "reg" or by path */ + tmp_bus = strstr(np->full_name, "/i2c-bus@"); + if (tmp_bus) + i2c_bus = *(tmp_bus + 9) - '0'; + else + i2c_bus = ((*prop) >> 8) & 0x0f; + + if (pmac_i2c_get_channel(bus) == i2c_bus) { + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = ((*prop) & 0xff) >> 1; + strlcpy(info.type, "ams", I2C_NAME_SIZE); + i2c_new_device(adapter, &info); + } + } + } + return rc; } -- cgit v1.2.3-70-g09d2 From 0a02002268bf624a8b0eaf3b4eb5c4207bd80d8b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (ams) Fix permissions on 'joystick' module parameter We should not allow writes to the 'joystick' module parameters since writing there will not trigger creation of the input device. Disable writes since we provide alternative way of enabling input device via AMS device's sysfs attribute. Signed-off-by: Dmitry Torokhov Acked-by: Johannes Berg Signed-off-by: Jean Delvare --- drivers/hwmon/ams/ams-input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c index 7b81e0c2c2d..48dbf7d6a66 100644 --- a/drivers/hwmon/ams/ams-input.c +++ b/drivers/hwmon/ams/ams-input.c @@ -20,11 +20,11 @@ #include "ams.h" static unsigned int joystick; -module_param(joystick, bool, 0644); +module_param(joystick, bool, S_IRUGO); MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); static unsigned int invert; -module_param(invert, bool, 0644); +module_param(invert, bool, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); static void ams_idev_poll(struct input_polled_dev *dev) -- cgit v1.2.3-70-g09d2 From ee4cd32ee8a68e22081f698602e4315fb4272853 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 17 Oct 2008 17:51:12 +0200 Subject: hwmon: (ams) Fix locking issues Use a separate mutex to serialize input device creation/removal, otheriwse we deadlock if we try to remove input device while it is being polled. Also do not take ams_info.lock when it is not needed. Signed-off-by: Dmitry Torokhov Signed-off-by: Johannes Berg Signed-off-by: Jean Delvare --- drivers/hwmon/ams/ams-core.c | 40 +++++++++++++---------------- drivers/hwmon/ams/ams-i2c.c | 4 --- drivers/hwmon/ams/ams-input.c | 59 +++++++++++++++++++++++++------------------ drivers/hwmon/ams/ams-pmu.c | 18 ++++--------- 4 files changed, 56 insertions(+), 65 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c index fbefa82a015..9b4a0e7a564 100644 --- a/drivers/hwmon/ams/ams-core.c +++ b/drivers/hwmon/ams/ams-core.c @@ -223,34 +223,28 @@ int __init ams_init(void) void ams_exit(void) { - mutex_lock(&ams_info.lock); - - if (ams_info.has_device) { - /* Remove input device */ - ams_input_exit(); + /* Remove input device */ + ams_input_exit(); - /* Shut down implementation */ - ams_info.exit(); - - /* Flush interrupt worker - * - * We do this after ams_info.exit(), because an interrupt might - * have arrived before disabling them. - */ - flush_scheduled_work(); + /* Remove attributes */ + device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); - /* Remove attributes */ - device_remove_file(&ams_info.of_dev->dev, &dev_attr_current); + /* Shut down implementation */ + ams_info.exit(); - /* Remove device */ - of_device_unregister(ams_info.of_dev); + /* Flush interrupt worker + * + * We do this after ams_info.exit(), because an interrupt might + * have arrived before disabling them. + */ + flush_scheduled_work(); - /* Remove handler */ - pmf_unregister_irq_client(&ams_shock_client); - pmf_unregister_irq_client(&ams_freefall_client); - } + /* Remove device */ + of_device_unregister(ams_info.of_dev); - mutex_unlock(&ams_info.lock); + /* Remove handler */ + pmf_unregister_irq_client(&ams_shock_client); + pmf_unregister_irq_client(&ams_freefall_client); } MODULE_AUTHOR("Stelian Pop, Michael Hanselmann"); diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c index da26de01068..2cbf8a6506c 100644 --- a/drivers/hwmon/ams/ams-i2c.c +++ b/drivers/hwmon/ams/ams-i2c.c @@ -261,8 +261,6 @@ int __init ams_i2c_init(struct device_node *np) { int result; - mutex_lock(&ams_info.lock); - /* Set implementation stuff */ ams_info.of_node = np; ams_info.exit = ams_i2c_exit; @@ -273,7 +271,5 @@ int __init ams_i2c_init(struct device_node *np) result = i2c_add_driver(&ams_i2c_driver); - mutex_unlock(&ams_info.lock); - return result; } diff --git a/drivers/hwmon/ams/ams-input.c b/drivers/hwmon/ams/ams-input.c index 48dbf7d6a66..8a712392cd3 100644 --- a/drivers/hwmon/ams/ams-input.c +++ b/drivers/hwmon/ams/ams-input.c @@ -27,6 +27,8 @@ static unsigned int invert; module_param(invert, bool, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); +static DEFINE_MUTEX(ams_input_mutex); + static void ams_idev_poll(struct input_polled_dev *dev) { struct input_dev *idev = dev->input; @@ -50,13 +52,11 @@ static void ams_idev_poll(struct input_polled_dev *dev) } /* Call with ams_info.lock held! */ -static void ams_input_enable(void) +static int ams_input_enable(void) { struct input_dev *input; s8 x, y, z; - - if (ams_info.idev) - return; + int error; ams_sensors(&x, &y, &z); ams_info.xcalib = x; @@ -65,7 +65,7 @@ static void ams_input_enable(void) ams_info.idev = input_allocate_polled_device(); if (!ams_info.idev) - return; + return -ENOMEM; ams_info.idev->poll = ams_idev_poll; ams_info.idev->poll_interval = 25; @@ -84,14 +84,18 @@ static void ams_input_enable(void) set_bit(EV_KEY, input->evbit); set_bit(BTN_TOUCH, input->keybit); - if (input_register_polled_device(ams_info.idev)) { + error = input_register_polled_device(ams_info.idev); + if (error) { input_free_polled_device(ams_info.idev); ams_info.idev = NULL; - return; + return error; } + + joystick = 1; + + return 0; } -/* Call with ams_info.lock held! */ static void ams_input_disable(void) { if (ams_info.idev) { @@ -99,6 +103,8 @@ static void ams_input_disable(void) input_free_polled_device(ams_info.idev); ams_info.idev = NULL; } + + joystick = 0; } static ssize_t ams_input_show_joystick(struct device *dev, @@ -110,39 +116,42 @@ static ssize_t ams_input_show_joystick(struct device *dev, static ssize_t ams_input_store_joystick(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - if (sscanf(buf, "%d\n", &joystick) != 1) + unsigned long enable; + int error = 0; + + if (strict_strtoul(buf, 0, &enable) || enable > 1) return -EINVAL; - mutex_lock(&ams_info.lock); + mutex_lock(&ams_input_mutex); - if (joystick) - ams_input_enable(); - else - ams_input_disable(); + if (enable != joystick) { + if (enable) + error = ams_input_enable(); + else + ams_input_disable(); + } - mutex_unlock(&ams_info.lock); + mutex_unlock(&ams_input_mutex); - return count; + return error ? error : count; } static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, ams_input_show_joystick, ams_input_store_joystick); -/* Call with ams_info.lock held! */ int ams_input_init(void) { - int result; - - result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); - - if (!result && joystick) + if (joystick) ams_input_enable(); - return result; + + return device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); } -/* Call with ams_info.lock held! */ void ams_input_exit(void) { - ams_input_disable(); device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); + + mutex_lock(&ams_input_mutex); + ams_input_disable(); + mutex_unlock(&ams_input_mutex); } diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c index 9463e9768f6..fb18b3d3162 100644 --- a/drivers/hwmon/ams/ams-pmu.c +++ b/drivers/hwmon/ams/ams-pmu.c @@ -149,8 +149,6 @@ int __init ams_pmu_init(struct device_node *np) const u32 *prop; int result; - mutex_lock(&ams_info.lock); - /* Set implementation stuff */ ams_info.of_node = np; ams_info.exit = ams_pmu_exit; @@ -161,10 +159,9 @@ int __init ams_pmu_init(struct device_node *np) /* Get PMU command, should be 0x4e, but we can never know */ prop = of_get_property(ams_info.of_node, "reg", NULL); - if (!prop) { - result = -ENODEV; - goto exit; - } + if (!prop) + return -ENODEV; + ams_pmu_cmd = ((*prop) >> 8) & 0xff; /* Disable interrupts */ @@ -175,7 +172,7 @@ int __init ams_pmu_init(struct device_node *np) result = ams_sensor_attach(); if (result < 0) - goto exit; + return result; /* Set default values */ ams_pmu_set_register(AMS_FF_LOW_LIMIT, 0x15); @@ -198,10 +195,5 @@ int __init ams_pmu_init(struct device_node *np) printk(KERN_INFO "ams: Found PMU based motion sensor\n"); - result = 0; - -exit: - mutex_unlock(&ams_info.lock); - - return result; + return 0; } -- cgit v1.2.3-70-g09d2 From dbee356262bb1c84cfa585530e33e7003534052a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (ams) Simplify IRQ handling routine Simplify the IRQ handling routine of ams driver. Signed-off-by: Dmitry Torokhov Signed-off-by: Johannes Berg Signed-off-by: Jean Delvare --- drivers/hwmon/ams/ams-core.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c index 9b4a0e7a564..6c9ace1b76f 100644 --- a/drivers/hwmon/ams/ams-core.c +++ b/drivers/hwmon/ams/ams-core.c @@ -99,39 +99,31 @@ static struct pmf_irq_client ams_shock_client = { */ static void ams_worker(struct work_struct *work) { - mutex_lock(&ams_info.lock); - - if (ams_info.has_device) { - unsigned long flags; + unsigned long flags; + u8 irqs_to_clear; - spin_lock_irqsave(&ams_info.irq_lock, flags); + mutex_lock(&ams_info.lock); - if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { - if (verbose) - printk(KERN_INFO "ams: freefall detected!\n"); + spin_lock_irqsave(&ams_info.irq_lock, flags); + irqs_to_clear = ams_info.worker_irqs; - ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; + if (ams_info.worker_irqs & AMS_IRQ_FREEFALL) { + if (verbose) + printk(KERN_INFO "ams: freefall detected!\n"); - /* we must call this with interrupts enabled */ - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - ams_info.clear_irq(AMS_IRQ_FREEFALL); - spin_lock_irqsave(&ams_info.irq_lock, flags); - } + ams_info.worker_irqs &= ~AMS_IRQ_FREEFALL; + } - if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { - if (verbose) - printk(KERN_INFO "ams: shock detected!\n"); + if (ams_info.worker_irqs & AMS_IRQ_SHOCK) { + if (verbose) + printk(KERN_INFO "ams: shock detected!\n"); - ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; + ams_info.worker_irqs &= ~AMS_IRQ_SHOCK; + } - /* we must call this with interrupts enabled */ - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - ams_info.clear_irq(AMS_IRQ_SHOCK); - spin_lock_irqsave(&ams_info.irq_lock, flags); - } + spin_unlock_irqrestore(&ams_info.irq_lock, flags); - spin_unlock_irqrestore(&ams_info.irq_lock, flags); - } + ams_info.clear_irq(irqs_to_clear); mutex_unlock(&ams_info.lock); } -- cgit v1.2.3-70-g09d2 From 69fc1feba2d5856ff74dedb6ae9d8c490210825c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (lm85) Rework the device detection Rework the device detection to make it clearer and faster in the general case (when a known device is found.) Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 123 +++++++++++++++++++++------------------------------ 1 file changed, 50 insertions(+), 73 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 12d446f54f9..3594a02f281 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1106,7 +1106,6 @@ static void lm85_init_client(struct i2c_client *client) static int lm85_detect(struct i2c_adapter *adapter, int address, int kind) { - int company, verstep; struct i2c_client *client; struct lm85_data *data; int err = 0; @@ -1117,10 +1116,6 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, goto ERROR0; } - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm85_{read,write}_value. */ - if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) { err = -ENOMEM; goto ERROR0; @@ -1132,75 +1127,57 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, client->adapter = adapter; client->driver = &lm85_driver; - /* Now, we do the remaining detection. */ - - company = lm85_read_value(client, LM85_REG_COMPANY); - verstep = lm85_read_value(client, LM85_REG_VERSTEP); - - dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with" - " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", - i2c_adapter_id(client->adapter), client->addr, - company, verstep); - - /* If auto-detecting, Determine the chip type. */ - if (kind <= 0) { - dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x ...\n", - i2c_adapter_id(adapter), address); - if (company == LM85_COMPANY_NATIONAL - && verstep == LM85_VERSTEP_LM85C) { - kind = lm85c; - } else if (company == LM85_COMPANY_NATIONAL - && verstep == LM85_VERSTEP_LM85B) { - kind = lm85b; - } else if (company == LM85_COMPANY_NATIONAL - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" - " Defaulting to LM85.\n", verstep); - kind = any_chip; - } else if (company == LM85_COMPANY_ANALOG_DEV - && verstep == LM85_VERSTEP_ADM1027) { - kind = adm1027; - } else if (company == LM85_COMPANY_ANALOG_DEV - && (verstep == LM85_VERSTEP_ADT7463 - || verstep == LM85_VERSTEP_ADT7463C)) { - kind = adt7463; - } else if (company == LM85_COMPANY_ANALOG_DEV - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" - " Defaulting to Generic LM85.\n", verstep); - kind = any_chip; - } else if (company == LM85_COMPANY_SMSC - && (verstep == LM85_VERSTEP_EMC6D100_A0 - || verstep == LM85_VERSTEP_EMC6D100_A1)) { - /* Unfortunately, we can't tell a '100 from a '101 - * from the registers. Since a '101 is a '100 - * in a package with fewer pins and therefore no - * 3.3V, 1.5V or 1.8V inputs, perhaps if those - * inputs read 0, then it's a '101. - */ - kind = emc6d100; - } else if (company == LM85_COMPANY_SMSC - && verstep == LM85_VERSTEP_EMC6D102) { - kind = emc6d102; - } else if (company == LM85_COMPANY_SMSC - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "lm85: Detected SMSC chip\n"); - dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x" - " Defaulting to Generic LM85.\n", verstep); - kind = any_chip; - } else if (kind == any_chip - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Generic LM85 Version 6 detected\n"); - /* Leave kind as "any_chip" */ - } else { - dev_dbg(&adapter->dev, "Autodetection failed\n"); - /* Not an LM85... */ - if (kind == any_chip) { /* User used force=x,y */ - dev_err(&adapter->dev, "Generic LM85 Version 6 not" - " found at %d,0x%02x. Try force_lm85c.\n", - i2c_adapter_id(adapter), address); + /* If auto-detecting, determine the chip type */ + if (kind < 0) { + int company = lm85_read_value(client, LM85_REG_COMPANY); + int verstep = lm85_read_value(client, LM85_REG_VERSTEP); + + dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " + "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + address, company, verstep); + + /* All supported chips have the version in common */ + if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) { + dev_dbg(&adapter->dev, "Autodetection failed: " + "unsupported version\n"); + goto ERROR1; + } + kind = any_chip; + + /* Now, refine the detection */ + if (company == LM85_COMPANY_NATIONAL) { + switch (verstep) { + case LM85_VERSTEP_LM85C: + kind = lm85c; + break; + case LM85_VERSTEP_LM85B: + kind = lm85b; + break; + } + } else if (company == LM85_COMPANY_ANALOG_DEV) { + switch (verstep) { + case LM85_VERSTEP_ADM1027: + kind = adm1027; + break; + case LM85_VERSTEP_ADT7463: + case LM85_VERSTEP_ADT7463C: + kind = adt7463; + break; } - err = 0; + } else if (company == LM85_COMPANY_SMSC) { + switch (verstep) { + case LM85_VERSTEP_EMC6D100_A0: + case LM85_VERSTEP_EMC6D100_A1: + /* Note: we can't tell a '100 from a '101 */ + kind = emc6d100; + break; + case LM85_VERSTEP_EMC6D102: + kind = emc6d102; + break; + } + } else { + dev_dbg(&adapter->dev, "Autodetection failed: " + "unknown vendor\n"); goto ERROR1; } } -- cgit v1.2.3-70-g09d2 From 34e7dc6ca4a663a1bb0a0a4e118426849dccd72d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (lm85) Implement the standard PWM frequency interface Implement the standard PWM frequency interface: pwm[1-*]_freq in units of 1 Hz, instead of the non-standard pwm[1-*]_auto_pwm_freq in units of 0.1 Hz. The old naming was not only non-standard, it was also confusing, because it suggested that the frequency value only applied in automatic fan speed mode, which isn't true. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- Documentation/hwmon/lm85 | 10 ------- drivers/hwmon/lm85.c | 77 ++++++++++++++++++++++++------------------------ 2 files changed, 38 insertions(+), 49 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85 index 6d41db7f17f..40062074129 100644 --- a/Documentation/hwmon/lm85 +++ b/Documentation/hwmon/lm85 @@ -163,16 +163,6 @@ configured individually according to the following options. * pwm#_auto_pwm_min - this specifies the PWM value for temp#_auto_temp_off temperature. (PWM value from 0 to 255) -* pwm#_auto_pwm_freq - select base frequency of PWM output. You can select - in range of 10.0 to 94.0 Hz in .1 Hz units. - (Values 100 to 940). - -The pwm#_auto_pwm_freq can be set to one of the following 8 values. Setting the -frequency to a value not on this list, will result in the next higher frequency -being selected. The actual device frequency may vary slightly from this -specification as designed by the manufacturer. Consult the datasheet for more -details. (PWM Frequency values: 100, 150, 230, 300, 380, 470, 620, 940) - * pwm#_auto_pwm_minctl - this flags selects for temp#_auto_temp_off temperature the bahaviour of fans. Write 1 to let fans spinning at pwm#_auto_pwm_min or write 0 to let them off. diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 3594a02f281..6b676df3547 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -191,8 +191,8 @@ static int RANGE_TO_REG(int range) #define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f] /* These are the PWM frequency encodings */ -static const int lm85_freq_map[] = { /* .1 Hz */ - 100, 150, 230, 300, 380, 470, 620, 940 +static const int lm85_freq_map[8] = { /* 1 Hz */ + 10, 15, 23, 30, 38, 47, 62, 94 }; static int FREQ_TO_REG(int freq) @@ -275,7 +275,6 @@ struct lm85_zone { struct lm85_autofan { u8 config; /* Register value */ - u8 freq; /* PWM frequency, encoded */ u8 min_pwm; /* Minimum PWM value, encoded */ u8 min_off; /* Min PWM or OFF below "limit", flag */ }; @@ -301,6 +300,7 @@ struct lm85_data { u16 fan[4]; /* Register value */ u16 fan_min[4]; /* Register value */ u8 pwm[3]; /* Register value */ + u8 pwm_freq[3]; /* Register encoding */ u8 temp_ext[3]; /* Decoded values */ u8 in_ext[8]; /* Decoded values */ u8 vid; /* Register value */ @@ -528,11 +528,38 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute return count; } +static ssize_t show_pwm_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf, "%d\n", FREQ_FROM_REG(data->pwm_freq[nr])); +} + +static ssize_t set_pwm_freq(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + data->pwm_freq[nr] = FREQ_TO_REG(val); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + (data->zone[nr].range << 4) + | data->pwm_freq[nr]); + mutex_unlock(&data->update_lock); + return count; +} + #define show_pwm_reg(offset) \ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ show_pwm, set_pwm, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ - show_pwm_enable, set_pwm_enable, offset - 1) + show_pwm_enable, set_pwm_enable, offset - 1); \ +static SENSOR_DEVICE_ATTR(pwm##offset##_freq, S_IRUGO | S_IWUSR, \ + show_pwm_freq, set_pwm_freq, offset - 1) show_pwm_reg(1); show_pwm_reg(2); @@ -761,31 +788,6 @@ static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, return count; } -static ssize_t show_pwm_auto_pwm_freq(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int nr = to_sensor_dev_attr(attr)->index; - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%d\n", FREQ_FROM_REG(data->autofan[nr].freq)); -} - -static ssize_t set_pwm_auto_pwm_freq(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(attr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - mutex_lock(&data->update_lock); - data->autofan[nr].freq = FREQ_TO_REG(val); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - (data->zone[nr].range << 4) - | data->autofan[nr].freq); - mutex_unlock(&data->update_lock); - return count; -} - #define pwm_auto(offset) \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels, \ S_IRUGO | S_IWUSR, show_pwm_auto_channels, \ @@ -795,10 +797,7 @@ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_min, \ set_pwm_auto_pwm_min, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, \ S_IRUGO | S_IWUSR, show_pwm_auto_pwm_minctl, \ - set_pwm_auto_pwm_minctl, offset - 1); \ -static SENSOR_DEVICE_ATTR(pwm##offset##_auto_pwm_freq, \ - S_IRUGO | S_IWUSR, show_pwm_auto_pwm_freq, \ - set_pwm_auto_pwm_freq, offset - 1); + set_pwm_auto_pwm_minctl, offset - 1) pwm_auto(1); pwm_auto(2); @@ -867,7 +866,7 @@ static ssize_t set_temp_auto_temp_min(struct device *dev, TEMP_FROM_REG(data->zone[nr].limit)); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); + | (data->pwm_freq[nr] & 0x07)); /* Update temp_auto_hyst and temp_auto_off */ data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG( @@ -910,7 +909,7 @@ static ssize_t set_temp_auto_temp_max(struct device *dev, val - min); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); + | (data->pwm_freq[nr] & 0x07)); mutex_unlock(&data->update_lock); return count; } @@ -984,6 +983,9 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_freq.dev_attr.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, + &sensor_dev_attr_pwm3_freq.dev_attr.attr, &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, @@ -1026,9 +1028,6 @@ static struct attribute *lm85_attributes[] = { &sensor_dev_attr_pwm1_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm2_auto_pwm_minctl.dev_attr.attr, &sensor_dev_attr_pwm3_auto_pwm_minctl.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_pwm_freq.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_pwm_freq.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_pwm_freq.dev_attr.attr, &sensor_dev_attr_temp1_auto_temp_off.dev_attr.attr, &sensor_dev_attr_temp2_auto_temp_off.dev_attr.attr, @@ -1458,7 +1457,7 @@ static struct lm85_data *lm85_update_device(struct device *dev) data->autofan[i].config = lm85_read_value(client, LM85_REG_AFAN_CONFIG(i)); val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i)); - data->autofan[i].freq = val & 0x07; + data->pwm_freq[i] = val & 0x07; data->zone[i].range = val >> 4; data->autofan[i].min_pwm = lm85_read_value(client, LM85_REG_AFAN_MINPWM(i)); -- cgit v1.2.3-70-g09d2 From 86010c982db2030aad2c31f178b208964f5f6806 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:13 +0200 Subject: hwmon: (lm85) Select the closest PWM frequency The LM85 and compatible chips only support 8 arbitrary PWM frequencies. The algorithm to pick one of them based on the user input is not optimum. Improve it to always pick the closest supported frequency. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 6b676df3547..c323c2b8980 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -199,10 +199,9 @@ static int FREQ_TO_REG(int freq) { int i; - if (freq >= lm85_freq_map[7]) - return 7; + /* Find the closest match */ for (i = 0; i < 7; ++i) - if (freq <= lm85_freq_map[i]) + if (freq <= (lm85_freq_map[i] + lm85_freq_map[i + 1]) / 2) break; return i; } -- cgit v1.2.3-70-g09d2 From 8a0795d9b8ce2247f9b34da81f8a229702c90f2d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:14 +0200 Subject: hwmon: (lm85) Support different PWM frequency tables The Analog Devices and SMSC devices supported by the lm85 driver do not have the same PWM frequency table as the National Semiconductor devices. Add support for per-device frequency tables. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index c323c2b8980..3a84dc863df 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -5,6 +5,7 @@ Copyright (c) 2002, 2003 Philip Pokorny Copyright (c) 2003 Margit Schubert-While Copyright (c) 2004 Justin Thiessen + Copyright (C) 2007, 2008 Jean Delvare Chip details at @@ -192,20 +193,27 @@ static int RANGE_TO_REG(int range) /* These are the PWM frequency encodings */ static const int lm85_freq_map[8] = { /* 1 Hz */ - 10, 15, 23, 30, 38, 47, 62, 94 + 10, 15, 23, 30, 38, 47, 61, 94 +}; +static const int adm1027_freq_map[8] = { /* 1 Hz */ + 11, 15, 22, 29, 35, 44, 59, 88 }; -static int FREQ_TO_REG(int freq) +static int FREQ_TO_REG(const int *map, int freq) { int i; /* Find the closest match */ for (i = 0; i < 7; ++i) - if (freq <= (lm85_freq_map[i] + lm85_freq_map[i + 1]) / 2) + if (freq <= (map[i] + map[i + 1]) / 2) break; return i; } -#define FREQ_FROM_REG(val) lm85_freq_map[(val) & 0x07] + +static int FREQ_FROM_REG(const int *map, u8 reg) +{ + return map[reg & 0x07]; +} /* Since we can't use strings, I'm abusing these numbers * to stand in for the following meanings: @@ -283,6 +291,7 @@ struct lm85_autofan { struct lm85_data { struct i2c_client client; struct device *hwmon_dev; + const int *freq_map; enum chips type; struct mutex update_lock; @@ -532,7 +541,8 @@ static ssize_t show_pwm_freq(struct device *dev, { int nr = to_sensor_dev_attr(attr)->index; struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%d\n", FREQ_FROM_REG(data->pwm_freq[nr])); + return sprintf(buf, "%d\n", FREQ_FROM_REG(data->freq_map, + data->pwm_freq[nr])); } static ssize_t set_pwm_freq(struct device *dev, @@ -544,7 +554,7 @@ static ssize_t set_pwm_freq(struct device *dev, long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); - data->pwm_freq[nr] = FREQ_TO_REG(val); + data->pwm_freq[nr] = FREQ_TO_REG(data->freq_map, val); lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), (data->zone[nr].range << 4) | data->pwm_freq[nr]); @@ -1184,24 +1194,31 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, switch (kind) { case lm85b: type_name = "lm85b"; + data->freq_map = lm85_freq_map; break; case lm85c: type_name = "lm85c"; + data->freq_map = lm85_freq_map; break; case adm1027: type_name = "adm1027"; + data->freq_map = adm1027_freq_map; break; case adt7463: type_name = "adt7463"; + data->freq_map = adm1027_freq_map; break; case emc6d100: type_name = "emc6d100"; + data->freq_map = adm1027_freq_map; break; case emc6d102: type_name = "emc6d102"; + data->freq_map = adm1027_freq_map; break; default: type_name = "lm85"; + data->freq_map = lm85_freq_map; } strlcpy(client->name, type_name, I2C_NAME_SIZE); -- cgit v1.2.3-70-g09d2 From 67712d01929295112b55fedd0b3c13f7d5c34f98 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:14 +0200 Subject: hwmon: (lm85) Convert to a new-style i2c driver The new-style lm85 driver implements the optional detect() callback to cover the use cases of the legacy driver. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 121 +++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 57 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 3a84dc863df..4ee9388df98 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -289,7 +289,6 @@ struct lm85_autofan { /* For each registered chip, we need to keep some data in memory. The structure is dynamically allocated. */ struct lm85_data { - struct i2c_client client; struct device *hwmon_dev; const int *freq_map; enum chips type; @@ -318,22 +317,40 @@ struct lm85_data { struct lm85_zone zone[3]; }; -static int lm85_attach_adapter(struct i2c_adapter *adapter); -static int lm85_detect(struct i2c_adapter *adapter, int address, - int kind); -static int lm85_detach_client(struct i2c_client *client); +static int lm85_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int lm85_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int lm85_remove(struct i2c_client *client); static int lm85_read_value(struct i2c_client *client, u8 reg); static void lm85_write_value(struct i2c_client *client, u8 reg, int value); static struct lm85_data *lm85_update_device(struct device *dev); +static const struct i2c_device_id lm85_id[] = { + { "adm1027", adm1027 }, + { "adt7463", adt7463 }, + { "lm85", any_chip }, + { "lm85b", lm85b }, + { "lm85c", lm85c }, + { "emc6d100", emc6d100 }, + { "emc6d101", emc6d100 }, + { "emc6d102", emc6d102 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm85_id); + static struct i2c_driver lm85_driver = { + .class = I2C_CLASS_HWMON, .driver = { .name = "lm85", }, - .attach_adapter = lm85_attach_adapter, - .detach_client = lm85_detach_client, + .probe = lm85_probe, + .remove = lm85_remove, + .id_table = lm85_id, + .detect = lm85_detect, + .address_data = &addr_data, }; @@ -965,13 +982,6 @@ temp_auto(1); temp_auto(2); temp_auto(3); -static int lm85_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_probe(adapter, &addr_data, lm85_detect); -} - static struct attribute *lm85_attributes[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, @@ -1111,30 +1121,19 @@ static void lm85_init_client(struct i2c_client *client) dev_warn(&client->dev, "Device is not ready\n"); } -static int lm85_detect(struct i2c_adapter *adapter, int address, - int kind) +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm85_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { - struct i2c_client *client; - struct lm85_data *data; - int err = 0; + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; const char *type_name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { /* We need to be able to do byte I/O */ - goto ERROR0; + return -ENODEV; } - if (!(data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR0; - } - - client = &data->client; - i2c_set_clientdata(client, data); - client->addr = address; - client->adapter = adapter; - client->driver = &lm85_driver; - /* If auto-detecting, determine the chip type */ if (kind < 0) { int company = lm85_read_value(client, LM85_REG_COMPANY); @@ -1148,7 +1147,7 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC) { dev_dbg(&adapter->dev, "Autodetection failed: " "unsupported version\n"); - goto ERROR1; + return -ENODEV; } kind = any_chip; @@ -1186,50 +1185,62 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, } else { dev_dbg(&adapter->dev, "Autodetection failed: " "unknown vendor\n"); - goto ERROR1; + return -ENODEV; } } - /* Fill in the chip specific driver values */ switch (kind) { case lm85b: type_name = "lm85b"; - data->freq_map = lm85_freq_map; break; case lm85c: type_name = "lm85c"; - data->freq_map = lm85_freq_map; break; case adm1027: type_name = "adm1027"; - data->freq_map = adm1027_freq_map; break; case adt7463: type_name = "adt7463"; - data->freq_map = adm1027_freq_map; break; case emc6d100: type_name = "emc6d100"; - data->freq_map = adm1027_freq_map; break; case emc6d102: type_name = "emc6d102"; - data->freq_map = adm1027_freq_map; break; default: type_name = "lm85"; - data->freq_map = lm85_freq_map; } - strlcpy(client->name, type_name, I2C_NAME_SIZE); + strlcpy(info->type, type_name, I2C_NAME_SIZE); + + return 0; +} - /* Fill in the remaining client fields */ - data->type = kind; +static int lm85_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm85_data *data; + int err; + + data = kzalloc(sizeof(struct lm85_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->type = id->driver_data; mutex_init(&data->update_lock); - /* Tell the I2C layer a new client has arrived */ - err = i2c_attach_client(client); - if (err) - goto ERROR1; + /* Fill in the chip specific driver values */ + switch (data->type) { + case adm1027: + case adt7463: + case emc6d100: + case emc6d102: + data->freq_map = adm1027_freq_map; + break; + default: + data->freq_map = lm85_freq_map; + } /* Set the VRM version */ data->vrm = vid_which_vrm(); @@ -1240,18 +1251,18 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &lm85_group); if (err) - goto ERROR2; + goto ERROR1; /* The ADT7463 has an optional VRM 10 mode where pin 21 is used as a sixth digital VID input rather than an analog input. */ data->vid = lm85_read_value(client, LM85_REG_VID); - if (!(kind == adt7463 && (data->vid & 0x80))) + if (!(data->type == adt7463 && (data->vid & 0x80))) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in4))) goto ERROR3; /* The EMC6D100 has 3 additional voltage inputs */ - if (kind == emc6d100) + if (data->type == emc6d100) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in567))) goto ERROR3; @@ -1268,17 +1279,14 @@ static int lm85_detect(struct i2c_adapter *adapter, int address, ERROR3: sysfs_remove_group(&client->dev.kobj, &lm85_group); sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); - if (kind == emc6d100) + if (data->type == emc6d100) sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - ERROR2: - i2c_detach_client(client); ERROR1: kfree(data); - ERROR0: return err; } -static int lm85_detach_client(struct i2c_client *client) +static int lm85_remove(struct i2c_client *client) { struct lm85_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); @@ -1286,7 +1294,6 @@ static int lm85_detach_client(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); if (data->type == emc6d100) sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - i2c_detach_client(client); kfree(data); return 0; } -- cgit v1.2.3-70-g09d2 From 1b92adaddd7e96e1ba68d4f17a25747ab8057efe Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:14 +0200 Subject: hwmon: (lm85) Simplify RANGE_TO_REG Function RANGE_TO_REG can easily be simplified. Credits go to Herbert Poetzl for indirectly suggesting this to me. I tested that the new implementation returns the same result as the original implementation for all input values. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 4ee9388df98..94a10ecd24d 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -174,20 +174,13 @@ static int RANGE_TO_REG(int range) { int i; - if (range >= lm85_range_map[15]) - return 15; - /* Find the closest match */ - for (i = 14; i >= 0; --i) { - if (range >= lm85_range_map[i]) { - if ((lm85_range_map[i + 1] - range) < - (range - lm85_range_map[i])) - return i + 1; - return i; - } + for (i = 0; i < 15; ++i) { + if (range <= (lm85_range_map[i] + lm85_range_map[i + 1]) / 2) + break; } - return 0; + return i; } #define RANGE_FROM_REG(val) lm85_range_map[(val) & 0x0f] -- cgit v1.2.3-70-g09d2 From f908037a01e53a48ff8eb049bb4a1fbb15449908 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:14 +0200 Subject: hwmon: (lm85) Better label names Label names ERROR1 and ERROR3 aren't exactly explicit. Change them for better names that indicate what we are up to. Signed-off-by: Jean Delvare Acked-by: Herbert Poetzl --- drivers/hwmon/lm85.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 94a10ecd24d..3ff0285396f 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1244,7 +1244,7 @@ static int lm85_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &lm85_group); if (err) - goto ERROR1; + goto err_kfree; /* The ADT7463 has an optional VRM 10 mode where pin 21 is used as a sixth digital VID input rather than an analog input. */ @@ -1252,29 +1252,29 @@ static int lm85_probe(struct i2c_client *client, if (!(data->type == adt7463 && (data->vid & 0x80))) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in4))) - goto ERROR3; + goto err_remove_files; /* The EMC6D100 has 3 additional voltage inputs */ if (data->type == emc6d100) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in567))) - goto ERROR3; + goto err_remove_files; data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto ERROR3; + goto err_remove_files; } return 0; /* Error out and cleanup code */ - ERROR3: + err_remove_files: sysfs_remove_group(&client->dev.kobj, &lm85_group); sysfs_remove_group(&client->dev.kobj, &lm85_group_in4); if (data->type == emc6d100) sysfs_remove_group(&client->dev.kobj, &lm85_group_in567); - ERROR1: + err_kfree: kfree(data); return err; } -- cgit v1.2.3-70-g09d2 From 47c15532ddcd6818f51cb15f914d63864b3ee9ab Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:15 +0200 Subject: hwmon: (lm78) Fix I/O resource conflict with PNP Only request I/O ports 0x295-0x296 instead of the full I/O address range. This solves a conflict with PNP resources on a few motherboards. Also request the I/O ports in two parts (4 low ports, 4 high ports) during device detection, otherwise the PNP resource make the request (and thus the detection) fail. This is the exact same fix that was applied to driver w83781d in March 2008 to address the same problem: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=2961cb22ef02850d90e7a12c28a14d74e327df8d Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index ed7859f0e16..2c96d8a548f 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -655,7 +655,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, LM78_EXTENT, "lm78")) { + if (!request_region(res->start + LM78_ADDR_REG_OFFSET, 2, "lm78")) { err = -EBUSY; goto exit; } @@ -699,7 +699,7 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_name); kfree(data); exit_release_region: - release_region(res->start, LM78_EXTENT); + release_region(res->start + LM78_ADDR_REG_OFFSET, 2); exit: return err; } @@ -711,7 +711,7 @@ static int __devexit lm78_isa_remove(struct platform_device *pdev) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &lm78_group); device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr, LM78_EXTENT); + release_region(data->client.addr + LM78_ADDR_REG_OFFSET, 2); kfree(data); return 0; @@ -837,8 +837,17 @@ static int __init lm78_isa_found(unsigned short address) { int val, save, found = 0; - if (!request_region(address, LM78_EXTENT, "lm78")) + /* We have to request the region in two parts because some + boards declare base+4 to base+7 as a PNP device */ + if (!request_region(address, 4, "lm78")) { + pr_debug("lm78: Failed to request low part of region\n"); return 0; + } + if (!request_region(address + 4, 4, "lm78")) { + pr_debug("lm78: Failed to request high part of region\n"); + release_region(address, 4); + return 0; + } #define REALLY_SLOW_IO /* We need the timeouts for at least some LM78-like @@ -901,7 +910,8 @@ static int __init lm78_isa_found(unsigned short address) val & 0x80 ? "LM79" : "LM78", (int)address); release: - release_region(address, LM78_EXTENT); + release_region(address + 4, 4); + release_region(address, 4); return found; } -- cgit v1.2.3-70-g09d2 From 18c73f90421f9a87a0f6bc3a08880d0f1f9b2a74 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:15 +0200 Subject: hwmon: (lm78) Detect alias chips The LM78 and LM79 can be accessed either on the I2C bus or the ISA bus. We must not access the same chip through both interfaces. So far we were relying on the user passing the correct ignore parameter to skip the registration of the I2C interface as suggested by sensors-detect, but this is fragile: the user may load the lm78 driver without running sensors-detect, and the i2c bus numbers are not stable across reboots and hardware changes. So, better detect alias chips in the driver directly, and skip any I2C chip which is obviously an alias of the ISA chip. This is done by comparing the value of 26 selected registers. Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 78 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 13 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 2c96d8a548f..ec601bbf91b 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -120,10 +120,7 @@ static inline int TEMP_FROM_REG(s8 val) LM78 chips available (well, actually, that is probably never done; but it is a clean illustration of how to handle a case like that). Finally, a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. Fortunately, in the case of the LM78 at - least, a register tells us what SMBus address we are on, so that helps - a bit - except if there could be more than one SMBus. Groan. No solution - for this yet. */ + not like to detect it double. */ /* For ISA chips, we abuse the i2c_client addr and name fields. We also use the driver field to differentiate between I2C and ISA chips. */ @@ -457,12 +454,24 @@ static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); /* This function is called when: * lm78_driver is inserted (when this module is loaded), for each available adapter - * when a new adapter is inserted (and lm78_driver is still present) */ + * when a new adapter is inserted (and lm78_driver is still present) + We block updates of the ISA device to minimize the risk of concurrent + access to the same LM78 chip through different interfaces. */ static int lm78_attach_adapter(struct i2c_adapter *adapter) { + struct lm78_data *data; + int err; + if (!(adapter->class & I2C_CLASS_HWMON)) return 0; - return i2c_probe(adapter, &addr_data, lm78_detect); + + data = pdev ? platform_get_drvdata(pdev) : NULL; + if (data) + mutex_lock(&data->update_lock); + err = i2c_probe(adapter, &addr_data, lm78_detect); + if (data) + mutex_unlock(&data->update_lock); + return err; } static struct attribute *lm78_attributes[] = { @@ -531,6 +540,40 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static DEVICE_ATTR(name, S_IRUGO, show_name, 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) +{ + struct lm78_data *i2c, *isa; + int i; + + if (!pdev) /* No ISA chip */ + return 0; + + i2c = i2c_get_clientdata(client); + isa = platform_get_drvdata(pdev); + + if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr) + return 0; /* Address doesn't match */ + if ((lm78_read_value(isa, LM78_REG_CHIPID) & 0xfe) != (chipid & 0xfe)) + return 0; /* Chip type doesn't match */ + + /* We compare all the limit registers, the config register and the + * interrupt mask registers */ + for (i = 0x2b; i <= 0x3d; i++) { + if (lm78_read_value(isa, i) != lm78_read_value(i2c, i)) + return 0; + } + if (lm78_read_value(isa, LM78_REG_CONFIG) != + lm78_read_value(i2c, LM78_REG_CONFIG)) + return 0; + for (i = 0x43; i <= 0x46; i++) { + if (lm78_read_value(isa, i) != lm78_read_value(i2c, i)) + return 0; + } + + return 1; +} + /* This function is called by i2c_probe */ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) { @@ -589,6 +632,13 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) err = -ENODEV; goto ERROR2; } + + if (lm78_alias_detect(new_client, i)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + err = -ENODEV; + goto ERROR2; + } } if (kind == lm78) { @@ -959,14 +1009,12 @@ static int __init sm_lm78_init(void) { int res; - res = i2c_add_driver(&lm78_driver); - if (res) - goto exit; - + /* 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) - goto exit_unreg_i2c_driver; + goto exit; /* Sets global pdev as a side effect */ res = lm78_isa_device_add(isa_address); @@ -974,12 +1022,16 @@ 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_unreg_i2c_driver: - i2c_del_driver(&lm78_driver); exit: return res; } -- cgit v1.2.3-70-g09d2 From ad3273be8e2a5bfe16f4940beef3da308daf259a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:15 +0200 Subject: hwmon: (lm78) Prevent misdetection of Winbond chips The LM78 detection is relatively weak, and sometimes recent Winbond chips can be misdetected as an LM78. We have had repeated reports of this happening. We have an explicit check against this for the ISA access, do the same for I2C access now. Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index ec601bbf91b..f284ecbb9ca 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -613,6 +613,12 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) err = -ENODEV; goto ERROR2; } + /* Explicitly prevent the misdetection of Winbond chips */ + i = lm78_read_value(data, 0x4f); + if (i == 0xa3 || i == 0x5c) { + err = -ENODEV; + goto ERROR2; + } } /* Determine the chip type. */ -- cgit v1.2.3-70-g09d2 From 6e1b5029dc0e4cfa765309312ebdc88711e37a20 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:15 +0200 Subject: hwmon: (lm78) Stop abusing struct i2c_client for ISA devices Upcoming changes to the I2C part of the lm78 driver will cause ISA devices to no longer have a struct i2c_client at hand. So, we must stop (ab)using it now. Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index f284ecbb9ca..177eaddde32 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -114,22 +114,16 @@ static inline int TEMP_FROM_REG(s8 val) #define DIV_FROM_REG(val) (1 << (val)) -/* There are some complications in a module like this. First off, LM78 chips - may be both present on the SMBus and the ISA bus, and we have to handle - those cases separately at some places. Second, there might be several - LM78 chips available (well, actually, that is probably never done; but - it is a clean illustration of how to handle a case like that). Finally, - a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. */ - -/* For ISA chips, we abuse the i2c_client addr and name fields. We also use - the driver field to differentiate between I2C and ISA chips. */ struct lm78_data { struct i2c_client client; struct device *hwmon_dev; struct mutex lock; enum chips type; + /* For ISA device only */ + const char *name; + int isa_addr; + struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -536,7 +530,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute { struct lm78_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); + return sprintf(buf, "%s\n", data->name); } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); @@ -707,7 +701,6 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) int err; struct lm78_data *data; struct resource *res; - const char *name; /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -721,18 +714,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) goto exit_release_region; } mutex_init(&data->lock); - data->client.addr = res->start; - i2c_set_clientdata(&data->client, data); + data->isa_addr = res->start; platform_set_drvdata(pdev, data); if (lm78_read_value(data, LM78_REG_CHIPID) & 0x80) { data->type = lm79; - name = "lm79"; + data->name = "lm79"; } else { data->type = lm78; - name = "lm78"; + data->name = "lm78"; } - strlcpy(data->client.name, name, I2C_NAME_SIZE); /* Initialize the LM78 chip */ lm78_init_device(data); @@ -763,13 +754,16 @@ static int __devinit lm78_isa_probe(struct platform_device *pdev) 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); - release_region(data->client.addr + LM78_ADDR_REG_OFFSET, 2); kfree(data); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + release_region(res->start + LM78_ADDR_REG_OFFSET, 2); + return 0; } @@ -785,8 +779,8 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) if (!client->driver) { /* ISA device */ int res; mutex_lock(&data->lock); - outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); - res = inb_p(client->addr + LM78_DATA_REG_OFFSET); + outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); + res = inb_p(data->isa_addr + LM78_DATA_REG_OFFSET); mutex_unlock(&data->lock); return res; } else @@ -806,8 +800,8 @@ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) if (!client->driver) { /* ISA device */ mutex_lock(&data->lock); - outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); - outb_p(value, client->addr + LM78_DATA_REG_OFFSET); + outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); + outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET); mutex_unlock(&data->lock); return 0; } else -- cgit v1.2.3-70-g09d2 From 0c6e97317102a8f480bdfb418f19fe989ad1c047 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (lm78) Convert to a new-style i2c driver The new-style lm78 driver implements the optional detect() callback to cover the use cases of the legacy driver. Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 194 +++++++++++++++++++++++---------------------------- 1 file changed, 89 insertions(+), 105 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 177eaddde32..b5e3b285169 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -115,7 +115,7 @@ static inline int TEMP_FROM_REG(s8 val) #define DIV_FROM_REG(val) (1 << (val)) struct lm78_data { - struct i2c_client client; + struct i2c_client *client; struct device *hwmon_dev; struct mutex lock; enum chips type; @@ -142,9 +142,11 @@ struct lm78_data { }; -static int lm78_attach_adapter(struct i2c_adapter *adapter); -static int lm78_detect(struct i2c_adapter *adapter, int address, int kind); -static int lm78_detach_client(struct i2c_client *client); +static int lm78_i2c_detect(struct i2c_client *client, int kind, + 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); @@ -155,12 +157,23 @@ 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", }, - .attach_adapter = lm78_attach_adapter, - .detach_client = lm78_detach_client, + .probe = lm78_i2c_probe, + .remove = lm78_i2c_remove, + .id_table = lm78_i2c_id, + .detect = lm78_i2c_detect, + .address_data = &addr_data, }; static struct platform_driver lm78_isa_driver = { @@ -445,29 +458,6 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7); static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4); -/* This function is called when: - * lm78_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and lm78_driver is still present) - We block updates of the ISA device to minimize the risk of concurrent - access to the same LM78 chip through different interfaces. */ -static int lm78_attach_adapter(struct i2c_adapter *adapter) -{ - struct lm78_data *data; - int err; - - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - - data = pdev ? platform_get_drvdata(pdev) : NULL; - if (data) - mutex_lock(&data->update_lock); - err = i2c_probe(adapter, &addr_data, lm78_detect); - if (data) - mutex_unlock(&data->update_lock); - return err; -} - static struct attribute *lm78_attributes[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, @@ -537,13 +527,11 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, 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) { - struct lm78_data *i2c, *isa; + struct lm78_data *isa; int i; if (!pdev) /* No ISA chip */ return 0; - - i2c = i2c_get_clientdata(client); isa = platform_get_drvdata(pdev); if (lm78_read_value(isa, LM78_REG_I2C_ADDR) != client->addr) @@ -554,70 +542,55 @@ static int lm78_alias_detect(struct i2c_client *client, u8 chipid) /* We compare all the limit registers, the config register and the * interrupt mask registers */ for (i = 0x2b; i <= 0x3d; i++) { - if (lm78_read_value(isa, i) != lm78_read_value(i2c, i)) + if (lm78_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) return 0; } if (lm78_read_value(isa, LM78_REG_CONFIG) != - lm78_read_value(i2c, LM78_REG_CONFIG)) + i2c_smbus_read_byte_data(client, LM78_REG_CONFIG)) return 0; for (i = 0x43; i <= 0x46; i++) { - if (lm78_read_value(isa, i) != lm78_read_value(i2c, i)) + if (lm78_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) return 0; } return 1; } -/* This function is called by i2c_probe */ -static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) +static int lm78_i2c_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { - int i, err; - struct i2c_client *new_client; - struct lm78_data *data; - const char *client_name = ""; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -ENODEV; - goto ERROR1; - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm78_{read,write}_value. */ + int i; + struct lm78_data *isa = pdev ? platform_get_drvdata(pdev) : NULL; + const char *client_name; + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; - if (!(data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm78_driver; + /* We block updates of the ISA device to minimize the risk of + concurrent access to the same LM78 chip through different + interfaces. */ + if (isa) + mutex_lock(&isa->update_lock); - /* Now, we do the remaining detection. */ if (kind < 0) { - if (lm78_read_value(data, LM78_REG_CONFIG) & 0x80) { - err = -ENODEV; - goto ERROR2; - } - if (lm78_read_value(data, LM78_REG_I2C_ADDR) != - address) { - err = -ENODEV; - goto ERROR2; - } + if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80) + || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) + != address) + goto err_nodev; + /* Explicitly prevent the misdetection of Winbond chips */ - i = lm78_read_value(data, 0x4f); - if (i == 0xa3 || i == 0x5c) { - err = -ENODEV; - goto ERROR2; - } + i = i2c_smbus_read_byte_data(client, 0x4f); + if (i == 0xa3 || i == 0x5c) + goto err_nodev; } /* Determine the chip type. */ if (kind <= 0) { - i = lm78_read_value(data, LM78_REG_CHIPID); + i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID); if (i == 0x00 || i == 0x20 /* LM78 */ || i == 0x40) /* LM78-J */ kind = lm78; @@ -629,40 +602,59 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) "parameter for unknown chip at " "adapter %d, address 0x%02x\n", i2c_adapter_id(adapter), address); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } - if (lm78_alias_detect(new_client, i)) { + if (lm78_alias_detect(client, i)) { dev_dbg(&adapter->dev, "Device at 0x%02x appears to " "be the same as ISA device\n", address); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } } - if (kind == lm78) { - client_name = "lm78"; - } else if (kind == lm79) { + if (isa) + mutex_unlock(&isa->update_lock); + + switch (kind) { + case lm79: client_name = "lm79"; + break; + default: + client_name = "lm78"; } + strlcpy(info->type, client_name, I2C_NAME_SIZE); - /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - data->type = kind; + return 0; - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR2; + err_nodev: + if (isa) + mutex_unlock(&isa->update_lock); + return -ENODEV; +} + +static int lm78_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm78_data *data; + int err; + + data = kzalloc(sizeof(struct lm78_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + data->type = id->driver_data; /* Initialize the LM78 chip */ lm78_init_device(data); /* Register sysfs hooks */ - if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group))) + err = sysfs_create_group(&client->dev.kobj, &lm78_group); + if (err) goto ERROR3; - data->hwmon_dev = hwmon_device_register(&new_client->dev); + data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto ERROR4; @@ -671,26 +663,18 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind) return 0; ERROR4: - sysfs_remove_group(&new_client->dev.kobj, &lm78_group); + sysfs_remove_group(&client->dev.kobj, &lm78_group); ERROR3: - i2c_detach_client(new_client); -ERROR2: kfree(data); -ERROR1: return err; } -static int lm78_detach_client(struct i2c_client *client) +static int lm78_i2c_remove(struct i2c_client *client) { struct lm78_data *data = i2c_get_clientdata(client); - int err; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &lm78_group); - - if ((err = i2c_detach_client(client))) - return err; - kfree(data); return 0; @@ -774,9 +758,9 @@ static int __devexit lm78_isa_remove(struct platform_device *pdev) would slow down the LM78 access and should not be necessary. */ static int lm78_read_value(struct lm78_data *data, u8 reg) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; - if (!client->driver) { /* ISA device */ + if (!client) { /* ISA device */ int res; mutex_lock(&data->lock); outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); @@ -796,9 +780,9 @@ static int lm78_read_value(struct lm78_data *data, u8 reg) nowhere else be necessary! */ static int lm78_write_value(struct lm78_data *data, u8 reg, u8 value) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; - if (!client->driver) { /* ISA device */ + if (!client) { /* ISA device */ mutex_lock(&data->lock); outb_p(reg, data->isa_addr + LM78_ADDR_REG_OFFSET); outb_p(value, data->isa_addr + LM78_DATA_REG_OFFSET); -- cgit v1.2.3-70-g09d2 From 4ed1077953f531b3fef4af4b4ade48a828c48869 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (it87) Fix thermal sensor type values The it87 driver doesn't follow the standard sensor type values as documented in Documentation/hwmon/sysfs-interface. It uses value 2 for thermistors instead of value 4. This causes "sensors" to tell the user that the chip is setup for a transistor while it is actually setup for a thermistor. Using value 4 for thermistors solves the problem. For compatibility reasons, we still accept value 2 but emit a warning message so that users update their configuration files. Signed-off-by: Jean Delvare Acked-by: Hans de Goede --- Documentation/hwmon/it87 | 4 ++-- drivers/hwmon/it87.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 3496b7020e7..042c0415140 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -136,10 +136,10 @@ once-only alarms. The IT87xx only updates its values each 1.5 seconds; reading it more often will do no harm, but will return 'old' values. -To change sensor N to a thermistor, 'echo 2 > tempN_type' where N is 1, 2, +To change sensor N to a thermistor, 'echo 4 > tempN_type' where N is 1, 2, or 3. To change sensor N to a thermal diode, 'echo 3 > tempN_type'. Give 0 for unused sensor. Any other value is invalid. To configure this at -startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor; +startup, consult lm_sensors's /etc/sensors.conf. (4 = thermistor; 3 = thermal diode) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index d793cc01199..b74c95735f9 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -477,7 +477,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, if (reg & (1 << nr)) return sprintf(buf, "3\n"); /* thermal diode */ if (reg & (8 << nr)) - return sprintf(buf, "2\n"); /* thermistor */ + return sprintf(buf, "4\n"); /* thermistor */ return sprintf(buf, "0\n"); /* disabled */ } static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, @@ -493,10 +493,15 @@ static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, data->sensor &= ~(1 << nr); data->sensor &= ~(8 << nr); - /* 3 = thermal diode; 2 = thermistor; 0 = disabled */ + if (val == 2) { /* backwards compatibility */ + dev_warn(dev, "Sensor type 2 is deprecated, please use 4 " + "instead\n"); + val = 4; + } + /* 3 = thermal diode; 4 = thermistor; 0 = disabled */ if (val == 3) data->sensor |= 1 << nr; - else if (val == 2) + else if (val == 4) data->sensor |= 8 << nr; else if (val != 0) { mutex_unlock(&data->update_lock); -- cgit v1.2.3-70-g09d2 From a80e8ee66793ec2e7ce27fd0495a7b9c8e0a2a24 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (max1619) Use inline functions instead of macros Macros evaluating their arguments more than once are evil. Signed-off-by: Jean Delvare --- drivers/hwmon/max1619.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 1ab1cacad59..7897754f3a5 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -69,11 +69,18 @@ I2C_CLIENT_INSMOD_1(max1619); #define MAX1619_REG_W_TCRIT_HYST 0x13 /* - * Conversions and various macros + * Conversions */ -#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) -#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000) +static int temp_from_reg(int val) +{ + return (val & 0x80 ? val-0x100 : val) * 1000; +} + +static int temp_to_reg(int val) +{ + return (val < 0 ? val+0x100*1000 : val) / 1000; +} /* * Functions declaration @@ -135,7 +142,7 @@ struct max1619_data { static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ { \ struct max1619_data *data = max1619_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ + return sprintf(buf, "%d\n", temp_from_reg(data->value)); \ } show_temp(temp_input1); show_temp(temp_input2); @@ -153,7 +160,7 @@ static ssize_t set_##value(struct device *dev, struct device_attribute *attr, co long val = simple_strtol(buf, NULL, 10); \ \ mutex_lock(&data->update_lock); \ - data->value = TEMP_TO_REG(val); \ + data->value = temp_to_reg(val); \ i2c_smbus_write_byte_data(client, reg, data->value); \ mutex_unlock(&data->update_lock); \ return count; \ -- cgit v1.2.3-70-g09d2 From 6e1ecd9b8f1358ed4d099f0c54434240dc40debe Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:16 +0200 Subject: hwmon: (w83791d) fan 4/5 pins can also be used for gpio Pins fan/pwm 4-5 can be in use as GPIO. If that is the case, do not create their sysfs-interface. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/w83791d.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index de21142d106..6b1cec9950f 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -160,6 +160,7 @@ static const u8 W83791D_REG_BEEP_CTRL[3] = { 0xA3, /* BEEP Control Register 3 */ }; +#define W83791D_REG_GPIO 0x15 #define W83791D_REG_CONFIG 0x40 #define W83791D_REG_VID_FANDIV 0x47 #define W83791D_REG_DID_VID4 0x49 @@ -908,8 +909,6 @@ static struct attribute *w83791d_attributes[] = { FAN_UNIT_ATTRS(0), FAN_UNIT_ATTRS(1), FAN_UNIT_ATTRS(2), - FAN_UNIT_ATTRS(3), - FAN_UNIT_ATTRS(4), TEMP_UNIT_ATTRS(0), TEMP_UNIT_ATTRS(1), TEMP_UNIT_ATTRS(2), @@ -925,6 +924,18 @@ static const struct attribute_group w83791d_group = { .attrs = w83791d_attributes, }; +/* Separate group of attributes for fan/pwm 4-5. Their pins can also be + in use for GPIO in which case their sysfs-interface should not be made + available */ +static struct attribute *w83791d_attributes_fanpwm45[] = { + FAN_UNIT_ATTRS(3), + FAN_UNIT_ATTRS(4), + NULL +}; + +static const struct attribute_group w83791d_group_fanpwm45 = { + .attrs = w83791d_attributes_fanpwm45, +}; static int w83791d_detect_subclients(struct i2c_client *client) { @@ -1056,6 +1067,7 @@ static int w83791d_probe(struct i2c_client *client, struct w83791d_data *data; struct device *dev = &client->dev; int i, err; + u8 has_fanpwm45; #ifdef DEBUG int val1; @@ -1090,15 +1102,27 @@ static int w83791d_probe(struct i2c_client *client, if ((err = sysfs_create_group(&client->dev.kobj, &w83791d_group))) goto error3; + /* Check if pins of fan/pwm 4-5 are in use as GPIO */ + has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10; + if (has_fanpwm45) { + err = sysfs_create_group(&client->dev.kobj, + &w83791d_group_fanpwm45); + if (err) + goto error4; + } + /* Everything is ready, now register the working device */ data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); - goto error4; + goto error5; } return 0; +error5: + if (has_fanpwm45) + sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45); error4: sysfs_remove_group(&client->dev.kobj, &w83791d_group); error3: -- cgit v1.2.3-70-g09d2 From 6495ce184033d5e70dfdf5bb8d149e9e02feaaa9 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add manual PWM support Add PWM manual control. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 15 ++++++---- drivers/hwmon/w83791d.c | 67 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 7 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index a67d3b7a709..49c0e94a118 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -58,29 +58,32 @@ internal state that allows no clean access (Bank with ID register is not currently selected). If you know the address of the chip, use a 'force' parameter; this will put it into a more well-behaved state first. -The driver implements three temperature sensors, five fan rotation speed -sensors, and ten voltage sensors. +The driver implements three temperature sensors, ten voltage sensors, +five fan rotation speed sensors and manual PWM control of each fan. Temperatures are measured in degrees Celsius and measurement resolution is 1 degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when the temperature gets higher than the Overtemperature Shutdown value; it stays on until the temperature falls below the Hysteresis value. +Voltage sensors (also known as IN sensors) report their values in millivolts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. + Fan rotation speeds are reported in RPM (rotations per minute). An alarm is triggered if the rotation speed has dropped below a programmable limit. Fan readings can be divided by a programmable divider (1, 2, 4, 8, 16, 32, 64 or 128 for all fans) to give the readings more range or accuracy. -Voltage sensors (also known as IN sensors) report their values in millivolts. -An alarm is triggered if the voltage has crossed a programmable minimum -or maximum limit. +Each fan controlled is controlled by PWM. The PWM duty cycle can be read and +set for each fan separately. Valid values range from 0 (stop) to 255 (full). The w83791d has a global bit used to enable beeping from the speaker when an alarm is triggered as well as a bitmask to enable or disable the beep for specific alarms. You need both the global beep enable bit and the corresponding beep bit to be on for a triggered alarm to sound a beep. -The sysfs interface to the gloabal enable is via the sysfs beep_enable file. +The sysfs interface to the global enable is via the sysfs beep_enable file. This file is used for both legacy and new code. The sysfs interface to the beep bitmask has migrated from the original legacy diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 6b1cec9950f..a8ff4e12671 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -23,7 +23,7 @@ Supports following chips: Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - w83791d 10 5 3 3 0x71 0x5ca3 yes no + w83791d 10 5 5 3 0x71 0x5ca3 yes no The w83791d chip appears to be part way between the 83781d and the 83792d. Thus, this file is derived from both the w83792d.c and @@ -45,6 +45,7 @@ #define NUMBER_OF_VIN 10 #define NUMBER_OF_FANIN 5 #define NUMBER_OF_TEMPIN 3 +#define NUMBER_OF_PWM 5 /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, @@ -116,6 +117,14 @@ static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = { 0xBD, /* FAN 5 Count Low Limit in DataSheet */ }; +static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = { + 0x81, /* PWM 1 duty cycle register in DataSheet */ + 0x83, /* PWM 2 duty cycle register in DataSheet */ + 0x94, /* PWM 3 duty cycle register in DataSheet */ + 0xA0, /* PWM 4 duty cycle register in DataSheet */ + 0xA1, /* PWM 5 duty cycle register in DataSheet */ +}; + static const u8 W83791D_REG_FAN_CFG[2] = { 0x84, /* FAN 1/2 configuration */ 0x95, /* FAN 3 configuration */ @@ -276,6 +285,9 @@ struct w83791d_data { two sensors with three values (cur, over, hyst) */ + /* PWMs */ + u8 pwm[5]; /* pwm duty cycle */ + /* Misc */ u32 alarms; /* realtime status register encoding,combined */ u8 beep_enable; /* Global beep enable */ @@ -653,6 +665,48 @@ static struct sensor_device_attribute sda_fan_alarm[] = { SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22), }; +/* read/write PWMs */ +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->pwm[nr]); +} + +static ssize_t store_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm[nr] = SENSORS_LIMIT(val, 0, 255); + w83791d_write(client, W83791D_REG_PWM[nr], data->pwm[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_pwm[] = { + SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 0), + SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 1), + SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 2), + SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 3), + SENSOR_ATTR(pwm5, S_IWUSR | S_IRUGO, + show_pwm, store_pwm, 4), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -917,6 +971,9 @@ static struct attribute *w83791d_attributes[] = { &sda_beep_ctrl[1].dev_attr.attr, &dev_attr_cpu0_vid.attr, &dev_attr_vrm.attr, + &sda_pwm[0].dev_attr.attr, + &sda_pwm[1].dev_attr.attr, + &sda_pwm[2].dev_attr.attr, NULL }; @@ -930,6 +987,8 @@ static const struct attribute_group w83791d_group = { static struct attribute *w83791d_attributes_fanpwm45[] = { FAN_UNIT_ATTRS(3), FAN_UNIT_ATTRS(4), + &sda_pwm[3].dev_attr.attr, + &sda_pwm[4].dev_attr.attr, NULL }; @@ -1260,6 +1319,12 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) for (i = 0; i < 3; i++) data->fan_div[i] |= (vbat_reg >> (3 + i)) & 0x04; + /* Update PWM duty cycle */ + for (i = 0; i < NUMBER_OF_PWM; i++) { + data->pwm[i] = w83791d_read(client, + W83791D_REG_PWM[i]); + } + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3-70-g09d2 From b5938f8c4a530b2fad18f2293ffaf79ac9f5a148 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add pwm_enable support Add support for pwm_enable. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 13 +++++++- drivers/hwmon/w83791d.c | 79 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index 49c0e94a118..b1e4798764e 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -108,6 +108,17 @@ going forward. The driver reads the hardware chip values at most once every three seconds. User mode code requesting values more often will receive cached values. +/sys files +---------- +The sysfs-interface is documented in the 'sysfs-interface' file. Only +chip-specific options are documented here. + +pwm[1-3]_enable - this file controls mode of fan/temperature control for + fan 1-3. Fan/PWM 4-5 only support manual mode. + * 1 Manual mode + * 2 Thermal Cruise mode (no further support) + * 3 Fan Speed Cruise mode (no further support) + Alarms bitmap vs. beep_mask bitmask ------------------------------------ For legacy code using the alarms and beep_mask files: @@ -138,4 +149,4 @@ global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) W83791D TODO: --------------- -Provide a patch for smart-fan control (still need appropriate motherboard/fans) +Provide a patch for Thermal Cruise registers. diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index a8ff4e12671..a4d2b02d9e0 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -287,6 +287,8 @@ struct w83791d_data { /* PWMs */ u8 pwm[5]; /* pwm duty cycle */ + u8 pwm_enable[3]; /* pwm enable status for fan 1-3 + (fan 4-5 only support manual mode) */ /* Misc */ u32 alarms; /* realtime status register encoding,combined */ @@ -707,6 +709,71 @@ static struct sensor_device_attribute sda_pwm[] = { show_pwm, store_pwm, 4), }; +static ssize_t show_pwmenable(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->pwm_enable[nr] + 1); +} + +static ssize_t store_pwmenable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 reg_cfg_tmp; + u8 reg_idx = 0; + u8 val_shift = 0; + u8 keep_mask = 0; + + int ret = strict_strtoul(buf, 10, &val); + + if (ret || val < 1 || val > 3) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm_enable[nr] = val - 1; + switch (nr) { + case 0: + reg_idx = 0; + val_shift = 2; + keep_mask = 0xf3; + break; + case 1: + reg_idx = 0; + val_shift = 4; + keep_mask = 0xcf; + break; + case 2: + reg_idx = 1; + val_shift = 2; + keep_mask = 0xf3; + break; + } + + reg_cfg_tmp = w83791d_read(client, W83791D_REG_FAN_CFG[reg_idx]); + reg_cfg_tmp = (reg_cfg_tmp & keep_mask) | + data->pwm_enable[nr] << val_shift; + + w83791d_write(client, W83791D_REG_FAN_CFG[reg_idx], reg_cfg_tmp); + mutex_unlock(&data->update_lock); + + return count; +} +static struct sensor_device_attribute sda_pwmenable[] = { + SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 0), + SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 1), + SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + show_pwmenable, store_pwmenable, 2), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -974,6 +1041,9 @@ static struct attribute *w83791d_attributes[] = { &sda_pwm[0].dev_attr.attr, &sda_pwm[1].dev_attr.attr, &sda_pwm[2].dev_attr.attr, + &sda_pwmenable[0].dev_attr.attr, + &sda_pwmenable[1].dev_attr.attr, + &sda_pwmenable[2].dev_attr.attr, NULL }; @@ -1325,6 +1395,15 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) W83791D_REG_PWM[i]); } + /* Update PWM enable status */ + for (i = 0; i < 2; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_FAN_CFG[i]); + } + data->pwm_enable[0] = (reg_array_tmp[0] >> 2) & 0x03; + data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03; + data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03; + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3-70-g09d2 From a5a4598cd2e7cae456a7f2a100bf0e5c3c7811c7 Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Fri, 17 Oct 2008 17:51:17 +0200 Subject: hwmon: (w83791d) add support for thermal cruise mode Add support to set target temperature and tolerance for thermal cruise mode. Signed-off-by: Marc Hulsman Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/w83791d | 19 ++++-- drivers/hwmon/w83791d.c | 148 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index b1e4798764e..5663e491655 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -77,6 +77,9 @@ readings can be divided by a programmable divider (1, 2, 4, 8, 16, Each fan controlled is controlled by PWM. The PWM duty cycle can be read and set for each fan separately. Valid values range from 0 (stop) to 255 (full). +PWM 1-3 support Thermal Cruise mode, in which the PWMs are automatically +regulated to keep respectively temp 1-3 at a certain target temperature. +See below for the description of the sysfs-interface. The w83791d has a global bit used to enable beeping from the speaker when an alarm is triggered as well as a bitmask to enable or disable the beep for @@ -116,9 +119,19 @@ chip-specific options are documented here. pwm[1-3]_enable - this file controls mode of fan/temperature control for fan 1-3. Fan/PWM 4-5 only support manual mode. * 1 Manual mode - * 2 Thermal Cruise mode (no further support) + * 2 Thermal Cruise mode * 3 Fan Speed Cruise mode (no further support) +temp[1-3]_target - defines the target temperature for Thermal Cruise mode. + Unit: millidegree Celsius + RW + +temp[1-3]_tolerance - temperature tolerance for Thermal Cruise mode. + Specifies an interval around the target temperature + in which the fan speed is not changed. + Unit: millidegree Celsius + RW + Alarms bitmap vs. beep_mask bitmask ------------------------------------ For legacy code using the alarms and beep_mask files: @@ -146,7 +159,3 @@ tart2 : alarms: 0x020000 beep_mask: 0x080000 <== mismatch tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch case_open : alarms: 0x001000 beep_mask: 0x001000 global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) - -W83791D TODO: ---------------- -Provide a patch for Thermal Cruise registers. diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index a4d2b02d9e0..5768def8a4f 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -125,6 +125,17 @@ static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = { 0xA1, /* PWM 5 duty cycle register in DataSheet */ }; +static const u8 W83791D_REG_TEMP_TARGET[3] = { + 0x85, /* PWM 1 target temperature for temp 1 */ + 0x86, /* PWM 2 target temperature for temp 2 */ + 0x96, /* PWM 3 target temperature for temp 3 */ +}; + +static const u8 W83791D_REG_TEMP_TOL[2] = { + 0x87, /* PWM 1/2 temperature tolerance */ + 0x97, /* PWM 3 temperature tolerance */ +}; + static const u8 W83791D_REG_FAN_CFG[2] = { 0x84, /* FAN 1/2 configuration */ 0x95, /* FAN 3 configuration */ @@ -234,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div) (val) < 0 ? ((val) - 250) / 500 * 128 : \ ((val) + 250) / 500 * 128) +/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */ +#define TARGET_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ + (val) >= 127000 ? 127 : \ + ((val) + 500) / 1000) + +/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */ +#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \ + (val) >= 15000 ? 15 : \ + ((val) + 500) / 1000) #define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) #define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff) @@ -290,6 +310,9 @@ struct w83791d_data { u8 pwm_enable[3]; /* pwm enable status for fan 1-3 (fan 4-5 only support manual mode) */ + u8 temp_target[3]; /* pwm 1-3 target temperature */ + u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */ + /* Misc */ u32 alarms; /* realtime status register encoding,combined */ u8 beep_enable; /* Global beep enable */ @@ -774,6 +797,110 @@ static struct sensor_device_attribute sda_pwmenable[] = { show_pwmenable, store_pwmenable, 2), }; +/* For Smart Fan I / Thermal Cruise */ +static ssize_t show_temp_target(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_target[nr])); +} + +static ssize_t store_temp_target(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 target_mask; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->temp_target[nr] = TARGET_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_TEMP_TARGET[nr]) & 0x80; + w83791d_write(client, W83791D_REG_TEMP_TARGET[nr], + data->temp_target[nr] | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_temp_target[] = { + SENSOR_ATTR(temp1_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 0), + SENSOR_ATTR(temp2_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 1), + SENSOR_ATTR(temp3_target, S_IWUSR | S_IRUGO, + show_temp_target, store_temp_target, 2), +}; + +static ssize_t show_temp_tolerance(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = sensor_attr->index; + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_tolerance[nr])); +} + +static ssize_t store_temp_tolerance(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long val; + u8 target_mask; + u8 reg_idx = 0; + u8 val_shift = 0; + u8 keep_mask = 0; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + switch (nr) { + case 0: + reg_idx = 0; + val_shift = 0; + keep_mask = 0xf0; + break; + case 1: + reg_idx = 0; + val_shift = 4; + keep_mask = 0x0f; + break; + case 2: + reg_idx = 1; + val_shift = 0; + keep_mask = 0xf0; + break; + } + + mutex_lock(&data->update_lock); + data->temp_tolerance[nr] = TOL_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_TEMP_TOL[reg_idx]) & keep_mask; + w83791d_write(client, W83791D_REG_TEMP_TOL[reg_idx], + (data->temp_tolerance[nr] << val_shift) | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_temp_tolerance[] = { + SENSOR_ATTR(temp1_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 0), + SENSOR_ATTR(temp2_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 1), + SENSOR_ATTR(temp3_tolerance, S_IWUSR | S_IRUGO, + show_temp_tolerance, store_temp_tolerance, 2), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1044,6 +1171,12 @@ static struct attribute *w83791d_attributes[] = { &sda_pwmenable[0].dev_attr.attr, &sda_pwmenable[1].dev_attr.attr, &sda_pwmenable[2].dev_attr.attr, + &sda_temp_target[0].dev_attr.attr, + &sda_temp_target[1].dev_attr.attr, + &sda_temp_target[2].dev_attr.attr, + &sda_temp_tolerance[0].dev_attr.attr, + &sda_temp_tolerance[1].dev_attr.attr, + &sda_temp_tolerance[2].dev_attr.attr, NULL }; @@ -1404,6 +1537,21 @@ static struct w83791d_data *w83791d_update_device(struct device *dev) data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03; data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03; + /* Update PWM target temperature */ + for (i = 0; i < 3; i++) { + data->temp_target[i] = w83791d_read(client, + W83791D_REG_TEMP_TARGET[i]) & 0x7f; + } + + /* Update PWM temperature tolerance */ + for (i = 0; i < 2; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_TEMP_TOL[i]); + } + data->temp_tolerance[0] = reg_array_tmp[0] & 0x0f; + data->temp_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f; + data->temp_tolerance[2] = reg_array_tmp[1] & 0x0f; + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, -- cgit v1.2.3-70-g09d2 From 2fbbbf148840332d614790ea00d9a7ecb38d648d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:18 +0200 Subject: hwmon: (w83781d) Refactor beep enable handling We can handle the beep enable bit as any other beep mask bit for slightly smaller code. Signed-off-by: Jean Delvare Cc: Wolfgang Grandegger --- drivers/hwmon/w83781d.c | 46 +++++++++------------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index f942ecdd47c..136bec3fd64 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -178,9 +178,9 @@ FAN_FROM_REG(u8 val, int div) #define TEMP_FROM_REG(val) ((val) * 1000) #define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \ - (val) ^ 0x7fff : (val)) + (~(val)) & 0x7fff : (val) & 0xff7fff) #define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \ - (~(val)) & 0x7fff : (val) & 0xffffff) + (~(val)) & 0x7fff : (val) & 0xff7fff) #define DIV_FROM_REG(val) (1 << (val)) @@ -240,7 +240,6 @@ struct w83781d_data { u8 vid; /* Register encoding, combined */ u32 alarms; /* Register encoding, combined */ u32 beep_mask; /* Register encoding, combined */ - u8 beep_enable; /* Boolean */ u8 pwm[4]; /* Register value */ u8 pwm2_enable; /* Boolean */ u16 sens[3]; /* 782D/783S only. @@ -513,11 +512,6 @@ static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr return sprintf(buf, "%ld\n", (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type)); } -static ssize_t show_beep_enable (struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", (long)data->beep_enable); -} static ssize_t store_beep_mask(struct device *dev, struct device_attribute *attr, @@ -529,12 +523,12 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); - data->beep_mask = BEEP_MASK_TO_REG(val, data->type); + data->beep_mask &= 0x8000; /* preserve beep enable */ + data->beep_mask |= BEEP_MASK_TO_REG(val, data->type); w83781d_write_value(data, W83781D_REG_BEEP_INTS1, data->beep_mask & 0xff); w83781d_write_value(data, W83781D_REG_BEEP_INTS2, - ((data->beep_mask >> 8) & 0x7f) - | data->beep_enable << 7); + (data->beep_mask >> 8) & 0xff); if (data->type != w83781d && data->type != as99127f) { w83781d_write_value(data, W83781D_REG_BEEP_INTS3, ((data->beep_mask) >> 16) & 0xff); @@ -544,31 +538,8 @@ store_beep_mask(struct device *dev, struct device_attribute *attr, return count; } -static ssize_t -store_beep_enable(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct w83781d_data *data = dev_get_drvdata(dev); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - if (val != 0 && val != 1) - return -EINVAL; - - mutex_lock(&data->update_lock); - data->beep_enable = val; - val = w83781d_read_value(data, W83781D_REG_BEEP_INTS2) & 0x7f; - val |= data->beep_enable << 7; - w83781d_write_value(data, W83781D_REG_BEEP_INTS2, val); - mutex_unlock(&data->update_lock); - - return count; -} - static DEVICE_ATTR(beep_mask, S_IRUGO | S_IWUSR, show_beep_mask, store_beep_mask); -static DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR, - show_beep_enable, store_beep_enable); static ssize_t show_beep(struct device *dev, struct device_attribute *attr, char *buf) @@ -663,6 +634,8 @@ static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, 5); static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_temp3_beep, store_beep, 13); +static SENSOR_DEVICE_ATTR(beep_enable, S_IRUGO | S_IWUSR, + show_beep, store_beep, 15); static ssize_t show_fan_div(struct device *dev, struct device_attribute *da, char *buf) @@ -1029,7 +1002,7 @@ static struct attribute* w83781d_attributes[] = { &dev_attr_vrm.attr, &dev_attr_alarms.attr, &dev_attr_beep_mask.attr, - &dev_attr_beep_enable.attr, + &sensor_dev_attr_beep_enable.dev_attr.attr, NULL }; static const struct attribute_group w83781d_group = { @@ -1775,8 +1748,7 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) W83781D_REG_ALARM2) << 8); } i = w83781d_read_value(data, W83781D_REG_BEEP_INTS2); - data->beep_enable = i >> 7; - data->beep_mask = ((i & 0x7f) << 8) + + data->beep_mask = (i << 8) + w83781d_read_value(data, W83781D_REG_BEEP_INTS1); if ((data->type != w83781d) && (data->type != as99127f)) { data->beep_mask |= -- cgit v1.2.3-70-g09d2 From c6566206c6f9583b529d62f05fb67182978b959e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:18 +0200 Subject: hwmon: (w83781d) Detect alias chips The W83781D and W83782D can be accessed either on the I2C bus or the ISA bus. We must not access the same chip through both interfaces. So far we were relying on the user passing the correct ignore parameter to skip the registration of the I2C interface as suggested by sensors-detect, but this is fragile: the user may load the w83781d driver without running sensors-detect, and the i2c bus numbers are not stable across reboots and hardware changes. So, better detect alias chips in the driver directly, and skip any I2C chip which is obviously an alias of the ISA chip. This is done by comparing the value of 26 selected registers. Signed-off-by: Jean Delvare Cc: Wolfgang Grandegger --- drivers/hwmon/w83781d.c | 79 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 13 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 136bec3fd64..1c00d9f7c14 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -205,10 +205,7 @@ DIV_TO_REG(long val, enum chips type) W83781D chips available (well, actually, that is probably never done; but it is a clean illustration of how to handle a case like that). Finally, a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. Fortunately, in the case of the W83781D at - least, a register tells us what SMBus address we are on, so that helps - a bit - except if there could be more than one SMBus. Groan. No solution - for this yet. */ + not like to detect it double. */ /* For ISA chips, we abuse the i2c_client addr and name fields. We also use the driver field to differentiate between I2C and ISA chips. */ @@ -852,13 +849,25 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* This function is called when: * w83781d_driver is inserted (when this module is loaded), for each available adapter - * when a new adapter is inserted (and w83781d_driver is still present) */ + * when a new adapter is inserted (and w83781d_driver is still present) + We block updates of the ISA device to minimize the risk of concurrent + access to the same W83781D chip through different interfaces. */ static int w83781d_attach_adapter(struct i2c_adapter *adapter) { + struct w83781d_data *data; + int err; + if (!(adapter->class & I2C_CLASS_HWMON)) return 0; - return i2c_probe(adapter, &addr_data, w83781d_detect); + + data = pdev ? platform_get_drvdata(pdev) : NULL; + if (data) + mutex_lock(&data->update_lock); + err = i2c_probe(adapter, &addr_data, w83781d_detect); + if (data) + mutex_unlock(&data->update_lock); + return err; } /* Assumes that adapter is of I2C, not ISA variety. @@ -1028,6 +1037,40 @@ static const struct attribute_group w83781d_group_opt = { .attrs = w83781d_attributes_opt, }; +/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ +static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) +{ + struct w83781d_data *i2c, *isa; + int i; + + if (!pdev) /* No ISA chip */ + return 0; + + i2c = i2c_get_clientdata(client); + isa = platform_get_drvdata(pdev); + + if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) + return 0; /* Address doesn't match */ + if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) + return 0; /* Chip type doesn't match */ + + /* We compare all the limit registers, the config register and the + * interrupt mask registers */ + for (i = 0x2b; i <= 0x3d; i++) { + if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + return 0; + } + if (w83781d_read_value(isa, W83781D_REG_CONFIG) != + w83781d_read_value(i2c, W83781D_REG_CONFIG)) + return 0; + for (i = 0x43; i <= 0x46; i++) { + if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + return 0; + } + + return 1; +} + /* No clean up is done on error, it's up to the caller */ static int w83781d_create_files(struct device *dev, int kind, int is_isa) @@ -1242,6 +1285,14 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) err = -EINVAL; goto ERROR2; } + + if ((kind == w83781d || kind == w83782d) + && w83781d_alias_detect(client, val1)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + err = -ENODEV; + goto ERROR2; + } } if (kind == w83781d) { @@ -1904,14 +1955,12 @@ sensors_w83781d_init(void) { int res; - res = i2c_add_driver(&w83781d_driver); - if (res) - goto exit; - + /* We register the ISA device first, so that we can skip the + * registration of an I2C interface to the same device. */ if (w83781d_isa_found(isa_address)) { res = platform_driver_register(&w83781d_isa_driver); if (res) - goto exit_unreg_i2c_driver; + goto exit; /* Sets global pdev as a side effect */ res = w83781d_isa_device_add(isa_address); @@ -1919,12 +1968,16 @@ sensors_w83781d_init(void) goto exit_unreg_isa_driver; } + res = i2c_add_driver(&w83781d_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(&w83781d_isa_driver); - exit_unreg_i2c_driver: - i2c_del_driver(&w83781d_driver); exit: return res; } -- cgit v1.2.3-70-g09d2 From 443850ce5880b24583d2be9d4b146e471fdf3dad Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 17 Oct 2008 17:51:18 +0200 Subject: hwmon: (w83781d) Make ISA interface depend on CONFIG_ISA Probing the ISA bus on systems without ISA bus may hang the system. This patch makes the ISA bus related code depend on the kernel configuration parameter CONFIG_ISA. It moves ISA bus related code into one #ifdef CONFIG_ISA ... #endif block and adds some helper function. Signed-off-by: Wolfgang Grandegger Signed-off-by: Jean Delvare --- drivers/hwmon/w83781d.c | 673 ++++++++++++++++++++++++++++-------------------- 1 file changed, 393 insertions(+), 280 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 1c00d9f7c14..1a729a74927 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -38,25 +38,24 @@ #include #include #include -#include -#include #include #include #include #include #include #include + +#ifdef CONFIG_ISA +#include +#include #include -#include "lm75.h" +#endif -/* ISA device, if found */ -static struct platform_device *pdev; +#include "lm75.h" /* 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; - /* Insmod parameters */ I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f); I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " @@ -245,13 +244,13 @@ struct w83781d_data { u8 vrm; }; +static struct w83781d_data *w83781d_data_if_isa(void); +static int w83781d_alias_detect(struct i2c_client *client, u8 chipid); + static int w83781d_attach_adapter(struct i2c_adapter *adapter); static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); static int w83781d_detach_client(struct i2c_client *client); -static int __devinit w83781d_isa_probe(struct platform_device *pdev); -static int __devexit w83781d_isa_remove(struct platform_device *pdev); - static int w83781d_read_value(struct w83781d_data *data, u16 reg); static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value); static struct w83781d_data *w83781d_update_device(struct device *dev); @@ -265,16 +264,6 @@ static struct i2c_driver w83781d_driver = { .detach_client = w83781d_detach_client, }; -static struct platform_driver w83781d_isa_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "w83781d", - }, - .probe = w83781d_isa_probe, - .remove = w83781d_isa_remove, -}; - - /* following are the sysfs callback functions */ #define show_in_reg(reg) \ static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ @@ -836,16 +825,6 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_sensor, store_sensor, 2); -/* 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 *devattr, char *buf) -{ - struct w83781d_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); -} -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - /* This function is called when: * w83781d_driver is inserted (when this module is loaded), for each available adapter @@ -855,13 +834,12 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static int w83781d_attach_adapter(struct i2c_adapter *adapter) { - struct w83781d_data *data; + struct w83781d_data *data = w83781d_data_if_isa(); int err; if (!(adapter->class & I2C_CLASS_HWMON)) return 0; - data = pdev ? platform_get_drvdata(pdev) : NULL; if (data) mutex_lock(&data->update_lock); err = i2c_probe(adapter, &addr_data, w83781d_detect); @@ -1037,40 +1015,6 @@ static const struct attribute_group w83781d_group_opt = { .attrs = w83781d_attributes_opt, }; -/* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ -static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) -{ - struct w83781d_data *i2c, *isa; - int i; - - if (!pdev) /* No ISA chip */ - return 0; - - i2c = i2c_get_clientdata(client); - isa = platform_get_drvdata(pdev); - - if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) - return 0; /* Address doesn't match */ - if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) - return 0; /* Chip type doesn't match */ - - /* We compare all the limit registers, the config register and the - * interrupt mask registers */ - for (i = 0x2b; i <= 0x3d; i++) { - if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) - return 0; - } - if (w83781d_read_value(isa, W83781D_REG_CONFIG) != - w83781d_read_value(i2c, W83781D_REG_CONFIG)) - return 0; - for (i = 0x43; i <= 0x46; i++) { - if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) - return 0; - } - - return 1; -} - /* No clean up is done on error, it's up to the caller */ static int w83781d_create_files(struct device *dev, int kind, int is_isa) @@ -1167,12 +1111,6 @@ w83781d_create_files(struct device *dev, int kind, int is_isa) } } - if (is_isa) { - err = device_create_file(&pdev->dev, &dev_attr_name); - if (err) - return err; - } - return 0; } @@ -1381,221 +1319,80 @@ w83781d_detach_client(struct i2c_client *client) return 0; } -static int __devinit -w83781d_isa_probe(struct platform_device *pdev) -{ - int err, reg; - struct w83781d_data *data; - struct resource *res; - const char *name; - - /* Reserve the ISA region */ - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2, - "w83781d")) { - err = -EBUSY; - goto exit; - } - - if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release_region; - } - mutex_init(&data->lock); - data->client.addr = res->start; - i2c_set_clientdata(&data->client, data); - platform_set_drvdata(pdev, data); - - reg = w83781d_read_value(data, W83781D_REG_WCHIPID); - switch (reg) { - case 0x30: - data->type = w83782d; - name = "w83782d"; - break; - default: - data->type = w83781d; - name = "w83781d"; - } - strlcpy(data->client.name, name, I2C_NAME_SIZE); - - /* Initialize the W83781D chip */ - w83781d_init_device(&pdev->dev); - - /* Register sysfs hooks */ - err = w83781d_create_files(&pdev->dev, data->type, 1); - if (err) - 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, &w83781d_group); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); - device_remove_file(&pdev->dev, &dev_attr_name); - kfree(data); - exit_release_region: - release_region(res->start + W83781D_ADDR_REG_OFFSET, 2); - exit: - return err; -} - -static int __devexit -w83781d_isa_remove(struct platform_device *pdev) -{ - struct w83781d_data *data = platform_get_drvdata(pdev); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); - sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); - device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2); - kfree(data); - - return 0; -} - -/* The SMBus locks itself, usually, but nothing may access the Winbond between - bank switches. ISA access must always be locked explicitly! - We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, - would slow down the W83781D access and should not be necessary. - There are some ugly typecasts here, but the good news is - they should - nowhere else be necessary! */ static int -w83781d_read_value(struct w83781d_data *data, u16 reg) +w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) { struct i2c_client *client = &data->client; - int res, word_sized, bank; + int res, bank; struct i2c_client *cl; - mutex_lock(&data->lock); - if (!client->driver) { /* ISA device */ - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x50) - || ((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - res = - (res << 8) + inb_p(client->addr + - W83781D_DATA_REG_OFFSET); - } - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + res = i2c_smbus_read_byte_data(client, reg & 0xff); } else { - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, - bank); - if (bank == 0 || bank > 2) { - res = i2c_smbus_read_byte_data(client, reg & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data(cl, 0)); - break; - case 0x52: /* CONFIG */ - res = i2c_smbus_read_byte_data(cl, 1); - break; - case 0x53: /* HYST */ - res = swab16(i2c_smbus_read_word_data(cl, 2)); - break; - case 0x55: /* OVER */ - default: - res = swab16(i2c_smbus_read_word_data(cl, 3)); - break; - } + /* switch to subclient */ + cl = data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x50: /* TEMP */ + res = swab16(i2c_smbus_read_word_data(cl, 0)); + break; + case 0x52: /* CONFIG */ + res = i2c_smbus_read_byte_data(cl, 1); + break; + case 0x53: /* HYST */ + res = swab16(i2c_smbus_read_word_data(cl, 2)); + break; + case 0x55: /* OVER */ + default: + res = swab16(i2c_smbus_read_word_data(cl, 3)); + break; } - if (bank > 2) - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); } - mutex_unlock(&data->lock); + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + return res; } static int -w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value) { struct i2c_client *client = &data->client; - int word_sized, bank; + int bank; struct i2c_client *cl; - mutex_lock(&data->lock); - if (!client->driver) { /* ISA device */ - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - } - outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + i2c_smbus_write_byte_data(client, reg & 0xff, + value & 0xff); } else { - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, - bank); - if (bank == 0 || bank > 2) { - i2c_smbus_write_byte_data(client, reg & 0xff, - value & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x52: /* CONFIG */ - i2c_smbus_write_byte_data(cl, 1, value & 0xff); - break; - case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); - break; - case 0x55: /* OVER */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); - break; - } + /* switch to subclient */ + cl = data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x52: /* CONFIG */ + i2c_smbus_write_byte_data(cl, 1, value & 0xff); + break; + case 0x53: /* HYST */ + i2c_smbus_write_word_data(cl, 2, swab16(value)); + break; + case 0x55: /* OVER */ + i2c_smbus_write_word_data(cl, 3, swab16(value)); + break; } - if (bank > 2) - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); } - mutex_unlock(&data->lock); + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + return 0; } @@ -1815,6 +1612,255 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) return data; } +#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 *devattr, char *buf) +{ + struct w83781d_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->client.name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct w83781d_data *w83781d_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 w83781d_alias_detect(struct i2c_client *client, u8 chipid) +{ + struct w83781d_data *i2c, *isa; + int i; + + if (!pdev) /* No ISA chip */ + return 0; + + i2c = i2c_get_clientdata(client); + isa = platform_get_drvdata(pdev); + + if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) + return 0; /* Address doesn't match */ + if (w83781d_read_value(isa, W83781D_REG_WCHIPID) != chipid) + return 0; /* Chip type doesn't match */ + + /* We compare all the limit registers, the config register and the + * interrupt mask registers */ + for (i = 0x2b; i <= 0x3d; i++) { + if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + return 0; + } + if (w83781d_read_value(isa, W83781D_REG_CONFIG) != + w83781d_read_value(i2c, W83781D_REG_CONFIG)) + return 0; + for (i = 0x43; i <= 0x46; i++) { + if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + return 0; + } + + return 1; +} + +static int +w83781d_read_value_isa(struct w83781d_data *data, u16 reg) +{ + struct i2c_client *client = &data->client; + int word_sized, res; + + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(client->addr + + W83781D_DATA_REG_OFFSET); + } + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + return res; +} + +static void +w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) +{ + struct i2c_client *client = &data->client; + int word_sized; + + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } +} + +/* The SMBus locks itself, usually, but nothing may access the Winbond between + bank switches. ISA access must always be locked explicitly! + We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + would slow down the W83781D access and should not be necessary. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +static int +w83781d_read_value(struct w83781d_data *data, u16 reg) +{ + struct i2c_client *client = &data->client; + int res; + + mutex_lock(&data->lock); + if (client->driver) + res = w83781d_read_value_i2c(data, reg); + else + res = w83781d_read_value_isa(data, reg); + mutex_unlock(&data->lock); + return res; +} + +static int +w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +{ + struct i2c_client *client = &data->client; + + mutex_lock(&data->lock); + if (client->driver) + w83781d_write_value_i2c(data, reg, value); + else + w83781d_write_value_isa(data, reg, value); + mutex_unlock(&data->lock); + return 0; +} + +static int __devinit +w83781d_isa_probe(struct platform_device *pdev) +{ + int err, reg; + struct w83781d_data *data; + struct resource *res; + const char *name; + + /* Reserve the ISA region */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!request_region(res->start + W83781D_ADDR_REG_OFFSET, 2, + "w83781d")) { + err = -EBUSY; + goto exit; + } + + data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit_release_region; + } + mutex_init(&data->lock); + data->client.addr = res->start; + i2c_set_clientdata(&data->client, data); + platform_set_drvdata(pdev, data); + + reg = w83781d_read_value(data, W83781D_REG_WCHIPID); + switch (reg) { + case 0x30: + data->type = w83782d; + name = "w83782d"; + break; + default: + data->type = w83781d; + name = "w83781d"; + } + strlcpy(data->client.name, name, I2C_NAME_SIZE); + + /* Initialize the W83781D chip */ + w83781d_init_device(&pdev->dev); + + /* Register sysfs hooks */ + err = w83781d_create_files(&pdev->dev, data->type, 1); + if (err) + goto exit_remove_files; + + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + 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, &w83781d_group); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + device_remove_file(&pdev->dev, &dev_attr_name); + kfree(data); + exit_release_region: + release_region(res->start + W83781D_ADDR_REG_OFFSET, 2); + exit: + return err; +} + +static int __devexit +w83781d_isa_remove(struct platform_device *pdev) +{ + struct w83781d_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); + sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); + device_remove_file(&pdev->dev, &dev_attr_name); + release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2); + kfree(data); + + return 0; +} + +static struct platform_driver w83781d_isa_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "w83781d", + }, + .probe = w83781d_isa_probe, + .remove = __devexit_p(w83781d_isa_remove), +}; + /* return 1 if a supported chip is found, 0 otherwise */ static int __init w83781d_isa_found(unsigned short address) @@ -1951,12 +1997,10 @@ w83781d_isa_device_add(unsigned short address) } static int __init -sensors_w83781d_init(void) +w83781d_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 (w83781d_isa_found(isa_address)) { res = platform_driver_register(&w83781d_isa_driver); if (res) @@ -1968,27 +2012,96 @@ sensors_w83781d_init(void) goto exit_unreg_isa_driver; } - res = i2c_add_driver(&w83781d_driver); - if (res) - goto exit_unreg_isa_device; - return 0; - exit_unreg_isa_device: - platform_device_unregister(pdev); - exit_unreg_isa_driver: +exit_unreg_isa_driver: platform_driver_unregister(&w83781d_isa_driver); - exit: +exit: return res; } static void __exit -sensors_w83781d_exit(void) +w83781d_isa_unregister(void) { if (pdev) { platform_device_unregister(pdev); platform_driver_unregister(&w83781d_isa_driver); } +} +#else /* !CONFIG_ISA */ + +static struct w83781d_data *w83781d_data_if_isa(void) +{ + return NULL; +} + +static int +w83781d_alias_detect(struct i2c_client *client, u8 chipid) +{ + return 0; +} + +static int +w83781d_read_value(struct w83781d_data *data, u16 reg) +{ + int res; + + mutex_lock(&data->lock); + res = w83781d_read_value_i2c(data, reg); + mutex_unlock(&data->lock); + + return res; +} + +static int +w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) +{ + mutex_lock(&data->lock); + w83781d_write_value_i2c(data, reg, value); + mutex_unlock(&data->lock); + + return 0; +} + +static int __init +w83781d_isa_register(void) +{ + return 0; +} + +static void __exit +w83781d_isa_unregister(void) +{ +} +#endif /* CONFIG_ISA */ + +static int __init +sensors_w83781d_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 = w83781d_isa_register(); + if (res) + goto exit; + + res = i2c_add_driver(&w83781d_driver); + if (res) + goto exit_unreg_isa; + + return 0; + + exit_unreg_isa: + w83781d_isa_unregister(); + exit: + return res; +} + +static void __exit +sensors_w83781d_exit(void) +{ + w83781d_isa_unregister(); i2c_del_driver(&w83781d_driver); } -- cgit v1.2.3-70-g09d2 From 360782dde00a2e6e7d9fd57535f90934707ab8a8 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:19 +0200 Subject: hwmon: (w83781d) Stop abusing struct i2c_client for ISA devices Upcoming changes to the I2C part of the w83781d driver will cause ISA devices to no longer have a struct i2c_client at hand. So, we must stop (ab)using it now. Signed-off-by: Jean Delvare Cc: Wolfgang Grandegger --- drivers/hwmon/w83781d.c | 63 ++++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 37 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 1a729a74927..50580c88305 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -4,7 +4,7 @@ Copyright (c) 1998 - 2001 Frodo Looijaard , Philip Edelbrock , and Mark Studebaker - Copyright (c) 2007 Jean Delvare + Copyright (c) 2007 - 2008 Jean Delvare 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 @@ -198,22 +198,16 @@ DIV_TO_REG(long val, enum chips type) return i; } -/* There are some complications in a module like this. First off, W83781D chips - may be both present on the SMBus and the ISA bus, and we have to handle - those cases separately at some places. Second, there might be several - W83781D chips available (well, actually, that is probably never done; but - it is a clean illustration of how to handle a case like that). Finally, - a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. */ - -/* For ISA chips, we abuse the i2c_client addr and name fields. We also use - the driver field to differentiate between I2C and ISA chips. */ struct w83781d_data { struct i2c_client client; struct device *hwmon_dev; struct mutex lock; enum chips type; + /* For ISA device only */ + const char *name; + int isa_addr; + struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -1625,7 +1619,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { struct w83781d_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->client.name); + return sprintf(buf, "%s\n", data->name); } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); @@ -1671,7 +1665,6 @@ static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) static int w83781d_read_value_isa(struct w83781d_data *data, u16 reg) { - struct i2c_client *client = &data->client; int word_sized, res; word_sized = (((reg & 0xff00) == 0x100) @@ -1681,23 +1674,23 @@ w83781d_read_value_isa(struct w83781d_data *data, u16 reg) || ((reg & 0x00ff) == 0x55)); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_DATA_REG_OFFSET); } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET); if (word_sized) { outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); res = - (res << 8) + inb_p(client->addr + + (res << 8) + inb_p(data->isa_addr + W83781D_DATA_REG_OFFSET); } if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET); } return res; } @@ -1705,7 +1698,6 @@ w83781d_read_value_isa(struct w83781d_data *data, u16 reg) static void w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) { - struct i2c_client *client = &data->client; int word_sized; word_sized = (((reg & 0xff00) == 0x100) @@ -1714,22 +1706,22 @@ w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) || ((reg & 0x00ff) == 0x55)); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_DATA_REG_OFFSET); } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg & 0xff, data->isa_addr + W83781D_ADDR_REG_OFFSET); if (word_sized) { outb_p(value >> 8, - client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_DATA_REG_OFFSET); outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); } - outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); + outb_p(value & 0xff, data->isa_addr + W83781D_DATA_REG_OFFSET); if (reg & 0xff00) { outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + data->isa_addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->isa_addr + W83781D_DATA_REG_OFFSET); } } @@ -1774,7 +1766,6 @@ w83781d_isa_probe(struct platform_device *pdev) int err, reg; struct w83781d_data *data; struct resource *res; - const char *name; /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1790,21 +1781,19 @@ w83781d_isa_probe(struct platform_device *pdev) goto exit_release_region; } mutex_init(&data->lock); - data->client.addr = res->start; - i2c_set_clientdata(&data->client, data); + data->isa_addr = res->start; platform_set_drvdata(pdev, data); reg = w83781d_read_value(data, W83781D_REG_WCHIPID); switch (reg) { case 0x30: data->type = w83782d; - name = "w83782d"; + data->name = "w83782d"; break; default: data->type = w83781d; - name = "w83781d"; + data->name = "w83781d"; } - strlcpy(data->client.name, name, I2C_NAME_SIZE); /* Initialize the W83781D chip */ w83781d_init_device(&pdev->dev); @@ -1846,7 +1835,7 @@ w83781d_isa_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &w83781d_group); sysfs_remove_group(&pdev->dev.kobj, &w83781d_group_opt); device_remove_file(&pdev->dev, &dev_attr_name); - release_region(data->client.addr + W83781D_ADDR_REG_OFFSET, 2); + release_region(data->isa_addr + W83781D_ADDR_REG_OFFSET, 2); kfree(data); return 0; -- cgit v1.2.3-70-g09d2 From 0217eae3a825d551b99991bc30555c3daeb0a4df Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Fri, 17 Oct 2008 17:51:19 +0200 Subject: hwmon: (w83781d) Use new style driver binding This patch modifies the w83781d driver to use new style driver binding. Substantial code modifications are required to deal with the new interface, especially legacy device detection. [JD: largely edited to make the patch smaller and to get the driver to work again on ISA devices.] Signed-off-by: Wolfgang Grandegger Signed-off-by: Jean Delvare --- drivers/hwmon/w83781d.c | 309 +++++++++++++++++++++--------------------------- 1 file changed, 134 insertions(+), 175 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 50580c88305..d4d1b859d4f 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -199,7 +199,7 @@ DIV_TO_REG(long val, enum chips type) } struct w83781d_data { - struct i2c_client client; + struct i2c_client *client; struct device *hwmon_dev; struct mutex lock; enum chips type; @@ -241,23 +241,11 @@ struct w83781d_data { static struct w83781d_data *w83781d_data_if_isa(void); static int w83781d_alias_detect(struct i2c_client *client, u8 chipid); -static int w83781d_attach_adapter(struct i2c_adapter *adapter); -static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); -static int w83781d_detach_client(struct i2c_client *client); - static int w83781d_read_value(struct w83781d_data *data, u16 reg); static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value); static struct w83781d_data *w83781d_update_device(struct device *dev); static void w83781d_init_device(struct device *dev); -static struct i2c_driver w83781d_driver = { - .driver = { - .name = "w83781d", - }, - .attach_adapter = w83781d_attach_adapter, - .detach_client = w83781d_detach_client, -}; - /* following are the sysfs callback functions */ #define show_in_reg(reg) \ static ssize_t show_##reg (struct device *dev, struct device_attribute *da, \ @@ -819,46 +807,19 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_sensor, store_sensor, 2); -/* This function is called when: - * w83781d_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and w83781d_driver is still present) - We block updates of the ISA device to minimize the risk of concurrent - access to the same W83781D chip through different interfaces. */ -static int -w83781d_attach_adapter(struct i2c_adapter *adapter) -{ - struct w83781d_data *data = w83781d_data_if_isa(); - int err; - - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - - if (data) - mutex_lock(&data->update_lock); - err = i2c_probe(adapter, &addr_data, w83781d_detect); - if (data) - mutex_unlock(&data->update_lock); - return err; -} - /* Assumes that adapter is of I2C, not ISA variety. * OTHERWISE DON'T CALL THIS */ static int -w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, - struct i2c_client *new_client) +w83781d_detect_subclients(struct i2c_client *new_client) { int i, val1 = 0, id; int err; - const char *client_name = ""; + int address = new_client->addr; + unsigned short sc_addr[2]; + struct i2c_adapter *adapter = new_client->adapter; struct w83781d_data *data = i2c_get_clientdata(new_client); - - data->lm75[0] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[0])) { - err = -ENOMEM; - goto ERROR_SC_0; - } + enum chips kind = data->type; id = i2c_adapter_id(adapter); @@ -876,55 +837,35 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, w83781d_write_value(data, W83781D_REG_I2C_SUBADDR, (force_subclients[2] & 0x07) | ((force_subclients[3] & 0x07) << 4)); - data->lm75[0]->addr = force_subclients[2]; + sc_addr[0] = force_subclients[2]; } else { val1 = w83781d_read_value(data, W83781D_REG_I2C_SUBADDR); - data->lm75[0]->addr = 0x48 + (val1 & 0x07); + sc_addr[0] = 0x48 + (val1 & 0x07); } if (kind != w83783s) { - data->lm75[1] = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[1])) { - err = -ENOMEM; - goto ERROR_SC_1; - } - if (force_subclients[0] == id && force_subclients[1] == address) { - data->lm75[1]->addr = force_subclients[3]; + sc_addr[1] = force_subclients[3]; } else { - data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07); + sc_addr[1] = 0x48 + ((val1 >> 4) & 0x07); } - if (data->lm75[0]->addr == data->lm75[1]->addr) { + if (sc_addr[0] == sc_addr[1]) { dev_err(&new_client->dev, "Duplicate addresses 0x%x for subclients.\n", - data->lm75[0]->addr); + sc_addr[0]); err = -EBUSY; goto ERROR_SC_2; } } - if (kind == w83781d) - client_name = "w83781d subclient"; - else if (kind == w83782d) - client_name = "w83782d subclient"; - else if (kind == w83783s) - client_name = "w83783s subclient"; - else if (kind == as99127f) - client_name = "as99127f subclient"; - for (i = 0; i <= 1; i++) { - /* store all data in w83781d */ - i2c_set_clientdata(data->lm75[i], NULL); - data->lm75[i]->adapter = adapter; - data->lm75[i]->driver = &w83781d_driver; - data->lm75[i]->flags = 0; - strlcpy(data->lm75[i]->name, client_name, - I2C_NAME_SIZE); - if ((err = i2c_attach_client(data->lm75[i]))) { + data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]); + if (!data->lm75[i]) { dev_err(&new_client->dev, "Subclient %d " "registration at address 0x%x " - "failed.\n", i, data->lm75[i]->addr); + "failed.\n", i, sc_addr[i]); + err = -ENOMEM; if (i == 1) goto ERROR_SC_3; goto ERROR_SC_2; @@ -937,12 +878,9 @@ w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, /* Undo inits in case of errors */ ERROR_SC_3: - i2c_detach_client(data->lm75[0]); + i2c_unregister_device(data->lm75[0]); ERROR_SC_2: - kfree(data->lm75[1]); ERROR_SC_1: - kfree(data->lm75[0]); -ERROR_SC_0: return err; } @@ -1108,87 +1046,71 @@ w83781d_create_files(struct device *dev, int kind, int is_isa) return 0; } +/* Return 0 if detection is successful, -ENODEV otherwise */ static int -w83781d_detect(struct i2c_adapter *adapter, int address, int kind) +w83781d_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) { int val1 = 0, val2; - struct i2c_client *client; - struct device *dev; - struct w83781d_data *data; - int err; + struct w83781d_data *isa = w83781d_data_if_isa(); + struct i2c_adapter *adapter = client->adapter; + int address = client->addr; const char *client_name = ""; enum vendor { winbond, asus } vendid; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -EINVAL; - goto ERROR1; - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access w83781d_{read,write}_value. */ - - if (!(data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } - - client = &data->client; - i2c_set_clientdata(client, data); - client->addr = address; - mutex_init(&data->lock); - client->adapter = adapter; - client->driver = &w83781d_driver; - dev = &client->dev; + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; - /* Now, we do the remaining detection. */ + /* We block updates of the ISA device to minimize the risk of + concurrent access to the same W83781D chip through different + interfaces. */ + if (isa) + mutex_lock(&isa->update_lock); /* The w8378?d may be stuck in some other bank than bank 0. This may make reading other information impossible. Specify a force=... or force_*=... parameter, and the Winbond will be reset to the right bank. */ if (kind < 0) { - if (w83781d_read_value(data, W83781D_REG_CONFIG) & 0x80) { + if (i2c_smbus_read_byte_data + (client, W83781D_REG_CONFIG) & 0x80) { dev_dbg(&adapter->dev, "Detection of w83781d chip " "failed at step 3\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } - val1 = w83781d_read_value(data, W83781D_REG_BANK); - val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK); + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); /* Check for Winbond or Asus ID if in bank 0 */ if ((!(val1 & 0x07)) && (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { dev_dbg(&adapter->dev, "Detection of w83781d chip " "failed at step 4\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } /* If Winbond SMBus, check address at 0x48. Asus doesn't support, except for as99127f rev.2 */ if ((!(val1 & 0x80) && (val2 == 0xa3)) || ((val1 & 0x80) && (val2 == 0x5c))) { - if (w83781d_read_value - (data, W83781D_REG_I2C_ADDR) != address) { + if (i2c_smbus_read_byte_data + (client, W83781D_REG_I2C_ADDR) != address) { dev_dbg(&adapter->dev, "Detection of w83781d " "chip failed at step 5\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } } } /* We have either had a force parameter, or we have already detected the Winbond. Put it now into bank 0 and Vendor ID High Byte */ - w83781d_write_value(data, W83781D_REG_BANK, - (w83781d_read_value(data, W83781D_REG_BANK) - & 0x78) | 0x80); + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + (i2c_smbus_read_byte_data(client, W83781D_REG_BANK) + & 0x78) | 0x80); /* Determine the chip type. */ if (kind <= 0) { /* get vendor ID */ - val2 = w83781d_read_value(data, W83781D_REG_CHIPMAN); + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); if (val2 == 0x5c) vendid = winbond; else if (val2 == 0x12) @@ -1196,11 +1118,10 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) else { dev_dbg(&adapter->dev, "w83781d chip vendor is " "neither Winbond nor Asus\n"); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } - val1 = w83781d_read_value(data, W83781D_REG_WCHIPID); + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID); if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) kind = w83781d; else if (val1 == 0x30 && vendid == winbond) @@ -1214,19 +1135,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) dev_warn(&adapter->dev, "Ignoring 'force' " "parameter for unknown chip at " "address 0x%02x\n", address); - err = -EINVAL; - goto ERROR2; + goto err_nodev; } if ((kind == w83781d || kind == w83782d) && w83781d_alias_detect(client, val1)) { dev_dbg(&adapter->dev, "Device at 0x%02x appears to " "be the same as ISA device\n", address); - err = -ENODEV; - goto ERROR2; + goto err_nodev; } } + if (isa) + mutex_unlock(&isa->update_lock); + if (kind == w83781d) { client_name = "w83781d"; } else if (kind == w83782d) { @@ -1237,24 +1159,46 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind) client_name = "as99127f"; } - /* Fill in the remaining client fields and put into the global list */ - strlcpy(client->name, client_name, I2C_NAME_SIZE); - data->type = kind; + strlcpy(info->type, client_name, I2C_NAME_SIZE); - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(client))) - goto ERROR2; + return 0; + + err_nodev: + if (isa) + mutex_unlock(&isa->update_lock); + return -ENODEV; +} + +static int +w83781d_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct w83781d_data *data; + int err; + + data = kzalloc(sizeof(struct w83781d_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto ERROR1; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + mutex_init(&data->update_lock); + + data->type = id->driver_data; + data->client = client; /* attach secondary i2c lm75-like clients */ - if ((err = w83781d_detect_subclients(adapter, address, - kind, client))) + err = w83781d_detect_subclients(client); + if (err) goto ERROR3; /* Initialize the chip */ w83781d_init_device(dev); /* Register sysfs hooks */ - err = w83781d_create_files(dev, kind, 0); + err = w83781d_create_files(dev, data->type, 0); if (err) goto ERROR4; @@ -1270,45 +1214,35 @@ ERROR4: sysfs_remove_group(&dev->kobj, &w83781d_group); sysfs_remove_group(&dev->kobj, &w83781d_group_opt); - if (data->lm75[1]) { - i2c_detach_client(data->lm75[1]); - kfree(data->lm75[1]); - } - if (data->lm75[0]) { - i2c_detach_client(data->lm75[0]); - kfree(data->lm75[0]); - } + if (data->lm75[0]) + i2c_unregister_device(data->lm75[0]); + if (data->lm75[1]) + i2c_unregister_device(data->lm75[1]); ERROR3: - i2c_detach_client(client); -ERROR2: + i2c_set_clientdata(client, NULL); kfree(data); ERROR1: return err; } static int -w83781d_detach_client(struct i2c_client *client) +w83781d_remove(struct i2c_client *client) { struct w83781d_data *data = i2c_get_clientdata(client); - int err; + struct device *dev = &client->dev; - /* main client */ - if (data) { - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &w83781d_group); - sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt); - } + hwmon_device_unregister(data->hwmon_dev); - if ((err = i2c_detach_client(client))) - return err; + sysfs_remove_group(&dev->kobj, &w83781d_group); + sysfs_remove_group(&dev->kobj, &w83781d_group_opt); - /* main client */ - if (data) - kfree(data); + if (data->lm75[0]) + i2c_unregister_device(data->lm75[0]); + if (data->lm75[1]) + i2c_unregister_device(data->lm75[1]); - /* subclient */ - else - kfree(client); + i2c_set_clientdata(client, NULL); + kfree(data); return 0; } @@ -1316,7 +1250,7 @@ w83781d_detach_client(struct i2c_client *client) static int w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int res, bank; struct i2c_client *cl; @@ -1356,7 +1290,7 @@ w83781d_read_value_i2c(struct w83781d_data *data, u16 reg) static int w83781d_write_value_i2c(struct w83781d_data *data, u16 reg, u16 value) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int bank; struct i2c_client *cl; @@ -1493,7 +1427,7 @@ w83781d_init_device(struct device *dev) static struct w83781d_data *w83781d_update_device(struct device *dev) { struct w83781d_data *data = dev_get_drvdata(dev); - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int i; mutex_lock(&data->update_lock); @@ -1606,6 +1540,30 @@ static struct w83781d_data *w83781d_update_device(struct device *dev) return data; } +static const struct i2c_device_id w83781d_ids[] = { + { "w83781d", w83781d, }, + { "w83782d", w83782d, }, + { "w83783s", w83783s, }, + { "as99127f", as99127f }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, w83781d_ids); + +static struct i2c_driver w83781d_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "w83781d", + }, + .probe = w83781d_probe, + .remove = w83781d_remove, + .id_table = w83781d_ids, + .detect = w83781d_detect, + .address_data = &addr_data, +}; + +/* + * ISA related code + */ #ifdef CONFIG_ISA /* ISA device, if found */ @@ -1631,13 +1589,12 @@ static struct w83781d_data *w83781d_data_if_isa(void) /* Returns 1 if the I2C chip appears to be an alias of the ISA chip */ static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) { - struct w83781d_data *i2c, *isa; + struct w83781d_data *isa; int i; if (!pdev) /* No ISA chip */ return 0; - i2c = i2c_get_clientdata(client); isa = platform_get_drvdata(pdev); if (w83781d_read_value(isa, W83781D_REG_I2C_ADDR) != client->addr) @@ -1648,14 +1605,16 @@ static int w83781d_alias_detect(struct i2c_client *client, u8 chipid) /* We compare all the limit registers, the config register and the * interrupt mask registers */ for (i = 0x2b; i <= 0x3d; i++) { - if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + if (w83781d_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) return 0; } if (w83781d_read_value(isa, W83781D_REG_CONFIG) != - w83781d_read_value(i2c, W83781D_REG_CONFIG)) + i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG)) return 0; for (i = 0x43; i <= 0x46; i++) { - if (w83781d_read_value(isa, i) != w83781d_read_value(i2c, i)) + if (w83781d_read_value(isa, i) != + i2c_smbus_read_byte_data(client, i)) return 0; } @@ -1734,11 +1693,11 @@ w83781d_write_value_isa(struct w83781d_data *data, u16 reg, u16 value) static int w83781d_read_value(struct w83781d_data *data, u16 reg) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; int res; mutex_lock(&data->lock); - if (client->driver) + if (client) res = w83781d_read_value_i2c(data, reg); else res = w83781d_read_value_isa(data, reg); @@ -1749,10 +1708,10 @@ w83781d_read_value(struct w83781d_data *data, u16 reg) static int w83781d_write_value(struct w83781d_data *data, u16 reg, u16 value) { - struct i2c_client *client = &data->client; + struct i2c_client *client = data->client; mutex_lock(&data->lock); - if (client->driver) + if (client) w83781d_write_value_i2c(data, reg, value); else w83781d_write_value_isa(data, reg, value); -- cgit v1.2.3-70-g09d2 From ff92136131c66a51b820798067e53f05a8f36a24 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 17 Oct 2008 17:51:19 +0200 Subject: hwmon: (ibmpex) Automatically load on IBM systems via DMI Signed-off-by: Darrick J. Wong Signed-off-by: Jean Delvare --- drivers/hwmon/ibmpex.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 4e9b19c6732..537d9fb2ff8 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -608,3 +608,9 @@ MODULE_LICENSE("GPL"); module_init(ibmpex_init); module_exit(ibmpex_exit); + +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); -- cgit v1.2.3-70-g09d2 From 21d93c14b32f1d74ebed09caca7d6c10cf3f952e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 17 Oct 2008 17:51:19 +0200 Subject: hwmon: (ibmaem) Automatically load on IBM systems via DMI Signed-off-by: Darrick J. Wong Signed-off-by: Jean Delvare --- drivers/hwmon/ibmaem.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 0f70dc20410..7b0ed5dea39 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -1118,3 +1118,10 @@ MODULE_LICENSE("GPL"); module_init(aem_init); module_exit(aem_exit); + +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3350-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3655-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3755-*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBM3850M2/x3950M2-*"); -- cgit v1.2.3-70-g09d2 From 366716e6aabfb6f7c65525cc1637e035bfaf422d Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Fri, 17 Oct 2008 17:51:20 +0200 Subject: hwmon: (adm1029) Use mask for fan_div value This is my patch for testing correct values of fan div in adm1029 and prevent a division by 0 for some (unlikely) register values. Signed-off-by: Corentin Labbe Signed-off-by: Jean Delvare --- drivers/hwmon/adm1029.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index ba84ca5923f..36718150b47 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -179,7 +179,8 @@ show_fan(struct device *dev, struct device_attribute *devattr, char *buf) struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); u16 val; - if (data->fan[attr->index] == 0 || data->fan_div[attr->index] == 0 + if (data->fan[attr->index] == 0 + || (data->fan_div[attr->index] & 0xC0) == 0 || data->fan[attr->index] == 255) { return sprintf(buf, "0\n"); } @@ -194,7 +195,7 @@ show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); - if (data->fan_div[attr->index] == 0) + if ((data->fan_div[attr->index] & 0xC0) == 0) return sprintf(buf, "0\n"); return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index])); } -- cgit v1.2.3-70-g09d2 From 885fe4b1f621fa2c8499e222059b59dbc98583e2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:20 +0200 Subject: hwmon: (adm1026) Fix debug messages * Add missing new-line to one debug message. * Remove leading colon from 3 debug messages. Signed-off-by: Jean Delvare Cc: Philip Pokorny --- drivers/hwmon/adm1026.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 7fe2441fc84..546be058879 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -455,7 +455,7 @@ static void adm1026_print_gpio(struct i2c_client *client) struct adm1026_data *data = i2c_get_clientdata(client); int i; - dev_dbg(&client->dev, "GPIO config is:"); + dev_dbg(&client->dev, "GPIO config is:\n"); for (i = 0;i <= 7;++i) { if (data->config2 & (1 << i)) { dev_dbg(&client->dev, "\t%sGP%s%d\n", @@ -1681,17 +1681,16 @@ static int adm1026_detect(struct i2c_client *client, int kind, kind = adm1026; } else if (company == ADM1026_COMPANY_ANALOG_DEV && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, ": Unrecognized stepping " + dev_err(&adapter->dev, "Unrecognized stepping " "0x%02x. Defaulting to ADM1026.\n", verstep); kind = adm1026; } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, ": Found version/stepping " + dev_err(&adapter->dev, "Found version/stepping " "0x%02x. Assuming generic ADM1026.\n", verstep); kind = any_chip; } else { - dev_dbg(&adapter->dev, ": Autodetection " - "failed\n"); + dev_dbg(&adapter->dev, "Autodetection failed\n"); /* Not an ADM1026 ... */ if (kind == 0) { /* User used force=x,y */ dev_err(&adapter->dev, "Generic ADM1026 not " -- cgit v1.2.3-70-g09d2 From a0cf354a71bd2969b2f1868530d3fecaebd6dc3d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 17 Oct 2008 17:51:20 +0200 Subject: hwmon: (adm1026) Prevent log spamming When debugging is enabled, the adm1026 driver currently logs the message "Setting VID from GPIO11-15" 108 times each time you run "sensors". Once should be enough. Signed-off-by: Jean Delvare Cc: Philip Pokorny --- drivers/hwmon/adm1026.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 546be058879..ff7de40b6e3 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -279,7 +279,6 @@ struct adm1026_data { u8 fan_min[8]; /* Register value */ u8 fan_div[8]; /* Decoded value */ struct pwm_data pwm1; /* Pwm control values */ - int vid; /* Decoded value */ u8 vrm; /* VRM version */ u8 analog_out; /* Register value (DAC) */ long alarms; /* Register encoding, combined */ @@ -697,8 +696,6 @@ static struct adm1026_data *adm1026_update_device(struct device *dev) data->last_config = jiffies; }; /* last_config */ - dev_dbg(&client->dev, "Setting VID from GPIO11-15.\n"); - data->vid = (data->gpio >> 11) & 0x1f; data->valid = 1; mutex_unlock(&data->update_lock); return data; @@ -1215,7 +1212,10 @@ static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg, static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf, "%d\n", vid_from_reg(data->vid & 0x3f, data->vrm)); + int vid = (data->gpio >> 11) & 0x1f; + + dev_dbg(dev, "Setting VID from GPIO11-15.\n"); + return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -- cgit v1.2.3-70-g09d2 From 28f74e71775b1ae0ebf7fe87f7a7f39ecb77a2b8 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sat, 18 Oct 2008 20:27:30 -0700 Subject: hwmon/pc87360 separate alarm files: define some constants Bring hwmon/pc87360 into agreement with Documentation/hwmon/sysfs-interface. Patchset adds separate limit alarms for voltages and temps, it also adds temp[123]_fault files. On my Soekris, temps 1,2 are unused/unconnected, so temp[123]_fault = 1,1,0 respectively. This agrees with /usr/bin/sensors, which has always shown them as OPEN. Temps 4,5,6 are thermistor based, and dont have a fault bit in their status register. This patch: 2 different kinds of constants added: - CHAN_ALM_* constants for (later) vin, temp alarm callbacks. - CHAN_* conversion constants, used in _init_device, partly for RW1C bits Signed-off-by: Jim Cromie Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/pc87360.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 9b462bb13fa..1043b116bde 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -489,6 +489,11 @@ static struct sensor_device_attribute in_max[] = { SENSOR_ATTR(in10_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 10), }; +/* (temp & vin) channel status register alarm bits (pdf sec.11.5.12) */ +#define CHAN_ALM_MIN 0x02 /* min limit crossed */ +#define CHAN_ALM_MAX 0x04 /* max limit exceeded */ +#define TEMP_ALM_CRIT 0x08 /* temp crit exceeded (temp only) */ + #define VIN_UNIT_ATTRS(X) \ &in_input[X].dev_attr.attr, \ &in_status[X].dev_attr.attr, \ @@ -1131,6 +1136,12 @@ static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, mutex_unlock(&(data->lock)); } +/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ +#define CHAN_CNVRTD 0x80 /* new data ready */ +#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ +#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ +#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ + static void pc87360_init_device(struct platform_device *pdev, int use_thermistors) { @@ -1156,7 +1167,7 @@ static void pc87360_init_device(struct platform_device *pdev, /* Forcibly enable voltage channel */ reg = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN_STATUS); - if (!(reg & 0x01)) { + if (!(reg & CHAN_ENA)) { dev_dbg(&pdev->dev, "Forcibly " "enabling in%d\n", i); pc87360_write_value(data, LD_IN, i, @@ -1171,7 +1182,7 @@ static void pc87360_init_device(struct platform_device *pdev, for (i = 11; i < data->innr; i++) { reg = pc87360_read_value(data, LD_IN, i, PC87365_REG_TEMP_STATUS); - use_thermistors = use_thermistors || (reg & 0x01); + use_thermistors = use_thermistors || (reg & CHAN_ENA); } i = use_thermistors ? 2 : 0; @@ -1180,7 +1191,7 @@ static void pc87360_init_device(struct platform_device *pdev, /* Forcibly enable temperature channel */ reg = pc87360_read_value(data, LD_TEMP, i, PC87365_REG_TEMP_STATUS); - if (!(reg & 0x01)) { + if (!(reg & CHAN_ENA)) { dev_dbg(&pdev->dev, "Forcibly " "enabling temp%d\n", i+1); pc87360_write_value(data, LD_TEMP, i, @@ -1197,7 +1208,7 @@ static void pc87360_init_device(struct platform_device *pdev, diodes */ reg = pc87360_read_value(data, LD_TEMP, (i-11)/2, PC87365_REG_TEMP_STATUS); - if (reg & 0x01) { + if (reg & CHAN_ENA) { dev_dbg(&pdev->dev, "Skipping " "temp%d, pin already in use " "by temp%d\n", i-7, (i-11)/2); @@ -1207,7 +1218,7 @@ static void pc87360_init_device(struct platform_device *pdev, /* Forcibly enable thermistor channel */ reg = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN_STATUS); - if (!(reg & 0x01)) { + if (!(reg & CHAN_ENA)) { dev_dbg(&pdev->dev, "Forcibly " "enabling temp%d\n", i-7); pc87360_write_value(data, LD_IN, i, @@ -1221,7 +1232,7 @@ static void pc87360_init_device(struct platform_device *pdev, if (data->innr) { reg = pc87360_read_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONFIG); - if (reg & 0x01) { + if (reg & CHAN_ENA) { dev_dbg(&pdev->dev, "Forcibly " "enabling monitoring (VLM)\n"); pc87360_write_value(data, LD_IN, NO_BANK, @@ -1233,7 +1244,7 @@ static void pc87360_init_device(struct platform_device *pdev, if (data->tempnr) { reg = pc87360_read_value(data, LD_TEMP, NO_BANK, PC87365_REG_TEMP_CONFIG); - if (reg & 0x01) { + if (reg & CHAN_ENA) { dev_dbg(&pdev->dev, "Forcibly enabling " "monitoring (TMS)\n"); pc87360_write_value(data, LD_TEMP, NO_BANK, @@ -1336,11 +1347,11 @@ static struct pc87360_data *pc87360_update_device(struct device *dev) pc87360_write_value(data, LD_IN, i, PC87365_REG_IN_STATUS, data->in_status[i]); - if ((data->in_status[i] & 0x81) == 0x81) { + if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { data->in[i] = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN); } - if (data->in_status[i] & 0x01) { + if (data->in_status[i] & CHAN_ENA) { data->in_min[i] = pc87360_read_value(data, LD_IN, i, PC87365_REG_IN_MIN); @@ -1373,12 +1384,12 @@ static struct pc87360_data *pc87360_update_device(struct device *dev) pc87360_write_value(data, LD_TEMP, i, PC87365_REG_TEMP_STATUS, data->temp_status[i]); - if ((data->temp_status[i] & 0x81) == 0x81) { + if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { data->temp[i] = pc87360_read_value(data, LD_TEMP, i, PC87365_REG_TEMP); } - if (data->temp_status[i] & 0x01) { + if (data->temp_status[i] & CHAN_ENA) { data->temp_min[i] = pc87360_read_value(data, LD_TEMP, i, PC87365_REG_TEMP_MIN); -- cgit v1.2.3-70-g09d2 From 492e9657d1a790f44ab9b0e03fc72c1d8145f590 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sat, 18 Oct 2008 20:27:32 -0700 Subject: hwmon/pc87360 separate alarm files: add in-min/max-alarms Adds vin-min/max-alarm callbacks, sensor-device-attribute declarations, and refs to those new decls in the macro used to initialize the vin_group (of sysfs files) [akpm@linux-foundation.org: cleanups] Signed-off-by: Jim Cromie Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/pc87360.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 1043b116bde..3a7d7fa64eb 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -494,11 +494,61 @@ static struct sensor_device_attribute in_max[] = { #define CHAN_ALM_MAX 0x04 /* max limit exceeded */ #define TEMP_ALM_CRIT 0x08 /* temp crit exceeded (temp only) */ +/* show_in_min/max_alarm() reads data from the per-channel status + register (sec 11.5.12), not the vin event status registers (sec + 11.5.2) that (legacy) show_in_alarm() resds (via data->in_alarms) */ + +static ssize_t show_in_min_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); +} +static ssize_t show_in_max_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); +} + +static struct sensor_device_attribute in_min_alarm[] = { + SENSOR_ATTR(in0_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 0), + SENSOR_ATTR(in1_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 1), + SENSOR_ATTR(in2_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 2), + SENSOR_ATTR(in3_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 3), + SENSOR_ATTR(in4_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 4), + SENSOR_ATTR(in5_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 5), + SENSOR_ATTR(in6_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 6), + SENSOR_ATTR(in7_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 7), + SENSOR_ATTR(in8_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 8), + SENSOR_ATTR(in9_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 9), + SENSOR_ATTR(in10_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 10), +}; +static struct sensor_device_attribute in_max_alarm[] = { + SENSOR_ATTR(in0_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 0), + SENSOR_ATTR(in1_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 1), + SENSOR_ATTR(in2_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 2), + SENSOR_ATTR(in3_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 3), + SENSOR_ATTR(in4_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 4), + SENSOR_ATTR(in5_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 5), + SENSOR_ATTR(in6_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 6), + SENSOR_ATTR(in7_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 7), + SENSOR_ATTR(in8_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 8), + SENSOR_ATTR(in9_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 9), + SENSOR_ATTR(in10_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 10), +}; + #define VIN_UNIT_ATTRS(X) \ &in_input[X].dev_attr.attr, \ &in_status[X].dev_attr.attr, \ &in_min[X].dev_attr.attr, \ - &in_max[X].dev_attr.attr + &in_max[X].dev_attr.attr, \ + &in_min_alarm[X].dev_attr.attr, \ + &in_max_alarm[X].dev_attr.attr static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { -- cgit v1.2.3-70-g09d2 From b267e8cdc638dfa53360d532aa56afa0ec12e6a5 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sat, 18 Oct 2008 20:27:32 -0700 Subject: hwmon/pc87360 separate alarm files: add temp-min/max/crit/fault-alarms Adds temp-min/max/crit/fault-alarm callbacks, sensor-device-attribute declarations, and refs to those new decls in the macro used to initialize the temp_group (of sysfs files) [akpm@linux-foundation.org: cleanups] Signed-off-by: Jim Cromie Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/pc87360.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 3a7d7fa64eb..c80ee5f056b 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -845,12 +845,76 @@ static ssize_t show_temp_alarms(struct device *dev, struct device_attribute *att } static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL); +/* show_temp_min/max_alarm() reads data from the per-channel status + register (sec 12.3.7), not the temp event status registers (sec + 12.3.2) that show_temp_alarm() reads (via data->temp_alarms) */ + +static ssize_t show_temp_min_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MIN)); +} +static ssize_t show_temp_max_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MAX)); +} +static ssize_t show_temp_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_ALM_CRIT)); +} + +static struct sensor_device_attribute temp_min_alarm[] = { + SENSOR_ATTR(temp1_min_alarm, S_IRUGO, show_temp_min_alarm, NULL, 0), + SENSOR_ATTR(temp2_min_alarm, S_IRUGO, show_temp_min_alarm, NULL, 1), + SENSOR_ATTR(temp3_min_alarm, S_IRUGO, show_temp_min_alarm, NULL, 2), +}; +static struct sensor_device_attribute temp_max_alarm[] = { + SENSOR_ATTR(temp1_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 0), + SENSOR_ATTR(temp2_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 1), + SENSOR_ATTR(temp3_max_alarm, S_IRUGO, show_temp_max_alarm, NULL, 2), +}; +static struct sensor_device_attribute temp_crit_alarm[] = { + SENSOR_ATTR(temp1_crit_alarm, S_IRUGO, show_temp_crit_alarm, NULL, 0), + SENSOR_ATTR(temp2_crit_alarm, S_IRUGO, show_temp_crit_alarm, NULL, 1), + SENSOR_ATTR(temp3_crit_alarm, S_IRUGO, show_temp_crit_alarm, NULL, 2), +}; + +#define TEMP_FAULT 0x40 /* open diode */ +static ssize_t show_temp_fault(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_FAULT)); +} +static struct sensor_device_attribute temp_fault[] = { + SENSOR_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0), + SENSOR_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1), + SENSOR_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2), +}; + #define TEMP_UNIT_ATTRS(X) \ &temp_input[X].dev_attr.attr, \ &temp_status[X].dev_attr.attr, \ &temp_min[X].dev_attr.attr, \ &temp_max[X].dev_attr.attr, \ - &temp_crit[X].dev_attr.attr + &temp_crit[X].dev_attr.attr, \ + &temp_min_alarm[X].dev_attr.attr, \ + &temp_max_alarm[X].dev_attr.attr, \ + &temp_crit_alarm[X].dev_attr.attr, \ + &temp_fault[X].dev_attr.attr static struct attribute * pc8736x_temp_attr_array[] = { TEMP_UNIT_ATTRS(0), @@ -864,8 +928,8 @@ static const struct attribute_group pc8736x_temp_group = { .attrs = pc8736x_temp_attr_array, }; -static ssize_t show_name(struct device *dev, struct device_attribute - *devattr, char *buf) +static ssize_t show_name(struct device *dev, + struct device_attribute *devattr, char *buf) { struct pc87360_data *data = dev_get_drvdata(dev); return sprintf(buf, "%s\n", data->name); @@ -1086,7 +1150,15 @@ static int __devinit pc87360_probe(struct platform_device *pdev) || (err = device_create_file(dev, &temp_crit[i].dev_attr)) || (err = device_create_file(dev, - &temp_status[i].dev_attr))) + &temp_status[i].dev_attr)) + || (err = device_create_file(dev, + &temp_min_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &temp_max_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &temp_crit_alarm[i].dev_attr)) + || (err = device_create_file(dev, + &temp_fault[i].dev_attr))) goto ERROR3; } if ((err = device_create_file(dev, &dev_attr_alarms_temp))) @@ -1192,6 +1264,10 @@ static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, #define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ #define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ +#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ +#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ +#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ + static void pc87360_init_device(struct platform_device *pdev, int use_thermistors) { -- cgit v1.2.3-70-g09d2 From 2a32ec2500514109754e07721d9d5d431a706a31 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sat, 18 Oct 2008 20:27:33 -0700 Subject: hwmon/pc87360 separate alarm files: define LDNI_MAX const Driver handles 3 logical devices in fixed length array. Give this a define-d constant. Signed-off-by: Jim Cromie Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/pc87360.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index c80ee5f056b..7153b630ad4 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -75,7 +75,8 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); #define FSCM 0x09 /* Logical device: fans */ #define VLM 0x0d /* Logical device: voltages */ #define TMS 0x0e /* Logical device: temperatures */ -static const u8 logdev[3] = { FSCM, VLM, TMS }; +#define LDNI_MAX 3 +static const u8 logdev[LDNI_MAX] = { FSCM, VLM, TMS }; #define LD_FAN 0 #define LD_IN 1 @@ -1074,7 +1075,7 @@ static int __devinit pc87360_probe(struct platform_device *pdev) mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); - for (i = 0; i < 3; i++) { + for (i = 0; i < LDNI_MAX; i++) { if (((data->address[i] = extra_isa[i])) && !request_region(extra_isa[i], PC87360_EXTENT, pc87360_driver.driver.name)) { -- cgit v1.2.3-70-g09d2 From 8ca136741e15e8546e787ae92a7f4aeca84be494 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sat, 18 Oct 2008 20:27:34 -0700 Subject: hwmon/pc87360 separate alarm files: add dev_dbg help temp and vin status register values may be set by chip specifications, set again by bios, or by this previously loaded driver. Debug output nicely displays modprobe init=\d actions. Signed-off-by: Jim Cromie Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/pc87360.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 7153b630ad4..6804a3570f3 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -1290,10 +1290,11 @@ static void pc87360_init_device(struct platform_device *pdev, nr = data->innr < 11 ? data->innr : 11; for (i = 0; i < nr; i++) { + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + dev_dbg(&pdev->dev, "bios in%d status:0x%02x\n", i, reg); if (init >= init_in[i]) { /* Forcibly enable voltage channel */ - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); if (!(reg & CHAN_ENA)) { dev_dbg(&pdev->dev, "Forcibly " "enabling in%d\n", i); @@ -1306,18 +1307,23 @@ static void pc87360_init_device(struct platform_device *pdev, /* We can't blindly trust the Super-I/O space configuration bit, most BIOS won't set it properly */ + dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors); for (i = 11; i < data->innr; i++) { reg = pc87360_read_value(data, LD_IN, i, PC87365_REG_TEMP_STATUS); use_thermistors = use_thermistors || (reg & CHAN_ENA); + /* thermistors are temp[4-6], measured on vin[11-14] */ + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i-7, reg); } + dev_dbg(&pdev->dev, "using thermistors:%d\n", use_thermistors); i = use_thermistors ? 2 : 0; for (; i < data->tempnr; i++) { + reg = pc87360_read_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i+1, reg); if (init >= init_temp[i]) { /* Forcibly enable temperature channel */ - reg = pc87360_read_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS); if (!(reg & CHAN_ENA)) { dev_dbg(&pdev->dev, "Forcibly " "enabling temp%d\n", i+1); @@ -1359,6 +1365,7 @@ static void pc87360_init_device(struct platform_device *pdev, if (data->innr) { reg = pc87360_read_value(data, LD_IN, NO_BANK, PC87365_REG_IN_CONFIG); + dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg); if (reg & CHAN_ENA) { dev_dbg(&pdev->dev, "Forcibly " "enabling monitoring (VLM)\n"); @@ -1371,6 +1378,7 @@ static void pc87360_init_device(struct platform_device *pdev, if (data->tempnr) { reg = pc87360_read_value(data, LD_TEMP, NO_BANK, PC87365_REG_TEMP_CONFIG); + dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg); if (reg & CHAN_ENA) { dev_dbg(&pdev->dev, "Forcibly enabling " "monitoring (TMS)\n"); -- cgit v1.2.3-70-g09d2 From 865c295360f61c2f3634fb1387b4468fdc8287da Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sat, 18 Oct 2008 20:27:35 -0700 Subject: hwmon/pc87360 separate alarm files: add therm-min/max/crit-alarms Adds therm-min/max/crit-alarm callbacks, sensor-device-attribute declarations, and refs to those new decls in the macro used to initialize the therm_group (of sysfs files) The thermistors use voltage channels to measure; so they don't have a fault-alarm, but unlike the other voltages, they do have an overtemp, which we call crit (by convention). [akpm@linux-foundation.org: cleanup] Signed-off-by: Jim Cromie Cc: Jean Delvare Cc: "Mark M. Hoffman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/pc87360.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 6804a3570f3..5fbfa34c110 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -714,12 +714,68 @@ static struct sensor_device_attribute therm_crit[] = { show_therm_crit, set_therm_crit, 2+11), }; +/* show_therm_min/max_alarm() reads data from the per-channel voltage + status register (sec 11.5.12) */ + +static ssize_t show_therm_min_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); +} +static ssize_t show_therm_max_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); +} +static ssize_t show_therm_crit_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & TEMP_ALM_CRIT)); +} + +static struct sensor_device_attribute therm_min_alarm[] = { + SENSOR_ATTR(temp4_min_alarm, S_IRUGO, + show_therm_min_alarm, NULL, 0+11), + SENSOR_ATTR(temp5_min_alarm, S_IRUGO, + show_therm_min_alarm, NULL, 1+11), + SENSOR_ATTR(temp6_min_alarm, S_IRUGO, + show_therm_min_alarm, NULL, 2+11), +}; +static struct sensor_device_attribute therm_max_alarm[] = { + SENSOR_ATTR(temp4_max_alarm, S_IRUGO, + show_therm_max_alarm, NULL, 0+11), + SENSOR_ATTR(temp5_max_alarm, S_IRUGO, + show_therm_max_alarm, NULL, 1+11), + SENSOR_ATTR(temp6_max_alarm, S_IRUGO, + show_therm_max_alarm, NULL, 2+11), +}; +static struct sensor_device_attribute therm_crit_alarm[] = { + SENSOR_ATTR(temp4_crit_alarm, S_IRUGO, + show_therm_crit_alarm, NULL, 0+11), + SENSOR_ATTR(temp5_crit_alarm, S_IRUGO, + show_therm_crit_alarm, NULL, 1+11), + SENSOR_ATTR(temp6_crit_alarm, S_IRUGO, + show_therm_crit_alarm, NULL, 2+11), +}; + #define THERM_UNIT_ATTRS(X) \ &therm_input[X].dev_attr.attr, \ &therm_status[X].dev_attr.attr, \ &therm_min[X].dev_attr.attr, \ &therm_max[X].dev_attr.attr, \ - &therm_crit[X].dev_attr.attr + &therm_crit[X].dev_attr.attr, \ + &therm_min_alarm[X].dev_attr.attr, \ + &therm_max_alarm[X].dev_attr.attr, \ + &therm_crit_alarm[X].dev_attr.attr static struct attribute * pc8736x_therm_attr_array[] = { THERM_UNIT_ATTRS(0), -- cgit v1.2.3-70-g09d2 From 05224091af06177c0ce7c1fae1f498455b47a6be Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Oct 2008 20:27:35 -0700 Subject: hwmon: applesmc: specified number of bytes to read should match actual At one single place in the code, the specified number of bytes to read and the actual number of bytes read differ by one. This one-liner patch fixes that inconsistency. Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Cc: Riki Oktarianto Cc: Mark M. Hoffman Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index b06b8e090a2..b401975bc0d 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -325,7 +325,7 @@ static int applesmc_get_key_type(char* key, char* type) return -EIO; } - outb(5, APPLESMC_DATA_PORT); + outb(6, APPLESMC_DATA_PORT); for (i = 0; i < 6; i++) { if (__wait_status(0x05)) -- cgit v1.2.3-70-g09d2 From 84d2d7f2ee98dc9bd799790c89b5eb8280af2d37 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Oct 2008 20:27:38 -0700 Subject: hwmon: applesmc: fix the 'wait status failed: c != 8' problem On many Macbooks since mid 2007, the Pro, C2D and Air models, applesmc fails to read some or all SMC ports. This problem has various effects, such as flooded logfiles, malfunctioning temperature sensors, accelerometers failing to initialize, and difficulties getting backlight functionality to work properly. The root of the problem seems to be the command protocol. The current code sends out a command byte, then repeatedly polls for an ack before continuing to send or recieve data. From experiments leading to this patch, it seems the command protocol never quite worked or changed so that one now sends a command byte, waits a little bit, polls for an ack, and if it fails, repeats the whole thing by sending the command byte again. This patch implements a send_command function according to the new interpretation of the protocol, and should work also for earlier models. Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Cc: Riki Oktarianto Cc: Mark M. Hoffman Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index b401975bc0d..d5bd0cadbf3 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -190,6 +190,26 @@ static int __wait_status(u8 val) return -EIO; } +/* + * special treatment of command port - on newer macbooks, it seems necessary + * to resend the command byte before polling the status again. Callers must + * hold applesmc_lock. + */ +static int send_command(u8 cmd) +{ + int i; + for (i = 0; i < 1000; i++) { + outb(cmd, APPLESMC_CMD_PORT); + udelay(5); + if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c) + return 0; + udelay(5); + } + printk(KERN_WARNING "applesmc: command failed: %x -> %x\n", + cmd, inb(APPLESMC_CMD_PORT)); + return -EIO; +} + /* * applesmc_read_key - reads len bytes from a given key, and put them in buffer. * Returns zero on success or a negative error on failure. Callers must @@ -205,8 +225,7 @@ static int applesmc_read_key(const char* key, u8* buffer, u8 len) return -EINVAL; } - outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT); - if (__wait_status(0x0c)) + if (send_command(APPLESMC_READ_CMD)) return -EIO; for (i = 0; i < 4; i++) { @@ -249,8 +268,7 @@ static int applesmc_write_key(const char* key, u8* buffer, u8 len) return -EINVAL; } - outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT); - if (__wait_status(0x0c)) + if (send_command(APPLESMC_WRITE_CMD)) return -EIO; for (i = 0; i < 4; i++) { @@ -284,8 +302,7 @@ static int applesmc_get_key_at_index(int index, char* key) readkey[2] = index >> 8; readkey[3] = index; - outb(APPLESMC_GET_KEY_BY_INDEX_CMD, APPLESMC_CMD_PORT); - if (__wait_status(0x0c)) + if (send_command(APPLESMC_GET_KEY_BY_INDEX_CMD)) return -EIO; for (i = 0; i < 4; i++) { @@ -315,8 +332,7 @@ static int applesmc_get_key_type(char* key, char* type) { int i; - outb(APPLESMC_GET_KEY_TYPE_CMD, APPLESMC_CMD_PORT); - if (__wait_status(0x0c)) + if (send_command(APPLESMC_GET_KEY_TYPE_CMD)) return -EIO; for (i = 0; i < 4; i++) { -- cgit v1.2.3-70-g09d2 From 02fcbd144d684167aa67b1d3ad68f18d265f2d08 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Oct 2008 20:27:39 -0700 Subject: hwmon: applesmc: prolong status wait The time to wait for a status change while reading or writing to the SMC ports is a balance between read reliability and system performance. The current setting yields rougly three errors in a thousand when simultaneously reading three different temperature values on a Macbook Air. This patch increases the setting to a value yielding roughly one error in ten thousand, with no noticable system performance degradation. Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Cc: Riki Oktarianto Cc: Mark M. Hoffman Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index d5bd0cadbf3..9a43c4b60ea 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -163,7 +163,7 @@ static unsigned int key_at_index; static struct workqueue_struct *applesmc_led_wq; /* - * __wait_status - Wait up to 2ms for the status port to get a certain value + * __wait_status - Wait up to 10ms for the status port to get a certain value * (masked with 0x0f), returning zero if the value is obtained. Callers must * hold applesmc_lock. */ @@ -173,7 +173,7 @@ static int __wait_status(u8 val) val = val & APPLESMC_STATUS_MASK; - for (i = 0; i < 200; i++) { + for (i = 0; i < 1000; i++) { if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) { if (debug) printk(KERN_DEBUG -- cgit v1.2.3-70-g09d2 From 8bd1a12a51871f0c870612d8220b1485d6aefc73 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Oct 2008 20:27:39 -0700 Subject: hwmon: applesmc: allow for variable ALV0 and ALV1 package length On some recent Macbooks, the package length for the light sensors ALV0 and ALV1 has changed from 6 to 10. This patch allows for a variable package length encompassing both variants. Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Cc: Riki Oktarianto Cc: Mark M. Hoffman Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 9a43c4b60ea..de696b67a4f 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -57,8 +57,8 @@ #define KEY_COUNT_KEY "#KEY" /* r-o ui32 */ -#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6 bytes) */ -#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6 bytes) */ +#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6-10 bytes) */ +#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6-10 bytes) */ #define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */ #define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */ @@ -543,17 +543,27 @@ out: static ssize_t applesmc_light_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { + static int data_length; int ret; u8 left = 0, right = 0; - u8 buffer[6]; + u8 buffer[10], query[6]; mutex_lock(&applesmc_lock); - ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6); + if (!data_length) { + ret = applesmc_get_key_type(LIGHT_SENSOR_LEFT_KEY, query); + if (ret) + goto out; + data_length = clamp_val(query[0], 0, 10); + printk(KERN_INFO "applesmc: light sensor data length set to " + "%d\n", data_length); + } + + ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length); left = buffer[2]; if (ret) goto out; - ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6); + ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length); right = buffer[2]; out: -- cgit v1.2.3-70-g09d2 From f5274c972bac2d14c092a9c634e03f51785c7b76 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Oct 2008 20:27:40 -0700 Subject: hwmon: applesmc: add support for Macbook Air This patch adds accelerometer, backlight and temperature sensor support for the Macbook Air. Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Cc: Riki Oktarianto Cc: Mark M. Hoffman Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index de696b67a4f..b57bd82504a 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -104,6 +104,9 @@ static const char* temperature_sensors_sets[][36] = { /* Set 6: Macbook3 set */ { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TTF0", "TW0P", "Th0H", "Th0S", "Th1H", NULL }, +/* Set 7: Macbook Air */ + { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TC0P", "TCFP", + "TTF0", "TW0P", "Th0H", "Tp0P", "TpFP", "Ts0P", "Ts0S", NULL }, }; /* List of keys used to read/write fan speeds */ @@ -1259,11 +1262,17 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 0, .light = 0, .temperature_set = 5 }, /* MacBook3: accelerometer and temperature set 6 */ { .accelerometer = 1, .light = 0, .temperature_set = 6 }, +/* MacBook Air: accelerometer, backlight and temperature set 7 */ + { .accelerometer = 1, .light = 1, .temperature_set = 7 }, }; /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ static __initdata struct dmi_system_id applesmc_whitelist[] = { + { applesmc_dmi_match, "Apple MacBook Air", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, + (void*)&applesmc_dmi_data[7]}, { applesmc_dmi_match, "Apple MacBook Pro", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, -- cgit v1.2.3-70-g09d2 From 7b5e3cb28f52f42d8cf2b36771be580f33bbc7ae Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 18 Oct 2008 20:27:41 -0700 Subject: drivers/hwmon/applesmc.c: remove unneeded casts dmi_system_id.driver_data is already void*. Cc: Henrik Rydberg Cc: Nicolas Boichat Cc: Riki Oktarianto Cc: Mark M. Hoffman Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index b57bd82504a..208520c6eb0 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -1272,35 +1272,35 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = { { applesmc_dmi_match, "Apple MacBook Air", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, - (void*)&applesmc_dmi_data[7]}, + &applesmc_dmi_data[7]}, { applesmc_dmi_match, "Apple MacBook Pro", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, - (void*)&applesmc_dmi_data[0]}, + &applesmc_dmi_data[0]}, { applesmc_dmi_match, "Apple MacBook (v2)", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook2") }, - (void*)&applesmc_dmi_data[1]}, + &applesmc_dmi_data[1]}, { applesmc_dmi_match, "Apple MacBook (v3)", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") }, - (void*)&applesmc_dmi_data[6]}, + &applesmc_dmi_data[6]}, { applesmc_dmi_match, "Apple MacBook", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") }, - (void*)&applesmc_dmi_data[2]}, + &applesmc_dmi_data[2]}, { applesmc_dmi_match, "Apple Macmini", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") }, - (void*)&applesmc_dmi_data[3]}, + &applesmc_dmi_data[3]}, { applesmc_dmi_match, "Apple MacPro2", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") }, - (void*)&applesmc_dmi_data[4]}, + &applesmc_dmi_data[4]}, { applesmc_dmi_match, "Apple iMac", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"iMac") }, - (void*)&applesmc_dmi_data[5]}, + &applesmc_dmi_data[5]}, { .ident = NULL } }; -- cgit v1.2.3-70-g09d2 From d7549905f157f217b888edb0f2055f5090eaf1d8 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Oct 2008 20:27:41 -0700 Subject: hwmon: applesmc: Add support for Macbook Pro 4 Adds temperature sensor support for the Macbook Pro 4. Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Cc: Riki Oktarianto Cc: Mark M. Hoffman Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 208520c6eb0..efca98a2e35 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -107,6 +107,9 @@ static const char* temperature_sensors_sets[][36] = { /* Set 7: Macbook Air */ { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TC0P", "TCFP", "TTF0", "TW0P", "Th0H", "Tp0P", "TpFP", "Ts0P", "Ts0S", NULL }, +/* Set 8: Macbook Pro 4,1 (Penryn) */ + { "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H", + "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, }; /* List of keys used to read/write fan speeds */ @@ -1264,6 +1267,8 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 1, .light = 0, .temperature_set = 6 }, /* MacBook Air: accelerometer, backlight and temperature set 7 */ { .accelerometer = 1, .light = 1, .temperature_set = 7 }, +/* MacBook Pro 4: accelerometer, backlight and temperature set 8 */ + { .accelerometer = 1, .light = 1, .temperature_set = 8 }, }; /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". @@ -1273,6 +1278,10 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, &applesmc_dmi_data[7]}, + { applesmc_dmi_match, "Apple MacBook Pro 4", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") }, + &applesmc_dmi_data[8]}, { applesmc_dmi_match, "Apple MacBook Pro", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, -- cgit v1.2.3-70-g09d2 From 07e8dbd3ebbdedc71335049dd4b0d542cb038d7d Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Oct 2008 20:27:42 -0700 Subject: hwmon: applesmc: Add support for Macbook Pro 3 Add temperature sensor support for Macbook Pro 3. Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Cc: Riki Oktarianto Cc: Mark M. Hoffman Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index efca98a2e35..59266ce60cc 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -110,6 +110,9 @@ static const char* temperature_sensors_sets[][36] = { /* Set 8: Macbook Pro 4,1 (Penryn) */ { "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", "Th0H", "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, +/* Set 9: Macbook Pro 3,1 (Santa Rosa) */ + { "TALP", "TB0T", "TC0D", "TC0P", "TG0D", "TG0H", "TTF0", "TW0P", + "Th0H", "Th1H", "Th2H", "Tm0P", "Ts0P", NULL }, }; /* List of keys used to read/write fan speeds */ @@ -1269,6 +1272,8 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 1, .light = 1, .temperature_set = 7 }, /* MacBook Pro 4: accelerometer, backlight and temperature set 8 */ { .accelerometer = 1, .light = 1, .temperature_set = 8 }, +/* MacBook Pro 3: accelerometer, backlight and temperature set 9 */ + { .accelerometer = 1, .light = 1, .temperature_set = 9 }, }; /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". @@ -1282,6 +1287,10 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4") }, &applesmc_dmi_data[8]}, + { applesmc_dmi_match, "Apple MacBook Pro 3", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3") }, + &applesmc_dmi_data[9]}, { applesmc_dmi_match, "Apple MacBook Pro", { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") }, -- cgit v1.2.3-70-g09d2 From 8c9398d1e9766e3659e277acb2e8ca1c17684139 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Oct 2008 20:27:43 -0700 Subject: hwmon: applesmc: lighter wait mechanism, drastic improvement The read fail ratio is sensitive to the delay between the first byte written and the first byte read; apparently the sensors cannot be rushed. Increasing the minimum wait time, without changing the total wait time, improves the fail ratio from a 8% chance that any of the sensors fails in one read, down to 0.4%, on a Macbook Air. On a Macbook Pro 3,1, the effect is even more apparent. By reducing the number of status polls, the ratio is further improved to below 0.1%. Finally, increasing the total wait time brings the fail ratio down to virtually zero. Signed-off-by: Henrik Rydberg Tested-by: Bob McElrath Cc: Nicolas Boichat Cc: "Mark M. Hoffman" Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 59266ce60cc..bc011da79e1 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -49,6 +49,9 @@ #define APPLESMC_MAX_DATA_LENGTH 32 +#define APPLESMC_MIN_WAIT 0x0040 +#define APPLESMC_MAX_WAIT 0x8000 + #define APPLESMC_STATUS_MASK 0x0f #define APPLESMC_READ_CMD 0x10 #define APPLESMC_WRITE_CMD 0x11 @@ -172,25 +175,25 @@ static unsigned int key_at_index; static struct workqueue_struct *applesmc_led_wq; /* - * __wait_status - Wait up to 10ms for the status port to get a certain value + * __wait_status - Wait up to 32ms for the status port to get a certain value * (masked with 0x0f), returning zero if the value is obtained. Callers must * hold applesmc_lock. */ static int __wait_status(u8 val) { - unsigned int i; + int us; val = val & APPLESMC_STATUS_MASK; - for (i = 0; i < 1000; i++) { + for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { + udelay(us); if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) { if (debug) printk(KERN_DEBUG - "Waited %d us for status %x\n", - i*10, val); + "Waited %d us for status %x\n", + 2 * us - APPLESMC_MIN_WAIT, val); return 0; } - udelay(10); } printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n", @@ -206,13 +209,12 @@ static int __wait_status(u8 val) */ static int send_command(u8 cmd) { - int i; - for (i = 0; i < 1000; i++) { + int us; + for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { outb(cmd, APPLESMC_CMD_PORT); - udelay(5); + udelay(us); if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c) return 0; - udelay(5); } printk(KERN_WARNING "applesmc: command failed: %x -> %x\n", cmd, inb(APPLESMC_CMD_PORT)); -- cgit v1.2.3-70-g09d2