13bcfa9e4SHerbert Valerio Riedel /* 23bcfa9e4SHerbert Valerio Riedel g760a - Driver for the Global Mixed-mode Technology Inc. G760A 33bcfa9e4SHerbert Valerio Riedel fan speed PWM controller chip 43bcfa9e4SHerbert Valerio Riedel 53bcfa9e4SHerbert Valerio Riedel Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> 63bcfa9e4SHerbert Valerio Riedel 73bcfa9e4SHerbert Valerio Riedel Complete datasheet is available at GMT's website: 8631dd1a8SJustin P. Mattock http://www.gmt.com.tw/product/datasheet/EDS-760A.pdf 93bcfa9e4SHerbert Valerio Riedel 103bcfa9e4SHerbert Valerio Riedel This program is free software; you can redistribute it and/or modify 113bcfa9e4SHerbert Valerio Riedel it under the terms of the GNU General Public License as published by 123bcfa9e4SHerbert Valerio Riedel the Free Software Foundation; either version 2 of the License, or 133bcfa9e4SHerbert Valerio Riedel (at your option) any later version. 143bcfa9e4SHerbert Valerio Riedel */ 153bcfa9e4SHerbert Valerio Riedel 163bcfa9e4SHerbert Valerio Riedel #include <linux/module.h> 173bcfa9e4SHerbert Valerio Riedel #include <linux/init.h> 183bcfa9e4SHerbert Valerio Riedel #include <linux/slab.h> 193bcfa9e4SHerbert Valerio Riedel #include <linux/jiffies.h> 203bcfa9e4SHerbert Valerio Riedel #include <linux/i2c.h> 213bcfa9e4SHerbert Valerio Riedel #include <linux/hwmon.h> 223bcfa9e4SHerbert Valerio Riedel #include <linux/hwmon-sysfs.h> 233bcfa9e4SHerbert Valerio Riedel #include <linux/err.h> 243bcfa9e4SHerbert Valerio Riedel #include <linux/mutex.h> 253bcfa9e4SHerbert Valerio Riedel #include <linux/sysfs.h> 263bcfa9e4SHerbert Valerio Riedel 273bcfa9e4SHerbert Valerio Riedel static const struct i2c_device_id g760a_id[] = { 283bcfa9e4SHerbert Valerio Riedel { "g760a", 0 }, 293bcfa9e4SHerbert Valerio Riedel { } 303bcfa9e4SHerbert Valerio Riedel }; 313bcfa9e4SHerbert Valerio Riedel MODULE_DEVICE_TABLE(i2c, g760a_id); 323bcfa9e4SHerbert Valerio Riedel 333bcfa9e4SHerbert Valerio Riedel enum g760a_regs { 343bcfa9e4SHerbert Valerio Riedel G760A_REG_SET_CNT = 0x00, 353bcfa9e4SHerbert Valerio Riedel G760A_REG_ACT_CNT = 0x01, 363bcfa9e4SHerbert Valerio Riedel G760A_REG_FAN_STA = 0x02 373bcfa9e4SHerbert Valerio Riedel }; 383bcfa9e4SHerbert Valerio Riedel 393bcfa9e4SHerbert Valerio Riedel #define G760A_REG_FAN_STA_RPM_OFF 0x1 /* +/-20% off */ 403bcfa9e4SHerbert Valerio Riedel #define G760A_REG_FAN_STA_RPM_LOW 0x2 /* below 1920rpm */ 413bcfa9e4SHerbert Valerio Riedel 423bcfa9e4SHerbert Valerio Riedel /* register data is read (and cached) at most once per second */ 433bcfa9e4SHerbert Valerio Riedel #define G760A_UPDATE_INTERVAL (HZ) 443bcfa9e4SHerbert Valerio Riedel 453bcfa9e4SHerbert Valerio Riedel struct g760a_data { 463bcfa9e4SHerbert Valerio Riedel struct i2c_client *client; 473bcfa9e4SHerbert Valerio Riedel struct device *hwmon_dev; 483bcfa9e4SHerbert Valerio Riedel struct mutex update_lock; 493bcfa9e4SHerbert Valerio Riedel 503bcfa9e4SHerbert Valerio Riedel /* board specific parameters */ 513bcfa9e4SHerbert Valerio Riedel u32 clk; /* default 32kHz */ 523bcfa9e4SHerbert Valerio Riedel u16 fan_div; /* default P=2 */ 533bcfa9e4SHerbert Valerio Riedel 543bcfa9e4SHerbert Valerio Riedel /* g760a register cache */ 553bcfa9e4SHerbert Valerio Riedel unsigned int valid:1; 563bcfa9e4SHerbert Valerio Riedel unsigned long last_updated; /* In jiffies */ 573bcfa9e4SHerbert Valerio Riedel 583bcfa9e4SHerbert Valerio Riedel u8 set_cnt; /* PWM (period) count number; 0xff stops fan */ 593bcfa9e4SHerbert Valerio Riedel u8 act_cnt; /* formula: cnt = (CLK * 30)/(rpm * P) */ 603bcfa9e4SHerbert Valerio Riedel u8 fan_sta; /* bit 0: set when actual fan speed more than 20% 613bcfa9e4SHerbert Valerio Riedel * outside requested fan speed 623bcfa9e4SHerbert Valerio Riedel * bit 1: set when fan speed below 1920 rpm */ 633bcfa9e4SHerbert Valerio Riedel }; 643bcfa9e4SHerbert Valerio Riedel 653bcfa9e4SHerbert Valerio Riedel #define G760A_DEFAULT_CLK 32768 663bcfa9e4SHerbert Valerio Riedel #define G760A_DEFAULT_FAN_DIV 2 673bcfa9e4SHerbert Valerio Riedel 683bcfa9e4SHerbert Valerio Riedel #define PWM_FROM_CNT(cnt) (0xff-(cnt)) 693bcfa9e4SHerbert Valerio Riedel #define PWM_TO_CNT(pwm) (0xff-(pwm)) 703bcfa9e4SHerbert Valerio Riedel 71ebec05bdSJean Delvare static inline unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) 723bcfa9e4SHerbert Valerio Riedel { 733bcfa9e4SHerbert Valerio Riedel return ((val == 0x00) ? 0 : ((clk*30)/(val*div))); 743bcfa9e4SHerbert Valerio Riedel } 753bcfa9e4SHerbert Valerio Riedel 763bcfa9e4SHerbert Valerio Riedel /* new-style driver model */ 773bcfa9e4SHerbert Valerio Riedel static int g760a_probe(struct i2c_client *client, 783bcfa9e4SHerbert Valerio Riedel const struct i2c_device_id *id); 793bcfa9e4SHerbert Valerio Riedel static int g760a_remove(struct i2c_client *client); 803bcfa9e4SHerbert Valerio Riedel 813bcfa9e4SHerbert Valerio Riedel static struct i2c_driver g760a_driver = { 823bcfa9e4SHerbert Valerio Riedel .driver = { 833bcfa9e4SHerbert Valerio Riedel .name = "g760a", 843bcfa9e4SHerbert Valerio Riedel }, 853bcfa9e4SHerbert Valerio Riedel .probe = g760a_probe, 863bcfa9e4SHerbert Valerio Riedel .remove = g760a_remove, 873bcfa9e4SHerbert Valerio Riedel .id_table = g760a_id, 883bcfa9e4SHerbert Valerio Riedel }; 893bcfa9e4SHerbert Valerio Riedel 903bcfa9e4SHerbert Valerio Riedel /* read/write wrappers */ 913bcfa9e4SHerbert Valerio Riedel static int g760a_read_value(struct i2c_client *client, enum g760a_regs reg) 923bcfa9e4SHerbert Valerio Riedel { 933bcfa9e4SHerbert Valerio Riedel return i2c_smbus_read_byte_data(client, reg); 943bcfa9e4SHerbert Valerio Riedel } 953bcfa9e4SHerbert Valerio Riedel 963bcfa9e4SHerbert Valerio Riedel static int g760a_write_value(struct i2c_client *client, enum g760a_regs reg, 973bcfa9e4SHerbert Valerio Riedel u16 value) 983bcfa9e4SHerbert Valerio Riedel { 993bcfa9e4SHerbert Valerio Riedel return i2c_smbus_write_byte_data(client, reg, value); 1003bcfa9e4SHerbert Valerio Riedel } 1013bcfa9e4SHerbert Valerio Riedel 1023bcfa9e4SHerbert Valerio Riedel /**************************************************************************** 1033bcfa9e4SHerbert Valerio Riedel * sysfs attributes 1043bcfa9e4SHerbert Valerio Riedel */ 1053bcfa9e4SHerbert Valerio Riedel 1063bcfa9e4SHerbert Valerio Riedel static struct g760a_data *g760a_update_client(struct device *dev) 1073bcfa9e4SHerbert Valerio Riedel { 1083bcfa9e4SHerbert Valerio Riedel struct i2c_client *client = to_i2c_client(dev); 1093bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = i2c_get_clientdata(client); 1103bcfa9e4SHerbert Valerio Riedel 1113bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 1123bcfa9e4SHerbert Valerio Riedel 1133bcfa9e4SHerbert Valerio Riedel if (time_after(jiffies, data->last_updated + G760A_UPDATE_INTERVAL) 1143bcfa9e4SHerbert Valerio Riedel || !data->valid) { 1153bcfa9e4SHerbert Valerio Riedel dev_dbg(&client->dev, "Starting g760a update\n"); 1163bcfa9e4SHerbert Valerio Riedel 1173bcfa9e4SHerbert Valerio Riedel data->set_cnt = g760a_read_value(client, G760A_REG_SET_CNT); 1183bcfa9e4SHerbert Valerio Riedel data->act_cnt = g760a_read_value(client, G760A_REG_ACT_CNT); 1193bcfa9e4SHerbert Valerio Riedel data->fan_sta = g760a_read_value(client, G760A_REG_FAN_STA); 1203bcfa9e4SHerbert Valerio Riedel 1213bcfa9e4SHerbert Valerio Riedel data->last_updated = jiffies; 1223bcfa9e4SHerbert Valerio Riedel data->valid = 1; 1233bcfa9e4SHerbert Valerio Riedel } 1243bcfa9e4SHerbert Valerio Riedel 1253bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1263bcfa9e4SHerbert Valerio Riedel 1273bcfa9e4SHerbert Valerio Riedel return data; 1283bcfa9e4SHerbert Valerio Riedel } 1293bcfa9e4SHerbert Valerio Riedel 1303bcfa9e4SHerbert Valerio Riedel static ssize_t show_fan(struct device *dev, struct device_attribute *da, 1313bcfa9e4SHerbert Valerio Riedel char *buf) 1323bcfa9e4SHerbert Valerio Riedel { 1333bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1343bcfa9e4SHerbert Valerio Riedel unsigned int rpm = 0; 1353bcfa9e4SHerbert Valerio Riedel 1363bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 1373bcfa9e4SHerbert Valerio Riedel if (!(data->fan_sta & G760A_REG_FAN_STA_RPM_LOW)) 1383bcfa9e4SHerbert Valerio Riedel rpm = rpm_from_cnt(data->act_cnt, data->clk, data->fan_div); 1393bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1403bcfa9e4SHerbert Valerio Riedel 1413bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", rpm); 1423bcfa9e4SHerbert Valerio Riedel } 1433bcfa9e4SHerbert Valerio Riedel 1443bcfa9e4SHerbert Valerio Riedel static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da, 1453bcfa9e4SHerbert Valerio Riedel char *buf) 1463bcfa9e4SHerbert Valerio Riedel { 1473bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1483bcfa9e4SHerbert Valerio Riedel 1493bcfa9e4SHerbert Valerio Riedel int fan_alarm = (data->fan_sta & G760A_REG_FAN_STA_RPM_OFF) ? 1 : 0; 1503bcfa9e4SHerbert Valerio Riedel 1513bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", fan_alarm); 1523bcfa9e4SHerbert Valerio Riedel } 1533bcfa9e4SHerbert Valerio Riedel 1543bcfa9e4SHerbert Valerio Riedel static ssize_t get_pwm(struct device *dev, struct device_attribute *da, 1553bcfa9e4SHerbert Valerio Riedel char *buf) 1563bcfa9e4SHerbert Valerio Riedel { 1573bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1583bcfa9e4SHerbert Valerio Riedel 1593bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", PWM_FROM_CNT(data->set_cnt)); 1603bcfa9e4SHerbert Valerio Riedel } 1613bcfa9e4SHerbert Valerio Riedel 1623bcfa9e4SHerbert Valerio Riedel static ssize_t set_pwm(struct device *dev, struct device_attribute *da, 1633bcfa9e4SHerbert Valerio Riedel const char *buf, size_t count) 1643bcfa9e4SHerbert Valerio Riedel { 1653bcfa9e4SHerbert Valerio Riedel struct i2c_client *client = to_i2c_client(dev); 1663bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1673bcfa9e4SHerbert Valerio Riedel unsigned long val; 1683bcfa9e4SHerbert Valerio Riedel 169179c4fdbSFrans Meulenbroeks if (kstrtoul(buf, 10, &val)) 1703bcfa9e4SHerbert Valerio Riedel return -EINVAL; 1713bcfa9e4SHerbert Valerio Riedel 1723bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 1733bcfa9e4SHerbert Valerio Riedel data->set_cnt = PWM_TO_CNT(SENSORS_LIMIT(val, 0, 255)); 1743bcfa9e4SHerbert Valerio Riedel g760a_write_value(client, G760A_REG_SET_CNT, data->set_cnt); 1753bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1763bcfa9e4SHerbert Valerio Riedel 1773bcfa9e4SHerbert Valerio Riedel return count; 1783bcfa9e4SHerbert Valerio Riedel } 1793bcfa9e4SHerbert Valerio Riedel 1803bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm); 1813bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL); 1823bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL); 1833bcfa9e4SHerbert Valerio Riedel 1843bcfa9e4SHerbert Valerio Riedel static struct attribute *g760a_attributes[] = { 1853bcfa9e4SHerbert Valerio Riedel &dev_attr_pwm1.attr, 1863bcfa9e4SHerbert Valerio Riedel &dev_attr_fan1_input.attr, 1873bcfa9e4SHerbert Valerio Riedel &dev_attr_fan1_alarm.attr, 1883bcfa9e4SHerbert Valerio Riedel NULL 1893bcfa9e4SHerbert Valerio Riedel }; 1903bcfa9e4SHerbert Valerio Riedel 1913bcfa9e4SHerbert Valerio Riedel static const struct attribute_group g760a_group = { 1923bcfa9e4SHerbert Valerio Riedel .attrs = g760a_attributes, 1933bcfa9e4SHerbert Valerio Riedel }; 1943bcfa9e4SHerbert Valerio Riedel 1953bcfa9e4SHerbert Valerio Riedel /**************************************************************************** 1963bcfa9e4SHerbert Valerio Riedel * new-style driver model code 1973bcfa9e4SHerbert Valerio Riedel */ 1983bcfa9e4SHerbert Valerio Riedel 1993bcfa9e4SHerbert Valerio Riedel static int g760a_probe(struct i2c_client *client, 2003bcfa9e4SHerbert Valerio Riedel const struct i2c_device_id *id) 2013bcfa9e4SHerbert Valerio Riedel { 2023bcfa9e4SHerbert Valerio Riedel struct g760a_data *data; 2033bcfa9e4SHerbert Valerio Riedel int err; 2043bcfa9e4SHerbert Valerio Riedel 2053bcfa9e4SHerbert Valerio Riedel if (!i2c_check_functionality(client->adapter, 2063bcfa9e4SHerbert Valerio Riedel I2C_FUNC_SMBUS_BYTE_DATA)) 2073bcfa9e4SHerbert Valerio Riedel return -EIO; 2083bcfa9e4SHerbert Valerio Riedel 2093bcfa9e4SHerbert Valerio Riedel data = kzalloc(sizeof(struct g760a_data), GFP_KERNEL); 2103bcfa9e4SHerbert Valerio Riedel if (!data) 2113bcfa9e4SHerbert Valerio Riedel return -ENOMEM; 2123bcfa9e4SHerbert Valerio Riedel 2133bcfa9e4SHerbert Valerio Riedel i2c_set_clientdata(client, data); 2143bcfa9e4SHerbert Valerio Riedel 2153bcfa9e4SHerbert Valerio Riedel data->client = client; 2163bcfa9e4SHerbert Valerio Riedel mutex_init(&data->update_lock); 2173bcfa9e4SHerbert Valerio Riedel 2183bcfa9e4SHerbert Valerio Riedel /* setup default configuration for now */ 2193bcfa9e4SHerbert Valerio Riedel data->fan_div = G760A_DEFAULT_FAN_DIV; 2203bcfa9e4SHerbert Valerio Riedel data->clk = G760A_DEFAULT_CLK; 2213bcfa9e4SHerbert Valerio Riedel 2223bcfa9e4SHerbert Valerio Riedel /* Register sysfs hooks */ 2233bcfa9e4SHerbert Valerio Riedel err = sysfs_create_group(&client->dev.kobj, &g760a_group); 2243bcfa9e4SHerbert Valerio Riedel if (err) 2253bcfa9e4SHerbert Valerio Riedel goto error_sysfs_create_group; 2263bcfa9e4SHerbert Valerio Riedel 2273bcfa9e4SHerbert Valerio Riedel data->hwmon_dev = hwmon_device_register(&client->dev); 2283bcfa9e4SHerbert Valerio Riedel if (IS_ERR(data->hwmon_dev)) { 2293bcfa9e4SHerbert Valerio Riedel err = PTR_ERR(data->hwmon_dev); 2303bcfa9e4SHerbert Valerio Riedel goto error_hwmon_device_register; 2313bcfa9e4SHerbert Valerio Riedel } 2323bcfa9e4SHerbert Valerio Riedel 2333bcfa9e4SHerbert Valerio Riedel return 0; 2343bcfa9e4SHerbert Valerio Riedel 2353bcfa9e4SHerbert Valerio Riedel error_hwmon_device_register: 2363bcfa9e4SHerbert Valerio Riedel sysfs_remove_group(&client->dev.kobj, &g760a_group); 2373bcfa9e4SHerbert Valerio Riedel error_sysfs_create_group: 2383bcfa9e4SHerbert Valerio Riedel kfree(data); 2393bcfa9e4SHerbert Valerio Riedel 2403bcfa9e4SHerbert Valerio Riedel return err; 2413bcfa9e4SHerbert Valerio Riedel } 2423bcfa9e4SHerbert Valerio Riedel 2433bcfa9e4SHerbert Valerio Riedel static int g760a_remove(struct i2c_client *client) 2443bcfa9e4SHerbert Valerio Riedel { 2453bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = i2c_get_clientdata(client); 2463bcfa9e4SHerbert Valerio Riedel hwmon_device_unregister(data->hwmon_dev); 2473bcfa9e4SHerbert Valerio Riedel sysfs_remove_group(&client->dev.kobj, &g760a_group); 2483bcfa9e4SHerbert Valerio Riedel kfree(data); 2493bcfa9e4SHerbert Valerio Riedel 2503bcfa9e4SHerbert Valerio Riedel return 0; 2513bcfa9e4SHerbert Valerio Riedel } 2523bcfa9e4SHerbert Valerio Riedel 253*f0967eeaSAxel Lin module_i2c_driver(g760a_driver); 2543bcfa9e4SHerbert Valerio Riedel 2553bcfa9e4SHerbert Valerio Riedel MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); 2563bcfa9e4SHerbert Valerio Riedel MODULE_DESCRIPTION("GMT G760A driver"); 2573bcfa9e4SHerbert Valerio Riedel MODULE_LICENSE("GPL"); 258