13bcfa9e4SHerbert Valerio Riedel /* 21b9c491dSGuenter Roeck * g760a - Driver for the Global Mixed-mode Technology Inc. G760A 31b9c491dSGuenter Roeck * fan speed PWM controller chip 41b9c491dSGuenter Roeck * 51b9c491dSGuenter Roeck * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> 61b9c491dSGuenter Roeck * 71b9c491dSGuenter Roeck * Complete datasheet is available at GMT's website: 81b9c491dSGuenter Roeck * http://www.gmt.com.tw/product/datasheet/EDS-760A.pdf 91b9c491dSGuenter Roeck * 101b9c491dSGuenter Roeck * This program is free software; you can redistribute it and/or modify 111b9c491dSGuenter Roeck * it under the terms of the GNU General Public License as published by 121b9c491dSGuenter Roeck * the Free Software Foundation; either version 2 of the License, or 131b9c491dSGuenter Roeck * (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 enum g760a_regs { 283bcfa9e4SHerbert Valerio Riedel G760A_REG_SET_CNT = 0x00, 293bcfa9e4SHerbert Valerio Riedel G760A_REG_ACT_CNT = 0x01, 303bcfa9e4SHerbert Valerio Riedel G760A_REG_FAN_STA = 0x02 313bcfa9e4SHerbert Valerio Riedel }; 323bcfa9e4SHerbert Valerio Riedel 333bcfa9e4SHerbert Valerio Riedel #define G760A_REG_FAN_STA_RPM_OFF 0x1 /* +/-20% off */ 343bcfa9e4SHerbert Valerio Riedel #define G760A_REG_FAN_STA_RPM_LOW 0x2 /* below 1920rpm */ 353bcfa9e4SHerbert Valerio Riedel 363bcfa9e4SHerbert Valerio Riedel /* register data is read (and cached) at most once per second */ 373bcfa9e4SHerbert Valerio Riedel #define G760A_UPDATE_INTERVAL (HZ) 383bcfa9e4SHerbert Valerio Riedel 393bcfa9e4SHerbert Valerio Riedel struct g760a_data { 403bcfa9e4SHerbert Valerio Riedel struct i2c_client *client; 413bcfa9e4SHerbert Valerio Riedel struct device *hwmon_dev; 423bcfa9e4SHerbert Valerio Riedel struct mutex update_lock; 433bcfa9e4SHerbert Valerio Riedel 443bcfa9e4SHerbert Valerio Riedel /* board specific parameters */ 453bcfa9e4SHerbert Valerio Riedel u32 clk; /* default 32kHz */ 463bcfa9e4SHerbert Valerio Riedel u16 fan_div; /* default P=2 */ 473bcfa9e4SHerbert Valerio Riedel 483bcfa9e4SHerbert Valerio Riedel /* g760a register cache */ 493bcfa9e4SHerbert Valerio Riedel unsigned int valid:1; 503bcfa9e4SHerbert Valerio Riedel unsigned long last_updated; /* In jiffies */ 513bcfa9e4SHerbert Valerio Riedel 523bcfa9e4SHerbert Valerio Riedel u8 set_cnt; /* PWM (period) count number; 0xff stops fan */ 533bcfa9e4SHerbert Valerio Riedel u8 act_cnt; /* formula: cnt = (CLK * 30)/(rpm * P) */ 543bcfa9e4SHerbert Valerio Riedel u8 fan_sta; /* bit 0: set when actual fan speed more than 20% 553bcfa9e4SHerbert Valerio Riedel * outside requested fan speed 561b9c491dSGuenter Roeck * bit 1: set when fan speed below 1920 rpm 571b9c491dSGuenter Roeck */ 583bcfa9e4SHerbert Valerio Riedel }; 593bcfa9e4SHerbert Valerio Riedel 603bcfa9e4SHerbert Valerio Riedel #define G760A_DEFAULT_CLK 32768 613bcfa9e4SHerbert Valerio Riedel #define G760A_DEFAULT_FAN_DIV 2 623bcfa9e4SHerbert Valerio Riedel 633bcfa9e4SHerbert Valerio Riedel #define PWM_FROM_CNT(cnt) (0xff-(cnt)) 643bcfa9e4SHerbert Valerio Riedel #define PWM_TO_CNT(pwm) (0xff-(pwm)) 653bcfa9e4SHerbert Valerio Riedel 66ebec05bdSJean Delvare static inline unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) 673bcfa9e4SHerbert Valerio Riedel { 683bcfa9e4SHerbert Valerio Riedel return ((val == 0x00) ? 0 : ((clk*30)/(val*div))); 693bcfa9e4SHerbert Valerio Riedel } 703bcfa9e4SHerbert Valerio Riedel 713bcfa9e4SHerbert Valerio Riedel /* read/write wrappers */ 723bcfa9e4SHerbert Valerio Riedel static int g760a_read_value(struct i2c_client *client, enum g760a_regs reg) 733bcfa9e4SHerbert Valerio Riedel { 743bcfa9e4SHerbert Valerio Riedel return i2c_smbus_read_byte_data(client, reg); 753bcfa9e4SHerbert Valerio Riedel } 763bcfa9e4SHerbert Valerio Riedel 773bcfa9e4SHerbert Valerio Riedel static int g760a_write_value(struct i2c_client *client, enum g760a_regs reg, 783bcfa9e4SHerbert Valerio Riedel u16 value) 793bcfa9e4SHerbert Valerio Riedel { 803bcfa9e4SHerbert Valerio Riedel return i2c_smbus_write_byte_data(client, reg, value); 813bcfa9e4SHerbert Valerio Riedel } 823bcfa9e4SHerbert Valerio Riedel 831b9c491dSGuenter Roeck /* 843bcfa9e4SHerbert Valerio Riedel * sysfs attributes 853bcfa9e4SHerbert Valerio Riedel */ 863bcfa9e4SHerbert Valerio Riedel 873bcfa9e4SHerbert Valerio Riedel static struct g760a_data *g760a_update_client(struct device *dev) 883bcfa9e4SHerbert Valerio Riedel { 893bcfa9e4SHerbert Valerio Riedel struct i2c_client *client = to_i2c_client(dev); 903bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = i2c_get_clientdata(client); 913bcfa9e4SHerbert Valerio Riedel 923bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 933bcfa9e4SHerbert Valerio Riedel 943bcfa9e4SHerbert Valerio Riedel if (time_after(jiffies, data->last_updated + G760A_UPDATE_INTERVAL) 953bcfa9e4SHerbert Valerio Riedel || !data->valid) { 963bcfa9e4SHerbert Valerio Riedel dev_dbg(&client->dev, "Starting g760a update\n"); 973bcfa9e4SHerbert Valerio Riedel 983bcfa9e4SHerbert Valerio Riedel data->set_cnt = g760a_read_value(client, G760A_REG_SET_CNT); 993bcfa9e4SHerbert Valerio Riedel data->act_cnt = g760a_read_value(client, G760A_REG_ACT_CNT); 1003bcfa9e4SHerbert Valerio Riedel data->fan_sta = g760a_read_value(client, G760A_REG_FAN_STA); 1013bcfa9e4SHerbert Valerio Riedel 1023bcfa9e4SHerbert Valerio Riedel data->last_updated = jiffies; 1033bcfa9e4SHerbert Valerio Riedel data->valid = 1; 1043bcfa9e4SHerbert Valerio Riedel } 1053bcfa9e4SHerbert Valerio Riedel 1063bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1073bcfa9e4SHerbert Valerio Riedel 1083bcfa9e4SHerbert Valerio Riedel return data; 1093bcfa9e4SHerbert Valerio Riedel } 1103bcfa9e4SHerbert Valerio Riedel 1113bcfa9e4SHerbert Valerio Riedel static ssize_t show_fan(struct device *dev, struct device_attribute *da, 1123bcfa9e4SHerbert Valerio Riedel char *buf) 1133bcfa9e4SHerbert Valerio Riedel { 1143bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1153bcfa9e4SHerbert Valerio Riedel unsigned int rpm = 0; 1163bcfa9e4SHerbert Valerio Riedel 1173bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 1183bcfa9e4SHerbert Valerio Riedel if (!(data->fan_sta & G760A_REG_FAN_STA_RPM_LOW)) 1193bcfa9e4SHerbert Valerio Riedel rpm = rpm_from_cnt(data->act_cnt, data->clk, data->fan_div); 1203bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1213bcfa9e4SHerbert Valerio Riedel 1223bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", rpm); 1233bcfa9e4SHerbert Valerio Riedel } 1243bcfa9e4SHerbert Valerio Riedel 1253bcfa9e4SHerbert Valerio Riedel static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da, 1263bcfa9e4SHerbert Valerio Riedel char *buf) 1273bcfa9e4SHerbert Valerio Riedel { 1283bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1293bcfa9e4SHerbert Valerio Riedel 1303bcfa9e4SHerbert Valerio Riedel int fan_alarm = (data->fan_sta & G760A_REG_FAN_STA_RPM_OFF) ? 1 : 0; 1313bcfa9e4SHerbert Valerio Riedel 1323bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", fan_alarm); 1333bcfa9e4SHerbert Valerio Riedel } 1343bcfa9e4SHerbert Valerio Riedel 1353bcfa9e4SHerbert Valerio Riedel static ssize_t get_pwm(struct device *dev, struct device_attribute *da, 1363bcfa9e4SHerbert Valerio Riedel char *buf) 1373bcfa9e4SHerbert Valerio Riedel { 1383bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1393bcfa9e4SHerbert Valerio Riedel 1403bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", PWM_FROM_CNT(data->set_cnt)); 1413bcfa9e4SHerbert Valerio Riedel } 1423bcfa9e4SHerbert Valerio Riedel 1433bcfa9e4SHerbert Valerio Riedel static ssize_t set_pwm(struct device *dev, struct device_attribute *da, 1443bcfa9e4SHerbert Valerio Riedel const char *buf, size_t count) 1453bcfa9e4SHerbert Valerio Riedel { 1463bcfa9e4SHerbert Valerio Riedel struct i2c_client *client = to_i2c_client(dev); 1473bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1483bcfa9e4SHerbert Valerio Riedel unsigned long val; 1493bcfa9e4SHerbert Valerio Riedel 150179c4fdbSFrans Meulenbroeks if (kstrtoul(buf, 10, &val)) 1513bcfa9e4SHerbert Valerio Riedel return -EINVAL; 1523bcfa9e4SHerbert Valerio Riedel 1533bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 1542a844c14SGuenter Roeck data->set_cnt = PWM_TO_CNT(clamp_val(val, 0, 255)); 1553bcfa9e4SHerbert Valerio Riedel g760a_write_value(client, G760A_REG_SET_CNT, data->set_cnt); 1563bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1573bcfa9e4SHerbert Valerio Riedel 1583bcfa9e4SHerbert Valerio Riedel return count; 1593bcfa9e4SHerbert Valerio Riedel } 1603bcfa9e4SHerbert Valerio Riedel 1613bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm); 1623bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL); 1633bcfa9e4SHerbert Valerio Riedel static DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL); 1643bcfa9e4SHerbert Valerio Riedel 1653bcfa9e4SHerbert Valerio Riedel static struct attribute *g760a_attributes[] = { 1663bcfa9e4SHerbert Valerio Riedel &dev_attr_pwm1.attr, 1673bcfa9e4SHerbert Valerio Riedel &dev_attr_fan1_input.attr, 1683bcfa9e4SHerbert Valerio Riedel &dev_attr_fan1_alarm.attr, 1693bcfa9e4SHerbert Valerio Riedel NULL 1703bcfa9e4SHerbert Valerio Riedel }; 1713bcfa9e4SHerbert Valerio Riedel 1723bcfa9e4SHerbert Valerio Riedel static const struct attribute_group g760a_group = { 1733bcfa9e4SHerbert Valerio Riedel .attrs = g760a_attributes, 1743bcfa9e4SHerbert Valerio Riedel }; 1753bcfa9e4SHerbert Valerio Riedel 1761b9c491dSGuenter Roeck /* 1773bcfa9e4SHerbert Valerio Riedel * new-style driver model code 1783bcfa9e4SHerbert Valerio Riedel */ 1793bcfa9e4SHerbert Valerio Riedel 1803bcfa9e4SHerbert Valerio Riedel static int g760a_probe(struct i2c_client *client, 1813bcfa9e4SHerbert Valerio Riedel const struct i2c_device_id *id) 1823bcfa9e4SHerbert Valerio Riedel { 1833bcfa9e4SHerbert Valerio Riedel struct g760a_data *data; 1843bcfa9e4SHerbert Valerio Riedel int err; 1853bcfa9e4SHerbert Valerio Riedel 1863bcfa9e4SHerbert Valerio Riedel if (!i2c_check_functionality(client->adapter, 1873bcfa9e4SHerbert Valerio Riedel I2C_FUNC_SMBUS_BYTE_DATA)) 1883bcfa9e4SHerbert Valerio Riedel return -EIO; 1893bcfa9e4SHerbert Valerio Riedel 190a81b0f73SGuenter Roeck data = devm_kzalloc(&client->dev, sizeof(struct g760a_data), 191a81b0f73SGuenter Roeck GFP_KERNEL); 1923bcfa9e4SHerbert Valerio Riedel if (!data) 1933bcfa9e4SHerbert Valerio Riedel return -ENOMEM; 1943bcfa9e4SHerbert Valerio Riedel 1953bcfa9e4SHerbert Valerio Riedel i2c_set_clientdata(client, data); 1963bcfa9e4SHerbert Valerio Riedel 1973bcfa9e4SHerbert Valerio Riedel data->client = client; 1983bcfa9e4SHerbert Valerio Riedel mutex_init(&data->update_lock); 1993bcfa9e4SHerbert Valerio Riedel 2003bcfa9e4SHerbert Valerio Riedel /* setup default configuration for now */ 2013bcfa9e4SHerbert Valerio Riedel data->fan_div = G760A_DEFAULT_FAN_DIV; 2023bcfa9e4SHerbert Valerio Riedel data->clk = G760A_DEFAULT_CLK; 2033bcfa9e4SHerbert Valerio Riedel 2043bcfa9e4SHerbert Valerio Riedel /* Register sysfs hooks */ 2053bcfa9e4SHerbert Valerio Riedel err = sysfs_create_group(&client->dev.kobj, &g760a_group); 2063bcfa9e4SHerbert Valerio Riedel if (err) 207a81b0f73SGuenter Roeck return err; 2083bcfa9e4SHerbert Valerio Riedel 2093bcfa9e4SHerbert Valerio Riedel data->hwmon_dev = hwmon_device_register(&client->dev); 2103bcfa9e4SHerbert Valerio Riedel if (IS_ERR(data->hwmon_dev)) { 2113bcfa9e4SHerbert Valerio Riedel err = PTR_ERR(data->hwmon_dev); 2123bcfa9e4SHerbert Valerio Riedel goto error_hwmon_device_register; 2133bcfa9e4SHerbert Valerio Riedel } 2143bcfa9e4SHerbert Valerio Riedel 2153bcfa9e4SHerbert Valerio Riedel return 0; 2163bcfa9e4SHerbert Valerio Riedel 2173bcfa9e4SHerbert Valerio Riedel error_hwmon_device_register: 2183bcfa9e4SHerbert Valerio Riedel sysfs_remove_group(&client->dev.kobj, &g760a_group); 2193bcfa9e4SHerbert Valerio Riedel return err; 2203bcfa9e4SHerbert Valerio Riedel } 2213bcfa9e4SHerbert Valerio Riedel 2223bcfa9e4SHerbert Valerio Riedel static int g760a_remove(struct i2c_client *client) 2233bcfa9e4SHerbert Valerio Riedel { 2243bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = i2c_get_clientdata(client); 2253bcfa9e4SHerbert Valerio Riedel hwmon_device_unregister(data->hwmon_dev); 2263bcfa9e4SHerbert Valerio Riedel sysfs_remove_group(&client->dev.kobj, &g760a_group); 2273bcfa9e4SHerbert Valerio Riedel return 0; 2283bcfa9e4SHerbert Valerio Riedel } 2293bcfa9e4SHerbert Valerio Riedel 230*15ac4ddbSAxel Lin static const struct i2c_device_id g760a_id[] = { 231*15ac4ddbSAxel Lin { "g760a", 0 }, 232*15ac4ddbSAxel Lin { } 233*15ac4ddbSAxel Lin }; 234*15ac4ddbSAxel Lin MODULE_DEVICE_TABLE(i2c, g760a_id); 235*15ac4ddbSAxel Lin 236*15ac4ddbSAxel Lin static struct i2c_driver g760a_driver = { 237*15ac4ddbSAxel Lin .driver = { 238*15ac4ddbSAxel Lin .name = "g760a", 239*15ac4ddbSAxel Lin }, 240*15ac4ddbSAxel Lin .probe = g760a_probe, 241*15ac4ddbSAxel Lin .remove = g760a_remove, 242*15ac4ddbSAxel Lin .id_table = g760a_id, 243*15ac4ddbSAxel Lin }; 244*15ac4ddbSAxel Lin 245f0967eeaSAxel Lin module_i2c_driver(g760a_driver); 2463bcfa9e4SHerbert Valerio Riedel 2473bcfa9e4SHerbert Valerio Riedel MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); 2483bcfa9e4SHerbert Valerio Riedel MODULE_DESCRIPTION("GMT G760A driver"); 2493bcfa9e4SHerbert Valerio Riedel MODULE_LICENSE("GPL"); 250