12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 23bcfa9e4SHerbert Valerio Riedel /* 31b9c491dSGuenter Roeck * g760a - Driver for the Global Mixed-mode Technology Inc. G760A 41b9c491dSGuenter Roeck * fan speed PWM controller chip 51b9c491dSGuenter Roeck * 61b9c491dSGuenter Roeck * Copyright (C) 2007 Herbert Valerio Riedel <hvr@gnu.org> 71b9c491dSGuenter Roeck * 81b9c491dSGuenter Roeck * Complete datasheet is available at GMT's website: 91b9c491dSGuenter Roeck * http://www.gmt.com.tw/product/datasheet/EDS-760A.pdf 103bcfa9e4SHerbert Valerio Riedel */ 113bcfa9e4SHerbert Valerio Riedel 123bcfa9e4SHerbert Valerio Riedel #include <linux/module.h> 133bcfa9e4SHerbert Valerio Riedel #include <linux/init.h> 143bcfa9e4SHerbert Valerio Riedel #include <linux/slab.h> 153bcfa9e4SHerbert Valerio Riedel #include <linux/jiffies.h> 163bcfa9e4SHerbert Valerio Riedel #include <linux/i2c.h> 173bcfa9e4SHerbert Valerio Riedel #include <linux/hwmon.h> 183bcfa9e4SHerbert Valerio Riedel #include <linux/hwmon-sysfs.h> 193bcfa9e4SHerbert Valerio Riedel #include <linux/err.h> 203bcfa9e4SHerbert Valerio Riedel #include <linux/mutex.h> 213bcfa9e4SHerbert Valerio Riedel #include <linux/sysfs.h> 223bcfa9e4SHerbert Valerio Riedel 233bcfa9e4SHerbert Valerio Riedel enum g760a_regs { 243bcfa9e4SHerbert Valerio Riedel G760A_REG_SET_CNT = 0x00, 253bcfa9e4SHerbert Valerio Riedel G760A_REG_ACT_CNT = 0x01, 263bcfa9e4SHerbert Valerio Riedel G760A_REG_FAN_STA = 0x02 273bcfa9e4SHerbert Valerio Riedel }; 283bcfa9e4SHerbert Valerio Riedel 293bcfa9e4SHerbert Valerio Riedel #define G760A_REG_FAN_STA_RPM_OFF 0x1 /* +/-20% off */ 303bcfa9e4SHerbert Valerio Riedel #define G760A_REG_FAN_STA_RPM_LOW 0x2 /* below 1920rpm */ 313bcfa9e4SHerbert Valerio Riedel 323bcfa9e4SHerbert Valerio Riedel /* register data is read (and cached) at most once per second */ 333bcfa9e4SHerbert Valerio Riedel #define G760A_UPDATE_INTERVAL (HZ) 343bcfa9e4SHerbert Valerio Riedel 353bcfa9e4SHerbert Valerio Riedel struct g760a_data { 363bcfa9e4SHerbert Valerio Riedel struct i2c_client *client; 373bcfa9e4SHerbert Valerio Riedel struct mutex update_lock; 383bcfa9e4SHerbert Valerio Riedel 393bcfa9e4SHerbert Valerio Riedel /* board specific parameters */ 403bcfa9e4SHerbert Valerio Riedel u32 clk; /* default 32kHz */ 413bcfa9e4SHerbert Valerio Riedel u16 fan_div; /* default P=2 */ 423bcfa9e4SHerbert Valerio Riedel 433bcfa9e4SHerbert Valerio Riedel /* g760a register cache */ 443bcfa9e4SHerbert Valerio Riedel unsigned int valid:1; 453bcfa9e4SHerbert Valerio Riedel unsigned long last_updated; /* In jiffies */ 463bcfa9e4SHerbert Valerio Riedel 473bcfa9e4SHerbert Valerio Riedel u8 set_cnt; /* PWM (period) count number; 0xff stops fan */ 483bcfa9e4SHerbert Valerio Riedel u8 act_cnt; /* formula: cnt = (CLK * 30)/(rpm * P) */ 493bcfa9e4SHerbert Valerio Riedel u8 fan_sta; /* bit 0: set when actual fan speed more than 20% 503bcfa9e4SHerbert Valerio Riedel * outside requested fan speed 511b9c491dSGuenter Roeck * bit 1: set when fan speed below 1920 rpm 521b9c491dSGuenter Roeck */ 533bcfa9e4SHerbert Valerio Riedel }; 543bcfa9e4SHerbert Valerio Riedel 553bcfa9e4SHerbert Valerio Riedel #define G760A_DEFAULT_CLK 32768 563bcfa9e4SHerbert Valerio Riedel #define G760A_DEFAULT_FAN_DIV 2 573bcfa9e4SHerbert Valerio Riedel 583bcfa9e4SHerbert Valerio Riedel #define PWM_FROM_CNT(cnt) (0xff-(cnt)) 593bcfa9e4SHerbert Valerio Riedel #define PWM_TO_CNT(pwm) (0xff-(pwm)) 603bcfa9e4SHerbert Valerio Riedel 61ebec05bdSJean Delvare static inline unsigned int rpm_from_cnt(u8 val, u32 clk, u16 div) 623bcfa9e4SHerbert Valerio Riedel { 633bcfa9e4SHerbert Valerio Riedel return ((val == 0x00) ? 0 : ((clk*30)/(val*div))); 643bcfa9e4SHerbert Valerio Riedel } 653bcfa9e4SHerbert Valerio Riedel 663bcfa9e4SHerbert Valerio Riedel /* read/write wrappers */ 673bcfa9e4SHerbert Valerio Riedel static int g760a_read_value(struct i2c_client *client, enum g760a_regs reg) 683bcfa9e4SHerbert Valerio Riedel { 693bcfa9e4SHerbert Valerio Riedel return i2c_smbus_read_byte_data(client, reg); 703bcfa9e4SHerbert Valerio Riedel } 713bcfa9e4SHerbert Valerio Riedel 723bcfa9e4SHerbert Valerio Riedel static int g760a_write_value(struct i2c_client *client, enum g760a_regs reg, 733bcfa9e4SHerbert Valerio Riedel u16 value) 743bcfa9e4SHerbert Valerio Riedel { 753bcfa9e4SHerbert Valerio Riedel return i2c_smbus_write_byte_data(client, reg, value); 763bcfa9e4SHerbert Valerio Riedel } 773bcfa9e4SHerbert Valerio Riedel 781b9c491dSGuenter Roeck /* 793bcfa9e4SHerbert Valerio Riedel * sysfs attributes 803bcfa9e4SHerbert Valerio Riedel */ 813bcfa9e4SHerbert Valerio Riedel 823bcfa9e4SHerbert Valerio Riedel static struct g760a_data *g760a_update_client(struct device *dev) 833bcfa9e4SHerbert Valerio Riedel { 84e47c39b3SAxel Lin struct g760a_data *data = dev_get_drvdata(dev); 85e47c39b3SAxel Lin struct i2c_client *client = data->client; 863bcfa9e4SHerbert Valerio Riedel 873bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 883bcfa9e4SHerbert Valerio Riedel 893bcfa9e4SHerbert Valerio Riedel if (time_after(jiffies, data->last_updated + G760A_UPDATE_INTERVAL) 903bcfa9e4SHerbert Valerio Riedel || !data->valid) { 913bcfa9e4SHerbert Valerio Riedel dev_dbg(&client->dev, "Starting g760a update\n"); 923bcfa9e4SHerbert Valerio Riedel 933bcfa9e4SHerbert Valerio Riedel data->set_cnt = g760a_read_value(client, G760A_REG_SET_CNT); 943bcfa9e4SHerbert Valerio Riedel data->act_cnt = g760a_read_value(client, G760A_REG_ACT_CNT); 953bcfa9e4SHerbert Valerio Riedel data->fan_sta = g760a_read_value(client, G760A_REG_FAN_STA); 963bcfa9e4SHerbert Valerio Riedel 973bcfa9e4SHerbert Valerio Riedel data->last_updated = jiffies; 98*952a11caSPaul Fertser data->valid = true; 993bcfa9e4SHerbert Valerio Riedel } 1003bcfa9e4SHerbert Valerio Riedel 1013bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1023bcfa9e4SHerbert Valerio Riedel 1033bcfa9e4SHerbert Valerio Riedel return data; 1043bcfa9e4SHerbert Valerio Riedel } 1053bcfa9e4SHerbert Valerio Riedel 10692a4873dSJulia Lawall static ssize_t fan1_input_show(struct device *dev, 10792a4873dSJulia Lawall struct device_attribute *da, char *buf) 1083bcfa9e4SHerbert Valerio Riedel { 1093bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1103bcfa9e4SHerbert Valerio Riedel unsigned int rpm = 0; 1113bcfa9e4SHerbert Valerio Riedel 1123bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 1133bcfa9e4SHerbert Valerio Riedel if (!(data->fan_sta & G760A_REG_FAN_STA_RPM_LOW)) 1143bcfa9e4SHerbert Valerio Riedel rpm = rpm_from_cnt(data->act_cnt, data->clk, data->fan_div); 1153bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1163bcfa9e4SHerbert Valerio Riedel 1173bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", rpm); 1183bcfa9e4SHerbert Valerio Riedel } 1193bcfa9e4SHerbert Valerio Riedel 12092a4873dSJulia Lawall static ssize_t fan1_alarm_show(struct device *dev, 12192a4873dSJulia Lawall struct device_attribute *da, char *buf) 1223bcfa9e4SHerbert Valerio Riedel { 1233bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 1243bcfa9e4SHerbert Valerio Riedel 1253bcfa9e4SHerbert Valerio Riedel int fan_alarm = (data->fan_sta & G760A_REG_FAN_STA_RPM_OFF) ? 1 : 0; 1263bcfa9e4SHerbert Valerio Riedel 1273bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", fan_alarm); 1283bcfa9e4SHerbert Valerio Riedel } 1293bcfa9e4SHerbert Valerio Riedel 13092a4873dSJulia Lawall static ssize_t pwm1_show(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 1353bcfa9e4SHerbert Valerio Riedel return sprintf(buf, "%d\n", PWM_FROM_CNT(data->set_cnt)); 1363bcfa9e4SHerbert Valerio Riedel } 1373bcfa9e4SHerbert Valerio Riedel 13892a4873dSJulia Lawall static ssize_t pwm1_store(struct device *dev, struct device_attribute *da, 1393bcfa9e4SHerbert Valerio Riedel const char *buf, size_t count) 1403bcfa9e4SHerbert Valerio Riedel { 1413bcfa9e4SHerbert Valerio Riedel struct g760a_data *data = g760a_update_client(dev); 142e47c39b3SAxel Lin struct i2c_client *client = data->client; 1433bcfa9e4SHerbert Valerio Riedel unsigned long val; 1443bcfa9e4SHerbert Valerio Riedel 145179c4fdbSFrans Meulenbroeks if (kstrtoul(buf, 10, &val)) 1463bcfa9e4SHerbert Valerio Riedel return -EINVAL; 1473bcfa9e4SHerbert Valerio Riedel 1483bcfa9e4SHerbert Valerio Riedel mutex_lock(&data->update_lock); 1492a844c14SGuenter Roeck data->set_cnt = PWM_TO_CNT(clamp_val(val, 0, 255)); 1503bcfa9e4SHerbert Valerio Riedel g760a_write_value(client, G760A_REG_SET_CNT, data->set_cnt); 1513bcfa9e4SHerbert Valerio Riedel mutex_unlock(&data->update_lock); 1523bcfa9e4SHerbert Valerio Riedel 1533bcfa9e4SHerbert Valerio Riedel return count; 1543bcfa9e4SHerbert Valerio Riedel } 1553bcfa9e4SHerbert Valerio Riedel 15692a4873dSJulia Lawall static DEVICE_ATTR_RW(pwm1); 15792a4873dSJulia Lawall static DEVICE_ATTR_RO(fan1_input); 15892a4873dSJulia Lawall static DEVICE_ATTR_RO(fan1_alarm); 1593bcfa9e4SHerbert Valerio Riedel 160e47c39b3SAxel Lin static struct attribute *g760a_attrs[] = { 1613bcfa9e4SHerbert Valerio Riedel &dev_attr_pwm1.attr, 1623bcfa9e4SHerbert Valerio Riedel &dev_attr_fan1_input.attr, 1633bcfa9e4SHerbert Valerio Riedel &dev_attr_fan1_alarm.attr, 1643bcfa9e4SHerbert Valerio Riedel NULL 1653bcfa9e4SHerbert Valerio Riedel }; 1663bcfa9e4SHerbert Valerio Riedel 167e47c39b3SAxel Lin ATTRIBUTE_GROUPS(g760a); 1683bcfa9e4SHerbert Valerio Riedel 1691b9c491dSGuenter Roeck /* 1703bcfa9e4SHerbert Valerio Riedel * new-style driver model code 1713bcfa9e4SHerbert Valerio Riedel */ 1723bcfa9e4SHerbert Valerio Riedel 17367487038SStephen Kitt static int g760a_probe(struct i2c_client *client) 1743bcfa9e4SHerbert Valerio Riedel { 175e47c39b3SAxel Lin struct device *dev = &client->dev; 1763bcfa9e4SHerbert Valerio Riedel struct g760a_data *data; 177e47c39b3SAxel Lin struct device *hwmon_dev; 1783bcfa9e4SHerbert Valerio Riedel 179e47c39b3SAxel Lin if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 1803bcfa9e4SHerbert Valerio Riedel return -EIO; 1813bcfa9e4SHerbert Valerio Riedel 182e47c39b3SAxel Lin data = devm_kzalloc(dev, sizeof(struct g760a_data), GFP_KERNEL); 1833bcfa9e4SHerbert Valerio Riedel if (!data) 1843bcfa9e4SHerbert Valerio Riedel return -ENOMEM; 1853bcfa9e4SHerbert Valerio Riedel 1863bcfa9e4SHerbert Valerio Riedel data->client = client; 1873bcfa9e4SHerbert Valerio Riedel mutex_init(&data->update_lock); 1883bcfa9e4SHerbert Valerio Riedel 1893bcfa9e4SHerbert Valerio Riedel /* setup default configuration for now */ 1903bcfa9e4SHerbert Valerio Riedel data->fan_div = G760A_DEFAULT_FAN_DIV; 1913bcfa9e4SHerbert Valerio Riedel data->clk = G760A_DEFAULT_CLK; 1923bcfa9e4SHerbert Valerio Riedel 193e47c39b3SAxel Lin hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 194e47c39b3SAxel Lin data, 195e47c39b3SAxel Lin g760a_groups); 196e47c39b3SAxel Lin return PTR_ERR_OR_ZERO(hwmon_dev); 1973bcfa9e4SHerbert Valerio Riedel } 1983bcfa9e4SHerbert Valerio Riedel 19915ac4ddbSAxel Lin static const struct i2c_device_id g760a_id[] = { 20015ac4ddbSAxel Lin { "g760a", 0 }, 20115ac4ddbSAxel Lin { } 20215ac4ddbSAxel Lin }; 20315ac4ddbSAxel Lin MODULE_DEVICE_TABLE(i2c, g760a_id); 20415ac4ddbSAxel Lin 20515ac4ddbSAxel Lin static struct i2c_driver g760a_driver = { 20615ac4ddbSAxel Lin .driver = { 20715ac4ddbSAxel Lin .name = "g760a", 20815ac4ddbSAxel Lin }, 20967487038SStephen Kitt .probe_new = g760a_probe, 21015ac4ddbSAxel Lin .id_table = g760a_id, 21115ac4ddbSAxel Lin }; 21215ac4ddbSAxel Lin 213f0967eeaSAxel Lin module_i2c_driver(g760a_driver); 2143bcfa9e4SHerbert Valerio Riedel 2153bcfa9e4SHerbert Valerio Riedel MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>"); 2163bcfa9e4SHerbert Valerio Riedel MODULE_DESCRIPTION("GMT G760A driver"); 2173bcfa9e4SHerbert Valerio Riedel MODULE_LICENSE("GPL"); 218